Merge branch '1.0' of git.carlh.net:git/libdcp into 1.0
[libdcp.git] / src / compose.hpp
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.aau.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 <locked_sstream.h>
37 #include <string>
38 #include <list>
39 #include <map>                  // for multimap
40
41 namespace dcp {
42
43 namespace StringPrivate
44 {
45   // the actual composition class - using string::compose is cleaner, so we
46   // hide it here
47   class 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     // compose and return string
58     std::string str() const;
59
60   private:
61     locked_stringstream os;
62     int arg_no;
63
64     // we store the output as a list - when the output string is requested, the
65     // list is concatenated to a string; this way we can keep iterators into
66     // the list instead of into a string where they're possibly invalidated on
67     // inserting a specification string
68     typedef std::list<std::string> output_list;
69     output_list output;
70
71     // the initial parse of the format string fills in the specification map
72     // with positions for each of the various %?s
73     typedef std::multimap<int, output_list::iterator> specification_map;
74     specification_map specs;
75   };
76
77   // helper for converting spec string numbers
78   inline int char_to_int(char c)
79   {
80     switch (c) {
81     case '0': return 0;
82     case '1': return 1;
83     case '2': return 2;
84     case '3': return 3;
85     case '4': return 4;
86     case '5': return 5;
87     case '6': return 6;
88     case '7': return 7;
89     case '8': return 8;
90     case '9': return 9;
91     default: return -1000;
92     }
93   }
94
95   inline bool is_number(int n)
96   {
97     switch (n) {
98     case '0':
99     case '1':
100     case '2':
101     case '3':
102     case '4':
103     case '5':
104     case '6':
105     case '7':
106     case '8':
107     case '9':
108       return true;
109
110     default:
111       return false;
112     }
113   }
114
115
116   // implementation of class Composition
117   template <typename T>
118   inline Composition &Composition::arg(const T &obj)
119   {
120     os << obj;
121
122     std::string rep = os.str();
123
124     if (!rep.empty()) {         // manipulators don't produce output
125       for (specification_map::const_iterator i = specs.lower_bound(arg_no),
126              end = specs.upper_bound(arg_no); i != end; ++i) {
127         output_list::iterator pos = i->second;
128         ++pos;
129
130         output.insert(pos, rep);
131       }
132
133       os.str(std::string());
134       //os.clear();
135       ++arg_no;
136     }
137
138     return *this;
139   }
140
141   inline Composition::Composition(std::string fmt)
142     : arg_no(1)
143   {
144     std::string::size_type b = 0, i = 0;
145
146     // fill in output with the strings between the %1 %2 %3 etc. and
147     // fill in specs with the positions
148     while (i < fmt.length()) {
149       if (fmt[i] == '%' && i + 1 < fmt.length()) {
150         if (fmt[i + 1] == '%') {        // catch %%
151           fmt.replace(i, 2, "%");
152           ++i;
153         }
154         else if (is_number(fmt[i + 1])) { // aha! a spec!
155           // save string
156           output.push_back(fmt.substr(b, i - b));
157
158           int n = 1;            // number of digits
159           int spec_no = 0;
160
161           do {
162             spec_no += char_to_int(fmt[i + n]);
163             spec_no *= 10;
164             ++n;
165           } while (i + n < fmt.length() && is_number(fmt[i + n]));
166
167           spec_no /= 10;
168           output_list::iterator pos = output.end();
169           --pos;                // safe since we have just inserted a string>
170
171           specs.insert(specification_map::value_type(spec_no, pos));
172
173           // jump over spec string
174           i += n;
175           b = i;
176         }
177         else
178           ++i;
179       }
180       else
181         ++i;
182     }
183
184     if (i - b > 0)              // add the rest of the string
185       output.push_back(fmt.substr(b, i - b));
186   }
187
188   inline std::string Composition::str() const
189   {
190     // assemble string
191     std::string str;
192
193     for (output_list::const_iterator i = output.begin(), end = output.end();
194          i != end; ++i)
195       str += *i;
196
197     return str;
198   }
199 }
200
201 // now for the real thing(s)
202 namespace String
203 {
204   // a series of functions which accept a format string on the form "text %1
205   // more %2 less %3" and a number of templated parameters and spits out the
206   // composited string
207   template <typename T1>
208   inline std::string compose(const std::string &fmt, const T1 &o1)
209   {
210     StringPrivate::Composition c(fmt);
211     c.arg(o1);
212     return c.str();
213   }
214
215   template <typename T1, typename T2>
216   inline std::string compose(const std::string &fmt,
217                              const T1 &o1, const T2 &o2)
218   {
219     StringPrivate::Composition c(fmt);
220     c.arg(o1).arg(o2);
221     return c.str();
222   }
223
224   template <typename T1, typename T2, typename T3>
225   inline std::string compose(const std::string &fmt,
226                              const T1 &o1, const T2 &o2, const T3 &o3)
227   {
228     StringPrivate::Composition c(fmt);
229     c.arg(o1).arg(o2).arg(o3);
230     return c.str();
231   }
232
233   template <typename T1, typename T2, typename T3, typename T4>
234   inline std::string compose(const std::string &fmt,
235                              const T1 &o1, const T2 &o2, const T3 &o3,
236                              const T4 &o4)
237   {
238     StringPrivate::Composition c(fmt);
239     c.arg(o1).arg(o2).arg(o3).arg(o4);
240     return c.str();
241   }
242
243   template <typename T1, typename T2, typename T3, typename T4, typename T5>
244   inline std::string compose(const std::string &fmt,
245                              const T1 &o1, const T2 &o2, const T3 &o3,
246                              const T4 &o4, const T5 &o5)
247   {
248     StringPrivate::Composition c(fmt);
249     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5);
250     return c.str();
251   }
252
253   template <typename T1, typename T2, typename T3, typename T4, typename T5,
254             typename T6>
255   inline std::string compose(const std::string &fmt,
256                              const T1 &o1, const T2 &o2, const T3 &o3,
257                              const T4 &o4, const T5 &o5, const T6 &o6)
258   {
259     StringPrivate::Composition c(fmt);
260     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6);
261     return c.str();
262   }
263
264   template <typename T1, typename T2, typename T3, typename T4, typename T5,
265             typename T6, typename T7>
266   inline std::string compose(const std::string &fmt,
267                              const T1 &o1, const T2 &o2, const T3 &o3,
268                              const T4 &o4, const T5 &o5, const T6 &o6,
269                              const T7 &o7)
270   {
271     StringPrivate::Composition c(fmt);
272     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7);
273     return c.str();
274   }
275
276   template <typename T1, typename T2, typename T3, typename T4, typename T5,
277             typename T6, typename T7, typename T8>
278   inline std::string compose(const std::string &fmt,
279                              const T1 &o1, const T2 &o2, const T3 &o3,
280                              const T4 &o4, const T5 &o5, const T6 &o6,
281                              const T7 &o7, const T8 &o8)
282   {
283     StringPrivate::Composition c(fmt);
284     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8);
285     return c.str();
286   }
287
288   template <typename T1, typename T2, typename T3, typename T4, typename T5,
289             typename T6, typename T7, typename T8, typename T9>
290   inline std::string compose(const std::string &fmt,
291                              const T1 &o1, const T2 &o2, const T3 &o3,
292                              const T4 &o4, const T5 &o5, const T6 &o6,
293                              const T7 &o7, const T8 &o8, const T9 &o9)
294   {
295     StringPrivate::Composition c(fmt);
296     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9);
297     return c.str();
298   }
299
300   template <typename T1, typename T2, typename T3, typename T4, typename T5,
301             typename T6, typename T7, typename T8, typename T9, typename T10>
302   inline std::string compose(const std::string &fmt,
303                              const T1 &o1, const T2 &o2, const T3 &o3,
304                              const T4 &o4, const T5 &o5, const T6 &o6,
305                              const T7 &o7, const T8 &o8, const T9 &o9,
306                              const T10 &o10)
307   {
308     StringPrivate::Composition c(fmt);
309     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
310       .arg(o10);
311     return c.str();
312   }
313
314   template <typename T1, typename T2, typename T3, typename T4, typename T5,
315             typename T6, typename T7, typename T8, typename T9, typename T10,
316             typename T11>
317   inline std::string compose(const std::string &fmt,
318                              const T1 &o1, const T2 &o2, const T3 &o3,
319                              const T4 &o4, const T5 &o5, const T6 &o6,
320                              const T7 &o7, const T8 &o8, const T9 &o9,
321                              const T10 &o10, const T11 &o11)
322   {
323     StringPrivate::Composition c(fmt);
324     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
325       .arg(o10).arg(o11);
326     return c.str();
327   }
328
329   template <typename T1, typename T2, typename T3, typename T4, typename T5,
330             typename T6, typename T7, typename T8, typename T9, typename T10,
331             typename T11, typename T12>
332   inline std::string compose(const std::string &fmt,
333                              const T1 &o1, const T2 &o2, const T3 &o3,
334                              const T4 &o4, const T5 &o5, const T6 &o6,
335                              const T7 &o7, const T8 &o8, const T9 &o9,
336                              const T10 &o10, const T11 &o11, const T12 &o12)
337   {
338     StringPrivate::Composition c(fmt);
339     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
340       .arg(o10).arg(o11).arg(o12);
341     return c.str();
342   }
343
344   template <typename T1, typename T2, typename T3, typename T4, typename T5,
345             typename T6, typename T7, typename T8, typename T9, typename T10,
346             typename T11, typename T12, typename T13>
347   inline std::string compose(const std::string &fmt,
348                              const T1 &o1, const T2 &o2, const T3 &o3,
349                              const T4 &o4, const T5 &o5, const T6 &o6,
350                              const T7 &o7, const T8 &o8, const T9 &o9,
351                              const T10 &o10, const T11 &o11, const T12 &o12,
352                              const T13 &o13)
353   {
354     StringPrivate::Composition c(fmt);
355     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
356       .arg(o10).arg(o11).arg(o12).arg(o13);
357     return c.str();
358   }
359
360   template <typename T1, typename T2, typename T3, typename T4, typename T5,
361             typename T6, typename T7, typename T8, typename T9, typename T10,
362             typename T11, typename T12, typename T13, typename T14>
363   inline std::string compose(const std::string &fmt,
364                              const T1 &o1, const T2 &o2, const T3 &o3,
365                              const T4 &o4, const T5 &o5, const T6 &o6,
366                              const T7 &o7, const T8 &o8, const T9 &o9,
367                              const T10 &o10, const T11 &o11, const T12 &o12,
368                              const T13 &o13, const T14 &o14)
369   {
370     StringPrivate::Composition c(fmt);
371     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
372       .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14);
373     return c.str();
374   }
375
376   template <typename T1, typename T2, typename T3, typename T4, typename T5,
377             typename T6, typename T7, typename T8, typename T9, typename T10,
378             typename T11, typename T12, typename T13, typename T14,
379             typename T15>
380   inline std::string compose(const std::string &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                              const T13 &o13, const T14 &o14, const T15 &o15)
386   {
387     StringPrivate::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).arg(o13).arg(o14).arg(o15);
390     return c.str();
391   }
392 }
393
394 }
395
396 #endif // STRING_COMPOSE_H