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