splice mode is undefined, undocumented, and buggy. ripple does most of what we want...
[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 void
77 EnumWriter::destroy ()
78 {
79         delete _instance;
80         _instance = 0;
81 }
82
83 EnumWriter::EnumWriter ()
84 {
85 }
86
87 EnumWriter::~EnumWriter ()
88 {
89 }
90
91 void
92 EnumWriter::register_distinct (string type, vector<int> v, vector<string> s)
93 {
94         pair<string,EnumRegistration> newpair;
95         pair<Registry::iterator,bool> result;
96
97         newpair.first = type;
98         newpair.second = EnumRegistration (v, s, false);
99         
100         result = registry.insert (newpair);
101
102         if (!result.second) {
103                 warning << string_compose (_("enum type \"%1\" already registered with the enum writer"), type) << endmsg;
104         }
105 }
106
107 void
108 EnumWriter::register_bits (string type, vector<int> v, vector<string> s)
109 {
110         pair<string,EnumRegistration> newpair;
111         pair<Registry::iterator,bool> result;
112
113         newpair.first = type;
114         newpair.second = EnumRegistration (v, s, true);
115         
116         result = registry.insert (newpair);
117
118         if (!result.second) {
119                 warning << _("enum type \"%1\" already registered with the enum writer") << endmsg;
120         }
121 }
122
123 string
124 EnumWriter::write (string type, int value)
125 {
126         Registry::iterator x = registry.find (type);
127
128         if (x == registry.end()) {
129                 error << string_compose (_("EnumWriter: unknown enumeration type \"%1\""), type) << endmsg;
130                 throw unknown_enumeration (type);
131         }
132
133         if (x->second.bitwise) {
134                 return write_bits (x->second, value);
135         } else {
136                 return write_distinct (x->second, value);
137         }
138 }
139
140 int
141 EnumWriter::read (string type, string value)
142 {
143         Registry::iterator x = registry.find (type);
144
145         if (x == registry.end()) {
146                 error << string_compose (_("EnumWriter: unknown enumeration type \"%1\""), type) << endmsg;
147                 throw unknown_enumeration (type);
148         }
149
150         if (x->second.bitwise) {
151                 return read_bits (x->second, value);
152         } else {
153                 return read_distinct (x->second, value);
154         }
155 }       
156
157 string
158 EnumWriter::write_bits (EnumRegistration& er, int value)
159 {
160         vector<int>::iterator i;
161         vector<string>::iterator s;
162         string result;
163
164         for (i = er.values.begin(), s = er.names.begin(); i != er.values.end(); ++i, ++s) {
165                 if (value & (*i)) {
166                         if (!result.empty()) {
167                                 result += ',';
168                         } 
169                         result += (*s);
170                 }
171         }
172
173         return result;
174 }
175
176 string
177 EnumWriter::write_distinct (EnumRegistration& er, int value)
178 {
179         vector<int>::iterator i;
180         vector<string>::iterator s;
181
182         for (i = er.values.begin(), s = er.names.begin(); i != er.values.end(); ++i, ++s) {
183                 if (value == (*i)) {
184                         return (*s);
185                 }
186         }
187
188         return string();
189 }
190
191 int
192 EnumWriter::validate (EnumRegistration& er, int val)
193 {
194         if (er.values.empty()) {
195                 return val;
196         }
197
198         if (val == 0) {
199                 /* zero is always a legal value for our enumerations, just about
200                  */
201                 return val;
202         }
203
204         vector<int>::iterator i;
205         string enum_name = _("unknown enumeration");
206         
207         for (Registry::iterator x = registry.begin(); x != registry.end(); ++x) {
208                 if (&er == &(*x).second) {
209                         enum_name = (*x).first;
210                 }
211         }
212         
213
214         for (i = er.values.begin(); i != er.values.end(); ++i) {
215                 if (*i == val) {
216                         return val;
217                 }
218         }
219         
220         warning << string_compose (_("Illegal value loaded for %1 (%2) - %3 used instead"),
221                                    enum_name, val, er.names.front()) 
222                 << endmsg;
223         return er.values.front();
224 }
225
226 int
227 EnumWriter::read_bits (EnumRegistration& er, string str)
228 {
229         vector<int>::iterator i;
230         vector<string>::iterator s;
231         int result = 0;
232         bool found = false;
233         string::size_type comma;
234
235         /* catch old-style hex numerics */
236
237         if (str.length() > 2 && str[0] == '0' && str[1] == 'x') {
238                 int val = strtol (str.c_str(), (char **) 0, 16);
239                 return validate (er, val);
240         }
241
242         /* catch old style dec numerics */
243
244         if (strspn (str.c_str(), "0123456789") == str.length()) {
245                 int val = strtol (str.c_str(), (char **) 0, 10);
246                 return validate (er, val);
247         }
248
249         do {
250                 
251                 comma = str.find_first_of (',');
252                 string segment = str.substr (0, comma);
253
254                 for (i = er.values.begin(), s = er.names.begin(); i != er.values.end(); ++i, ++s) {
255                         if (segment == *s || nocase_cmp (segment, *s) == 0) {
256                                 result |= (*i);
257                                 found = true;
258                         }
259                 }
260
261                 if (comma == string::npos) {
262                         break;
263                 }
264
265                 str = str.substr (comma+1);
266
267         } while (true);
268
269         if (!found) {
270                 throw unknown_enumeration (str);
271         }
272
273         return result;
274 }
275
276 int
277 EnumWriter::read_distinct (EnumRegistration& er, string str)
278 {
279         vector<int>::iterator i;
280         vector<string>::iterator s;
281
282         /* first, check to see if there a hack for the name we're looking up */
283
284         map<string,string>::iterator x;
285
286         if ((x  = hack_table.find (str)) != hack_table.end()) {
287
288                 cerr << "found hack for " << str << " = " << x->second << endl;
289
290                 str = x->second;
291
292                 for (i = er.values.begin(), s = er.names.begin(); i != er.values.end(); ++i, ++s) {
293                         if (str == (*s) || nocase_cmp (str, *s) == 0) {
294                                 return (*i);
295                         }
296                 }
297         }
298
299         /* catch old-style hex numerics */
300
301         if (str.length() > 2 && str[0] == '0' && str[1] == 'x') {
302                 int val = strtol (str.c_str(), (char **) 0, 16);
303                 return validate (er, val);
304         }
305
306         /* catch old style dec numerics */
307
308         if (strspn (str.c_str(), "0123456789") == str.length()) {
309                 int val = strtol (str.c_str(), (char **) 0, 10);
310                 return validate (er, val);
311         }
312
313         for (i = er.values.begin(), s = er.names.begin(); i != er.values.end(); ++i, ++s) {
314                 if (str == (*s) || nocase_cmp (str, *s) == 0) {
315                         return (*i);
316                 }
317         }
318
319         throw unknown_enumeration(str);
320 }
321
322 void
323 EnumWriter::add_to_hack_table (string str, string hacked)
324 {
325         hack_table[str] = hacked;
326 }