pxd: initial work
[pspp] / src / data / attributes.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013 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 "data/attributes.h"
20
21 #include <assert.h>
22 #include <string.h>
23
24 #include "libpspp/array.h"
25 #include "libpspp/compiler.h"
26 #include "libpspp/hash-functions.h"
27 #include "libpspp/i18n.h"
28 #include "libpspp/pxd.h"
29
30 #include "gl/xalloc.h"
31
32 /* A custom attribute of the sort maintained by the DATAFILE
33    ATTRIBUTE and VARIABLE ATTRIBUTE commands.
34
35    Attributes have a name (the rules for which are the same as
36    those for PSPP variable names) and one or more values, each of
37    which is a string.  An attribute may be part of one attribute
38    set. */
39 struct attribute
40   {
41     struct attrset *set;        /* Containing set, if any. */
42     struct hmap_node node;      /* Used by attrset. */
43
44     char *name;                 /* Name. */
45     char **values;              /* Each value. */
46     size_t n_values;            /* Number of values. */
47     size_t allocated_values;    /* Amount of allocated space for values. */
48   };
49
50 static void attrset_uncache__ (struct attrset *);
51
52 /* Creates and returns a new attribute with the given NAME.  The
53    attribute initially has no values.  (Attributes with no values
54    cannot be saved to system files, so at least one value should
55    be added before the attribute is made available to the PSPP
56    user.) */
57 struct attribute *
58 attribute_create (const char *name)
59 {
60   struct attribute *attr = xmalloc (sizeof *attr);
61   attr->set = NULL;
62   attr->name = xstrdup (name);
63   attr->values = NULL;
64   attr->n_values = 0;
65   attr->allocated_values = 0;
66   return attr;
67 }
68
69 /* Creates and returns a new attribute with the same name and
70    values as ORIG. */
71 struct attribute *
72 attribute_clone (const struct attribute *orig)
73 {
74   struct attribute *attr;
75   size_t i;
76
77   attr = attribute_create (orig->name);
78   for (i = 0; i < orig->n_values; i++)
79     attribute_add_value (attr, orig->values[i]);
80   return attr;
81 }
82
83 /* Destroys ATTR and frees all associated memory.
84
85    This function must not be called if ATTR is part of an
86    attribute set.  Use attrset_delete() instead. */
87 void
88 attribute_destroy (struct attribute *attr)
89 {
90   if (attr != NULL)
91     {
92       size_t i;
93
94       assert (attr->set == NULL);
95
96       for (i = 0; i < attr->n_values; i++)
97         free (attr->values[i]);
98       free (attr->values);
99       free (attr->name);
100       free (attr);
101     }
102 }
103
104 /* Returns the name of ATTR.  The caller must not free or modify
105    the returned string. */
106 const char *
107 attribute_get_name (const struct attribute *attr)
108 {
109   return attr->name;
110 }
111
112 /* Returns ATTR's value with the given INDEX, or a null pointer
113    if INDEX is greater than or equal to the number of values in
114    ATTR (that is, attributes are numbered starting from 0).  The
115    caller must not free or modify the returned string.  */
116 const char *
117 attribute_get_value (const struct attribute *attr, size_t index)
118 {
119   return index < attr->n_values ? attr->values[index] : NULL;
120 }
121
122 /* Returns ATTR's number of values. */
123 size_t
124 attribute_get_n_values (const struct attribute *attrs)
125 {
126   return attrs->n_values;
127 }
128
129 /* Adds a copy of VALUE as a new value to ATTR.  The caller
130    retains ownership of VALUE. */
131 void
132 attribute_add_value (struct attribute *attr, const char *value)
133 {
134   attrset_uncache__ (attr->set);
135
136   if (attr->n_values >= attr->allocated_values)
137     attr->values = x2nrealloc (attr->values, &attr->allocated_values,
138                                sizeof *attr->values);
139   attr->values[attr->n_values++] = xstrdup (value);
140 }
141
142 /* Adds or replaces the value with the given INDEX in ATTR by a
143    copy of VALUE.  The caller retains ownership of VALUE.
144
145    If INDEX is an existing value index, that value is replaced.
146    If no value index numbered INDEX exists in ATTR, then it is
147    added, and any values intermediate between the last maximum
148    index and INDEX are set to the empty string. */
149 void
150 attribute_set_value (struct attribute *attr, size_t index, const char *value)
151 {
152   attrset_uncache__ (attr->set);
153
154   if (index < attr->n_values)
155     {
156       /* Replace existing value. */
157       free (attr->values[index]);
158       attr->values[index] = xstrdup (value);
159     }
160   else
161     {
162       /* Add new value. */
163       while (index > attr->n_values)
164         attribute_add_value (attr, "");
165       attribute_add_value (attr, value);
166     }
167
168 }
169
170 /* Deletes the value with the given INDEX from ATTR.  Any values
171    with higher-numbered indexes are shifted down into the gap
172    that this creates.
173
174    If INDEX is greater than the maximum index, this has no effect.*/
175 void
176 attribute_del_value (struct attribute *attr, size_t index)
177 {
178   attrset_uncache__ (attr->set);
179
180   if (index < attr->n_values)
181     {
182       free (attr->values[index]);
183       remove_element (attr->values, attr->n_values, sizeof *attr->values,
184                       index);
185       attr->n_values--;
186     }
187 }
188
189 struct pxd_object *
190 attribute_save (const struct attribute *attr, struct pxd *pxd)
191 {
192   struct pxd_builder b;
193   size_t i;
194
195   pxd_builder_init (&b, pxd);
196
197   pxd_builder_put_string (&b, attr->name);
198   pxd_builder_put_size_t (&b, attr->n_values);
199   for (i = 0; i < attr->n_values; i++)
200     pxd_builder_put_string (&b, attr->values[i]);
201
202   return pxd_builder_commit (&b);
203 }
204
205 struct attribute *
206 attribute_load (struct pxd_object *object, const struct pxd *pxd)
207 {
208   struct attribute *attr;
209   struct pxd_parser p;
210   size_t n, i;
211   char *name;
212
213   pxd_parser_init (&p, object, pxd);
214
215   name = pxd_parser_get_string (&p);
216   attr = attribute_create (name);
217   free (name);
218
219   n = pxd_parser_get_size_t (&p);
220   for (i = 0; i < n; i++)
221     {
222       char *value = pxd_parser_get_string (&p);
223       attribute_add_value (attr, value);
224       free (value);
225     }
226
227   pxd_parser_destroy (&p);
228
229   return attr;
230 }
231 \f
232 /* Initializes SET as a new, initially empty attibute set. */
233 void
234 attrset_init (struct attrset *set)
235 {
236   hmap_init (&set->map);
237 }
238
239 /* Initializes NEW_SET as a new attribute set whose contents are
240    initially the same as that of OLD_SET. */
241 void
242 attrset_clone (struct attrset *new_set, const struct attrset *old_set)
243 {
244   struct attribute *old_attr;
245
246   attrset_init (new_set);
247   HMAP_FOR_EACH (old_attr, struct attribute, node, &old_set->map)
248     {
249       struct attribute *new_attr = attribute_clone (old_attr);
250       hmap_insert (&new_set->map, &new_attr->node,
251                    hmap_node_hash (&old_attr->node));
252     }
253 }
254
255 /* Frees the storage associated with SET, if SET is nonnull.
256    (Does not free SET itself.) */
257 void
258 attrset_destroy (struct attrset *set)
259 {
260   if (set != NULL)
261     {
262       struct attribute *attr, *next;
263
264       attrset_uncache__ (set);
265
266       HMAP_FOR_EACH_SAFE (attr, next, struct attribute, node, &set->map)
267         {
268           attr->set = NULL;
269           attribute_destroy (attr);
270         }
271       hmap_destroy (&set->map);
272     }
273 }
274
275 /* Returns the number of attributes in SET. */
276 size_t
277 attrset_count (const struct attrset *set)
278 {
279   return hmap_count (&set->map);
280 }
281
282 /* Returns the attribute in SET whose name matches NAME
283    case-insensitively, or a null pointer if SET does not contain
284    an attribute with that name. */
285 struct attribute *
286 attrset_lookup (struct attrset *set, const char *name)
287 {
288   struct attribute *attr;
289   HMAP_FOR_EACH_WITH_HASH (attr, struct attribute, node,
290                            utf8_hash_case_string (name, 0), &set->map)
291     if (!utf8_strcasecmp (attribute_get_name (attr), name))
292       break;
293   return attr;
294 }
295
296 /* Adds ATTR to SET, which must not already contain an attribute
297    with the same name (matched case insensitively).  Ownership of
298    ATTR is transferred to SET. */
299 void
300 attrset_add (struct attrset *set, struct attribute *attr)
301 {
302   const char *name = attribute_get_name (attr);
303
304   assert (attrset_lookup (set, name) == NULL);
305   assert (attr->set == NULL);
306
307   attrset_uncache__ (set);
308
309   attr->set = set;
310   hmap_insert (&set->map, &attr->node, utf8_hash_case_string (name, 0));
311 }
312
313 /* Deletes any attribute from SET that matches NAME
314    (case-insensitively). */
315 void
316 attrset_delete (struct attrset *set, const char *name)
317 {
318   struct attribute *attr = attrset_lookup (set, name);
319   if (attr != NULL)
320     {
321       attrset_uncache__ (set);
322       hmap_delete (&set->map, &attr->node);
323       attr->set = NULL;
324       attribute_destroy (attr);
325     }
326 }
327
328 /* Deletes all attributes from SET. */
329 void
330 attrset_clear (struct attrset *set)
331 {
332   if (!hmap_is_empty (&set->map))
333     {
334       attrset_destroy (set);
335       attrset_init (set);
336     }
337 }
338
339 struct pxd_object *
340 attrset_save (const struct attrset *set, struct pxd *pxd)
341 {
342   struct attribute *attr;
343   struct pxd_builder b;
344
345   pxd_builder_init (&b, pxd);
346
347   HMAP_FOR_EACH (attr, struct attribute, node, &set->map)
348     pxd_builder_put_link (&b, attribute_save (attr, pxd));
349
350   return pxd_builder_commit (&b);
351 }
352
353 void
354 attrset_load (struct attrset *set,
355               struct pxd_object *object, const struct pxd *pxd)
356 {
357   struct pxd_parser p;
358   unsigned int i;
359
360   pxd_parser_init (&p, object, pxd);
361
362   attrset_init (set);
363   for (i = 0; i < pxd_object_get_n_links (object); i++)
364     {
365       struct pxd_object *attr_obj = pxd_object_get_link (object, i, pxd);
366       struct attribute *attr = attribute_load (attr_obj, pxd);
367       attrset_add (set, attr);
368     }
369
370   pxd_parser_destroy (&p);
371 }
372
373 static struct attribute *iterator_data (struct attrset_iterator *iterator)
374 {
375   return HMAP_NULLABLE_DATA (iterator->node, struct attribute, node);
376 }
377
378 /* Returns the first attribute in SET and initializes ITERATOR.
379    If SET is empty, returns a null pointer.
380
381    The caller must not destroy the returned attribute, but it may
382    add or remove values.
383
384    Attributes are visited in no particular order.  Calling
385    attrset_add() during iteration can cause some attributes to
386    be visited more than once and others not at all. */
387 struct attribute *
388 attrset_first (const struct attrset *set, struct attrset_iterator *iterator)
389 {
390   iterator->node = hmap_first (&set->map);
391   return iterator_data (iterator);
392 }
393
394 /* Returns the next attribute in SET and advances ITERATOR, which
395    should have been initialized by calling attrset_first().  If
396    all the attributes in SET have already been visited, returns a
397    null pointer.
398
399    The caller must not destroy the returned attribute, but it may
400    add or remove values.
401
402    Attributes are visited in no particular order.  Calling
403    attrset_add() during iteration can cause some attributes to
404    be visited more than once and others not at all. */
405 struct attribute *
406 attrset_next (const struct attrset *set, struct attrset_iterator *iterator)
407 {
408   iterator->node = hmap_next (&set->map, iterator->node);
409   return iterator_data (iterator);
410 }
411
412 static int
413 compare_attribute_by_name (const void *a_, const void *b_)
414 {
415   const struct attribute *const *a = a_;
416   const struct attribute *const *b = b_;
417
418   return strcmp ((*a)->name, (*b)->name);
419 }
420
421 /* Allocates and returns an array of pointers to attributes
422    that is sorted by attribute name.  The array has
423    'attrset_count (SET)' elements.  The caller is responsible for
424    freeing the array. */
425 struct attribute **
426 attrset_sorted (const struct attrset *set)
427 {
428   if (set != NULL && attrset_count (set) > 0)
429     {
430       struct attribute **attrs;
431       struct attribute *attr;
432       size_t i;
433
434       attrs = xmalloc (attrset_count (set) * sizeof *attrs);
435       i = 0;
436       HMAP_FOR_EACH (attr, struct attribute, node, &set->map)
437         attrs[i++] = attr;
438       assert (i == attrset_count (set));
439       qsort (attrs, attrset_count (set), sizeof *attrs,
440              compare_attribute_by_name);
441       return attrs;
442     }
443   else
444     return NULL;
445 }
446
447 static void
448 attrset_uncache__ (struct attrset *set UNUSED)
449 {
450   /* XXX */
451 }
452