From 0a42d4959f930b0ef657674ceeb4eab4f02f7749 Mon Sep 17 00:00:00 2001 From: Travis Keep Date: Tue, 4 Feb 2014 00:29:17 +0000 Subject: [PATCH] ICU-10640 Add C++ MeasureFormat API. X-SVN-Rev: 35069 --- .gitattributes | 2 + icu4c/source/common/listformatter.cpp | 10 +- icu4c/source/i18n/Makefile.in | 2 +- icu4c/source/i18n/currfmt.cpp | 15 +- icu4c/source/i18n/currfmt.h | 7 +- icu4c/source/i18n/currunit.cpp | 19 +- icu4c/source/i18n/measfmt.cpp | 765 ++++++++++++++- icu4c/source/i18n/measunit.cpp | 734 ++++++++++++++ icu4c/source/i18n/measure.cpp | 13 +- icu4c/source/i18n/quantityformatter.cpp | 21 +- icu4c/source/i18n/quantityformatter.h | 7 + icu4c/source/i18n/reldatefmt.cpp | 3 +- icu4c/source/i18n/tmunit.cpp | 45 +- icu4c/source/i18n/tmutfmt.cpp | 178 +--- icu4c/source/i18n/ucln_in.h | 5 +- icu4c/source/i18n/unicode/currunit.h | 9 +- icu4c/source/i18n/unicode/measfmt.h | 236 ++++- icu4c/source/i18n/unicode/measunit.h | 295 +++++- icu4c/source/i18n/unicode/measure.h | 4 +- icu4c/source/i18n/unicode/tmunit.h | 22 +- icu4c/source/i18n/unicode/tmutfmt.h | 33 +- icu4c/source/test/intltest/Makefile.in | 2 +- icu4c/source/test/intltest/intltest.vcxproj | 1 + .../test/intltest/intltest.vcxproj.filters | 3 + icu4c/source/test/intltest/itformat.cpp | 12 +- icu4c/source/test/intltest/measfmttest.cpp | 901 ++++++++++++++++++ icu4c/source/test/intltest/numfmtst.cpp | 19 + icu4c/source/test/intltest/tufmtts.cpp | 29 +- 28 files changed, 3103 insertions(+), 289 deletions(-) create mode 100644 icu4c/source/i18n/measunit.cpp create mode 100644 icu4c/source/test/intltest/measfmttest.cpp diff --git a/.gitattributes b/.gitattributes index 06ff3b727b8..5b5feb4a149 100644 --- a/.gitattributes +++ b/.gitattributes @@ -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 diff --git a/icu4c/source/common/listformatter.cpp b/icu4c/source/common/listformatter.cpp index 1668037d924..17bc0666d8b 100644 --- a/icu4c/source/common/listformatter.cpp +++ b/icu4c/source/common/listformatter.cpp @@ -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; diff --git a/icu4c/source/i18n/Makefile.in b/icu4c/source/i18n/Makefile.in index 63ddbc02a3f..4f2316fba8a 100644 --- a/icu4c/source/i18n/Makefile.in +++ b/icu4c/source/i18n/Makefile.in @@ -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 diff --git a/icu4c/source/i18n/currfmt.cpp b/icu4c/source/i18n/currfmt.cpp index 97be9730f35..cbdcb376925 100644 --- a/icu4c/source/i18n/currfmt.cpp +++ b/icu4c/source/i18n/currfmt.cpp @@ -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); } diff --git a/icu4c/source/i18n/currfmt.h b/icu4c/source/i18n/currfmt.h index 82aae3ec050..b3f23e56f96 100644 --- a/icu4c/source/i18n/currfmt.h +++ b/icu4c/source/i18n/currfmt.h @@ -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. */ diff --git a/icu4c/source/i18n/currunit.cpp b/icu4c/source/i18n/currunit.cpp index 870d9d0c4ce..207d4122d9e 100644 --- a/icu4c/source/i18n/currunit.cpp +++ b/icu4c/source/i18n/currunit.cpp @@ -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 diff --git a/icu4c/source/i18n/measfmt.cpp b/icu4c/source/i18n/measfmt.cpp index 6f06852acf9..3fd41e4074d 100644 --- a/icu4c/source/i18n/measfmt.cpp +++ b/icu4c/source/i18n/measfmt.cpp @@ -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 @@ -13,13 +13,772 @@ #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); +}; -MeasureFormat::~MeasureFormat() {} +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; + SharedPtr pluralRules; + SharedPtr numberFormat; + SharedPtr currencyFormats[UMEASFMT_WIDTH_NARROW + 1]; + SharedPtr 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 result(new MeasureFormatData()); + LocalPointer 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 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 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 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 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 hourUnit(MeasureUnit::createHour(status)); + LocalPointer minuteUnit(MeasureUnit::createMinute(status)); + LocalPointer 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( + 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(&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(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 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 index 00000000000..42903abd5e9 --- /dev/null +++ b/icu4c/source/i18n/measunit.cpp @@ -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(&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 + + diff --git a/icu4c/source/i18n/measure.cpp b/icu4c/source/i18n/measure.cpp index 639de3e6db1..afc5bef6a09 100644 --- a/icu4c/source/i18n/measure.cpp +++ b/icu4c/source/i18n/measure.cpp @@ -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 diff --git a/icu4c/source/i18n/quantityformatter.cpp b/icu4c/source/i18n/quantityformatter.cpp index 2a26fb5053e..5b01f650ed3 100644 --- a/icu4c/source/i18n/quantityformatter.cpp +++ b/icu4c/source/i18n/quantityformatter.cpp @@ -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 diff --git a/icu4c/source/i18n/quantityformatter.h b/icu4c/source/i18n/quantityformatter.h index a1576160335..5dceaf652dc 100644 --- a/icu4c/source/i18n/quantityformatter.h +++ b/icu4c/source/i18n/quantityformatter.h @@ -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: diff --git a/icu4c/source/i18n/reldatefmt.cpp b/icu4c/source/i18n/reldatefmt.cpp index 8d8e6a9a6df..1b26cd91b6d 100644 --- a/icu4c/source/i18n/reldatefmt.cpp +++ b/icu4c/source/i18n/reldatefmt.cpp @@ -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); } diff --git a/icu4c/source/i18n/tmunit.cpp b/icu4c/source/i18n/tmunit.cpp index 121b81ce78c..48e0ff489fc 100644 --- a/icu4c/source/i18n/tmunit.cpp +++ b/icu4c/source/i18n/tmunit.cpp @@ -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() { } diff --git a/icu4c/source/i18n/tmutfmt.cpp b/icu4c/source/i18n/tmutfmt.cpp index ac1e780a750..f3bf3dc8a2f 100644 --- a/icu4c/source/i18n/tmutfmt.cpp +++ b/icu4c/source/i18n/tmutfmt.cpp @@ -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(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(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); } diff --git a/icu4c/source/i18n/ucln_in.h b/icu4c/source/i18n/ucln_in.h index b4276108995..33bd468112d 100644 --- a/icu4c/source/i18n/ucln_in.h +++ b/icu4c/source/i18n/ucln_in.h @@ -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, diff --git a/icu4c/source/i18n/unicode/currunit.h b/icu4c/source/i18n/unicode/currunit.h index ecd94110292..d79836bb749 100644 --- a/icu4c/source/i18n/unicode/currunit.h +++ b/icu4c/source/i18n/unicode/currunit.h @@ -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. diff --git a/icu4c/source/i18n/unicode/measfmt.h b/icu4c/source/i18n/unicode/measfmt.h index d49a9b572d2..b59dfd9e582 100644 --- a/icu4c/source/i18n/unicode/measfmt.h +++ b/icu4c/source/i18n/unicode/measfmt.h @@ -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 @@ -12,27 +12,74 @@ #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. - * - *

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. - * - *

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 diff --git a/icu4c/source/i18n/unicode/measunit.h b/icu4c/source/i18n/unicode/measunit.h index 9a210e65957..6dad871318d 100644 --- a/icu4c/source/i18n/unicode/measunit.h +++ b/icu4c/source/i18n/unicode/measunit.h @@ -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 @@ -12,10 +12,7 @@ #define __MEASUREUNIT_H__ #include "unicode/utypes.h" - -#if !UCONFIG_NO_FORMATTING - -#include "unicode/fmtable.h" +#include "unicode/unistr.h" /** * \file @@ -24,48 +21,312 @@ 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. * - *

This is an abstract class. - * * @author Alan Liu * @stable ICU 3.0 */ class U_I18N_API MeasureUnit: public UObject { public: + /** - * Return a polymorphic clone of this object. The result will + * 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); + + /** + * 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 */ - MeasureUnit(); + void initTime(const char *timeId); + + /** + * For ICU use only. + * @internal + */ + 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__ diff --git a/icu4c/source/i18n/unicode/measure.h b/icu4c/source/i18n/unicode/measure.h index 6b7a0497b47..0d08cc796dd 100644 --- a/icu4c/source/i18n/unicode/measure.h +++ b/icu4c/source/i18n/unicode/measure.h @@ -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 diff --git a/icu4c/source/i18n/unicode/tmunit.h b/icu4c/source/i18n/unicode/tmunit.h index 6eda105c12f..e417f3522bc 100644 --- a/icu4c/source/i18n/unicode/tmunit.h +++ b/icu4c/source/i18n/unicode/tmunit.h @@ -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 */ diff --git a/icu4c/source/i18n/unicode/tmutfmt.h b/icu4c/source/i18n/unicode/tmutfmt.h index 7ed7035b6b5..e1fde7255fc 100644 --- a/icu4c/source/i18n/unicode/tmutfmt.h +++ b/icu4c/source/i18n/unicode/tmutfmt.h @@ -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 diff --git a/icu4c/source/test/intltest/Makefile.in b/icu4c/source/test/intltest/Makefile.in index da33dc38441..0b7189134b5 100644 --- a/icu4c/source/test/intltest/Makefile.in +++ b/icu4c/source/test/intltest/Makefile.in @@ -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) diff --git a/icu4c/source/test/intltest/intltest.vcxproj b/icu4c/source/test/intltest/intltest.vcxproj index 7c45d363d49..13d8846ed32 100644 --- a/icu4c/source/test/intltest/intltest.vcxproj +++ b/icu4c/source/test/intltest/intltest.vcxproj @@ -308,6 +308,7 @@ false + diff --git a/icu4c/source/test/intltest/intltest.vcxproj.filters b/icu4c/source/test/intltest/intltest.vcxproj.filters index 884ef5f5b93..dacd9e24cbc 100644 --- a/icu4c/source/test/intltest/intltest.vcxproj.filters +++ b/icu4c/source/test/intltest/intltest.vcxproj.filters @@ -241,6 +241,9 @@ formatting + + formatting + formatting diff --git a/icu4c/source/test/intltest/itformat.cpp b/icu4c/source/test/intltest/itformat.cpp index 84f25dea08b..ed08d47bd56 100644 --- a/icu4c/source/test/intltest/itformat.cpp +++ b/icu4c/source/test/intltest/itformat.cpp @@ -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 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 index 00000000000..015e2be6c01 --- /dev/null +++ b/icu4c/source/test/intltest/measfmttest.cpp @@ -0,0 +1,901 @@ +/* +******************************************************************************* +* Copyright (C) 2014, International Business Machines Corporation and * +* others. All Rights Reserved. * +******************************************************************************* +* +* File MEASFMTTEST.CPP +* +******************************************************************************* +*/ +#include +#include + +#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 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 + diff --git a/icu4c/source/test/intltest/numfmtst.cpp b/icu4c/source/test/intltest/numfmtst.cpp index 9554ae0e557..0ee892fa0c6 100644 --- a/icu4c/source/test/intltest/numfmtst.cpp +++ b/icu4c/source/test/intltest/numfmtst.cpp @@ -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; } diff --git a/icu4c/source/test/intltest/tufmtts.cpp b/icu4c/source/test/intltest/tufmtts.cpp index 202fda8860a..a86d682337c 100644 --- a/icu4c/source/test/intltest/tufmtts.cpp +++ b/icu4c/source/test/intltest/tufmtts.cpp @@ -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 =================