Home | History | Annotate | Download | only in i18n
      1 //  2017 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html
      3 
      4 #include "unicode/utypes.h"
      5 
      6 #if !UCONFIG_NO_FORMATTING && !UPRV_INCOMPLETE_CPP11_SUPPORT
      7 
      8 #include "uassert.h"
      9 #include "unicode/numberformatter.h"
     10 #include "number_decimalquantity.h"
     11 #include "number_formatimpl.h"
     12 #include "umutex.h"
     13 
     14 using namespace icu;
     15 using namespace icu::number;
     16 using namespace icu::number::impl;
     17 
     18 template<typename Derived>
     19 Derived NumberFormatterSettings<Derived>::notation(const Notation &notation) const {
     20     Derived copy(*this);
     21     // NOTE: Slicing is OK.
     22     copy.fMacros.notation = notation;
     23     return copy;
     24 }
     25 
     26 template<typename Derived>
     27 Derived NumberFormatterSettings<Derived>::unit(const icu::MeasureUnit &unit) const {
     28     Derived copy(*this);
     29     // NOTE: Slicing occurs here. However, CurrencyUnit can be restored from MeasureUnit.
     30     // TimeUnit may be affected, but TimeUnit is not as relevant to number formatting.
     31     copy.fMacros.unit = unit;
     32     return copy;
     33 }
     34 
     35 template<typename Derived>
     36 Derived NumberFormatterSettings<Derived>::adoptUnit(const icu::MeasureUnit *unit) const {
     37     Derived copy(*this);
     38     // Just copy the unit into the MacroProps by value, and delete it since we have ownership.
     39     // NOTE: Slicing occurs here. However, CurrencyUnit can be restored from MeasureUnit.
     40     // TimeUnit may be affected, but TimeUnit is not as relevant to number formatting.
     41     if (unit != nullptr) {
     42         copy.fMacros.unit = *unit;
     43         delete unit;
     44     }
     45     return copy;
     46 }
     47 
     48 template<typename Derived>
     49 Derived NumberFormatterSettings<Derived>::rounding(const Rounder &rounder) const {
     50     Derived copy(*this);
     51     // NOTE: Slicing is OK.
     52     copy.fMacros.rounder = rounder;
     53     return copy;
     54 }
     55 
     56 template<typename Derived>
     57 Derived NumberFormatterSettings<Derived>::grouping(const Grouper &grouper) const {
     58     Derived copy(*this);
     59     copy.fMacros.grouper = grouper;
     60     return copy;
     61 }
     62 
     63 template<typename Derived>
     64 Derived NumberFormatterSettings<Derived>::integerWidth(const IntegerWidth &style) const {
     65     Derived copy(*this);
     66     copy.fMacros.integerWidth = style;
     67     return copy;
     68 }
     69 
     70 template<typename Derived>
     71 Derived NumberFormatterSettings<Derived>::symbols(const DecimalFormatSymbols &symbols) const {
     72     Derived copy(*this);
     73     copy.fMacros.symbols.setTo(symbols);
     74     return copy;
     75 }
     76 
     77 template<typename Derived>
     78 Derived NumberFormatterSettings<Derived>::adoptSymbols(const NumberingSystem *ns) const {
     79     Derived copy(*this);
     80     copy.fMacros.symbols.setTo(ns);
     81     return copy;
     82 }
     83 
     84 template<typename Derived>
     85 Derived NumberFormatterSettings<Derived>::unitWidth(const UNumberUnitWidth &width) const {
     86     Derived copy(*this);
     87     copy.fMacros.unitWidth = width;
     88     return copy;
     89 }
     90 
     91 template<typename Derived>
     92 Derived NumberFormatterSettings<Derived>::sign(const UNumberSignDisplay &style) const {
     93     Derived copy(*this);
     94     copy.fMacros.sign = style;
     95     return copy;
     96 }
     97 
     98 template<typename Derived>
     99 Derived NumberFormatterSettings<Derived>::decimal(const UNumberDecimalSeparatorDisplay &style) const {
    100     Derived copy(*this);
    101     copy.fMacros.decimal = style;
    102     return copy;
    103 }
    104 
    105 template<typename Derived>
    106 Derived NumberFormatterSettings<Derived>::padding(const Padder &padder) const {
    107     Derived copy(*this);
    108     copy.fMacros.padder = padder;
    109     return copy;
    110 }
    111 
    112 template<typename Derived>
    113 Derived NumberFormatterSettings<Derived>::threshold(int32_t threshold) const {
    114     Derived copy(*this);
    115     copy.fMacros.threshold = threshold;
    116     return copy;
    117 }
    118 
    119 // Declare all classes that implement NumberFormatterSettings
    120 // See https://stackoverflow.com/a/495056/1407170
    121 template
    122 class icu::number::NumberFormatterSettings<icu::number::UnlocalizedNumberFormatter>;
    123 template
    124 class icu::number::NumberFormatterSettings<icu::number::LocalizedNumberFormatter>;
    125 
    126 
    127 UnlocalizedNumberFormatter NumberFormatter::with() {
    128     UnlocalizedNumberFormatter result;
    129     return result;
    130 }
    131 
    132 LocalizedNumberFormatter NumberFormatter::withLocale(const Locale &locale) {
    133     return with().locale(locale);
    134 }
    135 
    136 // Make the child class constructor that takes the parent class call the parent class's copy constructor
    137 UnlocalizedNumberFormatter::UnlocalizedNumberFormatter(
    138         const NumberFormatterSettings <UnlocalizedNumberFormatter> &other)
    139         : NumberFormatterSettings<UnlocalizedNumberFormatter>(other) {
    140 }
    141 
    142 // Make the child class constructor that takes the parent class call the parent class's copy constructor
    143 // For LocalizedNumberFormatter, also copy over the extra fields
    144 LocalizedNumberFormatter::LocalizedNumberFormatter(
    145         const NumberFormatterSettings <LocalizedNumberFormatter> &other)
    146         : NumberFormatterSettings<LocalizedNumberFormatter>(other) {
    147     // No additional copies required
    148 }
    149 
    150 LocalizedNumberFormatter::LocalizedNumberFormatter(const MacroProps &macros, const Locale &locale) {
    151     fMacros = macros;
    152     fMacros.locale = locale;
    153 }
    154 
    155 LocalizedNumberFormatter UnlocalizedNumberFormatter::locale(const Locale &locale) const {
    156     return LocalizedNumberFormatter(fMacros, locale);
    157 }
    158 
    159 SymbolsWrapper::SymbolsWrapper(const SymbolsWrapper &other) {
    160     doCopyFrom(other);
    161 }
    162 
    163 SymbolsWrapper &SymbolsWrapper::operator=(const SymbolsWrapper &other) {
    164     if (this == &other) {
    165         return *this;
    166     }
    167     doCleanup();
    168     doCopyFrom(other);
    169     return *this;
    170 }
    171 
    172 SymbolsWrapper::~SymbolsWrapper() {
    173     doCleanup();
    174 }
    175 
    176 void SymbolsWrapper::setTo(const DecimalFormatSymbols &dfs) {
    177     doCleanup();
    178     fType = SYMPTR_DFS;
    179     fPtr.dfs = new DecimalFormatSymbols(dfs);
    180 }
    181 
    182 void SymbolsWrapper::setTo(const NumberingSystem *ns) {
    183     doCleanup();
    184     fType = SYMPTR_NS;
    185     fPtr.ns = ns;
    186 }
    187 
    188 void SymbolsWrapper::doCopyFrom(const SymbolsWrapper &other) {
    189     fType = other.fType;
    190     switch (fType) {
    191         case SYMPTR_NONE:
    192             // No action necessary
    193             break;
    194         case SYMPTR_DFS:
    195             // Memory allocation failures are exposed in copyErrorTo()
    196             if (other.fPtr.dfs != nullptr) {
    197                 fPtr.dfs = new DecimalFormatSymbols(*other.fPtr.dfs);
    198             } else {
    199                 fPtr.dfs = nullptr;
    200             }
    201             break;
    202         case SYMPTR_NS:
    203             // Memory allocation failures are exposed in copyErrorTo()
    204             if (other.fPtr.ns != nullptr) {
    205                 fPtr.ns = new NumberingSystem(*other.fPtr.ns);
    206             } else {
    207                 fPtr.ns = nullptr;
    208             }
    209             break;
    210     }
    211 }
    212 
    213 void SymbolsWrapper::doCleanup() {
    214     switch (fType) {
    215         case SYMPTR_NONE:
    216             // No action necessary
    217             break;
    218         case SYMPTR_DFS:
    219             delete fPtr.dfs;
    220             break;
    221         case SYMPTR_NS:
    222             delete fPtr.ns;
    223             break;
    224     }
    225 }
    226 
    227 bool SymbolsWrapper::isDecimalFormatSymbols() const {
    228     return fType == SYMPTR_DFS;
    229 }
    230 
    231 bool SymbolsWrapper::isNumberingSystem() const {
    232     return fType == SYMPTR_NS;
    233 }
    234 
    235 const DecimalFormatSymbols* SymbolsWrapper::getDecimalFormatSymbols() const {
    236     U_ASSERT(fType == SYMPTR_DFS);
    237     return fPtr.dfs;
    238 }
    239 
    240 const NumberingSystem* SymbolsWrapper::getNumberingSystem() const {
    241     U_ASSERT(fType == SYMPTR_NS);
    242     return fPtr.ns;
    243 }
    244 
    245 LocalizedNumberFormatter::~LocalizedNumberFormatter() {
    246     delete fCompiled;
    247 }
    248 
    249 FormattedNumber LocalizedNumberFormatter::formatInt(int64_t value, UErrorCode &status) const {
    250     if (U_FAILURE(status)) { return FormattedNumber(U_ILLEGAL_ARGUMENT_ERROR); }
    251     auto results = new NumberFormatterResults();
    252     if (results == nullptr) {
    253         status = U_MEMORY_ALLOCATION_ERROR;
    254         return FormattedNumber(status);
    255     }
    256     results->quantity.setToLong(value);
    257     return formatImpl(results, status);
    258 }
    259 
    260 FormattedNumber LocalizedNumberFormatter::formatDouble(double value, UErrorCode &status) const {
    261     if (U_FAILURE(status)) { return FormattedNumber(U_ILLEGAL_ARGUMENT_ERROR); }
    262     auto results = new NumberFormatterResults();
    263     if (results == nullptr) {
    264         status = U_MEMORY_ALLOCATION_ERROR;
    265         return FormattedNumber(status);
    266     }
    267     results->quantity.setToDouble(value);
    268     return formatImpl(results, status);
    269 }
    270 
    271 FormattedNumber LocalizedNumberFormatter::formatDecimal(StringPiece value, UErrorCode &status) const {
    272     if (U_FAILURE(status)) { return FormattedNumber(U_ILLEGAL_ARGUMENT_ERROR); }
    273     auto results = new NumberFormatterResults();
    274     if (results == nullptr) {
    275         status = U_MEMORY_ALLOCATION_ERROR;
    276         return FormattedNumber(status);
    277     }
    278     results->quantity.setToDecNumber(value);
    279     return formatImpl(results, status);
    280 }
    281 
    282 FormattedNumber
    283 LocalizedNumberFormatter::formatImpl(impl::NumberFormatterResults *results, UErrorCode &status) const {
    284     // fUnsafeCallCount contains memory to be interpreted as an atomic int, most commonly
    285     // std::atomic<int32_t>.  Since the type of atomic int is platform-dependent, we cast the
    286     // bytes in fUnsafeCallCount to u_atomic_int32_t, a typedef for the platform-dependent
    287     // atomic int type defined in umutex.h.
    288     static_assert(sizeof(u_atomic_int32_t) <= sizeof(fUnsafeCallCount),
    289         "Atomic integer size on this platform exceeds the size allocated by fUnsafeCallCount");
    290     u_atomic_int32_t* callCount = reinterpret_cast<u_atomic_int32_t*>(
    291         const_cast<LocalizedNumberFormatter*>(this)->fUnsafeCallCount);
    292 
    293     // A positive value in the atomic int indicates that the data structure is not yet ready;
    294     // a negative value indicates that it is ready. If, after the increment, the atomic int
    295     // is exactly threshold, then it is the current thread's job to build the data structure.
    296     // Note: We set the callCount to INT32_MIN so that if another thread proceeds to increment
    297     // the atomic int, the value remains below zero.
    298     int32_t currentCount = umtx_loadAcquire(*callCount);
    299     if (0 <= currentCount && currentCount <= fMacros.threshold && fMacros.threshold > 0) {
    300         currentCount = umtx_atomic_inc(callCount);
    301     }
    302 
    303     if (currentCount == fMacros.threshold && fMacros.threshold > 0) {
    304         // Build the data structure and then use it (slow to fast path).
    305         const NumberFormatterImpl* compiled =
    306             NumberFormatterImpl::fromMacros(fMacros, status);
    307         U_ASSERT(fCompiled == nullptr);
    308         const_cast<LocalizedNumberFormatter *>(this)->fCompiled = compiled;
    309         umtx_storeRelease(*callCount, INT32_MIN);
    310         compiled->apply(results->quantity, results->string, status);
    311     } else if (currentCount < 0) {
    312         // The data structure is already built; use it (fast path).
    313         U_ASSERT(fCompiled != nullptr);
    314         fCompiled->apply(results->quantity, results->string, status);
    315     } else {
    316         // Format the number without building the data structure (slow path).
    317         NumberFormatterImpl::applyStatic(fMacros, results->quantity, results->string, status);
    318     }
    319 
    320     // Do not save the results object if we encountered a failure.
    321     if (U_SUCCESS(status)) {
    322         return FormattedNumber(results);
    323     } else {
    324         delete results;
    325         return FormattedNumber(status);
    326     }
    327 }
    328 
    329 UnicodeString FormattedNumber::toString() const {
    330     if (fResults == nullptr) {
    331         // TODO: http://bugs.icu-project.org/trac/ticket/13437
    332         return {};
    333     }
    334     return fResults->string.toUnicodeString();
    335 }
    336 
    337 Appendable &FormattedNumber::appendTo(Appendable &appendable) {
    338     if (fResults == nullptr) {
    339         // TODO: http://bugs.icu-project.org/trac/ticket/13437
    340         return appendable;
    341     }
    342     appendable.appendString(fResults->string.chars(), fResults->string.length());
    343     return appendable;
    344 }
    345 
    346 void FormattedNumber::populateFieldPosition(FieldPosition &fieldPosition, UErrorCode &status) {
    347     if (U_FAILURE(status)) { return; }
    348     if (fResults == nullptr) {
    349         status = fErrorCode;
    350         return;
    351     }
    352     fResults->string.populateFieldPosition(fieldPosition, 0, status);
    353 }
    354 
    355 void
    356 FormattedNumber::populateFieldPositionIterator(FieldPositionIterator &iterator, UErrorCode &status) {
    357     if (U_FAILURE(status)) { return; }
    358     if (fResults == nullptr) {
    359         status = fErrorCode;
    360         return;
    361     }
    362     fResults->string.populateFieldPositionIterator(iterator, status);
    363 }
    364 
    365 FormattedNumber::~FormattedNumber() {
    366     delete fResults;
    367 }
    368 
    369 #endif /* #if !UCONFIG_NO_FORMATTING */
    370