pxd: initial work
[pspp] / src / data / attributes.c
index d99e945b014def2836ec1eb57ea33631fc5086d4..b04ebf0693ebe354be103a48c00b4f2ee0c37cc2 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 2008, 2009 Free Software Foundation, Inc.
+   Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013 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
 
 #include <config.h>
 
-#include <data/attributes.h>
+#include "data/attributes.h"
+
 #include <assert.h>
 #include <string.h>
-#include <libpspp/array.h>
-#include <libpspp/hash-functions.h>
-#include "xalloc.h"
+
+#include "libpspp/array.h"
+#include "libpspp/compiler.h"
+#include "libpspp/hash-functions.h"
+#include "libpspp/i18n.h"
+#include "libpspp/pxd.h"
+
+#include "gl/xalloc.h"
 
 /* A custom attribute of the sort maintained by the DATAFILE
    ATTRIBUTE and VARIABLE ATTRIBUTE commands.
    set. */
 struct attribute
   {
+    struct attrset *set;        /* Containing set, if any. */
     struct hmap_node node;      /* Used by attrset. */
+
     char *name;                 /* Name. */
     char **values;              /* Each value. */
     size_t n_values;            /* Number of values. */
     size_t allocated_values;    /* Amount of allocated space for values. */
   };
 
+static void attrset_uncache__ (struct attrset *);
+
 /* Creates and returns a new attribute with the given NAME.  The
    attribute initially has no values.  (Attributes with no values
    cannot be saved to system files, so at least one value should
@@ -48,6 +58,7 @@ struct attribute *
 attribute_create (const char *name)
 {
   struct attribute *attr = xmalloc (sizeof *attr);
+  attr->set = NULL;
   attr->name = xstrdup (name);
   attr->values = NULL;
   attr->n_values = 0;
@@ -80,6 +91,8 @@ attribute_destroy (struct attribute *attr)
     {
       size_t i;
 
+      assert (attr->set == NULL);
+
       for (i = 0; i < attr->n_values; i++)
         free (attr->values[i]);
       free (attr->values);
@@ -118,6 +131,8 @@ attribute_get_n_values (const struct attribute *attrs)
 void
 attribute_add_value (struct attribute *attr, const char *value)
 {
+  attrset_uncache__ (attr->set);
+
   if (attr->n_values >= attr->allocated_values)
     attr->values = x2nrealloc (attr->values, &attr->allocated_values,
                                sizeof *attr->values);
@@ -134,6 +149,8 @@ attribute_add_value (struct attribute *attr, const char *value)
 void
 attribute_set_value (struct attribute *attr, size_t index, const char *value)
 {
+  attrset_uncache__ (attr->set);
+
   if (index < attr->n_values)
     {
       /* Replace existing value. */
@@ -158,6 +175,8 @@ attribute_set_value (struct attribute *attr, size_t index, const char *value)
 void
 attribute_del_value (struct attribute *attr, size_t index)
 {
+  attrset_uncache__ (attr->set);
+
   if (index < attr->n_values)
     {
       free (attr->values[index]);
@@ -166,6 +185,49 @@ attribute_del_value (struct attribute *attr, size_t index)
       attr->n_values--;
     }
 }
+
+struct pxd_object *
+attribute_save (const struct attribute *attr, struct pxd *pxd)
+{
+  struct pxd_builder b;
+  size_t i;
+
+  pxd_builder_init (&b, pxd);
+
+  pxd_builder_put_string (&b, attr->name);
+  pxd_builder_put_size_t (&b, attr->n_values);
+  for (i = 0; i < attr->n_values; i++)
+    pxd_builder_put_string (&b, attr->values[i]);
+
+  return pxd_builder_commit (&b);
+}
+
+struct attribute *
+attribute_load (struct pxd_object *object, const struct pxd *pxd)
+{
+  struct attribute *attr;
+  struct pxd_parser p;
+  size_t n, i;
+  char *name;
+
+  pxd_parser_init (&p, object, pxd);
+
+  name = pxd_parser_get_string (&p);
+  attr = attribute_create (name);
+  free (name);
+
+  n = pxd_parser_get_size_t (&p);
+  for (i = 0; i < n; i++)
+    {
+      char *value = pxd_parser_get_string (&p);
+      attribute_add_value (attr, value);
+      free (value);
+    }
+
+  pxd_parser_destroy (&p);
+
+  return attr;
+}
 \f
 /* Initializes SET as a new, initially empty attibute set. */
 void
@@ -199,8 +261,13 @@ attrset_destroy (struct attrset *set)
     {
       struct attribute *attr, *next;
 
+      attrset_uncache__ (set);
+
       HMAP_FOR_EACH_SAFE (attr, next, struct attribute, node, &set->map)
-        attribute_destroy (attr);
+        {
+          attr->set = NULL;
+          attribute_destroy (attr);
+        }
       hmap_destroy (&set->map);
     }
 }
@@ -220,8 +287,8 @@ attrset_lookup (struct attrset *set, const char *name)
 {
   struct attribute *attr;
   HMAP_FOR_EACH_WITH_HASH (attr, struct attribute, node,
-                           hash_case_string (name, 0), &set->map)
-    if (!strcasecmp (attribute_get_name (attr), name))
+                           utf8_hash_case_string (name, 0), &set->map)
+    if (!utf8_strcasecmp (attribute_get_name (attr), name))
       break;
   return attr;
 }
@@ -233,8 +300,14 @@ void
 attrset_add (struct attrset *set, struct attribute *attr)
 {
   const char *name = attribute_get_name (attr);
+
   assert (attrset_lookup (set, name) == NULL);
-  hmap_insert (&set->map, &attr->node, hash_case_string (name, 0));
+  assert (attr->set == NULL);
+
+  attrset_uncache__ (set);
+
+  attr->set = set;
+  hmap_insert (&set->map, &attr->node, utf8_hash_case_string (name, 0));
 }
 
 /* Deletes any attribute from SET that matches NAME
@@ -245,7 +318,9 @@ attrset_delete (struct attrset *set, const char *name)
   struct attribute *attr = attrset_lookup (set, name);
   if (attr != NULL)
     {
+      attrset_uncache__ (set);
       hmap_delete (&set->map, &attr->node);
+      attr->set = NULL;
       attribute_destroy (attr);
     }
 }
@@ -254,8 +329,45 @@ attrset_delete (struct attrset *set, const char *name)
 void
 attrset_clear (struct attrset *set)
 {
-  attrset_destroy (set);
+  if (!hmap_is_empty (&set->map))
+    {
+      attrset_destroy (set);
+      attrset_init (set);
+    }
+}
+
+struct pxd_object *
+attrset_save (const struct attrset *set, struct pxd *pxd)
+{
+  struct attribute *attr;
+  struct pxd_builder b;
+
+  pxd_builder_init (&b, pxd);
+
+  HMAP_FOR_EACH (attr, struct attribute, node, &set->map)
+    pxd_builder_put_link (&b, attribute_save (attr, pxd));
+
+  return pxd_builder_commit (&b);
+}
+
+void
+attrset_load (struct attrset *set,
+              struct pxd_object *object, const struct pxd *pxd)
+{
+  struct pxd_parser p;
+  unsigned int i;
+
+  pxd_parser_init (&p, object, pxd);
+
   attrset_init (set);
+  for (i = 0; i < pxd_object_get_n_links (object); i++)
+    {
+      struct pxd_object *attr_obj = pxd_object_get_link (object, i, pxd);
+      struct attribute *attr = attribute_load (attr_obj, pxd);
+      attrset_add (set, attr);
+    }
+
+  pxd_parser_destroy (&p);
 }
 
 static struct attribute *iterator_data (struct attrset_iterator *iterator)
@@ -296,3 +408,45 @@ attrset_next (const struct attrset *set, struct attrset_iterator *iterator)
   iterator->node = hmap_next (&set->map, iterator->node);
   return iterator_data (iterator);
 }
+
+static int
+compare_attribute_by_name (const void *a_, const void *b_)
+{
+  const struct attribute *const *a = a_;
+  const struct attribute *const *b = b_;
+
+  return strcmp ((*a)->name, (*b)->name);
+}
+
+/* Allocates and returns an array of pointers to attributes
+   that is sorted by attribute name.  The array has
+   'attrset_count (SET)' elements.  The caller is responsible for
+   freeing the array. */
+struct attribute **
+attrset_sorted (const struct attrset *set)
+{
+  if (set != NULL && attrset_count (set) > 0)
+    {
+      struct attribute **attrs;
+      struct attribute *attr;
+      size_t i;
+
+      attrs = xmalloc (attrset_count (set) * sizeof *attrs);
+      i = 0;
+      HMAP_FOR_EACH (attr, struct attribute, node, &set->map)
+        attrs[i++] = attr;
+      assert (i == attrset_count (set));
+      qsort (attrs, attrset_count (set), sizeof *attrs,
+             compare_attribute_by_name);
+      return attrs;
+    }
+  else
+    return NULL;
+}
+
+static void
+attrset_uncache__ (struct attrset *set UNUSED)
+{
+  /* XXX */
+}
+