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