pxd: initial work
[pspp] / src / data / mrset.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 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/mrset.h"
20
21 #include <stdlib.h>
22
23 #include "data/dictionary.h"
24 #include "data/identifier.h"
25 #include "data/val-type.h"
26 #include "data/variable.h"
27 #include "libpspp/message.h"
28 #include "libpspp/pxd.h"
29
30 #include "gl/xalloc.h"
31
32 #include "gettext.h"
33 #define _(msgid) gettext (msgid)
34
35 /* Creates and returns a clone of OLD.  The caller is responsible for freeing
36    the new multiple response set (using mrset_destroy()). */
37 struct mrset *
38 mrset_clone (const struct mrset *old)
39 {
40   struct mrset *new;
41
42   new = xmalloc (sizeof *new);
43   new->name = xstrdup (old->name);
44   new->label = old->label != NULL ? xstrdup (old->label) : NULL;
45   new->type = old->type;
46   new->vars = xmemdup (old->vars, old->n_vars * sizeof *old->vars);
47   new->n_vars = old->n_vars;
48
49   new->cat_source = old->cat_source;
50   new->label_from_var_label = old->label_from_var_label;
51   value_clone (&new->counted, &old->counted, old->width);
52   new->width = old->width;
53
54   return new;
55 }
56
57 /* Frees MRSET and the data that it contains. */
58 void
59 mrset_destroy (struct mrset *mrset)
60 {
61   if (mrset != NULL)
62     {
63       free (mrset->name);
64       free (mrset->label);
65       free (mrset->vars);
66       value_destroy (&mrset->counted, mrset->width);
67       free (mrset);
68     }
69 }
70
71 /* Returns true if the UTF-8 encoded NAME is a valid name for a multiple
72    response set in a dictionary encoded in DICT_ENCODING, false otherwise.  If
73    ISSUE_ERROR is true, issues an explanatory error message on failure. */
74 bool
75 mrset_is_valid_name (const char *name, const char *dict_encoding,
76                      bool issue_error)
77 {
78   if (!id_is_valid (name, dict_encoding, issue_error))
79     return false;
80
81   if (name[0] != '$')
82     {
83       if (issue_error)
84         msg (SE, _("%s is not a valid name for a multiple response "
85                    "set.  Multiple response set names must begin with "
86                    "`$'."), name);
87       return false;
88     }
89
90   return true;
91 }
92
93 /* Checks various constraints on MRSET:
94
95    - MRSET's name begins with '$' and is valid as an identifier in DICT.
96
97    - MRSET has a valid type.
98
99    - MRSET has at least 2 variables.
100
101    - All of MRSET's variables are in DICT.
102
103    - All of MRSET's variables are the same type (numeric or string).
104
105    - If MRSET is a multiple dichotomy set, its counted value has the same type
106      as and is no wider than its narrowest variable.
107
108    Returns true if all the constraints are satisfied, otherwise false. */
109 bool
110 mrset_ok (const struct mrset *mrset, const struct dictionary *dict)
111 {
112   enum val_type type;
113   size_t i;
114
115   if (mrset->name == NULL
116       || !mrset_is_valid_name (mrset->name, dict_get_encoding (dict), false)
117       || (mrset->type != MRSET_MD && mrset->type != MRSET_MC)
118       || mrset->vars == NULL
119       || mrset->n_vars < 2)
120     return false;
121
122   type = var_get_type (mrset->vars[0]);
123   if (mrset->type == MRSET_MD && type != val_type_from_width (mrset->width))
124     return false;
125   for (i = 0; i < mrset->n_vars; i++)
126     if (!dict_contains_var (dict, mrset->vars[i])
127         || type != var_get_type (mrset->vars[i])
128         || (mrset->type == MRSET_MD
129             && mrset->width > var_get_width (mrset->vars[i])))
130       return false;
131
132   return true;
133 }
134
135 struct pxd_object *
136 mrset_save (const struct mrset *mrset, struct pxd *pxd)
137 {
138   struct pxd_builder b;
139   size_t i;
140
141   pxd_builder_init (&b, pxd);
142
143   pxd_builder_put_string (&b, mrset->name);
144   pxd_builder_put_string (&b, mrset->label != NULL ? mrset->label : "");
145   pxd_builder_put_u8 (&b, mrset->type);
146
147   pxd_builder_put_size_t (&b, mrset->n_vars);
148   for (i = 0; i < mrset->n_vars; i++)
149     pxd_builder_put_string (&b, var_get_name (mrset->vars[i]));
150
151   if (mrset->type == MRSET_MD)
152     {
153       pxd_builder_put_u8 (&b, mrset->cat_source);
154       pxd_builder_put_bool (&b, mrset->label_from_var_label);
155       pxd_builder_put_u16 (&b, mrset->width);
156       pxd_builder_put_value (&b, &mrset->counted, mrset->width);
157     }
158
159   return pxd_builder_commit (&b);
160 }
161
162 struct mrset *
163 mrset_load (struct pxd_object *object, const struct pxd *pxd,
164             const struct dictionary *dict)
165 {
166   struct pxd_parser p;
167   struct mrset *mrset;
168   size_t i;
169
170   mrset = xzalloc (sizeof *mrset);
171
172   pxd_parser_init (&p, object, pxd);
173
174   mrset->name = pxd_parser_get_string (&p);
175
176   mrset->label = pxd_parser_get_string (&p);
177   if (mrset->label[0] == '\0')
178     {
179       free (mrset->label);
180       mrset->label = NULL;
181     }
182
183   mrset->type = pxd_parser_get_u8 (&p);
184
185   mrset->n_vars = pxd_parser_get_size_t (&p);
186   mrset->vars = xmalloc (mrset->n_vars * sizeof *mrset->vars);
187   for (i = 0; i < mrset->n_vars; i++)
188     {
189       char *name = pxd_parser_get_string (&p);
190       mrset->vars[i] = dict_lookup_var_assert (dict, name);
191       free (name);
192     }
193
194   if (mrset->type == MRSET_MD)
195     {
196       mrset->cat_source = pxd_parser_get_u8 (&p);
197       mrset->label_from_var_label = pxd_parser_get_bool (&p);
198       mrset->width = pxd_parser_get_u16 (&p);
199       pxd_parser_get_value (&p, &mrset->counted, mrset->width);
200     }
201   else
202     value_init (&mrset->counted, 0);
203
204   return mrset;
205 }