| 1 | // © 2018 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 | 
| 7 |  | 
| 8 | // Allow implicit conversion from char16_t* to UnicodeString for this file: | 
| 9 | // Helpful in toString methods and elsewhere. | 
| 10 | #define UNISTR_FROM_STRING_EXPLICIT | 
| 11 |  | 
| 12 | #include "number_mapper.h" | 
| 13 | #include "number_patternstring.h" | 
| 14 | #include "unicode/errorcode.h" | 
| 15 | #include "number_utils.h" | 
| 16 |  | 
| 17 | using namespace icu; | 
| 18 | using namespace icu::number; | 
| 19 | using namespace icu::number::impl; | 
| 20 |  | 
| 21 |  | 
| 22 | UnlocalizedNumberFormatter NumberPropertyMapper::create(const DecimalFormatProperties& properties, | 
| 23 |                                                         const DecimalFormatSymbols& symbols, | 
| 24 |                                                         DecimalFormatWarehouse& warehouse, | 
| 25 |                                                         UErrorCode& status) { | 
| 26 |     return NumberFormatter::with().macros(oldToNew(properties, symbols, warehouse, nullptr, status)); | 
| 27 | } | 
| 28 |  | 
| 29 | UnlocalizedNumberFormatter NumberPropertyMapper::create(const DecimalFormatProperties& properties, | 
| 30 |                                                         const DecimalFormatSymbols& symbols, | 
| 31 |                                                         DecimalFormatWarehouse& warehouse, | 
| 32 |                                                         DecimalFormatProperties& exportedProperties, | 
| 33 |                                                         UErrorCode& status) { | 
| 34 |     return NumberFormatter::with().macros( | 
| 35 |             oldToNew( | 
| 36 |                     properties, symbols, warehouse, &exportedProperties, status)); | 
| 37 | } | 
| 38 |  | 
| 39 | MacroProps NumberPropertyMapper::oldToNew(const DecimalFormatProperties& properties, | 
| 40 |                                           const DecimalFormatSymbols& symbols, | 
| 41 |                                           DecimalFormatWarehouse& warehouse, | 
| 42 |                                           DecimalFormatProperties* exportedProperties, | 
| 43 |                                           UErrorCode& status) { | 
| 44 |     MacroProps macros; | 
| 45 |     Locale locale = symbols.getLocale(); | 
| 46 |  | 
| 47 |     ///////////// | 
| 48 |     // SYMBOLS // | 
| 49 |     ///////////// | 
| 50 |  | 
| 51 |     macros.symbols.setTo(symbols); | 
| 52 |  | 
| 53 |     ////////////////// | 
| 54 |     // PLURAL RULES // | 
| 55 |     ////////////////// | 
| 56 |  | 
| 57 |     if (!properties.currencyPluralInfo.fPtr.isNull()) { | 
| 58 |         macros.rules = properties.currencyPluralInfo.fPtr->getPluralRules(); | 
| 59 |     } | 
| 60 |  | 
| 61 |     ///////////// | 
| 62 |     // AFFIXES // | 
| 63 |     ///////////// | 
| 64 |  | 
| 65 |     warehouse.affixProvider.setTo(properties, status); | 
| 66 |     macros.affixProvider = &warehouse.affixProvider.get(); | 
| 67 |  | 
| 68 |     /////////// | 
| 69 |     // UNITS // | 
| 70 |     /////////// | 
| 71 |  | 
| 72 |     bool useCurrency = ( | 
| 73 |             !properties.currency.isNull() || | 
| 74 |             !properties.currencyPluralInfo.fPtr.isNull() || | 
| 75 |             !properties.currencyUsage.isNull() || | 
| 76 |             warehouse.affixProvider.get().hasCurrencySign()); | 
| 77 |     CurrencyUnit currency = resolveCurrency(properties, locale, status); | 
| 78 |     UCurrencyUsage currencyUsage = properties.currencyUsage.getOrDefault(UCURR_USAGE_STANDARD); | 
| 79 |     if (useCurrency) { | 
| 80 |         // NOTE: Slicing is OK. | 
| 81 |         macros.unit = currency; // NOLINT | 
| 82 |     } | 
| 83 |  | 
| 84 |     /////////////////////// | 
| 85 |     // ROUNDING STRATEGY // | 
| 86 |     /////////////////////// | 
| 87 |  | 
| 88 |     int32_t maxInt = properties.maximumIntegerDigits; | 
| 89 |     int32_t minInt = properties.minimumIntegerDigits; | 
| 90 |     int32_t maxFrac = properties.maximumFractionDigits; | 
| 91 |     int32_t minFrac = properties.minimumFractionDigits; | 
| 92 |     int32_t minSig = properties.minimumSignificantDigits; | 
| 93 |     int32_t maxSig = properties.maximumSignificantDigits; | 
| 94 |     double roundingIncrement = properties.roundingIncrement; | 
| 95 |     RoundingMode roundingMode = properties.roundingMode.getOrDefault(UNUM_ROUND_HALFEVEN); | 
| 96 |     bool explicitMinMaxFrac = minFrac != -1 || maxFrac != -1; | 
| 97 |     bool explicitMinMaxSig = minSig != -1 || maxSig != -1; | 
| 98 |     // Resolve min/max frac for currencies, required for the validation logic and for when minFrac or | 
| 99 |     // maxFrac was | 
| 100 |     // set (but not both) on a currency instance. | 
| 101 |     // NOTE: Increments are handled in "Precision.constructCurrency()". | 
| 102 |     if (useCurrency && (minFrac == -1 || maxFrac == -1)) { | 
| 103 |         int32_t digits = ucurr_getDefaultFractionDigitsForUsage( | 
| 104 |                 currency.getISOCurrency(), currencyUsage, &status); | 
| 105 |         if (minFrac == -1 && maxFrac == -1) { | 
| 106 |             minFrac = digits; | 
| 107 |             maxFrac = digits; | 
| 108 |         } else if (minFrac == -1) { | 
| 109 |             minFrac = std::min(maxFrac, digits); | 
| 110 |         } else /* if (maxFrac == -1) */ { | 
| 111 |             maxFrac = std::max(minFrac, digits); | 
| 112 |         } | 
| 113 |     } | 
| 114 |     // Validate min/max int/frac. | 
| 115 |     // For backwards compatibility, minimum overrides maximum if the two conflict. | 
| 116 |     if (minInt == 0 && maxFrac != 0) { | 
| 117 |         minFrac = (minFrac < 0 || (minFrac == 0 && maxInt == 0)) ? 1 : minFrac; | 
| 118 |         maxFrac = maxFrac < 0 ? -1 : maxFrac < minFrac ? minFrac : maxFrac; | 
| 119 |         minInt = 0; | 
| 120 |         maxInt = maxInt < 0 ? -1 : maxInt > kMaxIntFracSig ? -1 : maxInt; | 
| 121 |     } else { | 
| 122 |         // Force a digit before the decimal point. | 
| 123 |         minFrac = minFrac < 0 ? 0 : minFrac; | 
| 124 |         maxFrac = maxFrac < 0 ? -1 : maxFrac < minFrac ? minFrac : maxFrac; | 
| 125 |         minInt = minInt <= 0 ? 1 : minInt > kMaxIntFracSig ? 1 : minInt; | 
| 126 |         maxInt = maxInt < 0 ? -1 : maxInt < minInt ? minInt : maxInt > kMaxIntFracSig ? -1 : maxInt; | 
| 127 |     } | 
| 128 |     Precision precision; | 
| 129 |     if (!properties.currencyUsage.isNull()) { | 
| 130 |         precision = Precision::constructCurrency(currencyUsage).withCurrency(currency); | 
| 131 |     } else if (roundingIncrement != 0.0) { | 
| 132 |         if (PatternStringUtils::ignoreRoundingIncrement(roundingIncrement, maxFrac)) { | 
| 133 |             precision = Precision::constructFraction(minFrac, maxFrac); | 
| 134 |         } else { | 
| 135 |             precision = Precision::constructIncrement(roundingIncrement, minFrac); | 
| 136 |         } | 
| 137 |     } else if (explicitMinMaxSig) { | 
| 138 |         minSig = minSig < 1 ? 1 : minSig > kMaxIntFracSig ? kMaxIntFracSig : minSig; | 
| 139 |         maxSig = maxSig < 0 ? kMaxIntFracSig : maxSig < minSig ? minSig : maxSig > kMaxIntFracSig | 
| 140 |                                                                           ? kMaxIntFracSig : maxSig; | 
| 141 |         precision = Precision::constructSignificant(minSig, maxSig); | 
| 142 |     } else if (explicitMinMaxFrac) { | 
| 143 |         precision = Precision::constructFraction(minFrac, maxFrac); | 
| 144 |     } else if (useCurrency) { | 
| 145 |         precision = Precision::constructCurrency(currencyUsage); | 
| 146 |     } | 
| 147 |     if (!precision.isBogus()) { | 
| 148 |         precision.fRoundingMode = roundingMode; | 
| 149 |         macros.precision = precision; | 
| 150 |     } | 
| 151 |  | 
| 152 |     /////////////////// | 
| 153 |     // INTEGER WIDTH // | 
| 154 |     /////////////////// | 
| 155 |  | 
| 156 |     macros.integerWidth = IntegerWidth( | 
| 157 |             static_cast<digits_t>(minInt), | 
| 158 |             static_cast<digits_t>(maxInt), | 
| 159 |             properties.formatFailIfMoreThanMaxDigits); | 
| 160 |  | 
| 161 |     /////////////////////// | 
| 162 |     // GROUPING STRATEGY // | 
| 163 |     /////////////////////// | 
| 164 |  | 
| 165 |     macros.grouper = Grouper::forProperties(properties); | 
| 166 |  | 
| 167 |     ///////////// | 
| 168 |     // PADDING // | 
| 169 |     ///////////// | 
| 170 |  | 
| 171 |     if (properties.formatWidth > 0) { | 
| 172 |         macros.padder = Padder::forProperties(properties); | 
| 173 |     } | 
| 174 |  | 
| 175 |     /////////////////////////////// | 
| 176 |     // DECIMAL MARK ALWAYS SHOWN // | 
| 177 |     /////////////////////////////// | 
| 178 |  | 
| 179 |     macros.decimal = properties.decimalSeparatorAlwaysShown ? UNUM_DECIMAL_SEPARATOR_ALWAYS | 
| 180 |                                                             : UNUM_DECIMAL_SEPARATOR_AUTO; | 
| 181 |  | 
| 182 |     /////////////////////// | 
| 183 |     // SIGN ALWAYS SHOWN // | 
| 184 |     /////////////////////// | 
| 185 |  | 
| 186 |     macros.sign = properties.signAlwaysShown ? UNUM_SIGN_ALWAYS : UNUM_SIGN_AUTO; | 
| 187 |  | 
| 188 |     ///////////////////////// | 
| 189 |     // SCIENTIFIC NOTATION // | 
| 190 |     ///////////////////////// | 
| 191 |  | 
| 192 |     if (properties.minimumExponentDigits != -1) { | 
| 193 |         // Scientific notation is required. | 
| 194 |         // This whole section feels like a hack, but it is needed for regression tests. | 
| 195 |         // The mapping from property bag to scientific notation is nontrivial due to LDML rules. | 
| 196 |         if (maxInt > 8) { | 
| 197 |             // But #13110: The maximum of 8 digits has unknown origins and is not in the spec. | 
| 198 |             // If maxInt is greater than 8, it is set to minInt, even if minInt is greater than 8. | 
| 199 |             maxInt = minInt; | 
| 200 |             macros.integerWidth = IntegerWidth::zeroFillTo(minInt).truncateAt(maxInt); | 
| 201 |         } else if (maxInt > minInt && minInt > 1) { | 
| 202 |             // Bug #13289: if maxInt > minInt > 1, then minInt should be 1. | 
| 203 |             minInt = 1; | 
| 204 |             macros.integerWidth = IntegerWidth::zeroFillTo(minInt).truncateAt(maxInt); | 
| 205 |         } | 
| 206 |         int engineering = maxInt < 0 ? -1 : maxInt; | 
| 207 |         macros.notation = ScientificNotation( | 
| 208 |                 // Engineering interval: | 
| 209 |                 static_cast<int8_t>(engineering), | 
| 210 |                 // Enforce minimum integer digits (for patterns like "000.00E0"): | 
| 211 |                 (engineering == minInt), | 
| 212 |                 // Minimum exponent digits: | 
| 213 |                 static_cast<digits_t>(properties.minimumExponentDigits), | 
| 214 |                 // Exponent sign always shown: | 
| 215 |                 properties.exponentSignAlwaysShown ? UNUM_SIGN_ALWAYS : UNUM_SIGN_AUTO); | 
| 216 |         // Scientific notation also involves overriding the rounding mode. | 
| 217 |         // TODO: Overriding here is a bit of a hack. Should this logic go earlier? | 
| 218 |         if (macros.precision.fType == Precision::PrecisionType::RND_FRACTION) { | 
| 219 |             // For the purposes of rounding, get the original min/max int/frac, since the local | 
| 220 |             // variables have been manipulated for display purposes. | 
| 221 |             int maxInt_ = properties.maximumIntegerDigits; | 
| 222 |             int minInt_ = properties.minimumIntegerDigits; | 
| 223 |             int minFrac_ = properties.minimumFractionDigits; | 
| 224 |             int maxFrac_ = properties.maximumFractionDigits; | 
| 225 |             if (minInt_ == 0 && maxFrac_ == 0) { | 
| 226 |                 // Patterns like "#E0" and "##E0", which mean no rounding! | 
| 227 |                 macros.precision = Precision::unlimited(); | 
| 228 |             } else if (minInt_ == 0 && minFrac_ == 0) { | 
| 229 |                 // Patterns like "#.##E0" (no zeros in the mantissa), which mean round to maxFrac+1 | 
| 230 |                 macros.precision = Precision::constructSignificant(1, maxFrac_ + 1); | 
| 231 |             } else { | 
| 232 |                 int maxSig_ = minInt_ + maxFrac_; | 
| 233 |                 // Bug #20058: if maxInt_ > minInt_ > 1, then minInt_ should be 1. | 
| 234 |                 if (maxInt_ > minInt_ && minInt_ > 1) { | 
| 235 |                     minInt_ = 1; | 
| 236 |                 } | 
| 237 |                 int minSig_ = minInt_ + minFrac_; | 
| 238 |                 // To avoid regression, maxSig is not reset when minInt_ set to 1. | 
| 239 |                 // TODO: Reset maxSig_ = 1 + minFrac_ to follow the spec. | 
| 240 |                 macros.precision = Precision::constructSignificant(minSig_, maxSig_); | 
| 241 |             } | 
| 242 |             macros.precision.fRoundingMode = roundingMode; | 
| 243 |         } | 
| 244 |     } | 
| 245 |  | 
| 246 |     ////////////////////// | 
| 247 |     // COMPACT NOTATION // | 
| 248 |     ////////////////////// | 
| 249 |  | 
| 250 |     if (!properties.compactStyle.isNull()) { | 
| 251 |         if (properties.compactStyle.getNoError() == UNumberCompactStyle::UNUM_LONG) { | 
| 252 |             macros.notation = Notation::compactLong(); | 
| 253 |         } else { | 
| 254 |             macros.notation = Notation::compactShort(); | 
| 255 |         } | 
| 256 |         // Do not forward the affix provider. | 
| 257 |         macros.affixProvider = nullptr; | 
| 258 |     } | 
| 259 |  | 
| 260 |     ///////////////// | 
| 261 |     // MULTIPLIERS // | 
| 262 |     ///////////////// | 
| 263 |  | 
| 264 |     macros.scale = scaleFromProperties(properties); | 
| 265 |  | 
| 266 |     ////////////////////// | 
| 267 |     // PROPERTY EXPORTS // | 
| 268 |     ////////////////////// | 
| 269 |  | 
| 270 |     if (exportedProperties != nullptr) { | 
| 271 |  | 
| 272 |         exportedProperties->currency = currency; | 
| 273 |         exportedProperties->roundingMode = roundingMode; | 
| 274 |         exportedProperties->minimumIntegerDigits = minInt; | 
| 275 |         exportedProperties->maximumIntegerDigits = maxInt == -1 ? INT32_MAX : maxInt; | 
| 276 |  | 
| 277 |         Precision rounding_; | 
| 278 |         if (precision.fType == Precision::PrecisionType::RND_CURRENCY) { | 
| 279 |             rounding_ = precision.withCurrency(currency, status); | 
| 280 |         } else { | 
| 281 |             rounding_ = precision; | 
| 282 |         } | 
| 283 |         int minFrac_ = minFrac; | 
| 284 |         int maxFrac_ = maxFrac; | 
| 285 |         int minSig_ = minSig; | 
| 286 |         int maxSig_ = maxSig; | 
| 287 |         double increment_ = 0.0; | 
| 288 |         if (rounding_.fType == Precision::PrecisionType::RND_FRACTION) { | 
| 289 |             minFrac_ = rounding_.fUnion.fracSig.fMinFrac; | 
| 290 |             maxFrac_ = rounding_.fUnion.fracSig.fMaxFrac; | 
| 291 |         } else if (rounding_.fType == Precision::PrecisionType::RND_INCREMENT | 
| 292 |                 || rounding_.fType == Precision::PrecisionType::RND_INCREMENT_ONE | 
| 293 |                 || rounding_.fType == Precision::PrecisionType::RND_INCREMENT_FIVE) { | 
| 294 |             increment_ = rounding_.fUnion.increment.fIncrement; | 
| 295 |             minFrac_ = rounding_.fUnion.increment.fMinFrac; | 
| 296 |             maxFrac_ = rounding_.fUnion.increment.fMinFrac; | 
| 297 |         } else if (rounding_.fType == Precision::PrecisionType::RND_SIGNIFICANT) { | 
| 298 |             minSig_ = rounding_.fUnion.fracSig.fMinSig; | 
| 299 |             maxSig_ = rounding_.fUnion.fracSig.fMaxSig; | 
| 300 |         } | 
| 301 |  | 
| 302 |         exportedProperties->minimumFractionDigits = minFrac_; | 
| 303 |         exportedProperties->maximumFractionDigits = maxFrac_; | 
| 304 |         exportedProperties->minimumSignificantDigits = minSig_; | 
| 305 |         exportedProperties->maximumSignificantDigits = maxSig_; | 
| 306 |         exportedProperties->roundingIncrement = increment_; | 
| 307 |     } | 
| 308 |  | 
| 309 |     return macros; | 
| 310 | } | 
| 311 |  | 
| 312 |  | 
| 313 | void PropertiesAffixPatternProvider::setTo(const DecimalFormatProperties& properties, UErrorCode& status) { | 
| 314 |     fBogus = false; | 
| 315 |  | 
| 316 |     // There are two ways to set affixes in DecimalFormat: via the pattern string (applyPattern), and via the | 
| 317 |     // explicit setters (setPositivePrefix and friends).  The way to resolve the settings is as follows: | 
| 318 |     // | 
| 319 |     // 1) If the explicit setting is present for the field, use it. | 
| 320 |     // 2) Otherwise, follows UTS 35 rules based on the pattern string. | 
| 321 |     // | 
| 322 |     // Importantly, the explicit setters affect only the one field they override.  If you set the positive | 
| 323 |     // prefix, that should not affect the negative prefix. | 
| 324 |  | 
| 325 |     // Convenience: Extract the properties into local variables. | 
| 326 |     // Variables are named with three chars: [p/n][p/s][o/p] | 
| 327 |     // [p/n] => p for positive, n for negative | 
| 328 |     // [p/s] => p for prefix, s for suffix | 
| 329 |     // [o/p] => o for escaped custom override string, p for pattern string | 
| 330 |     UnicodeString ppo = AffixUtils::escape(properties.positivePrefix); | 
| 331 |     UnicodeString pso = AffixUtils::escape(properties.positiveSuffix); | 
| 332 |     UnicodeString npo = AffixUtils::escape(properties.negativePrefix); | 
| 333 |     UnicodeString nso = AffixUtils::escape(properties.negativeSuffix); | 
| 334 |     const UnicodeString& ppp = properties.positivePrefixPattern; | 
| 335 |     const UnicodeString& psp = properties.positiveSuffixPattern; | 
| 336 |     const UnicodeString& npp = properties.negativePrefixPattern; | 
| 337 |     const UnicodeString& nsp = properties.negativeSuffixPattern; | 
| 338 |  | 
| 339 |     if (!properties.positivePrefix.isBogus()) { | 
| 340 |         posPrefix = ppo; | 
| 341 |     } else if (!ppp.isBogus()) { | 
| 342 |         posPrefix = ppp; | 
| 343 |     } else { | 
| 344 |         // UTS 35: Default positive prefix is empty string. | 
| 345 |         posPrefix = u"" ; | 
| 346 |     } | 
| 347 |  | 
| 348 |     if (!properties.positiveSuffix.isBogus()) { | 
| 349 |         posSuffix = pso; | 
| 350 |     } else if (!psp.isBogus()) { | 
| 351 |         posSuffix = psp; | 
| 352 |     } else { | 
| 353 |         // UTS 35: Default positive suffix is empty string. | 
| 354 |         posSuffix = u"" ; | 
| 355 |     } | 
| 356 |  | 
| 357 |     if (!properties.negativePrefix.isBogus()) { | 
| 358 |         negPrefix = npo; | 
| 359 |     } else if (!npp.isBogus()) { | 
| 360 |         negPrefix = npp; | 
| 361 |     } else { | 
| 362 |         // UTS 35: Default negative prefix is "-" with positive prefix. | 
| 363 |         // Important: We prepend the "-" to the pattern, not the override! | 
| 364 |         negPrefix = ppp.isBogus() ? u"-"  : u"-"  + ppp; | 
| 365 |     } | 
| 366 |  | 
| 367 |     if (!properties.negativeSuffix.isBogus()) { | 
| 368 |         negSuffix = nso; | 
| 369 |     } else if (!nsp.isBogus()) { | 
| 370 |         negSuffix = nsp; | 
| 371 |     } else { | 
| 372 |         // UTS 35: Default negative prefix is the positive prefix. | 
| 373 |         negSuffix = psp.isBogus() ? u""  : psp; | 
| 374 |     } | 
| 375 |  | 
| 376 |     // For declaring if this is a currency pattern, we need to look at the | 
| 377 |     // original pattern, not at any user-specified overrides. | 
| 378 |     isCurrencyPattern = ( | 
| 379 |         AffixUtils::hasCurrencySymbols(ppp, status) || | 
| 380 |         AffixUtils::hasCurrencySymbols(psp, status) || | 
| 381 |         AffixUtils::hasCurrencySymbols(npp, status) || | 
| 382 |         AffixUtils::hasCurrencySymbols(nsp, status)); | 
| 383 | } | 
| 384 |  | 
| 385 | char16_t PropertiesAffixPatternProvider::charAt(int flags, int i) const { | 
| 386 |     return getStringInternal(flags).charAt(i); | 
| 387 | } | 
| 388 |  | 
| 389 | int PropertiesAffixPatternProvider::length(int flags) const { | 
| 390 |     return getStringInternal(flags).length(); | 
| 391 | } | 
| 392 |  | 
| 393 | UnicodeString PropertiesAffixPatternProvider::getString(int32_t flags) const { | 
| 394 |     return getStringInternal(flags); | 
| 395 | } | 
| 396 |  | 
| 397 | const UnicodeString& PropertiesAffixPatternProvider::getStringInternal(int32_t flags) const { | 
| 398 |     bool prefix = (flags & AFFIX_PREFIX) != 0; | 
| 399 |     bool negative = (flags & AFFIX_NEGATIVE_SUBPATTERN) != 0; | 
| 400 |     if (prefix && negative) { | 
| 401 |         return negPrefix; | 
| 402 |     } else if (prefix) { | 
| 403 |         return posPrefix; | 
| 404 |     } else if (negative) { | 
| 405 |         return negSuffix; | 
| 406 |     } else { | 
| 407 |         return posSuffix; | 
| 408 |     } | 
| 409 | } | 
| 410 |  | 
| 411 | bool PropertiesAffixPatternProvider::positiveHasPlusSign() const { | 
| 412 |     // TODO: Change the internal APIs to propagate out the error? | 
| 413 |     ErrorCode localStatus; | 
| 414 |     return AffixUtils::containsType(posPrefix, TYPE_PLUS_SIGN, localStatus) || | 
| 415 |            AffixUtils::containsType(posSuffix, TYPE_PLUS_SIGN, localStatus); | 
| 416 | } | 
| 417 |  | 
| 418 | bool PropertiesAffixPatternProvider::hasNegativeSubpattern() const { | 
| 419 |     return ( | 
| 420 |         (negSuffix != posSuffix) || | 
| 421 |         negPrefix.tempSubString(1) != posPrefix || | 
| 422 |         negPrefix.charAt(0) != u'-' | 
| 423 |     ); | 
| 424 | } | 
| 425 |  | 
| 426 | bool PropertiesAffixPatternProvider::negativeHasMinusSign() const { | 
| 427 |     ErrorCode localStatus; | 
| 428 |     return AffixUtils::containsType(negPrefix, TYPE_MINUS_SIGN, localStatus) || | 
| 429 |            AffixUtils::containsType(negSuffix, TYPE_MINUS_SIGN, localStatus); | 
| 430 | } | 
| 431 |  | 
| 432 | bool PropertiesAffixPatternProvider::hasCurrencySign() const { | 
| 433 |     return isCurrencyPattern; | 
| 434 | } | 
| 435 |  | 
| 436 | bool PropertiesAffixPatternProvider::containsSymbolType(AffixPatternType type, UErrorCode& status) const { | 
| 437 |     return AffixUtils::containsType(posPrefix, type, status) || | 
| 438 |            AffixUtils::containsType(posSuffix, type, status) || | 
| 439 |            AffixUtils::containsType(negPrefix, type, status) || | 
| 440 |            AffixUtils::containsType(negSuffix, type, status); | 
| 441 | } | 
| 442 |  | 
| 443 | bool PropertiesAffixPatternProvider::hasBody() const { | 
| 444 |     return true; | 
| 445 | } | 
| 446 |  | 
| 447 |  | 
| 448 | void CurrencyPluralInfoAffixProvider::setTo(const CurrencyPluralInfo& cpi, | 
| 449 |                                             const DecimalFormatProperties& properties, | 
| 450 |                                             UErrorCode& status) { | 
| 451 |     // We need to use a PropertiesAffixPatternProvider, not the simpler version ParsedPatternInfo, | 
| 452 |     // because user-specified affix overrides still need to work. | 
| 453 |     fBogus = false; | 
| 454 |     DecimalFormatProperties pluralProperties(properties); | 
| 455 |     for (int32_t plural = 0; plural < StandardPlural::COUNT; plural++) { | 
| 456 |         const char* keyword = StandardPlural::getKeyword(static_cast<StandardPlural::Form>(plural)); | 
| 457 |         UnicodeString patternString; | 
| 458 |         patternString = cpi.getCurrencyPluralPattern(keyword, patternString); | 
| 459 |         PatternParser::parseToExistingProperties( | 
| 460 |                 patternString, | 
| 461 |                 pluralProperties, | 
| 462 |                 IGNORE_ROUNDING_NEVER, | 
| 463 |                 status); | 
| 464 |         affixesByPlural[plural].setTo(pluralProperties, status); | 
| 465 |     } | 
| 466 | } | 
| 467 |  | 
| 468 | char16_t CurrencyPluralInfoAffixProvider::charAt(int32_t flags, int32_t i) const { | 
| 469 |     int32_t pluralOrdinal = (flags & AFFIX_PLURAL_MASK); | 
| 470 |     return affixesByPlural[pluralOrdinal].charAt(flags, i); | 
| 471 | } | 
| 472 |  | 
| 473 | int32_t CurrencyPluralInfoAffixProvider::length(int32_t flags) const { | 
| 474 |     int32_t pluralOrdinal = (flags & AFFIX_PLURAL_MASK); | 
| 475 |     return affixesByPlural[pluralOrdinal].length(flags); | 
| 476 | } | 
| 477 |  | 
| 478 | UnicodeString CurrencyPluralInfoAffixProvider::getString(int32_t flags) const { | 
| 479 |     int32_t pluralOrdinal = (flags & AFFIX_PLURAL_MASK); | 
| 480 |     return affixesByPlural[pluralOrdinal].getString(flags); | 
| 481 | } | 
| 482 |  | 
| 483 | bool CurrencyPluralInfoAffixProvider::positiveHasPlusSign() const { | 
| 484 |     return affixesByPlural[StandardPlural::OTHER].positiveHasPlusSign(); | 
| 485 | } | 
| 486 |  | 
| 487 | bool CurrencyPluralInfoAffixProvider::hasNegativeSubpattern() const { | 
| 488 |     return affixesByPlural[StandardPlural::OTHER].hasNegativeSubpattern(); | 
| 489 | } | 
| 490 |  | 
| 491 | bool CurrencyPluralInfoAffixProvider::negativeHasMinusSign() const { | 
| 492 |     return affixesByPlural[StandardPlural::OTHER].negativeHasMinusSign(); | 
| 493 | } | 
| 494 |  | 
| 495 | bool CurrencyPluralInfoAffixProvider::hasCurrencySign() const { | 
| 496 |     return affixesByPlural[StandardPlural::OTHER].hasCurrencySign(); | 
| 497 | } | 
| 498 |  | 
| 499 | bool CurrencyPluralInfoAffixProvider::containsSymbolType(AffixPatternType type, UErrorCode& status) const { | 
| 500 |     return affixesByPlural[StandardPlural::OTHER].containsSymbolType(type, status); | 
| 501 | } | 
| 502 |  | 
| 503 | bool CurrencyPluralInfoAffixProvider::hasBody() const { | 
| 504 |     return affixesByPlural[StandardPlural::OTHER].hasBody(); | 
| 505 | } | 
| 506 |  | 
| 507 |  | 
| 508 | #endif /* #if !UCONFIG_NO_FORMATTING */ | 
| 509 |  |