]> git.saurik.com Git - apple/icu.git/blob - icuSources/i18n/measfmt.cpp
ICU-551.51.4.tar.gz
[apple/icu.git] / icuSources / i18n / measfmt.cpp
1 /*
2 **********************************************************************
3 * Copyright (c) 2004-2015, 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 122
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.
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();
87 void adoptCurrencyFormat(int32_t widthIndex, NumberFormat *nfToAdopt) {
88 delete currencyFormats[widthIndex];
89 currencyFormats[widthIndex] = nfToAdopt;
90 }
91 const NumberFormat *getCurrencyFormat(int32_t widthIndex) const {
92 return currencyFormats[widthIndex];
93 }
94 void adoptIntegerFormat(NumberFormat *nfToAdopt) {
95 delete integerFormat;
96 integerFormat = nfToAdopt;
97 }
98 const NumberFormat *getIntegerFormat() const {
99 return integerFormat;
100 }
101 void adoptNumericDateFormatters(NumericDateFormatters *formattersToAdopt) {
102 delete numericDateFormatters;
103 numericDateFormatters = formattersToAdopt;
104 }
105 const NumericDateFormatters *getNumericDateFormatters() const {
106 return numericDateFormatters;
107 }
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 }
115 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
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
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
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
162 static UBool isCurrency(const MeasureUnit &unit) {
163 return (uprv_strcmp(unit.getType(), "currency") == 0);
164 }
165
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
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
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
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
346 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
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.
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
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
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
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
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
506 MeasureFormat::MeasureFormat() :
507 cache(NULL),
508 numberFormat(NULL),
509 pluralRules(NULL),
510 width(UMEASFMT_WIDTH_WIDE),
511 listFormatter(NULL) {
512 }
513
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
527 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
563 Format *MeasureFormat::clone() const {
564 return new MeasureFormat(*this);
565 }
566
567 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
585 void MeasureFormat::parseObject(
586 const UnicodeString & /*source*/,
587 Formattable & /*result*/,
588 ParsePosition& /*pos*/) const {
589 return;
590 }
591
592 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
626 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
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
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
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
744 // Apple-specific for now
745 UMeasureFormatWidth MeasureFormat::getWidth() const {
746 return width;
747 }
748
749 const NumberFormat &MeasureFormat::getNumberFormat() const {
750 return **numberFormat;
751 }
752
753 const PluralRules &MeasureFormat::getPluralRules() const {
754 return **pluralRules;
755 }
756
757 Locale MeasureFormat::getLocale(UErrorCode &status) const {
758 return Format::getLocale(ULOC_VALID_LOCALE, status);
759 }
760
761 const char *MeasureFormat::getLocaleID(UErrorCode &status) const {
762 return Format::getLocaleID(ULOC_VALID_LOCALE, status);
763 }
764
765 UnicodeString &MeasureFormat::formatMeasure(
766 const Measure &measure,
767 const NumberFormat &nf,
768 UnicodeString &appendTo,
769 FieldPosition &pos,
770 UErrorCode &status) const {
771 if (U_FAILURE(status)) {
772 return appendTo;
773 }
774 const Formattable& amtNumber = measure.getNumber();
775 const MeasureUnit& amtUnit = measure.getUnit();
776 if (isCurrency(amtUnit)) {
777 UChar isoCode[4];
778 u_charsToUChars(amtUnit.getSubtype(), isoCode, 4);
779 return cache->getCurrencyFormat(widthToIndex(width))->format(
780 new CurrencyAmount(amtNumber, isoCode, status),
781 appendTo,
782 pos,
783 status);
784 }
785 const QuantityFormatter *quantityFormatter = getQuantityFormatter(
786 amtUnit.getIndex(), widthToIndex(width), status);
787 if (U_FAILURE(status)) {
788 return appendTo;
789 }
790 return quantityFormatter->format(
791 amtNumber,
792 nf,
793 **pluralRules,
794 appendTo,
795 pos,
796 status);
797 }
798
799 // Formats hours-minutes-seconds as 5:37:23 or similar.
800 UnicodeString &MeasureFormat::formatNumeric(
801 const Formattable *hms, // always length 3
802 int32_t bitMap, // 1=hourset, 2=minuteset, 4=secondset
803 UnicodeString &appendTo,
804 UErrorCode &status) const {
805 if (U_FAILURE(status)) {
806 return appendTo;
807 }
808 UDate millis =
809 (UDate) (((uprv_trunc(hms[0].getDouble(status)) * 60.0
810 + uprv_trunc(hms[1].getDouble(status))) * 60.0
811 + uprv_trunc(hms[2].getDouble(status))) * 1000.0);
812 switch (bitMap) {
813 case 5: // hs
814 case 7: // hms
815 return formatNumeric(
816 millis,
817 cache->getNumericDateFormatters()->hourMinuteSecond,
818 UDAT_SECOND_FIELD,
819 hms[2],
820 appendTo,
821 status);
822 break;
823 case 6: // ms
824 return formatNumeric(
825 millis,
826 cache->getNumericDateFormatters()->minuteSecond,
827 UDAT_SECOND_FIELD,
828 hms[2],
829 appendTo,
830 status);
831 break;
832 case 3: // hm
833 return formatNumeric(
834 millis,
835 cache->getNumericDateFormatters()->hourMinute,
836 UDAT_MINUTE_FIELD,
837 hms[1],
838 appendTo,
839 status);
840 break;
841 default:
842 status = U_INTERNAL_PROGRAM_ERROR;
843 return appendTo;
844 break;
845 }
846 return appendTo;
847 }
848
849 static void appendRange(
850 const UnicodeString &src,
851 int32_t start,
852 int32_t end,
853 UnicodeString &dest) {
854 dest.append(src, start, end - start);
855 }
856
857 static void appendRange(
858 const UnicodeString &src,
859 int32_t end,
860 UnicodeString &dest) {
861 dest.append(src, end, src.length() - end);
862 }
863
864 // Formats time like 5:37:23
865 UnicodeString &MeasureFormat::formatNumeric(
866 UDate date, // Time since epoch 1:30:00 would be 5400000
867 const DateFormat &dateFmt, // h:mm, m:ss, or h:mm:ss
868 UDateFormatField smallestField, // seconds in 5:37:23.5
869 const Formattable &smallestAmount, // 23.5 for 5:37:23.5
870 UnicodeString &appendTo,
871 UErrorCode &status) const {
872 if (U_FAILURE(status)) {
873 return appendTo;
874 }
875 // Format the smallest amount with this object's NumberFormat
876 UnicodeString smallestAmountFormatted;
877
878 // We keep track of the integer part of smallest amount so that
879 // we can replace it later so that we get '0:00:09.3' instead of
880 // '0:00:9.3'
881 FieldPosition intFieldPosition(UNUM_INTEGER_FIELD);
882 (*numberFormat)->format(
883 smallestAmount, smallestAmountFormatted, intFieldPosition, status);
884 if (
885 intFieldPosition.getBeginIndex() == 0 &&
886 intFieldPosition.getEndIndex() == 0) {
887 status = U_INTERNAL_PROGRAM_ERROR;
888 return appendTo;
889 }
890
891 // Format time. draft becomes something like '5:30:45'
892 FieldPosition smallestFieldPosition(smallestField);
893 UnicodeString draft;
894 dateFmt.format(date, draft, smallestFieldPosition, status);
895
896 // If we find field for smallest amount replace it with the formatted
897 // smallest amount from above taking care to replace the integer part
898 // with what is in original time. For example, If smallest amount
899 // is 9.35s and the formatted time is 0:00:09 then 9.35 becomes 09.35
900 // and replacing yields 0:00:09.35
901 if (smallestFieldPosition.getBeginIndex() != 0 ||
902 smallestFieldPosition.getEndIndex() != 0) {
903 appendRange(draft, 0, smallestFieldPosition.getBeginIndex(), appendTo);
904 appendRange(
905 smallestAmountFormatted,
906 0,
907 intFieldPosition.getBeginIndex(),
908 appendTo);
909 appendRange(
910 draft,
911 smallestFieldPosition.getBeginIndex(),
912 smallestFieldPosition.getEndIndex(),
913 appendTo);
914 appendRange(
915 smallestAmountFormatted,
916 intFieldPosition.getEndIndex(),
917 appendTo);
918 appendRange(
919 draft,
920 smallestFieldPosition.getEndIndex(),
921 appendTo);
922 } else {
923 appendTo.append(draft);
924 }
925 return appendTo;
926 }
927
928 const QuantityFormatter *MeasureFormat::getQuantityFormatter(
929 int32_t index,
930 int32_t widthIndex,
931 UErrorCode &status) const {
932 if (U_FAILURE(status)) {
933 return NULL;
934 }
935 const QuantityFormatter *formatters =
936 cache->formatters[index];
937 if (formatters[widthIndex].isValid()) {
938 return &formatters[widthIndex];
939 }
940 if (formatters[UMEASFMT_WIDTH_SHORT].isValid()) {
941 return &formatters[UMEASFMT_WIDTH_SHORT];
942 }
943 if (formatters[UMEASFMT_WIDTH_WIDE].isValid()) {
944 return &formatters[UMEASFMT_WIDTH_WIDE];
945 }
946 status = U_MISSING_RESOURCE_ERROR;
947 return NULL;
948 }
949
950 const SimplePatternFormatter *MeasureFormat::getPerUnitFormatter(
951 int32_t index,
952 int32_t widthIndex) const {
953 const SimplePatternFormatter * const * perUnitFormatters =
954 cache->getPerUnitFormattersByIndex(index);
955 if (perUnitFormatters[widthIndex] != NULL) {
956 return perUnitFormatters[widthIndex];
957 }
958 if (perUnitFormatters[UMEASFMT_WIDTH_SHORT] != NULL) {
959 return perUnitFormatters[UMEASFMT_WIDTH_SHORT];
960 }
961 if (perUnitFormatters[UMEASFMT_WIDTH_WIDE] != NULL) {
962 return perUnitFormatters[UMEASFMT_WIDTH_WIDE];
963 }
964 return NULL;
965 }
966
967 const SimplePatternFormatter *MeasureFormat::getPerFormatter(
968 int32_t widthIndex,
969 UErrorCode &status) const {
970 if (U_FAILURE(status)) {
971 return NULL;
972 }
973 const SimplePatternFormatter * perFormatters = cache->perFormatters;
974
975 if (perFormatters[widthIndex].getPlaceholderCount() == 2) {
976 return &perFormatters[widthIndex];
977 }
978 if (perFormatters[UMEASFMT_WIDTH_SHORT].getPlaceholderCount() == 2) {
979 return &perFormatters[UMEASFMT_WIDTH_SHORT];
980 }
981 if (perFormatters[UMEASFMT_WIDTH_WIDE].getPlaceholderCount() == 2) {
982 return &perFormatters[UMEASFMT_WIDTH_WIDE];
983 }
984 status = U_MISSING_RESOURCE_ERROR;
985 return NULL;
986 }
987
988 static void getPerUnitString(
989 const QuantityFormatter &formatter,
990 UnicodeString &result) {
991 result = formatter.getByVariant("one")->getPatternWithNoPlaceholders();
992 result.trim();
993 }
994
995 int32_t MeasureFormat::withPerUnitAndAppend(
996 const UnicodeString &formatted,
997 const MeasureUnit &perUnit,
998 UnicodeString &appendTo,
999 UErrorCode &status) const {
1000 int32_t offset = -1;
1001 if (U_FAILURE(status)) {
1002 return offset;
1003 }
1004 const SimplePatternFormatter *perUnitFormatter = getPerUnitFormatter(
1005 perUnit.getIndex(), widthToIndex(width));
1006 if (perUnitFormatter != NULL) {
1007 const UnicodeString *params[] = {&formatted};
1008 perUnitFormatter->formatAndAppend(
1009 params,
1010 UPRV_LENGTHOF(params),
1011 appendTo,
1012 &offset,
1013 1,
1014 status);
1015 return offset;
1016 }
1017 const SimplePatternFormatter *perFormatter = getPerFormatter(
1018 widthToIndex(width), status);
1019 const QuantityFormatter *qf = getQuantityFormatter(
1020 perUnit.getIndex(), widthToIndex(width), status);
1021 if (U_FAILURE(status)) {
1022 return offset;
1023 }
1024 UnicodeString perUnitString;
1025 getPerUnitString(*qf, perUnitString);
1026 const UnicodeString *params[] = {&formatted, &perUnitString};
1027 perFormatter->formatAndAppend(
1028 params,
1029 UPRV_LENGTHOF(params),
1030 appendTo,
1031 &offset,
1032 1,
1033 status);
1034 return offset;
1035 }
1036
1037 UnicodeString &MeasureFormat::formatMeasuresSlowTrack(
1038 const Measure *measures,
1039 int32_t measureCount,
1040 UnicodeString& appendTo,
1041 FieldPosition& pos,
1042 UErrorCode& status) const {
1043 if (U_FAILURE(status)) {
1044 return appendTo;
1045 }
1046 FieldPosition dontCare(FieldPosition::DONT_CARE);
1047 FieldPosition fpos(pos.getField());
1048 UnicodeString *results = new UnicodeString[measureCount];
1049 int32_t fieldPositionFoundIndex = -1;
1050 for (int32_t i = 0; i < measureCount; ++i) {
1051 const NumberFormat *nf = cache->getIntegerFormat();
1052 if (i == measureCount - 1) {
1053 nf = numberFormat->get();
1054 }
1055 if (fieldPositionFoundIndex == -1) {
1056 formatMeasure(measures[i], *nf, results[i], fpos, status);
1057 if (U_FAILURE(status)) {
1058 delete [] results;
1059 return appendTo;
1060 }
1061 if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) {
1062 fieldPositionFoundIndex = i;
1063 }
1064 } else {
1065 formatMeasure(measures[i], *nf, results[i], dontCare, status);
1066 }
1067 }
1068 int32_t offset;
1069 listFormatter->format(
1070 results,
1071 measureCount,
1072 appendTo,
1073 fieldPositionFoundIndex,
1074 offset,
1075 status);
1076 if (U_FAILURE(status)) {
1077 delete [] results;
1078 return appendTo;
1079 }
1080 if (offset != -1) {
1081 pos.setBeginIndex(fpos.getBeginIndex() + offset);
1082 pos.setEndIndex(fpos.getEndIndex() + offset);
1083 }
1084 delete [] results;
1085 return appendTo;
1086 }
1087
1088 MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(const Locale& locale,
1089 UErrorCode& ec) {
1090 CurrencyFormat* fmt = NULL;
1091 if (U_SUCCESS(ec)) {
1092 fmt = new CurrencyFormat(locale, ec);
1093 if (U_FAILURE(ec)) {
1094 delete fmt;
1095 fmt = NULL;
1096 }
1097 }
1098 return fmt;
1099 }
1100
1101 MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(UErrorCode& ec) {
1102 if (U_FAILURE(ec)) {
1103 return NULL;
1104 }
1105 return MeasureFormat::createCurrencyFormat(Locale::getDefault(), ec);
1106 }
1107
1108 U_NAMESPACE_END
1109
1110 #endif /* #if !UCONFIG_NO_FORMATTING */