From 16557dbe12912127dbfeccc472e0bdf3b45a965a Mon Sep 17 00:00:00 2001 From: Craig Cornelius Date: Mon, 21 Mar 2016 17:18:32 +0000 Subject: [PATCH] ICU-12351 Add missing field position data in DecimalFormat X-SVN-Rev: 38544 --- .../src/com/ibm/icu/text/DecimalFormat.java | 83 ++++++-- .../icu/dev/test/format/NumberFormatTest.java | 189 ++++++++++++++++++ 2 files changed, 260 insertions(+), 12 deletions(-) diff --git a/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java b/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java index 6e6b889c29d..50b0b43c5c4 100644 --- a/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java +++ b/icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java @@ -843,6 +843,9 @@ public class DecimalFormat extends NumberFormat { } result.append(symbols.getNaN()); + // TODO: Combine setting a single FieldPosition or adding to an AttributedCharacterIterator + // into a function like recordAttribute(FieldAttribute, begin, end). + // [Spark/CDL] Add attribute for NaN here. // result.append(symbols.getNaN()); if (parseAttr) { @@ -1429,10 +1432,9 @@ public class DecimalFormat extends NumberFormat { // [Spark/CDL] Record the integer start index. int intBegin = result.length(); // Record field information for caller. - if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) { - fieldPosition.setBeginIndex(result.length()); - } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) { - fieldPosition.setBeginIndex(result.length()); + if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD || + fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) { + fieldPosition.setBeginIndex(intBegin); } long fractionalDigits = 0; int fractionalDigitsCount = 0; @@ -1484,17 +1486,22 @@ public class DecimalFormat extends NumberFormat { if (isGroupingPosition(i)) { result.append(grouping); // [Spark/CDL] Add grouping separator attribute here. + // Set only for the first instance. + // Length of grouping separator is 1. + if (fieldPosition.getFieldAttribute() == Field.GROUPING_SEPARATOR && + fieldPosition.getBeginIndex() == 0 && fieldPosition.getEndIndex() == 0) { + fieldPosition.setBeginIndex(result.length()-1); + fieldPosition.setEndIndex(result.length()); + } if (parseAttr) { - // Length of grouping separator is 1. addAttribute(Field.GROUPING_SEPARATOR, result.length() - 1, result.length()); } } } // Record field information for caller. - if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) { - fieldPosition.setEndIndex(result.length()); - } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) { + if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD || + fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) { fieldPosition.setEndIndex(result.length()); } @@ -1640,7 +1647,6 @@ public class DecimalFormat extends NumberFormat { fieldPosition.setBeginIndex(-1); } - // [Spark/CDL] // the begin index of integer part // the end index of integer part @@ -1716,7 +1722,13 @@ public class DecimalFormat extends NumberFormat { intEnd = result.length(); addAttribute(Field.INTEGER, intBegin, result.length()); } + if (fieldPosition.getFieldAttribute() == Field.DECIMAL_SEPARATOR) { + fieldPosition.setBeginIndex(result.length()); + } result.append(decimal); + if (fieldPosition.getFieldAttribute() == Field.DECIMAL_SEPARATOR) { + fieldPosition.setEndIndex(result.length()); + } // [Spark/CDL] Add attribute for decimal separator fracBegin = result.length(); if (parseAttr) { @@ -1777,7 +1789,7 @@ public class DecimalFormat extends NumberFormat { ((UFieldPosition) fieldPosition).setFractionDigits(fractionalDigitsCount, fractionalDigits); } - // [Spark/CDL] Calcuate the end index of integer part and fractional + // [Spark/CDL] Calculate the end index of integer part and fractional // part if they are not properly processed yet. if (parseAttr) { if (intEnd < 0) { @@ -1791,7 +1803,14 @@ public class DecimalFormat extends NumberFormat { // The exponent is output using the pattern-specified minimum exponent // digits. There is no maximum limit to the exponent digits, since truncating // the exponent would result in an unacceptable inaccuracy. + if (fieldPosition.getFieldAttribute() == Field.EXPONENT_SYMBOL) { + fieldPosition.setBeginIndex(result.length()); + } + result.append(symbols.getExponentSeparator()); + if (fieldPosition.getFieldAttribute() == Field.EXPONENT_SYMBOL) { + fieldPosition.setEndIndex(result.length()); + } // [Spark/CDL] For exponent symbol, add an attribute. if (parseAttr) { addAttribute(Field.EXPONENT_SYMBOL, result.length() - @@ -1806,7 +1825,13 @@ public class DecimalFormat extends NumberFormat { boolean negativeExponent = exponent < 0; if (negativeExponent) { exponent = -exponent; + if (fieldPosition.getFieldAttribute() == Field.EXPONENT_SIGN) { + fieldPosition.setBeginIndex(result.length()); + } result.append(symbols.getMinusString()); + if (fieldPosition.getFieldAttribute() == Field.EXPONENT_SIGN) { + fieldPosition.setEndIndex(result.length()); + } // [Spark/CDL] If exponent has sign, then add an exponent sign // attribute. if (parseAttr) { @@ -1814,7 +1839,13 @@ public class DecimalFormat extends NumberFormat { addAttribute(Field.EXPONENT_SIGN, result.length() - 1, result.length()); } } else if (exponentSignAlwaysShown) { + if (fieldPosition.getFieldAttribute() == Field.EXPONENT_SIGN) { + fieldPosition.setBeginIndex(result.length()); + } result.append(symbols.getPlusString()); + if (fieldPosition.getFieldAttribute() == Field.EXPONENT_SIGN) { + fieldPosition.setEndIndex(result.length()); + } // [Spark/CDL] Add an plus sign attribute. if (parseAttr) { // Length of exponent sign is 1. @@ -1837,6 +1868,10 @@ public class DecimalFormat extends NumberFormat { : digits[0]); } // [Spark/CDL] Add attribute for exponent part. + if (fieldPosition.getFieldAttribute() == Field.EXPONENT) { + fieldPosition.setBeginIndex(expBegin); + fieldPosition.setEndIndex(result.length()); + } if (parseAttr) { addAttribute(Field.EXPONENT, expBegin, result.length()); } @@ -4240,7 +4275,31 @@ public class DecimalFormat extends NumberFormat { } } - // If kCurrencySymbol or kIntlCurrencySymbol is in the affix, check for currency symbol. + // Look for SIGN, PERCENT, PERMILLE in the formatted affix. + if (fieldPosition.getFieldAttribute() == NumberFormat.Field.SIGN) { + String sign = isNegative ? symbols.getMinusString() : symbols.getPlusString(); + int firstPos = affix.indexOf(sign); + if (firstPos > -1) { + int startPos = buf.length() + firstPos; + fieldPosition.setBeginIndex(startPos); + fieldPosition.setEndIndex(startPos + sign.length()); + } + } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.PERCENT) { + int firstPos = affix.indexOf(symbols.getPercent()); + if (firstPos > -1) { + int startPos = buf.length() + firstPos; + fieldPosition.setBeginIndex(startPos); + fieldPosition.setEndIndex(startPos + 1); + } + } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.PERMILLE) { + int firstPos = affix.indexOf(symbols.getPerMill()); + if (firstPos > -1) { + int startPos = buf.length() + firstPos; + fieldPosition.setBeginIndex(startPos); + fieldPosition.setEndIndex(startPos + 1); + } + } else + // If CurrencySymbol or InternationalCurrencySymbol is in the affix, check for currency symbol. // Get spelled out name if "¤¤¤" is in the pattern. if (fieldPosition.getFieldAttribute() == NumberFormat.Field.CURRENCY) { if (affix.indexOf(symbols.getCurrencySymbol()) > -1) { @@ -4251,7 +4310,7 @@ public class DecimalFormat extends NumberFormat { fieldPosition.setBeginIndex(start); fieldPosition.setEndIndex(end); } else if (affix.indexOf(symbols.getInternationalCurrencySymbol()) > -1) { - String aff = symbols.getInternationalCurrencySymbol(); + String aff = symbols.getInternationalCurrencySymbol(); int firstPos = affix.indexOf(aff); int start = buf.length() + firstPos; int end = start + aff.length(); diff --git a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java index e0436797ab6..aa98f19c266 100644 --- a/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java +++ b/icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java @@ -16,6 +16,7 @@ import java.io.IOException; import java.math.BigInteger; import java.text.AttributedCharacterIterator; import java.text.FieldPosition; +import java.text.Format; import java.text.ParseException; import java.text.ParsePosition; import java.util.ArrayList; @@ -4509,4 +4510,192 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk { assertEquals("ICU and JDK placement of decimal in exponent", jdk, icu); } + private void checkFormatWithField(String testInfo, Format format, Object object, + String expected, Format.Field field, int begin, int end) { + StringBuffer buffer = new StringBuffer(); + FieldPosition pos = new FieldPosition(field); + format.format(object, buffer, pos); + + assertEquals("Test " + testInfo + ": incorrect formatted text", expected, buffer.toString()); + + if (begin != pos.getBeginIndex() || end != pos.getEndIndex()) { + assertEquals("Index mismatch", field + " " + begin + ".." + end, + pos.getFieldAttribute() + " " + pos.getBeginIndex() + ".." + pos.getEndIndex()); + } + } + + public void TestMissingFieldPositionsCurrency() { + DecimalFormat formatter = (DecimalFormat) NumberFormat.getCurrencyInstance(ULocale.US); + Number number = new Double(92314587.66); + String result = "$92,314,587.66"; + + checkFormatWithField("currency", formatter, number, result, + NumberFormat.Field.CURRENCY, 0, 1); + checkFormatWithField("integer", formatter, number, result, + NumberFormat.Field.INTEGER, 1, 11); + checkFormatWithField("grouping separator", formatter, number, result, + NumberFormat.Field.GROUPING_SEPARATOR, 3, 4); + checkFormatWithField("decimal separator", formatter, number, result, + NumberFormat.Field.DECIMAL_SEPARATOR, 11, 12); + checkFormatWithField("fraction", formatter, number, result, + NumberFormat.Field.FRACTION, 12, 14); + } + + public void TestMissingFieldPositionsNegativeDouble() { + // test for exponential fields with double + DecimalFormatSymbols us_symbols = new DecimalFormatSymbols(ULocale.US); + Number number = new Double(-12345678.90123); + DecimalFormat formatter = new DecimalFormat("0.#####E+00", us_symbols); + String numFmtted = formatter.format(number); + + checkFormatWithField("sign", formatter, number, numFmtted, + NumberFormat.Field.SIGN, 0, 1); + checkFormatWithField("integer", formatter, number, numFmtted, + NumberFormat.Field.INTEGER, 1, 2); + checkFormatWithField("decimal separator", formatter, number, numFmtted, + NumberFormat.Field.DECIMAL_SEPARATOR, 2, 3); + checkFormatWithField("exponent symbol", formatter, number, numFmtted, + NumberFormat.Field.EXPONENT_SYMBOL, 8, 9); + checkFormatWithField("exponent sign", formatter, number, numFmtted, + NumberFormat.Field.EXPONENT_SIGN, 9, 10); + checkFormatWithField("exponent", formatter, number, numFmtted, + NumberFormat.Field.EXPONENT, 10, 12); + } + + public void TestMissingFieldPositionsPerCent() { + // Check PERCENT + DecimalFormat percentFormat = (DecimalFormat) NumberFormat.getPercentInstance(ULocale.US); + Number number = new Double(-0.986); + String numberFormatted = percentFormat.format(number); + checkFormatWithField("sign", percentFormat, number, numberFormatted, + NumberFormat.Field.SIGN, 0, 1); + checkFormatWithField("integer", percentFormat, number, numberFormatted, + NumberFormat.Field.INTEGER, 1, 3); + checkFormatWithField("percent", percentFormat, number, numberFormatted, + NumberFormat.Field.PERCENT, 3, 4); + } + + public void TestMissingFieldPositionsPerCentPattern() { + // Check PERCENT with more digits + DecimalFormatSymbols us_symbols = new DecimalFormatSymbols(ULocale.US); + DecimalFormat fmtPercent = new DecimalFormat("0.#####%", us_symbols); + Number number = new Double(-0.986); + String numFmtted = fmtPercent.format(number); + + checkFormatWithField("sign", fmtPercent, number, numFmtted, + NumberFormat.Field.SIGN, 0, 1); + checkFormatWithField("integer", fmtPercent, number, numFmtted, + NumberFormat.Field.INTEGER, 1, 3); + checkFormatWithField("decimal separator", fmtPercent, number, numFmtted, + NumberFormat.Field.DECIMAL_SEPARATOR, 3, 4); + checkFormatWithField("fraction", fmtPercent, number, numFmtted, + NumberFormat.Field.FRACTION, 4, 5); + checkFormatWithField("percent", fmtPercent, number, numFmtted, + NumberFormat.Field.PERCENT, 5, 6); + } + + public void TestMissingFieldPositionsPerMille() { + // Check PERMILLE + DecimalFormatSymbols us_symbols = new DecimalFormatSymbols(ULocale.US); + DecimalFormat fmtPerMille = new DecimalFormat("0.######‰", us_symbols); + Number numberPermille = new Double(-0.98654); + String numFmtted = fmtPerMille.format(numberPermille); + + checkFormatWithField("sign", fmtPerMille, numberPermille, numFmtted, + NumberFormat.Field.SIGN, 0, 1); + checkFormatWithField("integer", fmtPerMille, numberPermille, numFmtted, + NumberFormat.Field.INTEGER, 1, 4); + checkFormatWithField("decimal separator", fmtPerMille, numberPermille, numFmtted, + NumberFormat.Field.DECIMAL_SEPARATOR, 4, 5); + checkFormatWithField("fraction", fmtPerMille, numberPermille, numFmtted, + NumberFormat.Field.FRACTION, 5, 7); + checkFormatWithField("permille", fmtPerMille, numberPermille, numFmtted, + NumberFormat.Field.PERMILLE, 7, 8); + } + + public void TestMissingFieldPositionsNegativeBigInt() { + DecimalFormatSymbols us_symbols = new DecimalFormatSymbols(ULocale.US); + DecimalFormat formatter = new DecimalFormat("0.#####E+0", us_symbols); + Number number = new BigDecimal("-123456789987654321"); + String bigDecFmtted = formatter.format(number); + + checkFormatWithField("sign", formatter, number, bigDecFmtted, + NumberFormat.Field.SIGN, 0, 1); + checkFormatWithField("integer", formatter, number, bigDecFmtted, + NumberFormat.Field.INTEGER, 1, 2); + checkFormatWithField("decimal separator", formatter, number, bigDecFmtted, + NumberFormat.Field.DECIMAL_SEPARATOR, 2, 3); + checkFormatWithField("exponent symbol", formatter, number, bigDecFmtted, + NumberFormat.Field.EXPONENT_SYMBOL, 8, 9); + checkFormatWithField("exponent sign", formatter, number, bigDecFmtted, + NumberFormat.Field.EXPONENT_SIGN, 9, 10); + checkFormatWithField("exponent", formatter, number, bigDecFmtted, + NumberFormat.Field.EXPONENT, 10, 12); + } + + public void TestMissingFieldPositionsNegativeLong() { + Number number = new Long("-123456789987654321"); + DecimalFormatSymbols us_symbols = new DecimalFormatSymbols(ULocale.US); + DecimalFormat formatter = new DecimalFormat("0.#####E+0", us_symbols); + String longFmtted = formatter.format(number); + + checkFormatWithField("sign", formatter, number, longFmtted, + NumberFormat.Field.SIGN, 0, 1); + checkFormatWithField("integer", formatter, number, longFmtted, + NumberFormat.Field.INTEGER, 1, 2); + checkFormatWithField("decimal separator", formatter, number, longFmtted, + NumberFormat.Field.DECIMAL_SEPARATOR, 2, 3); + checkFormatWithField("exponent symbol", formatter, number, longFmtted, + NumberFormat.Field.EXPONENT_SYMBOL, 8, 9); + checkFormatWithField("exponent sign", formatter, number, longFmtted, + NumberFormat.Field.EXPONENT_SIGN, 9, 10); + checkFormatWithField("exponent", formatter, number, longFmtted, + NumberFormat.Field.EXPONENT, 10, 12); + } + + public void TestMissingFieldPositionsPositiveBigDec() { + // Check complex positive;negative pattern. + DecimalFormatSymbols us_symbols = new DecimalFormatSymbols(ULocale.US); + DecimalFormat fmtPosNegSign = new DecimalFormat("+0.####E+00;-0.#######E+0", us_symbols); + Number positiveExp = new Double("9876543210"); + String posExpFormatted = fmtPosNegSign.format(positiveExp); + + checkFormatWithField("sign", fmtPosNegSign, positiveExp, posExpFormatted, + NumberFormat.Field.SIGN, 0, 1); + checkFormatWithField("integer", fmtPosNegSign, positiveExp, posExpFormatted, + NumberFormat.Field.INTEGER, 1, 2); + checkFormatWithField("decimal separator", fmtPosNegSign, positiveExp, posExpFormatted, + NumberFormat.Field.DECIMAL_SEPARATOR, 2, 3); + checkFormatWithField("fraction", fmtPosNegSign, positiveExp, posExpFormatted, + NumberFormat.Field.FRACTION, 3, 7); + checkFormatWithField("exponent symbol", fmtPosNegSign, positiveExp, posExpFormatted, + NumberFormat.Field.EXPONENT_SYMBOL, 7, 8); + checkFormatWithField("exponent sign", fmtPosNegSign, positiveExp, posExpFormatted, + NumberFormat.Field.EXPONENT_SIGN, 8, 9); + checkFormatWithField("exponent", fmtPosNegSign, positiveExp, posExpFormatted, + NumberFormat.Field.EXPONENT, 9, 11); + } + + public void TestMissingFieldPositionsNegativeBigDec() { + // Check complex positive;negative pattern. + DecimalFormatSymbols us_symbols = new DecimalFormatSymbols(ULocale.US); + DecimalFormat fmtPosNegSign = new DecimalFormat("+0.####E+00;-0.#######E+0", us_symbols); + Number negativeExp = new BigDecimal("-0.000000987654321083"); + String negExpFormatted = fmtPosNegSign.format(negativeExp); + + checkFormatWithField("sign", fmtPosNegSign, negativeExp, negExpFormatted, + NumberFormat.Field.SIGN, 0, 1); + checkFormatWithField("integer", fmtPosNegSign, negativeExp, negExpFormatted, + NumberFormat.Field.INTEGER, 1, 2); + checkFormatWithField("decimal separator", fmtPosNegSign, negativeExp, negExpFormatted, + NumberFormat.Field.DECIMAL_SEPARATOR, 2, 3); + checkFormatWithField("fraction", fmtPosNegSign, negativeExp, negExpFormatted, + NumberFormat.Field.FRACTION, 3, 7); + checkFormatWithField("exponent symbol", fmtPosNegSign, negativeExp, negExpFormatted, + NumberFormat.Field.EXPONENT_SYMBOL, 7, 8); + checkFormatWithField("exponent sign", fmtPosNegSign, negativeExp, negExpFormatted, + NumberFormat.Field.EXPONENT_SIGN, 8, 9); + checkFormatWithField("exponent", fmtPosNegSign, negativeExp, negExpFormatted, + NumberFormat.Field.EXPONENT, 9, 11); + } }