1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2009, 2010, 2011, 2012 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 "data/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/intern.h"
32 #include "libpspp/message.h"
33 #include "libpspp/pxd.h"
34 #include "libpspp/str.h"
36 #include "gl/xalloc.h"
38 static void val_labs_uncache__ (struct val_labs *);
39 static void val_lab_uncache__ (struct val_lab *);
41 /* Creates and returns a new, empty set of value labels with the
44 val_labs_create (int width)
46 struct val_labs *vls = xmalloc (sizeof *vls);
48 hmap_init (&vls->labels);
52 /* Creates and returns a new set of value labels identical to
53 VLS. Returns a null pointer if VLS is null. */
55 val_labs_clone (const struct val_labs *vls)
57 struct val_labs *copy;
58 struct val_lab *label;
63 copy = val_labs_create (vls->width);
64 HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels)
65 val_labs_add (copy, &label->value, label->escaped_label);
69 /* Determines whether VLS's width can be changed to NEW_WIDTH,
70 using the rules checked by value_is_resizable. */
72 val_labs_can_set_width (const struct val_labs *vls, int new_width)
74 struct val_lab *label;
76 HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels)
77 if (!value_is_resizable (&label->value, vls->width, new_width))
83 /* Changes the width of VLS to NEW_WIDTH. The original and new
84 width must be both numeric or both string. */
86 val_labs_set_width (struct val_labs *vls, int new_width)
88 assert (val_labs_can_set_width (vls, new_width));
89 if (value_needs_resize (vls->width, new_width))
91 struct val_lab *label;
92 HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels)
93 value_resize (&label->value, vls->width, new_width);
95 if (vls->width != new_width)
97 vls->width = new_width;
98 val_labs_uncache__ (vls);
104 val_labs_destroy (struct val_labs *vls)
108 val_labs_uncache__ (vls);
109 val_labs_clear (vls);
110 hmap_destroy (&vls->labels);
116 val_lab_clear (struct val_lab *lab, int width)
118 val_lab_uncache__ (lab);
119 value_destroy (&lab->value, width);
120 intern_unref (lab->label);
123 /* Removes all the value labels from VLS. */
125 val_labs_clear (struct val_labs *vls)
127 struct val_lab *label, *next;
129 HMAP_FOR_EACH_SAFE (label, next, struct val_lab, node, &vls->labels)
131 val_lab_clear (label, vls->width);
132 hmap_delete (&vls->labels, &label->node);
137 /* Returns the width of VLS. */
139 val_labs_get_width (const struct val_labs *vls)
144 /* Returns the number of value labels in VLS.
145 Returns 0 if VLS is null. */
147 val_labs_count (const struct val_labs *vls)
149 return vls == NULL ? 0 : hmap_count (&vls->labels);
153 set_label (struct val_lab *lab, const char *escaped_label)
155 lab->escaped_label = intern_new (escaped_label);
156 if (strstr (escaped_label, "\\n") == NULL)
157 lab->label = intern_ref (lab->escaped_label);
164 ds_extend (&s, intern_strlen (lab->escaped_label));
165 for (p = escaped_label; *p != '\0'; p++)
168 if (c == '\\' && p[1] == 'n')
175 lab->label = intern_new (ds_cstr (&s));
181 val_labs_uncache__ (struct val_labs *vls UNUSED)
186 val_lab_uncache__ (struct val_lab *vl UNUSED)
190 static struct pxd_object *
191 val_lab_save (const struct val_lab *vl, int width, struct pxd *pxd)
193 struct pxd_builder b;
195 pxd_builder_init (&b, pxd);
197 pxd_builder_put_value (&b, &vl->value, width);
198 pxd_builder_put_interned_string (&b, vl->label);
200 return pxd_builder_commit (&b);
204 val_labs_save (const struct val_labs *vls, struct pxd *pxd)
206 struct val_lab *label;
207 struct pxd_builder b;
209 pxd_builder_init (&b, pxd);
211 pxd_builder_put_u32 (&b, vls->width);
212 HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels)
213 pxd_builder_put_link (&b, val_lab_save (label, vls->width, pxd));
215 return pxd_builder_commit (&b);
218 static struct val_lab *
219 val_lab_load (struct pxd_object *object, const struct pxd *pxd, int width)
224 pxd_parser_init (&p, object, pxd);
226 vl = xmalloc (sizeof *vl);
227 pxd_parser_get_value (&p, &vl->value, width);
228 vl->label = pxd_parser_get_interned_string (&p);
230 pxd_parser_destroy (&p);
236 val_labs_load (struct pxd_object *object, const struct pxd *pxd)
238 struct val_labs *vls;
243 pxd_parser_init (&p, object, pxd);
244 width = pxd_parser_get_u32 (&p);
246 vls = val_labs_create (width);
247 for (i = 0; i < pxd_object_get_n_links (object); i++)
249 struct pxd_object *lab_obj = pxd_object_get_link (object, i, pxd);
250 struct val_lab *lab = val_lab_load (lab_obj, pxd, width);
251 hmap_insert (&vls->labels, &lab->node,
252 value_hash (&lab->value, width, 0));
255 pxd_parser_destroy (&p);
261 do_add_val_lab (struct val_labs *vls, const union value *value,
262 const char *escaped_label)
264 struct val_lab *lab = xmalloc (sizeof *lab);
265 value_clone (&lab->value, value, vls->width);
266 set_label (lab, escaped_label);
267 hmap_insert (&vls->labels, &lab->node, value_hash (value, vls->width, 0));
270 /* If VLS does not already contain a value label for VALUE, adds the UTF-8
271 encoded LABEL for it and returns true. Otherwise, returns false.
273 In LABEL, the two-byte sequence "\\n" is interpreted as a new-line. */
275 val_labs_add (struct val_labs *vls, const union value *value,
278 const struct val_lab *lab = val_labs_lookup (vls, value);
281 do_add_val_lab (vls, value, label);
288 /* Sets LABEL as the value label for VALUE in VLS, replacing any
289 existing label for VALUE.
291 In LABEL, the two-byte sequence "\\n" is interpreted as a new-line. */
293 val_labs_replace (struct val_labs *vls, const union value *value,
296 struct val_lab *vl = val_labs_lookup (vls, value);
299 val_lab_uncache__ (vl);
300 intern_unref (vl->label);
301 intern_unref (vl->escaped_label);
302 set_label (vl, label);
305 do_add_val_lab (vls, value, label);
308 /* Removes LABEL from VLS. */
310 val_labs_remove (struct val_labs *vls, struct val_lab *label)
312 val_lab_clear (label, vls->width);
313 hmap_delete (&vls->labels, &label->node);
317 /* Searches VLS for a value label for VALUE. If successful, returns the string
318 used as the label, as a UTF-8 encoded string in a format suitable for
319 output. Otherwise, returns a null pointer. Returns a null pointer if VLS
322 val_labs_find (const struct val_labs *vls, const union value *value)
324 const struct val_lab *label = val_labs_lookup (vls, value);
325 return label ? label->label : NULL;
328 /* Searches VLS for a value label for VALUE. If successful,
329 returns the value label; otherwise, returns a null pointer.
330 Returns a null pointer if VLS is null. */
331 static struct val_lab *
332 val_labs_lookup__ (const struct val_labs *vls, const union value *value,
335 struct val_lab *label;
337 HMAP_FOR_EACH_WITH_HASH (label, struct val_lab, node, hash, &vls->labels)
338 if (value_equal (&label->value, value, vls->width))
344 /* Searches VLS for a value label for VALUE. If successful,
345 returns the value label; otherwise, returns a null pointer.
346 Returns a null pointer if VLS is null. */
348 val_labs_lookup (const struct val_labs *vls, const union value *value)
350 return (vls == NULL ? NULL
351 : val_labs_lookup__ (vls, value, value_hash (value, vls->width, 0)));
354 /* Searches VLS for a value label whose label is exactly LABEL. If successful,
355 returns the corresponding value. Otherwise, returns a null pointer.
357 Returns a null pointer if VLS is null.
359 This function is O(n) in the number of labels in VLS. */
361 val_labs_find_value (const struct val_labs *vls, const char *label_)
363 const union value *value = NULL;
367 const struct val_lab *vl;
370 label = intern_new (label_);
371 HMAP_FOR_EACH (vl, struct val_lab, node, &vls->labels)
372 if (vl->label == label)
377 intern_unref (label);
383 /* Returns the first value label in VLS, in arbitrary order, or a
384 null pointer if VLS is empty or if VLS is a null pointer. If
385 the return value is non-null, then val_labs_next() may be used
386 to continue iterating. */
387 const struct val_lab *
388 val_labs_first (const struct val_labs *vls)
390 return vls ? HMAP_FIRST (struct val_lab, node, &vls->labels) : NULL;
393 /* Returns the next value label in an iteration begun by
394 val_labs_first(). If the return value is non-null, then
395 val_labs_next() may be used to continue iterating. */
396 const struct val_lab *
397 val_labs_next (const struct val_labs *vls, const struct val_lab *label)
399 return HMAP_NEXT (label, struct val_lab, node, &vls->labels);
403 compare_labels_by_value_3way (const void *a_, const void *b_, const void *vls_)
405 const struct val_lab *const *a = a_;
406 const struct val_lab *const *b = b_;
407 const struct val_labs *vls = vls_;
408 return value_compare_3way (&(*a)->value, &(*b)->value, vls->width);
411 /* Allocates and returns an array of pointers to value labels
412 that is sorted in increasing order by value. The array has
413 val_labs_count(VLS) elements. The caller is responsible for
414 freeing the array. */
415 const struct val_lab **
416 val_labs_sorted (const struct val_labs *vls)
420 const struct val_lab *label;
421 const struct val_lab **labels;
424 labels = xmalloc (val_labs_count (vls) * sizeof *labels);
426 HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels)
428 assert (i == val_labs_count (vls));
429 sort (labels, val_labs_count (vls), sizeof *labels,
430 compare_labels_by_value_3way, vls);
437 /* Returns a hash value that represents all of the labels in VLS, starting from
440 val_labs_hash (const struct val_labs *vls, unsigned int basis)
442 const struct val_lab *label;
445 hash = hash_int (val_labs_count (vls), basis);
446 HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels)
447 hash ^= value_hash (&label->value, vls->width,
448 hash_string (label->label, basis));
452 /* Returns true if A and B contain the same values with the same labels,
453 false if they differ in some way. */
455 val_labs_equal (const struct val_labs *a, const struct val_labs *b)
457 const struct val_lab *label;
459 if (val_labs_count (a) != val_labs_count (b))
462 if (a == NULL || b == NULL)
465 if (a->width != b->width)
468 HMAP_FOR_EACH (label, struct val_lab, node, &a->labels)
470 struct val_lab *label2 = val_labs_lookup__ (b, &label->value,
472 if (!label2 || label->label != label2->label)