work on lexer
[pspp] / src / data / mrset.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2010, 2011 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
29 #include "gl/xalloc.h"
30
31 #include "gettext.h"
32 #define _(msgid) gettext (msgid)
33
34 /* Creates and returns a clone of OLD.  The caller is responsible for freeing
35    the new multiple response set (using mrset_destroy()). */
36 struct mrset *
37 mrset_clone (const struct mrset *old)
38 {
39   struct mrset *new;
40
41   new = xmalloc (sizeof *new);
42   new->name = xstrdup (old->name);
43   new->label = xstrdup_if_nonnull (old->label);
44   new->type = old->type;
45   new->vars = xmemdup (old->vars, old->n_vars * sizeof *old->vars);
46   new->n_vars = old->n_vars;
47
48   new->cat_source = old->cat_source;
49   new->label_from_var_label = old->label_from_var_label;
50   value_clone (&new->counted, &old->counted, old->width);
51   new->width = old->width;
52
53   return new;
54 }
55
56 /* Frees MRSET and the data that it contains. */
57 void
58 mrset_destroy (struct mrset *mrset)
59 {
60   if (mrset != NULL)
61     {
62       free (mrset->name);
63       free (mrset->label);
64       free (mrset->vars);
65       value_destroy (&mrset->counted, mrset->width);
66       free (mrset);
67     }
68 }
69
70 /* Checks whether the UTF-8 encoded NAME is a valid name for a multiple
71    response set in a dictionary encoded in DICT_ENCODING.  Return NULL if it
72    is, otherwise an error message that the caller must free(). */
73 char * WARN_UNUSED_RESULT
74 mrset_is_valid_name__ (const char *name, const char *dict_encoding)
75 {
76   char *error = id_is_valid__ (name, dict_encoding);
77   if (error)
78     return error;
79
80   if (name[0] != '$')
81     return xasprintf (_("%s is not a valid name for a multiple response "
82                         "set.  Multiple response set names must begin with "
83                         "`$'."), name);
84
85   return NULL;
86 }
87
88 static bool
89 error_to_bool (char *error)
90 {
91   if (error)
92     {
93       free (error);
94       return false;
95     }
96   else
97     return true;
98 }
99
100 /* Returns true if the UTF-8 encoded NAME is a valid name for a multiple
101    response set in a dictionary encoded in DICT_ENCODING, false otherwise. */
102 bool
103 mrset_is_valid_name (const char *name, const char *dict_encoding)
104 {
105   return error_to_bool (mrset_is_valid_name__ (name, dict_encoding));
106 }
107
108 /* Checks various constraints on MRSET:
109
110    - MRSET's name begins with '$' and is valid as an identifier in DICT.
111
112    - MRSET has a valid type.
113
114    - MRSET has at least 2 variables.
115
116    - All of MRSET's variables are in DICT.
117
118    - All of MRSET's variables are the same type (numeric or string).
119
120    - If MRSET is a multiple dichotomy set, its counted value has the same type
121      as and is no wider than its narrowest variable.
122
123    Returns true if all the constraints are satisfied, otherwise false. */
124 bool
125 mrset_ok (const struct mrset *mrset, const struct dictionary *dict)
126 {
127   enum val_type type;
128   size_t i;
129
130   if (mrset->name == NULL
131       || !mrset_is_valid_name (mrset->name, dict_get_encoding (dict))
132       || (mrset->type != MRSET_MD && mrset->type != MRSET_MC)
133       || mrset->vars == NULL
134       || mrset->n_vars < 2)
135     return false;
136
137   type = var_get_type (mrset->vars[0]);
138   if (mrset->type == MRSET_MD && type != val_type_from_width (mrset->width))
139     return false;
140   for (i = 0; i < mrset->n_vars; i++)
141     if (!dict_contains_var (dict, mrset->vars[i])
142         || type != var_get_type (mrset->vars[i])
143         || (mrset->type == MRSET_MD
144             && mrset->width > var_get_width (mrset->vars[i])))
145       return false;
146
147   return true;
148 }