/* 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
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;
{
size_t i;
+ assert (attr->set == NULL);
+
for (i = 0; i < attr->n_values; i++)
free (attr->values[i]);
free (attr->values);
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);
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. */
void
attribute_del_value (struct attribute *attr, size_t index)
{
+ attrset_uncache__ (attr->set);
+
if (index < attr->n_values)
{
free (attr->values[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
{
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);
}
}
{
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;
}
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
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);
}
}
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)
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 */
+}
+