pxd: initial work
[pspp] / src / data / dictionary.c
index 04c082fca7854a3513baa4553a43fbf788b4a75e..7cf2a7917de2e74cb85e8ac7db01ccad356246c4 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2006, 2007, 2009, 2010, 2011, 2012, 2013 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2006, 2007, 2009-2015 Free Software Foundation, Inc.
 
    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
@@ -41,6 +41,7 @@
 #include "libpspp/message.h"
 #include "libpspp/misc.h"
 #include "libpspp/pool.h"
+#include "libpspp/pxd.h"
 #include "libpspp/str.h"
 #include "libpspp/string-array.h"
 
@@ -87,6 +88,8 @@ struct dictionary
 static void dict_unset_split_var (struct dictionary *, struct variable *);
 static void dict_unset_mrset_var (struct dictionary *, struct variable *);
 
+static void dict_uncache (struct dictionary *);
+
 /* Returns the encoding for data in dictionary D.  The return value is a
    nonnull string that contains an IANA character set name. */
 const char *
@@ -255,6 +258,8 @@ dict_clone (const struct dictionary *s)
 void
 dict_clear (struct dictionary *d)
 {
+  dict_uncache (d);
+
   /* FIXME?  Should we really clear case_limit, label, documents?
      Others are necessarily cleared by deleting all the variables.*/
   while (d->var_cnt > 0 )
@@ -547,6 +552,7 @@ static void
 reindex_var (struct dictionary *d, struct vardict_info *vardict)
 {
   struct variable *var = vardict->var;
+  struct variable *old = var_clone (var);
 
   var_set_vardict (var, vardict);
   hmap_insert_fast (&d->name_map, &vardict->name_node,
@@ -554,7 +560,8 @@ reindex_var (struct dictionary *d, struct vardict_info *vardict)
 
   if ( d->changed ) d->changed (d, d->changed_data);
   if ( d->callbacks &&  d->callbacks->var_changed )
-    d->callbacks->var_changed (d, var_get_dict_index (var), d->cb_data);
+    d->callbacks->var_changed (d, var_get_dict_index (var), VAR_TRAIT_POSITION, old, d->cb_data);
+  var_destroy (old);
 }
 
 /* Sets the case_index in V's vardict to CASE_INDEX. */
@@ -757,6 +764,7 @@ void
 dict_rename_var (struct dictionary *d, struct variable *v,
                  const char *new_name)
 {
+  struct variable *old = var_clone (v);
   assert (!utf8_strcasecmp (var_get_name (v), new_name)
           || dict_lookup_var (d, new_name) == NULL);
 
@@ -769,7 +777,9 @@ dict_rename_var (struct dictionary *d, struct variable *v,
 
   if ( d->changed ) d->changed (d, d->changed_data);
   if ( d->callbacks &&  d->callbacks->var_changed )
-    d->callbacks->var_changed (d, var_get_dict_index (v), d->cb_data);
+    d->callbacks->var_changed (d, var_get_dict_index (v), VAR_TRAIT_NAME, old, d->cb_data);
+
+  var_destroy (old);
 }
 
 /* Renames COUNT variables specified in VARS to the names given
@@ -902,7 +912,7 @@ make_hinted_name (const struct dictionary *dict, const char *hint)
           char *name;
 
           suffix[0] = '_';
-          if (!str_format_26adic (i + 1, &suffix[1], sizeof suffix - 1))
+          if (!str_format_26adic (i + 1, true, &suffix[1], sizeof suffix - 1))
             NOT_REACHED ();
 
           name = utf8_encoding_concat (root, suffix, dict->encoding, 64);
@@ -1591,10 +1601,12 @@ dict_has_attributes (const struct dictionary *d)
   return attrset_count (&d->attributes) > 0;
 }
 
-/* Called from variable.c to notify the dictionary that some property of
-   the variable has changed */
+/* Called from variable.c to notify the dictionary that some property (indicated
+   by WHAT) of the variable has changed.  OLDVAR is a copy of V as it existed
+   prior to the change.  OLDVAR is destroyed by this function.
+*/
 void
-dict_var_changed (const struct variable *v)
+dict_var_changed (const struct variable *v, unsigned int what, struct variable *oldvar)
 {
   if ( var_has_vardict (v))
     {
@@ -1606,49 +1618,12 @@ dict_var_changed (const struct variable *v)
 
       if (d->changed ) d->changed (d, d->changed_data);
       if ( d->callbacks && d->callbacks->var_changed )
-       d->callbacks->var_changed (d, var_get_dict_index (v), d->cb_data);
+       d->callbacks->var_changed (d, var_get_dict_index (v), what, oldvar, d->cb_data);
     }
+  var_destroy (oldvar);
 }
 
 
-/* Called from variable.c to notify the dictionary that the variable's width
-   has changed */
-void
-dict_var_resized (const struct variable *v, int old_width)
-{
-  if ( var_has_vardict (v))
-    {
-      const struct vardict_info *vardict = var_get_vardict (v);
-      struct dictionary *d;
-
-      d = vardict->dict;
-
-      if (d->changed) d->changed (d, d->changed_data);
-
-      invalidate_proto (d);
-      if ( d->callbacks && d->callbacks->var_resized )
-       d->callbacks->var_resized (d, var_get_dict_index (v), old_width,
-                                   d->cb_data);
-    }
-}
-
-/* Called from variable.c to notify the dictionary that the variable's display width
-   has changed */
-void
-dict_var_display_width_changed (const struct variable *v)
-{
-  if ( var_has_vardict (v))
-    {
-      const struct vardict_info *vardict = var_get_vardict (v);
-      struct dictionary *d;
-
-      d = vardict->dict;
-
-      if (d->changed) d->changed (d, d->changed_data);
-      if ( d->callbacks && d->callbacks->var_display_width_changed )
-       d->callbacks->var_display_width_changed (d, var_get_dict_index (v), d->cb_data);
-    }
-}
 \f
 /* Dictionary used to contain "internal variables". */
 static struct dictionary *internal_dict;
@@ -1704,3 +1679,166 @@ vardict_get_dict_index (const struct vardict_info *vardict)
 {
   return vardict - vardict->dict->var;
 }
+\f
+static struct pxd_object *
+vardict_info_save (const struct vardict_info *vardict, struct pxd *pxd)
+{
+  struct pxd_builder b;
+
+  pxd_builder_init (&b, pxd);
+  pxd_builder_put_s32 (&b, vardict->case_index);
+  pxd_builder_put_link (&b, var_save (vardict->var, pxd));
+  return pxd_builder_commit (&b);
+}
+
+struct pxd_object *
+dict_save (const struct dictionary *dict, struct pxd *pxd)
+{
+  struct pxd_array_builder ab;
+  struct pxd_object *vars;
+  struct pxd_builder b;
+  size_t i;
+
+  pxd_array_builder_init (&ab, pxd);
+  for (i = 0; i < dict->var_cnt; i++)
+    pxd_array_builder_add (&ab, vardict_info_save (&dict->var[i], pxd));
+  vars = pxd_array_builder_commit (&ab);
+
+  pxd_builder_init (&b, pxd);
+  pxd_builder_put_link (&b, vars);
+  pxd_builder_put_u32 (&b, dict->next_value_idx);
+
+  pxd_builder_put_size_t (&b, dict->split_cnt);
+  for (i = 0; i < dict->split_cnt; i++)
+    pxd_builder_put_u32 (&b, var_get_dict_index (dict->split[i]));
+
+  pxd_builder_put_s32 (&b,
+                       (dict->weight == NULL ? -1
+                        : var_get_dict_index (dict->weight)));
+  pxd_builder_put_s32 (&b,
+                       (dict->filter == NULL ? -1
+                        : var_get_dict_index (dict->filter)));
+
+  pxd_builder_put_casenumber (&b, dict->case_limit);
+  pxd_builder_put_string (&b, dict->label != NULL ? dict->label : "");
+  //XXX pxd_builder_put_string (&b, ds_cstr (&dict->documents));
+
+  pxd_builder_put_size_t (&b, dict->vector_cnt);
+  for (i = 0; i < dict->split_cnt; i++)
+    pxd_builder_put_link (&b, vector_save (dict->vector[i], pxd));
+
+  pxd_builder_put_link (&b, attrset_save (&dict->attributes, pxd));
+
+  pxd_builder_put_size_t (&b, dict->n_mrsets);
+  for (i = 0; i < dict->n_mrsets; i++)
+    pxd_builder_put_link (&b, mrset_save (dict->mrsets[i], pxd));
+
+  pxd_builder_put_string (&b, dict->encoding ? dict->encoding : "");
+
+  return pxd_builder_commit (&b);
+}
+
+static void
+vardict_info_load (struct dictionary *dict, struct pxd_object *obj,
+                   const struct pxd *pxd)
+{
+  struct pxd_parser p;
+  struct variable *var;
+  int case_index;
+
+  pxd_parser_init (&p, obj, pxd);
+  case_index = pxd_parser_get_u32 (&p);
+  var = add_var (dict, var_load (pxd_parser_get_link (&p), pxd));
+  var_get_vardict (var)->case_index = case_index;
+  pxd_parser_destroy (&p);
+}
+
+struct dictionary *
+dict_load (struct pxd_object *obj, const struct pxd *pxd)
+{
+  struct dictionary *dict;
+  struct pxd_array array;
+  struct pxd_parser p;
+  char *encoding;
+  int weight_idx;
+  int filter_idx;
+  size_t i;
+
+  pxd_parser_init (&p, obj, pxd);
+
+  encoding = pxd_parser_get_string (&p);
+  dict = dict_create (encoding);
+  free (encoding);
+
+  pxd_array_init (&array, pxd_parser_get_link (&p), pxd);
+  for (i = 0; i < pxd_array_size (&array); i++)
+    vardict_info_load (dict, pxd_array_get (&array, i), pxd);
+  pxd_array_destroy (&array);
+
+  dict->next_value_idx = pxd_parser_get_u32 (&p);
+
+  dict->split_cnt = pxd_parser_get_size_t (&p);
+  if (dict->split_cnt > 0)
+    {
+      dict->split = xnmalloc (dict->split_cnt, sizeof *dict->split);
+      for (i = 0; i < dict->split_cnt; i++)
+        dict->split[i] = dict_get_var (dict, i);
+    }
+
+  weight_idx = pxd_parser_get_s32 (&p);
+  if (weight_idx >= 0)
+    dict_set_weight (dict, dict_get_var (dict, weight_idx));
+
+  filter_idx = pxd_parser_get_s32 (&p);
+  if (filter_idx >= 0)
+    dict_set_filter (dict, dict_get_var (dict, filter_idx));
+
+  dict->case_limit = pxd_parser_get_casenumber (&p);
+
+  dict->label = pxd_parser_get_string (&p);
+  if (dict->label[0] == '\0')
+    dict_set_label (dict, NULL);
+
+#if 0                           /* XXX */
+  documents = pxd_parser_get_string (&p);
+  if (documents[0] != '\0')
+    ds_assign_cstr (&dict->documents, documents);
+  free (documents);
+#endif
+
+  dict->vector_cnt = pxd_parser_get_size_t (&p);
+  if (dict->vector_cnt > 0)
+    {
+      dict->vector = xnmalloc (dict->vector_cnt, sizeof *dict->vector);
+      for (i = 0; i < dict->vector_cnt; i++)
+        dict->vector[i] = vector_load (pxd_parser_get_link (&p), pxd, dict);
+    }
+
+  attrset_destroy (&dict->attributes);
+  attrset_load (&dict->attributes, pxd_parser_get_link (&p), pxd);
+
+  dict->n_mrsets = pxd_parser_get_size_t (&p);
+  if (dict->n_mrsets > 0)
+    {
+      dict->mrsets = xnmalloc (dict->n_mrsets, sizeof *dict->mrsets);
+      for (i = 0; i < dict->n_mrsets; i++)
+        dict->mrsets[i] = mrset_load (pxd_parser_get_link (&p), pxd, dict);
+    }
+
+  dict->encoding = pxd_parser_get_string (&p);
+  if (dict->encoding[0] == '\0')
+    {
+      free (dict->encoding);
+      dict->encoding = NULL;
+    }
+
+  pxd_parser_destroy (&p);
+
+  return dict;
+}
+
+static void
+dict_uncache (struct dictionary *dict UNUSED)
+{
+}
+