0c0f954797aa80c0eb14129d696acf914da5b7ad
[pspp] / src / output / spv / spv-legacy-decoder.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2017, 2018 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include "output/spv/spv-legacy-decoder.h"
20
21 #include <errno.h>
22 #include <inttypes.h>
23 #include <math.h>
24 #include <limits.h>
25 #include <stdlib.h>
26
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"
41
42 #include "gl/c-strtod.h"
43 #include "gl/xalloc.h"
44 #include "gl/xmemdup0.h"
45
46 #include <libxml/tree.h>
47
48 #include "gettext.h"
49 #define N_(msgid) msgid
50 #define _(msgid) gettext (msgid)
51
52 struct spv_series
53   {
54     struct hmap_node hmap_node; /* By name. */
55     char *name;
56     char *label;
57     struct fmt_spec format;
58
59     struct spv_series *label_series;
60     bool is_label_series;
61
62     const struct spvxml_node *xml;
63
64     struct spv_data_value *values;
65     size_t n_values;
66     struct hmap map;            /* Contains "struct spv_mapping". */
67     bool remapped;
68
69     struct pivot_dimension *dimension;
70
71     struct pivot_category **index_to_category;
72     size_t n_index;
73
74     struct spvdx_affix **affixes;
75     size_t n_affixes;
76   };
77
78 static void spv_map_destroy (struct hmap *);
79
80 static struct spv_series *
81 spv_series_first (struct hmap *series_map)
82 {
83   struct spv_series *series;
84   HMAP_FOR_EACH (series, struct spv_series, hmap_node, series_map)
85     return series;
86   return NULL;
87 }
88
89 static struct spv_series *
90 spv_series_find (const struct hmap *series_map, const char *name)
91 {
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))
96       return series;
97   return NULL;
98 }
99
100 static struct spv_series *
101 spv_series_from_ref (const struct hmap *series_map,
102                      const struct spvxml_node *ref)
103 {
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);
109 }
110
111 static void UNUSED
112 spv_series_dump (const struct spv_series *series)
113 {
114   printf ("series \"%s\"", series->name);
115   if (series->label)
116     printf (" (label \"%s\")", series->label);
117   printf (", %zu values:", series->n_values);
118   for (size_t i = 0; i < series->n_values; i++)
119     {
120       putchar (' ');
121       spv_data_value_dump (&series->values[i], stdout);
122     }
123   putchar ('\n');
124 }
125
126 static void
127 spv_series_destroy (struct hmap *series_map)
128 {
129   struct spv_series *series, *next_series;
130   HMAP_FOR_EACH_SAFE (series, next_series, struct spv_series, hmap_node,
131                       series_map)
132     {
133       free (series->name);
134       free (series->label);
135
136       for (size_t i = 0; i < series->n_values; i++)
137         spv_data_value_uninit (&series->values[i]);
138       free (series->values);
139
140       spv_map_destroy (&series->map);
141
142       free (series->index_to_category);
143
144       hmap_delete (series_map, &series->hmap_node);
145       free (series);
146     }
147   hmap_destroy (series_map);
148 }
149
150 struct spv_mapping
151   {
152     struct hmap_node hmap_node;
153     double from;
154     struct spv_data_value to;
155   };
156
157 static struct spv_mapping *
158 spv_map_search (const struct hmap *map, double from)
159 {
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)
164       return mapping;
165   return NULL;
166 }
167
168 static const struct spv_data_value *
169 spv_map_lookup (const struct hmap *map, const struct spv_data_value *in)
170 {
171   if (in->width >= 0)
172     return in;
173
174   const struct spv_mapping *m = spv_map_search (map, in->d);
175   return m ? &m->to : in;
176 }
177
178 static bool
179 parse_real (const char *s, double *real)
180 {
181   int save_errno = errno;
182   errno = 0;
183   char *end;
184   *real = c_strtod (s, &end);
185   bool ok = !errno && end > s && !*end;
186   errno = save_errno;
187
188   return ok;
189 }
190
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)
194 {
195   struct spv_mapping *mapping = xmalloc (sizeof *mapping);
196   mapping->from = from;
197
198   if ((try_strings_as_numbers || (format && fmt_is_numeric (format->type)))
199       && parse_real (to, &mapping->to.d))
200     {
201       if (try_strings_as_numbers)
202         mapping->to.width = -1;
203       else
204         {
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);
208         }
209     }
210   else
211     {
212       mapping->to.width = strlen (to);
213       mapping->to.s = xstrdup (to);
214     }
215
216   struct spv_mapping *old_mapping = spv_map_search (map, from);
217   if (old_mapping)
218     {
219       bool same = spv_data_value_equal (&old_mapping->to, &mapping->to);
220       spv_data_value_uninit (&mapping->to);
221       free (mapping);
222       return (same ? NULL
223               : xasprintf ("Duplicate relabeling differs for from=\"%.*g\"",
224                            DBL_DIG + 1, from));
225     }
226
227   hmap_insert (map, &mapping->hmap_node, hash_double (from, 0));
228   return NULL;
229 }
230
231 static void
232 spv_map_destroy (struct hmap *map)
233 {
234   struct spv_mapping *mapping, *next;
235   HMAP_FOR_EACH_SAFE (mapping, next, struct spv_mapping, hmap_node, map)
236     {
237       spv_data_value_uninit (&mapping->to);
238       hmap_delete (map, &mapping->hmap_node);
239       free (mapping);
240     }
241   hmap_destroy (map);
242 }
243
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)
249 {
250   for (size_t i = 0; i < n_relabels; i++)
251     {
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);
255       if (error)
256         return error;
257     }
258   return NULL;
259 }
260
261 static char * WARN_UNUSED_RESULT
262 spv_series_parse_value_map_entry (struct hmap *map,
263                                   const struct spvdx_value_map_entry *vme)
264 {
265   for (const char *p = vme->from; ; p++)
266     {
267       int save_errno = errno;
268       errno = 0;
269       char *end;
270       double from = c_strtod (p, &end);
271       bool ok = !errno && end > p && strchr (";", *end);
272       errno = save_errno;
273       if (!ok)
274         return xasprintf ("Syntax error in valueMapEntry from=\"%s\".",
275                           vme->from);
276
277       char *error = spv_map_insert (map, from, vme->to, true,
278                                     &(struct fmt_spec) { FMT_A, 40, 0 });
279       if (error)
280         return error;
281
282       p = end;
283       if (*p == '\0')
284         return NULL;
285       assert (*p == ';');
286     }
287 }
288
289 static struct fmt_spec
290 decode_date_time_format (const struct spvdx_date_time_format *dtf)
291 {
292   if (dtf->dt_base_format == SPVDX_DT_BASE_FORMAT_DATE)
293     {
294       enum fmt_type type
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
302            : FMT_ADATE);
303
304       int w = fmt_min_output_width (type);
305       if (dtf->year_abbreviation <= 0)
306         w += 2;
307       return (struct fmt_spec) { .type = type, .w = w };
308     }
309   else
310     {
311       enum fmt_type type
312         = (dtf->dt_base_format == SPVDX_DT_BASE_FORMAT_DATE_TIME
313            ? (dtf->mdy_order == SPVDX_MDY_ORDER_YEAR_MONTH_DAY
314               ? FMT_YMDHMS
315               : FMT_DATETIME)
316            : (dtf->show_day > 0 ? FMT_DTIME
317               : dtf->show_hour > 0 ? FMT_TIME
318               : FMT_MTIME));
319       int w = fmt_min_output_width (type);
320       int d = 0;
321       if (dtf->show_second > 0)
322         {
323           w += 3;
324           if (dtf->show_millis > 0)
325             {
326               d = 3;
327               w += d + 1;
328             }
329         }
330       return (struct fmt_spec) { .type = type, .w = w, .d = d };
331     }
332 }
333
334 static struct fmt_spec
335 decode_elapsed_time_format (const struct spvdx_elapsed_time_format *etf)
336 {
337   enum fmt_type type
338     = (etf->dt_base_format != SPVDX_DT_BASE_FORMAT_TIME ? FMT_DTIME
339        : etf->show_hour > 0 ? FMT_TIME
340        : FMT_MTIME);
341   int w = fmt_min_output_width (type);
342   int d = 0;
343   if (etf->show_second > 0)
344     {
345       w += 3;
346       if (etf->show_millis > 0)
347         {
348           d = 3;
349           w += d + 1;
350         }
351     }
352   return (struct fmt_spec) { .type = type, .w = w, .d = d };
353 }
354
355 static struct fmt_spec
356 decode_number_format (const struct spvdx_number_format *nf)
357 {
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
362                         : FMT_F);
363
364   int d = nf->maximum_fraction_digits;
365   if (d < 0 || d > 15)
366     d = 2;
367
368   struct fmt_spec f = (struct fmt_spec) { type, 40, d };
369   fmt_fix_output (&f);
370   return f;
371 }
372
373 /* Returns an *approximation* of IN as a fmt_spec.
374
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)
378 {
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)
382     {
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,
415       };
416       return decode_date_time_format (&dtf);
417     }
418   else if (in->f_base_format == SPVDX_F_BASE_FORMAT_ELAPSED_TIME)
419     {
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,
435       };
436       return decode_elapsed_time_format (&etf);
437     }
438   else
439     {
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,
447         .small = in->small,
448         .prefix = in->prefix,
449         .suffix = in->suffix,
450       };
451       return decode_number_format (&nf);
452     }
453 }
454
455 static void
456 spv_series_execute_mapping (struct spv_series *series)
457 {
458   if (!hmap_is_empty (&series->map))
459     {
460       series->remapped = true;
461       for (size_t i = 0; i < series->n_values; i++)
462         {
463           struct spv_data_value *value = &series->values[i];
464           if (value->width >= 0)
465             continue;
466
467           const struct spv_mapping *mapping = spv_map_search (&series->map,
468                                                               value->d);
469           if (mapping)
470             {
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);
476               else
477                 value->d = mapping->to.d;
478             }
479         }
480     }
481 }
482
483 static char * WARN_UNUSED_RESULT
484 spv_series_remap_formats (struct spv_series *series,
485                           struct spvxml_node **seq, size_t n_seq)
486 {
487   spv_map_destroy (&series->map);
488   hmap_init (&series->map);
489   for (size_t i = 0; i < n_seq; i++)
490     {
491       struct spvxml_node *node = seq[i];
492       if (spvdx_is_format (node))
493         {
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);
499           if (error)
500             return error;
501
502           series->affixes = f->affix;
503           series->n_affixes = f->n_affix;
504         }
505       else if (spvdx_is_string_format (node))
506         {
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,
510                                                    false, NULL);
511           if (error)
512             return error;
513
514           series->affixes = sf->affix;
515           series->n_affixes = sf->n_affix;
516         }
517       else
518         NOT_REACHED ();
519     }
520   spv_series_execute_mapping (series);
521   return NULL;
522 }
523
524 static char * WARN_UNUSED_RESULT
525 spv_series_remap_vmes (struct spv_series *series,
526                        struct spvdx_value_map_entry **vmes,
527                        size_t n_vmes)
528 {
529   spv_map_destroy (&series->map);
530   hmap_init (&series->map);
531   for (size_t i = 0; i < n_vmes; i++)
532     {
533       char *error = spv_series_parse_value_map_entry (&series->map, vmes[i]);
534       if (error)
535         return error;
536     }
537   spv_series_execute_mapping (series);
538   return NULL;
539 }
540
541 static void
542 decode_footnotes (struct pivot_table *table, const struct spvdx_footnotes *f)
543 {
544   if (f->n_footnote_mapping > 0)
545     pivot_table_create_footnote__ (table, f->n_footnote_mapping - 1,
546                                    NULL, NULL);
547   for (size_t i = 0; i < f->n_footnote_mapping; i++)
548     {
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),
552                                      NULL);
553     }
554 }
555
556 static struct cell_color
557 optional_color (int color, struct cell_color default_color)
558 {
559   return (color >= 0
560           ? (struct cell_color) CELL_COLOR (color >> 16, color >> 8, color)
561           : default_color);
562 }
563
564 static int
565 optional_length (const char *s, int default_length)
566 {
567   /* There is usually a "pt" suffix.  We ignore it. */
568   int length;
569   return s && sscanf (s, "%d", &length) == 1 ? length : default_length;
570 }
571
572 static int
573 optional_px (double inches, int default_px)
574 {
575   return inches != DBL_MAX ? inches * 96.0 : default_px;
576 }
577
578 static void
579 decode_spvdx_style_incremental (const struct spvdx_style *in,
580                                 const struct spvdx_style *bg,
581                                 struct table_area_style *out)
582 {
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)
590     {
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];
594     }
595   if (bg && bg->color >= 0)
596     {
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];
600     }
601   if (in && in->font_family)
602     {
603       free (out->font_style.typeface);
604       out->font_style.typeface = xstrdup (in->font_family);
605     }
606   if (in && in->font_size)
607     {
608       int size = optional_length (in->font_size, 0);
609       if (size)
610         out->font_style.size = size;
611     }
612   if (in && in->text_alignment)
613     out->cell_style.halign
614       = (in->text_alignment == SPVDX_TEXT_ALIGNMENT_LEFT
615          ? TABLE_HALIGN_LEFT
616          : in->text_alignment == SPVDX_TEXT_ALIGNMENT_RIGHT
617          ? TABLE_HALIGN_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
628        ? TABLE_VALIGN_TOP
629        : TABLE_VALIGN_CENTER);
630   if (in && in->decimal_offset != DBL_MAX)
631     out->cell_style.decimal_offset = optional_px (in->decimal_offset, 0);
632 #if 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);
641 #endif
642 }
643
644 static void
645 decode_spvdx_style (const struct spvdx_style *in,
646                     const struct spvdx_style *bg,
647                     struct table_area_style *out)
648 {
649   *out = (struct table_area_style) TABLE_AREA_STYLE_INITIALIZER;
650   decode_spvdx_style_incremental (in, bg, out);
651 }
652
653 static void
654 add_footnote (struct pivot_value *v, int idx, struct pivot_table *table)
655 {
656   if (idx < 1 || idx > table->n_footnotes)
657     return;
658
659   pivot_value_add_footnote (v, table->footnotes[idx - 1]);
660 }
661
662 static char * WARN_UNUSED_RESULT
663 decode_label_frame (struct pivot_table *table,
664                     const struct spvdx_label_frame *lf)
665 {
666   if (!lf->label)
667     return NULL;
668
669   struct pivot_value **target;
670   struct table_area_style *area;
671   if (lf->label->purpose == SPVDX_PURPOSE_TITLE)
672     {
673       target = &table->title;
674       area = &table->areas[PIVOT_AREA_TITLE];
675     }
676   else if (lf->label->purpose == SPVDX_PURPOSE_SUB_TITLE)
677     {
678       target = &table->caption;
679       area = &table->areas[PIVOT_AREA_CAPTION];
680     }
681   else if (lf->label->purpose == SPVDX_PURPOSE_FOOTNOTE)
682     {
683       if (lf->label->n_text > 0
684           && lf->label->text[0]->uses_reference != INT_MIN)
685         {
686           target = NULL;
687           area = &table->areas[PIVOT_AREA_FOOTER];
688         }
689       else
690         return NULL;
691     }
692   else if (lf->label->purpose == SPVDX_PURPOSE_LAYER)
693     {
694       target = NULL;
695       area = &table->areas[PIVOT_AREA_LAYERS];
696     }
697   else
698     return NULL;
699
700   table_area_style_uninit (area);
701   decode_spvdx_style (lf->label->style, lf->label->text_frame_style, area);
702
703   if (target)
704     {
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++)
708         {
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);
714           else
715             {
716               char *new = xasprintf ("%s%s", value->text.local, in->text);
717               free (value->text.local);
718               value->text.local = new;
719             }
720         }
721       pivot_value_destroy (*target);
722       *target = value;
723     }
724   else
725     for (size_t i = 0; i < lf->label->n_text; i++)
726       {
727         const struct spvdx_text *in = lf->label->text[i];
728         if (in->uses_reference == INT_MIN)
729           continue;
730         if (i % 2)
731           {
732             size_t length = strlen (in->text);
733             if (length && in->text[length - 1] == '\n')
734               length--;
735
736             pivot_table_create_footnote__ (
737               table, in->uses_reference - 1, NULL,
738               pivot_value_new_user_text (in->text, length));
739           }
740         else
741           {
742             size_t length = strlen (in->text);
743             if (length && in->text[length - 1] == '.')
744               length--;
745
746             pivot_table_create_footnote__ (
747               table, in->uses_reference - 1,
748               pivot_value_new_user_text (in->text, length), NULL);
749           }
750       }
751   return NULL;
752 }
753
754 /* Special return value for decode_spvdx_variable(). */
755 static char BAD_REFERENCE;
756
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)
761 {
762   const struct spvdx_source_variable *sv = spvdx_cast_source_variable (node);
763
764   struct spv_series *label_series = NULL;
765   if (sv->label_variable)
766     {
767       label_series = spv_series_find (series_map,
768                                       sv->label_variable->node_.id);
769       if (!label_series)
770         return &BAD_REFERENCE;
771
772       label_series->is_label_series = true;
773     }
774
775   const struct spv_data_variable *var = spv_data_find_variable (
776     data, sv->source, sv->source_name);
777   if (!var)
778     return xasprintf ("sourceVariable %s references nonexistent "
779                       "source %s variable %s.",
780                       sv->node_.id, sv->source, sv->source_name);
781
782   struct spv_series *s = xzalloc (sizeof *s);
783   s->name = xstrdup (node->id);
784   s->xml = node;
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;
789   s->format = F_8_0;
790   hmap_init (&s->map);
791   hmap_insert (series_map, &s->hmap_node, hash_string (s->name, 0));
792
793   char *error = spv_series_remap_formats (s, sv->seq, sv->n_seq);
794   if (error)
795     return error;
796
797   if (label_series && !s->remapped)
798     {
799       for (size_t i = 0; i < s->n_values; i++)
800         if (s->values[i].width < 0)
801           {
802             char *dest;
803             if (label_series->values[i].width < 0)
804               {
805                 union value v = { .f = label_series->values[i].d };
806                 dest = data_out_stretchy (&v, "UTF-8", &s->format, NULL);
807               }
808             else
809               dest = label_series->values[i].s;
810             char *error = spv_map_insert (&s->map, s->values[i].d,
811                                           dest, false, NULL);
812             free (error);   /* Duplicates are OK. */
813             if (label_series->values[i].width < 0)
814               free (dest);
815           }
816     }
817
818   return NULL;
819 }
820
821 static char * WARN_UNUSED_RESULT
822 decode_spvdx_derived_variable (const struct spvxml_node *node,
823                                struct hmap *series_map)
824 {
825   const struct spvdx_derived_variable *dv = spvdx_cast_derived_variable (node);
826
827   struct spv_data_value *values;
828   size_t n_values;
829
830   struct substring value = ss_cstr (dv->value);
831   if (ss_equals (value, ss_cstr ("constant(0)")))
832     {
833       struct spv_series *existing_series = spv_series_first (series_map);
834       if (!existing_series)
835         return &BAD_REFERENCE;
836
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;
841     }
842   else if (ss_starts_with (value, ss_cstr ("constant(")))
843     {
844       values = NULL;
845       n_values = 0;
846     }
847   else if (ss_starts_with (value, ss_cstr ("map("))
848            && ss_ends_with (value, ss_cstr (")")))
849     {
850       char *dependency_name = ss_xstrdup (ss_substr (value, 4,
851                                                      value.length - 5));
852       struct spv_series *dependency
853         = spv_series_find (series_map, dependency_name);
854       free (dependency_name);
855       if (!dependency)
856         return &BAD_REFERENCE;
857
858       values = spv_data_values_clone (dependency->values,
859                                       dependency->n_values);
860       n_values = dependency->n_values;
861     }
862   else
863     return xasprintf ("Derived variable %s has unknown value \"%s\"",
864                       node->id, dv->value);
865
866   struct spv_series *s = xzalloc (sizeof *s);
867   s->format = F_8_0;
868   s->name = xstrdup (node->id);
869   s->values = values;
870   s->n_values = n_values;
871   hmap_init (&s->map);
872   hmap_insert (series_map, &s->hmap_node, hash_string (s->name, 0));
873
874   char *error = spv_series_remap_vmes (s, dv->value_map_entry,
875                                        dv->n_value_map_entry);
876   if (error)
877     return error;
878
879   error = spv_series_remap_formats (s, dv->seq, dv->n_seq);
880   if (error)
881     return error;
882
883   if (n_values > 0)
884     {
885       for (size_t i = 0; i < n_values; i++)
886         if (values[i].width != 0)
887           goto nonempty;
888       for (size_t i = 0; i < n_values; i++)
889         spv_data_value_uninit (&s->values[i]);
890       free (s->values);
891
892       s->values = NULL;
893       s->n_values = 0;
894
895     nonempty:;
896     }
897   return NULL;
898 }
899
900 struct format_mapping
901   {
902     struct hmap_node hmap_node;
903     uint32_t from;
904     struct fmt_spec to;
905   };
906
907 static const struct format_mapping *
908 format_map_find (const struct hmap *format_map, uint32_t u32_format)
909 {
910   if (format_map)
911     {
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)
916           return fm;
917     }
918
919   return NULL;
920 }
921
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)
926 {
927   if (!data)
928     {
929       *out = fmt_for_output (FMT_F, 40, 2);
930       return NULL;
931     }
932
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);
935   if (fm)
936     {
937       *out = fm->to;
938       return NULL;
939     }
940   return spv_decode_fmt_spec (u32_format, out);
941 }
942
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)
948 {
949   *vp = NULL;
950
951   struct fmt_spec f;
952   char *error = spv_format_from_data_value (format, format_map, &f);
953   if (error)
954     return error;
955
956   struct pivot_value *v = xzalloc (sizeof *v);
957   if (data->width >= 0)
958     {
959       if (format && fmt_get_category (f.type) == FMT_CAT_DATE)
960         {
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,
964                       &msec, &len) == 7
965               && len == 23
966               && data->s[len] == '\0')
967             {
968               double date = calendar_gregorian_to_offset (year, month, day,
969                                                           NULL);
970               if (date != SYSMIS)
971                 {
972                   v->type = PIVOT_VALUE_NUMERIC;
973                   v->numeric.x = (date * 60. * 60. * 24.
974                                   + hour * 60. * 60.
975                                   + minute * 60.
976                                   + second
977                                   + msec / 1000.0);
978                   v->numeric.format = f;
979                   *vp = v;
980                   return NULL;
981                 }
982             }
983         }
984       else if (format && fmt_get_category (f.type) == FMT_CAT_TIME)
985         {
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
989               && len > 0
990               && data->s[len] == '\0')
991             {
992               v->type = PIVOT_VALUE_NUMERIC;
993               v->numeric.x = (hour * 60. * 60.
994                               + minute * 60.
995                               + second
996                               + msec / 1000.0);
997               v->numeric.format = f;
998               *vp = v;
999               return NULL;
1000             }
1001         }
1002       v->type = PIVOT_VALUE_STRING;
1003       v->string.s = xstrdup (data->s);
1004     }
1005   else
1006     {
1007       v->type = PIVOT_VALUE_NUMERIC;
1008       v->numeric.x = data->d;
1009       v->numeric.format = f;
1010     }
1011   *vp = v;
1012   return NULL;
1013 }
1014
1015 static void
1016 add_parents (struct pivot_category *cat, struct pivot_category *parent,
1017              size_t group_index)
1018 {
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);
1024 }
1025
1026 static const struct spvdx_facet_level *
1027 find_facet_level (const struct spvdx_visualization *v, int facet_level)
1028 {
1029   const struct spvdx_facet_layout *layout = v->graph->facet_layout;
1030   for (size_t i = 0; i < layout->n_facet_level; i++)
1031     {
1032       const struct spvdx_facet_level *fl = layout->facet_level[i];
1033       if (facet_level == fl->level)
1034         return fl;
1035     }
1036   return NULL;
1037 }
1038
1039 static bool
1040 should_show_label (const struct spvdx_facet_level *fl)
1041 {
1042   return fl && fl->axis->label && fl->axis->label->style->visible != 0;
1043 }
1044
1045 static size_t
1046 max_category (const struct spv_series *s)
1047 {
1048   double max_cat = -DBL_MAX;
1049   for (size_t i = 0; i < s->n_values; i++)
1050     {
1051       const struct spv_data_value *dv = &s->values[i];
1052       double d = dv->width < 0 ? dv->d : dv->index;
1053       if (d > max_cat)
1054         max_cat = d;
1055     }
1056   assert (max_cat >= 0 && max_cat < SIZE_MAX - 1);
1057
1058   return max_cat;
1059 }
1060
1061 static void
1062 add_affixes (struct pivot_table *table, struct pivot_value *value,
1063              struct spvdx_affix **affixes, size_t n_affixes)
1064 {
1065   for (size_t i = 0; i < n_affixes; i++)
1066     add_footnote (value, affixes[i]->defines_reference, table);
1067 }
1068
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)
1075 {
1076   char *error = NULL;
1077
1078   const struct spvdx_facet_level *fl
1079     = find_facet_level (v, base_facet_level + n);
1080   if (fl)
1081     {
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]
1086                                        : NULL);
1087       if (area && fl->axis->label)
1088         {
1089           table_area_style_uninit (area);
1090           decode_spvdx_style (fl->axis->label->style,
1091                               fl->axis->label->text_frame_style, area);
1092         }
1093     }
1094
1095   if (axis_type == PIVOT_AXIS_ROW)
1096     {
1097       const struct spvdx_facet_level *fl2
1098         = find_facet_level (v, base_facet_level + (n - 1));
1099       if (fl2)
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]);
1104     }
1105
1106   const struct spvdx_facet_level *fl3 = find_facet_level (v, base_facet_level);
1107   if (fl3 && fl3->axis->major_ticks->label_angle == -90)
1108     {
1109       if (axis_type == PIVOT_AXIS_COLUMN)
1110         table->rotate_inner_column_labels = true;
1111       else
1112         table->rotate_outer_row_labels = true;
1113     }
1114
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++)
1121     {
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)
1125         {
1126           size_t row = d;
1127           if (cat_rows[row] == SIZE_MAX)
1128             cat_rows[row] = k;
1129         }
1130     }
1131
1132   /* Drop missing categories and count what's left. */
1133   size_t n_cats = 0;
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);
1138
1139   /* Make the categories. */
1140   struct pivot_dimension *d = xzalloc (sizeof *d);
1141   table->dimensions[table->n_dimensions++] = d;
1142
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++)
1148     {
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);
1154       if (retval)
1155         {
1156           if (error)
1157             free (retval);
1158           else
1159             error = retval;
1160         }
1161       cat->parent = NULL;
1162       cat->dimension = d;
1163       cat->data_index = k;
1164       cat->presentation_index = cat_rows[k];
1165       cats[k] = cat;
1166       series[0]->index_to_category[dv_num] = cat;
1167
1168       if (cat->name)
1169         add_affixes (table, cat->name,
1170                      series[0]->affixes, series[0]->n_affixes);
1171     }
1172   free (cat_rows);
1173
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),
1182     .dimension = d,
1183     .show_label = should_show_label (fl),
1184     .data_index = SIZE_MAX,
1185     .presentation_index = SIZE_MAX,
1186   };
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;
1190
1191   /* Now group them, in one pass per grouping variable, innermost first. */
1192   for (size_t j = 1; j < n; j++)
1193     {
1194       struct pivot_category **new_cats = xnmalloc (n_cats, sizeof **cats);
1195       size_t n_new_cats = 0;
1196
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;)
1203         {
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];
1209           size_t cat2;
1210           for (cat2 = cat1 + 1; cat2 < n_cats; cat2++)
1211             {
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))
1215                 break;
1216             }
1217           size_t n_subs = cat2 - cat1;
1218
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)
1223             {
1224               /* The existing category stands on its own. */
1225               new_cat = cats[cat1++];
1226             }
1227           else
1228             {
1229               /* Create a new group with cat...cat2 as subcategories. */
1230               new_cat = xzalloc (sizeof *new_cat);
1231               *new_cat = (struct pivot_category) {
1232                 .dimension = d,
1233                 .subs = xnmalloc (n_subs, sizeof *new_cat->subs),
1234                 .n_subs = n_subs,
1235                 .show_label = true,
1236                 .data_index = SIZE_MAX,
1237                 .presentation_index = row1,
1238               };
1239               char *retval = pivot_value_from_data_value (name, NULL, NULL,
1240                                                           &new_cat->name);
1241               if (retval)
1242                 {
1243                   if (error)
1244                     free (retval);
1245                   else
1246                     error = retval;
1247                 }
1248               for (size_t k = 0; k < n_subs; k++)
1249                 new_cat->subs[k] = cats[cat1++];
1250
1251               int dv1_num = dv1->width < 0 ? dv1->d : dv1->index;
1252               series[j]->index_to_category[dv1_num] = new_cat;
1253             }
1254
1255           if (new_cat->name)
1256             add_affixes (table, new_cat->name,
1257                          series[j]->affixes, series[j]->n_affixes);
1258
1259           /* Append the new group to the list of new groups. */
1260           new_cats[n_new_cats++] = new_cat;
1261         }
1262
1263       free (cats);
1264       cats = new_cats;
1265       n_cats = n_new_cats;
1266     }
1267
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);
1271
1272   d->root->subs = cats;
1273   d->root->n_subs = n_cats;
1274
1275   if (error)
1276     {
1277       pivot_dimension_destroy (d);
1278       return error;
1279     }
1280
1281   dim_seriesp[(*n_dim_seriesp)++] = series[0];
1282   series[0]->dimension = d;
1283
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;
1288
1289   *dp = d;
1290   return NULL;
1291 }
1292
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,
1298                 int level_ofs)
1299 {
1300   struct pivot_axis *axis = &table->axes[axis_type];
1301   if (!axis->extent)
1302     axis->extent = 1;
1303
1304   if (!nest)
1305     return NULL;
1306
1307   struct spv_series **series = xnmalloc (nest->n_vars, sizeof *series);
1308   for (size_t i = 0; i < nest->n_vars;)
1309     {
1310       size_t n;
1311       for (n = 0; i + n < nest->n_vars; n++)
1312         {
1313           series[n] = spv_series_from_ref (series_map, nest->vars[i + n]->ref);
1314           if (!series[n] || !series[n]->n_values)
1315             break;
1316         }
1317
1318       if (n > 0)
1319         {
1320           struct pivot_dimension *d;
1321           char *error = add_dimension (series, n, axis_type, v, table,
1322                                        dim_seriesp, n_dim_seriesp,
1323                                        level_ofs + i, &d);
1324           if (error)
1325             {
1326               free (series);
1327               return error;
1328             }
1329         }
1330
1331       i += n + 1;
1332     }
1333   free (series);
1334
1335   return NULL;
1336 }
1337
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,
1343             int level_ofs)
1344 {
1345   struct pivot_axis *axis = &table->axes[PIVOT_AXIS_LAYER];
1346   if (!axis->extent)
1347     axis->extent = 1;
1348
1349   if (!n_layers)
1350     return NULL;
1351
1352   struct spv_series **series = xnmalloc (n_layers, sizeof *series);
1353   for (size_t i = 0; i < n_layers;)
1354     {
1355       size_t n;
1356       for (n = 0; i + n < n_layers; n++)
1357         {
1358           series[n] = spv_series_from_ref (series_map,
1359                                            layers[i + n]->variable);
1360           if (!series[n] || !series[n]->n_values)
1361             break;
1362         }
1363
1364       if (n > 0)
1365         {
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);
1370           if (error)
1371             {
1372               free (series);
1373               return error;
1374             }
1375
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;
1382         }
1383       i += n + 1;
1384     }
1385   free (series);
1386
1387   return NULL;
1388 }
1389
1390 static struct pivot_category *
1391 find_category (struct spv_series *series, int index)
1392 {
1393   return (index >= 0 && index < series->n_index
1394           ? series->index_to_category[index]
1395           : NULL);
1396 }
1397
1398 static bool
1399 int_in_array (int value, const int *array, size_t n)
1400 {
1401   for (size_t i = 0; i < n; i++)
1402     if (array[i] == value)
1403       return true;
1404
1405   return false;
1406 }
1407
1408 static void
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)
1415 {
1416   if (sf)
1417     {
1418       if (sf->reset > 0)
1419         {
1420           free (value->footnotes);
1421           value->footnotes = NULL;
1422           value->n_footnotes = 0;
1423         }
1424
1425       struct fmt_spec format = { .w = 0 };
1426       if (sf->format)
1427         {
1428           format = decode_format (sf->format);
1429           add_affixes (table, value, sf->format->affix, sf->format->n_affix);
1430         }
1431       else if (sf->number_format)
1432         {
1433           format = decode_number_format (sf->number_format);
1434           add_affixes (table, value, sf->number_format->affix,
1435                        sf->number_format->n_affix);
1436         }
1437       else if (sf->n_string_format)
1438         {
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);
1442         }
1443       else if (sf->date_time_format)
1444         {
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);
1448         }
1449       else if (sf->elapsed_time_format)
1450         {
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);
1454         }
1455
1456       if (format.w)
1457         {
1458           if (value->type == PIVOT_VALUE_NUMERIC)
1459             value->numeric.format = format;
1460
1461           /* Possibly we should try to apply date and time formats too,
1462              but none seem to occur in practice so far. */
1463         }
1464     }
1465   if (fg || bg)
1466     {
1467       struct table_area_style area;
1468       pivot_value_get_style (
1469         value,
1470         value->font_style ? value->font_style : &base_area_style->font_style,
1471         value->cell_style ? value->cell_style : &base_area_style->cell_style,
1472         &area);
1473       decode_spvdx_style_incremental (fg, bg, &area);
1474       pivot_value_set_style (value, &area);
1475       table_area_style_uninit (&area);
1476     }
1477 }
1478
1479 static void
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)
1489 {
1490   if (graph && labeling && intersect->alternating
1491       && !interval && !major_ticks && !frame && !set_format)
1492     {
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);
1501     }
1502   else if (graph
1503            && !labeling && !interval && !major_ticks && !frame && !set_format)
1504     {
1505       /* 'graph->width' likely just sets the width of the table as a
1506          whole.  */
1507     }
1508   else if (!graph && !labeling && !interval && !frame && !set_format
1509            && !major_ticks)
1510     {
1511       /* No-op.  (Presumably there's a setMetaData we don't care about.) */
1512     }
1513   else if (((set_format && spvdx_is_major_ticks (set_format->target))
1514             || major_ticks || frame)
1515            && intersect->n_where == 1)
1516     {
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);
1520       assert (s);
1521
1522       const char *p = w->include;
1523
1524       while (*p)
1525         {
1526           char *tail;
1527           int include = strtol (p, &tail, 10);
1528
1529           struct pivot_category *c = find_category (s, include);
1530           if (c)
1531             {
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);
1538             }
1539
1540           if (tail == p)
1541             break;
1542           p = tail;
1543           if (*p == ';')
1544             p++;
1545         }
1546     }
1547   else if ((set_format && spvdx_is_labeling (set_format->target))
1548            || labeling || interval)
1549     {
1550       /* Formatting for individual cells or groups of them with some dimensions
1551          in common. */
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);
1555
1556       for (size_t i = 0; i < intersect->n_where; i++)
1557         {
1558           const struct spvdx_where *w = intersect->where[i];
1559           struct spv_series *s = spv_series_find (series_map, w->variable->id);
1560           assert (s);
1561           if (!s->dimension)
1562             {
1563               /* Group indexes may be included even though they are redundant.
1564                  Ignore them. */
1565               continue;
1566             }
1567
1568           size_t j = s->dimension->top_index;
1569
1570           const char *p = w->include;
1571           while (*p)
1572             {
1573               char *tail;
1574               int include = strtol (p, &tail, 10);
1575
1576               struct pivot_category *c = find_category (s, include);
1577               if (c)
1578                 {
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;
1583                 }
1584
1585               if (tail == p)
1586                 break;
1587               p = tail;
1588               if (*p == ';')
1589                 p++;
1590             }
1591         }
1592
1593 #if 0
1594       printf ("match:");
1595       for (size_t i = 0; i < table->n_dimensions; i++)
1596         {
1597           if (n[i])
1598             {
1599               printf (" %d=(", i);
1600               for (size_t j = 0; j < n[i]; j++)
1601                 {
1602                   if (j)
1603                     putchar (',');
1604                   printf ("%d", indexes[i][j]);
1605                 }
1606               putchar (')');
1607             }
1608         }
1609       printf ("\n");
1610 #endif
1611
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
1617          possibility. */
1618
1619       struct pivot_cell *cell;
1620       HMAP_FOR_EACH (cell, struct pivot_cell, hmap_node, &table->cells)
1621         {
1622           for (size_t i = 0; i < table->n_dimensions; i++)
1623             {
1624               if (n[i] && !int_in_array (cell->idx[i], indexes[i], n[i]))
1625                 goto skip;
1626             }
1627           apply_styles_to_value (table, cell->value, set_format,
1628                                  &table->areas[PIVOT_AREA_DATA],
1629                                  labeling, interval);
1630
1631         skip: ;
1632         }
1633
1634       for (size_t i = 0; i < table->n_dimensions; i++)
1635         free (indexes[i]);
1636       free (indexes);
1637       free (n);
1638       free (allocated);
1639     }
1640   else
1641     NOT_REACHED ();
1642 }
1643
1644 static void
1645 decode_set_cell_properties (struct pivot_table *table, struct hmap *series_map,
1646                             struct spvdx_set_cell_properties **scps,
1647                             size_t n_scps)
1648 {
1649   for (size_t i = 0; i < n_scps; i++)
1650     {
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++)
1659         {
1660           const struct spvxml_node *node = scp->seq[j];
1661           if (spvdx_is_set_style (node))
1662             {
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;
1673               else
1674                 NOT_REACHED ();
1675             }
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);
1680           else
1681             assert (spvdx_is_set_meta_data (node));
1682         }
1683
1684       if (scp->union_ && scp->apply_to_converse <= 0)
1685         {
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);
1690         }
1691       else if (!scp->union_ && scp->apply_to_converse > 0)
1692         {
1693           if ((set_format && spvdx_is_labeling (set_format->target))
1694               || labeling || interval)
1695             {
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],
1700                                        NULL, NULL);
1701             }
1702         }
1703       else if (!scp->union_ && scp->apply_to_converse <= 0)
1704         {
1705           /* Appears to be used to set the font for something--but what? */
1706         }
1707       else
1708         NOT_REACHED ();
1709     }
1710 }
1711
1712 static struct spv_series *
1713 parse_formatting (const struct spvdx_visualization *v,
1714                   const struct hmap *series_map, struct hmap *format_map)
1715 {
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++)
1719     {
1720       const struct spvdx_formatting *f
1721         = spvdx_cast_formatting (labeling->seq[i]);
1722       if (!f)
1723         continue;
1724
1725       cell_format = spv_series_from_ref (series_map, f->variable);
1726       for (size_t j = 0; j < f->n_format_mapping; j++)
1727         {
1728           const struct spvdx_format_mapping *fm = f->format_mapping[j];
1729
1730           if (fm->format)
1731             {
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));
1737             }
1738         }
1739     }
1740
1741   return cell_format;
1742 }
1743
1744 static void
1745 format_map_destroy (struct hmap *format_map)
1746 {
1747   struct format_mapping *fm, *next;
1748   HMAP_FOR_EACH_SAFE (fm, next, struct format_mapping, hmap_node, format_map)
1749     {
1750       hmap_delete (format_map, &fm->hmap_node);
1751       free (fm);
1752     }
1753   hmap_destroy (format_map);
1754 }
1755
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)
1760 {
1761   struct pivot_table *table = pivot_table_create__ (NULL, subtype);
1762   spv_table_look_install (look, table);
1763
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;
1767   char *error;
1768
1769   struct spvdx_visualization_extension *ve = v->visualization_extension;
1770   table->show_grid_lines = ve && ve->show_gridline;
1771
1772   /* Sizing from the legacy properties can get overridden. */
1773   if (v->graph->cell_style->width)
1774     {
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')
1779         {
1780           table->sizing[TABLE_HORZ].range[0] = min_width;
1781           table->sizing[TABLE_HORZ].range[1] = max_width;
1782         }
1783     }
1784
1785   /* Footnotes.
1786
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.
1793
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. */
1797   if (v->container)
1798     for (size_t i = 0; i < v->container->n_label_frame; i++)
1799       {
1800         const struct spvdx_label_frame *lf = v->container->label_frame[i];
1801         if (lf->label
1802             && lf->label->purpose == SPVDX_PURPOSE_FOOTNOTE
1803             && lf->label->n_text > 0
1804             && lf->label->text[0]->uses_reference > 0)
1805           {
1806             pivot_table_create_footnote__ (
1807               table, lf->label->text[0]->uses_reference - 1,
1808               NULL, NULL);
1809           }
1810       }
1811
1812   if (v->graph->interval->footnotes)
1813     decode_footnotes (table, v->graph->interval->footnotes);
1814
1815   struct spv_series *footnotes = NULL;
1816   for (size_t i = 0; i < v->graph->interval->labeling->n_seq; i++)
1817     {
1818       const struct spvxml_node *node = v->graph->interval->labeling->seq[i];
1819       if (spvdx_is_footnotes (node))
1820         {
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);
1824         }
1825     }
1826   for (size_t i = 0; i < v->n_lf1; i++)
1827     {
1828       error = decode_label_frame (table, v->lf1[i]);
1829       if (error)
1830         goto exit;
1831     }
1832   for (size_t i = 0; i < v->n_lf2; i++)
1833     {
1834       error = decode_label_frame (table, v->lf2[i]);
1835       if (error)
1836         goto exit;
1837     }
1838   if (v->container)
1839     for (size_t i = 0; i < v->container->n_label_frame; i++)
1840       {
1841         error = decode_label_frame (table, v->container->label_frame[i]);
1842         if (error)
1843           goto exit;
1844       }
1845   if (v->graph->interval->labeling->style)
1846     {
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]);
1851     }
1852
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;
1856   while (n_nodes > 0)
1857     {
1858       bool progress = false;
1859       for (size_t i = 0; i < n_nodes;)
1860         {
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));
1864           if (!error)
1865             {
1866               nodes[i] = nodes[--n_nodes];
1867               progress = true;
1868             }
1869           else if (error == &BAD_REFERENCE)
1870             i++;
1871           else
1872             {
1873               free (nodes);
1874               goto exit;
1875             }
1876         }
1877
1878       if (!progress)
1879         {
1880           free (nodes);
1881           error = xasprintf ("Table has %zu variables with circular or "
1882                              "unresolved references, including variable %s.",
1883                              n_nodes, nodes[0]->id);
1884           goto exit;
1885         }
1886     }
1887   free (nodes);
1888
1889   const struct spvdx_cross *cross = v->graph->faceting->cross;
1890
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;
1894
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;
1898
1899   size_t max_layers = (v->graph->faceting->n_layers1
1900                        + v->graph->faceting->n_layers2);
1901
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;
1906
1907   error = add_dimensions (&series_map, columns, PIVOT_AXIS_COLUMN, v, table,
1908                           dim_series, &n_dim_series, 1);
1909   if (error)
1910     goto exit;
1911
1912   error = add_dimensions (&series_map, rows, PIVOT_AXIS_ROW, v, table,
1913                           dim_series, &n_dim_series, max_columns + 1);
1914   if (error)
1915     goto exit;
1916
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);
1921   if (error)
1922     goto exit;
1923
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
1928                        + 1));
1929   if (error)
1930     goto exit;
1931
1932   struct spv_series *cell = spv_series_find (&series_map, "cell");
1933   if (!cell)
1934     {
1935       error = xstrdup (_("Table lacks cell data."));
1936       goto exit;
1937     }
1938
1939   struct spv_series *cell_format = parse_formatting (v, &series_map,
1940                                                      &format_map);
1941
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++)
1945     {
1946       for (size_t j = 0; j < table->n_dimensions; j++)
1947         {
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);
1951           if (!cat)
1952             goto skip;
1953           dim_indexes[j] = cat->data_index;
1954         }
1955
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);
1960       if (error)
1961         goto exit;
1962
1963       if (footnotes)
1964         {
1965           const struct spv_data_value *d = &footnotes->values[i];
1966           if (d->width >= 0)
1967             {
1968               const char *p = d->s;
1969               while (*p)
1970                 {
1971                   char *tail;
1972                   int idx = strtol (p, &tail, 10);
1973                   add_footnote (value, idx, table);
1974                   if (tail == p)
1975                     break;
1976                   p = tail;
1977                   if (*p == ',')
1978                     p++;
1979                 }
1980             }
1981         }
1982
1983       if (value->type == PIVOT_VALUE_NUMERIC
1984           && value->numeric.x == SYSMIS
1985           && !value->n_footnotes)
1986         {
1987           /* Apparently, system-missing values are just empty cells? */
1988           pivot_value_destroy (value);
1989         }
1990       else
1991         pivot_table_put (table, dim_indexes, table->n_dimensions, value);
1992     skip:;
1993     }
1994   free (dim_indexes);
1995
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);
2000
2001   pivot_table_assign_label_depth (table);
2002
2003   format_map_destroy (&format_map);
2004
2005 exit:
2006   free (dim_series);
2007   spv_series_destroy (&series_map);
2008   if (error)
2009     {
2010       pivot_table_unref (table);
2011       *outp = NULL;
2012     }
2013   else
2014     *outp = table;
2015   return error;
2016 }