1 // © 2016 and later: Unicode, Inc. and others.
2 // License & terms of use: http://www.unicode.org/copyright.html
4 **********************************************************************
5 * Copyright (c) 2004-2016, International Business Machines
6 * Corporation and others. All Rights Reserved.
7 **********************************************************************
9 * Created: April 20, 2004
11 **********************************************************************
13 #include "utypeinfo.h" // for 'typeid' to work
14 #include "unicode/utypes.h"
16 #if !UCONFIG_NO_FORMATTING
18 #include "unicode/measfmt.h"
19 #include "unicode/numfmt.h"
21 #include "unicode/localpointer.h"
23 #include "unicode/simpleformatter.h"
24 #include "quantityformatter.h"
25 #include "unicode/plurrule.h"
26 #include "unicode/decimfmt.h"
28 #include "unicode/ures.h"
29 #include "unicode/ustring.h"
34 #include "unicode/listformatter.h"
36 #include "unicode/putil.h"
37 #include "unicode/smpdtfmt.h"
39 #include "unicode/uameasureformat.h"
42 #include "sharednumberformat.h"
43 #include "sharedpluralrules.h"
44 #include "standardplural.h"
45 #include "unifiedcache.h"
47 #define MEAS_UNIT_COUNT 135
48 #define WIDTH_INDEX_COUNT (UMEASFMT_WIDTH_NARROW + 1)
52 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MeasureFormat
)
54 // Used to format durations like 5:47 or 21:35:42.
55 class NumericDateFormatters
: public UMemory
{
58 SimpleDateFormat hourMinute
;
61 SimpleDateFormat minuteSecond
;
63 // formats like H:mm:ss
64 SimpleDateFormat hourMinuteSecond
;
66 // Constructor that takes the actual patterns for hour-minute,
67 // minute-second, and hour-minute-second respectively.
68 NumericDateFormatters(
69 const UnicodeString
&hm
,
70 const UnicodeString
&ms
,
71 const UnicodeString
&hms
,
73 hourMinute(hm
, status
),
74 minuteSecond(ms
, status
),
75 hourMinuteSecond(hms
, status
) {
76 const TimeZone
*gmt
= TimeZone::getGMT();
77 hourMinute
.setTimeZone(*gmt
);
78 minuteSecond
.setTimeZone(*gmt
);
79 hourMinuteSecond
.setTimeZone(*gmt
);
82 NumericDateFormatters(const NumericDateFormatters
&other
);
83 NumericDateFormatters
&operator=(const NumericDateFormatters
&other
);
86 static UMeasureFormatWidth
getRegularWidth(UMeasureFormatWidth width
) {
87 if (width
>= WIDTH_INDEX_COUNT
) {
88 return UMEASFMT_WIDTH_NARROW
;
94 * Instances contain all MeasureFormat specific data for a particular locale.
95 * This data is cached. It is never copied, but is shared via shared pointers.
97 * Note: We might change the cache data to have an array[WIDTH_INDEX_COUNT] of
98 * complete sets of unit & per patterns,
99 * to correspond to the resource data and its aliases.
101 * TODO: Maybe store more sparsely in general, with pointers rather than potentially-empty objects.
103 class MeasureFormatCacheData
: public SharedObject
{
105 static const int32_t PER_UNIT_INDEX
= StandardPlural::COUNT
;
106 static const int32_t PATTERN_COUNT
= PER_UNIT_INDEX
+ 1;
109 * Redirection data from root-bundle, top-level sideways aliases.
110 * - UMEASFMT_WIDTH_COUNT: initial value, just fall back to root
111 * - UMEASFMT_WIDTH_WIDE/SHORT/NARROW: sideways alias for missing data
113 UMeasureFormatWidth widthFallback
[WIDTH_INDEX_COUNT
];
114 /** Measure unit -> format width -> array of patterns ("{0} meters") (plurals + PER_UNIT_INDEX) */
115 SimpleFormatter
*patterns
[MEAS_UNIT_COUNT
][WIDTH_INDEX_COUNT
][PATTERN_COUNT
];
116 const UChar
* dnams
[MEAS_UNIT_COUNT
][WIDTH_INDEX_COUNT
];
117 SimpleFormatter perFormatters
[WIDTH_INDEX_COUNT
];
119 MeasureFormatCacheData();
120 virtual ~MeasureFormatCacheData();
122 UBool
hasPerFormatter(int32_t width
) const {
123 // TODO: Create a more obvious way to test if the per-formatter has been set?
124 // Use pointers, check for NULL? Or add an isValid() method?
125 return perFormatters
[width
].getArgumentLimit() == 2;
128 void adoptCurrencyFormat(int32_t widthIndex
, NumberFormat
*nfToAdopt
) {
129 delete currencyFormats
[widthIndex
];
130 currencyFormats
[widthIndex
] = nfToAdopt
;
132 const NumberFormat
*getCurrencyFormat(UMeasureFormatWidth width
) const {
133 return currencyFormats
[getRegularWidth(width
)];
135 void adoptIntegerFormat(NumberFormat
*nfToAdopt
) {
136 delete integerFormat
;
137 integerFormat
= nfToAdopt
;
139 const NumberFormat
*getIntegerFormat() const {
140 return integerFormat
;
142 void adoptNumericDateFormatters(NumericDateFormatters
*formattersToAdopt
) {
143 delete numericDateFormatters
;
144 numericDateFormatters
= formattersToAdopt
;
146 const NumericDateFormatters
*getNumericDateFormatters() const {
147 return numericDateFormatters
;
151 NumberFormat
*currencyFormats
[WIDTH_INDEX_COUNT
];
152 NumberFormat
*integerFormat
;
153 NumericDateFormatters
*numericDateFormatters
;
154 MeasureFormatCacheData(const MeasureFormatCacheData
&other
);
155 MeasureFormatCacheData
&operator=(const MeasureFormatCacheData
&other
);
158 MeasureFormatCacheData::MeasureFormatCacheData() {
159 for (int32_t i
= 0; i
< WIDTH_INDEX_COUNT
; ++i
) {
160 widthFallback
[i
] = UMEASFMT_WIDTH_COUNT
;
162 for (int32_t i
= 0; i
< UPRV_LENGTHOF(currencyFormats
); ++i
) {
163 currencyFormats
[i
] = NULL
;
165 uprv_memset(patterns
, 0, sizeof(patterns
));
166 uprv_memset(dnams
, 0, sizeof(dnams
));
167 integerFormat
= NULL
;
168 numericDateFormatters
= NULL
;
171 MeasureFormatCacheData::~MeasureFormatCacheData() {
172 for (int32_t i
= 0; i
< UPRV_LENGTHOF(currencyFormats
); ++i
) {
173 delete currencyFormats
[i
];
175 for (int32_t i
= 0; i
< MEAS_UNIT_COUNT
; ++i
) {
176 for (int32_t j
= 0; j
< WIDTH_INDEX_COUNT
; ++j
) {
177 for (int32_t k
= 0; k
< PATTERN_COUNT
; ++k
) {
178 delete patterns
[i
][j
][k
];
182 // Note: the contents of 'dnams' are pointers into the resource bundle
183 delete integerFormat
;
184 delete numericDateFormatters
;
187 static UBool
isCurrency(const MeasureUnit
&unit
) {
188 return (uprv_strcmp(unit
.getType(), "currency") == 0);
191 static UBool
getString(
192 const UResourceBundle
*resource
,
193 UnicodeString
&result
,
194 UErrorCode
&status
) {
196 const UChar
*resStr
= ures_getString(resource
, &len
, &status
);
197 if (U_FAILURE(status
)) {
200 result
.setTo(TRUE
, resStr
, len
);
206 static const UChar g_LOCALE_units
[] = {
207 0x2F, 0x4C, 0x4F, 0x43, 0x41, 0x4C, 0x45, 0x2F,
208 0x75, 0x6E, 0x69, 0x74, 0x73
210 static const UChar gShort
[] = { 0x53, 0x68, 0x6F, 0x72, 0x74 };
211 static const UChar gNarrow
[] = { 0x4E, 0x61, 0x72, 0x72, 0x6F, 0x77 };
214 * Sink for enumerating all of the measurement unit display names.
215 * Contains inner sink classes, each one corresponding to a type of resource table.
216 * The outer sink handles the top-level units, unitsNarrow, and unitsShort tables.
218 * More specific bundles (en_GB) are enumerated before their parents (en_001, en, root):
219 * Only store a value if it is still missing, that is, it has not been overridden.
221 * C++: Each inner sink class has a reference to the main outer sink.
222 * Java: Use non-static inner classes instead.
224 struct UnitDataSink
: public ResourceSink
{
227 MeasureFormatCacheData
&cacheData
;
229 // Path to current data.
230 UMeasureFormatWidth width
;
234 UnitDataSink(MeasureFormatCacheData
&outputData
)
235 : cacheData(outputData
),
236 width(UMEASFMT_WIDTH_COUNT
), type(NULL
), unitIndex(0) {}
239 void setFormatterIfAbsent(int32_t index
, const ResourceValue
&value
,
240 int32_t minPlaceholders
, UErrorCode
&errorCode
) {
241 SimpleFormatter
**patterns
= &cacheData
.patterns
[unitIndex
][width
][0];
242 if (U_SUCCESS(errorCode
) && patterns
[index
] == NULL
) {
243 if (minPlaceholders
>= 0) {
244 patterns
[index
] = new SimpleFormatter(
245 value
.getUnicodeString(errorCode
), minPlaceholders
, 1, errorCode
);
247 if (U_SUCCESS(errorCode
) && patterns
[index
] == NULL
) {
248 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
253 void setDnamIfAbsent(const ResourceValue
&value
, UErrorCode
& errorCode
) {
254 if (cacheData
.dnams
[unitIndex
][width
] == NULL
) {
256 cacheData
.dnams
[unitIndex
][width
] = value
.getString(length
, errorCode
);
261 * Consume a display pattern. For example,
262 * unitsShort/duration/hour contains other{"{0} hrs"}.
264 void consumePattern(const char *key
, const ResourceValue
&value
, UErrorCode
&errorCode
) {
265 if (U_FAILURE(errorCode
)) { return; }
266 if (uprv_strcmp(key
, "dnam") == 0) {
267 // The display name for the unit in the current width.
268 setDnamIfAbsent(value
, errorCode
);
269 } else if (uprv_strcmp(key
, "per") == 0) {
270 // For example, "{0}/h".
271 setFormatterIfAbsent(MeasureFormatCacheData::PER_UNIT_INDEX
, value
, 1, errorCode
);
273 // The key must be one of the plural form strings. For example:
276 setFormatterIfAbsent(StandardPlural::indexFromString(key
, errorCode
), value
, 0,
282 * Consume a table of per-unit tables. For example,
283 * unitsShort/duration contains tables for duration-unit subtypes day & hour.
285 void consumeSubtypeTable(const char *key
, ResourceValue
&value
, UErrorCode
&errorCode
) {
286 if (U_FAILURE(errorCode
)) { return; }
287 unitIndex
= MeasureUnit::internalGetIndexForTypeAndSubtype(type
, key
);
289 // TODO: How to handle unexpected data?
290 // See http://bugs.icu-project.org/trac/ticket/12597
294 // We no longer handle units like "coordinate" here (which do not have plural variants)
295 if (value
.getType() == URES_TABLE
) {
296 // Units that have plural variants
297 ResourceTable patternTableTable
= value
.getTable(errorCode
);
298 if (U_FAILURE(errorCode
)) { return; }
299 for (int i
= 0; patternTableTable
.getKeyAndValue(i
, key
, value
); ++i
) {
300 consumePattern(key
, value
, errorCode
);
303 // TODO: How to handle unexpected data?
304 // See http://bugs.icu-project.org/trac/ticket/12597
310 * Consume compound x-per-y display pattern. For example,
311 * unitsShort/compound/per may be "{0}/{1}".
313 void consumeCompoundPattern(const char *key
, const ResourceValue
&value
, UErrorCode
&errorCode
) {
314 if (U_SUCCESS(errorCode
) && uprv_strcmp(key
, "per") == 0) {
315 cacheData
.perFormatters
[width
].
316 applyPatternMinMaxArguments(value
.getUnicodeString(errorCode
), 2, 2, errorCode
);
321 * Consume a table of unit type tables. For example,
322 * unitsShort contains tables for area & duration.
323 * It also contains a table for the compound/per pattern.
325 void consumeUnitTypesTable(const char *key
, ResourceValue
&value
, UErrorCode
&errorCode
) {
326 if (U_FAILURE(errorCode
)) { return; }
327 if (uprv_strcmp(key
, "currency") == 0) {
329 } else if (uprv_strcmp(key
, "compound") == 0) {
330 if (!cacheData
.hasPerFormatter(width
)) {
331 ResourceTable compoundTable
= value
.getTable(errorCode
);
332 if (U_FAILURE(errorCode
)) { return; }
333 for (int i
= 0; compoundTable
.getKeyAndValue(i
, key
, value
); ++i
) {
334 consumeCompoundPattern(key
, value
, errorCode
);
337 } else if (uprv_strcmp(key
, "coordinate") == 0) {
338 // special handling but we need to determine what that is
341 ResourceTable subtypeTable
= value
.getTable(errorCode
);
342 if (U_FAILURE(errorCode
)) { return; }
343 for (int i
= 0; subtypeTable
.getKeyAndValue(i
, key
, value
); ++i
) {
344 consumeSubtypeTable(key
, value
, errorCode
);
349 void consumeAlias(const char *key
, const ResourceValue
&value
, UErrorCode
&errorCode
) {
350 // Handle aliases like
351 // units:alias{"/LOCALE/unitsShort"}
352 // which should only occur in the root bundle.
353 UMeasureFormatWidth sourceWidth
= widthFromKey(key
);
354 if (sourceWidth
== UMEASFMT_WIDTH_COUNT
) {
355 // Alias from something we don't care about.
358 UMeasureFormatWidth targetWidth
= widthFromAlias(value
, errorCode
);
359 if (targetWidth
== UMEASFMT_WIDTH_COUNT
) {
360 // We do not recognize what to fall back to.
361 errorCode
= U_INVALID_FORMAT_ERROR
;
364 // Check that we do not fall back to another fallback.
365 if (cacheData
.widthFallback
[targetWidth
] != UMEASFMT_WIDTH_COUNT
) {
366 errorCode
= U_INVALID_FORMAT_ERROR
;
369 cacheData
.widthFallback
[sourceWidth
] = targetWidth
;
372 void consumeTable(const char *key
, ResourceValue
&value
, UErrorCode
&errorCode
) {
373 if (U_SUCCESS(errorCode
) && (width
= widthFromKey(key
)) != UMEASFMT_WIDTH_COUNT
) {
374 ResourceTable unitTypesTable
= value
.getTable(errorCode
);
375 if (U_FAILURE(errorCode
)) { return; }
376 for (int i
= 0; unitTypesTable
.getKeyAndValue(i
, key
, value
); ++i
) {
377 consumeUnitTypesTable(key
, value
, errorCode
);
382 static UMeasureFormatWidth
widthFromKey(const char *key
) {
383 if (uprv_strncmp(key
, "units", 5) == 0) {
386 return UMEASFMT_WIDTH_WIDE
;
387 } else if (uprv_strcmp(key
, "Short") == 0) {
388 return UMEASFMT_WIDTH_SHORT
;
389 } else if (uprv_strcmp(key
, "Narrow") == 0) {
390 return UMEASFMT_WIDTH_NARROW
;
393 return UMEASFMT_WIDTH_COUNT
;
396 static UMeasureFormatWidth
widthFromAlias(const ResourceValue
&value
, UErrorCode
&errorCode
) {
398 const UChar
*s
= value
.getAliasString(length
, errorCode
);
399 // For example: "/LOCALE/unitsShort"
400 if (U_SUCCESS(errorCode
) && length
>= 13 && u_memcmp(s
, g_LOCALE_units
, 13) == 0) {
404 return UMEASFMT_WIDTH_WIDE
;
405 } else if (u_strCompare(s
, length
, gShort
, 5, FALSE
) == 0) {
406 return UMEASFMT_WIDTH_SHORT
;
407 } else if (u_strCompare(s
, length
, gNarrow
, 6, FALSE
) == 0) {
408 return UMEASFMT_WIDTH_NARROW
;
411 return UMEASFMT_WIDTH_COUNT
;
414 virtual void put(const char *key
, ResourceValue
&value
, UBool
/*noFallback*/,
415 UErrorCode
&errorCode
) {
416 // Main entry point to sink
417 ResourceTable widthsTable
= value
.getTable(errorCode
);
418 if (U_FAILURE(errorCode
)) { return; }
419 for (int i
= 0; widthsTable
.getKeyAndValue(i
, key
, value
); ++i
) {
420 if (value
.getType() == URES_ALIAS
) {
421 consumeAlias(key
, value
, errorCode
);
423 consumeTable(key
, value
, errorCode
);
429 // Virtual destructors must be defined out of line.
430 UnitDataSink::~UnitDataSink() {}
434 static const UAMeasureUnit indexToUAMsasUnit
[] = {
435 // UAMeasureUnit // UAMeasUnit vals # MeasUnit.getIndex()
436 UAMEASUNIT_ACCELERATION_G_FORCE
, // (0 << 8) + 0, # 0
437 UAMEASUNIT_ACCELERATION_METER_PER_SECOND_SQUARED
, // (0 << 8) + 1, # 1
438 UAMEASUNIT_ANGLE_ARC_MINUTE
, // (1 << 8) + 1, # 2
439 UAMEASUNIT_ANGLE_ARC_SECOND
, // (1 << 8) + 2, # 3
440 UAMEASUNIT_ANGLE_DEGREE
, // (1 << 8) + 0, # 4
441 UAMEASUNIT_ANGLE_RADIAN
, // (1 << 8) + 3, # 5
442 UAMEASUNIT_ANGLE_REVOLUTION
, // (1 << 8) + 4, # 6
443 UAMEASUNIT_AREA_ACRE
, // (2 << 8) + 4, # 7
444 UAMEASUNIT_AREA_HECTARE
, // (2 << 8) + 5, # 8
445 UAMEASUNIT_AREA_SQUARE_CENTIMETER
, // (2 << 8) + 6, # 9
446 UAMEASUNIT_AREA_SQUARE_FOOT
, // (2 << 8) + 2, # 10
447 UAMEASUNIT_AREA_SQUARE_INCH
, // (2 << 8) + 7, # 11
448 UAMEASUNIT_AREA_SQUARE_KILOMETER
, // (2 << 8) + 1, # 12
449 UAMEASUNIT_AREA_SQUARE_METER
, // (2 << 8) + 0, # 13
450 UAMEASUNIT_AREA_SQUARE_MILE
, // (2 << 8) + 3, # 14
451 UAMEASUNIT_AREA_SQUARE_YARD
, // (2 << 8) + 8, # 15
452 UAMEASUNIT_CONCENTRATION_KARAT
, // (18 << 8) + 0, # 16
453 UAMEASUNIT_CONCENTRATION_MILLIGRAM_PER_DECILITER
, // (18 << 8) + 1, # 17
454 UAMEASUNIT_CONCENTRATION_MILLIMOLE_PER_LITER
, // (18 << 8) + 2, # 18
455 UAMEASUNIT_CONCENTRATION_PART_PER_MILLION
, // (18 << 8) + 3, # 19
456 UAMEASUNIT_CONSUMPTION_LITER_PER_100_KILOMETERs
, // (13 << 8) + 2, # 20
457 UAMEASUNIT_CONSUMPTION_LITER_PER_KILOMETER
, // (13 << 8) + 0, # 21
458 UAMEASUNIT_CONSUMPTION_MILE_PER_GALLON
, // (13 << 8) + 1, # 22
459 UAMEASUNIT_CONSUMPTION_MILE_PER_GALLON_IMPERIAL
, // (13 << 8) + 3, # 23
460 UAMEASUNIT_DIGITAL_BIT
, // (14 << 8) + 0, # 24
461 UAMEASUNIT_DIGITAL_BYTE
, // (14 << 8) + 1, # 25
462 UAMEASUNIT_DIGITAL_GIGABIT
, // (14 << 8) + 2, # 26
463 UAMEASUNIT_DIGITAL_GIGABYTE
, // (14 << 8) + 3, # 27
464 UAMEASUNIT_DIGITAL_KILOBIT
, // (14 << 8) + 4, # 28
465 UAMEASUNIT_DIGITAL_KILOBYTE
, // (14 << 8) + 5, # 29
466 UAMEASUNIT_DIGITAL_MEGABIT
, // (14 << 8) + 6, # 30
467 UAMEASUNIT_DIGITAL_MEGABYTE
, // (14 << 8) + 7, # 31
468 UAMEASUNIT_DIGITAL_TERABIT
, // (14 << 8) + 8, # 32
469 UAMEASUNIT_DIGITAL_TERABYTE
, // (14 << 8) + 9, # 33
470 UAMEASUNIT_DURATION_CENTURY
, // (4 << 8) + 10, # 34
471 UAMEASUNIT_DURATION_DAY
, // (4 << 8) + 3, # 35
472 UAMEASUNIT_DURATION_HOUR
, // (4 << 8) + 4, # 36
473 UAMEASUNIT_DURATION_MICROSECOND
, // (4 << 8) + 8, # 37
474 UAMEASUNIT_DURATION_MILLISECOND
, // (4 << 8) + 7, # 38
475 UAMEASUNIT_DURATION_MINUTE
, // (4 << 8) + 5, # 39
476 UAMEASUNIT_DURATION_MONTH
, // (4 << 8) + 1, # 40
477 UAMEASUNIT_DURATION_NANOSECOND
, // (4 << 8) + 9, # 41
478 UAMEASUNIT_DURATION_SECOND
, // (4 << 8) + 6, # 42
479 UAMEASUNIT_DURATION_WEEK
, // (4 << 8) + 2, # 43
480 UAMEASUNIT_DURATION_YEAR
, // (4 << 8) + 0, # 44
481 UAMEASUNIT_ELECTRIC_AMPERE
, // (15 << 8) + 0, # 45
482 UAMEASUNIT_ELECTRIC_MILLIAMPERE
, // (15 << 8) + 1, # 46
483 UAMEASUNIT_ELECTRIC_OHM
, // (15 << 8) + 2, # 47
484 UAMEASUNIT_ELECTRIC_VOLT
, // (15 << 8) + 3, # 48
485 UAMEASUNIT_ENERGY_CALORIE
, // (12 << 8) + 0, # 49
486 UAMEASUNIT_ENERGY_FOODCALORIE
, // (12 << 8) + 1, # 50
487 UAMEASUNIT_ENERGY_JOULE
, // (12 << 8) + 2, # 51
488 UAMEASUNIT_ENERGY_KILOCALORIE
, // (12 << 8) + 3, # 52
489 UAMEASUNIT_ENERGY_KILOJOULE
, // (12 << 8) + 4, # 53
490 UAMEASUNIT_ENERGY_KILOWATT_HOUR
, // (12 << 8) + 5, # 54
491 UAMEASUNIT_FREQUENCY_GIGAHERTZ
, // (16 << 8) + 3, # 55
492 UAMEASUNIT_FREQUENCY_HERTZ
, // (16 << 8) + 0, # 56
493 UAMEASUNIT_FREQUENCY_KILOHERTZ
, // (16 << 8) + 1, # 57
494 UAMEASUNIT_FREQUENCY_MEGAHERTZ
, // (16 << 8) + 2, # 58
495 UAMEASUNIT_LENGTH_ASTRONOMICAL_UNIT
, // (5 << 8) + 16, # 59
496 UAMEASUNIT_LENGTH_CENTIMETER
, // (5 << 8) + 1, # 60
497 UAMEASUNIT_LENGTH_DECIMETER
, // (5 << 8) + 10, # 61
498 UAMEASUNIT_LENGTH_FATHOM
, // (5 << 8) + 14, # 62
499 UAMEASUNIT_LENGTH_FOOT
, // (5 << 8) + 5, # 63
500 UAMEASUNIT_LENGTH_FURLONG
, // (5 << 8) + 15, # 64
501 UAMEASUNIT_LENGTH_INCH
, // (5 << 8) + 6, # 65
502 UAMEASUNIT_LENGTH_KILOMETER
, // (5 << 8) + 2, # 66
503 UAMEASUNIT_LENGTH_LIGHT_YEAR
, // (5 << 8) + 9, # 67
504 UAMEASUNIT_LENGTH_METER
, // (5 << 8) + 0, # 68
505 UAMEASUNIT_LENGTH_MICROMETER
, // (5 << 8) + 11, # 69
506 UAMEASUNIT_LENGTH_MILE
, // (5 << 8) + 7, # 70
507 UAMEASUNIT_LENGTH_MILE_SCANDINAVIAN
, // (5 << 8) + 18, # 71
508 UAMEASUNIT_LENGTH_MILLIMETER
, // (5 << 8) + 3, # 72
509 UAMEASUNIT_LENGTH_NANOMETER
, // (5 << 8) + 12, # 73
510 UAMEASUNIT_LENGTH_NAUTICAL_MILE
, // (5 << 8) + 13, # 74
511 UAMEASUNIT_LENGTH_PARSEC
, // (5 << 8) + 17, # 75
512 UAMEASUNIT_LENGTH_PICOMETER
, // (5 << 8) + 4, # 76
513 UAMEASUNIT_LENGTH_POINT
, // (5 << 8) + 19, # 77
514 UAMEASUNIT_LENGTH_YARD
, // (5 << 8) + 8, # 78
515 UAMEASUNIT_LIGHT_LUX
, // (17 << 8) + 0, # 79
516 UAMEASUNIT_MASS_CARAT
, // (6 << 8) + 9, # 80
517 UAMEASUNIT_MASS_GRAM
, // (6 << 8) + 0, # 81
518 UAMEASUNIT_MASS_KILOGRAM
, // (6 << 8) + 1, # 82
519 UAMEASUNIT_MASS_METRIC_TON
, // (6 << 8) + 7, # 83
520 UAMEASUNIT_MASS_MICROGRAM
, // (6 << 8) + 5, # 84
521 UAMEASUNIT_MASS_MILLIGRAM
, // (6 << 8) + 6, # 85
522 UAMEASUNIT_MASS_OUNCE
, // (6 << 8) + 2, # 86
523 UAMEASUNIT_MASS_OUNCE_TROY
, // (6 << 8) + 10, # 87
524 UAMEASUNIT_MASS_POUND
, // (6 << 8) + 3, # 88
525 UAMEASUNIT_MASS_STONE
, // (6 << 8) + 4, # 89
526 UAMEASUNIT_MASS_TON
, // (6 << 8) + 8, # 90
527 UAMEASUNIT_POWER_GIGAWATT
, // (7 << 8) + 5, # 91
528 UAMEASUNIT_POWER_HORSEPOWER
, // (7 << 8) + 2, # 92
529 UAMEASUNIT_POWER_KILOWATT
, // (7 << 8) + 1, # 93
530 UAMEASUNIT_POWER_MEGAWATT
, // (7 << 8) + 4, # 94
531 UAMEASUNIT_POWER_MILLIWATT
, // (7 << 8) + 3, # 95
532 UAMEASUNIT_POWER_WATT
, // (7 << 8) + 0, # 96
533 UAMEASUNIT_PRESSURE_HECTOPASCAL
, // (8 << 8) + 0, # 97
534 UAMEASUNIT_PRESSURE_INCH_HG
, // (8 << 8) + 1, # 98
535 UAMEASUNIT_PRESSURE_MILLIBAR
, // (8 << 8) + 2, # 99
536 UAMEASUNIT_PRESSURE_MILLIMETER_OF_MERCURY
, // (8 << 8) + 3, # 100
537 UAMEASUNIT_PRESSURE_POUND_PER_SQUARE_INCH
, // (8 << 8) + 4, # 101
538 UAMEASUNIT_SPEED_KILOMETER_PER_HOUR
, // (9 << 8) + 1, # 102
539 UAMEASUNIT_SPEED_KNOT
, // (9 << 8) + 3, # 103
540 UAMEASUNIT_SPEED_METER_PER_SECOND
, // (9 << 8) + 0, # 104
541 UAMEASUNIT_SPEED_MILE_PER_HOUR
, // (9 << 8) + 2, # 105
542 UAMEASUNIT_TEMPERATURE_CELSIUS
, // (10 << 8) + 0, # 106
543 UAMEASUNIT_TEMPERATURE_FAHRENHEIT
, // (10 << 8) + 1, # 107
544 UAMEASUNIT_TEMPERATURE_GENERIC
, // (10 << 8) + 3, # 108
545 UAMEASUNIT_TEMPERATURE_KELVIN
, // (10 << 8) + 2, # 109
546 UAMEASUNIT_VOLUME_ACRE_FOOT
, // (11 << 8) + 13, # 110
547 UAMEASUNIT_VOLUME_BUSHEL
, // (11 << 8) + 14, # 111
548 UAMEASUNIT_VOLUME_CENTILITER
, // (11 << 8) + 4, # 112
549 UAMEASUNIT_VOLUME_CUBIC_CENTIMETER
, // (11 << 8) + 8, # 113
550 UAMEASUNIT_VOLUME_CUBIC_FOOT
, // (11 << 8) + 11, # 114
551 UAMEASUNIT_VOLUME_CUBIC_INCH
, // (11 << 8) + 10, # 115
552 UAMEASUNIT_VOLUME_CUBIC_KILOMETER
, // (11 << 8) + 1, # 116
553 UAMEASUNIT_VOLUME_CUBIC_METER
, // (11 << 8) + 9, # 117
554 UAMEASUNIT_VOLUME_CUBIC_MILE
, // (11 << 8) + 2, # 118
555 UAMEASUNIT_VOLUME_CUBIC_YARD
, // (11 << 8) + 12, # 119
556 UAMEASUNIT_VOLUME_CUP
, // (11 << 8) + 18, # 120
557 UAMEASUNIT_VOLUME_CUP_METRIC
, // (11 << 8) + 22, # 121
558 UAMEASUNIT_VOLUME_DECILITER
, // (11 << 8) + 5, # 122
559 UAMEASUNIT_VOLUME_FLUID_OUNCE
, // (11 << 8) + 17, # 123
560 UAMEASUNIT_VOLUME_GALLON
, // (11 << 8) + 21, # 124
561 UAMEASUNIT_VOLUME_GALLON_IMPERIAL
, // (11 << 8) + 24, # 125
562 UAMEASUNIT_VOLUME_HECTOLITER
, // (11 << 8) + 6, # 126
563 UAMEASUNIT_VOLUME_LITER
, // (11 << 8) + 0, # 127
564 UAMEASUNIT_VOLUME_MEGALITER
, // (11 << 8) + 7, # 128
565 UAMEASUNIT_VOLUME_MILLILITER
, // (11 << 8) + 3, # 129
566 UAMEASUNIT_VOLUME_PINT
, // (11 << 8) + 19, # 130
567 UAMEASUNIT_VOLUME_PINT_METRIC
, // (11 << 8) + 23, # 131
568 UAMEASUNIT_VOLUME_QUART
, // (11 << 8) + 20, # 132
569 UAMEASUNIT_VOLUME_TABLESPOON
, // (11 << 8) + 16, # 133
570 UAMEASUNIT_VOLUME_TEASPOON
, // (11 << 8) + 15, # 134
573 static UBool
loadMeasureUnitData(
574 const UResourceBundle
*resource
,
575 MeasureFormatCacheData
&cacheData
,
576 UErrorCode
&status
) {
577 UnitDataSink
sink(cacheData
);
578 ures_getAllItemsWithFallback(resource
, "", sink
, status
);
579 return U_SUCCESS(status
);
582 static UnicodeString
loadNumericDateFormatterPattern(
583 const UResourceBundle
*resource
,
585 UErrorCode
&status
) {
586 UnicodeString result
;
587 if (U_FAILURE(status
)) {
591 chs
.append("durationUnits", status
)
592 .append("/", status
).append(pattern
, status
);
593 LocalUResourceBundlePointer
patternBundle(
594 ures_getByKeyWithFallback(
599 if (U_FAILURE(status
)) {
602 getString(patternBundle
.getAlias(), result
, status
);
603 // Replace 'h' with 'H'
604 int32_t len
= result
.length();
605 UChar
*buffer
= result
.getBuffer(len
);
606 for (int32_t i
= 0; i
< len
; ++i
) {
607 if (buffer
[i
] == 0x68) { // 'h'
608 buffer
[i
] = 0x48; // 'H'
611 result
.releaseBuffer(len
);
615 static NumericDateFormatters
*loadNumericDateFormatters(
616 const UResourceBundle
*resource
,
617 UErrorCode
&status
) {
618 if (U_FAILURE(status
)) {
621 NumericDateFormatters
*result
= new NumericDateFormatters(
622 loadNumericDateFormatterPattern(resource
, "hm", status
),
623 loadNumericDateFormatterPattern(resource
, "ms", status
),
624 loadNumericDateFormatterPattern(resource
, "hms", status
),
626 if (U_FAILURE(status
)) {
633 template<> U_I18N_API
634 const MeasureFormatCacheData
*LocaleCacheKey
<MeasureFormatCacheData
>::createObject(
635 const void * /*unused*/, UErrorCode
&status
) const {
636 const char *localeId
= fLoc
.getName();
637 LocalUResourceBundlePointer
unitsBundle(ures_open(U_ICUDATA_UNIT
, localeId
, &status
));
638 static UNumberFormatStyle currencyStyles
[] = {
639 UNUM_CURRENCY_PLURAL
, UNUM_CURRENCY_ISO
, UNUM_CURRENCY
};
640 LocalPointer
<MeasureFormatCacheData
> result(new MeasureFormatCacheData(), status
);
641 if (U_FAILURE(status
)) {
644 if (!loadMeasureUnitData(
645 unitsBundle
.getAlias(),
650 result
->adoptNumericDateFormatters(loadNumericDateFormatters(
651 unitsBundle
.getAlias(), status
));
652 if (U_FAILURE(status
)) {
656 for (int32_t i
= 0; i
< WIDTH_INDEX_COUNT
; ++i
) {
657 // NumberFormat::createInstance can erase warning codes from status, so pass it
658 // a separate status instance
659 UErrorCode localStatus
= U_ZERO_ERROR
;
660 result
->adoptCurrencyFormat(i
, NumberFormat::createInstance(
661 localeId
, currencyStyles
[i
], localStatus
));
662 if (localStatus
!= U_ZERO_ERROR
) {
663 status
= localStatus
;
665 if (U_FAILURE(status
)) {
669 NumberFormat
*inf
= NumberFormat::createInstance(
670 localeId
, UNUM_DECIMAL
, status
);
671 if (U_FAILURE(status
)) {
674 inf
->setMaximumFractionDigits(0);
675 DecimalFormat
*decfmt
= dynamic_cast<DecimalFormat
*>(inf
);
676 if (decfmt
!= NULL
) {
677 decfmt
->setRoundingMode(DecimalFormat::kRoundDown
);
679 result
->adoptIntegerFormat(inf
);
681 return result
.orphan();
684 static UBool
isTimeUnit(const MeasureUnit
&mu
, const char *tu
) {
685 return uprv_strcmp(mu
.getType(), "duration") == 0 &&
686 uprv_strcmp(mu
.getSubtype(), tu
) == 0;
689 // Converts a composite measure into hours-minutes-seconds and stores at hms
690 // array. [0] is hours; [1] is minutes; [2] is seconds. Returns a bit map of
691 // units found: 1=hours, 2=minutes, 4=seconds. For example, if measures
692 // contains hours-minutes, this function would return 3.
694 // If measures cannot be converted into hours, minutes, seconds or if amounts
695 // are negative, or if hours, minutes, seconds are out of order, returns 0.
696 static int32_t toHMS(
697 const Measure
*measures
,
698 int32_t measureCount
,
700 UErrorCode
&status
) {
701 if (U_FAILURE(status
)) {
705 if (U_FAILURE(status
)) {
708 // We use copy constructor to ensure that both sides of equality operator
709 // are instances of MeasureUnit base class and not a subclass. Otherwise,
710 // operator== will immediately return false.
711 for (int32_t i
= 0; i
< measureCount
; ++i
) {
712 if (isTimeUnit(measures
[i
].getUnit(), "hour")) {
713 // hour must come first
717 hms
[0] = measures
[i
].getNumber();
718 if (hms
[0].getDouble() < 0.0) {
722 } else if (isTimeUnit(measures
[i
].getUnit(), "minute")) {
723 // minute must come after hour
727 hms
[1] = measures
[i
].getNumber();
728 if (hms
[1].getDouble() < 0.0) {
732 } else if (isTimeUnit(measures
[i
].getUnit(), "second")) {
733 // second must come after hour and minute
737 hms
[2] = measures
[i
].getNumber();
738 if (hms
[2].getDouble() < 0.0) {
750 MeasureFormat::MeasureFormat(
751 const Locale
&locale
, UMeasureFormatWidth w
, UErrorCode
&status
)
755 width((w
==UMEASFMT_WIDTH_SHORTER
)? UMEASFMT_WIDTH_SHORT
: w
),
756 stripPatternSpaces(w
==UMEASFMT_WIDTH_SHORTER
),
758 listFormatterStd(NULL
) {
759 initMeasureFormat(locale
, (w
==UMEASFMT_WIDTH_SHORTER
)? UMEASFMT_WIDTH_SHORT
: w
, NULL
, status
);
762 MeasureFormat::MeasureFormat(
763 const Locale
&locale
,
764 UMeasureFormatWidth w
,
765 NumberFormat
*nfToAdopt
,
770 width((w
==UMEASFMT_WIDTH_SHORTER
)? UMEASFMT_WIDTH_SHORT
: w
),
771 stripPatternSpaces(w
==UMEASFMT_WIDTH_SHORTER
),
773 listFormatterStd(NULL
) {
774 initMeasureFormat(locale
, (w
==UMEASFMT_WIDTH_SHORTER
)? UMEASFMT_WIDTH_SHORT
: w
, nfToAdopt
, status
);
777 MeasureFormat::MeasureFormat(const MeasureFormat
&other
) :
780 numberFormat(other
.numberFormat
),
781 pluralRules(other
.pluralRules
),
783 stripPatternSpaces(other
.stripPatternSpaces
),
785 listFormatterStd(NULL
) {
787 numberFormat
->addRef();
788 pluralRules
->addRef();
789 if (other
.listFormatter
!= NULL
) {
790 listFormatter
= new ListFormatter(*other
.listFormatter
);
792 if (other
.listFormatterStd
!= NULL
) {
793 listFormatterStd
= new ListFormatter(*other
.listFormatterStd
);
797 MeasureFormat
&MeasureFormat::operator=(const MeasureFormat
&other
) {
798 if (this == &other
) {
801 Format::operator=(other
);
802 SharedObject::copyPtr(other
.cache
, cache
);
803 SharedObject::copyPtr(other
.numberFormat
, numberFormat
);
804 SharedObject::copyPtr(other
.pluralRules
, pluralRules
);
806 stripPatternSpaces
= other
.stripPatternSpaces
;
807 delete listFormatter
;
808 if (other
.listFormatter
!= NULL
) {
809 listFormatter
= new ListFormatter(*other
.listFormatter
);
811 listFormatter
= NULL
;
813 delete listFormatterStd
;
814 if (other
.listFormatterStd
!= NULL
) {
815 listFormatterStd
= new ListFormatter(*other
.listFormatterStd
);
817 listFormatterStd
= NULL
;
822 MeasureFormat::MeasureFormat() :
826 width(UMEASFMT_WIDTH_SHORT
),
827 stripPatternSpaces(FALSE
),
829 listFormatterStd(NULL
) {
832 MeasureFormat::~MeasureFormat() {
836 if (numberFormat
!= NULL
) {
837 numberFormat
->removeRef();
839 if (pluralRules
!= NULL
) {
840 pluralRules
->removeRef();
842 delete listFormatter
;
843 delete listFormatterStd
;
846 UBool
MeasureFormat::operator==(const Format
&other
) const {
847 if (this == &other
) { // Same object, equal
850 if (!Format::operator==(other
)) {
853 const MeasureFormat
&rhs
= static_cast<const MeasureFormat
&>(other
);
855 // Note: Since the ListFormatter depends only on Locale and width, we
856 // don't have to check it here.
858 // differing widths aren't equivalent
859 if (width
!= rhs
.width
|| stripPatternSpaces
!= rhs
.stripPatternSpaces
) {
862 // Width the same check locales.
863 // We don't need to check locales if both objects have same cache.
864 if (cache
!= rhs
.cache
) {
865 UErrorCode status
= U_ZERO_ERROR
;
866 const char *localeId
= getLocaleID(status
);
867 const char *rhsLocaleId
= rhs
.getLocaleID(status
);
868 if (U_FAILURE(status
)) {
869 // On failure, assume not equal
872 if (uprv_strcmp(localeId
, rhsLocaleId
) != 0) {
876 // Locales same, check NumberFormat if shared data differs.
878 numberFormat
== rhs
.numberFormat
||
879 **numberFormat
== **rhs
.numberFormat
);
882 Format
*MeasureFormat::clone() const {
883 return new MeasureFormat(*this);
886 UnicodeString
&MeasureFormat::format(
887 const Formattable
&obj
,
888 UnicodeString
&appendTo
,
890 UErrorCode
&status
) const {
891 if (U_FAILURE(status
)) return appendTo
;
892 if (obj
.getType() == Formattable::kObject
) {
893 const UObject
* formatObj
= obj
.getObject();
894 const Measure
* amount
= dynamic_cast<const Measure
*>(formatObj
);
895 if (amount
!= NULL
) {
896 return formatMeasure(
897 *amount
, **numberFormat
, appendTo
, pos
, status
);
900 status
= U_ILLEGAL_ARGUMENT_ERROR
;
904 void MeasureFormat::parseObject(
905 const UnicodeString
& /*source*/,
906 Formattable
& /*result*/,
907 ParsePosition
& /*pos*/) const {
911 UnicodeString
&MeasureFormat::formatMeasurePerUnit(
912 const Measure
&measure
,
913 const MeasureUnit
&perUnit
,
914 UnicodeString
&appendTo
,
916 UErrorCode
&status
) const {
917 if (U_FAILURE(status
)) {
920 MeasureUnit
*resolvedUnit
=
921 MeasureUnit::resolveUnitPerUnit(measure
.getUnit(), perUnit
);
922 if (resolvedUnit
!= NULL
) {
923 Measure
newMeasure(measure
.getNumber(), resolvedUnit
, status
);
924 return formatMeasure(
925 newMeasure
, **numberFormat
, appendTo
, pos
, status
);
927 FieldPosition
fpos(pos
.getField());
928 UnicodeString result
;
929 int32_t offset
= withPerUnitAndAppend(
931 measure
, **numberFormat
, result
, fpos
, status
),
935 if (U_FAILURE(status
)) {
938 if (fpos
.getBeginIndex() != 0 || fpos
.getEndIndex() != 0) {
939 pos
.setBeginIndex(fpos
.getBeginIndex() + offset
);
940 pos
.setEndIndex(fpos
.getEndIndex() + offset
);
945 UnicodeString
&MeasureFormat::formatMeasures(
946 const Measure
*measures
,
947 int32_t measureCount
,
948 UnicodeString
&appendTo
,
950 UErrorCode
&status
) const {
951 if (U_FAILURE(status
)) {
954 if (measureCount
== 0) {
957 if (measureCount
== 1) {
958 return formatMeasure(measures
[0], **numberFormat
, appendTo
, pos
, status
);
960 if (width
== UMEASFMT_WIDTH_NUMERIC
) {
962 int32_t bitMap
= toHMS(measures
, measureCount
, hms
, status
);
964 FieldPositionIteratorHandler
handler(NULL
, status
);
965 return formatNumeric(hms
, bitMap
, appendTo
, handler
, status
);
968 if (pos
.getField() != FieldPosition::DONT_CARE
) {
969 return formatMeasuresSlowTrack(
970 measures
, measureCount
, appendTo
, pos
, status
);
972 UnicodeString
*results
= new UnicodeString
[measureCount
];
973 if (results
== NULL
) {
974 status
= U_MEMORY_ALLOCATION_ERROR
;
977 for (int32_t i
= 0; i
< measureCount
; ++i
) {
978 const NumberFormat
*nf
= cache
->getIntegerFormat();
979 if (i
== measureCount
- 1) {
980 nf
= numberFormat
->get();
989 listFormatter
->format(results
, measureCount
, appendTo
, status
);
994 // Apple-specific version for now;
995 // uses FieldPositionIterator* instead of FieldPosition&
996 UnicodeString
&MeasureFormat::formatMeasures(
997 const Measure
*measures
,
998 int32_t measureCount
,
999 UnicodeString
&appendTo
,
1000 FieldPositionIterator
* posIter
,
1001 UErrorCode
&status
) const {
1002 if (U_FAILURE(status
)) {
1005 FieldPositionIteratorHandler
handler(posIter
, status
);
1006 if (measureCount
== 0) {
1009 if (measureCount
== 1) {
1010 int32_t start
= appendTo
.length();
1011 int32_t field
= indexToUAMsasUnit
[measures
[0].getUnit().getIndex()];
1012 FieldPosition
pos(UAMEASFMT_NUMERIC_FIELD_FLAG
); // special field value to request range of entire numeric part
1013 formatMeasure(measures
[0], **numberFormat
, appendTo
, pos
, status
);
1014 handler
.addAttribute(field
, start
, appendTo
.length());
1015 handler
.addAttribute(field
| UAMEASFMT_NUMERIC_FIELD_FLAG
, pos
.getBeginIndex(), pos
.getEndIndex());
1018 if (width
== UMEASFMT_WIDTH_NUMERIC
) {
1020 int32_t bitMap
= toHMS(measures
, measureCount
, hms
, status
);
1022 return formatNumeric(hms
, bitMap
, appendTo
, handler
, status
);
1025 UnicodeString
*results
= new UnicodeString
[measureCount
];
1026 if (results
== NULL
) {
1027 status
= U_MEMORY_ALLOCATION_ERROR
;
1030 FieldPosition
*numPositions
= new FieldPosition
[measureCount
];
1031 if (results
== NULL
) {
1033 status
= U_MEMORY_ALLOCATION_ERROR
;
1037 for (int32_t i
= 0; i
< measureCount
; ++i
) {
1038 const NumberFormat
*nf
= cache
->getIntegerFormat();
1039 if (i
== measureCount
- 1) {
1040 nf
= numberFormat
->get();
1042 numPositions
[i
].setField(UAMEASFMT_NUMERIC_FIELD_FLAG
);
1050 listFormatter
->format(results
, measureCount
, appendTo
, status
);
1051 for (int32_t i
= 0; i
< measureCount
; ++i
) {
1052 int32_t begin
= appendTo
.indexOf(results
[i
]);
1054 int32_t field
= indexToUAMsasUnit
[measures
[i
].getUnit().getIndex()];
1055 handler
.addAttribute(field
, begin
, begin
+ results
[i
].length());
1056 int32_t numPosBegin
= numPositions
[i
].getBeginIndex();
1057 int32_t numPosEnd
= numPositions
[i
].getEndIndex();
1058 if (numPosBegin
>= 0 && numPosEnd
> numPosBegin
) {
1059 handler
.addAttribute(field
| UAMEASFMT_NUMERIC_FIELD_FLAG
, begin
+ numPosBegin
, begin
+ numPosEnd
);
1064 delete [] numPositions
;
1069 UnicodeString
MeasureFormat::getUnitDisplayName(const MeasureUnit
& unit
, UErrorCode
& /*status*/) const {
1070 UMeasureFormatWidth width
= getRegularWidth(this->width
);
1071 const UChar
* const* styleToDnam
= cache
->dnams
[unit
.getIndex()];
1072 const UChar
* dnam
= styleToDnam
[width
];
1074 int32_t fallbackWidth
= cache
->widthFallback
[width
];
1075 dnam
= styleToDnam
[fallbackWidth
];
1078 UnicodeString result
;
1080 result
.setToBogus();
1082 result
.setTo(dnam
, -1);
1087 void MeasureFormat::initMeasureFormat(
1088 const Locale
&locale
,
1089 UMeasureFormatWidth w
,
1090 NumberFormat
*nfToAdopt
,
1091 UErrorCode
&status
) {
1092 static const char *listStyles
[] = {"unit", "unit-short", "unit-narrow"};
1093 LocalPointer
<NumberFormat
> nf(nfToAdopt
);
1094 if (U_FAILURE(status
)) {
1097 const char *name
= locale
.getName();
1098 setLocaleIDs(name
, name
);
1100 UnifiedCache::getByLocale(locale
, cache
, status
);
1101 if (U_FAILURE(status
)) {
1105 const SharedPluralRules
*pr
= PluralRules::createSharedInstance(
1106 locale
, UPLURAL_TYPE_CARDINAL
, status
);
1107 if (U_FAILURE(status
)) {
1110 SharedObject::copyPtr(pr
, pluralRules
);
1113 const SharedNumberFormat
*shared
= NumberFormat::createSharedInstance(
1114 locale
, UNUM_DECIMAL
, status
);
1115 if (U_FAILURE(status
)) {
1118 SharedObject::copyPtr(shared
, numberFormat
);
1119 shared
->removeRef();
1121 adoptNumberFormat(nf
.orphan(), status
);
1122 if (U_FAILURE(status
)) {
1127 if (stripPatternSpaces
) {
1128 w
= UMEASFMT_WIDTH_NARROW
;
1130 delete listFormatter
;
1131 listFormatter
= ListFormatter::createInstance(
1133 listStyles
[getRegularWidth(w
)],
1135 delete listFormatterStd
;
1136 listFormatterStd
= ListFormatter::createInstance(
1142 void MeasureFormat::adoptNumberFormat(
1143 NumberFormat
*nfToAdopt
, UErrorCode
&status
) {
1144 LocalPointer
<NumberFormat
> nf(nfToAdopt
);
1145 if (U_FAILURE(status
)) {
1148 SharedNumberFormat
*shared
= new SharedNumberFormat(nf
.getAlias());
1149 if (shared
== NULL
) {
1150 status
= U_MEMORY_ALLOCATION_ERROR
;
1154 SharedObject::copyPtr(shared
, numberFormat
);
1157 UBool
MeasureFormat::setMeasureFormatLocale(const Locale
&locale
, UErrorCode
&status
) {
1158 if (U_FAILURE(status
) || locale
== getLocale(status
)) {
1161 initMeasureFormat(locale
, width
, NULL
, status
);
1162 return U_SUCCESS(status
);
1165 // Apple-specific for now
1166 UMeasureFormatWidth
MeasureFormat::getWidth() const {
1170 const NumberFormat
&MeasureFormat::getNumberFormat() const {
1171 return **numberFormat
;
1174 const PluralRules
&MeasureFormat::getPluralRules() const {
1175 return **pluralRules
;
1178 Locale
MeasureFormat::getLocale(UErrorCode
&status
) const {
1179 return Format::getLocale(ULOC_VALID_LOCALE
, status
);
1182 const char *MeasureFormat::getLocaleID(UErrorCode
&status
) const {
1183 return Format::getLocaleID(ULOC_VALID_LOCALE
, status
);
1187 // now just re-implement using standard getUnitDisplayName
1188 // so we no longer use cache->getDisplayName
1189 UnicodeString
&MeasureFormat::getUnitName(
1190 const MeasureUnit
* unit
,
1191 UnicodeString
&result
) const {
1192 UErrorCode status
= U_ZERO_ERROR
;
1193 result
= getUnitDisplayName(*unit
, status
); // does not use or set status
1198 UnicodeString
&MeasureFormat::getMultipleUnitNames(
1199 const MeasureUnit
** units
,
1201 UAMeasureNameListStyle listStyle
,
1202 UnicodeString
&result
) const {
1203 if (unitCount
== 0) {
1204 return result
.remove();
1206 if (unitCount
== 1) {
1207 return getUnitName(units
[0], result
);
1209 UnicodeString
*results
= new UnicodeString
[unitCount
];
1210 if (results
!= NULL
) {
1211 for (int32_t i
= 0; i
< unitCount
; ++i
) {
1212 getUnitName(units
[i
], results
[i
]);
1214 UErrorCode status
= U_ZERO_ERROR
;
1215 if (listStyle
== UAMEASNAME_LIST_STANDARD
) {
1216 listFormatterStd
->format(results
, unitCount
, result
, status
);
1218 listFormatter
->format(results
, unitCount
, result
, status
);
1221 if (U_SUCCESS(status
)) {
1225 result
.setToBogus();
1229 UnicodeString
&MeasureFormat::formatMeasure(
1230 const Measure
&measure
,
1231 const NumberFormat
&nf
,
1232 UnicodeString
&appendTo
,
1234 UErrorCode
&status
) const {
1235 if (U_FAILURE(status
)) {
1238 const Formattable
& amtNumber
= measure
.getNumber();
1239 const MeasureUnit
& amtUnit
= measure
.getUnit();
1240 if (isCurrency(amtUnit
)) {
1242 u_charsToUChars(amtUnit
.getSubtype(), isoCode
, 4);
1243 return cache
->getCurrencyFormat(width
)->format(
1244 new CurrencyAmount(amtNumber
, isoCode
, status
),
1249 UnicodeString formattedNumber
;
1250 UBool posForFullNumericPart
= (pos
.getField() == UAMEASFMT_NUMERIC_FIELD_FLAG
);
1251 if (posForFullNumericPart
) {
1252 pos
.setField(FieldPosition::DONT_CARE
);
1254 StandardPlural::Form pluralForm
= QuantityFormatter::selectPlural(
1255 amtNumber
, nf
, **pluralRules
, formattedNumber
, pos
, status
);
1256 if (posForFullNumericPart
) {
1257 pos
.setField(UAMEASFMT_NUMERIC_FIELD_FLAG
);
1258 pos
.setBeginIndex(0);
1259 pos
.setEndIndex(formattedNumber
.length());
1261 const SimpleFormatter
*formatter
= getPluralFormatter(amtUnit
, width
, pluralForm
, status
);
1262 int32_t cur
= appendTo
.length();
1263 QuantityFormatter::format(*formatter
, formattedNumber
, appendTo
, pos
, status
);
1264 if (stripPatternSpaces
) {
1265 const SimpleFormatter
*narrowFormatter
= getPluralFormatter(amtUnit
, UMEASFMT_WIDTH_NARROW
, pluralForm
, status
);
1266 if (U_SUCCESS(status
)) {
1267 // Get the narrow pattern with all {n} set to empty string.
1268 // If there are spaces in that, then do not continue to strip spaces
1269 // (i.e. even in the narrowest form this locale keeps spaces).
1270 UnicodeString narrowPatternNoArgs
= narrowFormatter
->getTextWithNoArguments();
1271 if (narrowPatternNoArgs
.indexOf((UChar
)0x0020) == -1 && narrowPatternNoArgs
.indexOf((UChar
)0x00A0) == -1) {
1272 int32_t end
= appendTo
.length();
1273 for (; cur
< end
; cur
++) {
1274 if (appendTo
.charAt(cur
) == 0x0020) {
1275 appendTo
.remove(cur
, 1);
1276 if (pos
.getBeginIndex() > cur
) {
1277 pos
.setBeginIndex(pos
.getBeginIndex() - 1);
1278 pos
.setEndIndex(pos
.getEndIndex() - 1);
1288 // Formats hours-minutes-seconds as 5:37:23 or similar.
1289 UnicodeString
&MeasureFormat::formatNumeric(
1290 const Formattable
*hms
, // always length 3
1291 int32_t bitMap
, // 1=hourset, 2=minuteset, 4=secondset
1292 UnicodeString
&appendTo
,
1293 FieldPositionHandler
& handler
,
1294 UErrorCode
&status
) const {
1295 if (U_FAILURE(status
)) {
1299 (UDate
) (((uprv_trunc(hms
[0].getDouble(status
)) * 60.0
1300 + uprv_trunc(hms
[1].getDouble(status
))) * 60.0
1301 + uprv_trunc(hms
[2].getDouble(status
))) * 1000.0);
1305 return formatNumeric(
1307 cache
->getNumericDateFormatters()->hourMinuteSecond
,
1315 return formatNumeric(
1317 cache
->getNumericDateFormatters()->minuteSecond
,
1325 return formatNumeric(
1327 cache
->getNumericDateFormatters()->hourMinute
,
1335 status
= U_INTERNAL_PROGRAM_ERROR
;
1342 static void appendRange(
1343 const UnicodeString
&src
,
1346 UnicodeString
&dest
) {
1347 dest
.append(src
, start
, end
- start
);
1350 static void appendRange(
1351 const UnicodeString
&src
,
1353 UnicodeString
&dest
) {
1354 dest
.append(src
, end
, src
.length() - end
);
1357 // Formats time like 5:37:23
1358 UnicodeString
&MeasureFormat::formatNumeric(
1359 UDate date
, // Time since epoch 1:30:00 would be 5400000
1360 const DateFormat
&dateFmt
, // h:mm, m:ss, or h:mm:ss
1361 UDateFormatField smallestField
, // seconds in 5:37:23.5
1362 const Formattable
&smallestAmount
, // 23.5 for 5:37:23.5
1363 UnicodeString
&appendTo
,
1364 FieldPositionHandler
& handler
,
1365 UErrorCode
&status
) const {
1366 if (U_FAILURE(status
)) {
1369 // Format the smallest amount with this object's NumberFormat
1370 UnicodeString smallestAmountFormatted
;
1372 // We keep track of the integer part of smallest amount so that
1373 // we can replace it later so that we get '0:00:09.3' instead of
1375 FieldPosition
intFieldPosition(UNUM_INTEGER_FIELD
);
1376 (*numberFormat
)->format(
1377 smallestAmount
, smallestAmountFormatted
, intFieldPosition
, status
);
1379 intFieldPosition
.getBeginIndex() == 0 &&
1380 intFieldPosition
.getEndIndex() == 0) {
1381 status
= U_INTERNAL_PROGRAM_ERROR
;
1385 // Format time. draft becomes something like '5:30:45'
1386 FieldPositionIterator posIter
;
1387 UnicodeString draft
;
1388 dateFmt
.format(date
, draft
, &posIter
, status
);
1390 int32_t start
= appendTo
.length();
1391 FieldPosition
smallestFieldPosition(smallestField
);
1393 int32_t measField
= -1;
1394 while (posIter
.next(fp
)) {
1395 int32_t dateField
= fp
.getField();
1396 switch (dateField
) {
1397 case UDAT_HOUR_OF_DAY1_FIELD
:
1398 case UDAT_HOUR_OF_DAY0_FIELD
:
1399 case UDAT_HOUR1_FIELD
:
1400 case UDAT_HOUR0_FIELD
:
1401 measField
= UAMEASUNIT_DURATION_HOUR
; break;
1402 case UDAT_MINUTE_FIELD
:
1403 measField
= UAMEASUNIT_DURATION_MINUTE
; break;
1404 case UDAT_SECOND_FIELD
:
1405 measField
= UAMEASUNIT_DURATION_SECOND
; break;
1407 measField
= -1; break;
1409 if (dateField
!= smallestField
) {
1410 if (measField
>= 0) {
1411 handler
.addAttribute(measField
, start
+ fp
.getBeginIndex(), start
+ fp
.getEndIndex());
1412 handler
.addAttribute(measField
| UAMEASFMT_NUMERIC_FIELD_FLAG
, start
+ fp
.getBeginIndex(), start
+ fp
.getEndIndex());
1415 smallestFieldPosition
.setBeginIndex(fp
.getBeginIndex());
1416 smallestFieldPosition
.setEndIndex(fp
.getEndIndex());
1421 // If we find field for smallest amount replace it with the formatted
1422 // smallest amount from above taking care to replace the integer part
1423 // with what is in original time. For example, If smallest amount
1424 // is 9.35s and the formatted time is 0:00:09 then 9.35 becomes 09.35
1425 // and replacing yields 0:00:09.35
1426 if (smallestFieldPosition
.getBeginIndex() != 0 ||
1427 smallestFieldPosition
.getEndIndex() != 0) {
1428 appendRange(draft
, 0, smallestFieldPosition
.getBeginIndex(), appendTo
);
1430 smallestAmountFormatted
,
1432 intFieldPosition
.getBeginIndex(),
1436 smallestFieldPosition
.getBeginIndex(),
1437 smallestFieldPosition
.getEndIndex(),
1440 smallestAmountFormatted
,
1441 intFieldPosition
.getEndIndex(),
1445 smallestFieldPosition
.getEndIndex(),
1447 handler
.addAttribute(measField
, start
+ smallestFieldPosition
.getBeginIndex(), appendTo
.length());
1448 handler
.addAttribute(measField
| UAMEASFMT_NUMERIC_FIELD_FLAG
, start
+ smallestFieldPosition
.getBeginIndex(), appendTo
.length());
1450 appendTo
.append(draft
);
1455 const SimpleFormatter
*MeasureFormat::getFormatterOrNull(
1456 const MeasureUnit
&unit
, UMeasureFormatWidth width
, int32_t index
) const {
1457 width
= getRegularWidth(width
);
1458 SimpleFormatter
*const (*unitPatterns
)[MeasureFormatCacheData::PATTERN_COUNT
] =
1459 &cache
->patterns
[unit
.getIndex()][0];
1460 if (unitPatterns
[width
][index
] != NULL
) {
1461 return unitPatterns
[width
][index
];
1463 int32_t fallbackWidth
= cache
->widthFallback
[width
];
1464 if (fallbackWidth
!= UMEASFMT_WIDTH_COUNT
&& unitPatterns
[fallbackWidth
][index
] != NULL
) {
1465 return unitPatterns
[fallbackWidth
][index
];
1470 const SimpleFormatter
*MeasureFormat::getFormatter(
1471 const MeasureUnit
&unit
, UMeasureFormatWidth width
, int32_t index
,
1472 UErrorCode
&errorCode
) const {
1473 if (U_FAILURE(errorCode
)) {
1476 const SimpleFormatter
*pattern
= getFormatterOrNull(unit
, width
, index
);
1477 if (pattern
== NULL
) {
1478 errorCode
= U_MISSING_RESOURCE_ERROR
;
1483 const SimpleFormatter
*MeasureFormat::getPluralFormatter(
1484 const MeasureUnit
&unit
, UMeasureFormatWidth width
, int32_t index
,
1485 UErrorCode
&errorCode
) const {
1486 if (U_FAILURE(errorCode
)) {
1489 if (index
!= StandardPlural::OTHER
) {
1490 const SimpleFormatter
*pattern
= getFormatterOrNull(unit
, width
, index
);
1491 if (pattern
!= NULL
) {
1495 return getFormatter(unit
, width
, StandardPlural::OTHER
, errorCode
);
1498 const SimpleFormatter
*MeasureFormat::getPerFormatter(
1499 UMeasureFormatWidth width
,
1500 UErrorCode
&status
) const {
1501 if (U_FAILURE(status
)) {
1504 width
= getRegularWidth(width
);
1505 const SimpleFormatter
* perFormatters
= cache
->perFormatters
;
1506 if (perFormatters
[width
].getArgumentLimit() == 2) {
1507 return &perFormatters
[width
];
1509 int32_t fallbackWidth
= cache
->widthFallback
[width
];
1510 if (fallbackWidth
!= UMEASFMT_WIDTH_COUNT
&&
1511 perFormatters
[fallbackWidth
].getArgumentLimit() == 2) {
1512 return &perFormatters
[fallbackWidth
];
1514 status
= U_MISSING_RESOURCE_ERROR
;
1518 int32_t MeasureFormat::withPerUnitAndAppend(
1519 const UnicodeString
&formatted
,
1520 const MeasureUnit
&perUnit
,
1521 UnicodeString
&appendTo
,
1522 UErrorCode
&status
) const {
1523 int32_t offset
= -1;
1524 if (U_FAILURE(status
)) {
1527 const SimpleFormatter
*perUnitFormatter
=
1528 getFormatterOrNull(perUnit
, width
, MeasureFormatCacheData::PER_UNIT_INDEX
);
1529 if (perUnitFormatter
!= NULL
) {
1530 const UnicodeString
*params
[] = {&formatted
};
1531 perUnitFormatter
->formatAndAppend(
1533 UPRV_LENGTHOF(params
),
1540 const SimpleFormatter
*perFormatter
= getPerFormatter(width
, status
);
1541 const SimpleFormatter
*pattern
=
1542 getPluralFormatter(perUnit
, width
, StandardPlural::ONE
, status
);
1543 if (U_FAILURE(status
)) {
1546 UnicodeString perUnitString
= pattern
->getTextWithNoArguments();
1547 perUnitString
.trim();
1548 const UnicodeString
*params
[] = {&formatted
, &perUnitString
};
1549 perFormatter
->formatAndAppend(
1551 UPRV_LENGTHOF(params
),
1559 UnicodeString
&MeasureFormat::formatMeasuresSlowTrack(
1560 const Measure
*measures
,
1561 int32_t measureCount
,
1562 UnicodeString
& appendTo
,
1564 UErrorCode
& status
) const {
1565 if (U_FAILURE(status
)) {
1568 FieldPosition
dontCare(FieldPosition::DONT_CARE
);
1569 FieldPosition
fpos(pos
.getField());
1570 UnicodeString
*results
= new UnicodeString
[measureCount
];
1571 int32_t fieldPositionFoundIndex
= -1;
1572 for (int32_t i
= 0; i
< measureCount
; ++i
) {
1573 const NumberFormat
*nf
= cache
->getIntegerFormat();
1574 if (i
== measureCount
- 1) {
1575 nf
= numberFormat
->get();
1577 if (fieldPositionFoundIndex
== -1) {
1578 formatMeasure(measures
[i
], *nf
, results
[i
], fpos
, status
);
1579 if (U_FAILURE(status
)) {
1583 if (fpos
.getBeginIndex() != 0 || fpos
.getEndIndex() != 0) {
1584 fieldPositionFoundIndex
= i
;
1587 formatMeasure(measures
[i
], *nf
, results
[i
], dontCare
, status
);
1591 listFormatter
->format(
1595 fieldPositionFoundIndex
,
1598 if (U_FAILURE(status
)) {
1603 pos
.setBeginIndex(fpos
.getBeginIndex() + offset
);
1604 pos
.setEndIndex(fpos
.getEndIndex() + offset
);
1610 MeasureFormat
* U_EXPORT2
MeasureFormat::createCurrencyFormat(const Locale
& locale
,
1612 CurrencyFormat
* fmt
= NULL
;
1613 if (U_SUCCESS(ec
)) {
1614 fmt
= new CurrencyFormat(locale
, ec
);
1615 if (U_FAILURE(ec
)) {
1623 MeasureFormat
* U_EXPORT2
MeasureFormat::createCurrencyFormat(UErrorCode
& ec
) {
1624 if (U_FAILURE(ec
)) {
1627 return MeasureFormat::createCurrencyFormat(Locale::getDefault(), ec
);
1632 #endif /* #if !UCONFIG_NO_FORMATTING */