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 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 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 (sizeof *s);
788 s->name = xstrdup (node->id);
790 s->label = sv->label ? xstrdup (sv->label) : NULL;
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 (sizeof *s);
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 (sizeof *v);
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 (sizeof *d);
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 (sizeof *cat);
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)
1432 free (value->footnote_indexes);
1433 value->footnote_indexes = NULL;
1434 value->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 struct table_area_style area;
1480 pivot_value_get_style (
1482 value->font_style ? value->font_style : &base_area_style->font_style,
1483 value->cell_style ? value->cell_style : &base_area_style->cell_style,
1485 decode_spvdx_style_incremental (fg, bg, &area);
1486 pivot_value_set_style (value, &area);
1487 table_area_style_uninit (&area);
1492 decode_set_cell_properties__ (struct pivot_table *table,
1493 struct hmap *series_map,
1494 const struct spvdx_intersect *intersect,
1495 const struct spvdx_style *interval,
1496 const struct spvdx_style *graph,
1497 const struct spvdx_style *labeling,
1498 const struct spvdx_style *frame,
1499 const struct spvdx_style *major_ticks,
1500 const struct spvdx_set_format *set_format)
1502 if (graph && labeling && intersect->alternating
1503 && !interval && !major_ticks && !frame && !set_format)
1505 /* Sets alt_fg_color and alt_bg_color. */
1506 struct table_area_style area;
1507 decode_spvdx_style (labeling, graph, &area);
1508 table->look->areas[PIVOT_AREA_DATA].font_style.fg[1]
1509 = area.font_style.fg[0];
1510 table->look->areas[PIVOT_AREA_DATA].font_style.bg[1]
1511 = area.font_style.bg[0];
1512 table_area_style_uninit (&area);
1515 && !labeling && !interval && !major_ticks && !frame && !set_format)
1517 /* 'graph->width' likely just sets the width of the table as a
1520 else if (!graph && !labeling && !interval && !frame && !set_format
1523 /* No-op. (Presumably there's a setMetaData we don't care about.) */
1525 else if (((set_format && spvdx_is_major_ticks (set_format->target))
1526 || major_ticks || frame)
1527 && intersect->n_where == 1)
1529 /* Formatting for individual row or column labels. */
1530 const struct spvdx_where *w = intersect->where[0];
1531 struct spv_series *s = spv_series_find (series_map, w->variable->id);
1534 const char *p = w->include;
1539 int include = strtol (p, &tail, 10);
1541 struct pivot_category *c = find_category (s, include);
1544 const struct table_area_style *base_area_style
1545 = (c->dimension->axis_type == PIVOT_AXIS_ROW
1546 ? &table->look->areas[PIVOT_AREA_ROW_LABELS]
1547 : &table->look->areas[PIVOT_AREA_COLUMN_LABELS]);
1548 apply_styles_to_value (table, c->name, set_format,
1549 base_area_style, major_ticks, frame);
1559 else if ((set_format && spvdx_is_labeling (set_format->target))
1560 || labeling || interval)
1562 /* Formatting for individual cells or groups of them with some dimensions
1564 int **indexes = XCALLOC (table->n_dimensions, int *);
1565 size_t *n = XCALLOC (table->n_dimensions, size_t);
1566 size_t *allocated = XCALLOC (table->n_dimensions, size_t);
1568 for (size_t i = 0; i < intersect->n_where; i++)
1570 const struct spvdx_where *w = intersect->where[i];
1571 struct spv_series *s = spv_series_find (series_map, w->variable->id);
1575 /* Group indexes may be included even though they are redundant.
1580 size_t j = s->dimension->top_index;
1582 const char *p = w->include;
1586 int include = strtol (p, &tail, 10);
1588 struct pivot_category *c = find_category (s, include);
1591 if (n[j] >= allocated[j])
1592 indexes[j] = x2nrealloc (indexes[j], &allocated[j],
1593 sizeof *indexes[j]);
1594 indexes[j][n[j]++] = c->data_index;
1607 for (size_t i = 0; i < table->n_dimensions; i++)
1611 printf (" %d=(", i);
1612 for (size_t j = 0; j < n[i]; j++)
1616 printf ("%d", indexes[i][j]);
1624 /* XXX This is inefficient in the common case where all of the dimensions
1625 are matched. We should use a heuristic where if all of the dimensions
1626 are matched and the product of n[*] is less than
1627 hmap_count(&table->cells) then iterate through all the possibilities
1628 rather than all the cells. Or even only do it if there is just one
1631 struct pivot_cell *cell;
1632 HMAP_FOR_EACH (cell, struct pivot_cell, hmap_node, &table->cells)
1634 for (size_t i = 0; i < table->n_dimensions; i++)
1636 if (n[i] && !int_in_array (cell->idx[i], indexes[i], n[i]))
1639 apply_styles_to_value (table, cell->value, set_format,
1640 &table->look->areas[PIVOT_AREA_DATA],
1641 labeling, interval);
1646 for (size_t i = 0; i < table->n_dimensions; i++)
1657 decode_set_cell_properties (struct pivot_table *table, struct hmap *series_map,
1658 struct spvdx_set_cell_properties **scps,
1661 for (size_t i = 0; i < n_scps; i++)
1663 const struct spvdx_set_cell_properties *scp = scps[i];
1664 const struct spvdx_style *interval = NULL;
1665 const struct spvdx_style *graph = NULL;
1666 const struct spvdx_style *labeling = NULL;
1667 const struct spvdx_style *frame = NULL;
1668 const struct spvdx_style *major_ticks = NULL;
1669 const struct spvdx_set_format *set_format = NULL;
1670 for (size_t j = 0; j < scp->n_seq; j++)
1672 const struct spvxml_node *node = scp->seq[j];
1673 if (spvdx_is_set_style (node))
1675 const struct spvdx_set_style *set_style
1676 = spvdx_cast_set_style (node);
1677 if (spvdx_is_graph (set_style->target))
1678 graph = set_style->style;
1679 else if (spvdx_is_labeling (set_style->target))
1680 labeling = set_style->style;
1681 else if (spvdx_is_interval (set_style->target))
1682 interval = set_style->style;
1683 else if (spvdx_is_major_ticks (set_style->target))
1684 major_ticks = set_style->style;
1688 else if (spvdx_is_set_frame_style (node))
1689 frame = spvdx_cast_set_frame_style (node)->style;
1690 else if (spvdx_is_set_format (node))
1691 set_format = spvdx_cast_set_format (node);
1693 assert (spvdx_is_set_meta_data (node));
1696 if (scp->union_ && scp->apply_to_converse <= 0)
1698 for (size_t j = 0; j < scp->union_->n_intersect; j++)
1699 decode_set_cell_properties__ (
1700 table, series_map, scp->union_->intersect[j],
1701 interval, graph, labeling, frame, major_ticks, set_format);
1703 else if (!scp->union_ && scp->apply_to_converse > 0)
1705 if ((set_format && spvdx_is_labeling (set_format->target))
1706 || labeling || interval)
1708 struct pivot_cell *cell;
1709 HMAP_FOR_EACH (cell, struct pivot_cell, hmap_node, &table->cells)
1710 apply_styles_to_value (table, cell->value, set_format,
1711 &table->look->areas[PIVOT_AREA_DATA],
1715 else if (!scp->union_ && scp->apply_to_converse <= 0)
1717 /* Appears to be used to set the font for something--but what? */
1724 static struct spv_series *
1725 parse_formatting (const struct spvdx_visualization *v,
1726 const struct hmap *series_map, struct hmap *format_map)
1728 const struct spvdx_labeling *labeling = v->graph->interval->labeling;
1729 struct spv_series *cell_format = NULL;
1730 for (size_t i = 0; i < labeling->n_seq; i++)
1732 const struct spvdx_formatting *f
1733 = spvdx_cast_formatting (labeling->seq[i]);
1737 cell_format = spv_series_from_ref (series_map, f->variable);
1738 for (size_t j = 0; j < f->n_format_mapping; j++)
1740 const struct spvdx_format_mapping *fm = f->format_mapping[j];
1744 struct format_mapping *out = xmalloc (sizeof *out);
1745 out->from = fm->from;
1746 out->to = decode_format (fm->format);
1747 hmap_insert (format_map, &out->hmap_node,
1748 hash_int (out->from, 0));
1757 format_map_destroy (struct hmap *format_map)
1759 struct format_mapping *fm, *next;
1760 HMAP_FOR_EACH_SAFE (fm, next, struct format_mapping, hmap_node, format_map)
1762 hmap_delete (format_map, &fm->hmap_node);
1765 hmap_destroy (format_map);
1768 char * WARN_UNUSED_RESULT
1769 decode_spvdx_table (const struct spvdx_visualization *v, const char *subtype,
1770 const struct pivot_table_look *look,
1771 struct spv_data *data, struct pivot_table **outp)
1773 struct pivot_table *table = pivot_table_create__ (NULL, subtype);
1775 pivot_table_set_look (table, look);
1776 table->look = pivot_table_look_unshare (table->look);
1778 struct hmap series_map = HMAP_INITIALIZER (series_map);
1779 struct hmap format_map = HMAP_INITIALIZER (format_map);
1780 struct spv_series **dim_series = NULL;
1783 struct spvdx_visualization_extension *ve = v->visualization_extension;
1784 table->show_grid_lines = ve && ve->show_gridline;
1786 /* Sizing from the legacy properties can get overridden. */
1787 if (v->graph->cell_style->width)
1789 int min_width, max_width, n = 0;
1790 if (sscanf (v->graph->cell_style->width, "%*d%%;%dpt;%dpt%n",
1791 &min_width, &max_width, &n)
1792 && v->graph->cell_style->width[n] == '\0')
1794 table->look->width_ranges[TABLE_HORZ][0] = min_width;
1795 table->look->width_ranges[TABLE_HORZ][1] = max_width;
1801 Any pivot_value might refer to footnotes, so it's important to process the
1802 footnotes early to ensure that those references can be resolved. There is
1803 a possible problem that a footnote might itself reference an
1804 as-yet-unprocessed footnote, but that's OK because footnote references
1805 don't actually look at the footnote contents but only resolve a pointer to
1806 where the footnote will go later.
1808 Before we really start, create all the footnotes we'll fill in. This is
1809 because sometimes footnotes refer to themselves or to each other and we
1810 don't want to reject those references. */
1812 for (size_t i = 0; i < v->container->n_label_frame; i++)
1814 const struct spvdx_label_frame *lf = v->container->label_frame[i];
1816 && lf->label->purpose == SPVDX_PURPOSE_FOOTNOTE
1817 && lf->label->n_text > 0
1818 && lf->label->text[0]->uses_reference > 0)
1820 pivot_table_create_footnote__ (
1821 table, lf->label->text[0]->uses_reference - 1,
1826 if (v->graph->interval->footnotes)
1827 decode_footnotes (table, v->graph->interval->footnotes);
1829 struct spv_series *footnotes = NULL;
1830 for (size_t i = 0; i < v->graph->interval->labeling->n_seq; i++)
1832 const struct spvxml_node *node = v->graph->interval->labeling->seq[i];
1833 if (spvdx_is_footnotes (node))
1835 const struct spvdx_footnotes *f = spvdx_cast_footnotes (node);
1836 footnotes = spv_series_from_ref (&series_map, f->variable);
1837 decode_footnotes (table, f);
1840 for (size_t i = 0; i < v->n_lf1; i++)
1842 error = decode_label_frame (table, v->lf1[i]);
1846 for (size_t i = 0; i < v->n_lf2; i++)
1848 error = decode_label_frame (table, v->lf2[i]);
1853 for (size_t i = 0; i < v->container->n_label_frame; i++)
1855 error = decode_label_frame (table, v->container->label_frame[i]);
1859 if (v->graph->interval->labeling->style)
1861 table_area_style_uninit (&table->look->areas[PIVOT_AREA_DATA]);
1862 decode_spvdx_style (v->graph->interval->labeling->style,
1863 v->graph->cell_style,
1864 &table->look->areas[PIVOT_AREA_DATA]);
1867 /* Decode all of the sourceVariable and derivedVariable */
1868 struct spvxml_node **nodes = xmemdup (v->seq, v->n_seq * sizeof *v->seq);
1869 size_t n_nodes = v->n_seq;
1872 bool progress = false;
1873 for (size_t i = 0; i < n_nodes;)
1875 error = (spvdx_is_source_variable (nodes[i])
1876 ? decode_spvdx_source_variable (nodes[i], data, &series_map)
1877 : decode_spvdx_derived_variable (nodes[i], &series_map));
1880 nodes[i] = nodes[--n_nodes];
1883 else if (error == &BAD_REFERENCE)
1895 error = xasprintf ("Table has %zu variables with circular or "
1896 "unresolved references, including variable %s.",
1897 n_nodes, nodes[0]->id);
1903 const struct spvdx_cross *cross = v->graph->faceting->cross;
1905 assert (cross->n_seq == 1);
1906 const struct spvdx_nest *columns = spvdx_cast_nest (cross->seq[0]);
1907 size_t max_columns = columns ? columns->n_vars : 0;
1909 assert (cross->n_seq2 == 1);
1910 const struct spvdx_nest *rows = spvdx_cast_nest (cross->seq2[0]);
1911 size_t max_rows = rows ? rows->n_vars : 0;
1913 size_t max_layers = (v->graph->faceting->n_layers1
1914 + v->graph->faceting->n_layers2);
1916 size_t max_dims = max_columns + max_rows + max_layers;
1917 table->dimensions = xnmalloc (max_dims, sizeof *table->dimensions);
1918 dim_series = xnmalloc (max_dims, sizeof *dim_series);
1919 size_t n_dim_series = 0;
1921 error = add_dimensions (&series_map, columns, PIVOT_AXIS_COLUMN, v, table,
1922 dim_series, &n_dim_series, 1);
1926 error = add_dimensions (&series_map, rows, PIVOT_AXIS_ROW, v, table,
1927 dim_series, &n_dim_series, max_columns + 1);
1931 error = add_layers (&series_map, v->graph->faceting->layers1,
1932 v->graph->faceting->n_layers1,
1933 v, table, dim_series, &n_dim_series,
1934 max_rows + max_columns + 1);
1938 error = add_layers (&series_map, v->graph->faceting->layers2,
1939 v->graph->faceting->n_layers2,
1940 v, table, dim_series, &n_dim_series,
1941 (max_rows + max_columns + v->graph->faceting->n_layers1
1946 struct spv_series *cell = spv_series_find (&series_map, "cell");
1949 error = xstrdup (_("Table lacks cell data."));
1953 struct spv_series *cell_format = parse_formatting (v, &series_map,
1956 assert (table->n_dimensions == n_dim_series);
1957 size_t *dim_indexes = xnmalloc (table->n_dimensions, sizeof *dim_indexes);
1958 for (size_t i = 0; i < cell->n_values; i++)
1960 for (size_t j = 0; j < table->n_dimensions; j++)
1962 const struct spv_data_value *value = &dim_series[j]->values[i];
1963 const struct pivot_category *cat = find_category (
1964 dim_series[j], value->width < 0 ? value->d : value->index);
1967 dim_indexes[j] = cat->data_index;
1970 struct pivot_value *value;
1971 error = pivot_value_from_data_value (
1972 &cell->values[i], cell_format ? &cell_format->values[i] : NULL,
1973 &format_map, &value);
1979 const struct spv_data_value *d = &footnotes->values[i];
1982 const char *p = d->s;
1986 int idx = strtol (p, &tail, 10);
1987 add_footnote (value, idx, table);
1997 if (value->type == PIVOT_VALUE_NUMERIC
1998 && value->numeric.x == SYSMIS
1999 && !value->n_footnotes)
2001 /* Apparently, system-missing values are just empty cells? */
2002 pivot_value_destroy (value);
2005 pivot_table_put (table, dim_indexes, table->n_dimensions, value);
2010 decode_set_cell_properties (table, &series_map, v->graph->facet_layout->scp1,
2011 v->graph->facet_layout->n_scp1);
2012 decode_set_cell_properties (table, &series_map, v->graph->facet_layout->scp2,
2013 v->graph->facet_layout->n_scp2);
2015 pivot_table_assign_label_depth (table);
2017 format_map_destroy (&format_map);
2021 spv_series_destroy (&series_map);
2024 pivot_table_unref (table);