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/str.h"
35 #include "gl/xalloc.h"
37 /* Creates and returns a new, empty set of value labels with the
40 val_labs_create (int width
)
42 struct val_labs
*vls
= xmalloc (sizeof *vls
);
44 hmap_init (&vls
->labels
);
48 /* Creates and returns a new set of value labels identical to
49 VLS. Returns a null pointer if VLS is null. */
51 val_labs_clone (const struct val_labs
*vls
)
53 struct val_labs
*copy
;
54 struct val_lab
*label
;
59 copy
= val_labs_create (vls
->width
);
60 HMAP_FOR_EACH (label
, struct val_lab
, node
, &vls
->labels
)
61 val_labs_add (copy
, &label
->value
, label
->escaped_label
);
65 /* Determines whether VLS's width can be changed to NEW_WIDTH,
66 using the rules checked by value_is_resizable. */
68 val_labs_can_set_width (const struct val_labs
*vls
, int new_width
)
70 struct val_lab
*label
;
72 HMAP_FOR_EACH (label
, struct val_lab
, node
, &vls
->labels
)
73 if (!value_is_resizable (&label
->value
, vls
->width
, new_width
))
79 /* Changes the width of VLS to NEW_WIDTH. The original and new
80 width must be both numeric or both string. */
82 val_labs_set_width (struct val_labs
*vls
, int new_width
)
84 assert (val_labs_can_set_width (vls
, new_width
));
85 if (value_needs_resize (vls
->width
, new_width
))
87 struct val_lab
*label
;
88 HMAP_FOR_EACH (label
, struct val_lab
, node
, &vls
->labels
)
89 value_resize (&label
->value
, vls
->width
, new_width
);
91 vls
->width
= new_width
;
96 val_labs_destroy (struct val_labs
*vls
)
100 val_labs_clear (vls
);
101 hmap_destroy (&vls
->labels
);
106 /* Removes all the value labels from VLS. */
108 val_labs_clear (struct val_labs
*vls
)
110 struct val_lab
*label
, *next
;
112 HMAP_FOR_EACH_SAFE (label
, next
, struct val_lab
, node
, &vls
->labels
)
114 hmap_delete (&vls
->labels
, &label
->node
);
115 value_destroy (&label
->value
, vls
->width
);
116 intern_unref (label
->label
);
117 intern_unref (label
->escaped_label
);
122 /* Returns the width of VLS. */
124 val_labs_get_width (const struct val_labs
*vls
)
129 /* Returns the number of value labels in VLS.
130 Returns 0 if VLS is null. */
132 val_labs_count (const struct val_labs
*vls
)
134 return vls
== NULL
? 0 : hmap_count (&vls
->labels
);
138 set_label (struct val_lab
*lab
, const char *escaped_label
)
140 lab
->escaped_label
= intern_new (escaped_label
);
141 if (strstr (escaped_label
, "\\n") == NULL
)
142 lab
->label
= intern_ref (lab
->escaped_label
);
149 ds_extend (&s
, intern_strlen (lab
->escaped_label
));
150 for (p
= escaped_label
; *p
!= '\0'; p
++)
153 if (c
== '\\' && p
[1] == 'n')
160 lab
->label
= intern_new (ds_cstr (&s
));
166 do_add_val_lab (struct val_labs
*vls
, const union value
*value
,
167 const char *escaped_label
)
169 struct val_lab
*lab
= xmalloc (sizeof *lab
);
170 value_clone (&lab
->value
, value
, vls
->width
);
171 set_label (lab
, escaped_label
);
172 hmap_insert (&vls
->labels
, &lab
->node
, value_hash (value
, vls
->width
, 0));
175 /* If VLS does not already contain a value label for VALUE, adds the UTF-8
176 encoded LABEL for it and returns true. Otherwise, returns false.
178 In LABEL, the two-byte sequence "\\n" is interpreted as a new-line. */
180 val_labs_add (struct val_labs
*vls
, const union value
*value
,
183 const struct val_lab
*lab
= val_labs_lookup (vls
, value
);
186 do_add_val_lab (vls
, value
, label
);
193 /* Sets LABEL as the value label for VALUE in VLS, replacing any
194 existing label for VALUE.
196 In LABEL, the two-byte sequence "\\n" is interpreted as a new-line. */
198 val_labs_replace (struct val_labs
*vls
, const union value
*value
,
201 struct val_lab
*vl
= val_labs_lookup (vls
, value
);
204 intern_unref (vl
->label
);
205 intern_unref (vl
->escaped_label
);
206 set_label (vl
, label
);
209 do_add_val_lab (vls
, value
, label
);
212 /* Removes LABEL from VLS. */
214 val_labs_remove (struct val_labs
*vls
, struct val_lab
*label
)
216 hmap_delete (&vls
->labels
, &label
->node
);
217 value_destroy (&label
->value
, vls
->width
);
218 intern_unref (label
->label
);
219 intern_unref (label
->escaped_label
);
223 /* Searches VLS for a value label for VALUE. If successful, returns the string
224 used as the label, as a UTF-8 encoded string in a format suitable for
225 output. Otherwise, returns a null pointer. Returns a null pointer if VLS
228 val_labs_find (const struct val_labs
*vls
, const union value
*value
)
230 const struct val_lab
*label
= val_labs_lookup (vls
, value
);
231 return label
? label
->label
: NULL
;
234 /* Searches VLS for a value label for VALUE. If successful,
235 returns the value label; otherwise, returns a null pointer.
236 Returns a null pointer if VLS is null. */
237 static struct val_lab
*
238 val_labs_lookup__ (const struct val_labs
*vls
, const union value
*value
,
241 struct val_lab
*label
;
243 HMAP_FOR_EACH_WITH_HASH (label
, struct val_lab
, node
, hash
, &vls
->labels
)
244 if (value_equal (&label
->value
, value
, vls
->width
))
250 /* Searches VLS for a value label for VALUE. If successful,
251 returns the value label; otherwise, returns a null pointer.
252 Returns a null pointer if VLS is null. */
254 val_labs_lookup (const struct val_labs
*vls
, const union value
*value
)
256 return (vls
== NULL
? NULL
257 : val_labs_lookup__ (vls
, value
, value_hash (value
, vls
->width
, 0)));
260 /* Searches VLS for a value label whose label is exactly LABEL. If successful,
261 returns the corresponding value. Otherwise, returns a null pointer.
263 Returns a null pointer if VLS is null.
265 This function is O(n) in the number of labels in VLS. */
267 val_labs_find_value (const struct val_labs
*vls
, const char *label_
)
269 const union value
*value
= NULL
;
273 const struct val_lab
*vl
;
276 label
= intern_new (label_
);
277 HMAP_FOR_EACH (vl
, struct val_lab
, node
, &vls
->labels
)
278 if (vl
->label
== label
)
283 intern_unref (label
);
289 /* Returns the first value label in VLS, in arbitrary order, or a
290 null pointer if VLS is empty or if VLS is a null pointer. If
291 the return value is non-null, then val_labs_next() may be used
292 to continue iterating. */
293 const struct val_lab
*
294 val_labs_first (const struct val_labs
*vls
)
296 return vls
? HMAP_FIRST (struct val_lab
, node
, &vls
->labels
) : NULL
;
299 /* Returns the next value label in an iteration begun by
300 val_labs_first(). If the return value is non-null, then
301 val_labs_next() may be used to continue iterating. */
302 const struct val_lab
*
303 val_labs_next (const struct val_labs
*vls
, const struct val_lab
*label
)
305 return HMAP_NEXT (label
, struct val_lab
, node
, &vls
->labels
);
309 compare_labels_by_value_3way (const void *a_
, const void *b_
, const void *vls_
)
311 const struct val_lab
*const *a
= a_
;
312 const struct val_lab
*const *b
= b_
;
313 const struct val_labs
*vls
= vls_
;
314 return value_compare_3way (&(*a
)->value
, &(*b
)->value
, vls
->width
);
317 /* Allocates and returns an array of pointers to value labels
318 that is sorted in increasing order by value. The array has
319 val_labs_count(VLS) elements. The caller is responsible for
320 freeing the array. */
321 const struct val_lab
**
322 val_labs_sorted (const struct val_labs
*vls
)
326 const struct val_lab
*label
;
327 const struct val_lab
**labels
;
330 labels
= xmalloc (val_labs_count (vls
) * sizeof *labels
);
332 HMAP_FOR_EACH (label
, struct val_lab
, node
, &vls
->labels
)
334 assert (i
== val_labs_count (vls
));
335 sort (labels
, val_labs_count (vls
), sizeof *labels
,
336 compare_labels_by_value_3way
, vls
);
343 /* Returns a hash value that represents all of the labels in VLS, starting from
346 val_labs_hash (const struct val_labs
*vls
, unsigned int basis
)
348 const struct val_lab
*label
;
351 hash
= hash_int (val_labs_count (vls
), basis
);
352 HMAP_FOR_EACH (label
, struct val_lab
, node
, &vls
->labels
)
353 hash
^= value_hash (&label
->value
, vls
->width
,
354 hash_string (label
->label
, basis
));
358 /* Returns true if A and B contain the same values with the same labels,
359 false if they differ in some way. */
361 val_labs_equal (const struct val_labs
*a
, const struct val_labs
*b
)
363 const struct val_lab
*label
;
365 if (val_labs_count (a
) != val_labs_count (b
))
368 if (a
== NULL
|| b
== NULL
)
371 if (a
->width
!= b
->width
)
374 HMAP_FOR_EACH (label
, struct val_lab
, node
, &a
->labels
)
376 struct val_lab
*label2
= val_labs_lookup__ (b
, &label
->value
,
378 if (!label2
|| label
->label
!= label2
->label
)