Reference count struct dictionary.
[pspp] / src / language / dictionary / modify-variables.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 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 <stdlib.h>
20
21 #include "data/dataset.h"
22 #include "data/dictionary.h"
23 #include "data/variable.h"
24 #include "language/command.h"
25 #include "language/lexer/lexer.h"
26 #include "language/lexer/variable-parser.h"
27 #include "libpspp/array.h"
28 #include "libpspp/assertion.h"
29 #include "libpspp/bit-vector.h"
30 #include "libpspp/compiler.h"
31 #include "libpspp/i18n.h"
32 #include "libpspp/message.h"
33 #include "libpspp/misc.h"
34 #include "libpspp/str.h"
35
36 #include "gl/xalloc.h"
37
38 #include "gettext.h"
39 #define _(msgid) gettext (msgid)
40
41 /* These control the ordering produced by
42    compare_variables_given_ordering(). */
43 struct ordering
44   {
45     int forward;                /* 1=FORWARD, 0=BACKWARD. */
46     int positional;             /* 1=POSITIONAL, 0=ALPHA. */
47   };
48
49 /* Increasing order of variable index. */
50 static struct ordering forward_positional_ordering = {1, 1};
51
52 static int compare_variables_given_ordering (const void *, const void *,
53                                              const void *ordering);
54
55 /* Explains how to modify the variables in a dictionary. */
56 struct var_modification
57   {
58     /* New variable ordering. */
59     struct variable **reorder_vars;
60     size_t reorder_cnt;
61
62     /* DROP/KEEP information. */
63     struct variable **drop_vars;
64     size_t drop_cnt;
65
66     /* New variable names. */
67     struct variable **rename_vars;
68     char **new_names;
69     size_t rename_cnt;
70   };
71
72 static bool rearrange_dict (struct dictionary *d,
73                            const struct var_modification *vm);
74
75 /* Performs MODIFY VARS command. */
76 int
77 cmd_modify_vars (struct lexer *lexer, struct dataset *ds)
78 {
79   /* Bits indicated whether we've already encountered a subcommand of
80      this type. */
81   unsigned already_encountered = 0;
82
83   /* What we are going to do to the active dataset. */
84   struct var_modification vm;
85
86   /* Return code. */
87   int ret_code = CMD_CASCADING_FAILURE;
88
89   size_t i;
90
91   if (proc_make_temporary_transformations_permanent (ds))
92     msg (SE, _("%s may not be used after %s.  "
93                "Temporary transformations will be made permanent."), "MODIFY VARS", "TEMPORARY");
94
95   vm.reorder_vars = NULL;
96   vm.reorder_cnt = 0;
97   vm.rename_vars = NULL;
98   vm.new_names = NULL;
99   vm.rename_cnt = 0;
100   vm.drop_vars = NULL;
101   vm.drop_cnt = 0;
102
103   /* Parse each subcommand. */
104   lex_match (lexer, T_SLASH);
105   for (;;)
106     {
107       if (lex_match_id (lexer, "REORDER"))
108         {
109           struct variable **v = NULL;
110           size_t nv = 0;
111
112           if (already_encountered & 1)
113             {
114               lex_sbc_only_once ("REORDER");
115               goto done;
116             }
117           already_encountered |= 1;
118
119           lex_match (lexer, T_EQUALS);
120           do
121             {
122               struct ordering ordering;
123               size_t prev_nv = nv;
124
125               ordering.forward = ordering.positional = 1;
126               if (lex_match_id (lexer, "FORWARD"));
127               else if (lex_match_id (lexer, "BACKWARD"))
128                 ordering.forward = 0;
129               if (lex_match_id (lexer, "POSITIONAL"));
130               else if (lex_match_id (lexer, "ALPHA"))
131                 ordering.positional = 0;
132
133               if (lex_match (lexer, T_ALL) || lex_token (lexer) == T_SLASH || lex_token (lexer) == T_ENDCMD)
134                 {
135                   if (prev_nv != 0)
136                     {
137                       msg (SE, _("Cannot specify ALL after specifying a set "
138                            "of variables."));
139                       goto done;
140                     }
141                   dict_get_vars_mutable (dataset_dict (ds), &v, &nv, DC_SYSTEM);
142                 }
143               else
144                 {
145                   if (!lex_match (lexer, T_LPAREN))
146                     {
147                       lex_error_expecting (lexer, "`('", NULL_SENTINEL);
148                       free (v);
149                       goto done;
150                     }
151                   if (!parse_variables (lexer, dataset_dict (ds), &v, &nv,
152                                         PV_APPEND | PV_NO_DUPLICATE))
153                     {
154                       free (v);
155                       goto done;
156                     }
157                   if (!lex_match (lexer, T_RPAREN))
158                     {
159                       lex_error_expecting (lexer, "`)'", NULL_SENTINEL);
160                       free (v);
161                       goto done;
162                     }
163                 }
164               sort (&v[prev_nv], nv - prev_nv, sizeof *v,
165                     compare_variables_given_ordering, &ordering);
166             }
167           while (lex_token (lexer) != T_SLASH
168                  && lex_token (lexer) != T_ENDCMD);
169
170           vm.reorder_vars = v;
171           vm.reorder_cnt = nv;
172         }
173       else if (lex_match_id (lexer, "RENAME"))
174         {
175           if (already_encountered & 2)
176             {
177               lex_sbc_only_once ("RENAME");
178               goto done;
179             }
180           already_encountered |= 2;
181
182           lex_match (lexer, T_EQUALS);
183           do
184             {
185               size_t prev_nv_1 = vm.rename_cnt;
186               size_t prev_nv_2 = vm.rename_cnt;
187
188               if (!lex_match (lexer, T_LPAREN))
189                 {
190                   lex_error_expecting (lexer, "`('", NULL_SENTINEL);
191                   goto done;
192                 }
193               if (!parse_variables (lexer, dataset_dict (ds),
194                                     &vm.rename_vars, &vm.rename_cnt,
195                                     PV_APPEND | PV_NO_DUPLICATE))
196                 goto done;
197               if (!lex_match (lexer, T_EQUALS))
198                 {
199                   lex_error_expecting (lexer, "`='", NULL_SENTINEL);
200                   goto done;
201                 }
202               if (!parse_DATA_LIST_vars (lexer, dataset_dict (ds),
203                                          &vm.new_names, &prev_nv_1, PV_APPEND))
204                 goto done;
205               if (prev_nv_1 != vm.rename_cnt)
206                 {
207                   msg (SE, _("Differing number of variables in old name list "
208                              "(%zu) and in new name list (%zu)."),
209                        vm.rename_cnt - prev_nv_2, prev_nv_1 - prev_nv_2);
210                   for (i = 0; i < prev_nv_1; i++)
211                     free (vm.new_names[i]);
212                   free (vm.new_names);
213                   vm.new_names = NULL;
214                   goto done;
215                 }
216               if (!lex_match (lexer, T_RPAREN))
217                 {
218                   lex_error_expecting (lexer, "`)'", NULL_SENTINEL);
219                   goto done;
220                 }
221             }
222           while (lex_token (lexer) != T_ENDCMD
223                  && lex_token (lexer) != T_SLASH);
224         }
225       else if (lex_match_id (lexer, "KEEP"))
226         {
227           struct variable **keep_vars, **all_vars, **drop_vars;
228           size_t keep_cnt, all_cnt, drop_cnt;
229
230           if (already_encountered & 4)
231             {
232               msg (SE, _("%s subcommand may be given at most once.  It may "
233                          "not be given in conjunction with the %s subcommand."),
234                    "KEEP", "DROP");
235               goto done;
236             }
237           already_encountered |= 4;
238
239           lex_match (lexer, T_EQUALS);
240           if (!parse_variables (lexer, dataset_dict (ds), &keep_vars, &keep_cnt, PV_NONE))
241             goto done;
242
243           /* Transform the list of variables to keep into a list of
244              variables to drop.  First sort the keep list, then figure
245              out which variables are missing. */
246           sort (keep_vars, keep_cnt, sizeof *keep_vars,
247                 compare_variables_given_ordering, &forward_positional_ordering);
248
249           dict_get_vars_mutable (dataset_dict (ds), &all_vars, &all_cnt, 0);
250           assert (all_cnt >= keep_cnt);
251
252           drop_cnt = all_cnt - keep_cnt;
253           drop_vars = xnmalloc (drop_cnt, sizeof *keep_vars);
254           if (set_difference (all_vars, all_cnt,
255                               keep_vars, keep_cnt,
256                               sizeof *all_vars,
257                               drop_vars,
258                               compare_variables_given_ordering,
259                               &forward_positional_ordering)
260               != drop_cnt)
261             NOT_REACHED ();
262
263           free (keep_vars);
264           free (all_vars);
265
266           vm.drop_vars = drop_vars;
267           vm.drop_cnt = drop_cnt;
268         }
269       else if (lex_match_id (lexer, "DROP"))
270         {
271           struct variable **drop_vars;
272           size_t drop_cnt;
273
274           if (already_encountered & 4)
275             {
276               msg (SE, _("%s subcommand may be given at most once.  It may "
277                          "not be given in conjunction with the %s "
278                          "subcommand."),
279                    "DROP", "KEEP"
280                    );
281               goto done;
282             }
283           already_encountered |= 4;
284
285           lex_match (lexer, T_EQUALS);
286           if (!parse_variables (lexer, dataset_dict (ds), &drop_vars, &drop_cnt, PV_NONE))
287             goto done;
288           vm.drop_vars = drop_vars;
289           vm.drop_cnt = drop_cnt;
290         }
291       else if (lex_match_id (lexer, "MAP"))
292         {
293           struct dictionary *temp = dict_clone (dataset_dict (ds));
294           int success = rearrange_dict (temp, &vm);
295           if (success)
296             {
297               /* FIXME: display new dictionary. */
298             }
299           dict_unref (temp);
300         }
301       else
302         {
303           if (lex_token (lexer) == T_ID)
304             msg (SE, _("Unrecognized subcommand name `%s'."), lex_tokcstr (lexer));
305           else
306             msg (SE, _("Subcommand name expected."));
307           goto done;
308         }
309
310       if (lex_token (lexer) == T_ENDCMD)
311         break;
312       if (lex_token (lexer) != T_SLASH)
313         {
314           lex_error_expecting (lexer, "`/'", "`.'", NULL_SENTINEL);
315           goto done;
316         }
317       lex_get (lexer);
318     }
319
320   if (already_encountered & (1 | 4))
321     {
322       /* Read the data. */
323       if (!proc_execute (ds))
324         goto done;
325     }
326
327   if (!rearrange_dict (dataset_dict (ds), &vm))
328     goto done;
329
330   ret_code = CMD_SUCCESS;
331
332 done:
333   free (vm.reorder_vars);
334   free (vm.rename_vars);
335   for (i = 0; i < vm.rename_cnt; i++)
336     free (vm.new_names[i]);
337   free (vm.new_names);
338   free (vm.drop_vars);
339   return ret_code;
340 }
341
342 /* Compares A and B according to the settings in
343    ORDERING, returning a strcmp()-type result. */
344 static int
345 compare_variables_given_ordering (const void *a_, const void *b_,
346                                   const void *ordering_)
347 {
348   struct variable *const *pa = a_;
349   struct variable *const *pb = b_;
350   const struct variable *a = *pa;
351   const struct variable *b = *pb;
352   const struct ordering *ordering = ordering_;
353
354   int result;
355   if (ordering->positional)
356     {
357       size_t a_index = var_get_dict_index (a);
358       size_t b_index = var_get_dict_index (b);
359       result = a_index < b_index ? -1 : a_index > b_index;
360     }
361   else
362     result = utf8_strcasecmp (var_get_name (a), var_get_name (b));
363   if (!ordering->forward)
364     result = -result;
365   return result;
366 }
367
368 /* Pairs a variable with a new name. */
369 struct var_renaming
370   {
371     struct variable *var;
372     const char *new_name;
373   };
374
375 /* A algo_compare_func that compares new_name members in struct
376    var_renaming structures A and B. */
377 static int
378 compare_var_renaming_by_new_name (const void *a_, const void *b_,
379                                   const void *aux UNUSED)
380 {
381   const struct var_renaming *a = a_;
382   const struct var_renaming *b = b_;
383
384   return utf8_strcasecmp (a->new_name, b->new_name);
385 }
386
387 /* Returns true if performing VM on dictionary D would not cause
388    problems such as duplicate variable names.  Returns false
389    otherwise, and issues an error message. */
390 static int
391 validate_var_modification (const struct dictionary *d,
392                            const struct var_modification *vm)
393 {
394   /* Variable reordering can't be a problem, so we don't simulate
395      it.  Variable renaming can cause duplicate names, but
396      dropping variables can eliminate them, so we simulate both
397      of those. */
398   struct variable **all_vars;
399   struct variable **keep_vars;
400   struct variable **drop_vars;
401   size_t keep_cnt, drop_cnt;
402   size_t all_cnt;
403
404   struct var_renaming *var_renaming;
405   int valid;
406   size_t i;
407
408   /* All variables, in index order. */
409   dict_get_vars_mutable (d, &all_vars, &all_cnt, 0);
410
411   /* Drop variables, in index order. */
412   drop_cnt = vm->drop_cnt;
413   drop_vars = xnmalloc (drop_cnt, sizeof *drop_vars);
414   memcpy (drop_vars, vm->drop_vars, drop_cnt * sizeof *drop_vars);
415   sort (drop_vars, drop_cnt, sizeof *drop_vars,
416         compare_variables_given_ordering, &forward_positional_ordering);
417
418   /* Keep variables, in index order. */
419   assert (all_cnt >= drop_cnt);
420   keep_cnt = all_cnt - drop_cnt;
421   keep_vars = xnmalloc (keep_cnt, sizeof *keep_vars);
422   if (set_difference (all_vars, all_cnt,
423                       drop_vars, drop_cnt,
424                       sizeof *all_vars,
425                       keep_vars,
426                       compare_variables_given_ordering,
427                       &forward_positional_ordering) != keep_cnt)
428     NOT_REACHED ();
429
430   /* Copy variables into var_renaming array. */
431   var_renaming = xnmalloc (keep_cnt, sizeof *var_renaming);
432   for (i = 0; i < keep_cnt; i++)
433     {
434       var_renaming[i].var = keep_vars[i];
435       var_renaming[i].new_name = var_get_name (keep_vars[i]);
436     }
437
438   /* Rename variables in var_renaming array. */
439   for (i = 0; i < vm->rename_cnt; i++)
440     {
441       struct variable *const *kv;
442       struct var_renaming *vr;
443
444       /* Get the var_renaming element. */
445       kv = binary_search (keep_vars, keep_cnt, sizeof *keep_vars,
446                           &vm->rename_vars[i],
447                           compare_variables_given_ordering,
448                           &forward_positional_ordering);
449       if (kv == NULL)
450         continue;
451       vr = var_renaming + (kv - keep_vars);
452
453       vr->new_name = vm->new_names[i];
454     }
455
456   /* Sort var_renaming array by new names and check for
457      duplicates. */
458   sort (var_renaming, keep_cnt, sizeof *var_renaming,
459         compare_var_renaming_by_new_name, NULL);
460   valid = adjacent_find_equal (var_renaming, keep_cnt, sizeof *var_renaming,
461                                compare_var_renaming_by_new_name, NULL) == NULL;
462
463   /* Clean up. */
464   free (all_vars);
465   free (keep_vars);
466   free (drop_vars);
467   free (var_renaming);
468
469   return valid;
470 }
471
472 /* Reoders, removes, and renames variables in dictionary D
473    according to VM.  Returns true if successful, false if there
474    would have been duplicate variable names if the modifications
475    had been carried out.  In the latter case, the dictionary is
476    not modified. */
477 static bool
478 rearrange_dict (struct dictionary *d, const struct var_modification *vm)
479 {
480   char **rename_old_names;
481
482   struct variable **rename_vars;
483   char **rename_new_names;
484   size_t rename_cnt;
485
486   size_t i;
487
488   /* Check whether the modifications will cause duplicate
489      names. */
490   if (!validate_var_modification (d, vm))
491     return false;
492
493   /* Record the old names of variables to rename.  After
494      variables are deleted, we can't depend on the variables to
495      still exist, but we can still look them up by name. */
496   rename_old_names = xnmalloc (vm->rename_cnt, sizeof *rename_old_names);
497   for (i = 0; i < vm->rename_cnt; i++)
498     rename_old_names[i] = xstrdup (var_get_name (vm->rename_vars[i]));
499
500   /* Reorder and delete variables. */
501   dict_reorder_vars (d, vm->reorder_vars, vm->reorder_cnt);
502   dict_delete_vars (d, vm->drop_vars, vm->drop_cnt);
503
504   /* Compose lists of variables to rename and their new names. */
505   rename_vars = xnmalloc (vm->rename_cnt, sizeof *rename_vars);
506   rename_new_names = xnmalloc (vm->rename_cnt, sizeof *rename_new_names);
507   rename_cnt = 0;
508   for (i = 0; i < vm->rename_cnt; i++)
509     {
510       struct variable *var = dict_lookup_var (d, rename_old_names[i]);
511       if (var == NULL)
512         continue;
513
514       rename_vars[rename_cnt] = var;
515       rename_new_names[rename_cnt] = vm->new_names[i];
516       rename_cnt++;
517     }
518
519   /* Do renaming. */
520   if (dict_rename_vars (d, rename_vars, rename_new_names, rename_cnt,
521                         NULL) == 0)
522     NOT_REACHED ();
523
524   /* Clean up. */
525   for (i = 0; i < vm->rename_cnt; i++)
526     free (rename_old_names[i]);
527   free (rename_old_names);
528   free (rename_vars);
529   free (rename_new_names);
530
531   return true;
532 }