Revert my revision 6996, which was wrong. Sources need to stay around in the session...
[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::read_bits (EnumRegistration& er, string str)
186 {
187         vector<int>::iterator i;
188         vector<string>::iterator s;
189         int result = 0;
190         bool found = false;
191         string::size_type comma;
192
193         /* catch old-style hex numerics */
194
195         if (str.length() > 2 && str[0] == '0' && str[1] == 'x') {
196                 return strtol (str.c_str(), (char **) 0, 16);
197         }
198
199         /* catch old style dec numerics */
200
201         if (strspn (str.c_str(), "0123456789") == str.length()) {
202                 return strtol (str.c_str(), (char **) 0, 10);
203         }
204
205         do {
206                 
207                 comma = str.find_first_of (',');
208                 string segment = str.substr (0, comma);
209
210                 for (i = er.values.begin(), s = er.names.begin(); i != er.values.end(); ++i, ++s) {
211                         if (segment == *s || nocase_cmp (segment, *s) == 0) {
212                                 result |= (*i);
213                                 found = true;
214                         }
215                 }
216
217                 if (comma == string::npos) {
218                         break;
219                 }
220
221                 str = str.substr (comma+1);
222
223         } while (true);
224
225         if (!found) {
226                 throw unknown_enumeration();
227         }
228
229         return result;
230 }
231
232 int
233 EnumWriter::read_distinct (EnumRegistration& er, string str)
234 {
235         vector<int>::iterator i;
236         vector<string>::iterator s;
237
238         /* catch old-style hex numerics */
239
240         if (str.length() > 2 && str[0] == '0' && str[1] == 'x') {
241                 return strtol (str.c_str(), (char **) 0, 16);
242         }
243
244         /* catch old style dec numerics */
245
246         if (strspn (str.c_str(), "0123456789") == str.length()) {
247                 return strtol (str.c_str(), (char **) 0, 10);
248         }
249
250         for (i = er.values.begin(), s = er.names.begin(); i != er.values.end(); ++i, ++s) {
251                 if (str == (*s) || nocase_cmp (str, *s) == 0) {
252                         return (*i);
253                 }
254         }
255
256         /* failed to find it as-is. check to see if there a hack for the name we're looking up */
257
258         map<string,string>::iterator x;
259
260         if ((x  = hack_table.find (str)) != hack_table.end()) {
261
262                 cerr << "found hack for " << str << " = " << x->second << endl;
263
264                 str = x->second;
265
266                 for (i = er.values.begin(), s = er.names.begin(); i != er.values.end(); ++i, ++s) {
267                         if (str == (*s) || nocase_cmp (str, *s) == 0) {
268                                 return (*i);
269                         }
270                 }
271         }
272
273         throw unknown_enumeration();
274 }
275
276 void
277 EnumWriter::add_to_hack_table (string str, string hacked)
278 {
279         hack_table[str] = hacked;
280 }