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/cast.h>
28 #include <libpspp/compiler.h>
29 #include <libpspp/hash-functions.h>
30 #include <libpspp/hmap.h>
31 #include <libpspp/message.h>
32 #include <libpspp/str.h>
36 static struct atom *atom_create (const char *string);
37 static void atom_destroy (struct atom *);
38 static const char *atom_to_string (const struct atom *);
40 /* Returns the label in VL. The caller must not modify or free
41 the returned value. */
43 val_lab_get_label (const struct val_lab *vl)
45 return atom_to_string (vl->label);
48 /* Creates and returns a new, empty set of value labels with the
51 val_labs_create (int width)
53 struct val_labs *vls = xmalloc (sizeof *vls);
55 hmap_init (&vls->labels);
59 /* Creates and returns a new set of value labels identical to
60 VLS. Returns a null pointer if VLS is null. */
62 val_labs_clone (const struct val_labs *vls)
64 struct val_labs *copy;
65 struct val_lab *label;
70 copy = val_labs_create (vls->width);
71 HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels)
72 val_labs_add (copy, &label->value, atom_to_string (label->label));
76 /* Determines whether VLS's width can be changed to NEW_WIDTH,
77 using the rules checked by value_is_resizable. */
79 val_labs_can_set_width (const struct val_labs *vls, int new_width)
81 struct val_lab *label;
83 HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels)
84 if (!value_is_resizable (&label->value, vls->width, new_width))
90 /* Changes the width of VLS to NEW_WIDTH. The original and new
91 width must be both numeric or both string. */
93 val_labs_set_width (struct val_labs *vls, int new_width)
95 assert (val_labs_can_set_width (vls, new_width));
96 if (value_needs_resize (vls->width, new_width))
98 struct val_lab *label;
99 HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels)
100 value_resize (&label->value, vls->width, new_width);
102 vls->width = new_width;
107 val_labs_destroy (struct val_labs *vls)
111 val_labs_clear (vls);
112 hmap_destroy (&vls->labels);
117 /* Removes all the value labels from VLS. */
119 val_labs_clear (struct val_labs *vls)
121 struct val_lab *label, *next;
123 HMAP_FOR_EACH_SAFE (label, next, struct val_lab, node, &vls->labels)
125 hmap_delete (&vls->labels, &label->node);
126 value_destroy (&label->value, vls->width);
127 atom_destroy (label->label);
132 /* Returns the number of value labels in VLS.
133 Returns 0 if VLS is null. */
135 val_labs_count (const struct val_labs *vls)
137 return vls == NULL ? 0 : hmap_count (&vls->labels);
141 do_add_val_lab (struct val_labs *vls, const union value *value,
144 struct val_lab *lab = xmalloc (sizeof *lab);
145 value_init (&lab->value, vls->width);
146 value_copy (&lab->value, value, vls->width);
147 lab->label = atom_create (label);
148 hmap_insert (&vls->labels, &lab->node, value_hash (value, vls->width, 0));
151 /* If VLS does not already contain a value label for VALUE, adds
152 LABEL for it and returns true. Otherwise, returns false. */
154 val_labs_add (struct val_labs *vls, const union value *value,
157 const struct val_lab *lab = val_labs_lookup (vls, value);
160 do_add_val_lab (vls, value, label);
167 /* Sets LABEL as the value label for VALUE in VLS, replacing any
168 existing label for VALUE. */
170 val_labs_replace (struct val_labs *vls, const union value *value,
173 struct val_lab *vl = CONST_CAST (struct val_lab *,
174 val_labs_lookup (vls, value));
177 atom_destroy (vl->label);
178 vl->label = atom_create (label);
181 do_add_val_lab (vls, value, label);
184 /* Removes LABEL from VLS. */
186 val_labs_remove (struct val_labs *vls, const struct val_lab *label_)
188 struct val_lab *label = CONST_CAST (struct val_lab *, label_);
189 hmap_delete (&vls->labels, &label->node);
190 value_destroy (&label->value, vls->width);
191 atom_destroy (label->label);
195 /* Searches VLS for a value label for VALUE. If successful,
196 returns the string used as the label; otherwise, returns a
197 null pointer. Returns a null pointer if VLS is null. */
199 val_labs_find (const struct val_labs *vls, const union value *value)
201 const struct val_lab *label = val_labs_lookup (vls, value);
202 return label ? atom_to_string (label->label) : NULL;
205 /* Searches VLS for a value label for VALUE. If successful,
206 returns the value label; otherwise, returns a null pointer.
207 Returns a null pointer if VLS is null. */
208 const struct val_lab *
209 val_labs_lookup (const struct val_labs *vls, const union value *value)
213 struct val_lab *label;
214 HMAP_FOR_EACH_WITH_HASH (label, struct val_lab, node,
215 value_hash (value, vls->width, 0), &vls->labels)
216 if (value_equal (&label->value, value, vls->width))
222 /* Returns the first value label in VLS, in arbitrary order, or a
223 null pointer if VLS is empty or if VLS is a null pointer. If
224 the return value is non-null, then val_labs_next() may be used
225 to continue iterating. */
226 const struct val_lab *
227 val_labs_first (const struct val_labs *vls)
229 return vls ? HMAP_FIRST (struct val_lab, node, &vls->labels) : NULL;
232 /* Returns the next value label in an iteration begun by
233 val_labs_first(). If the return value is non-null, then
234 val_labs_next() may be used to continue iterating. */
235 const struct val_lab *
236 val_labs_next (const struct val_labs *vls, const struct val_lab *label)
238 return HMAP_NEXT (label, struct val_lab, node, &vls->labels);
242 compare_labels_by_value_3way (const void *a_, const void *b_, const void *vls_)
244 const struct val_lab *const *a = a_;
245 const struct val_lab *const *b = b_;
246 const struct val_labs *vls = vls_;
247 return value_compare_3way (&(*a)->value, &(*b)->value, vls->width);
250 /* Allocates and returns an array of pointers to value labels
251 that is sorted in increasing order by value. The array has
252 val_labs_count(VLS) elements. The caller is responsible for
253 freeing the array. */
254 const struct val_lab **
255 val_labs_sorted (const struct val_labs *vls)
259 const struct val_lab *label;
260 const struct val_lab **labels;
263 labels = xmalloc (val_labs_count (vls) * sizeof *labels);
265 HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels)
267 assert (i == val_labs_count (vls));
268 sort (labels, val_labs_count (vls), sizeof *labels,
269 compare_labels_by_value_3way, vls);
276 /* Atoms: reference-counted constant strings. */
281 struct hmap_node node; /* Hash map node. */
282 char *string; /* String value. */
283 unsigned ref_count; /* Number of references. */
286 /* Hash table of atoms. */
287 static struct hmap atoms = HMAP_INITIALIZER (atoms);
289 static void free_atom (struct atom *atom);
290 static void free_all_atoms (void);
292 /* Creates and returns an atom for STRING. */
294 atom_create (const char *string)
296 static bool initialized;
300 assert (string != NULL);
305 atexit (free_all_atoms);
308 hash = hash_string (string, 0);
309 HMAP_FOR_EACH_WITH_HASH (atom, struct atom, node, hash, &atoms)
310 if (!strcmp (atom->string, string))
316 atom = xmalloc (sizeof *atom);
317 atom->string = xstrdup (string);
319 hmap_insert (&atoms, &atom->node, hash);
325 atom_destroy (struct atom *atom)
329 assert (atom->ref_count > 0);
331 if (atom->ref_count == 0)
333 hmap_delete (&atoms, &atom->node);
339 /* Returns the string associated with ATOM. */
341 atom_to_string (const struct atom *atom)
347 free_atom (struct atom *atom)
354 free_all_atoms (void)
356 struct atom *atom, *next;
358 HMAP_FOR_EACH_SAFE (atom, next, struct atom, node, &atoms)