enough with umpteen "i18n.h" files. Consolidate on pbd/i18n.h
[ardour.git] / libs / pbd / enumwriter.cc
1 /*
2     Copyright (C) 2006 Paul Davis
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 2 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, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18     $Id$
19 */
20
21 #include <cctype>
22 #include <algorithm>
23
24 #include <cstring>
25 #include <cstdlib>
26
27 #include "pbd/enumwriter.h"
28 #include "pbd/error.h"
29 #include "pbd/compose.h"
30
31 using namespace std;
32 using namespace PBD;
33
34 #include "pbd/i18n.h"
35
36 EnumWriter* EnumWriter::_instance = 0;
37 map<string,string> EnumWriter::hack_table;
38
39 static int
40 nocase_cmp(const string & s1, const string& s2)
41 {
42         string::const_iterator it1 = s1.begin();
43         string::const_iterator it2 = s2.begin();
44
45         while ((it1 != s1.end()) && (it2 != s2.end())) {
46                 if(::toupper(*it1) != ::toupper(*it2))  {//letters differ?
47                         // return -1 to indicate 'smaller than', 1 otherwise
48                         return (::toupper(*it1) < ::toupper(*it2)) ? -1 : 1;
49                 }
50
51                 ++it1;
52                 ++it2;
53         }
54
55         string::size_type size1 = s1.size();
56         string::size_type size2 = s2.size();
57
58         //return -1,0 or 1 according to strings' lengths
59
60         if (size1 == size2) {
61                 return 0;
62         }
63
64         return (size1 < size2) ? -1 : 1;
65 }
66
67 EnumWriter&
68 EnumWriter::instance()
69 {
70         if (_instance == 0) {
71                 _instance = new EnumWriter;
72         }
73
74         return *_instance;
75 }
76
77 void
78 EnumWriter::destroy ()
79 {
80         delete _instance;
81         _instance = 0;
82 }
83
84 EnumWriter::EnumWriter ()
85 {
86 }
87
88 EnumWriter::~EnumWriter ()
89 {
90 }
91
92 void
93 EnumWriter::register_distinct (string type, vector<int> v, vector<string> s)
94 {
95         pair<string,EnumRegistration> newpair;
96         pair<Registry::iterator,bool> result;
97
98         newpair.first = type;
99         newpair.second = EnumRegistration (v, s, false);
100
101         result = registry.insert (newpair);
102
103         if (!result.second) {
104                 warning << string_compose (_("enum type \"%1\" already registered with the enum writer"), type) << endmsg;
105         }
106 }
107
108 void
109 EnumWriter::register_bits (string type, vector<int> v, vector<string> s)
110 {
111         pair<string,EnumRegistration> newpair;
112         pair<Registry::iterator,bool> result;
113
114         newpair.first = type;
115         newpair.second = EnumRegistration (v, s, true);
116
117         result = registry.insert (newpair);
118
119         if (!result.second) {
120                 warning << _("enum type \"%1\" already registered with the enum writer") << endmsg;
121         }
122 }
123
124 string
125 EnumWriter::write (string type, int value)
126 {
127         Registry::iterator x = registry.find (type);
128
129         if (x == registry.end()) {
130                 error << string_compose (_("EnumWriter: unknown enumeration type \"%1\""), type) << endmsg;
131                 throw unknown_enumeration (type);
132         }
133
134         if (x->second.bitwise) {
135                 return write_bits (x->second, value);
136         } else {
137                 return write_distinct (x->second, value);
138         }
139 }
140
141 int
142 EnumWriter::read (string type, string value)
143 {
144         Registry::iterator x = registry.find (type);
145
146         if (x == registry.end()) {
147                 error << string_compose (_("EnumWriter: unknown enumeration type \"%1\""), type) << endmsg;
148                 throw unknown_enumeration (type);
149         }
150
151         if (x->second.bitwise) {
152                 return read_bits (x->second, value);
153         } else {
154                 return read_distinct (x->second, value);
155         }
156 }
157
158 string
159 EnumWriter::write_bits (EnumRegistration& er, int value)
160 {
161         vector<int>::iterator i;
162         vector<string>::iterator s;
163         string result;
164
165         for (i = er.values.begin(), s = er.names.begin(); i != er.values.end(); ++i, ++s) {
166                 if (value & (*i)) {
167                         if (!result.empty()) {
168                                 result += ',';
169                         }
170                         result += (*s);
171                 }
172         }
173
174         return result;
175 }
176
177 string
178 EnumWriter::write_distinct (EnumRegistration& er, int value)
179 {
180         vector<int>::iterator i;
181         vector<string>::iterator s;
182
183         for (i = er.values.begin(), s = er.names.begin(); i != er.values.end(); ++i, ++s) {
184                 if (value == (*i)) {
185                         return (*s);
186                 }
187         }
188
189         return string();
190 }
191
192 int
193 EnumWriter::validate (EnumRegistration& er, int val) const
194 {
195         if (er.values.empty()) {
196                 return val;
197         }
198
199         if (val == 0) {
200                 /* zero is always a legal value for our enumerations, just about
201                  */
202                 return val;
203         }
204
205         vector<int>::iterator i;
206         string enum_name = _("unknown enumeration");
207
208         for (Registry::const_iterator x = registry.begin(); x != registry.end(); ++x) {
209                 if (&er == &(*x).second) {
210                         enum_name = (*x).first;
211                 }
212         }
213
214
215         for (i = er.values.begin(); i != er.values.end(); ++i) {
216                 if (*i == val) {
217                         return val;
218                 }
219         }
220
221         warning << string_compose (_("Illegal value loaded for %1 (%2) - %3 used instead"),
222                                    enum_name, val, er.names.front())
223                 << endmsg;
224         return er.values.front();
225 }
226
227 int
228 EnumWriter::validate_bitwise (EnumRegistration& er, int val) const
229 {
230         int result = 0;
231         for (int p = 1; p <= val; p = p << 1) {
232                 if (std::find (er.values.begin(), er.values.end(), p) == er.values.end()) {
233                         continue;
234                 }
235                 if (p & val) {
236                         result |= p;
237                 }
238         }
239         return result;
240 }
241
242 int
243 EnumWriter::read_bits (EnumRegistration& er, string str)
244 {
245         vector<int>::iterator i;
246         vector<string>::iterator s;
247         int result = 0;
248         bool found = false;
249         string::size_type comma;
250
251         /* catch old-style hex numerics */
252
253         if (str.length() > 2 && str[0] == '0' && str[1] == 'x') {
254                 int val = strtol (str.c_str(), (char **) 0, 16);
255                 return validate_bitwise (er, val);
256         }
257
258         /* catch old style dec numerics */
259
260         if (strspn (str.c_str(), "0123456789") == str.length()) {
261                 int val = strtol (str.c_str(), (char **) 0, 10);
262                 return validate_bitwise (er, val);
263         }
264
265         do {
266
267                 comma = str.find_first_of (',');
268                 string segment = str.substr (0, comma);
269
270                 for (i = er.values.begin(), s = er.names.begin(); i != er.values.end(); ++i, ++s) {
271                         if (segment == *s || nocase_cmp (segment, *s) == 0) {
272                                 result |= (*i);
273                                 found = true;
274                         }
275                 }
276
277                 if (comma == string::npos) {
278                         break;
279                 }
280
281                 str = str.substr (comma+1);
282
283         } while (true);
284
285         if (!found) {
286                 throw unknown_enumeration (str);
287         }
288
289         return result;
290 }
291
292 int
293 EnumWriter::read_distinct (EnumRegistration& er, string str)
294 {
295         vector<int>::iterator i;
296         vector<string>::iterator s;
297
298         /* first, check to see if there a hack for the name we're looking up */
299
300         map<string,string>::iterator x;
301
302         if ((x  = hack_table.find (str)) != hack_table.end()) {
303
304                 cerr << "found hack for " << str << " = " << x->second << endl;
305
306                 str = x->second;
307
308                 for (i = er.values.begin(), s = er.names.begin(); i != er.values.end(); ++i, ++s) {
309                         if (str == (*s) || nocase_cmp (str, *s) == 0) {
310                                 return (*i);
311                         }
312                 }
313         }
314
315         /* catch old-style hex numerics */
316
317         if (str.length() > 2 && str[0] == '0' && str[1] == 'x') {
318                 int val = strtol (str.c_str(), (char **) 0, 16);
319                 return validate (er, val);
320         }
321
322         /* catch old style dec numerics */
323
324         if (strspn (str.c_str(), "0123456789") == str.length()) {
325                 int val = strtol (str.c_str(), (char **) 0, 10);
326                 return validate (er, val);
327         }
328
329         for (i = er.values.begin(), s = er.names.begin(); i != er.values.end(); ++i, ++s) {
330                 if (str == (*s) || nocase_cmp (str, *s) == 0) {
331                         return (*i);
332                 }
333         }
334
335         throw unknown_enumeration(str);
336 }
337
338 void
339 EnumWriter::add_to_hack_table (string str, string hacked)
340 {
341         hack_table[str] = hacked;
342 }