spv-legacy-decoder: Always give the pivot table a title.
[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,
207                                              settings_get_fmt_settings (),
208                                              NULL);
209           mapping->to.width = strlen (mapping->to.s);
210         }
211     }
212   else
213     {
214       mapping->to.width = strlen (to);
215       mapping->to.s = xstrdup (to);
216     }
217
218   struct spv_mapping *old_mapping = spv_map_search (map, from);
219   if (old_mapping)
220     {
221       bool same = spv_data_value_equal (&old_mapping->to, &mapping->to);
222       spv_data_value_uninit (&mapping->to);
223       free (mapping);
224       return (same ? NULL
225               : xasprintf ("Duplicate relabeling differs for from=\"%.*g\"",
226                            DBL_DIG + 1, from));
227     }
228
229   hmap_insert (map, &mapping->hmap_node, hash_double (from, 0));
230   return NULL;
231 }
232
233 static void
234 spv_map_destroy (struct hmap *map)
235 {
236   struct spv_mapping *mapping, *next;
237   HMAP_FOR_EACH_SAFE (mapping, next, struct spv_mapping, hmap_node, map)
238     {
239       spv_data_value_uninit (&mapping->to);
240       hmap_delete (map, &mapping->hmap_node);
241       free (mapping);
242     }
243   hmap_destroy (map);
244 }
245
246 static char * WARN_UNUSED_RESULT
247 spv_series_parse_relabels (struct hmap *map,
248                            struct spvdx_relabel **relabels, size_t n_relabels,
249                            bool try_strings_as_numbers,
250                            const struct fmt_spec *format)
251 {
252   for (size_t i = 0; i < n_relabels; i++)
253     {
254       const struct spvdx_relabel *relabel = relabels[i];
255       char *error = spv_map_insert (map, relabel->from, relabel->to,
256                                     try_strings_as_numbers, format);
257       if (error)
258         return error;
259     }
260   return NULL;
261 }
262
263 static char * WARN_UNUSED_RESULT
264 spv_series_parse_value_map_entry (struct hmap *map,
265                                   const struct spvdx_value_map_entry *vme)
266 {
267   for (const char *p = vme->from; ; p++)
268     {
269       int save_errno = errno;
270       errno = 0;
271       char *end;
272       double from = c_strtod (p, &end);
273       bool ok = !errno && end > p && strchr (";", *end);
274       errno = save_errno;
275       if (!ok)
276         return xasprintf ("Syntax error in valueMapEntry from=\"%s\".",
277                           vme->from);
278
279       char *error = spv_map_insert (map, from, vme->to, true,
280                                     &(struct fmt_spec) { .type = FMT_A, .w = 40 });
281       if (error)
282         return error;
283
284       p = end;
285       if (*p == '\0')
286         return NULL;
287       assert (*p == ';');
288     }
289 }
290
291 static struct fmt_spec
292 decode_date_time_format (const struct spvdx_date_time_format *dtf)
293 {
294   if (dtf->dt_base_format == SPVDX_DT_BASE_FORMAT_DATE)
295     {
296       enum fmt_type type
297         = (dtf->show_quarter > 0 ? FMT_QYR
298            : dtf->show_week > 0 ? FMT_WKYR
299            : dtf->mdy_order == SPVDX_MDY_ORDER_DAY_MONTH_YEAR
300            ? (dtf->month_format == SPVDX_MONTH_FORMAT_NUMBER
301               || dtf->month_format == SPVDX_MONTH_FORMAT_PADDED_NUMBER
302               ? FMT_EDATE : FMT_DATE)
303            : dtf->mdy_order == SPVDX_MDY_ORDER_YEAR_MONTH_DAY ? FMT_SDATE
304            : FMT_ADATE);
305
306       int w = fmt_min_output_width (type);
307       if (dtf->year_abbreviation <= 0)
308         w += 2;
309       return (struct fmt_spec) { .type = type, .w = w };
310     }
311   else
312     {
313       enum fmt_type type
314         = (dtf->dt_base_format == SPVDX_DT_BASE_FORMAT_DATE_TIME
315            ? (dtf->mdy_order == SPVDX_MDY_ORDER_YEAR_MONTH_DAY
316               ? FMT_YMDHMS
317               : FMT_DATETIME)
318            : (dtf->show_day > 0 ? FMT_DTIME
319               : dtf->show_hour > 0 ? FMT_TIME
320               : FMT_MTIME));
321       int w = fmt_min_output_width (type);
322       int d = 0;
323       if (dtf->show_second > 0)
324         {
325           w += 3;
326           if (dtf->show_millis > 0)
327             {
328               d = 3;
329               w += d + 1;
330             }
331         }
332       return (struct fmt_spec) { .type = type, .w = w, .d = d };
333     }
334 }
335
336 static struct fmt_spec
337 decode_elapsed_time_format (const struct spvdx_elapsed_time_format *etf)
338 {
339   enum fmt_type type
340     = (etf->dt_base_format != SPVDX_DT_BASE_FORMAT_TIME ? FMT_DTIME
341        : etf->show_hour > 0 ? FMT_TIME
342        : FMT_MTIME);
343   int w = fmt_min_output_width (type);
344   int d = 0;
345   if (etf->show_second > 0)
346     {
347       w += 3;
348       if (etf->show_millis > 0)
349         {
350           d = 3;
351           w += d + 1;
352         }
353     }
354   return (struct fmt_spec) { .type = type, .w = w, .d = d };
355 }
356
357 static struct fmt_spec
358 decode_number_format (const struct spvdx_number_format *nf)
359 {
360   enum fmt_type type = (nf->scientific == SPVDX_SCIENTIFIC_TRUE ? FMT_E
361                         : nf->prefix && !strcmp (nf->prefix, "$") ? FMT_DOLLAR
362                         : nf->suffix && !strcmp (nf->suffix, "%") ? FMT_PCT
363                         : nf->use_grouping ? FMT_COMMA
364                         : FMT_F);
365
366   int d = nf->maximum_fraction_digits;
367   if (d < 0 || d > 15)
368     d = 2;
369
370   struct fmt_spec f = (struct fmt_spec) { .type = type, .w = 40, .d = d };
371   fmt_fix_output (&f);
372   return f;
373 }
374
375 /* Returns an *approximation* of IN as a fmt_spec.
376
377    Not for use with string formats, which don't have any options anyway. */
378 static struct fmt_spec
379 decode_format (const struct spvdx_format *in)
380 {
381   if (in->f_base_format == SPVDX_F_BASE_FORMAT_DATE ||
382       in->f_base_format == SPVDX_F_BASE_FORMAT_TIME ||
383       in->f_base_format == SPVDX_F_BASE_FORMAT_DATE_TIME)
384     {
385       struct spvdx_date_time_format dtf = {
386         .dt_base_format = (in->f_base_format == SPVDX_F_BASE_FORMAT_DATE
387                            ? SPVDX_DT_BASE_FORMAT_DATE
388                            : in->f_base_format == SPVDX_F_BASE_FORMAT_TIME
389                            ? SPVDX_DT_BASE_FORMAT_TIME
390                            : SPVDX_DT_BASE_FORMAT_DATE_TIME),
391         .separator_chars = in->separator_chars,
392         .mdy_order = in->mdy_order,
393         .show_year = in->show_year,
394         .year_abbreviation = in->year_abbreviation,
395         .show_quarter = in->show_quarter,
396         .quarter_prefix = in->quarter_prefix,
397         .quarter_suffix = in->quarter_suffix,
398         .show_month = in->show_month,
399         .month_format = in->month_format,
400         .show_week = in->show_week,
401         .week_padding = in->week_padding,
402         .week_suffix = in->week_suffix,
403         .show_day_of_week = in->show_day_of_week,
404         .day_of_week_abbreviation = in->day_of_week_abbreviation,
405         .day_padding = in->day_padding,
406         .day_of_month_padding = in->day_of_month_padding,
407         .hour_padding = in->hour_padding,
408         .minute_padding = in->minute_padding,
409         .second_padding = in->second_padding,
410         .show_day = in->show_day,
411         .show_hour = in->show_hour,
412         .show_minute = in->show_minute,
413         .show_second = in->show_second,
414         .show_millis = in->show_millis,
415         .day_type = in->day_type,
416         .hour_format = in->hour_format,
417       };
418       return decode_date_time_format (&dtf);
419     }
420   else if (in->f_base_format == SPVDX_F_BASE_FORMAT_ELAPSED_TIME)
421     {
422       struct spvdx_elapsed_time_format etf = {
423         .dt_base_format = (in->f_base_format == SPVDX_F_BASE_FORMAT_DATE
424                            ? SPVDX_DT_BASE_FORMAT_DATE
425                            : in->f_base_format == SPVDX_F_BASE_FORMAT_TIME
426                            ? SPVDX_DT_BASE_FORMAT_TIME
427                            : SPVDX_DT_BASE_FORMAT_DATE_TIME),
428         .day_padding = in->day_padding,
429         .minute_padding = in->minute_padding,
430         .second_padding = in->second_padding,
431         .show_year = in->show_year,
432         .show_day = in->show_day,
433         .show_hour = in->show_hour,
434         .show_minute = in->show_minute,
435         .show_second = in->show_second,
436         .show_millis = in->show_millis,
437       };
438       return decode_elapsed_time_format (&etf);
439     }
440   else
441     {
442       assert (!in->f_base_format);
443       struct spvdx_number_format nf = {
444         .minimum_integer_digits = in->minimum_integer_digits,
445         .maximum_fraction_digits = in->maximum_fraction_digits,
446         .minimum_fraction_digits = in->minimum_fraction_digits,
447         .use_grouping = in->use_grouping,
448         .scientific = in->scientific,
449         .small = in->small,
450         .prefix = in->prefix,
451         .suffix = in->suffix,
452       };
453       return decode_number_format (&nf);
454     }
455 }
456
457 static void
458 spv_series_execute_mapping (struct spv_series *series)
459 {
460   if (!hmap_is_empty (&series->map))
461     {
462       series->remapped = true;
463       for (size_t i = 0; i < series->n_values; i++)
464         {
465           struct spv_data_value *value = &series->values[i];
466           if (value->width >= 0)
467             continue;
468
469           const struct spv_mapping *mapping = spv_map_search (&series->map,
470                                                               value->d);
471           if (mapping)
472             {
473               value->index = value->d;
474               assert (value->index == floor (value->index));
475               value->width = mapping->to.width;
476               if (value->width >= 0)
477                 value->s = xmemdup0 (mapping->to.s, mapping->to.width);
478               else
479                 value->d = mapping->to.d;
480             }
481         }
482     }
483 }
484
485 static char * WARN_UNUSED_RESULT
486 spv_series_remap_formats (struct spv_series *series,
487                           struct spvxml_node **seq, size_t n_seq)
488 {
489   spv_map_destroy (&series->map);
490   hmap_init (&series->map);
491   for (size_t i = 0; i < n_seq; i++)
492     {
493       struct spvxml_node *node = seq[i];
494       if (spvdx_is_format (node))
495         {
496           struct spvdx_format *f = spvdx_cast_format (node);
497           series->format = decode_format (f);
498           char *error = spv_series_parse_relabels (
499             &series->map, f->relabel, f->n_relabel,
500             f->try_strings_as_numbers > 0, &series->format);
501           if (error)
502             return error;
503
504           series->affixes = f->affix;
505           series->n_affixes = f->n_affix;
506         }
507       else if (spvdx_is_string_format (node))
508         {
509           struct spvdx_string_format *sf = spvdx_cast_string_format (node);
510           char *error = spv_series_parse_relabels (&series->map,
511                                                    sf->relabel, sf->n_relabel,
512                                                    false, NULL);
513           if (error)
514             return error;
515
516           series->affixes = sf->affix;
517           series->n_affixes = sf->n_affix;
518         }
519       else
520         NOT_REACHED ();
521     }
522   spv_series_execute_mapping (series);
523   return NULL;
524 }
525
526 static char * WARN_UNUSED_RESULT
527 spv_series_remap_vmes (struct spv_series *series,
528                        struct spvdx_value_map_entry **vmes,
529                        size_t n_vmes)
530 {
531   spv_map_destroy (&series->map);
532   hmap_init (&series->map);
533   for (size_t i = 0; i < n_vmes; i++)
534     {
535       char *error = spv_series_parse_value_map_entry (&series->map, vmes[i]);
536       if (error)
537         return error;
538     }
539   spv_series_execute_mapping (series);
540   return NULL;
541 }
542
543 static void
544 decode_footnotes (struct pivot_table *table, const struct spvdx_footnotes *f)
545 {
546   if (f->n_footnote_mapping > 0)
547     pivot_table_create_footnote__ (table, f->n_footnote_mapping - 1,
548                                    NULL, NULL);
549   for (size_t i = 0; i < f->n_footnote_mapping; i++)
550     {
551       const struct spvdx_footnote_mapping *fm = f->footnote_mapping[i];
552       pivot_table_create_footnote__ (table, fm->defines_reference - 1,
553                                      pivot_value_new_user_text (fm->to, -1),
554                                      NULL);
555     }
556 }
557
558 static struct cell_color
559 optional_color (int color, struct cell_color default_color)
560 {
561   return (color >= 0
562           ? (struct cell_color) CELL_COLOR (color >> 16, color >> 8, color)
563           : default_color);
564 }
565
566 static int
567 optional_length (const char *s, int default_length)
568 {
569   /* There is usually a "pt" suffix.  We ignore it. */
570   int length;
571   return s && sscanf (s, "%d", &length) == 1 ? length : default_length;
572 }
573
574 static int
575 optional_px (double inches, int default_px)
576 {
577   return inches != DBL_MAX ? inches * 96.0 : default_px;
578 }
579
580 static void
581 decode_spvdx_style_incremental (const struct spvdx_style *in,
582                                 const struct spvdx_style *bg,
583                                 struct table_area_style *out)
584 {
585   if (in && in->font_weight)
586     out->font_style.bold = in->font_weight == SPVDX_FONT_WEIGHT_BOLD;
587   if (in && in->font_style)
588     out->font_style.italic = in->font_style == SPVDX_FONT_STYLE_ITALIC;
589   if (in && in->font_underline)
590     out->font_style.underline = in->font_underline == SPVDX_FONT_UNDERLINE_UNDERLINE;
591   if (in && in->color >= 0)
592     {
593       out->font_style.fg[0] = optional_color (
594         in->color, (struct cell_color) CELL_COLOR_BLACK);
595       out->font_style.fg[1] = out->font_style.fg[0];
596     }
597   if (bg && bg->color >= 0)
598     {
599       out->font_style.bg[0] = optional_color (
600         bg->color, (struct cell_color) CELL_COLOR_WHITE);
601       out->font_style.bg[1] = out->font_style.bg[0];
602     }
603   if (in && in->font_family)
604     {
605       free (out->font_style.typeface);
606       out->font_style.typeface = xstrdup (in->font_family);
607     }
608   if (in && in->font_size)
609     {
610       int size = optional_length (in->font_size, 0);
611       if (size)
612         out->font_style.size = size;
613     }
614   if (in && in->text_alignment)
615     out->cell_style.halign
616       = (in->text_alignment == SPVDX_TEXT_ALIGNMENT_LEFT
617          ? TABLE_HALIGN_LEFT
618          : in->text_alignment == SPVDX_TEXT_ALIGNMENT_RIGHT
619          ? TABLE_HALIGN_RIGHT
620          : in->text_alignment == SPVDX_TEXT_ALIGNMENT_CENTER
621          ? TABLE_HALIGN_CENTER
622          : in->text_alignment == SPVDX_TEXT_ALIGNMENT_DECIMAL
623          ? TABLE_HALIGN_DECIMAL
624          : TABLE_HALIGN_MIXED);
625   if (in && in->label_location_vertical)
626     out->cell_style.valign =
627       (in->label_location_vertical == SPVDX_LABEL_LOCATION_VERTICAL_NEGATIVE
628        ? TABLE_VALIGN_BOTTOM
629        : in->label_location_vertical == SPVDX_LABEL_LOCATION_VERTICAL_POSITIVE
630        ? TABLE_VALIGN_TOP
631        : TABLE_VALIGN_CENTER);
632   if (in && in->decimal_offset != DBL_MAX)
633     out->cell_style.decimal_offset = optional_px (in->decimal_offset, 0);
634 #if 0
635   if (in && in->margin_left != DBL_MAX)
636     out->cell_style.margin[TABLE_HORZ][0] = optional_pt (in->margin_left, 8);
637   if (in && in->margin_right != DBL_MAX)
638     out->cell_style.margin[TABLE_HORZ][1] = optional_pt (in->margin_right, 11);
639   if (in && in->margin_top != DBL_MAX)
640     out->cell_style.margin[TABLE_VERT][0] = optional_pt (in->margin_top, 1);
641   if (in && in->margin_bottom != DBL_MAX)
642     out->cell_style.margin[TABLE_VERT][1] = optional_pt (in->margin_bottom, 1);
643 #endif
644 }
645
646 static void
647 decode_spvdx_style (const struct spvdx_style *in,
648                     const struct spvdx_style *bg,
649                     struct table_area_style *out)
650 {
651   *out = (struct table_area_style) TABLE_AREA_STYLE_INITIALIZER;
652   decode_spvdx_style_incremental (in, bg, out);
653 }
654
655 static void
656 add_footnote (struct pivot_value *v, int idx, struct pivot_table *table)
657 {
658   if (idx < 1 || idx > table->n_footnotes)
659     return;
660
661   pivot_value_add_footnote (v, table->footnotes[idx - 1]);
662 }
663
664 static char * WARN_UNUSED_RESULT
665 decode_label_frame (struct pivot_table *table,
666                     const struct spvdx_label_frame *lf)
667 {
668   if (!lf->label)
669     return NULL;
670
671   struct pivot_value **target;
672   struct table_area_style *area;
673   if (lf->label->purpose == SPVDX_PURPOSE_TITLE)
674     {
675       target = &table->title;
676       area = &table->look->areas[PIVOT_AREA_TITLE];
677     }
678   else if (lf->label->purpose == SPVDX_PURPOSE_SUB_TITLE)
679     {
680       target = &table->caption;
681       area = &table->look->areas[PIVOT_AREA_CAPTION];
682     }
683   else if (lf->label->purpose == SPVDX_PURPOSE_FOOTNOTE)
684     {
685       if (lf->label->n_text > 0
686           && lf->label->text[0]->uses_reference != INT_MIN)
687         {
688           target = NULL;
689           area = &table->look->areas[PIVOT_AREA_FOOTER];
690         }
691       else
692         return NULL;
693     }
694   else if (lf->label->purpose == SPVDX_PURPOSE_LAYER)
695     {
696       target = NULL;
697       area = &table->look->areas[PIVOT_AREA_LAYERS];
698     }
699   else
700     return NULL;
701
702   table_area_style_uninit (area);
703   decode_spvdx_style (lf->label->style, lf->label->text_frame_style, area);
704
705   if (target)
706     {
707       struct pivot_value *value = XZALLOC (struct pivot_value);
708       value->type = PIVOT_VALUE_TEXT;
709       for (size_t i = 0; i < lf->label->n_text; i++)
710         {
711           const struct spvdx_text *in = lf->label->text[i];
712           if (in->defines_reference != INT_MIN)
713             add_footnote (value, in->defines_reference, table);
714           else if (!value->text.local)
715             value->text.local = xstrdup (in->text);
716           else
717             {
718               char *new = xasprintf ("%s%s", value->text.local, in->text);
719               free (value->text.local);
720               value->text.local = new;
721             }
722         }
723       if (!value->text.local)
724         value->text.local = xstrdup ("");
725       value->text.c = value->text.id = value->text.local;
726       pivot_value_destroy (*target);
727       *target = value;
728     }
729   else
730     for (size_t i = 0; i < lf->label->n_text; i++)
731       {
732         const struct spvdx_text *in = lf->label->text[i];
733         if (in->uses_reference == INT_MIN)
734           continue;
735         if (i % 2)
736           {
737             size_t length = strlen (in->text);
738             if (length && in->text[length - 1] == '\n')
739               length--;
740
741             pivot_table_create_footnote__ (
742               table, in->uses_reference - 1, NULL,
743               pivot_value_new_user_text (in->text, length));
744           }
745         else
746           {
747             size_t length = strlen (in->text);
748             if (length && in->text[length - 1] == '.')
749               length--;
750
751             pivot_table_create_footnote__ (
752               table, in->uses_reference - 1,
753               pivot_value_new_user_text (in->text, length), NULL);
754           }
755       }
756   return NULL;
757 }
758
759 /* Special return value for decode_spvdx_variable(). */
760 static char BAD_REFERENCE;
761
762 static char * WARN_UNUSED_RESULT
763 decode_spvdx_source_variable (const struct spvxml_node *node,
764                               const struct spv_data *data,
765                               struct hmap *series_map)
766 {
767   const struct spvdx_source_variable *sv = spvdx_cast_source_variable (node);
768
769   struct spv_series *label_series = NULL;
770   if (sv->label_variable)
771     {
772       label_series = spv_series_find (series_map,
773                                       sv->label_variable->node_.id);
774       if (!label_series)
775         return &BAD_REFERENCE;
776
777       label_series->is_label_series = true;
778     }
779
780   const struct spv_data_variable *var = spv_data_find_variable (
781     data, sv->source, sv->source_name);
782   if (!var)
783     return xasprintf ("sourceVariable %s references nonexistent "
784                       "source %s variable %s.",
785                       sv->node_.id, sv->source, sv->source_name);
786
787   struct spv_series *s = XZALLOC (struct spv_series);
788   s->name = xstrdup (node->id);
789   s->xml = node;
790   s->label = xstrdup_if_nonnull (sv->label);
791   s->label_series = label_series;
792   s->values = spv_data_values_clone (var->values, var->n_values);
793   s->n_values = var->n_values;
794   s->format = F_8_0;
795   hmap_init (&s->map);
796   hmap_insert (series_map, &s->hmap_node, hash_string (s->name, 0));
797
798   char *error = spv_series_remap_formats (s, sv->seq, sv->n_seq);
799   if (error)
800     return error;
801
802   if (label_series && !s->remapped)
803     {
804       for (size_t i = 0; i < s->n_values; i++)
805         if (s->values[i].width < 0)
806           {
807             char *dest;
808             if (label_series->values[i].width < 0)
809               {
810                 union value v = { .f = label_series->values[i].d };
811                 dest = data_out_stretchy (&v, "UTF-8", &s->format,
812                                           settings_get_fmt_settings (), NULL);
813               }
814             else
815               dest = label_series->values[i].s;
816             char *error = spv_map_insert (&s->map, s->values[i].d,
817                                           dest, false, NULL);
818             free (error);   /* Duplicates are OK. */
819             if (label_series->values[i].width < 0)
820               free (dest);
821           }
822     }
823
824   return NULL;
825 }
826
827 static char * WARN_UNUSED_RESULT
828 decode_spvdx_derived_variable (const struct spvxml_node *node,
829                                struct hmap *series_map)
830 {
831   const struct spvdx_derived_variable *dv = spvdx_cast_derived_variable (node);
832
833   struct spv_data_value *values;
834   size_t n_values;
835
836   struct substring value = ss_cstr (dv->value);
837   if (ss_equals (value, ss_cstr ("constant(0)")))
838     {
839       struct spv_series *existing_series = spv_series_first (series_map);
840       if (!existing_series)
841         return &BAD_REFERENCE;
842
843       n_values = existing_series->n_values;
844       values = XCALLOC (n_values, struct spv_data_value);
845       for (size_t i = 0; i < n_values; i++)
846         values[i].width = -1;
847     }
848   else if (ss_starts_with (value, ss_cstr ("constant(")))
849     {
850       values = NULL;
851       n_values = 0;
852     }
853   else if (ss_starts_with (value, ss_cstr ("map("))
854            && ss_ends_with (value, ss_cstr (")")))
855     {
856       char *dependency_name = ss_xstrdup (ss_substr (value, 4,
857                                                      value.length - 5));
858       struct spv_series *dependency
859         = spv_series_find (series_map, dependency_name);
860       free (dependency_name);
861       if (!dependency)
862         return &BAD_REFERENCE;
863
864       values = spv_data_values_clone (dependency->values,
865                                       dependency->n_values);
866       n_values = dependency->n_values;
867     }
868   else
869     return xasprintf ("Derived variable %s has unknown value \"%s\"",
870                       node->id, dv->value);
871
872   struct spv_series *s = XZALLOC (struct spv_series);
873   s->format = F_8_0;
874   s->name = xstrdup (node->id);
875   s->values = values;
876   s->n_values = n_values;
877   hmap_init (&s->map);
878   hmap_insert (series_map, &s->hmap_node, hash_string (s->name, 0));
879
880   char *error = spv_series_remap_vmes (s, dv->value_map_entry,
881                                        dv->n_value_map_entry);
882   if (error)
883     return error;
884
885   error = spv_series_remap_formats (s, dv->seq, dv->n_seq);
886   if (error)
887     return error;
888
889   if (n_values > 0)
890     {
891       for (size_t i = 0; i < n_values; i++)
892         if (values[i].width != 0)
893           goto nonempty;
894       for (size_t i = 0; i < n_values; i++)
895         spv_data_value_uninit (&s->values[i]);
896       free (s->values);
897
898       s->values = NULL;
899       s->n_values = 0;
900
901     nonempty:;
902     }
903   return NULL;
904 }
905
906 struct format_mapping
907   {
908     struct hmap_node hmap_node;
909     uint32_t from;
910     struct fmt_spec to;
911   };
912
913 static const struct format_mapping *
914 format_map_find (const struct hmap *format_map, uint32_t u32_format)
915 {
916   if (format_map)
917     {
918       const struct format_mapping *fm;
919       HMAP_FOR_EACH_IN_BUCKET (fm, struct format_mapping, hmap_node,
920                                hash_int (u32_format, 0), format_map)
921         if (fm->from == u32_format)
922           return fm;
923     }
924
925   return NULL;
926 }
927
928 static char * WARN_UNUSED_RESULT
929 spv_format_from_data_value (const struct spv_data_value *data,
930                             const struct hmap *format_map,
931                             struct fmt_spec *out)
932 {
933   if (!data)
934     {
935       *out = fmt_for_output (FMT_F, 40, 2);
936       return NULL;
937     }
938
939   uint32_t u32_format = data->width < 0 ? data->d : atoi (data->s);
940   const struct format_mapping *fm = format_map_find (format_map, u32_format);
941   if (fm)
942     {
943       *out = fm->to;
944       return NULL;
945     }
946   return spv_decode_fmt_spec (u32_format, out);
947 }
948
949 static char * WARN_UNUSED_RESULT
950 pivot_value_from_data_value (const struct spv_data_value *data,
951                              const struct spv_data_value *format,
952                              const struct hmap *format_map,
953                              struct pivot_value **vp)
954 {
955   *vp = NULL;
956
957   struct fmt_spec f;
958   char *error = spv_format_from_data_value (format, format_map, &f);
959   if (error)
960     return error;
961
962   struct pivot_value *v = XZALLOC (struct pivot_value);
963   if (data->width >= 0)
964     {
965       if (format && fmt_get_category (f.type) == FMT_CAT_DATE)
966         {
967           int year, month, day, hour, minute, second, msec, len = -1;
968           if (sscanf (data->s, "%4d-%2d-%2dT%2d:%2d:%2d.%3d%n",
969                       &year, &month, &day, &hour, &minute, &second,
970                       &msec, &len) == 7
971               && len == 23
972               && data->s[len] == '\0')
973             {
974               double date = calendar_gregorian_to_offset (
975                 year, month, day, settings_get_fmt_settings (), NULL);
976               if (date != SYSMIS)
977                 {
978                   v->type = PIVOT_VALUE_NUMERIC;
979                   v->numeric.x = (date * 60. * 60. * 24.
980                                   + hour * 60. * 60.
981                                   + minute * 60.
982                                   + second
983                                   + msec / 1000.0);
984                   v->numeric.format = f;
985                   *vp = v;
986                   return NULL;
987                 }
988             }
989         }
990       else if (format && fmt_get_category (f.type) == FMT_CAT_TIME)
991         {
992           int hour, minute, second, msec, len = -1;
993           if (sscanf (data->s, "%d:%2d:%2d.%3d%n",
994                       &hour, &minute, &second, &msec, &len) == 4
995               && len > 0
996               && data->s[len] == '\0')
997             {
998               v->type = PIVOT_VALUE_NUMERIC;
999               v->numeric.x = (hour * 60. * 60.
1000                               + minute * 60.
1001                               + second
1002                               + msec / 1000.0);
1003               v->numeric.format = f;
1004               *vp = v;
1005               return NULL;
1006             }
1007         }
1008       v->type = PIVOT_VALUE_STRING;
1009       v->string.s = xstrdup (data->s);
1010     }
1011   else
1012     {
1013       v->type = PIVOT_VALUE_NUMERIC;
1014       v->numeric.x = data->d;
1015       v->numeric.format = f;
1016     }
1017   *vp = v;
1018   return NULL;
1019 }
1020
1021 static void
1022 add_parents (struct pivot_category *cat, struct pivot_category *parent,
1023              size_t group_index)
1024 {
1025   cat->parent = parent;
1026   cat->group_index = group_index;
1027   if (pivot_category_is_group (cat))
1028     for (size_t i = 0; i < cat->n_subs; i++)
1029       add_parents (cat->subs[i], cat, i);
1030 }
1031
1032 static const struct spvdx_facet_level *
1033 find_facet_level (const struct spvdx_visualization *v, int facet_level)
1034 {
1035   const struct spvdx_facet_layout *layout = v->graph->facet_layout;
1036   for (size_t i = 0; i < layout->n_facet_level; i++)
1037     {
1038       const struct spvdx_facet_level *fl = layout->facet_level[i];
1039       if (facet_level == fl->level)
1040         return fl;
1041     }
1042   return NULL;
1043 }
1044
1045 static bool
1046 should_show_label (const struct spvdx_facet_level *fl)
1047 {
1048   return fl && fl->axis->label && fl->axis->label->style->visible != 0;
1049 }
1050
1051 static size_t
1052 max_category (const struct spv_series *s)
1053 {
1054   double max_cat = -DBL_MAX;
1055   for (size_t i = 0; i < s->n_values; i++)
1056     {
1057       const struct spv_data_value *dv = &s->values[i];
1058       double d = dv->width < 0 ? dv->d : dv->index;
1059       if (d > max_cat)
1060         max_cat = d;
1061     }
1062   assert (max_cat >= 0 && max_cat < SIZE_MAX - 1);
1063
1064   return max_cat;
1065 }
1066
1067 static void
1068 add_affixes (struct pivot_table *table, struct pivot_value *value,
1069              struct spvdx_affix **affixes, size_t n_affixes)
1070 {
1071   for (size_t i = 0; i < n_affixes; i++)
1072     add_footnote (value, affixes[i]->defines_reference, table);
1073 }
1074
1075 static char * WARN_UNUSED_RESULT
1076 add_dimension (struct spv_series **series, size_t n,
1077                enum pivot_axis_type axis_type,
1078                const struct spvdx_visualization *v, struct pivot_table *table,
1079                struct spv_series **dim_seriesp, size_t *n_dim_seriesp,
1080                int base_facet_level, struct pivot_dimension **dp)
1081 {
1082   char *error = NULL;
1083
1084   const struct spvdx_facet_level *fl
1085     = find_facet_level (v, base_facet_level + n);
1086   if (fl)
1087     {
1088       struct table_area_style *area
1089         = (axis_type == PIVOT_AXIS_COLUMN
1090            ? &table->look->areas[PIVOT_AREA_COLUMN_LABELS]
1091            : axis_type == PIVOT_AXIS_ROW
1092            ? &table->look->areas[PIVOT_AREA_ROW_LABELS]
1093            : NULL);
1094       if (area && fl->axis->label)
1095         {
1096           table_area_style_uninit (area);
1097           decode_spvdx_style (fl->axis->label->style,
1098                               fl->axis->label->text_frame_style, area);
1099         }
1100     }
1101
1102   if (axis_type == PIVOT_AXIS_ROW)
1103     {
1104       const struct spvdx_facet_level *fl2
1105         = find_facet_level (v, base_facet_level + (n - 1));
1106       if (fl2)
1107         decode_spvdx_style_incremental (
1108           fl2->axis->major_ticks->style,
1109           fl2->axis->major_ticks->tick_frame_style,
1110           &table->look->areas[PIVOT_AREA_ROW_LABELS]);
1111     }
1112
1113   const struct spvdx_facet_level *fl3 = find_facet_level (v, base_facet_level);
1114   if (fl3 && fl3->axis->major_ticks->label_angle == -90)
1115     {
1116       if (axis_type == PIVOT_AXIS_COLUMN)
1117         table->rotate_inner_column_labels = true;
1118       else
1119         table->rotate_outer_row_labels = true;
1120     }
1121
1122   /* Find the first row for each category. */
1123   size_t max_cat = max_category (series[0]);
1124   size_t *cat_rows = xnmalloc (max_cat + 1, sizeof *cat_rows);
1125   for (size_t k = 0; k <= max_cat; k++)
1126     cat_rows[k] = SIZE_MAX;
1127   for (size_t k = 0; k < series[0]->n_values; k++)
1128     {
1129       const struct spv_data_value *dv = &series[0]->values[k];
1130       double d = dv->width < 0 ? dv->d : dv->index;
1131       if (d >= 0 && d < SIZE_MAX - 1)
1132         {
1133           size_t row = d;
1134           if (cat_rows[row] == SIZE_MAX)
1135             cat_rows[row] = k;
1136         }
1137     }
1138
1139   /* Drop missing categories and count what's left. */
1140   size_t n_cats = 0;
1141   for (size_t k = 0; k <= max_cat; k++)
1142     if (cat_rows[k] != SIZE_MAX)
1143       cat_rows[n_cats++] = cat_rows[k];
1144   assert (n_cats > 0);
1145
1146   /* Make the categories. */
1147   struct pivot_dimension *d = XZALLOC (struct pivot_dimension);
1148   table->dimensions[table->n_dimensions++] = d;
1149
1150   series[0]->n_index = max_cat + 1;
1151   series[0]->index_to_category = xcalloc (
1152     max_cat + 1, sizeof *series[0]->index_to_category);
1153   struct pivot_category **cats = xnmalloc (n_cats, sizeof **cats);
1154   for (size_t k = 0; k < n_cats; k++)
1155     {
1156       struct spv_data_value *dv = &series[0]->values[cat_rows[k]];
1157       int dv_num = dv ? dv->d : dv->index;
1158       struct pivot_category *cat = XZALLOC (struct pivot_category);
1159       char *retval = pivot_value_from_data_value (
1160         spv_map_lookup (&series[0]->map, dv), NULL, NULL, &cat->name);
1161       if (retval)
1162         {
1163           if (error)
1164             free (retval);
1165           else
1166             error = retval;
1167         }
1168       cat->parent = NULL;
1169       cat->dimension = d;
1170       cat->data_index = k;
1171       cat->presentation_index = cat_rows[k];
1172       cats[k] = cat;
1173       series[0]->index_to_category[dv_num] = cat;
1174
1175       if (cat->name)
1176         add_affixes (table, cat->name,
1177                      series[0]->affixes, series[0]->n_affixes);
1178     }
1179   free (cat_rows);
1180
1181   struct pivot_axis *axis = &table->axes[axis_type];
1182   d->axis_type = axis_type;
1183   d->level = axis->n_dimensions;
1184   d->top_index = table->n_dimensions - 1;
1185   d->root = xzalloc (sizeof *d->root);
1186   *d->root = (struct pivot_category) {
1187     .name = pivot_value_new_user_text (
1188       series[0]->label ? series[0]->label : "", -1),
1189     .dimension = d,
1190     .show_label = should_show_label (fl),
1191     .data_index = SIZE_MAX,
1192     .presentation_index = SIZE_MAX,
1193   };
1194   d->data_leaves = xmemdup (cats, n_cats * sizeof *cats);
1195   d->presentation_leaves = xmemdup (cats, n_cats * sizeof *cats);
1196   d->n_leaves = d->allocated_leaves = n_cats;
1197
1198   /* Now group them, in one pass per grouping variable, innermost first. */
1199   for (size_t j = 1; j < n; j++)
1200     {
1201       struct pivot_category **new_cats = xnmalloc (n_cats, sizeof **cats);
1202       size_t n_new_cats = 0;
1203
1204       /* Allocate a category index. */
1205       size_t max_cat = max_category (series[j]);
1206       series[j]->n_index = max_cat + 1;
1207       series[j]->index_to_category = xcalloc (
1208         max_cat + 1, sizeof *series[j]->index_to_category);
1209       for (size_t cat1 = 0; cat1 < n_cats;)
1210         {
1211           /* Find a sequence of categories cat1...cat2 (exclusive), that all
1212              have the same value in series 'j'.  (This might be only a single
1213              category; we will drop unnamed 1-category groups later.) */
1214           size_t row1 = cats[cat1]->presentation_index;
1215           const struct spv_data_value *dv1 = &series[j]->values[row1];
1216           size_t cat2;
1217           for (cat2 = cat1 + 1; cat2 < n_cats; cat2++)
1218             {
1219               size_t row2 = cats[cat2]->presentation_index;
1220               const struct spv_data_value *dv2 = &series[j]->values[row2];
1221               if (!spv_data_value_equal (dv1, dv2))
1222                 break;
1223             }
1224           size_t n_subs = cat2 - cat1;
1225
1226           struct pivot_category *new_cat;
1227           const struct spv_data_value *name
1228             = spv_map_lookup (&series[j]->map, dv1);
1229           if (n_subs == 1 && name->width == 0)
1230             {
1231               /* The existing category stands on its own. */
1232               new_cat = cats[cat1++];
1233             }
1234           else
1235             {
1236               /* Create a new group with cat...cat2 as subcategories. */
1237               new_cat = xzalloc (sizeof *new_cat);
1238               *new_cat = (struct pivot_category) {
1239                 .dimension = d,
1240                 .subs = xnmalloc (n_subs, sizeof *new_cat->subs),
1241                 .n_subs = n_subs,
1242                 .show_label = true,
1243                 .data_index = SIZE_MAX,
1244                 .presentation_index = row1,
1245               };
1246               char *retval = pivot_value_from_data_value (name, NULL, NULL,
1247                                                           &new_cat->name);
1248               if (retval)
1249                 {
1250                   if (error)
1251                     free (retval);
1252                   else
1253                     error = retval;
1254                 }
1255               for (size_t k = 0; k < n_subs; k++)
1256                 new_cat->subs[k] = cats[cat1++];
1257
1258               int dv1_num = dv1->width < 0 ? dv1->d : dv1->index;
1259               series[j]->index_to_category[dv1_num] = new_cat;
1260             }
1261
1262           if (new_cat->name)
1263             add_affixes (table, new_cat->name,
1264                          series[j]->affixes, series[j]->n_affixes);
1265
1266           /* Append the new group to the list of new groups. */
1267           new_cats[n_new_cats++] = new_cat;
1268         }
1269
1270       free (cats);
1271       cats = new_cats;
1272       n_cats = n_new_cats;
1273     }
1274
1275   /* Now drop unnamed 1-category groups and add parent pointers. */
1276   for (size_t j = 0; j < n_cats; j++)
1277     add_parents (cats[j], d->root, j);
1278   for (size_t j = 0; j < d->n_leaves; j++)
1279     {
1280       d->data_leaves[j]->data_index = j;
1281       d->presentation_leaves[j]->presentation_index = j;
1282     }
1283
1284   d->root->subs = cats;
1285   d->root->n_subs = n_cats;
1286
1287   if (error)
1288     {
1289       pivot_dimension_destroy (d);
1290       return error;
1291     }
1292
1293   dim_seriesp[(*n_dim_seriesp)++] = series[0];
1294   series[0]->dimension = d;
1295
1296   axis->dimensions = xnrealloc (axis->dimensions, axis->n_dimensions + 1,
1297                                sizeof *axis->dimensions);
1298   axis->dimensions[axis->n_dimensions++] = d;
1299   axis->extent *= d->n_leaves;
1300
1301   *dp = d;
1302   return NULL;
1303 }
1304
1305 static char * WARN_UNUSED_RESULT
1306 add_dimensions (struct hmap *series_map, const struct spvdx_nest *nest,
1307                 enum pivot_axis_type axis_type,
1308                 const struct spvdx_visualization *v, struct pivot_table *table,
1309                 struct spv_series **dim_seriesp, size_t *n_dim_seriesp,
1310                 int level_ofs)
1311 {
1312   struct pivot_axis *axis = &table->axes[axis_type];
1313   if (!axis->extent)
1314     axis->extent = 1;
1315
1316   if (!nest)
1317     return NULL;
1318
1319   struct spv_series **series = xnmalloc (nest->n_vars, sizeof *series);
1320   for (size_t i = 0; i < nest->n_vars;)
1321     {
1322       size_t n;
1323       for (n = 0; i + n < nest->n_vars; n++)
1324         {
1325           series[n] = spv_series_from_ref (series_map, nest->vars[i + n]->ref);
1326           if (!series[n] || !series[n]->n_values)
1327             break;
1328         }
1329
1330       if (n > 0)
1331         {
1332           struct pivot_dimension *d;
1333           char *error = add_dimension (series, n, axis_type, v, table,
1334                                        dim_seriesp, n_dim_seriesp,
1335                                        level_ofs + i, &d);
1336           if (error)
1337             {
1338               free (series);
1339               return error;
1340             }
1341         }
1342
1343       i += n + 1;
1344     }
1345   free (series);
1346
1347   return NULL;
1348 }
1349
1350 static char * WARN_UNUSED_RESULT
1351 add_layers (struct hmap *series_map,
1352             struct spvdx_layer **layers, size_t n_layers,
1353             const struct spvdx_visualization *v, struct pivot_table *table,
1354             struct spv_series **dim_seriesp, size_t *n_dim_seriesp,
1355             int level_ofs)
1356 {
1357   struct pivot_axis *axis = &table->axes[PIVOT_AXIS_LAYER];
1358   if (!axis->extent)
1359     axis->extent = 1;
1360
1361   if (!n_layers)
1362     return NULL;
1363
1364   struct spv_series **series = xnmalloc (n_layers, sizeof *series);
1365   for (size_t i = 0; i < n_layers;)
1366     {
1367       size_t n;
1368       for (n = 0; i + n < n_layers; n++)
1369         {
1370           series[n] = spv_series_from_ref (series_map,
1371                                            layers[i + n]->variable);
1372           if (!series[n] || !series[n]->n_values)
1373             break;
1374         }
1375
1376       if (n > 0)
1377         {
1378           struct pivot_dimension *d;
1379           char *error = add_dimension (
1380             series, n, PIVOT_AXIS_LAYER, v, table,
1381             dim_seriesp, n_dim_seriesp, level_ofs + i, &d);
1382           if (error)
1383             {
1384               free (series);
1385               return error;
1386             }
1387
1388           int index = atoi (layers[i]->value);
1389           assert (index < d->n_leaves);
1390           table->current_layer = xrealloc (
1391             table->current_layer,
1392             axis->n_dimensions * sizeof *table->current_layer);
1393           table->current_layer[axis->n_dimensions - 1] = index;
1394         }
1395       i += n + 1;
1396     }
1397   free (series);
1398
1399   return NULL;
1400 }
1401
1402 static struct pivot_category *
1403 find_category (struct spv_series *series, int index)
1404 {
1405   return (index >= 0 && index < series->n_index
1406           ? series->index_to_category[index]
1407           : NULL);
1408 }
1409
1410 static bool
1411 int_in_array (int value, const int *array, size_t n)
1412 {
1413   for (size_t i = 0; i < n; i++)
1414     if (array[i] == value)
1415       return true;
1416
1417   return false;
1418 }
1419
1420 static void
1421 apply_styles_to_value (struct pivot_table *table,
1422                        struct pivot_value *value,
1423                        const struct spvdx_set_format *sf,
1424                        const struct table_area_style *base_area_style,
1425                        const struct spvdx_style *fg,
1426                        const struct spvdx_style *bg)
1427 {
1428   if (sf)
1429     {
1430       if (sf->reset > 0 && value->ex)
1431         {
1432           free (value->ex->footnote_indexes);
1433           value->ex->footnote_indexes = NULL;
1434           value->ex->n_footnotes = 0;
1435         }
1436
1437       struct fmt_spec format = { .w = 0 };
1438       if (sf->format)
1439         {
1440           format = decode_format (sf->format);
1441           add_affixes (table, value, sf->format->affix, sf->format->n_affix);
1442         }
1443       else if (sf->number_format)
1444         {
1445           format = decode_number_format (sf->number_format);
1446           add_affixes (table, value, sf->number_format->affix,
1447                        sf->number_format->n_affix);
1448         }
1449       else if (sf->n_string_format)
1450         {
1451           for (size_t i = 0; i < sf->n_string_format; i++)
1452             add_affixes (table, value, sf->string_format[i]->affix,
1453                          sf->string_format[i]->n_affix);
1454         }
1455       else if (sf->date_time_format)
1456         {
1457           format = decode_date_time_format (sf->date_time_format);
1458           add_affixes (table, value, sf->date_time_format->affix,
1459                        sf->date_time_format->n_affix);
1460         }
1461       else if (sf->elapsed_time_format)
1462         {
1463           format = decode_elapsed_time_format (sf->elapsed_time_format);
1464           add_affixes (table, value, sf->elapsed_time_format->affix,
1465                        sf->elapsed_time_format->n_affix);
1466         }
1467
1468       if (format.w)
1469         {
1470           if (value->type == PIVOT_VALUE_NUMERIC)
1471             value->numeric.format = format;
1472
1473           /* Possibly we should try to apply date and time formats too,
1474              but none seem to occur in practice so far. */
1475         }
1476     }
1477   if (fg || bg)
1478     {
1479       const struct pivot_value_ex *ex = pivot_value_ex (value);
1480       struct table_area_style area;
1481       pivot_value_get_style (
1482         value,
1483         ex->font_style ? ex->font_style : &base_area_style->font_style,
1484         ex->cell_style ? ex->cell_style : &base_area_style->cell_style,
1485         &area);
1486       decode_spvdx_style_incremental (fg, bg, &area);
1487       pivot_value_set_style (value, &area);
1488       table_area_style_uninit (&area);
1489     }
1490 }
1491
1492 static void
1493 decode_set_cell_properties__ (struct pivot_table *table,
1494                               struct hmap *series_map,
1495                               const struct spvdx_intersect *intersect,
1496                               const struct spvdx_style *interval,
1497                               const struct spvdx_style *graph,
1498                               const struct spvdx_style *labeling,
1499                               const struct spvdx_style *frame,
1500                               const struct spvdx_style *major_ticks,
1501                               const struct spvdx_set_format *set_format)
1502 {
1503   if (graph && labeling && intersect->alternating
1504       && !interval && !major_ticks && !frame && !set_format)
1505     {
1506       /* Sets alt_fg_color and alt_bg_color. */
1507       struct table_area_style area;
1508       decode_spvdx_style (labeling, graph, &area);
1509       table->look->areas[PIVOT_AREA_DATA].font_style.fg[1]
1510         = area.font_style.fg[0];
1511       table->look->areas[PIVOT_AREA_DATA].font_style.bg[1]
1512         = area.font_style.bg[0];
1513       table_area_style_uninit (&area);
1514     }
1515   else if (graph
1516            && !labeling && !interval && !major_ticks && !frame && !set_format)
1517     {
1518       /* 'graph->width' likely just sets the width of the table as a
1519          whole.  */
1520     }
1521   else if (!graph && !labeling && !interval && !frame && !set_format
1522            && !major_ticks)
1523     {
1524       /* No-op.  (Presumably there's a setMetaData we don't care about.) */
1525     }
1526   else if (((set_format && spvdx_is_major_ticks (set_format->target))
1527             || major_ticks || frame)
1528            && intersect->n_where == 1)
1529     {
1530       /* Formatting for individual row or column labels. */
1531       const struct spvdx_where *w = intersect->where[0];
1532       struct spv_series *s = spv_series_find (series_map, w->variable->id);
1533       assert (s);
1534
1535       const char *p = w->include;
1536
1537       while (*p)
1538         {
1539           char *tail;
1540           int include = strtol (p, &tail, 10);
1541
1542           struct pivot_category *c = find_category (s, include);
1543           if (c)
1544             {
1545               const struct table_area_style *base_area_style
1546                 = (c->dimension->axis_type == PIVOT_AXIS_ROW
1547                    ? &table->look->areas[PIVOT_AREA_ROW_LABELS]
1548                    : &table->look->areas[PIVOT_AREA_COLUMN_LABELS]);
1549               apply_styles_to_value (table, c->name, set_format,
1550                                      base_area_style, major_ticks, frame);
1551             }
1552
1553           if (tail == p)
1554             break;
1555           p = tail;
1556           if (*p == ';')
1557             p++;
1558         }
1559     }
1560   else if ((set_format && spvdx_is_labeling (set_format->target))
1561            || labeling || interval)
1562     {
1563       /* Formatting for individual cells or groups of them with some dimensions
1564          in common. */
1565       int **indexes = XCALLOC (table->n_dimensions, int *);
1566       size_t *n = XCALLOC (table->n_dimensions, size_t);
1567       size_t *allocated = XCALLOC (table->n_dimensions, size_t);
1568
1569       for (size_t i = 0; i < intersect->n_where; i++)
1570         {
1571           const struct spvdx_where *w = intersect->where[i];
1572           struct spv_series *s = spv_series_find (series_map, w->variable->id);
1573           assert (s);
1574           if (!s->dimension)
1575             {
1576               /* Group indexes may be included even though they are redundant.
1577                  Ignore them. */
1578               continue;
1579             }
1580
1581           size_t j = s->dimension->top_index;
1582
1583           const char *p = w->include;
1584           while (*p)
1585             {
1586               char *tail;
1587               int include = strtol (p, &tail, 10);
1588
1589               struct pivot_category *c = find_category (s, include);
1590               if (c)
1591                 {
1592                   if (n[j] >= allocated[j])
1593                     indexes[j] = x2nrealloc (indexes[j], &allocated[j],
1594                                              sizeof *indexes[j]);
1595                   indexes[j][n[j]++] = c->data_index;
1596                 }
1597
1598               if (tail == p)
1599                 break;
1600               p = tail;
1601               if (*p == ';')
1602                 p++;
1603             }
1604         }
1605
1606 #if 0
1607       printf ("match:");
1608       for (size_t i = 0; i < table->n_dimensions; i++)
1609         {
1610           if (n[i])
1611             {
1612               printf (" %d=(", i);
1613               for (size_t j = 0; j < n[i]; j++)
1614                 {
1615                   if (j)
1616                     putchar (',');
1617                   printf ("%d", indexes[i][j]);
1618                 }
1619               putchar (')');
1620             }
1621         }
1622       printf ("\n");
1623 #endif
1624
1625       /* XXX This is inefficient in the common case where all of the dimensions
1626          are matched.  We should use a heuristic where if all of the dimensions
1627          are matched and the product of n[*] is less than
1628          hmap_count(&table->cells) then iterate through all the possibilities
1629          rather than all the cells.  Or even only do it if there is just one
1630          possibility. */
1631
1632       struct pivot_cell *cell;
1633       HMAP_FOR_EACH (cell, struct pivot_cell, hmap_node, &table->cells)
1634         {
1635           for (size_t i = 0; i < table->n_dimensions; i++)
1636             {
1637               if (n[i] && !int_in_array (cell->idx[i], indexes[i], n[i]))
1638                 goto skip;
1639             }
1640           apply_styles_to_value (table, cell->value, set_format,
1641                                  &table->look->areas[PIVOT_AREA_DATA],
1642                                  labeling, interval);
1643
1644         skip: ;
1645         }
1646
1647       for (size_t i = 0; i < table->n_dimensions; i++)
1648         free (indexes[i]);
1649       free (indexes);
1650       free (n);
1651       free (allocated);
1652     }
1653   else
1654     NOT_REACHED ();
1655 }
1656
1657 static void
1658 decode_set_cell_properties (struct pivot_table *table, struct hmap *series_map,
1659                             struct spvdx_set_cell_properties **scps,
1660                             size_t n_scps)
1661 {
1662   for (size_t i = 0; i < n_scps; i++)
1663     {
1664       const struct spvdx_set_cell_properties *scp = scps[i];
1665       const struct spvdx_style *interval = NULL;
1666       const struct spvdx_style *graph = NULL;
1667       const struct spvdx_style *labeling = NULL;
1668       const struct spvdx_style *frame = NULL;
1669       const struct spvdx_style *major_ticks = NULL;
1670       const struct spvdx_set_format *set_format = NULL;
1671       for (size_t j = 0; j < scp->n_seq; j++)
1672         {
1673           const struct spvxml_node *node = scp->seq[j];
1674           if (spvdx_is_set_style (node))
1675             {
1676               const struct spvdx_set_style *set_style
1677                 = spvdx_cast_set_style (node);
1678               if (spvdx_is_graph (set_style->target))
1679                 graph = set_style->style;
1680               else if (spvdx_is_labeling (set_style->target))
1681                 labeling = set_style->style;
1682               else if (spvdx_is_interval (set_style->target))
1683                 interval = set_style->style;
1684               else if (spvdx_is_major_ticks (set_style->target))
1685                 major_ticks = set_style->style;
1686               else
1687                 NOT_REACHED ();
1688             }
1689           else if (spvdx_is_set_frame_style (node))
1690             frame = spvdx_cast_set_frame_style (node)->style;
1691           else if (spvdx_is_set_format (node))
1692             set_format = spvdx_cast_set_format (node);
1693           else
1694             assert (spvdx_is_set_meta_data (node));
1695         }
1696
1697       if (scp->union_ && scp->apply_to_converse <= 0)
1698         {
1699           for (size_t j = 0; j < scp->union_->n_intersect; j++)
1700             decode_set_cell_properties__ (
1701               table, series_map, scp->union_->intersect[j],
1702               interval, graph, labeling, frame, major_ticks, set_format);
1703         }
1704       else if (!scp->union_ && scp->apply_to_converse > 0)
1705         {
1706           if ((set_format && spvdx_is_labeling (set_format->target))
1707               || labeling || interval)
1708             {
1709               struct pivot_cell *cell;
1710               HMAP_FOR_EACH (cell, struct pivot_cell, hmap_node, &table->cells)
1711                 apply_styles_to_value (table, cell->value, set_format,
1712                                        &table->look->areas[PIVOT_AREA_DATA],
1713                                        NULL, NULL);
1714             }
1715         }
1716       else if (!scp->union_ && scp->apply_to_converse <= 0)
1717         {
1718           /* Appears to be used to set the font for something--but what? */
1719         }
1720       else
1721         NOT_REACHED ();
1722     }
1723 }
1724
1725 static struct spv_series *
1726 parse_formatting (const struct spvdx_visualization *v,
1727                   const struct hmap *series_map, struct hmap *format_map)
1728 {
1729   const struct spvdx_labeling *labeling = v->graph->interval->labeling;
1730   struct spv_series *cell_format = NULL;
1731   for (size_t i = 0; i < labeling->n_seq; i++)
1732     {
1733       const struct spvdx_formatting *f
1734         = spvdx_cast_formatting (labeling->seq[i]);
1735       if (!f)
1736         continue;
1737
1738       cell_format = spv_series_from_ref (series_map, f->variable);
1739       for (size_t j = 0; j < f->n_format_mapping; j++)
1740         {
1741           const struct spvdx_format_mapping *fm = f->format_mapping[j];
1742
1743           if (fm->format)
1744             {
1745               struct format_mapping *out = xmalloc (sizeof *out);
1746               out->from = fm->from;
1747               out->to = decode_format (fm->format);
1748               hmap_insert (format_map, &out->hmap_node,
1749                            hash_int (out->from, 0));
1750             }
1751         }
1752     }
1753
1754   return cell_format;
1755 }
1756
1757 static void
1758 format_map_destroy (struct hmap *format_map)
1759 {
1760   struct format_mapping *fm, *next;
1761   HMAP_FOR_EACH_SAFE (fm, next, struct format_mapping, hmap_node, format_map)
1762     {
1763       hmap_delete (format_map, &fm->hmap_node);
1764       free (fm);
1765     }
1766   hmap_destroy (format_map);
1767 }
1768
1769 char * WARN_UNUSED_RESULT
1770 decode_spvdx_table (const struct spvdx_visualization *v, const char *subtype,
1771                     const struct pivot_table_look *look,
1772                     const struct spv_data *data, struct pivot_table **outp)
1773 {
1774   struct pivot_table *table = pivot_table_create__ (
1775     pivot_value_new_user_text (v->name, SIZE_MAX), subtype);
1776
1777   pivot_table_set_look (table, look);
1778   table->look = pivot_table_look_unshare (table->look);
1779
1780   struct hmap series_map = HMAP_INITIALIZER (series_map);
1781   struct hmap format_map = HMAP_INITIALIZER (format_map);
1782   struct spv_series **dim_series = NULL;
1783   char *error;
1784
1785   struct spvdx_visualization_extension *ve = v->visualization_extension;
1786   table->show_grid_lines = ve && ve->show_gridline;
1787
1788   /* Sizing from the legacy properties can get overridden. */
1789   if (v->graph->cell_style->width)
1790     {
1791       int min_width, max_width, n = 0;
1792       if (sscanf (v->graph->cell_style->width, "%*d%%;%dpt;%dpt%n",
1793                   &min_width, &max_width, &n)
1794           && v->graph->cell_style->width[n] == '\0')
1795         {
1796           table->look->width_ranges[TABLE_HORZ][0] = min_width;
1797           table->look->width_ranges[TABLE_HORZ][1] = max_width;
1798         }
1799     }
1800
1801   /* Footnotes.
1802
1803      Any pivot_value might refer to footnotes, so it's important to process the
1804      footnotes early to ensure that those references can be resolved.  There is
1805      a possible problem that a footnote might itself reference an
1806      as-yet-unprocessed footnote, but that's OK because footnote references
1807      don't actually look at the footnote contents but only resolve a pointer to
1808      where the footnote will go later.
1809
1810      Before we really start, create all the footnotes we'll fill in.  This is
1811      because sometimes footnotes refer to themselves or to each other and we
1812      don't want to reject those references. */
1813   if (v->container)
1814     for (size_t i = 0; i < v->container->n_label_frame; i++)
1815       {
1816         const struct spvdx_label_frame *lf = v->container->label_frame[i];
1817         if (lf->label
1818             && lf->label->purpose == SPVDX_PURPOSE_FOOTNOTE
1819             && lf->label->n_text > 0
1820             && lf->label->text[0]->uses_reference > 0)
1821           {
1822             pivot_table_create_footnote__ (
1823               table, lf->label->text[0]->uses_reference - 1,
1824               NULL, NULL);
1825           }
1826       }
1827
1828   if (v->graph->interval->footnotes)
1829     decode_footnotes (table, v->graph->interval->footnotes);
1830
1831   struct spv_series *footnotes = NULL;
1832   for (size_t i = 0; i < v->graph->interval->labeling->n_seq; i++)
1833     {
1834       const struct spvxml_node *node = v->graph->interval->labeling->seq[i];
1835       if (spvdx_is_footnotes (node))
1836         {
1837           const struct spvdx_footnotes *f = spvdx_cast_footnotes (node);
1838           footnotes = spv_series_from_ref (&series_map, f->variable);
1839           decode_footnotes (table, f);
1840         }
1841     }
1842   for (size_t i = 0; i < v->n_lf1; i++)
1843     {
1844       error = decode_label_frame (table, v->lf1[i]);
1845       if (error)
1846         goto exit;
1847     }
1848   for (size_t i = 0; i < v->n_lf2; i++)
1849     {
1850       error = decode_label_frame (table, v->lf2[i]);
1851       if (error)
1852         goto exit;
1853     }
1854   if (v->container)
1855     for (size_t i = 0; i < v->container->n_label_frame; i++)
1856       {
1857         error = decode_label_frame (table, v->container->label_frame[i]);
1858         if (error)
1859           goto exit;
1860       }
1861   if (v->graph->interval->labeling->style)
1862     {
1863       table_area_style_uninit (&table->look->areas[PIVOT_AREA_DATA]);
1864       decode_spvdx_style (v->graph->interval->labeling->style,
1865                           v->graph->cell_style,
1866                           &table->look->areas[PIVOT_AREA_DATA]);
1867     }
1868
1869   /* Decode all of the sourceVariable and derivedVariable  */
1870   struct spvxml_node **nodes = xmemdup (v->seq, v->n_seq * sizeof *v->seq);
1871   size_t n_nodes = v->n_seq;
1872   while (n_nodes > 0)
1873     {
1874       bool progress = false;
1875       for (size_t i = 0; i < n_nodes;)
1876         {
1877           error = (spvdx_is_source_variable (nodes[i])
1878                    ? decode_spvdx_source_variable (nodes[i], data, &series_map)
1879                    : decode_spvdx_derived_variable (nodes[i], &series_map));
1880           if (!error)
1881             {
1882               nodes[i] = nodes[--n_nodes];
1883               progress = true;
1884             }
1885           else if (error == &BAD_REFERENCE)
1886             i++;
1887           else
1888             {
1889               free (nodes);
1890               goto exit;
1891             }
1892         }
1893
1894       if (!progress)
1895         {
1896           free (nodes);
1897           error = xasprintf ("Table has %zu variables with circular or "
1898                              "unresolved references, including variable %s.",
1899                              n_nodes, nodes[0]->id);
1900           goto exit;
1901         }
1902     }
1903   free (nodes);
1904
1905   const struct spvdx_cross *cross = v->graph->faceting->cross;
1906
1907   assert (cross->n_seq == 1);
1908   const struct spvdx_nest *columns = spvdx_cast_nest (cross->seq[0]);
1909   size_t max_columns = columns ? columns->n_vars : 0;
1910
1911   assert (cross->n_seq2 == 1);
1912   const struct spvdx_nest *rows = spvdx_cast_nest (cross->seq2[0]);
1913   size_t max_rows = rows ? rows->n_vars : 0;
1914
1915   size_t max_layers = (v->graph->faceting->n_layers1
1916                        + v->graph->faceting->n_layers2);
1917
1918   size_t max_dims = max_columns + max_rows + max_layers;
1919   table->dimensions = xnmalloc (max_dims, sizeof *table->dimensions);
1920   dim_series = xnmalloc (max_dims, sizeof *dim_series);
1921   size_t n_dim_series = 0;
1922
1923   error = add_dimensions (&series_map, columns, PIVOT_AXIS_COLUMN, v, table,
1924                           dim_series, &n_dim_series, 1);
1925   if (error)
1926     goto exit;
1927
1928   error = add_dimensions (&series_map, rows, PIVOT_AXIS_ROW, v, table,
1929                           dim_series, &n_dim_series, max_columns + 1);
1930   if (error)
1931     goto exit;
1932
1933   error = add_layers (&series_map, v->graph->faceting->layers1,
1934                       v->graph->faceting->n_layers1,
1935                       v, table, dim_series, &n_dim_series,
1936                       max_rows + max_columns + 1);
1937   if (error)
1938     goto exit;
1939
1940   error = add_layers (&series_map, v->graph->faceting->layers2,
1941                       v->graph->faceting->n_layers2,
1942                       v, table, dim_series, &n_dim_series,
1943                       (max_rows + max_columns + v->graph->faceting->n_layers1
1944                        + 1));
1945   if (error)
1946     goto exit;
1947
1948   struct spv_series *cell = spv_series_find (&series_map, "cell");
1949   if (!cell)
1950     {
1951       error = xstrdup (_("Table lacks cell data."));
1952       goto exit;
1953     }
1954
1955   struct spv_series *cell_format = parse_formatting (v, &series_map,
1956                                                      &format_map);
1957
1958   assert (table->n_dimensions == n_dim_series);
1959   size_t *dim_indexes = xnmalloc (table->n_dimensions, sizeof *dim_indexes);
1960   for (size_t i = 0; i < cell->n_values; i++)
1961     {
1962       for (size_t j = 0; j < table->n_dimensions; j++)
1963         {
1964           const struct spv_data_value *value = &dim_series[j]->values[i];
1965           const struct pivot_category *cat = find_category (
1966             dim_series[j], value->width < 0 ? value->d : value->index);
1967           if (!cat)
1968             goto skip;
1969           dim_indexes[j] = cat->data_index;
1970         }
1971
1972       struct pivot_value *value;
1973       error = pivot_value_from_data_value (
1974         &cell->values[i], cell_format ? &cell_format->values[i] : NULL,
1975         &format_map, &value);
1976       if (error)
1977         goto exit;
1978
1979       if (footnotes)
1980         {
1981           const struct spv_data_value *d = &footnotes->values[i];
1982           if (d->width >= 0)
1983             {
1984               const char *p = d->s;
1985               while (*p)
1986                 {
1987                   char *tail;
1988                   int idx = strtol (p, &tail, 10);
1989                   add_footnote (value, idx, table);
1990                   if (tail == p)
1991                     break;
1992                   p = tail;
1993                   if (*p == ',')
1994                     p++;
1995                 }
1996             }
1997         }
1998
1999       if (value->type == PIVOT_VALUE_NUMERIC
2000           && value->numeric.x == SYSMIS
2001           && !pivot_value_ex (value)->n_footnotes)
2002         {
2003           /* Apparently, system-missing values are just empty cells? */
2004           pivot_value_destroy (value);
2005         }
2006       else
2007         pivot_table_put (table, dim_indexes, table->n_dimensions, value);
2008     skip:;
2009     }
2010   free (dim_indexes);
2011
2012   decode_set_cell_properties (table, &series_map, v->graph->facet_layout->scp1,
2013                               v->graph->facet_layout->n_scp1);
2014   decode_set_cell_properties (table, &series_map, v->graph->facet_layout->scp2,
2015                               v->graph->facet_layout->n_scp2);
2016
2017   pivot_table_assign_label_depth (table);
2018
2019   format_map_destroy (&format_map);
2020
2021 exit:
2022   free (dim_series);
2023   spv_series_destroy (&series_map);
2024   if (error)
2025     {
2026       pivot_table_unref (table);
2027       *outp = NULL;
2028     }
2029   else
2030     *outp = table;
2031   return error;
2032 }