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);
1276 d->root->subs = cats;
1277 d->root->n_subs = n_cats;
1281 pivot_dimension_destroy (d);
1285 dim_seriesp[(*n_dim_seriesp)++] = series[0];
1286 series[0]->dimension = d;
1288 axis->dimensions = xnrealloc (axis->dimensions, axis->n_dimensions + 1,
1289 sizeof *axis->dimensions);
1290 axis->dimensions[axis->n_dimensions++] = d;
1291 axis->extent *= d->n_leaves;
1297 static char * WARN_UNUSED_RESULT
1298 add_dimensions (struct hmap *series_map, const struct spvdx_nest *nest,
1299 enum pivot_axis_type axis_type,
1300 const struct spvdx_visualization *v, struct pivot_table *table,
1301 struct spv_series **dim_seriesp, size_t *n_dim_seriesp,
1304 struct pivot_axis *axis = &table->axes[axis_type];
1311 struct spv_series **series = xnmalloc (nest->n_vars, sizeof *series);
1312 for (size_t i = 0; i < nest->n_vars;)
1315 for (n = 0; i + n < nest->n_vars; n++)
1317 series[n] = spv_series_from_ref (series_map, nest->vars[i + n]->ref);
1318 if (!series[n] || !series[n]->n_values)
1324 struct pivot_dimension *d;
1325 char *error = add_dimension (series, n, axis_type, v, table,
1326 dim_seriesp, n_dim_seriesp,
1342 static char * WARN_UNUSED_RESULT
1343 add_layers (struct hmap *series_map,
1344 struct spvdx_layer **layers, size_t n_layers,
1345 const struct spvdx_visualization *v, struct pivot_table *table,
1346 struct spv_series **dim_seriesp, size_t *n_dim_seriesp,
1349 struct pivot_axis *axis = &table->axes[PIVOT_AXIS_LAYER];
1356 struct spv_series **series = xnmalloc (n_layers, sizeof *series);
1357 for (size_t i = 0; i < n_layers;)
1360 for (n = 0; i + n < n_layers; n++)
1362 series[n] = spv_series_from_ref (series_map,
1363 layers[i + n]->variable);
1364 if (!series[n] || !series[n]->n_values)
1370 struct pivot_dimension *d;
1371 char *error = add_dimension (
1372 series, n, PIVOT_AXIS_LAYER, v, table,
1373 dim_seriesp, n_dim_seriesp, level_ofs + i, &d);
1380 int index = atoi (layers[i]->value);
1381 assert (index < d->n_leaves);
1382 table->current_layer = xrealloc (
1383 table->current_layer,
1384 axis->n_dimensions * sizeof *table->current_layer);
1385 table->current_layer[axis->n_dimensions - 1] = index;
1394 static struct pivot_category *
1395 find_category (struct spv_series *series, int index)
1397 return (index >= 0 && index < series->n_index
1398 ? series->index_to_category[index]
1403 int_in_array (int value, const int *array, size_t n)
1405 for (size_t i = 0; i < n; i++)
1406 if (array[i] == value)
1413 apply_styles_to_value (struct pivot_table *table,
1414 struct pivot_value *value,
1415 const struct spvdx_set_format *sf,
1416 const struct table_area_style *base_area_style,
1417 const struct spvdx_style *fg,
1418 const struct spvdx_style *bg)
1424 free (value->footnote_indexes);
1425 value->footnote_indexes = NULL;
1426 value->n_footnotes = 0;
1429 struct fmt_spec format = { .w = 0 };
1432 format = decode_format (sf->format);
1433 add_affixes (table, value, sf->format->affix, sf->format->n_affix);
1435 else if (sf->number_format)
1437 format = decode_number_format (sf->number_format);
1438 add_affixes (table, value, sf->number_format->affix,
1439 sf->number_format->n_affix);
1441 else if (sf->n_string_format)
1443 for (size_t i = 0; i < sf->n_string_format; i++)
1444 add_affixes (table, value, sf->string_format[i]->affix,
1445 sf->string_format[i]->n_affix);
1447 else if (sf->date_time_format)
1449 format = decode_date_time_format (sf->date_time_format);
1450 add_affixes (table, value, sf->date_time_format->affix,
1451 sf->date_time_format->n_affix);
1453 else if (sf->elapsed_time_format)
1455 format = decode_elapsed_time_format (sf->elapsed_time_format);
1456 add_affixes (table, value, sf->elapsed_time_format->affix,
1457 sf->elapsed_time_format->n_affix);
1462 if (value->type == PIVOT_VALUE_NUMERIC)
1463 value->numeric.format = format;
1465 /* Possibly we should try to apply date and time formats too,
1466 but none seem to occur in practice so far. */
1471 struct table_area_style area;
1472 pivot_value_get_style (
1474 value->font_style ? value->font_style : &base_area_style->font_style,
1475 value->cell_style ? value->cell_style : &base_area_style->cell_style,
1477 decode_spvdx_style_incremental (fg, bg, &area);
1478 pivot_value_set_style (value, &area);
1479 table_area_style_uninit (&area);
1484 decode_set_cell_properties__ (struct pivot_table *table,
1485 struct hmap *series_map,
1486 const struct spvdx_intersect *intersect,
1487 const struct spvdx_style *interval,
1488 const struct spvdx_style *graph,
1489 const struct spvdx_style *labeling,
1490 const struct spvdx_style *frame,
1491 const struct spvdx_style *major_ticks,
1492 const struct spvdx_set_format *set_format)
1494 if (graph && labeling && intersect->alternating
1495 && !interval && !major_ticks && !frame && !set_format)
1497 /* Sets alt_fg_color and alt_bg_color. */
1498 struct table_area_style area;
1499 decode_spvdx_style (labeling, graph, &area);
1500 table->look->areas[PIVOT_AREA_DATA].font_style.fg[1]
1501 = area.font_style.fg[0];
1502 table->look->areas[PIVOT_AREA_DATA].font_style.bg[1]
1503 = area.font_style.bg[0];
1504 table_area_style_uninit (&area);
1507 && !labeling && !interval && !major_ticks && !frame && !set_format)
1509 /* 'graph->width' likely just sets the width of the table as a
1512 else if (!graph && !labeling && !interval && !frame && !set_format
1515 /* No-op. (Presumably there's a setMetaData we don't care about.) */
1517 else if (((set_format && spvdx_is_major_ticks (set_format->target))
1518 || major_ticks || frame)
1519 && intersect->n_where == 1)
1521 /* Formatting for individual row or column labels. */
1522 const struct spvdx_where *w = intersect->where[0];
1523 struct spv_series *s = spv_series_find (series_map, w->variable->id);
1526 const char *p = w->include;
1531 int include = strtol (p, &tail, 10);
1533 struct pivot_category *c = find_category (s, include);
1536 const struct table_area_style *base_area_style
1537 = (c->dimension->axis_type == PIVOT_AXIS_ROW
1538 ? &table->look->areas[PIVOT_AREA_ROW_LABELS]
1539 : &table->look->areas[PIVOT_AREA_COLUMN_LABELS]);
1540 apply_styles_to_value (table, c->name, set_format,
1541 base_area_style, major_ticks, frame);
1551 else if ((set_format && spvdx_is_labeling (set_format->target))
1552 || labeling || interval)
1554 /* Formatting for individual cells or groups of them with some dimensions
1556 int **indexes = XCALLOC (table->n_dimensions, int *);
1557 size_t *n = XCALLOC (table->n_dimensions, size_t);
1558 size_t *allocated = XCALLOC (table->n_dimensions, size_t);
1560 for (size_t i = 0; i < intersect->n_where; i++)
1562 const struct spvdx_where *w = intersect->where[i];
1563 struct spv_series *s = spv_series_find (series_map, w->variable->id);
1567 /* Group indexes may be included even though they are redundant.
1572 size_t j = s->dimension->top_index;
1574 const char *p = w->include;
1578 int include = strtol (p, &tail, 10);
1580 struct pivot_category *c = find_category (s, include);
1583 if (n[j] >= allocated[j])
1584 indexes[j] = x2nrealloc (indexes[j], &allocated[j],
1585 sizeof *indexes[j]);
1586 indexes[j][n[j]++] = c->data_index;
1599 for (size_t i = 0; i < table->n_dimensions; i++)
1603 printf (" %d=(", i);
1604 for (size_t j = 0; j < n[i]; j++)
1608 printf ("%d", indexes[i][j]);
1616 /* XXX This is inefficient in the common case where all of the dimensions
1617 are matched. We should use a heuristic where if all of the dimensions
1618 are matched and the product of n[*] is less than
1619 hmap_count(&table->cells) then iterate through all the possibilities
1620 rather than all the cells. Or even only do it if there is just one
1623 struct pivot_cell *cell;
1624 HMAP_FOR_EACH (cell, struct pivot_cell, hmap_node, &table->cells)
1626 for (size_t i = 0; i < table->n_dimensions; i++)
1628 if (n[i] && !int_in_array (cell->idx[i], indexes[i], n[i]))
1631 apply_styles_to_value (table, cell->value, set_format,
1632 &table->look->areas[PIVOT_AREA_DATA],
1633 labeling, interval);
1638 for (size_t i = 0; i < table->n_dimensions; i++)
1649 decode_set_cell_properties (struct pivot_table *table, struct hmap *series_map,
1650 struct spvdx_set_cell_properties **scps,
1653 for (size_t i = 0; i < n_scps; i++)
1655 const struct spvdx_set_cell_properties *scp = scps[i];
1656 const struct spvdx_style *interval = NULL;
1657 const struct spvdx_style *graph = NULL;
1658 const struct spvdx_style *labeling = NULL;
1659 const struct spvdx_style *frame = NULL;
1660 const struct spvdx_style *major_ticks = NULL;
1661 const struct spvdx_set_format *set_format = NULL;
1662 for (size_t j = 0; j < scp->n_seq; j++)
1664 const struct spvxml_node *node = scp->seq[j];
1665 if (spvdx_is_set_style (node))
1667 const struct spvdx_set_style *set_style
1668 = spvdx_cast_set_style (node);
1669 if (spvdx_is_graph (set_style->target))
1670 graph = set_style->style;
1671 else if (spvdx_is_labeling (set_style->target))
1672 labeling = set_style->style;
1673 else if (spvdx_is_interval (set_style->target))
1674 interval = set_style->style;
1675 else if (spvdx_is_major_ticks (set_style->target))
1676 major_ticks = set_style->style;
1680 else if (spvdx_is_set_frame_style (node))
1681 frame = spvdx_cast_set_frame_style (node)->style;
1682 else if (spvdx_is_set_format (node))
1683 set_format = spvdx_cast_set_format (node);
1685 assert (spvdx_is_set_meta_data (node));
1688 if (scp->union_ && scp->apply_to_converse <= 0)
1690 for (size_t j = 0; j < scp->union_->n_intersect; j++)
1691 decode_set_cell_properties__ (
1692 table, series_map, scp->union_->intersect[j],
1693 interval, graph, labeling, frame, major_ticks, set_format);
1695 else if (!scp->union_ && scp->apply_to_converse > 0)
1697 if ((set_format && spvdx_is_labeling (set_format->target))
1698 || labeling || interval)
1700 struct pivot_cell *cell;
1701 HMAP_FOR_EACH (cell, struct pivot_cell, hmap_node, &table->cells)
1702 apply_styles_to_value (table, cell->value, set_format,
1703 &table->look->areas[PIVOT_AREA_DATA],
1707 else if (!scp->union_ && scp->apply_to_converse <= 0)
1709 /* Appears to be used to set the font for something--but what? */
1716 static struct spv_series *
1717 parse_formatting (const struct spvdx_visualization *v,
1718 const struct hmap *series_map, struct hmap *format_map)
1720 const struct spvdx_labeling *labeling = v->graph->interval->labeling;
1721 struct spv_series *cell_format = NULL;
1722 for (size_t i = 0; i < labeling->n_seq; i++)
1724 const struct spvdx_formatting *f
1725 = spvdx_cast_formatting (labeling->seq[i]);
1729 cell_format = spv_series_from_ref (series_map, f->variable);
1730 for (size_t j = 0; j < f->n_format_mapping; j++)
1732 const struct spvdx_format_mapping *fm = f->format_mapping[j];
1736 struct format_mapping *out = xmalloc (sizeof *out);
1737 out->from = fm->from;
1738 out->to = decode_format (fm->format);
1739 hmap_insert (format_map, &out->hmap_node,
1740 hash_int (out->from, 0));
1749 format_map_destroy (struct hmap *format_map)
1751 struct format_mapping *fm, *next;
1752 HMAP_FOR_EACH_SAFE (fm, next, struct format_mapping, hmap_node, format_map)
1754 hmap_delete (format_map, &fm->hmap_node);
1757 hmap_destroy (format_map);
1760 char * WARN_UNUSED_RESULT
1761 decode_spvdx_table (const struct spvdx_visualization *v, const char *subtype,
1762 const struct pivot_table_look *look,
1763 struct spv_data *data, struct pivot_table **outp)
1765 struct pivot_table *table = pivot_table_create__ (NULL, subtype);
1767 pivot_table_set_look (table, look);
1768 table->look = pivot_table_look_unshare (table->look);
1770 struct hmap series_map = HMAP_INITIALIZER (series_map);
1771 struct hmap format_map = HMAP_INITIALIZER (format_map);
1772 struct spv_series **dim_series = NULL;
1775 struct spvdx_visualization_extension *ve = v->visualization_extension;
1776 table->show_grid_lines = ve && ve->show_gridline;
1778 /* Sizing from the legacy properties can get overridden. */
1779 if (v->graph->cell_style->width)
1781 int min_width, max_width, n = 0;
1782 if (sscanf (v->graph->cell_style->width, "%*d%%;%dpt;%dpt%n",
1783 &min_width, &max_width, &n)
1784 && v->graph->cell_style->width[n] == '\0')
1786 table->look->width_ranges[TABLE_HORZ][0] = min_width;
1787 table->look->width_ranges[TABLE_HORZ][1] = max_width;
1793 Any pivot_value might refer to footnotes, so it's important to process the
1794 footnotes early to ensure that those references can be resolved. There is
1795 a possible problem that a footnote might itself reference an
1796 as-yet-unprocessed footnote, but that's OK because footnote references
1797 don't actually look at the footnote contents but only resolve a pointer to
1798 where the footnote will go later.
1800 Before we really start, create all the footnotes we'll fill in. This is
1801 because sometimes footnotes refer to themselves or to each other and we
1802 don't want to reject those references. */
1804 for (size_t i = 0; i < v->container->n_label_frame; i++)
1806 const struct spvdx_label_frame *lf = v->container->label_frame[i];
1808 && lf->label->purpose == SPVDX_PURPOSE_FOOTNOTE
1809 && lf->label->n_text > 0
1810 && lf->label->text[0]->uses_reference > 0)
1812 pivot_table_create_footnote__ (
1813 table, lf->label->text[0]->uses_reference - 1,
1818 if (v->graph->interval->footnotes)
1819 decode_footnotes (table, v->graph->interval->footnotes);
1821 struct spv_series *footnotes = NULL;
1822 for (size_t i = 0; i < v->graph->interval->labeling->n_seq; i++)
1824 const struct spvxml_node *node = v->graph->interval->labeling->seq[i];
1825 if (spvdx_is_footnotes (node))
1827 const struct spvdx_footnotes *f = spvdx_cast_footnotes (node);
1828 footnotes = spv_series_from_ref (&series_map, f->variable);
1829 decode_footnotes (table, f);
1832 for (size_t i = 0; i < v->n_lf1; i++)
1834 error = decode_label_frame (table, v->lf1[i]);
1838 for (size_t i = 0; i < v->n_lf2; i++)
1840 error = decode_label_frame (table, v->lf2[i]);
1845 for (size_t i = 0; i < v->container->n_label_frame; i++)
1847 error = decode_label_frame (table, v->container->label_frame[i]);
1851 if (v->graph->interval->labeling->style)
1853 table_area_style_uninit (&table->look->areas[PIVOT_AREA_DATA]);
1854 decode_spvdx_style (v->graph->interval->labeling->style,
1855 v->graph->cell_style,
1856 &table->look->areas[PIVOT_AREA_DATA]);
1859 /* Decode all of the sourceVariable and derivedVariable */
1860 struct spvxml_node **nodes = xmemdup (v->seq, v->n_seq * sizeof *v->seq);
1861 size_t n_nodes = v->n_seq;
1864 bool progress = false;
1865 for (size_t i = 0; i < n_nodes;)
1867 error = (spvdx_is_source_variable (nodes[i])
1868 ? decode_spvdx_source_variable (nodes[i], data, &series_map)
1869 : decode_spvdx_derived_variable (nodes[i], &series_map));
1872 nodes[i] = nodes[--n_nodes];
1875 else if (error == &BAD_REFERENCE)
1887 error = xasprintf ("Table has %zu variables with circular or "
1888 "unresolved references, including variable %s.",
1889 n_nodes, nodes[0]->id);
1895 const struct spvdx_cross *cross = v->graph->faceting->cross;
1897 assert (cross->n_seq == 1);
1898 const struct spvdx_nest *columns = spvdx_cast_nest (cross->seq[0]);
1899 size_t max_columns = columns ? columns->n_vars : 0;
1901 assert (cross->n_seq2 == 1);
1902 const struct spvdx_nest *rows = spvdx_cast_nest (cross->seq2[0]);
1903 size_t max_rows = rows ? rows->n_vars : 0;
1905 size_t max_layers = (v->graph->faceting->n_layers1
1906 + v->graph->faceting->n_layers2);
1908 size_t max_dims = max_columns + max_rows + max_layers;
1909 table->dimensions = xnmalloc (max_dims, sizeof *table->dimensions);
1910 dim_series = xnmalloc (max_dims, sizeof *dim_series);
1911 size_t n_dim_series = 0;
1913 error = add_dimensions (&series_map, columns, PIVOT_AXIS_COLUMN, v, table,
1914 dim_series, &n_dim_series, 1);
1918 error = add_dimensions (&series_map, rows, PIVOT_AXIS_ROW, v, table,
1919 dim_series, &n_dim_series, max_columns + 1);
1923 error = add_layers (&series_map, v->graph->faceting->layers1,
1924 v->graph->faceting->n_layers1,
1925 v, table, dim_series, &n_dim_series,
1926 max_rows + max_columns + 1);
1930 error = add_layers (&series_map, v->graph->faceting->layers2,
1931 v->graph->faceting->n_layers2,
1932 v, table, dim_series, &n_dim_series,
1933 (max_rows + max_columns + v->graph->faceting->n_layers1
1938 struct spv_series *cell = spv_series_find (&series_map, "cell");
1941 error = xstrdup (_("Table lacks cell data."));
1945 struct spv_series *cell_format = parse_formatting (v, &series_map,
1948 assert (table->n_dimensions == n_dim_series);
1949 size_t *dim_indexes = xnmalloc (table->n_dimensions, sizeof *dim_indexes);
1950 for (size_t i = 0; i < cell->n_values; i++)
1952 for (size_t j = 0; j < table->n_dimensions; j++)
1954 const struct spv_data_value *value = &dim_series[j]->values[i];
1955 const struct pivot_category *cat = find_category (
1956 dim_series[j], value->width < 0 ? value->d : value->index);
1959 dim_indexes[j] = cat->data_index;
1962 struct pivot_value *value;
1963 error = pivot_value_from_data_value (
1964 &cell->values[i], cell_format ? &cell_format->values[i] : NULL,
1965 &format_map, &value);
1971 const struct spv_data_value *d = &footnotes->values[i];
1974 const char *p = d->s;
1978 int idx = strtol (p, &tail, 10);
1979 add_footnote (value, idx, table);
1989 if (value->type == PIVOT_VALUE_NUMERIC
1990 && value->numeric.x == SYSMIS
1991 && !value->n_footnotes)
1993 /* Apparently, system-missing values are just empty cells? */
1994 pivot_value_destroy (value);
1997 pivot_table_put (table, dim_indexes, table->n_dimensions, value);
2002 decode_set_cell_properties (table, &series_map, v->graph->facet_layout->scp1,
2003 v->graph->facet_layout->n_scp1);
2004 decode_set_cell_properties (table, &series_map, v->graph->facet_layout->scp2,
2005 v->graph->facet_layout->n_scp2);
2007 pivot_table_assign_label_depth (table);
2009 format_map_destroy (&format_map);
2013 spv_series_destroy (&series_map);
2016 pivot_table_unref (table);