1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2009 Free Software Foundation, Inc.
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.
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.
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/>. */
19 #include "value-labels.h"
23 #include <data/data-out.h>
24 #include <data/value.h>
25 #include <data/variable.h>
26 #include <libpspp/array.h>
27 #include <libpspp/compiler.h>
28 #include <libpspp/hash-functions.h>
29 #include <libpspp/hmap.h>
30 #include <libpspp/message.h>
31 #include <libpspp/str.h>
35 static struct atom *atom_create (const char *string);
36 static void atom_destroy (struct atom *);
37 static const char *atom_to_string (const struct atom *);
39 /* Returns the label in VL. The caller must not modify or free
40 the returned value. */
42 val_lab_get_label (const struct val_lab *vl)
44 return atom_to_string (vl->label);
47 /* Creates and returns a new, empty set of value labels with the
50 val_labs_create (int width)
52 struct val_labs *vls = xmalloc (sizeof *vls);
54 hmap_init (&vls->labels);
58 /* Creates and returns a new set of value labels identical to
59 VLS. Returns a null pointer if VLS is null. */
61 val_labs_clone (const struct val_labs *vls)
63 struct val_labs *copy;
64 struct val_lab *label;
69 copy = val_labs_create (vls->width);
70 HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels)
71 val_labs_add (copy, &label->value, atom_to_string (label->label));
75 /* Determines whether VLS's width can be changed to NEW_WIDTH,
76 using the rules checked by value_is_resizable. */
78 val_labs_can_set_width (const struct val_labs *vls, int new_width)
80 struct val_lab *label;
82 HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels)
83 if (!value_is_resizable (&label->value, vls->width, new_width))
89 /* Changes the width of VLS to NEW_WIDTH. The original and new
90 width must be both numeric or both string. */
92 val_labs_set_width (struct val_labs *vls, int new_width)
94 assert (val_labs_can_set_width (vls, new_width));
95 if (value_needs_resize (vls->width, new_width))
97 struct val_lab *label;
98 HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels)
99 value_resize (&label->value, vls->width, new_width);
101 vls->width = new_width;
106 val_labs_destroy (struct val_labs *vls)
110 val_labs_clear (vls);
111 hmap_destroy (&vls->labels);
116 /* Removes all the value labels from VLS. */
118 val_labs_clear (struct val_labs *vls)
120 struct val_lab *label, *next;
122 HMAP_FOR_EACH_SAFE (label, next, struct val_lab, node, &vls->labels)
124 hmap_delete (&vls->labels, &label->node);
125 value_destroy (&label->value, vls->width);
126 atom_destroy (label->label);
131 /* Returns the number of value labels in VLS.
132 Returns 0 if VLS is null. */
134 val_labs_count (const struct val_labs *vls)
136 return vls == NULL ? 0 : hmap_count (&vls->labels);
140 do_add_val_lab (struct val_labs *vls, const union value *value,
143 struct val_lab *lab = xmalloc (sizeof *lab);
144 value_init (&lab->value, vls->width);
145 value_copy (&lab->value, value, vls->width);
146 lab->label = atom_create (label);
147 hmap_insert (&vls->labels, &lab->node, value_hash (value, vls->width, 0));
150 /* If VLS does not already contain a value label for VALUE, adds
151 LABEL for it and returns true. Otherwise, returns false. */
153 val_labs_add (struct val_labs *vls, const union value *value,
156 const struct val_lab *lab = val_labs_lookup (vls, value);
159 do_add_val_lab (vls, value, label);
166 /* Sets LABEL as the value label for VALUE in VLS, replacing any
167 existing label for VALUE. */
169 val_labs_replace (struct val_labs *vls, const union value *value,
172 struct val_lab *vl = (struct val_lab *) val_labs_lookup (vls, value);
175 atom_destroy (vl->label);
176 vl->label = atom_create (label);
179 do_add_val_lab (vls, value, label);
182 /* Removes LABEL from VLS. */
184 val_labs_remove (struct val_labs *vls, const struct val_lab *label_)
186 struct val_lab *label = (struct val_lab *) label_;
187 hmap_delete (&vls->labels, &label->node);
188 value_destroy (&label->value, vls->width);
189 atom_destroy (label->label);
193 /* Searches VLS for a value label for VALUE. If successful,
194 returns the string used as the label; otherwise, returns a
195 null pointer. Returns a null pointer if VLS is null. */
197 val_labs_find (const struct val_labs *vls, const union value *value)
199 const struct val_lab *label = val_labs_lookup (vls, value);
200 return label ? atom_to_string (label->label) : NULL;
203 /* Searches VLS for a value label for VALUE. If successful,
204 returns the value label; otherwise, returns a null pointer.
205 Returns a null pointer if VLS is null. */
206 const struct val_lab *
207 val_labs_lookup (const struct val_labs *vls, const union value *value)
211 struct val_lab *label;
212 HMAP_FOR_EACH_WITH_HASH (label, struct val_lab, node,
213 value_hash (value, vls->width, 0), &vls->labels)
214 if (value_equal (&label->value, value, vls->width))
220 /* Returns the first value label in VLS, in arbitrary order, or a
221 null pointer if VLS is empty or if VLS is a null pointer. If
222 the return value is non-null, then val_labs_next() may be used
223 to continue iterating. */
224 const struct val_lab *
225 val_labs_first (const struct val_labs *vls)
227 return vls ? HMAP_FIRST (struct val_lab, node, &vls->labels) : NULL;
230 /* Returns the next value label in an iteration begun by
231 val_labs_first(). If the return value is non-null, then
232 val_labs_next() may be used to continue iterating. */
233 const struct val_lab *
234 val_labs_next (const struct val_labs *vls, const struct val_lab *label)
236 return HMAP_NEXT (label, struct val_lab, node, &vls->labels);
240 compare_labels_by_value_3way (const void *a_, const void *b_, const void *vls_)
242 const struct val_lab *const *a = a_;
243 const struct val_lab *const *b = b_;
244 const struct val_labs *vls = vls_;
245 return value_compare_3way (&(*a)->value, &(*b)->value, vls->width);
248 /* Allocates and returns an array of pointers to value labels
249 that is sorted in increasing order by value. The array has
250 val_labs_count(VLS) elements. The caller is responsible for
251 freeing the array. */
252 const struct val_lab **
253 val_labs_sorted (const struct val_labs *vls)
257 const struct val_lab *label;
258 const struct val_lab **labels;
261 labels = xmalloc (val_labs_count (vls) * sizeof *labels);
263 HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels)
265 assert (i == val_labs_count (vls));
266 sort (labels, val_labs_count (vls), sizeof *labels,
267 compare_labels_by_value_3way, vls);
274 /* Atoms: reference-counted constant strings. */
279 struct hmap_node node; /* Hash map node. */
280 char *string; /* String value. */
281 unsigned ref_count; /* Number of references. */
284 /* Hash table of atoms. */
285 static struct hmap atoms = HMAP_INITIALIZER (atoms);
287 static void free_atom (struct atom *atom);
288 static void free_all_atoms (void);
290 /* Creates and returns an atom for STRING. */
292 atom_create (const char *string)
294 static bool initialized;
298 assert (string != NULL);
303 atexit (free_all_atoms);
306 hash = hash_string (string, 0);
307 HMAP_FOR_EACH_WITH_HASH (atom, struct atom, node, hash, &atoms)
308 if (!strcmp (atom->string, string))
314 atom = xmalloc (sizeof *atom);
315 atom->string = xstrdup (string);
317 hmap_insert (&atoms, &atom->node, hash);
323 atom_destroy (struct atom *atom)
327 assert (atom->ref_count > 0);
329 if (atom->ref_count == 0)
331 hmap_delete (&atoms, &atom->node);
337 /* Returns the string associated with ATOM. */
339 atom_to_string (const struct atom *atom)
345 free_atom (struct atom *atom)
352 free_all_atoms (void)
354 struct atom *atom, *next;
356 HMAP_FOR_EACH_SAFE (atom, next, struct atom, node, &atoms)