231afa583e4ba0a53703ff99b2446f9ab75e97c1
[ardour.git] / libs / pbd / pbd / compose.h
1 /* Defines String::compose(fmt, arg...) for easy, i18n-friendly
2  * composition of strings.
3  *
4  * Version 1.0.
5  *
6  * Copyright (c) 2002 Ole Laursen <olau@hardworking.dk>.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; either version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21  * USA.
22  */
23
24 //
25 // Basic usage is like
26 //
27 //   std::cout << String::compose("This is a %1x%2 matrix.", rows, cols);
28 //
29 // See http://www.cs.auc.dk/~olau/compose/ or the included README.compose for
30 // more details.
31 //
32
33 #ifndef STRING_COMPOSE_H
34 #define STRING_COMPOSE_H
35
36 #include <sstream>
37 #include <string>
38 #include <list>
39 #include <map>                  // for multimap
40
41 #include "pbd/libpbd_visibility.h"
42
43 namespace StringPrivate
44 {
45   // the actual composition class - using string::compose is cleaner, so we
46   // hide it here
47   class LIBPBD_API Composition
48   {
49   public:
50     // initialize and prepare format string on the form "text %1 text %2 etc."
51     explicit Composition(std::string fmt);
52
53     // supply an replacement argument starting from %1
54     template <typename T>
55     Composition &arg(const T &obj);
56
57     // specialization to catch strings (C++ and C)
58     Composition &arg(const std::string &str);
59     Composition &arg(char const * const cstr);
60
61     // compose and return string
62     std::string str() const;
63
64   private:
65     std::ostringstream os;
66     int arg_no;
67
68     // we store the output as a list - when the output string is requested, the
69     // list is concatenated to a string; this way we can keep iterators into
70     // the list instead of into a string where they're possibly invalidated on
71     // inserting a specification string
72     typedef std::list<std::string> output_list;
73     output_list output;
74
75     // the initial parse of the format string fills in the specification map
76     // with positions for each of the various %?s
77     typedef std::multimap<int, output_list::iterator> specification_map;
78     specification_map specs;
79   };
80
81   // helper for converting spec string numbers
82   inline int char_to_int(char c)
83   {
84     switch (c) {
85     case '0': return 0;
86     case '1': return 1;
87     case '2': return 2;
88     case '3': return 3;
89     case '4': return 4;
90     case '5': return 5;
91     case '6': return 6;
92     case '7': return 7;
93     case '8': return 8;
94     case '9': return 9;
95     default: return -1000;
96     }
97   }
98
99   inline bool is_number(int n)
100   {
101     switch (n) {
102     case '0':
103     case '1':
104     case '2':
105     case '3':
106     case '4':
107     case '5':
108     case '6':
109     case '7':
110     case '8':
111     case '9':
112       return true;
113
114     default:
115       return false;
116     }
117   }
118
119
120   // implementation of class Composition
121   template <typename T>
122   inline Composition &Composition::arg(const T &obj)
123   {
124     os << obj;
125
126     std::string rep = os.str();
127
128     if (!rep.empty()) {         // manipulators don't produce output
129       for (specification_map::const_iterator i = specs.lower_bound(arg_no),
130              end = specs.upper_bound(arg_no); i != end; ++i) {
131         output_list::iterator pos = i->second;
132         ++pos;
133
134         output.insert(pos, rep);
135       }
136
137       os.str(std::string());
138       //os.clear();
139       ++arg_no;
140     }
141
142     return *this;
143   }
144
145   inline Composition &Composition::arg(const std::string &str)
146   {
147           /* specialization to ensure that empty strings show up
148            * in the output
149            */
150           for (specification_map::const_iterator i = specs.lower_bound(arg_no),
151                        end = specs.upper_bound(arg_no); i != end; ++i) {
152                   output_list::iterator pos = i->second;
153                   ++pos;
154
155                   output.insert(pos, str);
156           }
157
158           ++arg_no;
159
160           return *this;
161   }
162
163   inline Composition &Composition::arg(char const * const cstr)
164   {
165           /* specialization to ensure that empty C strings show up
166            * in the output
167            */
168           for (specification_map::const_iterator i = specs.lower_bound(arg_no),
169                        end = specs.upper_bound(arg_no); i != end; ++i) {
170                   output_list::iterator pos = i->second;
171                   ++pos;
172
173                   output.insert(pos, std::string (cstr));
174           }
175
176           ++arg_no;
177
178           return *this;
179   }
180
181   inline Composition::Composition(std::string fmt)
182     : arg_no(1)
183   {
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 std::string 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 // now for the real thing(s)
242 //namespace PBD
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 std::string string_compose(const std::string &fmt, const T1 &o1)
249   {
250     StringPrivate::Composition c(fmt);
251     c.arg(o1);
252     return c.str();
253   }
254
255   template <typename T1, typename T2>
256   inline std::string string_compose(const std::string &fmt,
257                              const T1 &o1, const T2 &o2)
258   {
259     StringPrivate::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 std::string string_compose(const std::string &fmt,
266                              const T1 &o1, const T2 &o2, const T3 &o3)
267   {
268     StringPrivate::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 std::string string_compose(const std::string &fmt,
275                              const T1 &o1, const T2 &o2, const T3 &o3,
276                              const T4 &o4)
277   {
278     StringPrivate::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 std::string string_compose(const std::string &fmt,
285                              const T1 &o1, const T2 &o2, const T3 &o3,
286                              const T4 &o4, const T5 &o5)
287   {
288     StringPrivate::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 std::string string_compose(const std::string &fmt,
296                              const T1 &o1, const T2 &o2, const T3 &o3,
297                              const T4 &o4, const T5 &o5, const T6 &o6)
298   {
299     StringPrivate::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 std::string string_compose(const std::string &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     StringPrivate::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 std::string string_compose(const std::string &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     StringPrivate::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 std::string string_compose(const std::string &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     StringPrivate::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 std::string string_compose(const std::string &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     StringPrivate::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 std::string string_compose(const std::string &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     StringPrivate::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 std::string string_compose(const std::string &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     StringPrivate::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 std::string string_compose(const std::string &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     StringPrivate::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 std::string string_compose(const std::string &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     StringPrivate::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 std::string string_compose(const std::string &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     StringPrivate::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_COMPOSE_H