better tests
[pspp] / src / data / attributes.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2008, 2009, 2011, 2012, 2016 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
29 #include "gl/xalloc.h"
30
31 /* A custom attribute of the sort maintained by the DATAFILE
32    ATTRIBUTE and VARIABLE ATTRIBUTE commands.
33
34    Attributes have a name (the rules for which are the same as
35    those for PSPP variable names) and one or more values, each of
36    which is a string.  An attribute may be part of one attribute
37    set. */
38 struct attribute
39   {
40     struct hmap_node node;      /* Used by attrset. */
41     char *name;                 /* Name. */
42     char **values;              /* Each value. */
43     size_t n_values;            /* Number of values. */
44     size_t allocated_values;    /* Amount of allocated space for values. */
45   };
46
47 /* Creates and returns a new attribute with the given NAME.  The
48    attribute initially has no values.  (Attributes with no values
49    cannot be saved to system files, so at least one value should
50    be added before the attribute is made available to the PSPP
51    user.) */
52 struct attribute *
53 attribute_create (const char *name)
54 {
55   struct attribute *attr = xmalloc (sizeof *attr);
56   attr->name = xstrdup (name);
57   attr->values = NULL;
58   attr->n_values = 0;
59   attr->allocated_values = 0;
60   return attr;
61 }
62
63 /* Creates and returns a new attribute with the same name and
64    values as ORIG. */
65 struct attribute *
66 attribute_clone (const struct attribute *orig)
67 {
68   struct attribute *attr;
69   size_t i;
70
71   attr = attribute_create (orig->name);
72   for (i = 0; i < orig->n_values; i++)
73     attribute_add_value (attr, orig->values[i]);
74   return attr;
75 }
76
77 /* Destroys ATTR and frees all associated memory.
78
79    This function must not be called if ATTR is part of an
80    attribute set.  Use attrset_delete() instead. */
81 void
82 attribute_destroy (struct attribute *attr)
83 {
84   if (attr != NULL)
85     {
86       size_t i;
87
88       for (i = 0; i < attr->n_values; i++)
89         free (attr->values[i]);
90       free (attr->values);
91       free (attr->name);
92       free (attr);
93     }
94 }
95
96 /* Returns the name of ATTR.  The caller must not free or modify
97    the returned string. */
98 const char *
99 attribute_get_name (const struct attribute *attr)
100 {
101   return attr->name;
102 }
103
104 /* Returns ATTR's value with the given INDEX, or a null pointer
105    if INDEX is greater than or equal to the number of values in
106    ATTR (that is, attributes are numbered starting from 0).  The
107    caller must not free or modify the returned string.  */
108 const char *
109 attribute_get_value (const struct attribute *attr, size_t index)
110 {
111   return index < attr->n_values ? attr->values[index] : NULL;
112 }
113
114 /* Returns ATTR's number of values. */
115 size_t
116 attribute_get_n_values (const struct attribute *attrs)
117 {
118   return attrs->n_values;
119 }
120
121 /* Adds a copy of VALUE as a new value to ATTR.  The caller
122    retains ownership of VALUE. */
123 void
124 attribute_add_value (struct attribute *attr, const char *value)
125 {
126   if (attr->n_values >= attr->allocated_values)
127     attr->values = x2nrealloc (attr->values, &attr->allocated_values,
128                                sizeof *attr->values);
129   attr->values[attr->n_values++] = xstrdup (value);
130 }
131
132 /* Adds or replaces the value with the given INDEX in ATTR by a
133    copy of VALUE.  The caller retains ownership of VALUE.
134
135    If INDEX is an existing value index, that value is replaced.
136    If no value index numbered INDEX exists in ATTR, then it is
137    added, and any values intermediate between the last maximum
138    index and INDEX are set to the empty string. */
139 void
140 attribute_set_value (struct attribute *attr, size_t index, const char *value)
141 {
142   if (index < attr->n_values)
143     {
144       /* Replace existing value. */
145       free (attr->values[index]);
146       attr->values[index] = xstrdup (value);
147     }
148   else
149     {
150       /* Add new value. */
151       while (index > attr->n_values)
152         attribute_add_value (attr, "");
153       attribute_add_value (attr, value);
154     }
155
156 }
157
158 /* Deletes the value with the given INDEX from ATTR.  Any values
159    with higher-numbered indexes are shifted down into the gap
160    that this creates.
161
162    If INDEX is greater than the maximum index, this has no effect.*/
163 void
164 attribute_del_value (struct attribute *attr, size_t index)
165 {
166   if (index < attr->n_values)
167     {
168       free (attr->values[index]);
169       remove_element (attr->values, attr->n_values, sizeof *attr->values,
170                       index);
171       attr->n_values--;
172     }
173 }
174 \f
175 /* Initializes SET as a new, initially empty attibute set. */
176 void
177 attrset_init (struct attrset *set)
178 {
179   hmap_init (&set->map);
180 }
181
182 /* Initializes NEW_SET as a new attribute set whose contents are
183    initially the same as that of OLD_SET. */
184 void
185 attrset_clone (struct attrset *new_set, const struct attrset *old_set)
186 {
187   struct attribute *old_attr;
188
189   attrset_init (new_set);
190   HMAP_FOR_EACH (old_attr, struct attribute, node, &old_set->map)
191     {
192       struct attribute *new_attr = attribute_clone (old_attr);
193       hmap_insert (&new_set->map, &new_attr->node,
194                    hmap_node_hash (&old_attr->node));
195     }
196 }
197
198 /* Frees the storage associated with SET, if SET is nonnull.
199    (Does not free SET itself.) */
200 void
201 attrset_destroy (struct attrset *set)
202 {
203   if (set != NULL)
204     {
205       struct attribute *attr, *next;
206
207       HMAP_FOR_EACH_SAFE (attr, next, struct attribute, node, &set->map)
208         attribute_destroy (attr);
209       hmap_destroy (&set->map);
210     }
211 }
212
213 /* Returns the number of attributes in SET. */
214 size_t
215 attrset_count (const struct attrset *set)
216 {
217   return hmap_count (&set->map);
218 }
219
220 /* Returns the attribute in SET whose name matches NAME
221    case-insensitively, or a null pointer if SET does not contain
222    an attribute with that name. */
223 struct attribute *
224 attrset_lookup (const struct attrset *set, const char *name)
225 {
226   const struct attribute *attr;
227   HMAP_FOR_EACH_WITH_HASH (attr, struct attribute, node,
228                            utf8_hash_case_string (name, 0), &set->map)
229     if (!utf8_strcasecmp (attribute_get_name (attr), name))
230       break;
231   return CONST_CAST (struct attribute *, attr);
232 }
233
234 /* Adds ATTR to SET.  Succeeds and returns true if SET does not already contain
235    an attribute with the same name (matched case insensitively); otherwise
236    fails and returns false.  On success only, ownership of ATTR is transferred
237    to SET. */
238 bool
239 attrset_try_add (struct attrset *set, struct attribute *attr)
240 {
241   const char *name = attribute_get_name (attr);
242   if (attrset_lookup (set, name))
243     return false;
244   hmap_insert (&set->map, &attr->node, utf8_hash_case_string (name, 0));
245   return true;
246 }
247
248 /* Adds ATTR to SET, which must not already contain an attribute
249    with the same name (matched case insensitively).  Ownership of
250    ATTR is transferred to SET. */
251 void
252 attrset_add (struct attrset *set, struct attribute *attr)
253 {
254   bool ok UNUSED = attrset_try_add (set, attr);
255   assert (ok);
256 }
257
258 /* Deletes any attribute from SET that matches NAME
259    (case-insensitively). */
260 void
261 attrset_delete (struct attrset *set, const char *name)
262 {
263   struct attribute *attr = attrset_lookup (set, name);
264   if (attr != NULL)
265     {
266       hmap_delete (&set->map, &attr->node);
267       attribute_destroy (attr);
268     }
269 }
270
271 /* Deletes all attributes from SET. */
272 void
273 attrset_clear (struct attrset *set)
274 {
275   attrset_destroy (set);
276   attrset_init (set);
277 }
278
279 static struct attribute *iterator_data (struct attrset_iterator *iterator)
280 {
281   return HMAP_NULLABLE_DATA (iterator->node, struct attribute, node);
282 }
283
284 /* Returns the first attribute in SET and initializes ITERATOR.
285    If SET is empty, returns a null pointer.
286
287    The caller must not destroy the returned attribute, but it may
288    add or remove values.
289
290    Attributes are visited in no particular order.  Calling
291    attrset_add() during iteration can cause some attributes to
292    be visited more than once and others not at all. */
293 struct attribute *
294 attrset_first (const struct attrset *set, struct attrset_iterator *iterator)
295 {
296   iterator->node = hmap_first (&set->map);
297   return iterator_data (iterator);
298 }
299
300 /* Returns the next attribute in SET and advances ITERATOR, which
301    should have been initialized by calling attrset_first().  If
302    all the attributes in SET have already been visited, returns a
303    null pointer.
304
305    The caller must not destroy the returned attribute, but it may
306    add or remove values.
307
308    Attributes are visited in no particular order.  Calling
309    attrset_add() during iteration can cause some attributes to
310    be visited more than once and others not at all. */
311 struct attribute *
312 attrset_next (const struct attrset *set, struct attrset_iterator *iterator)
313 {
314   iterator->node = hmap_next (&set->map, iterator->node);
315   return iterator_data (iterator);
316 }
317
318 static int
319 compare_attribute_by_name (const void *a_, const void *b_)
320 {
321   const struct attribute *const *a = a_;
322   const struct attribute *const *b = b_;
323
324   return strcmp ((*a)->name, (*b)->name);
325 }
326
327 /* Allocates and returns an array of pointers to attributes
328    that is sorted by attribute name.  The array has
329    'attrset_count (SET)' elements.  The caller is responsible for
330    freeing the array. */
331 struct attribute **
332 attrset_sorted (const struct attrset *set)
333 {
334   if (set != NULL && attrset_count (set) > 0)
335     {
336       struct attribute **attrs;
337       struct attribute *attr;
338       size_t i;
339
340       attrs = xmalloc (attrset_count (set) * sizeof *attrs);
341       i = 0;
342       HMAP_FOR_EACH (attr, struct attribute, node, &set->map)
343         attrs[i++] = attr;
344       assert (i == attrset_count (set));
345       qsort (attrs, attrset_count (set), sizeof *attrs,
346              compare_attribute_by_name);
347       return attrs;
348     }
349   else
350     return NULL;
351 }