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->look->areas[PIVOT_AREA_TITLE];
676 else if (lf->label->purpose == SPVDX_PURPOSE_SUB_TITLE)
678 target = &table->caption;
679 area = &table->look->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->look->areas[PIVOT_AREA_FOOTER];
692 else if (lf->label->purpose == SPVDX_PURPOSE_LAYER)
695 area = &table->look->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
1083 = (axis_type == PIVOT_AXIS_COLUMN
1084 ? &table->look->areas[PIVOT_AREA_COLUMN_LABELS]
1085 : axis_type == PIVOT_AXIS_ROW
1086 ? &table->look->areas[PIVOT_AREA_ROW_LABELS]
1088 if (area && fl->axis->label)
1090 table_area_style_uninit (area);
1091 decode_spvdx_style (fl->axis->label->style,
1092 fl->axis->label->text_frame_style, area);
1096 if (axis_type == PIVOT_AXIS_ROW)
1098 const struct spvdx_facet_level *fl2
1099 = find_facet_level (v, base_facet_level + (n - 1));
1101 decode_spvdx_style_incremental (
1102 fl2->axis->major_ticks->style,
1103 fl2->axis->major_ticks->tick_frame_style,
1104 &table->look->areas[PIVOT_AREA_ROW_LABELS]);
1107 const struct spvdx_facet_level *fl3 = find_facet_level (v, base_facet_level);
1108 if (fl3 && fl3->axis->major_ticks->label_angle == -90)
1110 if (axis_type == PIVOT_AXIS_COLUMN)
1111 table->rotate_inner_column_labels = true;
1113 table->rotate_outer_row_labels = true;
1116 /* Find the first row for each category. */
1117 size_t max_cat = max_category (series[0]);
1118 size_t *cat_rows = xnmalloc (max_cat + 1, sizeof *cat_rows);
1119 for (size_t k = 0; k <= max_cat; k++)
1120 cat_rows[k] = SIZE_MAX;
1121 for (size_t k = 0; k < series[0]->n_values; k++)
1123 const struct spv_data_value *dv = &series[0]->values[k];
1124 double d = dv->width < 0 ? dv->d : dv->index;
1125 if (d >= 0 && d < SIZE_MAX - 1)
1128 if (cat_rows[row] == SIZE_MAX)
1133 /* Drop missing categories and count what's left. */
1135 for (size_t k = 0; k <= max_cat; k++)
1136 if (cat_rows[k] != SIZE_MAX)
1137 cat_rows[n_cats++] = cat_rows[k];
1138 assert (n_cats > 0);
1140 /* Make the categories. */
1141 struct pivot_dimension *d = xzalloc (sizeof *d);
1142 table->dimensions[table->n_dimensions++] = d;
1144 series[0]->n_index = max_cat + 1;
1145 series[0]->index_to_category = xcalloc (
1146 max_cat + 1, sizeof *series[0]->index_to_category);
1147 struct pivot_category **cats = xnmalloc (n_cats, sizeof **cats);
1148 for (size_t k = 0; k < n_cats; k++)
1150 struct spv_data_value *dv = &series[0]->values[cat_rows[k]];
1151 int dv_num = dv ? dv->d : dv->index;
1152 struct pivot_category *cat = xzalloc (sizeof *cat);
1153 char *retval = pivot_value_from_data_value (
1154 spv_map_lookup (&series[0]->map, dv), NULL, NULL, &cat->name);
1164 cat->data_index = k;
1165 cat->presentation_index = cat_rows[k];
1167 series[0]->index_to_category[dv_num] = cat;
1170 add_affixes (table, cat->name,
1171 series[0]->affixes, series[0]->n_affixes);
1175 struct pivot_axis *axis = &table->axes[axis_type];
1176 d->axis_type = axis_type;
1177 d->level = axis->n_dimensions;
1178 d->top_index = table->n_dimensions - 1;
1179 d->root = xzalloc (sizeof *d->root);
1180 *d->root = (struct pivot_category) {
1181 .name = pivot_value_new_user_text (
1182 series[0]->label ? series[0]->label : "", -1),
1184 .show_label = should_show_label (fl),
1185 .data_index = SIZE_MAX,
1186 .presentation_index = SIZE_MAX,
1188 d->data_leaves = xmemdup (cats, n_cats * sizeof *cats);
1189 d->presentation_leaves = xmemdup (cats, n_cats * sizeof *cats);
1190 d->n_leaves = d->allocated_leaves = n_cats;
1192 /* Now group them, in one pass per grouping variable, innermost first. */
1193 for (size_t j = 1; j < n; j++)
1195 struct pivot_category **new_cats = xnmalloc (n_cats, sizeof **cats);
1196 size_t n_new_cats = 0;
1198 /* Allocate a category index. */
1199 size_t max_cat = max_category (series[j]);
1200 series[j]->n_index = max_cat + 1;
1201 series[j]->index_to_category = xcalloc (
1202 max_cat + 1, sizeof *series[j]->index_to_category);
1203 for (size_t cat1 = 0; cat1 < n_cats;)
1205 /* Find a sequence of categories cat1...cat2 (exclusive), that all
1206 have the same value in series 'j'. (This might be only a single
1207 category; we will drop unnamed 1-category groups later.) */
1208 size_t row1 = cats[cat1]->presentation_index;
1209 const struct spv_data_value *dv1 = &series[j]->values[row1];
1211 for (cat2 = cat1 + 1; cat2 < n_cats; cat2++)
1213 size_t row2 = cats[cat2]->presentation_index;
1214 const struct spv_data_value *dv2 = &series[j]->values[row2];
1215 if (!spv_data_value_equal (dv1, dv2))
1218 size_t n_subs = cat2 - cat1;
1220 struct pivot_category *new_cat;
1221 const struct spv_data_value *name
1222 = spv_map_lookup (&series[j]->map, dv1);
1223 if (n_subs == 1 && name->width == 0)
1225 /* The existing category stands on its own. */
1226 new_cat = cats[cat1++];
1230 /* Create a new group with cat...cat2 as subcategories. */
1231 new_cat = xzalloc (sizeof *new_cat);
1232 *new_cat = (struct pivot_category) {
1234 .subs = xnmalloc (n_subs, sizeof *new_cat->subs),
1237 .data_index = SIZE_MAX,
1238 .presentation_index = row1,
1240 char *retval = pivot_value_from_data_value (name, NULL, NULL,
1249 for (size_t k = 0; k < n_subs; k++)
1250 new_cat->subs[k] = cats[cat1++];
1252 int dv1_num = dv1->width < 0 ? dv1->d : dv1->index;
1253 series[j]->index_to_category[dv1_num] = new_cat;
1257 add_affixes (table, new_cat->name,
1258 series[j]->affixes, series[j]->n_affixes);
1260 /* Append the new group to the list of new groups. */
1261 new_cats[n_new_cats++] = new_cat;
1266 n_cats = n_new_cats;
1269 /* Now drop unnamed 1-category groups and add parent pointers. */
1270 for (size_t j = 0; j < n_cats; j++)
1271 add_parents (cats[j], d->root, j);
1273 d->root->subs = cats;
1274 d->root->n_subs = n_cats;
1278 pivot_dimension_destroy (d);
1282 dim_seriesp[(*n_dim_seriesp)++] = series[0];
1283 series[0]->dimension = d;
1285 axis->dimensions = xnrealloc (axis->dimensions, axis->n_dimensions + 1,
1286 sizeof *axis->dimensions);
1287 axis->dimensions[axis->n_dimensions++] = d;
1288 axis->extent *= d->n_leaves;
1294 static char * WARN_UNUSED_RESULT
1295 add_dimensions (struct hmap *series_map, const struct spvdx_nest *nest,
1296 enum pivot_axis_type axis_type,
1297 const struct spvdx_visualization *v, struct pivot_table *table,
1298 struct spv_series **dim_seriesp, size_t *n_dim_seriesp,
1301 struct pivot_axis *axis = &table->axes[axis_type];
1308 struct spv_series **series = xnmalloc (nest->n_vars, sizeof *series);
1309 for (size_t i = 0; i < nest->n_vars;)
1312 for (n = 0; i + n < nest->n_vars; n++)
1314 series[n] = spv_series_from_ref (series_map, nest->vars[i + n]->ref);
1315 if (!series[n] || !series[n]->n_values)
1321 struct pivot_dimension *d;
1322 char *error = add_dimension (series, n, axis_type, v, table,
1323 dim_seriesp, n_dim_seriesp,
1339 static char * WARN_UNUSED_RESULT
1340 add_layers (struct hmap *series_map,
1341 struct spvdx_layer **layers, size_t n_layers,
1342 const struct spvdx_visualization *v, struct pivot_table *table,
1343 struct spv_series **dim_seriesp, size_t *n_dim_seriesp,
1346 struct pivot_axis *axis = &table->axes[PIVOT_AXIS_LAYER];
1353 struct spv_series **series = xnmalloc (n_layers, sizeof *series);
1354 for (size_t i = 0; i < n_layers;)
1357 for (n = 0; i + n < n_layers; n++)
1359 series[n] = spv_series_from_ref (series_map,
1360 layers[i + n]->variable);
1361 if (!series[n] || !series[n]->n_values)
1367 struct pivot_dimension *d;
1368 char *error = add_dimension (
1369 series, n, PIVOT_AXIS_LAYER, v, table,
1370 dim_seriesp, n_dim_seriesp, level_ofs + i, &d);
1377 int index = atoi (layers[i]->value);
1378 assert (index < d->n_leaves);
1379 table->current_layer = xrealloc (
1380 table->current_layer,
1381 axis->n_dimensions * sizeof *table->current_layer);
1382 table->current_layer[axis->n_dimensions - 1] = index;
1391 static struct pivot_category *
1392 find_category (struct spv_series *series, int index)
1394 return (index >= 0 && index < series->n_index
1395 ? series->index_to_category[index]
1400 int_in_array (int value, const int *array, size_t n)
1402 for (size_t i = 0; i < n; i++)
1403 if (array[i] == value)
1410 apply_styles_to_value (struct pivot_table *table,
1411 struct pivot_value *value,
1412 const struct spvdx_set_format *sf,
1413 const struct table_area_style *base_area_style,
1414 const struct spvdx_style *fg,
1415 const struct spvdx_style *bg)
1421 free (value->footnotes);
1422 value->footnotes = NULL;
1423 value->n_footnotes = 0;
1426 struct fmt_spec format = { .w = 0 };
1429 format = decode_format (sf->format);
1430 add_affixes (table, value, sf->format->affix, sf->format->n_affix);
1432 else if (sf->number_format)
1434 format = decode_number_format (sf->number_format);
1435 add_affixes (table, value, sf->number_format->affix,
1436 sf->number_format->n_affix);
1438 else if (sf->n_string_format)
1440 for (size_t i = 0; i < sf->n_string_format; i++)
1441 add_affixes (table, value, sf->string_format[i]->affix,
1442 sf->string_format[i]->n_affix);
1444 else if (sf->date_time_format)
1446 format = decode_date_time_format (sf->date_time_format);
1447 add_affixes (table, value, sf->date_time_format->affix,
1448 sf->date_time_format->n_affix);
1450 else if (sf->elapsed_time_format)
1452 format = decode_elapsed_time_format (sf->elapsed_time_format);
1453 add_affixes (table, value, sf->elapsed_time_format->affix,
1454 sf->elapsed_time_format->n_affix);
1459 if (value->type == PIVOT_VALUE_NUMERIC)
1460 value->numeric.format = format;
1462 /* Possibly we should try to apply date and time formats too,
1463 but none seem to occur in practice so far. */
1468 struct table_area_style area;
1469 pivot_value_get_style (
1471 value->font_style ? value->font_style : &base_area_style->font_style,
1472 value->cell_style ? value->cell_style : &base_area_style->cell_style,
1474 decode_spvdx_style_incremental (fg, bg, &area);
1475 pivot_value_set_style (value, &area);
1476 table_area_style_uninit (&area);
1481 decode_set_cell_properties__ (struct pivot_table *table,
1482 struct hmap *series_map,
1483 const struct spvdx_intersect *intersect,
1484 const struct spvdx_style *interval,
1485 const struct spvdx_style *graph,
1486 const struct spvdx_style *labeling,
1487 const struct spvdx_style *frame,
1488 const struct spvdx_style *major_ticks,
1489 const struct spvdx_set_format *set_format)
1491 if (graph && labeling && intersect->alternating
1492 && !interval && !major_ticks && !frame && !set_format)
1494 /* Sets alt_fg_color and alt_bg_color. */
1495 struct table_area_style area;
1496 decode_spvdx_style (labeling, graph, &area);
1497 table->look->areas[PIVOT_AREA_DATA].font_style.fg[1]
1498 = area.font_style.fg[0];
1499 table->look->areas[PIVOT_AREA_DATA].font_style.bg[1]
1500 = area.font_style.bg[0];
1501 table_area_style_uninit (&area);
1504 && !labeling && !interval && !major_ticks && !frame && !set_format)
1506 /* 'graph->width' likely just sets the width of the table as a
1509 else if (!graph && !labeling && !interval && !frame && !set_format
1512 /* No-op. (Presumably there's a setMetaData we don't care about.) */
1514 else if (((set_format && spvdx_is_major_ticks (set_format->target))
1515 || major_ticks || frame)
1516 && intersect->n_where == 1)
1518 /* Formatting for individual row or column labels. */
1519 const struct spvdx_where *w = intersect->where[0];
1520 struct spv_series *s = spv_series_find (series_map, w->variable->id);
1523 const char *p = w->include;
1528 int include = strtol (p, &tail, 10);
1530 struct pivot_category *c = find_category (s, include);
1533 const struct table_area_style *base_area_style
1534 = (c->dimension->axis_type == PIVOT_AXIS_ROW
1535 ? &table->look->areas[PIVOT_AREA_ROW_LABELS]
1536 : &table->look->areas[PIVOT_AREA_COLUMN_LABELS]);
1537 apply_styles_to_value (table, c->name, set_format,
1538 base_area_style, major_ticks, frame);
1548 else if ((set_format && spvdx_is_labeling (set_format->target))
1549 || labeling || interval)
1551 /* Formatting for individual cells or groups of them with some dimensions
1553 int **indexes = XCALLOC (table->n_dimensions, int *);
1554 size_t *n = XCALLOC (table->n_dimensions, size_t);
1555 size_t *allocated = XCALLOC (table->n_dimensions, size_t);
1557 for (size_t i = 0; i < intersect->n_where; i++)
1559 const struct spvdx_where *w = intersect->where[i];
1560 struct spv_series *s = spv_series_find (series_map, w->variable->id);
1564 /* Group indexes may be included even though they are redundant.
1569 size_t j = s->dimension->top_index;
1571 const char *p = w->include;
1575 int include = strtol (p, &tail, 10);
1577 struct pivot_category *c = find_category (s, include);
1580 if (n[j] >= allocated[j])
1581 indexes[j] = x2nrealloc (indexes[j], &allocated[j],
1582 sizeof *indexes[j]);
1583 indexes[j][n[j]++] = c->data_index;
1596 for (size_t i = 0; i < table->n_dimensions; i++)
1600 printf (" %d=(", i);
1601 for (size_t j = 0; j < n[i]; j++)
1605 printf ("%d", indexes[i][j]);
1613 /* XXX This is inefficient in the common case where all of the dimensions
1614 are matched. We should use a heuristic where if all of the dimensions
1615 are matched and the product of n[*] is less than
1616 hmap_count(&table->cells) then iterate through all the possibilities
1617 rather than all the cells. Or even only do it if there is just one
1620 struct pivot_cell *cell;
1621 HMAP_FOR_EACH (cell, struct pivot_cell, hmap_node, &table->cells)
1623 for (size_t i = 0; i < table->n_dimensions; i++)
1625 if (n[i] && !int_in_array (cell->idx[i], indexes[i], n[i]))
1628 apply_styles_to_value (table, cell->value, set_format,
1629 &table->look->areas[PIVOT_AREA_DATA],
1630 labeling, interval);
1635 for (size_t i = 0; i < table->n_dimensions; i++)
1646 decode_set_cell_properties (struct pivot_table *table, struct hmap *series_map,
1647 struct spvdx_set_cell_properties **scps,
1650 for (size_t i = 0; i < n_scps; i++)
1652 const struct spvdx_set_cell_properties *scp = scps[i];
1653 const struct spvdx_style *interval = NULL;
1654 const struct spvdx_style *graph = NULL;
1655 const struct spvdx_style *labeling = NULL;
1656 const struct spvdx_style *frame = NULL;
1657 const struct spvdx_style *major_ticks = NULL;
1658 const struct spvdx_set_format *set_format = NULL;
1659 for (size_t j = 0; j < scp->n_seq; j++)
1661 const struct spvxml_node *node = scp->seq[j];
1662 if (spvdx_is_set_style (node))
1664 const struct spvdx_set_style *set_style
1665 = spvdx_cast_set_style (node);
1666 if (spvdx_is_graph (set_style->target))
1667 graph = set_style->style;
1668 else if (spvdx_is_labeling (set_style->target))
1669 labeling = set_style->style;
1670 else if (spvdx_is_interval (set_style->target))
1671 interval = set_style->style;
1672 else if (spvdx_is_major_ticks (set_style->target))
1673 major_ticks = set_style->style;
1677 else if (spvdx_is_set_frame_style (node))
1678 frame = spvdx_cast_set_frame_style (node)->style;
1679 else if (spvdx_is_set_format (node))
1680 set_format = spvdx_cast_set_format (node);
1682 assert (spvdx_is_set_meta_data (node));
1685 if (scp->union_ && scp->apply_to_converse <= 0)
1687 for (size_t j = 0; j < scp->union_->n_intersect; j++)
1688 decode_set_cell_properties__ (
1689 table, series_map, scp->union_->intersect[j],
1690 interval, graph, labeling, frame, major_ticks, set_format);
1692 else if (!scp->union_ && scp->apply_to_converse > 0)
1694 if ((set_format && spvdx_is_labeling (set_format->target))
1695 || labeling || interval)
1697 struct pivot_cell *cell;
1698 HMAP_FOR_EACH (cell, struct pivot_cell, hmap_node, &table->cells)
1699 apply_styles_to_value (table, cell->value, set_format,
1700 &table->look->areas[PIVOT_AREA_DATA],
1704 else if (!scp->union_ && scp->apply_to_converse <= 0)
1706 /* Appears to be used to set the font for something--but what? */
1713 static struct spv_series *
1714 parse_formatting (const struct spvdx_visualization *v,
1715 const struct hmap *series_map, struct hmap *format_map)
1717 const struct spvdx_labeling *labeling = v->graph->interval->labeling;
1718 struct spv_series *cell_format = NULL;
1719 for (size_t i = 0; i < labeling->n_seq; i++)
1721 const struct spvdx_formatting *f
1722 = spvdx_cast_formatting (labeling->seq[i]);
1726 cell_format = spv_series_from_ref (series_map, f->variable);
1727 for (size_t j = 0; j < f->n_format_mapping; j++)
1729 const struct spvdx_format_mapping *fm = f->format_mapping[j];
1733 struct format_mapping *out = xmalloc (sizeof *out);
1734 out->from = fm->from;
1735 out->to = decode_format (fm->format);
1736 hmap_insert (format_map, &out->hmap_node,
1737 hash_int (out->from, 0));
1746 format_map_destroy (struct hmap *format_map)
1748 struct format_mapping *fm, *next;
1749 HMAP_FOR_EACH_SAFE (fm, next, struct format_mapping, hmap_node, format_map)
1751 hmap_delete (format_map, &fm->hmap_node);
1754 hmap_destroy (format_map);
1757 char * WARN_UNUSED_RESULT
1758 decode_spvdx_table (const struct spvdx_visualization *v, const char *subtype,
1759 const struct pivot_table_look *look,
1760 struct spv_data *data, struct pivot_table **outp)
1762 struct pivot_table *table = pivot_table_create__ (NULL, subtype);
1764 pivot_table_set_look (table, look);
1765 table->look = pivot_table_look_unshare (table->look);
1767 struct hmap series_map = HMAP_INITIALIZER (series_map);
1768 struct hmap format_map = HMAP_INITIALIZER (format_map);
1769 struct spv_series **dim_series = NULL;
1772 struct spvdx_visualization_extension *ve = v->visualization_extension;
1773 table->show_grid_lines = ve && ve->show_gridline;
1775 /* Sizing from the legacy properties can get overridden. */
1776 if (v->graph->cell_style->width)
1778 int min_width, max_width, n = 0;
1779 if (sscanf (v->graph->cell_style->width, "%*d%%;%dpt;%dpt%n",
1780 &min_width, &max_width, &n)
1781 && v->graph->cell_style->width[n] == '\0')
1783 table->look->width_ranges[TABLE_HORZ][0] = min_width;
1784 table->look->width_ranges[TABLE_HORZ][1] = max_width;
1790 Any pivot_value might refer to footnotes, so it's important to process the
1791 footnotes early to ensure that those references can be resolved. There is
1792 a possible problem that a footnote might itself reference an
1793 as-yet-unprocessed footnote, but that's OK because footnote references
1794 don't actually look at the footnote contents but only resolve a pointer to
1795 where the footnote will go later.
1797 Before we really start, create all the footnotes we'll fill in. This is
1798 because sometimes footnotes refer to themselves or to each other and we
1799 don't want to reject those references. */
1801 for (size_t i = 0; i < v->container->n_label_frame; i++)
1803 const struct spvdx_label_frame *lf = v->container->label_frame[i];
1805 && lf->label->purpose == SPVDX_PURPOSE_FOOTNOTE
1806 && lf->label->n_text > 0
1807 && lf->label->text[0]->uses_reference > 0)
1809 pivot_table_create_footnote__ (
1810 table, lf->label->text[0]->uses_reference - 1,
1815 if (v->graph->interval->footnotes)
1816 decode_footnotes (table, v->graph->interval->footnotes);
1818 struct spv_series *footnotes = NULL;
1819 for (size_t i = 0; i < v->graph->interval->labeling->n_seq; i++)
1821 const struct spvxml_node *node = v->graph->interval->labeling->seq[i];
1822 if (spvdx_is_footnotes (node))
1824 const struct spvdx_footnotes *f = spvdx_cast_footnotes (node);
1825 footnotes = spv_series_from_ref (&series_map, f->variable);
1826 decode_footnotes (table, f);
1829 for (size_t i = 0; i < v->n_lf1; i++)
1831 error = decode_label_frame (table, v->lf1[i]);
1835 for (size_t i = 0; i < v->n_lf2; i++)
1837 error = decode_label_frame (table, v->lf2[i]);
1842 for (size_t i = 0; i < v->container->n_label_frame; i++)
1844 error = decode_label_frame (table, v->container->label_frame[i]);
1848 if (v->graph->interval->labeling->style)
1850 table_area_style_uninit (&table->look->areas[PIVOT_AREA_DATA]);
1851 decode_spvdx_style (v->graph->interval->labeling->style,
1852 v->graph->cell_style,
1853 &table->look->areas[PIVOT_AREA_DATA]);
1856 /* Decode all of the sourceVariable and derivedVariable */
1857 struct spvxml_node **nodes = xmemdup (v->seq, v->n_seq * sizeof *v->seq);
1858 size_t n_nodes = v->n_seq;
1861 bool progress = false;
1862 for (size_t i = 0; i < n_nodes;)
1864 error = (spvdx_is_source_variable (nodes[i])
1865 ? decode_spvdx_source_variable (nodes[i], data, &series_map)
1866 : decode_spvdx_derived_variable (nodes[i], &series_map));
1869 nodes[i] = nodes[--n_nodes];
1872 else if (error == &BAD_REFERENCE)
1884 error = xasprintf ("Table has %zu variables with circular or "
1885 "unresolved references, including variable %s.",
1886 n_nodes, nodes[0]->id);
1892 const struct spvdx_cross *cross = v->graph->faceting->cross;
1894 assert (cross->n_seq == 1);
1895 const struct spvdx_nest *columns = spvdx_cast_nest (cross->seq[0]);
1896 size_t max_columns = columns ? columns->n_vars : 0;
1898 assert (cross->n_seq2 == 1);
1899 const struct spvdx_nest *rows = spvdx_cast_nest (cross->seq2[0]);
1900 size_t max_rows = rows ? rows->n_vars : 0;
1902 size_t max_layers = (v->graph->faceting->n_layers1
1903 + v->graph->faceting->n_layers2);
1905 size_t max_dims = max_columns + max_rows + max_layers;
1906 table->dimensions = xnmalloc (max_dims, sizeof *table->dimensions);
1907 dim_series = xnmalloc (max_dims, sizeof *dim_series);
1908 size_t n_dim_series = 0;
1910 error = add_dimensions (&series_map, columns, PIVOT_AXIS_COLUMN, v, table,
1911 dim_series, &n_dim_series, 1);
1915 error = add_dimensions (&series_map, rows, PIVOT_AXIS_ROW, v, table,
1916 dim_series, &n_dim_series, max_columns + 1);
1920 error = add_layers (&series_map, v->graph->faceting->layers1,
1921 v->graph->faceting->n_layers1,
1922 v, table, dim_series, &n_dim_series,
1923 max_rows + max_columns + 1);
1927 error = add_layers (&series_map, v->graph->faceting->layers2,
1928 v->graph->faceting->n_layers2,
1929 v, table, dim_series, &n_dim_series,
1930 (max_rows + max_columns + v->graph->faceting->n_layers1
1935 struct spv_series *cell = spv_series_find (&series_map, "cell");
1938 error = xstrdup (_("Table lacks cell data."));
1942 struct spv_series *cell_format = parse_formatting (v, &series_map,
1945 assert (table->n_dimensions == n_dim_series);
1946 size_t *dim_indexes = xnmalloc (table->n_dimensions, sizeof *dim_indexes);
1947 for (size_t i = 0; i < cell->n_values; i++)
1949 for (size_t j = 0; j < table->n_dimensions; j++)
1951 const struct spv_data_value *value = &dim_series[j]->values[i];
1952 const struct pivot_category *cat = find_category (
1953 dim_series[j], value->width < 0 ? value->d : value->index);
1956 dim_indexes[j] = cat->data_index;
1959 struct pivot_value *value;
1960 error = pivot_value_from_data_value (
1961 &cell->values[i], cell_format ? &cell_format->values[i] : NULL,
1962 &format_map, &value);
1968 const struct spv_data_value *d = &footnotes->values[i];
1971 const char *p = d->s;
1975 int idx = strtol (p, &tail, 10);
1976 add_footnote (value, idx, table);
1986 if (value->type == PIVOT_VALUE_NUMERIC
1987 && value->numeric.x == SYSMIS
1988 && !value->n_footnotes)
1990 /* Apparently, system-missing values are just empty cells? */
1991 pivot_value_destroy (value);
1994 pivot_table_put (table, dim_indexes, table->n_dimensions, value);
1999 decode_set_cell_properties (table, &series_map, v->graph->facet_layout->scp1,
2000 v->graph->facet_layout->n_scp1);
2001 decode_set_cell_properties (table, &series_map, v->graph->facet_layout->scp2,
2002 v->graph->facet_layout->n_scp2);
2004 pivot_table_assign_label_depth (table);
2006 format_map_destroy (&format_map);
2010 spv_series_destroy (&series_map);
2013 pivot_table_unref (table);