Missed update to private test repo version.
[dcpomatic.git] / src / lib / compose.hpp
1 /* -*- c-basic-offset: 2 -*-
2  * Defines String::compose(fmt, arg...) for easy, i18n-friendly
3  * composition of strings.
4  *
5  * Version 1.0.
6  *
7  * Copyright (c) 2002 Ole Laursen <olau@hardworking.dk>.
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1 of
12  * the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22  * USA.
23  */
24
25 //
26 // Basic usage is like
27 //
28 //   std::cout << String::compose("This is a %1x%2 matrix.", rows, cols);
29 //
30 // See http://www.cs.aau.dk/~olau/compose/ or the included README.compose for
31 // more details.
32 //
33
34 #ifndef DCPOMATIC_STRING_COMPOSE_H
35 #define DCPOMATIC_STRING_COMPOSE_H
36
37 #include <dcp/locale_convert.h>
38 #include <boost/filesystem.hpp>
39 #include <string>
40 #include <list>
41 #include <map>
42 #include <inttypes.h>
43 #include <cstdio>
44
45 namespace StringPrivate
46 {
47   // the actual composition class - using string::compose is cleaner, so we
48   // hide it here
49   class Composition
50   {
51   public:
52     // initialize and prepare format string on the form "text %1 text %2 etc."
53     explicit Composition(std::string fmt);
54
55     // supply an replacement argument starting from %1
56     template <typename T>
57     Composition &arg(const T &obj);
58
59     // compose and return string
60     std::string str() const;
61
62   private:
63     std::string os;
64     int arg_no;
65
66     // we store the output as a list - when the output string is requested, the
67     // list is concatenated to a string; this way we can keep iterators into
68     // the list instead of into a string where they're possibly invalidated on
69     // inserting a specification string
70     typedef std::list<std::string> output_list;
71     output_list output;
72
73     // the initial parse of the format string fills in the specification map
74     // with positions for each of the various %?s
75     typedef std::multimap<int, output_list::iterator> specification_map;
76     specification_map specs;
77   };
78
79   // helper for converting spec string numbers
80   inline int char_to_int(char c)
81   {
82     switch (c) {
83     case '0': return 0;
84     case '1': return 1;
85     case '2': return 2;
86     case '3': return 3;
87     case '4': return 4;
88     case '5': return 5;
89     case '6': return 6;
90     case '7': return 7;
91     case '8': return 8;
92     case '9': return 9;
93     default: return -1000;
94     }
95   }
96
97   inline bool is_number(int n)
98   {
99     switch (n) {
100     case '0':
101     case '1':
102     case '2':
103     case '3':
104     case '4':
105     case '5':
106     case '6':
107     case '7':
108     case '8':
109     case '9':
110       return true;
111
112     default:
113       return false;
114     }
115   }
116
117   // implementation of class Composition
118   template <typename T>
119   inline Composition &Composition::arg(const T &obj)
120   {
121     os += dcp::locale_convert<std::string>(obj);
122
123     if (!os.empty()) {          // manipulators don't produce output
124       for (specification_map::const_iterator i = specs.lower_bound(arg_no), end = specs.upper_bound(arg_no); i != end; ++i) {
125         output_list::iterator pos = i->second;
126         ++pos;
127
128         output.insert(pos, os);
129       }
130
131       os = "";
132       ++arg_no;
133     }
134
135     return *this;
136   }
137
138   inline Composition::Composition(std::string fmt)
139     : arg_no(1)
140   {
141     std::string::size_type b = 0, i = 0;
142
143     // fill in output with the strings between the %1 %2 %3 etc. and
144     // fill in specs with the positions
145     while (i < fmt.length()) {
146       if (fmt[i] == '%' && i + 1 < fmt.length()) {
147         if (fmt[i + 1] == '%') {        // catch %%
148           fmt.replace(i, 2, "%");
149           ++i;
150         }
151         else if (is_number(fmt[i + 1])) { // aha! a spec!
152           // save string
153           output.push_back(fmt.substr(b, i - b));
154
155           int n = 1;            // number of digits
156           int spec_no = 0;
157
158           do {
159             spec_no += char_to_int(fmt[i + n]);
160             spec_no *= 10;
161             ++n;
162           } while (i + n < fmt.length() && is_number(fmt[i + n]));
163
164           spec_no /= 10;
165           output_list::iterator pos = output.end();
166           --pos;                // safe since we have just inserted a string>
167
168           specs.insert(specification_map::value_type(spec_no, pos));
169
170           // jump over spec string
171           i += n;
172           b = i;
173         }
174         else
175           ++i;
176       }
177       else
178         ++i;
179     }
180
181     if (i - b > 0)              // add the rest of the string
182       output.push_back(fmt.substr(b, i - b));
183   }
184
185   inline std::string Composition::str() const
186   {
187     // assemble string
188     std::string str;
189
190     for (output_list::const_iterator i = output.begin(), end = output.end();
191          i != end; ++i)
192       str += *i;
193
194     return str;
195   }
196 }
197
198 // now for the real thing(s)
199 namespace String
200 {
201   // a series of functions which accept a format string on the form "text %1
202   // more %2 less %3" and a number of templated parameters and spits out the
203   // composited string
204   template <typename T1>
205   inline std::string compose(const std::string &fmt, const T1 &o1)
206   {
207     StringPrivate::Composition c(fmt);
208     c.arg(o1);
209     return c.str();
210   }
211
212   template <typename T1, typename T2>
213   inline std::string compose(const std::string &fmt,
214                              const T1 &o1, const T2 &o2)
215   {
216     StringPrivate::Composition c(fmt);
217     c.arg(o1).arg(o2);
218     return c.str();
219   }
220
221   template <typename T1, typename T2, typename T3>
222   inline std::string compose(const std::string &fmt,
223                              const T1 &o1, const T2 &o2, const T3 &o3)
224   {
225     StringPrivate::Composition c(fmt);
226     c.arg(o1).arg(o2).arg(o3);
227     return c.str();
228   }
229
230   template <typename T1, typename T2, typename T3, typename T4>
231   inline std::string compose(const std::string &fmt,
232                              const T1 &o1, const T2 &o2, const T3 &o3,
233                              const T4 &o4)
234   {
235     StringPrivate::Composition c(fmt);
236     c.arg(o1).arg(o2).arg(o3).arg(o4);
237     return c.str();
238   }
239
240   template <typename T1, typename T2, typename T3, typename T4, typename T5>
241   inline std::string compose(const std::string &fmt,
242                              const T1 &o1, const T2 &o2, const T3 &o3,
243                              const T4 &o4, const T5 &o5)
244   {
245     StringPrivate::Composition c(fmt);
246     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5);
247     return c.str();
248   }
249
250   template <typename T1, typename T2, typename T3, typename T4, typename T5,
251             typename T6>
252   inline std::string compose(const std::string &fmt,
253                              const T1 &o1, const T2 &o2, const T3 &o3,
254                              const T4 &o4, const T5 &o5, const T6 &o6)
255   {
256     StringPrivate::Composition c(fmt);
257     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6);
258     return c.str();
259   }
260
261   template <typename T1, typename T2, typename T3, typename T4, typename T5,
262             typename T6, typename T7>
263   inline std::string compose(const std::string &fmt,
264                              const T1 &o1, const T2 &o2, const T3 &o3,
265                              const T4 &o4, const T5 &o5, const T6 &o6,
266                              const T7 &o7)
267   {
268     StringPrivate::Composition c(fmt);
269     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7);
270     return c.str();
271   }
272
273   template <typename T1, typename T2, typename T3, typename T4, typename T5,
274             typename T6, typename T7, typename T8>
275   inline std::string compose(const std::string &fmt,
276                              const T1 &o1, const T2 &o2, const T3 &o3,
277                              const T4 &o4, const T5 &o5, const T6 &o6,
278                              const T7 &o7, const T8 &o8)
279   {
280     StringPrivate::Composition c(fmt);
281     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8);
282     return c.str();
283   }
284
285   template <typename T1, typename T2, typename T3, typename T4, typename T5,
286             typename T6, typename T7, typename T8, typename T9>
287   inline std::string compose(const std::string &fmt,
288                              const T1 &o1, const T2 &o2, const T3 &o3,
289                              const T4 &o4, const T5 &o5, const T6 &o6,
290                              const T7 &o7, const T8 &o8, const T9 &o9)
291   {
292     StringPrivate::Composition c(fmt);
293     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9);
294     return c.str();
295   }
296
297   template <typename T1, typename T2, typename T3, typename T4, typename T5,
298             typename T6, typename T7, typename T8, typename T9, typename T10>
299   inline std::string compose(const std::string &fmt,
300                              const T1 &o1, const T2 &o2, const T3 &o3,
301                              const T4 &o4, const T5 &o5, const T6 &o6,
302                              const T7 &o7, const T8 &o8, const T9 &o9,
303                              const T10 &o10)
304   {
305     StringPrivate::Composition c(fmt);
306     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
307       .arg(o10);
308     return c.str();
309   }
310
311   template <typename T1, typename T2, typename T3, typename T4, typename T5,
312             typename T6, typename T7, typename T8, typename T9, typename T10,
313             typename T11>
314   inline std::string compose(const std::string &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, const T8 &o8, const T9 &o9,
318                              const T10 &o10, const T11 &o11)
319   {
320     StringPrivate::Composition c(fmt);
321     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
322       .arg(o10).arg(o11);
323     return c.str();
324   }
325
326   template <typename T1, typename T2, typename T3, typename T4, typename T5,
327             typename T6, typename T7, typename T8, typename T9, typename T10,
328             typename T11, typename T12>
329   inline std::string compose(const std::string &fmt,
330                              const T1 &o1, const T2 &o2, const T3 &o3,
331                              const T4 &o4, const T5 &o5, const T6 &o6,
332                              const T7 &o7, const T8 &o8, const T9 &o9,
333                              const T10 &o10, const T11 &o11, const T12 &o12)
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       .arg(o10).arg(o11).arg(o12);
338     return c.str();
339   }
340
341   template <typename T1, typename T2, typename T3, typename T4, typename T5,
342             typename T6, typename T7, typename T8, typename T9, typename T10,
343             typename T11, typename T12, typename T13>
344   inline std::string compose(const std::string &fmt,
345                              const T1 &o1, const T2 &o2, const T3 &o3,
346                              const T4 &o4, const T5 &o5, const T6 &o6,
347                              const T7 &o7, const T8 &o8, const T9 &o9,
348                              const T10 &o10, const T11 &o11, const T12 &o12,
349                              const T13 &o13)
350   {
351     StringPrivate::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).arg(o11).arg(o12).arg(o13);
354     return c.str();
355   }
356
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, typename T12, typename T13, typename T14>
360   inline std::string compose(const std::string &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, const T12 &o12,
365                              const T13 &o13, const T14 &o14)
366   {
367     StringPrivate::Composition c(fmt);
368     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
369       .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14);
370     return c.str();
371   }
372
373   template <typename T1, typename T2, typename T3, typename T4, typename T5,
374             typename T6, typename T7, typename T8, typename T9, typename T10,
375             typename T11, typename T12, typename T13, typename T14,
376             typename T15>
377   inline std::string compose(const std::string &fmt,
378                              const T1 &o1, const T2 &o2, const T3 &o3,
379                              const T4 &o4, const T5 &o5, const T6 &o6,
380                              const T7 &o7, const T8 &o8, const T9 &o9,
381                              const T10 &o10, const T11 &o11, const T12 &o12,
382                              const T13 &o13, const T14 &o14, const T15 &o15)
383   {
384     StringPrivate::Composition c(fmt);
385     c.arg(o1).arg(o2).arg(o3).arg(o4).arg(o5).arg(o6).arg(o7).arg(o8).arg(o9)
386       .arg(o10).arg(o11).arg(o12).arg(o13).arg(o14).arg(o15);
387     return c.str();
388   }
389 }
390
391
392 #endif // STRING_COMPOSE_H