]> granicus.if.org Git - icu/commitdiff
ICU-10640 Add C++ MeasureFormat API.
authorTravis Keep <keep94@gmail.com>
Tue, 4 Feb 2014 00:29:17 +0000 (00:29 +0000)
committerTravis Keep <keep94@gmail.com>
Tue, 4 Feb 2014 00:29:17 +0000 (00:29 +0000)
X-SVN-Rev: 35069

28 files changed:
.gitattributes
icu4c/source/common/listformatter.cpp
icu4c/source/i18n/Makefile.in
icu4c/source/i18n/currfmt.cpp
icu4c/source/i18n/currfmt.h
icu4c/source/i18n/currunit.cpp
icu4c/source/i18n/measfmt.cpp
icu4c/source/i18n/measunit.cpp [new file with mode: 0644]
icu4c/source/i18n/measure.cpp
icu4c/source/i18n/quantityformatter.cpp
icu4c/source/i18n/quantityformatter.h
icu4c/source/i18n/reldatefmt.cpp
icu4c/source/i18n/tmunit.cpp
icu4c/source/i18n/tmutfmt.cpp
icu4c/source/i18n/ucln_in.h
icu4c/source/i18n/unicode/currunit.h
icu4c/source/i18n/unicode/measfmt.h
icu4c/source/i18n/unicode/measunit.h
icu4c/source/i18n/unicode/measure.h
icu4c/source/i18n/unicode/tmunit.h
icu4c/source/i18n/unicode/tmutfmt.h
icu4c/source/test/intltest/Makefile.in
icu4c/source/test/intltest/intltest.vcxproj
icu4c/source/test/intltest/intltest.vcxproj.filters
icu4c/source/test/intltest/itformat.cpp
icu4c/source/test/intltest/measfmttest.cpp [new file with mode: 0644]
icu4c/source/test/intltest/numfmtst.cpp
icu4c/source/test/intltest/tufmtts.cpp

index 06ff3b727b82c0b65f4f61d35fe0474756e76937..5b5feb4a149a3609ca3a6fca63c03f9a43bcdc16 100644 (file)
@@ -77,6 +77,7 @@ icu4c/source/extra/uconv/uconv.vcxproj -text
 icu4c/source/extra/uconv/uconv.vcxproj.filters -text
 icu4c/source/i18n/i18n.vcxproj -text
 icu4c/source/i18n/i18n.vcxproj.filters -text
+icu4c/source/i18n/measunit.cpp -text
 icu4c/source/i18n/quantityformatter.cpp -text
 icu4c/source/i18n/quantityformatter.h -text
 icu4c/source/io/io.vcxproj -text
@@ -146,6 +147,7 @@ icu4c/source/test/cintltst/cintltst.vcxproj -text
 icu4c/source/test/cintltst/cintltst.vcxproj.filters -text
 icu4c/source/test/intltest/intltest.vcxproj -text
 icu4c/source/test/intltest/intltest.vcxproj.filters -text
+icu4c/source/test/intltest/measfmttest.cpp -text
 icu4c/source/test/intltest/simplepatternformattertest.cpp -text
 icu4c/source/test/iotest/iotest.vcxproj -text
 icu4c/source/test/iotest/iotest.vcxproj.filters -text
index 1668037d92417134cadf7491bc449897b1f6ebad..17bc0666d8bd6a030742b35e6ab731a8e3947d5d 100644 (file)
@@ -253,6 +253,10 @@ ListFormatter::~ListFormatter() {
 
 /**
  * Joins first and second using the pattern pat.
+ * On entry offset is an offset into first or -1 if offset unspecified.
+ * On exit offset is offset of second in result if recordOffset was set
+ * Otherwise if it was >=0 it is set to point into result where it used
+ * to point into first.
  */
 static void joinStrings(
         const SimplePatternFormatter& pat,
@@ -260,7 +264,7 @@ static void joinStrings(
         const UnicodeString& second,
         UnicodeString &result,
         UBool recordOffset,
-        int32_t offset,
+        int32_t &offset,
         UErrorCode& errorCode) {
     if (U_FAILURE(errorCode)) {
         return;
@@ -375,7 +379,9 @@ UnicodeString& ListFormatter::format(
             offset,
             errorCode);
     if (U_SUCCESS(errorCode)) {
-        offset += appendTo.length();
+        if (offset >= 0) {
+            offset += appendTo.length();
+        }
         appendTo += temp[npos];
     }
     return appendTo;
index 63ddbc02a3f558b52d1becfc392f1c81e38a7a3c..4f2316fba8a434117646ab257f438071736e1aaa 100644 (file)
@@ -87,7 +87,7 @@ uspoof.o uspoof_impl.o uspoof_build.o uspoof_conf.o uspoof_wsconf.o decfmtst.o s
 ztrans.o zrule.o vzone.o fphdlimp.o fpositer.o locdspnm.o \
 decNumber.o decContext.o alphaindex.o tznames.o tznames_impl.o tzgnames.o \
 tzfmt.o compactdecimalformat.o gender.o region.o scriptset.o identifier_info.o \
-uregion.o reldatefmt.o quantityformatter.o
+uregion.o reldatefmt.o quantityformatter.o measunit.o
 
 ## Header files to install
 HEADERS = $(srcdir)/unicode/*.h
index 97be9730f35595d01231970e98ac2cb855963730..cbdcb3769254787eddeb832c81387c809f9f8034 100644 (file)
@@ -1,6 +1,6 @@
 /*
 **********************************************************************
-* Copyright (c) 2004-2012 International Business Machines
+* Copyright (c) 2004-2014 International Business Machines
 * Corporation and others.  All Rights Reserved.
 **********************************************************************
 * Author: Alan Liu
@@ -21,7 +21,7 @@
 U_NAMESPACE_BEGIN
 
 CurrencyFormat::CurrencyFormat(const Locale& locale, UErrorCode& ec) :
-    fmt(NULL)
+    MeasureFormat(locale, UMEASFMT_WIDTH_WIDE, ec), fmt(NULL)
 {
     fmt = NumberFormat::createCurrencyInstance(locale, ec);
 }
@@ -36,17 +36,6 @@ CurrencyFormat::~CurrencyFormat() {
     delete fmt;
 }
 
-UBool CurrencyFormat::operator==(const Format& other) const {
-    if (this == &other) {
-        return TRUE;
-    }
-    if (typeid(*this) != typeid(other)) {
-        return FALSE;
-    }
-    const CurrencyFormat* c = (const CurrencyFormat*) &other;
-    return *fmt == *c->fmt;
-}
-
 Format* CurrencyFormat::clone() const {
     return new CurrencyFormat(*this);
 }
index 82aae3ec05040ff95d64b65e9730c4bdef7d967c..b3f23e56f9676b4f958082ac98a25a02d501d143 100644 (file)
@@ -1,6 +1,6 @@
 /*
 **********************************************************************
-* Copyright (c) 2004-2010, International Business Machines
+* Copyright (c) 2004-2014, International Business Machines
 * Corporation and others.  All Rights Reserved.
 **********************************************************************
 * Author: Alan Liu
@@ -52,11 +52,6 @@ class CurrencyFormat : public MeasureFormat {
      */
     virtual ~CurrencyFormat();
 
-    /**
-     * Override Format API.
-     */
-    virtual UBool operator==(const Format& other) const;
-
     /**
      * Override Format API.
      */
index 870d9d0c4ce8bb6f60500e9ac7ce9960ebd3f036..207d4122d9e7c036a1da09cd0331173951234084 100644 (file)
@@ -1,6 +1,6 @@
 /*
 **********************************************************************
-* Copyright (c) 2004-2012, International Business Machines
+* Copyright (c) 2004-2014, International Business Machines
 * Corporation and others.  All Rights Reserved.
 **********************************************************************
 * Author: Alan Liu
@@ -24,6 +24,9 @@ CurrencyUnit::CurrencyUnit(const UChar* _isoCode, UErrorCode& ec) {
     if (U_SUCCESS(ec)) {
         if (_isoCode && u_strlen(_isoCode)==3) {
             u_strcpy(isoCode, _isoCode);
+            char simpleIsoCode[4];
+            u_UCharsToChars(isoCode, simpleIsoCode, 4);
+            initCurrency(simpleIsoCode);
         } else {
             ec = U_ILLEGAL_ARGUMENT_ERROR;
         }
@@ -32,13 +35,15 @@ CurrencyUnit::CurrencyUnit(const UChar* _isoCode, UErrorCode& ec) {
 
 CurrencyUnit::CurrencyUnit(const CurrencyUnit& other) :
     MeasureUnit(other) {
-    *this = other;
+    u_strcpy(isoCode, other.isoCode);
 }
 
 CurrencyUnit& CurrencyUnit::operator=(const CurrencyUnit& other) {
-    if (this != &other) {
-        u_strcpy(isoCode, other.isoCode);
+    if (this == &other) {
+        return *this;
     }
+    MeasureUnit::operator=(other);
+    u_strcpy(isoCode, other.isoCode);
     return *this;
 }
 
@@ -49,12 +54,6 @@ UObject* CurrencyUnit::clone() const {
 CurrencyUnit::~CurrencyUnit() {
 }
     
-UBool CurrencyUnit::operator==(const UObject& other) const {
-    const CurrencyUnit& c = (const CurrencyUnit&) other;
-    return typeid(*this) == typeid(other) &&
-        u_strcmp(isoCode, c.isoCode) == 0;    
-}
-
 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(CurrencyUnit)
 
 U_NAMESPACE_END
index 6f06852acf997cb8fc09316c9f83232a8775fc12..3fd41e4074d57c9c8f42d5489b0208d6f644b5d9 100644 (file)
@@ -1,6 +1,6 @@
 /*
 **********************************************************************
-* Copyright (c) 2004-2011, International Business Machines
+* Copyright (c) 2004-2014, International Business Machines
 * Corporation and others.  All Rights Reserved.
 **********************************************************************
 * Author: Alan Liu
 #if !UCONFIG_NO_FORMATTING
 
 #include "unicode/measfmt.h"
+#include "unicode/numfmt.h"
 #include "currfmt.h"
+#include "unicode/localpointer.h"
+#include "quantityformatter.h"
+#include "unicode/plurrule.h"
+#include "unicode/decimfmt.h"
+#include "lrucache.h"
+#include "uresimp.h"
+#include "unicode/ures.h"
+#include "cstring.h"
+#include "mutex.h"
+#include "ucln_in.h"
+#include "unicode/listformatter.h"
+#include "charstr.h"
+#include "unicode/putil.h"
+#include "unicode/smpdtfmt.h"
+
+#include "sharedptr.h"
+
+#define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
+#define MEAS_UNIT_COUNT 46
+
+static icu::LRUCache *gCache = NULL;
+static UMutex gCacheMutex = U_MUTEX_INITIALIZER;
+static icu::UInitOnce gCacheInitOnce = U_INITONCE_INITIALIZER;
+
+U_CDECL_BEGIN
+static UBool U_CALLCONV measfmt_cleanup() {
+    gCacheInitOnce.reset();
+    if (gCache) {
+        delete gCache;
+        gCache = NULL;
+    }
+    return TRUE;
+}
+U_CDECL_END
 
 U_NAMESPACE_BEGIN
 
-MeasureFormat::MeasureFormat() {}
+class UnitFormatters : public UMemory {
+public:
+    UnitFormatters() { }
+    QuantityFormatter formatters[MEAS_UNIT_COUNT][UMEASFMT_WIDTH_NARROW + 1];
+private:
+    UnitFormatters(const UnitFormatters &other);
+    UnitFormatters &operator=(const UnitFormatters &other);
+};
+
+class NumericDateFormatters : public UMemory {
+public:
+    SimpleDateFormat hourMinute;
+    SimpleDateFormat minuteSecond;
+    SimpleDateFormat hourMinuteSecond;
+    NumericDateFormatters(
+            const UnicodeString &hm,
+            const UnicodeString &ms,
+            const UnicodeString &hms,
+            UErrorCode &status) : 
+            hourMinute(hm, status),
+            minuteSecond(ms, status), 
+            hourMinuteSecond(hms, status) {
+        const TimeZone *gmt = TimeZone::getGMT();
+        hourMinute.setTimeZone(*gmt);
+        minuteSecond.setTimeZone(*gmt);
+        hourMinuteSecond.setTimeZone(*gmt);
+    }
+private:
+    NumericDateFormatters(const NumericDateFormatters &other);
+    NumericDateFormatters &operator=(const NumericDateFormatters &other);
+};
+
+class MeasureFormatData : public SharedObject {
+public:
+    SharedPtr<UnitFormatters> unitFormatters;
+    SharedPtr<PluralRules> pluralRules;
+    SharedPtr<NumberFormat> numberFormat;
+    SharedPtr<NumberFormat> currencyFormats[UMEASFMT_WIDTH_NARROW + 1];
+    SharedPtr<NumericDateFormatters> numericDateFormatters;
+    virtual ~MeasureFormatData();
+private:
+    MeasureFormatData &operator=(const MeasureFormatData& other);
+};
+
+MeasureFormatData::~MeasureFormatData() {
+}
+
+static int32_t widthToIndex(UMeasureFormatWidth width) {
+    if (width > UMEASFMT_WIDTH_NARROW) {
+        return UMEASFMT_WIDTH_NARROW;
+    }
+    return width;
+}
+
+static UBool isCurrency(const MeasureUnit &unit) {
+    return (uprv_strcmp(unit.getType(), "currency") == 0);
+}
+
+static UBool getString(
+        const UResourceBundle *resource,
+        UnicodeString &result,
+        UErrorCode &status) {
+    int32_t len = 0;
+    const UChar *resStr = ures_getString(resource, &len, &status);
+    if (U_FAILURE(status)) {
+        return FALSE;
+    }
+    result.setTo(TRUE, resStr, len);
+    return TRUE;
+}
+
+
+static UBool load(
+        const UResourceBundle *resource,
+        UnitFormatters &unitFormatters,
+        UErrorCode &status) {
+    if (U_FAILURE(status)) {
+        return FALSE;
+    }
+    static const char *widthPath[] = {"units", "unitsShort", "unitsNarrow"};
+    MeasureUnit *units = NULL;
+    int32_t unitCount = MeasureUnit::getAvailable(units, 0, status);
+    while (status == U_BUFFER_OVERFLOW_ERROR) {
+        status = U_ZERO_ERROR;
+        delete [] units;
+        units = new MeasureUnit[unitCount];
+        if (units == NULL) {
+            status = U_MEMORY_ALLOCATION_ERROR;
+            return FALSE;
+        }
+        unitCount = MeasureUnit::getAvailable(units, unitCount, status);
+    }
+    for (int32_t currentWidth = 0; currentWidth <= UMEASFMT_WIDTH_NARROW; ++currentWidth) {
+        // Be sure status is clear since next resource bundle lookup may fail.
+        if (U_FAILURE(status)) {
+            delete [] units;
+            return FALSE;
+        }
+        LocalUResourceBundlePointer widthBundle(
+                ures_getByKeyWithFallback(
+                        resource, widthPath[currentWidth], NULL, &status));
+        // We may not have data for all widths in all locales.
+        if (status == U_MISSING_RESOURCE_ERROR) {
+            status = U_ZERO_ERROR;
+            continue;
+        }
+        for (int32_t currentUnit = 0; currentUnit < unitCount; ++currentUnit) {
+            // Be sure status is clear next lookup may fail.
+            if (U_FAILURE(status)) {
+                delete [] units;
+                return FALSE;
+            }
+            if (isCurrency(units[currentUnit])) {
+                continue;
+            }
+            CharString pathBuffer;
+            pathBuffer.append(units[currentUnit].getType(), status)
+                    .append("/", status)
+                    .append(units[currentUnit].getSubtype(), status);
+            LocalUResourceBundlePointer unitBundle(
+                    ures_getByKeyWithFallback(
+                            widthBundle.getAlias(),
+                            pathBuffer.data(),
+                            NULL,
+                            &status));
+            // We may not have data for all units in all widths
+            if (status == U_MISSING_RESOURCE_ERROR) {
+                status = U_ZERO_ERROR;
+                continue;
+            }
+            // We must have the unit bundle to proceed
+            if (U_FAILURE(status)) {
+                delete [] units;
+                return FALSE;
+            }
+            int32_t size = ures_getSize(unitBundle.getAlias());
+            for (int32_t plIndex = 0; plIndex < size; ++plIndex) {
+                LocalUResourceBundlePointer pluralBundle(
+                        ures_getByIndex(
+                                unitBundle.getAlias(), plIndex, NULL, &status));
+                if (U_FAILURE(status)) {
+                    delete [] units;
+                    return FALSE;
+                }
+                UnicodeString rawPattern;
+                getString(pluralBundle.getAlias(), rawPattern, status);
+                unitFormatters.formatters[units[currentUnit].getIndex()][currentWidth].add(
+                        ures_getKey(pluralBundle.getAlias()),
+                        rawPattern,
+                        status);
+            }
+        }
+    }
+    delete [] units;
+    return U_SUCCESS(status);
+}
+
+static UnicodeString loadNumericDateFormatterPattern(
+        const UResourceBundle *resource,
+        const char *pattern,
+        UErrorCode &status) {
+    UnicodeString result;
+    if (U_FAILURE(status)) {
+        return result;
+    }
+    CharString chs;
+    chs.append("durationUnits", status)
+            .append("/", status).append(pattern, status);
+    LocalUResourceBundlePointer patternBundle(
+            ures_getByKeyWithFallback(
+                resource,
+                chs.data(),
+                NULL,
+                &status));
+    if (U_FAILURE(status)) {
+        return result;
+    }
+    getString(patternBundle.getAlias(), result, status);
+    // Replace 'h' with 'H'
+    int32_t len = result.length();
+    UChar *buffer = result.getBuffer(len);
+    for (int32_t i = 0; i < len; ++i) {
+        if (buffer[i] == 0x68) { // 'h'
+            buffer[i] = 0x48; // 'H'
+        }
+    }
+    result.releaseBuffer(len);
+    return result;
+}
+
+static NumericDateFormatters *loadNumericDateFormatters(
+        const UResourceBundle *resource,
+        UErrorCode &status) {
+    if (U_FAILURE(status)) {
+        return NULL;
+    }
+    NumericDateFormatters *result = new NumericDateFormatters(
+        loadNumericDateFormatterPattern(resource, "hm", status),
+        loadNumericDateFormatterPattern(resource, "ms", status),
+        loadNumericDateFormatterPattern(resource, "hms", status),
+        status);
+    if (U_FAILURE(status)) {
+        delete result;
+        return NULL;
+    }
+    return result;
+}
+
+static SharedObject *U_CALLCONV createData(
+        const char *localeId, UErrorCode &status) {
+    LocalUResourceBundlePointer topLevel(ures_open(NULL, localeId, &status));
+    static UNumberFormatStyle currencyStyles[] = {
+            UNUM_CURRENCY_PLURAL, UNUM_CURRENCY_ISO, UNUM_CURRENCY};
+    if (U_FAILURE(status)) {
+        return NULL;
+    }
+    LocalPointer<MeasureFormatData> result(new MeasureFormatData());
+    LocalPointer<UnitFormatters> unitFormatters(new UnitFormatters());
+    if (result.getAlias() == NULL
+            || unitFormatters.getAlias() == NULL) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+        return NULL;
+    }
+    if (!load(
+            topLevel.getAlias(),
+            *unitFormatters,
+            status)) {
+        return NULL;
+    }
+    if (!result->unitFormatters.reset(unitFormatters.orphan())) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+        return NULL;
+    }
+
+    LocalPointer<NumericDateFormatters> ndf(
+            loadNumericDateFormatters(topLevel.getAlias(), status));
+    if (U_FAILURE(status)) {
+        return NULL;
+    }
+    if (!result->numericDateFormatters.reset(ndf.orphan())) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+        return NULL;
+    }
+
+    LocalPointer<PluralRules> pr(PluralRules::forLocale(localeId, status));
+    if (U_FAILURE(status)) {
+        return NULL;
+    }
+    if (!result->pluralRules.reset(pr.orphan())) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+        return NULL;
+    }
+
+    LocalPointer<NumberFormat> nf(
+            NumberFormat::createInstance(localeId, status));
+    if (U_FAILURE(status)) {
+        return NULL;
+    }
+    if (!result->numberFormat.reset(nf.orphan())) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+        return NULL;
+    }
+
+    for (int32_t i = 0; i <= UMEASFMT_WIDTH_NARROW; ++i) {
+        LocalPointer<NumberFormat> cf(
+                NumberFormat::createInstance(
+                        localeId, currencyStyles[i], status));
+        if (U_FAILURE(status)) {
+            return NULL;
+        }
+        if (!result->currencyFormats[i].reset(cf.orphan())) {
+            status = U_MEMORY_ALLOCATION_ERROR;
+            return NULL;
+        }
+    }
+
+    return result.orphan();
+}
+
+static void U_CALLCONV cacheInit(UErrorCode &status) {
+    U_ASSERT(gCache == NULL);
+    U_ASSERT(MeasureUnit::getIndexCount() == MEAS_UNIT_COUNT);
+    ucln_i18n_registerCleanup(UCLN_I18N_MEASFMT, measfmt_cleanup);
+    gCache = new SimpleLRUCache(100, &createData, status);
+    if (U_FAILURE(status)) {
+        delete gCache;
+        gCache = NULL;
+    }
+}
+
+static void getFromCache(
+        const char *locale,
+        const MeasureFormatData *&ptr,
+        UErrorCode &status) {
+    umtx_initOnce(gCacheInitOnce, &cacheInit, status);
+    if (U_FAILURE(status)) {
+        return;
+    }
+    Mutex lock(&gCacheMutex);
+    gCache->get(locale, ptr, status);
+}
+
+static int32_t toHMS(
+        const Measure *measures,
+        int32_t measureCount,
+        Formattable *hms,
+        UErrorCode &status) {
+    if (U_FAILURE(status)) {
+        return 0;
+    }
+    int32_t result = 0;
+    LocalPointer<MeasureUnit> hourUnit(MeasureUnit::createHour(status));
+    LocalPointer<MeasureUnit> minuteUnit(MeasureUnit::createMinute(status));
+    LocalPointer<MeasureUnit> secondUnit(MeasureUnit::createSecond(status));
+    if (U_FAILURE(status)) {
+        return 0;
+    }
+    int32_t count = 0;
+    for (int32_t i = 0; i < measureCount; ++i) {
+        if (measures[i].getUnit() == *hourUnit) {
+            if ((result & 1) == 0) {
+                ++count;
+            } else {
+                return 0;
+            }
+            hms[0] = measures[i].getNumber();
+            result |= 1;
+        } else if (measures[i].getUnit() == *minuteUnit) {
+            if ((result & 2) == 0) {
+                ++count;
+            } else {
+                return 0;
+            }
+            hms[1] = measures[i].getNumber();
+            result |= 2;
+        } else if (measures[i].getUnit() == *secondUnit) {
+            if ((result & 4) == 0) {
+                ++count;
+            } else {
+                return 0;
+            }
+            hms[2] = measures[i].getNumber();
+            result |= 4;
+        } else {
+            return 0;
+        }
+    }
+    return result;
+}
+
 
-MeasureFormat::~MeasureFormat() {}
+MeasureFormat::MeasureFormat(
+        const Locale &locale, UMeasureFormatWidth w, UErrorCode &status)
+        : ptr(NULL), width(w) {
+    initMeasureFormat(locale, w, status);
+}
+
+MeasureFormat::MeasureFormat(
+        const Locale &locale,
+        UMeasureFormatWidth w,
+        NumberFormat *nfToAdopt,
+        UErrorCode &status) 
+        : ptr(NULL), width(w) {
+    initMeasureFormat(locale, w, status);
+    adoptNumberFormat(nfToAdopt, status);
+}
+
+MeasureFormat::MeasureFormat(const MeasureFormat &other)
+        : Format(other), ptr(other.ptr), width(other.width) {
+    ptr->addRef();
+}
+
+MeasureFormat &MeasureFormat::operator=(const MeasureFormat &other) {
+    if (this == &other) {
+        return *this;
+    }
+    Format::operator=(other);
+    SharedObject::copyPtr(other.ptr, ptr);
+    width = other.width;
+    return *this;
+}
+
+MeasureFormat::MeasureFormat() : ptr(NULL), width(UMEASFMT_WIDTH_WIDE) {
+}
+
+MeasureFormat::~MeasureFormat() {
+    if (ptr != NULL) {
+        ptr->removeRef();
+    }
+}
+
+UBool MeasureFormat::operator==(const Format &other) const {
+    const MeasureFormat *rhs = dynamic_cast<const MeasureFormat *>(&other);
+    if (rhs == NULL) {
+        return FALSE;
+    }
+    // Same objects are equivalent
+    if (this == rhs) {
+        return TRUE;
+    }
+    // differing widths aren't equivalent
+    if (width != rhs->width) {
+        return FALSE;
+    }
+    // Width the same, same shared data -> equivlanet
+    if (ptr == rhs->ptr) {
+        return TRUE;
+    }
+    // Width same, but differing shred data. Depends on locale
+    // and number format objects being the same.
+    UErrorCode status = U_ZERO_ERROR;
+    const char *localeId = getLocaleID(status);
+    const char *rhsLocaleId = rhs->getLocaleID(status);
+    if (U_FAILURE(status)) {
+        // On failure, assume not equal
+        return FALSE;
+    }
+    return (uprv_strcmp(localeId, rhsLocaleId) == 0
+            && *ptr->numberFormat == *rhs->ptr->numberFormat);
+}
+
+Format *MeasureFormat::clone() const {
+    return new MeasureFormat(*this);
+}
+
+UnicodeString &MeasureFormat::format(
+        const Formattable &obj,
+        UnicodeString &appendTo,
+        FieldPosition &pos,
+        UErrorCode &status) const {
+    if (U_FAILURE(status)) return appendTo;
+    if (obj.getType() == Formattable::kObject) {
+        const UObject* formatObj = obj.getObject();
+        const Measure* amount = dynamic_cast<const Measure*>(formatObj);
+        if (amount != NULL) {
+            return formatMeasure(*amount, appendTo, pos, status);
+        }
+    }
+    status = U_ILLEGAL_ARGUMENT_ERROR;
+    return appendTo;
+}
+
+void MeasureFormat::parseObject(
+        const UnicodeString & /*source*/,
+        Formattable & /*result*/,
+        ParsePosition& /*pos*/) const {
+    return;
+}
+
+UnicodeString &MeasureFormat::formatMeasures(
+        const Measure *measures,
+        int32_t measureCount,
+        UnicodeString &appendTo,
+        FieldPosition &pos,
+        UErrorCode &status) const {
+    static const char *listStyles[] = {"unit", "unit-short", "unit-narrow"};
+    if (U_FAILURE(status)) {
+        return appendTo;
+    }
+    if (measureCount == 0) {
+        return appendTo;
+    }
+    if (measureCount == 1) {
+        return formatMeasure(measures[0], appendTo, pos, status);
+    }
+    if (width == UMEASFMT_WIDTH_NUMERIC) {
+        Formattable hms[3];
+        int32_t bitMap = toHMS(measures, measureCount, hms, status);
+        if (bitMap > 0) {
+            return formatNumeric(hms, bitMap, appendTo, status);
+        }
+    }
+    LocalPointer<ListFormatter> lf(
+            ListFormatter::createInstance(
+                    getLocale(status),
+                    listStyles[widthToIndex(width)],
+                    status));
+    if (U_FAILURE(status)) {
+        return appendTo;
+    }
+    if (pos.getField() != FieldPosition::DONT_CARE) {
+        return formatMeasuresSlowTrack(
+                measures, measureCount, *lf, appendTo, pos, status);
+    }
+    UnicodeString *results = new UnicodeString[measureCount];
+    if (results == NULL) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+        return appendTo;
+    }
+    for (int32_t i = 0; i < measureCount; ++i) {
+        formatMeasure(measures[i], results[i], pos, status);
+    }
+    lf->format(results, measureCount, appendTo, status);
+    delete [] results; 
+    return appendTo;
+}
+
+void MeasureFormat::initMeasureFormat(
+        const Locale &locale, UMeasureFormatWidth w, UErrorCode &status) {
+    if (U_FAILURE(status)) {
+        return;
+    }
+    const char *name = locale.getName();
+    setLocaleIDs(name, name);
+    width = w;
+    getFromCache(name, ptr, status);
+}
+
+void MeasureFormat::adoptNumberFormat(NumberFormat *nfToAdopt, UErrorCode &status) {
+    if (U_FAILURE(status)) {
+        return;
+    }
+    MeasureFormatData* wptr = SharedObject::copyOnWrite(ptr);
+    if (wptr == NULL) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+        return;
+    }
+    if (!wptr->numberFormat.reset(nfToAdopt)) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+        return;
+    }
+}
+
+UBool MeasureFormat::setMeasureFormatLocale(const Locale &locale, UErrorCode &status) {
+    if (U_FAILURE(status) || locale == getLocale(status)) {
+        return FALSE;
+    }
+    initMeasureFormat(locale, width, status);
+    return U_SUCCESS(status);
+} 
+
+const NumberFormat &MeasureFormat::getNumberFormat() const {
+    return *ptr->numberFormat;
+}
+
+const PluralRules &MeasureFormat::getPluralRules() const {
+    return *ptr->pluralRules;
+}
+
+Locale MeasureFormat::getLocale(UErrorCode &status) const {
+    return Format::getLocale(ULOC_VALID_LOCALE, status);
+}
+
+const char *MeasureFormat::getLocaleID(UErrorCode &status) const {
+    return Format::getLocaleID(ULOC_VALID_LOCALE, status);
+}
+
+UnicodeString &MeasureFormat::formatMeasure(
+        const Measure &measure,
+        UnicodeString &appendTo,
+        FieldPosition &pos,
+        UErrorCode &status) const {
+    if (U_FAILURE(status)) {
+        return appendTo;
+    }
+    const Formattable& amtNumber = measure.getNumber();
+    const MeasureUnit& amtUnit = measure.getUnit();
+    if (isCurrency(amtUnit)) {
+        UChar isoCode[4];
+        u_charsToUChars(amtUnit.getSubtype(), isoCode, 4);
+        return ptr->currencyFormats[widthToIndex(width)]->format(
+                new CurrencyAmount(amtNumber, isoCode, status),
+                appendTo,
+                pos,
+                status);
+    }
+    const QuantityFormatter *quantityFormatter = getQuantityFormatter(
+            amtUnit.getIndex(), widthToIndex(width), status);
+    if (U_FAILURE(status)) {
+        return appendTo;
+    }
+    return quantityFormatter->format(
+            amtNumber,
+            *ptr->numberFormat,
+            *ptr->pluralRules, appendTo,
+            pos,
+            status);
+}
+
+UnicodeString &MeasureFormat::formatNumeric(
+        const Formattable *hms,  // always length 3
+        int32_t bitMap,   // 1=hourset, 2=minuteset, 4=secondset
+        UnicodeString &appendTo,
+        UErrorCode &status) const {
+    if (U_FAILURE(status)) {
+        return appendTo;
+    }
+    UDate millis = 
+        (UDate) (((hms[0].getDouble(status) * 60.0
+             + hms[1].getDouble(status)) * 60.0
+                  + hms[2].getDouble(status)) * 1000.0);
+    switch (bitMap) {
+    case 5: // hs
+    case 7: // hms
+        return formatNumeric(
+                millis,
+                ptr->numericDateFormatters->hourMinuteSecond,
+                UDAT_SECOND_FIELD,
+                hms[2],
+                appendTo,
+                status);
+        break;
+    case 6: // ms
+        return formatNumeric(
+                millis,
+                ptr->numericDateFormatters->minuteSecond,
+                UDAT_SECOND_FIELD,
+                hms[2],
+                appendTo,
+                status);
+        break;
+    case 3: // hm
+        return formatNumeric(
+                millis,
+                ptr->numericDateFormatters->hourMinute,
+                UDAT_MINUTE_FIELD,
+                hms[1],
+                appendTo,
+                status);
+        break;
+    default:
+        status = U_INTERNAL_PROGRAM_ERROR;
+        return appendTo;
+        break;
+    }
+    return appendTo;
+}
+
+UnicodeString &MeasureFormat::formatNumeric(
+        UDate date,
+        const DateFormat &dateFmt,
+        UDateFormatField smallestField,
+        const Formattable &smallestAmount,
+        UnicodeString &appendTo,
+        UErrorCode &status) const {
+    if (U_FAILURE(status)) {
+        return appendTo;
+    }
+    UnicodeString smallestAmountFormatted;
+    ptr->numberFormat->format(
+            smallestAmount, smallestAmountFormatted, status);
+    FieldPosition smallestFieldPosition(smallestField);
+    UnicodeString draft;
+    dateFmt.format(date, draft, smallestFieldPosition, status);
+    if (smallestFieldPosition.getBeginIndex() != 0 ||
+        smallestFieldPosition.getEndIndex() != 0) {
+        appendTo.append(draft, 0, smallestFieldPosition.getBeginIndex());
+        appendTo.append(smallestAmountFormatted);
+        appendTo.append(
+                draft,
+                smallestFieldPosition.getEndIndex(),
+                draft.length());
+    } else {
+        appendTo.append(draft);
+    }
+    return appendTo;
+}
+
+const QuantityFormatter *MeasureFormat::getQuantityFormatter(
+        int32_t index,
+        int32_t widthIndex,
+        UErrorCode &status) const {
+    if (U_FAILURE(status)) {
+        return NULL;
+    }
+    const QuantityFormatter *formatters =
+            ptr->unitFormatters->formatters[index];
+    if (formatters[widthIndex].isValid()) {
+        return &formatters[widthIndex];
+    }
+    if (formatters[UMEASFMT_WIDTH_SHORT].isValid()) {
+        return &formatters[UMEASFMT_WIDTH_SHORT];
+    }
+    if (formatters[UMEASFMT_WIDTH_WIDE].isValid()) {
+        return &formatters[UMEASFMT_WIDTH_WIDE];
+    }
+    status = U_MISSING_RESOURCE_ERROR;
+    return NULL;
+}
+
+UnicodeString &MeasureFormat::formatMeasuresSlowTrack(
+        const Measure *measures,
+        int32_t measureCount,
+        const ListFormatter& lf,
+        UnicodeString& appendTo,
+        FieldPosition& pos,
+        UErrorCode& status) const {
+    if (U_FAILURE(status)) {
+        return appendTo;
+    }
+    FieldPosition dontCare(FieldPosition::DONT_CARE);
+    FieldPosition fpos(pos.getField());
+    UnicodeString *results = new UnicodeString[measureCount];
+    int32_t fieldPositionFoundIndex = -1;
+    for (int32_t i = 0; i < measureCount; ++i) {
+        if (fieldPositionFoundIndex == -1) {
+            formatMeasure(measures[i], results[i], fpos, status);
+            if (U_FAILURE(status)) {
+                delete [] results;
+                return appendTo;
+            }
+            if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) {
+                fieldPositionFoundIndex = i;
+            }
+        } else {
+            formatMeasure(measures[i], results[i], dontCare, status);
+        }
+    }
+    int32_t offset;
+    lf.format(
+            results,
+            measureCount,
+            appendTo,
+            fieldPositionFoundIndex,
+            offset,
+            status);
+    if (U_FAILURE(status)) {
+        delete [] results;
+        return appendTo;
+    }
+    if (offset != -1) {
+        pos.setBeginIndex(fpos.getBeginIndex() + offset);
+        pos.setEndIndex(fpos.getEndIndex() + offset);
+    }
+    delete [] results;
+    return appendTo;
+}
 
 MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(const Locale& locale,
                                                    UErrorCode& ec) {
diff --git a/icu4c/source/i18n/measunit.cpp b/icu4c/source/i18n/measunit.cpp
new file mode 100644 (file)
index 0000000..42903ab
--- /dev/null
@@ -0,0 +1,734 @@
+/*
+**********************************************************************
+* Copyright (c) 2004-2014, International Business Machines
+* Corporation and others.  All Rights Reserved.
+**********************************************************************
+* Author: Alan Liu
+* Created: April 26, 2004
+* Since: ICU 3.0
+**********************************************************************
+*/
+#include "unicode/measunit.h"
+#include "unicode/uenum.h"
+#include "ustrenum.h"
+#include "cstring.h"
+#include "uassert.h"
+
+#define LENGTHOF(array) (int32_t)(sizeof(array) / sizeof((array)[0]))
+
+U_NAMESPACE_BEGIN
+
+static const int32_t gOffsets[] = {
+    0,
+    1,
+    4,
+    10,
+    270,
+    278,
+    288,
+    292,
+    295,
+    298,
+    301,
+    303,
+    306
+};
+
+static const int32_t gIndexes[] = {
+    0,
+    1,
+    4,
+    10,
+    10,
+    18,
+    28,
+    32,
+    35,
+    38,
+    41,
+    43,
+    46
+};
+
+static const char * const gTypes[] = {
+    "acceleration",
+    "angle",
+    "area",
+    "currency",
+    "duration",
+    "length",
+    "mass",
+    "power",
+    "pressure",
+    "speed",
+    "temperature",
+    "volume"
+};
+
+static const char * const gSubTypes[] = {
+    "g-force",
+    "arc-minute",
+    "arc-second",
+    "degree",
+    "acre",
+    "hectare",
+    "square-foot",
+    "square-kilometer",
+    "square-meter",
+    "square-mile",
+    "ADP",
+    "AED",
+    "AFA",
+    "AFN",
+    "ALL",
+    "AMD",
+    "ANG",
+    "AOA",
+    "AON",
+    "AOR",
+    "ARA",
+    "ARP",
+    "ARS",
+    "ATS",
+    "AUD",
+    "AWG",
+    "AYM",
+    "AZM",
+    "AZN",
+    "BAD",
+    "BAM",
+    "BBD",
+    "BDT",
+    "BEC",
+    "BEF",
+    "BEL",
+    "BGL",
+    "BGN",
+    "BHD",
+    "BIF",
+    "BMD",
+    "BND",
+    "BOB",
+    "BOV",
+    "BRC",
+    "BRE",
+    "BRL",
+    "BRN",
+    "BRR",
+    "BSD",
+    "BTN",
+    "BWP",
+    "BYB",
+    "BYR",
+    "BZD",
+    "CAD",
+    "CDF",
+    "CHC",
+    "CHE",
+    "CHF",
+    "CHW",
+    "CLF",
+    "CLP",
+    "CNY",
+    "COP",
+    "COU",
+    "CRC",
+    "CSD",
+    "CSK",
+    "CUC",
+    "CUP",
+    "CVE",
+    "CYP",
+    "CZK",
+    "DDM",
+    "DEM",
+    "DJF",
+    "DKK",
+    "DOP",
+    "DZD",
+    "ECS",
+    "ECV",
+    "EEK",
+    "EGP",
+    "ERN",
+    "ESA",
+    "ESB",
+    "ESP",
+    "ETB",
+    "EUR",
+    "FIM",
+    "FJD",
+    "FKP",
+    "FRF",
+    "GBP",
+    "GEK",
+    "GEL",
+    "GHC",
+    "GHP",
+    "GHS",
+    "GIP",
+    "GMD",
+    "GNF",
+    "GQE",
+    "GRD",
+    "GTQ",
+    "GWP",
+    "GYD",
+    "HKD",
+    "HNL",
+    "HRD",
+    "HRK",
+    "HTG",
+    "HUF",
+    "IDR",
+    "IEP",
+    "ILS",
+    "INR",
+    "IQD",
+    "IRR",
+    "ISK",
+    "ITL",
+    "JMD",
+    "JOD",
+    "JPY",
+    "KES",
+    "KGS",
+    "KHR",
+    "KMF",
+    "KPW",
+    "KRW",
+    "KWD",
+    "KYD",
+    "KZT",
+    "LAK",
+    "LBP",
+    "LKR",
+    "LRD",
+    "LSL",
+    "LTL",
+    "LTT",
+    "LUC",
+    "LUF",
+    "LUL",
+    "LVL",
+    "LVR",
+    "LYD",
+    "MAD",
+    "MDL",
+    "MGA",
+    "MGF",
+    "MKD",
+    "MLF",
+    "MMK",
+    "MNT",
+    "MOP",
+    "MRO",
+    "MTL",
+    "MUR",
+    "MVR",
+    "MWK",
+    "MXN",
+    "MXV",
+    "MYR",
+    "MZM",
+    "MZN",
+    "NAD",
+    "NGN",
+    "NIO",
+    "NLG",
+    "NOK",
+    "NPR",
+    "NZD",
+    "OMR",
+    "PAB",
+    "PEI",
+    "PEN",
+    "PES",
+    "PGK",
+    "PHP",
+    "PKR",
+    "PLN",
+    "PLZ",
+    "PTE",
+    "PYG",
+    "QAR",
+    "ROL",
+    "RON",
+    "RSD",
+    "RUB",
+    "RUR",
+    "RWF",
+    "SAR",
+    "SBD",
+    "SCR",
+    "SDD",
+    "SDG",
+    "SEK",
+    "SGD",
+    "SHP",
+    "SIT",
+    "SKK",
+    "SLL",
+    "SOS",
+    "SRD",
+    "SRG",
+    "SSP",
+    "STD",
+    "SVC",
+    "SYP",
+    "SZL",
+    "THB",
+    "TJR",
+    "TJS",
+    "TMM",
+    "TMT",
+    "TND",
+    "TOP",
+    "TPE",
+    "TRL",
+    "TRY",
+    "TTD",
+    "TWD",
+    "TZS",
+    "UAH",
+    "UAK",
+    "UGX",
+    "USD",
+    "USN",
+    "USS",
+    "UYI",
+    "UYU",
+    "UZS",
+    "VEB",
+    "VEF",
+    "VND",
+    "VUV",
+    "WST",
+    "XAF",
+    "XAG",
+    "XAU",
+    "XBA",
+    "XBB",
+    "XBC",
+    "XBD",
+    "XCD",
+    "XDR",
+    "XEU",
+    "XOF",
+    "XPD",
+    "XPF",
+    "XPT",
+    "XSU",
+    "XTS",
+    "XUA",
+    "XXX",
+    "YDD",
+    "YER",
+    "YUM",
+    "YUN",
+    "ZAL",
+    "ZAR",
+    "ZMK",
+    "ZMW",
+    "ZRN",
+    "ZRZ",
+    "ZWD",
+    "ZWL",
+    "ZWN",
+    "ZWR",
+    "day",
+    "hour",
+    "millisecond",
+    "minute",
+    "month",
+    "second",
+    "week",
+    "year",
+    "centimeter",
+    "foot",
+    "inch",
+    "kilometer",
+    "light-year",
+    "meter",
+    "mile",
+    "millimeter",
+    "picometer",
+    "yard",
+    "gram",
+    "kilogram",
+    "ounce",
+    "pound",
+    "horsepower",
+    "kilowatt",
+    "watt",
+    "hectopascal",
+    "inch-hg",
+    "millibar",
+    "kilometer-per-hour",
+    "meter-per-second",
+    "mile-per-hour",
+    "celsius",
+    "fahrenheit",
+    "cubic-kilometer",
+    "cubic-mile",
+    "liter"
+};
+
+MeasureUnit *MeasureUnit::createGForce(UErrorCode &status) {
+    return MeasureUnit::create(0, 0, status);
+}
+
+MeasureUnit *MeasureUnit::createArcMinute(UErrorCode &status) {
+    return MeasureUnit::create(1, 0, status);
+}
+
+MeasureUnit *MeasureUnit::createArcSecond(UErrorCode &status) {
+    return MeasureUnit::create(1, 1, status);
+}
+
+MeasureUnit *MeasureUnit::createDegree(UErrorCode &status) {
+    return MeasureUnit::create(1, 2, status);
+}
+
+MeasureUnit *MeasureUnit::createAcre(UErrorCode &status) {
+    return MeasureUnit::create(2, 0, status);
+}
+
+MeasureUnit *MeasureUnit::createHectare(UErrorCode &status) {
+    return MeasureUnit::create(2, 1, status);
+}
+
+MeasureUnit *MeasureUnit::createSquareFoot(UErrorCode &status) {
+    return MeasureUnit::create(2, 2, status);
+}
+
+MeasureUnit *MeasureUnit::createSquareKilometer(UErrorCode &status) {
+    return MeasureUnit::create(2, 3, status);
+}
+
+MeasureUnit *MeasureUnit::createSquareMeter(UErrorCode &status) {
+    return MeasureUnit::create(2, 4, status);
+}
+
+MeasureUnit *MeasureUnit::createSquareMile(UErrorCode &status) {
+    return MeasureUnit::create(2, 5, status);
+}
+
+MeasureUnit *MeasureUnit::createDay(UErrorCode &status) {
+    return MeasureUnit::create(4, 0, status);
+}
+
+MeasureUnit *MeasureUnit::createHour(UErrorCode &status) {
+    return MeasureUnit::create(4, 1, status);
+}
+
+MeasureUnit *MeasureUnit::createMillisecond(UErrorCode &status) {
+    return MeasureUnit::create(4, 2, status);
+}
+
+MeasureUnit *MeasureUnit::createMinute(UErrorCode &status) {
+    return MeasureUnit::create(4, 3, status);
+}
+
+MeasureUnit *MeasureUnit::createMonth(UErrorCode &status) {
+    return MeasureUnit::create(4, 4, status);
+}
+
+MeasureUnit *MeasureUnit::createSecond(UErrorCode &status) {
+    return MeasureUnit::create(4, 5, status);
+}
+
+MeasureUnit *MeasureUnit::createWeek(UErrorCode &status) {
+    return MeasureUnit::create(4, 6, status);
+}
+
+MeasureUnit *MeasureUnit::createYear(UErrorCode &status) {
+    return MeasureUnit::create(4, 7, status);
+}
+
+MeasureUnit *MeasureUnit::createCentimeter(UErrorCode &status) {
+    return MeasureUnit::create(5, 0, status);
+}
+
+MeasureUnit *MeasureUnit::createFoot(UErrorCode &status) {
+    return MeasureUnit::create(5, 1, status);
+}
+
+MeasureUnit *MeasureUnit::createInch(UErrorCode &status) {
+    return MeasureUnit::create(5, 2, status);
+}
+
+MeasureUnit *MeasureUnit::createKilometer(UErrorCode &status) {
+    return MeasureUnit::create(5, 3, status);
+}
+
+MeasureUnit *MeasureUnit::createLightYear(UErrorCode &status) {
+    return MeasureUnit::create(5, 4, status);
+}
+
+MeasureUnit *MeasureUnit::createMeter(UErrorCode &status) {
+    return MeasureUnit::create(5, 5, status);
+}
+
+MeasureUnit *MeasureUnit::createMile(UErrorCode &status) {
+    return MeasureUnit::create(5, 6, status);
+}
+
+MeasureUnit *MeasureUnit::createMillimeter(UErrorCode &status) {
+    return MeasureUnit::create(5, 7, status);
+}
+
+MeasureUnit *MeasureUnit::createPicometer(UErrorCode &status) {
+    return MeasureUnit::create(5, 8, status);
+}
+
+MeasureUnit *MeasureUnit::createYard(UErrorCode &status) {
+    return MeasureUnit::create(5, 9, status);
+}
+
+MeasureUnit *MeasureUnit::createGram(UErrorCode &status) {
+    return MeasureUnit::create(6, 0, status);
+}
+
+MeasureUnit *MeasureUnit::createKilogram(UErrorCode &status) {
+    return MeasureUnit::create(6, 1, status);
+}
+
+MeasureUnit *MeasureUnit::createOunce(UErrorCode &status) {
+    return MeasureUnit::create(6, 2, status);
+}
+
+MeasureUnit *MeasureUnit::createPound(UErrorCode &status) {
+    return MeasureUnit::create(6, 3, status);
+}
+
+MeasureUnit *MeasureUnit::createHorsepower(UErrorCode &status) {
+    return MeasureUnit::create(7, 0, status);
+}
+
+MeasureUnit *MeasureUnit::createKilowatt(UErrorCode &status) {
+    return MeasureUnit::create(7, 1, status);
+}
+
+MeasureUnit *MeasureUnit::createWatt(UErrorCode &status) {
+    return MeasureUnit::create(7, 2, status);
+}
+
+MeasureUnit *MeasureUnit::createHectopascal(UErrorCode &status) {
+    return MeasureUnit::create(8, 0, status);
+}
+
+MeasureUnit *MeasureUnit::createInchHg(UErrorCode &status) {
+    return MeasureUnit::create(8, 1, status);
+}
+
+MeasureUnit *MeasureUnit::createMillibar(UErrorCode &status) {
+    return MeasureUnit::create(8, 2, status);
+}
+
+MeasureUnit *MeasureUnit::createKilometerPerHour(UErrorCode &status) {
+    return MeasureUnit::create(9, 0, status);
+}
+
+MeasureUnit *MeasureUnit::createMeterPerSecond(UErrorCode &status) {
+    return MeasureUnit::create(9, 1, status);
+}
+
+MeasureUnit *MeasureUnit::createMilePerHour(UErrorCode &status) {
+    return MeasureUnit::create(9, 2, status);
+}
+
+MeasureUnit *MeasureUnit::createCelsius(UErrorCode &status) {
+    return MeasureUnit::create(10, 0, status);
+}
+
+MeasureUnit *MeasureUnit::createFahrenheit(UErrorCode &status) {
+    return MeasureUnit::create(10, 1, status);
+}
+
+MeasureUnit *MeasureUnit::createCubicKilometer(UErrorCode &status) {
+    return MeasureUnit::create(11, 0, status);
+}
+
+MeasureUnit *MeasureUnit::createCubicMile(UErrorCode &status) {
+    return MeasureUnit::create(11, 1, status);
+}
+
+MeasureUnit *MeasureUnit::createLiter(UErrorCode &status) {
+    return MeasureUnit::create(11, 2, status);
+}
+
+static int32_t binarySearch(
+        const char * const * array, int32_t start, int32_t end, const char * key) {
+    while (start < end) {
+        int32_t mid = (start + end) / 2;
+        int32_t cmp = uprv_strcmp(array[mid], key);
+        if (cmp < 0) {
+            start = mid + 1;
+            continue;
+        }
+        if (cmp == 0) {
+            return mid;
+        }
+        end = mid;
+    }
+    return -1;
+}
+    
+MeasureUnit::MeasureUnit(const MeasureUnit &other)
+        : fTypeId(other.fTypeId), fSubTypeId(other.fSubTypeId) {
+    uprv_strcpy(fCurrency, other.fCurrency);
+}
+
+MeasureUnit &MeasureUnit::operator=(const MeasureUnit &other) {
+    if (this == &other) {
+        return *this;
+    }
+    fTypeId = other.fTypeId;
+    fSubTypeId = other.fSubTypeId;
+    uprv_strcpy(fCurrency, other.fCurrency);
+    return *this;
+}
+
+UObject *MeasureUnit::clone() const {
+    return new MeasureUnit(*this);
+}
+
+MeasureUnit::~MeasureUnit() {
+}
+
+const char *MeasureUnit::getType() const {
+    return gTypes[fTypeId];
+}
+
+const char *MeasureUnit::getSubtype() const {
+    return fCurrency[0] == 0 ? gSubTypes[getOffset()] : fCurrency;
+}
+
+UBool MeasureUnit::operator==(const UObject& other) const {
+    const MeasureUnit *rhs = dynamic_cast<const MeasureUnit*>(&other);
+    if (rhs == NULL) {
+        return FALSE;
+    }
+    return (
+            fTypeId == rhs->fTypeId
+            && fSubTypeId == rhs->fSubTypeId
+            && uprv_strcmp(fCurrency, rhs->fCurrency) == 0);
+}
+
+int32_t MeasureUnit::getIndex() const {
+    return gIndexes[fTypeId] + fSubTypeId;
+}
+
+int32_t MeasureUnit::getAvailable(
+        MeasureUnit *dest,
+        int32_t destCapacity,
+        UErrorCode &errorCode) {
+    if (U_FAILURE(errorCode)) {
+        return 0;
+    }
+    if (destCapacity < LENGTHOF(gSubTypes)) {
+        errorCode = U_BUFFER_OVERFLOW_ERROR;
+        return LENGTHOF(gSubTypes);
+    }
+    int32_t idx = 0;
+    for (int32_t typeIdx = 0; typeIdx < LENGTHOF(gTypes); ++typeIdx) {
+        int32_t len = gOffsets[typeIdx + 1] - gOffsets[typeIdx];
+        for (int32_t subTypeIdx = 0; subTypeIdx < len; ++subTypeIdx) {
+            dest[idx].setTo(typeIdx, subTypeIdx);
+            ++idx;
+        }
+    }
+    U_ASSERT(idx == LENGTHOF(gSubTypes));
+    return LENGTHOF(gSubTypes);
+}
+
+int32_t MeasureUnit::getAvailable(
+        const char *type,
+        MeasureUnit *dest,
+        int32_t destCapacity,
+        UErrorCode &errorCode) {
+    if (U_FAILURE(errorCode)) {
+        return 0;
+    }
+    int32_t typeIdx = binarySearch(gTypes, 0, LENGTHOF(gTypes), type);
+    if (typeIdx == -1) {
+        return 0;
+    }
+    int32_t len = gOffsets[typeIdx + 1] - gOffsets[typeIdx];
+    if (destCapacity < len) {
+        errorCode = U_BUFFER_OVERFLOW_ERROR;
+        return len;
+    }
+    for (int subTypeIdx = 0; subTypeIdx < len; ++subTypeIdx) {
+        dest[subTypeIdx].setTo(typeIdx, subTypeIdx);
+    }
+    return len;
+}
+
+StringEnumeration* MeasureUnit::getAvailableTypes(UErrorCode &errorCode) {
+    UEnumeration *uenum = uenum_openCharStringsEnumeration(
+            gTypes, LENGTHOF(gTypes), &errorCode);
+    if (U_FAILURE(errorCode)) {
+        uenum_close(uenum);
+        return NULL;
+    }
+    StringEnumeration *result = new UStringEnumeration(uenum);
+    if (result == NULL) {
+        errorCode = U_MEMORY_ALLOCATION_ERROR;
+        uenum_close(uenum);
+        return NULL;
+    }
+    return result;
+}
+
+int32_t MeasureUnit::getIndexCount() {
+    return gIndexes[LENGTHOF(gIndexes) - 1];
+}
+
+MeasureUnit *MeasureUnit::create(int typeId, int subTypeId, UErrorCode &status) {
+    if (U_FAILURE(status)) {
+        return NULL;
+    }
+    MeasureUnit *result = new MeasureUnit(typeId, subTypeId);
+    if (result == NULL) {
+        status = U_MEMORY_ALLOCATION_ERROR;
+    }
+    return result;
+}
+
+void MeasureUnit::initTime(const char *timeId) {
+    int32_t result = binarySearch(gTypes, 0, LENGTHOF(gTypes), "duration");
+    U_ASSERT(result != -1);
+    fTypeId = result;
+    result = binarySearch(gSubTypes, gOffsets[fTypeId], gOffsets[fTypeId + 1], timeId);
+    U_ASSERT(result != -1);
+    fSubTypeId = result - gOffsets[fTypeId]; 
+}
+
+void MeasureUnit::initCurrency(const char *isoCurrency) {
+    int32_t result = binarySearch(gTypes, 0, LENGTHOF(gTypes), "currency");
+    U_ASSERT(result != -1);
+    fTypeId = result;
+    result = binarySearch(
+            gSubTypes, gOffsets[fTypeId], gOffsets[fTypeId + 1], isoCurrency);
+    if (result != -1) {
+        fSubTypeId = result - gOffsets[fTypeId];
+    } else {
+        uprv_strncpy(fCurrency, isoCurrency, LENGTHOF(fCurrency));
+    }
+}
+
+void MeasureUnit::setTo(int32_t typeId, int32_t subTypeId) {
+    fTypeId = typeId;
+    fSubTypeId = subTypeId;
+    fCurrency[0] = 0;
+}
+
+int32_t MeasureUnit::getOffset() const {
+    return gOffsets[fTypeId] + fSubTypeId;
+}
+
+U_NAMESPACE_END
+
+
index 639de3e6db17ac4aef8eb1e50d2fcb1c7d8dab92..afc5bef6a09f9c44d88514113ea0982973eaf221 100644 (file)
@@ -1,6 +1,6 @@
 /*
 **********************************************************************
-* Copyright (c) 2004-2012, International Business Machines
+* Copyright (c) 2004-2014, International Business Machines
 * Corporation and others.  All Rights Reserved.
 **********************************************************************
 * Author: Alan Liu
@@ -44,6 +44,10 @@ Measure& Measure::operator=(const Measure& other) {
     return *this;
 }
 
+UObject *Measure::clone() const {
+    return new Measure(*this);
+}
+
 Measure::~Measure() {
     delete unit;
 }
@@ -55,13 +59,6 @@ UBool Measure::operator==(const UObject& other) const {
         (unit != NULL && *unit == m->getUnit());
 }
 
-//----------------------------------------------------------------------
-// MeasureUnit implementation
-
-MeasureUnit:: MeasureUnit() {}
-
-MeasureUnit::~MeasureUnit() {}
-
 U_NAMESPACE_END
 
 #endif // !UCONFIG_NO_FORMATTING
index 2a26fb5053eb6df92ee66bb12bfdd56f04c5cdb5..5b01f650ed3579bbafe1e43a8d0e1c177241962e 100644 (file)
@@ -15,6 +15,7 @@
 #include "unicode/plurrule.h"
 #include "charstr.h"
 #include "unicode/fmtable.h"
+#include "unicode/fieldpos.h"
 
 #define LENGTHOF(array) (int32_t)(sizeof(array) / sizeof((array)[0]))
 
@@ -107,11 +108,16 @@ UBool QuantityFormatter::add(
     return TRUE;
 }
 
+UBool QuantityFormatter::isValid() const {
+    return formatters[0] != NULL;
+}
+
 UnicodeString &QuantityFormatter::format(
             const Formattable& quantity,
             const NumberFormat &fmt,
             const PluralRules &rules,
             UnicodeString &appendTo,
+            FieldPosition &pos,
             UErrorCode &status) const {
     if (U_FAILURE(status)) {
         return appendTo;
@@ -154,9 +160,18 @@ UnicodeString &QuantityFormatter::format(
         return appendTo;
     }
     UnicodeString formattedNumber;
-    FieldPosition pos(0);
-    fmt.format(quantity, formattedNumber, pos, status);
-    return pattern->format(formattedNumber, appendTo, status);
+    FieldPosition fpos(pos.getField());
+    fmt.format(quantity, formattedNumber, fpos, status);
+    const UnicodeString *params[1] = {&formattedNumber};
+    int32_t offsets[1];
+    pattern->format(params, LENGTHOF(params), appendTo, offsets, LENGTHOF(offsets), status);
+    if (offsets[0] != -1) {
+        if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) {
+            pos.setBeginIndex(fpos.getBeginIndex() + offsets[0]);
+            pos.setEndIndex(fpos.getEndIndex() + offsets[0]);
+        }
+    }
+    return appendTo;
 }
 
 U_NAMESPACE_END
index a1576160335d96805224cf685db82f7b8c95805f..5dceaf652dceb101025fee1b31374c745db1cc17 100644 (file)
@@ -19,6 +19,7 @@ class UnicodeString;
 class PluralRules;
 class NumberFormat;
 class Formattable;
+class FieldPosition;
 
 /**
  * A plural aware formatter that is good for expressing a single quantity and
@@ -74,6 +75,11 @@ public:
             const UnicodeString &rawPattern,
             UErrorCode &status);
 
+    /**
+     * returns TRUE if this object has at least the "other" variant.
+     */
+    UBool isValid() 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
@@ -91,6 +97,7 @@ public:
             const NumberFormat &fmt,
             const PluralRules &rules,
             UnicodeString &appendTo,
+            FieldPosition &pos,
             UErrorCode &status) const;
 
 private:
index 8d8e6a9a6dfd0205e6d6092ba18cd33d106cb188..1b26cd91b6de690bbcd4aef4238e224d487ade00 100644 (file)
@@ -22,7 +22,6 @@
 #include "uresimp.h"
 #include "unicode/ures.h"
 #include "cstring.h"
-#include "plurrule_impl.h"
 #include "ucln_in.h"
 #include "mutex.h"
 #include "charstr.h"
@@ -648,11 +647,13 @@ UnicodeString& RelativeDateTimeFormatter::format(
         return appendTo;
     }
     int32_t bFuture = direction == UDAT_DIRECTION_NEXT ? 1 : 0;
+    FieldPosition pos(FieldPosition::DONT_CARE);
     return ptr->quantitativeUnits->data[unit][bFuture].format(
             quantity,
             *ptr->numberFormat,
             *ptr->pluralRules,
             appendTo,
+            pos,
             status);
 }
 
index 121b81ce78c6056c621f92a9ab74729bc5e79e00..48e0ff489fc9e320d723e45e850488e25cedb4f9 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 2008-2012, Google, International Business Machines Corporation and
+ * Copyright (C) 2008-2014, Google, International Business Machines Corporation and
  * others. All Rights Reserved.
  *******************************************************************************
  */
@@ -8,6 +8,7 @@
 #include "utypeinfo.h"  // for 'typeid' to work
 
 #include "unicode/tmunit.h"
+#include "uassert.h"
 
 #if !UCONFIG_NO_FORMATTING
 
@@ -70,44 +71,58 @@ TimeUnit::createInstance(TimeUnit::UTimeUnitFields timeUnitField,
 
 TimeUnit::TimeUnit(TimeUnit::UTimeUnitFields timeUnitField) {
     fTimeUnitField = timeUnitField;
+    switch (fTimeUnitField) {
+    case UTIMEUNIT_YEAR:
+        initTime("year");
+        break;
+    case UTIMEUNIT_MONTH:
+        initTime("month");
+        break;
+    case UTIMEUNIT_DAY:
+        initTime("day");
+        break;
+    case UTIMEUNIT_WEEK:
+        initTime("week");
+        break;
+    case UTIMEUNIT_HOUR:
+        initTime("hour");
+        break;
+    case UTIMEUNIT_MINUTE:
+        initTime("minute");
+        break;
+    case UTIMEUNIT_SECOND:
+        initTime("second");
+        break;
+    default:
+        U_ASSERT(false);
+        break;
+    }
 }
 
-
 TimeUnit::TimeUnit(const TimeUnit& other) 
-:   MeasureUnit(other) {
-    *this = other;
+:   MeasureUnit(other), fTimeUnitField(other.fTimeUnitField) {
 }
 
-
 UObject* 
 TimeUnit::clone() const {
     return new TimeUnit(*this);
 }
 
-
 TimeUnit&
 TimeUnit::operator=(const TimeUnit& other) {
     if (this == &other) {
         return *this;
     }
+    MeasureUnit::operator=(other);
     fTimeUnitField = other.fTimeUnitField;
     return *this;
 }
 
-
-UBool 
-TimeUnit::operator==(const UObject& other) const {
-    return (typeid(*this) == typeid(other)
-            && fTimeUnitField == ((TimeUnit*)&other)->fTimeUnitField);
-}
-
-
 TimeUnit::UTimeUnitFields
 TimeUnit::getTimeUnitField() const {
     return fTimeUnitField;
 }
 
-
 TimeUnit::~TimeUnit() {
 }
 
index ac1e780a7504a392e7e95ee80d284eb1c143be51..f3bf3dc8a2fb3eeb267121b3bb0971a0a17ed152 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 2008-2013, Google, International Business Machines Corporation
+ * Copyright (C) 2008-2014, Google, International Business Machines Corporation
  * and others. All Rights Reserved.
  *******************************************************************************
  */
@@ -78,53 +78,59 @@ static const UChar PLURAL_COUNT_ZERO[] = {LOW_Z, LOW_E, LOW_R, LOW_O, 0};
 static const UChar PLURAL_COUNT_ONE[] = {LOW_O, LOW_N, LOW_E, 0};
 static const UChar PLURAL_COUNT_TWO[] = {LOW_T, LOW_W, LOW_O, 0};
 
-TimeUnitFormat::TimeUnitFormat(UErrorCode& status)
-:   fNumberFormat(NULL),
-    fPluralRules(NULL) {
-    create(Locale::getDefault(), UTMUTFMT_FULL_STYLE, status);
+TimeUnitFormat::TimeUnitFormat(UErrorCode& status) {
+    initMeasureFormat(Locale::getDefault(), UMEASFMT_WIDTH_WIDE, status);
+    create(UTMUTFMT_FULL_STYLE, status);
 }
 
 
-TimeUnitFormat::TimeUnitFormat(const Locale& locale, UErrorCode& status)
-:   fNumberFormat(NULL),
-    fPluralRules(NULL) {
-    create(locale, UTMUTFMT_FULL_STYLE, status);
+TimeUnitFormat::TimeUnitFormat(const Locale& locale, UErrorCode& status) {
+    initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, status);
+    create(UTMUTFMT_FULL_STYLE, status);
 }
 
 
-TimeUnitFormat::TimeUnitFormat(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status)
-:   fNumberFormat(NULL),
-    fPluralRules(NULL) {
-    create(locale, style, status);
+TimeUnitFormat::TimeUnitFormat(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status) {
+    switch (style) {
+    case UTMUTFMT_FULL_STYLE:
+        initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, status);
+        break;
+    case UTMUTFMT_ABBREVIATED_STYLE:
+        initMeasureFormat(locale, UMEASFMT_WIDTH_SHORT, status);
+        break;
+    default:
+        initMeasureFormat(locale, UMEASFMT_WIDTH_WIDE, status);
+        break;
+    }
+    create(style, status);
 }
 
-
 TimeUnitFormat::TimeUnitFormat(const TimeUnitFormat& other)
 :   MeasureFormat(other),
-    fNumberFormat(NULL),
-    fPluralRules(NULL),
-    fStyle(UTMUTFMT_FULL_STYLE)
+    fStyle(other.fStyle)
 {
     for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
          i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
          i = (TimeUnit::UTimeUnitFields)(i+1)) {
-        fTimeUnitToCountToPatterns[i] = NULL;
-    }
-    *this = other;
+        UErrorCode status = U_ZERO_ERROR;
+        fTimeUnitToCountToPatterns[i] = initHash(status);
+        if (U_SUCCESS(status)) {
+            copyHash(other.fTimeUnitToCountToPatterns[i], fTimeUnitToCountToPatterns[i], status);
+        } else {
+            delete fTimeUnitToCountToPatterns[i];
+            fTimeUnitToCountToPatterns[i] = NULL;
+        }
+    } 
 }
 
 
 TimeUnitFormat::~TimeUnitFormat() {
-    delete fNumberFormat;
-    fNumberFormat = NULL;
     for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
          i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
          i = (TimeUnit::UTimeUnitFields)(i+1)) {
         deleteHash(fTimeUnitToCountToPatterns[i]);
         fTimeUnitToCountToPatterns[i] = NULL;
     }
-    delete fPluralRules;
-    fPluralRules = NULL;
 }
 
 
@@ -139,20 +145,13 @@ TimeUnitFormat::operator=(const TimeUnitFormat& other) {
     if (this == &other) {
         return *this;
     }
-    delete fNumberFormat;
+    MeasureFormat::operator=(other);
     for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
          i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
          i = (TimeUnit::UTimeUnitFields)(i+1)) {
         deleteHash(fTimeUnitToCountToPatterns[i]);
         fTimeUnitToCountToPatterns[i] = NULL;
     }
-    delete fPluralRules;
-    if (other.fNumberFormat) {
-        fNumberFormat = (NumberFormat*)other.fNumberFormat->clone();
-    } else {
-        fNumberFormat = NULL;
-    }
-    fLocale = other.fLocale;
     for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
          i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
          i = (TimeUnit::UTimeUnitFields)(i+1)) {
@@ -165,92 +164,10 @@ TimeUnitFormat::operator=(const TimeUnitFormat& other) {
             fTimeUnitToCountToPatterns[i] = NULL;
         }
     } 
-    if (other.fPluralRules) {
-        fPluralRules = (PluralRules*)other.fPluralRules->clone();
-    } else {
-        fPluralRules = NULL;
-    }
     fStyle = other.fStyle;
     return *this;
 }
 
-
-UBool 
-TimeUnitFormat::operator==(const Format& other) const {
-    if (typeid(*this) == typeid(other)) {
-        TimeUnitFormat* fmt = (TimeUnitFormat*)&other;
-        UBool ret =  ( ((fNumberFormat && fmt->fNumberFormat && *fNumberFormat == *fmt->fNumberFormat)
-                            || fNumberFormat == fmt->fNumberFormat ) 
-                        && fLocale == fmt->fLocale 
-                        && ((fPluralRules && fmt->fPluralRules && *fPluralRules == *fmt->fPluralRules) 
-                            || fPluralRules == fmt->fPluralRules) 
-                        && fStyle == fmt->fStyle); 
-        if (ret) {
-            for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
-                 i < TimeUnit::UTIMEUNIT_FIELD_COUNT && ret;
-                 i = (TimeUnit::UTimeUnitFields)(i+1)) {
-                ret = fTimeUnitToCountToPatterns[i]->equals(*(fmt->fTimeUnitToCountToPatterns[i]));
-            }
-        }
-        return ret;
-    }
-    return false;
-}
-
-
-UnicodeString& 
-TimeUnitFormat::format(const Formattable& obj, UnicodeString& toAppendTo,
-                       FieldPosition& pos, UErrorCode& status) const {
-    if (U_FAILURE(status)) {
-        return toAppendTo;
-    }
-    if (obj.getType() == Formattable::kObject) {
-        const UObject* formatObj = obj.getObject();
-        const TimeUnitAmount* amount = dynamic_cast<const TimeUnitAmount*>(formatObj);
-        if (amount != NULL){
-            Hashtable* countToPattern = fTimeUnitToCountToPatterns[amount->getTimeUnitField()];
-            const Formattable& amtNumber = amount->getNumber();
-            UnicodeString formattedNumber;
-            fNumberFormat->format(amtNumber, formattedNumber, status);
-            if (U_FAILURE(status)) {
-                return toAppendTo;
-            }
-            UnicodeString count;
-            const DecimalFormat* decfmt = dynamic_cast<const DecimalFormat*>(fNumberFormat);
-            if (decfmt != NULL) {
-                FixedDecimal fd = decfmt->getFixedDecimal(amtNumber, status);
-                if (U_FAILURE(status)) {
-                    return toAppendTo;
-                }
-                count = fPluralRules->select(fd);
-            } else {
-                if (amtNumber.getType() == Formattable::kDouble) {
-                    count = fPluralRules->select(amtNumber.getDouble());
-                } else if (amtNumber.getType() == Formattable::kLong) {
-                    count = fPluralRules->select(amtNumber.getLong());
-                } else if (amtNumber.getType() == Formattable::kInt64) {
-                    count = fPluralRules->select((double) amtNumber.getInt64());
-                } else {
-                    status = U_ILLEGAL_ARGUMENT_ERROR;
-                    return toAppendTo;
-                }
-            }
-#ifdef TMUTFMT_DEBUG
-            char result[1000];
-            count.extract(0, count.length(), result, "UTF-8");
-            std::cout << "number: " << number << "; format plural count: " << result << "\n";           
-#endif
-            MessageFormat* pattern = ((MessageFormat**)countToPattern->get(count))[fStyle];
-            Formattable formattable[1];
-            formattable[0].setString(formattedNumber);
-            return pattern->format(formattable, 1, toAppendTo, pos, status);
-        }
-    }
-    status = U_ILLEGAL_ARGUMENT_ERROR;
-    return toAppendTo;
-}
-
-
 void 
 TimeUnitFormat::parseObject(const UnicodeString& source, 
                             Formattable& result,
@@ -305,7 +222,7 @@ TimeUnitFormat::parseObject(const UnicodeString& source,
                     if (temp.getType() == Formattable::kString) {
                         UnicodeString tmpString;
                         UErrorCode pStatus = U_ZERO_ERROR;
-                        fNumberFormat->parse(temp.getString(tmpString), tmpNumber, pStatus);
+                        getNumberFormat().parse(temp.getString(tmpString), tmpNumber, pStatus);
                         if (U_FAILURE(pStatus)) {
                             continue;
                         }
@@ -368,7 +285,7 @@ TimeUnitFormat::parseObject(const UnicodeString& source,
 }
 
 void
-TimeUnitFormat::create(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status) {
+TimeUnitFormat::create(UTimeUnitFormatStyle style, UErrorCode& status) {
     if (U_FAILURE(status)) {
         return;
     }
@@ -377,12 +294,12 @@ TimeUnitFormat::create(const Locale& locale, UTimeUnitFormatStyle style, UErrorC
         return;
     }
     fStyle = style;
-    fLocale = locale;
     for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
          i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
          i = (TimeUnit::UTimeUnitFields)(i+1)) {
         fTimeUnitToCountToPatterns[i] = NULL;
     }
+
     //TODO: format() and parseObj() are const member functions,
     //so, can not do lazy initialization in C++.
     //setup has to be done in constructors.
@@ -399,7 +316,7 @@ TimeUnitFormat::setup(UErrorCode& err) {
     initDataMembers(err);
 
     UVector pluralCounts(0, uhash_compareUnicodeString, 6, err);
-    StringEnumeration* keywords = fPluralRules->getKeywords(err);
+    StringEnumeration* keywords = getPluralRules().getKeywords(err);
     if (U_FAILURE(err)) {
         return;
     }
@@ -420,11 +337,6 @@ TimeUnitFormat::initDataMembers(UErrorCode& err){
     if (U_FAILURE(err)) {
         return;
     }
-    if (fNumberFormat == NULL) {
-        fNumberFormat = NumberFormat::createInstance(fLocale, err);
-    }
-    delete fPluralRules;
-    fPluralRules = PluralRules::forLocale(fLocale, err);
     for (TimeUnit::UTimeUnitFields i = TimeUnit::UTIMEUNIT_YEAR;
          i < TimeUnit::UTIMEUNIT_FIELD_COUNT;
          i = (TimeUnit::UTimeUnitFields)(i+1)) {
@@ -445,7 +357,7 @@ TimeUnitFormat::readFromCurrentLocale(UTimeUnitFormatStyle style, const char* ke
     // status does not affect "err".
     UErrorCode status = U_ZERO_ERROR;
     UResourceBundle *rb, *unitsRes;
-    rb = ures_open(NULL, fLocale.getName(), &status);
+    rb = ures_open(NULL, getLocaleID(status), &status);
     unitsRes = ures_getByKey(rb, key, NULL, &status);
     unitsRes = ures_getByKey(unitsRes, "duration", unitsRes, &status);
     if (U_FAILURE(status)) {
@@ -515,7 +427,7 @@ TimeUnitFormat::readFromCurrentLocale(UTimeUnitFormatStyle style, const char* ke
                 if (!pluralCounts.contains(&pluralCountUniStr)) {
                   continue;
                 }
-                MessageFormat* messageFormat = new MessageFormat(pattern, fLocale, err);
+                MessageFormat* messageFormat = new MessageFormat(pattern, getLocale(err), err);
                 if ( U_SUCCESS(err) ) {
                   MessageFormat** formatters = (MessageFormat**)countToPatterns->get(pluralCountUniStr);
                   if (formatters == NULL) {
@@ -577,7 +489,7 @@ TimeUnitFormat::checkConsistency(UTimeUnitFormatStyle style, const char* key, UE
     // Following is consistency check to create pattern for each
     // plural rule in each time unit using above fall-back rule.
     //
-    StringEnumeration* keywords = fPluralRules->getKeywords(err);
+    StringEnumeration* keywords = getPluralRules().getKeywords(err);
     if (U_SUCCESS(err)) {
         const UnicodeString* pluralCount;
         while ((pluralCount = keywords->snext(err)) != NULL) {
@@ -597,7 +509,7 @@ TimeUnitFormat::checkConsistency(UTimeUnitFormatStyle style, const char* key, UE
                     MessageFormat** formatters = (MessageFormat**)countToPatterns->get(*pluralCount);
                     if( formatters == NULL || formatters[style] == NULL ) {
                         // look through parents
-                        const char* localeName = fLocale.getName();
+                        const char* localeName = getLocaleID(err);
                         CharString pluralCountChars;
                         pluralCountChars.appendInvariantChars(*pluralCount, err);
                         searchInLocaleChain(style, key, localeName,
@@ -650,7 +562,7 @@ TimeUnitFormat::searchInLocaleChain(UTimeUnitFormatStyle style, const char* key,
         pattern = ures_getStringByKeyWithFallback(countsToPatternRB, searchPluralCount, &ptLength, &status);
         if (U_SUCCESS(status)) {
             //found
-            MessageFormat* messageFormat = new MessageFormat(UnicodeString(TRUE, pattern, ptLength), fLocale, err);
+            MessageFormat* messageFormat = new MessageFormat(UnicodeString(TRUE, pattern, ptLength), getLocale(err), err);
             if (U_SUCCESS(err)) {
                 MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
                 if (formatters == NULL) {
@@ -725,7 +637,7 @@ TimeUnitFormat::searchInLocaleChain(UTimeUnitFormatStyle style, const char* key,
             pattern = DEFAULT_PATTERN_FOR_YEAR;
         }
         if (pattern != NULL) {
-            messageFormat = new MessageFormat(UnicodeString(TRUE, pattern, -1), fLocale, err);
+            messageFormat = new MessageFormat(UnicodeString(TRUE, pattern, -1), getLocale(err), err);
         }
         if (U_SUCCESS(err)) {
             MessageFormat** formatters = (MessageFormat**)countToPatterns->get(srcPluralCount);
@@ -755,8 +667,7 @@ TimeUnitFormat::searchInLocaleChain(UTimeUnitFormatStyle style, const char* key,
 
 void 
 TimeUnitFormat::setLocale(const Locale& locale, UErrorCode& status) {
-    if (U_SUCCESS(status) && fLocale != locale) {
-        fLocale = locale;
+    if (setMeasureFormatLocale(locale, status)) {
         setup(status);
     }
 }
@@ -764,11 +675,10 @@ TimeUnitFormat::setLocale(const Locale& locale, UErrorCode& status) {
 
 void 
 TimeUnitFormat::setNumberFormat(const NumberFormat& format, UErrorCode& status){
-    if (U_FAILURE(status) || (fNumberFormat && format == *fNumberFormat)) {
+    if (U_FAILURE(status)) {
         return;
     }
-    delete fNumberFormat;
-    fNumberFormat = (NumberFormat*)format.clone();
+    adoptNumberFormat((NumberFormat *)format.clone(), status);
 }
 
 
index b42761089951653b7e2dc3b33fda07186953cd83..33bd468112d17e56dbcf31b1f1ed9c02e7db8975 100644 (file)
@@ -1,7 +1,7 @@
 /*
 ******************************************************************************
 *                                                                            *
-* Copyright (C) 2001-2013, International Business Machines                   *
+* Copyright (C) 2001-2014, International Business Machines                   *
 *                Corporation and others. All Rights Reserved.                *
 *                                                                            *
 ******************************************************************************
@@ -26,7 +26,6 @@ as the functions are suppose to be called.
 It's usually best to have child dependencies called first. */
 typedef enum ECleanupI18NType {
     UCLN_I18N_START = -1,
-    UCLN_I18N_RELDATEFMT,
     UCLN_I18N_IDENTIFIER_INFO,
     UCLN_I18N_SPOOF,
     UCLN_I18N_TRANSLITERATOR,
@@ -46,6 +45,8 @@ typedef enum ECleanupI18NType {
     UCLN_I18N_CURRENCY,
     UCLN_I18N_DECFMT,
     UCLN_I18N_NUMFMT,
+    UCLN_I18N_RELDATEFMT,
+    UCLN_I18N_MEASFMT,
     UCLN_I18N_SMPDTFMT,
     UCLN_I18N_USEARCH,
     UCLN_I18N_COLLATOR,
index ecd941102923ee7ac594111838daf330953e1f0d..d79836bb7495264ebab19219c656cbc524adbcb7 100644 (file)
@@ -1,6 +1,6 @@
 /*
 **********************************************************************
-* Copyright (c) 2004-2006, International Business Machines
+* Copyright (c) 2004-2014, International Business Machines
 * Corporation and others.  All Rights Reserved.
 **********************************************************************
 * Author: Alan Liu
@@ -69,13 +69,6 @@ class U_I18N_API CurrencyUnit: public MeasureUnit {
      */
     virtual ~CurrencyUnit();
 
-    /**
-     * Equality operator.  Return true if this object is equal
-     * to the given object.
-     * @stable ICU 3.0
-     */
-    UBool operator==(const UObject& other) const;
-
     /**
      * Returns a unique class ID for this object POLYMORPHICALLY.
      * This method implements a simple form of RTTI used by ICU.
index d49a9b572d2072aa69ac43bb86e45d1ccfe48469..b59dfd9e582d2905460f24f025c50cb73d5e941c 100644 (file)
@@ -1,6 +1,6 @@
 /*
 **********************************************************************
-* Copyright (c) 2004-2011, International Business Machines
+* Copyright (c) 2004-2014, International Business Machines
 * Corporation and others.  All Rights Reserved.
 **********************************************************************
 * Author: Alan Liu
 #define MEASUREFORMAT_H
 
 #include "unicode/utypes.h"
+#include "unicode/measure.h"
 
 #if !UCONFIG_NO_FORMATTING
 
 #include "unicode/format.h"
+#include "unicode/udat.h"
 
 /**
  * \file 
  * \brief C++ API: Formatter for measure objects.
  */
 
+/**
+ * Constants for various widths.
+ * There are 3 widths: Wide, Short, Narrow.
+ * For example, for English, when formatting "3 hours"
+ * Wide is "3 hours"; short is "3 hrs"; narrow is "3h"
+ * @draft ICU 53
+ */
+enum UMeasureFormatWidth {
+
+    // Wide, short, and narrow must be first and in this order.
+    /**
+     * Spell out measure units.
+     * @draft ICU 53 
+     */
+    UMEASFMT_WIDTH_WIDE,
+    /**
+     * Abbreviate measure units.
+     * @draft ICU 53
+     */
+    UMEASFMT_WIDTH_SHORT,
+
+    /**
+     * Use symbols for measure units when possible.
+     * @draft ICU 53
+     */
+    UMEASFMT_WIDTH_NARROW,
+
+    /**
+     * Completely omit measure units when possible. For example, format
+     * '5 hours, 37 minutes' as '5:37'
+     * @draft ICU 53
+     */
+    UMEASFMT_WIDTH_NUMERIC,
+
+    /**
+     * Count of values in this enum.
+     * @draft ICU 53
+     */
+    UMEASFMT_WIDTH_COUNT
+};
+/** @draft ICU 53 */
+typedef enum UMeasureFormatWidth UMeasureFormatWidth; 
+
 U_NAMESPACE_BEGIN
 
+class NumberFormat;
+class PluralRules;
+class MeasureFormatData;
+class QuantityFormatter;
+class ListFormatter;
+class DateFormat;
+
 /**
  * 
- * A formatter for measure objects.  This is an abstract base class.
- *
- * <p>To format or parse a measure object, first create a formatter
- * object using a MeasureFormat factory method.  Then use that
- * object's format and parse methods.
- *
- * <p>This is an abstract class.
+ * A formatter for measure objects.
  *
  * @see Format
  * @author Alan Liu
@@ -40,12 +87,96 @@ U_NAMESPACE_BEGIN
  */
 class U_I18N_API MeasureFormat : public Format {
  public:
+    using Format::parseObject;
+    using Format::format;
+
+    /**
+     * Constructor.
+     * @draft ICU 53.
+     */
+    MeasureFormat(
+            const Locale &locale, UMeasureFormatWidth width, UErrorCode &status);
+
+    /**
+     * Constructor.
+     * @draft ICU 53.
+     */
+    MeasureFormat(
+            const Locale &locale,
+            UMeasureFormatWidth width,
+            NumberFormat *nfToAdopt,
+            UErrorCode &status);
+
+    /**
+     * Copy constructor.
+     * @draft ICU 53.
+     */
+    MeasureFormat(const MeasureFormat &other);
+
+    /**
+     * Assignment operator.
+     * @draft ICU 53.
+     */
+    MeasureFormat &operator=(const MeasureFormat &rhs);
+    
     /**
      * Destructor.
      * @stable ICU 3.0
      */
     virtual ~MeasureFormat();
 
+    /**
+     * Return true if given Format objects are semantically equal.
+     * @draft ICU 53
+     */
+    virtual UBool operator==(const Format &other) const;
+
+    /**
+     * Clones this object polymorphically.
+     * @draft ICU 53
+     */
+    virtual Format *clone() const;
+
+    /**
+     * Formats object to produce a string.
+     * @draft ICU 53
+     */
+    virtual UnicodeString &format(
+            const Formattable &obj,
+            UnicodeString &appendTo,
+            FieldPosition &pos,
+            UErrorCode &status) const;
+
+    /**
+     * Parse a string to produce an object. This implementation sets
+     * status to U_UNSUPPORTED_ERROR.
+     *
+     * @draft ICU 53
+     */
+    virtual void parseObject(
+            const UnicodeString &source,
+            Formattable &reslt,
+            ParsePosition &pos) const;
+
+    /**
+     * Formats measure objects to produce a string.
+     * @param measures measure objects.
+     * @param measureCount the number of measure objects.
+     * @param appendTo formatted string appended here.
+     * @param pos the field position.
+     * @param status the error.
+     * @return appendTo reference
+     *
+     * @draft ICU 53
+     */
+    UnicodeString &formatMeasures(
+            const Measure *measures,
+            int32_t measureCount,
+            UnicodeString &appendTo,
+            FieldPosition &pos,
+            UErrorCode &status) const;
+
+
     /**
      * Return a formatter for CurrencyAmount objects in the given
      * locale.
@@ -67,12 +198,99 @@ class U_I18N_API MeasureFormat : public Format {
     static MeasureFormat* U_EXPORT2 createCurrencyFormat(UErrorCode& ec);
 
  protected:
-
     /**
      * Default constructor.
      * @stable ICU 3.0
      */
     MeasureFormat();
+
+#ifndef U_HIDE_INTERNAL_API 
+
+    /**
+     * ICU use only.
+     * Initialize MeasureFormat class from base class.
+     * @internal.
+     */
+    void initMeasureFormat(const Locale &locale, UMeasureFormatWidth width, UErrorCode &status);
+
+    /**
+     * ICU use only.
+     * Allows subclass to change locale. Note that this method also changes
+     * the NumberFormat object. Returns TRUE if locale changed; FALSE if no
+     * change was made.
+     * @internal.
+     */
+    UBool setMeasureFormatLocale(const Locale &locale, UErrorCode &status);
+
+    /**
+     * ICU use only.
+     * Let subclass change NumberFormat.
+     * @internal.
+     */
+    void adoptNumberFormat(NumberFormat *nfToAdopt, UErrorCode &status);
+
+    /**
+     * ICU use only.
+     * @internal.
+     */
+    const NumberFormat &getNumberFormat() const;
+
+    /**
+     * ICU use only.
+     * @internal.
+     */
+    const PluralRules &getPluralRules() const;
+
+    /**
+     * ICU use only.
+     * @internal.
+     */
+    Locale getLocale(UErrorCode &status) const;
+
+    /**
+     * ICU use only.
+     * @internal.
+     */
+    const char *getLocaleID(UErrorCode &status) const;
+
+#endif /* U_HIDE_INTERNAL_API */
+
+ private:
+    const MeasureFormatData *ptr;
+    UMeasureFormatWidth width;    
+
+    const QuantityFormatter *getQuantityFormatter(
+            int32_t index,
+            int32_t widthIndex,
+            UErrorCode &status) const;
+
+    UnicodeString &formatMeasure(
+        const Measure &measure,
+        UnicodeString &appendTo,
+        FieldPosition &pos,
+        UErrorCode &status) const;
+
+    UnicodeString &formatMeasuresSlowTrack(
+        const Measure *measures,
+        int32_t measureCount,
+        const ListFormatter& lf,
+        UnicodeString& appendTo,
+        FieldPosition& pos,
+        UErrorCode& status) const;
+
+    UnicodeString &formatNumeric(
+        const Formattable *hms,  // always length 3
+        int32_t bitMap,   // 1=hourset, 2=minuteset, 4=secondset
+        UnicodeString &appendTo,
+        UErrorCode &status) const;
+
+    UnicodeString &formatNumeric(
+        UDate date,
+        const DateFormat &dateFmt,
+        UDateFormatField smallestField,
+        const Formattable &smallestAmount,
+        UnicodeString &appendTo,
+        UErrorCode &status) const;
 };
 
 U_NAMESPACE_END
index 9a210e659575c009f923888ed65b5177e0f92bd1..6dad871318da92367e363dd75c1e9d3565796876 100644 (file)
@@ -1,6 +1,6 @@
 /*
 **********************************************************************
-* Copyright (c) 2004-2006, International Business Machines
+* Copyright (c) 2004-2014, International Business Machines
 * Corporation and others.  All Rights Reserved.
 **********************************************************************
 * Author: Alan Liu
 #define __MEASUREUNIT_H__
 
 #include "unicode/utypes.h"
-
-#if !UCONFIG_NO_FORMATTING
-
-#include "unicode/fmtable.h"
+#include "unicode/unistr.h"
 
 /**
  * \file 
  
 U_NAMESPACE_BEGIN
 
+class StringEnumeration;
+
 /**
  * A unit such as length, mass, volume, currency, etc.  A unit is
  * coupled with a numeric amount to produce a Measure.
  *
- * <p>This is an abstract class.
- *
  * @author Alan Liu
  * @stable ICU 3.0
  */
 class U_I18N_API MeasureUnit: public UObject {
  public:
+
+    /**
+     * Default constructor.
+     * @stable ICU 3.0
+     */
+    MeasureUnit() : fTypeId(0), fSubTypeId(0) { 
+        fCurrency[0] = 0;
+    }
+    
+    /**
+     * Copy constructor.
+     * @draft ICU 53
+     */
+    MeasureUnit(const MeasureUnit &other);
+        
+    /**
+     * Assignment operator.
+     * @draft ICU 53.
+     */
+    MeasureUnit &operator=(const MeasureUnit &other);
+
     /**
-     * Return a polymorphic clone of this object.  The result will
+     * Returns a polymorphic clone of this object.  The result will
      * have the same class as returned by getDynamicClassID().
      * @stable ICU 3.0
      */
-    virtual UObject* clone() const = 0;
+    virtual UObject* clone() const;
 
     /**
      * Destructor
      * @stable ICU 3.0
      */
     virtual ~MeasureUnit();
-    
+
     /**
      * Equality operator.  Return true if this object is equal
      * to the given object.
      * @stable ICU 3.0
      */
-    virtual UBool operator==(const UObject& other) const = 0;
+    virtual UBool operator==(const UObject& other) const;
+
+    /**
+     * Inequality operator.  Return true if this object is not equal
+     * to the given object.
+     * @draft ICU 53
+     */
+    UBool operator!=(const UObject& other) const {
+        return !(*this == other);
+    }
+
+    /**
+     * Get the type.
+     * @draft ICU 53
+     */
+    const char *getType() const;
+
+    /**
+     * Get the sub type.
+     * @draft ICU 53
+     */
+    const char *getSubtype() const;
+
+    /**
+     * getAvailable gets all of the available units.
+     * If there are too many units to fit into destCapacity then the
+     * error code is set to U_BUFFER_OVERFLOW_ERROR.
+     *
+     * @param destArray destination buffer.
+     * @param destCapacity number of MeasureUnit instances available at dest.
+     * @param errorCode ICU error code.
+     * @return number of available units.
+     * @draft ICU 53
+     */
+    static int32_t getAvailable(
+            MeasureUnit *destArray,
+            int32_t destCapacity,
+            UErrorCode &errorCode);
+
+    /**
+     * getAvailable gets all of the available units for a specific type.
+     * If there are too many units to fit into destCapacity then the
+     * error code is set to U_BUFFER_OVERFLOW_ERROR.
+     *
+     * @param type the type
+     * @param destArray destination buffer.
+     * @param destCapacity number of MeasureUnit instances available at dest.
+     * @param errorCode ICU error code.
+     * @return number of available units for type.
+     * @draft ICU 53
+     */
+    static int32_t getAvailable(
+            const char *type,
+            MeasureUnit *destArray,
+            int32_t destCapacity,
+            UErrorCode &errorCode);
+
+    /**
+     * getAvailableTypes gets all of the available types. Caller owns the
+     * returned StringEnumeration and must delete it when finished using it.
+     *
+     * @param errorCode ICU error code.
+     * @return the types.
+     * @draft ICU 53
+     */
+    static StringEnumeration* getAvailableTypes(UErrorCode &errorCode);
+
+#ifndef U_HIDE_INTERNAL_API
+
+    /**
+     * ICU use only.
+     * Returns associated array index for this measure unit. Only valid for
+     * non-currency measure units.
+     * @internal
+     */
+    int32_t getIndex() const;
+
+    /**
+     * ICU use only.
+     * Returns maximum value from getIndex plus 1.
+     * @internal
+     */
+    static int32_t getIndexCount();
+
+#endif /* U_HIDE_INTERNAL_API */
+
+// Start generated createXXX methods
+
+    /** Constant for unit of acceleration: g-force */
+    static MeasureUnit *createGForce(UErrorCode &status);
+
+    /** Constant for unit of angle: arc-minute */
+    static MeasureUnit *createArcMinute(UErrorCode &status);
+
+    /** Constant for unit of angle: arc-second */
+    static MeasureUnit *createArcSecond(UErrorCode &status);
+
+    /** Constant for unit of angle: degree */
+    static MeasureUnit *createDegree(UErrorCode &status);
+
+    /** Constant for unit of area: acre */
+    static MeasureUnit *createAcre(UErrorCode &status);
+
+    /** Constant for unit of area: hectare */
+    static MeasureUnit *createHectare(UErrorCode &status);
+
+    /** Constant for unit of area: square-foot */
+    static MeasureUnit *createSquareFoot(UErrorCode &status);
+
+    /** Constant for unit of area: square-kilometer */
+    static MeasureUnit *createSquareKilometer(UErrorCode &status);
+
+    /** Constant for unit of area: square-meter */
+    static MeasureUnit *createSquareMeter(UErrorCode &status);
+
+    /** Constant for unit of area: square-mile */
+    static MeasureUnit *createSquareMile(UErrorCode &status);
+
+    /** Constant for unit of duration: day */
+    static MeasureUnit *createDay(UErrorCode &status);
+
+    /** Constant for unit of duration: hour */
+    static MeasureUnit *createHour(UErrorCode &status);
+
+    /** Constant for unit of duration: millisecond */
+    static MeasureUnit *createMillisecond(UErrorCode &status);
+
+    /** Constant for unit of duration: minute */
+    static MeasureUnit *createMinute(UErrorCode &status);
+
+    /** Constant for unit of duration: month */
+    static MeasureUnit *createMonth(UErrorCode &status);
+
+    /** Constant for unit of duration: second */
+    static MeasureUnit *createSecond(UErrorCode &status);
+
+    /** Constant for unit of duration: week */
+    static MeasureUnit *createWeek(UErrorCode &status);
+
+    /** Constant for unit of duration: year */
+    static MeasureUnit *createYear(UErrorCode &status);
+
+    /** Constant for unit of length: centimeter */
+    static MeasureUnit *createCentimeter(UErrorCode &status);
+
+    /** Constant for unit of length: foot */
+    static MeasureUnit *createFoot(UErrorCode &status);
+
+    /** Constant for unit of length: inch */
+    static MeasureUnit *createInch(UErrorCode &status);
+
+    /** Constant for unit of length: kilometer */
+    static MeasureUnit *createKilometer(UErrorCode &status);
+
+    /** Constant for unit of length: light-year */
+    static MeasureUnit *createLightYear(UErrorCode &status);
+
+    /** Constant for unit of length: meter */
+    static MeasureUnit *createMeter(UErrorCode &status);
+
+    /** Constant for unit of length: mile */
+    static MeasureUnit *createMile(UErrorCode &status);
+
+    /** Constant for unit of length: millimeter */
+    static MeasureUnit *createMillimeter(UErrorCode &status);
+
+    /** Constant for unit of length: picometer */
+    static MeasureUnit *createPicometer(UErrorCode &status);
+
+    /** Constant for unit of length: yard */
+    static MeasureUnit *createYard(UErrorCode &status);
+
+    /** Constant for unit of mass: gram */
+    static MeasureUnit *createGram(UErrorCode &status);
+
+    /** Constant for unit of mass: kilogram */
+    static MeasureUnit *createKilogram(UErrorCode &status);
+
+    /** Constant for unit of mass: ounce */
+    static MeasureUnit *createOunce(UErrorCode &status);
+
+    /** Constant for unit of mass: pound */
+    static MeasureUnit *createPound(UErrorCode &status);
+
+    /** Constant for unit of power: horsepower */
+    static MeasureUnit *createHorsepower(UErrorCode &status);
+
+    /** Constant for unit of power: kilowatt */
+    static MeasureUnit *createKilowatt(UErrorCode &status);
+
+    /** Constant for unit of power: watt */
+    static MeasureUnit *createWatt(UErrorCode &status);
+
+    /** Constant for unit of pressure: hectopascal */
+    static MeasureUnit *createHectopascal(UErrorCode &status);
+
+    /** Constant for unit of pressure: inch-hg */
+    static MeasureUnit *createInchHg(UErrorCode &status);
+
+    /** Constant for unit of pressure: millibar */
+    static MeasureUnit *createMillibar(UErrorCode &status);
+
+    /** Constant for unit of speed: kilometer-per-hour */
+    static MeasureUnit *createKilometerPerHour(UErrorCode &status);
+
+    /** Constant for unit of speed: meter-per-second */
+    static MeasureUnit *createMeterPerSecond(UErrorCode &status);
+
+    /** Constant for unit of speed: mile-per-hour */
+    static MeasureUnit *createMilePerHour(UErrorCode &status);
+
+    /** Constant for unit of temperature: celsius */
+    static MeasureUnit *createCelsius(UErrorCode &status);
+
+    /** Constant for unit of temperature: fahrenheit */
+    static MeasureUnit *createFahrenheit(UErrorCode &status);
+
+    /** Constant for unit of volume: cubic-kilometer */
+    static MeasureUnit *createCubicKilometer(UErrorCode &status);
+
+    /** Constant for unit of volume: cubic-mile */
+    static MeasureUnit *createCubicMile(UErrorCode &status);
+
+    /** Constant for unit of volume: liter */
+    static MeasureUnit *createLiter(UErrorCode &status);
 
  protected:
+
+#ifndef U_HIDE_INTERNAL_API
     /**
-     * Default constructor.
-     * @stable ICU 3.0
+     * For ICU use only.
+     * @internal
+     */
+    void initTime(const char *timeId);
+
+    /**
+     * For ICU use only.
+     * @internal
      */
-    MeasureUnit();
+    void initCurrency(const char *isoCurrency);
+
+#endif
+
+private:
+    int32_t fTypeId;
+    int32_t fSubTypeId;
+    char fCurrency[4];
+
+    MeasureUnit(int32_t typeId, int32_t subTypeId) : fTypeId(typeId), fSubTypeId(subTypeId) {
+        fCurrency[0] = 0;
+    }
+    void setTo(int32_t typeId, int32_t subTypeId);
+    int32_t getOffset() const;
+    static MeasureUnit *create(int typeId, int subTypeId, UErrorCode &status);
 };
 
 U_NAMESPACE_END
 
-// NOTE: There is no measunit.cpp. For implementation, see measure.cpp. [alan]
-
-#endif // !UCONFIG_NO_FORMATTING
 #endif // __MEASUREUNIT_H__
index 6b7a0497b476bd290f03d19fe0e8f8591fc761bc..0d08cc796dddfa574652bbe63085bd9fb73170c3 100644 (file)
@@ -1,6 +1,6 @@
 /*
 **********************************************************************
-* Copyright (c) 2004-2006, International Business Machines
+* Copyright (c) 2004-2014, International Business Machines
 * Corporation and others.  All Rights Reserved.
 **********************************************************************
 * Author: Alan Liu
@@ -74,7 +74,7 @@ class U_I18N_API Measure: public UObject {
      * have the same class as returned by getDynamicClassID().
      * @stable ICU 3.0
      */
-    virtual UObject* clone() const = 0;
+    virtual UObject* clone() const;
 
     /**
      * Destructor
index 6eda105c12f8920b14521e88b66da9e5a0546218..e417f3522bceb3c5cee6ac3a41c24fe6b6aabc07 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 2009-2010, Google, International Business Machines Corporation and *
+ * Copyright (C) 2009-2014, Google, International Business Machines Corporation and *
  * others. All Rights Reserved.                                                *
  *******************************************************************************
  */
@@ -76,20 +76,6 @@ public:
      */
     TimeUnit& operator=(const TimeUnit& other);
 
-    /**
-     * Equality operator. 
-     * @return true if 2 objects are the same.
-     * @stable ICU 4.2 
-     */
-    virtual UBool operator==(const UObject& other) const;
-
-    /**
-     * Non-Equality operator. 
-     * @return true if 2 objects are not the same.
-     * @stable ICU 4.2 
-     */
-    UBool operator!=(const UObject& other) const;
-
     /**
      * Returns a unique class ID for this object POLYMORPHICALLY.
      * This method implements a simple form of RTTI used by ICU.
@@ -134,12 +120,6 @@ private:
 };
 
 
-inline UBool 
-TimeUnit::operator!=(const UObject& other) const {
-    return !operator==(other);
-}
-
-
 U_NAMESPACE_END
 
 #endif /* #if !UCONFIG_NO_FORMATTING */
index 7ed7035b6b526f19f808b7e0effe7d461e6283c6..e1fde7255fcd5f2ec95712f9557f62b4b953ea62 100644 (file)
@@ -1,6 +1,6 @@
 /*
  *******************************************************************************
- * Copyright (C) 2008-2013, Google, International Business Machines Corporation
+ * Copyright (C) 2008-2014, Google, International Business Machines Corporation
  * and others. All Rights Reserved.
  *******************************************************************************
  */
@@ -125,16 +125,6 @@ public:
      */
     TimeUnitFormat& operator=(const TimeUnitFormat& other);
 
-
-    /**
-     * Return true if the given Format objects are semantically equal. Objects
-     * of different subclasses are considered unequal.
-     * @param other    the object to be compared with.
-     * @return         true if the given Format objects are semantically equal.
-     * @stable ICU 4.2
-     */
-    virtual UBool operator==(const Format& other) const;
-
     /**
      * Return true if the given Format objects are not semantically equal.
      * Objects of different subclasses are considered unequal.
@@ -161,22 +151,6 @@ public:
      */
     void setNumberFormat(const NumberFormat& format, UErrorCode& status);
 
-
-    using MeasureFormat::format;
-
-    /**
-     * Format a TimeUnitAmount.
-     * If the formattable object is not a time unit amount object,
-     * or the number in time unit amount is not a double type or long type
-     * numeric, it returns a failing status: U_ILLEGAL_ARGUMENT_ERROR.
-     * @see Format#format(const Formattable&, UnicodeString&, FieldPosition&,  UErrorCode&) const
-     * @stable ICU 4.2
-     */
-    virtual UnicodeString& format(const Formattable& obj,
-                                  UnicodeString& toAppendTo,
-                                  FieldPosition& pos,
-                                  UErrorCode& status) const;
-
     /**
      * Parse a TimeUnitAmount.
      * @see Format#parseObject(const UnicodeString&, Formattable&, ParsePosition&) const;
@@ -213,13 +187,10 @@ public:
     virtual UClassID getDynamicClassID(void) const;
 
 private:
-    NumberFormat* fNumberFormat;
-    Locale        fLocale;
     Hashtable*    fTimeUnitToCountToPatterns[TimeUnit::UTIMEUNIT_FIELD_COUNT];
-    PluralRules*  fPluralRules;
     UTimeUnitFormatStyle fStyle;
 
-    void create(const Locale& locale, UTimeUnitFormatStyle style, UErrorCode& status);
+    void create(UTimeUnitFormatStyle style, UErrorCode& status);
 
     // it might actually be simpler to make them Decimal Formats later.
     // initialize all private data members
index da33dc38441754b4468c8a73eed2c0edab1af1fc..0b7189134b50648602e2cf20e2d0f29344594c8b 100644 (file)
@@ -56,7 +56,7 @@ uobjtest.o idnaref.o idnaconf.o nptrans.o punyref.o testidn.o testidna.o uts46te
 incaltst.o calcasts.o v32test.o uvectest.o textfile.o tokiter.o utxttest.o \
 windttst.o winnmtst.o winutil.o csdetest.o tzrulets.o tzoffloc.o tzfmttst.o ssearch.o dtifmtts.o \
 tufmtts.o itspoof.o simplethread.o bidiconf.o locnmtst.o dcfmtest.o alphaindextst.o listformattertest.o genderinfotest.o compactdecimalformattest.o regiontst.o \
-reldatefmttest.o lrucachetest.o simplepatternformattertest.o
+reldatefmttest.o lrucachetest.o simplepatternformattertest.o measfmttest.o
 
 DEPS = $(OBJECTS:.o=.d)
 
index 7c45d363d49cc5f0928a052b7f42fd12b4ab7fa4..13d8846ed32b434f26212c06c3a38147ee9cd3e9 100644 (file)
     <ClCompile Include="lrucachetest.cpp">\r
       <DisableLanguageExtensions>false</DisableLanguageExtensions>\r
     </ClCompile>\r
+    <ClCompile Include="measfmttest.cpp" />\r
     <ClCompile Include="miscdtfm.cpp" />\r
     <ClCompile Include="msfmrgts.cpp" />\r
     <ClCompile Include="nmfmapts.cpp" />\r
index 884ef5f5b9311f80feffcb64d26ba6725a838ed1..dacd9e24cbc12871479033cc6cd0a92f1df47438 100644 (file)
     <ClCompile Include="locnmtst.cpp">\r
       <Filter>formatting</Filter>\r
     </ClCompile>\r
+    <ClCompile Include="measfmttest.cpp">\r
+      <Filter>formatting</Filter>\r
+    </ClCompile>\r
     <ClCompile Include="miscdtfm.cpp">\r
       <Filter>formatting</Filter>\r
     </ClCompile>\r
index 84f25dea08b6b39a3ebca951ef301bae6349bea0..ed08d47bd5697e39c872a24a60c2fedc1ceb3bad 100644 (file)
@@ -1,6 +1,6 @@
 /********************************************************************
  * COPYRIGHT:
- * Copyright (c) 1997-2013, International Business Machines
+ * Copyright (c) 1997-2014, International Business Machines
  * Corporation and others. All Rights Reserved.
  ********************************************************************/
 
@@ -62,6 +62,7 @@
 extern IntlTest *createCompactDecimalFormatTest();
 extern IntlTest *createGenderInfoTest();
 extern IntlTest *createRelativeDateTimeFormatterTest();
+extern IntlTest *createMeasureFormatTest();
 
 #define TESTCLASS(id, TestClass)          \
     case id:                              \
@@ -167,6 +168,15 @@ void IntlTestFormat::runIndexedTest( int32_t index, UBool exec, const char* &nam
             callTest(*test, par);
           }
           break;
+        case 47:
+          name = "MeasureFormatTest";
+          if (exec) {
+            logln("MeasureFormatTest test---");
+            logln((UnicodeString)"");
+            LocalPointer<IntlTest> test(createMeasureFormatTest());
+            callTest(*test, par);
+          }
+          break;
         default: name = ""; break; //needed to end loop
     }
     if (exec) {
diff --git a/icu4c/source/test/intltest/measfmttest.cpp b/icu4c/source/test/intltest/measfmttest.cpp
new file mode 100644 (file)
index 0000000..015e2be
--- /dev/null
@@ -0,0 +1,901 @@
+/*
+*******************************************************************************
+* Copyright (C) 2014, International Business Machines Corporation and         *
+* others. All Rights Reserved.                                                *
+*******************************************************************************
+*
+* File MEASFMTTEST.CPP
+*
+*******************************************************************************
+*/
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "intltest.h"
+
+#if !UCONFIG_NO_FORMATTING
+
+#include "unicode/decimfmt.h"
+#include "unicode/measfmt.h"
+#include "unicode/measure.h"
+#include "unicode/measunit.h"
+#include "unicode/tmutamt.h"
+#include "charstr.h"
+
+#define LENGTHOF(array) (int32_t)(sizeof(array) / sizeof((array)[0]))
+
+struct ExpectedResult {
+    const Measure *measures;
+    int32_t count;
+    const char *expected;
+};
+
+class MeasureFormatTest : public IntlTest {
+public:
+    MeasureFormatTest() {
+    }
+
+    void runIndexedTest(int32_t index, UBool exec, const char *&name, char *par=0);
+private:
+    void TestBasic();
+    void TestGetAvailable();
+    void TestExamplesInDocs();
+    void TestFormatPeriodEn();
+    void Test10219FractionalPlurals();
+    void TestGreek();
+    void TestFormatSingleArg();
+    void TestFormatMeasuresZeroArg();
+    void TestMultiples();
+    void TestGram();
+    void TestCurrencies();
+    void TestFieldPosition();
+    void TestFieldPositionMultiple();
+    void TestBadArg();
+    void TestEquality();
+    void verifyFormat(
+        const char *description,
+        const MeasureFormat &fmt,
+        const Measure *measures,
+        int32_t measureCount,
+        const char *expected);
+    void verifyFormatWithPrefix(
+        const char *description,
+        const MeasureFormat &fmt,
+        const UnicodeString &prefix,
+        const Measure *measures,
+        int32_t measureCount,
+        const char *expected);
+    void verifyFormat(
+        const char *description,
+        const MeasureFormat &fmt,
+        const ExpectedResult *expectedResults,
+        int32_t count);
+    void helperTestMultiples(
+        const Locale &locale,
+        UMeasureFormatWidth width,
+        const char *expected);
+    void verifyFieldPosition(
+        const char *description,
+        const MeasureFormat &fmt,
+        const UnicodeString &prefix,
+        const Measure *measures,
+        int32_t measureCount,
+        NumberFormat::EAlignmentFields field,
+        int32_t start,
+        int32_t end);
+};
+
+void MeasureFormatTest::runIndexedTest(
+        int32_t index, UBool exec, const char *&name, char *) {
+    if (exec) {
+        logln("TestSuite MeasureFormatTest: ");
+    }
+    TESTCASE_AUTO_BEGIN;
+    TESTCASE_AUTO(TestBasic);
+    TESTCASE_AUTO(TestGetAvailable);
+    TESTCASE_AUTO(TestExamplesInDocs);
+    TESTCASE_AUTO(TestFormatPeriodEn);
+    TESTCASE_AUTO(Test10219FractionalPlurals);
+    TESTCASE_AUTO(TestGreek);
+    TESTCASE_AUTO(TestFormatSingleArg);
+    TESTCASE_AUTO(TestFormatMeasuresZeroArg);
+    TESTCASE_AUTO(TestMultiples);
+    TESTCASE_AUTO(TestGram);
+    TESTCASE_AUTO(TestCurrencies);
+    TESTCASE_AUTO(TestFieldPosition);
+    TESTCASE_AUTO(TestFieldPositionMultiple);
+    TESTCASE_AUTO(TestBadArg);
+    TESTCASE_AUTO(TestEquality);
+    TESTCASE_AUTO_END;
+}
+
+void MeasureFormatTest::TestBasic() {
+    UErrorCode status = U_ZERO_ERROR;
+    MeasureUnit *ptr1 = MeasureUnit::createArcMinute(status);
+    MeasureUnit *ptr2 = MeasureUnit::createArcMinute(status);
+    if (!(*ptr1 == *ptr2)) {
+        errln("Expect == to work.");
+    }
+    if (*ptr1 != *ptr2) {
+        errln("Expect != to work.");
+    }
+    MeasureUnit *ptr3 = MeasureUnit::createMeter(status);
+    if (*ptr1 == *ptr3) {
+        errln("Expect == to work.");
+    }
+    if (!(*ptr1 != *ptr3)) {
+        errln("Expect != to work.");
+    }
+    MeasureUnit *ptr4 = (MeasureUnit *) ptr1->clone();
+    if (*ptr1 != *ptr4) {
+        errln("Expect clone to work.");
+    }
+    MeasureUnit stack;
+    stack = *ptr1;
+    if (*ptr1 != stack) {
+        errln("Expect assignment to work.");
+    }
+
+    delete ptr1;
+    delete ptr2;
+    delete ptr3;
+    delete ptr4;
+}
+
+void MeasureFormatTest::TestGetAvailable() {
+    MeasureUnit *units = NULL;
+    UErrorCode status = U_ZERO_ERROR;
+    int32_t totalCount = MeasureUnit::getAvailable(units, 0, status);
+    while (status == U_BUFFER_OVERFLOW_ERROR) {
+        status = U_ZERO_ERROR;
+        delete [] units;
+        units = new MeasureUnit[totalCount];
+        totalCount = MeasureUnit::getAvailable(units, totalCount, status);
+    }
+    if (U_FAILURE(status)) {
+        dataerrln("Failure creating format object - %s", u_errorName(status));
+        delete [] units;
+        return;
+    }
+    if (totalCount < 200) {
+        errln("Expect at least 200 measure units including currencies.");
+    }
+    delete [] units;
+    StringEnumeration *types = MeasureUnit::getAvailableTypes(status);
+    if (U_FAILURE(status)) {
+        dataerrln("Failure getting types - %s", u_errorName(status));
+        delete types;
+        return;
+    }
+    if (types->count(status) < 10) {
+        errln("Expect at least 10 distinct unit types.");
+    }
+    units = NULL;
+    int32_t unitCapacity = 0;
+    int32_t unitCountSum = 0;
+    for (
+            const char* type = types->next(NULL, status);
+            type != NULL;
+            type = types->next(NULL, status)) {
+        int32_t unitCount = MeasureUnit::getAvailable(type, units, unitCapacity, status);
+        while (status == U_BUFFER_OVERFLOW_ERROR) {
+            status = U_ZERO_ERROR;
+            delete [] units;
+            units = new MeasureUnit[unitCount];
+            unitCapacity = unitCount;
+            unitCount = MeasureUnit::getAvailable(type, units, unitCapacity, status);
+        }
+        if (U_FAILURE(status)) {
+            dataerrln("Failure getting units - %s", u_errorName(status));
+            delete [] units;
+            delete types;
+            return;
+        }
+        if (unitCount < 1) {
+            errln("Expect at least one unit count per type.");
+        }
+        unitCountSum += unitCount;
+    }
+    if (unitCountSum != totalCount) {
+        errln("Expected total unit count to equal sum of unit counts by type.");
+    }
+    delete [] units;
+    delete types;
+}
+
+void MeasureFormatTest::TestExamplesInDocs() {
+    UErrorCode status = U_ZERO_ERROR;
+    MeasureFormat fmtFr(Locale::getFrench(), UMEASFMT_WIDTH_SHORT, status);
+    MeasureFormat fmtFrFull(
+            Locale::getFrench(), UMEASFMT_WIDTH_WIDE, status);
+    MeasureFormat fmtFrNarrow(
+            Locale::getFrench(), UMEASFMT_WIDTH_NARROW, status);
+    MeasureFormat fmtEn(Locale::getUS(), UMEASFMT_WIDTH_WIDE, status);
+    if (!assertSuccess("Error creating formatters", status)) {
+        return;
+    }
+    Measure measureC(23, MeasureUnit::createCelsius(status), status);
+    Measure measureF(70, MeasureUnit::createFahrenheit(status), status);
+    Measure feetAndInches[] = {
+            Measure(70, MeasureUnit::createFoot(status), status),
+            Measure(5.3, MeasureUnit::createInch(status), status)};
+    Measure footAndInch[] = {
+            Measure(1, MeasureUnit::createFoot(status), status),
+            Measure(1, MeasureUnit::createInch(status), status)};
+    Measure inchAndFeet[] = {
+            Measure(1, MeasureUnit::createInch(status), status),
+            Measure(2, MeasureUnit::createFoot(status), status)};
+    if (!assertSuccess("Error creating measurements.", status)) {
+        return;
+    }
+    verifyFormat(
+            "Celsius",
+            fmtFr,
+            &measureC,
+            1,
+            "23 \\u00B0C");
+    verifyFormatWithPrefix(
+            "Celsius",
+            fmtFr,
+            "Prefix: ",
+            &measureC,
+            1,
+            "Prefix: 23 \\u00B0C");
+    verifyFormat(
+            "Fahrenheit",
+            fmtFr,
+            &measureF,
+            1,
+            "70 \\u00B0F");
+    verifyFormat(
+            "Feet and inches",
+            fmtFrFull,
+            feetAndInches,
+            LENGTHOF(feetAndInches),
+            "70 pieds et 5,3 pouces");
+    verifyFormatWithPrefix(
+            "Feet and inches",
+            fmtFrFull,
+            "Prefix: ",
+            feetAndInches,
+            LENGTHOF(feetAndInches),
+            "Prefix: 70 pieds et 5,3 pouces");
+    verifyFormat(
+            "Foot and inch",
+            fmtFrFull,
+            footAndInch,
+            LENGTHOF(footAndInch),
+            "1 pied et 1 pouce");
+    verifyFormat(
+            "Foot and inch narrow",
+            fmtFrNarrow,
+            footAndInch,
+            LENGTHOF(footAndInch),
+            "1\\u2032 1\\u2033");
+    verifyFormat(
+            "Inch and feet",
+            fmtEn,
+            inchAndFeet,
+            LENGTHOF(inchAndFeet),
+            "1 inch, 2 feet");
+}
+
+void MeasureFormatTest::TestFormatPeriodEn() {
+    UErrorCode status = U_ZERO_ERROR;
+    Measure t_19m[] = {Measure(19, MeasureUnit::createMinute(status), status)};
+    Measure t_1h_23_5s[] = {
+            Measure(1.0, MeasureUnit::createHour(status), status),
+            Measure(23.5, MeasureUnit::createSecond(status), status)
+    };
+    Measure t_1h_23_5m[] = {
+            Measure(1.0, MeasureUnit::createHour(status), status),
+            Measure(23.5, MeasureUnit::createMinute(status), status)
+    };
+    Measure t_1h_0m_23s[] = {
+            Measure(1.0, MeasureUnit::createHour(status), status),
+            Measure(0.0, MeasureUnit::createMinute(status), status),
+            Measure(23, MeasureUnit::createSecond(status), status)
+    };
+    Measure t_2y_5M_3w_4d[] = {
+            Measure(2.0, MeasureUnit::createYear(status), status),
+            Measure(5.0, MeasureUnit::createMonth(status), status),
+            Measure(3.0, MeasureUnit::createWeek(status), status),
+            Measure(4.0, MeasureUnit::createDay(status), status)
+    };
+    Measure t_1m_59_9996s[] = {
+            Measure(1.0, MeasureUnit::createMinute(status), status),
+            Measure(59.9996, MeasureUnit::createSecond(status), status)
+    };
+    Measure t_5h_17m[] = {
+            Measure(5.0, MeasureUnit::createHour(status), status),
+            Measure(17.0, MeasureUnit::createMinute(status), status)
+    };
+    Measure t_19m_28s[] = {
+            Measure(19.0, MeasureUnit::createMinute(status), status),
+            Measure(28.0, MeasureUnit::createSecond(status), status)
+    };
+    Measure t_0h_0m_17s[] = {
+            Measure(0.0, MeasureUnit::createHour(status), status),
+            Measure(0.0, MeasureUnit::createMinute(status), status),
+            Measure(17.0, MeasureUnit::createSecond(status), status)
+    };
+    Measure t_6h_56_92m[] = {
+            Measure(6.0, MeasureUnit::createHour(status), status),
+            Measure(56.92, MeasureUnit::createMinute(status), status)
+    };
+    Measure t_3h_5h[] = {
+            Measure(3.0, MeasureUnit::createHour(status), status),
+            Measure(5.0, MeasureUnit::createHour(status), status)
+    };
+
+    if (!assertSuccess("Error creating Measure objects", status)) {
+        return;
+    }
+    
+    ExpectedResult fullData[] = {
+            {t_1m_59_9996s, LENGTHOF(t_1m_59_9996s), "1 minute, 59.9996 seconds"},
+            {t_19m, LENGTHOF(t_19m), "19 minutes"},
+            {t_1h_23_5s, LENGTHOF(t_1h_23_5s), "1 hour, 23.5 seconds"},
+            {t_1h_23_5m, LENGTHOF(t_1h_23_5m), "1 hour, 23.5 minutes"},
+            {t_1h_0m_23s, LENGTHOF(t_1h_0m_23s), "1 hour, 0 minutes, 23 seconds"},
+            {t_2y_5M_3w_4d, LENGTHOF(t_2y_5M_3w_4d), "2 years, 5 months, 3 weeks, 4 days"}};
+
+    ExpectedResult abbrevData[] = {
+            {t_1m_59_9996s, LENGTHOF(t_1m_59_9996s), "1 min, 59.9996 secs"},
+            {t_19m, LENGTHOF(t_19m), "19 mins"},
+            {t_1h_23_5s, LENGTHOF(t_1h_23_5s), "1 hr, 23.5 secs"},
+            {t_1h_23_5m, LENGTHOF(t_1h_23_5m), "1 hr, 23.5 mins"},
+            {t_1h_0m_23s, LENGTHOF(t_1h_0m_23s), "1 hr, 0 mins, 23 secs"},
+            {t_2y_5M_3w_4d, LENGTHOF(t_2y_5M_3w_4d), "2 yrs, 5 mths, 3 wks, 4 days"}};
+
+    ExpectedResult narrowData[] = {
+            {t_1m_59_9996s, LENGTHOF(t_1m_59_9996s), "1m 59.9996s"},
+            {t_19m, LENGTHOF(t_19m), "19m"},
+            {t_1h_23_5s, LENGTHOF(t_1h_23_5s), "1h 23.5s"},
+            {t_1h_23_5m, LENGTHOF(t_1h_23_5m), "1h 23.5m"},
+            {t_1h_0m_23s, LENGTHOF(t_1h_0m_23s), "1h 0m 23s"},
+            {t_2y_5M_3w_4d, LENGTHOF(t_2y_5M_3w_4d), "2y 5m 3w 4d"}};
+
+    ExpectedResult numericData[] = {
+            {t_1m_59_9996s, LENGTHOF(t_1m_59_9996s), "1:59.9996"},
+            {t_19m, LENGTHOF(t_19m), "19m"},
+            {t_1h_23_5s, LENGTHOF(t_1h_23_5s), "1:00:23.5"},
+            {t_1h_23_5m, LENGTHOF(t_1h_23_5m), "1:23.5"},
+            {t_1h_0m_23s, LENGTHOF(t_1h_0m_23s), "1:00:23"},
+            {t_5h_17m, LENGTHOF(t_5h_17m), "5:17"},
+            {t_19m_28s, LENGTHOF(t_19m_28s), "19:28"},
+            {t_2y_5M_3w_4d, LENGTHOF(t_2y_5M_3w_4d), "2y 5m 3w 4d"},
+            {t_0h_0m_17s, LENGTHOF(t_0h_0m_17s), "0:00:17"},
+            {t_6h_56_92m, LENGTHOF(t_6h_56_92m), "6:56.92"},
+            {t_3h_5h, LENGTHOF(t_3h_5h), "3h 5h"}};
+
+    ExpectedResult fullDataDe[] = {
+            {t_1m_59_9996s, LENGTHOF(t_1m_59_9996s), "1 Minute und 59,9996 Sekunden"},
+            {t_19m, LENGTHOF(t_19m), "19 Minuten"},
+            {t_1h_23_5s, LENGTHOF(t_1h_23_5s), "1 Stunde und 23,5 Sekunden"},
+            {t_1h_23_5m, LENGTHOF(t_1h_23_5m), "1 Stunde und 23,5 Minuten"},
+            {t_1h_0m_23s, LENGTHOF(t_1h_0m_23s), "1 Stunde, 0 Minuten und 23 Sekunden"},
+            {t_2y_5M_3w_4d, LENGTHOF(t_2y_5M_3w_4d), "2 Jahre, 5 Monate, 3 Wochen und 4 Tage"}};
+
+    ExpectedResult numericDataDe[] = {
+            {t_1m_59_9996s, LENGTHOF(t_1m_59_9996s), "1:59,9996"},
+            {t_19m, LENGTHOF(t_19m), "19 Min."},
+            {t_1h_23_5s, LENGTHOF(t_1h_23_5s), "1:00:23,5"},
+            {t_1h_23_5m, LENGTHOF(t_1h_23_5m), "1:23,5"},
+            {t_1h_0m_23s, LENGTHOF(t_1h_0m_23s), "1:00:23"},
+            {t_5h_17m, LENGTHOF(t_5h_17m), "5:17"},
+            {t_19m_28s, LENGTHOF(t_19m_28s), "19:28"},
+            {t_2y_5M_3w_4d, LENGTHOF(t_2y_5M_3w_4d), "2 J, 5 M, 3 W und 4 T"},
+            {t_0h_0m_17s, LENGTHOF(t_0h_0m_17s), "0:00:17"},
+            {t_6h_56_92m, LENGTHOF(t_6h_56_92m), "6:56,92"},
+            {t_3h_5h, LENGTHOF(t_3h_5h), "3 Std., 5 Std."}};
+
+    Locale en(Locale::getEnglish());
+    LocalPointer<NumberFormat> nf(NumberFormat::createInstance(en, status));
+    if (!assertSuccess("Error creating number format en object", status)) {
+        return;
+    }
+    nf->setMaximumFractionDigits(4);
+    MeasureFormat mf(en, UMEASFMT_WIDTH_WIDE, (NumberFormat *) nf->clone(), status);
+    if (!assertSuccess("Error creating measure format en WIDE", status)) {
+        return;
+    }
+    verifyFormat("en WIDE", mf, fullData, LENGTHOF(fullData));
+
+    // exercise copy constructor
+    {
+        MeasureFormat mf2(mf);
+        verifyFormat("en WIDE copy", mf2, fullData, LENGTHOF(fullData));
+    }
+    // exercise clone
+    {
+        MeasureFormat *mf3 = (MeasureFormat *) mf.clone();
+        verifyFormat("en WIDE copy", *mf3, fullData, LENGTHOF(fullData));
+        delete mf3;
+    }
+    mf = MeasureFormat(en, UMEASFMT_WIDTH_SHORT, (NumberFormat *) nf->clone(), status);
+    if (!assertSuccess("Error creating measure format en SHORT", status)) {
+        return;
+    }
+    verifyFormat("en SHORT", mf, abbrevData, LENGTHOF(abbrevData));
+    mf = MeasureFormat(en, UMEASFMT_WIDTH_NARROW, (NumberFormat *) nf->clone(), status);
+    if (!assertSuccess("Error creating measure format en NARROW", status)) {
+        return;
+    }
+    verifyFormat("en NARROW", mf, narrowData, LENGTHOF(narrowData));
+    mf = MeasureFormat(en, UMEASFMT_WIDTH_NUMERIC, (NumberFormat *) nf->clone(), status);
+    if (!assertSuccess("Error creating measure format en NUMERIC", status)) {
+        return;
+    }
+    verifyFormat("en NUMERIC", mf, numericData, LENGTHOF(numericData));
+    
+    Locale de(Locale::getGerman());
+    nf.adoptInstead(NumberFormat::createInstance(de, status));
+    if (!assertSuccess("Error creating number format de object", status)) {
+        return;
+    }
+    nf->setMaximumFractionDigits(4);
+    mf = MeasureFormat(de, UMEASFMT_WIDTH_WIDE, (NumberFormat *) nf->clone(), status);
+    if (!assertSuccess("Error creating measure format de WIDE", status)) {
+        return;
+    }
+    verifyFormat("de WIDE", mf, fullDataDe, LENGTHOF(fullDataDe));
+    mf = MeasureFormat(de, UMEASFMT_WIDTH_NUMERIC, (NumberFormat *) nf->clone(), status);
+    if (!assertSuccess("Error creating measure format de NUMERIC", status)) {
+        return;
+    }
+    verifyFormat("de NUMERIC", mf, numericDataDe, LENGTHOF(numericDataDe));
+}
+
+void MeasureFormatTest::Test10219FractionalPlurals() {
+    Locale en(Locale::getEnglish());
+    double values[] = {1.588, 1.011};
+    const char *expected[2][3] = {
+            {"1 minute", "1.5 minutes", "1.58 minutes"},
+            {"1 minute", "1.0 minutes", "1.01 minutes"}
+    };
+    UErrorCode status = U_ZERO_ERROR;
+    for (int j = 0; j < LENGTHOF(values); j++) {
+        for (int i = 0; i < LENGTHOF(expected[j]); i++) {
+            DecimalFormat *df =
+                (DecimalFormat *) NumberFormat::createInstance(en, status);
+            if (!assertSuccess("Error creating Number format", status)) {
+                return;
+            }
+            df->setRoundingMode(DecimalFormat::kRoundDown);
+            df->setMinimumFractionDigits(i);
+            df->setMaximumFractionDigits(i);
+            MeasureFormat mf(en, UMEASFMT_WIDTH_WIDE, df, status);
+            if (!assertSuccess("Error creating Measure format", status)) {
+                return;
+            }
+            Measure measure(values[j], MeasureUnit::createMinute(status), status);
+            if (!assertSuccess("Error creating Measure unit", status)) {
+                return;
+            }
+            verifyFormat("Test10219", mf, &measure, 1, expected[j][i]);
+        }   
+    }
+}
+
+static MeasureUnit toMeasureUnit(MeasureUnit *adopted) {
+    MeasureUnit result(*adopted);
+    delete adopted;
+    return result;
+}
+
+void MeasureFormatTest::TestGreek() {
+    Locale locales[] = {Locale("el_GR"), Locale("el")};
+    UErrorCode status = U_ZERO_ERROR;
+    MeasureUnit units[] = {
+        toMeasureUnit(MeasureUnit::createSecond(status)),
+        toMeasureUnit(MeasureUnit::createMinute(status)),
+        toMeasureUnit(MeasureUnit::createHour(status)),
+        toMeasureUnit(MeasureUnit::createDay(status)),
+        toMeasureUnit(MeasureUnit::createWeek(status)),
+        toMeasureUnit(MeasureUnit::createMonth(status)),
+        toMeasureUnit(MeasureUnit::createYear(status))};
+    if (!assertSuccess("Error creating Measure units", status)) {
+        return;
+    }
+    UMeasureFormatWidth styles[] = {
+            UMEASFMT_WIDTH_WIDE,
+            UMEASFMT_WIDTH_SHORT};
+    int32_t numbers[] = {1, 7};
+    const char *expected[] = {
+        "1 \\u03B4\\u03B5\\u03C5\\u03C4\\u03B5\\u03C1\\u03CC\\u03BB\\u03B5\\u03C0\\u03C4\\u03BF",
+        "1 \\u03BB\\u03B5\\u03C0\\u03C4\\u03CC",
+        "1 \\u03CE\\u03C1\\u03B1",
+        "1 \\u03B7\\u03BC\\u03AD\\u03C1\\u03B1",
+        "1 \\u03B5\\u03B2\\u03B4\\u03BF\\u03BC\\u03AC\\u03B4\\u03B1",
+        "1 \\u03BC\\u03AE\\u03BD\\u03B1\\u03C2",
+        "1 \\u03AD\\u03C4\\u03BF\\u03C2",
+        "1 \\u03B4\\u03B5\\u03C5\\u03C4.",
+        "1 \\u03BB\\u03B5\\u03C0.",
+        "1 \\u03CE\\u03C1\\u03B1",
+        "1 \\u03B7\\u03BC\\u03AD\\u03C1\\u03B1",
+        "1 \\u03B5\\u03B2\\u03B4.",
+        "1 \\u03BC\\u03AE\\u03BD.",
+        "1 \\u03AD\\u03C4\\u03BF\\u03C2",
+        "7 \\u03B4\\u03B5\\u03C5\\u03C4\\u03B5\\u03C1\\u03CC\\u03BB\\u03B5\\u03C0\\u03C4\\u03B1",
+        "7 \\u03BB\\u03B5\\u03C0\\u03C4\\u03AC",
+        "7 \\u03CE\\u03C1\\u03B5\\u03C2",
+        "7 \\u03B7\\u03BC\\u03AD\\u03C1\\u03B5\\u03C2",
+        "7 \\u03B5\\u03B2\\u03B4\\u03BF\\u03BC\\u03AC\\u03B4\\u03B5\\u03C2",
+        "7 \\u03BC\\u03AE\\u03BD\\u03B5\\u03C2",
+        "7 \\u03AD\\u03C4\\u03B7",
+        "7 \\u03B4\\u03B5\\u03C5\\u03C4.",
+        "7 \\u03BB\\u03B5\\u03C0.",
+        "7 \\u03CE\\u03C1\\u03B5\\u03C2",
+        "7 \\u03B7\\u03BC\\u03AD\\u03C1\\u03B5\\u03C2",
+        "7 \\u03B5\\u03B2\\u03B4.",
+        "7 \\u03BC\\u03AE\\u03BD.",
+        "7 \\u03AD\\u03C4\\u03B7",
+        "1 \\u03B4\\u03B5\\u03C5\\u03C4\\u03B5\\u03C1\\u03CC\\u03BB\\u03B5\\u03C0\\u03C4\\u03BF",
+        "1 \\u03BB\\u03B5\\u03C0\\u03C4\\u03CC",
+        "1 \\u03CE\\u03C1\\u03B1",
+        "1 \\u03B7\\u03BC\\u03AD\\u03C1\\u03B1",
+        "1 \\u03B5\\u03B2\\u03B4\\u03BF\\u03BC\\u03AC\\u03B4\\u03B1",
+        "1 \\u03BC\\u03AE\\u03BD\\u03B1\\u03C2",
+        "1 \\u03AD\\u03C4\\u03BF\\u03C2",
+        "1 \\u03B4\\u03B5\\u03C5\\u03C4.",
+        "1 \\u03BB\\u03B5\\u03C0.",
+        "1 \\u03CE\\u03C1\\u03B1",
+        "1 \\u03B7\\u03BC\\u03AD\\u03C1\\u03B1",
+        "1 \\u03B5\\u03B2\\u03B4.",
+        "1 \\u03BC\\u03AE\\u03BD.",
+        "1 \\u03AD\\u03C4\\u03BF\\u03C2",
+        "7 \\u03B4\\u03B5\\u03C5\\u03C4\\u03B5\\u03C1\\u03CC\\u03BB\\u03B5\\u03C0\\u03C4\\u03B1",
+        "7 \\u03BB\\u03B5\\u03C0\\u03C4\\u03AC",
+        "7 \\u03CE\\u03C1\\u03B5\\u03C2",
+        "7 \\u03B7\\u03BC\\u03AD\\u03C1\\u03B5\\u03C2",
+        "7 \\u03B5\\u03B2\\u03B4\\u03BF\\u03BC\\u03AC\\u03B4\\u03B5\\u03C2",
+        "7 \\u03BC\\u03AE\\u03BD\\u03B5\\u03C2",
+        "7 \\u03AD\\u03C4\\u03B7",
+        "7 \\u03B4\\u03B5\\u03C5\\u03C4.",
+        "7 \\u03BB\\u03B5\\u03C0.",
+        "7 \\u03CE\\u03C1\\u03B5\\u03C2",
+        "7 \\u03B7\\u03BC\\u03AD\\u03C1\\u03B5\\u03C2",
+        "7 \\u03B5\\u03B2\\u03B4.",
+        "7 \\u03BC\\u03AE\\u03BD.",
+        "7 \\u03AD\\u03C4\\u03B7"};
+
+    int32_t counter = 0;
+    for (int32_t locIndex = 0; locIndex < LENGTHOF(locales); ++locIndex ) {
+        for( int32_t numIndex = 0; numIndex < LENGTHOF(numbers); ++numIndex ) {
+            for ( int32_t styleIndex = 0; styleIndex < LENGTHOF(styles); ++styleIndex ) {
+                for ( int32_t unitIndex = 0; unitIndex < LENGTHOF(units); ++unitIndex ) {
+                    Measure measure(numbers[numIndex], new MeasureUnit(units[unitIndex]), status);
+                    if (!assertSuccess("Error creating Measure", status)) {
+                        return;
+                    }
+                    MeasureFormat fmt(locales[locIndex], styles[styleIndex], status);
+                    if (!assertSuccess("Error creating Measure format", status)) {
+                        return;
+                    }
+                    verifyFormat("TestGreek", fmt, &measure, 1, expected[counter]);
+                    ++counter;
+                }
+            }
+        }
+    }
+}
+
+void MeasureFormatTest::TestFormatSingleArg() {
+    UErrorCode status = U_ZERO_ERROR;
+    MeasureFormat fmt("en", UMEASFMT_WIDTH_WIDE, status);
+    if (!assertSuccess("Error creating formatter", status)) {
+        return;
+    }
+    UnicodeString buffer;
+    FieldPosition pos(0);
+    fmt.format(
+            new Measure(3.5, MeasureUnit::createFoot(status), status),
+            buffer,
+            pos,
+            status);
+    if (!assertSuccess("Error formatting", status)) {
+        return;
+    }
+    assertEquals(
+            "TestFormatSingleArg",
+            UnicodeString("3.5 feet"),
+            buffer);
+}
+
+void MeasureFormatTest::TestFormatMeasuresZeroArg() {
+    UErrorCode status = U_ZERO_ERROR;
+    MeasureFormat fmt("en", UMEASFMT_WIDTH_WIDE, status);
+    verifyFormat("TestFormatMeasuresZeroArg", fmt, NULL, 0, "");
+}
+
+void MeasureFormatTest::TestMultiples() {
+    Locale ru("ru");
+    Locale en("en");
+    helperTestMultiples(en, UMEASFMT_WIDTH_WIDE, "2 miles, 1 foot, 2.3 inches");
+    helperTestMultiples(en, UMEASFMT_WIDTH_SHORT, "2 mi, 1 ft, 2.3 in");
+    helperTestMultiples(en, UMEASFMT_WIDTH_NARROW, "2mi 1\\u2032 2.3\\u2033");
+    helperTestMultiples(ru, UMEASFMT_WIDTH_WIDE, "2 \\u043C\\u0438\\u043B\\u0438, 1 \\u0444\\u0443\\u0442 \\u0438 2,3 \\u0434\\u044E\\u0439\\u043C\\u0430");
+    helperTestMultiples(ru, UMEASFMT_WIDTH_SHORT, "2 \\u043C\\u0438\\u043B\\u0438 1 \\u0444\\u0443\\u0442 2,3 \\u0434\\u044E\\u0439\\u043C\\u0430");
+    helperTestMultiples(ru, UMEASFMT_WIDTH_NARROW, "2 \\u043C\\u0438\\u043B\\u0438, 1 \\u0444\\u0443\\u0442, 2,3 \\u0434\\u044E\\u0439\\u043C\\u0430");
+}
+
+void MeasureFormatTest::helperTestMultiples(
+        const Locale &locale,
+        UMeasureFormatWidth width,
+        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.formatMeasures(measures, LENGTHOF(measures), buffer, pos, status);
+    if (!assertSuccess("Error formatting measures", status)) {
+        return;
+    }
+    assertEquals("TestMultiples", UnicodeString(expected).unescape(), buffer);
+}
+
+void MeasureFormatTest::TestGram() {
+    UErrorCode status = U_ZERO_ERROR;
+    MeasureFormat fmt("en", UMEASFMT_WIDTH_SHORT, status);
+    if (!assertSuccess("Error creating format object", status)) {
+        return;
+    }
+    Measure gram(1, MeasureUnit::createGram(status), status);
+    Measure gforce(1, MeasureUnit::createGForce(status), status);
+    if (!assertSuccess("Error creating measures", status)) {
+        return;
+    }
+    verifyFormat("TestGram", fmt, &gram, 1, "1 g");
+    verifyFormat("TestGram", fmt, &gforce, 1, "1 G");
+}
+
+void MeasureFormatTest::TestCurrencies() {
+    UChar USD[] = {'U', 'S', 'D', 0};
+    UErrorCode status = U_ZERO_ERROR;
+    CurrencyAmount USD_1(1.0, USD, status);
+    CurrencyAmount USD_2(2.0, USD, status);
+    CurrencyAmount USD_NEG_1(-1.0, USD, status);
+    if (!assertSuccess("Error creating measures", status)) {
+        return;
+    }
+    Locale en("en");
+    MeasureFormat fmt(en, UMEASFMT_WIDTH_WIDE, status);
+    if (!assertSuccess("Error creating format object", status)) {
+        return;
+    }
+    verifyFormat("TestCurrenciesWide", fmt, &USD_NEG_1, 1, "-1.00 US dollars");
+    verifyFormat("TestCurrenciesWide", fmt, &USD_1, 1, "1.00 US dollars");
+    verifyFormat("TestCurrenciesWide", fmt, &USD_2, 1, "2.00 US dollars");
+    fmt = MeasureFormat(en, UMEASFMT_WIDTH_SHORT, status);
+    if (!assertSuccess("Error creating format object", status)) {
+        return;
+    }
+    verifyFormat("TestCurrenciesShort", fmt, &USD_NEG_1, 1, "-USD1.00");
+    verifyFormat("TestCurrenciesShort", fmt, &USD_1, 1, "USD1.00");
+    verifyFormat("TestCurrenciesShort", fmt, &USD_2, 1, "USD2.00");
+    fmt = MeasureFormat(en, UMEASFMT_WIDTH_NARROW, status);
+    if (!assertSuccess("Error creating format object", status)) {
+        return;
+    }
+    verifyFormat("TestCurrenciesNarrow", fmt, &USD_NEG_1, 1, "-$1.00");
+    verifyFormat("TestCurrenciesNarrow", fmt, &USD_1, 1, "$1.00");
+    verifyFormat("TestCurrenciesNarrow", fmt, &USD_2, 1, "$2.00");
+    fmt = MeasureFormat(en, UMEASFMT_WIDTH_NUMERIC, status);
+    if (!assertSuccess("Error creating format object", status)) {
+        return;
+    }
+    verifyFormat("TestCurrenciesNumeric", fmt, &USD_NEG_1, 1, "-$1.00");
+    verifyFormat("TestCurrenciesNumeric", fmt, &USD_1, 1, "$1.00");
+    verifyFormat("TestCurrenciesNumeric", fmt, &USD_2, 1, "$2.00");
+}
+
+void MeasureFormatTest::TestFieldPosition() {
+    UErrorCode status = U_ZERO_ERROR;
+    MeasureFormat fmt("en", UMEASFMT_WIDTH_SHORT, status);
+    if (!assertSuccess("Error creating format object", status)) {
+        return;
+    }
+    Measure measure(43.5, MeasureUnit::createFoot(status), status);
+    if (!assertSuccess("Error creating measure object 1", status)) {
+        return;
+    }
+    UnicodeString prefix("123456: ");
+    verifyFieldPosition(
+            "",
+            fmt,
+            prefix,
+            &measure,
+            1,
+            NumberFormat::kDecimalSeparatorField,
+            10,
+            11);
+    measure = Measure(43, MeasureUnit::createFoot(status), status);
+    if (!assertSuccess("Error creating measure object 2", status)) {
+        return;
+    }
+    verifyFieldPosition(
+            "",
+            fmt,
+            prefix,
+            &measure,
+            1,
+            NumberFormat::kDecimalSeparatorField,
+            0,
+            0);
+}
+
+void MeasureFormatTest::TestFieldPositionMultiple() {
+    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: ");
+    verifyFieldPosition(
+            "Integer",
+            fmt,
+            prefix,
+            first,
+            LENGTHOF(first),
+            NumberFormat::kIntegerField,
+            8,
+            11);
+    verifyFieldPosition(
+            "Decimal separator",
+            fmt,
+            prefix,
+            second,
+            LENGTHOF(second),
+            NumberFormat::kDecimalSeparatorField,
+            23,
+            24);
+    verifyFieldPosition(
+            "no decimal separator",
+            fmt,
+            prefix,
+            third,
+            LENGTHOF(third),
+            NumberFormat::kDecimalSeparatorField,
+            0,
+            0);
+}
+
+void MeasureFormatTest::TestBadArg() {
+    UErrorCode status = U_ZERO_ERROR;
+    MeasureFormat fmt("en", UMEASFMT_WIDTH_SHORT, status);
+    if (!assertSuccess("Error creating format object", status)) {
+        return;
+    }
+    FieldPosition pos(0);
+    UnicodeString buffer;
+    fmt.format(
+            9.3,
+            buffer,
+            pos,
+            status);
+    if (status != U_ILLEGAL_ARGUMENT_ERROR) {
+        errln("Expected ILLEGAL_ARGUMENT_ERROR");
+    }
+}
+
+void MeasureFormatTest::TestEquality() {
+    UErrorCode status = U_ZERO_ERROR;
+    NumberFormat* nfeq = NumberFormat::createInstance("en", status);
+    NumberFormat* nfne = NumberFormat::createInstance("fr", status);
+    MeasureFormat fmt("en", UMEASFMT_WIDTH_SHORT, status);
+    MeasureFormat fmtEq(fmt);
+    MeasureFormat fmtEq2("en", UMEASFMT_WIDTH_SHORT, nfeq, status);
+    MeasureFormat fmtne1("en", UMEASFMT_WIDTH_WIDE, status);
+    MeasureFormat fmtne2("fr", UMEASFMT_WIDTH_SHORT, status);
+    MeasureFormat fmtne3("en", UMEASFMT_WIDTH_SHORT, nfne, status);
+    assertSuccess("Error creating MeasureFormats", status);
+    assertTrue("Equal", fmt == fmtEq);
+    assertTrue("Equal2", fmt == fmtEq2);
+    assertFalse("Equal Neg", fmt != fmtEq);
+    assertTrue("Not Equal 1", fmt != fmtne1);
+    assertFalse("Not Equal Neg 1", fmt == fmtne1);
+    assertTrue("Not Equal 2", fmt != fmtne2);
+    assertTrue("Not Equal 3", fmt != fmtne3);
+}
+
+void MeasureFormatTest::verifyFieldPosition(
+        const char *description,
+        const MeasureFormat &fmt,
+        const UnicodeString &prefix,
+        const Measure *measures,
+        int32_t measureCount,
+        NumberFormat::EAlignmentFields field,
+        int32_t start,
+        int32_t end) {
+    // 8 char lead
+    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.formatMeasures(measures, measureCount, 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,
+        const Measure *measures,
+        int32_t measureCount,
+        const char *expected) {
+    verifyFormatWithPrefix(
+            description,
+            fmt,
+            "",
+            measures,
+            measureCount,
+            expected);
+}
+
+void MeasureFormatTest::verifyFormatWithPrefix(
+        const char *description,
+        const MeasureFormat &fmt,
+        const UnicodeString &prefix,
+        const Measure *measures,
+        int32_t measureCount,
+        const char *expected) {
+    UnicodeString result(prefix);
+    FieldPosition pos(0);
+    UErrorCode status = U_ZERO_ERROR;
+    fmt.formatMeasures(measures, measureCount, result, pos, status);
+    if (!assertSuccess("Error formatting", status)) {
+        return;
+    }
+    assertEquals(description, UnicodeString(expected).unescape(), result);
+}
+
+void MeasureFormatTest::verifyFormat(
+        const char *description,
+        const MeasureFormat &fmt,
+        const ExpectedResult *expectedResults,
+        int32_t count) {
+    for (int32_t i = 0; i < count; ++i) {
+        verifyFormat(description, fmt, expectedResults[i].measures, expectedResults[i].count, expectedResults[i].expected);
+    }
+}
+
+extern IntlTest *createMeasureFormatTest() {
+    return new MeasureFormatTest();
+}
+
+#endif
+
index 9554ae0e557d96382cc85b645024c1d7e75bb35a..0ee892fa0c60436124dee550c21010fbf7ebeaa5 100644 (file)
@@ -1896,6 +1896,8 @@ void NumberFormatTest::TestCurrencyNames(void) {
 void NumberFormatTest::TestCurrencyUnit(void){
     UErrorCode ec = U_ZERO_ERROR;
     static const UChar USD[] = {85, 83, 68, 0}; /*USD*/
+    static const UChar BAD[] = {63, 63, 63, 0}; /*???*/
+    static const UChar BAD2[] = {63, 63, 65, 0}; /*???*/
     CurrencyUnit cu(USD, ec);
     assertSuccess("CurrencyUnit", ec);
 
@@ -1911,6 +1913,23 @@ void NumberFormatTest::TestCurrencyUnit(void){
     if (!(*cu3 == cu)){
         errln("CurrencyUnit cloned object should be same");
     }
+    CurrencyUnit bad(BAD, ec);
+    assertSuccess("CurrencyUnit", ec);
+    if (cu.getIndex() == bad.getIndex()) {
+        errln("Indexes of different currencies should differ.");
+    }
+    CurrencyUnit bad2(BAD2, ec);
+    assertSuccess("CurrencyUnit", ec);
+    if (bad2.getIndex() != bad.getIndex()) {
+        errln("Indexes of unrecognized currencies should be the same.");
+    }
+    if (bad == bad2) {
+        errln("Different unrecognized currencies should not be equal.");
+    }
+    bad = bad2;
+    if (bad != bad2) {
+        errln("Currency unit assignment should be the same.");
+    }
     delete cu3;
 }
 
index 202fda8860a698ee0e717b2b06fa78eb358781af..a86d682337c41a770878cea76ece5f53ffa97fc3 100644 (file)
@@ -138,10 +138,37 @@ void TimeUnitTest::testAPI() {
 
     TimeUnit::UTimeUnitFields field = tmunit_m->getTimeUnitField();
     assertTrue("field of month time unit is month", (field == TimeUnit::UTIMEUNIT_MONTH));
-    
+
+    //===== Interropability with MeasureUnit ======
+    MeasureUnit **ptrs = new MeasureUnit *[TimeUnit::UTIMEUNIT_FIELD_COUNT];
+
+    ptrs[TimeUnit::UTIMEUNIT_YEAR] = MeasureUnit::createYear(status);
+    ptrs[TimeUnit::UTIMEUNIT_MONTH] = MeasureUnit::createMonth(status);
+    ptrs[TimeUnit::UTIMEUNIT_DAY] = MeasureUnit::createDay(status);
+    ptrs[TimeUnit::UTIMEUNIT_WEEK] = MeasureUnit::createWeek(status);
+    ptrs[TimeUnit::UTIMEUNIT_HOUR] = MeasureUnit::createHour(status);
+    ptrs[TimeUnit::UTIMEUNIT_MINUTE] = MeasureUnit::createMinute(status);
+    ptrs[TimeUnit::UTIMEUNIT_SECOND] = MeasureUnit::createSecond(status);
+    if (!assertSuccess("TimeUnit::createInstance", status)) return;
+
+    for (TimeUnit::UTimeUnitFields j = TimeUnit::UTIMEUNIT_YEAR; 
+            j < TimeUnit::UTIMEUNIT_FIELD_COUNT; 
+            j = (TimeUnit::UTimeUnitFields)(j+1)) {
+        MeasureUnit *ptr = TimeUnit::createInstance(j, status);
+        if (!assertSuccess("TimeUnit::createInstance", status)) return;
+        assertTrue(
+                "Time unit should be equal to corresponding MeasureUnit",
+                *ptr == *ptrs[j]);
+        delete ptr;
+    }
     delete tmunit;
     delete another;
     delete tmunit_m;
+    for (int i = 0; i < TimeUnit::UTIMEUNIT_FIELD_COUNT; ++i) {
+        delete ptrs[i];
+    }
+    delete [] ptrs;
+
     //
     //================= TimeUnitAmount =================