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, NULL);
207 mapping->to.width = strlen (mapping->to.s);
212 mapping->to.width = strlen (to);
213 mapping->to.s = xstrdup (to);
216 struct spv_mapping *old_mapping = spv_map_search (map, from);
219 bool same = spv_data_value_equal (&old_mapping->to, &mapping->to);
220 spv_data_value_uninit (&mapping->to);
223 : xasprintf ("Duplicate relabeling differs for from=\"%.*g\"",
227 hmap_insert (map, &mapping->hmap_node, hash_double (from, 0));
232 spv_map_destroy (struct hmap *map)
234 struct spv_mapping *mapping, *next;
235 HMAP_FOR_EACH_SAFE (mapping, next, struct spv_mapping, hmap_node, map)
237 spv_data_value_uninit (&mapping->to);
238 hmap_delete (map, &mapping->hmap_node);
244 static char * WARN_UNUSED_RESULT
245 spv_series_parse_relabels (struct hmap *map,
246 struct spvdx_relabel **relabels, size_t n_relabels,
247 bool try_strings_as_numbers,
248 const struct fmt_spec *format)
250 for (size_t i = 0; i < n_relabels; i++)
252 const struct spvdx_relabel *relabel = relabels[i];
253 char *error = spv_map_insert (map, relabel->from, relabel->to,
254 try_strings_as_numbers, format);
261 static char * WARN_UNUSED_RESULT
262 spv_series_parse_value_map_entry (struct hmap *map,
263 const struct spvdx_value_map_entry *vme)
265 for (const char *p = vme->from; ; p++)
267 int save_errno = errno;
270 double from = c_strtod (p, &end);
271 bool ok = !errno && end > p && strchr (";", *end);
274 return xasprintf ("Syntax error in valueMapEntry from=\"%s\".",
277 char *error = spv_map_insert (map, from, vme->to, true,
278 &(struct fmt_spec) { FMT_A, 40, 0 });
289 static struct fmt_spec
290 decode_date_time_format (const struct spvdx_date_time_format *dtf)
292 if (dtf->dt_base_format == SPVDX_DT_BASE_FORMAT_DATE)
295 = (dtf->show_quarter > 0 ? FMT_QYR
296 : dtf->show_week > 0 ? FMT_WKYR
297 : dtf->mdy_order == SPVDX_MDY_ORDER_DAY_MONTH_YEAR
298 ? (dtf->month_format == SPVDX_MONTH_FORMAT_NUMBER
299 || dtf->month_format == SPVDX_MONTH_FORMAT_PADDED_NUMBER
300 ? FMT_EDATE : FMT_DATE)
301 : dtf->mdy_order == SPVDX_MDY_ORDER_YEAR_MONTH_DAY ? FMT_SDATE
304 int w = fmt_min_output_width (type);
305 if (dtf->year_abbreviation <= 0)
307 return (struct fmt_spec) { .type = type, .w = w };
312 = (dtf->dt_base_format == SPVDX_DT_BASE_FORMAT_DATE_TIME
313 ? (dtf->mdy_order == SPVDX_MDY_ORDER_YEAR_MONTH_DAY
316 : (dtf->show_day > 0 ? FMT_DTIME
317 : dtf->show_hour > 0 ? FMT_TIME
319 int w = fmt_min_output_width (type);
321 if (dtf->show_second > 0)
324 if (dtf->show_millis > 0)
330 return (struct fmt_spec) { .type = type, .w = w, .d = d };
334 static struct fmt_spec
335 decode_elapsed_time_format (const struct spvdx_elapsed_time_format *etf)
338 = (etf->dt_base_format != SPVDX_DT_BASE_FORMAT_TIME ? FMT_DTIME
339 : etf->show_hour > 0 ? FMT_TIME
341 int w = fmt_min_output_width (type);
343 if (etf->show_second > 0)
346 if (etf->show_millis > 0)
352 return (struct fmt_spec) { .type = type, .w = w, .d = d };
355 static struct fmt_spec
356 decode_number_format (const struct spvdx_number_format *nf)
358 enum fmt_type type = (nf->scientific == SPVDX_SCIENTIFIC_TRUE ? FMT_E
359 : nf->prefix && !strcmp (nf->prefix, "$") ? FMT_DOLLAR
360 : nf->suffix && !strcmp (nf->suffix, "%") ? FMT_PCT
361 : nf->use_grouping ? FMT_COMMA
364 int d = nf->maximum_fraction_digits;
368 struct fmt_spec f = (struct fmt_spec) { type, 40, d };
373 /* Returns an *approximation* of IN as a fmt_spec.
375 Not for use with string formats, which don't have any options anyway. */
376 static struct fmt_spec
377 decode_format (const struct spvdx_format *in)
379 if (in->f_base_format == SPVDX_F_BASE_FORMAT_DATE ||
380 in->f_base_format == SPVDX_F_BASE_FORMAT_TIME ||
381 in->f_base_format == SPVDX_F_BASE_FORMAT_DATE_TIME)
383 struct spvdx_date_time_format dtf = {
384 .dt_base_format = (in->f_base_format == SPVDX_F_BASE_FORMAT_DATE
385 ? SPVDX_DT_BASE_FORMAT_DATE
386 : in->f_base_format == SPVDX_F_BASE_FORMAT_TIME
387 ? SPVDX_DT_BASE_FORMAT_TIME
388 : SPVDX_DT_BASE_FORMAT_DATE_TIME),
389 .separator_chars = in->separator_chars,
390 .mdy_order = in->mdy_order,
391 .show_year = in->show_year,
392 .year_abbreviation = in->year_abbreviation,
393 .show_quarter = in->show_quarter,
394 .quarter_prefix = in->quarter_prefix,
395 .quarter_suffix = in->quarter_suffix,
396 .show_month = in->show_month,
397 .month_format = in->month_format,
398 .show_week = in->show_week,
399 .week_padding = in->week_padding,
400 .week_suffix = in->week_suffix,
401 .show_day_of_week = in->show_day_of_week,
402 .day_of_week_abbreviation = in->day_of_week_abbreviation,
403 .day_padding = in->day_padding,
404 .day_of_month_padding = in->day_of_month_padding,
405 .hour_padding = in->hour_padding,
406 .minute_padding = in->minute_padding,
407 .second_padding = in->second_padding,
408 .show_day = in->show_day,
409 .show_hour = in->show_hour,
410 .show_minute = in->show_minute,
411 .show_second = in->show_second,
412 .show_millis = in->show_millis,
413 .day_type = in->day_type,
414 .hour_format = in->hour_format,
416 return decode_date_time_format (&dtf);
418 else if (in->f_base_format == SPVDX_F_BASE_FORMAT_ELAPSED_TIME)
420 struct spvdx_elapsed_time_format etf = {
421 .dt_base_format = (in->f_base_format == SPVDX_F_BASE_FORMAT_DATE
422 ? SPVDX_DT_BASE_FORMAT_DATE
423 : in->f_base_format == SPVDX_F_BASE_FORMAT_TIME
424 ? SPVDX_DT_BASE_FORMAT_TIME
425 : SPVDX_DT_BASE_FORMAT_DATE_TIME),
426 .day_padding = in->day_padding,
427 .minute_padding = in->minute_padding,
428 .second_padding = in->second_padding,
429 .show_year = in->show_year,
430 .show_day = in->show_day,
431 .show_hour = in->show_hour,
432 .show_minute = in->show_minute,
433 .show_second = in->show_second,
434 .show_millis = in->show_millis,
436 return decode_elapsed_time_format (&etf);
440 assert (!in->f_base_format);
441 struct spvdx_number_format nf = {
442 .minimum_integer_digits = in->minimum_integer_digits,
443 .maximum_fraction_digits = in->maximum_fraction_digits,
444 .minimum_fraction_digits = in->minimum_fraction_digits,
445 .use_grouping = in->use_grouping,
446 .scientific = in->scientific,
448 .prefix = in->prefix,
449 .suffix = in->suffix,
451 return decode_number_format (&nf);
456 spv_series_execute_mapping (struct spv_series *series)
458 if (!hmap_is_empty (&series->map))
460 series->remapped = true;
461 for (size_t i = 0; i < series->n_values; i++)
463 struct spv_data_value *value = &series->values[i];
464 if (value->width >= 0)
467 const struct spv_mapping *mapping = spv_map_search (&series->map,
471 value->index = value->d;
472 assert (value->index == floor (value->index));
473 value->width = mapping->to.width;
474 if (value->width >= 0)
475 value->s = xmemdup0 (mapping->to.s, mapping->to.width);
477 value->d = mapping->to.d;
483 static char * WARN_UNUSED_RESULT
484 spv_series_remap_formats (struct spv_series *series,
485 struct spvxml_node **seq, size_t n_seq)
487 spv_map_destroy (&series->map);
488 hmap_init (&series->map);
489 for (size_t i = 0; i < n_seq; i++)
491 struct spvxml_node *node = seq[i];
492 if (spvdx_is_format (node))
494 struct spvdx_format *f = spvdx_cast_format (node);
495 series->format = decode_format (f);
496 char *error = spv_series_parse_relabels (
497 &series->map, f->relabel, f->n_relabel,
498 f->try_strings_as_numbers > 0, &series->format);
502 series->affixes = f->affix;
503 series->n_affixes = f->n_affix;
505 else if (spvdx_is_string_format (node))
507 struct spvdx_string_format *sf = spvdx_cast_string_format (node);
508 char *error = spv_series_parse_relabels (&series->map,
509 sf->relabel, sf->n_relabel,
514 series->affixes = sf->affix;
515 series->n_affixes = sf->n_affix;
520 spv_series_execute_mapping (series);
524 static char * WARN_UNUSED_RESULT
525 spv_series_remap_vmes (struct spv_series *series,
526 struct spvdx_value_map_entry **vmes,
529 spv_map_destroy (&series->map);
530 hmap_init (&series->map);
531 for (size_t i = 0; i < n_vmes; i++)
533 char *error = spv_series_parse_value_map_entry (&series->map, vmes[i]);
537 spv_series_execute_mapping (series);
542 decode_footnotes (struct pivot_table *table, const struct spvdx_footnotes *f)
544 if (f->n_footnote_mapping > 0)
545 pivot_table_create_footnote__ (table, f->n_footnote_mapping - 1,
547 for (size_t i = 0; i < f->n_footnote_mapping; i++)
549 const struct spvdx_footnote_mapping *fm = f->footnote_mapping[i];
550 pivot_table_create_footnote__ (table, fm->defines_reference - 1,
551 pivot_value_new_user_text (fm->to, -1),
556 static struct cell_color
557 optional_color (int color, struct cell_color default_color)
560 ? (struct cell_color) CELL_COLOR (color >> 16, color >> 8, color)
565 optional_length (const char *s, int default_length)
567 /* There is usually a "pt" suffix. We ignore it. */
569 return s && sscanf (s, "%d", &length) == 1 ? length : default_length;
573 optional_px (double inches, int default_px)
575 return inches != DBL_MAX ? inches * 96.0 : default_px;
579 decode_spvdx_style_incremental (const struct spvdx_style *in,
580 const struct spvdx_style *bg,
581 struct table_area_style *out)
583 if (in && in->font_weight)
584 out->font_style.bold = in->font_weight == SPVDX_FONT_WEIGHT_BOLD;
585 if (in && in->font_style)
586 out->font_style.italic = in->font_style == SPVDX_FONT_STYLE_ITALIC;
587 if (in && in->font_underline)
588 out->font_style.underline = in->font_underline == SPVDX_FONT_UNDERLINE_UNDERLINE;
589 if (in && in->color >= 0)
591 out->font_style.fg[0] = optional_color (
592 in->color, (struct cell_color) CELL_COLOR_BLACK);
593 out->font_style.fg[1] = out->font_style.fg[0];
595 if (bg && bg->color >= 0)
597 out->font_style.bg[0] = optional_color (
598 bg->color, (struct cell_color) CELL_COLOR_WHITE);
599 out->font_style.bg[1] = out->font_style.bg[0];
601 if (in && in->font_family)
603 free (out->font_style.typeface);
604 out->font_style.typeface = xstrdup (in->font_family);
606 if (in && in->font_size)
608 int size = optional_length (in->font_size, 0);
610 out->font_style.size = size;
612 if (in && in->text_alignment)
613 out->cell_style.halign
614 = (in->text_alignment == SPVDX_TEXT_ALIGNMENT_LEFT
616 : in->text_alignment == SPVDX_TEXT_ALIGNMENT_RIGHT
618 : in->text_alignment == SPVDX_TEXT_ALIGNMENT_CENTER
619 ? TABLE_HALIGN_CENTER
620 : in->text_alignment == SPVDX_TEXT_ALIGNMENT_DECIMAL
621 ? TABLE_HALIGN_DECIMAL
622 : TABLE_HALIGN_MIXED);
623 if (in && in->label_location_vertical)
624 out->cell_style.valign =
625 (in->label_location_vertical == SPVDX_LABEL_LOCATION_VERTICAL_NEGATIVE
626 ? TABLE_VALIGN_BOTTOM
627 : in->label_location_vertical == SPVDX_LABEL_LOCATION_VERTICAL_POSITIVE
629 : TABLE_VALIGN_CENTER);
630 if (in && in->decimal_offset != DBL_MAX)
631 out->cell_style.decimal_offset = optional_px (in->decimal_offset, 0);
633 if (in && in->margin_left != DBL_MAX)
634 out->cell_style.margin[TABLE_HORZ][0] = optional_pt (in->margin_left, 8);
635 if (in && in->margin_right != DBL_MAX)
636 out->cell_style.margin[TABLE_HORZ][1] = optional_pt (in->margin_right, 11);
637 if (in && in->margin_top != DBL_MAX)
638 out->cell_style.margin[TABLE_VERT][0] = optional_pt (in->margin_top, 1);
639 if (in && in->margin_bottom != DBL_MAX)
640 out->cell_style.margin[TABLE_VERT][1] = optional_pt (in->margin_bottom, 1);
645 decode_spvdx_style (const struct spvdx_style *in,
646 const struct spvdx_style *bg,
647 struct table_area_style *out)
649 *out = (struct table_area_style) TABLE_AREA_STYLE_INITIALIZER;
650 decode_spvdx_style_incremental (in, bg, out);
654 add_footnote (struct pivot_value *v, int idx, struct pivot_table *table)
656 if (idx < 1 || idx > table->n_footnotes)
659 pivot_value_add_footnote (v, table->footnotes[idx - 1]);
662 static char * WARN_UNUSED_RESULT
663 decode_label_frame (struct pivot_table *table,
664 const struct spvdx_label_frame *lf)
669 struct pivot_value **target;
670 struct table_area_style *area;
671 if (lf->label->purpose == SPVDX_PURPOSE_TITLE)
673 target = &table->title;
674 area = &table->areas[PIVOT_AREA_TITLE];
676 else if (lf->label->purpose == SPVDX_PURPOSE_SUB_TITLE)
678 target = &table->caption;
679 area = &table->areas[PIVOT_AREA_CAPTION];
681 else if (lf->label->purpose == SPVDX_PURPOSE_FOOTNOTE)
683 if (lf->label->n_text > 0
684 && lf->label->text[0]->uses_reference != INT_MIN)
687 area = &table->areas[PIVOT_AREA_FOOTER];
692 else if (lf->label->purpose == SPVDX_PURPOSE_LAYER)
695 area = &table->areas[PIVOT_AREA_LAYERS];
700 table_area_style_uninit (area);
701 decode_spvdx_style (lf->label->style, lf->label->text_frame_style, area);
705 struct pivot_value *value = xzalloc (sizeof *value);
706 value->type = PIVOT_VALUE_TEXT;
707 for (size_t i = 0; i < lf->label->n_text; i++)
709 const struct spvdx_text *in = lf->label->text[i];
710 if (in->defines_reference != INT_MIN)
711 add_footnote (value, in->defines_reference, table);
712 else if (!value->text.local)
713 value->text.local = xstrdup (in->text);
716 char *new = xasprintf ("%s%s", value->text.local, in->text);
717 free (value->text.local);
718 value->text.local = new;
721 pivot_value_destroy (*target);
725 for (size_t i = 0; i < lf->label->n_text; i++)
727 const struct spvdx_text *in = lf->label->text[i];
728 if (in->uses_reference == INT_MIN)
732 size_t length = strlen (in->text);
733 if (length && in->text[length - 1] == '\n')
736 pivot_table_create_footnote__ (
737 table, in->uses_reference - 1, NULL,
738 pivot_value_new_user_text (in->text, length));
742 size_t length = strlen (in->text);
743 if (length && in->text[length - 1] == '.')
746 pivot_table_create_footnote__ (
747 table, in->uses_reference - 1,
748 pivot_value_new_user_text (in->text, length), NULL);
754 /* Special return value for decode_spvdx_variable(). */
755 static char BAD_REFERENCE;
757 static char * WARN_UNUSED_RESULT
758 decode_spvdx_source_variable (const struct spvxml_node *node,
759 struct spv_data *data,
760 struct hmap *series_map)
762 const struct spvdx_source_variable *sv = spvdx_cast_source_variable (node);
764 struct spv_series *label_series = NULL;
765 if (sv->label_variable)
767 label_series = spv_series_find (series_map,
768 sv->label_variable->node_.id);
770 return &BAD_REFERENCE;
772 label_series->is_label_series = true;
775 const struct spv_data_variable *var = spv_data_find_variable (
776 data, sv->source, sv->source_name);
778 return xasprintf ("sourceVariable %s references nonexistent "
779 "source %s variable %s.",
780 sv->node_.id, sv->source, sv->source_name);
782 struct spv_series *s = xzalloc (sizeof *s);
783 s->name = xstrdup (node->id);
785 s->label = sv->label ? xstrdup (sv->label) : NULL;
786 s->label_series = label_series;
787 s->values = spv_data_values_clone (var->values, var->n_values);
788 s->n_values = var->n_values;
791 hmap_insert (series_map, &s->hmap_node, hash_string (s->name, 0));
793 char *error = spv_series_remap_formats (s, sv->seq, sv->n_seq);
797 if (label_series && !s->remapped)
799 for (size_t i = 0; i < s->n_values; i++)
800 if (s->values[i].width < 0)
803 if (label_series->values[i].width < 0)
805 union value v = { .f = label_series->values[i].d };
806 dest = data_out_stretchy (&v, "UTF-8", &s->format, NULL);
809 dest = label_series->values[i].s;
810 char *error = spv_map_insert (&s->map, s->values[i].d,
812 free (error); /* Duplicates are OK. */
813 if (label_series->values[i].width < 0)
821 static char * WARN_UNUSED_RESULT
822 decode_spvdx_derived_variable (const struct spvxml_node *node,
823 struct hmap *series_map)
825 const struct spvdx_derived_variable *dv = spvdx_cast_derived_variable (node);
827 struct spv_data_value *values;
830 struct substring value = ss_cstr (dv->value);
831 if (ss_equals (value, ss_cstr ("constant(0)")))
833 struct spv_series *existing_series = spv_series_first (series_map);
834 if (!existing_series)
835 return &BAD_REFERENCE;
837 n_values = existing_series->n_values;
838 values = XCALLOC (n_values, struct spv_data_value);
839 for (size_t i = 0; i < n_values; i++)
840 values[i].width = -1;
842 else if (ss_starts_with (value, ss_cstr ("constant(")))
847 else if (ss_starts_with (value, ss_cstr ("map("))
848 && ss_ends_with (value, ss_cstr (")")))
850 char *dependency_name = ss_xstrdup (ss_substr (value, 4,
852 struct spv_series *dependency
853 = spv_series_find (series_map, dependency_name);
854 free (dependency_name);
856 return &BAD_REFERENCE;
858 values = spv_data_values_clone (dependency->values,
859 dependency->n_values);
860 n_values = dependency->n_values;
863 return xasprintf ("Derived variable %s has unknown value \"%s\"",
864 node->id, dv->value);
866 struct spv_series *s = xzalloc (sizeof *s);
868 s->name = xstrdup (node->id);
870 s->n_values = n_values;
872 hmap_insert (series_map, &s->hmap_node, hash_string (s->name, 0));
874 char *error = spv_series_remap_vmes (s, dv->value_map_entry,
875 dv->n_value_map_entry);
879 error = spv_series_remap_formats (s, dv->seq, dv->n_seq);
885 for (size_t i = 0; i < n_values; i++)
886 if (values[i].width != 0)
888 for (size_t i = 0; i < n_values; i++)
889 spv_data_value_uninit (&s->values[i]);
900 struct format_mapping
902 struct hmap_node hmap_node;
907 static const struct format_mapping *
908 format_map_find (const struct hmap *format_map, uint32_t u32_format)
912 const struct format_mapping *fm;
913 HMAP_FOR_EACH_IN_BUCKET (fm, struct format_mapping, hmap_node,
914 hash_int (u32_format, 0), format_map)
915 if (fm->from == u32_format)
922 static char * WARN_UNUSED_RESULT
923 spv_format_from_data_value (const struct spv_data_value *data,
924 const struct hmap *format_map,
925 struct fmt_spec *out)
929 *out = fmt_for_output (FMT_F, 40, 2);
933 uint32_t u32_format = data->width < 0 ? data->d : atoi (data->s);
934 const struct format_mapping *fm = format_map_find (format_map, u32_format);
940 return spv_decode_fmt_spec (u32_format, out);
943 static char * WARN_UNUSED_RESULT
944 pivot_value_from_data_value (const struct spv_data_value *data,
945 const struct spv_data_value *format,
946 const struct hmap *format_map,
947 struct pivot_value **vp)
952 char *error = spv_format_from_data_value (format, format_map, &f);
956 struct pivot_value *v = xzalloc (sizeof *v);
957 if (data->width >= 0)
959 if (format && fmt_get_category (f.type) == FMT_CAT_DATE)
961 int year, month, day, hour, minute, second, msec, len = -1;
962 if (sscanf (data->s, "%4d-%2d-%2dT%2d:%2d:%2d.%3d%n",
963 &year, &month, &day, &hour, &minute, &second,
966 && data->s[len] == '\0')
968 double date = calendar_gregorian_to_offset (year, month, day,
972 v->type = PIVOT_VALUE_NUMERIC;
973 v->numeric.x = (date * 60. * 60. * 24.
978 v->numeric.format = f;
984 else if (format && fmt_get_category (f.type) == FMT_CAT_TIME)
986 int hour, minute, second, msec, len = -1;
987 if (sscanf (data->s, "%d:%2d:%2d.%3d%n",
988 &hour, &minute, &second, &msec, &len) == 4
990 && data->s[len] == '\0')
992 v->type = PIVOT_VALUE_NUMERIC;
993 v->numeric.x = (hour * 60. * 60.
997 v->numeric.format = f;
1002 v->type = PIVOT_VALUE_STRING;
1003 v->string.s = xstrdup (data->s);
1007 v->type = PIVOT_VALUE_NUMERIC;
1008 v->numeric.x = data->d;
1009 v->numeric.format = f;
1016 add_parents (struct pivot_category *cat, struct pivot_category *parent,
1019 cat->parent = parent;
1020 cat->group_index = group_index;
1021 if (pivot_category_is_group (cat))
1022 for (size_t i = 0; i < cat->n_subs; i++)
1023 add_parents (cat->subs[i], cat, i);
1026 static const struct spvdx_facet_level *
1027 find_facet_level (const struct spvdx_visualization *v, int facet_level)
1029 const struct spvdx_facet_layout *layout = v->graph->facet_layout;
1030 for (size_t i = 0; i < layout->n_facet_level; i++)
1032 const struct spvdx_facet_level *fl = layout->facet_level[i];
1033 if (facet_level == fl->level)
1040 should_show_label (const struct spvdx_facet_level *fl)
1042 return fl && fl->axis->label && fl->axis->label->style->visible != 0;
1046 max_category (const struct spv_series *s)
1048 double max_cat = -DBL_MAX;
1049 for (size_t i = 0; i < s->n_values; i++)
1051 const struct spv_data_value *dv = &s->values[i];
1052 double d = dv->width < 0 ? dv->d : dv->index;
1056 assert (max_cat >= 0 && max_cat < SIZE_MAX - 1);
1062 add_affixes (struct pivot_table *table, struct pivot_value *value,
1063 struct spvdx_affix **affixes, size_t n_affixes)
1065 for (size_t i = 0; i < n_affixes; i++)
1066 add_footnote (value, affixes[i]->defines_reference, table);
1069 static char * WARN_UNUSED_RESULT
1070 add_dimension (struct spv_series **series, size_t n,
1071 enum pivot_axis_type axis_type,
1072 const struct spvdx_visualization *v, struct pivot_table *table,
1073 struct spv_series **dim_seriesp, size_t *n_dim_seriesp,
1074 int base_facet_level, struct pivot_dimension **dp)
1078 const struct spvdx_facet_level *fl
1079 = find_facet_level (v, base_facet_level + n);
1082 struct table_area_style *area = (axis_type == PIVOT_AXIS_COLUMN
1083 ? &table->areas[PIVOT_AREA_COLUMN_LABELS]
1084 : axis_type == PIVOT_AXIS_ROW
1085 ? &table->areas[PIVOT_AREA_ROW_LABELS]
1087 if (area && fl->axis->label)
1089 table_area_style_uninit (area);
1090 decode_spvdx_style (fl->axis->label->style,
1091 fl->axis->label->text_frame_style, area);
1095 if (axis_type == PIVOT_AXIS_ROW)
1097 const struct spvdx_facet_level *fl2
1098 = find_facet_level (v, base_facet_level + (n - 1));
1100 decode_spvdx_style_incremental (
1101 fl2->axis->major_ticks->style,
1102 fl2->axis->major_ticks->tick_frame_style,
1103 &table->areas[PIVOT_AREA_ROW_LABELS]);
1106 const struct spvdx_facet_level *fl3 = find_facet_level (v, base_facet_level);
1107 if (fl3 && fl3->axis->major_ticks->label_angle == -90)
1109 if (axis_type == PIVOT_AXIS_COLUMN)
1110 table->rotate_inner_column_labels = true;
1112 table->rotate_outer_row_labels = true;
1115 /* Find the first row for each category. */
1116 size_t max_cat = max_category (series[0]);
1117 size_t *cat_rows = xnmalloc (max_cat + 1, sizeof *cat_rows);
1118 for (size_t k = 0; k <= max_cat; k++)
1119 cat_rows[k] = SIZE_MAX;
1120 for (size_t k = 0; k < series[0]->n_values; k++)
1122 const struct spv_data_value *dv = &series[0]->values[k];
1123 double d = dv->width < 0 ? dv->d : dv->index;
1124 if (d >= 0 && d < SIZE_MAX - 1)
1127 if (cat_rows[row] == SIZE_MAX)
1132 /* Drop missing categories and count what's left. */
1134 for (size_t k = 0; k <= max_cat; k++)
1135 if (cat_rows[k] != SIZE_MAX)
1136 cat_rows[n_cats++] = cat_rows[k];
1137 assert (n_cats > 0);
1139 /* Make the categories. */
1140 struct pivot_dimension *d = xzalloc (sizeof *d);
1141 table->dimensions[table->n_dimensions++] = d;
1143 series[0]->n_index = max_cat + 1;
1144 series[0]->index_to_category = xcalloc (
1145 max_cat + 1, sizeof *series[0]->index_to_category);
1146 struct pivot_category **cats = xnmalloc (n_cats, sizeof **cats);
1147 for (size_t k = 0; k < n_cats; k++)
1149 struct spv_data_value *dv = &series[0]->values[cat_rows[k]];
1150 int dv_num = dv ? dv->d : dv->index;
1151 struct pivot_category *cat = xzalloc (sizeof *cat);
1152 char *retval = pivot_value_from_data_value (
1153 spv_map_lookup (&series[0]->map, dv), NULL, NULL, &cat->name);
1163 cat->data_index = k;
1164 cat->presentation_index = cat_rows[k];
1166 series[0]->index_to_category[dv_num] = cat;
1169 add_affixes (table, cat->name,
1170 series[0]->affixes, series[0]->n_affixes);
1174 struct pivot_axis *axis = &table->axes[axis_type];
1175 d->axis_type = axis_type;
1176 d->level = axis->n_dimensions;
1177 d->top_index = table->n_dimensions - 1;
1178 d->root = xzalloc (sizeof *d->root);
1179 *d->root = (struct pivot_category) {
1180 .name = pivot_value_new_user_text (
1181 series[0]->label ? series[0]->label : "", -1),
1183 .show_label = should_show_label (fl),
1184 .data_index = SIZE_MAX,
1185 .presentation_index = SIZE_MAX,
1187 d->data_leaves = xmemdup (cats, n_cats * sizeof *cats);
1188 d->presentation_leaves = xmemdup (cats, n_cats * sizeof *cats);
1189 d->n_leaves = d->allocated_leaves = n_cats;
1191 /* Now group them, in one pass per grouping variable, innermost first. */
1192 for (size_t j = 1; j < n; j++)
1194 struct pivot_category **new_cats = xnmalloc (n_cats, sizeof **cats);
1195 size_t n_new_cats = 0;
1197 /* Allocate a category index. */
1198 size_t max_cat = max_category (series[j]);
1199 series[j]->n_index = max_cat + 1;
1200 series[j]->index_to_category = xcalloc (
1201 max_cat + 1, sizeof *series[j]->index_to_category);
1202 for (size_t cat1 = 0; cat1 < n_cats;)
1204 /* Find a sequence of categories cat1...cat2 (exclusive), that all
1205 have the same value in series 'j'. (This might be only a single
1206 category; we will drop unnamed 1-category groups later.) */
1207 size_t row1 = cats[cat1]->presentation_index;
1208 const struct spv_data_value *dv1 = &series[j]->values[row1];
1210 for (cat2 = cat1 + 1; cat2 < n_cats; cat2++)
1212 size_t row2 = cats[cat2]->presentation_index;
1213 const struct spv_data_value *dv2 = &series[j]->values[row2];
1214 if (!spv_data_value_equal (dv1, dv2))
1217 size_t n_subs = cat2 - cat1;
1219 struct pivot_category *new_cat;
1220 const struct spv_data_value *name
1221 = spv_map_lookup (&series[j]->map, dv1);
1222 if (n_subs == 1 && name->width == 0)
1224 /* The existing category stands on its own. */
1225 new_cat = cats[cat1++];
1229 /* Create a new group with cat...cat2 as subcategories. */
1230 new_cat = xzalloc (sizeof *new_cat);
1231 *new_cat = (struct pivot_category) {
1233 .subs = xnmalloc (n_subs, sizeof *new_cat->subs),
1236 .data_index = SIZE_MAX,
1237 .presentation_index = row1,
1239 char *retval = pivot_value_from_data_value (name, NULL, NULL,
1248 for (size_t k = 0; k < n_subs; k++)
1249 new_cat->subs[k] = cats[cat1++];
1251 int dv1_num = dv1->width < 0 ? dv1->d : dv1->index;
1252 series[j]->index_to_category[dv1_num] = new_cat;
1256 add_affixes (table, new_cat->name,
1257 series[j]->affixes, series[j]->n_affixes);
1259 /* Append the new group to the list of new groups. */
1260 new_cats[n_new_cats++] = new_cat;
1265 n_cats = n_new_cats;
1268 /* Now drop unnamed 1-category groups and add parent pointers. */
1269 for (size_t j = 0; j < n_cats; j++)
1270 add_parents (cats[j], d->root, j);
1272 d->root->subs = cats;
1273 d->root->n_subs = n_cats;
1277 pivot_dimension_destroy (d);
1281 dim_seriesp[(*n_dim_seriesp)++] = series[0];
1282 series[0]->dimension = d;
1284 axis->dimensions = xnrealloc (axis->dimensions, axis->n_dimensions + 1,
1285 sizeof *axis->dimensions);
1286 axis->dimensions[axis->n_dimensions++] = d;
1287 axis->extent *= d->n_leaves;
1293 static char * WARN_UNUSED_RESULT
1294 add_dimensions (struct hmap *series_map, const struct spvdx_nest *nest,
1295 enum pivot_axis_type axis_type,
1296 const struct spvdx_visualization *v, struct pivot_table *table,
1297 struct spv_series **dim_seriesp, size_t *n_dim_seriesp,
1300 struct pivot_axis *axis = &table->axes[axis_type];
1307 struct spv_series **series = xnmalloc (nest->n_vars, sizeof *series);
1308 for (size_t i = 0; i < nest->n_vars;)
1311 for (n = 0; i + n < nest->n_vars; n++)
1313 series[n] = spv_series_from_ref (series_map, nest->vars[i + n]->ref);
1314 if (!series[n] || !series[n]->n_values)
1320 struct pivot_dimension *d;
1321 char *error = add_dimension (series, n, axis_type, v, table,
1322 dim_seriesp, n_dim_seriesp,
1338 static char * WARN_UNUSED_RESULT
1339 add_layers (struct hmap *series_map,
1340 struct spvdx_layer **layers, size_t n_layers,
1341 const struct spvdx_visualization *v, struct pivot_table *table,
1342 struct spv_series **dim_seriesp, size_t *n_dim_seriesp,
1345 struct pivot_axis *axis = &table->axes[PIVOT_AXIS_LAYER];
1352 struct spv_series **series = xnmalloc (n_layers, sizeof *series);
1353 for (size_t i = 0; i < n_layers;)
1356 for (n = 0; i + n < n_layers; n++)
1358 series[n] = spv_series_from_ref (series_map,
1359 layers[i + n]->variable);
1360 if (!series[n] || !series[n]->n_values)
1366 struct pivot_dimension *d;
1367 char *error = add_dimension (
1368 series, n, PIVOT_AXIS_LAYER, v, table,
1369 dim_seriesp, n_dim_seriesp, level_ofs + i, &d);
1376 int index = atoi (layers[i]->value);
1377 assert (index < d->n_leaves);
1378 table->current_layer = xrealloc (
1379 table->current_layer,
1380 axis->n_dimensions * sizeof *table->current_layer);
1381 table->current_layer[axis->n_dimensions - 1] = index;
1390 static struct pivot_category *
1391 find_category (struct spv_series *series, int index)
1393 return (index >= 0 && index < series->n_index
1394 ? series->index_to_category[index]
1399 int_in_array (int value, const int *array, size_t n)
1401 for (size_t i = 0; i < n; i++)
1402 if (array[i] == value)
1409 apply_styles_to_value (struct pivot_table *table,
1410 struct pivot_value *value,
1411 const struct spvdx_set_format *sf,
1412 const struct table_area_style *base_area_style,
1413 const struct spvdx_style *fg,
1414 const struct spvdx_style *bg)
1420 free (value->footnotes);
1421 value->footnotes = NULL;
1422 value->n_footnotes = 0;
1425 struct fmt_spec format = { .w = 0 };
1428 format = decode_format (sf->format);
1429 add_affixes (table, value, sf->format->affix, sf->format->n_affix);
1431 else if (sf->number_format)
1433 format = decode_number_format (sf->number_format);
1434 add_affixes (table, value, sf->number_format->affix,
1435 sf->number_format->n_affix);
1437 else if (sf->n_string_format)
1439 for (size_t i = 0; i < sf->n_string_format; i++)
1440 add_affixes (table, value, sf->string_format[i]->affix,
1441 sf->string_format[i]->n_affix);
1443 else if (sf->date_time_format)
1445 format = decode_date_time_format (sf->date_time_format);
1446 add_affixes (table, value, sf->date_time_format->affix,
1447 sf->date_time_format->n_affix);
1449 else if (sf->elapsed_time_format)
1451 format = decode_elapsed_time_format (sf->elapsed_time_format);
1452 add_affixes (table, value, sf->elapsed_time_format->affix,
1453 sf->elapsed_time_format->n_affix);
1458 if (value->type == PIVOT_VALUE_NUMERIC)
1459 value->numeric.format = format;
1461 /* Possibly we should try to apply date and time formats too,
1462 but none seem to occur in practice so far. */
1467 struct table_area_style area;
1468 pivot_value_get_style (
1470 value->font_style ? value->font_style : &base_area_style->font_style,
1471 value->cell_style ? value->cell_style : &base_area_style->cell_style,
1473 decode_spvdx_style_incremental (fg, bg, &area);
1474 pivot_value_set_style (value, &area);
1475 table_area_style_uninit (&area);
1480 decode_set_cell_properties__ (struct pivot_table *table,
1481 struct hmap *series_map,
1482 const struct spvdx_intersect *intersect,
1483 const struct spvdx_style *interval,
1484 const struct spvdx_style *graph,
1485 const struct spvdx_style *labeling,
1486 const struct spvdx_style *frame,
1487 const struct spvdx_style *major_ticks,
1488 const struct spvdx_set_format *set_format)
1490 if (graph && labeling && intersect->alternating
1491 && !interval && !major_ticks && !frame && !set_format)
1493 /* Sets alt_fg_color and alt_bg_color. */
1494 struct table_area_style area;
1495 decode_spvdx_style (labeling, graph, &area);
1496 table->areas[PIVOT_AREA_DATA].font_style.fg[1]
1497 = area.font_style.fg[0];
1498 table->areas[PIVOT_AREA_DATA].font_style.bg[1]
1499 = area.font_style.bg[0];
1500 table_area_style_uninit (&area);
1503 && !labeling && !interval && !major_ticks && !frame && !set_format)
1505 /* 'graph->width' likely just sets the width of the table as a
1508 else if (!graph && !labeling && !interval && !frame && !set_format
1511 /* No-op. (Presumably there's a setMetaData we don't care about.) */
1513 else if (((set_format && spvdx_is_major_ticks (set_format->target))
1514 || major_ticks || frame)
1515 && intersect->n_where == 1)
1517 /* Formatting for individual row or column labels. */
1518 const struct spvdx_where *w = intersect->where[0];
1519 struct spv_series *s = spv_series_find (series_map, w->variable->id);
1522 const char *p = w->include;
1527 int include = strtol (p, &tail, 10);
1529 struct pivot_category *c = find_category (s, include);
1532 const struct table_area_style *base_area_style
1533 = (c->dimension->axis_type == PIVOT_AXIS_ROW
1534 ? &table->areas[PIVOT_AREA_ROW_LABELS]
1535 : &table->areas[PIVOT_AREA_COLUMN_LABELS]);
1536 apply_styles_to_value (table, c->name, set_format,
1537 base_area_style, major_ticks, frame);
1547 else if ((set_format && spvdx_is_labeling (set_format->target))
1548 || labeling || interval)
1550 /* Formatting for individual cells or groups of them with some dimensions
1552 int **indexes = XCALLOC (table->n_dimensions, int *);
1553 size_t *n = XCALLOC (table->n_dimensions, size_t);
1554 size_t *allocated = XCALLOC (table->n_dimensions, size_t);
1556 for (size_t i = 0; i < intersect->n_where; i++)
1558 const struct spvdx_where *w = intersect->where[i];
1559 struct spv_series *s = spv_series_find (series_map, w->variable->id);
1563 /* Group indexes may be included even though they are redundant.
1568 size_t j = s->dimension->top_index;
1570 const char *p = w->include;
1574 int include = strtol (p, &tail, 10);
1576 struct pivot_category *c = find_category (s, include);
1579 if (n[j] >= allocated[j])
1580 indexes[j] = x2nrealloc (indexes[j], &allocated[j],
1581 sizeof *indexes[j]);
1582 indexes[j][n[j]++] = c->data_index;
1595 for (size_t i = 0; i < table->n_dimensions; i++)
1599 printf (" %d=(", i);
1600 for (size_t j = 0; j < n[i]; j++)
1604 printf ("%d", indexes[i][j]);
1612 /* XXX This is inefficient in the common case where all of the dimensions
1613 are matched. We should use a heuristic where if all of the dimensions
1614 are matched and the product of n[*] is less than
1615 hmap_count(&table->cells) then iterate through all the possibilities
1616 rather than all the cells. Or even only do it if there is just one
1619 struct pivot_cell *cell;
1620 HMAP_FOR_EACH (cell, struct pivot_cell, hmap_node, &table->cells)
1622 for (size_t i = 0; i < table->n_dimensions; i++)
1624 if (n[i] && !int_in_array (cell->idx[i], indexes[i], n[i]))
1627 apply_styles_to_value (table, cell->value, set_format,
1628 &table->areas[PIVOT_AREA_DATA],
1629 labeling, interval);
1634 for (size_t i = 0; i < table->n_dimensions; i++)
1645 decode_set_cell_properties (struct pivot_table *table, struct hmap *series_map,
1646 struct spvdx_set_cell_properties **scps,
1649 for (size_t i = 0; i < n_scps; i++)
1651 const struct spvdx_set_cell_properties *scp = scps[i];
1652 const struct spvdx_style *interval = NULL;
1653 const struct spvdx_style *graph = NULL;
1654 const struct spvdx_style *labeling = NULL;
1655 const struct spvdx_style *frame = NULL;
1656 const struct spvdx_style *major_ticks = NULL;
1657 const struct spvdx_set_format *set_format = NULL;
1658 for (size_t j = 0; j < scp->n_seq; j++)
1660 const struct spvxml_node *node = scp->seq[j];
1661 if (spvdx_is_set_style (node))
1663 const struct spvdx_set_style *set_style
1664 = spvdx_cast_set_style (node);
1665 if (spvdx_is_graph (set_style->target))
1666 graph = set_style->style;
1667 else if (spvdx_is_labeling (set_style->target))
1668 labeling = set_style->style;
1669 else if (spvdx_is_interval (set_style->target))
1670 interval = set_style->style;
1671 else if (spvdx_is_major_ticks (set_style->target))
1672 major_ticks = set_style->style;
1676 else if (spvdx_is_set_frame_style (node))
1677 frame = spvdx_cast_set_frame_style (node)->style;
1678 else if (spvdx_is_set_format (node))
1679 set_format = spvdx_cast_set_format (node);
1681 assert (spvdx_is_set_meta_data (node));
1684 if (scp->union_ && scp->apply_to_converse <= 0)
1686 for (size_t j = 0; j < scp->union_->n_intersect; j++)
1687 decode_set_cell_properties__ (
1688 table, series_map, scp->union_->intersect[j],
1689 interval, graph, labeling, frame, major_ticks, set_format);
1691 else if (!scp->union_ && scp->apply_to_converse > 0)
1693 if ((set_format && spvdx_is_labeling (set_format->target))
1694 || labeling || interval)
1696 struct pivot_cell *cell;
1697 HMAP_FOR_EACH (cell, struct pivot_cell, hmap_node, &table->cells)
1698 apply_styles_to_value (table, cell->value, set_format,
1699 &table->areas[PIVOT_AREA_DATA],
1703 else if (!scp->union_ && scp->apply_to_converse <= 0)
1705 /* Appears to be used to set the font for something--but what? */
1712 static struct spv_series *
1713 parse_formatting (const struct spvdx_visualization *v,
1714 const struct hmap *series_map, struct hmap *format_map)
1716 const struct spvdx_labeling *labeling = v->graph->interval->labeling;
1717 struct spv_series *cell_format = NULL;
1718 for (size_t i = 0; i < labeling->n_seq; i++)
1720 const struct spvdx_formatting *f
1721 = spvdx_cast_formatting (labeling->seq[i]);
1725 cell_format = spv_series_from_ref (series_map, f->variable);
1726 for (size_t j = 0; j < f->n_format_mapping; j++)
1728 const struct spvdx_format_mapping *fm = f->format_mapping[j];
1732 struct format_mapping *out = xmalloc (sizeof *out);
1733 out->from = fm->from;
1734 out->to = decode_format (fm->format);
1735 hmap_insert (format_map, &out->hmap_node,
1736 hash_int (out->from, 0));
1745 format_map_destroy (struct hmap *format_map)
1747 struct format_mapping *fm, *next;
1748 HMAP_FOR_EACH_SAFE (fm, next, struct format_mapping, hmap_node, format_map)
1750 hmap_delete (format_map, &fm->hmap_node);
1753 hmap_destroy (format_map);
1756 char * WARN_UNUSED_RESULT
1757 decode_spvdx_table (const struct spvdx_visualization *v, const char *subtype,
1758 const struct spv_table_look *look,
1759 struct spv_data *data, struct pivot_table **outp)
1761 struct pivot_table *table = pivot_table_create__ (NULL, subtype);
1762 spv_table_look_install (look, table);
1764 struct hmap series_map = HMAP_INITIALIZER (series_map);
1765 struct hmap format_map = HMAP_INITIALIZER (format_map);
1766 struct spv_series **dim_series = NULL;
1769 struct spvdx_visualization_extension *ve = v->visualization_extension;
1770 table->show_grid_lines = ve && ve->show_gridline;
1772 /* Sizing from the legacy properties can get overridden. */
1773 if (v->graph->cell_style->width)
1775 int min_width, max_width, n = 0;
1776 if (sscanf (v->graph->cell_style->width, "%*d%%;%dpt;%dpt%n",
1777 &min_width, &max_width, &n)
1778 && v->graph->cell_style->width[n] == '\0')
1780 table->sizing[TABLE_HORZ].range[0] = min_width;
1781 table->sizing[TABLE_HORZ].range[1] = max_width;
1787 Any pivot_value might refer to footnotes, so it's important to process the
1788 footnotes early to ensure that those references can be resolved. There is
1789 a possible problem that a footnote might itself reference an
1790 as-yet-unprocessed footnote, but that's OK because footnote references
1791 don't actually look at the footnote contents but only resolve a pointer to
1792 where the footnote will go later.
1794 Before we really start, create all the footnotes we'll fill in. This is
1795 because sometimes footnotes refer to themselves or to each other and we
1796 don't want to reject those references. */
1798 for (size_t i = 0; i < v->container->n_label_frame; i++)
1800 const struct spvdx_label_frame *lf = v->container->label_frame[i];
1802 && lf->label->purpose == SPVDX_PURPOSE_FOOTNOTE
1803 && lf->label->n_text > 0
1804 && lf->label->text[0]->uses_reference > 0)
1806 pivot_table_create_footnote__ (
1807 table, lf->label->text[0]->uses_reference - 1,
1812 if (v->graph->interval->footnotes)
1813 decode_footnotes (table, v->graph->interval->footnotes);
1815 struct spv_series *footnotes = NULL;
1816 for (size_t i = 0; i < v->graph->interval->labeling->n_seq; i++)
1818 const struct spvxml_node *node = v->graph->interval->labeling->seq[i];
1819 if (spvdx_is_footnotes (node))
1821 const struct spvdx_footnotes *f = spvdx_cast_footnotes (node);
1822 footnotes = spv_series_from_ref (&series_map, f->variable);
1823 decode_footnotes (table, f);
1826 for (size_t i = 0; i < v->n_lf1; i++)
1828 error = decode_label_frame (table, v->lf1[i]);
1832 for (size_t i = 0; i < v->n_lf2; i++)
1834 error = decode_label_frame (table, v->lf2[i]);
1839 for (size_t i = 0; i < v->container->n_label_frame; i++)
1841 error = decode_label_frame (table, v->container->label_frame[i]);
1845 if (v->graph->interval->labeling->style)
1847 table_area_style_uninit (&table->areas[PIVOT_AREA_DATA]);
1848 decode_spvdx_style (v->graph->interval->labeling->style,
1849 v->graph->cell_style,
1850 &table->areas[PIVOT_AREA_DATA]);
1853 /* Decode all of the sourceVariable and derivedVariable */
1854 struct spvxml_node **nodes = xmemdup (v->seq, v->n_seq * sizeof *v->seq);
1855 size_t n_nodes = v->n_seq;
1858 bool progress = false;
1859 for (size_t i = 0; i < n_nodes;)
1861 error = (spvdx_is_source_variable (nodes[i])
1862 ? decode_spvdx_source_variable (nodes[i], data, &series_map)
1863 : decode_spvdx_derived_variable (nodes[i], &series_map));
1866 nodes[i] = nodes[--n_nodes];
1869 else if (error == &BAD_REFERENCE)
1881 error = xasprintf ("Table has %zu variables with circular or "
1882 "unresolved references, including variable %s.",
1883 n_nodes, nodes[0]->id);
1889 const struct spvdx_cross *cross = v->graph->faceting->cross;
1891 assert (cross->n_seq == 1);
1892 const struct spvdx_nest *columns = spvdx_cast_nest (cross->seq[0]);
1893 size_t max_columns = columns ? columns->n_vars : 0;
1895 assert (cross->n_seq2 == 1);
1896 const struct spvdx_nest *rows = spvdx_cast_nest (cross->seq2[0]);
1897 size_t max_rows = rows ? rows->n_vars : 0;
1899 size_t max_layers = (v->graph->faceting->n_layers1
1900 + v->graph->faceting->n_layers2);
1902 size_t max_dims = max_columns + max_rows + max_layers;
1903 table->dimensions = xnmalloc (max_dims, sizeof *table->dimensions);
1904 dim_series = xnmalloc (max_dims, sizeof *dim_series);
1905 size_t n_dim_series = 0;
1907 error = add_dimensions (&series_map, columns, PIVOT_AXIS_COLUMN, v, table,
1908 dim_series, &n_dim_series, 1);
1912 error = add_dimensions (&series_map, rows, PIVOT_AXIS_ROW, v, table,
1913 dim_series, &n_dim_series, max_columns + 1);
1917 error = add_layers (&series_map, v->graph->faceting->layers1,
1918 v->graph->faceting->n_layers1,
1919 v, table, dim_series, &n_dim_series,
1920 max_rows + max_columns + 1);
1924 error = add_layers (&series_map, v->graph->faceting->layers2,
1925 v->graph->faceting->n_layers2,
1926 v, table, dim_series, &n_dim_series,
1927 (max_rows + max_columns + v->graph->faceting->n_layers1
1932 struct spv_series *cell = spv_series_find (&series_map, "cell");
1935 error = xstrdup (_("Table lacks cell data."));
1939 struct spv_series *cell_format = parse_formatting (v, &series_map,
1942 assert (table->n_dimensions == n_dim_series);
1943 size_t *dim_indexes = xnmalloc (table->n_dimensions, sizeof *dim_indexes);
1944 for (size_t i = 0; i < cell->n_values; i++)
1946 for (size_t j = 0; j < table->n_dimensions; j++)
1948 const struct spv_data_value *value = &dim_series[j]->values[i];
1949 const struct pivot_category *cat = find_category (
1950 dim_series[j], value->width < 0 ? value->d : value->index);
1953 dim_indexes[j] = cat->data_index;
1956 struct pivot_value *value;
1957 error = pivot_value_from_data_value (
1958 &cell->values[i], cell_format ? &cell_format->values[i] : NULL,
1959 &format_map, &value);
1965 const struct spv_data_value *d = &footnotes->values[i];
1968 const char *p = d->s;
1972 int idx = strtol (p, &tail, 10);
1973 add_footnote (value, idx, table);
1983 if (value->type == PIVOT_VALUE_NUMERIC
1984 && value->numeric.x == SYSMIS
1985 && !value->n_footnotes)
1987 /* Apparently, system-missing values are just empty cells? */
1988 pivot_value_destroy (value);
1991 pivot_table_put (table, dim_indexes, table->n_dimensions, value);
1996 decode_set_cell_properties (table, &series_map, v->graph->facet_layout->scp1,
1997 v->graph->facet_layout->n_scp1);
1998 decode_set_cell_properties (table, &series_map, v->graph->facet_layout->scp2,
1999 v->graph->facet_layout->n_scp2);
2001 pivot_table_assign_label_depth (table);
2003 format_map_destroy (&format_map);
2007 spv_series_destroy (&series_map);
2010 pivot_table_unref (table);