]> git.sesse.net Git - casparcg/blob - dependencies/boost/boost/locale/format.hpp
Manually merged pull request #222
[casparcg] / dependencies / boost / boost / locale / format.hpp
1 //
2 //  Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
3 //
4 //  Distributed under the Boost Software License, Version 1.0. (See
5 //  accompanying file LICENSE_1_0.txt or copy at
6 //  http://www.boost.org/LICENSE_1_0.txt)
7 //
8 #ifndef BOOST_LOCALE_FORMAT_HPP_INCLUDED
9 #define BOOST_LOCALE_FORMAT_HPP_INCLUDED
10
11 #include <boost/locale/config.hpp>
12 #ifdef BOOST_MSVC
13 #  pragma warning(push)
14 #  pragma warning(disable : 4275 4251 4231 4660)
15 #endif
16 #include <boost/locale/message.hpp>
17 #include <boost/locale/formatting.hpp>
18
19 #include <sstream>
20
21
22 namespace boost {
23     namespace locale {
24         
25         ///
26         /// \defgroup format Format
27         ///
28         /// This module provides printf like functionality integrated into iostreams and suitable for localization
29         ///
30         /// @{
31         ///
32
33         /// \cond INTERNAL
34         namespace details {
35
36             template<typename CharType>
37             struct formattible {
38                 typedef std::basic_ostream<CharType> stream_type;
39                 typedef void (*writer_type)(stream_type &output,void const *ptr);
40
41                 formattible() :
42                     pointer_(0),
43                     writer_(&formattible::void_write)
44                 {
45                 }
46                 
47                 formattible(formattible const &other) :
48                     pointer_(other.pointer_),
49                     writer_(other.writer_)
50                 {
51                 }
52
53                 formattible const &operator=(formattible const &other)
54                 {
55                     if(this != &other) {
56                         pointer_=other.pointer_;
57                         writer_=other.writer_;
58                     }
59                     return *this;
60                 }
61                 
62                 template<typename Type>
63                 formattible(Type const &value)
64                 {
65                     pointer_ = static_cast<void const *>(&value);
66                     writer_ = &write<Type>;
67                 }
68
69                 template<typename Type>
70                 formattible const &operator=(Type const &other)
71                 {
72                     *this = formattible(other);
73                     return *this;
74                 }
75
76                 friend stream_type &operator<<(stream_type &out,formattible const &fmt)
77                 {
78                     fmt.writer_(out,fmt.pointer_);
79                     return out;
80                 }
81
82             private:
83                 static void void_write(stream_type &output,void const * /*ptr*/)
84                 {
85                     CharType empty_string[1]={0};
86                     output<<empty_string;
87                 }
88
89                 template<typename Type>
90                 static void write(stream_type &output,void const *ptr)
91                 {
92                     output << *static_cast<Type const *>(ptr);
93                 }
94                 
95                 void const *pointer_;
96                 writer_type writer_;
97             }; // formattible
98     
99             class BOOST_LOCALE_DECL format_parser  {
100             public:
101                 format_parser(std::ios_base &ios,void *,void (*imbuer)(void *,std::locale const &));
102                 ~format_parser();
103                 
104                 unsigned get_position();
105                 
106                 void set_one_flag(std::string const &key,std::string const &value);
107
108                 template<typename CharType>
109                 void set_flag_with_str(std::string const &key,std::basic_string<CharType> const &value)
110                 {
111                     if(key=="ftime" || key=="strftime") {
112                         as::strftime(ios_);
113                         ios_info::get(ios_).date_time_pattern(value);
114                     }
115                 }
116                 void restore();
117             private:
118                 void imbue(std::locale const &);
119                 format_parser(format_parser const &);
120                 void operator=(format_parser const &);
121
122                 std::ios_base &ios_;
123                 struct data;
124                 std::auto_ptr<data> d;
125             };
126
127         }
128
129         /// \endcond
130
131         ///
132         /// \brief a printf like class that allows type-safe and locale aware message formatting
133         ///
134         /// This class creates a formatted message similar to printf or boost::format and receives
135         /// formatted entries via operator %.
136         ///
137         /// For example
138         /// \code
139         ///  cout << format("Hello {1}, you are {2} years old") % name % age << endl;
140         /// \endcode
141         ///
142         /// Formatting is enclosed between curly brackets \c { \c } and defined by a comma separated list of flags in the format key[=value]
143         /// value may also be text included between single quotes \c ' that is used for special purposes where inclusion of non-ASCII
144         /// text is allowed
145         ///
146         /// Including of literal \c { and \c } is possible by specifying double brackets \c {{ and \c }} accordingly.
147         ///
148         ///
149         /// For example:
150         ///
151         /// \code 
152         ///   cout << format("The height of water at {1,time} is {2,num=fixed,precision=3}") % time % height;
153         /// \endcode
154         ///
155         /// The special key -- a number without a value defines the position of an input parameter.
156         /// List of keys:
157         /// -   \c [0-9]+ -- digits, the index of a formatted parameter -- mandatory key.
158         /// -   \c num or \c number -- format a number. Optional values are:
159         ///     -  \c hex -- display hexadecimal number
160         ///     -  \c oct -- display in octal format
161         ///     -  \c sci or \c scientific -- display in scientific format
162         ///     -  \c fix or \c fixed -- display in fixed format
163         ///     .      
164         ///     For example \c number=sci
165         /// -  \c cur or \c currency -- format currency. Optional values are:
166         /// 
167         ///     -  \c iso -- display using ISO currency symbol.
168         ///     -  \c nat or \c national -- display using national currency symbol.
169         ///     .
170         /// -  \c per or \c percent -- format percent value.
171         /// -  \c date, \c time , \c datetime or \c dt -- format date, time or date and time. Optional values are:
172         ///     -  \c s or \c short -- display in short format
173         ///     -  \c m or \c medium -- display in medium format.
174         ///     -  \c l or \c long -- display in long format.
175         ///     -  \c f or \c full -- display in full format.
176         ///     .
177         /// -  \c ftime with string (quoted) parameter -- display as with \c strftime see, \c as::ftime manipulator
178         /// -  \c spell or \c spellout -- spell the number.
179         /// -  \c ord or \c ordinal -- format ordinal number (1st, 2nd... etc)
180         /// -  \c left or \c < -- align to left.
181         /// -  \c right or \c > -- align to right.
182         /// -  \c width or \c w -- set field width (requires parameter).
183         /// -  \c precision or \c p -- set precision (requires parameter).
184         /// -  \c locale -- with parameter -- switch locale for current operation. This command generates locale
185         ///     with formatting facets giving more fine grained control of formatting. For example:
186         ///    \code
187         ///    cout << format("Today {1,date} ({1,date,locale=he_IL.UTF-8@calendar=hebrew,date} Hebrew Date)") % date;
188         ///    \endcode
189         /// -  \c timezone or \c tz -- the name of the timezone to display the time in. For example:\n
190         ///    \code
191         ///    cout << format("Time is: Local {1,time}, ({1,time,tz=EET} Eastern European Time)") % date;
192         ///    \endcode
193         /// -  \c local - display the time in local time
194         /// -  \c gmt - display the time in UTC time scale
195         ///    \code
196         ///    cout << format("Local time is: {1,time,local}, universal time is {1,time,gmt}") % time;
197         ///    \endcode
198         /// 
199         /// 
200         /// Invalid formatting strings are slightly ignored. This would prevent from translator
201         /// to crash the program in unexpected location.
202         /// 
203         template<typename CharType>
204         class basic_format {
205         public:
206             typedef CharType char_type; ///< Underlying character type
207             typedef basic_message<char_type> message_type; ///< The translation message type
208             /// \cond INTERNAL
209             typedef details::formattible<CharType> formattible_type; 
210             /// \endcond 
211
212             typedef std::basic_string<CharType> string_type; ///< string type for this type of character
213             typedef std::basic_ostream<CharType> stream_type; ///< output stream type for this type of character
214  
215
216             ///
217             /// Create a format class for \a format_string
218             ///
219             basic_format(string_type format_string) : 
220                 format_(format_string),
221                 translate_(false),
222                 parameters_count_(0)
223             {
224             }
225             ///
226             /// Create a format class using message \a trans. The message if translated first according
227             /// to the rules of target locale and then interpreted as format string
228             ///
229             basic_format(message_type const &trans) : 
230                 message_(trans),
231                 translate_(true),
232                 parameters_count_(0)
233             {
234             }
235
236             ///
237             /// Add new parameter to format list. The object should be a type
238             /// with defined expression out << object where \c out is \c std::basic_ostream.
239             ///
240             template<typename Formattible>
241             basic_format &operator % (Formattible const &object)
242             {
243                 add(formattible_type(object));
244                 return *this;
245             }
246
247             ///
248             /// Format a string using a locale \a loc
249             ///
250             string_type str(std::locale const &loc = std::locale()) const
251             {
252                 std::basic_ostringstream<CharType> buffer;
253                 buffer.imbue(loc);
254                 write(buffer);
255                 return buffer.str();
256             }
257
258             ///
259             /// write a formatted string to output stream \a out using out's locale
260             ///
261             void write(stream_type &out) const
262             {
263                 string_type format;
264                 if(translate_)
265                     format = message_.str(out.getloc(),ios_info::get(out).domain_id());
266                 else
267                     format = format_;
268                
269                 format_output(out,format);
270
271             }
272                         
273             
274         private:
275
276             class format_guard {
277             public:
278                 format_guard(details::format_parser &fmt) : 
279                     fmt_(&fmt),
280                     restored_(false)
281                 {
282                 }
283                 void restore()
284                 {
285                     if(restored_)
286                         return;
287                     fmt_->restore();
288                     restored_ = true;
289                 }
290                 ~format_guard()
291                 {
292                     try {
293                         restore();
294                     }
295                     catch(...) {
296                     }
297                 }
298             private:
299                 details::format_parser *fmt_;
300                 bool restored_;
301             };
302             
303             void format_output(stream_type &out,string_type const &sformat) const
304             {
305                 char_type obrk='{';
306                 char_type cbrk='}';
307                 char_type eq='=';
308                 char_type comma=',';
309                 char_type quote='\'';
310
311                 size_t pos = 0;
312                 size_t size=sformat.size();
313                 CharType const *format=sformat.c_str();
314                 while(format[pos]!=0) {
315                     if(format[pos] != obrk) {
316                         if(format[pos]==cbrk && format[pos+1]==cbrk) {
317                             out << cbrk;
318                             pos+=2;
319                         }
320                         else {
321                             out<<format[pos];
322                             pos++;
323                         }
324                         continue;
325                     }
326
327                     if(pos+1 < size && format[pos+1]==obrk) {
328                         out << obrk;
329                         pos+=2;
330                         continue;
331                     }
332                     pos++;
333                   
334                     details::format_parser fmt(out,static_cast<void *>(&out),&basic_format::imbue_locale);
335
336                     format_guard guard(fmt);
337
338                     while(pos < size) { 
339                         std::string key;
340                         std::string svalue;
341                         string_type value;
342                         bool use_svalue = true;
343                         for(;format[pos];pos++) {
344                             char_type c=format[pos];
345                             if(c==comma || c==eq || c==cbrk)
346                                 break;
347                             else {
348                                 key+=static_cast<char>(c);
349                             }
350                         }
351
352                         if(format[pos]==eq) {
353                             pos++;
354                             if(format[pos]==quote) {
355                                 pos++;
356                                 use_svalue = false;
357                                 while(format[pos]) {
358                                     if(format[pos]==quote) {
359                                         if(format[pos+1]==quote) {
360                                             value+=quote;
361                                             pos+=2;
362                                         }
363                                         else {
364                                             pos++;
365                                             break;
366                                         }
367                                     }
368                                     else {
369                                         value+=format[pos];
370                                         pos++;
371                                     }
372                                 }
373                             }
374                             else {
375                                 char_type c;
376                                 while((c=format[pos])!=0 && c!=comma && c!=cbrk) {
377                                     svalue+=static_cast<char>(c);
378                                     pos++;
379                                 }
380                             }
381                         }
382
383                         if(use_svalue) {
384                             fmt.set_one_flag(key,svalue);
385                         }
386                         else 
387                             fmt.set_flag_with_str(key,value);
388                         
389                         if(format[pos]==comma) {
390                             pos++;
391                             continue;
392                         }
393                         else if(format[pos]==cbrk)  {
394                             unsigned position = fmt.get_position();
395                             out << get(position);
396                             guard.restore();
397                             pos++;
398                             break;
399                         }
400                         else {                        
401                             guard.restore();
402                             break;
403                         }
404                     }
405                 }
406             }
407
408       
409             //
410             // Non-copyable 
411             //
412             basic_format(basic_format const &other);
413             void operator=(basic_format const &other);
414
415             void add(formattible_type const &param)
416             {
417                 if(parameters_count_ >= base_params_)
418                     ext_params_.push_back(param);
419                 else 
420                     parameters_[parameters_count_] = param;
421                 parameters_count_++;
422             }
423
424             formattible_type get(unsigned id) const
425             {
426                 if(id >= parameters_count_)
427                     return formattible_type();
428                 else if(id >= base_params_)
429                     return ext_params_[id - base_params_];
430                 else
431                     return parameters_[id];
432             }
433
434             static void imbue_locale(void *ptr,std::locale const &l)
435             {
436                 reinterpret_cast<stream_type *>(ptr)->imbue(l);
437             }
438
439
440
441             static unsigned const base_params_ = 8;
442             
443             message_type message_;
444             string_type format_;
445             bool translate_;
446
447
448             formattible_type parameters_[base_params_];
449             unsigned parameters_count_;
450             std::vector<formattible_type> ext_params_;
451         };
452
453         ///
454         /// Write formatted message to stream.
455         ///
456         /// This operator actually causes actual text formatting. It uses the locale of \a out stream
457         ///
458         template<typename CharType>
459         std::basic_ostream<CharType> &operator<<(std::basic_ostream<CharType> &out,basic_format<CharType> const &fmt)
460         {
461             fmt.write(out);
462             return out;
463         }
464
465
466         ///
467         /// Definition of char based format
468         ///
469         typedef basic_format<char> format;
470
471         ///
472         /// Definition of wchar_t based format
473         ///
474         typedef basic_format<wchar_t> wformat;
475
476         #ifdef BOOST_HAS_CHAR16_T
477         ///
478         /// Definition of char16_t based format
479         ///
480         typedef basic_format<char16_t> u16format;
481         #endif
482
483         #ifdef BOOST_HAS_CHAR32_T
484         ///
485         /// Definition of char32_t based format
486         ///
487         typedef basic_format<char32_t> u32format;
488         #endif
489
490         ///
491         /// @}
492         ///
493
494     }
495 }
496
497 #ifdef BOOST_MSVC
498 #pragma warning(pop)
499 #endif
500
501 #endif
502
503 ///
504 /// \example hello.cpp
505 ///
506 /// Basic example of using various functions provided by this library
507 ///
508 /// \example whello.cpp
509 ///
510 /// Basic example of using various functions with wide strings provided by this library
511 ///
512 ///
513
514 // vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
515