]>
Commit | Line | Data |
---|---|---|
f3c0d7a5 A |
1 | // © 2016 and later: Unicode, Inc. and others. |
2 | // License & terms of use: http://www.unicode.org/copyright.html | |
374ca955 A |
3 | /* |
4 | ********************************************************************** | |
2ca993e8 | 5 | * Copyright (c) 2004-2016, International Business Machines |
374ca955 A |
6 | * Corporation and others. All Rights Reserved. |
7 | ********************************************************************** | |
8 | * Author: Alan Liu | |
9 | * Created: April 20, 2004 | |
10 | * Since: ICU 3.0 | |
11 | ********************************************************************** | |
12 | */ | |
57a6839d | 13 | #include "utypeinfo.h" // for 'typeid' to work |
374ca955 A |
14 | #include "unicode/utypes.h" |
15 | ||
16 | #if !UCONFIG_NO_FORMATTING | |
17 | ||
18 | #include "unicode/measfmt.h" | |
57a6839d | 19 | #include "unicode/numfmt.h" |
374ca955 | 20 | #include "currfmt.h" |
57a6839d | 21 | #include "unicode/localpointer.h" |
2ca993e8 A |
22 | #include "resource.h" |
23 | #include "unicode/simpleformatter.h" | |
57a6839d A |
24 | #include "quantityformatter.h" |
25 | #include "unicode/plurrule.h" | |
26 | #include "unicode/decimfmt.h" | |
57a6839d A |
27 | #include "uresimp.h" |
28 | #include "unicode/ures.h" | |
f3c0d7a5 | 29 | #include "unicode/ustring.h" |
b331163b | 30 | #include "ureslocs.h" |
57a6839d A |
31 | #include "cstring.h" |
32 | #include "mutex.h" | |
33 | #include "ucln_in.h" | |
34 | #include "unicode/listformatter.h" | |
35 | #include "charstr.h" | |
36 | #include "unicode/putil.h" | |
37 | #include "unicode/smpdtfmt.h" | |
b331163b | 38 | #include "uassert.h" |
2ca993e8 A |
39 | #include "unicode/uameasureformat.h" |
40 | #include "fphdlimp.h" | |
57a6839d A |
41 | |
42 | #include "sharednumberformat.h" | |
43 | #include "sharedpluralrules.h" | |
2ca993e8 | 44 | #include "standardplural.h" |
b331163b | 45 | #include "unifiedcache.h" |
57a6839d | 46 | |
f3c0d7a5 | 47 | #define MEAS_UNIT_COUNT 135 |
57a6839d A |
48 | #define WIDTH_INDEX_COUNT (UMEASFMT_WIDTH_NARROW + 1) |
49 | ||
374ca955 A |
50 | U_NAMESPACE_BEGIN |
51 | ||
57a6839d A |
52 | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MeasureFormat) |
53 | ||
54 | // Used to format durations like 5:47 or 21:35:42. | |
55 | class NumericDateFormatters : public UMemory { | |
56 | public: | |
57 | // Formats like H:mm | |
58 | SimpleDateFormat hourMinute; | |
59 | ||
60 | // formats like M:ss | |
61 | SimpleDateFormat minuteSecond; | |
62 | ||
63 | // formats like H:mm:ss | |
64 | SimpleDateFormat hourMinuteSecond; | |
65 | ||
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, | |
f3c0d7a5 | 72 | UErrorCode &status) : |
57a6839d | 73 | hourMinute(hm, status), |
f3c0d7a5 | 74 | minuteSecond(ms, status), |
57a6839d A |
75 | hourMinuteSecond(hms, status) { |
76 | const TimeZone *gmt = TimeZone::getGMT(); | |
77 | hourMinute.setTimeZone(*gmt); | |
78 | minuteSecond.setTimeZone(*gmt); | |
79 | hourMinuteSecond.setTimeZone(*gmt); | |
80 | } | |
81 | private: | |
82 | NumericDateFormatters(const NumericDateFormatters &other); | |
83 | NumericDateFormatters &operator=(const NumericDateFormatters &other); | |
84 | }; | |
85 | ||
2ca993e8 A |
86 | static UMeasureFormatWidth getRegularWidth(UMeasureFormatWidth width) { |
87 | if (width >= WIDTH_INDEX_COUNT) { | |
88 | return UMEASFMT_WIDTH_NARROW; | |
89 | } | |
90 | return width; | |
91 | } | |
92 | ||
93 | /** | |
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. | |
96 | * | |
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. | |
100 | * | |
101 | * TODO: Maybe store more sparsely in general, with pointers rather than potentially-empty objects. | |
102 | */ | |
57a6839d A |
103 | class MeasureFormatCacheData : public SharedObject { |
104 | public: | |
2ca993e8 A |
105 | static const int32_t PER_UNIT_INDEX = StandardPlural::COUNT; |
106 | static const int32_t PATTERN_COUNT = PER_UNIT_INDEX + 1; | |
107 | ||
108 | /** | |
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 | |
112 | */ | |
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]; | |
f3c0d7a5 | 116 | const UChar* dnams[MEAS_UNIT_COUNT][WIDTH_INDEX_COUNT]; |
2ca993e8 | 117 | SimpleFormatter perFormatters[WIDTH_INDEX_COUNT]; |
b331163b | 118 | |
57a6839d | 119 | MeasureFormatCacheData(); |
2ca993e8 A |
120 | virtual ~MeasureFormatCacheData(); |
121 | ||
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; | |
126 | } | |
127 | ||
57a6839d A |
128 | void adoptCurrencyFormat(int32_t widthIndex, NumberFormat *nfToAdopt) { |
129 | delete currencyFormats[widthIndex]; | |
130 | currencyFormats[widthIndex] = nfToAdopt; | |
131 | } | |
2ca993e8 A |
132 | const NumberFormat *getCurrencyFormat(UMeasureFormatWidth width) const { |
133 | return currencyFormats[getRegularWidth(width)]; | |
57a6839d A |
134 | } |
135 | void adoptIntegerFormat(NumberFormat *nfToAdopt) { | |
136 | delete integerFormat; | |
137 | integerFormat = nfToAdopt; | |
138 | } | |
139 | const NumberFormat *getIntegerFormat() const { | |
140 | return integerFormat; | |
141 | } | |
142 | void adoptNumericDateFormatters(NumericDateFormatters *formattersToAdopt) { | |
143 | delete numericDateFormatters; | |
144 | numericDateFormatters = formattersToAdopt; | |
145 | } | |
146 | const NumericDateFormatters *getNumericDateFormatters() const { | |
147 | return numericDateFormatters; | |
148 | } | |
2ca993e8 | 149 | |
57a6839d A |
150 | private: |
151 | NumberFormat *currencyFormats[WIDTH_INDEX_COUNT]; | |
152 | NumberFormat *integerFormat; | |
153 | NumericDateFormatters *numericDateFormatters; | |
154 | MeasureFormatCacheData(const MeasureFormatCacheData &other); | |
155 | MeasureFormatCacheData &operator=(const MeasureFormatCacheData &other); | |
156 | }; | |
157 | ||
158 | MeasureFormatCacheData::MeasureFormatCacheData() { | |
2ca993e8 A |
159 | for (int32_t i = 0; i < WIDTH_INDEX_COUNT; ++i) { |
160 | widthFallback[i] = UMEASFMT_WIDTH_COUNT; | |
161 | } | |
b331163b | 162 | for (int32_t i = 0; i < UPRV_LENGTHOF(currencyFormats); ++i) { |
57a6839d A |
163 | currencyFormats[i] = NULL; |
164 | } | |
2ca993e8 | 165 | uprv_memset(patterns, 0, sizeof(patterns)); |
f3c0d7a5 | 166 | uprv_memset(dnams, 0, sizeof(dnams)); |
2ca993e8 A |
167 | integerFormat = NULL; |
168 | numericDateFormatters = NULL; | |
57a6839d A |
169 | } |
170 | ||
171 | MeasureFormatCacheData::~MeasureFormatCacheData() { | |
b331163b | 172 | for (int32_t i = 0; i < UPRV_LENGTHOF(currencyFormats); ++i) { |
57a6839d A |
173 | delete currencyFormats[i]; |
174 | } | |
b331163b A |
175 | for (int32_t i = 0; i < MEAS_UNIT_COUNT; ++i) { |
176 | for (int32_t j = 0; j < WIDTH_INDEX_COUNT; ++j) { | |
2ca993e8 A |
177 | for (int32_t k = 0; k < PATTERN_COUNT; ++k) { |
178 | delete patterns[i][j][k]; | |
179 | } | |
b331163b A |
180 | } |
181 | } | |
f3c0d7a5 | 182 | // Note: the contents of 'dnams' are pointers into the resource bundle |
57a6839d A |
183 | delete integerFormat; |
184 | delete numericDateFormatters; | |
185 | } | |
186 | ||
57a6839d A |
187 | static UBool isCurrency(const MeasureUnit &unit) { |
188 | return (uprv_strcmp(unit.getType(), "currency") == 0); | |
189 | } | |
190 | ||
191 | static UBool getString( | |
192 | const UResourceBundle *resource, | |
193 | UnicodeString &result, | |
194 | UErrorCode &status) { | |
195 | int32_t len = 0; | |
196 | const UChar *resStr = ures_getString(resource, &len, &status); | |
197 | if (U_FAILURE(status)) { | |
198 | return FALSE; | |
199 | } | |
200 | result.setTo(TRUE, resStr, len); | |
201 | return TRUE; | |
202 | } | |
203 | ||
2ca993e8 | 204 | namespace { |
57a6839d | 205 | |
2ca993e8 A |
206 | static const UChar g_LOCALE_units[] = { |
207 | 0x2F, 0x4C, 0x4F, 0x43, 0x41, 0x4C, 0x45, 0x2F, | |
208 | 0x75, 0x6E, 0x69, 0x74, 0x73 | |
209 | }; | |
210 | static const UChar gShort[] = { 0x53, 0x68, 0x6F, 0x72, 0x74 }; | |
211 | static const UChar gNarrow[] = { 0x4E, 0x61, 0x72, 0x72, 0x6F, 0x77 }; | |
212 | ||
213 | /** | |
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. | |
217 | * | |
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. | |
220 | * | |
221 | * C++: Each inner sink class has a reference to the main outer sink. | |
222 | * Java: Use non-static inner classes instead. | |
223 | */ | |
f3c0d7a5 A |
224 | struct UnitDataSink : public ResourceSink { |
225 | ||
226 | // Output data. | |
227 | MeasureFormatCacheData &cacheData; | |
228 | ||
229 | // Path to current data. | |
230 | UMeasureFormatWidth width; | |
231 | const char *type; | |
232 | int32_t unitIndex; | |
233 | ||
234 | UnitDataSink(MeasureFormatCacheData &outputData) | |
235 | : cacheData(outputData), | |
236 | width(UMEASFMT_WIDTH_COUNT), type(NULL), unitIndex(0) {} | |
237 | ~UnitDataSink(); | |
238 | ||
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) { | |
2ca993e8 | 244 | patterns[index] = new SimpleFormatter( |
f3c0d7a5 A |
245 | value.getUnicodeString(errorCode), minPlaceholders, 1, errorCode); |
246 | } | |
247 | if (U_SUCCESS(errorCode) && patterns[index] == NULL) { | |
248 | errorCode = U_MEMORY_ALLOCATION_ERROR; | |
2ca993e8 | 249 | } |
57a6839d | 250 | } |
f3c0d7a5 | 251 | } |
2ca993e8 | 252 | |
f3c0d7a5 A |
253 | void setDnamIfAbsent(const ResourceValue &value, UErrorCode& errorCode) { |
254 | if (cacheData.dnams[unitIndex][width] == NULL) { | |
255 | int32_t length; | |
256 | cacheData.dnams[unitIndex][width] = value.getString(length, errorCode); | |
57a6839d | 257 | } |
f3c0d7a5 | 258 | } |
2ca993e8 | 259 | |
f3c0d7a5 A |
260 | /** |
261 | * Consume a display pattern. For example, | |
262 | * unitsShort/duration/hour contains other{"{0} hrs"}. | |
263 | */ | |
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); | |
272 | } else { | |
273 | // The key must be one of the plural form strings. For example: | |
274 | // one{"{0} hr"} | |
275 | // other{"{0} hrs"} | |
276 | setFormatterIfAbsent(StandardPlural::indexFromString(key, errorCode), value, 0, | |
277 | errorCode); | |
b331163b | 278 | } |
f3c0d7a5 | 279 | } |
2ca993e8 A |
280 | |
281 | /** | |
f3c0d7a5 | 282 | * Consume a table of per-unit tables. For example, |
2ca993e8 A |
283 | * unitsShort/duration contains tables for duration-unit subtypes day & hour. |
284 | */ | |
f3c0d7a5 A |
285 | void consumeSubtypeTable(const char *key, ResourceValue &value, UErrorCode &errorCode) { |
286 | if (U_FAILURE(errorCode)) { return; } | |
287 | unitIndex = MeasureUnit::internalGetIndexForTypeAndSubtype(type, key); | |
288 | if (unitIndex < 0) { | |
289 | // TODO: How to handle unexpected data? | |
290 | // See http://bugs.icu-project.org/trac/ticket/12597 | |
291 | return; | |
292 | } | |
293 | ||
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); | |
57a6839d | 301 | } |
f3c0d7a5 A |
302 | } else { |
303 | // TODO: How to handle unexpected data? | |
304 | // See http://bugs.icu-project.org/trac/ticket/12597 | |
305 | return; | |
2ca993e8 | 306 | } |
f3c0d7a5 | 307 | } |
2ca993e8 A |
308 | |
309 | /** | |
f3c0d7a5 | 310 | * Consume compound x-per-y display pattern. For example, |
2ca993e8 A |
311 | * unitsShort/compound/per may be "{0}/{1}". |
312 | */ | |
f3c0d7a5 A |
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); | |
2ca993e8 | 317 | } |
f3c0d7a5 | 318 | } |
2ca993e8 A |
319 | |
320 | /** | |
f3c0d7a5 | 321 | * Consume a table of unit type tables. For example, |
2ca993e8 A |
322 | * unitsShort contains tables for area & duration. |
323 | * It also contains a table for the compound/per pattern. | |
324 | */ | |
f3c0d7a5 A |
325 | void consumeUnitTypesTable(const char *key, ResourceValue &value, UErrorCode &errorCode) { |
326 | if (U_FAILURE(errorCode)) { return; } | |
327 | if (uprv_strcmp(key, "currency") == 0) { | |
328 | // Skip. | |
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); | |
2ca993e8 | 335 | } |
57a6839d | 336 | } |
f3c0d7a5 A |
337 | } else if (uprv_strcmp(key, "coordinate") == 0) { |
338 | // special handling but we need to determine what that is | |
339 | } else { | |
340 | type = key; | |
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); | |
345 | } | |
2ca993e8 | 346 | } |
f3c0d7a5 | 347 | } |
2ca993e8 | 348 | |
f3c0d7a5 | 349 | void consumeAlias(const char *key, const ResourceValue &value, UErrorCode &errorCode) { |
2ca993e8 A |
350 | // Handle aliases like |
351 | // units:alias{"/LOCALE/unitsShort"} | |
352 | // which should only occur in the root bundle. | |
2ca993e8 A |
353 | UMeasureFormatWidth sourceWidth = widthFromKey(key); |
354 | if (sourceWidth == UMEASFMT_WIDTH_COUNT) { | |
355 | // Alias from something we don't care about. | |
356 | return; | |
357 | } | |
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; | |
362 | return; | |
363 | } | |
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; | |
367 | return; | |
368 | } | |
369 | cacheData.widthFallback[sourceWidth] = targetWidth; | |
370 | } | |
f3c0d7a5 A |
371 | |
372 | void consumeTable(const char *key, ResourceValue &value, UErrorCode &errorCode) { | |
2ca993e8 | 373 | if (U_SUCCESS(errorCode) && (width = widthFromKey(key)) != UMEASFMT_WIDTH_COUNT) { |
f3c0d7a5 A |
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); | |
378 | } | |
2ca993e8 | 379 | } |
2ca993e8 A |
380 | } |
381 | ||
382 | static UMeasureFormatWidth widthFromKey(const char *key) { | |
383 | if (uprv_strncmp(key, "units", 5) == 0) { | |
384 | key += 5; | |
385 | if (*key == 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; | |
57a6839d | 391 | } |
2ca993e8 A |
392 | } |
393 | return UMEASFMT_WIDTH_COUNT; | |
394 | } | |
395 | ||
396 | static UMeasureFormatWidth widthFromAlias(const ResourceValue &value, UErrorCode &errorCode) { | |
397 | int32_t length; | |
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) { | |
401 | s += 13; | |
402 | length -= 13; | |
403 | if (*s == 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; | |
57a6839d A |
409 | } |
410 | } | |
2ca993e8 A |
411 | return UMEASFMT_WIDTH_COUNT; |
412 | } | |
413 | ||
f3c0d7a5 A |
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); | |
422 | } else { | |
423 | consumeTable(key, value, errorCode); | |
424 | } | |
425 | } | |
426 | } | |
2ca993e8 A |
427 | }; |
428 | ||
429 | // Virtual destructors must be defined out of line. | |
2ca993e8 A |
430 | UnitDataSink::~UnitDataSink() {} |
431 | ||
432 | } // namespace | |
433 | ||
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 | |
f3c0d7a5 A |
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 | |
2ca993e8 A |
571 | }; |
572 | ||
573 | static UBool loadMeasureUnitData( | |
574 | const UResourceBundle *resource, | |
575 | MeasureFormatCacheData &cacheData, | |
576 | UErrorCode &status) { | |
577 | UnitDataSink sink(cacheData); | |
f3c0d7a5 | 578 | ures_getAllItemsWithFallback(resource, "", sink, status); |
57a6839d A |
579 | return U_SUCCESS(status); |
580 | } | |
581 | ||
582 | static UnicodeString loadNumericDateFormatterPattern( | |
583 | const UResourceBundle *resource, | |
584 | const char *pattern, | |
585 | UErrorCode &status) { | |
586 | UnicodeString result; | |
587 | if (U_FAILURE(status)) { | |
588 | return result; | |
589 | } | |
590 | CharString chs; | |
591 | chs.append("durationUnits", status) | |
592 | .append("/", status).append(pattern, status); | |
593 | LocalUResourceBundlePointer patternBundle( | |
594 | ures_getByKeyWithFallback( | |
595 | resource, | |
596 | chs.data(), | |
597 | NULL, | |
598 | &status)); | |
599 | if (U_FAILURE(status)) { | |
600 | return result; | |
601 | } | |
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' | |
609 | } | |
610 | } | |
611 | result.releaseBuffer(len); | |
612 | return result; | |
613 | } | |
374ca955 | 614 | |
57a6839d A |
615 | static NumericDateFormatters *loadNumericDateFormatters( |
616 | const UResourceBundle *resource, | |
617 | UErrorCode &status) { | |
618 | if (U_FAILURE(status)) { | |
619 | return NULL; | |
620 | } | |
621 | NumericDateFormatters *result = new NumericDateFormatters( | |
622 | loadNumericDateFormatterPattern(resource, "hm", status), | |
623 | loadNumericDateFormatterPattern(resource, "ms", status), | |
624 | loadNumericDateFormatterPattern(resource, "hms", status), | |
625 | status); | |
626 | if (U_FAILURE(status)) { | |
627 | delete result; | |
628 | return NULL; | |
629 | } | |
630 | return result; | |
631 | } | |
632 | ||
b331163b A |
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)); | |
57a6839d A |
638 | static UNumberFormatStyle currencyStyles[] = { |
639 | UNUM_CURRENCY_PLURAL, UNUM_CURRENCY_ISO, UNUM_CURRENCY}; | |
b331163b | 640 | LocalPointer<MeasureFormatCacheData> result(new MeasureFormatCacheData(), status); |
57a6839d A |
641 | if (U_FAILURE(status)) { |
642 | return NULL; | |
643 | } | |
57a6839d | 644 | if (!loadMeasureUnitData( |
b331163b | 645 | unitsBundle.getAlias(), |
57a6839d A |
646 | *result, |
647 | status)) { | |
648 | return NULL; | |
649 | } | |
650 | result->adoptNumericDateFormatters(loadNumericDateFormatters( | |
b331163b | 651 | unitsBundle.getAlias(), status)); |
57a6839d A |
652 | if (U_FAILURE(status)) { |
653 | return NULL; | |
654 | } | |
655 | ||
656 | for (int32_t i = 0; i < WIDTH_INDEX_COUNT; ++i) { | |
f3c0d7a5 A |
657 | // NumberFormat::createInstance can erase warning codes from status, so pass it |
658 | // a separate status instance | |
659 | UErrorCode localStatus = U_ZERO_ERROR; | |
57a6839d | 660 | result->adoptCurrencyFormat(i, NumberFormat::createInstance( |
f3c0d7a5 A |
661 | localeId, currencyStyles[i], localStatus)); |
662 | if (localStatus != U_ZERO_ERROR) { | |
663 | status = localStatus; | |
664 | } | |
57a6839d A |
665 | if (U_FAILURE(status)) { |
666 | return NULL; | |
667 | } | |
668 | } | |
669 | NumberFormat *inf = NumberFormat::createInstance( | |
670 | localeId, UNUM_DECIMAL, status); | |
671 | if (U_FAILURE(status)) { | |
672 | return NULL; | |
673 | } | |
674 | inf->setMaximumFractionDigits(0); | |
675 | DecimalFormat *decfmt = dynamic_cast<DecimalFormat *>(inf); | |
676 | if (decfmt != NULL) { | |
677 | decfmt->setRoundingMode(DecimalFormat::kRoundDown); | |
678 | } | |
679 | result->adoptIntegerFormat(inf); | |
b331163b | 680 | result->addRef(); |
57a6839d A |
681 | return result.orphan(); |
682 | } | |
683 | ||
57a6839d A |
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; | |
687 | } | |
688 | ||
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. | |
693 | // | |
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, | |
699 | Formattable *hms, | |
700 | UErrorCode &status) { | |
701 | if (U_FAILURE(status)) { | |
702 | return 0; | |
703 | } | |
704 | int32_t result = 0; | |
705 | if (U_FAILURE(status)) { | |
706 | return 0; | |
707 | } | |
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 | |
714 | if (result >= 1) { | |
715 | return 0; | |
716 | } | |
717 | hms[0] = measures[i].getNumber(); | |
718 | if (hms[0].getDouble() < 0.0) { | |
719 | return 0; | |
720 | } | |
721 | result |= 1; | |
722 | } else if (isTimeUnit(measures[i].getUnit(), "minute")) { | |
723 | // minute must come after hour | |
724 | if (result >= 2) { | |
725 | return 0; | |
726 | } | |
727 | hms[1] = measures[i].getNumber(); | |
728 | if (hms[1].getDouble() < 0.0) { | |
729 | return 0; | |
730 | } | |
731 | result |= 2; | |
732 | } else if (isTimeUnit(measures[i].getUnit(), "second")) { | |
733 | // second must come after hour and minute | |
734 | if (result >= 4) { | |
735 | return 0; | |
736 | } | |
737 | hms[2] = measures[i].getNumber(); | |
738 | if (hms[2].getDouble() < 0.0) { | |
739 | return 0; | |
740 | } | |
741 | result |= 4; | |
742 | } else { | |
743 | return 0; | |
744 | } | |
745 | } | |
746 | return result; | |
747 | } | |
748 | ||
749 | ||
750 | MeasureFormat::MeasureFormat( | |
751 | const Locale &locale, UMeasureFormatWidth w, UErrorCode &status) | |
752 | : cache(NULL), | |
753 | numberFormat(NULL), | |
754 | pluralRules(NULL), | |
2ca993e8 A |
755 | width((w==UMEASFMT_WIDTH_SHORTER)? UMEASFMT_WIDTH_SHORT: w), |
756 | stripPatternSpaces(w==UMEASFMT_WIDTH_SHORTER), | |
757 | listFormatter(NULL), | |
758 | listFormatterStd(NULL) { | |
759 | initMeasureFormat(locale, (w==UMEASFMT_WIDTH_SHORTER)? UMEASFMT_WIDTH_SHORT: w, NULL, status); | |
57a6839d A |
760 | } |
761 | ||
762 | MeasureFormat::MeasureFormat( | |
763 | const Locale &locale, | |
764 | UMeasureFormatWidth w, | |
765 | NumberFormat *nfToAdopt, | |
f3c0d7a5 | 766 | UErrorCode &status) |
57a6839d A |
767 | : cache(NULL), |
768 | numberFormat(NULL), | |
769 | pluralRules(NULL), | |
2ca993e8 A |
770 | width((w==UMEASFMT_WIDTH_SHORTER)? UMEASFMT_WIDTH_SHORT: w), |
771 | stripPatternSpaces(w==UMEASFMT_WIDTH_SHORTER), | |
772 | listFormatter(NULL), | |
773 | listFormatterStd(NULL) { | |
774 | initMeasureFormat(locale, (w==UMEASFMT_WIDTH_SHORTER)? UMEASFMT_WIDTH_SHORT: w, nfToAdopt, status); | |
57a6839d A |
775 | } |
776 | ||
777 | MeasureFormat::MeasureFormat(const MeasureFormat &other) : | |
778 | Format(other), | |
779 | cache(other.cache), | |
780 | numberFormat(other.numberFormat), | |
781 | pluralRules(other.pluralRules), | |
782 | width(other.width), | |
2ca993e8 A |
783 | stripPatternSpaces(other.stripPatternSpaces), |
784 | listFormatter(NULL), | |
785 | listFormatterStd(NULL) { | |
57a6839d A |
786 | cache->addRef(); |
787 | numberFormat->addRef(); | |
788 | pluralRules->addRef(); | |
2ca993e8 A |
789 | if (other.listFormatter != NULL) { |
790 | listFormatter = new ListFormatter(*other.listFormatter); | |
791 | } | |
792 | if (other.listFormatterStd != NULL) { | |
793 | listFormatterStd = new ListFormatter(*other.listFormatterStd); | |
794 | } | |
57a6839d A |
795 | } |
796 | ||
797 | MeasureFormat &MeasureFormat::operator=(const MeasureFormat &other) { | |
798 | if (this == &other) { | |
799 | return *this; | |
800 | } | |
801 | Format::operator=(other); | |
802 | SharedObject::copyPtr(other.cache, cache); | |
803 | SharedObject::copyPtr(other.numberFormat, numberFormat); | |
804 | SharedObject::copyPtr(other.pluralRules, pluralRules); | |
805 | width = other.width; | |
2ca993e8 | 806 | stripPatternSpaces = other.stripPatternSpaces; |
57a6839d | 807 | delete listFormatter; |
2ca993e8 A |
808 | if (other.listFormatter != NULL) { |
809 | listFormatter = new ListFormatter(*other.listFormatter); | |
810 | } else { | |
811 | listFormatter = NULL; | |
812 | } | |
813 | delete listFormatterStd; | |
814 | if (other.listFormatterStd != NULL) { | |
815 | listFormatterStd = new ListFormatter(*other.listFormatterStd); | |
816 | } else { | |
817 | listFormatterStd = NULL; | |
818 | } | |
57a6839d A |
819 | return *this; |
820 | } | |
821 | ||
822 | MeasureFormat::MeasureFormat() : | |
823 | cache(NULL), | |
824 | numberFormat(NULL), | |
825 | pluralRules(NULL), | |
2ca993e8 A |
826 | width(UMEASFMT_WIDTH_SHORT), |
827 | stripPatternSpaces(FALSE), | |
828 | listFormatter(NULL), | |
829 | listFormatterStd(NULL) { | |
57a6839d A |
830 | } |
831 | ||
832 | MeasureFormat::~MeasureFormat() { | |
833 | if (cache != NULL) { | |
834 | cache->removeRef(); | |
835 | } | |
836 | if (numberFormat != NULL) { | |
837 | numberFormat->removeRef(); | |
838 | } | |
839 | if (pluralRules != NULL) { | |
840 | pluralRules->removeRef(); | |
841 | } | |
842 | delete listFormatter; | |
2ca993e8 | 843 | delete listFormatterStd; |
57a6839d A |
844 | } |
845 | ||
846 | UBool MeasureFormat::operator==(const Format &other) const { | |
847 | if (this == &other) { // Same object, equal | |
848 | return TRUE; | |
849 | } | |
850 | if (!Format::operator==(other)) { | |
851 | return FALSE; | |
852 | } | |
853 | const MeasureFormat &rhs = static_cast<const MeasureFormat &>(other); | |
854 | ||
855 | // Note: Since the ListFormatter depends only on Locale and width, we | |
856 | // don't have to check it here. | |
857 | ||
858 | // differing widths aren't equivalent | |
2ca993e8 | 859 | if (width != rhs.width || stripPatternSpaces != rhs.stripPatternSpaces) { |
57a6839d A |
860 | return FALSE; |
861 | } | |
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 | |
870 | return FALSE; | |
871 | } | |
872 | if (uprv_strcmp(localeId, rhsLocaleId) != 0) { | |
873 | return FALSE; | |
874 | } | |
875 | } | |
876 | // Locales same, check NumberFormat if shared data differs. | |
877 | return ( | |
878 | numberFormat == rhs.numberFormat || | |
879 | **numberFormat == **rhs.numberFormat); | |
880 | } | |
881 | ||
882 | Format *MeasureFormat::clone() const { | |
883 | return new MeasureFormat(*this); | |
884 | } | |
885 | ||
886 | UnicodeString &MeasureFormat::format( | |
887 | const Formattable &obj, | |
888 | UnicodeString &appendTo, | |
889 | FieldPosition &pos, | |
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); | |
898 | } | |
899 | } | |
900 | status = U_ILLEGAL_ARGUMENT_ERROR; | |
901 | return appendTo; | |
902 | } | |
903 | ||
904 | void MeasureFormat::parseObject( | |
905 | const UnicodeString & /*source*/, | |
906 | Formattable & /*result*/, | |
907 | ParsePosition& /*pos*/) const { | |
908 | return; | |
909 | } | |
910 | ||
b331163b A |
911 | UnicodeString &MeasureFormat::formatMeasurePerUnit( |
912 | const Measure &measure, | |
913 | const MeasureUnit &perUnit, | |
914 | UnicodeString &appendTo, | |
915 | FieldPosition &pos, | |
916 | UErrorCode &status) const { | |
917 | if (U_FAILURE(status)) { | |
918 | return appendTo; | |
919 | } | |
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); | |
926 | } | |
927 | FieldPosition fpos(pos.getField()); | |
928 | UnicodeString result; | |
929 | int32_t offset = withPerUnitAndAppend( | |
930 | formatMeasure( | |
931 | measure, **numberFormat, result, fpos, status), | |
932 | perUnit, | |
933 | appendTo, | |
934 | status); | |
935 | if (U_FAILURE(status)) { | |
936 | return appendTo; | |
937 | } | |
938 | if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) { | |
939 | pos.setBeginIndex(fpos.getBeginIndex() + offset); | |
940 | pos.setEndIndex(fpos.getEndIndex() + offset); | |
941 | } | |
942 | return appendTo; | |
943 | } | |
944 | ||
57a6839d A |
945 | UnicodeString &MeasureFormat::formatMeasures( |
946 | const Measure *measures, | |
947 | int32_t measureCount, | |
948 | UnicodeString &appendTo, | |
949 | FieldPosition &pos, | |
950 | UErrorCode &status) const { | |
951 | if (U_FAILURE(status)) { | |
952 | return appendTo; | |
953 | } | |
954 | if (measureCount == 0) { | |
955 | return appendTo; | |
956 | } | |
957 | if (measureCount == 1) { | |
958 | return formatMeasure(measures[0], **numberFormat, appendTo, pos, status); | |
959 | } | |
960 | if (width == UMEASFMT_WIDTH_NUMERIC) { | |
961 | Formattable hms[3]; | |
962 | int32_t bitMap = toHMS(measures, measureCount, hms, status); | |
963 | if (bitMap > 0) { | |
2ca993e8 A |
964 | FieldPositionIteratorHandler handler(NULL, status); |
965 | return formatNumeric(hms, bitMap, appendTo, handler, status); | |
57a6839d A |
966 | } |
967 | } | |
968 | if (pos.getField() != FieldPosition::DONT_CARE) { | |
969 | return formatMeasuresSlowTrack( | |
970 | measures, measureCount, appendTo, pos, status); | |
971 | } | |
972 | UnicodeString *results = new UnicodeString[measureCount]; | |
973 | if (results == NULL) { | |
974 | status = U_MEMORY_ALLOCATION_ERROR; | |
975 | return appendTo; | |
976 | } | |
977 | for (int32_t i = 0; i < measureCount; ++i) { | |
978 | const NumberFormat *nf = cache->getIntegerFormat(); | |
979 | if (i == measureCount - 1) { | |
980 | nf = numberFormat->get(); | |
981 | } | |
982 | formatMeasure( | |
983 | measures[i], | |
984 | *nf, | |
985 | results[i], | |
986 | pos, | |
987 | status); | |
988 | } | |
989 | listFormatter->format(results, measureCount, appendTo, status); | |
2ca993e8 A |
990 | delete [] results; |
991 | return appendTo; | |
992 | } | |
993 | ||
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)) { | |
1003 | return appendTo; | |
1004 | } | |
1005 | FieldPositionIteratorHandler handler(posIter, status); | |
1006 | if (measureCount == 0) { | |
1007 | return appendTo; | |
1008 | } | |
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()); | |
1016 | return appendTo; | |
1017 | } | |
1018 | if (width == UMEASFMT_WIDTH_NUMERIC) { | |
1019 | Formattable hms[3]; | |
1020 | int32_t bitMap = toHMS(measures, measureCount, hms, status); | |
1021 | if (bitMap > 0) { | |
1022 | return formatNumeric(hms, bitMap, appendTo, handler, status); | |
1023 | } | |
1024 | } | |
1025 | UnicodeString *results = new UnicodeString[measureCount]; | |
1026 | if (results == NULL) { | |
1027 | status = U_MEMORY_ALLOCATION_ERROR; | |
1028 | return appendTo; | |
1029 | } | |
1030 | FieldPosition *numPositions = new FieldPosition[measureCount]; | |
1031 | if (results == NULL) { | |
1032 | delete [] results; | |
1033 | status = U_MEMORY_ALLOCATION_ERROR; | |
1034 | return appendTo; | |
1035 | } | |
1036 | ||
1037 | for (int32_t i = 0; i < measureCount; ++i) { | |
1038 | const NumberFormat *nf = cache->getIntegerFormat(); | |
1039 | if (i == measureCount - 1) { | |
1040 | nf = numberFormat->get(); | |
1041 | } | |
1042 | numPositions[i].setField(UAMEASFMT_NUMERIC_FIELD_FLAG); | |
1043 | formatMeasure( | |
1044 | measures[i], | |
1045 | *nf, | |
1046 | results[i], | |
1047 | numPositions[i], | |
1048 | status); | |
1049 | } | |
1050 | listFormatter->format(results, measureCount, appendTo, status); | |
1051 | for (int32_t i = 0; i < measureCount; ++i) { | |
1052 | int32_t begin = appendTo.indexOf(results[i]); | |
1053 | if (begin >= 0) { | |
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); | |
1060 | } | |
1061 | } | |
1062 | } | |
1063 | delete [] results; | |
1064 | delete [] numPositions; | |
57a6839d A |
1065 | return appendTo; |
1066 | } | |
1067 | ||
2ca993e8 | 1068 | |
f3c0d7a5 A |
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]; | |
1073 | if (dnam == NULL) { | |
1074 | int32_t fallbackWidth = cache->widthFallback[width]; | |
1075 | dnam = styleToDnam[fallbackWidth]; | |
1076 | } | |
1077 | ||
1078 | UnicodeString result; | |
1079 | if (dnam == NULL) { | |
1080 | result.setToBogus(); | |
1081 | } else { | |
1082 | result.setTo(dnam, -1); | |
1083 | } | |
1084 | return result; | |
1085 | } | |
1086 | ||
57a6839d A |
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)) { | |
1095 | return; | |
1096 | } | |
1097 | const char *name = locale.getName(); | |
1098 | setLocaleIDs(name, name); | |
1099 | ||
b331163b A |
1100 | UnifiedCache::getByLocale(locale, cache, status); |
1101 | if (U_FAILURE(status)) { | |
57a6839d A |
1102 | return; |
1103 | } | |
1104 | ||
b331163b A |
1105 | const SharedPluralRules *pr = PluralRules::createSharedInstance( |
1106 | locale, UPLURAL_TYPE_CARDINAL, status); | |
57a6839d A |
1107 | if (U_FAILURE(status)) { |
1108 | return; | |
1109 | } | |
b331163b A |
1110 | SharedObject::copyPtr(pr, pluralRules); |
1111 | pr->removeRef(); | |
57a6839d | 1112 | if (nf.isNull()) { |
b331163b A |
1113 | const SharedNumberFormat *shared = NumberFormat::createSharedInstance( |
1114 | locale, UNUM_DECIMAL, status); | |
57a6839d A |
1115 | if (U_FAILURE(status)) { |
1116 | return; | |
1117 | } | |
b331163b A |
1118 | SharedObject::copyPtr(shared, numberFormat); |
1119 | shared->removeRef(); | |
57a6839d A |
1120 | } else { |
1121 | adoptNumberFormat(nf.orphan(), status); | |
1122 | if (U_FAILURE(status)) { | |
1123 | return; | |
1124 | } | |
1125 | } | |
1126 | width = w; | |
2ca993e8 A |
1127 | if (stripPatternSpaces) { |
1128 | w = UMEASFMT_WIDTH_NARROW; | |
1129 | } | |
57a6839d A |
1130 | delete listFormatter; |
1131 | listFormatter = ListFormatter::createInstance( | |
1132 | locale, | |
2ca993e8 A |
1133 | listStyles[getRegularWidth(w)], |
1134 | status); | |
1135 | delete listFormatterStd; | |
1136 | listFormatterStd = ListFormatter::createInstance( | |
1137 | locale, | |
1138 | "standard", | |
57a6839d A |
1139 | status); |
1140 | } | |
1141 | ||
1142 | void MeasureFormat::adoptNumberFormat( | |
1143 | NumberFormat *nfToAdopt, UErrorCode &status) { | |
1144 | LocalPointer<NumberFormat> nf(nfToAdopt); | |
1145 | if (U_FAILURE(status)) { | |
1146 | return; | |
1147 | } | |
1148 | SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias()); | |
1149 | if (shared == NULL) { | |
1150 | status = U_MEMORY_ALLOCATION_ERROR; | |
1151 | return; | |
1152 | } | |
1153 | nf.orphan(); | |
1154 | SharedObject::copyPtr(shared, numberFormat); | |
1155 | } | |
1156 | ||
1157 | UBool MeasureFormat::setMeasureFormatLocale(const Locale &locale, UErrorCode &status) { | |
1158 | if (U_FAILURE(status) || locale == getLocale(status)) { | |
1159 | return FALSE; | |
1160 | } | |
1161 | initMeasureFormat(locale, width, NULL, status); | |
1162 | return U_SUCCESS(status); | |
f3c0d7a5 | 1163 | } |
57a6839d | 1164 | |
b331163b A |
1165 | // Apple-specific for now |
1166 | UMeasureFormatWidth MeasureFormat::getWidth() const { | |
1167 | return width; | |
1168 | } | |
1169 | ||
57a6839d A |
1170 | const NumberFormat &MeasureFormat::getNumberFormat() const { |
1171 | return **numberFormat; | |
1172 | } | |
1173 | ||
1174 | const PluralRules &MeasureFormat::getPluralRules() const { | |
1175 | return **pluralRules; | |
1176 | } | |
1177 | ||
1178 | Locale MeasureFormat::getLocale(UErrorCode &status) const { | |
1179 | return Format::getLocale(ULOC_VALID_LOCALE, status); | |
1180 | } | |
1181 | ||
1182 | const char *MeasureFormat::getLocaleID(UErrorCode &status) const { | |
1183 | return Format::getLocaleID(ULOC_VALID_LOCALE, status); | |
1184 | } | |
1185 | ||
2ca993e8 | 1186 | // Apple=specific |
f3c0d7a5 A |
1187 | // now just re-implement using standard getUnitDisplayName |
1188 | // so we no longer use cache->getDisplayName | |
2ca993e8 A |
1189 | UnicodeString &MeasureFormat::getUnitName( |
1190 | const MeasureUnit* unit, | |
1191 | UnicodeString &result ) const { | |
f3c0d7a5 A |
1192 | UErrorCode status = U_ZERO_ERROR; |
1193 | result = getUnitDisplayName(*unit, status); // does not use or set status | |
2ca993e8 A |
1194 | return result; |
1195 | } | |
1196 | ||
1197 | // Apple=specific | |
1198 | UnicodeString &MeasureFormat::getMultipleUnitNames( | |
1199 | const MeasureUnit** units, | |
1200 | int32_t unitCount, | |
1201 | UAMeasureNameListStyle listStyle, | |
1202 | UnicodeString &result ) const { | |
1203 | if (unitCount == 0) { | |
1204 | return result.remove(); | |
1205 | } | |
1206 | if (unitCount == 1) { | |
1207 | return getUnitName(units[0], result); | |
1208 | } | |
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]); | |
1213 | } | |
1214 | UErrorCode status = U_ZERO_ERROR; | |
1215 | if (listStyle == UAMEASNAME_LIST_STANDARD) { | |
1216 | listFormatterStd->format(results, unitCount, result, status); | |
1217 | } else { | |
1218 | listFormatter->format(results, unitCount, result, status); | |
1219 | } | |
1220 | delete [] results; | |
1221 | if (U_SUCCESS(status)) { | |
1222 | return result; | |
1223 | } | |
1224 | } | |
1225 | result.setToBogus(); | |
1226 | return result; | |
1227 | } | |
1228 | ||
57a6839d A |
1229 | UnicodeString &MeasureFormat::formatMeasure( |
1230 | const Measure &measure, | |
1231 | const NumberFormat &nf, | |
1232 | UnicodeString &appendTo, | |
1233 | FieldPosition &pos, | |
1234 | UErrorCode &status) const { | |
1235 | if (U_FAILURE(status)) { | |
1236 | return appendTo; | |
1237 | } | |
1238 | const Formattable& amtNumber = measure.getNumber(); | |
1239 | const MeasureUnit& amtUnit = measure.getUnit(); | |
1240 | if (isCurrency(amtUnit)) { | |
1241 | UChar isoCode[4]; | |
1242 | u_charsToUChars(amtUnit.getSubtype(), isoCode, 4); | |
2ca993e8 | 1243 | return cache->getCurrencyFormat(width)->format( |
57a6839d A |
1244 | new CurrencyAmount(amtNumber, isoCode, status), |
1245 | appendTo, | |
1246 | pos, | |
1247 | status); | |
1248 | } | |
2ca993e8 A |
1249 | UnicodeString formattedNumber; |
1250 | UBool posForFullNumericPart = (pos.getField() == UAMEASFMT_NUMERIC_FIELD_FLAG); | |
1251 | if (posForFullNumericPart) { | |
1252 | pos.setField(FieldPosition::DONT_CARE); | |
1253 | } | |
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()); | |
1260 | } | |
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); | |
1279 | } | |
1280 | } | |
1281 | } | |
1282 | } | |
1283 | } | |
57a6839d | 1284 | } |
2ca993e8 | 1285 | return appendTo; |
57a6839d A |
1286 | } |
1287 | ||
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, | |
2ca993e8 | 1293 | FieldPositionHandler& handler, |
57a6839d A |
1294 | UErrorCode &status) const { |
1295 | if (U_FAILURE(status)) { | |
1296 | return appendTo; | |
1297 | } | |
f3c0d7a5 | 1298 | UDate millis = |
57a6839d A |
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); | |
1302 | switch (bitMap) { | |
1303 | case 5: // hs | |
1304 | case 7: // hms | |
1305 | return formatNumeric( | |
1306 | millis, | |
1307 | cache->getNumericDateFormatters()->hourMinuteSecond, | |
1308 | UDAT_SECOND_FIELD, | |
1309 | hms[2], | |
1310 | appendTo, | |
2ca993e8 | 1311 | handler, |
57a6839d A |
1312 | status); |
1313 | break; | |
1314 | case 6: // ms | |
1315 | return formatNumeric( | |
1316 | millis, | |
1317 | cache->getNumericDateFormatters()->minuteSecond, | |
1318 | UDAT_SECOND_FIELD, | |
1319 | hms[2], | |
1320 | appendTo, | |
2ca993e8 | 1321 | handler, |
57a6839d A |
1322 | status); |
1323 | break; | |
1324 | case 3: // hm | |
1325 | return formatNumeric( | |
1326 | millis, | |
1327 | cache->getNumericDateFormatters()->hourMinute, | |
1328 | UDAT_MINUTE_FIELD, | |
1329 | hms[1], | |
1330 | appendTo, | |
2ca993e8 | 1331 | handler, |
57a6839d A |
1332 | status); |
1333 | break; | |
1334 | default: | |
1335 | status = U_INTERNAL_PROGRAM_ERROR; | |
1336 | return appendTo; | |
1337 | break; | |
1338 | } | |
1339 | return appendTo; | |
1340 | } | |
1341 | ||
1342 | static void appendRange( | |
1343 | const UnicodeString &src, | |
1344 | int32_t start, | |
1345 | int32_t end, | |
1346 | UnicodeString &dest) { | |
1347 | dest.append(src, start, end - start); | |
1348 | } | |
1349 | ||
1350 | static void appendRange( | |
1351 | const UnicodeString &src, | |
1352 | int32_t end, | |
1353 | UnicodeString &dest) { | |
1354 | dest.append(src, end, src.length() - end); | |
1355 | } | |
1356 | ||
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, | |
2ca993e8 | 1364 | FieldPositionHandler& handler, |
57a6839d A |
1365 | UErrorCode &status) const { |
1366 | if (U_FAILURE(status)) { | |
1367 | return appendTo; | |
1368 | } | |
1369 | // Format the smallest amount with this object's NumberFormat | |
1370 | UnicodeString smallestAmountFormatted; | |
1371 | ||
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 | |
1374 | // '0:00:9.3' | |
1375 | FieldPosition intFieldPosition(UNUM_INTEGER_FIELD); | |
1376 | (*numberFormat)->format( | |
1377 | smallestAmount, smallestAmountFormatted, intFieldPosition, status); | |
1378 | if ( | |
1379 | intFieldPosition.getBeginIndex() == 0 && | |
1380 | intFieldPosition.getEndIndex() == 0) { | |
1381 | status = U_INTERNAL_PROGRAM_ERROR; | |
1382 | return appendTo; | |
1383 | } | |
1384 | ||
1385 | // Format time. draft becomes something like '5:30:45' | |
2ca993e8 | 1386 | FieldPositionIterator posIter; |
57a6839d | 1387 | UnicodeString draft; |
2ca993e8 A |
1388 | dateFmt.format(date, draft, &posIter, status); |
1389 | ||
1390 | int32_t start = appendTo.length(); | |
1391 | FieldPosition smallestFieldPosition(smallestField); | |
1392 | FieldPosition fp; | |
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; | |
1406 | default: | |
1407 | measField = -1; break; | |
1408 | } | |
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()); | |
1413 | } | |
1414 | } else { | |
1415 | smallestFieldPosition.setBeginIndex(fp.getBeginIndex()); | |
1416 | smallestFieldPosition.setEndIndex(fp.getEndIndex()); | |
1417 | break; | |
1418 | } | |
1419 | } | |
57a6839d A |
1420 | |
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); | |
1429 | appendRange( | |
1430 | smallestAmountFormatted, | |
1431 | 0, | |
1432 | intFieldPosition.getBeginIndex(), | |
1433 | appendTo); | |
1434 | appendRange( | |
1435 | draft, | |
1436 | smallestFieldPosition.getBeginIndex(), | |
1437 | smallestFieldPosition.getEndIndex(), | |
1438 | appendTo); | |
1439 | appendRange( | |
1440 | smallestAmountFormatted, | |
1441 | intFieldPosition.getEndIndex(), | |
1442 | appendTo); | |
1443 | appendRange( | |
1444 | draft, | |
1445 | smallestFieldPosition.getEndIndex(), | |
1446 | appendTo); | |
2ca993e8 A |
1447 | handler.addAttribute(measField, start + smallestFieldPosition.getBeginIndex(), appendTo.length()); |
1448 | handler.addAttribute(measField | UAMEASFMT_NUMERIC_FIELD_FLAG, start + smallestFieldPosition.getBeginIndex(), appendTo.length()); | |
57a6839d A |
1449 | } else { |
1450 | appendTo.append(draft); | |
1451 | } | |
1452 | return appendTo; | |
1453 | } | |
1454 | ||
2ca993e8 A |
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]; | |
57a6839d | 1462 | } |
2ca993e8 A |
1463 | int32_t fallbackWidth = cache->widthFallback[width]; |
1464 | if (fallbackWidth != UMEASFMT_WIDTH_COUNT && unitPatterns[fallbackWidth][index] != NULL) { | |
1465 | return unitPatterns[fallbackWidth][index]; | |
57a6839d | 1466 | } |
57a6839d A |
1467 | return NULL; |
1468 | } | |
1469 | ||
2ca993e8 A |
1470 | const SimpleFormatter *MeasureFormat::getFormatter( |
1471 | const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index, | |
1472 | UErrorCode &errorCode) const { | |
1473 | if (U_FAILURE(errorCode)) { | |
1474 | return NULL; | |
b331163b | 1475 | } |
2ca993e8 A |
1476 | const SimpleFormatter *pattern = getFormatterOrNull(unit, width, index); |
1477 | if (pattern == NULL) { | |
1478 | errorCode = U_MISSING_RESOURCE_ERROR; | |
b331163b | 1479 | } |
2ca993e8 A |
1480 | return pattern; |
1481 | } | |
1482 | ||
1483 | const SimpleFormatter *MeasureFormat::getPluralFormatter( | |
1484 | const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index, | |
1485 | UErrorCode &errorCode) const { | |
1486 | if (U_FAILURE(errorCode)) { | |
1487 | return NULL; | |
b331163b | 1488 | } |
2ca993e8 A |
1489 | if (index != StandardPlural::OTHER) { |
1490 | const SimpleFormatter *pattern = getFormatterOrNull(unit, width, index); | |
1491 | if (pattern != NULL) { | |
1492 | return pattern; | |
1493 | } | |
1494 | } | |
1495 | return getFormatter(unit, width, StandardPlural::OTHER, errorCode); | |
b331163b A |
1496 | } |
1497 | ||
2ca993e8 A |
1498 | const SimpleFormatter *MeasureFormat::getPerFormatter( |
1499 | UMeasureFormatWidth width, | |
b331163b A |
1500 | UErrorCode &status) const { |
1501 | if (U_FAILURE(status)) { | |
1502 | return NULL; | |
1503 | } | |
2ca993e8 A |
1504 | width = getRegularWidth(width); |
1505 | const SimpleFormatter * perFormatters = cache->perFormatters; | |
1506 | if (perFormatters[width].getArgumentLimit() == 2) { | |
1507 | return &perFormatters[width]; | |
b331163b | 1508 | } |
2ca993e8 A |
1509 | int32_t fallbackWidth = cache->widthFallback[width]; |
1510 | if (fallbackWidth != UMEASFMT_WIDTH_COUNT && | |
1511 | perFormatters[fallbackWidth].getArgumentLimit() == 2) { | |
1512 | return &perFormatters[fallbackWidth]; | |
b331163b A |
1513 | } |
1514 | status = U_MISSING_RESOURCE_ERROR; | |
1515 | return NULL; | |
1516 | } | |
1517 | ||
b331163b A |
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)) { | |
1525 | return offset; | |
1526 | } | |
2ca993e8 A |
1527 | const SimpleFormatter *perUnitFormatter = |
1528 | getFormatterOrNull(perUnit, width, MeasureFormatCacheData::PER_UNIT_INDEX); | |
b331163b A |
1529 | if (perUnitFormatter != NULL) { |
1530 | const UnicodeString *params[] = {&formatted}; | |
1531 | perUnitFormatter->formatAndAppend( | |
1532 | params, | |
1533 | UPRV_LENGTHOF(params), | |
1534 | appendTo, | |
1535 | &offset, | |
1536 | 1, | |
1537 | status); | |
1538 | return offset; | |
1539 | } | |
2ca993e8 A |
1540 | const SimpleFormatter *perFormatter = getPerFormatter(width, status); |
1541 | const SimpleFormatter *pattern = | |
1542 | getPluralFormatter(perUnit, width, StandardPlural::ONE, status); | |
b331163b A |
1543 | if (U_FAILURE(status)) { |
1544 | return offset; | |
1545 | } | |
2ca993e8 A |
1546 | UnicodeString perUnitString = pattern->getTextWithNoArguments(); |
1547 | perUnitString.trim(); | |
b331163b A |
1548 | const UnicodeString *params[] = {&formatted, &perUnitString}; |
1549 | perFormatter->formatAndAppend( | |
1550 | params, | |
1551 | UPRV_LENGTHOF(params), | |
1552 | appendTo, | |
1553 | &offset, | |
1554 | 1, | |
1555 | status); | |
1556 | return offset; | |
1557 | } | |
1558 | ||
57a6839d A |
1559 | UnicodeString &MeasureFormat::formatMeasuresSlowTrack( |
1560 | const Measure *measures, | |
1561 | int32_t measureCount, | |
1562 | UnicodeString& appendTo, | |
1563 | FieldPosition& pos, | |
1564 | UErrorCode& status) const { | |
1565 | if (U_FAILURE(status)) { | |
1566 | return appendTo; | |
1567 | } | |
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(); | |
1576 | } | |
1577 | if (fieldPositionFoundIndex == -1) { | |
1578 | formatMeasure(measures[i], *nf, results[i], fpos, status); | |
1579 | if (U_FAILURE(status)) { | |
1580 | delete [] results; | |
1581 | return appendTo; | |
1582 | } | |
1583 | if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) { | |
1584 | fieldPositionFoundIndex = i; | |
1585 | } | |
1586 | } else { | |
1587 | formatMeasure(measures[i], *nf, results[i], dontCare, status); | |
1588 | } | |
1589 | } | |
1590 | int32_t offset; | |
1591 | listFormatter->format( | |
1592 | results, | |
1593 | measureCount, | |
1594 | appendTo, | |
1595 | fieldPositionFoundIndex, | |
1596 | offset, | |
1597 | status); | |
1598 | if (U_FAILURE(status)) { | |
1599 | delete [] results; | |
1600 | return appendTo; | |
1601 | } | |
1602 | if (offset != -1) { | |
1603 | pos.setBeginIndex(fpos.getBeginIndex() + offset); | |
1604 | pos.setEndIndex(fpos.getEndIndex() + offset); | |
1605 | } | |
1606 | delete [] results; | |
1607 | return appendTo; | |
1608 | } | |
4388f060 | 1609 | |
374ca955 A |
1610 | MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(const Locale& locale, |
1611 | UErrorCode& ec) { | |
1612 | CurrencyFormat* fmt = NULL; | |
1613 | if (U_SUCCESS(ec)) { | |
1614 | fmt = new CurrencyFormat(locale, ec); | |
1615 | if (U_FAILURE(ec)) { | |
1616 | delete fmt; | |
1617 | fmt = NULL; | |
1618 | } | |
1619 | } | |
1620 | return fmt; | |
1621 | } | |
1622 | ||
1623 | MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(UErrorCode& ec) { | |
1624 | if (U_FAILURE(ec)) { | |
1625 | return NULL; | |
1626 | } | |
1627 | return MeasureFormat::createCurrencyFormat(Locale::getDefault(), ec); | |
1628 | } | |
1629 | ||
1630 | U_NAMESPACE_END | |
1631 | ||
1632 | #endif /* #if !UCONFIG_NO_FORMATTING */ |