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) { .type = FMT_A, .w = 40 });
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 = type, .w = 40, .d = 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 (struct pivot_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 if (!value->text.local)
724 value->text.local = xstrdup ("");
725 value->text.c = value->text.id = value->text.local;
726 pivot_value_destroy (*target);
730 for (size_t i = 0; i < lf->label->n_text; i++)
732 const struct spvdx_text *in = lf->label->text[i];
733 if (in->uses_reference == INT_MIN)
737 size_t length = strlen (in->text);
738 if (length && in->text[length - 1] == '\n')
741 pivot_table_create_footnote__ (
742 table, in->uses_reference - 1, NULL,
743 pivot_value_new_user_text (in->text, length));
747 size_t length = strlen (in->text);
748 if (length && in->text[length - 1] == '.')
751 pivot_table_create_footnote__ (
752 table, in->uses_reference - 1,
753 pivot_value_new_user_text (in->text, length), NULL);
759 /* Special return value for decode_spvdx_variable(). */
760 static char BAD_REFERENCE;
762 static char * WARN_UNUSED_RESULT
763 decode_spvdx_source_variable (const struct spvxml_node *node,
764 const struct spv_data *data,
765 struct hmap *series_map)
767 const struct spvdx_source_variable *sv = spvdx_cast_source_variable (node);
769 struct spv_series *label_series = NULL;
770 if (sv->label_variable)
772 label_series = spv_series_find (series_map,
773 sv->label_variable->node_.id);
775 return &BAD_REFERENCE;
777 label_series->is_label_series = true;
780 const struct spv_data_variable *var = spv_data_find_variable (
781 data, sv->source, sv->source_name);
783 return xasprintf ("sourceVariable %s references nonexistent "
784 "source %s variable %s.",
785 sv->node_.id, sv->source, sv->source_name);
787 struct spv_series *s = XZALLOC (struct spv_series);
788 s->name = xstrdup (node->id);
790 s->label = xstrdup_if_nonnull (sv->label);
791 s->label_series = label_series;
792 s->values = spv_data_values_clone (var->values, var->n_values);
793 s->n_values = var->n_values;
796 hmap_insert (series_map, &s->hmap_node, hash_string (s->name, 0));
798 char *error = spv_series_remap_formats (s, sv->seq, sv->n_seq);
802 if (label_series && !s->remapped)
804 for (size_t i = 0; i < s->n_values; i++)
805 if (s->values[i].width < 0)
808 if (label_series->values[i].width < 0)
810 union value v = { .f = label_series->values[i].d };
811 dest = data_out_stretchy (&v, "UTF-8", &s->format,
812 settings_get_fmt_settings (), NULL);
815 dest = label_series->values[i].s;
816 char *error = spv_map_insert (&s->map, s->values[i].d,
818 free (error); /* Duplicates are OK. */
819 if (label_series->values[i].width < 0)
827 static char * WARN_UNUSED_RESULT
828 decode_spvdx_derived_variable (const struct spvxml_node *node,
829 struct hmap *series_map)
831 const struct spvdx_derived_variable *dv = spvdx_cast_derived_variable (node);
833 struct spv_data_value *values;
836 struct substring value = ss_cstr (dv->value);
837 if (ss_equals (value, ss_cstr ("constant(0)")))
839 struct spv_series *existing_series = spv_series_first (series_map);
840 if (!existing_series)
841 return &BAD_REFERENCE;
843 n_values = existing_series->n_values;
844 values = XCALLOC (n_values, struct spv_data_value);
845 for (size_t i = 0; i < n_values; i++)
846 values[i].width = -1;
848 else if (ss_starts_with (value, ss_cstr ("constant(")))
853 else if (ss_starts_with (value, ss_cstr ("map("))
854 && ss_ends_with (value, ss_cstr (")")))
856 char *dependency_name = ss_xstrdup (ss_substr (value, 4,
858 struct spv_series *dependency
859 = spv_series_find (series_map, dependency_name);
860 free (dependency_name);
862 return &BAD_REFERENCE;
864 values = spv_data_values_clone (dependency->values,
865 dependency->n_values);
866 n_values = dependency->n_values;
869 return xasprintf ("Derived variable %s has unknown value \"%s\"",
870 node->id, dv->value);
872 struct spv_series *s = XZALLOC (struct spv_series);
874 s->name = xstrdup (node->id);
876 s->n_values = n_values;
878 hmap_insert (series_map, &s->hmap_node, hash_string (s->name, 0));
880 char *error = spv_series_remap_vmes (s, dv->value_map_entry,
881 dv->n_value_map_entry);
885 error = spv_series_remap_formats (s, dv->seq, dv->n_seq);
891 for (size_t i = 0; i < n_values; i++)
892 if (values[i].width != 0)
894 for (size_t i = 0; i < n_values; i++)
895 spv_data_value_uninit (&s->values[i]);
906 struct format_mapping
908 struct hmap_node hmap_node;
913 static const struct format_mapping *
914 format_map_find (const struct hmap *format_map, uint32_t u32_format)
918 const struct format_mapping *fm;
919 HMAP_FOR_EACH_IN_BUCKET (fm, struct format_mapping, hmap_node,
920 hash_int (u32_format, 0), format_map)
921 if (fm->from == u32_format)
928 static char * WARN_UNUSED_RESULT
929 spv_format_from_data_value (const struct spv_data_value *data,
930 const struct hmap *format_map,
931 struct fmt_spec *out)
935 *out = fmt_for_output (FMT_F, 40, 2);
939 uint32_t u32_format = data->width < 0 ? data->d : atoi (data->s);
940 const struct format_mapping *fm = format_map_find (format_map, u32_format);
946 return spv_decode_fmt_spec (u32_format, out);
949 static char * WARN_UNUSED_RESULT
950 pivot_value_from_data_value (const struct spv_data_value *data,
951 const struct spv_data_value *format,
952 const struct hmap *format_map,
953 struct pivot_value **vp)
958 char *error = spv_format_from_data_value (format, format_map, &f);
962 struct pivot_value *v = XZALLOC (struct pivot_value);
963 if (data->width >= 0)
965 if (format && fmt_get_category (f.type) == FMT_CAT_DATE)
967 int year, month, day, hour, minute, second, msec, len = -1;
968 if (sscanf (data->s, "%4d-%2d-%2dT%2d:%2d:%2d.%3d%n",
969 &year, &month, &day, &hour, &minute, &second,
972 && data->s[len] == '\0')
974 double date = calendar_gregorian_to_offset (
975 year, month, day, settings_get_fmt_settings (), NULL);
978 v->type = PIVOT_VALUE_NUMERIC;
979 v->numeric.x = (date * 60. * 60. * 24.
984 v->numeric.format = f;
990 else if (format && fmt_get_category (f.type) == FMT_CAT_TIME)
992 int hour, minute, second, msec, len = -1;
993 if (sscanf (data->s, "%d:%2d:%2d.%3d%n",
994 &hour, &minute, &second, &msec, &len) == 4
996 && data->s[len] == '\0')
998 v->type = PIVOT_VALUE_NUMERIC;
999 v->numeric.x = (hour * 60. * 60.
1003 v->numeric.format = f;
1008 v->type = PIVOT_VALUE_STRING;
1009 v->string.s = xstrdup (data->s);
1013 v->type = PIVOT_VALUE_NUMERIC;
1014 v->numeric.x = data->d;
1015 v->numeric.format = f;
1022 add_parents (struct pivot_category *cat, struct pivot_category *parent,
1025 cat->parent = parent;
1026 cat->group_index = group_index;
1027 if (pivot_category_is_group (cat))
1028 for (size_t i = 0; i < cat->n_subs; i++)
1029 add_parents (cat->subs[i], cat, i);
1032 static const struct spvdx_facet_level *
1033 find_facet_level (const struct spvdx_visualization *v, int facet_level)
1035 const struct spvdx_facet_layout *layout = v->graph->facet_layout;
1036 for (size_t i = 0; i < layout->n_facet_level; i++)
1038 const struct spvdx_facet_level *fl = layout->facet_level[i];
1039 if (facet_level == fl->level)
1046 should_show_label (const struct spvdx_facet_level *fl)
1048 return fl && fl->axis->label && fl->axis->label->style->visible != 0;
1052 max_category (const struct spv_series *s)
1054 double max_cat = -DBL_MAX;
1055 for (size_t i = 0; i < s->n_values; i++)
1057 const struct spv_data_value *dv = &s->values[i];
1058 double d = dv->width < 0 ? dv->d : dv->index;
1062 assert (max_cat >= 0 && max_cat < SIZE_MAX - 1);
1068 add_affixes (struct pivot_table *table, struct pivot_value *value,
1069 struct spvdx_affix **affixes, size_t n_affixes)
1071 for (size_t i = 0; i < n_affixes; i++)
1072 add_footnote (value, affixes[i]->defines_reference, table);
1075 static char * WARN_UNUSED_RESULT
1076 add_dimension (struct spv_series **series, size_t n,
1077 enum pivot_axis_type axis_type,
1078 const struct spvdx_visualization *v, struct pivot_table *table,
1079 struct spv_series **dim_seriesp, size_t *n_dim_seriesp,
1080 int base_facet_level, struct pivot_dimension **dp)
1084 const struct spvdx_facet_level *fl
1085 = find_facet_level (v, base_facet_level + n);
1088 struct table_area_style *area
1089 = (axis_type == PIVOT_AXIS_COLUMN
1090 ? &table->look->areas[PIVOT_AREA_COLUMN_LABELS]
1091 : axis_type == PIVOT_AXIS_ROW
1092 ? &table->look->areas[PIVOT_AREA_ROW_LABELS]
1094 if (area && fl->axis->label)
1096 table_area_style_uninit (area);
1097 decode_spvdx_style (fl->axis->label->style,
1098 fl->axis->label->text_frame_style, area);
1102 if (axis_type == PIVOT_AXIS_ROW)
1104 const struct spvdx_facet_level *fl2
1105 = find_facet_level (v, base_facet_level + (n - 1));
1107 decode_spvdx_style_incremental (
1108 fl2->axis->major_ticks->style,
1109 fl2->axis->major_ticks->tick_frame_style,
1110 &table->look->areas[PIVOT_AREA_ROW_LABELS]);
1113 const struct spvdx_facet_level *fl3 = find_facet_level (v, base_facet_level);
1114 if (fl3 && fl3->axis->major_ticks->label_angle == -90)
1116 if (axis_type == PIVOT_AXIS_COLUMN)
1117 table->rotate_inner_column_labels = true;
1119 table->rotate_outer_row_labels = true;
1122 /* Find the first row for each category. */
1123 size_t max_cat = max_category (series[0]);
1124 size_t *cat_rows = xnmalloc (max_cat + 1, sizeof *cat_rows);
1125 for (size_t k = 0; k <= max_cat; k++)
1126 cat_rows[k] = SIZE_MAX;
1127 for (size_t k = 0; k < series[0]->n_values; k++)
1129 const struct spv_data_value *dv = &series[0]->values[k];
1130 double d = dv->width < 0 ? dv->d : dv->index;
1131 if (d >= 0 && d < SIZE_MAX - 1)
1134 if (cat_rows[row] == SIZE_MAX)
1139 /* Drop missing categories and count what's left. */
1141 for (size_t k = 0; k <= max_cat; k++)
1142 if (cat_rows[k] != SIZE_MAX)
1143 cat_rows[n_cats++] = cat_rows[k];
1144 assert (n_cats > 0);
1146 /* Make the categories. */
1147 struct pivot_dimension *d = XZALLOC (struct pivot_dimension);
1148 table->dimensions[table->n_dimensions++] = d;
1150 series[0]->n_index = max_cat + 1;
1151 series[0]->index_to_category = xcalloc (
1152 max_cat + 1, sizeof *series[0]->index_to_category);
1153 struct pivot_category **cats = xnmalloc (n_cats, sizeof **cats);
1154 for (size_t k = 0; k < n_cats; k++)
1156 struct spv_data_value *dv = &series[0]->values[cat_rows[k]];
1157 int dv_num = dv ? dv->d : dv->index;
1158 struct pivot_category *cat = XZALLOC (struct pivot_category);
1159 char *retval = pivot_value_from_data_value (
1160 spv_map_lookup (&series[0]->map, dv), NULL, NULL, &cat->name);
1170 cat->data_index = k;
1171 cat->presentation_index = cat_rows[k];
1173 series[0]->index_to_category[dv_num] = cat;
1176 add_affixes (table, cat->name,
1177 series[0]->affixes, series[0]->n_affixes);
1181 struct pivot_axis *axis = &table->axes[axis_type];
1182 d->axis_type = axis_type;
1183 d->level = axis->n_dimensions;
1184 d->top_index = table->n_dimensions - 1;
1185 d->root = xzalloc (sizeof *d->root);
1186 *d->root = (struct pivot_category) {
1187 .name = pivot_value_new_user_text (
1188 series[0]->label ? series[0]->label : "", -1),
1190 .show_label = should_show_label (fl),
1191 .data_index = SIZE_MAX,
1192 .presentation_index = SIZE_MAX,
1194 d->data_leaves = xmemdup (cats, n_cats * sizeof *cats);
1195 d->presentation_leaves = xmemdup (cats, n_cats * sizeof *cats);
1196 d->n_leaves = d->allocated_leaves = n_cats;
1198 /* Now group them, in one pass per grouping variable, innermost first. */
1199 for (size_t j = 1; j < n; j++)
1201 struct pivot_category **new_cats = xnmalloc (n_cats, sizeof **cats);
1202 size_t n_new_cats = 0;
1204 /* Allocate a category index. */
1205 size_t max_cat = max_category (series[j]);
1206 series[j]->n_index = max_cat + 1;
1207 series[j]->index_to_category = xcalloc (
1208 max_cat + 1, sizeof *series[j]->index_to_category);
1209 for (size_t cat1 = 0; cat1 < n_cats;)
1211 /* Find a sequence of categories cat1...cat2 (exclusive), that all
1212 have the same value in series 'j'. (This might be only a single
1213 category; we will drop unnamed 1-category groups later.) */
1214 size_t row1 = cats[cat1]->presentation_index;
1215 const struct spv_data_value *dv1 = &series[j]->values[row1];
1217 for (cat2 = cat1 + 1; cat2 < n_cats; cat2++)
1219 size_t row2 = cats[cat2]->presentation_index;
1220 const struct spv_data_value *dv2 = &series[j]->values[row2];
1221 if (!spv_data_value_equal (dv1, dv2))
1224 size_t n_subs = cat2 - cat1;
1226 struct pivot_category *new_cat;
1227 const struct spv_data_value *name
1228 = spv_map_lookup (&series[j]->map, dv1);
1229 if (n_subs == 1 && name->width == 0)
1231 /* The existing category stands on its own. */
1232 new_cat = cats[cat1++];
1236 /* Create a new group with cat...cat2 as subcategories. */
1237 new_cat = xzalloc (sizeof *new_cat);
1238 *new_cat = (struct pivot_category) {
1240 .subs = xnmalloc (n_subs, sizeof *new_cat->subs),
1243 .data_index = SIZE_MAX,
1244 .presentation_index = row1,
1246 char *retval = pivot_value_from_data_value (name, NULL, NULL,
1255 for (size_t k = 0; k < n_subs; k++)
1256 new_cat->subs[k] = cats[cat1++];
1258 int dv1_num = dv1->width < 0 ? dv1->d : dv1->index;
1259 series[j]->index_to_category[dv1_num] = new_cat;
1263 add_affixes (table, new_cat->name,
1264 series[j]->affixes, series[j]->n_affixes);
1266 /* Append the new group to the list of new groups. */
1267 new_cats[n_new_cats++] = new_cat;
1272 n_cats = n_new_cats;
1275 /* Now drop unnamed 1-category groups and add parent pointers. */
1276 for (size_t j = 0; j < n_cats; j++)
1277 add_parents (cats[j], d->root, j);
1278 for (size_t j = 0; j < d->n_leaves; j++)
1280 d->data_leaves[j]->data_index = j;
1281 d->presentation_leaves[j]->presentation_index = j;
1284 d->root->subs = cats;
1285 d->root->n_subs = n_cats;
1289 pivot_dimension_destroy (d);
1293 dim_seriesp[(*n_dim_seriesp)++] = series[0];
1294 series[0]->dimension = d;
1296 axis->dimensions = xnrealloc (axis->dimensions, axis->n_dimensions + 1,
1297 sizeof *axis->dimensions);
1298 axis->dimensions[axis->n_dimensions++] = d;
1299 axis->extent *= d->n_leaves;
1305 static char * WARN_UNUSED_RESULT
1306 add_dimensions (struct hmap *series_map, const struct spvdx_nest *nest,
1307 enum pivot_axis_type axis_type,
1308 const struct spvdx_visualization *v, struct pivot_table *table,
1309 struct spv_series **dim_seriesp, size_t *n_dim_seriesp,
1312 struct pivot_axis *axis = &table->axes[axis_type];
1319 struct spv_series **series = xnmalloc (nest->n_vars, sizeof *series);
1320 for (size_t i = 0; i < nest->n_vars;)
1323 for (n = 0; i + n < nest->n_vars; n++)
1325 series[n] = spv_series_from_ref (series_map, nest->vars[i + n]->ref);
1326 if (!series[n] || !series[n]->n_values)
1332 struct pivot_dimension *d;
1333 char *error = add_dimension (series, n, axis_type, v, table,
1334 dim_seriesp, n_dim_seriesp,
1350 static char * WARN_UNUSED_RESULT
1351 add_layers (struct hmap *series_map,
1352 struct spvdx_layer **layers, size_t n_layers,
1353 const struct spvdx_visualization *v, struct pivot_table *table,
1354 struct spv_series **dim_seriesp, size_t *n_dim_seriesp,
1357 struct pivot_axis *axis = &table->axes[PIVOT_AXIS_LAYER];
1364 struct spv_series **series = xnmalloc (n_layers, sizeof *series);
1365 for (size_t i = 0; i < n_layers;)
1368 for (n = 0; i + n < n_layers; n++)
1370 series[n] = spv_series_from_ref (series_map,
1371 layers[i + n]->variable);
1372 if (!series[n] || !series[n]->n_values)
1378 struct pivot_dimension *d;
1379 char *error = add_dimension (
1380 series, n, PIVOT_AXIS_LAYER, v, table,
1381 dim_seriesp, n_dim_seriesp, level_ofs + i, &d);
1388 int index = atoi (layers[i]->value);
1389 assert (index < d->n_leaves);
1390 table->current_layer = xrealloc (
1391 table->current_layer,
1392 axis->n_dimensions * sizeof *table->current_layer);
1393 table->current_layer[axis->n_dimensions - 1] = index;
1402 static struct pivot_category *
1403 find_category (struct spv_series *series, int index)
1405 return (index >= 0 && index < series->n_index
1406 ? series->index_to_category[index]
1411 int_in_array (int value, const int *array, size_t n)
1413 for (size_t i = 0; i < n; i++)
1414 if (array[i] == value)
1421 apply_styles_to_value (struct pivot_table *table,
1422 struct pivot_value *value,
1423 const struct spvdx_set_format *sf,
1424 const struct table_area_style *base_area_style,
1425 const struct spvdx_style *fg,
1426 const struct spvdx_style *bg)
1430 if (sf->reset > 0 && value->ex)
1432 free (value->ex->footnote_indexes);
1433 value->ex->footnote_indexes = NULL;
1434 value->ex->n_footnotes = 0;
1437 struct fmt_spec format = { .w = 0 };
1440 format = decode_format (sf->format);
1441 add_affixes (table, value, sf->format->affix, sf->format->n_affix);
1443 else if (sf->number_format)
1445 format = decode_number_format (sf->number_format);
1446 add_affixes (table, value, sf->number_format->affix,
1447 sf->number_format->n_affix);
1449 else if (sf->n_string_format)
1451 for (size_t i = 0; i < sf->n_string_format; i++)
1452 add_affixes (table, value, sf->string_format[i]->affix,
1453 sf->string_format[i]->n_affix);
1455 else if (sf->date_time_format)
1457 format = decode_date_time_format (sf->date_time_format);
1458 add_affixes (table, value, sf->date_time_format->affix,
1459 sf->date_time_format->n_affix);
1461 else if (sf->elapsed_time_format)
1463 format = decode_elapsed_time_format (sf->elapsed_time_format);
1464 add_affixes (table, value, sf->elapsed_time_format->affix,
1465 sf->elapsed_time_format->n_affix);
1470 if (value->type == PIVOT_VALUE_NUMERIC)
1471 value->numeric.format = format;
1473 /* Possibly we should try to apply date and time formats too,
1474 but none seem to occur in practice so far. */
1479 const struct pivot_value_ex *ex = pivot_value_ex (value);
1480 struct table_area_style area;
1481 pivot_value_get_style (
1483 ex->font_style ? ex->font_style : &base_area_style->font_style,
1484 ex->cell_style ? ex->cell_style : &base_area_style->cell_style,
1486 decode_spvdx_style_incremental (fg, bg, &area);
1487 pivot_value_set_style (value, &area);
1488 table_area_style_uninit (&area);
1493 decode_set_cell_properties__ (struct pivot_table *table,
1494 struct hmap *series_map,
1495 const struct spvdx_intersect *intersect,
1496 const struct spvdx_style *interval,
1497 const struct spvdx_style *graph,
1498 const struct spvdx_style *labeling,
1499 const struct spvdx_style *frame,
1500 const struct spvdx_style *major_ticks,
1501 const struct spvdx_set_format *set_format)
1503 if (graph && labeling && intersect->alternating
1504 && !interval && !major_ticks && !frame && !set_format)
1506 /* Sets alt_fg_color and alt_bg_color. */
1507 struct table_area_style area;
1508 decode_spvdx_style (labeling, graph, &area);
1509 table->look->areas[PIVOT_AREA_DATA].font_style.fg[1]
1510 = area.font_style.fg[0];
1511 table->look->areas[PIVOT_AREA_DATA].font_style.bg[1]
1512 = area.font_style.bg[0];
1513 table_area_style_uninit (&area);
1516 && !labeling && !interval && !major_ticks && !frame && !set_format)
1518 /* 'graph->width' likely just sets the width of the table as a
1521 else if (!graph && !labeling && !interval && !frame && !set_format
1524 /* No-op. (Presumably there's a setMetaData we don't care about.) */
1526 else if (((set_format && spvdx_is_major_ticks (set_format->target))
1527 || major_ticks || frame)
1528 && intersect->n_where == 1)
1530 /* Formatting for individual row or column labels. */
1531 const struct spvdx_where *w = intersect->where[0];
1532 struct spv_series *s = spv_series_find (series_map, w->variable->id);
1535 const char *p = w->include;
1540 int include = strtol (p, &tail, 10);
1542 struct pivot_category *c = find_category (s, include);
1545 const struct table_area_style *base_area_style
1546 = (c->dimension->axis_type == PIVOT_AXIS_ROW
1547 ? &table->look->areas[PIVOT_AREA_ROW_LABELS]
1548 : &table->look->areas[PIVOT_AREA_COLUMN_LABELS]);
1549 apply_styles_to_value (table, c->name, set_format,
1550 base_area_style, major_ticks, frame);
1560 else if ((set_format && spvdx_is_labeling (set_format->target))
1561 || labeling || interval)
1563 /* Formatting for individual cells or groups of them with some dimensions
1565 int **indexes = XCALLOC (table->n_dimensions, int *);
1566 size_t *n = XCALLOC (table->n_dimensions, size_t);
1567 size_t *allocated = XCALLOC (table->n_dimensions, size_t);
1569 for (size_t i = 0; i < intersect->n_where; i++)
1571 const struct spvdx_where *w = intersect->where[i];
1572 struct spv_series *s = spv_series_find (series_map, w->variable->id);
1576 /* Group indexes may be included even though they are redundant.
1581 size_t j = s->dimension->top_index;
1583 const char *p = w->include;
1587 int include = strtol (p, &tail, 10);
1589 struct pivot_category *c = find_category (s, include);
1592 if (n[j] >= allocated[j])
1593 indexes[j] = x2nrealloc (indexes[j], &allocated[j],
1594 sizeof *indexes[j]);
1595 indexes[j][n[j]++] = c->data_index;
1608 for (size_t i = 0; i < table->n_dimensions; i++)
1612 printf (" %d=(", i);
1613 for (size_t j = 0; j < n[i]; j++)
1617 printf ("%d", indexes[i][j]);
1625 /* XXX This is inefficient in the common case where all of the dimensions
1626 are matched. We should use a heuristic where if all of the dimensions
1627 are matched and the product of n[*] is less than
1628 hmap_count(&table->cells) then iterate through all the possibilities
1629 rather than all the cells. Or even only do it if there is just one
1632 struct pivot_cell *cell;
1633 HMAP_FOR_EACH (cell, struct pivot_cell, hmap_node, &table->cells)
1635 for (size_t i = 0; i < table->n_dimensions; i++)
1637 if (n[i] && !int_in_array (cell->idx[i], indexes[i], n[i]))
1640 apply_styles_to_value (table, cell->value, set_format,
1641 &table->look->areas[PIVOT_AREA_DATA],
1642 labeling, interval);
1647 for (size_t i = 0; i < table->n_dimensions; i++)
1658 decode_set_cell_properties (struct pivot_table *table, struct hmap *series_map,
1659 struct spvdx_set_cell_properties **scps,
1662 for (size_t i = 0; i < n_scps; i++)
1664 const struct spvdx_set_cell_properties *scp = scps[i];
1665 const struct spvdx_style *interval = NULL;
1666 const struct spvdx_style *graph = NULL;
1667 const struct spvdx_style *labeling = NULL;
1668 const struct spvdx_style *frame = NULL;
1669 const struct spvdx_style *major_ticks = NULL;
1670 const struct spvdx_set_format *set_format = NULL;
1671 for (size_t j = 0; j < scp->n_seq; j++)
1673 const struct spvxml_node *node = scp->seq[j];
1674 if (spvdx_is_set_style (node))
1676 const struct spvdx_set_style *set_style
1677 = spvdx_cast_set_style (node);
1678 if (spvdx_is_graph (set_style->target))
1679 graph = set_style->style;
1680 else if (spvdx_is_labeling (set_style->target))
1681 labeling = set_style->style;
1682 else if (spvdx_is_interval (set_style->target))
1683 interval = set_style->style;
1684 else if (spvdx_is_major_ticks (set_style->target))
1685 major_ticks = set_style->style;
1689 else if (spvdx_is_set_frame_style (node))
1690 frame = spvdx_cast_set_frame_style (node)->style;
1691 else if (spvdx_is_set_format (node))
1692 set_format = spvdx_cast_set_format (node);
1694 assert (spvdx_is_set_meta_data (node));
1697 if (scp->union_ && scp->apply_to_converse <= 0)
1699 for (size_t j = 0; j < scp->union_->n_intersect; j++)
1700 decode_set_cell_properties__ (
1701 table, series_map, scp->union_->intersect[j],
1702 interval, graph, labeling, frame, major_ticks, set_format);
1704 else if (!scp->union_ && scp->apply_to_converse > 0)
1706 if ((set_format && spvdx_is_labeling (set_format->target))
1707 || labeling || interval)
1709 struct pivot_cell *cell;
1710 HMAP_FOR_EACH (cell, struct pivot_cell, hmap_node, &table->cells)
1711 apply_styles_to_value (table, cell->value, set_format,
1712 &table->look->areas[PIVOT_AREA_DATA],
1716 else if (!scp->union_ && scp->apply_to_converse <= 0)
1718 /* Appears to be used to set the font for something--but what? */
1725 static struct spv_series *
1726 parse_formatting (const struct spvdx_visualization *v,
1727 const struct hmap *series_map, struct hmap *format_map)
1729 const struct spvdx_labeling *labeling = v->graph->interval->labeling;
1730 struct spv_series *cell_format = NULL;
1731 for (size_t i = 0; i < labeling->n_seq; i++)
1733 const struct spvdx_formatting *f
1734 = spvdx_cast_formatting (labeling->seq[i]);
1738 cell_format = spv_series_from_ref (series_map, f->variable);
1739 for (size_t j = 0; j < f->n_format_mapping; j++)
1741 const struct spvdx_format_mapping *fm = f->format_mapping[j];
1745 struct format_mapping *out = xmalloc (sizeof *out);
1746 out->from = fm->from;
1747 out->to = decode_format (fm->format);
1748 hmap_insert (format_map, &out->hmap_node,
1749 hash_int (out->from, 0));
1758 format_map_destroy (struct hmap *format_map)
1760 struct format_mapping *fm, *next;
1761 HMAP_FOR_EACH_SAFE (fm, next, struct format_mapping, hmap_node, format_map)
1763 hmap_delete (format_map, &fm->hmap_node);
1766 hmap_destroy (format_map);
1769 char * WARN_UNUSED_RESULT
1770 decode_spvdx_table (const struct spvdx_visualization *v, const char *subtype,
1771 const struct pivot_table_look *look,
1772 const struct spv_data *data, struct pivot_table **outp)
1774 struct pivot_table *table = pivot_table_create__ (
1775 pivot_value_new_user_text (v->name, SIZE_MAX), subtype);
1777 pivot_table_set_look (table, look);
1778 table->look = pivot_table_look_unshare (table->look);
1780 struct hmap series_map = HMAP_INITIALIZER (series_map);
1781 struct hmap format_map = HMAP_INITIALIZER (format_map);
1782 struct spv_series **dim_series = NULL;
1785 struct spvdx_visualization_extension *ve = v->visualization_extension;
1786 table->show_grid_lines = ve && ve->show_gridline;
1788 /* Sizing from the legacy properties can get overridden. */
1789 if (v->graph->cell_style->width)
1791 int min_width, max_width, n = 0;
1792 if (sscanf (v->graph->cell_style->width, "%*d%%;%dpt;%dpt%n",
1793 &min_width, &max_width, &n)
1794 && v->graph->cell_style->width[n] == '\0')
1796 table->look->width_ranges[TABLE_HORZ][0] = min_width;
1797 table->look->width_ranges[TABLE_HORZ][1] = max_width;
1803 Any pivot_value might refer to footnotes, so it's important to process the
1804 footnotes early to ensure that those references can be resolved. There is
1805 a possible problem that a footnote might itself reference an
1806 as-yet-unprocessed footnote, but that's OK because footnote references
1807 don't actually look at the footnote contents but only resolve a pointer to
1808 where the footnote will go later.
1810 Before we really start, create all the footnotes we'll fill in. This is
1811 because sometimes footnotes refer to themselves or to each other and we
1812 don't want to reject those references. */
1814 for (size_t i = 0; i < v->container->n_label_frame; i++)
1816 const struct spvdx_label_frame *lf = v->container->label_frame[i];
1818 && lf->label->purpose == SPVDX_PURPOSE_FOOTNOTE
1819 && lf->label->n_text > 0
1820 && lf->label->text[0]->uses_reference > 0)
1822 pivot_table_create_footnote__ (
1823 table, lf->label->text[0]->uses_reference - 1,
1828 if (v->graph->interval->footnotes)
1829 decode_footnotes (table, v->graph->interval->footnotes);
1831 struct spv_series *footnotes = NULL;
1832 for (size_t i = 0; i < v->graph->interval->labeling->n_seq; i++)
1834 const struct spvxml_node *node = v->graph->interval->labeling->seq[i];
1835 if (spvdx_is_footnotes (node))
1837 const struct spvdx_footnotes *f = spvdx_cast_footnotes (node);
1838 footnotes = spv_series_from_ref (&series_map, f->variable);
1839 decode_footnotes (table, f);
1842 for (size_t i = 0; i < v->n_lf1; i++)
1844 error = decode_label_frame (table, v->lf1[i]);
1848 for (size_t i = 0; i < v->n_lf2; i++)
1850 error = decode_label_frame (table, v->lf2[i]);
1855 for (size_t i = 0; i < v->container->n_label_frame; i++)
1857 error = decode_label_frame (table, v->container->label_frame[i]);
1861 if (v->graph->interval->labeling->style)
1863 table_area_style_uninit (&table->look->areas[PIVOT_AREA_DATA]);
1864 decode_spvdx_style (v->graph->interval->labeling->style,
1865 v->graph->cell_style,
1866 &table->look->areas[PIVOT_AREA_DATA]);
1869 /* Decode all of the sourceVariable and derivedVariable */
1870 struct spvxml_node **nodes = xmemdup (v->seq, v->n_seq * sizeof *v->seq);
1871 size_t n_nodes = v->n_seq;
1874 bool progress = false;
1875 for (size_t i = 0; i < n_nodes;)
1877 error = (spvdx_is_source_variable (nodes[i])
1878 ? decode_spvdx_source_variable (nodes[i], data, &series_map)
1879 : decode_spvdx_derived_variable (nodes[i], &series_map));
1882 nodes[i] = nodes[--n_nodes];
1885 else if (error == &BAD_REFERENCE)
1896 error = xasprintf ("Table has %zu variables with circular or "
1897 "unresolved references, including variable %s.",
1898 n_nodes, nodes[0]->id);
1905 const struct spvdx_cross *cross = v->graph->faceting->cross;
1907 assert (cross->n_seq == 1);
1908 const struct spvdx_nest *columns = spvdx_cast_nest (cross->seq[0]);
1909 size_t max_columns = columns ? columns->n_vars : 0;
1911 assert (cross->n_seq2 == 1);
1912 const struct spvdx_nest *rows = spvdx_cast_nest (cross->seq2[0]);
1913 size_t max_rows = rows ? rows->n_vars : 0;
1915 size_t max_layers = (v->graph->faceting->n_layers1
1916 + v->graph->faceting->n_layers2);
1918 size_t max_dims = max_columns + max_rows + max_layers;
1919 table->dimensions = xnmalloc (max_dims, sizeof *table->dimensions);
1920 dim_series = xnmalloc (max_dims, sizeof *dim_series);
1921 size_t n_dim_series = 0;
1923 error = add_dimensions (&series_map, columns, PIVOT_AXIS_COLUMN, v, table,
1924 dim_series, &n_dim_series, 1);
1928 error = add_dimensions (&series_map, rows, PIVOT_AXIS_ROW, v, table,
1929 dim_series, &n_dim_series, max_columns + 1);
1933 error = add_layers (&series_map, v->graph->faceting->layers1,
1934 v->graph->faceting->n_layers1,
1935 v, table, dim_series, &n_dim_series,
1936 max_rows + max_columns + 1);
1940 error = add_layers (&series_map, v->graph->faceting->layers2,
1941 v->graph->faceting->n_layers2,
1942 v, table, dim_series, &n_dim_series,
1943 (max_rows + max_columns + v->graph->faceting->n_layers1
1948 struct spv_series *cell = spv_series_find (&series_map, "cell");
1951 error = xstrdup (_("Table lacks cell data."));
1955 struct spv_series *cell_format = parse_formatting (v, &series_map,
1958 assert (table->n_dimensions == n_dim_series);
1959 size_t *dim_indexes = xnmalloc (table->n_dimensions, sizeof *dim_indexes);
1960 for (size_t i = 0; i < cell->n_values; i++)
1962 for (size_t j = 0; j < table->n_dimensions; j++)
1964 const struct spv_data_value *value = &dim_series[j]->values[i];
1965 const struct pivot_category *cat = find_category (
1966 dim_series[j], value->width < 0 ? value->d : value->index);
1969 dim_indexes[j] = cat->data_index;
1972 struct pivot_value *value;
1973 error = pivot_value_from_data_value (
1974 &cell->values[i], cell_format ? &cell_format->values[i] : NULL,
1975 &format_map, &value);
1981 const struct spv_data_value *d = &footnotes->values[i];
1984 const char *p = d->s;
1988 int idx = strtol (p, &tail, 10);
1989 add_footnote (value, idx, table);
1999 if (value->type == PIVOT_VALUE_NUMERIC
2000 && value->numeric.x == SYSMIS
2001 && !pivot_value_ex (value)->n_footnotes)
2003 /* Apparently, system-missing values are just empty cells? */
2004 pivot_value_destroy (value);
2007 pivot_table_put (table, dim_indexes, table->n_dimensions, value);
2012 decode_set_cell_properties (table, &series_map, v->graph->facet_layout->scp1,
2013 v->graph->facet_layout->n_scp1);
2014 decode_set_cell_properties (table, &series_map, v->graph->facet_layout->scp2,
2015 v->graph->facet_layout->n_scp2);
2017 pivot_table_assign_label_depth (table);
2019 format_map_destroy (&format_map);
2023 spv_series_destroy (&series_map);
2026 pivot_table_unref (table);