]> granicus.if.org Git - icu/commitdiff
ICU-10999 Add per unit measure formatting.
authorTravis Keep <keep94@gmail.com>
Wed, 10 Sep 2014 20:06:52 +0000 (20:06 +0000)
committerTravis Keep <keep94@gmail.com>
Wed, 10 Sep 2014 20:06:52 +0000 (20:06 +0000)
X-SVN-Rev: 36441

icu4c/source/common/simplepatternformatter.h
icu4c/source/i18n/measfmt.cpp
icu4c/source/i18n/quantityformatter.cpp
icu4c/source/i18n/quantityformatter.h
icu4c/source/i18n/unicode/measfmt.h
icu4c/source/test/intltest/measfmttest.cpp

index 4a43c37d9d65d76c738a55c979b3780b74b1a4cc..b286a79cc35821a254041b75090e1c580245e9c2 100644 (file)
@@ -96,6 +96,13 @@ public:
      */
     UBool startsWithPlaceholder(int32_t id) const;
 
+    /**
+     * Returns this pattern with none of the placeholders.
+     */
+    const UnicodeString &getPatternWithNoPlaceholders() const {
+        return noPlaceholders;
+    }
+
     /**
      * Formats given value.
      */
index d2c1ba3e55ae9b1ef004f3c9625d9760e02841b4..e13b04fc5a7e3f5ce46aeea2646b91e8c004c9df 100644 (file)
@@ -17,6 +17,7 @@
 #include "unicode/numfmt.h"
 #include "currfmt.h"
 #include "unicode/localpointer.h"
+#include "simplepatternformatter.h"
 #include "quantityformatter.h"
 #include "unicode/plurrule.h"
 #include "unicode/decimfmt.h"
@@ -79,6 +80,8 @@ private:
 class MeasureFormatCacheData : public SharedObject {
 public:
     QuantityFormatter formatters[MEAS_UNIT_COUNT][WIDTH_INDEX_COUNT];
+    SimplePatternFormatter perFormatters[WIDTH_INDEX_COUNT];
+
     MeasureFormatCacheData();
     void adoptCurrencyFormat(int32_t widthIndex, NumberFormat *nfToAdopt) {
         delete currencyFormats[widthIndex];
@@ -101,11 +104,23 @@ public:
     const NumericDateFormatters *getNumericDateFormatters() const {
         return numericDateFormatters;
     }
+    void adoptPerUnitFormatter(
+            int32_t index,
+            int32_t widthIndex,
+            SimplePatternFormatter *formatterToAdopt) {
+        delete perUnitFormatters[index][widthIndex];
+        perUnitFormatters[index][widthIndex] = formatterToAdopt;
+    }
+    const SimplePatternFormatter * const * getPerUnitFormattersByIndex(
+            int32_t index) const {
+        return perUnitFormatters[index];
+    }
     virtual ~MeasureFormatCacheData();
 private:
     NumberFormat *currencyFormats[WIDTH_INDEX_COUNT];
     NumberFormat *integerFormat;
     NumericDateFormatters *numericDateFormatters;
+    SimplePatternFormatter *perUnitFormatters[MEAS_UNIT_COUNT][WIDTH_INDEX_COUNT];
     MeasureFormatCacheData(const MeasureFormatCacheData &other);
     MeasureFormatCacheData &operator=(const MeasureFormatCacheData &other);
 };
@@ -114,6 +129,11 @@ MeasureFormatCacheData::MeasureFormatCacheData() {
     for (int32_t i = 0; i < UPRV_LENGTHOF(currencyFormats); ++i) {
         currencyFormats[i] = NULL;
     }
+    for (int32_t i = 0; i < MEAS_UNIT_COUNT; ++i) {
+        for (int32_t j = 0; j < WIDTH_INDEX_COUNT; ++j) {
+            perUnitFormatters[i][j] = NULL;
+        }
+    }
     integerFormat = NULL;
     numericDateFormatters = NULL;
 }
@@ -122,6 +142,11 @@ MeasureFormatCacheData::~MeasureFormatCacheData() {
     for (int32_t i = 0; i < UPRV_LENGTHOF(currencyFormats); ++i) {
         delete currencyFormats[i];
     }
+    for (int32_t i = 0; i < MEAS_UNIT_COUNT; ++i) {
+        for (int32_t j = 0; j < WIDTH_INDEX_COUNT; ++j) {
+            delete perUnitFormatters[i][j];
+        }
+    }
     delete integerFormat;
     delete numericDateFormatters;
 }
@@ -185,6 +210,22 @@ static UBool loadMeasureUnitData(
             status = U_ZERO_ERROR;
             continue;
         }
+        {
+            // compound per
+            LocalUResourceBundlePointer compoundPerBundle(
+                    ures_getByKeyWithFallback(
+                            widthBundle.getAlias(),
+                            "compound/per",
+                            NULL,
+                            &status));
+            if (U_FAILURE(status)) {
+                status = U_ZERO_ERROR;
+            } else {
+                UnicodeString perPattern;
+                getString(compoundPerBundle.getAlias(), perPattern, status);
+                cacheData.perFormatters[currentWidth].compile(perPattern, status);
+            }
+        }
         for (int32_t currentUnit = 0; currentUnit < unitCount; ++currentUnit) {
             // Be sure status is clear next lookup may fail.
             if (U_FAILURE(status)) {
@@ -224,9 +265,18 @@ static UBool loadMeasureUnitData(
                     return FALSE;
                 }
                 const char * resKey = ures_getKey(pluralBundle.getAlias());
-                if (uprv_strcmp(resKey, "dnam") == 0 || uprv_strcmp(resKey, "per") == 0) {
+                if (uprv_strcmp(resKey, "dnam") == 0) {
                     continue; // skip display name & per pattern (new in CLDR 26 / ICU 54) for now, not part of plurals
                 }
+                if (uprv_strcmp(resKey, "per") == 0) {
+                    UnicodeString perPattern;
+                    getString(pluralBundle.getAlias(), perPattern, status);
+                    cacheData.adoptPerUnitFormatter(
+                            units[currentUnit].getIndex(),
+                            currentWidth, 
+                            new SimplePatternFormatter(perPattern));
+                    continue;
+                }
                 UnicodeString rawPattern;
                 getString(pluralBundle.getAlias(), rawPattern, status);
                 cacheData.formatters[units[currentUnit].getIndex()][currentWidth].add(
@@ -542,6 +592,31 @@ void MeasureFormat::parseObject(
     return;
 }
 
+UnicodeString &MeasureFormat::formatMeasuresPer(
+        const Measure *measures,
+        int32_t measureCount,
+        const MeasureUnit &perUnit,
+        UnicodeString &appendTo,
+        FieldPosition &pos,
+        UErrorCode &status) const {
+    FieldPosition fpos(pos.getField());
+    UnicodeString measuresString;
+    int32_t offset = withPerUnit(
+            formatMeasures(
+                    measures, measureCount, measuresString, fpos, status),
+            perUnit,
+            appendTo,
+            status);
+    if (U_FAILURE(status)) {
+        return appendTo;
+    }
+    if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) {
+        pos.setBeginIndex(fpos.getBeginIndex() + offset);
+        pos.setEndIndex(fpos.getEndIndex() + offset);
+    }
+    return appendTo;
+}
+
 UnicodeString &MeasureFormat::formatMeasures(
         const Measure *measures,
         int32_t measureCount,
@@ -861,6 +936,93 @@ const QuantityFormatter *MeasureFormat::getQuantityFormatter(
     return NULL;
 }
 
+const SimplePatternFormatter *MeasureFormat::getPerUnitFormatter(
+        int32_t index,
+        int32_t widthIndex) const {
+    const SimplePatternFormatter * const * perUnitFormatters =
+            cache->getPerUnitFormattersByIndex(index);
+    if (perUnitFormatters[widthIndex] != NULL) {
+        return perUnitFormatters[widthIndex];
+    }
+    if (perUnitFormatters[UMEASFMT_WIDTH_SHORT] != NULL) {
+        return perUnitFormatters[UMEASFMT_WIDTH_SHORT];
+    }
+    if (perUnitFormatters[UMEASFMT_WIDTH_WIDE] != NULL) {
+        return perUnitFormatters[UMEASFMT_WIDTH_WIDE];
+    }
+    return NULL;
+}
+
+const SimplePatternFormatter *MeasureFormat::getPerFormatter(
+        int32_t widthIndex,
+        UErrorCode &status) const {
+    if (U_FAILURE(status)) {
+        return NULL;
+    }
+    const SimplePatternFormatter * perFormatters = cache->perFormatters;
+    
+    if (perFormatters[widthIndex].getPlaceholderCount() == 2) {
+        return &perFormatters[widthIndex];
+    }
+    if (perFormatters[UMEASFMT_WIDTH_SHORT].getPlaceholderCount() == 2) {
+        return &perFormatters[UMEASFMT_WIDTH_SHORT];
+    }
+    if (perFormatters[UMEASFMT_WIDTH_WIDE].getPlaceholderCount() == 2) {
+        return &perFormatters[UMEASFMT_WIDTH_WIDE];
+    }
+    status = U_MISSING_RESOURCE_ERROR;
+    return NULL;
+}
+
+static void getPerUnitString(
+        const QuantityFormatter &formatter,
+        UnicodeString &result) {
+    result = formatter.getByVariant("one")->getPatternWithNoPlaceholders();
+    result.trim();
+}
+
+int32_t MeasureFormat::withPerUnit(
+        const UnicodeString &formatted,
+        const MeasureUnit &perUnit,
+        UnicodeString &appendTo,
+        UErrorCode &status) const {
+    int32_t offset = -1;
+    if (U_FAILURE(status)) {
+        return offset;
+    }
+    const SimplePatternFormatter *perUnitFormatter = getPerUnitFormatter(
+            perUnit.getIndex(), widthToIndex(width));
+    if (perUnitFormatter != NULL) {
+        const UnicodeString *params[] = {&formatted};
+        perUnitFormatter->format(
+                params,
+                UPRV_LENGTHOF(params),
+                appendTo,
+                &offset,
+                1,
+                status);
+        return offset;
+    }
+    const SimplePatternFormatter *perFormatter = getPerFormatter(
+            widthToIndex(width), status);
+    const QuantityFormatter *qf = getQuantityFormatter(
+            perUnit.getIndex(), widthToIndex(width), status);
+    if (U_FAILURE(status)) {
+        return offset;
+    }
+    UnicodeString perUnitString;
+    getPerUnitString(*qf, perUnitString);
+    const UnicodeString *params[] = {&formatted, &perUnitString};
+    perFormatter->format(
+            params,
+            UPRV_LENGTHOF(params),
+            appendTo,
+            &offset,
+            1,
+            status);
+    return offset;
+}
+
 UnicodeString &MeasureFormat::formatMeasuresSlowTrack(
         const Measure *measures,
         int32_t measureCount,
index 8bec7a9734668b91eef427cc5ff42fe630e1ede6..bef509a718691f08cbd628bb98ba4fb313f8fdbf 100644 (file)
@@ -112,6 +112,19 @@ UBool QuantityFormatter::isValid() const {
     return formatters[0] != NULL;
 }
 
+const SimplePatternFormatter *QuantityFormatter::getByVariant(
+        const char *variant) const {
+    int32_t pluralIndex = getPluralIndex(variant);
+    if (pluralIndex == -1) {
+        pluralIndex = 0;
+    }
+    const SimplePatternFormatter *pattern = formatters[pluralIndex];
+    if (pattern == NULL) {
+        pattern = formatters[0];
+    }
+    return pattern;
+}
+
 UnicodeString &QuantityFormatter::format(
             const Formattable& quantity,
             const NumberFormat &fmt,
@@ -147,14 +160,7 @@ UnicodeString &QuantityFormatter::format(
     if (U_FAILURE(status)) {
         return appendTo;
     }
-    int32_t pluralIndex = getPluralIndex(buffer.data());
-    if (pluralIndex == -1) {
-        pluralIndex = 0;
-    }
-    const SimplePatternFormatter *pattern = formatters[pluralIndex];
-    if (pattern == NULL) {
-        pattern = formatters[0];
-    }
+    const SimplePatternFormatter *pattern = getByVariant(buffer.data());
     if (pattern == NULL) {
         status = U_INVALID_STATE_ERROR;
         return appendTo;
index 5dceaf652dceb101025fee1b31374c745db1cc17..b71a1760760a797218292afebcf9695cfe86078d 100644 (file)
@@ -80,6 +80,13 @@ public:
      */
     UBool isValid() const;
 
+    /**
+     * Gets the pattern formatter that would be used for a particular variant.
+     * If isValid() returns TRUE, this method is guaranteed to return a
+     * non-NULL value.
+     */
+    const SimplePatternFormatter *getByVariant(const char *variant) const;
+
     /**
      * Formats a quantity with this object appending the result to appendTo.
      * At least the "other" variant must be added to this object for this
index 188aefdf84066a0e10833932424ced1d447d0254..4bbc27d7dcd3a09e6414b51f00fb824739d26bb5 100644 (file)
@@ -73,12 +73,14 @@ typedef enum UMeasureFormatWidth UMeasureFormatWidth;
 U_NAMESPACE_BEGIN
 
 class Measure;
+class MeasureUnit;
 class NumberFormat;
 class PluralRules;
 class MeasureFormatCacheData;
 class SharedNumberFormat;
 class SharedPluralRules;
 class QuantityFormatter;
+class SimplePatternFormatter;
 class ListFormatter;
 class DateFormat;
 
@@ -190,6 +192,29 @@ class U_I18N_API MeasureFormat : public Format {
             UErrorCode &status) const;
 #endif  /* U_HIDE_DRAFT_API */
 
+#ifndef U_HIDE_INTERNAL_API
+    /**
+     * Works like formatMeasures but adds a per unit. An example of such a
+     * formatted string is 3 meters, 3.5 centimeters per second.
+     * @param measures array of measure objects.
+     * @param measureCount the number of measure objects.
+     * @param perUnit The per unit. In the example formatted string,
+     *        it is *MeasureUnit::createSecond(status).
+     * @param appendTo formatted string appended here.
+     * @param pos the field position.
+     * @param status the error.
+     * @return appendTo reference
+     *
+     * @internal
+     */
+    UnicodeString &formatMeasuresPer(
+            const Measure *measures,
+            int32_t measureCount,
+            const MeasureUnit &perUnit,
+            UnicodeString &appendTo,
+            FieldPosition &pos,
+            UErrorCode &status) const;
+#endif /* U_HIDE_INTERNAL_API */
 
     /**
      * Return a formatter for CurrencyAmount objects in the given
@@ -318,6 +343,20 @@ class U_I18N_API MeasureFormat : public Format {
             int32_t widthIndex,
             UErrorCode &status) const;
 
+    const SimplePatternFormatter *getPerUnitFormatter(
+            int32_t index,
+            int32_t widthIndex) const;
+
+    const SimplePatternFormatter *getPerFormatter(
+            int32_t widthIndex,
+            UErrorCode &status) const;
+
+    int32_t withPerUnit(
+        const UnicodeString &formatted,
+        const MeasureUnit &perUnit,
+        UnicodeString &appendTo,
+        UErrorCode &status) const;
+
     UnicodeString &formatMeasure(
         const Measure &measure,
         const NumberFormat &nf,
index 5b1cbcaa95af9956a6e8d987038a994de5544494..221d641663498cfa936f3386eb6c9e1155f074fa 100644 (file)
@@ -47,11 +47,13 @@ private:
     void TestGreek();
     void TestFormatSingleArg();
     void TestFormatMeasuresZeroArg();
+    void TestMultiplesWithPer();
     void TestMultiples();
     void TestGram();
     void TestCurrencies();
     void TestFieldPosition();
     void TestFieldPositionMultiple();
+    void TestFieldPositionMultipleWithPer();
     void TestBadArg();
     void TestEquality();
     void TestGroupingSeparator();
@@ -74,6 +76,11 @@ private:
         const MeasureFormat &fmt,
         const ExpectedResult *expectedResults,
         int32_t count);
+    void helperTestMultiplesWithPer(
+        const Locale &locale,
+        UMeasureFormatWidth width,
+        const MeasureUnit &unit,
+        const char *expected);
     void helperTestMultiples(
         const Locale &locale,
         UMeasureFormatWidth width,
@@ -87,6 +94,16 @@ private:
         NumberFormat::EAlignmentFields field,
         int32_t start,
         int32_t end);
+    void verifyFieldPositionWithPer(
+        const char *description,
+        const MeasureFormat &fmt,
+        const UnicodeString &prefix,
+        const Measure *measures,
+        int32_t measureCount,
+        const MeasureUnit &perUnit,
+        NumberFormat::EAlignmentFields field,
+        int32_t start,
+        int32_t end);
 };
 
 void MeasureFormatTest::runIndexedTest(
@@ -105,11 +122,13 @@ void MeasureFormatTest::runIndexedTest(
     TESTCASE_AUTO(TestGreek);
     TESTCASE_AUTO(TestFormatSingleArg);
     TESTCASE_AUTO(TestFormatMeasuresZeroArg);
+    TESTCASE_AUTO(TestMultiplesWithPer);
     TESTCASE_AUTO(TestMultiples);
     TESTCASE_AUTO(TestGram);
     TESTCASE_AUTO(TestCurrencies);
     TESTCASE_AUTO(TestFieldPosition);
     TESTCASE_AUTO(TestFieldPositionMultiple);
+    TESTCASE_AUTO(TestFieldPositionMultipleWithPer);
     TESTCASE_AUTO(TestBadArg);
     TESTCASE_AUTO(TestEquality);
     TESTCASE_AUTO(TestGroupingSeparator);
@@ -841,6 +860,67 @@ void MeasureFormatTest::TestFormatMeasuresZeroArg() {
     verifyFormat("TestFormatMeasuresZeroArg", fmt, NULL, 0, "");
 }
 
+void MeasureFormatTest::TestMultiplesWithPer() {
+    Locale en("en");
+    UErrorCode status = U_ZERO_ERROR;
+    LocalPointer<MeasureUnit> second(MeasureUnit::createSecond(status));
+    LocalPointer<MeasureUnit> minute(MeasureUnit::createMinute(status));
+    if (!assertSuccess("", status)) {
+        return;
+    }
+
+    // Per unit test.
+    helperTestMultiplesWithPer(
+            en, UMEASFMT_WIDTH_WIDE, *second, "2 miles, 1 foot, 2.3 inches per second");
+    helperTestMultiplesWithPer(
+            en, UMEASFMT_WIDTH_SHORT, *second, "2 mi, 1 ft, 2.3 inps");
+    helperTestMultiplesWithPer(
+            en, UMEASFMT_WIDTH_NARROW, *second, "2mi 1\\u2032 2.3\\u2033/s");
+
+    // Fallback compound per test
+    helperTestMultiplesWithPer(
+            en, UMEASFMT_WIDTH_WIDE, *minute, "2 miles, 1 foot, 2.3 inches per minute");
+    helperTestMultiplesWithPer(
+            en, UMEASFMT_WIDTH_SHORT, *minute, "2 mi, 1 ft, 2.3 in/min");
+    helperTestMultiplesWithPer(
+            en, UMEASFMT_WIDTH_NARROW, *minute, "2mi 1\\u2032 2.3\\u2033/m");
+}
+
+void MeasureFormatTest::helperTestMultiplesWithPer(
+        const Locale &locale,
+        UMeasureFormatWidth width,
+        const MeasureUnit &perUnit,
+        const char *expected) {
+    UErrorCode status = U_ZERO_ERROR;
+    FieldPosition pos(0);
+    MeasureFormat fmt(locale, width, status);
+    if (!assertSuccess("Error creating format object", status)) {
+        return;
+    }
+    Measure measures[] = {
+            Measure(2, MeasureUnit::createMile(status), status),
+            Measure(1, MeasureUnit::createFoot(status), status),
+            Measure(2.3, MeasureUnit::createInch(status), status)};
+    if (!assertSuccess("Error creating measures", status)) {
+        return;
+    }
+    UnicodeString buffer;
+    fmt.formatMeasuresPer(
+            measures,
+            UPRV_LENGTHOF(measures),
+            perUnit,
+            buffer,
+            pos,
+            status);
+    if (!assertSuccess("Error formatting measures with per", status)) {
+        return;
+    }
+    assertEquals(
+            "TestMultiplesWithPer",
+            UnicodeString(expected).unescape(),
+            buffer);
+}
+
 void MeasureFormatTest::TestMultiples() {
     Locale ru("ru");
     Locale en("en");
@@ -1017,6 +1097,99 @@ void MeasureFormatTest::TestFieldPositionMultiple() {
             0);
 }
 
+void MeasureFormatTest::TestFieldPositionMultipleWithPer() {
+    UErrorCode status = U_ZERO_ERROR;
+    MeasureFormat fmt("en", UMEASFMT_WIDTH_SHORT, status);
+    if (!assertSuccess("Error creating format object", status)) {
+        return;
+    }
+    Measure first[] = {
+            Measure(354, MeasureUnit::createMeter(status), status),
+            Measure(23, MeasureUnit::createCentimeter(status), status)};
+    Measure second[] = {
+            Measure(354, MeasureUnit::createMeter(status), status),
+            Measure(23, MeasureUnit::createCentimeter(status), status),
+            Measure(5.4, MeasureUnit::createMillimeter(status), status)};
+    Measure third[] = {
+            Measure(3, MeasureUnit::createMeter(status), status),
+            Measure(23, MeasureUnit::createCentimeter(status), status),
+            Measure(5, MeasureUnit::createMillimeter(status), status)};
+    if (!assertSuccess("Error creating measure objects", status)) {
+        return;
+    }
+    UnicodeString prefix("123456: ");
+
+    LocalPointer<MeasureUnit> secondUnit(MeasureUnit::createSecond(status));
+    LocalPointer<MeasureUnit> minuteUnit(MeasureUnit::createMinute(status));
+    if (!assertSuccess("Error creating format object", status)) {
+        return;
+    }
+
+    // per unit test
+    verifyFieldPositionWithPer(
+            "Integer",
+            fmt,
+            prefix,
+            first,
+            UPRV_LENGTHOF(first),
+            *secondUnit,
+            NumberFormat::kIntegerField,
+            8,
+            11);
+    verifyFieldPositionWithPer(
+            "Decimal separator",
+            fmt,
+            prefix,
+            second,
+            UPRV_LENGTHOF(second),
+            *secondUnit,
+            NumberFormat::kDecimalSeparatorField,
+            23,
+            24);
+    verifyFieldPositionWithPer(
+            "no decimal separator",
+            fmt,
+            prefix,
+            third,
+            UPRV_LENGTHOF(third),
+            *secondUnit,
+            NumberFormat::kDecimalSeparatorField,
+            0,
+            0);
+
+    // Fallback to compound per test
+    verifyFieldPositionWithPer(
+            "Integer",
+            fmt,
+            prefix,
+            first,
+            UPRV_LENGTHOF(first),
+            *minuteUnit,
+            NumberFormat::kIntegerField,
+            8,
+            11);
+    verifyFieldPositionWithPer(
+            "Decimal separator",
+            fmt,
+            prefix,
+            second,
+            UPRV_LENGTHOF(second),
+            *minuteUnit,
+            NumberFormat::kDecimalSeparatorField,
+            23,
+            24);
+    verifyFieldPositionWithPer(
+            "no decimal separator",
+            fmt,
+            prefix,
+            third,
+            UPRV_LENGTHOF(third),
+            *minuteUnit,
+            NumberFormat::kDecimalSeparatorField,
+            0,
+            0);
+}
+
 void MeasureFormatTest::TestBadArg() {
     UErrorCode status = U_ZERO_ERROR;
     MeasureFormat fmt("en", UMEASFMT_WIDTH_SHORT, status);
@@ -1150,6 +1323,40 @@ void MeasureFormatTest::verifyFieldPosition(
     assertEquals(endIndex.data(), end, pos.getEndIndex());
 }
 
+void MeasureFormatTest::verifyFieldPositionWithPer(
+        const char *description,
+        const MeasureFormat &fmt,
+        const UnicodeString &prefix,
+        const Measure *measures,
+        int32_t measureCount,
+        const MeasureUnit &perUnit,
+        NumberFormat::EAlignmentFields field,
+        int32_t start,
+        int32_t end) {
+    UnicodeString result(prefix);
+    FieldPosition pos(field);
+    UErrorCode status = U_ZERO_ERROR;
+    CharString ch;
+    const char *descPrefix = ch.append(description, status)
+            .append(": ", status).data();
+    CharString beginIndex;
+    beginIndex.append(descPrefix, status).append("beginIndex", status);
+    CharString endIndex;
+    endIndex.append(descPrefix, status).append("endIndex", status);
+    fmt.formatMeasuresPer(
+            measures,
+            measureCount,
+            perUnit,
+            result,
+            pos,
+            status);
+    if (!assertSuccess("Error formatting", status)) {
+        return;
+    }
+    assertEquals(beginIndex.data(), start, pos.getBeginIndex());
+    assertEquals(endIndex.data(), end, pos.getEndIndex());
+}
+
 void MeasureFormatTest::verifyFormat(
         const char *description,
         const MeasureFormat &fmt,