| // |
| // Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) |
| // |
| // Distributed under the Boost Software License, Version 1.0. (See |
| // accompanying file LICENSE_1_0.txt or copy at |
| // http://www.boost.org/LICENSE_1_0.txt) |
| // |
| #define BOOST_LOCALE_SOURCE |
| #include <boost/locale/formatting.hpp> |
| #include "formatter.hpp" |
| #include <boost/locale/info.hpp> |
| #include "uconv.hpp" |
| |
| |
| #include <unicode/numfmt.h> |
| #include <unicode/rbnf.h> |
| #include <unicode/datefmt.h> |
| #include <unicode/smpdtfmt.h> |
| #include <unicode/decimfmt.h> |
| |
| #include <limits> |
| |
| #include <iostream> |
| |
| #include "predefined_formatters.hpp" |
| #include "time_zone.hpp" |
| |
| #ifdef BOOST_MSVC |
| # pragma warning(disable : 4244) // lose data |
| #endif |
| |
| |
| namespace boost { |
| namespace locale { |
| namespace impl_icu { |
| |
| |
| std::locale::id icu_formatters_cache::id; |
| |
| namespace { |
| struct init { init() { std::has_facet<icu_formatters_cache>(std::locale::classic()); } } instance; |
| } |
| |
| |
| template<typename CharType> |
| class number_format : public formatter<CharType> { |
| public: |
| typedef CharType char_type; |
| typedef std::basic_string<CharType> string_type; |
| |
| virtual string_type format(double value,size_t &code_points) const |
| { |
| icu::UnicodeString tmp; |
| icu_fmt_->format(value,tmp); |
| code_points=tmp.countChar32(); |
| return cvt_.std(tmp); |
| } |
| virtual string_type format(int64_t value,size_t &code_points) const |
| { |
| icu::UnicodeString tmp; |
| icu_fmt_->format(value,tmp); |
| code_points=tmp.countChar32(); |
| return cvt_.std(tmp); |
| } |
| |
| virtual string_type format(int32_t value,size_t &code_points) const |
| { |
| icu::UnicodeString tmp; |
| #ifdef __SUNPRO_CC |
| icu_fmt_->format(static_cast<int>(value),tmp); |
| #else |
| icu_fmt_->format(::int32_t(value),tmp); |
| #endif |
| code_points=tmp.countChar32(); |
| return cvt_.std(tmp); |
| } |
| |
| virtual size_t parse(string_type const &str,double &value) const |
| { |
| return do_parse(str,value); |
| } |
| |
| virtual size_t parse(string_type const &str,int64_t &value) const |
| { |
| return do_parse(str,value); |
| } |
| virtual size_t parse(string_type const &str,int32_t &value) const |
| { |
| return do_parse(str,value); |
| } |
| |
| number_format(icu::NumberFormat *fmt,std::string codepage) : |
| cvt_(codepage), |
| icu_fmt_(fmt) |
| { |
| } |
| |
| private: |
| |
| bool get_value(double &v,icu::Formattable &fmt) const |
| { |
| UErrorCode err=U_ZERO_ERROR; |
| v=fmt.getDouble(err); |
| if(U_FAILURE(err)) |
| return false; |
| return true; |
| } |
| |
| bool get_value(int64_t &v,icu::Formattable &fmt) const |
| { |
| UErrorCode err=U_ZERO_ERROR; |
| v=fmt.getInt64(err); |
| if(U_FAILURE(err)) |
| return false; |
| return true; |
| } |
| |
| bool get_value(int32_t &v,icu::Formattable &fmt) const |
| { |
| UErrorCode err=U_ZERO_ERROR; |
| v=fmt.getLong(err); |
| if(U_FAILURE(err)) |
| return false; |
| return true; |
| } |
| |
| template<typename ValueType> |
| size_t do_parse(string_type const &str,ValueType &v) const |
| { |
| icu::Formattable val; |
| icu::ParsePosition pp; |
| icu::UnicodeString tmp = cvt_.icu(str.data(),str.data()+str.size()); |
| |
| icu_fmt_->parse(tmp,val,pp); |
| |
| ValueType tmp_v; |
| |
| if(pp.getIndex() == 0 || !get_value(tmp_v,val)) |
| return 0; |
| size_t cut = cvt_.cut(tmp,str.data(),str.data()+str.size(),pp.getIndex()); |
| if(cut==0) |
| return 0; |
| v=tmp_v; |
| return cut; |
| } |
| |
| icu_std_converter<CharType> cvt_; |
| icu::NumberFormat *icu_fmt_; |
| }; |
| |
| |
| template<typename CharType> |
| class date_format : public formatter<CharType> { |
| public: |
| typedef CharType char_type; |
| typedef std::basic_string<CharType> string_type; |
| |
| virtual string_type format(double value,size_t &code_points) const |
| { |
| return do_format(value,code_points); |
| } |
| virtual string_type format(int64_t value,size_t &code_points) const |
| { |
| return do_format(value,code_points); |
| } |
| |
| virtual string_type format(int32_t value,size_t &code_points) const |
| { |
| return do_format(value,code_points); |
| } |
| |
| virtual size_t parse(string_type const &str,double &value) const |
| { |
| return do_parse(str,value); |
| } |
| virtual size_t parse(string_type const &str,int64_t &value) const |
| { |
| return do_parse(str,value); |
| } |
| virtual size_t parse(string_type const &str,int32_t &value) const |
| { |
| return do_parse(str,value); |
| } |
| |
| date_format(std::auto_ptr<icu::DateFormat> fmt,std::string codepage) : |
| cvt_(codepage), |
| aicu_fmt_(fmt) |
| { |
| icu_fmt_ = aicu_fmt_.get(); |
| } |
| date_format(icu::DateFormat *fmt,std::string codepage) : |
| cvt_(codepage), |
| icu_fmt_(fmt) |
| { |
| } |
| |
| private: |
| |
| template<typename ValueType> |
| size_t do_parse(string_type const &str,ValueType &value) const |
| { |
| icu::ParsePosition pp; |
| icu::UnicodeString tmp = cvt_.icu(str.data(),str.data() + str.size()); |
| |
| UDate udate = icu_fmt_->parse(tmp,pp); |
| if(pp.getIndex() == 0) |
| return 0; |
| double date = udate / 1000.0; |
| typedef std::numeric_limits<ValueType> limits_type; |
| if(date > limits_type::max() || date < limits_type::min()) |
| return 0; |
| size_t cut = cvt_.cut(tmp,str.data(),str.data()+str.size(),pp.getIndex()); |
| if(cut==0) |
| return 0; |
| value=static_cast<ValueType>(date); |
| return cut; |
| |
| } |
| |
| string_type do_format(double value,size_t &codepoints) const |
| { |
| UDate date = value * 1000.0; /// UDate is time_t in miliseconds |
| icu::UnicodeString tmp; |
| icu_fmt_->format(date,tmp); |
| codepoints=tmp.countChar32(); |
| return cvt_.std(tmp); |
| } |
| |
| icu_std_converter<CharType> cvt_; |
| std::auto_ptr<icu::DateFormat> aicu_fmt_; |
| icu::DateFormat *icu_fmt_; |
| }; |
| |
| icu::UnicodeString strftime_to_icu_full(icu::DateFormat *dfin,char const *alt) |
| { |
| std::auto_ptr<icu::DateFormat> df(dfin); |
| icu::SimpleDateFormat *sdf=dynamic_cast<icu::SimpleDateFormat *>(df.get()); |
| icu::UnicodeString tmp; |
| if(sdf) { |
| sdf->toPattern(tmp); |
| } |
| else { |
| tmp=alt; |
| } |
| return tmp; |
| |
| } |
| |
| icu::UnicodeString strftime_to_icu_symbol(char c,icu::Locale const &locale,icu_formatters_cache const *cache=0) |
| { |
| switch(c) { |
| case 'a': // Abbr Weekday |
| return "EE"; |
| case 'A': // Full Weekday |
| return "EEEE"; |
| case 'b': // Abbr Month |
| return "MMM"; |
| case 'B': // Full Month |
| return "MMMM"; |
| case 'c': // DateTile Full |
| { |
| if(cache) |
| return cache->date_time_format_[1][1]; |
| return strftime_to_icu_full( |
| icu::DateFormat::createDateTimeInstance(icu::DateFormat::kFull,icu::DateFormat::kFull,locale), |
| "YYYY-MM-dd HH:mm:ss" |
| ); |
| } |
| // not supported by ICU ;( |
| // case 'C': // Century -> 1980 -> 19 |
| // retur |
| case 'd': // Day of Month [01,31] |
| return "dd"; |
| case 'D': // %m/%d/%y |
| return "MM/dd/YY"; |
| case 'e': // Day of Month [1,31] |
| return "d"; |
| case 'h': // == b |
| return "MMM"; |
| case 'H': // 24 clock hour 00,23 |
| return "HH"; |
| case 'I': // 12 clock hour 01,12 |
| return "hh"; |
| case 'j': // day of year 001,366 |
| return "D"; |
| case 'm': // month as [01,12] |
| return "MM"; |
| case 'M': // minute [00,59] |
| return "mm"; |
| case 'n': // \n |
| return "\n"; |
| case 'p': // am-pm |
| return "a"; |
| case 'r': // time with AM/PM %I:%M:%S %p |
| return "hh:mm:ss a"; |
| case 'R': // %H:%M |
| return "HH:mm"; |
| case 'S': // second [00,61] |
| return "ss"; |
| case 't': // \t |
| return "\t"; |
| case 'T': // %H:%M:%S |
| return "HH:mm:ss"; |
| /* case 'u': // weekday 1,7 1=Monday |
| case 'U': // week number of year [00,53] Sunday first |
| case 'V': // week number of year [01,53] Moday first |
| case 'w': // weekday 0,7 0=Sunday |
| case 'W': // week number of year [00,53] Moday first, */ |
| case 'x': // Date |
| { |
| if(cache) |
| return cache->date_format_[1]; |
| return strftime_to_icu_full( |
| icu::DateFormat::createDateInstance(icu::DateFormat::kMedium,locale), |
| "YYYY-MM-dd" |
| ); |
| } |
| case 'X': // Time |
| { |
| if(cache) |
| return cache->time_format_[1]; |
| return strftime_to_icu_full( |
| icu::DateFormat::createTimeInstance(icu::DateFormat::kMedium,locale), |
| "HH:mm:ss" |
| ); |
| } |
| case 'y': // Year [00-99] |
| return "YY"; |
| case 'Y': // Year 1998 |
| return "YYYY"; |
| case 'Z': // timezone |
| return "vvvv"; |
| case '%': // % |
| return "%"; |
| default: |
| return ""; |
| } |
| } |
| |
| icu::UnicodeString strftime_to_icu(icu::UnicodeString const &ftime,icu::Locale const &locale) |
| { |
| unsigned len=ftime.length(); |
| icu::UnicodeString result; |
| bool escaped=false; |
| for(unsigned i=0;i<len;i++) { |
| UChar c=ftime[i]; |
| if(c=='%') { |
| i++; |
| c=ftime[i]; |
| if(c=='E' || c=='O') { |
| i++; |
| c=ftime[i]; |
| } |
| if(escaped) { |
| result+="'"; |
| escaped=false; |
| } |
| result+=strftime_to_icu_symbol(c,locale); |
| } |
| else if(c=='\'') { |
| result+="''"; |
| } |
| else { |
| if(!escaped) { |
| result+="'"; |
| escaped=true; |
| } |
| result+=c; |
| } |
| } |
| if(escaped) |
| result+="'"; |
| return result; |
| } |
| |
| template<typename CharType> |
| std::auto_ptr<formatter<CharType> > generate_formatter( |
| std::ios_base &ios, |
| icu::Locale const &locale, |
| std::string const &encoding) |
| { |
| using namespace boost::locale::flags; |
| |
| std::auto_ptr<formatter<CharType> > fmt; |
| ios_info &info=ios_info::get(ios); |
| uint64_t disp = info.display_flags(); |
| |
| icu_formatters_cache const &cache = std::use_facet<icu_formatters_cache>(ios.getloc()); |
| |
| |
| if(disp == posix) |
| return fmt; |
| |
| UErrorCode err=U_ZERO_ERROR; |
| |
| switch(disp) { |
| case number: |
| { |
| std::ios_base::fmtflags how = (ios.flags() & std::ios_base::floatfield); |
| icu::NumberFormat *nf = 0; |
| |
| if(how == std::ios_base::scientific) |
| nf = cache.number_format(icu_formatters_cache::fmt_sci); |
| else |
| nf = cache.number_format(icu_formatters_cache::fmt_number); |
| |
| nf->setMaximumFractionDigits(ios.precision()); |
| if(how == std::ios_base::scientific || how == std::ios_base::fixed ) { |
| nf->setMinimumFractionDigits(ios.precision()); |
| } |
| else { |
| nf->setMinimumFractionDigits(0); |
| } |
| fmt.reset(new number_format<CharType>(nf,encoding)); |
| } |
| break; |
| case currency: |
| { |
| icu::NumberFormat *nf; |
| |
| uint64_t curr = info.currency_flags(); |
| |
| if(curr == currency_default || curr == currency_national) |
| nf = cache.number_format(icu_formatters_cache::fmt_curr_nat); |
| else |
| nf = cache.number_format(icu_formatters_cache::fmt_curr_iso); |
| |
| fmt.reset(new number_format<CharType>(nf,encoding)); |
| } |
| break; |
| case percent: |
| { |
| icu::NumberFormat *nf = cache.number_format(icu_formatters_cache::fmt_per); |
| nf->setMaximumFractionDigits(ios.precision()); |
| std::ios_base::fmtflags how = (ios.flags() & std::ios_base::floatfield); |
| if(how == std::ios_base::scientific || how == std::ios_base::fixed ) { |
| nf->setMinimumFractionDigits(ios.precision()); |
| } |
| else { |
| nf->setMinimumFractionDigits(0); |
| } |
| fmt.reset(new number_format<CharType>(nf,encoding)); |
| |
| } |
| break; |
| case spellout: |
| fmt.reset(new number_format<CharType>(cache.number_format(icu_formatters_cache::fmt_spell),encoding)); |
| break; |
| case ordinal: |
| fmt.reset(new number_format<CharType>(cache.number_format(icu_formatters_cache::fmt_ord),encoding)); |
| break; |
| case date: |
| case time: |
| case datetime: |
| case strftime: |
| { |
| using namespace flags; |
| std::auto_ptr<icu::DateFormat> adf; |
| icu::DateFormat *df = 0; |
| icu::SimpleDateFormat *sdf = cache.date_formatter(); |
| // try to use cached first |
| if(sdf) { |
| int tmf=info.time_flags(); |
| switch(tmf) { |
| case time_short: |
| tmf=0; |
| break; |
| case time_long: |
| tmf=2; |
| break; |
| case time_full: |
| tmf=3; |
| break; |
| case time_default: |
| case time_medium: |
| default: |
| tmf=1; |
| } |
| int dtf=info.date_flags(); |
| switch(dtf) { |
| case date_short: |
| dtf=0; |
| break; |
| case date_long: |
| dtf=2; |
| break; |
| case date_full: |
| dtf=3; |
| break; |
| case date_default: |
| case date_medium: |
| default: |
| dtf=1; |
| } |
| |
| icu::UnicodeString pattern; |
| switch(disp) { |
| case date: |
| pattern = cache.date_format_[dtf]; |
| break; |
| case time: |
| pattern = cache.time_format_[tmf]; |
| break; |
| case datetime: |
| pattern = cache.date_time_format_[dtf][tmf]; |
| break; |
| case strftime: |
| { |
| if( !cache.date_format_[1].isEmpty() |
| && !cache.time_format_[1].isEmpty() |
| && !cache.date_time_format_[1][1].isEmpty()) |
| { |
| icu_std_converter<CharType> cvt_(encoding); |
| std::basic_string<CharType> const &f=info.date_time_pattern<CharType>(); |
| pattern = strftime_to_icu(cvt_.icu(f.c_str(),f.c_str()+f.size()),locale); |
| } |
| } |
| break; |
| } |
| if(!pattern.isEmpty()) { |
| sdf->applyPattern(pattern); |
| df = sdf; |
| sdf = 0; |
| } |
| sdf = 0; |
| } |
| |
| if(!df) { |
| icu::DateFormat::EStyle dstyle = icu::DateFormat::kDefault; |
| icu::DateFormat::EStyle tstyle = icu::DateFormat::kDefault; |
| |
| switch(info.time_flags()) { |
| case time_short: tstyle=icu::DateFormat::kShort; break; |
| case time_medium: tstyle=icu::DateFormat::kMedium; break; |
| case time_long: tstyle=icu::DateFormat::kLong; break; |
| case time_full: tstyle=icu::DateFormat::kFull; break; |
| } |
| switch(info.date_flags()) { |
| case date_short: dstyle=icu::DateFormat::kShort; break; |
| case date_medium: dstyle=icu::DateFormat::kMedium; break; |
| case date_long: dstyle=icu::DateFormat::kLong; break; |
| case date_full: dstyle=icu::DateFormat::kFull; break; |
| } |
| |
| if(disp==date) |
| adf.reset(icu::DateFormat::createDateInstance(dstyle,locale)); |
| else if(disp==time) |
| adf.reset(icu::DateFormat::createTimeInstance(tstyle,locale)); |
| else if(disp==datetime) |
| adf.reset(icu::DateFormat::createDateTimeInstance(dstyle,tstyle,locale)); |
| else {// strftime |
| icu_std_converter<CharType> cvt_(encoding); |
| std::basic_string<CharType> const &f=info.date_time_pattern<CharType>(); |
| icu::UnicodeString fmt = strftime_to_icu(cvt_.icu(f.data(),f.data()+f.size()),locale); |
| adf.reset(new icu::SimpleDateFormat(fmt,locale,err)); |
| } |
| if(U_FAILURE(err)) |
| return fmt; |
| df = adf.get(); |
| } |
| |
| df->adoptTimeZone(get_time_zone(info.time_zone())); |
| |
| // Depending if we own formatter or not |
| if(adf.get()) |
| fmt.reset(new date_format<CharType>(adf,encoding)); |
| else |
| fmt.reset(new date_format<CharType>(df,encoding)); |
| } |
| break; |
| } |
| |
| return fmt; |
| } |
| |
| |
| |
| template<> |
| std::auto_ptr<formatter<char> > formatter<char>::create(std::ios_base &ios,icu::Locale const &l,std::string const &e) |
| { |
| return generate_formatter<char>(ios,l,e); |
| } |
| |
| template<> |
| std::auto_ptr<formatter<wchar_t> > formatter<wchar_t>::create(std::ios_base &ios,icu::Locale const &l,std::string const &e) |
| { |
| return generate_formatter<wchar_t>(ios,l,e); |
| } |
| |
| |
| #ifdef BOOST_HAS_CHAR16_T |
| template<> |
| std::auto_ptr<formatter<char16_t> > formatter<char16_t>::create(std::ios_base &ios,icu::Locale const &l,std::string const &e) |
| { |
| return generate_formatter<char16_t>(ios,l,e); |
| } |
| |
| #endif |
| |
| #ifdef BOOST_HAS_CHAR32_T |
| template<> |
| std::auto_ptr<formatter<char32_t> > formatter<char32_t>::create(std::ios_base &ios,icu::Locale const &l,std::string const &e) |
| { |
| return generate_formatter<char32_t>(ios,l,e); |
| } |
| |
| #endif |
| |
| } // impl_icu |
| |
| } // locale |
| } // boost |
| |
| |
| // vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 |
| |
| |
| |