• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 = 142;  // 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__anonfec2ae5e0111::UnitDataSink234     UnitDataSink(MeasureFormatCacheData &outputData)
235             : cacheData(outputData),
236               width(UMEASFMT_WIDTH_COUNT), type(NULL), unitIndex(0) {}
237     ~UnitDataSink();
238 
setFormatterIfAbsent__anonfec2ae5e0111::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__anonfec2ae5e0111::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__anonfec2ae5e0111::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__anonfec2ae5e0111::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__anonfec2ae5e0111::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__anonfec2ae5e0111::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__anonfec2ae5e0111::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__anonfec2ae5e0111::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__anonfec2ae5e0111::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__anonfec2ae5e0111::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__anonfec2ae5e0111::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           fWidth(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           fWidth(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         fWidth(other.fWidth),
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     fWidth = other.fWidth;
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         fWidth(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 (fWidth != rhs.fWidth) {
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     bool isResolved = false;
768     MeasureUnit resolvedUnit =
769         MeasureUnit::resolveUnitPerUnit(measure.getUnit(), perUnit, &isResolved);
770     if (isResolved) {
771         Measure newMeasure(measure.getNumber(), new MeasureUnit(resolvedUnit), status);
772         return formatMeasure(
773                 newMeasure, **numberFormat, appendTo, pos, status);
774     }
775     FieldPosition fpos(pos.getField());
776     UnicodeString result;
777     int32_t offset = withPerUnitAndAppend(
778             formatMeasure(
779                     measure, **numberFormat, result, fpos, status),
780             perUnit,
781             appendTo,
782             status);
783     if (U_FAILURE(status)) {
784         return appendTo;
785     }
786     if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) {
787         pos.setBeginIndex(fpos.getBeginIndex() + offset);
788         pos.setEndIndex(fpos.getEndIndex() + offset);
789     }
790     return appendTo;
791 }
792 
formatMeasures(const Measure * measures,int32_t measureCount,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const793 UnicodeString &MeasureFormat::formatMeasures(
794         const Measure *measures,
795         int32_t measureCount,
796         UnicodeString &appendTo,
797         FieldPosition &pos,
798         UErrorCode &status) const {
799     if (U_FAILURE(status)) {
800         return appendTo;
801     }
802     if (measureCount == 0) {
803         return appendTo;
804     }
805     if (measureCount == 1) {
806         return formatMeasure(measures[0], **numberFormat, appendTo, pos, status);
807     }
808     if (fWidth == UMEASFMT_WIDTH_NUMERIC) {
809         Formattable hms[3];
810         int32_t bitMap = toHMS(measures, measureCount, hms, status);
811         if (bitMap > 0) {
812             return formatNumeric(hms, bitMap, appendTo, status);
813         }
814     }
815     if (pos.getField() != FieldPosition::DONT_CARE) {
816         return formatMeasuresSlowTrack(
817                 measures, measureCount, appendTo, pos, status);
818     }
819     UnicodeString *results = new UnicodeString[measureCount];
820     if (results == NULL) {
821         status = U_MEMORY_ALLOCATION_ERROR;
822         return appendTo;
823     }
824     for (int32_t i = 0; i < measureCount; ++i) {
825         const NumberFormat *nf = cache->getIntegerFormat();
826         if (i == measureCount - 1) {
827             nf = numberFormat->get();
828         }
829         formatMeasure(
830                 measures[i],
831                 *nf,
832                 results[i],
833                 pos,
834                 status);
835     }
836     listFormatter->format(results, measureCount, appendTo, status);
837     delete [] results;
838     return appendTo;
839 }
840 
getUnitDisplayName(const MeasureUnit & unit,UErrorCode &) const841 UnicodeString MeasureFormat::getUnitDisplayName(const MeasureUnit& unit, UErrorCode& /*status*/) const {
842     UMeasureFormatWidth width = getRegularWidth(fWidth);
843     const UChar* const* styleToDnam = cache->dnams[unit.getIndex()];
844     const UChar* dnam = styleToDnam[width];
845     if (dnam == NULL) {
846         int32_t fallbackWidth = cache->widthFallback[width];
847         dnam = styleToDnam[fallbackWidth];
848     }
849 
850     UnicodeString result;
851     if (dnam == NULL) {
852         result.setToBogus();
853     } else {
854         result.setTo(dnam, -1);
855     }
856     return result;
857 }
858 
initMeasureFormat(const Locale & locale,UMeasureFormatWidth w,NumberFormat * nfToAdopt,UErrorCode & status)859 void MeasureFormat::initMeasureFormat(
860         const Locale &locale,
861         UMeasureFormatWidth w,
862         NumberFormat *nfToAdopt,
863         UErrorCode &status) {
864     static const char *listStyles[] = {"unit", "unit-short", "unit-narrow"};
865     LocalPointer<NumberFormat> nf(nfToAdopt);
866     if (U_FAILURE(status)) {
867         return;
868     }
869     const char *name = locale.getName();
870     setLocaleIDs(name, name);
871 
872     UnifiedCache::getByLocale(locale, cache, status);
873     if (U_FAILURE(status)) {
874         return;
875     }
876 
877     const SharedPluralRules *pr = PluralRules::createSharedInstance(
878             locale, UPLURAL_TYPE_CARDINAL, status);
879     if (U_FAILURE(status)) {
880         return;
881     }
882     SharedObject::copyPtr(pr, pluralRules);
883     pr->removeRef();
884     if (nf.isNull()) {
885         const SharedNumberFormat *shared = NumberFormat::createSharedInstance(
886                 locale, UNUM_DECIMAL, status);
887         if (U_FAILURE(status)) {
888             return;
889         }
890         SharedObject::copyPtr(shared, numberFormat);
891         shared->removeRef();
892     } else {
893         adoptNumberFormat(nf.orphan(), status);
894         if (U_FAILURE(status)) {
895             return;
896         }
897     }
898     fWidth = w;
899     delete listFormatter;
900     listFormatter = ListFormatter::createInstance(
901             locale,
902             listStyles[getRegularWidth(fWidth)],
903             status);
904 }
905 
adoptNumberFormat(NumberFormat * nfToAdopt,UErrorCode & status)906 void MeasureFormat::adoptNumberFormat(
907         NumberFormat *nfToAdopt, UErrorCode &status) {
908     LocalPointer<NumberFormat> nf(nfToAdopt);
909     if (U_FAILURE(status)) {
910         return;
911     }
912     SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias());
913     if (shared == NULL) {
914         status = U_MEMORY_ALLOCATION_ERROR;
915         return;
916     }
917     nf.orphan();
918     SharedObject::copyPtr(shared, numberFormat);
919 }
920 
setMeasureFormatLocale(const Locale & locale,UErrorCode & status)921 UBool MeasureFormat::setMeasureFormatLocale(const Locale &locale, UErrorCode &status) {
922     if (U_FAILURE(status) || locale == getLocale(status)) {
923         return FALSE;
924     }
925     initMeasureFormat(locale, fWidth, NULL, status);
926     return U_SUCCESS(status);
927 }
928 
getNumberFormat() const929 const NumberFormat &MeasureFormat::getNumberFormat() const {
930     return **numberFormat;
931 }
932 
getPluralRules() const933 const PluralRules &MeasureFormat::getPluralRules() const {
934     return **pluralRules;
935 }
936 
getLocale(UErrorCode & status) const937 Locale MeasureFormat::getLocale(UErrorCode &status) const {
938     return Format::getLocale(ULOC_VALID_LOCALE, status);
939 }
940 
getLocaleID(UErrorCode & status) const941 const char *MeasureFormat::getLocaleID(UErrorCode &status) const {
942     return Format::getLocaleID(ULOC_VALID_LOCALE, status);
943 }
944 
formatMeasure(const Measure & measure,const NumberFormat & nf,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const945 UnicodeString &MeasureFormat::formatMeasure(
946         const Measure &measure,
947         const NumberFormat &nf,
948         UnicodeString &appendTo,
949         FieldPosition &pos,
950         UErrorCode &status) const {
951     if (U_FAILURE(status)) {
952         return appendTo;
953     }
954     const Formattable& amtNumber = measure.getNumber();
955     const MeasureUnit& amtUnit = measure.getUnit();
956     if (isCurrency(amtUnit)) {
957         UChar isoCode[4];
958         u_charsToUChars(amtUnit.getSubtype(), isoCode, 4);
959         return cache->getCurrencyFormat(fWidth)->format(
960                 new CurrencyAmount(amtNumber, isoCode, status),
961                 appendTo,
962                 pos,
963                 status);
964     }
965     UnicodeString formattedNumber;
966     StandardPlural::Form pluralForm = QuantityFormatter::selectPlural(
967             amtNumber, nf, **pluralRules, formattedNumber, pos, status);
968     const SimpleFormatter *formatter = getPluralFormatter(amtUnit, fWidth, pluralForm, status);
969     return QuantityFormatter::format(*formatter, formattedNumber, appendTo, pos, status);
970 }
971 
972 // Formats hours-minutes-seconds as 5:37:23 or similar.
formatNumeric(const Formattable * hms,int32_t bitMap,UnicodeString & appendTo,UErrorCode & status) const973 UnicodeString &MeasureFormat::formatNumeric(
974         const Formattable *hms,  // always length 3
975         int32_t bitMap,   // 1=hourset, 2=minuteset, 4=secondset
976         UnicodeString &appendTo,
977         UErrorCode &status) const {
978     if (U_FAILURE(status)) {
979         return appendTo;
980     }
981     UDate millis =
982         (UDate) (((uprv_trunc(hms[0].getDouble(status)) * 60.0
983              + uprv_trunc(hms[1].getDouble(status))) * 60.0
984                   + uprv_trunc(hms[2].getDouble(status))) * 1000.0);
985     switch (bitMap) {
986     case 5: // hs
987     case 7: // hms
988         return formatNumeric(
989                 millis,
990                 cache->getNumericDateFormatters()->hourMinuteSecond,
991                 UDAT_SECOND_FIELD,
992                 hms[2],
993                 appendTo,
994                 status);
995         break;
996     case 6: // ms
997         return formatNumeric(
998                 millis,
999                 cache->getNumericDateFormatters()->minuteSecond,
1000                 UDAT_SECOND_FIELD,
1001                 hms[2],
1002                 appendTo,
1003                 status);
1004         break;
1005     case 3: // hm
1006         return formatNumeric(
1007                 millis,
1008                 cache->getNumericDateFormatters()->hourMinute,
1009                 UDAT_MINUTE_FIELD,
1010                 hms[1],
1011                 appendTo,
1012                 status);
1013         break;
1014     default:
1015         status = U_INTERNAL_PROGRAM_ERROR;
1016         return appendTo;
1017         break;
1018     }
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     // #13606: DateFormat is not thread-safe, but MeasureFormat advertises itself as thread-safe.
1065     FieldPosition smallestFieldPosition(smallestField);
1066     UnicodeString draft;
1067     static UMutex dateFmtMutex = U_MUTEX_INITIALIZER;
1068     umtx_lock(&dateFmtMutex);
1069     dateFmt.format(date, draft, smallestFieldPosition, status);
1070     umtx_unlock(&dateFmtMutex);
1071 
1072     // If we find field for smallest amount replace it with the formatted
1073     // smallest amount from above taking care to replace the integer part
1074     // with what is in original time. For example, If smallest amount
1075     // is 9.35s and the formatted time is 0:00:09 then 9.35 becomes 09.35
1076     // and replacing yields 0:00:09.35
1077     if (smallestFieldPosition.getBeginIndex() != 0 ||
1078             smallestFieldPosition.getEndIndex() != 0) {
1079         appendRange(draft, 0, smallestFieldPosition.getBeginIndex(), appendTo);
1080         appendRange(
1081                 smallestAmountFormatted,
1082                 0,
1083                 intFieldPosition.getBeginIndex(),
1084                 appendTo);
1085         appendRange(
1086                 draft,
1087                 smallestFieldPosition.getBeginIndex(),
1088                 smallestFieldPosition.getEndIndex(),
1089                 appendTo);
1090         appendRange(
1091                 smallestAmountFormatted,
1092                 intFieldPosition.getEndIndex(),
1093                 appendTo);
1094         appendRange(
1095                 draft,
1096                 smallestFieldPosition.getEndIndex(),
1097                 appendTo);
1098     } else {
1099         appendTo.append(draft);
1100     }
1101     return appendTo;
1102 }
1103 
getFormatterOrNull(const MeasureUnit & unit,UMeasureFormatWidth width,int32_t index) const1104 const SimpleFormatter *MeasureFormat::getFormatterOrNull(
1105         const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index) const {
1106     width = getRegularWidth(width);
1107     SimpleFormatter *const (*unitPatterns)[PATTERN_COUNT] = &cache->patterns[unit.getIndex()][0];
1108     if (unitPatterns[width][index] != NULL) {
1109         return unitPatterns[width][index];
1110     }
1111     int32_t fallbackWidth = cache->widthFallback[width];
1112     if (fallbackWidth != UMEASFMT_WIDTH_COUNT && unitPatterns[fallbackWidth][index] != NULL) {
1113         return unitPatterns[fallbackWidth][index];
1114     }
1115     return NULL;
1116 }
1117 
getFormatter(const MeasureUnit & unit,UMeasureFormatWidth width,int32_t index,UErrorCode & errorCode) const1118 const SimpleFormatter *MeasureFormat::getFormatter(
1119         const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index,
1120         UErrorCode &errorCode) const {
1121     if (U_FAILURE(errorCode)) {
1122         return NULL;
1123     }
1124     const SimpleFormatter *pattern = getFormatterOrNull(unit, width, index);
1125     if (pattern == NULL) {
1126         errorCode = U_MISSING_RESOURCE_ERROR;
1127     }
1128     return pattern;
1129 }
1130 
getPluralFormatter(const MeasureUnit & unit,UMeasureFormatWidth width,int32_t index,UErrorCode & errorCode) const1131 const SimpleFormatter *MeasureFormat::getPluralFormatter(
1132         const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index,
1133         UErrorCode &errorCode) const {
1134     if (U_FAILURE(errorCode)) {
1135         return NULL;
1136     }
1137     if (index != StandardPlural::OTHER) {
1138         const SimpleFormatter *pattern = getFormatterOrNull(unit, width, index);
1139         if (pattern != NULL) {
1140             return pattern;
1141         }
1142     }
1143     return getFormatter(unit, width, StandardPlural::OTHER, errorCode);
1144 }
1145 
getPerFormatter(UMeasureFormatWidth width,UErrorCode & status) const1146 const SimpleFormatter *MeasureFormat::getPerFormatter(
1147         UMeasureFormatWidth width,
1148         UErrorCode &status) const {
1149     if (U_FAILURE(status)) {
1150         return NULL;
1151     }
1152     width = getRegularWidth(width);
1153     const SimpleFormatter * perFormatters = cache->perFormatters;
1154     if (perFormatters[width].getArgumentLimit() == 2) {
1155         return &perFormatters[width];
1156     }
1157     int32_t fallbackWidth = cache->widthFallback[width];
1158     if (fallbackWidth != UMEASFMT_WIDTH_COUNT &&
1159             perFormatters[fallbackWidth].getArgumentLimit() == 2) {
1160         return &perFormatters[fallbackWidth];
1161     }
1162     status = U_MISSING_RESOURCE_ERROR;
1163     return NULL;
1164 }
1165 
withPerUnitAndAppend(const UnicodeString & formatted,const MeasureUnit & perUnit,UnicodeString & appendTo,UErrorCode & status) const1166 int32_t MeasureFormat::withPerUnitAndAppend(
1167         const UnicodeString &formatted,
1168         const MeasureUnit &perUnit,
1169         UnicodeString &appendTo,
1170         UErrorCode &status) const {
1171     int32_t offset = -1;
1172     if (U_FAILURE(status)) {
1173         return offset;
1174     }
1175     const SimpleFormatter *perUnitFormatter = getFormatterOrNull(perUnit, fWidth, PER_UNIT_INDEX);
1176     if (perUnitFormatter != NULL) {
1177         const UnicodeString *params[] = {&formatted};
1178         perUnitFormatter->formatAndAppend(
1179                 params,
1180                 UPRV_LENGTHOF(params),
1181                 appendTo,
1182                 &offset,
1183                 1,
1184                 status);
1185         return offset;
1186     }
1187     const SimpleFormatter *perFormatter = getPerFormatter(fWidth, status);
1188     const SimpleFormatter *pattern =
1189             getPluralFormatter(perUnit, fWidth, StandardPlural::ONE, status);
1190     if (U_FAILURE(status)) {
1191         return offset;
1192     }
1193     UnicodeString perUnitString = pattern->getTextWithNoArguments();
1194     perUnitString.trim();
1195     const UnicodeString *params[] = {&formatted, &perUnitString};
1196     perFormatter->formatAndAppend(
1197             params,
1198             UPRV_LENGTHOF(params),
1199             appendTo,
1200             &offset,
1201             1,
1202             status);
1203     return offset;
1204 }
1205 
formatMeasuresSlowTrack(const Measure * measures,int32_t measureCount,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const1206 UnicodeString &MeasureFormat::formatMeasuresSlowTrack(
1207         const Measure *measures,
1208         int32_t measureCount,
1209         UnicodeString& appendTo,
1210         FieldPosition& pos,
1211         UErrorCode& status) const {
1212     if (U_FAILURE(status)) {
1213         return appendTo;
1214     }
1215     FieldPosition dontCare(FieldPosition::DONT_CARE);
1216     FieldPosition fpos(pos.getField());
1217     UnicodeString *results = new UnicodeString[measureCount];
1218     int32_t fieldPositionFoundIndex = -1;
1219     for (int32_t i = 0; i < measureCount; ++i) {
1220         const NumberFormat *nf = cache->getIntegerFormat();
1221         if (i == measureCount - 1) {
1222             nf = numberFormat->get();
1223         }
1224         if (fieldPositionFoundIndex == -1) {
1225             formatMeasure(measures[i], *nf, results[i], fpos, status);
1226             if (U_FAILURE(status)) {
1227                 delete [] results;
1228                 return appendTo;
1229             }
1230             if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) {
1231                 fieldPositionFoundIndex = i;
1232             }
1233         } else {
1234             formatMeasure(measures[i], *nf, results[i], dontCare, status);
1235         }
1236     }
1237     int32_t offset;
1238     listFormatter->format(
1239             results,
1240             measureCount,
1241             appendTo,
1242             fieldPositionFoundIndex,
1243             offset,
1244             status);
1245     if (U_FAILURE(status)) {
1246         delete [] results;
1247         return appendTo;
1248     }
1249     if (offset != -1) {
1250         pos.setBeginIndex(fpos.getBeginIndex() + offset);
1251         pos.setEndIndex(fpos.getEndIndex() + offset);
1252     }
1253     delete [] results;
1254     return appendTo;
1255 }
1256 
createCurrencyFormat(const Locale & locale,UErrorCode & ec)1257 MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(const Locale& locale,
1258                                                    UErrorCode& ec) {
1259     CurrencyFormat* fmt = NULL;
1260     if (U_SUCCESS(ec)) {
1261         fmt = new CurrencyFormat(locale, ec);
1262         if (U_FAILURE(ec)) {
1263             delete fmt;
1264             fmt = NULL;
1265         }
1266     }
1267     return fmt;
1268 }
1269 
createCurrencyFormat(UErrorCode & ec)1270 MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(UErrorCode& ec) {
1271     if (U_FAILURE(ec)) {
1272         return NULL;
1273     }
1274     return MeasureFormat::createCurrencyFormat(Locale::getDefault(), ec);
1275 }
1276 
1277 U_NAMESPACE_END
1278 
1279 #endif /* #if !UCONFIG_NO_FORMATTING */
1280