1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
3 /*
4 **********************************************************************
5 * Copyright (c) 2004-2016, International Business Machines
6 * Corporation and others.  All Rights Reserved.
7 **********************************************************************
8 * Author: Alan Liu
9 * Created: April 20, 2004
10 * Since: ICU 3.0
11 **********************************************************************
12 */
13 #include "utypeinfo.h"  // for 'typeid' to work
14 #include "unicode/utypes.h"
15 
16 #if !UCONFIG_NO_FORMATTING
17 
18 #include "unicode/measfmt.h"
19 #include "unicode/numfmt.h"
20 #include "currfmt.h"
21 #include "unicode/localpointer.h"
22 #include "resource.h"
23 #include "unicode/simpleformatter.h"
24 #include "quantityformatter.h"
25 #include "unicode/plurrule.h"
26 #include "unicode/decimfmt.h"
27 #include "uresimp.h"
28 #include "unicode/ures.h"
29 #include "unicode/ustring.h"
30 #include "ureslocs.h"
31 #include "cstring.h"
32 #include "mutex.h"
33 #include "ucln_in.h"
34 #include "unicode/listformatter.h"
35 #include "charstr.h"
36 #include "unicode/putil.h"
37 #include "unicode/smpdtfmt.h"
38 #include "uassert.h"
39 
40 #include "sharednumberformat.h"
41 #include "sharedpluralrules.h"
42 #include "standardplural.h"
43 #include "unifiedcache.h"
44 
45 
46 U_NAMESPACE_BEGIN
47 
48 static constexpr int32_t PER_UNIT_INDEX = StandardPlural::COUNT;
49 static constexpr int32_t PATTERN_COUNT = PER_UNIT_INDEX + 1;
50 static constexpr int32_t MEAS_UNIT_COUNT = 138;  // see assertion in MeasureFormatCacheData constructor
51 static constexpr int32_t WIDTH_INDEX_COUNT = UMEASFMT_WIDTH_NARROW + 1;
52 
53 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MeasureFormat)
54 
55 // Used to format durations like 5:47 or 21:35:42.
56 class NumericDateFormatters : public UMemory {
57 public:
58     // Formats like H:mm
59     SimpleDateFormat hourMinute;
60 
61     // formats like M:ss
62     SimpleDateFormat minuteSecond;
63 
64     // formats like H:mm:ss
65     SimpleDateFormat hourMinuteSecond;
66 
67     // Constructor that takes the actual patterns for hour-minute,
68     // minute-second, and hour-minute-second respectively.
NumericDateFormatters(const UnicodeString & hm,const UnicodeString & ms,const UnicodeString & hms,UErrorCode & status)69     NumericDateFormatters(
70             const UnicodeString &hm,
71             const UnicodeString &ms,
72             const UnicodeString &hms,
73             UErrorCode &status) :
74             hourMinute(hm, status),
75             minuteSecond(ms, status),
76             hourMinuteSecond(hms, status) {
77         const TimeZone *gmt = TimeZone::getGMT();
78         hourMinute.setTimeZone(*gmt);
79         minuteSecond.setTimeZone(*gmt);
80         hourMinuteSecond.setTimeZone(*gmt);
81     }
82 private:
83     NumericDateFormatters(const NumericDateFormatters &other);
84     NumericDateFormatters &operator=(const NumericDateFormatters &other);
85 };
86 
getRegularWidth(UMeasureFormatWidth width)87 static UMeasureFormatWidth getRegularWidth(UMeasureFormatWidth width) {
88     if (width >= WIDTH_INDEX_COUNT) {
89         return UMEASFMT_WIDTH_NARROW;
90     }
91     return width;
92 }
93 
94 /**
95  * Instances contain all MeasureFormat specific data for a particular locale.
96  * This data is cached. It is never copied, but is shared via shared pointers.
97  *
98  * Note: We might change the cache data to have an array[WIDTH_INDEX_COUNT] of
99  * complete sets of unit & per patterns,
100  * to correspond to the resource data and its aliases.
101  *
102  * TODO: Maybe store more sparsely in general, with pointers rather than potentially-empty objects.
103  */
104 class MeasureFormatCacheData : public SharedObject {
105 public:
106 
107     /**
108      * Redirection data from root-bundle, top-level sideways aliases.
109      * - UMEASFMT_WIDTH_COUNT: initial value, just fall back to root
110      * - UMEASFMT_WIDTH_WIDE/SHORT/NARROW: sideways alias for missing data
111      */
112     UMeasureFormatWidth widthFallback[WIDTH_INDEX_COUNT];
113     /** Measure unit -> format width -> array of patterns ("{0} meters") (plurals + PER_UNIT_INDEX) */
114     SimpleFormatter* patterns[MEAS_UNIT_COUNT][WIDTH_INDEX_COUNT][PATTERN_COUNT];
115     const UChar* dnams[MEAS_UNIT_COUNT][WIDTH_INDEX_COUNT];
116     SimpleFormatter perFormatters[WIDTH_INDEX_COUNT];
117 
118     MeasureFormatCacheData();
119     virtual ~MeasureFormatCacheData();
120 
hasPerFormatter(int32_t width) const121     UBool hasPerFormatter(int32_t width) const {
122         // TODO: Create a more obvious way to test if the per-formatter has been set?
123         // Use pointers, check for NULL? Or add an isValid() method?
124         return perFormatters[width].getArgumentLimit() == 2;
125     }
126 
adoptCurrencyFormat(int32_t widthIndex,NumberFormat * nfToAdopt)127     void adoptCurrencyFormat(int32_t widthIndex, NumberFormat *nfToAdopt) {
128         delete currencyFormats[widthIndex];
129         currencyFormats[widthIndex] = nfToAdopt;
130     }
getCurrencyFormat(UMeasureFormatWidth width) const131     const NumberFormat *getCurrencyFormat(UMeasureFormatWidth width) const {
132         return currencyFormats[getRegularWidth(width)];
133     }
adoptIntegerFormat(NumberFormat * nfToAdopt)134     void adoptIntegerFormat(NumberFormat *nfToAdopt) {
135         delete integerFormat;
136         integerFormat = nfToAdopt;
137     }
getIntegerFormat() const138     const NumberFormat *getIntegerFormat() const {
139         return integerFormat;
140     }
adoptNumericDateFormatters(NumericDateFormatters * formattersToAdopt)141     void adoptNumericDateFormatters(NumericDateFormatters *formattersToAdopt) {
142         delete numericDateFormatters;
143         numericDateFormatters = formattersToAdopt;
144     }
getNumericDateFormatters() const145     const NumericDateFormatters *getNumericDateFormatters() const {
146         return numericDateFormatters;
147     }
148 
149 private:
150     NumberFormat* currencyFormats[WIDTH_INDEX_COUNT];
151     NumberFormat* integerFormat;
152     NumericDateFormatters* numericDateFormatters;
153 
154     MeasureFormatCacheData(const MeasureFormatCacheData &other);
155     MeasureFormatCacheData &operator=(const MeasureFormatCacheData &other);
156 };
157 
MeasureFormatCacheData()158 MeasureFormatCacheData::MeasureFormatCacheData()
159         : integerFormat(nullptr), numericDateFormatters(nullptr) {
160     // Please update MEAS_UNIT_COUNT if it gets out of sync with the true count!
161     U_ASSERT(MEAS_UNIT_COUNT == MeasureUnit::getIndexCount());
162 
163     for (int32_t i = 0; i < WIDTH_INDEX_COUNT; ++i) {
164         widthFallback[i] = UMEASFMT_WIDTH_COUNT;
165     }
166     memset(&patterns[0][0][0], 0, sizeof(patterns));
167     memset(&dnams[0][0], 0, sizeof(dnams));
168     memset(currencyFormats, 0, sizeof(currencyFormats));
169 }
170 
~MeasureFormatCacheData()171 MeasureFormatCacheData::~MeasureFormatCacheData() {
172     for (int32_t i = 0; i < UPRV_LENGTHOF(currencyFormats); ++i) {
173         delete currencyFormats[i];
174     }
175     for (int32_t i = 0; i < MEAS_UNIT_COUNT; ++i) {
176         for (int32_t j = 0; j < WIDTH_INDEX_COUNT; ++j) {
177             for (int32_t k = 0; k < PATTERN_COUNT; ++k) {
178                 delete patterns[i][j][k];
179             }
180         }
181     }
182     // Note: the contents of 'dnams' are pointers into the resource bundle
183     delete integerFormat;
184     delete numericDateFormatters;
185 }
186 
isCurrency(const MeasureUnit & unit)187 static UBool isCurrency(const MeasureUnit &unit) {
188     return (uprv_strcmp(unit.getType(), "currency") == 0);
189 }
190 
getString(const UResourceBundle * resource,UnicodeString & result,UErrorCode & status)191 static UBool getString(
192         const UResourceBundle *resource,
193         UnicodeString &result,
194         UErrorCode &status) {
195     int32_t len = 0;
196     const UChar *resStr = ures_getString(resource, &len, &status);
197     if (U_FAILURE(status)) {
198         return FALSE;
199     }
200     result.setTo(TRUE, resStr, len);
201     return TRUE;
202 }
203 
204 namespace {
205 
206 static const UChar g_LOCALE_units[] = {
207     0x2F, 0x4C, 0x4F, 0x43, 0x41, 0x4C, 0x45, 0x2F,
208     0x75, 0x6E, 0x69, 0x74, 0x73
209 };
210 static const UChar gShort[] = { 0x53, 0x68, 0x6F, 0x72, 0x74 };
211 static const UChar gNarrow[] = { 0x4E, 0x61, 0x72, 0x72, 0x6F, 0x77 };
212 
213 /**
214  * Sink for enumerating all of the measurement unit display names.
215  * Contains inner sink classes, each one corresponding to a type of resource table.
216  * The outer sink handles the top-level units, unitsNarrow, and unitsShort tables.
217  *
218  * More specific bundles (en_GB) are enumerated before their parents (en_001, en, root):
219  * Only store a value if it is still missing, that is, it has not been overridden.
220  *
221  * C++: Each inner sink class has a reference to the main outer sink.
222  * Java: Use non-static inner classes instead.
223  */
224 struct UnitDataSink : public ResourceSink {
225 
226     // Output data.
227     MeasureFormatCacheData &cacheData;
228 
229     // Path to current data.
230     UMeasureFormatWidth width;
231     const char *type;
232     int32_t unitIndex;
233 
UnitDataSink__anon882b43d50111::UnitDataSink234     UnitDataSink(MeasureFormatCacheData &outputData)
235             : cacheData(outputData),
236               width(UMEASFMT_WIDTH_COUNT), type(NULL), unitIndex(0) {}
237     ~UnitDataSink();
238 
setFormatterIfAbsent__anon882b43d50111::UnitDataSink239     void setFormatterIfAbsent(int32_t index, const ResourceValue &value,
240                                 int32_t minPlaceholders, UErrorCode &errorCode) {
241         U_ASSERT(unitIndex < MEAS_UNIT_COUNT);
242         U_ASSERT(width < WIDTH_INDEX_COUNT);
243         U_ASSERT(index < PATTERN_COUNT);
244         SimpleFormatter **patterns = &cacheData.patterns[unitIndex][width][0];
245         if (U_SUCCESS(errorCode) && patterns[index] == NULL) {
246             if (minPlaceholders >= 0) {
247                 patterns[index] = new SimpleFormatter(
248                         value.getUnicodeString(errorCode), minPlaceholders, 1, errorCode);
249             }
250             if (U_SUCCESS(errorCode) && patterns[index] == NULL) {
251                 errorCode = U_MEMORY_ALLOCATION_ERROR;
252             }
253         }
254     }
255 
setDnamIfAbsent__anon882b43d50111::UnitDataSink256     void setDnamIfAbsent(const ResourceValue &value, UErrorCode& errorCode) {
257         U_ASSERT(unitIndex < MEAS_UNIT_COUNT);
258         U_ASSERT(width < WIDTH_INDEX_COUNT);
259         if (cacheData.dnams[unitIndex][width] == NULL) {
260             int32_t length;
261             cacheData.dnams[unitIndex][width] = value.getString(length, errorCode);
262         }
263     }
264 
265     /**
266      * Consume a display pattern. For example,
267      * unitsShort/duration/hour contains other{"{0} hrs"}.
268      */
consumePattern__anon882b43d50111::UnitDataSink269     void consumePattern(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
270         if (U_FAILURE(errorCode)) { return; }
271         if (uprv_strcmp(key, "dnam") == 0) {
272             // The display name for the unit in the current width.
273             setDnamIfAbsent(value, errorCode);
274         } else if (uprv_strcmp(key, "per") == 0) {
275             // For example, "{0}/h".
276             setFormatterIfAbsent(PER_UNIT_INDEX, value, 1, errorCode);
277         } else {
278             // The key must be one of the plural form strings. For example:
279             // one{"{0} hr"}
280             // other{"{0} hrs"}
281             setFormatterIfAbsent(StandardPlural::indexFromString(key, errorCode), value, 0,
282                                     errorCode);
283         }
284     }
285 
286     /**
287      * Consume a table of per-unit tables. For example,
288      * unitsShort/duration contains tables for duration-unit subtypes day & hour.
289      */
consumeSubtypeTable__anon882b43d50111::UnitDataSink290     void consumeSubtypeTable(const char *key, ResourceValue &value, UErrorCode &errorCode) {
291         if (U_FAILURE(errorCode)) { return; }
292         unitIndex = MeasureUnit::internalGetIndexForTypeAndSubtype(type, key);
293         if (unitIndex < 0) {
294             // TODO: How to handle unexpected data?
295             // See http://bugs.icu-project.org/trac/ticket/12597
296             return;
297         }
298 
299         // We no longer handle units like "coordinate" here (which do not have plural variants)
300         if (value.getType() == URES_TABLE) {
301             // Units that have plural variants
302             ResourceTable patternTableTable = value.getTable(errorCode);
303             if (U_FAILURE(errorCode)) { return; }
304             for (int i = 0; patternTableTable.getKeyAndValue(i, key, value); ++i) {
305                 consumePattern(key, value, errorCode);
306             }
307         } else {
308             // TODO: How to handle unexpected data?
309             // See http://bugs.icu-project.org/trac/ticket/12597
310             return;
311         }
312     }
313 
314     /**
315      * Consume compound x-per-y display pattern. For example,
316      * unitsShort/compound/per may be "{0}/{1}".
317      */
consumeCompoundPattern__anon882b43d50111::UnitDataSink318     void consumeCompoundPattern(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
319         if (U_SUCCESS(errorCode) && uprv_strcmp(key, "per") == 0) {
320             cacheData.perFormatters[width].
321                     applyPatternMinMaxArguments(value.getUnicodeString(errorCode), 2, 2, errorCode);
322         }
323     }
324 
325     /**
326      * Consume a table of unit type tables. For example,
327      * unitsShort contains tables for area & duration.
328      * It also contains a table for the compound/per pattern.
329      */
consumeUnitTypesTable__anon882b43d50111::UnitDataSink330     void consumeUnitTypesTable(const char *key, ResourceValue &value, UErrorCode &errorCode) {
331         if (U_FAILURE(errorCode)) { return; }
332         if (uprv_strcmp(key, "currency") == 0) {
333             // Skip.
334         } else if (uprv_strcmp(key, "compound") == 0) {
335             if (!cacheData.hasPerFormatter(width)) {
336                 ResourceTable compoundTable = value.getTable(errorCode);
337                 if (U_FAILURE(errorCode)) { return; }
338                 for (int i = 0; compoundTable.getKeyAndValue(i, key, value); ++i) {
339                     consumeCompoundPattern(key, value, errorCode);
340                 }
341             }
342         } else if (uprv_strcmp(key, "coordinate") == 0) {
343             // special handling but we need to determine what that is
344         } else {
345             type = key;
346             ResourceTable subtypeTable = value.getTable(errorCode);
347             if (U_FAILURE(errorCode)) { return; }
348             for (int i = 0; subtypeTable.getKeyAndValue(i, key, value); ++i) {
349                 consumeSubtypeTable(key, value, errorCode);
350             }
351         }
352     }
353 
consumeAlias__anon882b43d50111::UnitDataSink354     void consumeAlias(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
355         // Handle aliases like
356         // units:alias{"/LOCALE/unitsShort"}
357         // which should only occur in the root bundle.
358         UMeasureFormatWidth sourceWidth = widthFromKey(key);
359         if (sourceWidth == UMEASFMT_WIDTH_COUNT) {
360             // Alias from something we don't care about.
361             return;
362         }
363         UMeasureFormatWidth targetWidth = widthFromAlias(value, errorCode);
364         if (targetWidth == UMEASFMT_WIDTH_COUNT) {
365             // We do not recognize what to fall back to.
366             errorCode = U_INVALID_FORMAT_ERROR;
367             return;
368         }
369         // Check that we do not fall back to another fallback.
370         if (cacheData.widthFallback[targetWidth] != UMEASFMT_WIDTH_COUNT) {
371             errorCode = U_INVALID_FORMAT_ERROR;
372             return;
373         }
374         cacheData.widthFallback[sourceWidth] = targetWidth;
375     }
376 
consumeTable__anon882b43d50111::UnitDataSink377     void consumeTable(const char *key, ResourceValue &value, UErrorCode &errorCode) {
378         if (U_SUCCESS(errorCode) && (width = widthFromKey(key)) != UMEASFMT_WIDTH_COUNT) {
379             ResourceTable unitTypesTable = value.getTable(errorCode);
380             if (U_FAILURE(errorCode)) { return; }
381             for (int i = 0; unitTypesTable.getKeyAndValue(i, key, value); ++i) {
382                 consumeUnitTypesTable(key, value, errorCode);
383             }
384         }
385     }
386 
widthFromKey__anon882b43d50111::UnitDataSink387     static UMeasureFormatWidth widthFromKey(const char *key) {
388         if (uprv_strncmp(key, "units", 5) == 0) {
389             key += 5;
390             if (*key == 0) {
391                 return UMEASFMT_WIDTH_WIDE;
392             } else if (uprv_strcmp(key, "Short") == 0) {
393                 return UMEASFMT_WIDTH_SHORT;
394             } else if (uprv_strcmp(key, "Narrow") == 0) {
395                 return UMEASFMT_WIDTH_NARROW;
396             }
397         }
398         return UMEASFMT_WIDTH_COUNT;
399     }
400 
widthFromAlias__anon882b43d50111::UnitDataSink401     static UMeasureFormatWidth widthFromAlias(const ResourceValue &value, UErrorCode &errorCode) {
402         int32_t length;
403         const UChar *s = value.getAliasString(length, errorCode);
404         // For example: "/LOCALE/unitsShort"
405         if (U_SUCCESS(errorCode) && length >= 13 && u_memcmp(s, g_LOCALE_units, 13) == 0) {
406             s += 13;
407             length -= 13;
408             if (*s == 0) {
409                 return UMEASFMT_WIDTH_WIDE;
410             } else if (u_strCompare(s, length, gShort, 5, FALSE) == 0) {
411                 return UMEASFMT_WIDTH_SHORT;
412             } else if (u_strCompare(s, length, gNarrow, 6, FALSE) == 0) {
413                 return UMEASFMT_WIDTH_NARROW;
414             }
415         }
416         return UMEASFMT_WIDTH_COUNT;
417     }
418 
put__anon882b43d50111::UnitDataSink419     virtual void put(const char *key, ResourceValue &value, UBool /*noFallback*/,
420             UErrorCode &errorCode) {
421         // Main entry point to sink
422         ResourceTable widthsTable = value.getTable(errorCode);
423         if (U_FAILURE(errorCode)) { return; }
424         for (int i = 0; widthsTable.getKeyAndValue(i, key, value); ++i) {
425             if (value.getType() == URES_ALIAS) {
426                 consumeAlias(key, value, errorCode);
427             } else {
428                 consumeTable(key, value, errorCode);
429             }
430         }
431     }
432 };
433 
434 // Virtual destructors must be defined out of line.
~UnitDataSink()435 UnitDataSink::~UnitDataSink() {}
436 
437 }  // namespace
438 
loadMeasureUnitData(const UResourceBundle * resource,MeasureFormatCacheData & cacheData,UErrorCode & status)439 static UBool loadMeasureUnitData(
440         const UResourceBundle *resource,
441         MeasureFormatCacheData &cacheData,
442         UErrorCode &status) {
443     UnitDataSink sink(cacheData);
444     ures_getAllItemsWithFallback(resource, "", sink, status);
445     return U_SUCCESS(status);
446 }
447 
loadNumericDateFormatterPattern(const UResourceBundle * resource,const char * pattern,UErrorCode & status)448 static UnicodeString loadNumericDateFormatterPattern(
449         const UResourceBundle *resource,
450         const char *pattern,
451         UErrorCode &status) {
452     UnicodeString result;
453     if (U_FAILURE(status)) {
454         return result;
455     }
456     CharString chs;
457     chs.append("durationUnits", status)
458             .append("/", status).append(pattern, status);
459     LocalUResourceBundlePointer patternBundle(
460             ures_getByKeyWithFallback(
461                 resource,
462                 chs.data(),
463                 NULL,
464                 &status));
465     if (U_FAILURE(status)) {
466         return result;
467     }
468     getString(patternBundle.getAlias(), result, status);
469     // Replace 'h' with 'H'
470     int32_t len = result.length();
471     UChar *buffer = result.getBuffer(len);
472     for (int32_t i = 0; i < len; ++i) {
473         if (buffer[i] == 0x68) { // 'h'
474             buffer[i] = 0x48; // 'H'
475         }
476     }
477     result.releaseBuffer(len);
478     return result;
479 }
480 
loadNumericDateFormatters(const UResourceBundle * resource,UErrorCode & status)481 static NumericDateFormatters *loadNumericDateFormatters(
482         const UResourceBundle *resource,
483         UErrorCode &status) {
484     if (U_FAILURE(status)) {
485         return NULL;
486     }
487     NumericDateFormatters *result = new NumericDateFormatters(
488         loadNumericDateFormatterPattern(resource, "hm", status),
489         loadNumericDateFormatterPattern(resource, "ms", status),
490         loadNumericDateFormatterPattern(resource, "hms", status),
491         status);
492     if (U_FAILURE(status)) {
493         delete result;
494         return NULL;
495     }
496     return result;
497 }
498 
499 template<> U_I18N_API
createObject(const void *,UErrorCode & status) const500 const MeasureFormatCacheData *LocaleCacheKey<MeasureFormatCacheData>::createObject(
501         const void * /*unused*/, UErrorCode &status) const {
502     const char *localeId = fLoc.getName();
503     LocalUResourceBundlePointer unitsBundle(ures_open(U_ICUDATA_UNIT, localeId, &status));
504     static UNumberFormatStyle currencyStyles[] = {
505             UNUM_CURRENCY_PLURAL, UNUM_CURRENCY_ISO, UNUM_CURRENCY};
506     LocalPointer<MeasureFormatCacheData> result(new MeasureFormatCacheData(), status);
507     if (U_FAILURE(status)) {
508         return NULL;
509     }
510     if (!loadMeasureUnitData(
511             unitsBundle.getAlias(),
512             *result,
513             status)) {
514         return NULL;
515     }
516     result->adoptNumericDateFormatters(loadNumericDateFormatters(
517             unitsBundle.getAlias(), status));
518     if (U_FAILURE(status)) {
519         return NULL;
520     }
521 
522     for (int32_t i = 0; i < WIDTH_INDEX_COUNT; ++i) {
523         // NumberFormat::createInstance can erase warning codes from status, so pass it
524         // a separate status instance
525         UErrorCode localStatus = U_ZERO_ERROR;
526         result->adoptCurrencyFormat(i, NumberFormat::createInstance(
527                 localeId, currencyStyles[i], localStatus));
528         if (localStatus != U_ZERO_ERROR) {
529             status = localStatus;
530         }
531         if (U_FAILURE(status)) {
532             return NULL;
533         }
534     }
535     NumberFormat *inf = NumberFormat::createInstance(
536             localeId, UNUM_DECIMAL, status);
537     if (U_FAILURE(status)) {
538         return NULL;
539     }
540     inf->setMaximumFractionDigits(0);
541     DecimalFormat *decfmt = dynamic_cast<DecimalFormat *>(inf);
542     if (decfmt != NULL) {
543         decfmt->setRoundingMode(DecimalFormat::kRoundDown);
544     }
545     result->adoptIntegerFormat(inf);
546     result->addRef();
547     return result.orphan();
548 }
549 
isTimeUnit(const MeasureUnit & mu,const char * tu)550 static UBool isTimeUnit(const MeasureUnit &mu, const char *tu) {
551     return uprv_strcmp(mu.getType(), "duration") == 0 &&
552             uprv_strcmp(mu.getSubtype(), tu) == 0;
553 }
554 
555 // Converts a composite measure into hours-minutes-seconds and stores at hms
556 // array. [0] is hours; [1] is minutes; [2] is seconds. Returns a bit map of
557 // units found: 1=hours, 2=minutes, 4=seconds. For example, if measures
558 // contains hours-minutes, this function would return 3.
559 //
560 // If measures cannot be converted into hours, minutes, seconds or if amounts
561 // are negative, or if hours, minutes, seconds are out of order, returns 0.
toHMS(const Measure * measures,int32_t measureCount,Formattable * hms,UErrorCode & status)562 static int32_t toHMS(
563         const Measure *measures,
564         int32_t measureCount,
565         Formattable *hms,
566         UErrorCode &status) {
567     if (U_FAILURE(status)) {
568         return 0;
569     }
570     int32_t result = 0;
571     if (U_FAILURE(status)) {
572         return 0;
573     }
574     // We use copy constructor to ensure that both sides of equality operator
575     // are instances of MeasureUnit base class and not a subclass. Otherwise,
576     // operator== will immediately return false.
577     for (int32_t i = 0; i < measureCount; ++i) {
578         if (isTimeUnit(measures[i].getUnit(), "hour")) {
579             // hour must come first
580             if (result >= 1) {
581                 return 0;
582             }
583             hms[0] = measures[i].getNumber();
584             if (hms[0].getDouble() < 0.0) {
585                 return 0;
586             }
587             result |= 1;
588         } else if (isTimeUnit(measures[i].getUnit(), "minute")) {
589             // minute must come after hour
590             if (result >= 2) {
591                 return 0;
592             }
593             hms[1] = measures[i].getNumber();
594             if (hms[1].getDouble() < 0.0) {
595                 return 0;
596             }
597             result |= 2;
598         } else if (isTimeUnit(measures[i].getUnit(), "second")) {
599             // second must come after hour and minute
600             if (result >= 4) {
601                 return 0;
602             }
603             hms[2] = measures[i].getNumber();
604             if (hms[2].getDouble() < 0.0) {
605                 return 0;
606             }
607             result |= 4;
608         } else {
609             return 0;
610         }
611     }
612     return result;
613 }
614 
615 
MeasureFormat(const Locale & locale,UMeasureFormatWidth w,UErrorCode & status)616 MeasureFormat::MeasureFormat(
617         const Locale &locale, UMeasureFormatWidth w, UErrorCode &status)
618         : cache(NULL),
619           numberFormat(NULL),
620           pluralRules(NULL),
621           width(w),
622           listFormatter(NULL) {
623     initMeasureFormat(locale, w, NULL, status);
624 }
625 
MeasureFormat(const Locale & locale,UMeasureFormatWidth w,NumberFormat * nfToAdopt,UErrorCode & status)626 MeasureFormat::MeasureFormat(
627         const Locale &locale,
628         UMeasureFormatWidth w,
629         NumberFormat *nfToAdopt,
630         UErrorCode &status)
631         : cache(NULL),
632           numberFormat(NULL),
633           pluralRules(NULL),
634           width(w),
635           listFormatter(NULL) {
636     initMeasureFormat(locale, w, nfToAdopt, status);
637 }
638 
MeasureFormat(const MeasureFormat & other)639 MeasureFormat::MeasureFormat(const MeasureFormat &other) :
640         Format(other),
641         cache(other.cache),
642         numberFormat(other.numberFormat),
643         pluralRules(other.pluralRules),
644         width(other.width),
645         listFormatter(NULL) {
646     cache->addRef();
647     numberFormat->addRef();
648     pluralRules->addRef();
649     if (other.listFormatter != NULL) {
650         listFormatter = new ListFormatter(*other.listFormatter);
651     }
652 }
653 
operator =(const MeasureFormat & other)654 MeasureFormat &MeasureFormat::operator=(const MeasureFormat &other) {
655     if (this == &other) {
656         return *this;
657     }
658     Format::operator=(other);
659     SharedObject::copyPtr(other.cache, cache);
660     SharedObject::copyPtr(other.numberFormat, numberFormat);
661     SharedObject::copyPtr(other.pluralRules, pluralRules);
662     width = other.width;
663     delete listFormatter;
664     if (other.listFormatter != NULL) {
665         listFormatter = new ListFormatter(*other.listFormatter);
666     } else {
667         listFormatter = NULL;
668     }
669     return *this;
670 }
671 
MeasureFormat()672 MeasureFormat::MeasureFormat() :
673         cache(NULL),
674         numberFormat(NULL),
675         pluralRules(NULL),
676         width(UMEASFMT_WIDTH_SHORT),
677         listFormatter(NULL) {
678 }
679 
~MeasureFormat()680 MeasureFormat::~MeasureFormat() {
681     if (cache != NULL) {
682         cache->removeRef();
683     }
684     if (numberFormat != NULL) {
685         numberFormat->removeRef();
686     }
687     if (pluralRules != NULL) {
688         pluralRules->removeRef();
689     }
690     delete listFormatter;
691 }
692 
operator ==(const Format & other) const693 UBool MeasureFormat::operator==(const Format &other) const {
694     if (this == &other) { // Same object, equal
695         return TRUE;
696     }
697     if (!Format::operator==(other)) {
698         return FALSE;
699     }
700     const MeasureFormat &rhs = static_cast<const MeasureFormat &>(other);
701 
702     // Note: Since the ListFormatter depends only on Locale and width, we
703     // don't have to check it here.
704 
705     // differing widths aren't equivalent
706     if (width != rhs.width) {
707         return FALSE;
708     }
709     // Width the same check locales.
710     // We don't need to check locales if both objects have same cache.
711     if (cache != rhs.cache) {
712         UErrorCode status = U_ZERO_ERROR;
713         const char *localeId = getLocaleID(status);
714         const char *rhsLocaleId = rhs.getLocaleID(status);
715         if (U_FAILURE(status)) {
716             // On failure, assume not equal
717             return FALSE;
718         }
719         if (uprv_strcmp(localeId, rhsLocaleId) != 0) {
720             return FALSE;
721         }
722     }
723     // Locales same, check NumberFormat if shared data differs.
724     return (
725             numberFormat == rhs.numberFormat ||
726             **numberFormat == **rhs.numberFormat);
727 }
728 
clone() const729 Format *MeasureFormat::clone() const {
730     return new MeasureFormat(*this);
731 }
732 
format(const Formattable & obj,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const733 UnicodeString &MeasureFormat::format(
734         const Formattable &obj,
735         UnicodeString &appendTo,
736         FieldPosition &pos,
737         UErrorCode &status) const {
738     if (U_FAILURE(status)) return appendTo;
739     if (obj.getType() == Formattable::kObject) {
740         const UObject* formatObj = obj.getObject();
741         const Measure* amount = dynamic_cast<const Measure*>(formatObj);
742         if (amount != NULL) {
743             return formatMeasure(
744                     *amount, **numberFormat, appendTo, pos, status);
745         }
746     }
747     status = U_ILLEGAL_ARGUMENT_ERROR;
748     return appendTo;
749 }
750 
parseObject(const UnicodeString &,Formattable &,ParsePosition &) const751 void MeasureFormat::parseObject(
752         const UnicodeString & /*source*/,
753         Formattable & /*result*/,
754         ParsePosition& /*pos*/) const {
755     return;
756 }
757 
formatMeasurePerUnit(const Measure & measure,const MeasureUnit & perUnit,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const758 UnicodeString &MeasureFormat::formatMeasurePerUnit(
759         const Measure &measure,
760         const MeasureUnit &perUnit,
761         UnicodeString &appendTo,
762         FieldPosition &pos,
763         UErrorCode &status) const {
764     if (U_FAILURE(status)) {
765         return appendTo;
766     }
767     MeasureUnit *resolvedUnit =
768             MeasureUnit::resolveUnitPerUnit(measure.getUnit(), perUnit);
769     if (resolvedUnit != NULL) {
770         Measure newMeasure(measure.getNumber(), resolvedUnit, status);
771         return formatMeasure(
772                 newMeasure, **numberFormat, appendTo, pos, status);
773     }
774     FieldPosition fpos(pos.getField());
775     UnicodeString result;
776     int32_t offset = withPerUnitAndAppend(
777             formatMeasure(
778                     measure, **numberFormat, result, fpos, status),
779             perUnit,
780             appendTo,
781             status);
782     if (U_FAILURE(status)) {
783         return appendTo;
784     }
785     if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) {
786         pos.setBeginIndex(fpos.getBeginIndex() + offset);
787         pos.setEndIndex(fpos.getEndIndex() + offset);
788     }
789     return appendTo;
790 }
791 
formatMeasures(const Measure * measures,int32_t measureCount,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const792 UnicodeString &MeasureFormat::formatMeasures(
793         const Measure *measures,
794         int32_t measureCount,
795         UnicodeString &appendTo,
796         FieldPosition &pos,
797         UErrorCode &status) const {
798     if (U_FAILURE(status)) {
799         return appendTo;
800     }
801     if (measureCount == 0) {
802         return appendTo;
803     }
804     if (measureCount == 1) {
805         return formatMeasure(measures[0], **numberFormat, appendTo, pos, status);
806     }
807     if (width == UMEASFMT_WIDTH_NUMERIC) {
808         Formattable hms[3];
809         int32_t bitMap = toHMS(measures, measureCount, hms, status);
810         if (bitMap > 0) {
811             return formatNumeric(hms, bitMap, appendTo, status);
812         }
813     }
814     if (pos.getField() != FieldPosition::DONT_CARE) {
815         return formatMeasuresSlowTrack(
816                 measures, measureCount, appendTo, pos, status);
817     }
818     UnicodeString *results = new UnicodeString[measureCount];
819     if (results == NULL) {
820         status = U_MEMORY_ALLOCATION_ERROR;
821         return appendTo;
822     }
823     for (int32_t i = 0; i < measureCount; ++i) {
824         const NumberFormat *nf = cache->getIntegerFormat();
825         if (i == measureCount - 1) {
826             nf = numberFormat->get();
827         }
828         formatMeasure(
829                 measures[i],
830                 *nf,
831                 results[i],
832                 pos,
833                 status);
834     }
835     listFormatter->format(results, measureCount, appendTo, status);
836     delete [] results;
837     return appendTo;
838 }
839 
getUnitDisplayName(const MeasureUnit & unit,UErrorCode &) const840 UnicodeString MeasureFormat::getUnitDisplayName(const MeasureUnit& unit, UErrorCode& /*status*/) const {
841     UMeasureFormatWidth width = getRegularWidth(this->width);
842     const UChar* const* styleToDnam = cache->dnams[unit.getIndex()];
843     const UChar* dnam = styleToDnam[width];
844     if (dnam == NULL) {
845         int32_t fallbackWidth = cache->widthFallback[width];
846         dnam = styleToDnam[fallbackWidth];
847     }
848 
849     UnicodeString result;
850     if (dnam == NULL) {
851         result.setToBogus();
852     } else {
853         result.setTo(dnam, -1);
854     }
855     return result;
856 }
857 
initMeasureFormat(const Locale & locale,UMeasureFormatWidth w,NumberFormat * nfToAdopt,UErrorCode & status)858 void MeasureFormat::initMeasureFormat(
859         const Locale &locale,
860         UMeasureFormatWidth w,
861         NumberFormat *nfToAdopt,
862         UErrorCode &status) {
863     static const char *listStyles[] = {"unit", "unit-short", "unit-narrow"};
864     LocalPointer<NumberFormat> nf(nfToAdopt);
865     if (U_FAILURE(status)) {
866         return;
867     }
868     const char *name = locale.getName();
869     setLocaleIDs(name, name);
870 
871     UnifiedCache::getByLocale(locale, cache, status);
872     if (U_FAILURE(status)) {
873         return;
874     }
875 
876     const SharedPluralRules *pr = PluralRules::createSharedInstance(
877             locale, UPLURAL_TYPE_CARDINAL, status);
878     if (U_FAILURE(status)) {
879         return;
880     }
881     SharedObject::copyPtr(pr, pluralRules);
882     pr->removeRef();
883     if (nf.isNull()) {
884         const SharedNumberFormat *shared = NumberFormat::createSharedInstance(
885                 locale, UNUM_DECIMAL, status);
886         if (U_FAILURE(status)) {
887             return;
888         }
889         SharedObject::copyPtr(shared, numberFormat);
890         shared->removeRef();
891     } else {
892         adoptNumberFormat(nf.orphan(), status);
893         if (U_FAILURE(status)) {
894             return;
895         }
896     }
897     width = w;
898     delete listFormatter;
899     listFormatter = ListFormatter::createInstance(
900             locale,
901             listStyles[getRegularWidth(width)],
902             status);
903 }
904 
adoptNumberFormat(NumberFormat * nfToAdopt,UErrorCode & status)905 void MeasureFormat::adoptNumberFormat(
906         NumberFormat *nfToAdopt, UErrorCode &status) {
907     LocalPointer<NumberFormat> nf(nfToAdopt);
908     if (U_FAILURE(status)) {
909         return;
910     }
911     SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias());
912     if (shared == NULL) {
913         status = U_MEMORY_ALLOCATION_ERROR;
914         return;
915     }
916     nf.orphan();
917     SharedObject::copyPtr(shared, numberFormat);
918 }
919 
setMeasureFormatLocale(const Locale & locale,UErrorCode & status)920 UBool MeasureFormat::setMeasureFormatLocale(const Locale &locale, UErrorCode &status) {
921     if (U_FAILURE(status) || locale == getLocale(status)) {
922         return FALSE;
923     }
924     initMeasureFormat(locale, width, NULL, status);
925     return U_SUCCESS(status);
926 }
927 
getNumberFormat() const928 const NumberFormat &MeasureFormat::getNumberFormat() const {
929     return **numberFormat;
930 }
931 
getPluralRules() const932 const PluralRules &MeasureFormat::getPluralRules() const {
933     return **pluralRules;
934 }
935 
getLocale(UErrorCode & status) const936 Locale MeasureFormat::getLocale(UErrorCode &status) const {
937     return Format::getLocale(ULOC_VALID_LOCALE, status);
938 }
939 
getLocaleID(UErrorCode & status) const940 const char *MeasureFormat::getLocaleID(UErrorCode &status) const {
941     return Format::getLocaleID(ULOC_VALID_LOCALE, status);
942 }
943 
formatMeasure(const Measure & measure,const NumberFormat & nf,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const944 UnicodeString &MeasureFormat::formatMeasure(
945         const Measure &measure,
946         const NumberFormat &nf,
947         UnicodeString &appendTo,
948         FieldPosition &pos,
949         UErrorCode &status) const {
950     if (U_FAILURE(status)) {
951         return appendTo;
952     }
953     const Formattable& amtNumber = measure.getNumber();
954     const MeasureUnit& amtUnit = measure.getUnit();
955     if (isCurrency(amtUnit)) {
956         UChar isoCode[4];
957         u_charsToUChars(amtUnit.getSubtype(), isoCode, 4);
958         return cache->getCurrencyFormat(width)->format(
959                 new CurrencyAmount(amtNumber, isoCode, status),
960                 appendTo,
961                 pos,
962                 status);
963     }
964     UnicodeString formattedNumber;
965     StandardPlural::Form pluralForm = QuantityFormatter::selectPlural(
966             amtNumber, nf, **pluralRules, formattedNumber, pos, status);
967     const SimpleFormatter *formatter = getPluralFormatter(amtUnit, width, pluralForm, status);
968     return QuantityFormatter::format(*formatter, formattedNumber, appendTo, pos, status);
969 }
970 
971 // Formats hours-minutes-seconds as 5:37:23 or similar.
formatNumeric(const Formattable * hms,int32_t bitMap,UnicodeString & appendTo,UErrorCode & status) const972 UnicodeString &MeasureFormat::formatNumeric(
973         const Formattable *hms,  // always length 3
974         int32_t bitMap,   // 1=hourset, 2=minuteset, 4=secondset
975         UnicodeString &appendTo,
976         UErrorCode &status) const {
977     if (U_FAILURE(status)) {
978         return appendTo;
979     }
980     UDate millis =
981         (UDate) (((uprv_trunc(hms[0].getDouble(status)) * 60.0
982              + uprv_trunc(hms[1].getDouble(status))) * 60.0
983                   + uprv_trunc(hms[2].getDouble(status))) * 1000.0);
984     switch (bitMap) {
985     case 5: // hs
986     case 7: // hms
987         return formatNumeric(
988                 millis,
989                 cache->getNumericDateFormatters()->hourMinuteSecond,
990                 UDAT_SECOND_FIELD,
991                 hms[2],
992                 appendTo,
993                 status);
994         break;
995     case 6: // ms
996         return formatNumeric(
997                 millis,
998                 cache->getNumericDateFormatters()->minuteSecond,
999                 UDAT_SECOND_FIELD,
1000                 hms[2],
1001                 appendTo,
1002                 status);
1003         break;
1004     case 3: // hm
1005         return formatNumeric(
1006                 millis,
1007                 cache->getNumericDateFormatters()->hourMinute,
1008                 UDAT_MINUTE_FIELD,
1009                 hms[1],
1010                 appendTo,
1011                 status);
1012         break;
1013     default:
1014         status = U_INTERNAL_PROGRAM_ERROR;
1015         return appendTo;
1016         break;
1017     }
1018     return appendTo;
1019 }
1020 
appendRange(const UnicodeString & src,int32_t start,int32_t end,UnicodeString & dest)1021 static void appendRange(
1022         const UnicodeString &src,
1023         int32_t start,
1024         int32_t end,
1025         UnicodeString &dest) {
1026     dest.append(src, start, end - start);
1027 }
1028 
appendRange(const UnicodeString & src,int32_t end,UnicodeString & dest)1029 static void appendRange(
1030         const UnicodeString &src,
1031         int32_t end,
1032         UnicodeString &dest) {
1033     dest.append(src, end, src.length() - end);
1034 }
1035 
1036 // Formats time like 5:37:23
formatNumeric(UDate date,const DateFormat & dateFmt,UDateFormatField smallestField,const Formattable & smallestAmount,UnicodeString & appendTo,UErrorCode & status) const1037 UnicodeString &MeasureFormat::formatNumeric(
1038         UDate date, // Time since epoch 1:30:00 would be 5400000
1039         const DateFormat &dateFmt, // h:mm, m:ss, or h:mm:ss
1040         UDateFormatField smallestField, // seconds in 5:37:23.5
1041         const Formattable &smallestAmount, // 23.5 for 5:37:23.5
1042         UnicodeString &appendTo,
1043         UErrorCode &status) const {
1044     if (U_FAILURE(status)) {
1045         return appendTo;
1046     }
1047     // Format the smallest amount with this object's NumberFormat
1048     UnicodeString smallestAmountFormatted;
1049 
1050     // We keep track of the integer part of smallest amount so that
1051     // we can replace it later so that we get '0:00:09.3' instead of
1052     // '0:00:9.3'
1053     FieldPosition intFieldPosition(UNUM_INTEGER_FIELD);
1054     (*numberFormat)->format(
1055             smallestAmount, smallestAmountFormatted, intFieldPosition, status);
1056     if (
1057             intFieldPosition.getBeginIndex() == 0 &&
1058             intFieldPosition.getEndIndex() == 0) {
1059         status = U_INTERNAL_PROGRAM_ERROR;
1060         return appendTo;
1061     }
1062 
1063     // Format time. draft becomes something like '5:30:45'
1064     FieldPosition smallestFieldPosition(smallestField);
1065     UnicodeString draft;
1066     dateFmt.format(date, draft, smallestFieldPosition, status);
1067 
1068     // If we find field for smallest amount replace it with the formatted
1069     // smallest amount from above taking care to replace the integer part
1070     // with what is in original time. For example, If smallest amount
1071     // is 9.35s and the formatted time is 0:00:09 then 9.35 becomes 09.35
1072     // and replacing yields 0:00:09.35
1073     if (smallestFieldPosition.getBeginIndex() != 0 ||
1074             smallestFieldPosition.getEndIndex() != 0) {
1075         appendRange(draft, 0, smallestFieldPosition.getBeginIndex(), appendTo);
1076         appendRange(
1077                 smallestAmountFormatted,
1078                 0,
1079                 intFieldPosition.getBeginIndex(),
1080                 appendTo);
1081         appendRange(
1082                 draft,
1083                 smallestFieldPosition.getBeginIndex(),
1084                 smallestFieldPosition.getEndIndex(),
1085                 appendTo);
1086         appendRange(
1087                 smallestAmountFormatted,
1088                 intFieldPosition.getEndIndex(),
1089                 appendTo);
1090         appendRange(
1091                 draft,
1092                 smallestFieldPosition.getEndIndex(),
1093                 appendTo);
1094     } else {
1095         appendTo.append(draft);
1096     }
1097     return appendTo;
1098 }
1099 
getFormatterOrNull(const MeasureUnit & unit,UMeasureFormatWidth width,int32_t index) const1100 const SimpleFormatter *MeasureFormat::getFormatterOrNull(
1101         const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index) const {
1102     width = getRegularWidth(width);
1103     SimpleFormatter *const (*unitPatterns)[PATTERN_COUNT] = &cache->patterns[unit.getIndex()][0];
1104     if (unitPatterns[width][index] != NULL) {
1105         return unitPatterns[width][index];
1106     }
1107     int32_t fallbackWidth = cache->widthFallback[width];
1108     if (fallbackWidth != UMEASFMT_WIDTH_COUNT && unitPatterns[fallbackWidth][index] != NULL) {
1109         return unitPatterns[fallbackWidth][index];
1110     }
1111     return NULL;
1112 }
1113 
getFormatter(const MeasureUnit & unit,UMeasureFormatWidth width,int32_t index,UErrorCode & errorCode) const1114 const SimpleFormatter *MeasureFormat::getFormatter(
1115         const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index,
1116         UErrorCode &errorCode) const {
1117     if (U_FAILURE(errorCode)) {
1118         return NULL;
1119     }
1120     const SimpleFormatter *pattern = getFormatterOrNull(unit, width, index);
1121     if (pattern == NULL) {
1122         errorCode = U_MISSING_RESOURCE_ERROR;
1123     }
1124     return pattern;
1125 }
1126 
getPluralFormatter(const MeasureUnit & unit,UMeasureFormatWidth width,int32_t index,UErrorCode & errorCode) const1127 const SimpleFormatter *MeasureFormat::getPluralFormatter(
1128         const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index,
1129         UErrorCode &errorCode) const {
1130     if (U_FAILURE(errorCode)) {
1131         return NULL;
1132     }
1133     if (index != StandardPlural::OTHER) {
1134         const SimpleFormatter *pattern = getFormatterOrNull(unit, width, index);
1135         if (pattern != NULL) {
1136             return pattern;
1137         }
1138     }
1139     return getFormatter(unit, width, StandardPlural::OTHER, errorCode);
1140 }
1141 
getPerFormatter(UMeasureFormatWidth width,UErrorCode & status) const1142 const SimpleFormatter *MeasureFormat::getPerFormatter(
1143         UMeasureFormatWidth width,
1144         UErrorCode &status) const {
1145     if (U_FAILURE(status)) {
1146         return NULL;
1147     }
1148     width = getRegularWidth(width);
1149     const SimpleFormatter * perFormatters = cache->perFormatters;
1150     if (perFormatters[width].getArgumentLimit() == 2) {
1151         return &perFormatters[width];
1152     }
1153     int32_t fallbackWidth = cache->widthFallback[width];
1154     if (fallbackWidth != UMEASFMT_WIDTH_COUNT &&
1155             perFormatters[fallbackWidth].getArgumentLimit() == 2) {
1156         return &perFormatters[fallbackWidth];
1157     }
1158     status = U_MISSING_RESOURCE_ERROR;
1159     return NULL;
1160 }
1161 
withPerUnitAndAppend(const UnicodeString & formatted,const MeasureUnit & perUnit,UnicodeString & appendTo,UErrorCode & status) const1162 int32_t MeasureFormat::withPerUnitAndAppend(
1163         const UnicodeString &formatted,
1164         const MeasureUnit &perUnit,
1165         UnicodeString &appendTo,
1166         UErrorCode &status) const {
1167     int32_t offset = -1;
1168     if (U_FAILURE(status)) {
1169         return offset;
1170     }
1171     const SimpleFormatter *perUnitFormatter = getFormatterOrNull(perUnit, width, PER_UNIT_INDEX);
1172     if (perUnitFormatter != NULL) {
1173         const UnicodeString *params[] = {&formatted};
1174         perUnitFormatter->formatAndAppend(
1175                 params,
1176                 UPRV_LENGTHOF(params),
1177                 appendTo,
1178                 &offset,
1179                 1,
1180                 status);
1181         return offset;
1182     }
1183     const SimpleFormatter *perFormatter = getPerFormatter(width, status);
1184     const SimpleFormatter *pattern =
1185             getPluralFormatter(perUnit, width, StandardPlural::ONE, status);
1186     if (U_FAILURE(status)) {
1187         return offset;
1188     }
1189     UnicodeString perUnitString = pattern->getTextWithNoArguments();
1190     perUnitString.trim();
1191     const UnicodeString *params[] = {&formatted, &perUnitString};
1192     perFormatter->formatAndAppend(
1193             params,
1194             UPRV_LENGTHOF(params),
1195             appendTo,
1196             &offset,
1197             1,
1198             status);
1199     return offset;
1200 }
1201 
formatMeasuresSlowTrack(const Measure * measures,int32_t measureCount,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const1202 UnicodeString &MeasureFormat::formatMeasuresSlowTrack(
1203         const Measure *measures,
1204         int32_t measureCount,
1205         UnicodeString& appendTo,
1206         FieldPosition& pos,
1207         UErrorCode& status) const {
1208     if (U_FAILURE(status)) {
1209         return appendTo;
1210     }
1211     FieldPosition dontCare(FieldPosition::DONT_CARE);
1212     FieldPosition fpos(pos.getField());
1213     UnicodeString *results = new UnicodeString[measureCount];
1214     int32_t fieldPositionFoundIndex = -1;
1215     for (int32_t i = 0; i < measureCount; ++i) {
1216         const NumberFormat *nf = cache->getIntegerFormat();
1217         if (i == measureCount - 1) {
1218             nf = numberFormat->get();
1219         }
1220         if (fieldPositionFoundIndex == -1) {
1221             formatMeasure(measures[i], *nf, results[i], fpos, status);
1222             if (U_FAILURE(status)) {
1223                 delete [] results;
1224                 return appendTo;
1225             }
1226             if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) {
1227                 fieldPositionFoundIndex = i;
1228             }
1229         } else {
1230             formatMeasure(measures[i], *nf, results[i], dontCare, status);
1231         }
1232     }
1233     int32_t offset;
1234     listFormatter->format(
1235             results,
1236             measureCount,
1237             appendTo,
1238             fieldPositionFoundIndex,
1239             offset,
1240             status);
1241     if (U_FAILURE(status)) {
1242         delete [] results;
1243         return appendTo;
1244     }
1245     if (offset != -1) {
1246         pos.setBeginIndex(fpos.getBeginIndex() + offset);
1247         pos.setEndIndex(fpos.getEndIndex() + offset);
1248     }
1249     delete [] results;
1250     return appendTo;
1251 }
1252 
createCurrencyFormat(const Locale & locale,UErrorCode & ec)1253 MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(const Locale& locale,
1254                                                    UErrorCode& ec) {
1255     CurrencyFormat* fmt = NULL;
1256     if (U_SUCCESS(ec)) {
1257         fmt = new CurrencyFormat(locale, ec);
1258         if (U_FAILURE(ec)) {
1259             delete fmt;
1260             fmt = NULL;
1261         }
1262     }
1263     return fmt;
1264 }
1265 
createCurrencyFormat(UErrorCode & ec)1266 MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(UErrorCode& ec) {
1267     if (U_FAILURE(ec)) {
1268         return NULL;
1269     }
1270     return MeasureFormat::createCurrencyFormat(Locale::getDefault(), ec);
1271 }
1272 
1273 U_NAMESPACE_END
1274 
1275 #endif /* #if !UCONFIG_NO_FORMATTING */
1276