]> granicus.if.org Git - icu/commitdiff
ICU-20138 Implementing ufmtval_nextPosition and additional test infra.
authorShane F. Carr <shane@unicode.org>
Mon, 4 Feb 2019 22:21:41 +0000 (14:21 -0800)
committerShane F. Carr <shane@unicode.org>
Wed, 6 Feb 2019 06:54:13 +0000 (22:54 -0800)
- Adds test infra for multi-category formatted values.
- Adds helper method ConstrainedFieldPosition#matchesField, currently internal.

icu4c/source/i18n/formattedvalue.cpp
icu4c/source/i18n/unicode/formattedvalue.h
icu4c/source/i18n/unicode/uformattedvalue.h
icu4c/source/test/cintltst/cformtst.h
icu4c/source/test/cintltst/uformattedvaluetst.c
icu4c/source/test/cintltst/unumberformattertst.c
icu4c/source/test/intltest/formattedvaluetest.cpp
icu4c/source/test/intltest/itformat.h
icu4c/source/test/intltest/numbertest_api.cpp
icu4j/main/classes/core/src/com/ibm/icu/text/ConstrainedFieldPosition.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/FormattedValueTest.java

index fa1888994f22fd9777ffa40547b5fc01703c9b58..219834a30200339a5aa78c3af736662f7ee840d9 100644 (file)
@@ -51,6 +51,19 @@ void ConstrainedFieldPosition::setState(
     fLimit = limit;
 }
 
+UBool ConstrainedFieldPosition::matchesField(UFieldCategory category, int32_t field) {
+    switch (fConstraint) {
+    case UCFPOS_CONSTRAINT_NONE:
+        return TRUE;
+    case UCFPOS_CONSTRAINT_CATEGORY:
+        return fCategory == category;
+    case UCFPOS_CONSTRAINT_FIELD:
+        return fCategory == category && fField == field;
+    default:
+        UPRV_UNREACHABLE;
+    }
+}
+
 
 FormattedValue::~FormattedValue() = default;
 
@@ -200,6 +213,20 @@ ufmtval_getString(
 }
 
 
+U_DRAFT UBool U_EXPORT2
+ufmtval_nextPosition(
+        const UFormattedValue* ufmtval,
+        UConstrainedFieldPosition* ucfpos,
+        UErrorCode* ec) {
+    const auto* fmtval = number::impl::UFormattedValueApiHelper::validate(ufmtval, *ec);
+    auto* cfpos = UConstrainedFieldPositionImpl::validate(ucfpos, *ec);
+    if (U_FAILURE(*ec)) {
+        return FALSE;
+    }
+    return fmtval->fFormattedValue->nextPosition(cfpos->fImpl, *ec);
+}
+
+
 U_NAMESPACE_END
 
 #endif /* #if !UCONFIG_NO_FORMATTING */
index 54593d60689bb94d606cb350a2cf055730bd3f45..ee2f615e8ef9a286b17074cf239d354cf8459af9 100644 (file)
@@ -221,6 +221,9 @@ class U_I18N_API ConstrainedFieldPosition : public UMemory {
         int32_t start,
         int32_t limit);
 
+    /** @internal */
+    UBool matchesField(UFieldCategory category, int32_t field);
+
   private:
     int64_t fContext = 0LL;
     int32_t fField = 0;
index d24ecb2fb2ebd997c5b8b1228b7a4868afc1cdd9..dee34e69d970e581779201db8d77f350ed639519 100644 (file)
@@ -54,6 +54,11 @@ typedef enum UFieldCategory {
      */
     UFIELD_CATEGORY_LIST,
 
+#ifndef U_HIDE_INTERNAL_API
+    /** @internal */
+    UFIELD_CATEGORY_COUNT
+#endif
+
 } UFieldCategory;
 
 
index e7c233d525dbe159b895898dea317d656c3ec801..bcee4c42f285ad8277eb3a05b3a1945e7e768201 100644 (file)
 UChar* myDateFormat(UDateFormat *dat, UDate d); 
 
 
-// The following is implemented in uformattedvaluetest.c
-// TODO: When needed, add overload with a different category for each position
+typedef struct UFieldPositionWithCategory {
+    UFieldCategory category;
+    int32_t field;
+    int32_t beginIndex;
+    int32_t endIndex;
+} UFieldPositionWithCategory;
+
+// The following are implemented in uformattedvaluetest.c
 void checkFormattedValue(
     const char* message,
     const UFormattedValue* fv,
@@ -42,6 +48,13 @@ void checkFormattedValue(
     const UFieldPosition* expectedFieldPositions,
     int32_t expectedFieldPositionsLength);
 
+void checkMixedFormattedValue(
+    const char* message,
+    const UFormattedValue* fv,
+    const UChar* expectedString,
+    const UFieldPositionWithCategory* expectedFieldPositions,
+    int32_t length);
+
 
 #endif /* #if !UCONFIG_NO_FORMATTING */
 
index 98a16e19826d2e966fa2644aeda8f78f592f7e04..a6f4f6d3861659138087e472ec0f9d9671c7e0cb 100644 (file)
@@ -170,23 +170,83 @@ static void AssertAllPartsEqual(
 }
 
 
-// Declared in cformtst.h
-void checkFormattedValue(
+static void checkFormattedValueString(
         const char* message,
         const UFormattedValue* fv,
         const UChar* expectedString,
-        UFieldCategory expectedCategory,
-        const UFieldPosition* expectedFieldPositions,
-        int32_t expectedFieldPositionsLength) {
-    UErrorCode status = U_ZERO_ERROR;
+        UErrorCode* ec) {
     int32_t length;
-    const UChar* actualString = ufmtval_getString(fv, &length, &status);
-    assertSuccess(message, &status);
+    const UChar* actualString = ufmtval_getString(fv, &length, ec);
+    assertSuccess(message, ec);
     // The string is guaranteed to be NUL-terminated.
     int32_t actualLength = u_strlen(actualString);
     assertIntEquals(message, actualLength, length);
     assertUEquals(message, expectedString, actualString);
 }
 
+// Declared in cformtst.h
+void checkFormattedValue(
+        const char* message,
+        const UFormattedValue* fv,
+        const UChar* expectedString,
+        UFieldCategory expectedCategory,
+        const UFieldPosition* expectedFieldPositions,
+        int32_t expectedFieldPositionsLength) {
+    UErrorCode ec = U_ZERO_ERROR;
+    checkFormattedValueString(message, fv, expectedString, &ec);
+    if (U_FAILURE(ec)) { return; }
+
+    // Basic loop over the fields (more rigorous testing in C++)
+    UConstrainedFieldPosition* ucfpos = ucfpos_open(&ec);
+    int32_t i = 0;
+    while (ufmtval_nextPosition(fv, ucfpos, &ec)) {
+        assertIntEquals("category",
+            expectedCategory, ucfpos_getCategory(ucfpos, &ec));
+        assertIntEquals("field",
+            expectedFieldPositions[i].field, ucfpos_getField(ucfpos, &ec));
+        int32_t start, limit;
+        ucfpos_getIndexes(ucfpos, &start, &limit, &ec);
+        assertIntEquals("start",
+            expectedFieldPositions[i].beginIndex, start);
+        assertIntEquals("limit",
+            expectedFieldPositions[i].endIndex, limit);
+        i++;
+    }
+    assertTrue("After loop", !ufmtval_nextPosition(fv, ucfpos, &ec));
+    assertSuccess("After loop", &ec);
+    ucfpos_close(ucfpos);
+}
+
+void checkMixedFormattedValue(
+        const char* message,
+        const UFormattedValue* fv,
+        const UChar* expectedString,
+        const UFieldPositionWithCategory* expectedFieldPositions,
+        int32_t length) {
+    UErrorCode ec = U_ZERO_ERROR;
+    checkFormattedValueString(message, fv, expectedString, &ec);
+    if (U_FAILURE(ec)) { return; }
+
+    // Basic loop over the fields (more rigorous testing in C++)
+    UConstrainedFieldPosition* ucfpos = ucfpos_open(&ec);
+    int32_t i = 0;
+    while (ufmtval_nextPosition(fv, ucfpos, &ec)) {
+        assertIntEquals("category",
+            expectedFieldPositions[i].category, ucfpos_getCategory(ucfpos, &ec));
+        assertIntEquals("field",
+            expectedFieldPositions[i].field, ucfpos_getField(ucfpos, &ec));
+        int32_t start, limit;
+        ucfpos_getIndexes(ucfpos, &start, &limit, &ec);
+        assertIntEquals("start",
+            expectedFieldPositions[i].beginIndex, start);
+        assertIntEquals("limit",
+            expectedFieldPositions[i].endIndex, limit);
+        i++;
+    }
+    assertTrue("After loop", !ufmtval_nextPosition(fv, ucfpos, &ec));
+    assertSuccess("After loop", &ec);
+    ucfpos_close(ucfpos);
+}
+
 
 #endif /* #if !UCONFIG_NO_FORMATTING */
index bbbdfb82a8addfce5d56f60521b5fb2e0505fc68..59e65f2afdb57e23291707658dcaa0fedafa1b12 100644 (file)
@@ -211,12 +211,10 @@ static void TestFormattedValue() {
         assertSuccess("Should convert without error", &ec);
         static const UFieldPosition expectedFieldPositions[] = {
             // field, begin index, end index
-            {UNUM_GROUPING_SEPARATOR_FIELD, 2, 3},
-            {UNUM_GROUPING_SEPARATOR_FIELD, 6, 7},
-            {UNUM_INTEGER_FIELD, 0, 10},
-            {UNUM_GROUPING_SEPARATOR_FIELD, 13, 14},
-            {UNUM_GROUPING_SEPARATOR_FIELD, 17, 18},
-            {UNUM_INTEGER_FIELD, 11, 21}};
+            {UNUM_INTEGER_FIELD, 0, 2},
+            {UNUM_DECIMAL_SEPARATOR_FIELD, 2, 3},
+            {UNUM_FRACTION_FIELD, 3, 5},
+            {UNUM_COMPACT_FIELD, 5, 6}};
         checkFormattedValue(
             "FormattedNumber as FormattedValue",
             fv,
index 1cd1e3c336d52ad7079c0e9eba1a483add47c38d..b8a911e7dac95e04156b2747728dd40dcb8456db 100644 (file)
@@ -159,7 +159,39 @@ void IntlTestWithFieldPosition::checkFormattedValue(
         UFieldCategory expectedCategory,
         const UFieldPosition* expectedFieldPositions,
         int32_t length) {
-    IcuTestErrorCode status(*this, "checkFormattedValue");
+    LocalArray<UFieldPositionWithCategory> converted(new UFieldPositionWithCategory[length]);
+    for (int32_t i=0; i<length; i++) {
+        converted[i].category = expectedCategory;
+        converted[i].field = expectedFieldPositions[i].field;
+        converted[i].beginIndex = expectedFieldPositions[i].beginIndex;
+        converted[i].endIndex = expectedFieldPositions[i].endIndex;
+    }
+    checkMixedFormattedValue(message, fv, expectedString, converted.getAlias(), length);
+}
+
+
+UnicodeString CFPosToUnicodeString(const ConstrainedFieldPosition& cfpos) {
+    UnicodeString sb;
+    sb.append(u"CFPos[");
+    sb.append(Int64ToUnicodeString(cfpos.getStart()));
+    sb.append(u'-');
+    sb.append(Int64ToUnicodeString(cfpos.getLimit()));
+    sb.append(u' ');
+    sb.append(Int64ToUnicodeString(cfpos.getCategory()));
+    sb.append(u':');
+    sb.append(Int64ToUnicodeString(cfpos.getField()));
+    sb.append(u']');
+    return sb;
+}
+
+
+void IntlTestWithFieldPosition::checkMixedFormattedValue(
+        const char16_t* message,
+        const FormattedValue& fv,
+        UnicodeString expectedString,
+        const UFieldPositionWithCategory* expectedFieldPositions,
+        int32_t length) {
+    IcuTestErrorCode status(*this, "checkMixedFormattedValue");
     UnicodeString baseMessage = UnicodeString(message) + u": " + fv.toString(status) + u": ";
 
     // Check string values
@@ -173,49 +205,81 @@ void IntlTestWithFieldPosition::checkFormattedValue(
 
     // Check nextPosition over all fields
     ConstrainedFieldPosition cfpos;
-    cfpos.constrainCategory(expectedCategory);
     for (int32_t i = 0; i < length; i++) {
         assertTrue(baseMessage + i, fv.nextPosition(cfpos, status));
+        int32_t expectedCategory = expectedFieldPositions[i].category;
         int32_t expectedField = expectedFieldPositions[i].field;
         int32_t expectedStart = expectedFieldPositions[i].beginIndex;
         int32_t expectedLimit = expectedFieldPositions[i].endIndex;
-        assertEquals(baseMessage + u"category " + Int64ToUnicodeString(i),
+        assertEquals(baseMessage + u"category " + Int64ToUnicodeString(i),
             expectedCategory, cfpos.getCategory());
-        assertEquals(baseMessage + u"field " + Int64ToUnicodeString(i),
+        assertEquals(baseMessage + u"field " + Int64ToUnicodeString(i),
             expectedField, cfpos.getField());
-        assertEquals(baseMessage + u"start " + Int64ToUnicodeString(i),
+        assertEquals(baseMessage + u"start " + Int64ToUnicodeString(i),
             expectedStart, cfpos.getStart());
-        assertEquals(baseMessage + u"limit " + Int64ToUnicodeString(i),
+        assertEquals(baseMessage + u"limit " + Int64ToUnicodeString(i),
             expectedLimit, cfpos.getLimit());
     }
-    assertFalse(baseMessage + u"after loop", fv.nextPosition(cfpos, status));
+    UBool afterLoopResult = fv.nextPosition(cfpos, status);
+    assertFalse(baseMessage + u"A after loop: " + CFPosToUnicodeString(cfpos), afterLoopResult);
+
+    // Check nextPosition constrained over each category one at a time
+    for (int32_t category=0; category<UFIELD_CATEGORY_COUNT; category++) {
+        cfpos.reset();
+        cfpos.constrainCategory(static_cast<UFieldCategory>(category));
+        for (int32_t i = 0; i < length; i++) {
+            if (expectedFieldPositions[i].category != category) {
+                continue;
+            }
+            assertTrue(baseMessage + i, fv.nextPosition(cfpos, status));
+            int32_t expectedCategory = expectedFieldPositions[i].category;
+            int32_t expectedField = expectedFieldPositions[i].field;
+            int32_t expectedStart = expectedFieldPositions[i].beginIndex;
+            int32_t expectedLimit = expectedFieldPositions[i].endIndex;
+            assertEquals(baseMessage + u"B category " + Int64ToUnicodeString(i),
+                expectedCategory, cfpos.getCategory());
+            assertEquals(baseMessage + u"B field " + Int64ToUnicodeString(i),
+                expectedField, cfpos.getField());
+            assertEquals(baseMessage + u"B start " + Int64ToUnicodeString(i),
+                expectedStart, cfpos.getStart());
+            assertEquals(baseMessage + u"B limit " + Int64ToUnicodeString(i),
+                expectedLimit, cfpos.getLimit());
+        }
+        UBool afterLoopResult = fv.nextPosition(cfpos, status);
+        assertFalse(baseMessage + u"B after loop: " + CFPosToUnicodeString(cfpos), afterLoopResult);
+    }
 
     // Check nextPosition constrained over each field one at a time
-    std::set<int32_t> uniqueFields;
+    std::set<std::pair<UFieldCategory, int32_t>> uniqueFields;
     for (int32_t i = 0; i < length; i++) {
-        uniqueFields.insert(expectedFieldPositions[i].field);
+        uniqueFields.insert({expectedFieldPositions[i].category, expectedFieldPositions[i].field});
     }
-    for (int32_t field : uniqueFields) {
+    for (std::pair<UFieldCategory, int32_t> categoryAndField : uniqueFields) {
         cfpos.reset();
-        cfpos.constrainField(expectedCategory, field);
+        cfpos.constrainField(categoryAndField.first, categoryAndField.second);
         for (int32_t i = 0; i < length; i++) {
-            if (expectedFieldPositions[i].field != field) {
+            if (expectedFieldPositions[i].category != categoryAndField.first) {
+                continue;
+            }
+            if (expectedFieldPositions[i].field != categoryAndField.second) {
                 continue;
             }
             assertTrue(baseMessage + i, fv.nextPosition(cfpos, status));
+            int32_t expectedCategory = expectedFieldPositions[i].category;
             int32_t expectedField = expectedFieldPositions[i].field;
             int32_t expectedStart = expectedFieldPositions[i].beginIndex;
             int32_t expectedLimit = expectedFieldPositions[i].endIndex;
-            assertEquals(baseMessage + u"category " + Int64ToUnicodeString(i),
+            assertEquals(baseMessage + u"category " + Int64ToUnicodeString(i),
                 expectedCategory, cfpos.getCategory());
-            assertEquals(baseMessage + u"field " + Int64ToUnicodeString(i),
+            assertEquals(baseMessage + u"field " + Int64ToUnicodeString(i),
                 expectedField, cfpos.getField());
-            assertEquals(baseMessage + u"start " + Int64ToUnicodeString(i),
+            assertEquals(baseMessage + u"start " + Int64ToUnicodeString(i),
                 expectedStart, cfpos.getStart());
-            assertEquals(baseMessage + u"limit " + Int64ToUnicodeString(i),
+            assertEquals(baseMessage + u"limit " + Int64ToUnicodeString(i),
                 expectedLimit, cfpos.getLimit());
         }
-        assertFalse(baseMessage + u"after loop", fv.nextPosition(cfpos, status));
+        UBool afterLoopResult = fv.nextPosition(cfpos, status);
+        assertFalse(baseMessage + u"C after loop: " + CFPosToUnicodeString(cfpos), afterLoopResult);
     }
 }
 
index 8069b6d9986aeff60119e73d113149b4f3556b8e..d8b17993c30f8374bea9085e2d4e6d00b13641bd 100644 (file)
@@ -26,9 +26,15 @@ class IntlTestFormat: public IntlTest {
 };
 
 
+typedef struct UFieldPositionWithCategory {
+    UFieldCategory category;
+    int32_t field;
+    int32_t beginIndex;
+    int32_t endIndex;
+} UFieldPositionWithCategory;
+
 class IntlTestWithFieldPosition : public IntlTest {
 public:
-    // TODO: When needed, add overload with a different category for each position
     void checkFormattedValue(
         const char16_t* message,
         const FormattedValue& fv,
@@ -36,6 +42,13 @@ public:
         UFieldCategory expectedCategory,
         const UFieldPosition* expectedFieldPositions,
         int32_t length);
+
+    void checkMixedFormattedValue(
+        const char16_t* message,
+        const FormattedValue& fv,
+        UnicodeString expectedString,
+        const UFieldPositionWithCategory* expectedFieldPositions,
+        int32_t length);
 };
 
 
index bfa0ba23dbc12a025bf5350a9b727b6e2df096b4..b413a202be454015fef6601187724e883e1501b8 100644 (file)
@@ -2980,15 +2980,6 @@ void NumberFormatterApiTest::assertNumberFieldPositions(
         expectedFieldPositions,
         length);
 
-    // Check no field positions in an unrelated category
-    checkFormattedValue(
-        message,
-        static_cast<const FormattedValue&>(formattedNumber),
-        formattedNumber.toString(status),
-        UFIELD_CATEGORY_DATE,
-        nullptr,
-        0);
-
     // Check FormattedNumber-specific functions
     UnicodeString baseMessage = UnicodeString(message) + u": " + formattedNumber.toString(status) + u": ";
     FieldPositionIterator fpi;
index 7a44f93ea91ab21415ab89c88deb39f6f6cfce84..998190a16469f92bbc1c1c352ee563d38c1e6c17 100644 (file)
@@ -305,6 +305,22 @@ public class ConstrainedFieldPosition {
         fLimit = limit;
     }
 
+    /** @internal */
+    public boolean matchesField(Field field) {
+        // If this method ever becomes public, change assert to throw IllegalArgumentException
+        assert field != null;
+        switch (fConstraint) {
+        case NONE:
+            return true;
+        case CLASS:
+            return fClassConstraint.isAssignableFrom(field.getClass());
+        case FIELD:
+            return fField == field;
+        default:
+            throw new AssertionError();
+        }
+    }
+
     @Override
     public String toString() {
         StringBuilder sb = new StringBuilder();
index c1a037b364d890a505ec436ef0f04a925a117ace..fc50106503d387955413562d66c2319f66080c11 100644 (file)
@@ -176,7 +176,8 @@ public class FormattedValueTest {
             assertEquals(baseMessage + "limit " + i, expectedLimit, cfpos.getLimit());
             i++;
         }
-        assertFalse(baseMessage + "after loop", fv.nextPosition(cfpos));
+        boolean afterLoopResult = fv.nextPosition(cfpos);
+        assertFalse(baseMessage + "after loop: " + cfpos, afterLoopResult);
 
         // Check nextPosition constrained over each class one at a time
         for (Class<?> classConstraint : uniqueFieldClasses) {
@@ -196,7 +197,8 @@ public class FormattedValueTest {
                 assertEquals(baseMessage + "limit " + i, expectedLimit, cfpos.getLimit());
                 i++;
             }
-            assertFalse(baseMessage + "after loop", fv.nextPosition(cfpos));
+            afterLoopResult = fv.nextPosition(cfpos);
+            assertFalse(baseMessage + "after loop: " + cfpos, afterLoopResult);
         }
 
         // Check nextPosition constrained over an unrelated class
@@ -222,7 +224,8 @@ public class FormattedValueTest {
                 assertEquals(baseMessage + "limit " + i, expectedLimit, cfpos.getLimit());
                 i++;
             }
-            assertFalse(baseMessage + "after loop", fv.nextPosition(cfpos));
+            afterLoopResult = fv.nextPosition(cfpos);
+            assertFalse(baseMessage + "after loop: " + cfpos, afterLoopResult);
         }
     }