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