Implement new command SORT VARIABLES.
[pspp.git] / src / language / dictionary / sort-variables.c
blob8c653af7248c1cf8470009e06d7c53dba483a129
1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 2016 Free Software Foundation, Inc.
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.
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.
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/>. */
17 #include <config.h>
19 #include <stdlib.h>
21 #include "data/attributes.h"
22 #include "data/dataset.h"
23 #include "data/dictionary.h"
24 #include "data/format.h"
25 #include "data/variable.h"
26 #include "language/command.h"
27 #include "language/lexer/lexer.h"
28 #include "libpspp/array.h"
29 #include "libpspp/assertion.h"
30 #include "libpspp/i18n.h"
31 #include "libpspp/message.h"
32 #include "libpspp/str.h"
34 #include "gl/xalloc.h"
36 #include "gettext.h"
37 #define _(msgid) gettext (msgid)
39 enum key
41 K_NAME,
42 K_TYPE,
43 K_FORMAT,
44 K_VAR_LABEL,
45 K_VALUE_LABELS,
46 K_MISSING_VALUES,
47 K_MEASURE,
48 K_ROLE,
49 K_COLUMNS,
50 K_ALIGNMENT,
51 K_ATTRIBUTE,
54 struct criterion
56 enum key key;
57 char *attr_name;
58 bool descending;
61 static int
62 compare_ints (int a, int b)
64 return a < b ? -1 : a > b;
67 static int
68 compare_formats (const struct fmt_spec *a, const struct fmt_spec *b)
70 int retval = compare_ints (fmt_to_io (a->type), fmt_to_io (b->type));
71 if (!retval)
72 retval = compare_ints (a->w, b->w);
73 if (!retval)
74 retval = compare_ints (a->d, b->d);
75 return retval;
78 static int
79 compare_var_labels (const struct variable *a, const struct variable *b)
81 const char *a_label = var_get_label (a);
82 const char *b_label = var_get_label (b);
83 return utf8_strcasecmp (a_label ? a_label : "",
84 b_label ? b_label : "");
87 static int
88 map_measure (enum measure m)
90 return (m == MEASURE_NOMINAL ? 0
91 : m == MEASURE_ORDINAL ? 1
92 : 2);
95 static int
96 map_role (enum var_role r)
98 return (r == ROLE_INPUT ? 0
99 : r == ROLE_TARGET ? 1
100 : r == ROLE_BOTH ? 2
101 : r == ROLE_NONE ? 3
102 : r == ROLE_PARTITION ? 4
103 : 5);
106 static const char *
107 get_attribute (const struct variable *v, const char *name)
109 const struct attrset *set = var_get_attributes (v);
110 const struct attribute *attr = attrset_lookup (set, name);
111 const char *value = attr ? attribute_get_value (attr, 0) : NULL;
112 return value ? value : "";
115 static int
116 map_alignment (enum alignment a)
118 return (a == ALIGN_LEFT ? 0
119 : a == ALIGN_RIGHT ? 1
120 : 2);
123 static int
124 compare_vars (const void *a_, const void *b_, const void *c_)
126 const struct variable *const *ap = a_;
127 const struct variable *const *bp = b_;
128 const struct variable *a = *ap;
129 const struct variable *b = *bp;
130 const struct criterion *c = c_;
132 int retval;
133 switch (c->key)
135 case K_NAME:
136 retval = utf8_strverscasecmp (var_get_name (a), var_get_name (b));
137 break;
139 case K_TYPE:
140 retval = compare_ints (var_get_width (a), var_get_width (b));
141 break;
143 case K_FORMAT:
144 retval = compare_formats (var_get_print_format (a),
145 var_get_print_format (b));
146 break;
148 case K_VAR_LABEL:
149 retval = compare_var_labels (a, b);
150 break;
152 case K_VALUE_LABELS:
153 retval = compare_ints (var_has_value_labels (a),
154 var_has_value_labels (b));
155 break;
157 case K_MISSING_VALUES:
158 retval = compare_ints (var_has_missing_values (a),
159 var_has_missing_values (b));
160 break;
162 case K_MEASURE:
163 retval = compare_ints (map_measure (var_get_measure (a)),
164 map_measure (var_get_measure (b)));
165 break;
167 case K_ROLE:
168 retval = compare_ints (map_role (var_get_role (a)),
169 map_role (var_get_role (b)));
170 break;
172 case K_COLUMNS:
173 retval = compare_ints (var_get_display_width (a),
174 var_get_display_width (b));
175 break;
177 case K_ALIGNMENT:
178 retval = compare_ints (map_alignment (var_get_alignment (a)),
179 map_alignment (var_get_alignment (b)));
180 break;
182 case K_ATTRIBUTE:
183 retval = utf8_strcasecmp (get_attribute (a, c->attr_name),
184 get_attribute (b, c->attr_name));
185 break;
187 default:
188 NOT_REACHED ();
191 /* Make this a stable sort. */
192 if (!retval)
193 retval = a < b ? -1 : a > b;
195 if (c->descending)
196 retval = -retval;
198 return retval;
201 /* Performs SORT VARIABLES command. */
203 cmd_sort_variables (struct lexer *lexer, struct dataset *ds)
205 enum cmd_result result = CMD_FAILURE;
207 lex_match (lexer, T_BY);
209 /* Parse sort key. */
210 struct criterion c = { .attr_name = NULL };
211 if (lex_match_id (lexer, "NAME"))
212 c.key = K_NAME;
213 else if (lex_match_id (lexer, "TYPE"))
214 c.key = K_TYPE;
215 else if (lex_match_id (lexer, "FORMAT"))
216 c.key = K_FORMAT;
217 else if (lex_match_id (lexer, "LABEL"))
218 c.key = K_VAR_LABEL;
219 else if (lex_match_id (lexer, "VALUES"))
220 c.key = K_VALUE_LABELS;
221 else if (lex_match_id (lexer, "MISSING"))
222 c.key = K_MISSING_VALUES;
223 else if (lex_match_id (lexer, "MEASURE"))
224 c.key = K_MEASURE;
225 else if (lex_match_id (lexer, "ROLE"))
226 c.key = K_ROLE;
227 else if (lex_match_id (lexer, "COLUMNS"))
228 c.key = K_COLUMNS;
229 else if (lex_match_id (lexer, "ALIGNMENT"))
230 c.key = K_ALIGNMENT;
231 else if (lex_match_id (lexer, "ATTRIBUTE"))
233 if (!lex_force_id (lexer))
234 goto exit;
235 c.key = K_ATTRIBUTE;
236 c.attr_name = xstrdup (lex_tokcstr (lexer));
237 lex_get (lexer);
240 /* Parse sort direction. */
241 if (lex_match (lexer, T_LPAREN))
243 if (lex_match_id (lexer, "A") || lex_match_id (lexer, "UP"))
244 c.descending = false;
245 else if (lex_match_id (lexer, "D") || lex_match_id (lexer, "DOWN"))
246 c.descending = true;
247 else
249 lex_error (lexer, NULL);
250 goto exit;
252 if (!lex_force_match (lexer, T_RPAREN))
253 goto exit;
255 else
256 c.descending = false;
258 /* Sort variables. */
259 struct dictionary *d = dataset_dict (ds);
260 struct variable **vars;
261 size_t n_vars;
262 dict_get_vars_mutable (d, &vars, &n_vars, 0);
263 sort (vars, n_vars, sizeof *vars, compare_vars, &c);
264 dict_reorder_vars (d, CONST_CAST (struct variable *const *, vars), n_vars);
265 free (vars);
267 result = CMD_SUCCESS;
269 exit:
270 free (c.attr_name);
271 return result;