1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2017, 2018 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
19 #include "output/spv/spv-legacy-decoder.h"
27 #include "data/data-out.h"
28 #include "data/calendar.h"
29 #include "data/format.h"
30 #include "data/value.h"
31 #include "libpspp/assertion.h"
32 #include "libpspp/hash-functions.h"
33 #include "libpspp/hmap.h"
34 #include "libpspp/message.h"
35 #include "output/pivot-table.h"
36 #include "output/spv/detail-xml-parser.h"
37 #include "output/spv/spv-legacy-data.h"
38 #include "output/spv/spv-table-look.h"
39 #include "output/spv/spv.h"
40 #include "output/spv/structure-xml-parser.h"
42 #include "gl/c-strtod.h"
43 #include "gl/xalloc.h"
44 #include "gl/xmemdup0.h"
46 #include <libxml/tree.h>
49 #define N_(msgid) msgid
50 #define _(msgid) gettext (msgid)
54 struct hmap_node hmap_node; /* By name. */
57 struct fmt_spec format;
59 struct spv_series *label_series;
62 const struct spvxml_node *xml;
64 struct spv_data_value *values;
66 struct hmap map; /* Contains "struct spv_mapping". */
69 struct pivot_dimension *dimension;
71 struct pivot_category **index_to_category;
74 struct spvdx_affix **affixes;
78 static void spv_map_destroy (struct hmap *);
80 static struct spv_series *
81 spv_series_first (struct hmap *series_map)
83 struct spv_series *series;
84 HMAP_FOR_EACH (series, struct spv_series, hmap_node, series_map)
89 static struct spv_series *
90 spv_series_find (const struct hmap *series_map, const char *name)
92 struct spv_series *series;
93 HMAP_FOR_EACH_WITH_HASH (series, struct spv_series, hmap_node,
94 hash_string (name, 0), series_map)
95 if (!strcmp (name, series->name))
100 static struct spv_series *
101 spv_series_from_ref (const struct hmap *series_map,
102 const struct spvxml_node *ref)
104 const struct spvxml_node *node
105 = (spvdx_is_source_variable (ref)
106 ? &spvdx_cast_source_variable (ref)->node_
107 : &spvdx_cast_derived_variable (ref)->node_);
108 return spv_series_find (series_map, node->id);
112 spv_series_dump (const struct spv_series *series)
114 printf ("series \"%s\"", series->name);
116 printf (" (label \"%s\")", series->label);
117 printf (", %zu values:", series->n_values);
118 for (size_t i = 0; i < series->n_values; i++)
121 spv_data_value_dump (&series->values[i], stdout);
127 spv_series_destroy (struct hmap *series_map)
129 struct spv_series *series, *next_series;
130 HMAP_FOR_EACH_SAFE (series, next_series, struct spv_series, hmap_node,
134 free (series->label);
136 for (size_t i = 0; i < series->n_values; i++)
137 spv_data_value_uninit (&series->values[i]);
138 free (series->values);
140 spv_map_destroy (&series->map);
142 free (series->index_to_category);
144 hmap_delete (series_map, &series->hmap_node);
147 hmap_destroy (series_map);
152 struct hmap_node hmap_node;
154 struct spv_data_value to;
157 static struct spv_mapping *
158 spv_map_search (const struct hmap *map, double from)
160 struct spv_mapping *mapping;
161 HMAP_FOR_EACH_WITH_HASH (mapping, struct spv_mapping, hmap_node,
162 hash_double (from, 0), map)
163 if (mapping->from == from)
168 static const struct spv_data_value *
169 spv_map_lookup (const struct hmap *map, const struct spv_data_value *in)
174 const struct spv_mapping *m = spv_map_search (map, in->d);
175 return m ? &m->to : in;
179 parse_real (const char *s, double *real)
181 int save_errno = errno;
184 *real = c_strtod (s, &end);
185 bool ok = !errno && end > s && !*end;
191 static char * WARN_UNUSED_RESULT
192 spv_map_insert (struct hmap *map, double from, const char *to,
193 bool try_strings_as_numbers, const struct fmt_spec *format)
195 struct spv_mapping *mapping = xmalloc (sizeof *mapping);
196 mapping->from = from;
198 if ((try_strings_as_numbers || (format && fmt_is_numeric (format->type)))
199 && parse_real (to, &mapping->to.d))
201 if (try_strings_as_numbers)
202 mapping->to.width = -1;
205 union value v = { .f = mapping->to.d };
206 mapping->to.s = data_out_stretchy (&v, NULL, format,
207 settings_get_fmt_settings (),
209 mapping->to.width = strlen (mapping->to.s);
214 mapping->to.width = strlen (to);
215 mapping->to.s = xstrdup (to);
218 struct spv_mapping *old_mapping = spv_map_search (map, from);
221 bool same = spv_data_value_equal (&old_mapping->to, &mapping->to);
222 spv_data_value_uninit (&mapping->to);
225 : xasprintf ("Duplicate relabeling differs for from=\"%.*g\"",
229 hmap_insert (map, &mapping->hmap_node, hash_double (from, 0));
234 spv_map_destroy (struct hmap *map)
236 struct spv_mapping *mapping, *next;
237 HMAP_FOR_EACH_SAFE (mapping, next, struct spv_mapping, hmap_node, map)
239 spv_data_value_uninit (&mapping->to);
240 hmap_delete (map, &mapping->hmap_node);
246 static char * WARN_UNUSED_RESULT
247 spv_series_parse_relabels (struct hmap *map,
248 struct spvdx_relabel **relabels, size_t n_relabels,
249 bool try_strings_as_numbers,
250 const struct fmt_spec *format)
252 for (size_t i = 0; i < n_relabels; i++)
254 const struct spvdx_relabel *relabel = relabels[i];
255 char *error = spv_map_insert (map, relabel->from, relabel->to,
256 try_strings_as_numbers, format);
263 static char * WARN_UNUSED_RESULT
264 spv_series_parse_value_map_entry (struct hmap *map,
265 const struct spvdx_value_map_entry *vme)
267 for (const char *p = vme->from; ; p++)
269 int save_errno = errno;
272 double from = c_strtod (p, &end);
273 bool ok = !errno && end > p && strchr (";", *end);
276 return xasprintf ("Syntax error in valueMapEntry from=\"%s\".",
279 char *error = spv_map_insert (map, from, vme->to, true,
280 &(struct fmt_spec) { FMT_A, 40, 0 });
291 static struct fmt_spec
292 decode_date_time_format (const struct spvdx_date_time_format *dtf)
294 if (dtf->dt_base_format == SPVDX_DT_BASE_FORMAT_DATE)
297 = (dtf->show_quarter > 0 ? FMT_QYR
298 : dtf->show_week > 0 ? FMT_WKYR
299 : dtf->mdy_order == SPVDX_MDY_ORDER_DAY_MONTH_YEAR
300 ? (dtf->month_format == SPVDX_MONTH_FORMAT_NUMBER
301 || dtf->month_format == SPVDX_MONTH_FORMAT_PADDED_NUMBER
302 ? FMT_EDATE : FMT_DATE)
303 : dtf->mdy_order == SPVDX_MDY_ORDER_YEAR_MONTH_DAY ? FMT_SDATE
306 int w = fmt_min_output_width (type);
307 if (dtf->year_abbreviation <= 0)
309 return (struct fmt_spec) { .type = type, .w = w };
314 = (dtf->dt_base_format == SPVDX_DT_BASE_FORMAT_DATE_TIME
315 ? (dtf->mdy_order == SPVDX_MDY_ORDER_YEAR_MONTH_DAY
318 : (dtf->show_day > 0 ? FMT_DTIME
319 : dtf->show_hour > 0 ? FMT_TIME
321 int w = fmt_min_output_width (type);
323 if (dtf->show_second > 0)
326 if (dtf->show_millis > 0)
332 return (struct fmt_spec) { .type = type, .w = w, .d = d };
336 static struct fmt_spec
337 decode_elapsed_time_format (const struct spvdx_elapsed_time_format *etf)
340 = (etf->dt_base_format != SPVDX_DT_BASE_FORMAT_TIME ? FMT_DTIME
341 : etf->show_hour > 0 ? FMT_TIME
343 int w = fmt_min_output_width (type);
345 if (etf->show_second > 0)
348 if (etf->show_millis > 0)
354 return (struct fmt_spec) { .type = type, .w = w, .d = d };
357 static struct fmt_spec
358 decode_number_format (const struct spvdx_number_format *nf)
360 enum fmt_type type = (nf->scientific == SPVDX_SCIENTIFIC_TRUE ? FMT_E
361 : nf->prefix && !strcmp (nf->prefix, "$") ? FMT_DOLLAR
362 : nf->suffix && !strcmp (nf->suffix, "%") ? FMT_PCT
363 : nf->use_grouping ? FMT_COMMA
366 int d = nf->maximum_fraction_digits;
370 struct fmt_spec f = (struct fmt_spec) { type, 40, d };
375 /* Returns an *approximation* of IN as a fmt_spec.
377 Not for use with string formats, which don't have any options anyway. */
378 static struct fmt_spec
379 decode_format (const struct spvdx_format *in)
381 if (in->f_base_format == SPVDX_F_BASE_FORMAT_DATE ||
382 in->f_base_format == SPVDX_F_BASE_FORMAT_TIME ||
383 in->f_base_format == SPVDX_F_BASE_FORMAT_DATE_TIME)
385 struct spvdx_date_time_format dtf = {
386 .dt_base_format = (in->f_base_format == SPVDX_F_BASE_FORMAT_DATE
387 ? SPVDX_DT_BASE_FORMAT_DATE
388 : in->f_base_format == SPVDX_F_BASE_FORMAT_TIME
389 ? SPVDX_DT_BASE_FORMAT_TIME
390 : SPVDX_DT_BASE_FORMAT_DATE_TIME),
391 .separator_chars = in->separator_chars,
392 .mdy_order = in->mdy_order,
393 .show_year = in->show_year,
394 .year_abbreviation = in->year_abbreviation,
395 .show_quarter = in->show_quarter,
396 .quarter_prefix = in->quarter_prefix,
397 .quarter_suffix = in->quarter_suffix,
398 .show_month = in->show_month,
399 .month_format = in->month_format,
400 .show_week = in->show_week,
401 .week_padding = in->week_padding,
402 .week_suffix = in->week_suffix,
403 .show_day_of_week = in->show_day_of_week,
404 .day_of_week_abbreviation = in->day_of_week_abbreviation,
405 .day_padding = in->day_padding,
406 .day_of_month_padding = in->day_of_month_padding,
407 .hour_padding = in->hour_padding,
408 .minute_padding = in->minute_padding,
409 .second_padding = in->second_padding,
410 .show_day = in->show_day,
411 .show_hour = in->show_hour,
412 .show_minute = in->show_minute,
413 .show_second = in->show_second,
414 .show_millis = in->show_millis,
415 .day_type = in->day_type,
416 .hour_format = in->hour_format,
418 return decode_date_time_format (&dtf);
420 else if (in->f_base_format == SPVDX_F_BASE_FORMAT_ELAPSED_TIME)
422 struct spvdx_elapsed_time_format etf = {
423 .dt_base_format = (in->f_base_format == SPVDX_F_BASE_FORMAT_DATE
424 ? SPVDX_DT_BASE_FORMAT_DATE
425 : in->f_base_format == SPVDX_F_BASE_FORMAT_TIME
426 ? SPVDX_DT_BASE_FORMAT_TIME
427 : SPVDX_DT_BASE_FORMAT_DATE_TIME),
428 .day_padding = in->day_padding,
429 .minute_padding = in->minute_padding,
430 .second_padding = in->second_padding,
431 .show_year = in->show_year,
432 .show_day = in->show_day,
433 .show_hour = in->show_hour,
434 .show_minute = in->show_minute,
435 .show_second = in->show_second,
436 .show_millis = in->show_millis,
438 return decode_elapsed_time_format (&etf);
442 assert (!in->f_base_format);
443 struct spvdx_number_format nf = {
444 .minimum_integer_digits = in->minimum_integer_digits,
445 .maximum_fraction_digits = in->maximum_fraction_digits,
446 .minimum_fraction_digits = in->minimum_fraction_digits,
447 .use_grouping = in->use_grouping,
448 .scientific = in->scientific,
450 .prefix = in->prefix,
451 .suffix = in->suffix,
453 return decode_number_format (&nf);
458 spv_series_execute_mapping (struct spv_series *series)
460 if (!hmap_is_empty (&series->map))
462 series->remapped = true;
463 for (size_t i = 0; i < series->n_values; i++)
465 struct spv_data_value *value = &series->values[i];
466 if (value->width >= 0)
469 const struct spv_mapping *mapping = spv_map_search (&series->map,
473 value->index = value->d;
474 assert (value->index == floor (value->index));
475 value->width = mapping->to.width;
476 if (value->width >= 0)
477 value->s = xmemdup0 (mapping->to.s, mapping->to.width);
479 value->d = mapping->to.d;
485 static char * WARN_UNUSED_RESULT
486 spv_series_remap_formats (struct spv_series *series,
487 struct spvxml_node **seq, size_t n_seq)
489 spv_map_destroy (&series->map);
490 hmap_init (&series->map);
491 for (size_t i = 0; i < n_seq; i++)
493 struct spvxml_node *node = seq[i];
494 if (spvdx_is_format (node))
496 struct spvdx_format *f = spvdx_cast_format (node);
497 series->format = decode_format (f);
498 char *error = spv_series_parse_relabels (
499 &series->map, f->relabel, f->n_relabel,
500 f->try_strings_as_numbers > 0, &series->format);
504 series->affixes = f->affix;
505 series->n_affixes = f->n_affix;
507 else if (spvdx_is_string_format (node))
509 struct spvdx_string_format *sf = spvdx_cast_string_format (node);
510 char *error = spv_series_parse_relabels (&series->map,
511 sf->relabel, sf->n_relabel,
516 series->affixes = sf->affix;
517 series->n_affixes = sf->n_affix;
522 spv_series_execute_mapping (series);
526 static char * WARN_UNUSED_RESULT
527 spv_series_remap_vmes (struct spv_series *series,
528 struct spvdx_value_map_entry **vmes,
531 spv_map_destroy (&series->map);
532 hmap_init (&series->map);
533 for (size_t i = 0; i < n_vmes; i++)
535 char *error = spv_series_parse_value_map_entry (&series->map, vmes[i]);
539 spv_series_execute_mapping (series);
544 decode_footnotes (struct pivot_table *table, const struct spvdx_footnotes *f)
546 if (f->n_footnote_mapping > 0)
547 pivot_table_create_footnote__ (table, f->n_footnote_mapping - 1,
549 for (size_t i = 0; i < f->n_footnote_mapping; i++)
551 const struct spvdx_footnote_mapping *fm = f->footnote_mapping[i];
552 pivot_table_create_footnote__ (table, fm->defines_reference - 1,
553 pivot_value_new_user_text (fm->to, -1),
558 static struct cell_color
559 optional_color (int color, struct cell_color default_color)
562 ? (struct cell_color) CELL_COLOR (color >> 16, color >> 8, color)
567 optional_length (const char *s, int default_length)
569 /* There is usually a "pt" suffix. We ignore it. */
571 return s && sscanf (s, "%d", &length) == 1 ? length : default_length;
575 optional_px (double inches, int default_px)
577 return inches != DBL_MAX ? inches * 96.0 : default_px;
581 decode_spvdx_style_incremental (const struct spvdx_style *in,
582 const struct spvdx_style *bg,
583 struct table_area_style *out)
585 if (in && in->font_weight)
586 out->font_style.bold = in->font_weight == SPVDX_FONT_WEIGHT_BOLD;
587 if (in && in->font_style)
588 out->font_style.italic = in->font_style == SPVDX_FONT_STYLE_ITALIC;
589 if (in && in->font_underline)
590 out->font_style.underline = in->font_underline == SPVDX_FONT_UNDERLINE_UNDERLINE;
591 if (in && in->color >= 0)
593 out->font_style.fg[0] = optional_color (
594 in->color, (struct cell_color) CELL_COLOR_BLACK);
595 out->font_style.fg[1] = out->font_style.fg[0];
597 if (bg && bg->color >= 0)
599 out->font_style.bg[0] = optional_color (
600 bg->color, (struct cell_color) CELL_COLOR_WHITE);
601 out->font_style.bg[1] = out->font_style.bg[0];
603 if (in && in->font_family)
605 free (out->font_style.typeface);
606 out->font_style.typeface = xstrdup (in->font_family);
608 if (in && in->font_size)
610 int size = optional_length (in->font_size, 0);
612 out->font_style.size = size;
614 if (in && in->text_alignment)
615 out->cell_style.halign
616 = (in->text_alignment == SPVDX_TEXT_ALIGNMENT_LEFT
618 : in->text_alignment == SPVDX_TEXT_ALIGNMENT_RIGHT
620 : in->text_alignment == SPVDX_TEXT_ALIGNMENT_CENTER
621 ? TABLE_HALIGN_CENTER
622 : in->text_alignment == SPVDX_TEXT_ALIGNMENT_DECIMAL
623 ? TABLE_HALIGN_DECIMAL
624 : TABLE_HALIGN_MIXED);
625 if (in && in->label_location_vertical)
626 out->cell_style.valign =
627 (in->label_location_vertical == SPVDX_LABEL_LOCATION_VERTICAL_NEGATIVE
628 ? TABLE_VALIGN_BOTTOM
629 : in->label_location_vertical == SPVDX_LABEL_LOCATION_VERTICAL_POSITIVE
631 : TABLE_VALIGN_CENTER);
632 if (in && in->decimal_offset != DBL_MAX)
633 out->cell_style.decimal_offset = optional_px (in->decimal_offset, 0);
635 if (in && in->margin_left != DBL_MAX)
636 out->cell_style.margin[TABLE_HORZ][0] = optional_pt (in->margin_left, 8);
637 if (in && in->margin_right != DBL_MAX)
638 out->cell_style.margin[TABLE_HORZ][1] = optional_pt (in->margin_right, 11);
639 if (in && in->margin_top != DBL_MAX)
640 out->cell_style.margin[TABLE_VERT][0] = optional_pt (in->margin_top, 1);
641 if (in && in->margin_bottom != DBL_MAX)
642 out->cell_style.margin[TABLE_VERT][1] = optional_pt (in->margin_bottom, 1);
647 decode_spvdx_style (const struct spvdx_style *in,
648 const struct spvdx_style *bg,
649 struct table_area_style *out)
651 *out = (struct table_area_style) TABLE_AREA_STYLE_INITIALIZER;
652 decode_spvdx_style_incremental (in, bg, out);
656 add_footnote (struct pivot_value *v, int idx, struct pivot_table *table)
658 if (idx < 1 || idx > table->n_footnotes)
661 pivot_value_add_footnote (v, table->footnotes[idx - 1]);
664 static char * WARN_UNUSED_RESULT
665 decode_label_frame (struct pivot_table *table,
666 const struct spvdx_label_frame *lf)
671 struct pivot_value **target;
672 struct table_area_style *area;
673 if (lf->label->purpose == SPVDX_PURPOSE_TITLE)
675 target = &table->title;
676 area = &table->look->areas[PIVOT_AREA_TITLE];
678 else if (lf->label->purpose == SPVDX_PURPOSE_SUB_TITLE)
680 target = &table->caption;
681 area = &table->look->areas[PIVOT_AREA_CAPTION];
683 else if (lf->label->purpose == SPVDX_PURPOSE_FOOTNOTE)
685 if (lf->label->n_text > 0
686 && lf->label->text[0]->uses_reference != INT_MIN)
689 area = &table->look->areas[PIVOT_AREA_FOOTER];
694 else if (lf->label->purpose == SPVDX_PURPOSE_LAYER)
697 area = &table->look->areas[PIVOT_AREA_LAYERS];
702 table_area_style_uninit (area);
703 decode_spvdx_style (lf->label->style, lf->label->text_frame_style, area);
707 struct pivot_value *value = xzalloc (sizeof *value);
708 value->type = PIVOT_VALUE_TEXT;
709 for (size_t i = 0; i < lf->label->n_text; i++)
711 const struct spvdx_text *in = lf->label->text[i];
712 if (in->defines_reference != INT_MIN)
713 add_footnote (value, in->defines_reference, table);
714 else if (!value->text.local)
715 value->text.local = xstrdup (in->text);
718 char *new = xasprintf ("%s%s", value->text.local, in->text);
719 free (value->text.local);
720 value->text.local = new;
723 pivot_value_destroy (*target);
727 for (size_t i = 0; i < lf->label->n_text; i++)
729 const struct spvdx_text *in = lf->label->text[i];
730 if (in->uses_reference == INT_MIN)
734 size_t length = strlen (in->text);
735 if (length && in->text[length - 1] == '\n')
738 pivot_table_create_footnote__ (
739 table, in->uses_reference - 1, NULL,
740 pivot_value_new_user_text (in->text, length));
744 size_t length = strlen (in->text);
745 if (length && in->text[length - 1] == '.')
748 pivot_table_create_footnote__ (
749 table, in->uses_reference - 1,
750 pivot_value_new_user_text (in->text, length), NULL);
756 /* Special return value for decode_spvdx_variable(). */
757 static char BAD_REFERENCE;
759 static char * WARN_UNUSED_RESULT
760 decode_spvdx_source_variable (const struct spvxml_node *node,
761 struct spv_data *data,
762 struct hmap *series_map)
764 const struct spvdx_source_variable *sv = spvdx_cast_source_variable (node);
766 struct spv_series *label_series = NULL;
767 if (sv->label_variable)
769 label_series = spv_series_find (series_map,
770 sv->label_variable->node_.id);
772 return &BAD_REFERENCE;
774 label_series->is_label_series = true;
777 const struct spv_data_variable *var = spv_data_find_variable (
778 data, sv->source, sv->source_name);
780 return xasprintf ("sourceVariable %s references nonexistent "
781 "source %s variable %s.",
782 sv->node_.id, sv->source, sv->source_name);
784 struct spv_series *s = xzalloc (sizeof *s);
785 s->name = xstrdup (node->id);
787 s->label = sv->label ? xstrdup (sv->label) : NULL;
788 s->label_series = label_series;
789 s->values = spv_data_values_clone (var->values, var->n_values);
790 s->n_values = var->n_values;
793 hmap_insert (series_map, &s->hmap_node, hash_string (s->name, 0));
795 char *error = spv_series_remap_formats (s, sv->seq, sv->n_seq);
799 if (label_series && !s->remapped)
801 for (size_t i = 0; i < s->n_values; i++)
802 if (s->values[i].width < 0)
805 if (label_series->values[i].width < 0)
807 union value v = { .f = label_series->values[i].d };
808 dest = data_out_stretchy (&v, "UTF-8", &s->format,
809 settings_get_fmt_settings (), NULL);
812 dest = label_series->values[i].s;
813 char *error = spv_map_insert (&s->map, s->values[i].d,
815 free (error); /* Duplicates are OK. */
816 if (label_series->values[i].width < 0)
824 static char * WARN_UNUSED_RESULT
825 decode_spvdx_derived_variable (const struct spvxml_node *node,
826 struct hmap *series_map)
828 const struct spvdx_derived_variable *dv = spvdx_cast_derived_variable (node);
830 struct spv_data_value *values;
833 struct substring value = ss_cstr (dv->value);
834 if (ss_equals (value, ss_cstr ("constant(0)")))
836 struct spv_series *existing_series = spv_series_first (series_map);
837 if (!existing_series)
838 return &BAD_REFERENCE;
840 n_values = existing_series->n_values;
841 values = XCALLOC (n_values, struct spv_data_value);
842 for (size_t i = 0; i < n_values; i++)
843 values[i].width = -1;
845 else if (ss_starts_with (value, ss_cstr ("constant(")))
850 else if (ss_starts_with (value, ss_cstr ("map("))
851 && ss_ends_with (value, ss_cstr (")")))
853 char *dependency_name = ss_xstrdup (ss_substr (value, 4,
855 struct spv_series *dependency
856 = spv_series_find (series_map, dependency_name);
857 free (dependency_name);
859 return &BAD_REFERENCE;
861 values = spv_data_values_clone (dependency->values,
862 dependency->n_values);
863 n_values = dependency->n_values;
866 return xasprintf ("Derived variable %s has unknown value \"%s\"",
867 node->id, dv->value);
869 struct spv_series *s = xzalloc (sizeof *s);
871 s->name = xstrdup (node->id);
873 s->n_values = n_values;
875 hmap_insert (series_map, &s->hmap_node, hash_string (s->name, 0));
877 char *error = spv_series_remap_vmes (s, dv->value_map_entry,
878 dv->n_value_map_entry);
882 error = spv_series_remap_formats (s, dv->seq, dv->n_seq);
888 for (size_t i = 0; i < n_values; i++)
889 if (values[i].width != 0)
891 for (size_t i = 0; i < n_values; i++)
892 spv_data_value_uninit (&s->values[i]);
903 struct format_mapping
905 struct hmap_node hmap_node;
910 static const struct format_mapping *
911 format_map_find (const struct hmap *format_map, uint32_t u32_format)
915 const struct format_mapping *fm;
916 HMAP_FOR_EACH_IN_BUCKET (fm, struct format_mapping, hmap_node,
917 hash_int (u32_format, 0), format_map)
918 if (fm->from == u32_format)
925 static char * WARN_UNUSED_RESULT
926 spv_format_from_data_value (const struct spv_data_value *data,
927 const struct hmap *format_map,
928 struct fmt_spec *out)
932 *out = fmt_for_output (FMT_F, 40, 2);
936 uint32_t u32_format = data->width < 0 ? data->d : atoi (data->s);
937 const struct format_mapping *fm = format_map_find (format_map, u32_format);
943 return spv_decode_fmt_spec (u32_format, out);
946 static char * WARN_UNUSED_RESULT
947 pivot_value_from_data_value (const struct spv_data_value *data,
948 const struct spv_data_value *format,
949 const struct hmap *format_map,
950 struct pivot_value **vp)
955 char *error = spv_format_from_data_value (format, format_map, &f);
959 struct pivot_value *v = xzalloc (sizeof *v);
960 if (data->width >= 0)
962 if (format && fmt_get_category (f.type) == FMT_CAT_DATE)
964 int year, month, day, hour, minute, second, msec, len = -1;
965 if (sscanf (data->s, "%4d-%2d-%2dT%2d:%2d:%2d.%3d%n",
966 &year, &month, &day, &hour, &minute, &second,
969 && data->s[len] == '\0')
971 double date = calendar_gregorian_to_offset (
972 year, month, day, settings_get_fmt_settings (), NULL);
975 v->type = PIVOT_VALUE_NUMERIC;
976 v->numeric.x = (date * 60. * 60. * 24.
981 v->numeric.format = f;
987 else if (format && fmt_get_category (f.type) == FMT_CAT_TIME)
989 int hour, minute, second, msec, len = -1;
990 if (sscanf (data->s, "%d:%2d:%2d.%3d%n",
991 &hour, &minute, &second, &msec, &len) == 4
993 && data->s[len] == '\0')
995 v->type = PIVOT_VALUE_NUMERIC;
996 v->numeric.x = (hour * 60. * 60.
1000 v->numeric.format = f;
1005 v->type = PIVOT_VALUE_STRING;
1006 v->string.s = xstrdup (data->s);
1010 v->type = PIVOT_VALUE_NUMERIC;
1011 v->numeric.x = data->d;
1012 v->numeric.format = f;
1019 add_parents (struct pivot_category *cat, struct pivot_category *parent,
1022 cat->parent = parent;
1023 cat->group_index = group_index;
1024 if (pivot_category_is_group (cat))
1025 for (size_t i = 0; i < cat->n_subs; i++)
1026 add_parents (cat->subs[i], cat, i);
1029 static const struct spvdx_facet_level *
1030 find_facet_level (const struct spvdx_visualization *v, int facet_level)
1032 const struct spvdx_facet_layout *layout = v->graph->facet_layout;
1033 for (size_t i = 0; i < layout->n_facet_level; i++)
1035 const struct spvdx_facet_level *fl = layout->facet_level[i];
1036 if (facet_level == fl->level)
1043 should_show_label (const struct spvdx_facet_level *fl)
1045 return fl && fl->axis->label && fl->axis->label->style->visible != 0;
1049 max_category (const struct spv_series *s)
1051 double max_cat = -DBL_MAX;
1052 for (size_t i = 0; i < s->n_values; i++)
1054 const struct spv_data_value *dv = &s->values[i];
1055 double d = dv->width < 0 ? dv->d : dv->index;
1059 assert (max_cat >= 0 && max_cat < SIZE_MAX - 1);
1065 add_affixes (struct pivot_table *table, struct pivot_value *value,
1066 struct spvdx_affix **affixes, size_t n_affixes)
1068 for (size_t i = 0; i < n_affixes; i++)
1069 add_footnote (value, affixes[i]->defines_reference, table);
1072 static char * WARN_UNUSED_RESULT
1073 add_dimension (struct spv_series **series, size_t n,
1074 enum pivot_axis_type axis_type,
1075 const struct spvdx_visualization *v, struct pivot_table *table,
1076 struct spv_series **dim_seriesp, size_t *n_dim_seriesp,
1077 int base_facet_level, struct pivot_dimension **dp)
1081 const struct spvdx_facet_level *fl
1082 = find_facet_level (v, base_facet_level + n);
1085 struct table_area_style *area
1086 = (axis_type == PIVOT_AXIS_COLUMN
1087 ? &table->look->areas[PIVOT_AREA_COLUMN_LABELS]
1088 : axis_type == PIVOT_AXIS_ROW
1089 ? &table->look->areas[PIVOT_AREA_ROW_LABELS]
1091 if (area && fl->axis->label)
1093 table_area_style_uninit (area);
1094 decode_spvdx_style (fl->axis->label->style,
1095 fl->axis->label->text_frame_style, area);
1099 if (axis_type == PIVOT_AXIS_ROW)
1101 const struct spvdx_facet_level *fl2
1102 = find_facet_level (v, base_facet_level + (n - 1));
1104 decode_spvdx_style_incremental (
1105 fl2->axis->major_ticks->style,
1106 fl2->axis->major_ticks->tick_frame_style,
1107 &table->look->areas[PIVOT_AREA_ROW_LABELS]);
1110 const struct spvdx_facet_level *fl3 = find_facet_level (v, base_facet_level);
1111 if (fl3 && fl3->axis->major_ticks->label_angle == -90)
1113 if (axis_type == PIVOT_AXIS_COLUMN)
1114 table->rotate_inner_column_labels = true;
1116 table->rotate_outer_row_labels = true;
1119 /* Find the first row for each category. */
1120 size_t max_cat = max_category (series[0]);
1121 size_t *cat_rows = xnmalloc (max_cat + 1, sizeof *cat_rows);
1122 for (size_t k = 0; k <= max_cat; k++)
1123 cat_rows[k] = SIZE_MAX;
1124 for (size_t k = 0; k < series[0]->n_values; k++)
1126 const struct spv_data_value *dv = &series[0]->values[k];
1127 double d = dv->width < 0 ? dv->d : dv->index;
1128 if (d >= 0 && d < SIZE_MAX - 1)
1131 if (cat_rows[row] == SIZE_MAX)
1136 /* Drop missing categories and count what's left. */
1138 for (size_t k = 0; k <= max_cat; k++)
1139 if (cat_rows[k] != SIZE_MAX)
1140 cat_rows[n_cats++] = cat_rows[k];
1141 assert (n_cats > 0);
1143 /* Make the categories. */
1144 struct pivot_dimension *d = xzalloc (sizeof *d);
1145 table->dimensions[table->n_dimensions++] = d;
1147 series[0]->n_index = max_cat + 1;
1148 series[0]->index_to_category = xcalloc (
1149 max_cat + 1, sizeof *series[0]->index_to_category);
1150 struct pivot_category **cats = xnmalloc (n_cats, sizeof **cats);
1151 for (size_t k = 0; k < n_cats; k++)
1153 struct spv_data_value *dv = &series[0]->values[cat_rows[k]];
1154 int dv_num = dv ? dv->d : dv->index;
1155 struct pivot_category *cat = xzalloc (sizeof *cat);
1156 char *retval = pivot_value_from_data_value (
1157 spv_map_lookup (&series[0]->map, dv), NULL, NULL, &cat->name);
1167 cat->data_index = k;
1168 cat->presentation_index = cat_rows[k];
1170 series[0]->index_to_category[dv_num] = cat;
1173 add_affixes (table, cat->name,
1174 series[0]->affixes, series[0]->n_affixes);
1178 struct pivot_axis *axis = &table->axes[axis_type];
1179 d->axis_type = axis_type;
1180 d->level = axis->n_dimensions;
1181 d->top_index = table->n_dimensions - 1;
1182 d->root = xzalloc (sizeof *d->root);
1183 *d->root = (struct pivot_category) {
1184 .name = pivot_value_new_user_text (
1185 series[0]->label ? series[0]->label : "", -1),
1187 .show_label = should_show_label (fl),
1188 .data_index = SIZE_MAX,
1189 .presentation_index = SIZE_MAX,
1191 d->data_leaves = xmemdup (cats, n_cats * sizeof *cats);
1192 d->presentation_leaves = xmemdup (cats, n_cats * sizeof *cats);
1193 d->n_leaves = d->allocated_leaves = n_cats;
1195 /* Now group them, in one pass per grouping variable, innermost first. */
1196 for (size_t j = 1; j < n; j++)
1198 struct pivot_category **new_cats = xnmalloc (n_cats, sizeof **cats);
1199 size_t n_new_cats = 0;
1201 /* Allocate a category index. */
1202 size_t max_cat = max_category (series[j]);
1203 series[j]->n_index = max_cat + 1;
1204 series[j]->index_to_category = xcalloc (
1205 max_cat + 1, sizeof *series[j]->index_to_category);
1206 for (size_t cat1 = 0; cat1 < n_cats;)
1208 /* Find a sequence of categories cat1...cat2 (exclusive), that all
1209 have the same value in series 'j'. (This might be only a single
1210 category; we will drop unnamed 1-category groups later.) */
1211 size_t row1 = cats[cat1]->presentation_index;
1212 const struct spv_data_value *dv1 = &series[j]->values[row1];
1214 for (cat2 = cat1 + 1; cat2 < n_cats; cat2++)
1216 size_t row2 = cats[cat2]->presentation_index;
1217 const struct spv_data_value *dv2 = &series[j]->values[row2];
1218 if (!spv_data_value_equal (dv1, dv2))
1221 size_t n_subs = cat2 - cat1;
1223 struct pivot_category *new_cat;
1224 const struct spv_data_value *name
1225 = spv_map_lookup (&series[j]->map, dv1);
1226 if (n_subs == 1 && name->width == 0)
1228 /* The existing category stands on its own. */
1229 new_cat = cats[cat1++];
1233 /* Create a new group with cat...cat2 as subcategories. */
1234 new_cat = xzalloc (sizeof *new_cat);
1235 *new_cat = (struct pivot_category) {
1237 .subs = xnmalloc (n_subs, sizeof *new_cat->subs),
1240 .data_index = SIZE_MAX,
1241 .presentation_index = row1,
1243 char *retval = pivot_value_from_data_value (name, NULL, NULL,
1252 for (size_t k = 0; k < n_subs; k++)
1253 new_cat->subs[k] = cats[cat1++];
1255 int dv1_num = dv1->width < 0 ? dv1->d : dv1->index;
1256 series[j]->index_to_category[dv1_num] = new_cat;
1260 add_affixes (table, new_cat->name,
1261 series[j]->affixes, series[j]->n_affixes);
1263 /* Append the new group to the list of new groups. */
1264 new_cats[n_new_cats++] = new_cat;
1269 n_cats = n_new_cats;
1272 /* Now drop unnamed 1-category groups and add parent pointers. */
1273 for (size_t j = 0; j < n_cats; j++)
1274 add_parents (cats[j], d->root, j);
1275 for (size_t j = 0; j < d->n_leaves; j++)
1277 d->data_leaves[j]->data_index = j;
1278 d->presentation_leaves[j]->presentation_index = j;
1281 d->root->subs = cats;
1282 d->root->n_subs = n_cats;
1286 pivot_dimension_destroy (d);
1290 dim_seriesp[(*n_dim_seriesp)++] = series[0];
1291 series[0]->dimension = d;
1293 axis->dimensions = xnrealloc (axis->dimensions, axis->n_dimensions + 1,
1294 sizeof *axis->dimensions);
1295 axis->dimensions[axis->n_dimensions++] = d;
1296 axis->extent *= d->n_leaves;
1302 static char * WARN_UNUSED_RESULT
1303 add_dimensions (struct hmap *series_map, const struct spvdx_nest *nest,
1304 enum pivot_axis_type axis_type,
1305 const struct spvdx_visualization *v, struct pivot_table *table,
1306 struct spv_series **dim_seriesp, size_t *n_dim_seriesp,
1309 struct pivot_axis *axis = &table->axes[axis_type];
1316 struct spv_series **series = xnmalloc (nest->n_vars, sizeof *series);
1317 for (size_t i = 0; i < nest->n_vars;)
1320 for (n = 0; i + n < nest->n_vars; n++)
1322 series[n] = spv_series_from_ref (series_map, nest->vars[i + n]->ref);
1323 if (!series[n] || !series[n]->n_values)
1329 struct pivot_dimension *d;
1330 char *error = add_dimension (series, n, axis_type, v, table,
1331 dim_seriesp, n_dim_seriesp,
1347 static char * WARN_UNUSED_RESULT
1348 add_layers (struct hmap *series_map,
1349 struct spvdx_layer **layers, size_t n_layers,
1350 const struct spvdx_visualization *v, struct pivot_table *table,
1351 struct spv_series **dim_seriesp, size_t *n_dim_seriesp,
1354 struct pivot_axis *axis = &table->axes[PIVOT_AXIS_LAYER];
1361 struct spv_series **series = xnmalloc (n_layers, sizeof *series);
1362 for (size_t i = 0; i < n_layers;)
1365 for (n = 0; i + n < n_layers; n++)
1367 series[n] = spv_series_from_ref (series_map,
1368 layers[i + n]->variable);
1369 if (!series[n] || !series[n]->n_values)
1375 struct pivot_dimension *d;
1376 char *error = add_dimension (
1377 series, n, PIVOT_AXIS_LAYER, v, table,
1378 dim_seriesp, n_dim_seriesp, level_ofs + i, &d);
1385 int index = atoi (layers[i]->value);
1386 assert (index < d->n_leaves);
1387 table->current_layer = xrealloc (
1388 table->current_layer,
1389 axis->n_dimensions * sizeof *table->current_layer);
1390 table->current_layer[axis->n_dimensions - 1] = index;
1399 static struct pivot_category *
1400 find_category (struct spv_series *series, int index)
1402 return (index >= 0 && index < series->n_index
1403 ? series->index_to_category[index]
1408 int_in_array (int value, const int *array, size_t n)
1410 for (size_t i = 0; i < n; i++)
1411 if (array[i] == value)
1418 apply_styles_to_value (struct pivot_table *table,
1419 struct pivot_value *value,
1420 const struct spvdx_set_format *sf,
1421 const struct table_area_style *base_area_style,
1422 const struct spvdx_style *fg,
1423 const struct spvdx_style *bg)
1429 free (value->footnote_indexes);
1430 value->footnote_indexes = NULL;
1431 value->n_footnotes = 0;
1434 struct fmt_spec format = { .w = 0 };
1437 format = decode_format (sf->format);
1438 add_affixes (table, value, sf->format->affix, sf->format->n_affix);
1440 else if (sf->number_format)
1442 format = decode_number_format (sf->number_format);
1443 add_affixes (table, value, sf->number_format->affix,
1444 sf->number_format->n_affix);
1446 else if (sf->n_string_format)
1448 for (size_t i = 0; i < sf->n_string_format; i++)
1449 add_affixes (table, value, sf->string_format[i]->affix,
1450 sf->string_format[i]->n_affix);
1452 else if (sf->date_time_format)
1454 format = decode_date_time_format (sf->date_time_format);
1455 add_affixes (table, value, sf->date_time_format->affix,
1456 sf->date_time_format->n_affix);
1458 else if (sf->elapsed_time_format)
1460 format = decode_elapsed_time_format (sf->elapsed_time_format);
1461 add_affixes (table, value, sf->elapsed_time_format->affix,
1462 sf->elapsed_time_format->n_affix);
1467 if (value->type == PIVOT_VALUE_NUMERIC)
1468 value->numeric.format = format;
1470 /* Possibly we should try to apply date and time formats too,
1471 but none seem to occur in practice so far. */
1476 struct table_area_style area;
1477 pivot_value_get_style (
1479 value->font_style ? value->font_style : &base_area_style->font_style,
1480 value->cell_style ? value->cell_style : &base_area_style->cell_style,
1482 decode_spvdx_style_incremental (fg, bg, &area);
1483 pivot_value_set_style (value, &area);
1484 table_area_style_uninit (&area);
1489 decode_set_cell_properties__ (struct pivot_table *table,
1490 struct hmap *series_map,
1491 const struct spvdx_intersect *intersect,
1492 const struct spvdx_style *interval,
1493 const struct spvdx_style *graph,
1494 const struct spvdx_style *labeling,
1495 const struct spvdx_style *frame,
1496 const struct spvdx_style *major_ticks,
1497 const struct spvdx_set_format *set_format)
1499 if (graph && labeling && intersect->alternating
1500 && !interval && !major_ticks && !frame && !set_format)
1502 /* Sets alt_fg_color and alt_bg_color. */
1503 struct table_area_style area;
1504 decode_spvdx_style (labeling, graph, &area);
1505 table->look->areas[PIVOT_AREA_DATA].font_style.fg[1]
1506 = area.font_style.fg[0];
1507 table->look->areas[PIVOT_AREA_DATA].font_style.bg[1]
1508 = area.font_style.bg[0];
1509 table_area_style_uninit (&area);
1512 && !labeling && !interval && !major_ticks && !frame && !set_format)
1514 /* 'graph->width' likely just sets the width of the table as a
1517 else if (!graph && !labeling && !interval && !frame && !set_format
1520 /* No-op. (Presumably there's a setMetaData we don't care about.) */
1522 else if (((set_format && spvdx_is_major_ticks (set_format->target))
1523 || major_ticks || frame)
1524 && intersect->n_where == 1)
1526 /* Formatting for individual row or column labels. */
1527 const struct spvdx_where *w = intersect->where[0];
1528 struct spv_series *s = spv_series_find (series_map, w->variable->id);
1531 const char *p = w->include;
1536 int include = strtol (p, &tail, 10);
1538 struct pivot_category *c = find_category (s, include);
1541 const struct table_area_style *base_area_style
1542 = (c->dimension->axis_type == PIVOT_AXIS_ROW
1543 ? &table->look->areas[PIVOT_AREA_ROW_LABELS]
1544 : &table->look->areas[PIVOT_AREA_COLUMN_LABELS]);
1545 apply_styles_to_value (table, c->name, set_format,
1546 base_area_style, major_ticks, frame);
1556 else if ((set_format && spvdx_is_labeling (set_format->target))
1557 || labeling || interval)
1559 /* Formatting for individual cells or groups of them with some dimensions
1561 int **indexes = XCALLOC (table->n_dimensions, int *);
1562 size_t *n = XCALLOC (table->n_dimensions, size_t);
1563 size_t *allocated = XCALLOC (table->n_dimensions, size_t);
1565 for (size_t i = 0; i < intersect->n_where; i++)
1567 const struct spvdx_where *w = intersect->where[i];
1568 struct spv_series *s = spv_series_find (series_map, w->variable->id);
1572 /* Group indexes may be included even though they are redundant.
1577 size_t j = s->dimension->top_index;
1579 const char *p = w->include;
1583 int include = strtol (p, &tail, 10);
1585 struct pivot_category *c = find_category (s, include);
1588 if (n[j] >= allocated[j])
1589 indexes[j] = x2nrealloc (indexes[j], &allocated[j],
1590 sizeof *indexes[j]);
1591 indexes[j][n[j]++] = c->data_index;
1604 for (size_t i = 0; i < table->n_dimensions; i++)
1608 printf (" %d=(", i);
1609 for (size_t j = 0; j < n[i]; j++)
1613 printf ("%d", indexes[i][j]);
1621 /* XXX This is inefficient in the common case where all of the dimensions
1622 are matched. We should use a heuristic where if all of the dimensions
1623 are matched and the product of n[*] is less than
1624 hmap_count(&table->cells) then iterate through all the possibilities
1625 rather than all the cells. Or even only do it if there is just one
1628 struct pivot_cell *cell;
1629 HMAP_FOR_EACH (cell, struct pivot_cell, hmap_node, &table->cells)
1631 for (size_t i = 0; i < table->n_dimensions; i++)
1633 if (n[i] && !int_in_array (cell->idx[i], indexes[i], n[i]))
1636 apply_styles_to_value (table, cell->value, set_format,
1637 &table->look->areas[PIVOT_AREA_DATA],
1638 labeling, interval);
1643 for (size_t i = 0; i < table->n_dimensions; i++)
1654 decode_set_cell_properties (struct pivot_table *table, struct hmap *series_map,
1655 struct spvdx_set_cell_properties **scps,
1658 for (size_t i = 0; i < n_scps; i++)
1660 const struct spvdx_set_cell_properties *scp = scps[i];
1661 const struct spvdx_style *interval = NULL;
1662 const struct spvdx_style *graph = NULL;
1663 const struct spvdx_style *labeling = NULL;
1664 const struct spvdx_style *frame = NULL;
1665 const struct spvdx_style *major_ticks = NULL;
1666 const struct spvdx_set_format *set_format = NULL;
1667 for (size_t j = 0; j < scp->n_seq; j++)
1669 const struct spvxml_node *node = scp->seq[j];
1670 if (spvdx_is_set_style (node))
1672 const struct spvdx_set_style *set_style
1673 = spvdx_cast_set_style (node);
1674 if (spvdx_is_graph (set_style->target))
1675 graph = set_style->style;
1676 else if (spvdx_is_labeling (set_style->target))
1677 labeling = set_style->style;
1678 else if (spvdx_is_interval (set_style->target))
1679 interval = set_style->style;
1680 else if (spvdx_is_major_ticks (set_style->target))
1681 major_ticks = set_style->style;
1685 else if (spvdx_is_set_frame_style (node))
1686 frame = spvdx_cast_set_frame_style (node)->style;
1687 else if (spvdx_is_set_format (node))
1688 set_format = spvdx_cast_set_format (node);
1690 assert (spvdx_is_set_meta_data (node));
1693 if (scp->union_ && scp->apply_to_converse <= 0)
1695 for (size_t j = 0; j < scp->union_->n_intersect; j++)
1696 decode_set_cell_properties__ (
1697 table, series_map, scp->union_->intersect[j],
1698 interval, graph, labeling, frame, major_ticks, set_format);
1700 else if (!scp->union_ && scp->apply_to_converse > 0)
1702 if ((set_format && spvdx_is_labeling (set_format->target))
1703 || labeling || interval)
1705 struct pivot_cell *cell;
1706 HMAP_FOR_EACH (cell, struct pivot_cell, hmap_node, &table->cells)
1707 apply_styles_to_value (table, cell->value, set_format,
1708 &table->look->areas[PIVOT_AREA_DATA],
1712 else if (!scp->union_ && scp->apply_to_converse <= 0)
1714 /* Appears to be used to set the font for something--but what? */
1721 static struct spv_series *
1722 parse_formatting (const struct spvdx_visualization *v,
1723 const struct hmap *series_map, struct hmap *format_map)
1725 const struct spvdx_labeling *labeling = v->graph->interval->labeling;
1726 struct spv_series *cell_format = NULL;
1727 for (size_t i = 0; i < labeling->n_seq; i++)
1729 const struct spvdx_formatting *f
1730 = spvdx_cast_formatting (labeling->seq[i]);
1734 cell_format = spv_series_from_ref (series_map, f->variable);
1735 for (size_t j = 0; j < f->n_format_mapping; j++)
1737 const struct spvdx_format_mapping *fm = f->format_mapping[j];
1741 struct format_mapping *out = xmalloc (sizeof *out);
1742 out->from = fm->from;
1743 out->to = decode_format (fm->format);
1744 hmap_insert (format_map, &out->hmap_node,
1745 hash_int (out->from, 0));
1754 format_map_destroy (struct hmap *format_map)
1756 struct format_mapping *fm, *next;
1757 HMAP_FOR_EACH_SAFE (fm, next, struct format_mapping, hmap_node, format_map)
1759 hmap_delete (format_map, &fm->hmap_node);
1762 hmap_destroy (format_map);
1765 char * WARN_UNUSED_RESULT
1766 decode_spvdx_table (const struct spvdx_visualization *v, const char *subtype,
1767 const struct pivot_table_look *look,
1768 struct spv_data *data, struct pivot_table **outp)
1770 struct pivot_table *table = pivot_table_create__ (NULL, subtype);
1772 pivot_table_set_look (table, look);
1773 table->look = pivot_table_look_unshare (table->look);
1775 struct hmap series_map = HMAP_INITIALIZER (series_map);
1776 struct hmap format_map = HMAP_INITIALIZER (format_map);
1777 struct spv_series **dim_series = NULL;
1780 struct spvdx_visualization_extension *ve = v->visualization_extension;
1781 table->show_grid_lines = ve && ve->show_gridline;
1783 /* Sizing from the legacy properties can get overridden. */
1784 if (v->graph->cell_style->width)
1786 int min_width, max_width, n = 0;
1787 if (sscanf (v->graph->cell_style->width, "%*d%%;%dpt;%dpt%n",
1788 &min_width, &max_width, &n)
1789 && v->graph->cell_style->width[n] == '\0')
1791 table->look->width_ranges[TABLE_HORZ][0] = min_width;
1792 table->look->width_ranges[TABLE_HORZ][1] = max_width;
1798 Any pivot_value might refer to footnotes, so it's important to process the
1799 footnotes early to ensure that those references can be resolved. There is
1800 a possible problem that a footnote might itself reference an
1801 as-yet-unprocessed footnote, but that's OK because footnote references
1802 don't actually look at the footnote contents but only resolve a pointer to
1803 where the footnote will go later.
1805 Before we really start, create all the footnotes we'll fill in. This is
1806 because sometimes footnotes refer to themselves or to each other and we
1807 don't want to reject those references. */
1809 for (size_t i = 0; i < v->container->n_label_frame; i++)
1811 const struct spvdx_label_frame *lf = v->container->label_frame[i];
1813 && lf->label->purpose == SPVDX_PURPOSE_FOOTNOTE
1814 && lf->label->n_text > 0
1815 && lf->label->text[0]->uses_reference > 0)
1817 pivot_table_create_footnote__ (
1818 table, lf->label->text[0]->uses_reference - 1,
1823 if (v->graph->interval->footnotes)
1824 decode_footnotes (table, v->graph->interval->footnotes);
1826 struct spv_series *footnotes = NULL;
1827 for (size_t i = 0; i < v->graph->interval->labeling->n_seq; i++)
1829 const struct spvxml_node *node = v->graph->interval->labeling->seq[i];
1830 if (spvdx_is_footnotes (node))
1832 const struct spvdx_footnotes *f = spvdx_cast_footnotes (node);
1833 footnotes = spv_series_from_ref (&series_map, f->variable);
1834 decode_footnotes (table, f);
1837 for (size_t i = 0; i < v->n_lf1; i++)
1839 error = decode_label_frame (table, v->lf1[i]);
1843 for (size_t i = 0; i < v->n_lf2; i++)
1845 error = decode_label_frame (table, v->lf2[i]);
1850 for (size_t i = 0; i < v->container->n_label_frame; i++)
1852 error = decode_label_frame (table, v->container->label_frame[i]);
1856 if (v->graph->interval->labeling->style)
1858 table_area_style_uninit (&table->look->areas[PIVOT_AREA_DATA]);
1859 decode_spvdx_style (v->graph->interval->labeling->style,
1860 v->graph->cell_style,
1861 &table->look->areas[PIVOT_AREA_DATA]);
1864 /* Decode all of the sourceVariable and derivedVariable */
1865 struct spvxml_node **nodes = xmemdup (v->seq, v->n_seq * sizeof *v->seq);
1866 size_t n_nodes = v->n_seq;
1869 bool progress = false;
1870 for (size_t i = 0; i < n_nodes;)
1872 error = (spvdx_is_source_variable (nodes[i])
1873 ? decode_spvdx_source_variable (nodes[i], data, &series_map)
1874 : decode_spvdx_derived_variable (nodes[i], &series_map));
1877 nodes[i] = nodes[--n_nodes];
1880 else if (error == &BAD_REFERENCE)
1892 error = xasprintf ("Table has %zu variables with circular or "
1893 "unresolved references, including variable %s.",
1894 n_nodes, nodes[0]->id);
1900 const struct spvdx_cross *cross = v->graph->faceting->cross;
1902 assert (cross->n_seq == 1);
1903 const struct spvdx_nest *columns = spvdx_cast_nest (cross->seq[0]);
1904 size_t max_columns = columns ? columns->n_vars : 0;
1906 assert (cross->n_seq2 == 1);
1907 const struct spvdx_nest *rows = spvdx_cast_nest (cross->seq2[0]);
1908 size_t max_rows = rows ? rows->n_vars : 0;
1910 size_t max_layers = (v->graph->faceting->n_layers1
1911 + v->graph->faceting->n_layers2);
1913 size_t max_dims = max_columns + max_rows + max_layers;
1914 table->dimensions = xnmalloc (max_dims, sizeof *table->dimensions);
1915 dim_series = xnmalloc (max_dims, sizeof *dim_series);
1916 size_t n_dim_series = 0;
1918 error = add_dimensions (&series_map, columns, PIVOT_AXIS_COLUMN, v, table,
1919 dim_series, &n_dim_series, 1);
1923 error = add_dimensions (&series_map, rows, PIVOT_AXIS_ROW, v, table,
1924 dim_series, &n_dim_series, max_columns + 1);
1928 error = add_layers (&series_map, v->graph->faceting->layers1,
1929 v->graph->faceting->n_layers1,
1930 v, table, dim_series, &n_dim_series,
1931 max_rows + max_columns + 1);
1935 error = add_layers (&series_map, v->graph->faceting->layers2,
1936 v->graph->faceting->n_layers2,
1937 v, table, dim_series, &n_dim_series,
1938 (max_rows + max_columns + v->graph->faceting->n_layers1
1943 struct spv_series *cell = spv_series_find (&series_map, "cell");
1946 error = xstrdup (_("Table lacks cell data."));
1950 struct spv_series *cell_format = parse_formatting (v, &series_map,
1953 assert (table->n_dimensions == n_dim_series);
1954 size_t *dim_indexes = xnmalloc (table->n_dimensions, sizeof *dim_indexes);
1955 for (size_t i = 0; i < cell->n_values; i++)
1957 for (size_t j = 0; j < table->n_dimensions; j++)
1959 const struct spv_data_value *value = &dim_series[j]->values[i];
1960 const struct pivot_category *cat = find_category (
1961 dim_series[j], value->width < 0 ? value->d : value->index);
1964 dim_indexes[j] = cat->data_index;
1967 struct pivot_value *value;
1968 error = pivot_value_from_data_value (
1969 &cell->values[i], cell_format ? &cell_format->values[i] : NULL,
1970 &format_map, &value);
1976 const struct spv_data_value *d = &footnotes->values[i];
1979 const char *p = d->s;
1983 int idx = strtol (p, &tail, 10);
1984 add_footnote (value, idx, table);
1994 if (value->type == PIVOT_VALUE_NUMERIC
1995 && value->numeric.x == SYSMIS
1996 && !value->n_footnotes)
1998 /* Apparently, system-missing values are just empty cells? */
1999 pivot_value_destroy (value);
2002 pivot_table_put (table, dim_indexes, table->n_dimensions, value);
2007 decode_set_cell_properties (table, &series_map, v->graph->facet_layout->scp1,
2008 v->graph->facet_layout->n_scp1);
2009 decode_set_cell_properties (table, &series_map, v->graph->facet_layout->scp2,
2010 v->graph->facet_layout->n_scp2);
2012 pivot_table_assign_label_depth (table);
2014 format_map_destroy (&format_map);
2018 spv_series_destroy (&series_map);
2021 pivot_table_unref (table);