]> granicus.if.org Git - icu/commitdiff
ICU-9597 Be sure that FieldPosition adjusted correctly in DecimalFormat.java
authorTravis Keep <keep94@gmail.com>
Tue, 9 Oct 2012 22:30:13 +0000 (22:30 +0000)
committerTravis Keep <keep94@gmail.com>
Tue, 9 Oct 2012 22:30:13 +0000 (22:30 +0000)
X-SVN-Rev: 32581

icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java

index f2ef8b58b12338282e93af8034a3b732a625ed8b..fc293afffd5fb942f919bba47320564956c369e7 100644 (file)
@@ -1285,16 +1285,7 @@ public class DecimalFormat extends NumberFormat {
         // digitList.count = 0;
         // }
 
-        int i;
-        char [] digits = symbols.getDigitsLocal();
 
-        char grouping = currencySignCount > 0 ? symbols.getMonetaryGroupingSeparator() :
-            symbols.getGroupingSeparator();
-        char decimal = currencySignCount > 0 ? symbols.getMonetaryDecimalSeparator() :
-            symbols.getDecimalSeparator();
-        boolean useSigDig = areSignificantDigitsUsed();
-        int maxIntDig = getMaximumIntegerDigits();
-        int minIntDig = getMinimumIntegerDigits();
 
         // Per bug 4147706, DecimalFormat must respect the sign of numbers which format as
         // zero. This allows sensible computations and preserves relations such as
@@ -1307,362 +1298,398 @@ public class DecimalFormat extends NumberFormat {
         int prefixLen = appendAffix(result, isNegative, true, parseAttr);
 
         if (useExponentialNotation) {
-            // Record field information for caller.
-            if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
-                fieldPosition.setBeginIndex(result.length());
-                fieldPosition.setEndIndex(-1);
-            } else if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) {
-                fieldPosition.setBeginIndex(-1);
-            } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
-                fieldPosition.setBeginIndex(result.length());
-                fieldPosition.setEndIndex(-1);
-            } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) {
-                fieldPosition.setBeginIndex(-1);
-            }
+            subformatExponential(result, fieldPosition, parseAttr);
+        } else {
+            subformatFixed(result, fieldPosition, isInteger, parseAttr);
+        }
 
-            // [Spark/CDL]
-            // the begin index of integer part
-            // the end index of integer part
-            // the begin index of fractional part
-            int intBegin = result.length();
-            int intEnd = -1;
-            int fracBegin = -1;
-            int minFracDig = 0;
-            if (useSigDig) {
-                maxIntDig = minIntDig = 1;
-                minFracDig = getMinimumSignificantDigits() - 1;
-            } else {
-                minFracDig = getMinimumFractionDigits();
-                if (maxIntDig > MAX_SCIENTIFIC_INTEGER_DIGITS) {
-                    maxIntDig = 1;
-                    if (maxIntDig < minIntDig) {
-                        maxIntDig = minIntDig;
-                    }
-                }
-                if (maxIntDig > minIntDig) {
-                    minIntDig = 1;
-                }
-            }
+        int suffixLen = appendAffix(result, isNegative, false, parseAttr);
 
-            // Minimum integer digits are handled in exponential format by adjusting the
-            // exponent. For example, 0.01234 with 3 minimum integer digits is "123.4E-4".
+        addPadding(result, fieldPosition, prefixLen, suffixLen);
+        return result;
+    }
 
-            // Maximum integer digits are interpreted as indicating the repeating
-            // range. This is useful for engineering notation, in which the exponent is
-            // restricted to a multiple of 3. For example, 0.01234 with 3 maximum integer
-            // digits is "12.34e-3".  If maximum integer digits are defined and are larger
-            // than minimum integer digits, then minimum integer digits are ignored.
+    private void subformatFixed(StringBuffer result,
+            FieldPosition fieldPosition,
+            boolean isInteger,
+            boolean parseAttr) {
+        char [] digits = symbols.getDigitsLocal();
 
-            int exponent = digitList.decimalAt;
-            if (maxIntDig > 1 && maxIntDig != minIntDig) {
-                // A exponent increment is defined; adjust to it.
-                exponent = (exponent > 0) ? (exponent - 1) / maxIntDig : (exponent / maxIntDig) - 1;
-                exponent *= maxIntDig;
+        char grouping = currencySignCount > 0 ? symbols.getMonetaryGroupingSeparator() :
+            symbols.getGroupingSeparator();
+        char decimal = currencySignCount > 0 ? symbols.getMonetaryDecimalSeparator() :
+            symbols.getDecimalSeparator();
+        boolean useSigDig = areSignificantDigitsUsed();
+        int maxIntDig = getMaximumIntegerDigits();
+        int minIntDig = getMinimumIntegerDigits();
+        int i;
+        // [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());
+        }
+
+        int sigCount = 0;
+        int minSigDig = getMinimumSignificantDigits();
+        int maxSigDig = getMaximumSignificantDigits();
+        if (!useSigDig) {
+            minSigDig = 0;
+            maxSigDig = Integer.MAX_VALUE;
+        }
+
+        // Output the integer portion. Here 'count' is the total number of integer
+        // digits we will display, including both leading zeros required to satisfy
+        // getMinimumIntegerDigits, and actual digits present in the number.
+        int count = useSigDig ? Math.max(1, digitList.decimalAt) : minIntDig;
+        if (digitList.decimalAt > 0 && count < digitList.decimalAt) {
+            count = digitList.decimalAt;
+        }
+
+        // Handle the case where getMaximumIntegerDigits() is smaller than the real
+        // number of integer digits. If this is so, we output the least significant
+        // max integer digits. For example, the value 1997 printed with 2 max integer
+        // digits is just "97".
+
+        int digitIndex = 0; // Index into digitList.fDigits[]
+        if (count > maxIntDig && maxIntDig >= 0) {
+            count = maxIntDig;
+            digitIndex = digitList.decimalAt - count;
+        }
+
+        int sizeBeforeIntegerPart = result.length();
+        for (i = count - 1; i >= 0; --i) {
+            if (i < digitList.decimalAt && digitIndex < digitList.count
+                && sigCount < maxSigDig) {
+                // Output a real digit
+                result.append(digits[digitList.getDigitValue(digitIndex++)]);
+                ++sigCount;
             } else {
-                // No exponent increment is defined; use minimum integer digits.
-                // If none is specified, as in "#E0", generate 1 integer digit.
-                exponent -= (minIntDig > 0 || minFracDig > 0) ? minIntDig : 1;
-            }
-
-            // We now output a minimum number of digits, and more if there are more
-            // digits, up to the maximum number of digits. We place the decimal point
-            // after the "integer" digits, which are the first (decimalAt - exponent)
-            // digits.
-            int minimumDigits = minIntDig + minFracDig;
-            // The number of integer digits is handled specially if the number
-            // is zero, since then there may be no digits.
-            int integerDigits = digitList.isZero() ? minIntDig : digitList.decimalAt - exponent;
-            int totalDigits = digitList.count;
-            if (minimumDigits > totalDigits)
-                totalDigits = minimumDigits;
-            if (integerDigits > totalDigits)
-                totalDigits = integerDigits;
-
-            for (i = 0; i < totalDigits; ++i) {
-                if (i == integerDigits) {
-                    // Record field information for caller.
-                    if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
-                        fieldPosition.setEndIndex(result.length());
-                    } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
-                        fieldPosition.setEndIndex(result.length());
-                    }
-
-                    // [Spark/CDL] Add attribute for integer part
-                    if (parseAttr) {
-                        intEnd = result.length();
-                        addAttribute(Field.INTEGER, intBegin, result.length());
-                    }
-                    result.append(decimal);
-                    // [Spark/CDL] Add attribute for decimal separator
-                    if (parseAttr) {
-                        // Length of decimal separator is 1.
-                        int decimalSeparatorBegin = result.length() - 1;
-                        addAttribute(Field.DECIMAL_SEPARATOR, decimalSeparatorBegin,
-                                     result.length());
-                        fracBegin = result.length();
-                    }
-                    // Record field information for caller.
-                    if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) {
-                        fieldPosition.setBeginIndex(result.length());
-                    } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) {
-                        fieldPosition.setBeginIndex(result.length());
-                    }
-                }
-                result.append((i < digitList.count)
-                              ? digits[digitList.getDigitValue(i)]
-                              : digits[0]);
-            }
-
-            // For ICU compatibility and format 0 to 0E0 with pattern "#E0" [Richard/GCL]
-            if (digitList.isZero() && (totalDigits == 0)) {
+                // Output a zero (leading or trailing)
                 result.append(digits[0]);
-            }
-
-            // Record field information
-            if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
-                if (fieldPosition.getEndIndex() < 0) {
-                    fieldPosition.setEndIndex(result.length());
-                }
-            } else if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) {
-                if (fieldPosition.getBeginIndex() < 0) {
-                    fieldPosition.setBeginIndex(result.length());
-                }
-                fieldPosition.setEndIndex(result.length());
-            } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
-                if (fieldPosition.getEndIndex() < 0) {
-                    fieldPosition.setEndIndex(result.length());
-                }
-            } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) {
-                if (fieldPosition.getBeginIndex() < 0) {
-                    fieldPosition.setBeginIndex(result.length());
-                }
-                fieldPosition.setEndIndex(result.length());
-            }
-
-            // [Spark/CDL] Calcuate the end index of integer part and fractional
-            // part if they are not properly processed yet.
-            if (parseAttr) {
-                if (intEnd < 0) {
-                    addAttribute(Field.INTEGER, intBegin, result.length());
-                }
-                if (fracBegin > 0) {
-                    addAttribute(Field.FRACTION, fracBegin, result.length());
+                if (sigCount > 0) {
+                    ++sigCount;
                 }
             }
 
-            // 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.
-            result.append(symbols.getExponentSeparator());
-            // [Spark/CDL] For exponent symbol, add an attribute.
-            if (parseAttr) {
-                addAttribute(Field.EXPONENT_SYMBOL, result.length() -
-                             symbols.getExponentSeparator().length(), result.length());
-            }
-            // For zero values, we force the exponent to zero. We must do this here, and
-            // not earlier, because the value is used to determine integer digit count
-            // above.
-            if (digitList.isZero())
-                exponent = 0;
-
-            boolean negativeExponent = exponent < 0;
-            if (negativeExponent) {
-                exponent = -exponent;
-                result.append(symbols.getMinusSign());
-                // [Spark/CDL] If exponent has sign, then add an exponent sign
-                // attribute.
-                if (parseAttr) {
-                    // Length of exponent sign is 1.
-                    addAttribute(Field.EXPONENT_SIGN, result.length() - 1, result.length());
-                }
-            } else if (exponentSignAlwaysShown) {
-                result.append(symbols.getPlusSign());
-                // [Spark/CDL] Add an plus sign attribute.
+            // Output grouping separator if necessary.
+            if (isGroupingPosition(i)) {
+                result.append(grouping);
+                // [Spark/CDL] Add grouping separator attribute here.
                 if (parseAttr) {
-                    // Length of exponent sign is 1.
-                    int expSignBegin = result.length() - 1;
-                    addAttribute(Field.EXPONENT_SIGN, expSignBegin, result.length());
+                    // Length of grouping separator is 1.
+                    addAttribute(Field.GROUPING_SEPARATOR, result.length() - 1, result.length());
                 }
             }
-            int expBegin = result.length();
-            digitList.set(exponent);
-            {
-                int expDig = minExponentDigits;
-                if (useExponentialNotation && expDig < 1) {
-                    expDig = 1;
-                }
-                for (i = digitList.decimalAt; i < expDig; ++i)
-                    result.append(digits[0]);
+        }
+
+        // Record field information for caller.
+        if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
+            fieldPosition.setEndIndex(result.length());
+        } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
+            fieldPosition.setEndIndex(result.length());
+        }
+
+        // Determine whether or not there are any printable fractional digits. If
+        // we've used up the digits we know there aren't.
+        boolean fractionPresent = (!isInteger && digitIndex < digitList.count)
+                || (useSigDig ? (sigCount < minSigDig) : (getMinimumFractionDigits() > 0));
+
+        // If there is no fraction present, and we haven't printed any integer digits,
+        // then print a zero. Otherwise we won't print _any_ digits, and we won't be
+        // able to parse this string.
+        if (!fractionPresent && result.length() == sizeBeforeIntegerPart)
+            result.append(digits[0]);
+        // [Spark/CDL] Add attribute for integer part.
+        if (parseAttr) {
+            addAttribute(Field.INTEGER, intBegin, result.length());
+        }
+        // Output the decimal separator if we always do so.
+        if (decimalSeparatorAlwaysShown || fractionPresent) {
+            if (fieldPosition.getFieldAttribute() == Field.DECIMAL_SEPARATOR) {
+                fieldPosition.setBeginIndex(result.length());
             }
-            for (i = 0; i < digitList.decimalAt; ++i) {
-                result.append((i < digitList.count) ? digits[digitList.getDigitValue(i)]
-                              : digits[0]);
+            result.append(decimal);
+            if (fieldPosition.getFieldAttribute() == Field.DECIMAL_SEPARATOR) {
+                fieldPosition.setEndIndex(result.length());
             }
-            // [Spark/CDL] Add attribute for exponent part.
+            // [Spark/CDL] Add attribute for decimal separator
             if (parseAttr) {
-                addAttribute(Field.EXPONENT, expBegin, result.length());
-            }
-        } else {
-            // [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());
-            }
-
-            int sigCount = 0;
-            int minSigDig = getMinimumSignificantDigits();
-            int maxSigDig = getMaximumSignificantDigits();
-            if (!useSigDig) {
-                minSigDig = 0;
-                maxSigDig = Integer.MAX_VALUE;
+                addAttribute(Field.DECIMAL_SEPARATOR, result.length() - 1, result.length());
             }
+        }
 
-            // Output the integer portion. Here 'count' is the total number of integer
-            // digits we will display, including both leading zeros required to satisfy
-            // getMinimumIntegerDigits, and actual digits present in the number.
-            int count = useSigDig ? Math.max(1, digitList.decimalAt) : minIntDig;
-            if (digitList.decimalAt > 0 && count < digitList.decimalAt) {
-                count = digitList.decimalAt;
-            }
+        // Record field information for caller.
+        if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) {
+            fieldPosition.setBeginIndex(result.length());
+        } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) {
+            fieldPosition.setBeginIndex(result.length());
+        }
 
-            // Handle the case where getMaximumIntegerDigits() is smaller than the real
-            // number of integer digits. If this is so, we output the least significant
-            // max integer digits. For example, the value 1997 printed with 2 max integer
-            // digits is just "97".
+        // [Spark/CDL] Record the begin index of fraction part.
+        int fracBegin = result.length();
 
-            int digitIndex = 0; // Index into digitList.fDigits[]
-            if (count > maxIntDig && maxIntDig >= 0) {
-                count = maxIntDig;
-                digitIndex = digitList.decimalAt - count;
+        count = useSigDig ? Integer.MAX_VALUE : getMaximumFractionDigits();
+        if (useSigDig && (sigCount == maxSigDig ||
+                          (sigCount >= minSigDig && digitIndex == digitList.count))) {
+            count = 0;
+        }
+        for (i = 0; i < count; ++i) {
+            // Here is where we escape from the loop. We escape if we've output the
+            // maximum fraction digits (specified in the for expression above). We
+            // also stop when we've output the minimum digits and either: we have an
+            // integer, so there is no fractional stuff to display, or we're out of
+            // significant digits.
+            if (!useSigDig && i >= getMinimumFractionDigits() &&
+                (isInteger || digitIndex >= digitList.count)) {
+                break;
             }
 
-            int sizeBeforeIntegerPart = result.length();
-            for (i = count - 1; i >= 0; --i) {
-                if (i < digitList.decimalAt && digitIndex < digitList.count
-                    && sigCount < maxSigDig) {
-                    // Output a real digit
-                    result.append(digits[digitList.getDigitValue(digitIndex++)]);
-                    ++sigCount;
-                } else {
-                    // Output a zero (leading or trailing)
-                    result.append(digits[0]);
-                    if (sigCount > 0) {
-                        ++sigCount;
-                    }
-                }
+            // Output leading fractional zeros. These are zeros that come after the
+            // decimal but before any significant digits. These are only output if
+            // abs(number being formatted) < 1.0.
+            if (-1 - i > (digitList.decimalAt - 1)) {
+                result.append(digits[0]);
+                continue;
+            }
 
-                // Output grouping separator if necessary.
-                if (isGroupingPosition(i)) {
-                    result.append(grouping);
-                    // [Spark/CDL] Add grouping separator attribute here.
-                    if (parseAttr) {
-                        // Length of grouping separator is 1.
-                        addAttribute(Field.GROUPING_SEPARATOR, result.length() - 1, result.length());
-                    }
-                }
+            // Output a digit, if we have any precision left, or a zero if we
+            // don't. We don't want to output noise digits.
+            if (!isInteger && digitIndex < digitList.count) {
+                result.append(digits[digitList.getDigitValue(digitIndex++)]);
+            } else {
+                result.append(digits[0]);
             }
 
-            // Record field information for caller.
-            if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
-                fieldPosition.setEndIndex(result.length());
-            } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
-                fieldPosition.setEndIndex(result.length());
+            // If we reach the maximum number of significant digits, or if we output
+            // all the real digits and reach the minimum, then we are done.
+            ++sigCount;
+            if (useSigDig && (sigCount == maxSigDig ||
+                              (digitIndex == digitList.count && sigCount >= minSigDig))) {
+                break;
             }
+        }
 
-            // Determine whether or not there are any printable fractional digits. If
-            // we've used up the digits we know there aren't.
-            boolean fractionPresent = (!isInteger && digitIndex < digitList.count)
-                    || (useSigDig ? (sigCount < minSigDig) : (getMinimumFractionDigits() > 0));
+        // Record field information for caller.
+        if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) {
+            fieldPosition.setEndIndex(result.length());
+        } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) {
+            fieldPosition.setEndIndex(result.length());
+        }
 
-            // If there is no fraction present, and we haven't printed any integer digits,
-            // then print a zero. Otherwise we won't print _any_ digits, and we won't be
-            // able to parse this string.
-            if (!fractionPresent && result.length() == sizeBeforeIntegerPart)
-                result.append(digits[0]);
-            // [Spark/CDL] Add attribute for integer part.
-            if (parseAttr) {
-                addAttribute(Field.INTEGER, intBegin, result.length());
-            }
-            // Output the decimal separator if we always do so.
-            if (decimalSeparatorAlwaysShown || fractionPresent) {
-                result.append(decimal);
-                // [Spark/CDL] Add attribute for decimal separator
-                if (parseAttr) {
-                    addAttribute(Field.DECIMAL_SEPARATOR, result.length() - 1, result.length());
+        // [Spark/CDL] Add attribute information if necessary.
+        if (parseAttr && (decimalSeparatorAlwaysShown || fractionPresent)) {
+            addAttribute(Field.FRACTION, fracBegin, result.length());
+        }
+    }
+
+    private void subformatExponential(StringBuffer result,
+            FieldPosition fieldPosition,
+            boolean parseAttr) {
+        char [] digits = symbols.getDigitsLocal();
+        char decimal = currencySignCount > 0 ? symbols.getMonetaryDecimalSeparator() :
+            symbols.getDecimalSeparator();
+        boolean useSigDig = areSignificantDigitsUsed();
+        int maxIntDig = getMaximumIntegerDigits();
+        int minIntDig = getMinimumIntegerDigits();
+        int i;
+        // Record field information for caller.
+        if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
+            fieldPosition.setBeginIndex(result.length());
+            fieldPosition.setEndIndex(-1);
+        } else if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) {
+            fieldPosition.setBeginIndex(-1);
+        } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
+            fieldPosition.setBeginIndex(result.length());
+            fieldPosition.setEndIndex(-1);
+        } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) {
+            fieldPosition.setBeginIndex(-1);
+        }
+
+        // [Spark/CDL]
+        // the begin index of integer part
+        // the end index of integer part
+        // the begin index of fractional part
+        int intBegin = result.length();
+        int intEnd = -1;
+        int fracBegin = -1;
+        int minFracDig = 0;
+        if (useSigDig) {
+            maxIntDig = minIntDig = 1;
+            minFracDig = getMinimumSignificantDigits() - 1;
+        } else {
+            minFracDig = getMinimumFractionDigits();
+            if (maxIntDig > MAX_SCIENTIFIC_INTEGER_DIGITS) {
+                maxIntDig = 1;
+                if (maxIntDig < minIntDig) {
+                    maxIntDig = minIntDig;
                 }
             }
-
-            // Record field information for caller.
-            if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) {
-                fieldPosition.setBeginIndex(result.length());
-            } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) {
-                fieldPosition.setBeginIndex(result.length());
+            if (maxIntDig > minIntDig) {
+                minIntDig = 1;
             }
+        }
 
-            // [Spark/CDL] Record the begin index of fraction part.
-            int fracBegin = result.length();
+        // Minimum integer digits are handled in exponential format by adjusting the
+        // exponent. For example, 0.01234 with 3 minimum integer digits is "123.4E-4".
 
-            count = useSigDig ? Integer.MAX_VALUE : getMaximumFractionDigits();
-            if (useSigDig && (sigCount == maxSigDig ||
-                              (sigCount >= minSigDig && digitIndex == digitList.count))) {
-                count = 0;
-            }
-            for (i = 0; i < count; ++i) {
-                // Here is where we escape from the loop. We escape if we've output the
-                // maximum fraction digits (specified in the for expression above). We
-                // also stop when we've output the minimum digits and either: we have an
-                // integer, so there is no fractional stuff to display, or we're out of
-                // significant digits.
-                if (!useSigDig && i >= getMinimumFractionDigits() &&
-                    (isInteger || digitIndex >= digitList.count)) {
-                    break;
-                }
+        // Maximum integer digits are interpreted as indicating the repeating
+        // range. This is useful for engineering notation, in which the exponent is
+        // restricted to a multiple of 3. For example, 0.01234 with 3 maximum integer
+        // digits is "12.34e-3".  If maximum integer digits are defined and are larger
+        // than minimum integer digits, then minimum integer digits are ignored.
 
-                // Output leading fractional zeros. These are zeros that come after the
-                // decimal but before any significant digits. These are only output if
-                // abs(number being formatted) < 1.0.
-                if (-1 - i > (digitList.decimalAt - 1)) {
-                    result.append(digits[0]);
-                    continue;
+        int exponent = digitList.decimalAt;
+        if (maxIntDig > 1 && maxIntDig != minIntDig) {
+            // A exponent increment is defined; adjust to it.
+            exponent = (exponent > 0) ? (exponent - 1) / maxIntDig : (exponent / maxIntDig) - 1;
+            exponent *= maxIntDig;
+        } else {
+            // No exponent increment is defined; use minimum integer digits.
+            // If none is specified, as in "#E0", generate 1 integer digit.
+            exponent -= (minIntDig > 0 || minFracDig > 0) ? minIntDig : 1;
+        }
+
+        // We now output a minimum number of digits, and more if there are more
+        // digits, up to the maximum number of digits. We place the decimal point
+        // after the "integer" digits, which are the first (decimalAt - exponent)
+        // digits.
+        int minimumDigits = minIntDig + minFracDig;
+        // The number of integer digits is handled specially if the number
+        // is zero, since then there may be no digits.
+        int integerDigits = digitList.isZero() ? minIntDig : digitList.decimalAt - exponent;
+        int totalDigits = digitList.count;
+        if (minimumDigits > totalDigits)
+            totalDigits = minimumDigits;
+        if (integerDigits > totalDigits)
+            totalDigits = integerDigits;
+
+        for (i = 0; i < totalDigits; ++i) {
+            if (i == integerDigits) {
+                // Record field information for caller.
+                if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
+                    fieldPosition.setEndIndex(result.length());
+                } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
+                    fieldPosition.setEndIndex(result.length());
                 }
 
-                // Output a digit, if we have any precision left, or a zero if we
-                // don't. We don't want to output noise digits.
-                if (!isInteger && digitIndex < digitList.count) {
-                    result.append(digits[digitList.getDigitValue(digitIndex++)]);
-                } else {
-                    result.append(digits[0]);
+                // [Spark/CDL] Add attribute for integer part
+                if (parseAttr) {
+                    intEnd = result.length();
+                    addAttribute(Field.INTEGER, intBegin, result.length());
                 }
-
-                // If we reach the maximum number of significant digits, or if we output
-                // all the real digits and reach the minimum, then we are done.
-                ++sigCount;
-                if (useSigDig && (sigCount == maxSigDig ||
-                                  (digitIndex == digitList.count && sigCount >= minSigDig))) {
-                    break;
+                result.append(decimal);
+                // [Spark/CDL] Add attribute for decimal separator
+                if (parseAttr) {
+                    // Length of decimal separator is 1.
+                    int decimalSeparatorBegin = result.length() - 1;
+                    addAttribute(Field.DECIMAL_SEPARATOR, decimalSeparatorBegin,
+                                 result.length());
+                    fracBegin = result.length();
+                }
+                // Record field information for caller.
+                if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) {
+                    fieldPosition.setBeginIndex(result.length());
+                } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) {
+                    fieldPosition.setBeginIndex(result.length());
                 }
             }
+            result.append((i < digitList.count)
+                          ? digits[digitList.getDigitValue(i)]
+                          : digits[0]);
+        }
+
+        // For ICU compatibility and format 0 to 0E0 with pattern "#E0" [Richard/GCL]
+        if (digitList.isZero() && (totalDigits == 0)) {
+            result.append(digits[0]);
+        }
 
-            // Record field information for caller.
-            if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) {
+        // Record field information
+        if (fieldPosition.getField() == NumberFormat.INTEGER_FIELD) {
+            if (fieldPosition.getEndIndex() < 0) {
                 fieldPosition.setEndIndex(result.length());
-            } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) {
+            }
+        } else if (fieldPosition.getField() == NumberFormat.FRACTION_FIELD) {
+            if (fieldPosition.getBeginIndex() < 0) {
+                fieldPosition.setBeginIndex(result.length());
+            }
+            fieldPosition.setEndIndex(result.length());
+        } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.INTEGER) {
+            if (fieldPosition.getEndIndex() < 0) {
                 fieldPosition.setEndIndex(result.length());
             }
+        } else if (fieldPosition.getFieldAttribute() == NumberFormat.Field.FRACTION) {
+            if (fieldPosition.getBeginIndex() < 0) {
+                fieldPosition.setBeginIndex(result.length());
+            }
+            fieldPosition.setEndIndex(result.length());
+        }
 
-            // [Spark/CDL] Add attribute information if necessary.
-            if (parseAttr && (decimalSeparatorAlwaysShown || fractionPresent)) {
+        // [Spark/CDL] Calcuate the end index of integer part and fractional
+        // part if they are not properly processed yet.
+        if (parseAttr) {
+            if (intEnd < 0) {
+                addAttribute(Field.INTEGER, intBegin, result.length());
+            }
+            if (fracBegin > 0) {
                 addAttribute(Field.FRACTION, fracBegin, result.length());
             }
         }
 
-        int suffixLen = appendAffix(result, isNegative, false, parseAttr);
-
-        addPadding(result, fieldPosition, prefixLen, suffixLen);
-        return result;
+        // 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.
+        result.append(symbols.getExponentSeparator());
+        // [Spark/CDL] For exponent symbol, add an attribute.
+        if (parseAttr) {
+            addAttribute(Field.EXPONENT_SYMBOL, result.length() -
+                         symbols.getExponentSeparator().length(), result.length());
+        }
+        // For zero values, we force the exponent to zero. We must do this here, and
+        // not earlier, because the value is used to determine integer digit count
+        // above.
+        if (digitList.isZero())
+            exponent = 0;
+
+        boolean negativeExponent = exponent < 0;
+        if (negativeExponent) {
+            exponent = -exponent;
+            result.append(symbols.getMinusSign());
+            // [Spark/CDL] If exponent has sign, then add an exponent sign
+            // attribute.
+            if (parseAttr) {
+                // Length of exponent sign is 1.
+                addAttribute(Field.EXPONENT_SIGN, result.length() - 1, result.length());
+            }
+        } else if (exponentSignAlwaysShown) {
+            result.append(symbols.getPlusSign());
+            // [Spark/CDL] Add an plus sign attribute.
+            if (parseAttr) {
+                // Length of exponent sign is 1.
+                int expSignBegin = result.length() - 1;
+                addAttribute(Field.EXPONENT_SIGN, expSignBegin, result.length());
+            }
+        }
+        int expBegin = result.length();
+        digitList.set(exponent);
+        {
+            int expDig = minExponentDigits;
+            if (useExponentialNotation && expDig < 1) {
+                expDig = 1;
+            }
+            for (i = digitList.decimalAt; i < expDig; ++i)
+                result.append(digits[0]);
+        }
+        for (i = 0; i < digitList.decimalAt; ++i) {
+            result.append((i < digitList.count) ? digits[digitList.getDigitValue(i)]
+                          : digits[0]);
+        }
+        // [Spark/CDL] Add attribute for exponent part.
+        if (parseAttr) {
+            addAttribute(Field.EXPONENT, expBegin, result.length());
+        }
     }
 
     private final void addPadding(StringBuffer result, FieldPosition fieldPosition, int prefixLen,
index cf927fd9f357b3f26c71998d3400a0a8b11eb564..32e1ea025197afe5df58934c7e4d876b88c1dac2 100644 (file)
@@ -222,7 +222,7 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
         logln("Pattern \"" + fmt.toPattern() + "\"");
         logln(" Format " + 1234.56 + " . " + s);
         assertEquals("symbol, pos", "$1,234.56", s);
-        
+
         s = ((NumberFormat) fmt).format(-1234.56);
         logln(" Format " + Double.toString(-1234.56) + " . " + s);
         assertEquals("symbol, neg", "-$1,234.56", s);
@@ -235,7 +235,7 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
         logln("Pattern \"" + fmt.toPattern() + "\"");
         logln(" Format " + Double.toString(1234.56) + " . " + s);
         assertEquals("name, pos", "USD 1,234.56", s);
-        
+
         s = ((NumberFormat) fmt).format(-1234.56);
         logln(" Format " + Double.toString(-1234.56) + " . " + s);
         assertEquals("name, neg", "USD -1,234.56", s);
@@ -253,7 +253,7 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
             {" $ 124 ", "0", "0"}, // TODO: need to handle space correctly
             {"124$", "0", "3"}, // TODO: need to handle space correctly
             // {"124 $", "5", "-1"}, TODO: OK or NOT?
-            {"124 $", "0", "3"}, 
+            {"124 $", "0", "3"},
         };
         NumberFormat foo = NumberFormat.getCurrencyInstance();
         for (int i = 0; i < DATA.length; ++i) {
@@ -281,16 +281,16 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
     public void TestMultiCurrencySign() {
         String[][] DATA = {
             // the fields in the following test are:
-            // locale, 
-            // currency pattern (with negative pattern), 
+            // locale,
+            // currency pattern (with negative pattern),
             // currency number to be formatted,
             // currency format using currency symbol name, such as "$" for USD,
             // currency format using currency ISO name, such as "USD",
             // currency format using plural name, such as "US dollars".
             // for US locale
-            {"en_US", "\u00A4#,##0.00;-\u00A4#,##0.00", "1234.56", "$1,234.56", "USD1,234.56", "US dollars1,234.56"}, 
-            {"en_US", "\u00A4#,##0.00;-\u00A4#,##0.00", "-1234.56", "-$1,234.56", "-USD1,234.56", "-US dollars1,234.56"}, 
-            {"en_US", "\u00A4#,##0.00;-\u00A4#,##0.00", "1", "$1.00", "USD1.00", "US dollar1.00"}, 
+            {"en_US", "\u00A4#,##0.00;-\u00A4#,##0.00", "1234.56", "$1,234.56", "USD1,234.56", "US dollars1,234.56"},
+            {"en_US", "\u00A4#,##0.00;-\u00A4#,##0.00", "-1234.56", "-$1,234.56", "-USD1,234.56", "-US dollars1,234.56"},
+            {"en_US", "\u00A4#,##0.00;-\u00A4#,##0.00", "1", "$1.00", "USD1.00", "US dollar1.00"},
             // for CHINA locale
             {"zh_CN", "\u00A4#,##0.00;(\u00A4#,##0.00)", "1234.56", "\uFFE51,234.56", "CNY1,234.56", "\u4EBA\u6C11\u5E011,234.56"},
             {"zh_CN", "\u00A4#,##0.00;(\u00A4#,##0.00)", "-1234.56", "(\uFFE51,234.56)", "(CNY1,234.56)", "(\u4EBA\u6C11\u5E011,234.56)"},
@@ -336,7 +336,7 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
                       // DATA[i][5] is the currency format result using
                       // triple currency sign.
                       String oneCurrencyFormat = DATA[i][k];
-                      if (fmt.parse(oneCurrencyFormat).doubleValue() != 
+                      if (fmt.parse(oneCurrencyFormat).doubleValue() !=
                           numberToBeFormat.doubleValue()) {
                         errln("FAILED parse " + oneCurrencyFormat);
                       }
@@ -384,12 +384,12 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
         String[][] DATA = {
             // the data are:
             // string to be parsed, the parsed result (number)
-            {"$1.00", "1"},    
-            {"USD1.00", "1"},    
-            {"1.00 US dollar", "1"},    
-            {"$1,234.56", "1234.56"},    
-            {"USD1,234.56", "1234.56"},    
-            {"1,234.56 US dollar", "1234.56"},    
+            {"$1.00", "1"},
+            {"USD1.00", "1"},
+            {"1.00 US dollar", "1"},
+            {"$1,234.56", "1234.56"},
+            {"USD1,234.56", "1234.56"},
+            {"1,234.56 US dollar", "1234.56"},
         };
         try {
             for (int i = 0; i < DATA.length; ++i) {
@@ -403,7 +403,7 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
         } catch (ParseException e) {
             errln("FAILED, DecimalFormat parse currency: " + e.toString());
         }
-    } 
+    }
 
     /**
      * Test localized currency patterns.
@@ -473,7 +473,7 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
     public void TestCurrencyIsoPluralFormat() {
         String[][] DATA = {
             // the data are:
-            // locale, 
+            // locale,
             // currency amount to be formatted,
             // currency ISO code to be formatted,
             // format result using CURRENCYSTYLE,
@@ -482,10 +482,10 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
             {"en_US", "1", "USD", "$1.00", "USD1.00", "1.00 US dollar"},
             {"en_US", "1234.56", "USD", "$1,234.56", "USD1,234.56", "1,234.56 US dollars"},
             {"en_US", "-1234.56", "USD", "($1,234.56)", "(USD1,234.56)", "-1,234.56 US dollars"},
-            {"zh_CN", "1", "USD", "US$1.00", "USD1.00", "1.00\u7F8E\u5143"}, 
+            {"zh_CN", "1", "USD", "US$1.00", "USD1.00", "1.00\u7F8E\u5143"},
             {"zh_CN", "1234.56", "USD", "US$1,234.56", "USD1,234.56", "1,234.56\u7F8E\u5143"},
             {"zh_CN", "1", "CNY", "\uFFE51.00", "CNY1.00", "1.00\u4EBA\u6C11\u5E01"},
-            {"zh_CN", "1234.56", "CNY", "\uFFE51,234.56", "CNY1,234.56", "1,234.56\u4EBA\u6C11\u5E01"}, 
+            {"zh_CN", "1234.56", "CNY", "\uFFE51,234.56", "CNY1,234.56", "1,234.56\u4EBA\u6C11\u5E01"},
             {"ru_RU", "1", "RUB", "1,00\u00A0\u0440\u0443\u0431.", "1,00\u00A0RUB", "1,00 \u0440\u043E\u0441\u0441\u0438\u0439\u0441\u043A\u0438\u0439 \u0440\u0443\u0431\u043B\u044C"},
             {"ru_RU", "2", "RUB", "2,00\u00A0\u0440\u0443\u0431.", "2,00\u00A0RUB", "2,00 \u0440\u043E\u0441\u0441\u0438\u0439\u0441\u043A\u0438\u0445 \u0440\u0443\u0431\u043B\u044F"},
             {"ru_RU", "5", "RUB", "5,00\u00A0\u0440\u0443\u0431.", "5,00\u00A0RUB", "5,00 \u0440\u043E\u0441\u0441\u0438\u0439\u0441\u043A\u0438\u0445 \u0440\u0443\u0431\u043B\u0435\u0439"},
@@ -497,7 +497,7 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
             {"es_AR", "1", "INR", "\u20B91,00", "INR1,00", "1,00 rupia india"},
             {"ar_EG", "1", "USD", "US$\u00A0\u0661\u066B\u0660\u0660", "USD\u00a0\u0661\u066b\u0660\u0660", "\u0661\u066b\u0660\u0660 \u062f\u0648\u0644\u0627\u0631 \u0623\u0645\u0631\u064a\u0643\u064a"},
         };
-        
+
         for (int i=0; i<DATA.length; ++i) {
           for (int k = NumberFormat.CURRENCYSTYLE;
                k <= NumberFormat.PLURALCURRENCYSTYLE;
@@ -528,7 +528,7 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
             try {
                 // test parsing, and test parsing for all currency formats.
                 for (int j = 3; j < 6; ++j) {
-                    // DATA[i][3] is the currency format result using 
+                    // DATA[i][3] is the currency format result using
                     // CURRENCYSTYLE formatter.
                     // DATA[i][4] is the currency format result using
                     // ISOCURRENCYSTYLE formatter.
@@ -544,7 +544,7 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
             catch (ParseException e) {
                 errln("FAIL: " + e.getMessage());
             }
-          }  
+          }
         }
     }
 
@@ -580,14 +580,14 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
 
     public void TestParseCurrency() {
         class ParseCurrencyItem {
-            private String localeString;
-            private String descrip;
-            private String currStr;
-            private int    numExpectPos;
-            private int    numExpectVal;
-            private int    curExpectPos;
-            private int    curExpectVal;
-            private String curExpectCurr;
+            private final String localeString;
+            private final String descrip;
+            private final String currStr;
+            private final int    numExpectPos;
+            private final int    numExpectVal;
+            private final int    curExpectPos;
+            private final int    curExpectVal;
+            private final String curExpectCurr;
 
             ParseCurrencyItem(String locStr, String desc, String curr, int numExPos, int numExVal, int curExPos, int curExVal, String curExCurr) {
                 localeString  = locStr;
@@ -608,7 +608,7 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
             public int    getCurExpectVal()  { return curExpectVal; }
             public String getCurExpectCurr() { return curExpectCurr; }
         }
-        final ParseCurrencyItem[] parseCurrencyItems = { 
+        final ParseCurrencyItem[] parseCurrencyItems = {
             new ParseCurrencyItem( "en_US", "dollars2", "$2.00",            5,  2,  5,  2,  "USD" ),
             new ParseCurrencyItem( "en_US", "dollars4", "$4",               2,  4,  2,  4,  "USD" ),
             new ParseCurrencyItem( "en_US", "dollars9", "9\u00A0$",         0,  0,  0,  0,  ""    ),
@@ -647,26 +647,26 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
             Number numVal = fmt.parse(currStr, parsePos);
             if ( parsePos.getIndex() != item.getNumExpectPos() || (numVal != null && numVal.intValue() != item.getNumExpectVal()) ) {
                 if (numVal != null) {
-                    errln("NumberFormat.getCurrencyInstance parse " + localeString + "/" + item.getDescrip() + 
+                    errln("NumberFormat.getCurrencyInstance parse " + localeString + "/" + item.getDescrip() +
                             ", expect pos/val " + item.getNumExpectPos() + "/" + item.getNumExpectVal() +
                             ", get " + parsePos.getIndex() + "/" + numVal.intValue() );
                 } else {
-                    errln("NumberFormat.getCurrencyInstance parse " + localeString + "/" + item.getDescrip() + 
+                    errln("NumberFormat.getCurrencyInstance parse " + localeString + "/" + item.getDescrip() +
                             ", expect pos/val " + item.getNumExpectPos() + "/" + item.getNumExpectVal() +
                             ", get " + parsePos.getIndex() + "/(NULL)" );
                 }
             }
-            
+
             parsePos.setIndex(0);
             CurrencyAmount currAmt = fmt.parseCurrency(currStr, parsePos);
             if ( parsePos.getIndex() != item.getCurExpectPos() || (currAmt != null && (currAmt.getNumber().intValue() != item.getCurExpectVal() ||
                     currAmt.getCurrency().getCurrencyCode().compareTo(item.getCurExpectCurr()) != 0)) ) {
                 if (currAmt != null) {
-                    errln("NumberFormat.getCurrencyInstance parseCurrency " + localeString + "/" + item.getDescrip() + 
+                    errln("NumberFormat.getCurrencyInstance parseCurrency " + localeString + "/" + item.getDescrip() +
                             ", expect pos/val/curr " + item.getCurExpectPos() + "/" + item.getCurExpectVal() + "/" + item.getCurExpectCurr() +
                             ", get " + parsePos.getIndex() + "/" + currAmt.getNumber().intValue() + "/" + currAmt.getCurrency().getCurrencyCode() );
                 } else {
-                    errln("NumberFormat.getCurrencyInstance parseCurrency " + localeString + "/" + item.getDescrip() + 
+                    errln("NumberFormat.getCurrencyInstance parseCurrency " + localeString + "/" + item.getDescrip() +
                             ", expect pos/val/curr " + item.getCurExpectPos() + "/" + item.getCurExpectVal() + "/" + item.getCurExpectCurr() +
                             ", get " + parsePos.getIndex() + "/(NULL)" );
                 }
@@ -1137,6 +1137,7 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
                 currencyStyle = NumberFormat.getIntegerInstance(SWAP_LOC);
             }
 
+            @Override
             public NumberFormat createFormat(ULocale loc, int formatType) {
                 if (formatType == FORMAT_CURRENCY) {
                     return currencyStyle;
@@ -1206,11 +1207,17 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
         private static final long serialVersionUID = -305601227915602172L;
 
         private PI() {}
+        @Override
         public int intValue() { return (int)Math.PI; }
+        @Override
         public long longValue() { return (long)Math.PI; }
+        @Override
         public float  floatValue() { return (float)Math.PI; }
-        public double doubleValue() { return (double)Math.PI; }
+        @Override
+        public double doubleValue() { return Math.PI; }
+        @Override
         public byte byteValue() { return (byte)Math.PI; }
+        @Override
         public short shortValue() { return (short)Math.PI; }
 
         public static final Number INSTANCE = new PI();
@@ -1352,20 +1359,20 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
      */
     public void TestNumberingSystems() {
         class TestNumberingSystemItem {
-            private String localeName;
-            private double value;
-            private boolean isRBNF;
-            private String expectedResult;
+            private final String localeName;
+            private final double value;
+            private final boolean isRBNF;
+            private final String expectedResult;
+
             TestNumberingSystemItem(String loc, double val, boolean rbnf, String exp) {
                 localeName  = loc;
                 value = val;
                 isRBNF = rbnf;
                 expectedResult = exp;
             }
-       }        
+       }
 
-        final TestNumberingSystemItem[] DATA = { 
+        final TestNumberingSystemItem[] DATA = {
                 new TestNumberingSystemItem( "en_US@numbers=thai",        1234.567, false, "\u0e51,\u0e52\u0e53\u0e54.\u0e55\u0e56\u0e57" ),
                 new TestNumberingSystemItem( "en_US@numbers=thai",        1234.567, false, "\u0E51,\u0E52\u0E53\u0E54.\u0E55\u0E56\u0E57" ),
                 new TestNumberingSystemItem( "en_US@numbers=hebr",        5678.0,   true,  "\u05D4\u05F3\u05EA\u05E8\u05E2\u05F4\u05D7" ),
@@ -1382,15 +1389,15 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
                 new TestNumberingSystemItem( "zh_TW@numbers=traditional", 1234.567, true,  "\u4E00\u5343\u4E8C\u767E\u4E09\u5341\u56DB\u9EDE\u4E94\u516D\u4E03" ),
                 new TestNumberingSystemItem( "zh_TW@numbers=finance",     1234.567, true,  "\u58F9\u4EDF\u8CB3\u4F70\u53C3\u62FE\u8086\u9EDE\u4F0D\u9678\u67D2" )
         };
-        
-        
+
+
         for (TestNumberingSystemItem item : DATA) {
             ULocale loc = new ULocale(item.localeName);
             NumberFormat fmt = NumberFormat.getInstance(loc);
             if (item.isRBNF) {
                 expect3(fmt,item.value,item.expectedResult);
             } else {
-                expect2(fmt,item.value,item.expectedResult);               
+                expect2(fmt,item.value,item.expectedResult);
             }
         }
     }
@@ -1401,13 +1408,13 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
         NumberFormat nfmt = NumberFormat.getCurrencyInstance(new Locale("und", "PH"));
         DecimalFormatSymbols decsym = ((DecimalFormat)nfmt).getDecimalFormatSymbols();
         Currency cur2 = decsym.getCurrency();
-        
+
         if ( !cur1.getCurrencyCode().equals("PHP") || !cur2.getCurrencyCode().equals("PHP")) {
             errln("FAIL: Currencies should match PHP: cur1 = "+cur1.getCurrencyCode()+"; cur2 = "+cur2.getCurrencyCode());
         }
 
     }
-    
+
     public void TestThreadedFormat() {
 
         class FormatTask implements Runnable {
@@ -1612,11 +1619,11 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
                             // rt: <pattern or '-'> <number> <string>
                             String num = tokens.next();
                             str = tokens.next();
-                            Number n = (Number) ref.parse(num);
+                            Number n = ref.parse(num);
                             assertEquals(where + '"' + pat + "\".format(" + num + ")",
                                          str, fmt.format(n));
                             if (cmd == 3) { // fp:
-                                n = (Number) ref.parse(tokens.next());
+                                n = ref.parse(tokens.next());
                             }
                             if (cmd != 2) { // != f:
                                 assertEquals(where + '"' + pat + "\".parse(\"" + str + "\")",
@@ -1628,7 +1635,7 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
                             str = tokens.next();
                             String expstr = tokens.next();
                             Number parsed = fmt.parse(str);
-                            Number exp = (Number) ref.parse(expstr);
+                            Number exp = ref.parse(expstr);
                             assertEquals(where + '"' + pat + "\".parse(\"" + str + "\")",
                                          exp, parsed);
                         }
@@ -1730,6 +1737,54 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
         }
     }
 
+    public void TestFieldPositionDecimal() {
+        DecimalFormat nf = (DecimalFormat) com.ibm.icu.text.NumberFormat.getInstance(ULocale.ENGLISH);
+        nf.setPositivePrefix("FOO");
+        nf.setPositiveSuffix("BA");
+        StringBuffer buffer = new StringBuffer();
+        FieldPosition fp = new FieldPosition(NumberFormat.Field.DECIMAL_SEPARATOR);
+        nf.format(35.47, buffer, fp);
+        assertEquals("35.47", "FOO35.47BA", buffer.toString());
+        assertEquals("fp begin", 5, fp.getBeginIndex());
+        assertEquals("fp end", 6, fp.getEndIndex());
+    }
+
+    public void TestFieldPositionInteger() {
+        DecimalFormat nf = (DecimalFormat) com.ibm.icu.text.NumberFormat.getInstance(ULocale.ENGLISH);
+        nf.setPositivePrefix("FOO");
+        nf.setPositiveSuffix("BA");
+        StringBuffer buffer = new StringBuffer();
+        FieldPosition fp = new FieldPosition(NumberFormat.Field.INTEGER);
+        nf.format(35.47, buffer, fp);
+        assertEquals("35.47", "FOO35.47BA", buffer.toString());
+        assertEquals("fp begin", 3, fp.getBeginIndex());
+        assertEquals("fp end", 5, fp.getEndIndex());
+    }
+
+    public void TestFieldPositionFractionButInteger() {
+        DecimalFormat nf = (DecimalFormat) com.ibm.icu.text.NumberFormat.getInstance(ULocale.ENGLISH);
+        nf.setPositivePrefix("FOO");
+        nf.setPositiveSuffix("BA");
+        StringBuffer buffer = new StringBuffer();
+        FieldPosition fp = new FieldPosition(NumberFormat.Field.FRACTION);
+        nf.format(35, buffer, fp);
+        assertEquals("35", "FOO35BA", buffer.toString());
+        assertEquals("fp begin", 5, fp.getBeginIndex());
+        assertEquals("fp end", 5, fp.getEndIndex());
+    }
+
+    public void TestFieldPositionFraction() {
+        DecimalFormat nf = (DecimalFormat) com.ibm.icu.text.NumberFormat.getInstance(ULocale.ENGLISH);
+        nf.setPositivePrefix("FOO");
+        nf.setPositiveSuffix("BA");
+        StringBuffer buffer = new StringBuffer();
+        FieldPosition fp = new FieldPosition(NumberFormat.Field.FRACTION);
+        nf.format(35.47, buffer, fp);
+        assertEquals("35.47", "FOO35.47BA", buffer.toString());
+        assertEquals("fp begin", 6, fp.getBeginIndex());
+        assertEquals("fp end", 8, fp.getEndIndex());
+    }
+
     public void TestRounding() {
         DecimalFormat nf = (DecimalFormat) com.ibm.icu.text.NumberFormat.getInstance(ULocale.ENGLISH);
         if (false) { // for debugging specific value
@@ -1752,14 +1807,14 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
             }
         }
     }
-    
+
     public void TestRoundingPattern() {
         class TestRoundingPatternItem {
             String     pattern;
             double     roundingIncrement;
             double     testCase;
             String     expected;
-            
+
             TestRoundingPatternItem(String pattern, double roundingIncrement, double testCase, String expected) {
                 this.pattern = pattern;
                 this.roundingIncrement = roundingIncrement;
@@ -1767,30 +1822,30 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
                 this.expected = expected;
             }
         };
-        
+
         TestRoundingPatternItem []tests = {
                 new TestRoundingPatternItem("##0.65", 0.65, 1.234, "1.30"),
                 new TestRoundingPatternItem("#50", 50.0, 1230, "1250")
         };
-        
+
         DecimalFormat df = (DecimalFormat) com.ibm.icu.text.NumberFormat.getInstance(ULocale.ENGLISH);
         String result;
         BigDecimal bd;
         for (int i = 0; i < tests.length; i++) {
             df.applyPattern(tests[i].pattern);
-            
+
             result = df.format(tests[i].testCase);
-            
+
             if (!tests[i].expected.equals(result)) {
                 errln("String Pattern Rounding Test Failed: Pattern: \"" + tests[i].pattern + "\" Number: " + tests[i].testCase + " - Got: " + result + " Expected: " + tests[i].expected);
             }
-            
+
             bd = new BigDecimal(tests[i].roundingIncrement);
-            
+
             df.setRoundingIncrement(bd);
-            
+
             result = df.format(tests[i].testCase);
-            
+
             if (!tests[i].expected.equals(result)) {
                 errln("BigDecimal Rounding Test Failed: Pattern: \"" + tests[i].pattern + "\" Number: " + tests[i].testCase + " - Got: " + result + " Expected: " + tests[i].expected);
             }
@@ -1798,11 +1853,11 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
     }
 
     public void TestBigDecimalRounding() {
-       String figure = "50.000000004"; 
-       Double dbl = new Double(figure); 
+       String figure = "50.000000004";
+       Double dbl = new Double(figure);
        BigDecimal dec = new BigDecimal(figure);
 
-       DecimalFormat f = (DecimalFormat) NumberFormat.getInstance(); 
+       DecimalFormat f = (DecimalFormat) NumberFormat.getInstance();
        f.applyPattern("00.00######");
 
        assertEquals("double format", "50.00", f.format(dbl));
@@ -1812,11 +1867,11 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
        BigDecimal roundingIncrement = new BigDecimal("1").movePointLeft(maxFracDigits);
 
        f.setRoundingIncrement(roundingIncrement);
-       f.setRoundingMode(BigDecimal.ROUND_DOWN); 
+       f.setRoundingMode(BigDecimal.ROUND_DOWN);
        assertEquals("Rounding down", f.format(dbl), f.format(dec));
 
        f.setRoundingIncrement(roundingIncrement);
-       f.setRoundingMode(BigDecimal.ROUND_HALF_UP); 
+       f.setRoundingMode(BigDecimal.ROUND_HALF_UP);
        assertEquals("Rounding half up", f.format(dbl), f.format(dec));
     }
 
@@ -1846,7 +1901,7 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
                   + ",\tRounding-increment: " + nf.getRoundingIncrement()
                   + ",\tdouble: " + formattedDouble
                   + ",\tBigDecimal: " + formatedBigDecimal);
-                  
+
         } else {
             logln("Value: " + iValue
                   + ",\tRounding-mode: " + roundingModeNames[nf.getRoundingMode()]
@@ -2014,7 +2069,7 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
     public void expect(NumberFormat fmt, String str, Number n) {
         Number num = null;
         try {
-            num = (Number) fmt.parse(str);
+            num = fmt.parse(str);
         } catch (ParseException e) {
             errln(e.getMessage());
             return;
@@ -2036,7 +2091,7 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
     public void expect_rbnf(NumberFormat fmt, String str, Number n) {
         Number num = null;
         try {
-            num = (Number) fmt.parse(str);
+            num = fmt.parse(str);
         } catch (ParseException e) {
             errln(e.getMessage());
             return;
@@ -2398,10 +2453,10 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
     }
 
     static private class ParseThreadJB5358 extends Thread {
-        private DecimalFormat decfmt;
-        private String numstr;
-        private double expect;
-        private ArrayList errors;
+        private final DecimalFormat decfmt;
+        private final String numstr;
+        private final double expect;
+        private final ArrayList errors;
 
         public ParseThreadJB5358(DecimalFormat decfmt, String numstr, double expect, ArrayList errors) {
             this.decfmt = decfmt;
@@ -2410,6 +2465,7 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
             this.errors = errors;
         }
 
+        @Override
         public void run() {
             for (int i = 0; i < 10000; i++) {
                 try {
@@ -2427,7 +2483,7 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
             }
         }
     }
-    
+
     public void TestSetCurrency() {
         DecimalFormatSymbols decf1 = DecimalFormatSymbols.getInstance(ULocale.US);
         DecimalFormatSymbols decf2 = DecimalFormatSymbols.getInstance(ULocale.US);
@@ -2439,7 +2495,7 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
         format2.setCurrency(euro);
         assertEquals("Reset with currency symbol", format1, format2);
     }
-    
+
     /*
      * Testing the method public StringBuffer format(Object number, ...)
      */
@@ -2450,7 +2506,7 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
 
         // Tests when "if (number instanceof Long)" is true
         try {
-            nf.format((Object)new Long("0"), sb, fp);
+            nf.format(new Long("0"), sb, fp);
         } catch (Exception e) {
             errln("NumberFormat.format(Object number, ...) was not suppose to "
                     + "return an exception for a Long object. Error: " + e);
@@ -2491,7 +2547,7 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
 
         // Tests when "else if (number instanceof Number)" is true
         try {
-            nf.format((Object)(Number) 0.0, sb, fp);
+            nf.format(0.0, sb, fp);
         } catch (Exception e) {
             errln("NumberFormat.format(Object number, ...) was not suppose to "
                     + "to return an exception for a Number object. Error: " + e);
@@ -2576,10 +2632,12 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
          * tested.
          */
         class TestFactory extends NumberFormatFactory {
+            @Override
             public Set<String> getSupportedLocaleNames() {
                 return null;
             }
 
+            @Override
             public NumberFormat createFormat(ULocale loc, int formatType) {
                 return null;
             }
@@ -2590,10 +2648,12 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
          * tested.
          */
         class TestFactory1 extends NumberFormatFactory {
+            @Override
             public Set<String> getSupportedLocaleNames() {
                 return null;
             }
 
+            @Override
             public NumberFormat createFormat(Locale loc, int formatType) {
                 return null;
             }
@@ -2648,26 +2708,32 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
         // Tests when "if (shim == null)" is true
         @SuppressWarnings("serial")
         class TestGetAvailableLocales extends NumberFormat {
+            @Override
             public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition pos) {
                 return null;
             }
 
+            @Override
             public StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition pos) {
                 return null;
             }
 
+            @Override
             public StringBuffer format(BigInteger number, StringBuffer toAppendTo, FieldPosition pos) {
                 return null;
             }
 
+            @Override
             public StringBuffer format(java.math.BigDecimal number, StringBuffer toAppendTo, FieldPosition pos) {
                 return null;
             }
 
+            @Override
             public StringBuffer format(BigDecimal number, StringBuffer toAppendTo, FieldPosition pos) {
                 return null;
             }
 
+            @Override
             public Number parse(String text, ParsePosition parsePosition) {
                 return null;
             }
@@ -2720,26 +2786,32 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
     public void TestRoundingMode() {
         @SuppressWarnings("serial")
         class TestRoundingMode extends NumberFormat {
+            @Override
             public StringBuffer format(double number, StringBuffer toAppendTo, FieldPosition pos) {
                 return null;
             }
 
+            @Override
             public StringBuffer format(long number, StringBuffer toAppendTo, FieldPosition pos) {
                 return null;
             }
 
+            @Override
             public StringBuffer format(BigInteger number, StringBuffer toAppendTo, FieldPosition pos) {
                 return null;
             }
 
+            @Override
             public StringBuffer format(java.math.BigDecimal number, StringBuffer toAppendTo, FieldPosition pos) {
                 return null;
             }
 
+            @Override
             public StringBuffer format(BigDecimal number, StringBuffer toAppendTo, FieldPosition pos) {
                 return null;
             }
 
+            @Override
             public Number parse(String text, ParsePosition parsePosition) {
                 return null;
             }
@@ -2842,33 +2914,33 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
                     + text1 + " text2=" + text2);
         }
     }
-    
+
     /*
      * Testing rounding to negative zero problem
      * reported by ticket#7609
      */
-    public void TestNegZeroRounding() {    
-  
-        DecimalFormat df = (DecimalFormat) NumberFormat.getInstance(); 
-        df.setRoundingMode(MathContext.ROUND_HALF_UP); 
-        df.setMinimumFractionDigits(1); 
-        df.setMaximumFractionDigits(1); 
+    public void TestNegZeroRounding() {
+
+        DecimalFormat df = (DecimalFormat) NumberFormat.getInstance();
+        df.setRoundingMode(MathContext.ROUND_HALF_UP);
+        df.setMinimumFractionDigits(1);
+        df.setMaximumFractionDigits(1);
         String text1 = df.format(-0.01);
-        
-        df.setRoundingIncrement(0.1);         
+
+        df.setRoundingIncrement(0.1);
         String text2 = df.format(-0.01);
-        
+
         // output1 and output2 must be identical
         if (!text1.equals(text2)) {
             errln("NumberFormat.format() should return the same result - text1="
                     + text1 + " text2=" + text2);
         }
-  
+
     }
-    
+
     public void TestCurrencyAmountCoverage() {
         CurrencyAmount ca, cb;
-        
+
         try {
             ca = new CurrencyAmount(null, null);
             errln("NullPointerException should have been thrown.");
@@ -2879,7 +2951,7 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
             errln("NullPointerException should have been thrown.");
         } catch (NullPointerException ex) {
         }
-        
+
         ca = new CurrencyAmount(new Integer(0), Currency.getInstance(new ULocale("ja_JP")));
         cb = new CurrencyAmount(new Integer(1), Currency.getInstance(new ULocale("ja_JP")));
         if (ca.equals(null)) {
@@ -2973,9 +3045,9 @@ public class NumberFormatTest extends com.ibm.icu.dev.test.TestFmwk {
     }
 
     private static class FormatCharItrTestThread implements Runnable {
-        private NumberFormat fmt;
-        private int num;
-        private int[] result;
+        private final NumberFormat fmt;
+        private final int num;
+        private final int[] result;
 
         FormatCharItrTestThread(NumberFormat fmt, int num, int[] result) {
             this.fmt = fmt;