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