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::__anona4d9ec770111::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(static_cast< ::int64_t>(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(icu::DateFormat * fmt,bool transfer_owneship,std::string codepage)185 date_format(icu::DateFormat *fmt,bool transfer_owneship,std::string codepage) : 186 cvt_(codepage) 187 { 188 if(transfer_owneship) { 189 aicu_fmt_.reset(fmt); 190 icu_fmt_ = aicu_fmt_.get(); 191 } 192 else { 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 hold_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 hold_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 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 hold_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.release(); 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 hold_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.release(); 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.release(),true,encoding)); 567 else 568 fmt.reset(new date_format<CharType>(df,false,encoding)); 569 } 570 break; 571 } 572 573 return fmt.release(); 574 } 575 576 577 578 template<> create(std::ios_base & ios,icu::Locale const & l,std::string const & e)579 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 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_LOCALE_ENABLE_CHAR16_T 592 template<> create(std::ios_base & ios,icu::Locale const & l,std::string const & e)593 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_LOCALE_ENABLE_CHAR32_T 601 template<> create(std::ios_base & ios,icu::Locale const & l,std::string const & e)602 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