better tests
[pspp] / src / data / short-names.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2007, 2009, 2010, 2011, 2014 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/short-names.h"
20
21 #include "data/dictionary.h"
22 #include "data/sys-file-private.h"
23 #include "data/variable.h"
24 #include "libpspp/assertion.h"
25 #include "libpspp/compiler.h"
26 #include "libpspp/i18n.h"
27 #include "libpspp/message.h"
28 #include "libpspp/str.h"
29 #include "libpspp/stringi-set.h"
30
31 #include "gettext.h"
32 #define _(msgid) gettext (msgid)
33
34 static void
35 claim_short_name (struct variable *v, size_t i,
36                   struct stringi_set *short_names)
37 {
38   const char *short_name = var_get_short_name (v, i);
39   if (short_name != NULL && !stringi_set_insert (short_names, short_name))
40     var_set_short_name (v, i, NULL);
41 }
42
43 /* Form initial short_name from the variable name, then try _A,
44    _B, ... _AA, _AB, etc., if needed. */
45 static void
46 assign_short_name (struct variable *v, size_t i,
47                    struct stringi_set *short_names)
48 {
49   int trial;
50
51   if (var_get_short_name (v, i) != NULL)
52     return;
53
54   for (trial = 0; ; trial++)
55     {
56       char suffix[SHORT_NAME_LEN + 1];
57       char *short_name;
58
59       /* Compose suffix. */
60       if (trial == 0)
61         suffix[0] = '\0';
62       else
63         {
64           suffix[0] = '_';
65           str_format_26adic (trial, true, &suffix[1], sizeof suffix - 1);
66         }
67
68       /* Set name. */
69       short_name = utf8_encoding_concat (var_get_name (v), suffix,
70                                          var_get_encoding (v), SHORT_NAME_LEN);
71       if (stringi_set_insert (short_names, short_name))
72         {
73           var_set_short_name (v, i, short_name);
74           free (short_name);
75           return;
76         }
77       free (short_name);
78     }
79 }
80
81 /* Assigns a valid, unique short_name[] to each variable in D.
82    Each variable whose actual name is short has highest priority
83    for that short name.  Otherwise, variables with an existing
84    short_name[] have the next highest priority for a given short
85    name; if it is already taken, then the variable is treated as
86    if short_name[] had been empty.  Otherwise, long names are
87    truncated to form short names.  If that causes conflicts,
88    variables are renamed as PREFIX_A, PREFIX_B, and so on. */
89 void
90 short_names_assign (struct dictionary *d)
91 {
92   size_t n_vars = dict_get_n_vars (d);
93   struct stringi_set short_names;
94
95   stringi_set_init (&short_names);
96
97   /* Clear short names that conflict with a variable name. */
98   for (size_t i = 0; i < n_vars; i++)
99     {
100       struct variable *v = dict_get_var (d, i);
101       int n_segments = sfm_width_to_segments (var_get_width (v));
102       for (size_t j = 0; j < n_segments; j++)
103         {
104           const char *name = var_get_short_name (v, j);
105           if (name != NULL)
106             {
107               struct variable *ov = dict_lookup_var (d, name);
108               if (ov != NULL && (ov != v || j > 0))
109                 var_set_short_name (v, j, NULL);
110             }
111         }
112     }
113
114   /* Give variables whose names are short the corresponding short
115      name. */
116   for (size_t i = 0; i < n_vars; i++)
117     {
118       struct variable *v = dict_get_var (d, i);
119       const char *name = var_get_name (v);
120       int len = recode_string_len (var_get_encoding (v), "UTF-8", name, -1);
121       if (len <= SHORT_NAME_LEN)
122         var_set_short_name (v, 0, name);
123     }
124
125   /* Each variable with an assigned short name for its first
126      segment now gets it unless there is a conflict.  In case of
127      conflict, the claimant earlier in dictionary order wins.
128      Then similarly for additional segments of very long
129      strings. */
130   for (size_t i = 0; i < n_vars; i++)
131     {
132       struct variable *v = dict_get_var (d, i);
133       claim_short_name (v, 0, &short_names);
134     }
135   for (size_t i = 0; i < n_vars; i++)
136     {
137       struct variable *v = dict_get_var (d, i);
138       int n_segments = sfm_width_to_segments (var_get_width (v));
139       for (size_t j = 1; j < n_segments; j++)
140         claim_short_name (v, j, &short_names);
141     }
142
143   /* Assign short names to first segment of remaining variables,
144      then similarly for additional segments. */
145   for (size_t i = 0; i < n_vars; i++)
146     {
147       struct variable *v = dict_get_var (d, i);
148       assign_short_name (v, 0, &short_names);
149     }
150   for (size_t i = 0; i < n_vars; i++)
151     {
152       struct variable *v = dict_get_var (d, i);
153       int n_segments = sfm_width_to_segments (var_get_width (v));
154       for (size_t j = 1; j < n_segments; j++)
155         assign_short_name (v, j, &short_names);
156     }
157
158   stringi_set_destroy (&short_names);
159 }