pivot-table: Add some more utility functions for pivot data structures.
[pspp] / src / output / pivot-table.c
index aa5b181c52dda70e78a48a2bc5ff57ddfd833c6f..3bd0911b46e7adec6bbd8cda843f3c6d9a060360 100644 (file)
@@ -25,6 +25,7 @@
 #include "data/value.h"
 #include "data/variable.h"
 #include "data/file-name.h"
+#include "libpspp/assertion.h"
 #include "libpspp/hash-functions.h"
 #include "libpspp/i18n.h"
 #include "output/driver.h"
@@ -370,7 +371,8 @@ pivot_axis_iterator_next (size_t *indexes, const struct pivot_axis *axis)
           if (axis->dimensions[i]->n_leaves == 0)
             return NULL;
 
-      return xcalloc (axis->n_dimensions, sizeof *indexes);
+      size_t size = axis->n_dimensions * sizeof *indexes;
+      return xzalloc (MAX (size, 1));
     }
 
   for (size_t i = 0; i < axis->n_dimensions; i++)
@@ -395,6 +397,13 @@ pivot_category_set_rc (struct pivot_category *category, const char *s)
     category->dimension->table, s);
   if (format)
     category->format = *format;
+
+  /* Ensure that the category itself, in addition to the cells within it, takes
+     the format.  (It's kind of rare for a category to have a numeric format
+     though.) */
+  struct pivot_value *name = category->name;
+  if (name->type == PIVOT_VALUE_NUMERIC && !name->numeric.format.w)
+    name->numeric.format = format ? *format : *settings_get_format ();
 }
 
 static void
@@ -788,6 +797,12 @@ pivot_result_class_change (const char *s_, const struct fmt_spec *format)
 
   return rc != NULL;
 }
+
+bool
+is_pivot_result_class (const char *s)
+{
+  return pivot_result_class_find (s) != NULL;
+}
 \f
 /* Pivot tables. */
 
@@ -905,6 +920,7 @@ pivot_table_unref (struct pivot_table *table)
   pivot_value_destroy (table->subtype);
   pivot_value_destroy (table->corner_text);
   pivot_value_destroy (table->caption);
+  free (table->notes);
 
   for (size_t i = 0; i < table->n_dimensions; i++)
     pivot_dimension_destroy (table->dimensions[i]);
@@ -1389,10 +1405,6 @@ pivot_table_assign_label_depth (struct pivot_table *table)
   pivot_axis_assign_label_depth (table, PIVOT_AXIS_LAYER, false);
 }
 \f
-/* Footnotes. */
-
-\f
-\f
 static void
 indent (int indentation)
 {
@@ -1907,7 +1919,10 @@ interpret_show (enum settings_value_show global_show,
 /* Appends a text representation of the body of VALUE to OUT.  SHOW_VALUES and
    SHOW_VARIABLES control whether variable and value labels are included.
 
-   The "body" omits subscripts and superscripts and footnotes. */
+   The "body" omits subscripts and superscripts and footnotes.
+
+   Returns true if OUT is a number (or a number plus a value label), false
+   otherwise.  */
 bool
 pivot_value_format_body (const struct pivot_value *value,
                          enum settings_value_show show_values,
@@ -2031,6 +2046,78 @@ pivot_value_to_string (const struct pivot_value *value,
   return ds_steal_cstr (&s);
 }
 
+static char *
+xstrdup_if_nonnull (const char *s)
+{
+  return s ? xstrdup (s) : NULL;
+}
+
+struct pivot_value *
+pivot_value_clone (const struct pivot_value *old)
+{
+  struct pivot_value *new = xmemdup (old, sizeof *new);
+  if (old->font_style)
+    {
+      new->font_style = xmalloc (sizeof *new->font_style);
+      font_style_copy (NULL, new->font_style, old->font_style);
+    }
+  if (old->cell_style)
+    new->font_style = xmemdup (old->font_style, sizeof *new->font_style);
+  if (old->n_subscripts)
+    {
+      new->subscripts = xnmalloc (old->n_subscripts, sizeof *new->subscripts);
+      for (size_t i = 0; i < old->n_subscripts; i++)
+        new->subscripts[i] = xstrdup (old->subscripts[i]);
+    }
+  if (old->n_footnotes)
+    new->footnotes = xmemdup (old->footnotes,
+                              old->n_footnotes * sizeof *new->footnotes);
+
+  switch (new->type)
+    {
+    case PIVOT_VALUE_NUMERIC:
+      new->numeric.var_name = xstrdup_if_nonnull (new->numeric.var_name);
+      new->numeric.value_label = xstrdup_if_nonnull (new->numeric.value_label);
+      break;
+
+    case PIVOT_VALUE_STRING:
+      new->string.s = xstrdup (new->string.s);
+      new->string.var_name = xstrdup_if_nonnull (new->string.var_name);
+      new->string.value_label = xstrdup_if_nonnull (new->string.value_label);
+      break;
+
+    case PIVOT_VALUE_VARIABLE:
+      new->variable.var_name = xstrdup_if_nonnull (new->variable.var_name);
+      new->variable.var_label = xstrdup_if_nonnull (new->variable.var_label);
+      break;
+
+    case PIVOT_VALUE_TEXT:
+      new->text.local = xstrdup (old->text.local);
+      new->text.c = (old->text.c == old->text.local ? new->text.local
+                     : xstrdup (old->text.c));
+      new->text.id = (old->text.id == old->text.local ? new->text.local
+                      : old->text.id == old->text.c ? new->text.c
+                      : xstrdup (old->text.id));
+      break;
+
+    case PIVOT_VALUE_TEMPLATE:
+      new->template.local = xstrdup (old->template.local);
+      new->template.id = (old->template.id == old->template.local
+                          ? new->template.local
+                          : xstrdup (old->template.id));
+      new->template.args = xmalloc (new->template.n_args
+                                    * sizeof *new->template.args);
+      for (size_t i = 0; i < old->template.n_args; i++)
+        pivot_argument_copy (&new->template.args[i],
+                             &old->template.args[i]);
+      break;
+
+    default:
+      NOT_REACHED ();
+    }
+  return new;
+}
+
 /* Frees the data owned by V. */
 void
 pivot_value_destroy (struct pivot_value *value)
@@ -2083,6 +2170,9 @@ pivot_value_destroy (struct pivot_value *value)
             pivot_argument_uninit (&value->template.args[i]);
           free (value->template.args);
           break;
+
+        default:
+          NOT_REACHED ();
         }
       free (value);
     }
@@ -2108,16 +2198,42 @@ pivot_value_get_style (struct pivot_value *value,
 void
 pivot_value_set_style (struct pivot_value *value,
                        const struct table_area_style *area)
+{
+  pivot_value_set_font_style (value, &area->font_style);
+  pivot_value_set_cell_style (value, &area->cell_style);
+}
+
+void
+pivot_value_set_font_style (struct pivot_value *value,
+                            const struct font_style *font_style)
 {
   if (value->font_style)
     font_style_uninit (value->font_style);
   else
     value->font_style = xmalloc (sizeof *value->font_style);
-  font_style_copy (NULL, value->font_style, &area->font_style);
+  font_style_copy (NULL, value->font_style, font_style);
+}
 
+void
+pivot_value_set_cell_style (struct pivot_value *value,
+                            const struct cell_style *cell_style)
+{
   if (!value->cell_style)
     value->cell_style = xmalloc (sizeof *value->cell_style);
-  *value->cell_style = area->cell_style;
+  *value->cell_style = *cell_style;
+}
+
+void
+pivot_argument_copy (struct pivot_argument *dst,
+                     const struct pivot_argument *src)
+{
+  *dst = (struct pivot_argument) {
+    .n = src->n,
+    .values = xmalloc (src->n * sizeof *dst->values),
+  };
+
+  for (size_t i = 0; i < src->n; i++)
+    dst->values[i] = pivot_value_clone (src->values[i]);
 }
 
 /* Frees the data owned by ARG (but not ARG itself). */