#include "unicode/utf16.h"
#include "number_utils.h"
#include "number_roundingutils.h"
+#include "number_mapper.h"
using namespace icu;
using namespace icu::number;
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);
}
}
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);
}
/// 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) {
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);
}
// 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'.');
}
}
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);
// 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;