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