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 os.imbue(std::locale("")); // use the user's locale for the stream
186 #endif
187 std::string::size_type b = 0, i = 0;
189 // fill in output with the strings between the %1 %2 %3 etc. and
190 // fill in specs with the positions
191 while (i < fmt.length()) {
192 if (fmt[i] == '%' && i + 1 < fmt.length()) {
193 if (fmt[i + 1] == '%') { // catch %%
194 fmt.replace(i, 2, "%");
195 ++i;
196 }
197 else if (is_number(fmt[i + 1])) { // aha! a spec!
198 // save string
199 output.push_back(fmt.substr(b, i - b));
201 int n = 1; // number of digits
202 int spec_no = 0;
204 do {
205 spec_no += char_to_int(fmt[i + n]);
206 spec_no *= 10;
207 ++n;
208 } while (i + n < fmt.length() && is_number(fmt[i + n]));
210 spec_no /= 10;
211 output_list::iterator pos = output.end();
212 --pos; // safe since we have just inserted a string
214 specs.insert(specification_map::value_type(spec_no, pos));
216 // jump over spec string
217 i += n;
218 b = i;
219 }
220 else
221 ++i;
222 }
223 else
224 ++i;
225 }
227 if (i - b > 0) // add the rest of the string
228 output.push_back(fmt.substr(b, i - b));
229 }
231 inline Glib::ustring Composition::str() const
232 {
233 // assemble string
234 std::string str;
236 for (output_list::const_iterator i = output.begin(), end = output.end();
237 i != end; ++i)
238 str += *i;
240 return str;
241 }
242 }
245 namespace String
246 {
247 // a series of functions which accept a format string on the form "text %1
248 // more %2 less %3" and a number of templated parameters and spits out the
249 // composited string
250 template <typename T1>
251 inline Glib::ustring ucompose(const Glib::ustring &fmt, const T1 &o1)
252 {
253 UStringPrivate::Composition c(fmt);
254 c.arg(o1);
255 return c.str();
256 }
258 template <typename T1, typename T2>
259 inline Glib::ustring ucompose(const Glib::ustring &fmt,
260 const T1 &o1, const T2 &o2)
261 {
262 UStringPrivate::Composition c(fmt);
263 c.arg(o1).arg(o2);
264 return c.str();
265 }
267 template <typename T1, typename T2, typename T3>
268 inline Glib::ustring ucompose(const Glib::ustring &fmt,
269 const T1 &o1, const T2 &o2, const T3 &o3)
270 {
271 UStringPrivate::Composition c(fmt);
272 c.arg(o1).arg(o2).arg(o3);
273 return c.str();
274 }
276 template <typename T1, typename T2, typename T3, typename T4>
277 inline Glib::ustring ucompose(const Glib::ustring &fmt,
278 const T1 &o1, const T2 &o2, const T3 &o3,
279 const T4 &o4)
280 {
281 UStringPrivate::Composition c(fmt);
282 c.arg(o1).arg(o2).arg(o3).arg(o4);
283 return c.str();
284 }
286 template <typename T1, typename T2, typename T3, typename T4, typename T5>
287 inline Glib::ustring ucompose(const Glib::ustring &fmt,
288 const T1 &o1, const T2 &o2, const T3 &o3,
289 const T4 &o4, const T5 &o5)
290 {
291 UStringPrivate::Composition c(fmt);
292 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5);
293 return c.str();
294 }
296 template <typename T1, typename T2, typename T3, typename T4, typename T5,
297 typename T6>
298 inline Glib::ustring ucompose(const Glib::ustring &fmt,
299 const T1 &o1, const T2 &o2, const T3 &o3,
300 const T4 &o4, const T5 &o5, const T6 &o6)
301 {
302 UStringPrivate::Composition c(fmt);
303 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6);
304 return c.str();
305 }
307 template <typename T1, typename T2, typename T3, typename T4, typename T5,
308 typename T6, typename T7>
309 inline Glib::ustring ucompose(const Glib::ustring &fmt,
310 const T1 &o1, const T2 &o2, const T3 &o3,
311 const T4 &o4, const T5 &o5, const T6 &o6,
312 const T7 &o7)
313 {
314 UStringPrivate::Composition c(fmt);
315 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7);
316 return c.str();
317 }
319 template <typename T1, typename T2, typename T3, typename T4, typename T5,
320 typename T6, typename T7, typename T8>
321 inline Glib::ustring ucompose(const Glib::ustring &fmt,
322 const T1 &o1, const T2 &o2, const T3 &o3,
323 const T4 &o4, const T5 &o5, const T6 &o6,
324 const T7 &o7, const T8 &o8)
325 {
326 UStringPrivate::Composition c(fmt);
327 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8);
328 return c.str();
329 }
331 template <typename T1, typename T2, typename T3, typename T4, typename T5,
332 typename T6, typename T7, typename T8, typename T9>
333 inline Glib::ustring ucompose(const Glib::ustring &fmt,
334 const T1 &o1, const T2 &o2, const T3 &o3,
335 const T4 &o4, const T5 &o5, const T6 &o6,
336 const T7 &o7, const T8 &o8, const T9 &o9)
337 {
338 UStringPrivate::Composition c(fmt);
339 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9);
340 return c.str();
341 }
343 template <typename T1, typename T2, typename T3, typename T4, typename T5,
344 typename T6, typename T7, typename T8, typename T9, typename T10>
345 inline Glib::ustring ucompose(const Glib::ustring &fmt,
346 const T1 &o1, const T2 &o2, const T3 &o3,
347 const T4 &o4, const T5 &o5, const T6 &o6,
348 const T7 &o7, const T8 &o8, const T9 &o9,
349 const T10 &o10)
350 {
351 UStringPrivate::Composition c(fmt);
352 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
353 .arg(o10);
354 return c.str();
355 }
357 template <typename T1, typename T2, typename T3, typename T4, typename T5,
358 typename T6, typename T7, typename T8, typename T9, typename T10,
359 typename T11>
360 inline Glib::ustring ucompose(const Glib::ustring &fmt,
361 const T1 &o1, const T2 &o2, const T3 &o3,
362 const T4 &o4, const T5 &o5, const T6 &o6,
363 const T7 &o7, const T8 &o8, const T9 &o9,
364 const T10 &o10, const T11 &o11)
365 {
366 UStringPrivate::Composition c(fmt);
367 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
368 .arg(o10).arg(o11);
369 return c.str();
370 }
372 template <typename T1, typename T2, typename T3, typename T4, typename T5,
373 typename T6, typename T7, typename T8, typename T9, typename T10,
374 typename T11, typename T12>
375 inline Glib::ustring ucompose(const Glib::ustring &fmt,
376 const T1 &o1, const T2 &o2, const T3 &o3,
377 const T4 &o4, const T5 &o5, const T6 &o6,
378 const T7 &o7, const T8 &o8, const T9 &o9,
379 const T10 &o10, const T11 &o11, const T12 &o12)
380 {
381 UStringPrivate::Composition c(fmt);
382 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
383 .arg(o10).arg(o11).arg(o12);
384 return c.str();
385 }
387 template <typename T1, typename T2, typename T3, typename T4, typename T5,
388 typename T6, typename T7, typename T8, typename T9, typename T10,
389 typename T11, typename T12, typename T13>
390 inline Glib::ustring ucompose(const Glib::ustring &fmt,
391 const T1 &o1, const T2 &o2, const T3 &o3,
392 const T4 &o4, const T5 &o5, const T6 &o6,
393 const T7 &o7, const T8 &o8, const T9 &o9,
394 const T10 &o10, const T11 &o11, const T12 &o12,
395 const T13 &o13)
396 {
397 UStringPrivate::Composition c(fmt);
398 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
399 .arg(o10).arg(o11).arg(o12).arg(o13);
400 return c.str();
401 }
403 template <typename T1, typename T2, typename T3, typename T4, typename T5,
404 typename T6, typename T7, typename T8, typename T9, typename T10,
405 typename T11, typename T12, typename T13, typename T14>
406 inline Glib::ustring ucompose(const Glib::ustring &fmt,
407 const T1 &o1, const T2 &o2, const T3 &o3,
408 const T4 &o4, const T5 &o5, const T6 &o6,
409 const T7 &o7, const T8 &o8, const T9 &o9,
410 const T10 &o10, const T11 &o11, const T12 &o12,
411 const T13 &o13, const T14 &o14)
412 {
413 UStringPrivate::Composition c(fmt);
414 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
415 .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14);
416 return c.str();
417 }
419 template <typename T1, typename T2, typename T3, typename T4, typename T5,
420 typename T6, typename T7, typename T8, typename T9, typename T10,
421 typename T11, typename T12, typename T13, typename T14,
422 typename T15>
423 inline Glib::ustring ucompose(const Glib::ustring &fmt,
424 const T1 &o1, const T2 &o2, const T3 &o3,
425 const T4 &o4, const T5 &o5, const T6 &o6,
426 const T7 &o7, const T8 &o8, const T9 &o9,
427 const T10 &o10, const T11 &o11, const T12 &o12,
428 const T13 &o13, const T14 &o14, const T15 &o15)
429 {
430 UStringPrivate::Composition c(fmt);
431 c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
432 .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14).arg(o15);
433 return c.str();
434 }
435 }
438 #endif // STRING_UCOMPOSE_HPP