initial commit, lordsawar source, slightly modified
[lordsawar] / src / ucompose.hpp
1 /* Defines String::ucompose(fmt, arg...) for easy, i18n-friendly
2  * composition of strings with Gtkmm >= 1.3.* (see www.gtkmm.org).
3  * Uses Glib::ustring instead of std::string which doesn't work with
4  * Gtkmm due to character encoding troubles with stringstreams.
5  *
6  * Version 1.0.4.
7  *
8  * Copyright (c) 2002, 03, 04 Ole Laursen <olau@hardworking.dk>.
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public License
12  * as published by the Free Software Foundation; either version 2.1 of
13  * the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this file; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 
23  * 02110-1301, USA.
24  */
25
26 //
27 // Basic usage is like
28 //
29 //   String::ucompose("This is a %1x%2 matrix.", rows, cols);
30 //
31 // See http://www.cs.aau.dk/~olau/compose/ or the included
32 // README.compose for more details.
33 //
34
35 #ifndef STRING_UCOMPOSE_HPP
36 #define STRING_UCOMPOSE_HPP
37
38 #include <glibmm/ustring.h>
39 #include <glibmm/convert.h>
40
41 #include <sstream>
42 #include <string>
43 #include <list>
44 #include <map>                  // for multimap
45
46 namespace UStringPrivate
47 {
48   // the actual composition class - using String::ucompose is cleaner, so we
49   // hide it here
50   class Composition
51   {
52   public:
53     // initialize and prepare format string on the form "text %1 text %2 etc."
54     explicit Composition(std::string fmt);
55
56     // supply an replacement argument starting from %1
57     template <typename T>
58     Composition &arg(const T &obj);
59
60     // compose and return string
61     Glib::ustring str() const;
62
63   private:
64     std::wostringstream os;
65     int arg_no;
66
67     // we store the output as a list - when the output string is requested, the
68     // list is concatenated to a string; this way we can keep iterators into
69     // the list instead of into a string where they're possibly invalidated
70     // when inserting a specification string
71     typedef std::list<std::string> output_list;
72     output_list output;
73
74     // the initial parse of the format string fills in the specification map
75     // with positions for each of the various %?s
76     typedef std::multimap<int, output_list::iterator> specification_map;
77     specification_map specs;
78
79     template <typename T>
80     std::string stringify(T obj);
81   };
82
83   // helper for converting spec string numbers
84   inline int char_to_int(char c)
85   {
86     switch (c) {
87     case '0': return 0;
88     case '1': return 1;
89     case '2': return 2;
90     case '3': return 3;
91     case '4': return 4;
92     case '5': return 5;
93     case '6': return 6;
94     case '7': return 7;
95     case '8': return 8;
96     case '9': return 9;
97     default: return -1000;
98     }
99   }
100
101   inline bool is_number(int n)
102   {
103     switch (n) {
104     case '0':
105     case '1':
106     case '2':
107     case '3':
108     case '4':
109     case '5':
110     case '6':
111     case '7':
112     case '8':
113     case '9':
114       return true;
115     
116     default:
117       return false;
118     }
119   }
120
121   template <typename T>
122   inline std::string Composition::stringify(T obj)
123   {
124     os << obj;
125
126     std::wstring str = os.str();
127     
128     return Glib::convert(std::string(reinterpret_cast<const char *>(str.data()),
129                                      str.size() * sizeof(wchar_t)),
130                          "UTF-8", "WCHAR_T");
131   }
132
133   // specialisations for the common string types
134   template <>
135   inline std::string
136   Composition::stringify<std::string>(std::string obj)
137   {
138     return obj;
139   }
140   
141   template <>
142   inline std::string
143   Composition::stringify<Glib::ustring>(Glib::ustring obj)
144   {
145     return obj;
146   }
147   
148   template <>
149   inline std::string
150   Composition::stringify<const char *>(const char *obj)
151   {
152     return obj;
153   }
154   
155   // implementation of class Composition
156   template <typename T>
157   inline Composition &Composition::arg(const T &obj)
158   {
159     Glib::ustring rep = stringify(obj);
160     
161     if (!rep.empty()) {         // manipulators don't produce output
162       for (specification_map::const_iterator i = specs.lower_bound(arg_no),
163              end = specs.upper_bound(arg_no); i != end; ++i) {
164         output_list::iterator pos = i->second;
165         ++pos;
166       
167         output.insert(pos, rep);
168       }
169     
170       os.str(std::wstring());
171       //os.clear();
172       ++arg_no;
173     }
174   
175     return *this;
176   }
177
178   inline Composition::Composition(std::string fmt)
179     : arg_no(1)
180   {
181 //#if __GNUC__ >= 3
182     //os.imbue(std::locale("")); // use the user's locale for the stream
183 //#endif
184     std::string::size_type b = 0, i = 0;
185   
186     // fill in output with the strings between the %1 %2 %3 etc. and
187     // fill in specs with the positions
188     while (i < fmt.length()) {
189       if (fmt[i] == '%' && i + 1 < fmt.length()) {
190         if (fmt[i + 1] == '%') { // catch %%
191           fmt.replace(i, 2, "%");
192           ++i;
193         }
194         else if (is_number(fmt[i + 1])) { // aha! a spec!
195           // save string
196           output.push_back(fmt.substr(b, i - b));
197         
198           int n = 1;            // number of digits
199           int spec_no = 0;
200
201           do {
202             spec_no += char_to_int(fmt[i + n]);
203             spec_no *= 10;
204             ++n;
205           } while (i + n < fmt.length() && is_number(fmt[i + n]));
206
207           spec_no /= 10;
208           output_list::iterator pos = output.end();
209           --pos;                // safe since we have just inserted a string
210         
211           specs.insert(specification_map::value_type(spec_no, pos));
212         
213           // jump over spec string
214           i += n;
215           b = i;
216         }
217         else
218           ++i;
219       }
220       else
221         ++i;
222     }
223   
224     if (i - b > 0)              // add the rest of the string
225       output.push_back(fmt.substr(b, i - b));
226   }
227
228   inline Glib::ustring Composition::str() const
229   {
230     // assemble string
231     std::string str;
232   
233     for (output_list::const_iterator i = output.begin(), end = output.end();
234          i != end; ++i)
235       str += *i;
236   
237     return str;
238   }
239 }
240
241
242 namespace String 
243 {
244   // a series of functions which accept a format string on the form "text %1
245   // more %2 less %3" and a number of templated parameters and spits out the
246   // composited string
247   template <typename T1>
248   inline Glib::ustring ucompose(const Glib::ustring &fmt, const T1 &o1)
249   {
250     UStringPrivate::Composition c(fmt);
251     c.arg(o1);
252     return c.str();
253   }
254
255   template <typename T1, typename T2>
256   inline Glib::ustring ucompose(const Glib::ustring &fmt,
257                                 const T1 &o1, const T2 &o2)
258   {
259     UStringPrivate::Composition c(fmt);
260     c.arg(o1).arg(o2);
261     return c.str();
262   }
263
264   template <typename T1, typename T2, typename T3>
265   inline Glib::ustring ucompose(const Glib::ustring &fmt,
266                                 const T1 &o1, const T2 &o2, const T3 &o3)
267   {
268     UStringPrivate::Composition c(fmt);
269     c.arg(o1).arg(o2).arg(o3);
270     return c.str();
271   }
272
273   template <typename T1, typename T2, typename T3, typename T4>
274   inline Glib::ustring ucompose(const Glib::ustring &fmt,
275                                 const T1 &o1, const T2 &o2, const T3 &o3,
276                                 const T4 &o4)
277   {
278     UStringPrivate::Composition c(fmt);
279     c.arg(o1).arg(o2).arg(o3).arg(o4);
280     return c.str();
281   }
282
283   template <typename T1, typename T2, typename T3, typename T4, typename T5>
284   inline Glib::ustring ucompose(const Glib::ustring &fmt,
285                                 const T1 &o1, const T2 &o2, const T3 &o3,
286                                 const T4 &o4, const T5 &o5)
287   {
288     UStringPrivate::Composition c(fmt);
289     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5);
290     return c.str();
291   }
292
293   template <typename T1, typename T2, typename T3, typename T4, typename T5,
294             typename T6>
295   inline Glib::ustring ucompose(const Glib::ustring &fmt,
296                                 const T1 &o1, const T2 &o2, const T3 &o3,
297                                 const T4 &o4, const T5 &o5, const T6 &o6)
298   {
299     UStringPrivate::Composition c(fmt);
300     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6);
301     return c.str();
302   }
303
304   template <typename T1, typename T2, typename T3, typename T4, typename T5,
305             typename T6, typename T7>
306   inline Glib::ustring ucompose(const Glib::ustring &fmt,
307                                 const T1 &o1, const T2 &o2, const T3 &o3,
308                                 const T4 &o4, const T5 &o5, const T6 &o6,
309                                 const T7 &o7)
310   {
311     UStringPrivate::Composition c(fmt);
312     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7);
313     return c.str();
314   }
315
316   template <typename T1, typename T2, typename T3, typename T4, typename T5,
317             typename T6, typename T7, typename T8>
318   inline Glib::ustring ucompose(const Glib::ustring &fmt,
319                                 const T1 &o1, const T2 &o2, const T3 &o3,
320                                 const T4 &o4, const T5 &o5, const T6 &o6,
321                                 const T7 &o7, const T8 &o8)
322   {
323     UStringPrivate::Composition c(fmt);
324     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8);
325     return c.str();
326   }
327
328   template <typename T1, typename T2, typename T3, typename T4, typename T5,
329             typename T6, typename T7, typename T8, typename T9>
330   inline Glib::ustring ucompose(const Glib::ustring &fmt,
331                                 const T1 &o1, const T2 &o2, const T3 &o3,
332                                 const T4 &o4, const T5 &o5, const T6 &o6,
333                                 const T7 &o7, const T8 &o8, const T9 &o9)
334   {
335     UStringPrivate::Composition c(fmt);
336     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9);
337     return c.str();
338   }
339
340   template <typename T1, typename T2, typename T3, typename T4, typename T5,
341             typename T6, typename T7, typename T8, typename T9, typename T10>
342   inline Glib::ustring ucompose(const Glib::ustring &fmt,
343                                 const T1 &o1, const T2 &o2, const T3 &o3,
344                                 const T4 &o4, const T5 &o5, const T6 &o6,
345                                 const T7 &o7, const T8 &o8, const T9 &o9,
346                                 const T10 &o10)
347   {
348     UStringPrivate::Composition c(fmt);
349     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
350       .arg(o10);
351     return c.str();
352   }
353   
354   template <typename T1, typename T2, typename T3, typename T4, typename T5,
355             typename T6, typename T7, typename T8, typename T9, typename T10,
356             typename T11>
357   inline Glib::ustring ucompose(const Glib::ustring &fmt,
358                                 const T1 &o1, const T2 &o2, const T3 &o3,
359                                 const T4 &o4, const T5 &o5, const T6 &o6,
360                                 const T7 &o7, const T8 &o8, const T9 &o9,
361                                 const T10 &o10, const T11 &o11)
362   {
363     UStringPrivate::Composition c(fmt);
364     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
365       .arg(o10).arg(o11);
366     return c.str();
367   }
368
369   template <typename T1, typename T2, typename T3, typename T4, typename T5,
370             typename T6, typename T7, typename T8, typename T9, typename T10,
371             typename T11, typename T12>
372   inline Glib::ustring ucompose(const Glib::ustring &fmt,
373                                 const T1 &o1, const T2 &o2, const T3 &o3,
374                                 const T4 &o4, const T5 &o5, const T6 &o6,
375                                 const T7 &o7, const T8 &o8, const T9 &o9,
376                                 const T10 &o10, const T11 &o11, const T12 &o12)
377   {
378     UStringPrivate::Composition c(fmt);
379     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
380       .arg(o10).arg(o11).arg(o12);
381     return c.str();
382   }
383
384   template <typename T1, typename T2, typename T3, typename T4, typename T5,
385             typename T6, typename T7, typename T8, typename T9, typename T10,
386             typename T11, typename T12, typename T13>
387   inline Glib::ustring ucompose(const Glib::ustring &fmt,
388                                 const T1 &o1, const T2 &o2, const T3 &o3,
389                                 const T4 &o4, const T5 &o5, const T6 &o6,
390                                 const T7 &o7, const T8 &o8, const T9 &o9,
391                                 const T10 &o10, const T11 &o11, const T12 &o12,
392                                 const T13 &o13)
393   {
394     UStringPrivate::Composition c(fmt);
395     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
396       .arg(o10).arg(o11).arg(o12).arg(o13);
397     return c.str();
398   }
399
400   template <typename T1, typename T2, typename T3, typename T4, typename T5,
401             typename T6, typename T7, typename T8, typename T9, typename T10,
402             typename T11, typename T12, typename T13, typename T14>
403   inline Glib::ustring ucompose(const Glib::ustring &fmt,
404                                 const T1 &o1, const T2 &o2, const T3 &o3,
405                                 const T4 &o4, const T5 &o5, const T6 &o6,
406                                 const T7 &o7, const T8 &o8, const T9 &o9,
407                                 const T10 &o10, const T11 &o11, const T12 &o12,
408                                 const T13 &o13, const T14 &o14)
409   {
410     UStringPrivate::Composition c(fmt);
411     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
412       .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14);
413     return c.str();
414   }
415
416   template <typename T1, typename T2, typename T3, typename T4, typename T5,
417             typename T6, typename T7, typename T8, typename T9, typename T10,
418             typename T11, typename T12, typename T13, typename T14,
419             typename T15>
420   inline Glib::ustring ucompose(const Glib::ustring &fmt,
421                                 const T1 &o1, const T2 &o2, const T3 &o3,
422                                 const T4 &o4, const T5 &o5, const T6 &o6,
423                                 const T7 &o7, const T8 &o8, const T9 &o9,
424                                 const T10 &o10, const T11 &o11, const T12 &o12,
425                                 const T13 &o13, const T14 &o14, const T15 &o15)
426   {
427     UStringPrivate::Composition c(fmt);
428     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
429       .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14).arg(o15);
430     return c.str();
431   }
432 }
433
434
435 #endif // STRING_UCOMPOSE_HPP