lexer: Reimplement for better testability and internationalization.
[pspp-builds.git] / 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 = old->label != NULL ? xstrdup (old->label) : NULL;
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 /* Returns true if the UTF-8 encoded NAME is a valid name for a multiple
71    response set in a dictionary encoded in DICT_ENCODING, false otherwise.  If
72    ISSUE_ERROR is true, issues an explanatory error message on failure. */
73 bool
74 mrset_is_valid_name (const char *name, const char *dict_encoding,
75                      bool issue_error)
76 {
77   if (!id_is_valid (name, dict_encoding, issue_error))
78     return false;
79
80   if (name[0] != '$')
81     {
82       if (issue_error)
83         msg (SE, _("%s is not a valid name for a multiple response "
84                    "set.  Multiple response set names must begin with "
85                    "`$'."), name);
86       return false;
87     }
88
89   return true;
90 }
91
92 /* Checks various constraints on MRSET:
93
94    - MRSET's name begins with '$' and is valid as an identifier in DICT.
95
96    - MRSET has a valid type.
97
98    - MRSET has at least 2 variables.
99
100    - All of MRSET's variables are in DICT.
101
102    - All of MRSET's variables are the same type (numeric or string).
103
104    - If MRSET is a multiple dichotomy set, its counted value has the same type
105      as and is no wider than its narrowest variable.
106
107    Returns true if all the constraints are satisfied, otherwise false. */
108 bool
109 mrset_ok (const struct mrset *mrset, const struct dictionary *dict)
110 {
111   enum val_type type;
112   size_t i;
113
114   if (mrset->name == NULL
115       || !mrset_is_valid_name (mrset->name, dict_get_encoding (dict), false)
116       || (mrset->type != MRSET_MD && mrset->type != MRSET_MC)
117       || mrset->vars == NULL
118       || mrset->n_vars < 2)
119     return false;
120
121   type = var_get_type (mrset->vars[0]);
122   if (mrset->type == MRSET_MD && type != val_type_from_width (mrset->width))
123     return false;
124   for (i = 0; i < mrset->n_vars; i++)
125     if (!dict_contains_var (dict, mrset->vars[i])
126         || type != var_get_type (mrset->vars[i])
127         || (mrset->type == MRSET_MD
128             && mrset->width > var_get_width (mrset->vars[i])))
129       return false;
130
131   return true;
132 }