b1c0dc7ae2682c9fec73dd2d322fea651e251002
[pspp-builds.git] / src / data / value-labels.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2009, 2010 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 "value-labels.h"
20
21 #include <stdlib.h>
22
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>
34
35 #include "xalloc.h"
36
37 /* Creates and returns a new, empty set of value labels with the
38    given WIDTH. */
39 struct val_labs *
40 val_labs_create (int width)
41 {
42   struct val_labs *vls = xmalloc (sizeof *vls);
43   vls->width = width;
44   hmap_init (&vls->labels);
45   return vls;
46 }
47
48 /* Creates and returns a new set of value labels identical to
49    VLS.  Returns a null pointer if VLS is null. */
50 struct val_labs *
51 val_labs_clone (const struct val_labs *vls)
52 {
53   struct val_labs *copy;
54   struct val_lab *label;
55
56   if (vls == NULL)
57     return NULL;
58
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->label);
62   return copy;
63 }
64
65 /* Determines whether VLS's width can be changed to NEW_WIDTH,
66    using the rules checked by value_is_resizable. */
67 bool
68 val_labs_can_set_width (const struct val_labs *vls, int new_width)
69 {
70   struct val_lab *label;
71
72   HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels)
73     if (!value_is_resizable (&label->value, vls->width, new_width))
74       return false;
75
76   return true;
77 }
78
79 /* Changes the width of VLS to NEW_WIDTH.  The original and new
80    width must be both numeric or both string. */
81 void
82 val_labs_set_width (struct val_labs *vls, int new_width)
83 {
84   assert (val_labs_can_set_width (vls, new_width));
85   if (value_needs_resize (vls->width, new_width))
86     {
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);
90     }
91   vls->width = new_width;
92 }
93
94 /* Destroys VLS. */
95 void
96 val_labs_destroy (struct val_labs *vls)
97 {
98   if (vls != NULL)
99     {
100       val_labs_clear (vls);
101       hmap_destroy (&vls->labels);
102       free (vls);
103     }
104 }
105
106 /* Removes all the value labels from VLS. */
107 void
108 val_labs_clear (struct val_labs *vls)
109 {
110   struct val_lab *label, *next;
111
112   HMAP_FOR_EACH_SAFE (label, next, struct val_lab, node, &vls->labels)
113     {
114       hmap_delete (&vls->labels, &label->node);
115       value_destroy (&label->value, vls->width);
116       intern_unref (label->label);
117       free (label);
118     }
119 }
120
121 /* Returns the width of VLS. */
122 int
123 val_labs_get_width (const struct val_labs *vls)
124 {
125   return vls->width;
126 }
127
128 /* Returns the number of value labels in VLS.
129    Returns 0 if VLS is null. */
130 size_t
131 val_labs_count (const struct val_labs *vls)
132 {
133   return vls == NULL ? 0 : hmap_count (&vls->labels);
134 }
135 \f
136 static void
137 do_add_val_lab (struct val_labs *vls, const union value *value,
138                 const char *label)
139 {
140   struct val_lab *lab = xmalloc (sizeof *lab);
141   value_clone (&lab->value, value, vls->width);
142   lab->label = intern_new (label);
143   hmap_insert (&vls->labels, &lab->node, value_hash (value, vls->width, 0));
144 }
145
146 /* If VLS does not already contain a value label for VALUE, adds
147    LABEL for it and returns true.  Otherwise, returns false. */
148 bool
149 val_labs_add (struct val_labs *vls, const union value *value,
150               const char *label)
151 {
152   const struct val_lab *lab = val_labs_lookup (vls, value);
153   if (lab == NULL)
154     {
155       do_add_val_lab (vls, value, label);
156       return true;
157     }
158   else
159     return false;
160 }
161
162 /* Sets LABEL as the value label for VALUE in VLS, replacing any
163    existing label for VALUE. */
164 void
165 val_labs_replace (struct val_labs *vls, const union value *value,
166                   const char *label)
167 {
168   struct val_lab *vl = val_labs_lookup (vls, value);
169   if (vl != NULL)
170     {
171       intern_unref (vl->label);
172       vl->label = intern_new (label);
173     }
174   else
175     do_add_val_lab (vls, value, label);
176 }
177
178 /* Removes LABEL from VLS. */
179 void
180 val_labs_remove (struct val_labs *vls, struct val_lab *label)
181 {
182   hmap_delete (&vls->labels, &label->node);
183   value_destroy (&label->value, vls->width);
184   intern_unref (label->label);
185   free (label);
186 }
187
188 /* Searches VLS for a value label for VALUE.  If successful,
189    returns the string used as the label; otherwise, returns a
190    null pointer.  Returns a null pointer if VLS is null. */
191 const char *
192 val_labs_find (const struct val_labs *vls, const union value *value)
193 {
194   const struct val_lab *label = val_labs_lookup (vls, value);
195   return label ? label->label : NULL;
196 }
197
198 /* Searches VLS for a value label for VALUE.  If successful,
199    returns the value label; otherwise, returns a null pointer.
200    Returns a null pointer if VLS is null. */
201 struct val_lab *
202 val_labs_lookup (const struct val_labs *vls, const union value *value)
203 {
204   if (vls != NULL)
205     {
206       struct val_lab *label;
207       HMAP_FOR_EACH_WITH_HASH (label, struct val_lab, node,
208                                value_hash (value, vls->width, 0), &vls->labels)
209         if (value_equal (&label->value, value, vls->width))
210           return label;
211     }
212   return NULL;
213 }
214 \f
215 /* Returns the first value label in VLS, in arbitrary order, or a
216    null pointer if VLS is empty or if VLS is a null pointer.  If
217    the return value is non-null, then val_labs_next() may be used
218    to continue iterating. */
219 const struct val_lab *
220 val_labs_first (const struct val_labs *vls)
221 {
222   return vls ? HMAP_FIRST (struct val_lab, node, &vls->labels) : NULL;
223 }
224
225 /* Returns the next value label in an iteration begun by
226    val_labs_first().  If the return value is non-null, then
227    val_labs_next() may be used to continue iterating. */
228 const struct val_lab *
229 val_labs_next (const struct val_labs *vls, const struct val_lab *label)
230 {
231   return HMAP_NEXT (label, struct val_lab, node, &vls->labels);
232 }
233
234 static int
235 compare_labels_by_value_3way (const void *a_, const void *b_, const void *vls_)
236 {
237   const struct val_lab *const *a = a_;
238   const struct val_lab *const *b = b_;
239   const struct val_labs *vls = vls_;
240   return value_compare_3way (&(*a)->value, &(*b)->value, vls->width);
241 }
242
243 /* Allocates and returns an array of pointers to value labels
244    that is sorted in increasing order by value.  The array has
245    val_labs_count(VLS) elements.  The caller is responsible for
246    freeing the array. */
247 const struct val_lab **
248 val_labs_sorted (const struct val_labs *vls)
249 {
250   if (vls != NULL)
251     {
252       const struct val_lab *label;
253       const struct val_lab **labels;
254       size_t i;
255
256       labels = xmalloc (val_labs_count (vls) * sizeof *labels);
257       i = 0;
258       HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels)
259         labels[i++] = label;
260       assert (i == val_labs_count (vls));
261       sort (labels, val_labs_count (vls), sizeof *labels,
262             compare_labels_by_value_3way, vls);
263       return labels;
264     }
265   else
266     return NULL;
267 }