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 "unicode/simpleformatter.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 134
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 SimpleFormatter *patterns[MEAS_UNIT_COUNT][WIDTH_INDEX_COUNT][PATTERN_COUNT];
111 SimpleFormatter 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].getArgumentLimit() == 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__anona245636e0111::UnitDataSink::UnitPatternSink222 UnitPatternSink(UnitDataSink &sink) : outer(sink) {}
223 ~UnitPatternSink();
224
setFormatterIfAbsent__anona245636e0111::UnitDataSink::UnitPatternSink225 void setFormatterIfAbsent(int32_t index, const ResourceValue &value,
226 int32_t minPlaceholders, UErrorCode &errorCode) {
227 SimpleFormatter **patterns =
228 &outer.cacheData.patterns[outer.unitIndex][outer.width][0];
229 if (U_SUCCESS(errorCode) && patterns[index] == NULL) {
230 patterns[index] = new SimpleFormatter(
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__anona245636e0111::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 setFormatterIfAbsent(MeasureFormatCacheData::PER_UNIT_INDEX, value, 1, errorCode);
245 } else {
246 // The key must be one of the plural form strings. For example:
247 // one{"{0} hr"}
248 // other{"{0} hrs"}
249 setFormatterIfAbsent(StandardPlural::indexFromString(key, errorCode), value, 0,
250 errorCode);
251 }
252 }
253 UnitDataSink &outer;
254 } patternSink;
255
256 /**
257 * Sink for a table of per-unit tables. For example,
258 * unitsShort/duration contains tables for duration-unit subtypes day & hour.
259 */
260 struct UnitSubtypeSink : public ResourceTableSink {
UnitSubtypeSink__anona245636e0111::UnitDataSink::UnitSubtypeSink261 UnitSubtypeSink(UnitDataSink &sink) : outer(sink) {}
262 ~UnitSubtypeSink();
getOrCreateTableSink__anona245636e0111::UnitDataSink::UnitSubtypeSink263 virtual ResourceTableSink *getOrCreateTableSink(
264 const char *key, int32_t /* initialSize */, UErrorCode &errorCode) {
265 if (U_FAILURE(errorCode)) { return NULL; }
266 outer.unitIndex = MeasureUnit::internalGetIndexForTypeAndSubtype(outer.type, key);
267 if (outer.unitIndex >= 0) {
268 return &outer.patternSink;
269 }
270 return NULL;
271 }
272 UnitDataSink &outer;
273 } subtypeSink;
274
275 /**
276 * Sink for compound x-per-y display pattern. For example,
277 * unitsShort/compound/per may be "{0}/{1}".
278 */
279 struct UnitCompoundSink : public ResourceTableSink {
UnitCompoundSink__anona245636e0111::UnitDataSink::UnitCompoundSink280 UnitCompoundSink(UnitDataSink &sink) : outer(sink) {}
281 ~UnitCompoundSink();
put__anona245636e0111::UnitDataSink::UnitCompoundSink282 virtual void put(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
283 if (U_SUCCESS(errorCode) && uprv_strcmp(key, "per") == 0) {
284 outer.cacheData.perFormatters[outer.width].
285 applyPatternMinMaxArguments(value.getUnicodeString(errorCode), 2, 2, errorCode);
286 }
287 }
288 UnitDataSink &outer;
289 } compoundSink;
290
291 /**
292 * Sink for a table of unit type tables. For example,
293 * unitsShort contains tables for area & duration.
294 * It also contains a table for the compound/per pattern.
295 */
296 struct UnitTypeSink : public ResourceTableSink {
UnitTypeSink__anona245636e0111::UnitDataSink::UnitTypeSink297 UnitTypeSink(UnitDataSink &sink) : outer(sink) {}
298 ~UnitTypeSink();
getOrCreateTableSink__anona245636e0111::UnitDataSink::UnitTypeSink299 virtual ResourceTableSink *getOrCreateTableSink(
300 const char *key, int32_t /* initialSize */, UErrorCode &errorCode) {
301 if (U_FAILURE(errorCode)) { return NULL; }
302 if (uprv_strcmp(key, "currency") == 0) {
303 // Skip.
304 } else if (uprv_strcmp(key, "compound") == 0) {
305 if (!outer.cacheData.hasPerFormatter(outer.width)) {
306 return &outer.compoundSink;
307 }
308 } else {
309 outer.type = key;
310 return &outer.subtypeSink;
311 }
312 return NULL;
313 }
314 UnitDataSink &outer;
315 } typeSink;
316
UnitDataSink__anona245636e0111::UnitDataSink317 UnitDataSink(MeasureFormatCacheData &outputData)
318 : patternSink(*this), subtypeSink(*this), compoundSink(*this), typeSink(*this),
319 cacheData(outputData),
320 width(UMEASFMT_WIDTH_COUNT), type(NULL), unitIndex(0) {}
321 ~UnitDataSink();
put__anona245636e0111::UnitDataSink322 virtual void put(const char *key, const ResourceValue &value, UErrorCode &errorCode) {
323 // Handle aliases like
324 // units:alias{"/LOCALE/unitsShort"}
325 // which should only occur in the root bundle.
326 if (U_FAILURE(errorCode) || value.getType() != URES_ALIAS) { return; }
327 UMeasureFormatWidth sourceWidth = widthFromKey(key);
328 if (sourceWidth == UMEASFMT_WIDTH_COUNT) {
329 // Alias from something we don't care about.
330 return;
331 }
332 UMeasureFormatWidth targetWidth = widthFromAlias(value, errorCode);
333 if (targetWidth == UMEASFMT_WIDTH_COUNT) {
334 // We do not recognize what to fall back to.
335 errorCode = U_INVALID_FORMAT_ERROR;
336 return;
337 }
338 // Check that we do not fall back to another fallback.
339 if (cacheData.widthFallback[targetWidth] != UMEASFMT_WIDTH_COUNT) {
340 errorCode = U_INVALID_FORMAT_ERROR;
341 return;
342 }
343 cacheData.widthFallback[sourceWidth] = targetWidth;
344 }
getOrCreateTableSink__anona245636e0111::UnitDataSink345 virtual ResourceTableSink *getOrCreateTableSink(
346 const char *key, int32_t /* initialSize */, UErrorCode &errorCode) {
347 if (U_SUCCESS(errorCode) && (width = widthFromKey(key)) != UMEASFMT_WIDTH_COUNT) {
348 return &typeSink;
349 }
350 return NULL;
351 }
352
widthFromKey__anona245636e0111::UnitDataSink353 static UMeasureFormatWidth widthFromKey(const char *key) {
354 if (uprv_strncmp(key, "units", 5) == 0) {
355 key += 5;
356 if (*key == 0) {
357 return UMEASFMT_WIDTH_WIDE;
358 } else if (uprv_strcmp(key, "Short") == 0) {
359 return UMEASFMT_WIDTH_SHORT;
360 } else if (uprv_strcmp(key, "Narrow") == 0) {
361 return UMEASFMT_WIDTH_NARROW;
362 }
363 }
364 return UMEASFMT_WIDTH_COUNT;
365 }
366
widthFromAlias__anona245636e0111::UnitDataSink367 static UMeasureFormatWidth widthFromAlias(const ResourceValue &value, UErrorCode &errorCode) {
368 int32_t length;
369 const UChar *s = value.getAliasString(length, errorCode);
370 // For example: "/LOCALE/unitsShort"
371 if (U_SUCCESS(errorCode) && length >= 13 && u_memcmp(s, g_LOCALE_units, 13) == 0) {
372 s += 13;
373 length -= 13;
374 if (*s == 0) {
375 return UMEASFMT_WIDTH_WIDE;
376 } else if (u_strCompare(s, length, gShort, 5, FALSE) == 0) {
377 return UMEASFMT_WIDTH_SHORT;
378 } else if (u_strCompare(s, length, gNarrow, 6, FALSE) == 0) {
379 return UMEASFMT_WIDTH_NARROW;
380 }
381 }
382 return UMEASFMT_WIDTH_COUNT;
383 }
384
385 // Output data.
386 MeasureFormatCacheData &cacheData;
387
388 // Path to current data.
389 UMeasureFormatWidth width;
390 const char *type;
391 int32_t unitIndex;
392 };
393
394 // Virtual destructors must be defined out of line.
~UnitPatternSink()395 UnitDataSink::UnitPatternSink::~UnitPatternSink() {}
~UnitSubtypeSink()396 UnitDataSink::UnitSubtypeSink::~UnitSubtypeSink() {}
~UnitCompoundSink()397 UnitDataSink::UnitCompoundSink::~UnitCompoundSink() {}
~UnitTypeSink()398 UnitDataSink::UnitTypeSink::~UnitTypeSink() {}
~UnitDataSink()399 UnitDataSink::~UnitDataSink() {}
400
401 } // namespace
402
loadMeasureUnitData(const UResourceBundle * resource,MeasureFormatCacheData & cacheData,UErrorCode & status)403 static UBool loadMeasureUnitData(
404 const UResourceBundle *resource,
405 MeasureFormatCacheData &cacheData,
406 UErrorCode &status) {
407 UnitDataSink sink(cacheData);
408 ures_getAllTableItemsWithFallback(resource, "", sink, status);
409 return U_SUCCESS(status);
410 }
411
loadNumericDateFormatterPattern(const UResourceBundle * resource,const char * pattern,UErrorCode & status)412 static UnicodeString loadNumericDateFormatterPattern(
413 const UResourceBundle *resource,
414 const char *pattern,
415 UErrorCode &status) {
416 UnicodeString result;
417 if (U_FAILURE(status)) {
418 return result;
419 }
420 CharString chs;
421 chs.append("durationUnits", status)
422 .append("/", status).append(pattern, status);
423 LocalUResourceBundlePointer patternBundle(
424 ures_getByKeyWithFallback(
425 resource,
426 chs.data(),
427 NULL,
428 &status));
429 if (U_FAILURE(status)) {
430 return result;
431 }
432 getString(patternBundle.getAlias(), result, status);
433 // Replace 'h' with 'H'
434 int32_t len = result.length();
435 UChar *buffer = result.getBuffer(len);
436 for (int32_t i = 0; i < len; ++i) {
437 if (buffer[i] == 0x68) { // 'h'
438 buffer[i] = 0x48; // 'H'
439 }
440 }
441 result.releaseBuffer(len);
442 return result;
443 }
444
loadNumericDateFormatters(const UResourceBundle * resource,UErrorCode & status)445 static NumericDateFormatters *loadNumericDateFormatters(
446 const UResourceBundle *resource,
447 UErrorCode &status) {
448 if (U_FAILURE(status)) {
449 return NULL;
450 }
451 NumericDateFormatters *result = new NumericDateFormatters(
452 loadNumericDateFormatterPattern(resource, "hm", status),
453 loadNumericDateFormatterPattern(resource, "ms", status),
454 loadNumericDateFormatterPattern(resource, "hms", status),
455 status);
456 if (U_FAILURE(status)) {
457 delete result;
458 return NULL;
459 }
460 return result;
461 }
462
463 template<> U_I18N_API
createObject(const void *,UErrorCode & status) const464 const MeasureFormatCacheData *LocaleCacheKey<MeasureFormatCacheData>::createObject(
465 const void * /*unused*/, UErrorCode &status) const {
466 const char *localeId = fLoc.getName();
467 LocalUResourceBundlePointer unitsBundle(ures_open(U_ICUDATA_UNIT, localeId, &status));
468 static UNumberFormatStyle currencyStyles[] = {
469 UNUM_CURRENCY_PLURAL, UNUM_CURRENCY_ISO, UNUM_CURRENCY};
470 LocalPointer<MeasureFormatCacheData> result(new MeasureFormatCacheData(), status);
471 if (U_FAILURE(status)) {
472 return NULL;
473 }
474 if (!loadMeasureUnitData(
475 unitsBundle.getAlias(),
476 *result,
477 status)) {
478 return NULL;
479 }
480 result->adoptNumericDateFormatters(loadNumericDateFormatters(
481 unitsBundle.getAlias(), status));
482 if (U_FAILURE(status)) {
483 return NULL;
484 }
485
486 for (int32_t i = 0; i < WIDTH_INDEX_COUNT; ++i) {
487 result->adoptCurrencyFormat(i, NumberFormat::createInstance(
488 localeId, currencyStyles[i], status));
489 if (U_FAILURE(status)) {
490 return NULL;
491 }
492 }
493 NumberFormat *inf = NumberFormat::createInstance(
494 localeId, UNUM_DECIMAL, status);
495 if (U_FAILURE(status)) {
496 return NULL;
497 }
498 inf->setMaximumFractionDigits(0);
499 DecimalFormat *decfmt = dynamic_cast<DecimalFormat *>(inf);
500 if (decfmt != NULL) {
501 decfmt->setRoundingMode(DecimalFormat::kRoundDown);
502 }
503 result->adoptIntegerFormat(inf);
504 result->addRef();
505 return result.orphan();
506 }
507
isTimeUnit(const MeasureUnit & mu,const char * tu)508 static UBool isTimeUnit(const MeasureUnit &mu, const char *tu) {
509 return uprv_strcmp(mu.getType(), "duration") == 0 &&
510 uprv_strcmp(mu.getSubtype(), tu) == 0;
511 }
512
513 // Converts a composite measure into hours-minutes-seconds and stores at hms
514 // array. [0] is hours; [1] is minutes; [2] is seconds. Returns a bit map of
515 // units found: 1=hours, 2=minutes, 4=seconds. For example, if measures
516 // contains hours-minutes, this function would return 3.
517 //
518 // If measures cannot be converted into hours, minutes, seconds or if amounts
519 // are negative, or if hours, minutes, seconds are out of order, returns 0.
toHMS(const Measure * measures,int32_t measureCount,Formattable * hms,UErrorCode & status)520 static int32_t toHMS(
521 const Measure *measures,
522 int32_t measureCount,
523 Formattable *hms,
524 UErrorCode &status) {
525 if (U_FAILURE(status)) {
526 return 0;
527 }
528 int32_t result = 0;
529 if (U_FAILURE(status)) {
530 return 0;
531 }
532 // We use copy constructor to ensure that both sides of equality operator
533 // are instances of MeasureUnit base class and not a subclass. Otherwise,
534 // operator== will immediately return false.
535 for (int32_t i = 0; i < measureCount; ++i) {
536 if (isTimeUnit(measures[i].getUnit(), "hour")) {
537 // hour must come first
538 if (result >= 1) {
539 return 0;
540 }
541 hms[0] = measures[i].getNumber();
542 if (hms[0].getDouble() < 0.0) {
543 return 0;
544 }
545 result |= 1;
546 } else if (isTimeUnit(measures[i].getUnit(), "minute")) {
547 // minute must come after hour
548 if (result >= 2) {
549 return 0;
550 }
551 hms[1] = measures[i].getNumber();
552 if (hms[1].getDouble() < 0.0) {
553 return 0;
554 }
555 result |= 2;
556 } else if (isTimeUnit(measures[i].getUnit(), "second")) {
557 // second must come after hour and minute
558 if (result >= 4) {
559 return 0;
560 }
561 hms[2] = measures[i].getNumber();
562 if (hms[2].getDouble() < 0.0) {
563 return 0;
564 }
565 result |= 4;
566 } else {
567 return 0;
568 }
569 }
570 return result;
571 }
572
573
MeasureFormat(const Locale & locale,UMeasureFormatWidth w,UErrorCode & status)574 MeasureFormat::MeasureFormat(
575 const Locale &locale, UMeasureFormatWidth w, UErrorCode &status)
576 : cache(NULL),
577 numberFormat(NULL),
578 pluralRules(NULL),
579 width(w),
580 listFormatter(NULL) {
581 initMeasureFormat(locale, w, NULL, status);
582 }
583
MeasureFormat(const Locale & locale,UMeasureFormatWidth w,NumberFormat * nfToAdopt,UErrorCode & status)584 MeasureFormat::MeasureFormat(
585 const Locale &locale,
586 UMeasureFormatWidth w,
587 NumberFormat *nfToAdopt,
588 UErrorCode &status)
589 : cache(NULL),
590 numberFormat(NULL),
591 pluralRules(NULL),
592 width(w),
593 listFormatter(NULL) {
594 initMeasureFormat(locale, w, nfToAdopt, status);
595 }
596
MeasureFormat(const MeasureFormat & other)597 MeasureFormat::MeasureFormat(const MeasureFormat &other) :
598 Format(other),
599 cache(other.cache),
600 numberFormat(other.numberFormat),
601 pluralRules(other.pluralRules),
602 width(other.width),
603 listFormatter(NULL) {
604 cache->addRef();
605 numberFormat->addRef();
606 pluralRules->addRef();
607 if (other.listFormatter != NULL) {
608 listFormatter = new ListFormatter(*other.listFormatter);
609 }
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 if (other.listFormatter != NULL) {
623 listFormatter = new ListFormatter(*other.listFormatter);
624 } else {
625 listFormatter = NULL;
626 }
627 return *this;
628 }
629
MeasureFormat()630 MeasureFormat::MeasureFormat() :
631 cache(NULL),
632 numberFormat(NULL),
633 pluralRules(NULL),
634 width(UMEASFMT_WIDTH_SHORT),
635 listFormatter(NULL) {
636 }
637
~MeasureFormat()638 MeasureFormat::~MeasureFormat() {
639 if (cache != NULL) {
640 cache->removeRef();
641 }
642 if (numberFormat != NULL) {
643 numberFormat->removeRef();
644 }
645 if (pluralRules != NULL) {
646 pluralRules->removeRef();
647 }
648 delete listFormatter;
649 }
650
operator ==(const Format & other) const651 UBool MeasureFormat::operator==(const Format &other) const {
652 if (this == &other) { // Same object, equal
653 return TRUE;
654 }
655 if (!Format::operator==(other)) {
656 return FALSE;
657 }
658 const MeasureFormat &rhs = static_cast<const MeasureFormat &>(other);
659
660 // Note: Since the ListFormatter depends only on Locale and width, we
661 // don't have to check it here.
662
663 // differing widths aren't equivalent
664 if (width != rhs.width) {
665 return FALSE;
666 }
667 // Width the same check locales.
668 // We don't need to check locales if both objects have same cache.
669 if (cache != rhs.cache) {
670 UErrorCode status = U_ZERO_ERROR;
671 const char *localeId = getLocaleID(status);
672 const char *rhsLocaleId = rhs.getLocaleID(status);
673 if (U_FAILURE(status)) {
674 // On failure, assume not equal
675 return FALSE;
676 }
677 if (uprv_strcmp(localeId, rhsLocaleId) != 0) {
678 return FALSE;
679 }
680 }
681 // Locales same, check NumberFormat if shared data differs.
682 return (
683 numberFormat == rhs.numberFormat ||
684 **numberFormat == **rhs.numberFormat);
685 }
686
clone() const687 Format *MeasureFormat::clone() const {
688 return new MeasureFormat(*this);
689 }
690
format(const Formattable & obj,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const691 UnicodeString &MeasureFormat::format(
692 const Formattable &obj,
693 UnicodeString &appendTo,
694 FieldPosition &pos,
695 UErrorCode &status) const {
696 if (U_FAILURE(status)) return appendTo;
697 if (obj.getType() == Formattable::kObject) {
698 const UObject* formatObj = obj.getObject();
699 const Measure* amount = dynamic_cast<const Measure*>(formatObj);
700 if (amount != NULL) {
701 return formatMeasure(
702 *amount, **numberFormat, appendTo, pos, status);
703 }
704 }
705 status = U_ILLEGAL_ARGUMENT_ERROR;
706 return appendTo;
707 }
708
parseObject(const UnicodeString &,Formattable &,ParsePosition &) const709 void MeasureFormat::parseObject(
710 const UnicodeString & /*source*/,
711 Formattable & /*result*/,
712 ParsePosition& /*pos*/) const {
713 return;
714 }
715
formatMeasurePerUnit(const Measure & measure,const MeasureUnit & perUnit,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const716 UnicodeString &MeasureFormat::formatMeasurePerUnit(
717 const Measure &measure,
718 const MeasureUnit &perUnit,
719 UnicodeString &appendTo,
720 FieldPosition &pos,
721 UErrorCode &status) const {
722 if (U_FAILURE(status)) {
723 return appendTo;
724 }
725 MeasureUnit *resolvedUnit =
726 MeasureUnit::resolveUnitPerUnit(measure.getUnit(), perUnit);
727 if (resolvedUnit != NULL) {
728 Measure newMeasure(measure.getNumber(), resolvedUnit, status);
729 return formatMeasure(
730 newMeasure, **numberFormat, appendTo, pos, status);
731 }
732 FieldPosition fpos(pos.getField());
733 UnicodeString result;
734 int32_t offset = withPerUnitAndAppend(
735 formatMeasure(
736 measure, **numberFormat, result, fpos, status),
737 perUnit,
738 appendTo,
739 status);
740 if (U_FAILURE(status)) {
741 return appendTo;
742 }
743 if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) {
744 pos.setBeginIndex(fpos.getBeginIndex() + offset);
745 pos.setEndIndex(fpos.getEndIndex() + offset);
746 }
747 return appendTo;
748 }
749
formatMeasures(const Measure * measures,int32_t measureCount,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const750 UnicodeString &MeasureFormat::formatMeasures(
751 const Measure *measures,
752 int32_t measureCount,
753 UnicodeString &appendTo,
754 FieldPosition &pos,
755 UErrorCode &status) const {
756 if (U_FAILURE(status)) {
757 return appendTo;
758 }
759 if (measureCount == 0) {
760 return appendTo;
761 }
762 if (measureCount == 1) {
763 return formatMeasure(measures[0], **numberFormat, appendTo, pos, status);
764 }
765 if (width == UMEASFMT_WIDTH_NUMERIC) {
766 Formattable hms[3];
767 int32_t bitMap = toHMS(measures, measureCount, hms, status);
768 if (bitMap > 0) {
769 return formatNumeric(hms, bitMap, appendTo, status);
770 }
771 }
772 if (pos.getField() != FieldPosition::DONT_CARE) {
773 return formatMeasuresSlowTrack(
774 measures, measureCount, appendTo, pos, status);
775 }
776 UnicodeString *results = new UnicodeString[measureCount];
777 if (results == NULL) {
778 status = U_MEMORY_ALLOCATION_ERROR;
779 return appendTo;
780 }
781 for (int32_t i = 0; i < measureCount; ++i) {
782 const NumberFormat *nf = cache->getIntegerFormat();
783 if (i == measureCount - 1) {
784 nf = numberFormat->get();
785 }
786 formatMeasure(
787 measures[i],
788 *nf,
789 results[i],
790 pos,
791 status);
792 }
793 listFormatter->format(results, measureCount, appendTo, status);
794 delete [] results;
795 return appendTo;
796 }
797
initMeasureFormat(const Locale & locale,UMeasureFormatWidth w,NumberFormat * nfToAdopt,UErrorCode & status)798 void MeasureFormat::initMeasureFormat(
799 const Locale &locale,
800 UMeasureFormatWidth w,
801 NumberFormat *nfToAdopt,
802 UErrorCode &status) {
803 static const char *listStyles[] = {"unit", "unit-short", "unit-narrow"};
804 LocalPointer<NumberFormat> nf(nfToAdopt);
805 if (U_FAILURE(status)) {
806 return;
807 }
808 const char *name = locale.getName();
809 setLocaleIDs(name, name);
810
811 UnifiedCache::getByLocale(locale, cache, status);
812 if (U_FAILURE(status)) {
813 return;
814 }
815
816 const SharedPluralRules *pr = PluralRules::createSharedInstance(
817 locale, UPLURAL_TYPE_CARDINAL, status);
818 if (U_FAILURE(status)) {
819 return;
820 }
821 SharedObject::copyPtr(pr, pluralRules);
822 pr->removeRef();
823 if (nf.isNull()) {
824 const SharedNumberFormat *shared = NumberFormat::createSharedInstance(
825 locale, UNUM_DECIMAL, status);
826 if (U_FAILURE(status)) {
827 return;
828 }
829 SharedObject::copyPtr(shared, numberFormat);
830 shared->removeRef();
831 } else {
832 adoptNumberFormat(nf.orphan(), status);
833 if (U_FAILURE(status)) {
834 return;
835 }
836 }
837 width = w;
838 delete listFormatter;
839 listFormatter = ListFormatter::createInstance(
840 locale,
841 listStyles[getRegularWidth(width)],
842 status);
843 }
844
adoptNumberFormat(NumberFormat * nfToAdopt,UErrorCode & status)845 void MeasureFormat::adoptNumberFormat(
846 NumberFormat *nfToAdopt, UErrorCode &status) {
847 LocalPointer<NumberFormat> nf(nfToAdopt);
848 if (U_FAILURE(status)) {
849 return;
850 }
851 SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias());
852 if (shared == NULL) {
853 status = U_MEMORY_ALLOCATION_ERROR;
854 return;
855 }
856 nf.orphan();
857 SharedObject::copyPtr(shared, numberFormat);
858 }
859
setMeasureFormatLocale(const Locale & locale,UErrorCode & status)860 UBool MeasureFormat::setMeasureFormatLocale(const Locale &locale, UErrorCode &status) {
861 if (U_FAILURE(status) || locale == getLocale(status)) {
862 return FALSE;
863 }
864 initMeasureFormat(locale, width, NULL, status);
865 return U_SUCCESS(status);
866 }
867
getNumberFormat() const868 const NumberFormat &MeasureFormat::getNumberFormat() const {
869 return **numberFormat;
870 }
871
getPluralRules() const872 const PluralRules &MeasureFormat::getPluralRules() const {
873 return **pluralRules;
874 }
875
getLocale(UErrorCode & status) const876 Locale MeasureFormat::getLocale(UErrorCode &status) const {
877 return Format::getLocale(ULOC_VALID_LOCALE, status);
878 }
879
getLocaleID(UErrorCode & status) const880 const char *MeasureFormat::getLocaleID(UErrorCode &status) const {
881 return Format::getLocaleID(ULOC_VALID_LOCALE, status);
882 }
883
formatMeasure(const Measure & measure,const NumberFormat & nf,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const884 UnicodeString &MeasureFormat::formatMeasure(
885 const Measure &measure,
886 const NumberFormat &nf,
887 UnicodeString &appendTo,
888 FieldPosition &pos,
889 UErrorCode &status) const {
890 if (U_FAILURE(status)) {
891 return appendTo;
892 }
893 const Formattable& amtNumber = measure.getNumber();
894 const MeasureUnit& amtUnit = measure.getUnit();
895 if (isCurrency(amtUnit)) {
896 UChar isoCode[4];
897 u_charsToUChars(amtUnit.getSubtype(), isoCode, 4);
898 return cache->getCurrencyFormat(width)->format(
899 new CurrencyAmount(amtNumber, isoCode, status),
900 appendTo,
901 pos,
902 status);
903 }
904 UnicodeString formattedNumber;
905 StandardPlural::Form pluralForm = QuantityFormatter::selectPlural(
906 amtNumber, nf, **pluralRules, formattedNumber, pos, status);
907 const SimpleFormatter *formatter = getPluralFormatter(amtUnit, width, pluralForm, status);
908 return QuantityFormatter::format(*formatter, formattedNumber, appendTo, pos, status);
909 }
910
911 // Formats hours-minutes-seconds as 5:37:23 or similar.
formatNumeric(const Formattable * hms,int32_t bitMap,UnicodeString & appendTo,UErrorCode & status) const912 UnicodeString &MeasureFormat::formatNumeric(
913 const Formattable *hms, // always length 3
914 int32_t bitMap, // 1=hourset, 2=minuteset, 4=secondset
915 UnicodeString &appendTo,
916 UErrorCode &status) const {
917 if (U_FAILURE(status)) {
918 return appendTo;
919 }
920 UDate millis =
921 (UDate) (((uprv_trunc(hms[0].getDouble(status)) * 60.0
922 + uprv_trunc(hms[1].getDouble(status))) * 60.0
923 + uprv_trunc(hms[2].getDouble(status))) * 1000.0);
924 switch (bitMap) {
925 case 5: // hs
926 case 7: // hms
927 return formatNumeric(
928 millis,
929 cache->getNumericDateFormatters()->hourMinuteSecond,
930 UDAT_SECOND_FIELD,
931 hms[2],
932 appendTo,
933 status);
934 break;
935 case 6: // ms
936 return formatNumeric(
937 millis,
938 cache->getNumericDateFormatters()->minuteSecond,
939 UDAT_SECOND_FIELD,
940 hms[2],
941 appendTo,
942 status);
943 break;
944 case 3: // hm
945 return formatNumeric(
946 millis,
947 cache->getNumericDateFormatters()->hourMinute,
948 UDAT_MINUTE_FIELD,
949 hms[1],
950 appendTo,
951 status);
952 break;
953 default:
954 status = U_INTERNAL_PROGRAM_ERROR;
955 return appendTo;
956 break;
957 }
958 return appendTo;
959 }
960
appendRange(const UnicodeString & src,int32_t start,int32_t end,UnicodeString & dest)961 static void appendRange(
962 const UnicodeString &src,
963 int32_t start,
964 int32_t end,
965 UnicodeString &dest) {
966 dest.append(src, start, end - start);
967 }
968
appendRange(const UnicodeString & src,int32_t end,UnicodeString & dest)969 static void appendRange(
970 const UnicodeString &src,
971 int32_t end,
972 UnicodeString &dest) {
973 dest.append(src, end, src.length() - end);
974 }
975
976 // Formats time like 5:37:23
formatNumeric(UDate date,const DateFormat & dateFmt,UDateFormatField smallestField,const Formattable & smallestAmount,UnicodeString & appendTo,UErrorCode & status) const977 UnicodeString &MeasureFormat::formatNumeric(
978 UDate date, // Time since epoch 1:30:00 would be 5400000
979 const DateFormat &dateFmt, // h:mm, m:ss, or h:mm:ss
980 UDateFormatField smallestField, // seconds in 5:37:23.5
981 const Formattable &smallestAmount, // 23.5 for 5:37:23.5
982 UnicodeString &appendTo,
983 UErrorCode &status) const {
984 if (U_FAILURE(status)) {
985 return appendTo;
986 }
987 // Format the smallest amount with this object's NumberFormat
988 UnicodeString smallestAmountFormatted;
989
990 // We keep track of the integer part of smallest amount so that
991 // we can replace it later so that we get '0:00:09.3' instead of
992 // '0:00:9.3'
993 FieldPosition intFieldPosition(UNUM_INTEGER_FIELD);
994 (*numberFormat)->format(
995 smallestAmount, smallestAmountFormatted, intFieldPosition, status);
996 if (
997 intFieldPosition.getBeginIndex() == 0 &&
998 intFieldPosition.getEndIndex() == 0) {
999 status = U_INTERNAL_PROGRAM_ERROR;
1000 return appendTo;
1001 }
1002
1003 // Format time. draft becomes something like '5:30:45'
1004 FieldPosition smallestFieldPosition(smallestField);
1005 UnicodeString draft;
1006 dateFmt.format(date, draft, smallestFieldPosition, status);
1007
1008 // If we find field for smallest amount replace it with the formatted
1009 // smallest amount from above taking care to replace the integer part
1010 // with what is in original time. For example, If smallest amount
1011 // is 9.35s and the formatted time is 0:00:09 then 9.35 becomes 09.35
1012 // and replacing yields 0:00:09.35
1013 if (smallestFieldPosition.getBeginIndex() != 0 ||
1014 smallestFieldPosition.getEndIndex() != 0) {
1015 appendRange(draft, 0, smallestFieldPosition.getBeginIndex(), appendTo);
1016 appendRange(
1017 smallestAmountFormatted,
1018 0,
1019 intFieldPosition.getBeginIndex(),
1020 appendTo);
1021 appendRange(
1022 draft,
1023 smallestFieldPosition.getBeginIndex(),
1024 smallestFieldPosition.getEndIndex(),
1025 appendTo);
1026 appendRange(
1027 smallestAmountFormatted,
1028 intFieldPosition.getEndIndex(),
1029 appendTo);
1030 appendRange(
1031 draft,
1032 smallestFieldPosition.getEndIndex(),
1033 appendTo);
1034 } else {
1035 appendTo.append(draft);
1036 }
1037 return appendTo;
1038 }
1039
getFormatterOrNull(const MeasureUnit & unit,UMeasureFormatWidth width,int32_t index) const1040 const SimpleFormatter *MeasureFormat::getFormatterOrNull(
1041 const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index) const {
1042 width = getRegularWidth(width);
1043 SimpleFormatter *const (*unitPatterns)[MeasureFormatCacheData::PATTERN_COUNT] =
1044 &cache->patterns[unit.getIndex()][0];
1045 if (unitPatterns[width][index] != NULL) {
1046 return unitPatterns[width][index];
1047 }
1048 int32_t fallbackWidth = cache->widthFallback[width];
1049 if (fallbackWidth != UMEASFMT_WIDTH_COUNT && unitPatterns[fallbackWidth][index] != NULL) {
1050 return unitPatterns[fallbackWidth][index];
1051 }
1052 return NULL;
1053 }
1054
getFormatter(const MeasureUnit & unit,UMeasureFormatWidth width,int32_t index,UErrorCode & errorCode) const1055 const SimpleFormatter *MeasureFormat::getFormatter(
1056 const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index,
1057 UErrorCode &errorCode) const {
1058 if (U_FAILURE(errorCode)) {
1059 return NULL;
1060 }
1061 const SimpleFormatter *pattern = getFormatterOrNull(unit, width, index);
1062 if (pattern == NULL) {
1063 errorCode = U_MISSING_RESOURCE_ERROR;
1064 }
1065 return pattern;
1066 }
1067
getPluralFormatter(const MeasureUnit & unit,UMeasureFormatWidth width,int32_t index,UErrorCode & errorCode) const1068 const SimpleFormatter *MeasureFormat::getPluralFormatter(
1069 const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index,
1070 UErrorCode &errorCode) const {
1071 if (U_FAILURE(errorCode)) {
1072 return NULL;
1073 }
1074 if (index != StandardPlural::OTHER) {
1075 const SimpleFormatter *pattern = getFormatterOrNull(unit, width, index);
1076 if (pattern != NULL) {
1077 return pattern;
1078 }
1079 }
1080 return getFormatter(unit, width, StandardPlural::OTHER, errorCode);
1081 }
1082
getPerFormatter(UMeasureFormatWidth width,UErrorCode & status) const1083 const SimpleFormatter *MeasureFormat::getPerFormatter(
1084 UMeasureFormatWidth width,
1085 UErrorCode &status) const {
1086 if (U_FAILURE(status)) {
1087 return NULL;
1088 }
1089 width = getRegularWidth(width);
1090 const SimpleFormatter * perFormatters = cache->perFormatters;
1091 if (perFormatters[width].getArgumentLimit() == 2) {
1092 return &perFormatters[width];
1093 }
1094 int32_t fallbackWidth = cache->widthFallback[width];
1095 if (fallbackWidth != UMEASFMT_WIDTH_COUNT &&
1096 perFormatters[fallbackWidth].getArgumentLimit() == 2) {
1097 return &perFormatters[fallbackWidth];
1098 }
1099 status = U_MISSING_RESOURCE_ERROR;
1100 return NULL;
1101 }
1102
withPerUnitAndAppend(const UnicodeString & formatted,const MeasureUnit & perUnit,UnicodeString & appendTo,UErrorCode & status) const1103 int32_t MeasureFormat::withPerUnitAndAppend(
1104 const UnicodeString &formatted,
1105 const MeasureUnit &perUnit,
1106 UnicodeString &appendTo,
1107 UErrorCode &status) const {
1108 int32_t offset = -1;
1109 if (U_FAILURE(status)) {
1110 return offset;
1111 }
1112 const SimpleFormatter *perUnitFormatter =
1113 getFormatterOrNull(perUnit, width, MeasureFormatCacheData::PER_UNIT_INDEX);
1114 if (perUnitFormatter != NULL) {
1115 const UnicodeString *params[] = {&formatted};
1116 perUnitFormatter->formatAndAppend(
1117 params,
1118 UPRV_LENGTHOF(params),
1119 appendTo,
1120 &offset,
1121 1,
1122 status);
1123 return offset;
1124 }
1125 const SimpleFormatter *perFormatter = getPerFormatter(width, status);
1126 const SimpleFormatter *pattern =
1127 getPluralFormatter(perUnit, width, StandardPlural::ONE, status);
1128 if (U_FAILURE(status)) {
1129 return offset;
1130 }
1131 UnicodeString perUnitString = pattern->getTextWithNoArguments();
1132 perUnitString.trim();
1133 const UnicodeString *params[] = {&formatted, &perUnitString};
1134 perFormatter->formatAndAppend(
1135 params,
1136 UPRV_LENGTHOF(params),
1137 appendTo,
1138 &offset,
1139 1,
1140 status);
1141 return offset;
1142 }
1143
formatMeasuresSlowTrack(const Measure * measures,int32_t measureCount,UnicodeString & appendTo,FieldPosition & pos,UErrorCode & status) const1144 UnicodeString &MeasureFormat::formatMeasuresSlowTrack(
1145 const Measure *measures,
1146 int32_t measureCount,
1147 UnicodeString& appendTo,
1148 FieldPosition& pos,
1149 UErrorCode& status) const {
1150 if (U_FAILURE(status)) {
1151 return appendTo;
1152 }
1153 FieldPosition dontCare(FieldPosition::DONT_CARE);
1154 FieldPosition fpos(pos.getField());
1155 UnicodeString *results = new UnicodeString[measureCount];
1156 int32_t fieldPositionFoundIndex = -1;
1157 for (int32_t i = 0; i < measureCount; ++i) {
1158 const NumberFormat *nf = cache->getIntegerFormat();
1159 if (i == measureCount - 1) {
1160 nf = numberFormat->get();
1161 }
1162 if (fieldPositionFoundIndex == -1) {
1163 formatMeasure(measures[i], *nf, results[i], fpos, status);
1164 if (U_FAILURE(status)) {
1165 delete [] results;
1166 return appendTo;
1167 }
1168 if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) {
1169 fieldPositionFoundIndex = i;
1170 }
1171 } else {
1172 formatMeasure(measures[i], *nf, results[i], dontCare, status);
1173 }
1174 }
1175 int32_t offset;
1176 listFormatter->format(
1177 results,
1178 measureCount,
1179 appendTo,
1180 fieldPositionFoundIndex,
1181 offset,
1182 status);
1183 if (U_FAILURE(status)) {
1184 delete [] results;
1185 return appendTo;
1186 }
1187 if (offset != -1) {
1188 pos.setBeginIndex(fpos.getBeginIndex() + offset);
1189 pos.setEndIndex(fpos.getEndIndex() + offset);
1190 }
1191 delete [] results;
1192 return appendTo;
1193 }
1194
createCurrencyFormat(const Locale & locale,UErrorCode & ec)1195 MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(const Locale& locale,
1196 UErrorCode& ec) {
1197 CurrencyFormat* fmt = NULL;
1198 if (U_SUCCESS(ec)) {
1199 fmt = new CurrencyFormat(locale, ec);
1200 if (U_FAILURE(ec)) {
1201 delete fmt;
1202 fmt = NULL;
1203 }
1204 }
1205 return fmt;
1206 }
1207
createCurrencyFormat(UErrorCode & ec)1208 MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(UErrorCode& ec) {
1209 if (U_FAILURE(ec)) {
1210 return NULL;
1211 }
1212 return MeasureFormat::createCurrencyFormat(Locale::getDefault(), ec);
1213 }
1214
1215 U_NAMESPACE_END
1216
1217 #endif /* #if !UCONFIG_NO_FORMATTING */
1218