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