pxd: initial work
[pspp] / src / data / value-labels.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2009, 2010, 2011, 2012 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 "data/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/pxd.h"
34 #include "libpspp/str.h"
35
36 #include "gl/xalloc.h"
37
38 static void val_labs_uncache__ (struct val_labs *);
39 static void val_lab_uncache__ (struct val_lab *);
40
41 /* Creates and returns a new, empty set of value labels with the
42    given WIDTH. */
43 struct val_labs *
44 val_labs_create (int width)
45 {
46   struct val_labs *vls = xmalloc (sizeof *vls);
47   vls->width = width;
48   hmap_init (&vls->labels);
49   return vls;
50 }
51
52 /* Creates and returns a new set of value labels identical to
53    VLS.  Returns a null pointer if VLS is null. */
54 struct val_labs *
55 val_labs_clone (const struct val_labs *vls)
56 {
57   struct val_labs *copy;
58   struct val_lab *label;
59
60   if (vls == NULL)
61     return NULL;
62
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);
66   return copy;
67 }
68
69 /* Determines whether VLS's width can be changed to NEW_WIDTH,
70    using the rules checked by value_is_resizable. */
71 bool
72 val_labs_can_set_width (const struct val_labs *vls, int new_width)
73 {
74   struct val_lab *label;
75
76   HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels)
77     if (!value_is_resizable (&label->value, vls->width, new_width))
78       return false;
79
80   return true;
81 }
82
83 /* Changes the width of VLS to NEW_WIDTH.  The original and new
84    width must be both numeric or both string. */
85 void
86 val_labs_set_width (struct val_labs *vls, int new_width)
87 {
88   assert (val_labs_can_set_width (vls, new_width));
89   if (value_needs_resize (vls->width, new_width))
90     {
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);
94     }
95   if (vls->width != new_width)
96     {
97       vls->width = new_width;
98       val_labs_uncache__ (vls);
99     }
100 }
101
102 /* Destroys VLS. */
103 void
104 val_labs_destroy (struct val_labs *vls)
105 {
106   if (vls != NULL)
107     {
108       val_labs_uncache__ (vls);
109       val_labs_clear (vls);
110       hmap_destroy (&vls->labels);
111       free (vls);
112     }
113 }
114
115 static void
116 val_lab_clear (struct val_lab *lab, int width)
117 {
118   val_lab_uncache__ (lab);
119   value_destroy (&lab->value, width);
120   intern_unref (lab->label);
121 }
122
123 /* Removes all the value labels from VLS. */
124 void
125 val_labs_clear (struct val_labs *vls)
126 {
127   struct val_lab *label, *next;
128
129   HMAP_FOR_EACH_SAFE (label, next, struct val_lab, node, &vls->labels)
130     {
131       val_lab_clear (label, vls->width);
132       hmap_delete (&vls->labels, &label->node);
133       free (label);
134     }
135 }
136
137 /* Returns the width of VLS. */
138 int
139 val_labs_get_width (const struct val_labs *vls)
140 {
141   return vls->width;
142 }
143
144 /* Returns the number of value labels in VLS.
145    Returns 0 if VLS is null. */
146 size_t
147 val_labs_count (const struct val_labs *vls)
148 {
149   return vls == NULL ? 0 : hmap_count (&vls->labels);
150 }
151 \f
152 static void
153 set_label (struct val_lab *lab, const char *escaped_label)
154 {
155   lab->escaped_label = intern_new (escaped_label);
156   if (strstr (escaped_label, "\\n") == NULL)
157     lab->label = intern_ref (lab->escaped_label);
158   else
159     {
160       struct string s;
161       const char *p;
162
163       ds_init_empty (&s);
164       ds_extend (&s, intern_strlen (lab->escaped_label));
165       for (p = escaped_label; *p != '\0'; p++)
166         {
167           char c = *p;
168           if (c == '\\' && p[1] == 'n')
169             {
170               c = '\n';
171               p++;
172             }
173           ds_put_byte (&s, c);
174         }
175       lab->label = intern_new (ds_cstr (&s));
176       ds_destroy (&s);
177     }
178 }
179
180 static void
181 val_labs_uncache__ (struct val_labs *vls UNUSED)
182 {
183 }
184
185 static void
186 val_lab_uncache__ (struct val_lab *vl UNUSED)
187 {
188 }
189
190 static struct pxd_object *
191 val_lab_save (const struct val_lab *vl, int width, struct pxd *pxd)
192 {
193   struct pxd_builder b;
194
195   pxd_builder_init (&b, pxd);
196
197   pxd_builder_put_value (&b, &vl->value, width);
198   pxd_builder_put_interned_string (&b, vl->label);
199
200   return pxd_builder_commit (&b);
201 }
202
203 struct pxd_object *
204 val_labs_save (const struct val_labs *vls, struct pxd *pxd)
205 {
206   struct val_lab *label;
207   struct pxd_builder b;
208
209   pxd_builder_init (&b, pxd);
210
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));
214
215   return pxd_builder_commit (&b);
216 }
217
218 static struct val_lab *
219 val_lab_load (struct pxd_object *object, const struct pxd *pxd, int width)
220 {
221   struct pxd_parser p;
222   struct val_lab *vl;
223
224   pxd_parser_init (&p, object, pxd);
225
226   vl = xmalloc (sizeof *vl);
227   pxd_parser_get_value (&p, &vl->value, width);
228   vl->label = pxd_parser_get_interned_string (&p);
229
230   pxd_parser_destroy (&p);
231
232   return vl;
233 }
234
235 struct val_labs *
236 val_labs_load (struct pxd_object *object, const struct pxd *pxd)
237 {
238   struct val_labs *vls;
239   struct pxd_parser p;
240   unsigned int i;
241   int width;
242
243   pxd_parser_init (&p, object, pxd);
244   width = pxd_parser_get_u32 (&p);
245
246   vls = val_labs_create (width);
247   for (i = 0; i < pxd_object_get_n_links (object); i++)
248     {
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));
253     }
254
255   pxd_parser_destroy (&p);
256
257   return vls;
258 }
259 \f
260 static void
261 do_add_val_lab (struct val_labs *vls, const union value *value,
262                 const char *escaped_label)
263 {
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));
268 }
269
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.
272
273    In LABEL, the two-byte sequence "\\n" is interpreted as a new-line. */
274 bool
275 val_labs_add (struct val_labs *vls, const union value *value,
276               const char *label)
277 {
278   const struct val_lab *lab = val_labs_lookup (vls, value);
279   if (lab == NULL)
280     {
281       do_add_val_lab (vls, value, label);
282       return true;
283     }
284   else
285     return false;
286 }
287
288 /* Sets LABEL as the value label for VALUE in VLS, replacing any
289    existing label for VALUE.
290
291    In LABEL, the two-byte sequence "\\n" is interpreted as a new-line. */
292 void
293 val_labs_replace (struct val_labs *vls, const union value *value,
294                   const char *label)
295 {
296   struct val_lab *vl = val_labs_lookup (vls, value);
297   if (vl != NULL)
298     {
299       val_lab_uncache__ (vl);
300       intern_unref (vl->label);
301       intern_unref (vl->escaped_label);
302       set_label (vl, label);
303     }
304   else
305     do_add_val_lab (vls, value, label);
306 }
307
308 /* Removes LABEL from VLS. */
309 void
310 val_labs_remove (struct val_labs *vls, struct val_lab *label)
311 {
312   val_lab_clear (label, vls->width);
313   hmap_delete (&vls->labels, &label->node);
314   free (label);
315 }
316
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
320    is null. */
321 const char *
322 val_labs_find (const struct val_labs *vls, const union value *value)
323 {
324   const struct val_lab *label = val_labs_lookup (vls, value);
325   return label ? label->label : NULL;
326 }
327
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,
333                    unsigned int hash)
334 {
335   struct val_lab *label;
336
337   HMAP_FOR_EACH_WITH_HASH (label, struct val_lab, node, hash, &vls->labels)
338     if (value_equal (&label->value, value, vls->width))
339       return label;
340
341   return NULL;
342 }
343
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. */
347 struct val_lab *
348 val_labs_lookup (const struct val_labs *vls, const union value *value)
349 {
350   return (vls == NULL ? NULL
351           : val_labs_lookup__ (vls, value, value_hash (value, vls->width, 0)));
352 }
353
354 /* Searches VLS for a value label whose label is exactly LABEL.  If successful,
355    returns the corresponding value.  Otherwise, returns a null pointer.
356
357    Returns a null pointer if VLS is null.
358
359    This function is O(n) in the number of labels in VLS. */
360 const union value *
361 val_labs_find_value (const struct val_labs *vls, const char *label_)
362 {
363   const union value *value = NULL;
364
365   if (vls != NULL)
366     {
367       const struct val_lab *vl;
368       const char *label;
369
370       label = intern_new (label_);
371       HMAP_FOR_EACH (vl, struct val_lab, node, &vls->labels)
372         if (vl->label == label)
373           {
374             value = &vl->value;
375             break;
376           }
377       intern_unref (label);
378     }
379
380   return value;
381 }
382 \f
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)
389 {
390   return vls ? HMAP_FIRST (struct val_lab, node, &vls->labels) : NULL;
391 }
392
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)
398 {
399   return HMAP_NEXT (label, struct val_lab, node, &vls->labels);
400 }
401
402 static int
403 compare_labels_by_value_3way (const void *a_, const void *b_, const void *vls_)
404 {
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);
409 }
410
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)
417 {
418   if (vls != NULL)
419     {
420       const struct val_lab *label;
421       const struct val_lab **labels;
422       size_t i;
423
424       labels = xmalloc (val_labs_count (vls) * sizeof *labels);
425       i = 0;
426       HMAP_FOR_EACH (label, struct val_lab, node, &vls->labels)
427         labels[i++] = label;
428       assert (i == val_labs_count (vls));
429       sort (labels, val_labs_count (vls), sizeof *labels,
430             compare_labels_by_value_3way, vls);
431       return labels;
432     }
433   else
434     return NULL;
435 }
436
437 /* Returns a hash value that represents all of the labels in VLS, starting from
438    BASIS. */
439 unsigned int
440 val_labs_hash (const struct val_labs *vls, unsigned int basis)
441 {
442   const struct val_lab *label;
443   unsigned int hash;
444
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));
449   return hash;
450 }
451
452 /* Returns true if A and B contain the same values with the same labels,
453    false if they differ in some way. */
454 bool
455 val_labs_equal (const struct val_labs *a, const struct val_labs *b)
456 {
457   const struct val_lab *label;
458
459   if (val_labs_count (a) != val_labs_count (b))
460     return false;
461   
462   if (a == NULL || b == NULL)
463     return true;
464
465   if (a->width != b->width)
466     return false;
467
468   HMAP_FOR_EACH (label, struct val_lab, node, &a->labels)
469     {
470       struct val_lab *label2 = val_labs_lookup__ (b, &label->value,
471                                                   label->node.hash);
472       if (!label2 || label->label != label2->label)
473         return false;
474     }
475
476   return true;
477 }