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 #define BOOST_LOCALE_SOURCE
9 #include <boost/locale/formatting.hpp>
10 #include "formatter.hpp"
11 #include <boost/locale/info.hpp>
12 #include "uconv.hpp"
13 
14 
15 #include <unicode/numfmt.h>
16 #include <unicode/rbnf.h>
17 #include <unicode/datefmt.h>
18 #include <unicode/smpdtfmt.h>
19 #include <unicode/decimfmt.h>
20 
21 #include <limits>
22 
23 #include <iostream>
24 
25 #include "predefined_formatters.hpp"
26 #include "time_zone.hpp"
27 
28 #ifdef BOOST_MSVC
29 #  pragma warning(disable : 4244) // lose data
30 #endif
31 
32 
33 namespace boost {
34 namespace locale {
35     namespace impl_icu {
36 
37 
38         std::locale::id icu_formatters_cache::id;
39 
40         namespace {
initboost::locale::impl_icu::__anonea4a1c6c0111::init41             struct init { init() { std::has_facet<icu_formatters_cache>(std::locale::classic()); } } instance;
42         }
43 
44 
45         template<typename CharType>
46         class number_format : public formatter<CharType> {
47         public:
48             typedef CharType char_type;
49             typedef std::basic_string<CharType> string_type;
50 
format(double value,size_t & code_points) const51             virtual string_type format(double value,size_t &code_points) const
52             {
53                 icu::UnicodeString tmp;
54                 icu_fmt_->format(value,tmp);
55                 code_points=tmp.countChar32();
56                 return cvt_.std(tmp);
57             }
format(int64_t value,size_t & code_points) const58             virtual string_type format(int64_t value,size_t &code_points) const
59             {
60                 icu::UnicodeString tmp;
61                 icu_fmt_->format(value,tmp);
62                 code_points=tmp.countChar32();
63                 return cvt_.std(tmp);
64             }
65 
format(int32_t value,size_t & code_points) const66             virtual string_type format(int32_t value,size_t &code_points) const
67             {
68                 icu::UnicodeString tmp;
69                 #ifdef __SUNPRO_CC
70                 icu_fmt_->format(static_cast<int>(value),tmp);
71                 #else
72                 icu_fmt_->format(::int32_t(value),tmp);
73                 #endif
74                 code_points=tmp.countChar32();
75                 return cvt_.std(tmp);
76             }
77 
parse(string_type const & str,double & value) const78             virtual size_t parse(string_type const &str,double &value) const
79             {
80                 return do_parse(str,value);
81             }
82 
parse(string_type const & str,int64_t & value) const83             virtual size_t parse(string_type const &str,int64_t &value) const
84             {
85                 return do_parse(str,value);
86             }
parse(string_type const & str,int32_t & value) const87             virtual size_t parse(string_type const &str,int32_t &value) const
88             {
89                 return do_parse(str,value);
90             }
91 
number_format(icu::NumberFormat * fmt,std::string codepage)92             number_format(icu::NumberFormat *fmt,std::string codepage) :
93                 cvt_(codepage),
94                 icu_fmt_(fmt)
95             {
96             }
97 
98         private:
99 
get_value(double & v,icu::Formattable & fmt) const100             bool get_value(double &v,icu::Formattable &fmt) const
101             {
102                 UErrorCode err=U_ZERO_ERROR;
103                 v=fmt.getDouble(err);
104                 if(U_FAILURE(err))
105                     return false;
106                 return true;
107             }
108 
get_value(int64_t & v,icu::Formattable & fmt) const109             bool get_value(int64_t &v,icu::Formattable &fmt) const
110             {
111                 UErrorCode err=U_ZERO_ERROR;
112                 v=fmt.getInt64(err);
113                 if(U_FAILURE(err))
114                     return false;
115                 return true;
116             }
117 
get_value(int32_t & v,icu::Formattable & fmt) const118             bool get_value(int32_t &v,icu::Formattable &fmt) const
119             {
120                 UErrorCode err=U_ZERO_ERROR;
121                 v=fmt.getLong(err);
122                 if(U_FAILURE(err))
123                     return false;
124                 return true;
125             }
126 
127             template<typename ValueType>
do_parse(string_type const & str,ValueType & v) const128             size_t do_parse(string_type const &str,ValueType &v) const
129             {
130                 icu::Formattable val;
131                 icu::ParsePosition pp;
132                 icu::UnicodeString tmp = cvt_.icu(str.data(),str.data()+str.size());
133 
134                 icu_fmt_->parse(tmp,val,pp);
135 
136                 ValueType tmp_v;
137 
138                 if(pp.getIndex() == 0 || !get_value(tmp_v,val))
139                     return 0;
140                 size_t cut = cvt_.cut(tmp,str.data(),str.data()+str.size(),pp.getIndex());
141                 if(cut==0)
142                     return 0;
143                 v=tmp_v;
144                 return cut;
145             }
146 
147             icu_std_converter<CharType> cvt_;
148             icu::NumberFormat *icu_fmt_;
149         };
150 
151 
152         template<typename CharType>
153         class date_format : public formatter<CharType> {
154         public:
155             typedef CharType char_type;
156             typedef std::basic_string<CharType> string_type;
157 
format(double value,size_t & code_points) const158             virtual string_type format(double value,size_t &code_points) const
159             {
160                 return do_format(value,code_points);
161             }
format(int64_t value,size_t & code_points) const162             virtual string_type format(int64_t value,size_t &code_points) const
163             {
164                 return do_format(value,code_points);
165             }
166 
format(int32_t value,size_t & code_points) const167             virtual string_type format(int32_t value,size_t &code_points) const
168             {
169                 return do_format(value,code_points);
170             }
171 
parse(string_type const & str,double & value) const172             virtual size_t parse(string_type const &str,double &value) const
173             {
174                 return do_parse(str,value);
175             }
parse(string_type const & str,int64_t & value) const176             virtual size_t parse(string_type const &str,int64_t &value) const
177             {
178                 return do_parse(str,value);
179             }
parse(string_type const & str,int32_t & value) const180             virtual size_t parse(string_type const &str,int32_t &value) const
181             {
182                 return do_parse(str,value);
183             }
184 
date_format(std::auto_ptr<icu::DateFormat> fmt,std::string codepage)185             date_format(std::auto_ptr<icu::DateFormat> fmt,std::string codepage) :
186                 cvt_(codepage),
187                 aicu_fmt_(fmt)
188             {
189                 icu_fmt_ = aicu_fmt_.get();
190             }
date_format(icu::DateFormat * fmt,std::string codepage)191             date_format(icu::DateFormat *fmt,std::string codepage) :
192                 cvt_(codepage),
193                 icu_fmt_(fmt)
194             {
195             }
196 
197         private:
198 
199             template<typename ValueType>
do_parse(string_type const & str,ValueType & value) const200             size_t do_parse(string_type const &str,ValueType &value) const
201             {
202                 icu::ParsePosition pp;
203                 icu::UnicodeString tmp = cvt_.icu(str.data(),str.data() + str.size());
204 
205                 UDate udate = icu_fmt_->parse(tmp,pp);
206                 if(pp.getIndex() == 0)
207                     return 0;
208                 double date = udate / 1000.0;
209                 typedef std::numeric_limits<ValueType> limits_type;
210                 if(date > limits_type::max() || date < limits_type::min())
211                     return 0;
212                 size_t cut = cvt_.cut(tmp,str.data(),str.data()+str.size(),pp.getIndex());
213                 if(cut==0)
214                     return 0;
215                 value=static_cast<ValueType>(date);
216                 return cut;
217 
218             }
219 
do_format(double value,size_t & codepoints) const220             string_type do_format(double value,size_t &codepoints) const
221             {
222                 UDate date = value * 1000.0; /// UDate is time_t in miliseconds
223                 icu::UnicodeString tmp;
224                 icu_fmt_->format(date,tmp);
225                 codepoints=tmp.countChar32();
226                 return cvt_.std(tmp);
227             }
228 
229             icu_std_converter<CharType> cvt_;
230             std::auto_ptr<icu::DateFormat> aicu_fmt_;
231             icu::DateFormat *icu_fmt_;
232         };
233 
strftime_to_icu_full(icu::DateFormat * dfin,char const * alt)234         icu::UnicodeString strftime_to_icu_full(icu::DateFormat *dfin,char const *alt)
235         {
236             std::auto_ptr<icu::DateFormat> df(dfin);
237             icu::SimpleDateFormat *sdf=dynamic_cast<icu::SimpleDateFormat *>(df.get());
238             icu::UnicodeString tmp;
239             if(sdf) {
240                 sdf->toPattern(tmp);
241             }
242             else {
243                 tmp=alt;
244             }
245             return tmp;
246 
247         }
248 
strftime_to_icu_symbol(char c,icu::Locale const & locale,icu_formatters_cache const * cache=0)249         icu::UnicodeString strftime_to_icu_symbol(char c,icu::Locale const &locale,icu_formatters_cache const *cache=0)
250         {
251             switch(c) {
252             case 'a': // Abbr Weekday
253                 return "EE";
254             case 'A': // Full Weekday
255                 return "EEEE";
256             case 'b': // Abbr Month
257                 return "MMM";
258             case 'B': // Full Month
259                 return "MMMM";
260             case 'c': // DateTile Full
261                 {
262                     if(cache)
263                         return cache->date_time_format_[1][1];
264                     return strftime_to_icu_full(
265                         icu::DateFormat::createDateTimeInstance(icu::DateFormat::kFull,icu::DateFormat::kFull,locale),
266                         "YYYY-MM-dd HH:mm:ss"
267                     );
268                 }
269             // not supported by ICU ;(
270             //  case 'C': // Century -> 1980 -> 19
271             //  retur
272             case 'd': // Day of Month [01,31]
273                 return "dd";
274             case 'D': // %m/%d/%y
275                 return "MM/dd/YY";
276             case 'e': // Day of Month [1,31]
277                 return "d";
278             case 'h': // == b
279                 return "MMM";
280             case 'H': // 24 clock hour 00,23
281                 return "HH";
282             case 'I': // 12 clock hour 01,12
283                 return "hh";
284             case 'j': // day of year 001,366
285                 return "D";
286             case 'm': // month as [01,12]
287                 return "MM";
288             case 'M': // minute [00,59]
289                 return "mm";
290             case 'n': // \n
291                 return "\n";
292             case 'p': // am-pm
293                 return "a";
294             case 'r': // time with AM/PM %I:%M:%S %p
295                 return "hh:mm:ss a";
296             case 'R': // %H:%M
297                 return "HH:mm";
298             case 'S': // second [00,61]
299                 return "ss";
300             case 't': // \t
301                 return "\t";
302             case 'T': // %H:%M:%S
303                 return "HH:mm:ss";
304 /*          case 'u': // weekday 1,7 1=Monday
305             case 'U': // week number of year [00,53] Sunday first
306             case 'V': // week number of year [01,53] Moday first
307             case 'w': // weekday 0,7 0=Sunday
308             case 'W': // week number of year [00,53] Moday first, */
309             case 'x': // Date
310                 {
311                     if(cache)
312                         return cache->date_format_[1];
313                     return strftime_to_icu_full(
314                         icu::DateFormat::createDateInstance(icu::DateFormat::kMedium,locale),
315                         "YYYY-MM-dd"
316                     );
317                 }
318             case 'X': // Time
319                 {
320                     if(cache)
321                         return cache->time_format_[1];
322                     return strftime_to_icu_full(
323                         icu::DateFormat::createTimeInstance(icu::DateFormat::kMedium,locale),
324                         "HH:mm:ss"
325                     );
326                 }
327             case 'y': // Year [00-99]
328                 return "YY";
329             case 'Y': // Year 1998
330                 return "YYYY";
331             case 'Z': // timezone
332                 return "vvvv";
333             case '%': // %
334                 return "%";
335             default:
336                 return "";
337             }
338         }
339 
strftime_to_icu(icu::UnicodeString const & ftime,icu::Locale const & locale)340         icu::UnicodeString strftime_to_icu(icu::UnicodeString const &ftime,icu::Locale const &locale)
341         {
342             unsigned len=ftime.length();
343             icu::UnicodeString result;
344             bool escaped=false;
345             for(unsigned i=0;i<len;i++) {
346                 UChar c=ftime[i];
347                 if(c=='%') {
348                     i++;
349                     c=ftime[i];
350                     if(c=='E' || c=='O') {
351                         i++;
352                         c=ftime[i];
353                     }
354                     if(escaped) {
355                         result+="'";
356                         escaped=false;
357                     }
358                     result+=strftime_to_icu_symbol(c,locale);
359                 }
360                 else if(c=='\'') {
361                         result+="''";
362                 }
363                 else {
364                     if(!escaped) {
365                         result+="'";
366                         escaped=true;
367                     }
368                     result+=c;
369                 }
370             }
371             if(escaped)
372                 result+="'";
373             return result;
374         }
375 
376         template<typename CharType>
generate_formatter(std::ios_base & ios,icu::Locale const & locale,std::string const & encoding)377         std::auto_ptr<formatter<CharType> > generate_formatter(
378                     std::ios_base &ios,
379                     icu::Locale const &locale,
380                     std::string const &encoding)
381         {
382             using namespace boost::locale::flags;
383 
384             std::auto_ptr<formatter<CharType> > fmt;
385             ios_info &info=ios_info::get(ios);
386             uint64_t disp = info.display_flags();
387 
388             icu_formatters_cache const &cache = std::use_facet<icu_formatters_cache>(ios.getloc());
389 
390 
391             if(disp == posix)
392                 return fmt;
393 
394             UErrorCode err=U_ZERO_ERROR;
395 
396             switch(disp) {
397             case number:
398                 {
399                     std::ios_base::fmtflags how = (ios.flags() & std::ios_base::floatfield);
400                     icu::NumberFormat *nf = 0;
401 
402                     if(how == std::ios_base::scientific)
403                         nf = cache.number_format(icu_formatters_cache::fmt_sci);
404                     else
405                         nf = cache.number_format(icu_formatters_cache::fmt_number);
406 
407                     nf->setMaximumFractionDigits(ios.precision());
408                     if(how == std::ios_base::scientific || how == std::ios_base::fixed ) {
409                         nf->setMinimumFractionDigits(ios.precision());
410                     }
411                     else {
412                         nf->setMinimumFractionDigits(0);
413                     }
414                     fmt.reset(new number_format<CharType>(nf,encoding));
415                 }
416                 break;
417             case currency:
418                 {
419                     icu::NumberFormat *nf;
420 
421                     uint64_t curr = info.currency_flags();
422 
423                     if(curr == currency_default || curr == currency_national)
424                         nf = cache.number_format(icu_formatters_cache::fmt_curr_nat);
425                     else
426                         nf = cache.number_format(icu_formatters_cache::fmt_curr_iso);
427 
428                     fmt.reset(new number_format<CharType>(nf,encoding));
429                 }
430                 break;
431             case percent:
432                 {
433                     icu::NumberFormat *nf = cache.number_format(icu_formatters_cache::fmt_per);
434                     nf->setMaximumFractionDigits(ios.precision());
435                     std::ios_base::fmtflags how = (ios.flags() & std::ios_base::floatfield);
436                     if(how == std::ios_base::scientific || how == std::ios_base::fixed ) {
437                         nf->setMinimumFractionDigits(ios.precision());
438                     }
439                     else {
440                         nf->setMinimumFractionDigits(0);
441                     }
442                     fmt.reset(new number_format<CharType>(nf,encoding));
443 
444                 }
445                 break;
446             case spellout:
447                 fmt.reset(new number_format<CharType>(cache.number_format(icu_formatters_cache::fmt_spell),encoding));
448                 break;
449             case ordinal:
450                 fmt.reset(new number_format<CharType>(cache.number_format(icu_formatters_cache::fmt_ord),encoding));
451                 break;
452             case date:
453             case time:
454             case datetime:
455             case strftime:
456                 {
457                     using namespace flags;
458                     std::auto_ptr<icu::DateFormat> adf;
459                     icu::DateFormat *df = 0;
460                     icu::SimpleDateFormat *sdf = cache.date_formatter();
461                     // try to use cached first
462                     if(sdf) {
463                         int tmf=info.time_flags();
464                         switch(tmf) {
465                         case time_short:
466                             tmf=0;
467                             break;
468                         case time_long:
469                             tmf=2;
470                             break;
471                         case time_full:
472                             tmf=3;
473                             break;
474                         case time_default:
475                         case time_medium:
476                         default:
477                             tmf=1;
478                         }
479                         int dtf=info.date_flags();
480                         switch(dtf) {
481                         case date_short:
482                             dtf=0;
483                             break;
484                         case date_long:
485                             dtf=2;
486                             break;
487                         case date_full:
488                             dtf=3;
489                             break;
490                         case date_default:
491                         case date_medium:
492                         default:
493                             dtf=1;
494                         }
495 
496                         icu::UnicodeString pattern;
497                         switch(disp) {
498                         case date:
499                             pattern = cache.date_format_[dtf];
500                             break;
501                         case time:
502                             pattern = cache.time_format_[tmf];
503                             break;
504                         case datetime:
505                             pattern = cache.date_time_format_[dtf][tmf];
506                             break;
507                         case strftime:
508                             {
509                                 if( !cache.date_format_[1].isEmpty()
510                                     && !cache.time_format_[1].isEmpty()
511                                     && !cache.date_time_format_[1][1].isEmpty())
512                                 {
513                                     icu_std_converter<CharType> cvt_(encoding);
514                                     std::basic_string<CharType> const &f=info.date_time_pattern<CharType>();
515                                     pattern = strftime_to_icu(cvt_.icu(f.c_str(),f.c_str()+f.size()),locale);
516                                 }
517                             }
518                             break;
519                         }
520                         if(!pattern.isEmpty()) {
521                             sdf->applyPattern(pattern);
522                             df = sdf;
523                             sdf = 0;
524                         }
525                         sdf = 0;
526                     }
527 
528                     if(!df) {
529                         icu::DateFormat::EStyle dstyle = icu::DateFormat::kDefault;
530                         icu::DateFormat::EStyle tstyle = icu::DateFormat::kDefault;
531 
532                         switch(info.time_flags()) {
533                         case time_short:    tstyle=icu::DateFormat::kShort; break;
534                         case time_medium:   tstyle=icu::DateFormat::kMedium; break;
535                         case time_long:     tstyle=icu::DateFormat::kLong; break;
536                         case time_full:     tstyle=icu::DateFormat::kFull; break;
537                         }
538                         switch(info.date_flags()) {
539                         case date_short:    dstyle=icu::DateFormat::kShort; break;
540                         case date_medium:   dstyle=icu::DateFormat::kMedium; break;
541                         case date_long:     dstyle=icu::DateFormat::kLong; break;
542                         case date_full:     dstyle=icu::DateFormat::kFull; break;
543                         }
544 
545                         if(disp==date)
546                             adf.reset(icu::DateFormat::createDateInstance(dstyle,locale));
547                         else if(disp==time)
548                             adf.reset(icu::DateFormat::createTimeInstance(tstyle,locale));
549                         else if(disp==datetime)
550                             adf.reset(icu::DateFormat::createDateTimeInstance(dstyle,tstyle,locale));
551                         else {// strftime
552                             icu_std_converter<CharType> cvt_(encoding);
553                             std::basic_string<CharType> const &f=info.date_time_pattern<CharType>();
554                             icu::UnicodeString fmt = strftime_to_icu(cvt_.icu(f.data(),f.data()+f.size()),locale);
555                             adf.reset(new icu::SimpleDateFormat(fmt,locale,err));
556                         }
557                         if(U_FAILURE(err))
558                             return fmt;
559                         df = adf.get();
560                     }
561 
562                     df->adoptTimeZone(get_time_zone(info.time_zone()));
563 
564                     // Depending if we own formatter or not
565                     if(adf.get())
566                         fmt.reset(new date_format<CharType>(adf,encoding));
567                     else
568                         fmt.reset(new date_format<CharType>(df,encoding));
569                 }
570                 break;
571             }
572 
573             return fmt;
574         }
575 
576 
577 
578     template<>
create(std::ios_base & ios,icu::Locale const & l,std::string const & e)579     std::auto_ptr<formatter<char> > formatter<char>::create(std::ios_base &ios,icu::Locale const &l,std::string const &e)
580     {
581         return generate_formatter<char>(ios,l,e);
582     }
583 
584     template<>
create(std::ios_base & ios,icu::Locale const & l,std::string const & e)585     std::auto_ptr<formatter<wchar_t> > formatter<wchar_t>::create(std::ios_base &ios,icu::Locale const &l,std::string const &e)
586     {
587         return generate_formatter<wchar_t>(ios,l,e);
588     }
589 
590 
591     #ifdef BOOST_HAS_CHAR16_T
592     template<>
create(std::ios_base & ios,icu::Locale const & l,std::string const & e)593     std::auto_ptr<formatter<char16_t> > formatter<char16_t>::create(std::ios_base &ios,icu::Locale const &l,std::string const &e)
594     {
595         return generate_formatter<char16_t>(ios,l,e);
596     }
597 
598     #endif
599 
600     #ifdef BOOST_HAS_CHAR32_T
601     template<>
create(std::ios_base & ios,icu::Locale const & l,std::string const & e)602     std::auto_ptr<formatter<char32_t> > formatter<char32_t>::create(std::ios_base &ios,icu::Locale const &l,std::string const &e)
603     {
604         return generate_formatter<char32_t>(ios,l,e);
605     }
606 
607     #endif
608 
609 } // impl_icu
610 
611 } // locale
612 } // boost
613 
614 
615 // vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
616 
617 
618 
619