]> granicus.if.org Git - icu/commitdiff
ICU-13662 Improving NumberFormatter field position method names and behavior.
authorShane Carr <shane@unicode.org>
Thu, 3 May 2018 01:34:19 +0000 (01:34 +0000)
committerShane Carr <shane@unicode.org>
Thu, 3 May 2018 01:34:19 +0000 (01:34 +0000)
X-SVN-Rev: 41313

22 files changed:
icu4c/source/i18n/decimfmt.cpp
icu4c/source/i18n/fieldposutil.h [deleted file]
icu4c/source/i18n/i18n.vcxproj
icu4c/source/i18n/i18n.vcxproj.filters
icu4c/source/i18n/i18n_uwp.vcxproj
icu4c/source/i18n/number_capi.cpp
icu4c/source/i18n/number_fluent.cpp
icu4c/source/i18n/number_stringbuilder.cpp
icu4c/source/i18n/number_stringbuilder.h
icu4c/source/i18n/unicode/decimfmt.h
icu4c/source/i18n/unicode/numberformatter.h
icu4c/source/i18n/unicode/unumberformatter.h
icu4c/source/test/cintltst/unumberformattertst.c
icu4c/source/test/intltest/numbertest.h
icu4c/source/test/intltest/numbertest_api.cpp
icu4c/source/test/intltest/numbertest_stringbuilder.cpp
icu4j/main/classes/core/src/com/ibm/icu/impl/number/NumberStringBuilder.java
icu4j/main/classes/core/src/com/ibm/icu/number/FormattedNumber.java
icu4j/main/classes/core/src/com/ibm/icu/text/DecimalFormat.java
icu4j/main/classes/core/src/com/ibm/icu/text/MeasureFormat.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberFormatterApiTest.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberStringBuilderTest.java

index 855f890e8d358e7e25626826efac75a856a6e287..7d4d8b006d4909be1229aee5e17bba0496ffe488 100644 (file)
@@ -428,7 +428,7 @@ UnicodeString& DecimalFormat::format(double number, UnicodeString& appendTo, Fie
     }
     UErrorCode localStatus = U_ZERO_ERROR;
     FormattedNumber output = fields->formatter->formatDouble(number, localStatus);
-    output.populateFieldPosition(pos, localStatus);
+    fieldPositionHelper(output, pos, appendTo.length(), localStatus);
     auto appendable = UnicodeStringAppendable(appendTo);
     output.appendTo(appendable);
     return appendTo;
@@ -440,7 +440,7 @@ UnicodeString& DecimalFormat::format(double number, UnicodeString& appendTo, Fie
         return appendTo;
     }
     FormattedNumber output = fields->formatter->formatDouble(number, status);
-    output.populateFieldPosition(pos, status);
+    fieldPositionHelper(output, pos, appendTo.length(), status);
     auto appendable = UnicodeStringAppendable(appendTo);
     output.appendTo(appendable);
     return appendTo;
@@ -482,7 +482,7 @@ UnicodeString& DecimalFormat::format(int64_t number, UnicodeString& appendTo, Fi
     }
     UErrorCode localStatus = U_ZERO_ERROR;
     FormattedNumber output = fields->formatter->formatInt(number, localStatus);
-    output.populateFieldPosition(pos, localStatus);
+    fieldPositionHelper(output, pos, appendTo.length(), localStatus);
     auto appendable = UnicodeStringAppendable(appendTo);
     output.appendTo(appendable);
     return appendTo;
@@ -494,7 +494,7 @@ UnicodeString& DecimalFormat::format(int64_t number, UnicodeString& appendTo, Fi
         return appendTo;
     }
     FormattedNumber output = fields->formatter->formatInt(number, status);
-    output.populateFieldPosition(pos, status);
+    fieldPositionHelper(output, pos, appendTo.length(), status);
     auto appendable = UnicodeStringAppendable(appendTo);
     output.appendTo(appendable);
     return appendTo;
@@ -542,7 +542,7 @@ UnicodeString&
 DecimalFormat::format(const DecimalQuantity& number, UnicodeString& appendTo, FieldPosition& pos,
                       UErrorCode& status) const {
     FormattedNumber output = fields->formatter->formatDecimalQuantity(number, status);
-    output.populateFieldPosition(pos, status);
+    fieldPositionHelper(output, pos, appendTo.length(), status);
     auto appendable = UnicodeStringAppendable(appendTo);
     output.appendTo(appendable);
     return appendTo;
@@ -1227,6 +1227,17 @@ const numparse::impl::NumberParserImpl* DecimalFormat::getCurrencyParser(UErrorC
     }
 }
 
+void
+DecimalFormat::fieldPositionHelper(const number::FormattedNumber& formatted, FieldPosition& fieldPosition,
+                                   int32_t offset, UErrorCode& status) {
+    fieldPosition.setEndIndex(0); // always return first occurrence
+    bool found = formatted.nextFieldPosition(fieldPosition, status);
+    if (found && offset != 0) {
+        fieldPosition.setBeginIndex(fieldPosition.getBeginIndex() + offset);
+        fieldPosition.setEndIndex(fieldPosition.getEndIndex() + offset);
+    }
+}
+
 // To debug fast-format, change void(x) to printf(x)
 #define trace(x) void(x)
 
diff --git a/icu4c/source/i18n/fieldposutil.h b/icu4c/source/i18n/fieldposutil.h
deleted file mode 100644 (file)
index 8a20536..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-// © 2018 and later: Unicode, Inc. and others.
-// License & terms of use: http://www.unicode.org/copyright.html
-
-#include "unicode/utypes.h"
-
-#if !UCONFIG_NO_FORMATTING
-#ifndef __SOURCE_FIELDPOSUTIL_H__
-#define __SOURCE_FIELDPOSUTIL_H__
-
-U_NAMESPACE_BEGIN
-
-/**
- * Wraps a UFieldPosition and makes it usable as a FieldPosition. Example:
- *
- * <pre>
- * UFieldPositionWrapper wrapper(myUFPos);
- * u_favorite_function_taking_ufpos(wrapper);
- * // when destructed, the wrapper saves the data back into myUFPos
- * </pre>
- */
-class UFieldPositionWrapper : public UMemory {
-  public:
-    explicit UFieldPositionWrapper(UFieldPosition& ufpos)
-            : _ufpos(ufpos) {
-        _fpos.setField(_ufpos.field);
-        _fpos.setBeginIndex(_ufpos.beginIndex);
-        _fpos.setEndIndex(_ufpos.endIndex);
-    }
-
-    /** When destructed, copies the information from the fpos into the ufpos. */
-    ~UFieldPositionWrapper() {
-        _ufpos.field = _fpos.getField();
-        _ufpos.beginIndex = _fpos.getBeginIndex();
-        _ufpos.endIndex = _fpos.getEndIndex();
-    }
-
-    /** Conversion operator to FieldPosition */
-    operator FieldPosition&() {
-        return _fpos;
-    }
-
-  private:
-    FieldPosition _fpos;
-    UFieldPosition& _ufpos;
-};
-
-U_NAMESPACE_END
-
-#endif //__SOURCE_FIELDPOSUTIL_H__
-#endif /* #if !UCONFIG_NO_FORMATTING */
index a2cce5f62cee1fb69eb74be0f0842953a8d19150..322ba85cff8a6658d9597c0b934b4f3e591977ee 100644 (file)
     <ClInclude Include="number_multiplier.h" />
     <ClInclude Include="number_currencysymbols.h" />
     <ClInclude Include="number_skeletons.h" />
-    <ClInclude Include="fieldposutil.h" />
     <ClInclude Include="numparse_stringsegment.h" />
     <ClInclude Include="numparse_impl.h" />
     <ClInclude Include="numparse_symbols.h" />
index 7e1dbb8c11515db591e5b1ac14c93cde9669d5ad..6930f87de18b67d38c5e099dcdc69ed38e05d2e0 100644 (file)
     <ClInclude Include="number_skeletons.h">
       <Filter>formatting</Filter>
     </ClInclude>
-    <ClInclude Include="fieldposutil.h">
-      <Filter>formatting</Filter>
-    </ClInclude>
     <ClInclude Include="numparse_stringsegment.h">
       <Filter>formatting</Filter>
     </ClInclude>
index 2cd3e7af62af0a53cd5aabd42ca2c68cb349c117..47639e7b1e87d238f13d47768167139c81566bf1 100644 (file)
     <ClInclude Include="number_multiplier.h" />
     <ClInclude Include="number_currencysymbols.h" />
     <ClInclude Include="number_skeletons.h" />
-    <ClInclude Include="fieldposutil.h" />
     <ClInclude Include="numparse_stringsegment.h" />
     <ClInclude Include="numparse_impl.h" />
     <ClInclude Include="numparse_symbols.h" />
index 7e79e8ec6488330e479ff74ec48ed8ef20964633..9c150931bf8ae9ae4f480c250272e9a6f20e140c 100644 (file)
@@ -13,7 +13,6 @@
 #include "number_utypes.h"
 #include "unicode/numberformatter.h"
 #include "unicode/unumberformatter.h"
-#include "fieldposutil.h"
 
 using namespace icu;
 using namespace icu::number;
@@ -158,23 +157,30 @@ unumf_resultToString(const UFormattedNumber* uresult, UChar* buffer, int32_t buf
     return result->string.toTempUnicodeString().extract(buffer, bufferCapacity, *ec);
 }
 
-U_CAPI void U_EXPORT2
-unumf_resultGetField(const UFormattedNumber* uresult, UFieldPosition* ufpos, UErrorCode* ec) {
+U_CAPI UBool U_EXPORT2
+unumf_resultNextFieldPosition(const UFormattedNumber* uresult, UFieldPosition* ufpos, UErrorCode* ec) {
     if (ufpos == nullptr) {
         *ec = U_ILLEGAL_ARGUMENT_ERROR;
-        return;
+        return FALSE;
     }
 
     const UFormattedNumberData* result = UFormattedNumberData::validate(uresult, *ec);
-    if (U_FAILURE(*ec)) { return; }
+    if (U_FAILURE(*ec)) { return FALSE; }
 
-    UFieldPositionWrapper helper(*ufpos);
-    result->string.populateFieldPosition(helper, 0, *ec);
+    FieldPosition fp;
+    fp.setField(ufpos->field);
+    fp.setBeginIndex(ufpos->beginIndex);
+    fp.setEndIndex(ufpos->endIndex);
+    bool retval = result->string.nextFieldPosition(fp, *ec);
+    ufpos->beginIndex = fp.getBeginIndex();
+    ufpos->endIndex = fp.getEndIndex();
+    // NOTE: MSVC sometimes complains when implicitly converting between bool and UBool
+    return retval ? TRUE : FALSE;
 }
 
 U_CAPI void U_EXPORT2
-unumf_resultGetAllFields(const UFormattedNumber* uresult, UFieldPositionIterator* ufpositer,
-                         UErrorCode* ec) {
+unumf_resultGetAllFieldPositions(const UFormattedNumber* uresult, UFieldPositionIterator* ufpositer,
+                                 UErrorCode* ec) {
     if (ufpositer == nullptr) {
         *ec = U_ILLEGAL_ARGUMENT_ERROR;
         return;
@@ -184,7 +190,7 @@ unumf_resultGetAllFields(const UFormattedNumber* uresult, UFieldPositionIterator
     if (U_FAILURE(*ec)) { return; }
 
     auto* helper = reinterpret_cast<FieldPositionIterator*>(ufpositer);
-    result->string.populateFieldPositionIterator(*helper, *ec);
+    result->string.getAllFieldPositions(*helper, *ec);
 }
 
 U_CAPI void U_EXPORT2
index cea4125ae10023a860dfedaa90a1f7d32cbd1558..459cb04bb8d07b87f2482a14283b0bcec1b662a9 100644 (file)
@@ -776,7 +776,21 @@ void FormattedNumber::populateFieldPosition(FieldPosition& fieldPosition, UError
         status = fErrorCode;
         return;
     }
-    fResults->string.populateFieldPosition(fieldPosition, 0, status);
+    // in case any users were depending on the old behavior:
+    fieldPosition.setEndIndex(0);
+    fResults->string.nextFieldPosition(fieldPosition, status);
+}
+
+UBool FormattedNumber::nextFieldPosition(FieldPosition& fieldPosition, UErrorCode& status) const {
+    if (U_FAILURE(status)) {
+        return FALSE;
+    }
+    if (fResults == nullptr) {
+        status = fErrorCode;
+        return FALSE;
+    }
+    // NOTE: MSVC sometimes complains when implicitly converting between bool and UBool
+    return fResults->string.nextFieldPosition(fieldPosition, status) ? TRUE : FALSE;
 }
 
 void FormattedNumber::populateFieldPositionIterator(FieldPositionIterator& iterator, UErrorCode& status) {
@@ -787,7 +801,18 @@ void FormattedNumber::populateFieldPositionIterator(FieldPositionIterator& itera
         status = fErrorCode;
         return;
     }
-    fResults->string.populateFieldPositionIterator(iterator, status);
+    fResults->string.getAllFieldPositions(iterator, status);
+}
+
+void FormattedNumber::getAllFieldPositions(FieldPositionIterator& iterator, UErrorCode& status) const {
+    if (U_FAILURE(status)) {
+        return;
+    }
+    if (fResults == nullptr) {
+        status = fErrorCode;
+        return;
+    }
+    fResults->string.getAllFieldPositions(iterator, status);
 }
 
 void FormattedNumber::getDecimalQuantity(DecimalQuantity& output, UErrorCode& status) const {
index 501b02bdcb5837c191f49f8fc93455b4c963bdae..a71e85e7c84050acfd9fe4b3fe0041a18b36b20f 100644 (file)
@@ -413,23 +413,24 @@ bool NumberStringBuilder::contentEquals(const NumberStringBuilder &other) const
     return true;
 }
 
-void NumberStringBuilder::populateFieldPosition(FieldPosition &fp, int32_t offset, UErrorCode &status) const {
+bool NumberStringBuilder::nextFieldPosition(FieldPosition& fp, UErrorCode& status) const {
     int32_t rawField = fp.getField();
 
     if (rawField == FieldPosition::DONT_CARE) {
-        return;
+        return FALSE;
     }
 
     if (rawField < 0 || rawField >= UNUM_FIELD_COUNT) {
         status = U_ILLEGAL_ARGUMENT_ERROR;
-        return;
+        return FALSE;
     }
 
     auto field = static_cast<Field>(rawField);
 
     bool seenStart = false;
     int32_t fractionStart = -1;
-    for (int i = fZero; i <= fZero + fLength; i++) {
+    int32_t startIndex = fp.getEndIndex();
+    for (int i = fZero + startIndex; i <= fZero + fLength; i++) {
         Field _field = UNUM_FIELD_COUNT;
         if (i < fZero + fLength) {
             _field = getFieldPtr()[i];
@@ -439,10 +440,10 @@ void NumberStringBuilder::populateFieldPosition(FieldPosition &fp, int32_t offse
             if (field == UNUM_INTEGER_FIELD && _field == UNUM_GROUPING_SEPARATOR_FIELD) {
                 continue;
             }
-            fp.setEndIndex(i - fZero + offset);
+            fp.setEndIndex(i - fZero);
             break;
         } else if (!seenStart && field == _field) {
-            fp.setBeginIndex(i - fZero + offset);
+            fp.setBeginIndex(i - fZero);
             seenStart = true;
         }
         if (_field == UNUM_INTEGER_FIELD || _field == UNUM_DECIMAL_SEPARATOR_FIELD) {
@@ -450,14 +451,17 @@ void NumberStringBuilder::populateFieldPosition(FieldPosition &fp, int32_t offse
         }
     }
 
-    // Backwards compatibility: FRACTION needs to start after INTEGER if empty
-    if (field == UNUM_FRACTION_FIELD && !seenStart) {
-        fp.setBeginIndex(fractionStart + offset);
-        fp.setEndIndex(fractionStart + offset);
+    // Backwards compatibility: FRACTION needs to start after INTEGER if empty.
+    // Do not return that a field was found, though, since there is not actually a fraction part.
+    if (field == UNUM_FRACTION_FIELD && !seenStart && fractionStart != -1) {
+        fp.setBeginIndex(fractionStart);
+        fp.setEndIndex(fractionStart);
     }
+
+    return seenStart;
 }
 
-void NumberStringBuilder::populateFieldPositionIterator(FieldPositionIterator &fpi, UErrorCode &status) const {
+void NumberStringBuilder::getAllFieldPositions(FieldPositionIterator& fpi, UErrorCode& status) const {
     // TODO: Set an initial capacity on uvec?
     LocalPointer <UVector32> uvec(new UVector32(status), status);
     if (U_FAILURE(status)) {
index f8d11a0fddc12b25220662fd2994096a03a5a472..82e44e30f416d3e1484a7a1c5bf5d6a9bb377c50 100644 (file)
@@ -101,9 +101,9 @@ class U_I18N_API NumberStringBuilder : public UMemory {
 
     bool contentEquals(const NumberStringBuilder &other) const;
 
-    void populateFieldPosition(FieldPosition &fp, int32_t offset, UErrorCode &status) const;
+    bool nextFieldPosition(FieldPosition& fp, UErrorCode& status) const;
 
-    void populateFieldPositionIterator(FieldPositionIterator &fpi, UErrorCode &status) const;
+    void getAllFieldPositions(FieldPositionIterator& fpi, UErrorCode& status) const;
 
   private:
     bool fUsingHeap = false;
index 863250d023f98e1d94b57fa605a67d28993d2d3c..94945178a44b2aee754d50d49f82354bdfd49896 100644 (file)
@@ -60,6 +60,7 @@ class CompactDecimalFormat;
 
 namespace number {
 class LocalizedNumberFormatter;
+class FormattedNumber;
 namespace impl {
 class DecimalQuantity;
 struct DecimalFormatFields;
@@ -2111,6 +2112,9 @@ class U_I18N_API DecimalFormat : public NumberFormat {
 
     const numparse::impl::NumberParserImpl* getCurrencyParser(UErrorCode& status) const;
 
+    static void fieldPositionHelper(const number::FormattedNumber& formatted, FieldPosition& fieldPosition,
+                                    int32_t offset, UErrorCode& status);
+
     void setupFastFormat();
 
     bool fastFormatDouble(double input, UnicodeString& output) const;
index 9176ffaf95440ea3e34719f5acd53e3194e0601e..47b4f9a7aee92101d63a3247de32c1a6a2a784b4 100644 (file)
@@ -2378,11 +2378,45 @@ class U_I18N_API FormattedNumber : public UMemory {
      *            The FieldPosition to populate with the start and end indices of the desired field.
      * @param status
      *            Set if an error occurs while populating the FieldPosition.
-     * @draft ICU 60
+     * @deprecated ICU 62 Use {@link #toCharacterIterator} instead. This method will be removed in a future
+     *             release. See http://bugs.icu-project.org/trac/ticket/13746
      * @see UNumberFormatFields
      */
     void populateFieldPosition(FieldPosition &fieldPosition, UErrorCode &status);
 
+    /**
+     * Determines the start and end indices of the next occurrence of the given <em>field</em> in the
+     * output string. This allows you to determine the locations of, for example, the integer part,
+     * fraction part, or symbols.
+     *
+     * If a field occurs just once, calling this method will find that occurrence and return it. If a
+     * field occurs multiple times, this method may be called repeatedly with the following pattern:
+     *
+     * <pre>
+     * FieldPosition fpos(UNUM_GROUPING_SEPARATOR_FIELD);
+     * while (formattedNumber.nextFieldPosition(fpos, status)) {
+     *   // do something with fpos.
+     * }
+     * </pre>
+     *
+     * This method is useful if you know which field to query. If you want all available field position
+     * information, use #getAllFields().
+     *
+     * @param fieldPosition
+     *            Input+output variable. On input, the "field" property determines which field to look up,
+     *            and the "endIndex" property determines where to begin the search. On output, the
+     *            "beginIndex" field is set to the beginning of the first occurrence of the field after the
+     *            input "endIndex", and "endIndex" is set to the end of that occurrence of the field
+     *            (exclusive index). If a field position is not found, the FieldPosition is not changed and
+     *            the method returns FALSE.
+     * @param status
+     *            Set if an error occurs while populating the FieldPosition.
+     * @return TRUE if a new occurrence of the field was found; FALSE otherwise.
+     * @draft ICU 62
+     * @see UNumberFormatFields
+     */
+    UBool nextFieldPosition(FieldPosition& fieldPosition, UErrorCode& status) const;
+
     /**
      * Export the formatted number to a FieldPositionIterator. This allows you to determine which characters in
      * the output string correspond to which <em>fields</em>, such as the integer part, fraction part, and sign.
@@ -2394,11 +2428,27 @@ class U_I18N_API FormattedNumber : public UMemory {
      *            The FieldPositionIterator to populate with all of the fields present in the formatted number.
      * @param status
      *            Set if an error occurs while populating the FieldPositionIterator.
-     * @draft ICU 60
+     * @deprecated ICU 62 Use {@link #getAllFieldPositions} instead. This method will be removed in a
+     *             future release. See http://bugs.icu-project.org/trac/ticket/13746
      * @see UNumberFormatFields
      */
     void populateFieldPositionIterator(FieldPositionIterator &iterator, UErrorCode &status);
 
+    /**
+     * Export the formatted number to a FieldPositionIterator. This allows you to determine which characters in
+     * the output string correspond to which <em>fields</em>, such as the integer part, fraction part, and sign.
+     *
+     * If information on only one field is needed, use #nextFieldPosition().
+     *
+     * @param iterator
+     *            The FieldPositionIterator to populate with all of the fields present in the formatted number.
+     * @param status
+     *            Set if an error occurs while populating the FieldPositionIterator.
+     * @draft ICU 62
+     * @see UNumberFormatFields
+     */
+    void getAllFieldPositions(FieldPositionIterator &iterator, UErrorCode &status) const;
+
 #ifndef U_HIDE_INTERNAL_API
 
     /**
index 040213fcf5969cd60ddf6688020af9087abac078..e73ab827bbc34df3a13618229dd4f0c04b098c16 100644 (file)
@@ -532,30 +532,44 @@ unumf_resultToString(const UFormattedNumber* uresult, UChar* buffer, int32_t buf
 
 
 /**
- * Determines the start and end indices of the first occurrence of the given field in the output string.
- * This allows you to determine the locations of the integer part, fraction part, and sign.
+ * Determines the start and end indices of the next occurrence of the given <em>field</em> in the
+ * output string. This allows you to determine the locations of, for example, the integer part,
+ * fraction part, or symbols.
  *
- * If a field occurs multiple times in an output string, such as a grouping separator, this method will
- * only ever return the first occurrence. Use unumf_resultGetAllFields() to access all occurrences of an
- * attribute.
+ * If a field occurs just once, calling this method will find that occurrence and return it. If a
+ * field occurs multiple times, this method may be called repeatedly with the following pattern:
  *
- * @param uresult The object containing the formatted number.
- * @param fpos
- *         A pointer to a UFieldPosition. On input, position->field is read. On output,
- *         position->beginIndex and position->endIndex indicate the beginning and ending indices of field
- *         number position->field, if such a field exists.
+ * <pre>
+ * UFieldPosition ufpos = {UNUM_GROUPING_SEPARATOR_FIELD, 0, 0};
+ * while (unumf_resultNextFieldPosition(uresult, ufpos, &ec)) {
+ *   // do something with ufpos.
+ * }
+ * </pre>
+ *
+ * This method is useful if you know which field to query. If you want all available field position
+ * information, use unumf_resultGetAllFieldPositions().
+ *
+ * NOTE: All fields of the UFieldPosition must be initialized before calling this method.
+ *
+ * @param fieldPosition
+ *            Input+output variable. On input, the "field" property determines which field to look up,
+ *            and the "endIndex" property determines where to begin the search. On output, the
+ *            "beginIndex" field is set to the beginning of the first occurrence of the field after the
+ *            input "endIndex", and "endIndex" is set to the end of that occurrence of the field
+ *            (exclusive index). If a field position is not found, the FieldPosition is not changed and
+ *            the method returns FALSE.
  * @param ec Set if an error occurs.
  * @draft ICU 62
  */
-U_DRAFT void U_EXPORT2
-unumf_resultGetField(const UFormattedNumber* uresult, UFieldPosition* ufpos, UErrorCode* ec);
+U_DRAFT UBool U_EXPORT2
+unumf_resultNextFieldPosition(const UFormattedNumber* uresult, UFieldPosition* ufpos, UErrorCode* ec);
 
 
 /**
  * Populates the given iterator with all fields in the formatted output string. This allows you to
  * determine the locations of the integer part, fraction part, and sign.
  *
- * If you need information on only one field, consider using unumf_resultGetField().
+ * If you need information on only one field, use unumf_resultNextFieldPosition().
  *
  * @param uresult The object containing the formatted number.
  * @param fpositer
@@ -570,8 +584,8 @@ unumf_resultGetField(const UFormattedNumber* uresult, UFieldPosition* ufpos, UEr
  * @draft ICU 62
  */
 U_DRAFT void U_EXPORT2
-unumf_resultGetAllFields(const UFormattedNumber* uresult, UFieldPositionIterator* ufpositer,
-                         UErrorCode* ec);
+unumf_resultGetAllFieldPositions(const UFormattedNumber* uresult, UFieldPositionIterator* ufpositer,
+                                 UErrorCode* ec);
 
 
 /**
index b968f43842b87fdd1321338da0195a8b5bb90c82..dc3946f3967128ffb01df702aa06a4147695f1f9 100644 (file)
@@ -83,14 +83,14 @@ static void TestSkeletonFormatToFields() {
 
     // field position test:
     UFieldPosition ufpos = {UNUM_DECIMAL_SEPARATOR_FIELD};
-    unumf_resultGetField(uresult, &ufpos, &ec);
+    unumf_resultNextFieldPosition(uresult, &ufpos, &ec);
     assertIntEquals("Field position should be correct", 14, ufpos.beginIndex);
     assertIntEquals("Field position should be correct", 15, ufpos.endIndex);
 
     // field position iterator test:
     UFieldPositionIterator* ufpositer = ufieldpositer_open(&ec);
     assertSuccess("Should create iterator without error", &ec);
-    unumf_resultGetAllFields(uresult, ufpositer, &ec);
+    unumf_resultGetAllFieldPositions(uresult, ufpositer, &ec);
     static const UFieldPosition expectedFields[] = {
             // Field, begin index, end index
             {UNUM_SIGN_FIELD, 0, 1},
@@ -128,6 +128,18 @@ static void TestSkeletonFormatToFields() {
     actual.field = ufieldpositer_next(ufpositer, &actual.beginIndex, &actual.endIndex);
     assertTrue("No more fields; should return a negative index", actual.field < 0);
 
+    // next field iteration:
+    actual.field = UNUM_GROUPING_SEPARATOR_FIELD;
+    actual.beginIndex = 0;
+    actual.endIndex = 0;
+    int32_t i = 1;
+    while (unumf_resultNextFieldPosition(uresult, &actual, &ec)) {
+        UFieldPosition expected = expectedFields[i++];
+        assertIntEquals("Grouping separator begin index", expected.beginIndex, actual.beginIndex);
+        assertIntEquals("Grouping separator end index", expected.endIndex, actual.endIndex);
+    }
+    assertIntEquals("Should have seen all grouping separators", 4, i);
+
     // cleanup:
     unumf_closeResult(uresult);
     unumf_close(uformatter);
index 33a35e570f71930692700361cc4724b29259b3e6..e689196088096bf4d4a0b89bad58ebea0cd0bcef 100644 (file)
@@ -67,6 +67,7 @@ class NumberFormatterApiTest : public IntlTest {
     void scale();
     void locale();
     void formatTypes();
+    void fieldPosition();
     void errors();
     void validRanges();
     void copyMove();
index 65611729952f17825f0e687ff775092a6893bcba..bbb2f767a13e0070c5c72d9a4b6e0edc0530e658 100644 (file)
@@ -81,6 +81,7 @@ void NumberFormatterApiTest::runIndexedTest(int32_t index, UBool exec, const cha
         TESTCASE_AUTO(scale);
         TESTCASE_AUTO(locale);
         TESTCASE_AUTO(formatTypes);
+        TESTCASE_AUTO(fieldPosition);
         TESTCASE_AUTO(errors);
         TESTCASE_AUTO(validRanges);
         TESTCASE_AUTO(copyMove);
@@ -2066,6 +2067,94 @@ void NumberFormatterApiTest::formatTypes() {
     assertEquals("Format decNumber to 40 digits", str, actual);
 }
 
+void NumberFormatterApiTest::fieldPosition() {
+    IcuTestErrorCode status(*this, "fieldPosition");
+    FormattedNumber fmtd = NumberFormatter::withLocale("en").formatDouble(-9876543210.12, status);
+    assertEquals("Should have expected format output", u"-9,876,543,210.12", fmtd.toString(status));
+
+    static const UFieldPosition expectedFieldPositions[] = {
+            // field, begin index, end index
+            {UNUM_SIGN_FIELD, 0, 1},
+            {UNUM_GROUPING_SEPARATOR_FIELD, 2, 3},
+            {UNUM_GROUPING_SEPARATOR_FIELD, 6, 7},
+            {UNUM_GROUPING_SEPARATOR_FIELD, 10, 11},
+            {UNUM_INTEGER_FIELD, 1, 14},
+            {UNUM_DECIMAL_SEPARATOR_FIELD, 14, 15},
+            {UNUM_FRACTION_FIELD, 15, 17}};
+
+    FieldPositionIterator fpi;
+    fmtd.getAllFieldPositions(fpi, status);
+    int32_t i = 0;
+    FieldPosition actual;
+    while (fpi.next(actual)) {
+        UFieldPosition expected = expectedFieldPositions[i++];
+        assertEquals(
+                UnicodeString(u"Field, case #") + Int64ToUnicodeString(i),
+                expected.field,
+                actual.getField());
+        assertEquals(
+                UnicodeString(u"Iterator, begin index, case #") + Int64ToUnicodeString(i),
+                expected.beginIndex,
+                actual.getBeginIndex());
+        assertEquals(
+                UnicodeString(u"Iterator, end index, case #") + Int64ToUnicodeString(i),
+                expected.endIndex,
+                actual.getEndIndex());
+
+        // Check for the first location of the field
+        if (expected.field != UNUM_GROUPING_SEPARATOR_FIELD) {
+            FieldPosition actual2(expected.field);
+            UBool found = fmtd.nextFieldPosition(actual2, status);
+            assertEquals(
+                    UnicodeString(u"Next, found first time, case #") + Int64ToUnicodeString(i),
+                    (UBool) TRUE,
+                    found);
+            assertEquals(
+                    UnicodeString(u"Next, begin index, case #") + Int64ToUnicodeString(i),
+                    expected.beginIndex,
+                    actual2.getBeginIndex());
+            assertEquals(
+                    UnicodeString(u"Next, end index, case #") + Int64ToUnicodeString(i),
+                    expected.endIndex,
+                    actual2.getEndIndex());
+            found = fmtd.nextFieldPosition(actual2, status);
+            assertEquals(
+                    UnicodeString(u"Next, found second time, case #") + Int64ToUnicodeString(i),
+                    (UBool) FALSE,
+                    found);
+        }
+    }
+    assertEquals(
+            "Should have seen every field position",
+            sizeof(expectedFieldPositions) / sizeof(*expectedFieldPositions),
+            i);
+
+    // Test the iteration functionality of nextFieldPosition
+    actual = {UNUM_GROUPING_SEPARATOR_FIELD};
+    i = 1;
+    while (fmtd.nextFieldPosition(actual, status)) {
+        UFieldPosition expected = expectedFieldPositions[i++];
+        assertEquals(
+                UnicodeString(u"Next for grouping, field, case #") + Int64ToUnicodeString(i),
+                expected.field,
+                actual.getField());
+        assertEquals(
+                UnicodeString(u"Next for grouping, begin index, case #") + Int64ToUnicodeString(i),
+                expected.beginIndex,
+                actual.getBeginIndex());
+        assertEquals(
+                UnicodeString(u"Next for grouping, end index, case #") + Int64ToUnicodeString(i),
+                expected.endIndex,
+                actual.getEndIndex());
+    }
+    assertEquals(u"Should have seen all grouping separators", 4, i);
+
+    // Make sure strings without fraction do not contain fraction field
+    actual = {UNUM_FRACTION_FIELD};
+    fmtd = NumberFormatter::withLocale("en").formatInt(5, status);
+    assertFalse(u"No fraction part in an integer", fmtd.nextFieldPosition(actual, status));
+}
+
 void NumberFormatterApiTest::errors() {
     LocalizedNumberFormatter lnf = NumberFormatter::withLocale(Locale::getEnglish()).rounding(
             Rounder::fixedFraction(
@@ -2293,7 +2382,7 @@ void NumberFormatterApiTest::localPointerCAPI() {
 
     // Get the location of the percent sign:
     UFieldPosition ufpos = {UNUM_PERCENT_FIELD, 0, 0};
-    unumf_resultGetField(uresult.getAlias(), &ufpos, &ec);
+    unumf_resultNextFieldPosition(uresult.getAlias(), &ufpos, &ec);
     assertEquals("Percent sign location within '0.00987%'", 7, ufpos.beginIndex);
     assertEquals("Percent sign location within '0.00987%'", 8, ufpos.endIndex);
 
index 04ef81b7da69586c76128f0d151912c286837c1f..3106bedb3106e4374a3a970e79e1a75aa3f4409b 100644 (file)
@@ -191,7 +191,7 @@ void NumberStringBuilderTest::testFields() {
         // Very basic FieldPosition test. More robust tests happen in NumberFormatTest.
         // Let NumberFormatTest also take care of FieldPositionIterator material.
         FieldPosition fp(UNUM_CURRENCY_FIELD);
-        sb.populateFieldPosition(fp, 0, status);
+        sb.nextFieldPosition(fp, status);
         assertSuccess("Populating the FieldPosition", status);
         assertEquals("Currency start position", str.length(), fp.getBeginIndex());
         assertEquals("Currency end position", str.length() * 2, fp.getEndIndex());
index f95603fc156baa6d0e2a130f79000e2e8cb25bf1..2eae5cd1f347d413a5f0ed8216fa05c04e193380 100644 (file)
@@ -486,10 +486,9 @@ public class NumberStringBuilder implements CharSequence {
      *
      * @param fp
      *            The FieldPosition to populate.
-     * @param offset
-     *            An offset to add to the field position index; can be zero.
+     * @return true if the field was found; false if it was not found.
      */
-    public void populateFieldPosition(FieldPosition fp, int offset) {
+    public boolean nextFieldPosition(FieldPosition fp) {
         java.text.Format.Field rawField = fp.getFieldAttribute();
 
         if (rawField == null) {
@@ -500,21 +499,22 @@ public class NumberStringBuilder implements CharSequence {
                 rawField = NumberFormat.Field.FRACTION;
             } else {
                 // No field is set
-                return;
+                return false;
             }
         }
 
-        if (!(rawField instanceof com.ibm.icu.text.NumberFormat.Field)) {
+        if (!(rawField instanceof NumberFormat.Field)) {
             throw new IllegalArgumentException(
                     "You must pass an instance of com.ibm.icu.text.NumberFormat.Field as your FieldPosition attribute.  You passed: "
                             + rawField.getClass().toString());
         }
 
-        /* com.ibm.icu.text.NumberFormat. */ Field field = (Field) rawField;
+        NumberFormat.Field field = (NumberFormat.Field) rawField;
 
         boolean seenStart = false;
         int fractionStart = -1;
-        for (int i = zero; i <= zero + length; i++) {
+        int startIndex = fp.getEndIndex();
+        for (int i = zero + startIndex; i <= zero + length; i++) {
             Field _field = (i < zero + length) ? fields[i] : null;
             if (seenStart && field != _field) {
                 // Special case: GROUPING_SEPARATOR counts as an INTEGER.
@@ -522,10 +522,10 @@ public class NumberStringBuilder implements CharSequence {
                         && _field == NumberFormat.Field.GROUPING_SEPARATOR) {
                     continue;
                 }
-                fp.setEndIndex(i - zero + offset);
+                fp.setEndIndex(i - zero);
                 break;
             } else if (!seenStart && field == _field) {
-                fp.setBeginIndex(i - zero + offset);
+                fp.setBeginIndex(i - zero);
                 seenStart = true;
             }
             if (_field == NumberFormat.Field.INTEGER || _field == NumberFormat.Field.DECIMAL_SEPARATOR) {
@@ -533,14 +533,17 @@ public class NumberStringBuilder implements CharSequence {
             }
         }
 
-        // Backwards compatibility: FRACTION needs to start after INTEGER if empty
-        if (field == NumberFormat.Field.FRACTION && !seenStart) {
-            fp.setBeginIndex(fractionStart + offset);
-            fp.setEndIndex(fractionStart + offset);
+        // Backwards compatibility: FRACTION needs to start after INTEGER if empty.
+        // Do not return that a field was found, though, since there is not actually a fraction part.
+        if (field == NumberFormat.Field.FRACTION && !seenStart && fractionStart != -1) {
+            fp.setBeginIndex(fractionStart);
+            fp.setEndIndex(fractionStart);
         }
+
+        return seenStart;
     }
 
-    public AttributedCharacterIterator getIterator() {
+    public AttributedCharacterIterator toCharacterIterator() {
         AttributedString as = new AttributedString(toString());
         Field current = null;
         int currentStart = -1;
index 8c8a10aeace2e336dd1870deb3e405704ed540ba..72acb399bec4c6508a22f9acb995bcb374aa3fd9 100644 (file)
@@ -85,43 +85,91 @@ public class FormattedNumber {
      *
      * @param fieldPosition
      *            The FieldPosition to populate with the start and end indices of the desired field.
-     * @draft ICU 60
-     * @provisional This API might change or be removed in a future release.
+     * @deprecated ICU 62 Use {@link #nextFieldPosition} instead. This method will be removed in a future
+     *             release. See http://bugs.icu-project.org/trac/ticket/13746
      * @see com.ibm.icu.text.NumberFormat.Field
      * @see NumberFormatter
      */
+    @Deprecated
     public void populateFieldPosition(FieldPosition fieldPosition) {
-        populateFieldPosition(fieldPosition, 0);
+        // in case any users were depending on the old behavior:
+        fieldPosition.setEndIndex(0);
+        nextFieldPosition(fieldPosition);
     }
 
     /**
-     * @internal
-     * @deprecated This API is ICU internal only.
+     * Determines the start and end indices of the next occurrence of the given <em>field</em> in the
+     * output string. This allows you to determine the locations of, for example, the integer part,
+     * fraction part, or symbols.
+     * <p>
+     * If a field occurs just once, calling this method will find that occurrence and return it. If a
+     * field occurs multiple times, this method may be called repeatedly with the following pattern:
+     * <p>
+     * <pre>
+     * FieldPosition fpos = new FieldPosition(NumberFormat.Field.GROUPING_SEPARATOR);
+     * while (formattedNumber.nextFieldPosition(fpos, status)) {
+     *   // do something with fpos.
+     * }
+     * </pre>
+     * <p>
+     * This method is useful if you know which field to query. If you want all available field position
+     * information, use #getAllFields().
+     *
+     * @param fieldPosition
+     *            Input+output variable. On input, the "field" property determines which field to look up,
+     *            and the "endIndex" property determines where to begin the search. On output, the
+     *            "beginIndex" field is set to the beginning of the first occurrence of the field after the
+     *            input "endIndex", and "endIndex" is set to the end of that occurrence of the field
+     *            (exclusive index). If a field position is not found, the FieldPosition is not changed and
+     *            the method returns false.
+     * @return true if a new occurrence of the field was found; false otherwise.
+     * @draft ICU 62
+     * @provisional This API might change or be removed in a future release.
+     * @see com.ibm.icu.text.NumberFormat.Field
+     * @see NumberFormatter
      */
-    @Deprecated
-    public void populateFieldPosition(FieldPosition fieldPosition, int offset) {
-        nsb.populateFieldPosition(fieldPosition, offset);
+    public boolean nextFieldPosition(FieldPosition fieldPosition) {
         fq.populateUFieldPosition(fieldPosition);
+        return nsb.nextFieldPosition(fieldPosition);
     }
 
     /**
      * Export the formatted number as an AttributedCharacterIterator. This allows you to determine which
      * characters in the output string correspond to which <em>fields</em>, such as the integer part,
      * fraction part, and sign.
+     * <p>
+     * If information on only one field is needed, consider using populateFieldPosition() instead.
      *
+     * @return An AttributedCharacterIterator, containing information on the field attributes of the
+     *         number string.
+     * @deprecated ICU 62 Use {@link #toCharacterIterator} instead. This method will be removed in a future
+     *             release. See http://bugs.icu-project.org/trac/ticket/13746
+     * @see com.ibm.icu.text.NumberFormat.Field
+     * @see AttributedCharacterIterator
+     * @see NumberFormatter
+     */
+    @Deprecated
+    public AttributedCharacterIterator getFieldIterator() {
+        return nsb.toCharacterIterator();
+    }
+
+    /**
+     * Export the formatted number as an AttributedCharacterIterator. This allows you to determine which
+     * characters in the output string correspond to which <em>fields</em>, such as the integer part,
+     * fraction part, and sign.
      * <p>
      * If information on only one field is needed, consider using populateFieldPosition() instead.
      *
      * @return An AttributedCharacterIterator, containing information on the field attributes of the
      *         number string.
-     * @draft ICU 60
+     * @draft ICU 62
      * @provisional This API might change or be removed in a future release.
      * @see com.ibm.icu.text.NumberFormat.Field
      * @see AttributedCharacterIterator
      * @see NumberFormatter
      */
-    public AttributedCharacterIterator getFieldIterator() {
-        return nsb.getIterator();
+    public AttributedCharacterIterator toCharacterIterator() {
+        return nsb.toCharacterIterator();
     }
 
     /**
index 1800efc47a4c77aa069494a15df4c0831f8986cb..7bf58f9273f854e9623bb54c9423f516a3b6cd0b 100644 (file)
@@ -709,7 +709,7 @@ public class DecimalFormat extends NumberFormat {
   @Override
   public StringBuffer format(double number, StringBuffer result, FieldPosition fieldPosition) {
     FormattedNumber output = formatter.format(number);
-    output.populateFieldPosition(fieldPosition, result.length());
+    fieldPositionHelper(output, fieldPosition, result.length());
     output.appendTo(result);
     return result;
   }
@@ -722,7 +722,7 @@ public class DecimalFormat extends NumberFormat {
   @Override
   public StringBuffer format(long number, StringBuffer result, FieldPosition fieldPosition) {
     FormattedNumber output = formatter.format(number);
-    output.populateFieldPosition(fieldPosition, result.length());
+    fieldPositionHelper(output, fieldPosition, result.length());
     output.appendTo(result);
     return result;
   }
@@ -735,7 +735,7 @@ public class DecimalFormat extends NumberFormat {
   @Override
   public StringBuffer format(BigInteger number, StringBuffer result, FieldPosition fieldPosition) {
     FormattedNumber output = formatter.format(number);
-    output.populateFieldPosition(fieldPosition, result.length());
+    fieldPositionHelper(output, fieldPosition, result.length());
     output.appendTo(result);
     return result;
   }
@@ -749,7 +749,7 @@ public class DecimalFormat extends NumberFormat {
   public StringBuffer format(
       java.math.BigDecimal number, StringBuffer result, FieldPosition fieldPosition) {
     FormattedNumber output = formatter.format(number);
-    output.populateFieldPosition(fieldPosition, result.length());
+    fieldPositionHelper(output, fieldPosition, result.length());
     output.appendTo(result);
     return result;
   }
@@ -762,7 +762,7 @@ public class DecimalFormat extends NumberFormat {
   @Override
   public StringBuffer format(BigDecimal number, StringBuffer result, FieldPosition fieldPosition) {
     FormattedNumber output = formatter.format(number);
-    output.populateFieldPosition(fieldPosition, result.length());
+    fieldPositionHelper(output, fieldPosition, result.length());
     output.appendTo(result);
     return result;
   }
@@ -786,11 +786,11 @@ public class DecimalFormat extends NumberFormat {
    * @stable ICU 3.0
    */
   @Override
-  public StringBuffer format(CurrencyAmount currAmt, StringBuffer toAppendTo, FieldPosition pos) {
+  public StringBuffer format(CurrencyAmount currAmt, StringBuffer result, FieldPosition fieldPosition) {
     FormattedNumber output = formatter.format(currAmt);
-    output.populateFieldPosition(pos, toAppendTo.length());
-    output.appendTo(toAppendTo);
-    return toAppendTo;
+    fieldPositionHelper(output, fieldPosition, result.length());
+    output.appendTo(result);
+    return result;
   }
 
   /**
@@ -2566,6 +2566,15 @@ public class DecimalFormat extends NumberFormat {
     PatternStringParser.parseToExistingProperties(pattern, properties, ignoreRounding);
   }
 
+  static void fieldPositionHelper(FormattedNumber formatted, FieldPosition fieldPosition, int offset) {
+      fieldPosition.setEndIndex(0); // always return first occurrence
+      boolean found = formatted.nextFieldPosition(fieldPosition);
+      if (found && offset != 0) {
+          fieldPosition.setBeginIndex(fieldPosition.getBeginIndex() + offset);
+          fieldPosition.setEndIndex(fieldPosition.getEndIndex() + offset);
+      }
+  }
+
   /**
    * @internal
    * @deprecated This API is ICU internal only.
index cf0d2dcdda25d5e94745859718c9db4530daaac5..0fe0220adc0748f1c94556a0d96804c891294153 100644 (file)
@@ -388,7 +388,7 @@ public class MeasureFormat extends UFormat {
         FormattedNumber result = getUnitFormatterFromCache(NUMBER_FORMATTER_STANDARD,
                 measure.getUnit(),
                 perUnit).format(measure.getNumber());
-        result.populateFieldPosition(pos, appendTo.length());
+        DecimalFormat.fieldPositionHelper(result, pos, appendTo.length());
         result.appendTo(appendTo);
         return appendTo;
     }
index d27de4be1ee62352ad0ba27adb7ac87cb4c86970..6a07b30d5057bb74e03a9ed805060a88d5bbafab 100644 (file)
@@ -3,6 +3,7 @@
 package com.ibm.icu.dev.test.number;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
@@ -11,6 +12,8 @@ import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
+import java.text.AttributedCharacterIterator;
+import java.text.FieldPosition;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Locale;
@@ -26,6 +29,7 @@ import com.ibm.icu.impl.number.Padder;
 import com.ibm.icu.impl.number.Padder.PadPosition;
 import com.ibm.icu.impl.number.PatternStringParser;
 import com.ibm.icu.number.CompactNotation;
+import com.ibm.icu.number.FormattedNumber;
 import com.ibm.icu.number.FractionRounder;
 import com.ibm.icu.number.IntegerWidth;
 import com.ibm.icu.number.LocalizedNumberFormatter;
@@ -40,6 +44,7 @@ import com.ibm.icu.number.Scale;
 import com.ibm.icu.number.ScientificNotation;
 import com.ibm.icu.number.UnlocalizedNumberFormatter;
 import com.ibm.icu.text.DecimalFormatSymbols;
+import com.ibm.icu.text.NumberFormat;
 import com.ibm.icu.text.NumberingSystem;
 import com.ibm.icu.util.Currency;
 import com.ibm.icu.util.Currency.CurrencyUsage;
@@ -2040,6 +2045,79 @@ public class NumberFormatterApiTest {
                         .toString());
     }
 
+    @Test
+    public void fieldPosition() {
+        FormattedNumber fmtd = NumberFormatter.withLocale(ULocale.ENGLISH).format(-9876543210.12);
+        assertEquals("Should have expected format output", "-9,876,543,210.12", fmtd.toString());
+
+        Object[][] expectedFieldPositions = new Object[][]{
+                {NumberFormat.Field.SIGN, 0, 1},
+                {NumberFormat.Field.GROUPING_SEPARATOR, 2, 3},
+                {NumberFormat.Field.GROUPING_SEPARATOR, 6, 7},
+                {NumberFormat.Field.GROUPING_SEPARATOR, 10, 11},
+                {NumberFormat.Field.INTEGER, 1, 14},
+                {NumberFormat.Field.DECIMAL_SEPARATOR, 14, 15},
+                {NumberFormat.Field.FRACTION, 15, 17}};
+
+        AttributedCharacterIterator fpi = fmtd.getFieldIterator();
+        Set<AttributedCharacterIterator.Attribute> allAttributes = fpi.getAllAttributeKeys();
+        assertEquals("All known fields should be in the iterator", 5, allAttributes.size());
+        assertEquals("Iterator should have length of string output", 17, fpi.getEndIndex());
+        int i = 0;
+        for (char c = fpi.first(); c != AttributedCharacterIterator.DONE; c = fpi.next(), i++) {
+            Set<AttributedCharacterIterator.Attribute> currentAttributes = fpi.getAttributes().keySet();
+            int attributesRemaining = currentAttributes.size();
+            for (Object[] cas : expectedFieldPositions) {
+                NumberFormat.Field expectedField = (NumberFormat.Field) cas[0];
+                int expectedBeginIndex = (Integer) cas[1];
+                int expectedEndIndex = (Integer) cas[2];
+                if (expectedBeginIndex > i || expectedEndIndex <= i) {
+                    // Field position does not overlap with the current character
+                    continue;
+                }
+
+                assertTrue("Current character should have expected field", currentAttributes.contains(expectedField));
+                assertTrue("Field should be a known attribute", allAttributes.contains(expectedField));
+                int actualBeginIndex = fpi.getRunStart(expectedField);
+                int actualEndIndex = fpi.getRunLimit(expectedField);
+                assertEquals(expectedField + " begin index @" + i, expectedBeginIndex, actualBeginIndex);
+                assertEquals(expectedField + " end index @" + i, expectedEndIndex, actualEndIndex);
+                attributesRemaining--;
+            }
+            assertEquals("Should have looked at every field", 0, attributesRemaining);
+        }
+        assertEquals("Should have looked at every character", 17, i);
+
+        // Test the iteration functionality of nextFieldPosition
+        FieldPosition actual = new FieldPosition(NumberFormat.Field.GROUPING_SEPARATOR);
+        i = 1;
+        while (fmtd.nextFieldPosition(actual)) {
+            Object[] cas = expectedFieldPositions[i++];
+            NumberFormat.Field expectedField = (NumberFormat.Field) cas[0];
+            int expectedBeginIndex = (Integer) cas[1];
+            int expectedEndIndex = (Integer) cas[2];
+
+            assertEquals(
+                    "Next for grouping, field, case #" + i,
+                    expectedField,
+                    actual.getFieldAttribute());
+            assertEquals(
+                    "Next for grouping, begin index, case #" + i,
+                    expectedBeginIndex,
+                    actual.getBeginIndex());
+            assertEquals(
+                    "Next for grouping, end index, case #" + i,
+                    expectedEndIndex,
+                    actual.getEndIndex());
+        }
+        assertEquals("Should have seen all grouping separators", 4, i);
+
+        // Make sure strings without fraction do not contain fraction field
+        actual = new FieldPosition(NumberFormat.Field.FRACTION);
+        fmtd = NumberFormatter.withLocale(ULocale.ENGLISH).format(5);
+        assertFalse("No fraction part in an integer", fmtd.nextFieldPosition(actual));
+    }
+
     @Test
     public void plurals() {
         // TODO: Expand this test.
index 6b7e0a2d338a06c42cb899d1854a00986a06097b..e838d30b5fc0253bcf3deb3b0555bad7a68130a8 100644 (file)
@@ -170,7 +170,7 @@ public class NumberStringBuilderTest {
             // Very basic FieldPosition test. More robust tests happen in NumberFormatTest.
             // Let NumberFormatTest also take care of AttributedCharacterIterator material.
             FieldPosition fp = new FieldPosition(NumberFormat.Field.CURRENCY);
-            sb.populateFieldPosition(fp, 0);
+            sb.nextFieldPosition(fp);
             assertEquals(str.length(), fp.getBeginIndex());
             assertEquals(str.length() * 2, fp.getEndIndex());