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"
50 static constexpr int32_t PER_UNIT_INDEX
= StandardPlural::COUNT
;
51 static constexpr int32_t PATTERN_COUNT
= PER_UNIT_INDEX
+ 1;
52 static constexpr int32_t MEAS_UNIT_COUNT
= 138; // see assertion in MeasureFormatCacheData constructor
53 static constexpr int32_t WIDTH_INDEX_COUNT
= UMEASFMT_WIDTH_NARROW
+ 1;
55 UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MeasureFormat
)
57 // Used to format durations like 5:47 or 21:35:42.
58 class NumericDateFormatters
: public UMemory
{
61 SimpleDateFormat hourMinute
;
64 SimpleDateFormat minuteSecond
;
66 // formats like H:mm:ss
67 SimpleDateFormat hourMinuteSecond
;
69 // Constructor that takes the actual patterns for hour-minute,
70 // minute-second, and hour-minute-second respectively.
71 NumericDateFormatters(
72 const UnicodeString
&hm
,
73 const UnicodeString
&ms
,
74 const UnicodeString
&hms
,
76 hourMinute(hm
, status
),
77 minuteSecond(ms
, status
),
78 hourMinuteSecond(hms
, status
) {
79 const TimeZone
*gmt
= TimeZone::getGMT();
80 hourMinute
.setTimeZone(*gmt
);
81 minuteSecond
.setTimeZone(*gmt
);
82 hourMinuteSecond
.setTimeZone(*gmt
);
85 NumericDateFormatters(const NumericDateFormatters
&other
);
86 NumericDateFormatters
&operator=(const NumericDateFormatters
&other
);
89 static UMeasureFormatWidth
getRegularWidth(UMeasureFormatWidth width
) {
90 if (width
>= WIDTH_INDEX_COUNT
) {
91 return UMEASFMT_WIDTH_NARROW
;
97 * Instances contain all MeasureFormat specific data for a particular locale.
98 * This data is cached. It is never copied, but is shared via shared pointers.
100 * Note: We might change the cache data to have an array[WIDTH_INDEX_COUNT] of
101 * complete sets of unit & per patterns,
102 * to correspond to the resource data and its aliases.
104 * TODO: Maybe store more sparsely in general, with pointers rather than potentially-empty objects.
106 class MeasureFormatCacheData
: public SharedObject
{
110 * Redirection data from root-bundle, top-level sideways aliases.
111 * - UMEASFMT_WIDTH_COUNT: initial value, just fall back to root
112 * - UMEASFMT_WIDTH_WIDE/SHORT/NARROW: sideways alias for missing data
114 UMeasureFormatWidth widthFallback
[WIDTH_INDEX_COUNT
];
115 /** Measure unit -> format width -> array of patterns ("{0} meters") (plurals + PER_UNIT_INDEX) */
116 SimpleFormatter
* patterns
[MEAS_UNIT_COUNT
][WIDTH_INDEX_COUNT
][PATTERN_COUNT
];
117 const UChar
* dnams
[MEAS_UNIT_COUNT
][WIDTH_INDEX_COUNT
];
118 SimpleFormatter perFormatters
[WIDTH_INDEX_COUNT
];
120 MeasureFormatCacheData();
121 virtual ~MeasureFormatCacheData();
123 UBool
hasPerFormatter(int32_t width
) const {
124 // TODO: Create a more obvious way to test if the per-formatter has been set?
125 // Use pointers, check for NULL? Or add an isValid() method?
126 return perFormatters
[width
].getArgumentLimit() == 2;
129 void adoptCurrencyFormat(int32_t widthIndex
, NumberFormat
*nfToAdopt
) {
130 delete currencyFormats
[widthIndex
];
131 currencyFormats
[widthIndex
] = nfToAdopt
;
133 const NumberFormat
*getCurrencyFormat(UMeasureFormatWidth width
) const {
134 return currencyFormats
[getRegularWidth(width
)];
136 void adoptIntegerFormat(NumberFormat
*nfToAdopt
) {
137 delete integerFormat
;
138 integerFormat
= nfToAdopt
;
140 const NumberFormat
*getIntegerFormat() const {
141 return integerFormat
;
143 void adoptNumericDateFormatters(NumericDateFormatters
*formattersToAdopt
) {
144 delete numericDateFormatters
;
145 numericDateFormatters
= formattersToAdopt
;
147 const NumericDateFormatters
*getNumericDateFormatters() const {
148 return numericDateFormatters
;
152 NumberFormat
* currencyFormats
[WIDTH_INDEX_COUNT
];
153 NumberFormat
* integerFormat
;
154 NumericDateFormatters
* numericDateFormatters
;
156 MeasureFormatCacheData(const MeasureFormatCacheData
&other
);
157 MeasureFormatCacheData
&operator=(const MeasureFormatCacheData
&other
);
160 MeasureFormatCacheData::MeasureFormatCacheData()
161 : integerFormat(nullptr), numericDateFormatters(nullptr) {
162 // Please update MEAS_UNIT_COUNT if it gets out of sync with the true count!
163 U_ASSERT(MEAS_UNIT_COUNT
== MeasureUnit::getIndexCount());
165 for (int32_t i
= 0; i
< WIDTH_INDEX_COUNT
; ++i
) {
166 widthFallback
[i
] = UMEASFMT_WIDTH_COUNT
;
168 memset(&patterns
[0][0][0], 0, sizeof(patterns
));
169 memset(&dnams
[0][0], 0, sizeof(dnams
));
170 memset(currencyFormats
, 0, sizeof(currencyFormats
));
173 MeasureFormatCacheData::~MeasureFormatCacheData() {
174 for (int32_t i
= 0; i
< UPRV_LENGTHOF(currencyFormats
); ++i
) {
175 delete currencyFormats
[i
];
177 for (int32_t i
= 0; i
< MEAS_UNIT_COUNT
; ++i
) {
178 for (int32_t j
= 0; j
< WIDTH_INDEX_COUNT
; ++j
) {
179 for (int32_t k
= 0; k
< PATTERN_COUNT
; ++k
) {
180 delete patterns
[i
][j
][k
];
184 // Note: the contents of 'dnams' are pointers into the resource bundle
185 delete integerFormat
;
186 delete numericDateFormatters
;
189 static UBool
isCurrency(const MeasureUnit
&unit
) {
190 return (uprv_strcmp(unit
.getType(), "currency") == 0);
193 static UBool
getString(
194 const UResourceBundle
*resource
,
195 UnicodeString
&result
,
196 UErrorCode
&status
) {
198 const UChar
*resStr
= ures_getString(resource
, &len
, &status
);
199 if (U_FAILURE(status
)) {
202 result
.setTo(TRUE
, resStr
, len
);
208 static const UChar g_LOCALE_units
[] = {
209 0x2F, 0x4C, 0x4F, 0x43, 0x41, 0x4C, 0x45, 0x2F,
210 0x75, 0x6E, 0x69, 0x74, 0x73
212 static const UChar gShort
[] = { 0x53, 0x68, 0x6F, 0x72, 0x74 };
213 static const UChar gNarrow
[] = { 0x4E, 0x61, 0x72, 0x72, 0x6F, 0x77 };
216 * Sink for enumerating all of the measurement unit display names.
217 * Contains inner sink classes, each one corresponding to a type of resource table.
218 * The outer sink handles the top-level units, unitsNarrow, and unitsShort tables.
220 * More specific bundles (en_GB) are enumerated before their parents (en_001, en, root):
221 * Only store a value if it is still missing, that is, it has not been overridden.
223 * C++: Each inner sink class has a reference to the main outer sink.
224 * Java: Use non-static inner classes instead.
226 struct UnitDataSink
: public ResourceSink
{
229 MeasureFormatCacheData
&cacheData
;
231 // Path to current data.
232 UMeasureFormatWidth width
;
236 UnitDataSink(MeasureFormatCacheData
&outputData
)
237 : cacheData(outputData
),
238 width(UMEASFMT_WIDTH_COUNT
), type(NULL
), unitIndex(0) {}
241 void setFormatterIfAbsent(int32_t index
, const ResourceValue
&value
,
242 int32_t minPlaceholders
, UErrorCode
&errorCode
) {
243 U_ASSERT(unitIndex
< MEAS_UNIT_COUNT
);
244 U_ASSERT(width
< WIDTH_INDEX_COUNT
);
245 U_ASSERT(index
< PATTERN_COUNT
);
246 SimpleFormatter
**patterns
= &cacheData
.patterns
[unitIndex
][width
][0];
247 if (U_SUCCESS(errorCode
) && patterns
[index
] == NULL
) {
248 if (minPlaceholders
>= 0) {
249 patterns
[index
] = new SimpleFormatter(
250 value
.getUnicodeString(errorCode
), minPlaceholders
, 1, errorCode
);
252 if (U_SUCCESS(errorCode
) && patterns
[index
] == NULL
) {
253 errorCode
= U_MEMORY_ALLOCATION_ERROR
;
258 void setDnamIfAbsent(const ResourceValue
&value
, UErrorCode
& errorCode
) {
259 U_ASSERT(unitIndex
< MEAS_UNIT_COUNT
);
260 U_ASSERT(width
< WIDTH_INDEX_COUNT
);
261 if (cacheData
.dnams
[unitIndex
][width
] == NULL
) {
263 cacheData
.dnams
[unitIndex
][width
] = value
.getString(length
, errorCode
);
268 * Consume a display pattern. For example,
269 * unitsShort/duration/hour contains other{"{0} hrs"}.
271 void consumePattern(const char *key
, const ResourceValue
&value
, UErrorCode
&errorCode
) {
272 if (U_FAILURE(errorCode
)) { return; }
273 if (uprv_strcmp(key
, "dnam") == 0) {
274 // The display name for the unit in the current width.
275 setDnamIfAbsent(value
, errorCode
);
276 } else if (uprv_strcmp(key
, "per") == 0) {
277 // For example, "{0}/h".
278 setFormatterIfAbsent(PER_UNIT_INDEX
, value
, 1, errorCode
);
280 // The key must be one of the plural form strings. For example:
283 setFormatterIfAbsent(StandardPlural::indexFromString(key
, errorCode
), value
, 0,
289 * Consume a table of per-unit tables. For example,
290 * unitsShort/duration contains tables for duration-unit subtypes day & hour.
292 void consumeSubtypeTable(const char *key
, ResourceValue
&value
, UErrorCode
&errorCode
) {
293 if (U_FAILURE(errorCode
)) { return; }
294 unitIndex
= MeasureUnit::internalGetIndexForTypeAndSubtype(type
, key
);
296 // TODO: How to handle unexpected data?
297 // See http://bugs.icu-project.org/trac/ticket/12597
301 // We no longer handle units like "coordinate" here (which do not have plural variants)
302 if (value
.getType() == URES_TABLE
) {
303 // Units that have plural variants
304 ResourceTable patternTableTable
= value
.getTable(errorCode
);
305 if (U_FAILURE(errorCode
)) { return; }
306 for (int i
= 0; patternTableTable
.getKeyAndValue(i
, key
, value
); ++i
) {
307 consumePattern(key
, value
, errorCode
);
310 // TODO: How to handle unexpected data?
311 // See http://bugs.icu-project.org/trac/ticket/12597
317 * Consume compound x-per-y display pattern. For example,
318 * unitsShort/compound/per may be "{0}/{1}".
320 void consumeCompoundPattern(const char *key
, const ResourceValue
&value
, UErrorCode
&errorCode
) {
321 if (U_SUCCESS(errorCode
) && uprv_strcmp(key
, "per") == 0) {
322 cacheData
.perFormatters
[width
].
323 applyPatternMinMaxArguments(value
.getUnicodeString(errorCode
), 2, 2, errorCode
);
328 * Consume a table of unit type tables. For example,
329 * unitsShort contains tables for area & duration.
330 * It also contains a table for the compound/per pattern.
332 void consumeUnitTypesTable(const char *key
, ResourceValue
&value
, UErrorCode
&errorCode
) {
333 if (U_FAILURE(errorCode
)) { return; }
334 if (uprv_strcmp(key
, "currency") == 0) {
336 } else if (uprv_strcmp(key
, "compound") == 0) {
337 if (!cacheData
.hasPerFormatter(width
)) {
338 ResourceTable compoundTable
= value
.getTable(errorCode
);
339 if (U_FAILURE(errorCode
)) { return; }
340 for (int i
= 0; compoundTable
.getKeyAndValue(i
, key
, value
); ++i
) {
341 consumeCompoundPattern(key
, value
, errorCode
);
344 } else if (uprv_strcmp(key
, "coordinate") == 0) {
345 // special handling but we need to determine what that is
348 ResourceTable subtypeTable
= value
.getTable(errorCode
);
349 if (U_FAILURE(errorCode
)) { return; }
350 for (int i
= 0; subtypeTable
.getKeyAndValue(i
, key
, value
); ++i
) {
351 consumeSubtypeTable(key
, value
, errorCode
);
356 void consumeAlias(const char *key
, const ResourceValue
&value
, UErrorCode
&errorCode
) {
357 // Handle aliases like
358 // units:alias{"/LOCALE/unitsShort"}
359 // which should only occur in the root bundle.
360 UMeasureFormatWidth sourceWidth
= widthFromKey(key
);
361 if (sourceWidth
== UMEASFMT_WIDTH_COUNT
) {
362 // Alias from something we don't care about.
365 UMeasureFormatWidth targetWidth
= widthFromAlias(value
, errorCode
);
366 if (targetWidth
== UMEASFMT_WIDTH_COUNT
) {
367 // We do not recognize what to fall back to.
368 errorCode
= U_INVALID_FORMAT_ERROR
;
371 // Check that we do not fall back to another fallback.
372 if (cacheData
.widthFallback
[targetWidth
] != UMEASFMT_WIDTH_COUNT
) {
373 errorCode
= U_INVALID_FORMAT_ERROR
;
376 cacheData
.widthFallback
[sourceWidth
] = targetWidth
;
379 void consumeTable(const char *key
, ResourceValue
&value
, UErrorCode
&errorCode
) {
380 if (U_SUCCESS(errorCode
) && (width
= widthFromKey(key
)) != UMEASFMT_WIDTH_COUNT
) {
381 ResourceTable unitTypesTable
= value
.getTable(errorCode
);
382 if (U_FAILURE(errorCode
)) { return; }
383 for (int i
= 0; unitTypesTable
.getKeyAndValue(i
, key
, value
); ++i
) {
384 consumeUnitTypesTable(key
, value
, errorCode
);
389 static UMeasureFormatWidth
widthFromKey(const char *key
) {
390 if (uprv_strncmp(key
, "units", 5) == 0) {
393 return UMEASFMT_WIDTH_WIDE
;
394 } else if (uprv_strcmp(key
, "Short") == 0) {
395 return UMEASFMT_WIDTH_SHORT
;
396 } else if (uprv_strcmp(key
, "Narrow") == 0) {
397 return UMEASFMT_WIDTH_NARROW
;
400 return UMEASFMT_WIDTH_COUNT
;
403 static UMeasureFormatWidth
widthFromAlias(const ResourceValue
&value
, UErrorCode
&errorCode
) {
405 const UChar
*s
= value
.getAliasString(length
, errorCode
);
406 // For example: "/LOCALE/unitsShort"
407 if (U_SUCCESS(errorCode
) && length
>= 13 && u_memcmp(s
, g_LOCALE_units
, 13) == 0) {
411 return UMEASFMT_WIDTH_WIDE
;
412 } else if (u_strCompare(s
, length
, gShort
, 5, FALSE
) == 0) {
413 return UMEASFMT_WIDTH_SHORT
;
414 } else if (u_strCompare(s
, length
, gNarrow
, 6, FALSE
) == 0) {
415 return UMEASFMT_WIDTH_NARROW
;
418 return UMEASFMT_WIDTH_COUNT
;
421 virtual void put(const char *key
, ResourceValue
&value
, UBool
/*noFallback*/,
422 UErrorCode
&errorCode
) {
423 // Main entry point to sink
424 ResourceTable widthsTable
= value
.getTable(errorCode
);
425 if (U_FAILURE(errorCode
)) { return; }
426 for (int i
= 0; widthsTable
.getKeyAndValue(i
, key
, value
); ++i
) {
427 if (value
.getType() == URES_ALIAS
) {
428 consumeAlias(key
, value
, errorCode
);
430 consumeTable(key
, value
, errorCode
);
436 // Virtual destructors must be defined out of line.
437 UnitDataSink::~UnitDataSink() {}
441 static const UAMeasureUnit indexToUAMsasUnit
[] = {
442 // UAMeasureUnit // UAMeasUnit vals # MeasUnit.getIndex()
443 UAMEASUNIT_ACCELERATION_G_FORCE
, // (0 << 8) + 0, # 0
444 UAMEASUNIT_ACCELERATION_METER_PER_SECOND_SQUARED
, // (0 << 8) + 1, # 1
445 UAMEASUNIT_ANGLE_ARC_MINUTE
, // (1 << 8) + 1, # 2
446 UAMEASUNIT_ANGLE_ARC_SECOND
, // (1 << 8) + 2, # 3
447 UAMEASUNIT_ANGLE_DEGREE
, // (1 << 8) + 0, # 4
448 UAMEASUNIT_ANGLE_RADIAN
, // (1 << 8) + 3, # 5
449 UAMEASUNIT_ANGLE_REVOLUTION
, // (1 << 8) + 4, # 6
450 UAMEASUNIT_AREA_ACRE
, // (2 << 8) + 4, # 7
451 UAMEASUNIT_AREA_HECTARE
, // (2 << 8) + 5, # 8
452 UAMEASUNIT_AREA_SQUARE_CENTIMETER
, // (2 << 8) + 6, # 9
453 UAMEASUNIT_AREA_SQUARE_FOOT
, // (2 << 8) + 2, # 10
454 UAMEASUNIT_AREA_SQUARE_INCH
, // (2 << 8) + 7, # 11
455 UAMEASUNIT_AREA_SQUARE_KILOMETER
, // (2 << 8) + 1, # 12
456 UAMEASUNIT_AREA_SQUARE_METER
, // (2 << 8) + 0, # 13
457 UAMEASUNIT_AREA_SQUARE_MILE
, // (2 << 8) + 3, # 14
458 UAMEASUNIT_AREA_SQUARE_YARD
, // (2 << 8) + 8, # 15
459 UAMEASUNIT_CONCENTRATION_KARAT
, // (18 << 8) + 0, # 16
460 UAMEASUNIT_CONCENTRATION_MILLIGRAM_PER_DECILITER
, // (18 << 8) + 1, # 17
461 UAMEASUNIT_CONCENTRATION_MILLIMOLE_PER_LITER
, // (18 << 8) + 2, # 18
462 UAMEASUNIT_CONCENTRATION_PART_PER_MILLION
, // (18 << 8) + 3, # 19
463 UAMEASUNIT_CONSUMPTION_LITER_PER_100_KILOMETERs
, // (13 << 8) + 2, # 20
464 UAMEASUNIT_CONSUMPTION_LITER_PER_KILOMETER
, // (13 << 8) + 0, # 21
465 UAMEASUNIT_CONSUMPTION_MILE_PER_GALLON
, // (13 << 8) + 1, # 22
466 UAMEASUNIT_CONSUMPTION_MILE_PER_GALLON_IMPERIAL
, // (13 << 8) + 3, # 23
467 UAMEASUNIT_DIGITAL_BIT
, // (14 << 8) + 0, # 24
468 UAMEASUNIT_DIGITAL_BYTE
, // (14 << 8) + 1, # 25
469 UAMEASUNIT_DIGITAL_GIGABIT
, // (14 << 8) + 2, # 26
470 UAMEASUNIT_DIGITAL_GIGABYTE
, // (14 << 8) + 3, # 27
471 UAMEASUNIT_DIGITAL_KILOBIT
, // (14 << 8) + 4, # 28
472 UAMEASUNIT_DIGITAL_KILOBYTE
, // (14 << 8) + 5, # 29
473 UAMEASUNIT_DIGITAL_MEGABIT
, // (14 << 8) + 6, # 30
474 UAMEASUNIT_DIGITAL_MEGABYTE
, // (14 << 8) + 7, # 31
475 UAMEASUNIT_DIGITAL_TERABIT
, // (14 << 8) + 8, # 32
476 UAMEASUNIT_DIGITAL_TERABYTE
, // (14 << 8) + 9, # 33
477 UAMEASUNIT_DURATION_CENTURY
, // (4 << 8) + 10, # 34
478 UAMEASUNIT_DURATION_DAY
, // (4 << 8) + 3, # 35
479 UAMEASUNIT_DURATION_HOUR
, // (4 << 8) + 4, # 36
480 UAMEASUNIT_DURATION_MICROSECOND
, // (4 << 8) + 8, # 37
481 UAMEASUNIT_DURATION_MILLISECOND
, // (4 << 8) + 7, # 38
482 UAMEASUNIT_DURATION_MINUTE
, // (4 << 8) + 5, # 39
483 UAMEASUNIT_DURATION_MONTH
, // (4 << 8) + 1, # 40
484 UAMEASUNIT_DURATION_NANOSECOND
, // (4 << 8) + 9, # 41
485 UAMEASUNIT_DURATION_SECOND
, // (4 << 8) + 6, # 42
486 UAMEASUNIT_DURATION_WEEK
, // (4 << 8) + 2, # 43
487 UAMEASUNIT_DURATION_YEAR
, // (4 << 8) + 0, # 44
488 UAMEASUNIT_ELECTRIC_AMPERE
, // (15 << 8) + 0, # 45
489 UAMEASUNIT_ELECTRIC_MILLIAMPERE
, // (15 << 8) + 1, # 46
490 UAMEASUNIT_ELECTRIC_OHM
, // (15 << 8) + 2, # 47
491 UAMEASUNIT_ELECTRIC_VOLT
, // (15 << 8) + 3, # 48
492 UAMEASUNIT_ENERGY_CALORIE
, // (12 << 8) + 0, # 49
493 UAMEASUNIT_ENERGY_FOODCALORIE
, // (12 << 8) + 1, # 50
494 UAMEASUNIT_ENERGY_JOULE
, // (12 << 8) + 2, # 51
495 UAMEASUNIT_ENERGY_KILOCALORIE
, // (12 << 8) + 3, # 52
496 UAMEASUNIT_ENERGY_KILOJOULE
, // (12 << 8) + 4, # 53
497 UAMEASUNIT_ENERGY_KILOWATT_HOUR
, // (12 << 8) + 5, # 54
498 UAMEASUNIT_FREQUENCY_GIGAHERTZ
, // (16 << 8) + 3, # 55
499 UAMEASUNIT_FREQUENCY_HERTZ
, // (16 << 8) + 0, # 56
500 UAMEASUNIT_FREQUENCY_KILOHERTZ
, // (16 << 8) + 1, # 57
501 UAMEASUNIT_FREQUENCY_MEGAHERTZ
, // (16 << 8) + 2, # 58
502 UAMEASUNIT_LENGTH_ASTRONOMICAL_UNIT
, // (5 << 8) + 16, # 59
503 UAMEASUNIT_LENGTH_CENTIMETER
, // (5 << 8) + 1, # 60
504 UAMEASUNIT_LENGTH_DECIMETER
, // (5 << 8) + 10, # 61
505 UAMEASUNIT_LENGTH_FATHOM
, // (5 << 8) + 14, # 62
506 UAMEASUNIT_LENGTH_FOOT
, // (5 << 8) + 5, # 63
507 UAMEASUNIT_LENGTH_FURLONG
, // (5 << 8) + 15, # 64
508 UAMEASUNIT_LENGTH_INCH
, // (5 << 8) + 6, # 65
509 UAMEASUNIT_LENGTH_KILOMETER
, // (5 << 8) + 2, # 66
510 UAMEASUNIT_LENGTH_LIGHT_YEAR
, // (5 << 8) + 9, # 67
511 UAMEASUNIT_LENGTH_METER
, // (5 << 8) + 0, # 68
512 UAMEASUNIT_LENGTH_MICROMETER
, // (5 << 8) + 11, # 69
513 UAMEASUNIT_LENGTH_MILE
, // (5 << 8) + 7, # 70
514 UAMEASUNIT_LENGTH_MILE_SCANDINAVIAN
, // (5 << 8) + 18, # 71
515 UAMEASUNIT_LENGTH_MILLIMETER
, // (5 << 8) + 3, # 72
516 UAMEASUNIT_LENGTH_NANOMETER
, // (5 << 8) + 12, # 73
517 UAMEASUNIT_LENGTH_NAUTICAL_MILE
, // (5 << 8) + 13, # 74
518 UAMEASUNIT_LENGTH_PARSEC
, // (5 << 8) + 17, # 75
519 UAMEASUNIT_LENGTH_PICOMETER
, // (5 << 8) + 4, # 76
520 UAMEASUNIT_LENGTH_POINT
, // (5 << 8) + 19, # 77
521 UAMEASUNIT_LENGTH_YARD
, // (5 << 8) + 8, # 78
522 UAMEASUNIT_LIGHT_LUX
, // (17 << 8) + 0, # 79
523 UAMEASUNIT_MASS_CARAT
, // (6 << 8) + 9, # 80
524 UAMEASUNIT_MASS_GRAM
, // (6 << 8) + 0, # 81
525 UAMEASUNIT_MASS_KILOGRAM
, // (6 << 8) + 1, # 82
526 UAMEASUNIT_MASS_METRIC_TON
, // (6 << 8) + 7, # 83
527 UAMEASUNIT_MASS_MICROGRAM
, // (6 << 8) + 5, # 84
528 UAMEASUNIT_MASS_MILLIGRAM
, // (6 << 8) + 6, # 85
529 UAMEASUNIT_MASS_OUNCE
, // (6 << 8) + 2, # 86
530 UAMEASUNIT_MASS_OUNCE_TROY
, // (6 << 8) + 10, # 87
531 UAMEASUNIT_MASS_POUND
, // (6 << 8) + 3, # 88
532 UAMEASUNIT_MASS_STONE
, // (6 << 8) + 4, # 89
533 UAMEASUNIT_MASS_TON
, // (6 << 8) + 8, # 90
534 UAMEASUNIT_POWER_GIGAWATT
, // (7 << 8) + 5, # 91
535 UAMEASUNIT_POWER_HORSEPOWER
, // (7 << 8) + 2, # 92
536 UAMEASUNIT_POWER_KILOWATT
, // (7 << 8) + 1, # 93
537 UAMEASUNIT_POWER_MEGAWATT
, // (7 << 8) + 4, # 94
538 UAMEASUNIT_POWER_MILLIWATT
, // (7 << 8) + 3, # 95
539 UAMEASUNIT_POWER_WATT
, // (7 << 8) + 0, # 96
540 UAMEASUNIT_PRESSURE_HECTOPASCAL
, // (8 << 8) + 0, # 97
541 UAMEASUNIT_PRESSURE_INCH_HG
, // (8 << 8) + 1, # 98
542 UAMEASUNIT_PRESSURE_MILLIBAR
, // (8 << 8) + 2, # 99
543 UAMEASUNIT_PRESSURE_MILLIMETER_OF_MERCURY
, // (8 << 8) + 3, # 100
544 UAMEASUNIT_PRESSURE_POUND_PER_SQUARE_INCH
, // (8 << 8) + 4, # 101
545 UAMEASUNIT_SPEED_KILOMETER_PER_HOUR
, // (9 << 8) + 1, # 102
546 UAMEASUNIT_SPEED_KNOT
, // (9 << 8) + 3, # 103
547 UAMEASUNIT_SPEED_METER_PER_SECOND
, // (9 << 8) + 0, # 104
548 UAMEASUNIT_SPEED_MILE_PER_HOUR
, // (9 << 8) + 2, # 105
549 UAMEASUNIT_TEMPERATURE_CELSIUS
, // (10 << 8) + 0, # 106
550 UAMEASUNIT_TEMPERATURE_FAHRENHEIT
, // (10 << 8) + 1, # 107
551 UAMEASUNIT_TEMPERATURE_GENERIC
, // (10 << 8) + 3, # 108
552 UAMEASUNIT_TEMPERATURE_KELVIN
, // (10 << 8) + 2, # 109
553 UAMEASUNIT_VOLUME_ACRE_FOOT
, // (11 << 8) + 13, # 110
554 UAMEASUNIT_VOLUME_BUSHEL
, // (11 << 8) + 14, # 111
555 UAMEASUNIT_VOLUME_CENTILITER
, // (11 << 8) + 4, # 112
556 UAMEASUNIT_VOLUME_CUBIC_CENTIMETER
, // (11 << 8) + 8, # 113
557 UAMEASUNIT_VOLUME_CUBIC_FOOT
, // (11 << 8) + 11, # 114
558 UAMEASUNIT_VOLUME_CUBIC_INCH
, // (11 << 8) + 10, # 115
559 UAMEASUNIT_VOLUME_CUBIC_KILOMETER
, // (11 << 8) + 1, # 116
560 UAMEASUNIT_VOLUME_CUBIC_METER
, // (11 << 8) + 9, # 117
561 UAMEASUNIT_VOLUME_CUBIC_MILE
, // (11 << 8) + 2, # 118
562 UAMEASUNIT_VOLUME_CUBIC_YARD
, // (11 << 8) + 12, # 119
563 UAMEASUNIT_VOLUME_CUP
, // (11 << 8) + 18, # 120
564 UAMEASUNIT_VOLUME_CUP_METRIC
, // (11 << 8) + 22, # 121
565 UAMEASUNIT_VOLUME_DECILITER
, // (11 << 8) + 5, # 122
566 UAMEASUNIT_VOLUME_FLUID_OUNCE
, // (11 << 8) + 17, # 123
567 UAMEASUNIT_VOLUME_GALLON
, // (11 << 8) + 21, # 124
568 UAMEASUNIT_VOLUME_GALLON_IMPERIAL
, // (11 << 8) + 24, # 125
569 UAMEASUNIT_VOLUME_HECTOLITER
, // (11 << 8) + 6, # 126
570 UAMEASUNIT_VOLUME_LITER
, // (11 << 8) + 0, # 127
571 UAMEASUNIT_VOLUME_MEGALITER
, // (11 << 8) + 7, # 128
572 UAMEASUNIT_VOLUME_MILLILITER
, // (11 << 8) + 3, # 129
573 UAMEASUNIT_VOLUME_PINT
, // (11 << 8) + 19, # 130
574 UAMEASUNIT_VOLUME_PINT_METRIC
, // (11 << 8) + 23, # 131
575 UAMEASUNIT_VOLUME_QUART
, // (11 << 8) + 20, # 132
576 UAMEASUNIT_VOLUME_TABLESPOON
, // (11 << 8) + 16, # 133
577 UAMEASUNIT_VOLUME_TEASPOON
, // (11 << 8) + 15, # 134
580 static UBool
loadMeasureUnitData(
581 const UResourceBundle
*resource
,
582 MeasureFormatCacheData
&cacheData
,
583 UErrorCode
&status
) {
584 UnitDataSink
sink(cacheData
);
585 ures_getAllItemsWithFallback(resource
, "", sink
, status
);
586 return U_SUCCESS(status
);
589 static UnicodeString
loadNumericDateFormatterPattern(
590 const UResourceBundle
*resource
,
592 UErrorCode
&status
) {
593 UnicodeString result
;
594 if (U_FAILURE(status
)) {
598 chs
.append("durationUnits", status
)
599 .append("/", status
).append(pattern
, status
);
600 LocalUResourceBundlePointer
patternBundle(
601 ures_getByKeyWithFallback(
606 if (U_FAILURE(status
)) {
609 getString(patternBundle
.getAlias(), result
, status
);
610 // Replace 'h' with 'H'
611 int32_t len
= result
.length();
612 UChar
*buffer
= result
.getBuffer(len
);
613 for (int32_t i
= 0; i
< len
; ++i
) {
614 if (buffer
[i
] == 0x68) { // 'h'
615 buffer
[i
] = 0x48; // 'H'
618 result
.releaseBuffer(len
);
622 static NumericDateFormatters
*loadNumericDateFormatters(
623 const UResourceBundle
*resource
,
624 UErrorCode
&status
) {
625 if (U_FAILURE(status
)) {
628 NumericDateFormatters
*result
= new NumericDateFormatters(
629 loadNumericDateFormatterPattern(resource
, "hm", status
),
630 loadNumericDateFormatterPattern(resource
, "ms", status
),
631 loadNumericDateFormatterPattern(resource
, "hms", status
),
633 if (U_FAILURE(status
)) {
640 template<> U_I18N_API
641 const MeasureFormatCacheData
*LocaleCacheKey
<MeasureFormatCacheData
>::createObject(
642 const void * /*unused*/, UErrorCode
&status
) const {
643 const char *localeId
= fLoc
.getName();
644 LocalUResourceBundlePointer
unitsBundle(ures_open(U_ICUDATA_UNIT
, localeId
, &status
));
645 static UNumberFormatStyle currencyStyles
[] = {
646 UNUM_CURRENCY_PLURAL
, UNUM_CURRENCY_ISO
, UNUM_CURRENCY
};
647 LocalPointer
<MeasureFormatCacheData
> result(new MeasureFormatCacheData(), status
);
648 if (U_FAILURE(status
)) {
651 if (!loadMeasureUnitData(
652 unitsBundle
.getAlias(),
657 result
->adoptNumericDateFormatters(loadNumericDateFormatters(
658 unitsBundle
.getAlias(), status
));
659 if (U_FAILURE(status
)) {
663 for (int32_t i
= 0; i
< WIDTH_INDEX_COUNT
; ++i
) {
664 // NumberFormat::createInstance can erase warning codes from status, so pass it
665 // a separate status instance
666 UErrorCode localStatus
= U_ZERO_ERROR
;
667 result
->adoptCurrencyFormat(i
, NumberFormat::createInstance(
668 localeId
, currencyStyles
[i
], localStatus
));
669 if (localStatus
!= U_ZERO_ERROR
) {
670 status
= localStatus
;
672 if (U_FAILURE(status
)) {
676 NumberFormat
*inf
= NumberFormat::createInstance(
677 localeId
, UNUM_DECIMAL
, status
);
678 if (U_FAILURE(status
)) {
681 inf
->setMaximumFractionDigits(0);
682 DecimalFormat
*decfmt
= dynamic_cast<DecimalFormat
*>(inf
);
683 if (decfmt
!= NULL
) {
684 decfmt
->setRoundingMode(DecimalFormat::kRoundDown
);
686 result
->adoptIntegerFormat(inf
);
688 return result
.orphan();
691 static UBool
isTimeUnit(const MeasureUnit
&mu
, const char *tu
) {
692 return uprv_strcmp(mu
.getType(), "duration") == 0 &&
693 uprv_strcmp(mu
.getSubtype(), tu
) == 0;
696 // Converts a composite measure into hours-minutes-seconds and stores at hms
697 // array. [0] is hours; [1] is minutes; [2] is seconds. Returns a bit map of
698 // units found: 1=hours, 2=minutes, 4=seconds. For example, if measures
699 // contains hours-minutes, this function would return 3.
701 // If measures cannot be converted into hours, minutes, seconds or if amounts
702 // are negative, or if hours, minutes, seconds are out of order, returns 0.
703 static int32_t toHMS(
704 const Measure
*measures
,
705 int32_t measureCount
,
707 UErrorCode
&status
) {
708 if (U_FAILURE(status
)) {
712 if (U_FAILURE(status
)) {
715 // We use copy constructor to ensure that both sides of equality operator
716 // are instances of MeasureUnit base class and not a subclass. Otherwise,
717 // operator== will immediately return false.
718 for (int32_t i
= 0; i
< measureCount
; ++i
) {
719 if (isTimeUnit(measures
[i
].getUnit(), "hour")) {
720 // hour must come first
724 hms
[0] = measures
[i
].getNumber();
725 if (hms
[0].getDouble() < 0.0) {
729 } else if (isTimeUnit(measures
[i
].getUnit(), "minute")) {
730 // minute must come after hour
734 hms
[1] = measures
[i
].getNumber();
735 if (hms
[1].getDouble() < 0.0) {
739 } else if (isTimeUnit(measures
[i
].getUnit(), "second")) {
740 // second must come after hour and minute
744 hms
[2] = measures
[i
].getNumber();
745 if (hms
[2].getDouble() < 0.0) {
757 MeasureFormat::MeasureFormat(
758 const Locale
&locale
, UMeasureFormatWidth w
, UErrorCode
&status
)
762 width((w
==UMEASFMT_WIDTH_SHORTER
)? UMEASFMT_WIDTH_SHORT
: w
),
763 stripPatternSpaces(w
==UMEASFMT_WIDTH_SHORTER
),
765 listFormatterStd(NULL
) {
766 initMeasureFormat(locale
, (w
==UMEASFMT_WIDTH_SHORTER
)? UMEASFMT_WIDTH_SHORT
: w
, NULL
, status
);
769 MeasureFormat::MeasureFormat(
770 const Locale
&locale
,
771 UMeasureFormatWidth w
,
772 NumberFormat
*nfToAdopt
,
777 width((w
==UMEASFMT_WIDTH_SHORTER
)? UMEASFMT_WIDTH_SHORT
: w
),
778 stripPatternSpaces(w
==UMEASFMT_WIDTH_SHORTER
),
780 listFormatterStd(NULL
) {
781 initMeasureFormat(locale
, (w
==UMEASFMT_WIDTH_SHORTER
)? UMEASFMT_WIDTH_SHORT
: w
, nfToAdopt
, status
);
784 MeasureFormat::MeasureFormat(const MeasureFormat
&other
) :
787 numberFormat(other
.numberFormat
),
788 pluralRules(other
.pluralRules
),
790 stripPatternSpaces(other
.stripPatternSpaces
),
792 listFormatterStd(NULL
) {
794 numberFormat
->addRef();
795 pluralRules
->addRef();
796 if (other
.listFormatter
!= NULL
) {
797 listFormatter
= new ListFormatter(*other
.listFormatter
);
799 if (other
.listFormatterStd
!= NULL
) {
800 listFormatterStd
= new ListFormatter(*other
.listFormatterStd
);
804 MeasureFormat
&MeasureFormat::operator=(const MeasureFormat
&other
) {
805 if (this == &other
) {
808 Format::operator=(other
);
809 SharedObject::copyPtr(other
.cache
, cache
);
810 SharedObject::copyPtr(other
.numberFormat
, numberFormat
);
811 SharedObject::copyPtr(other
.pluralRules
, pluralRules
);
813 stripPatternSpaces
= other
.stripPatternSpaces
;
814 delete listFormatter
;
815 if (other
.listFormatter
!= NULL
) {
816 listFormatter
= new ListFormatter(*other
.listFormatter
);
818 listFormatter
= NULL
;
820 delete listFormatterStd
;
821 if (other
.listFormatterStd
!= NULL
) {
822 listFormatterStd
= new ListFormatter(*other
.listFormatterStd
);
824 listFormatterStd
= NULL
;
829 MeasureFormat::MeasureFormat() :
833 width(UMEASFMT_WIDTH_SHORT
),
834 stripPatternSpaces(FALSE
),
836 listFormatterStd(NULL
) {
839 MeasureFormat::~MeasureFormat() {
843 if (numberFormat
!= NULL
) {
844 numberFormat
->removeRef();
846 if (pluralRules
!= NULL
) {
847 pluralRules
->removeRef();
849 delete listFormatter
;
850 delete listFormatterStd
;
853 UBool
MeasureFormat::operator==(const Format
&other
) const {
854 if (this == &other
) { // Same object, equal
857 if (!Format::operator==(other
)) {
860 const MeasureFormat
&rhs
= static_cast<const MeasureFormat
&>(other
);
862 // Note: Since the ListFormatter depends only on Locale and width, we
863 // don't have to check it here.
865 // differing widths aren't equivalent
866 if (width
!= rhs
.width
|| stripPatternSpaces
!= rhs
.stripPatternSpaces
) {
869 // Width the same check locales.
870 // We don't need to check locales if both objects have same cache.
871 if (cache
!= rhs
.cache
) {
872 UErrorCode status
= U_ZERO_ERROR
;
873 const char *localeId
= getLocaleID(status
);
874 const char *rhsLocaleId
= rhs
.getLocaleID(status
);
875 if (U_FAILURE(status
)) {
876 // On failure, assume not equal
879 if (uprv_strcmp(localeId
, rhsLocaleId
) != 0) {
883 // Locales same, check NumberFormat if shared data differs.
885 numberFormat
== rhs
.numberFormat
||
886 **numberFormat
== **rhs
.numberFormat
);
889 Format
*MeasureFormat::clone() const {
890 return new MeasureFormat(*this);
893 UnicodeString
&MeasureFormat::format(
894 const Formattable
&obj
,
895 UnicodeString
&appendTo
,
897 UErrorCode
&status
) const {
898 if (U_FAILURE(status
)) return appendTo
;
899 if (obj
.getType() == Formattable::kObject
) {
900 const UObject
* formatObj
= obj
.getObject();
901 const Measure
* amount
= dynamic_cast<const Measure
*>(formatObj
);
902 if (amount
!= NULL
) {
903 return formatMeasure(
904 *amount
, **numberFormat
, appendTo
, pos
, status
);
907 status
= U_ILLEGAL_ARGUMENT_ERROR
;
911 void MeasureFormat::parseObject(
912 const UnicodeString
& /*source*/,
913 Formattable
& /*result*/,
914 ParsePosition
& /*pos*/) const {
918 UnicodeString
&MeasureFormat::formatMeasurePerUnit(
919 const Measure
&measure
,
920 const MeasureUnit
&perUnit
,
921 UnicodeString
&appendTo
,
923 UErrorCode
&status
) const {
924 if (U_FAILURE(status
)) {
927 bool isResolved
= false;
928 MeasureUnit resolvedUnit
=
929 MeasureUnit::resolveUnitPerUnit(measure
.getUnit(), perUnit
, &isResolved
);
931 Measure
newMeasure(measure
.getNumber(), new MeasureUnit(resolvedUnit
), status
);
932 return formatMeasure(
933 newMeasure
, **numberFormat
, appendTo
, pos
, status
);
935 FieldPosition
fpos(pos
.getField());
936 UnicodeString result
;
937 int32_t offset
= withPerUnitAndAppend(
939 measure
, **numberFormat
, result
, fpos
, status
),
943 if (U_FAILURE(status
)) {
946 if (fpos
.getBeginIndex() != 0 || fpos
.getEndIndex() != 0) {
947 pos
.setBeginIndex(fpos
.getBeginIndex() + offset
);
948 pos
.setEndIndex(fpos
.getEndIndex() + offset
);
953 UnicodeString
&MeasureFormat::formatMeasures(
954 const Measure
*measures
,
955 int32_t measureCount
,
956 UnicodeString
&appendTo
,
958 UErrorCode
&status
) const {
959 if (U_FAILURE(status
)) {
962 if (measureCount
== 0) {
965 if (measureCount
== 1) {
966 return formatMeasure(measures
[0], **numberFormat
, appendTo
, pos
, status
);
968 if (width
== UMEASFMT_WIDTH_NUMERIC
) {
970 int32_t bitMap
= toHMS(measures
, measureCount
, hms
, status
);
972 FieldPositionIteratorHandler
handler(NULL
, status
);
973 return formatNumeric(hms
, bitMap
, appendTo
, handler
, status
);
976 if (pos
.getField() != FieldPosition::DONT_CARE
) {
977 return formatMeasuresSlowTrack(
978 measures
, measureCount
, appendTo
, pos
, status
);
980 UnicodeString
*results
= new UnicodeString
[measureCount
];
981 if (results
== NULL
) {
982 status
= U_MEMORY_ALLOCATION_ERROR
;
985 for (int32_t i
= 0; i
< measureCount
; ++i
) {
986 const NumberFormat
*nf
= cache
->getIntegerFormat();
987 if (i
== measureCount
- 1) {
988 nf
= numberFormat
->get();
997 listFormatter
->format(results
, measureCount
, appendTo
, status
);
1002 // Apple-specific version for now;
1003 // uses FieldPositionIterator* instead of FieldPosition&
1004 UnicodeString
&MeasureFormat::formatMeasures(
1005 const Measure
*measures
,
1006 int32_t measureCount
,
1007 UnicodeString
&appendTo
,
1008 FieldPositionIterator
* posIter
,
1009 UErrorCode
&status
) const {
1010 if (U_FAILURE(status
)) {
1013 FieldPositionIteratorHandler
handler(posIter
, status
);
1014 if (measureCount
== 0) {
1017 if (measureCount
== 1) {
1018 int32_t start
= appendTo
.length();
1019 int32_t field
= indexToUAMsasUnit
[measures
[0].getUnit().getIndex()];
1020 FieldPosition
pos(UAMEASFMT_NUMERIC_FIELD_FLAG
); // special field value to request range of entire numeric part
1021 formatMeasure(measures
[0], **numberFormat
, appendTo
, pos
, status
);
1022 handler
.addAttribute(field
, start
, appendTo
.length());
1023 handler
.addAttribute(field
| UAMEASFMT_NUMERIC_FIELD_FLAG
, pos
.getBeginIndex(), pos
.getEndIndex());
1026 if (width
== UMEASFMT_WIDTH_NUMERIC
) {
1028 int32_t bitMap
= toHMS(measures
, measureCount
, hms
, status
);
1030 return formatNumeric(hms
, bitMap
, appendTo
, handler
, status
);
1033 UnicodeString
*results
= new UnicodeString
[measureCount
];
1034 if (results
== NULL
) {
1035 status
= U_MEMORY_ALLOCATION_ERROR
;
1038 FieldPosition
*numPositions
= new FieldPosition
[measureCount
];
1039 if (results
== NULL
) {
1041 status
= U_MEMORY_ALLOCATION_ERROR
;
1045 for (int32_t i
= 0; i
< measureCount
; ++i
) {
1046 const NumberFormat
*nf
= cache
->getIntegerFormat();
1047 if (i
== measureCount
- 1) {
1048 nf
= numberFormat
->get();
1050 numPositions
[i
].setField(UAMEASFMT_NUMERIC_FIELD_FLAG
);
1058 listFormatter
->format(results
, measureCount
, appendTo
, status
);
1059 for (int32_t i
= 0; i
< measureCount
; ++i
) {
1060 int32_t begin
= appendTo
.indexOf(results
[i
]);
1062 int32_t field
= indexToUAMsasUnit
[measures
[i
].getUnit().getIndex()];
1063 handler
.addAttribute(field
, begin
, begin
+ results
[i
].length());
1064 int32_t numPosBegin
= numPositions
[i
].getBeginIndex();
1065 int32_t numPosEnd
= numPositions
[i
].getEndIndex();
1066 if (numPosBegin
>= 0 && numPosEnd
> numPosBegin
) {
1067 handler
.addAttribute(field
| UAMEASFMT_NUMERIC_FIELD_FLAG
, begin
+ numPosBegin
, begin
+ numPosEnd
);
1072 delete [] numPositions
;
1077 UnicodeString
MeasureFormat::getUnitDisplayName(const MeasureUnit
& unit
, UErrorCode
& /*status*/) const {
1078 UMeasureFormatWidth width
= getRegularWidth(this->width
);
1079 const UChar
* const* styleToDnam
= cache
->dnams
[unit
.getIndex()];
1080 const UChar
* dnam
= styleToDnam
[width
];
1082 int32_t fallbackWidth
= cache
->widthFallback
[width
];
1083 dnam
= styleToDnam
[fallbackWidth
];
1086 UnicodeString result
;
1088 result
.setToBogus();
1090 result
.setTo(dnam
, -1);
1095 void MeasureFormat::initMeasureFormat(
1096 const Locale
&locale
,
1097 UMeasureFormatWidth w
,
1098 NumberFormat
*nfToAdopt
,
1099 UErrorCode
&status
) {
1100 static const char *listStyles
[] = {"unit", "unit-short", "unit-narrow"};
1101 LocalPointer
<NumberFormat
> nf(nfToAdopt
);
1102 if (U_FAILURE(status
)) {
1105 const char *name
= locale
.getName();
1106 setLocaleIDs(name
, name
);
1108 UnifiedCache::getByLocale(locale
, cache
, status
);
1109 if (U_FAILURE(status
)) {
1113 const SharedPluralRules
*pr
= PluralRules::createSharedInstance(
1114 locale
, UPLURAL_TYPE_CARDINAL
, status
);
1115 if (U_FAILURE(status
)) {
1118 SharedObject::copyPtr(pr
, pluralRules
);
1121 const SharedNumberFormat
*shared
= NumberFormat::createSharedInstance(
1122 locale
, UNUM_DECIMAL
, status
);
1123 if (U_FAILURE(status
)) {
1126 SharedObject::copyPtr(shared
, numberFormat
);
1127 shared
->removeRef();
1129 adoptNumberFormat(nf
.orphan(), status
);
1130 if (U_FAILURE(status
)) {
1135 if (stripPatternSpaces
) {
1136 w
= UMEASFMT_WIDTH_NARROW
;
1138 delete listFormatter
;
1139 listFormatter
= ListFormatter::createInstance(
1141 listStyles
[getRegularWidth(w
)],
1143 delete listFormatterStd
;
1144 listFormatterStd
= ListFormatter::createInstance(
1150 void MeasureFormat::adoptNumberFormat(
1151 NumberFormat
*nfToAdopt
, UErrorCode
&status
) {
1152 LocalPointer
<NumberFormat
> nf(nfToAdopt
);
1153 if (U_FAILURE(status
)) {
1156 SharedNumberFormat
*shared
= new SharedNumberFormat(nf
.getAlias());
1157 if (shared
== NULL
) {
1158 status
= U_MEMORY_ALLOCATION_ERROR
;
1162 SharedObject::copyPtr(shared
, numberFormat
);
1165 UBool
MeasureFormat::setMeasureFormatLocale(const Locale
&locale
, UErrorCode
&status
) {
1166 if (U_FAILURE(status
) || locale
== getLocale(status
)) {
1169 initMeasureFormat(locale
, width
, NULL
, status
);
1170 return U_SUCCESS(status
);
1173 // Apple-specific for now
1174 UMeasureFormatWidth
MeasureFormat::getWidth() const {
1178 const NumberFormat
&MeasureFormat::getNumberFormat() const {
1179 return **numberFormat
;
1182 const PluralRules
&MeasureFormat::getPluralRules() const {
1183 return **pluralRules
;
1186 Locale
MeasureFormat::getLocale(UErrorCode
&status
) const {
1187 return Format::getLocale(ULOC_VALID_LOCALE
, status
);
1190 const char *MeasureFormat::getLocaleID(UErrorCode
&status
) const {
1191 return Format::getLocaleID(ULOC_VALID_LOCALE
, status
);
1195 // now just re-implement using standard getUnitDisplayName
1196 // so we no longer use cache->getDisplayName
1197 UnicodeString
&MeasureFormat::getUnitName(
1198 const MeasureUnit
* unit
,
1199 UnicodeString
&result
) const {
1200 UErrorCode status
= U_ZERO_ERROR
;
1201 result
= getUnitDisplayName(*unit
, status
); // does not use or set status
1206 UnicodeString
&MeasureFormat::getMultipleUnitNames(
1207 const MeasureUnit
** units
,
1209 UAMeasureNameListStyle listStyle
,
1210 UnicodeString
&result
) const {
1211 if (unitCount
== 0) {
1212 return result
.remove();
1214 if (unitCount
== 1) {
1215 return getUnitName(units
[0], result
);
1217 UnicodeString
*results
= new UnicodeString
[unitCount
];
1218 if (results
!= NULL
) {
1219 for (int32_t i
= 0; i
< unitCount
; ++i
) {
1220 getUnitName(units
[i
], results
[i
]);
1222 UErrorCode status
= U_ZERO_ERROR
;
1223 if (listStyle
== UAMEASNAME_LIST_STANDARD
) {
1224 listFormatterStd
->format(results
, unitCount
, result
, status
);
1226 listFormatter
->format(results
, unitCount
, result
, status
);
1229 if (U_SUCCESS(status
)) {
1233 result
.setToBogus();
1237 UnicodeString
&MeasureFormat::formatMeasure(
1238 const Measure
&measure
,
1239 const NumberFormat
&nf
,
1240 UnicodeString
&appendTo
,
1242 UErrorCode
&status
) const {
1243 if (U_FAILURE(status
)) {
1246 const Formattable
& amtNumber
= measure
.getNumber();
1247 const MeasureUnit
& amtUnit
= measure
.getUnit();
1248 if (isCurrency(amtUnit
)) {
1250 u_charsToUChars(amtUnit
.getSubtype(), isoCode
, 4);
1251 return cache
->getCurrencyFormat(width
)->format(
1252 new CurrencyAmount(amtNumber
, isoCode
, status
),
1257 UnicodeString formattedNumber
;
1258 UBool posForFullNumericPart
= (pos
.getField() == UAMEASFMT_NUMERIC_FIELD_FLAG
);
1259 if (posForFullNumericPart
) {
1260 pos
.setField(FieldPosition::DONT_CARE
);
1262 StandardPlural::Form pluralForm
= QuantityFormatter::selectPlural(
1263 amtNumber
, nf
, **pluralRules
, formattedNumber
, pos
, status
);
1264 if (posForFullNumericPart
) {
1265 pos
.setField(UAMEASFMT_NUMERIC_FIELD_FLAG
);
1266 pos
.setBeginIndex(0);
1267 pos
.setEndIndex(formattedNumber
.length());
1269 const SimpleFormatter
*formatter
= getPluralFormatter(amtUnit
, width
, pluralForm
, status
);
1270 int32_t cur
= appendTo
.length();
1271 QuantityFormatter::format(*formatter
, formattedNumber
, appendTo
, pos
, status
);
1272 if (stripPatternSpaces
) {
1273 const SimpleFormatter
*narrowFormatter
= getPluralFormatter(amtUnit
, UMEASFMT_WIDTH_NARROW
, pluralForm
, status
);
1274 if (U_SUCCESS(status
)) {
1275 // Get the narrow pattern with all {n} set to empty string.
1276 // If there are spaces in that, then do not continue to strip spaces
1277 // (i.e. even in the narrowest form this locale keeps spaces).
1278 UnicodeString narrowPatternNoArgs
= narrowFormatter
->getTextWithNoArguments();
1279 if (narrowPatternNoArgs
.indexOf((UChar
)0x0020) == -1 && narrowPatternNoArgs
.indexOf((UChar
)0x00A0) == -1) {
1280 int32_t end
= appendTo
.length();
1281 for (; cur
< end
; cur
++) {
1282 if (appendTo
.charAt(cur
) == 0x0020) {
1283 appendTo
.remove(cur
, 1);
1284 if (pos
.getBeginIndex() > cur
) {
1285 pos
.setBeginIndex(pos
.getBeginIndex() - 1);
1286 pos
.setEndIndex(pos
.getEndIndex() - 1);
1296 // Formats hours-minutes-seconds as 5:37:23 or similar.
1297 UnicodeString
&MeasureFormat::formatNumeric(
1298 const Formattable
*hms
, // always length 3
1299 int32_t bitMap
, // 1=hourset, 2=minuteset, 4=secondset
1300 UnicodeString
&appendTo
,
1301 FieldPositionHandler
& handler
,
1302 UErrorCode
&status
) const {
1303 if (U_FAILURE(status
)) {
1307 (UDate
) (((uprv_trunc(hms
[0].getDouble(status
)) * 60.0
1308 + uprv_trunc(hms
[1].getDouble(status
))) * 60.0
1309 + uprv_trunc(hms
[2].getDouble(status
))) * 1000.0);
1313 return formatNumeric(
1315 cache
->getNumericDateFormatters()->hourMinuteSecond
,
1323 return formatNumeric(
1325 cache
->getNumericDateFormatters()->minuteSecond
,
1333 return formatNumeric(
1335 cache
->getNumericDateFormatters()->hourMinute
,
1343 status
= U_INTERNAL_PROGRAM_ERROR
;
1350 static void appendRange(
1351 const UnicodeString
&src
,
1354 UnicodeString
&dest
) {
1355 dest
.append(src
, start
, end
- start
);
1358 static void appendRange(
1359 const UnicodeString
&src
,
1361 UnicodeString
&dest
) {
1362 dest
.append(src
, end
, src
.length() - end
);
1365 // Formats time like 5:37:23
1366 UnicodeString
&MeasureFormat::formatNumeric(
1367 UDate date
, // Time since epoch 1:30:00 would be 5400000
1368 const DateFormat
&dateFmt
, // h:mm, m:ss, or h:mm:ss
1369 UDateFormatField smallestField
, // seconds in 5:37:23.5
1370 const Formattable
&smallestAmount
, // 23.5 for 5:37:23.5
1371 UnicodeString
&appendTo
,
1372 FieldPositionHandler
& handler
,
1373 UErrorCode
&status
) const {
1374 if (U_FAILURE(status
)) {
1377 // Format the smallest amount with this object's NumberFormat
1378 UnicodeString smallestAmountFormatted
;
1380 // We keep track of the integer part of smallest amount so that
1381 // we can replace it later so that we get '0:00:09.3' instead of
1383 FieldPosition
intFieldPosition(UNUM_INTEGER_FIELD
);
1384 (*numberFormat
)->format(
1385 smallestAmount
, smallestAmountFormatted
, intFieldPosition
, status
);
1387 intFieldPosition
.getBeginIndex() == 0 &&
1388 intFieldPosition
.getEndIndex() == 0) {
1389 status
= U_INTERNAL_PROGRAM_ERROR
;
1393 // Format time. draft becomes something like '5:30:45'
1394 // #13606: DateFormat is not thread-safe, but MeasureFormat advertises itself as thread-safe.
1395 FieldPositionIterator posIter
;
1396 UnicodeString draft
;
1397 static UMutex dateFmtMutex
= U_MUTEX_INITIALIZER
;
1398 umtx_lock(&dateFmtMutex
);
1399 dateFmt
.format(date
, draft
, &posIter
, status
);
1400 umtx_unlock(&dateFmtMutex
);
1402 int32_t start
= appendTo
.length();
1403 FieldPosition
smallestFieldPosition(smallestField
);
1405 int32_t measField
= -1;
1406 while (posIter
.next(fp
)) {
1407 int32_t dateField
= fp
.getField();
1408 switch (dateField
) {
1409 case UDAT_HOUR_OF_DAY1_FIELD
:
1410 case UDAT_HOUR_OF_DAY0_FIELD
:
1411 case UDAT_HOUR1_FIELD
:
1412 case UDAT_HOUR0_FIELD
:
1413 measField
= UAMEASUNIT_DURATION_HOUR
; break;
1414 case UDAT_MINUTE_FIELD
:
1415 measField
= UAMEASUNIT_DURATION_MINUTE
; break;
1416 case UDAT_SECOND_FIELD
:
1417 measField
= UAMEASUNIT_DURATION_SECOND
; break;
1419 measField
= -1; break;
1421 if (dateField
!= smallestField
) {
1422 if (measField
>= 0) {
1423 handler
.addAttribute(measField
, start
+ fp
.getBeginIndex(), start
+ fp
.getEndIndex());
1424 handler
.addAttribute(measField
| UAMEASFMT_NUMERIC_FIELD_FLAG
, start
+ fp
.getBeginIndex(), start
+ fp
.getEndIndex());
1427 smallestFieldPosition
.setBeginIndex(fp
.getBeginIndex());
1428 smallestFieldPosition
.setEndIndex(fp
.getEndIndex());
1433 // If we find field for smallest amount replace it with the formatted
1434 // smallest amount from above taking care to replace the integer part
1435 // with what is in original time. For example, If smallest amount
1436 // is 9.35s and the formatted time is 0:00:09 then 9.35 becomes 09.35
1437 // and replacing yields 0:00:09.35
1438 if (smallestFieldPosition
.getBeginIndex() != 0 ||
1439 smallestFieldPosition
.getEndIndex() != 0) {
1440 appendRange(draft
, 0, smallestFieldPosition
.getBeginIndex(), appendTo
);
1442 smallestAmountFormatted
,
1444 intFieldPosition
.getBeginIndex(),
1448 smallestFieldPosition
.getBeginIndex(),
1449 smallestFieldPosition
.getEndIndex(),
1452 smallestAmountFormatted
,
1453 intFieldPosition
.getEndIndex(),
1457 smallestFieldPosition
.getEndIndex(),
1459 handler
.addAttribute(measField
, start
+ smallestFieldPosition
.getBeginIndex(), appendTo
.length());
1460 handler
.addAttribute(measField
| UAMEASFMT_NUMERIC_FIELD_FLAG
, start
+ smallestFieldPosition
.getBeginIndex(), appendTo
.length());
1462 appendTo
.append(draft
);
1467 const SimpleFormatter
*MeasureFormat::getFormatterOrNull(
1468 const MeasureUnit
&unit
, UMeasureFormatWidth width
, int32_t index
) const {
1469 width
= getRegularWidth(width
);
1470 SimpleFormatter
*const (*unitPatterns
)[PATTERN_COUNT
] = &cache
->patterns
[unit
.getIndex()][0];
1471 if (unitPatterns
[width
][index
] != NULL
) {
1472 return unitPatterns
[width
][index
];
1474 int32_t fallbackWidth
= cache
->widthFallback
[width
];
1475 if (fallbackWidth
!= UMEASFMT_WIDTH_COUNT
&& unitPatterns
[fallbackWidth
][index
] != NULL
) {
1476 return unitPatterns
[fallbackWidth
][index
];
1481 const SimpleFormatter
*MeasureFormat::getFormatter(
1482 const MeasureUnit
&unit
, UMeasureFormatWidth width
, int32_t index
,
1483 UErrorCode
&errorCode
) const {
1484 if (U_FAILURE(errorCode
)) {
1487 const SimpleFormatter
*pattern
= getFormatterOrNull(unit
, width
, index
);
1488 if (pattern
== NULL
) {
1489 errorCode
= U_MISSING_RESOURCE_ERROR
;
1494 const SimpleFormatter
*MeasureFormat::getPluralFormatter(
1495 const MeasureUnit
&unit
, UMeasureFormatWidth width
, int32_t index
,
1496 UErrorCode
&errorCode
) const {
1497 if (U_FAILURE(errorCode
)) {
1500 if (index
!= StandardPlural::OTHER
) {
1501 const SimpleFormatter
*pattern
= getFormatterOrNull(unit
, width
, index
);
1502 if (pattern
!= NULL
) {
1506 return getFormatter(unit
, width
, StandardPlural::OTHER
, errorCode
);
1509 const SimpleFormatter
*MeasureFormat::getPerFormatter(
1510 UMeasureFormatWidth width
,
1511 UErrorCode
&status
) const {
1512 if (U_FAILURE(status
)) {
1515 width
= getRegularWidth(width
);
1516 const SimpleFormatter
* perFormatters
= cache
->perFormatters
;
1517 if (perFormatters
[width
].getArgumentLimit() == 2) {
1518 return &perFormatters
[width
];
1520 int32_t fallbackWidth
= cache
->widthFallback
[width
];
1521 if (fallbackWidth
!= UMEASFMT_WIDTH_COUNT
&&
1522 perFormatters
[fallbackWidth
].getArgumentLimit() == 2) {
1523 return &perFormatters
[fallbackWidth
];
1525 status
= U_MISSING_RESOURCE_ERROR
;
1529 int32_t MeasureFormat::withPerUnitAndAppend(
1530 const UnicodeString
&formatted
,
1531 const MeasureUnit
&perUnit
,
1532 UnicodeString
&appendTo
,
1533 UErrorCode
&status
) const {
1534 int32_t offset
= -1;
1535 if (U_FAILURE(status
)) {
1538 const SimpleFormatter
*perUnitFormatter
= getFormatterOrNull(perUnit
, width
, PER_UNIT_INDEX
);
1539 if (perUnitFormatter
!= NULL
) {
1540 const UnicodeString
*params
[] = {&formatted
};
1541 perUnitFormatter
->formatAndAppend(
1543 UPRV_LENGTHOF(params
),
1550 const SimpleFormatter
*perFormatter
= getPerFormatter(width
, status
);
1551 const SimpleFormatter
*pattern
=
1552 getPluralFormatter(perUnit
, width
, StandardPlural::ONE
, status
);
1553 if (U_FAILURE(status
)) {
1556 UnicodeString perUnitString
= pattern
->getTextWithNoArguments();
1557 perUnitString
.trim();
1558 const UnicodeString
*params
[] = {&formatted
, &perUnitString
};
1559 perFormatter
->formatAndAppend(
1561 UPRV_LENGTHOF(params
),
1569 UnicodeString
&MeasureFormat::formatMeasuresSlowTrack(
1570 const Measure
*measures
,
1571 int32_t measureCount
,
1572 UnicodeString
& appendTo
,
1574 UErrorCode
& status
) const {
1575 if (U_FAILURE(status
)) {
1578 FieldPosition
dontCare(FieldPosition::DONT_CARE
);
1579 FieldPosition
fpos(pos
.getField());
1580 UnicodeString
*results
= new UnicodeString
[measureCount
];
1581 int32_t fieldPositionFoundIndex
= -1;
1582 for (int32_t i
= 0; i
< measureCount
; ++i
) {
1583 const NumberFormat
*nf
= cache
->getIntegerFormat();
1584 if (i
== measureCount
- 1) {
1585 nf
= numberFormat
->get();
1587 if (fieldPositionFoundIndex
== -1) {
1588 formatMeasure(measures
[i
], *nf
, results
[i
], fpos
, status
);
1589 if (U_FAILURE(status
)) {
1593 if (fpos
.getBeginIndex() != 0 || fpos
.getEndIndex() != 0) {
1594 fieldPositionFoundIndex
= i
;
1597 formatMeasure(measures
[i
], *nf
, results
[i
], dontCare
, status
);
1601 listFormatter
->format(
1605 fieldPositionFoundIndex
,
1608 if (U_FAILURE(status
)) {
1613 pos
.setBeginIndex(fpos
.getBeginIndex() + offset
);
1614 pos
.setEndIndex(fpos
.getEndIndex() + offset
);
1620 MeasureFormat
* U_EXPORT2
MeasureFormat::createCurrencyFormat(const Locale
& locale
,
1622 CurrencyFormat
* fmt
= NULL
;
1623 if (U_SUCCESS(ec
)) {
1624 fmt
= new CurrencyFormat(locale
, ec
);
1625 if (U_FAILURE(ec
)) {
1633 MeasureFormat
* U_EXPORT2
MeasureFormat::createCurrencyFormat(UErrorCode
& ec
) {
1634 if (U_FAILURE(ec
)) {
1637 return MeasureFormat::createCurrencyFormat(Locale::getDefault(), ec
);
1642 #endif /* #if !UCONFIG_NO_FORMATTING */