]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 ¶m) | |
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_LOCALE_ENABLE_CHAR16_T | |
477 | /// | |
478 | /// Definition of char16_t based format | |
479 | /// | |
480 | typedef basic_format<char16_t> u16format; | |
481 | #endif | |
482 | ||
483 | #ifdef BOOST_LOCALE_ENABLE_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 |