]> git.saurik.com Git - apple/icu.git/blobdiff - icuSources/i18n/number_patternstring.cpp
ICU-64243.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / number_patternstring.cpp
index 63195eed989e54f4f9a1888c75e7d85143f247d2..c7201a6997921faf725c429166cc53405bc61a3c 100644 (file)
@@ -15,6 +15,7 @@
 #include "unicode/utf16.h"
 #include "number_utils.h"
 #include "number_roundingutils.h"
+#include "number_mapper.h"
 
 using namespace icu;
 using namespace icu::number;
@@ -49,7 +50,7 @@ PatternParser::parseToExistingProperties(const UnicodeString& pattern, DecimalFo
 char16_t ParsedPatternInfo::charAt(int32_t flags, int32_t index) const {
     const Endpoints& endpoints = getEndpoints(flags);
     if (index < 0 || index >= endpoints.end - endpoints.start) {
-        U_ASSERT(false);
+        UPRV_UNREACHABLE;
     }
     return pattern.charAt(endpoints.start + index);
 }
@@ -468,7 +469,15 @@ void PatternParser::parseToExistingPropertiesImpl(const UnicodeString& pattern,
     }
 
     ParsedPatternInfo patternInfo;
-    parseToPatternInfo(pattern, patternInfo, status);
+    if (pattern.compare(u"#.##E+00;-#.##E+00",18)==0) {
+        // Special hack for Numbers; if applying the Numbers-style scientific pattern,
+        // then replace it with the pattern beginning with 0 that would have resulted
+        // in older versions of ICU such as ICU 61. rdar://51601250
+        UnicodeString newPattern(TRUE,u"0.##E+00",8);
+        parseToPatternInfo(newPattern, patternInfo, status);
+    } else {
+        parseToPatternInfo(pattern, patternInfo, status);
+    }
     if (U_FAILURE(status)) { return; }
     patternInfoToProperties(properties, patternInfo, ignoreRounding, status);
 }
@@ -643,69 +652,74 @@ PatternParser::patternInfoToProperties(DecimalFormatProperties& properties, Pars
 /// End PatternStringParser.java; begin PatternStringUtils.java ///
 ///////////////////////////////////////////////////////////////////
 
+// Determine whether a given roundingIncrement should be ignored for formatting
+// based on the current maxFrac value (maximum fraction digits). For example a
+// roundingIncrement of 0.01 should be ignored if maxFrac is 1, but not if maxFrac
+// is 2 or more. Note that roundingIncrements are rounded in significance, so
+// a roundingIncrement of 0.006 is treated like 0.01 for this determination, i.e.
+// it should not be ignored if maxFrac is 2 or more (but a roundingIncrement of
+// 0.005 is treated like 0.001 for significance). This is the reason for the
+// initial doubling below.
+// roundIncr must be non-zero.
+// Apple enhancement per rdar://51452216: Takes pointer to roundIncr; if function
+// returns false, roundIncr will be rounded as necessary given maxFrac value.
+bool PatternStringUtils::ignoreRoundingIncrement(double* roundIncrPtr, int32_t maxFrac) {
+    if (maxFrac < 0) {
+        return false;
+    }
+    int32_t frac = 0;
+    double denom = 20.0;
+    double roundIncr = *roundIncrPtr * 2.0;
+    for (frac = 0; frac <= maxFrac && roundIncr <= 1.0; frac++, roundIncr *= 10.0, denom *= 10.0);
+    if (frac <= maxFrac) {
+        *roundIncrPtr = (double)((int)(roundIncr*10.0))/denom;
+        return false;
+    }
+    return true;
+}
+
 UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatProperties& properties,
                                                             UErrorCode& status) {
     UnicodeString sb;
 
     // Convenience references
     // The uprv_min() calls prevent DoS
-    int dosMax = 100;
-    int groupingSize = uprv_min(properties.secondaryGroupingSize, dosMax);
-    int firstGroupingSize = uprv_min(properties.groupingSize, dosMax);
-    int paddingWidth = uprv_min(properties.formatWidth, dosMax);
+    int32_t dosMax = 100;
+    int32_t grouping1 = uprv_max(0, uprv_min(properties.groupingSize, dosMax));
+    int32_t grouping2 = uprv_max(0, uprv_min(properties.secondaryGroupingSize, dosMax));
+    bool useGrouping = properties.groupingUsed;
+    int32_t paddingWidth = uprv_min(properties.formatWidth, dosMax);
     NullableValue<PadPosition> paddingLocation = properties.padPosition;
     UnicodeString paddingString = properties.padString;
-    int minInt = uprv_max(uprv_min(properties.minimumIntegerDigits, dosMax), 0);
-    int maxInt = uprv_min(properties.maximumIntegerDigits, dosMax);
-    int minFrac = uprv_max(uprv_min(properties.minimumFractionDigits, dosMax), 0);
-    int maxFrac = uprv_min(properties.maximumFractionDigits, dosMax);
-    int minSig = uprv_min(properties.minimumSignificantDigits, dosMax);
-    int maxSig = uprv_min(properties.maximumSignificantDigits, dosMax);
+    int32_t minInt = uprv_max(0, uprv_min(properties.minimumIntegerDigits, dosMax));
+    int32_t maxInt = uprv_min(properties.maximumIntegerDigits, dosMax);
+    int32_t minFrac = uprv_max(0, uprv_min(properties.minimumFractionDigits, dosMax));
+    int32_t maxFrac = uprv_min(properties.maximumFractionDigits, dosMax);
+    int32_t minSig = uprv_min(properties.minimumSignificantDigits, dosMax);
+    int32_t maxSig = uprv_min(properties.maximumSignificantDigits, dosMax);
     bool alwaysShowDecimal = properties.decimalSeparatorAlwaysShown;
-    int exponentDigits = uprv_min(properties.minimumExponentDigits, dosMax);
+    int32_t exponentDigits = uprv_min(properties.minimumExponentDigits, dosMax);
     bool exponentShowPlusSign = properties.exponentSignAlwaysShown;
-    UnicodeString pp = properties.positivePrefix;
-    UnicodeString ppp = properties.positivePrefixPattern;
-    UnicodeString ps = properties.positiveSuffix;
-    UnicodeString psp = properties.positiveSuffixPattern;
-    UnicodeString np = properties.negativePrefix;
-    UnicodeString npp = properties.negativePrefixPattern;
-    UnicodeString ns = properties.negativeSuffix;
-    UnicodeString nsp = properties.negativeSuffixPattern;
+
+    PropertiesAffixPatternProvider affixes(properties, status);
 
     // Prefixes
-    if (!ppp.isBogus()) {
-        sb.append(ppp);
-    }
-    sb.append(AffixUtils::escape(pp));
-    int afterPrefixPos = sb.length();
+    sb.append(affixes.getString(AffixPatternProvider::AFFIX_POS_PREFIX));
+    int32_t afterPrefixPos = sb.length();
 
     // Figure out the grouping sizes.
-    int grouping1, grouping2, grouping;
-    if (groupingSize != uprv_min(dosMax, -1) && firstGroupingSize != uprv_min(dosMax, -1) &&
-        groupingSize != firstGroupingSize) {
-        grouping = groupingSize;
-        grouping1 = groupingSize;
-        grouping2 = firstGroupingSize;
-    } else if (groupingSize != uprv_min(dosMax, -1)) {
-        grouping = groupingSize;
-        grouping1 = 0;
-        grouping2 = groupingSize;
-    } else if (firstGroupingSize != uprv_min(dosMax, -1)) {
-        grouping = groupingSize;
-        grouping1 = 0;
-        grouping2 = firstGroupingSize;
-    } else {
-        grouping = 0;
+    if (!useGrouping) {
         grouping1 = 0;
         grouping2 = 0;
+    } else if (grouping1 == grouping2) {
+        grouping1 = 0;
     }
-    int groupingLength = grouping1 + grouping2 + 1;
+    int32_t groupingLength = grouping1 + grouping2 + 1;
 
     // Figure out the digits we need to put in the pattern.
     double roundingInterval = properties.roundingIncrement;
     UnicodeString digitsString;
-    int digitsStringScale = 0;
+    int32_t digitsStringScale = 0;
     if (maxSig != uprv_min(dosMax, -1)) {
         // Significant Digits.
         while (digitsString.length() < minSig) {
@@ -714,9 +728,9 @@ UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatP
         while (digitsString.length() < maxSig) {
             digitsString.append(u'#');
         }
-    } else if (roundingInterval != 0.0) {
+    } else if (roundingInterval != 0.0 && !ignoreRoundingIncrement(&roundingInterval,maxFrac)) {
         // Rounding Interval.
-        digitsStringScale = -roundingutils::doubleFractionLength(roundingInterval);
+        digitsStringScale = -roundingutils::doubleFractionLength(roundingInterval, nullptr);
         // TODO: Check for DoS here?
         DecimalQuantity incrementQuantity;
         incrementQuantity.setToDouble(roundingInterval);
@@ -739,22 +753,30 @@ UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatP
     }
 
     // Write the digits to the string builder
-    int m0 = uprv_max(groupingLength, digitsString.length() + digitsStringScale);
+    int32_t m0 = uprv_max(groupingLength, digitsString.length() + digitsStringScale);
     m0 = (maxInt != dosMax) ? uprv_max(maxInt, m0) - 1 : m0 - 1;
-    int mN = (maxFrac != dosMax) ? uprv_min(-maxFrac, digitsStringScale) : digitsStringScale;
-    for (int magnitude = m0; magnitude >= mN; magnitude--) {
-        int di = digitsString.length() + digitsStringScale - magnitude - 1;
+    int32_t mN = (maxFrac != dosMax) ? uprv_min(-maxFrac, digitsStringScale) : digitsStringScale;
+    for (int32_t magnitude = m0; magnitude >= mN; magnitude--) {
+        int32_t di = digitsString.length() + digitsStringScale - magnitude - 1;
         if (di < 0 || di >= digitsString.length()) {
             sb.append(u'#');
         } else {
             sb.append(digitsString.charAt(di));
         }
-        if (magnitude > grouping2 && grouping > 0 && (magnitude - grouping2) % grouping == 0) {
+        // Decimal separator
+        if (magnitude == 0 && (alwaysShowDecimal || mN < 0)) {
+            sb.append(u'.');
+        }
+        if (!useGrouping) {
+            continue;
+        }
+        // Least-significant grouping separator
+        if (magnitude > 0 && magnitude == grouping1) {
             sb.append(u',');
-        } else if (magnitude > 0 && magnitude == grouping2) {
+        }
+        // All other grouping separators
+        if (magnitude > grouping1 && grouping2 > 0 && (magnitude - grouping1) % grouping2 == 0) {
             sb.append(u',');
-        } else if (magnitude == 0 && (alwaysShowDecimal || mN < 0)) {
-            sb.append(u'.');
         }
     }
 
@@ -764,25 +786,22 @@ UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatP
         if (exponentShowPlusSign) {
             sb.append(u'+');
         }
-        for (int i = 0; i < exponentDigits; i++) {
+        for (int32_t i = 0; i < exponentDigits; i++) {
             sb.append(u'0');
         }
     }
 
     // Suffixes
-    int beforeSuffixPos = sb.length();
-    if (!psp.isBogus()) {
-        sb.append(psp);
-    }
-    sb.append(AffixUtils::escape(ps));
+    int32_t beforeSuffixPos = sb.length();
+    sb.append(affixes.getString(AffixPatternProvider::AFFIX_POS_SUFFIX));
 
     // Resolve Padding
-    if (paddingWidth != -1 && !paddingLocation.isNull()) {
+    if (paddingWidth > 0 && !paddingLocation.isNull()) {
         while (paddingWidth - sb.length() > 0) {
             sb.insert(afterPrefixPos, u'#');
             beforeSuffixPos++;
         }
-        int addedLength;
+        int32_t addedLength;
         switch (paddingLocation.get(status)) {
             case PadPosition::UNUM_PAD_BEFORE_PREFIX:
                 addedLength = escapePaddingString(paddingString, sb, 0, status);
@@ -810,23 +829,16 @@ UnicodeString PatternStringUtils::propertiesToPatternString(const DecimalFormatP
 
     // Negative affixes
     // Ignore if the negative prefix pattern is "-" and the negative suffix is empty
-    if (!np.isBogus() || !ns.isBogus() || (npp.isBogus() && !nsp.isBogus()) ||
-        (!npp.isBogus() && (npp.length() != 1 || npp.charAt(0) != u'-' || nsp.length() != 0))) {
+    if (affixes.hasNegativeSubpattern()) {
         sb.append(u';');
-        if (!npp.isBogus()) {
-            sb.append(npp);
-        }
-        sb.append(AffixUtils::escape(np));
+        sb.append(affixes.getString(AffixPatternProvider::AFFIX_NEG_PREFIX));
         // Copy the positive digit format into the negative.
         // This is optional; the pattern is the same as if '#' were appended here instead.
         // NOTE: It is not safe to append the UnicodeString to itself, so we need to copy.
         // See http://bugs.icu-project.org/trac/ticket/13707
         UnicodeString copy(sb);
         sb.append(copy, afterPrefixPos, beforeSuffixPos - afterPrefixPos);
-        if (!nsp.isBogus()) {
-            sb.append(nsp);
-        }
-        sb.append(AffixUtils::escape(ns));
+        sb.append(affixes.getString(AffixPatternProvider::AFFIX_NEG_SUFFIX));
     }
 
     return sb;