• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
measfmt_cleanup()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.
NumericDateFormatters(const UnicodeString & hm,const UnicodeString & ms,const UnicodeString & hms,UErrorCode & status)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();
adoptCurrencyFormat(int32_t widthIndex,NumberFormat * nfToAdopt)98     void adoptCurrencyFormat(int32_t widthIndex, NumberFormat *nfToAdopt) {
99         delete currencyFormats[widthIndex];
100         currencyFormats[widthIndex] = nfToAdopt;
101     }
getCurrencyFormat(int32_t widthIndex) const102     const NumberFormat *getCurrencyFormat(int32_t widthIndex) const {
103         return currencyFormats[widthIndex];
104     }
adoptIntegerFormat(NumberFormat * nfToAdopt)105     void adoptIntegerFormat(NumberFormat *nfToAdopt) {
106         delete integerFormat;
107         integerFormat = nfToAdopt;
108     }
getIntegerFormat() const109     const NumberFormat *getIntegerFormat() const {
110         return integerFormat;
111     }
adoptNumericDateFormatters(NumericDateFormatters * formattersToAdopt)112     void adoptNumericDateFormatters(NumericDateFormatters *formattersToAdopt) {
113         delete numericDateFormatters;
114         numericDateFormatters = formattersToAdopt;
115     }
getNumericDateFormatters() const116     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 
MeasureFormatCacheData()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 
~MeasureFormatCacheData()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 
widthToIndex(UMeasureFormatWidth width)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 
isCurrency(const MeasureUnit & unit)151 static UBool isCurrency(const MeasureUnit &unit) {
152     return (uprv_strcmp(unit.getType(), "currency") == 0);
153 }
154 
getString(const UResourceBundle * resource,UnicodeString & result,UErrorCode & status)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 
loadMeasureUnitData(const UResourceBundle * resource,MeasureFormatCacheData & cacheData,UErrorCode & status)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 
loadNumericDateFormatterPattern(const UResourceBundle * resource,const char * pattern,UErrorCode & status)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 
loadNumericDateFormatters(const UResourceBundle * resource,UErrorCode & status)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
createData(const char * localeId,UErrorCode & status)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 
cacheInit(UErrorCode & status)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 
getFromCache(const char * locale,const MeasureFormatCacheData * & ptr,UErrorCode & status)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 
isTimeUnit(const MeasureUnit & mu,const char * tu)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.
toHMS(const Measure * measures,int32_t measureCount,Formattable * hms,UErrorCode & status)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 
MeasureFormat(const Locale & locale,UMeasureFormatWidth w,UErrorCode & status)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 
MeasureFormat(const Locale & locale,UMeasureFormatWidth w,NumberFormat * nfToAdopt,UErrorCode & status)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 
MeasureFormat(const MeasureFormat & other)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 
operator =(const MeasureFormat & other)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 
MeasureFormat()492 MeasureFormat::MeasureFormat() :
493         cache(NULL),
494         numberFormat(NULL),
495         pluralRules(NULL),
496         width(UMEASFMT_WIDTH_WIDE),
497         listFormatter(NULL) {
498 }
499 
~MeasureFormat()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 
operator ==(const Format & other) const513 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 
clone() const549 Format *MeasureFormat::clone() const {
550     return new MeasureFormat(*this);
551 }
552 
format(const Formattable & obj,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const553 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 
parseObject(const UnicodeString &,Formattable &,ParsePosition &) const571 void MeasureFormat::parseObject(
572         const UnicodeString & /*source*/,
573         Formattable & /*result*/,
574         ParsePosition& /*pos*/) const {
575     return;
576 }
577 
formatMeasures(const Measure * measures,int32_t measureCount,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const578 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 
initMeasureFormat(const Locale & locale,UMeasureFormatWidth w,NumberFormat * nfToAdopt,UErrorCode & status)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 
adoptNumberFormat(NumberFormat * nfToAdopt,UErrorCode & status)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 
setMeasureFormatLocale(const Locale & locale,UErrorCode & status)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 
getNumberFormat() const697 const NumberFormat &MeasureFormat::getNumberFormat() const {
698     return **numberFormat;
699 }
700 
getPluralRules() const701 const PluralRules &MeasureFormat::getPluralRules() const {
702     return **pluralRules;
703 }
704 
getLocale(UErrorCode & status) const705 Locale MeasureFormat::getLocale(UErrorCode &status) const {
706     return Format::getLocale(ULOC_VALID_LOCALE, status);
707 }
708 
getLocaleID(UErrorCode & status) const709 const char *MeasureFormat::getLocaleID(UErrorCode &status) const {
710     return Format::getLocaleID(ULOC_VALID_LOCALE, status);
711 }
712 
formatMeasure(const Measure & measure,const NumberFormat & nf,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const713 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.
formatNumeric(const Formattable * hms,int32_t bitMap,UnicodeString & appendTo,UErrorCode & status) const748 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 
appendRange(const UnicodeString & src,int32_t start,int32_t end,UnicodeString & dest)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 
appendRange(const UnicodeString & src,int32_t end,UnicodeString & dest)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
formatNumeric(UDate date,const DateFormat & dateFmt,UDateFormatField smallestField,const Formattable & smallestAmount,UnicodeString & appendTo,UErrorCode & status) const813 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 
getQuantityFormatter(int32_t index,int32_t widthIndex,UErrorCode & status) const876 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 
formatMeasuresSlowTrack(const Measure * measures,int32_t measureCount,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const898 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 
createCurrencyFormat(const Locale & locale,UErrorCode & ec)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 
createCurrencyFormat(UErrorCode & ec)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