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
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23 * USA.
24 */
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 //
35 #ifndef STRING_UCOMPOSE_HPP
36 #define STRING_UCOMPOSE_HPP
38 #include <glibmm/ustring.h>
39 #include <glibmm/convert.h>
41 #include <stdexcept>
42 #include <sstream>
43 #include <string>
44 #include <list>
45 #include <map> // for multimap
47 namespace UStringPrivate
48 {
49 // the actual composition class - using String::ucompose is cleaner, so we
50 // hide it here
51 class Composition
52 {
53 public:
54 // initialize and prepare format string on the form "text %1 text %2 etc."
55 explicit Composition(std::string fmt);
57 // supply an replacement argument starting from %1
58 template <typename T>
59 Composition &arg(const T &obj);
61 // compose and return string
62 Glib::ustring str() const;
64 private:
66 //This is standard, not GCC-specific like wostringstream
67 std::basic_ostringstream<wchar_t> os;
69 int arg_no;
71 // we store the output as a list - when the output string is requested, the
72 // list is concatenated to a string; this way we can keep iterators into
73 // the list instead of into a string where they're possibly invalidated
74 // when inserting a specification string
75 typedef std::list<std::string> output_list;
76 output_list output;
78 // the initial parse of the format string fills in the specification map
79 // with positions for each of the various %?s
80 typedef std::multimap<int, output_list::iterator> specification_map;
81 specification_map specs;
83 template <typename T>
84 std::string stringify(T obj);
85 };
87 // helper for converting spec string numbers
88 inline int char_to_int(char c)
89 {
90 switch (c) {
91 case '0': return 0;
92 case '1': return 1;
93 case '2': return 2;
94 case '3': return 3;
95 case '4': return 4;
96 case '5': return 5;
97 case '6': return 6;
98 case '7': return 7;
99 case '8': return 8;
100 case '9': return 9;
101 default: return -1000;
102 }
103 }
105 inline bool is_number(int n)
106 {
107 switch (n) {
108 case '0':
109 case '1':
110 case '2':
111 case '3':
112 case '4':
113 case '5':
114 case '6':
115 case '7':
116 case '8':
117 case '9':
118 return true;
120 default:
121 return false;
122 }
123 }
125 template <typename T>
126 inline std::string Composition::stringify(T obj)
127 {
128 os << obj;
130 std::wstring str = os.str();
132 return Glib::convert(std::string(reinterpret_cast<const char *>(str.data()),
133 str.size() * sizeof(wchar_t)),
134 "UTF-8", "WCHAR_T");
135 }
137 // specialisations for the common string types
138 template <>
139 inline std::string
140 Composition::stringify<std::string>(std::string obj)
141 {
142 return obj;
143 }
145 template <>
146 inline std::string
147 Composition::stringify<Glib::ustring>(Glib::ustring obj)
148 {
149 return obj;
150 }
152 template <>
153 inline std::string
154 Composition::stringify<const char *>(const char *obj)
155 {
156 return obj;
157 }
159 // implementation of class Composition
160 template <typename T>
161 inline Composition &Composition::arg(const T &obj)
162 {
163 Glib::ustring rep = stringify(obj);
165 if (!rep.empty()) { // manipulators don't produce output
166 for (specification_map::const_iterator i = specs.lower_bound(arg_no),
167 end = specs.upper_bound(arg_no); i != end; ++i) {
168 output_list::iterator pos = i->second;
169 ++pos;
171 output.insert(pos, rep);
172 }
174 os.str(std::wstring());
175 //os.clear();
176 ++arg_no;
177 }
179 return *this;
180 }
182 inline Composition::Composition(std::string fmt)
183 : arg_no(1)
184 {
185 #if __GNUC__ >= 3
186 try {
187 os.imbue(std::locale("")); // use the user's locale for the stream
188 }
189 catch (std::runtime_error& e) { // fallback to classic if it failed
190 os.imbue(std::locale::classic());
191 }
192 #endif
193 std::string::size_type b = 0, i = 0;
195 // fill in output with the strings between the %1 %2 %3 etc. and
196 // fill in specs with the positions
197 while (i < fmt.length()) {
198 if (fmt[i] == '%' && i + 1 < fmt.length()) {
199 if (fmt[i + 1] == '%') { // catch %%
200 fmt.replace(i, 2, "%");
201 ++i;
202 }
203 else if (is_number(fmt[i + 1])) { // aha! a spec!
204 // save string
205 output.push_back(fmt.substr(b, i - b));
207 int n = 1; // number of digits
208 int spec_no = 0;
210 do {
211 spec_no += char_to_int(fmt[i + n]);
212 spec_no *= 10;
213 ++n;
214 } while (i + n < fmt.length() && is_number(fmt[i + n]));
216 spec_no /= 10;
217 output_list::iterator pos = output.end();
218 --pos; // safe since we have just inserted a string
220 specs.insert(specification_map::value_type(spec_no, pos));
222 // jump over spec string
223 i += n;
224 b = i;
225 }
226 else
227 ++i;
228 }
229 else
230 ++i;
231 }
233 if (i - b > 0) // add the rest of the string
234 output.push_back(fmt.substr(b, i - b));
235 }
237 inline Glib::ustring Composition::str() const
238 {
239 // assemble string
240 std::string str;
242 for (output_list::const_iterator i = output.begin(), end = output.end();
243 i != end; ++i)
244 str += *i;
246 return str;
247 }
248 }
251 namespace String
252 {
253 // a series of functions which accept a format string on the form "text %1
254 // more %2 less %3" and a number of templated parameters and spits out the
255 // composited string
256 template <typename T1>
257 inline Glib::ustring ucompose(const Glib::ustring &fmt, const T1 &o1)
258 {
259 UStringPrivate::Composition c(fmt);
260 c.arg(o1);
261 return c.str();
262 }
264 template <typename T1, typename T2>
265 inline Glib::ustring ucompose(const Glib::ustring &fmt,
266 const T1 &o1, const T2 &o2)
267 {
268 UStringPrivate::Composition c(fmt);
269 c.arg(o1).arg(o2);
270 return c.str();
271 }
273 template <typename T1, typename T2, typename T3>
274 inline Glib::ustring ucompose(const Glib::ustring &fmt,
275 const T1 &o1, const T2 &o2, const T3 &o3)
276 {
277 UStringPrivate::Composition c(fmt);
278 c.arg(o1).arg(o2).arg(o3);
279 return c.str();
280 }
282 template <typename T1, typename T2, typename T3, typename T4>
283 inline Glib::ustring ucompose(const Glib::ustring &fmt,
284 const T1 &o1, const T2 &o2, const T3 &o3,
285 const T4 &o4)
286 {
287 UStringPrivate::Composition c(fmt);
288 c.arg(o1).arg(o2).arg(o3).arg(o4);
289 return c.str();
290 }
292 template <typename T1, typename T2, typename T3, typename T4, typename T5>
293 inline Glib::ustring ucompose(const Glib::ustring &fmt,
294 const T1 &o1, const T2 &o2, const T3 &o3,
295 const T4 &o4, const T5 &o5)
296 {
297 UStringPrivate::Composition c(fmt);
298 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5);
299 return c.str();
300 }
302 template <typename T1, typename T2, typename T3, typename T4, typename T5,
303 typename T6>
304 inline Glib::ustring ucompose(const Glib::ustring &fmt,
305 const T1 &o1, const T2 &o2, const T3 &o3,
306 const T4 &o4, const T5 &o5, const T6 &o6)
307 {
308 UStringPrivate::Composition c(fmt);
309 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6);
310 return c.str();
311 }
313 template <typename T1, typename T2, typename T3, typename T4, typename T5,
314 typename T6, typename T7>
315 inline Glib::ustring ucompose(const Glib::ustring &fmt,
316 const T1 &o1, const T2 &o2, const T3 &o3,
317 const T4 &o4, const T5 &o5, const T6 &o6,
318 const T7 &o7)
319 {
320 UStringPrivate::Composition c(fmt);
321 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7);
322 return c.str();
323 }
325 template <typename T1, typename T2, typename T3, typename T4, typename T5,
326 typename T6, typename T7, typename T8>
327 inline Glib::ustring ucompose(const Glib::ustring &fmt,
328 const T1 &o1, const T2 &o2, const T3 &o3,
329 const T4 &o4, const T5 &o5, const T6 &o6,
330 const T7 &o7, const T8 &o8)
331 {
332 UStringPrivate::Composition c(fmt);
333 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8);
334 return c.str();
335 }
337 template <typename T1, typename T2, typename T3, typename T4, typename T5,
338 typename T6, typename T7, typename T8, typename T9>
339 inline Glib::ustring ucompose(const Glib::ustring &fmt,
340 const T1 &o1, const T2 &o2, const T3 &o3,
341 const T4 &o4, const T5 &o5, const T6 &o6,
342 const T7 &o7, const T8 &o8, const T9 &o9)
343 {
344 UStringPrivate::Composition c(fmt);
345 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9);
346 return c.str();
347 }
349 template <typename T1, typename T2, typename T3, typename T4, typename T5,
350 typename T6, typename T7, typename T8, typename T9, typename T10>
351 inline Glib::ustring ucompose(const Glib::ustring &fmt,
352 const T1 &o1, const T2 &o2, const T3 &o3,
353 const T4 &o4, const T5 &o5, const T6 &o6,
354 const T7 &o7, const T8 &o8, const T9 &o9,
355 const T10 &o10)
356 {
357 UStringPrivate::Composition c(fmt);
358 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
359 .arg(o10);
360 return c.str();
361 }
363 template <typename T1, typename T2, typename T3, typename T4, typename T5,
364 typename T6, typename T7, typename T8, typename T9, typename T10,
365 typename T11>
366 inline Glib::ustring ucompose(const Glib::ustring &fmt,
367 const T1 &o1, const T2 &o2, const T3 &o3,
368 const T4 &o4, const T5 &o5, const T6 &o6,
369 const T7 &o7, const T8 &o8, const T9 &o9,
370 const T10 &o10, const T11 &o11)
371 {
372 UStringPrivate::Composition c(fmt);
373 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
374 .arg(o10).arg(o11);
375 return c.str();
376 }
378 template <typename T1, typename T2, typename T3, typename T4, typename T5,
379 typename T6, typename T7, typename T8, typename T9, typename T10,
380 typename T11, typename T12>
381 inline Glib::ustring ucompose(const Glib::ustring &fmt,
382 const T1 &o1, const T2 &o2, const T3 &o3,
383 const T4 &o4, const T5 &o5, const T6 &o6,
384 const T7 &o7, const T8 &o8, const T9 &o9,
385 const T10 &o10, const T11 &o11, const T12 &o12)
386 {
387 UStringPrivate::Composition c(fmt);
388 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
389 .arg(o10).arg(o11).arg(o12);
390 return c.str();
391 }
393 template <typename T1, typename T2, typename T3, typename T4, typename T5,
394 typename T6, typename T7, typename T8, typename T9, typename T10,
395 typename T11, typename T12, typename T13>
396 inline Glib::ustring ucompose(const Glib::ustring &fmt,
397 const T1 &o1, const T2 &o2, const T3 &o3,
398 const T4 &o4, const T5 &o5, const T6 &o6,
399 const T7 &o7, const T8 &o8, const T9 &o9,
400 const T10 &o10, const T11 &o11, const T12 &o12,
401 const T13 &o13)
402 {
403 UStringPrivate::Composition c(fmt);
404 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
405 .arg(o10).arg(o11).arg(o12).arg(o13);
406 return c.str();
407 }
409 template <typename T1, typename T2, typename T3, typename T4, typename T5,
410 typename T6, typename T7, typename T8, typename T9, typename T10,
411 typename T11, typename T12, typename T13, typename T14>
412 inline Glib::ustring ucompose(const Glib::ustring &fmt,
413 const T1 &o1, const T2 &o2, const T3 &o3,
414 const T4 &o4, const T5 &o5, const T6 &o6,
415 const T7 &o7, const T8 &o8, const T9 &o9,
416 const T10 &o10, const T11 &o11, const T12 &o12,
417 const T13 &o13, const T14 &o14)
418 {
419 UStringPrivate::Composition c(fmt);
420 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
421 .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14);
422 return c.str();
423 }
425 template <typename T1, typename T2, typename T3, typename T4, typename T5,
426 typename T6, typename T7, typename T8, typename T9, typename T10,
427 typename T11, typename T12, typename T13, typename T14,
428 typename T15>
429 inline Glib::ustring ucompose(const Glib::ustring &fmt,
430 const T1 &o1, const T2 &o2, const T3 &o3,
431 const T4 &o4, const T5 &o5, const T6 &o6,
432 const T7 &o7, const T8 &o8, const T9 &o9,
433 const T10 &o10, const T11 &o11, const T12 &o12,
434 const T13 &o13, const T14 &o14, const T15 &o15)
435 {
436 UStringPrivate::Composition c(fmt);
437 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
438 .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14).arg(o15);
439 return c.str();
440 }
441 }
444 #endif // STRING_UCOMPOSE_HPP