add new sigc++2 directory
[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 <ctype.h>
22
23 #include <string.h>
24 #include <stdlib.h>
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::EnumWriter ()
67 {
68         if (_instance == 0) {
69                 _instance = this;
70         }
71 }
72
73 EnumWriter::~EnumWriter ()
74 {
75 }
76
77 void
78 EnumWriter::register_distinct (string type, vector<int> v, vector<string> s)
79 {
80         pair<string,EnumRegistration> newpair;
81         pair<Registry::iterator,bool> result;
82
83         newpair.first = type;
84         newpair.second = EnumRegistration (v, s, false);
85         
86         result = registry.insert (newpair);
87
88         if (!result.second) {
89                 warning << string_compose (_("enum type \"%1\" already registered with the enum writer"), type) << endmsg;
90         }
91 }
92
93 void
94 EnumWriter::register_bits (string type, vector<int> v, vector<string> s)
95 {
96         pair<string,EnumRegistration> newpair;
97         pair<Registry::iterator,bool> result;
98
99         newpair.first = type;
100         newpair.second = EnumRegistration (v, s, true);
101         
102         result = registry.insert (newpair);
103
104         if (!result.second) {
105                 warning << _("enum type \"%1\" already registered with the enum writer") << endmsg;
106         }
107 }
108
109 string
110 EnumWriter::write (string type, int value)
111 {
112         Registry::iterator x = registry.find (type);
113
114         if (x == registry.end()) {
115                 error << string_compose (_("EnumWriter: unknown enumeration type \"%1\""), type) << endmsg;
116                 throw unknown_enumeration();
117         }
118
119         if (x->second.bitwise) {
120                 return write_bits (x->second, value);
121         } else {
122                 return write_distinct (x->second, value);
123         }
124 }
125
126 int
127 EnumWriter::read (string type, string value)
128 {
129         Registry::iterator x = registry.find (type);
130
131         if (x == registry.end()) {
132                 error << string_compose (_("EnumWriter: unknown enumeration type \"%1\""), type) << endmsg;
133                 throw unknown_enumeration();
134         }
135
136         if (x->second.bitwise) {
137                 return read_bits (x->second, value);
138         } else {
139                 return read_distinct (x->second, value);
140         }
141 }       
142
143 string
144 EnumWriter::write_bits (EnumRegistration& er, int value)
145 {
146         vector<int>::iterator i;
147         vector<string>::iterator s;
148         string result;
149
150         for (i = er.values.begin(), s = er.names.begin(); i != er.values.end(); ++i, ++s) {
151                 if (value & (*i)) {
152                         if (!result.empty()) {
153                                 result += ',';
154                         } 
155                         result += (*s);
156                 }
157         }
158
159         return result;
160 }
161
162 string
163 EnumWriter::write_distinct (EnumRegistration& er, int value)
164 {
165         vector<int>::iterator i;
166         vector<string>::iterator s;
167
168         for (i = er.values.begin(), s = er.names.begin(); i != er.values.end(); ++i, ++s) {
169                 if (value == (*i)) {
170                         return (*s);
171                 }
172         }
173
174         return string();
175 }
176
177 int
178 EnumWriter::read_bits (EnumRegistration& er, string str)
179 {
180         vector<int>::iterator i;
181         vector<string>::iterator s;
182         int result = 0;
183         bool found = false;
184         string::size_type comma;
185
186         /* catch old-style hex numerics */
187
188         if (str.length() > 2 && str[0] == '0' && str[1] == 'x') {
189                 return strtol (str.c_str(), (char **) 0, 16);
190         }
191
192         /* catch old style dec numerics */
193
194         if (strspn (str.c_str(), "0123456789") == str.length()) {
195                 return strtol (str.c_str(), (char **) 0, 10);
196         }
197
198         do {
199                 
200                 comma = str.find_first_of (',');
201                 string segment = str.substr (0, comma);
202
203                 for (i = er.values.begin(), s = er.names.begin(); i != er.values.end(); ++i, ++s) {
204                         if (segment == *s || nocase_cmp (segment, *s) == 0) {
205                                 result |= (*i);
206                                 found = true;
207                         }
208                 }
209
210                 if (comma == string::npos) {
211                         break;
212                 }
213
214                 str = str.substr (comma+1);
215
216         } while (true);
217
218         if (!found) {
219                 throw unknown_enumeration();
220         }
221
222         return result;
223 }
224
225 int
226 EnumWriter::read_distinct (EnumRegistration& er, string str)
227 {
228         vector<int>::iterator i;
229         vector<string>::iterator s;
230
231         /* catch old-style hex numerics */
232
233         if (str.length() > 2 && str[0] == '0' && str[1] == 'x') {
234                 return strtol (str.c_str(), (char **) 0, 16);
235         }
236
237         /* catch old style dec numerics */
238
239         if (strspn (str.c_str(), "0123456789") == str.length()) {
240                 return strtol (str.c_str(), (char **) 0, 10);
241         }
242
243         for (i = er.values.begin(), s = er.names.begin(); i != er.values.end(); ++i, ++s) {
244                 if (str == (*s) || nocase_cmp (str, *s) == 0) {
245                         return (*i);
246                 }
247         }
248
249         /* failed to find it as-is. check to see if there a hack for the name we're looking up */
250
251         map<string,string>::iterator x;
252
253         if ((x  = hack_table.find (str)) != hack_table.end()) {
254
255                 cerr << "found hack for " << str << " = " << x->second << endl;
256
257                 str = x->second;
258
259                 for (i = er.values.begin(), s = er.names.begin(); i != er.values.end(); ++i, ++s) {
260                         if (str == (*s) || nocase_cmp (str, *s) == 0) {
261                                 return (*i);
262                         }
263                 }
264         }
265
266         throw unknown_enumeration();
267 }
268
269 void
270 EnumWriter::add_to_hack_table (string str, string hacked)
271 {
272         hack_table[str] = hacked;
273 }