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