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 <sstream>
42 #include <string>
43 #include <list>
44 #include <map> // for multimap
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);
56 // supply an replacement argument starting from %1
57 template <typename T>
58 Composition &arg(const T &obj);
60 // compose and return string
61 Glib::ustring str() const;
63 private:
65 //This is standard, not GCC-specific like wostringstream
66 std::basic_ostringstream<wchar_t> os;
68 int arg_no;
70 // we store the output as a list - when the output string is requested, the
71 // list is concatenated to a string; this way we can keep iterators into
72 // the list instead of into a string where they're possibly invalidated
73 // when inserting a specification string
74 typedef std::list<std::string> output_list;
75 output_list output;
77 // the initial parse of the format string fills in the specification map
78 // with positions for each of the various %?s
79 typedef std::multimap<int, output_list::iterator> specification_map;
80 specification_map specs;
82 template <typename T>
83 std::string stringify(T obj);
84 };
86 // helper for converting spec string numbers
87 inline int char_to_int(char c)
88 {
89 switch (c) {
90 case '0': return 0;
91 case '1': return 1;
92 case '2': return 2;
93 case '3': return 3;
94 case '4': return 4;
95 case '5': return 5;
96 case '6': return 6;
97 case '7': return 7;
98 case '8': return 8;
99 case '9': return 9;
100 default: return -1000;
101 }
102 }
104 inline bool is_number(int n)
105 {
106 switch (n) {
107 case '0':
108 case '1':
109 case '2':
110 case '3':
111 case '4':
112 case '5':
113 case '6':
114 case '7':
115 case '8':
116 case '9':
117 return true;
119 default:
120 return false;
121 }
122 }
124 template <typename T>
125 inline std::string Composition::stringify(T obj)
126 {
127 os << obj;
129 std::wstring str = os.str();
131 return Glib::convert(std::string(reinterpret_cast<const char *>(str.data()),
132 str.size() * sizeof(wchar_t)),
133 "UTF-8", "WCHAR_T");
134 }
136 // specialisations for the common string types
137 template <>
138 inline std::string
139 Composition::stringify<std::string>(std::string obj)
140 {
141 return obj;
142 }
144 template <>
145 inline std::string
146 Composition::stringify<Glib::ustring>(Glib::ustring obj)
147 {
148 return obj;
149 }
151 template <>
152 inline std::string
153 Composition::stringify<const char *>(const char *obj)
154 {
155 return obj;
156 }
158 // implementation of class Composition
159 template <typename T>
160 inline Composition &Composition::arg(const T &obj)
161 {
162 Glib::ustring rep = stringify(obj);
164 if (!rep.empty()) { // manipulators don't produce output
165 for (specification_map::const_iterator i = specs.lower_bound(arg_no),
166 end = specs.upper_bound(arg_no); i != end; ++i) {
167 output_list::iterator pos = i->second;
168 ++pos;
170 output.insert(pos, rep);
171 }
173 os.str(std::wstring());
174 //os.clear();
175 ++arg_no;
176 }
178 return *this;
179 }
181 inline Composition::Composition(std::string fmt)
182 : arg_no(1)
183 {
184 #if __GNUC__ >= 3
185 try {
186 os.imbue(std::locale("")); // use the user's locale for the stream
187 }
188 catch (std::runtime_error& e) { // fallback to classic if it failed
189 os.imbue(std::locale::classic());
190 }
191 #endif
192 std::string::size_type b = 0, i = 0;
194 // fill in output with the strings between the %1 %2 %3 etc. and
195 // fill in specs with the positions
196 while (i < fmt.length()) {
197 if (fmt[i] == '%' && i + 1 < fmt.length()) {
198 if (fmt[i + 1] == '%') { // catch %%
199 fmt.replace(i, 2, "%");
200 ++i;
201 }
202 else if (is_number(fmt[i + 1])) { // aha! a spec!
203 // save string
204 output.push_back(fmt.substr(b, i - b));
206 int n = 1; // number of digits
207 int spec_no = 0;
209 do {
210 spec_no += char_to_int(fmt[i + n]);
211 spec_no *= 10;
212 ++n;
213 } while (i + n < fmt.length() && is_number(fmt[i + n]));
215 spec_no /= 10;
216 output_list::iterator pos = output.end();
217 --pos; // safe since we have just inserted a string
219 specs.insert(specification_map::value_type(spec_no, pos));
221 // jump over spec string
222 i += n;
223 b = i;
224 }
225 else
226 ++i;
227 }
228 else
229 ++i;
230 }
232 if (i - b > 0) // add the rest of the string
233 output.push_back(fmt.substr(b, i - b));
234 }
236 inline Glib::ustring Composition::str() const
237 {
238 // assemble string
239 std::string str;
241 for (output_list::const_iterator i = output.begin(), end = output.end();
242 i != end; ++i)
243 str += *i;
245 return str;
246 }
247 }
250 namespace String
251 {
252 // a series of functions which accept a format string on the form "text %1
253 // more %2 less %3" and a number of templated parameters and spits out the
254 // composited string
255 template <typename T1>
256 inline Glib::ustring ucompose(const Glib::ustring &fmt, const T1 &o1)
257 {
258 UStringPrivate::Composition c(fmt);
259 c.arg(o1);
260 return c.str();
261 }
263 template <typename T1, typename T2>
264 inline Glib::ustring ucompose(const Glib::ustring &fmt,
265 const T1 &o1, const T2 &o2)
266 {
267 UStringPrivate::Composition c(fmt);
268 c.arg(o1).arg(o2);
269 return c.str();
270 }
272 template <typename T1, typename T2, typename T3>
273 inline Glib::ustring ucompose(const Glib::ustring &fmt,
274 const T1 &o1, const T2 &o2, const T3 &o3)
275 {
276 UStringPrivate::Composition c(fmt);
277 c.arg(o1).arg(o2).arg(o3);
278 return c.str();
279 }
281 template <typename T1, typename T2, typename T3, typename T4>
282 inline Glib::ustring ucompose(const Glib::ustring &fmt,
283 const T1 &o1, const T2 &o2, const T3 &o3,
284 const T4 &o4)
285 {
286 UStringPrivate::Composition c(fmt);
287 c.arg(o1).arg(o2).arg(o3).arg(o4);
288 return c.str();
289 }
291 template <typename T1, typename T2, typename T3, typename T4, typename T5>
292 inline Glib::ustring ucompose(const Glib::ustring &fmt,
293 const T1 &o1, const T2 &o2, const T3 &o3,
294 const T4 &o4, const T5 &o5)
295 {
296 UStringPrivate::Composition c(fmt);
297 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5);
298 return c.str();
299 }
301 template <typename T1, typename T2, typename T3, typename T4, typename T5,
302 typename T6>
303 inline Glib::ustring ucompose(const Glib::ustring &fmt,
304 const T1 &o1, const T2 &o2, const T3 &o3,
305 const T4 &o4, const T5 &o5, const T6 &o6)
306 {
307 UStringPrivate::Composition c(fmt);
308 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6);
309 return c.str();
310 }
312 template <typename T1, typename T2, typename T3, typename T4, typename T5,
313 typename T6, typename T7>
314 inline Glib::ustring ucompose(const Glib::ustring &fmt,
315 const T1 &o1, const T2 &o2, const T3 &o3,
316 const T4 &o4, const T5 &o5, const T6 &o6,
317 const T7 &o7)
318 {
319 UStringPrivate::Composition c(fmt);
320 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7);
321 return c.str();
322 }
324 template <typename T1, typename T2, typename T3, typename T4, typename T5,
325 typename T6, typename T7, typename T8>
326 inline Glib::ustring ucompose(const Glib::ustring &fmt,
327 const T1 &o1, const T2 &o2, const T3 &o3,
328 const T4 &o4, const T5 &o5, const T6 &o6,
329 const T7 &o7, const T8 &o8)
330 {
331 UStringPrivate::Composition c(fmt);
332 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8);
333 return c.str();
334 }
336 template <typename T1, typename T2, typename T3, typename T4, typename T5,
337 typename T6, typename T7, typename T8, typename T9>
338 inline Glib::ustring ucompose(const Glib::ustring &fmt,
339 const T1 &o1, const T2 &o2, const T3 &o3,
340 const T4 &o4, const T5 &o5, const T6 &o6,
341 const T7 &o7, const T8 &o8, const T9 &o9)
342 {
343 UStringPrivate::Composition c(fmt);
344 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9);
345 return c.str();
346 }
348 template <typename T1, typename T2, typename T3, typename T4, typename T5,
349 typename T6, typename T7, typename T8, typename T9, typename T10>
350 inline Glib::ustring ucompose(const Glib::ustring &fmt,
351 const T1 &o1, const T2 &o2, const T3 &o3,
352 const T4 &o4, const T5 &o5, const T6 &o6,
353 const T7 &o7, const T8 &o8, const T9 &o9,
354 const T10 &o10)
355 {
356 UStringPrivate::Composition c(fmt);
357 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
358 .arg(o10);
359 return c.str();
360 }
362 template <typename T1, typename T2, typename T3, typename T4, typename T5,
363 typename T6, typename T7, typename T8, typename T9, typename T10,
364 typename T11>
365 inline Glib::ustring ucompose(const Glib::ustring &fmt,
366 const T1 &o1, const T2 &o2, const T3 &o3,
367 const T4 &o4, const T5 &o5, const T6 &o6,
368 const T7 &o7, const T8 &o8, const T9 &o9,
369 const T10 &o10, const T11 &o11)
370 {
371 UStringPrivate::Composition c(fmt);
372 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
373 .arg(o10).arg(o11);
374 return c.str();
375 }
377 template <typename T1, typename T2, typename T3, typename T4, typename T5,
378 typename T6, typename T7, typename T8, typename T9, typename T10,
379 typename T11, typename T12>
380 inline Glib::ustring ucompose(const Glib::ustring &fmt,
381 const T1 &o1, const T2 &o2, const T3 &o3,
382 const T4 &o4, const T5 &o5, const T6 &o6,
383 const T7 &o7, const T8 &o8, const T9 &o9,
384 const T10 &o10, const T11 &o11, const T12 &o12)
385 {
386 UStringPrivate::Composition c(fmt);
387 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
388 .arg(o10).arg(o11).arg(o12);
389 return c.str();
390 }
392 template <typename T1, typename T2, typename T3, typename T4, typename T5,
393 typename T6, typename T7, typename T8, typename T9, typename T10,
394 typename T11, typename T12, typename T13>
395 inline Glib::ustring ucompose(const Glib::ustring &fmt,
396 const T1 &o1, const T2 &o2, const T3 &o3,
397 const T4 &o4, const T5 &o5, const T6 &o6,
398 const T7 &o7, const T8 &o8, const T9 &o9,
399 const T10 &o10, const T11 &o11, const T12 &o12,
400 const T13 &o13)
401 {
402 UStringPrivate::Composition c(fmt);
403 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
404 .arg(o10).arg(o11).arg(o12).arg(o13);
405 return c.str();
406 }
408 template <typename T1, typename T2, typename T3, typename T4, typename T5,
409 typename T6, typename T7, typename T8, typename T9, typename T10,
410 typename T11, typename T12, typename T13, typename T14>
411 inline Glib::ustring ucompose(const Glib::ustring &fmt,
412 const T1 &o1, const T2 &o2, const T3 &o3,
413 const T4 &o4, const T5 &o5, const T6 &o6,
414 const T7 &o7, const T8 &o8, const T9 &o9,
415 const T10 &o10, const T11 &o11, const T12 &o12,
416 const T13 &o13, const T14 &o14)
417 {
418 UStringPrivate::Composition c(fmt);
419 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
420 .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14);
421 return c.str();
422 }
424 template <typename T1, typename T2, typename T3, typename T4, typename T5,
425 typename T6, typename T7, typename T8, typename T9, typename T10,
426 typename T11, typename T12, typename T13, typename T14,
427 typename T15>
428 inline Glib::ustring ucompose(const Glib::ustring &fmt,
429 const T1 &o1, const T2 &o2, const T3 &o3,
430 const T4 &o4, const T5 &o5, const T6 &o6,
431 const T7 &o7, const T8 &o8, const T9 &o9,
432 const T10 &o10, const T11 &o11, const T12 &o12,
433 const T13 &o13, const T14 &o14, const T15 &o15)
434 {
435 UStringPrivate::Composition c(fmt);
436 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
437 .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14).arg(o15);
438 return c.str();
439 }
440 }
443 #endif // STRING_UCOMPOSE_HPP