Home | History | Annotate | Download | only in i18n
      1 /*
      2 **********************************************************************
      3 * Copyright (c) 2004-2014, International Business Machines
      4 * Corporation and others.  All Rights Reserved.
      5 **********************************************************************
      6 * Author: Alan Liu
      7 * Created: April 20, 2004
      8 * Since: ICU 3.0
      9 **********************************************************************
     10 */
     11 #include "utypeinfo.h"  // for 'typeid' to work
     12 #include "unicode/utypes.h"
     13 
     14 #if !UCONFIG_NO_FORMATTING
     15 
     16 #include "unicode/measfmt.h"
     17 #include "unicode/numfmt.h"
     18 #include "currfmt.h"
     19 #include "unicode/localpointer.h"
     20 #include "quantityformatter.h"
     21 #include "unicode/plurrule.h"
     22 #include "unicode/decimfmt.h"
     23 #include "lrucache.h"
     24 #include "uresimp.h"
     25 #include "unicode/ures.h"
     26 #include "cstring.h"
     27 #include "mutex.h"
     28 #include "ucln_in.h"
     29 #include "unicode/listformatter.h"
     30 #include "charstr.h"
     31 #include "unicode/putil.h"
     32 #include "unicode/smpdtfmt.h"
     33 
     34 #include "sharednumberformat.h"
     35 #include "sharedpluralrules.h"
     36 
     37 #define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0]))
     38 #define MEAS_UNIT_COUNT 46
     39 #define WIDTH_INDEX_COUNT (UMEASFMT_WIDTH_NARROW + 1)
     40 
     41 static icu::LRUCache *gCache = NULL;
     42 static UMutex gCacheMutex = U_MUTEX_INITIALIZER;
     43 static icu::UInitOnce gCacheInitOnce = U_INITONCE_INITIALIZER;
     44 
     45 U_CDECL_BEGIN
     46 static UBool U_CALLCONV measfmt_cleanup() {
     47     gCacheInitOnce.reset();
     48     if (gCache) {
     49         delete gCache;
     50         gCache = NULL;
     51     }
     52     return TRUE;
     53 }
     54 U_CDECL_END
     55 
     56 U_NAMESPACE_BEGIN
     57 
     58 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MeasureFormat)
     59 
     60 // Used to format durations like 5:47 or 21:35:42.
     61 class NumericDateFormatters : public UMemory {
     62 public:
     63     // Formats like H:mm
     64     SimpleDateFormat hourMinute;
     65 
     66     // formats like M:ss
     67     SimpleDateFormat minuteSecond;
     68 
     69     // formats like H:mm:ss
     70     SimpleDateFormat hourMinuteSecond;
     71 
     72     // Constructor that takes the actual patterns for hour-minute,
     73     // minute-second, and hour-minute-second respectively.
     74     NumericDateFormatters(
     75             const UnicodeString &hm,
     76             const UnicodeString &ms,
     77             const UnicodeString &hms,
     78             UErrorCode &status) :
     79             hourMinute(hm, status),
     80             minuteSecond(ms, status),
     81             hourMinuteSecond(hms, status) {
     82         const TimeZone *gmt = TimeZone::getGMT();
     83         hourMinute.setTimeZone(*gmt);
     84         minuteSecond.setTimeZone(*gmt);
     85         hourMinuteSecond.setTimeZone(*gmt);
     86     }
     87 private:
     88     NumericDateFormatters(const NumericDateFormatters &other);
     89     NumericDateFormatters &operator=(const NumericDateFormatters &other);
     90 };
     91 
     92 // Instances contain all MeasureFormat specific data for a particular locale.
     93 // This data is cached. It is never copied, but is shared via shared pointers.
     94 class MeasureFormatCacheData : public SharedObject {
     95 public:
     96     QuantityFormatter formatters[MEAS_UNIT_COUNT][WIDTH_INDEX_COUNT];
     97     MeasureFormatCacheData();
     98     void adoptCurrencyFormat(int32_t widthIndex, NumberFormat *nfToAdopt) {
     99         delete currencyFormats[widthIndex];
    100         currencyFormats[widthIndex] = nfToAdopt;
    101     }
    102     const NumberFormat *getCurrencyFormat(int32_t widthIndex) const {
    103         return currencyFormats[widthIndex];
    104     }
    105     void adoptIntegerFormat(NumberFormat *nfToAdopt) {
    106         delete integerFormat;
    107         integerFormat = nfToAdopt;
    108     }
    109     const NumberFormat *getIntegerFormat() const {
    110         return integerFormat;
    111     }
    112     void adoptNumericDateFormatters(NumericDateFormatters *formattersToAdopt) {
    113         delete numericDateFormatters;
    114         numericDateFormatters = formattersToAdopt;
    115     }
    116     const NumericDateFormatters *getNumericDateFormatters() const {
    117         return numericDateFormatters;
    118     }
    119     virtual ~MeasureFormatCacheData();
    120 private:
    121     NumberFormat *currencyFormats[WIDTH_INDEX_COUNT];
    122     NumberFormat *integerFormat;
    123     NumericDateFormatters *numericDateFormatters;
    124     MeasureFormatCacheData(const MeasureFormatCacheData &other);
    125     MeasureFormatCacheData &operator=(const MeasureFormatCacheData &other);
    126 };
    127 
    128 MeasureFormatCacheData::MeasureFormatCacheData() {
    129     for (int32_t i = 0; i < LENGTHOF(currencyFormats); ++i) {
    130         currencyFormats[i] = NULL;
    131     }
    132     integerFormat = NULL;
    133     numericDateFormatters = NULL;
    134 }
    135 
    136 MeasureFormatCacheData::~MeasureFormatCacheData() {
    137     for (int32_t i = 0; i < LENGTHOF(currencyFormats); ++i) {
    138         delete currencyFormats[i];
    139     }
    140     delete integerFormat;
    141     delete numericDateFormatters;
    142 }
    143 
    144 static int32_t widthToIndex(UMeasureFormatWidth width) {
    145     if (width >= WIDTH_INDEX_COUNT) {
    146         return WIDTH_INDEX_COUNT - 1;
    147     }
    148     return width;
    149 }
    150 
    151 static UBool isCurrency(const MeasureUnit &unit) {
    152     return (uprv_strcmp(unit.getType(), "currency") == 0);
    153 }
    154 
    155 static UBool getString(
    156         const UResourceBundle *resource,
    157         UnicodeString &result,
    158         UErrorCode &status) {
    159     int32_t len = 0;
    160     const UChar *resStr = ures_getString(resource, &len, &status);
    161     if (U_FAILURE(status)) {
    162         return FALSE;
    163     }
    164     result.setTo(TRUE, resStr, len);
    165     return TRUE;
    166 }
    167 
    168 
    169 static UBool loadMeasureUnitData(
    170         const UResourceBundle *resource,
    171         MeasureFormatCacheData &cacheData,
    172         UErrorCode &status) {
    173     if (U_FAILURE(status)) {
    174         return FALSE;
    175     }
    176     static const char *widthPath[] = {"units", "unitsShort", "unitsNarrow"};
    177     MeasureUnit *units = NULL;
    178     int32_t unitCount = MeasureUnit::getAvailable(units, 0, status);
    179     while (status == U_BUFFER_OVERFLOW_ERROR) {
    180         status = U_ZERO_ERROR;
    181         delete [] units;
    182         units = new MeasureUnit[unitCount];
    183         if (units == NULL) {
    184             status = U_MEMORY_ALLOCATION_ERROR;
    185             return FALSE;
    186         }
    187         unitCount = MeasureUnit::getAvailable(units, unitCount, status);
    188     }
    189     for (int32_t currentWidth = 0; currentWidth < WIDTH_INDEX_COUNT; ++currentWidth) {
    190         // Be sure status is clear since next resource bundle lookup may fail.
    191         if (U_FAILURE(status)) {
    192             delete [] units;
    193             return FALSE;
    194         }
    195         LocalUResourceBundlePointer widthBundle(
    196                 ures_getByKeyWithFallback(
    197                         resource, widthPath[currentWidth], NULL, &status));
    198         // We may not have data for all widths in all locales.
    199         if (status == U_MISSING_RESOURCE_ERROR) {
    200             status = U_ZERO_ERROR;
    201             continue;
    202         }
    203         for (int32_t currentUnit = 0; currentUnit < unitCount; ++currentUnit) {
    204             // Be sure status is clear next lookup may fail.
    205             if (U_FAILURE(status)) {
    206                 delete [] units;
    207                 return FALSE;
    208             }
    209             if (isCurrency(units[currentUnit])) {
    210                 continue;
    211             }
    212             CharString pathBuffer;
    213             pathBuffer.append(units[currentUnit].getType(), status)
    214                     .append("/", status)
    215                     .append(units[currentUnit].getSubtype(), status);
    216             LocalUResourceBundlePointer unitBundle(
    217                     ures_getByKeyWithFallback(
    218                             widthBundle.getAlias(),
    219                             pathBuffer.data(),
    220                             NULL,
    221                             &status));
    222             // We may not have data for all units in all widths
    223             if (status == U_MISSING_RESOURCE_ERROR) {
    224                 status = U_ZERO_ERROR;
    225                 continue;
    226             }
    227             // We must have the unit bundle to proceed
    228             if (U_FAILURE(status)) {
    229                 delete [] units;
    230                 return FALSE;
    231             }
    232             int32_t size = ures_getSize(unitBundle.getAlias());
    233             for (int32_t plIndex = 0; plIndex < size; ++plIndex) {
    234                 LocalUResourceBundlePointer pluralBundle(
    235                         ures_getByIndex(
    236                                 unitBundle.getAlias(), plIndex, NULL, &status));
    237                 if (U_FAILURE(status)) {
    238                     delete [] units;
    239                     return FALSE;
    240                 }
    241                 UnicodeString rawPattern;
    242                 getString(pluralBundle.getAlias(), rawPattern, status);
    243                 cacheData.formatters[units[currentUnit].getIndex()][currentWidth].add(
    244                         ures_getKey(pluralBundle.getAlias()),
    245                         rawPattern,
    246                         status);
    247             }
    248         }
    249     }
    250     delete [] units;
    251     return U_SUCCESS(status);
    252 }
    253 
    254 static UnicodeString loadNumericDateFormatterPattern(
    255         const UResourceBundle *resource,
    256         const char *pattern,
    257         UErrorCode &status) {
    258     UnicodeString result;
    259     if (U_FAILURE(status)) {
    260         return result;
    261     }
    262     CharString chs;
    263     chs.append("durationUnits", status)
    264             .append("/", status).append(pattern, status);
    265     LocalUResourceBundlePointer patternBundle(
    266             ures_getByKeyWithFallback(
    267                 resource,
    268                 chs.data(),
    269                 NULL,
    270                 &status));
    271     if (U_FAILURE(status)) {
    272         return result;
    273     }
    274     getString(patternBundle.getAlias(), result, status);
    275     // Replace 'h' with 'H'
    276     int32_t len = result.length();
    277     UChar *buffer = result.getBuffer(len);
    278     for (int32_t i = 0; i < len; ++i) {
    279         if (buffer[i] == 0x68) { // 'h'
    280             buffer[i] = 0x48; // 'H'
    281         }
    282     }
    283     result.releaseBuffer(len);
    284     return result;
    285 }
    286 
    287 static NumericDateFormatters *loadNumericDateFormatters(
    288         const UResourceBundle *resource,
    289         UErrorCode &status) {
    290     if (U_FAILURE(status)) {
    291         return NULL;
    292     }
    293     NumericDateFormatters *result = new NumericDateFormatters(
    294         loadNumericDateFormatterPattern(resource, "hm", status),
    295         loadNumericDateFormatterPattern(resource, "ms", status),
    296         loadNumericDateFormatterPattern(resource, "hms", status),
    297         status);
    298     if (U_FAILURE(status)) {
    299         delete result;
    300         return NULL;
    301     }
    302     return result;
    303 }
    304 
    305 // Creates the MeasureFormatCacheData for a particular locale
    306 static SharedObject *U_CALLCONV createData(
    307         const char *localeId, UErrorCode &status) {
    308     LocalUResourceBundlePointer topLevel(ures_open(NULL, localeId, &status));
    309     static UNumberFormatStyle currencyStyles[] = {
    310             UNUM_CURRENCY_PLURAL, UNUM_CURRENCY_ISO, UNUM_CURRENCY};
    311     if (U_FAILURE(status)) {
    312         return NULL;
    313     }
    314     LocalPointer<MeasureFormatCacheData> result(new MeasureFormatCacheData());
    315     if (result.isNull()) {
    316         status = U_MEMORY_ALLOCATION_ERROR;
    317         return NULL;
    318     }
    319     if (!loadMeasureUnitData(
    320             topLevel.getAlias(),
    321             *result,
    322             status)) {
    323         return NULL;
    324     }
    325     result->adoptNumericDateFormatters(loadNumericDateFormatters(
    326             topLevel.getAlias(), status));
    327     if (U_FAILURE(status)) {
    328         return NULL;
    329     }
    330 
    331     for (int32_t i = 0; i < WIDTH_INDEX_COUNT; ++i) {
    332         result->adoptCurrencyFormat(i, NumberFormat::createInstance(
    333                 localeId, currencyStyles[i], status));
    334         if (U_FAILURE(status)) {
    335             return NULL;
    336         }
    337     }
    338     NumberFormat *inf = NumberFormat::createInstance(
    339             localeId, UNUM_DECIMAL, status);
    340     if (U_FAILURE(status)) {
    341         return NULL;
    342     }
    343     inf->setMaximumFractionDigits(0);
    344     DecimalFormat *decfmt = dynamic_cast<DecimalFormat *>(inf);
    345     if (decfmt != NULL) {
    346         decfmt->setRoundingMode(DecimalFormat::kRoundDown);
    347     }
    348     result->adoptIntegerFormat(inf);
    349     return result.orphan();
    350 }
    351 
    352 static void U_CALLCONV cacheInit(UErrorCode &status) {
    353     U_ASSERT(gCache == NULL);
    354     U_ASSERT(MeasureUnit::getIndexCount() == MEAS_UNIT_COUNT);
    355     ucln_i18n_registerCleanup(UCLN_I18N_MEASFMT, measfmt_cleanup);
    356     gCache = new SimpleLRUCache(100, &createData, status);
    357     if (U_FAILURE(status)) {
    358         delete gCache;
    359         gCache = NULL;
    360     }
    361 }
    362 
    363 static UBool getFromCache(
    364         const char *locale,
    365         const MeasureFormatCacheData *&ptr,
    366         UErrorCode &status) {
    367     umtx_initOnce(gCacheInitOnce, &cacheInit, status);
    368     if (U_FAILURE(status)) {
    369         return FALSE;
    370     }
    371     Mutex lock(&gCacheMutex);
    372     gCache->get(locale, ptr, status);
    373     return U_SUCCESS(status);
    374 }
    375 
    376 static UBool isTimeUnit(const MeasureUnit &mu, const char *tu) {
    377     return uprv_strcmp(mu.getType(), "duration") == 0 &&
    378             uprv_strcmp(mu.getSubtype(), tu) == 0;
    379 }
    380 
    381 // Converts a composite measure into hours-minutes-seconds and stores at hms
    382 // array. [0] is hours; [1] is minutes; [2] is seconds. Returns a bit map of
    383 // units found: 1=hours, 2=minutes, 4=seconds. For example, if measures
    384 // contains hours-minutes, this function would return 3.
    385 //
    386 // If measures cannot be converted into hours, minutes, seconds or if amounts
    387 // are negative, or if hours, minutes, seconds are out of order, returns 0.
    388 static int32_t toHMS(
    389         const Measure *measures,
    390         int32_t measureCount,
    391         Formattable *hms,
    392         UErrorCode &status) {
    393     if (U_FAILURE(status)) {
    394         return 0;
    395     }
    396     int32_t result = 0;
    397     if (U_FAILURE(status)) {
    398         return 0;
    399     }
    400     // We use copy constructor to ensure that both sides of equality operator
    401     // are instances of MeasureUnit base class and not a subclass. Otherwise,
    402     // operator== will immediately return false.
    403     for (int32_t i = 0; i < measureCount; ++i) {
    404         if (isTimeUnit(measures[i].getUnit(), "hour")) {
    405             // hour must come first
    406             if (result >= 1) {
    407                 return 0;
    408             }
    409             hms[0] = measures[i].getNumber();
    410             if (hms[0].getDouble() < 0.0) {
    411                 return 0;
    412             }
    413             result |= 1;
    414         } else if (isTimeUnit(measures[i].getUnit(), "minute")) {
    415             // minute must come after hour
    416             if (result >= 2) {
    417                 return 0;
    418             }
    419             hms[1] = measures[i].getNumber();
    420             if (hms[1].getDouble() < 0.0) {
    421                 return 0;
    422             }
    423             result |= 2;
    424         } else if (isTimeUnit(measures[i].getUnit(), "second")) {
    425             // second must come after hour and minute
    426             if (result >= 4) {
    427                 return 0;
    428             }
    429             hms[2] = measures[i].getNumber();
    430             if (hms[2].getDouble() < 0.0) {
    431                 return 0;
    432             }
    433             result |= 4;
    434         } else {
    435             return 0;
    436         }
    437     }
    438     return result;
    439 }
    440 
    441 
    442 MeasureFormat::MeasureFormat(
    443         const Locale &locale, UMeasureFormatWidth w, UErrorCode &status)
    444         : cache(NULL),
    445           numberFormat(NULL),
    446           pluralRules(NULL),
    447           width(w),
    448           listFormatter(NULL) {
    449     initMeasureFormat(locale, w, NULL, status);
    450 }
    451 
    452 MeasureFormat::MeasureFormat(
    453         const Locale &locale,
    454         UMeasureFormatWidth w,
    455         NumberFormat *nfToAdopt,
    456         UErrorCode &status)
    457         : cache(NULL),
    458           numberFormat(NULL),
    459           pluralRules(NULL),
    460           width(w),
    461           listFormatter(NULL) {
    462     initMeasureFormat(locale, w, nfToAdopt, status);
    463 }
    464 
    465 MeasureFormat::MeasureFormat(const MeasureFormat &other) :
    466         Format(other),
    467         cache(other.cache),
    468         numberFormat(other.numberFormat),
    469         pluralRules(other.pluralRules),
    470         width(other.width),
    471         listFormatter(NULL) {
    472     cache->addRef();
    473     numberFormat->addRef();
    474     pluralRules->addRef();
    475     listFormatter = new ListFormatter(*other.listFormatter);
    476 }
    477 
    478 MeasureFormat &MeasureFormat::operator=(const MeasureFormat &other) {
    479     if (this == &other) {
    480         return *this;
    481     }
    482     Format::operator=(other);
    483     SharedObject::copyPtr(other.cache, cache);
    484     SharedObject::copyPtr(other.numberFormat, numberFormat);
    485     SharedObject::copyPtr(other.pluralRules, pluralRules);
    486     width = other.width;
    487     delete listFormatter;
    488     listFormatter = new ListFormatter(*other.listFormatter);
    489     return *this;
    490 }
    491 
    492 MeasureFormat::MeasureFormat() :
    493         cache(NULL),
    494         numberFormat(NULL),
    495         pluralRules(NULL),
    496         width(UMEASFMT_WIDTH_WIDE),
    497         listFormatter(NULL) {
    498 }
    499 
    500 MeasureFormat::~MeasureFormat() {
    501     if (cache != NULL) {
    502         cache->removeRef();
    503     }
    504     if (numberFormat != NULL) {
    505         numberFormat->removeRef();
    506     }
    507     if (pluralRules != NULL) {
    508         pluralRules->removeRef();
    509     }
    510     delete listFormatter;
    511 }
    512 
    513 UBool MeasureFormat::operator==(const Format &other) const {
    514     if (this == &other) { // Same object, equal
    515         return TRUE;
    516     }
    517     if (!Format::operator==(other)) {
    518         return FALSE;
    519     }
    520     const MeasureFormat &rhs = static_cast<const MeasureFormat &>(other);
    521 
    522     // Note: Since the ListFormatter depends only on Locale and width, we
    523     // don't have to check it here.
    524 
    525     // differing widths aren't equivalent
    526     if (width != rhs.width) {
    527         return FALSE;
    528     }
    529     // Width the same check locales.
    530     // We don't need to check locales if both objects have same cache.
    531     if (cache != rhs.cache) {
    532         UErrorCode status = U_ZERO_ERROR;
    533         const char *localeId = getLocaleID(status);
    534         const char *rhsLocaleId = rhs.getLocaleID(status);
    535         if (U_FAILURE(status)) {
    536             // On failure, assume not equal
    537             return FALSE;
    538         }
    539         if (uprv_strcmp(localeId, rhsLocaleId) != 0) {
    540             return FALSE;
    541         }
    542     }
    543     // Locales same, check NumberFormat if shared data differs.
    544     return (
    545             numberFormat == rhs.numberFormat ||
    546             **numberFormat == **rhs.numberFormat);
    547 }
    548 
    549 Format *MeasureFormat::clone() const {
    550     return new MeasureFormat(*this);
    551 }
    552 
    553 UnicodeString &MeasureFormat::format(
    554         const Formattable &obj,
    555         UnicodeString &appendTo,
    556         FieldPosition &pos,
    557         UErrorCode &status) const {
    558     if (U_FAILURE(status)) return appendTo;
    559     if (obj.getType() == Formattable::kObject) {
    560         const UObject* formatObj = obj.getObject();
    561         const Measure* amount = dynamic_cast<const Measure*>(formatObj);
    562         if (amount != NULL) {
    563             return formatMeasure(
    564                     *amount, **numberFormat, appendTo, pos, status);
    565         }
    566     }
    567     status = U_ILLEGAL_ARGUMENT_ERROR;
    568     return appendTo;
    569 }
    570 
    571 void MeasureFormat::parseObject(
    572         const UnicodeString & /*source*/,
    573         Formattable & /*result*/,
    574         ParsePosition& /*pos*/) const {
    575     return;
    576 }
    577 
    578 UnicodeString &MeasureFormat::formatMeasures(
    579         const Measure *measures,
    580         int32_t measureCount,
    581         UnicodeString &appendTo,
    582         FieldPosition &pos,
    583         UErrorCode &status) const {
    584     if (U_FAILURE(status)) {
    585         return appendTo;
    586     }
    587     if (measureCount == 0) {
    588         return appendTo;
    589     }
    590     if (measureCount == 1) {
    591         return formatMeasure(measures[0], **numberFormat, appendTo, pos, status);
    592     }
    593     if (width == UMEASFMT_WIDTH_NUMERIC) {
    594         Formattable hms[3];
    595         int32_t bitMap = toHMS(measures, measureCount, hms, status);
    596         if (bitMap > 0) {
    597             return formatNumeric(hms, bitMap, appendTo, status);
    598         }
    599     }
    600     if (pos.getField() != FieldPosition::DONT_CARE) {
    601         return formatMeasuresSlowTrack(
    602                 measures, measureCount, appendTo, pos, status);
    603     }
    604     UnicodeString *results = new UnicodeString[measureCount];
    605     if (results == NULL) {
    606         status = U_MEMORY_ALLOCATION_ERROR;
    607         return appendTo;
    608     }
    609     for (int32_t i = 0; i < measureCount; ++i) {
    610         const NumberFormat *nf = cache->getIntegerFormat();
    611         if (i == measureCount - 1) {
    612             nf = numberFormat->get();
    613         }
    614         formatMeasure(
    615                 measures[i],
    616                 *nf,
    617                 results[i],
    618                 pos,
    619                 status);
    620     }
    621     listFormatter->format(results, measureCount, appendTo, status);
    622     delete [] results;
    623     return appendTo;
    624 }
    625 
    626 void MeasureFormat::initMeasureFormat(
    627         const Locale &locale,
    628         UMeasureFormatWidth w,
    629         NumberFormat *nfToAdopt,
    630         UErrorCode &status) {
    631     static const char *listStyles[] = {"unit", "unit-short", "unit-narrow"};
    632     LocalPointer<NumberFormat> nf(nfToAdopt);
    633     if (U_FAILURE(status)) {
    634         return;
    635     }
    636     const char *name = locale.getName();
    637     setLocaleIDs(name, name);
    638 
    639     if (!getFromCache(name, cache, status)) {
    640         return;
    641     }
    642 
    643     SharedObject::copyPtr(
    644             PluralRules::createSharedInstance(
    645                     locale, UPLURAL_TYPE_CARDINAL, status),
    646             pluralRules);
    647     if (U_FAILURE(status)) {
    648         return;
    649     }
    650     pluralRules->removeRef();
    651     if (nf.isNull()) {
    652         SharedObject::copyPtr(
    653                 NumberFormat::createSharedInstance(
    654                         locale, UNUM_DECIMAL, status),
    655                 numberFormat);
    656         if (U_FAILURE(status)) {
    657             return;
    658         }
    659         numberFormat->removeRef();
    660     } else {
    661         adoptNumberFormat(nf.orphan(), status);
    662         if (U_FAILURE(status)) {
    663             return;
    664         }
    665     }
    666     width = w;
    667     delete listFormatter;
    668     listFormatter = ListFormatter::createInstance(
    669             locale,
    670             listStyles[widthToIndex(width)],
    671             status);
    672 }
    673 
    674 void MeasureFormat::adoptNumberFormat(
    675         NumberFormat *nfToAdopt, UErrorCode &status) {
    676     LocalPointer<NumberFormat> nf(nfToAdopt);
    677     if (U_FAILURE(status)) {
    678         return;
    679     }
    680     SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias());
    681     if (shared == NULL) {
    682         status = U_MEMORY_ALLOCATION_ERROR;
    683         return;
    684     }
    685     nf.orphan();
    686     SharedObject::copyPtr(shared, numberFormat);
    687 }
    688 
    689 UBool MeasureFormat::setMeasureFormatLocale(const Locale &locale, UErrorCode &status) {
    690     if (U_FAILURE(status) || locale == getLocale(status)) {
    691         return FALSE;
    692     }
    693     initMeasureFormat(locale, width, NULL, status);
    694     return U_SUCCESS(status);
    695 }
    696 
    697 const NumberFormat &MeasureFormat::getNumberFormat() const {
    698     return **numberFormat;
    699 }
    700 
    701 const PluralRules &MeasureFormat::getPluralRules() const {
    702     return **pluralRules;
    703 }
    704 
    705 Locale MeasureFormat::getLocale(UErrorCode &status) const {
    706     return Format::getLocale(ULOC_VALID_LOCALE, status);
    707 }
    708 
    709 const char *MeasureFormat::getLocaleID(UErrorCode &status) const {
    710     return Format::getLocaleID(ULOC_VALID_LOCALE, status);
    711 }
    712 
    713 UnicodeString &MeasureFormat::formatMeasure(
    714         const Measure &measure,
    715         const NumberFormat &nf,
    716         UnicodeString &appendTo,
    717         FieldPosition &pos,
    718         UErrorCode &status) const {
    719     if (U_FAILURE(status)) {
    720         return appendTo;
    721     }
    722     const Formattable& amtNumber = measure.getNumber();
    723     const MeasureUnit& amtUnit = measure.getUnit();
    724     if (isCurrency(amtUnit)) {
    725         UChar isoCode[4];
    726         u_charsToUChars(amtUnit.getSubtype(), isoCode, 4);
    727         return cache->getCurrencyFormat(widthToIndex(width))->format(
    728                 new CurrencyAmount(amtNumber, isoCode, status),
    729                 appendTo,
    730                 pos,
    731                 status);
    732     }
    733     const QuantityFormatter *quantityFormatter = getQuantityFormatter(
    734             amtUnit.getIndex(), widthToIndex(width), status);
    735     if (U_FAILURE(status)) {
    736         return appendTo;
    737     }
    738     return quantityFormatter->format(
    739             amtNumber,
    740             nf,
    741             **pluralRules,
    742             appendTo,
    743             pos,
    744             status);
    745 }
    746 
    747 // Formats hours-minutes-seconds as 5:37:23 or similar.
    748 UnicodeString &MeasureFormat::formatNumeric(
    749         const Formattable *hms,  // always length 3
    750         int32_t bitMap,   // 1=hourset, 2=minuteset, 4=secondset
    751         UnicodeString &appendTo,
    752         UErrorCode &status) const {
    753     if (U_FAILURE(status)) {
    754         return appendTo;
    755     }
    756     UDate millis =
    757         (UDate) (((uprv_trunc(hms[0].getDouble(status)) * 60.0
    758              + uprv_trunc(hms[1].getDouble(status))) * 60.0
    759                   + uprv_trunc(hms[2].getDouble(status))) * 1000.0);
    760     switch (bitMap) {
    761     case 5: // hs
    762     case 7: // hms
    763         return formatNumeric(
    764                 millis,
    765                 cache->getNumericDateFormatters()->hourMinuteSecond,
    766                 UDAT_SECOND_FIELD,
    767                 hms[2],
    768                 appendTo,
    769                 status);
    770         break;
    771     case 6: // ms
    772         return formatNumeric(
    773                 millis,
    774                 cache->getNumericDateFormatters()->minuteSecond,
    775                 UDAT_SECOND_FIELD,
    776                 hms[2],
    777                 appendTo,
    778                 status);
    779         break;
    780     case 3: // hm
    781         return formatNumeric(
    782                 millis,
    783                 cache->getNumericDateFormatters()->hourMinute,
    784                 UDAT_MINUTE_FIELD,
    785                 hms[1],
    786                 appendTo,
    787                 status);
    788         break;
    789     default:
    790         status = U_INTERNAL_PROGRAM_ERROR;
    791         return appendTo;
    792         break;
    793     }
    794     return appendTo;
    795 }
    796 
    797 static void appendRange(
    798         const UnicodeString &src,
    799         int32_t start,
    800         int32_t end,
    801         UnicodeString &dest) {
    802     dest.append(src, start, end - start);
    803 }
    804 
    805 static void appendRange(
    806         const UnicodeString &src,
    807         int32_t end,
    808         UnicodeString &dest) {
    809     dest.append(src, end, src.length() - end);
    810 }
    811 
    812 // Formats time like 5:37:23
    813 UnicodeString &MeasureFormat::formatNumeric(
    814         UDate date, // Time since epoch 1:30:00 would be 5400000
    815         const DateFormat &dateFmt, // h:mm, m:ss, or h:mm:ss
    816         UDateFormatField smallestField, // seconds in 5:37:23.5
    817         const Formattable &smallestAmount, // 23.5 for 5:37:23.5
    818         UnicodeString &appendTo,
    819         UErrorCode &status) const {
    820     if (U_FAILURE(status)) {
    821         return appendTo;
    822     }
    823     // Format the smallest amount with this object's NumberFormat
    824     UnicodeString smallestAmountFormatted;
    825 
    826     // We keep track of the integer part of smallest amount so that
    827     // we can replace it later so that we get '0:00:09.3' instead of
    828     // '0:00:9.3'
    829     FieldPosition intFieldPosition(UNUM_INTEGER_FIELD);
    830     (*numberFormat)->format(
    831             smallestAmount, smallestAmountFormatted, intFieldPosition, status);
    832     if (
    833             intFieldPosition.getBeginIndex() == 0 &&
    834             intFieldPosition.getEndIndex() == 0) {
    835         status = U_INTERNAL_PROGRAM_ERROR;
    836         return appendTo;
    837     }
    838 
    839     // Format time. draft becomes something like '5:30:45'
    840     FieldPosition smallestFieldPosition(smallestField);
    841     UnicodeString draft;
    842     dateFmt.format(date, draft, smallestFieldPosition, status);
    843 
    844     // If we find field for smallest amount replace it with the formatted
    845     // smallest amount from above taking care to replace the integer part
    846     // with what is in original time. For example, If smallest amount
    847     // is 9.35s and the formatted time is 0:00:09 then 9.35 becomes 09.35
    848     // and replacing yields 0:00:09.35
    849     if (smallestFieldPosition.getBeginIndex() != 0 ||
    850             smallestFieldPosition.getEndIndex() != 0) {
    851         appendRange(draft, 0, smallestFieldPosition.getBeginIndex(), appendTo);
    852         appendRange(
    853                 smallestAmountFormatted,
    854                 0,
    855                 intFieldPosition.getBeginIndex(),
    856                 appendTo);
    857         appendRange(
    858                 draft,
    859                 smallestFieldPosition.getBeginIndex(),
    860                 smallestFieldPosition.getEndIndex(),
    861                 appendTo);
    862         appendRange(
    863                 smallestAmountFormatted,
    864                 intFieldPosition.getEndIndex(),
    865                 appendTo);
    866         appendRange(
    867                 draft,
    868                 smallestFieldPosition.getEndIndex(),
    869                 appendTo);
    870     } else {
    871         appendTo.append(draft);
    872     }
    873     return appendTo;
    874 }
    875 
    876 const QuantityFormatter *MeasureFormat::getQuantityFormatter(
    877         int32_t index,
    878         int32_t widthIndex,
    879         UErrorCode &status) const {
    880     if (U_FAILURE(status)) {
    881         return NULL;
    882     }
    883     const QuantityFormatter *formatters =
    884             cache->formatters[index];
    885     if (formatters[widthIndex].isValid()) {
    886         return &formatters[widthIndex];
    887     }
    888     if (formatters[UMEASFMT_WIDTH_SHORT].isValid()) {
    889         return &formatters[UMEASFMT_WIDTH_SHORT];
    890     }
    891     if (formatters[UMEASFMT_WIDTH_WIDE].isValid()) {
    892         return &formatters[UMEASFMT_WIDTH_WIDE];
    893     }
    894     status = U_MISSING_RESOURCE_ERROR;
    895     return NULL;
    896 }
    897 
    898 UnicodeString &MeasureFormat::formatMeasuresSlowTrack(
    899         const Measure *measures,
    900         int32_t measureCount,
    901         UnicodeString& appendTo,
    902         FieldPosition& pos,
    903         UErrorCode& status) const {
    904     if (U_FAILURE(status)) {
    905         return appendTo;
    906     }
    907     FieldPosition dontCare(FieldPosition::DONT_CARE);
    908     FieldPosition fpos(pos.getField());
    909     UnicodeString *results = new UnicodeString[measureCount];
    910     int32_t fieldPositionFoundIndex = -1;
    911     for (int32_t i = 0; i < measureCount; ++i) {
    912         const NumberFormat *nf = cache->getIntegerFormat();
    913         if (i == measureCount - 1) {
    914             nf = numberFormat->get();
    915         }
    916         if (fieldPositionFoundIndex == -1) {
    917             formatMeasure(measures[i], *nf, results[i], fpos, status);
    918             if (U_FAILURE(status)) {
    919                 delete [] results;
    920                 return appendTo;
    921             }
    922             if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) {
    923                 fieldPositionFoundIndex = i;
    924             }
    925         } else {
    926             formatMeasure(measures[i], *nf, results[i], dontCare, status);
    927         }
    928     }
    929     int32_t offset;
    930     listFormatter->format(
    931             results,
    932             measureCount,
    933             appendTo,
    934             fieldPositionFoundIndex,
    935             offset,
    936             status);
    937     if (U_FAILURE(status)) {
    938         delete [] results;
    939         return appendTo;
    940     }
    941     if (offset != -1) {
    942         pos.setBeginIndex(fpos.getBeginIndex() + offset);
    943         pos.setEndIndex(fpos.getEndIndex() + offset);
    944     }
    945     delete [] results;
    946     return appendTo;
    947 }
    948 
    949 MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(const Locale& locale,
    950                                                    UErrorCode& ec) {
    951     CurrencyFormat* fmt = NULL;
    952     if (U_SUCCESS(ec)) {
    953         fmt = new CurrencyFormat(locale, ec);
    954         if (U_FAILURE(ec)) {
    955             delete fmt;
    956             fmt = NULL;
    957         }
    958     }
    959     return fmt;
    960 }
    961 
    962 MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(UErrorCode& ec) {
    963     if (U_FAILURE(ec)) {
    964         return NULL;
    965     }
    966     return MeasureFormat::createCurrencyFormat(Locale::getDefault(), ec);
    967 }
    968 
    969 U_NAMESPACE_END
    970 
    971 #endif /* #if !UCONFIG_NO_FORMATTING */
    972