]>
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 | |
57a6839d | 47 | |
374ca955 A |
48 | U_NAMESPACE_BEGIN |
49 | ||
0f5d89e8 A |
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; | |
54 | ||
57a6839d A |
55 | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MeasureFormat) |
56 | ||
57 | // Used to format durations like 5:47 or 21:35:42. | |
58 | class NumericDateFormatters : public UMemory { | |
59 | public: | |
60 | // Formats like H:mm | |
61 | SimpleDateFormat hourMinute; | |
62 | ||
63 | // formats like M:ss | |
64 | SimpleDateFormat minuteSecond; | |
65 | ||
66 | // formats like H:mm:ss | |
67 | SimpleDateFormat hourMinuteSecond; | |
68 | ||
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, | |
f3c0d7a5 | 75 | UErrorCode &status) : |
57a6839d | 76 | hourMinute(hm, status), |
f3c0d7a5 | 77 | minuteSecond(ms, status), |
57a6839d A |
78 | hourMinuteSecond(hms, status) { |
79 | const TimeZone *gmt = TimeZone::getGMT(); | |
80 | hourMinute.setTimeZone(*gmt); | |
81 | minuteSecond.setTimeZone(*gmt); | |
82 | hourMinuteSecond.setTimeZone(*gmt); | |
83 | } | |
84 | private: | |
85 | NumericDateFormatters(const NumericDateFormatters &other); | |
86 | NumericDateFormatters &operator=(const NumericDateFormatters &other); | |
87 | }; | |
88 | ||
2ca993e8 A |
89 | static UMeasureFormatWidth getRegularWidth(UMeasureFormatWidth width) { |
90 | if (width >= WIDTH_INDEX_COUNT) { | |
91 | return UMEASFMT_WIDTH_NARROW; | |
92 | } | |
93 | return width; | |
94 | } | |
95 | ||
96 | /** | |
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. | |
99 | * | |
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. | |
103 | * | |
104 | * TODO: Maybe store more sparsely in general, with pointers rather than potentially-empty objects. | |
105 | */ | |
57a6839d A |
106 | class MeasureFormatCacheData : public SharedObject { |
107 | public: | |
2ca993e8 A |
108 | |
109 | /** | |
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 | |
113 | */ | |
114 | UMeasureFormatWidth widthFallback[WIDTH_INDEX_COUNT]; | |
115 | /** Measure unit -> format width -> array of patterns ("{0} meters") (plurals + PER_UNIT_INDEX) */ | |
0f5d89e8 | 116 | SimpleFormatter* patterns[MEAS_UNIT_COUNT][WIDTH_INDEX_COUNT][PATTERN_COUNT]; |
f3c0d7a5 | 117 | const UChar* dnams[MEAS_UNIT_COUNT][WIDTH_INDEX_COUNT]; |
2ca993e8 | 118 | SimpleFormatter perFormatters[WIDTH_INDEX_COUNT]; |
b331163b | 119 | |
57a6839d | 120 | MeasureFormatCacheData(); |
2ca993e8 A |
121 | virtual ~MeasureFormatCacheData(); |
122 | ||
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; | |
127 | } | |
128 | ||
57a6839d A |
129 | void adoptCurrencyFormat(int32_t widthIndex, NumberFormat *nfToAdopt) { |
130 | delete currencyFormats[widthIndex]; | |
131 | currencyFormats[widthIndex] = nfToAdopt; | |
132 | } | |
2ca993e8 A |
133 | const NumberFormat *getCurrencyFormat(UMeasureFormatWidth width) const { |
134 | return currencyFormats[getRegularWidth(width)]; | |
57a6839d A |
135 | } |
136 | void adoptIntegerFormat(NumberFormat *nfToAdopt) { | |
137 | delete integerFormat; | |
138 | integerFormat = nfToAdopt; | |
139 | } | |
140 | const NumberFormat *getIntegerFormat() const { | |
141 | return integerFormat; | |
142 | } | |
143 | void adoptNumericDateFormatters(NumericDateFormatters *formattersToAdopt) { | |
144 | delete numericDateFormatters; | |
145 | numericDateFormatters = formattersToAdopt; | |
146 | } | |
147 | const NumericDateFormatters *getNumericDateFormatters() const { | |
148 | return numericDateFormatters; | |
149 | } | |
2ca993e8 | 150 | |
57a6839d | 151 | private: |
0f5d89e8 A |
152 | NumberFormat* currencyFormats[WIDTH_INDEX_COUNT]; |
153 | NumberFormat* integerFormat; | |
154 | NumericDateFormatters* numericDateFormatters; | |
155 | ||
57a6839d A |
156 | MeasureFormatCacheData(const MeasureFormatCacheData &other); |
157 | MeasureFormatCacheData &operator=(const MeasureFormatCacheData &other); | |
158 | }; | |
159 | ||
0f5d89e8 A |
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()); | |
164 | ||
2ca993e8 A |
165 | for (int32_t i = 0; i < WIDTH_INDEX_COUNT; ++i) { |
166 | widthFallback[i] = UMEASFMT_WIDTH_COUNT; | |
167 | } | |
0f5d89e8 A |
168 | memset(&patterns[0][0][0], 0, sizeof(patterns)); |
169 | memset(&dnams[0][0], 0, sizeof(dnams)); | |
170 | memset(currencyFormats, 0, sizeof(currencyFormats)); | |
57a6839d A |
171 | } |
172 | ||
173 | MeasureFormatCacheData::~MeasureFormatCacheData() { | |
b331163b | 174 | for (int32_t i = 0; i < UPRV_LENGTHOF(currencyFormats); ++i) { |
57a6839d A |
175 | delete currencyFormats[i]; |
176 | } | |
b331163b A |
177 | for (int32_t i = 0; i < MEAS_UNIT_COUNT; ++i) { |
178 | for (int32_t j = 0; j < WIDTH_INDEX_COUNT; ++j) { | |
2ca993e8 A |
179 | for (int32_t k = 0; k < PATTERN_COUNT; ++k) { |
180 | delete patterns[i][j][k]; | |
181 | } | |
b331163b A |
182 | } |
183 | } | |
f3c0d7a5 | 184 | // Note: the contents of 'dnams' are pointers into the resource bundle |
57a6839d A |
185 | delete integerFormat; |
186 | delete numericDateFormatters; | |
187 | } | |
188 | ||
57a6839d A |
189 | static UBool isCurrency(const MeasureUnit &unit) { |
190 | return (uprv_strcmp(unit.getType(), "currency") == 0); | |
191 | } | |
192 | ||
193 | static UBool getString( | |
194 | const UResourceBundle *resource, | |
195 | UnicodeString &result, | |
196 | UErrorCode &status) { | |
197 | int32_t len = 0; | |
198 | const UChar *resStr = ures_getString(resource, &len, &status); | |
199 | if (U_FAILURE(status)) { | |
200 | return FALSE; | |
201 | } | |
202 | result.setTo(TRUE, resStr, len); | |
203 | return TRUE; | |
204 | } | |
205 | ||
2ca993e8 | 206 | namespace { |
57a6839d | 207 | |
2ca993e8 A |
208 | static const UChar g_LOCALE_units[] = { |
209 | 0x2F, 0x4C, 0x4F, 0x43, 0x41, 0x4C, 0x45, 0x2F, | |
210 | 0x75, 0x6E, 0x69, 0x74, 0x73 | |
211 | }; | |
212 | static const UChar gShort[] = { 0x53, 0x68, 0x6F, 0x72, 0x74 }; | |
213 | static const UChar gNarrow[] = { 0x4E, 0x61, 0x72, 0x72, 0x6F, 0x77 }; | |
214 | ||
215 | /** | |
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. | |
219 | * | |
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. | |
222 | * | |
223 | * C++: Each inner sink class has a reference to the main outer sink. | |
224 | * Java: Use non-static inner classes instead. | |
225 | */ | |
f3c0d7a5 A |
226 | struct UnitDataSink : public ResourceSink { |
227 | ||
228 | // Output data. | |
229 | MeasureFormatCacheData &cacheData; | |
230 | ||
231 | // Path to current data. | |
232 | UMeasureFormatWidth width; | |
233 | const char *type; | |
234 | int32_t unitIndex; | |
235 | ||
236 | UnitDataSink(MeasureFormatCacheData &outputData) | |
237 | : cacheData(outputData), | |
238 | width(UMEASFMT_WIDTH_COUNT), type(NULL), unitIndex(0) {} | |
239 | ~UnitDataSink(); | |
240 | ||
241 | void setFormatterIfAbsent(int32_t index, const ResourceValue &value, | |
242 | int32_t minPlaceholders, UErrorCode &errorCode) { | |
0f5d89e8 A |
243 | U_ASSERT(unitIndex < MEAS_UNIT_COUNT); |
244 | U_ASSERT(width < WIDTH_INDEX_COUNT); | |
245 | U_ASSERT(index < PATTERN_COUNT); | |
f3c0d7a5 A |
246 | SimpleFormatter **patterns = &cacheData.patterns[unitIndex][width][0]; |
247 | if (U_SUCCESS(errorCode) && patterns[index] == NULL) { | |
248 | if (minPlaceholders >= 0) { | |
2ca993e8 | 249 | patterns[index] = new SimpleFormatter( |
f3c0d7a5 A |
250 | value.getUnicodeString(errorCode), minPlaceholders, 1, errorCode); |
251 | } | |
252 | if (U_SUCCESS(errorCode) && patterns[index] == NULL) { | |
253 | errorCode = U_MEMORY_ALLOCATION_ERROR; | |
2ca993e8 | 254 | } |
57a6839d | 255 | } |
f3c0d7a5 | 256 | } |
2ca993e8 | 257 | |
f3c0d7a5 | 258 | void setDnamIfAbsent(const ResourceValue &value, UErrorCode& errorCode) { |
0f5d89e8 A |
259 | U_ASSERT(unitIndex < MEAS_UNIT_COUNT); |
260 | U_ASSERT(width < WIDTH_INDEX_COUNT); | |
f3c0d7a5 A |
261 | if (cacheData.dnams[unitIndex][width] == NULL) { |
262 | int32_t length; | |
263 | cacheData.dnams[unitIndex][width] = value.getString(length, errorCode); | |
57a6839d | 264 | } |
f3c0d7a5 | 265 | } |
2ca993e8 | 266 | |
f3c0d7a5 A |
267 | /** |
268 | * Consume a display pattern. For example, | |
269 | * unitsShort/duration/hour contains other{"{0} hrs"}. | |
270 | */ | |
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". | |
0f5d89e8 | 278 | setFormatterIfAbsent(PER_UNIT_INDEX, value, 1, errorCode); |
f3c0d7a5 A |
279 | } else { |
280 | // The key must be one of the plural form strings. For example: | |
281 | // one{"{0} hr"} | |
282 | // other{"{0} hrs"} | |
283 | setFormatterIfAbsent(StandardPlural::indexFromString(key, errorCode), value, 0, | |
284 | errorCode); | |
b331163b | 285 | } |
f3c0d7a5 | 286 | } |
2ca993e8 A |
287 | |
288 | /** | |
f3c0d7a5 | 289 | * Consume a table of per-unit tables. For example, |
2ca993e8 A |
290 | * unitsShort/duration contains tables for duration-unit subtypes day & hour. |
291 | */ | |
f3c0d7a5 A |
292 | void consumeSubtypeTable(const char *key, ResourceValue &value, UErrorCode &errorCode) { |
293 | if (U_FAILURE(errorCode)) { return; } | |
294 | unitIndex = MeasureUnit::internalGetIndexForTypeAndSubtype(type, key); | |
295 | if (unitIndex < 0) { | |
296 | // TODO: How to handle unexpected data? | |
297 | // See http://bugs.icu-project.org/trac/ticket/12597 | |
298 | return; | |
299 | } | |
300 | ||
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); | |
57a6839d | 308 | } |
f3c0d7a5 A |
309 | } else { |
310 | // TODO: How to handle unexpected data? | |
311 | // See http://bugs.icu-project.org/trac/ticket/12597 | |
312 | return; | |
2ca993e8 | 313 | } |
f3c0d7a5 | 314 | } |
2ca993e8 A |
315 | |
316 | /** | |
f3c0d7a5 | 317 | * Consume compound x-per-y display pattern. For example, |
2ca993e8 A |
318 | * unitsShort/compound/per may be "{0}/{1}". |
319 | */ | |
f3c0d7a5 A |
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); | |
2ca993e8 | 324 | } |
f3c0d7a5 | 325 | } |
2ca993e8 A |
326 | |
327 | /** | |
f3c0d7a5 | 328 | * Consume a table of unit type tables. For example, |
2ca993e8 A |
329 | * unitsShort contains tables for area & duration. |
330 | * It also contains a table for the compound/per pattern. | |
331 | */ | |
f3c0d7a5 A |
332 | void consumeUnitTypesTable(const char *key, ResourceValue &value, UErrorCode &errorCode) { |
333 | if (U_FAILURE(errorCode)) { return; } | |
334 | if (uprv_strcmp(key, "currency") == 0) { | |
335 | // Skip. | |
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); | |
2ca993e8 | 342 | } |
57a6839d | 343 | } |
f3c0d7a5 A |
344 | } else if (uprv_strcmp(key, "coordinate") == 0) { |
345 | // special handling but we need to determine what that is | |
346 | } else { | |
347 | type = key; | |
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); | |
352 | } | |
2ca993e8 | 353 | } |
f3c0d7a5 | 354 | } |
2ca993e8 | 355 | |
f3c0d7a5 | 356 | void consumeAlias(const char *key, const ResourceValue &value, UErrorCode &errorCode) { |
2ca993e8 A |
357 | // Handle aliases like |
358 | // units:alias{"/LOCALE/unitsShort"} | |
359 | // which should only occur in the root bundle. | |
2ca993e8 A |
360 | UMeasureFormatWidth sourceWidth = widthFromKey(key); |
361 | if (sourceWidth == UMEASFMT_WIDTH_COUNT) { | |
362 | // Alias from something we don't care about. | |
363 | return; | |
364 | } | |
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; | |
369 | return; | |
370 | } | |
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; | |
374 | return; | |
375 | } | |
376 | cacheData.widthFallback[sourceWidth] = targetWidth; | |
377 | } | |
f3c0d7a5 A |
378 | |
379 | void consumeTable(const char *key, ResourceValue &value, UErrorCode &errorCode) { | |
2ca993e8 | 380 | if (U_SUCCESS(errorCode) && (width = widthFromKey(key)) != UMEASFMT_WIDTH_COUNT) { |
f3c0d7a5 A |
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); | |
385 | } | |
2ca993e8 | 386 | } |
2ca993e8 A |
387 | } |
388 | ||
389 | static UMeasureFormatWidth widthFromKey(const char *key) { | |
390 | if (uprv_strncmp(key, "units", 5) == 0) { | |
391 | key += 5; | |
392 | if (*key == 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; | |
57a6839d | 398 | } |
2ca993e8 A |
399 | } |
400 | return UMEASFMT_WIDTH_COUNT; | |
401 | } | |
402 | ||
403 | static UMeasureFormatWidth widthFromAlias(const ResourceValue &value, UErrorCode &errorCode) { | |
404 | int32_t length; | |
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) { | |
408 | s += 13; | |
409 | length -= 13; | |
410 | if (*s == 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; | |
57a6839d A |
416 | } |
417 | } | |
2ca993e8 A |
418 | return UMEASFMT_WIDTH_COUNT; |
419 | } | |
420 | ||
f3c0d7a5 A |
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); | |
429 | } else { | |
430 | consumeTable(key, value, errorCode); | |
431 | } | |
432 | } | |
433 | } | |
2ca993e8 A |
434 | }; |
435 | ||
436 | // Virtual destructors must be defined out of line. | |
2ca993e8 A |
437 | UnitDataSink::~UnitDataSink() {} |
438 | ||
439 | } // namespace | |
440 | ||
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 | |
f3c0d7a5 A |
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 | |
2ca993e8 A |
578 | }; |
579 | ||
580 | static UBool loadMeasureUnitData( | |
581 | const UResourceBundle *resource, | |
582 | MeasureFormatCacheData &cacheData, | |
583 | UErrorCode &status) { | |
584 | UnitDataSink sink(cacheData); | |
f3c0d7a5 | 585 | ures_getAllItemsWithFallback(resource, "", sink, status); |
57a6839d A |
586 | return U_SUCCESS(status); |
587 | } | |
588 | ||
589 | static UnicodeString loadNumericDateFormatterPattern( | |
590 | const UResourceBundle *resource, | |
591 | const char *pattern, | |
592 | UErrorCode &status) { | |
593 | UnicodeString result; | |
594 | if (U_FAILURE(status)) { | |
595 | return result; | |
596 | } | |
597 | CharString chs; | |
598 | chs.append("durationUnits", status) | |
599 | .append("/", status).append(pattern, status); | |
600 | LocalUResourceBundlePointer patternBundle( | |
601 | ures_getByKeyWithFallback( | |
602 | resource, | |
603 | chs.data(), | |
604 | NULL, | |
605 | &status)); | |
606 | if (U_FAILURE(status)) { | |
607 | return result; | |
608 | } | |
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' | |
616 | } | |
617 | } | |
618 | result.releaseBuffer(len); | |
619 | return result; | |
620 | } | |
374ca955 | 621 | |
57a6839d A |
622 | static NumericDateFormatters *loadNumericDateFormatters( |
623 | const UResourceBundle *resource, | |
624 | UErrorCode &status) { | |
625 | if (U_FAILURE(status)) { | |
626 | return NULL; | |
627 | } | |
628 | NumericDateFormatters *result = new NumericDateFormatters( | |
629 | loadNumericDateFormatterPattern(resource, "hm", status), | |
630 | loadNumericDateFormatterPattern(resource, "ms", status), | |
631 | loadNumericDateFormatterPattern(resource, "hms", status), | |
632 | status); | |
633 | if (U_FAILURE(status)) { | |
634 | delete result; | |
635 | return NULL; | |
636 | } | |
637 | return result; | |
638 | } | |
639 | ||
b331163b A |
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)); | |
57a6839d A |
645 | static UNumberFormatStyle currencyStyles[] = { |
646 | UNUM_CURRENCY_PLURAL, UNUM_CURRENCY_ISO, UNUM_CURRENCY}; | |
b331163b | 647 | LocalPointer<MeasureFormatCacheData> result(new MeasureFormatCacheData(), status); |
57a6839d A |
648 | if (U_FAILURE(status)) { |
649 | return NULL; | |
650 | } | |
57a6839d | 651 | if (!loadMeasureUnitData( |
b331163b | 652 | unitsBundle.getAlias(), |
57a6839d A |
653 | *result, |
654 | status)) { | |
655 | return NULL; | |
656 | } | |
657 | result->adoptNumericDateFormatters(loadNumericDateFormatters( | |
b331163b | 658 | unitsBundle.getAlias(), status)); |
57a6839d A |
659 | if (U_FAILURE(status)) { |
660 | return NULL; | |
661 | } | |
662 | ||
663 | for (int32_t i = 0; i < WIDTH_INDEX_COUNT; ++i) { | |
f3c0d7a5 A |
664 | // NumberFormat::createInstance can erase warning codes from status, so pass it |
665 | // a separate status instance | |
666 | UErrorCode localStatus = U_ZERO_ERROR; | |
57a6839d | 667 | result->adoptCurrencyFormat(i, NumberFormat::createInstance( |
f3c0d7a5 A |
668 | localeId, currencyStyles[i], localStatus)); |
669 | if (localStatus != U_ZERO_ERROR) { | |
670 | status = localStatus; | |
671 | } | |
57a6839d A |
672 | if (U_FAILURE(status)) { |
673 | return NULL; | |
674 | } | |
675 | } | |
676 | NumberFormat *inf = NumberFormat::createInstance( | |
677 | localeId, UNUM_DECIMAL, status); | |
678 | if (U_FAILURE(status)) { | |
679 | return NULL; | |
680 | } | |
681 | inf->setMaximumFractionDigits(0); | |
682 | DecimalFormat *decfmt = dynamic_cast<DecimalFormat *>(inf); | |
683 | if (decfmt != NULL) { | |
684 | decfmt->setRoundingMode(DecimalFormat::kRoundDown); | |
685 | } | |
686 | result->adoptIntegerFormat(inf); | |
b331163b | 687 | result->addRef(); |
57a6839d A |
688 | return result.orphan(); |
689 | } | |
690 | ||
57a6839d A |
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; | |
694 | } | |
695 | ||
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. | |
700 | // | |
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, | |
706 | Formattable *hms, | |
707 | UErrorCode &status) { | |
708 | if (U_FAILURE(status)) { | |
709 | return 0; | |
710 | } | |
711 | int32_t result = 0; | |
712 | if (U_FAILURE(status)) { | |
713 | return 0; | |
714 | } | |
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 | |
721 | if (result >= 1) { | |
722 | return 0; | |
723 | } | |
724 | hms[0] = measures[i].getNumber(); | |
725 | if (hms[0].getDouble() < 0.0) { | |
726 | return 0; | |
727 | } | |
728 | result |= 1; | |
729 | } else if (isTimeUnit(measures[i].getUnit(), "minute")) { | |
730 | // minute must come after hour | |
731 | if (result >= 2) { | |
732 | return 0; | |
733 | } | |
734 | hms[1] = measures[i].getNumber(); | |
735 | if (hms[1].getDouble() < 0.0) { | |
736 | return 0; | |
737 | } | |
738 | result |= 2; | |
739 | } else if (isTimeUnit(measures[i].getUnit(), "second")) { | |
740 | // second must come after hour and minute | |
741 | if (result >= 4) { | |
742 | return 0; | |
743 | } | |
744 | hms[2] = measures[i].getNumber(); | |
745 | if (hms[2].getDouble() < 0.0) { | |
746 | return 0; | |
747 | } | |
748 | result |= 4; | |
749 | } else { | |
750 | return 0; | |
751 | } | |
752 | } | |
753 | return result; | |
754 | } | |
755 | ||
756 | ||
757 | MeasureFormat::MeasureFormat( | |
758 | const Locale &locale, UMeasureFormatWidth w, UErrorCode &status) | |
759 | : cache(NULL), | |
760 | numberFormat(NULL), | |
761 | pluralRules(NULL), | |
2ca993e8 A |
762 | width((w==UMEASFMT_WIDTH_SHORTER)? UMEASFMT_WIDTH_SHORT: w), |
763 | stripPatternSpaces(w==UMEASFMT_WIDTH_SHORTER), | |
764 | listFormatter(NULL), | |
765 | listFormatterStd(NULL) { | |
766 | initMeasureFormat(locale, (w==UMEASFMT_WIDTH_SHORTER)? UMEASFMT_WIDTH_SHORT: w, NULL, status); | |
57a6839d A |
767 | } |
768 | ||
769 | MeasureFormat::MeasureFormat( | |
770 | const Locale &locale, | |
771 | UMeasureFormatWidth w, | |
772 | NumberFormat *nfToAdopt, | |
f3c0d7a5 | 773 | UErrorCode &status) |
57a6839d A |
774 | : cache(NULL), |
775 | numberFormat(NULL), | |
776 | pluralRules(NULL), | |
2ca993e8 A |
777 | width((w==UMEASFMT_WIDTH_SHORTER)? UMEASFMT_WIDTH_SHORT: w), |
778 | stripPatternSpaces(w==UMEASFMT_WIDTH_SHORTER), | |
779 | listFormatter(NULL), | |
780 | listFormatterStd(NULL) { | |
781 | initMeasureFormat(locale, (w==UMEASFMT_WIDTH_SHORTER)? UMEASFMT_WIDTH_SHORT: w, nfToAdopt, status); | |
57a6839d A |
782 | } |
783 | ||
784 | MeasureFormat::MeasureFormat(const MeasureFormat &other) : | |
785 | Format(other), | |
786 | cache(other.cache), | |
787 | numberFormat(other.numberFormat), | |
788 | pluralRules(other.pluralRules), | |
789 | width(other.width), | |
2ca993e8 A |
790 | stripPatternSpaces(other.stripPatternSpaces), |
791 | listFormatter(NULL), | |
792 | listFormatterStd(NULL) { | |
57a6839d A |
793 | cache->addRef(); |
794 | numberFormat->addRef(); | |
795 | pluralRules->addRef(); | |
2ca993e8 A |
796 | if (other.listFormatter != NULL) { |
797 | listFormatter = new ListFormatter(*other.listFormatter); | |
798 | } | |
799 | if (other.listFormatterStd != NULL) { | |
800 | listFormatterStd = new ListFormatter(*other.listFormatterStd); | |
801 | } | |
57a6839d A |
802 | } |
803 | ||
804 | MeasureFormat &MeasureFormat::operator=(const MeasureFormat &other) { | |
805 | if (this == &other) { | |
806 | return *this; | |
807 | } | |
808 | Format::operator=(other); | |
809 | SharedObject::copyPtr(other.cache, cache); | |
810 | SharedObject::copyPtr(other.numberFormat, numberFormat); | |
811 | SharedObject::copyPtr(other.pluralRules, pluralRules); | |
812 | width = other.width; | |
2ca993e8 | 813 | stripPatternSpaces = other.stripPatternSpaces; |
57a6839d | 814 | delete listFormatter; |
2ca993e8 A |
815 | if (other.listFormatter != NULL) { |
816 | listFormatter = new ListFormatter(*other.listFormatter); | |
817 | } else { | |
818 | listFormatter = NULL; | |
819 | } | |
820 | delete listFormatterStd; | |
821 | if (other.listFormatterStd != NULL) { | |
822 | listFormatterStd = new ListFormatter(*other.listFormatterStd); | |
823 | } else { | |
824 | listFormatterStd = NULL; | |
825 | } | |
57a6839d A |
826 | return *this; |
827 | } | |
828 | ||
829 | MeasureFormat::MeasureFormat() : | |
830 | cache(NULL), | |
831 | numberFormat(NULL), | |
832 | pluralRules(NULL), | |
2ca993e8 A |
833 | width(UMEASFMT_WIDTH_SHORT), |
834 | stripPatternSpaces(FALSE), | |
835 | listFormatter(NULL), | |
836 | listFormatterStd(NULL) { | |
57a6839d A |
837 | } |
838 | ||
839 | MeasureFormat::~MeasureFormat() { | |
840 | if (cache != NULL) { | |
841 | cache->removeRef(); | |
842 | } | |
843 | if (numberFormat != NULL) { | |
844 | numberFormat->removeRef(); | |
845 | } | |
846 | if (pluralRules != NULL) { | |
847 | pluralRules->removeRef(); | |
848 | } | |
849 | delete listFormatter; | |
2ca993e8 | 850 | delete listFormatterStd; |
57a6839d A |
851 | } |
852 | ||
853 | UBool MeasureFormat::operator==(const Format &other) const { | |
854 | if (this == &other) { // Same object, equal | |
855 | return TRUE; | |
856 | } | |
857 | if (!Format::operator==(other)) { | |
858 | return FALSE; | |
859 | } | |
860 | const MeasureFormat &rhs = static_cast<const MeasureFormat &>(other); | |
861 | ||
862 | // Note: Since the ListFormatter depends only on Locale and width, we | |
863 | // don't have to check it here. | |
864 | ||
865 | // differing widths aren't equivalent | |
2ca993e8 | 866 | if (width != rhs.width || stripPatternSpaces != rhs.stripPatternSpaces) { |
57a6839d A |
867 | return FALSE; |
868 | } | |
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 | |
877 | return FALSE; | |
878 | } | |
879 | if (uprv_strcmp(localeId, rhsLocaleId) != 0) { | |
880 | return FALSE; | |
881 | } | |
882 | } | |
883 | // Locales same, check NumberFormat if shared data differs. | |
884 | return ( | |
885 | numberFormat == rhs.numberFormat || | |
886 | **numberFormat == **rhs.numberFormat); | |
887 | } | |
888 | ||
889 | Format *MeasureFormat::clone() const { | |
890 | return new MeasureFormat(*this); | |
891 | } | |
892 | ||
893 | UnicodeString &MeasureFormat::format( | |
894 | const Formattable &obj, | |
895 | UnicodeString &appendTo, | |
896 | FieldPosition &pos, | |
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); | |
905 | } | |
906 | } | |
907 | status = U_ILLEGAL_ARGUMENT_ERROR; | |
908 | return appendTo; | |
909 | } | |
910 | ||
911 | void MeasureFormat::parseObject( | |
912 | const UnicodeString & /*source*/, | |
913 | Formattable & /*result*/, | |
914 | ParsePosition& /*pos*/) const { | |
915 | return; | |
916 | } | |
917 | ||
b331163b A |
918 | UnicodeString &MeasureFormat::formatMeasurePerUnit( |
919 | const Measure &measure, | |
920 | const MeasureUnit &perUnit, | |
921 | UnicodeString &appendTo, | |
922 | FieldPosition &pos, | |
923 | UErrorCode &status) const { | |
924 | if (U_FAILURE(status)) { | |
925 | return appendTo; | |
926 | } | |
0f5d89e8 A |
927 | bool isResolved = false; |
928 | MeasureUnit resolvedUnit = | |
929 | MeasureUnit::resolveUnitPerUnit(measure.getUnit(), perUnit, &isResolved); | |
930 | if (isResolved) { | |
931 | Measure newMeasure(measure.getNumber(), new MeasureUnit(resolvedUnit), status); | |
b331163b A |
932 | return formatMeasure( |
933 | newMeasure, **numberFormat, appendTo, pos, status); | |
934 | } | |
935 | FieldPosition fpos(pos.getField()); | |
936 | UnicodeString result; | |
937 | int32_t offset = withPerUnitAndAppend( | |
938 | formatMeasure( | |
939 | measure, **numberFormat, result, fpos, status), | |
940 | perUnit, | |
941 | appendTo, | |
942 | status); | |
943 | if (U_FAILURE(status)) { | |
944 | return appendTo; | |
945 | } | |
946 | if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) { | |
947 | pos.setBeginIndex(fpos.getBeginIndex() + offset); | |
948 | pos.setEndIndex(fpos.getEndIndex() + offset); | |
949 | } | |
950 | return appendTo; | |
951 | } | |
952 | ||
57a6839d A |
953 | UnicodeString &MeasureFormat::formatMeasures( |
954 | const Measure *measures, | |
955 | int32_t measureCount, | |
956 | UnicodeString &appendTo, | |
957 | FieldPosition &pos, | |
958 | UErrorCode &status) const { | |
959 | if (U_FAILURE(status)) { | |
960 | return appendTo; | |
961 | } | |
962 | if (measureCount == 0) { | |
963 | return appendTo; | |
964 | } | |
965 | if (measureCount == 1) { | |
966 | return formatMeasure(measures[0], **numberFormat, appendTo, pos, status); | |
967 | } | |
968 | if (width == UMEASFMT_WIDTH_NUMERIC) { | |
969 | Formattable hms[3]; | |
970 | int32_t bitMap = toHMS(measures, measureCount, hms, status); | |
971 | if (bitMap > 0) { | |
2ca993e8 A |
972 | FieldPositionIteratorHandler handler(NULL, status); |
973 | return formatNumeric(hms, bitMap, appendTo, handler, status); | |
57a6839d A |
974 | } |
975 | } | |
976 | if (pos.getField() != FieldPosition::DONT_CARE) { | |
977 | return formatMeasuresSlowTrack( | |
978 | measures, measureCount, appendTo, pos, status); | |
979 | } | |
980 | UnicodeString *results = new UnicodeString[measureCount]; | |
981 | if (results == NULL) { | |
982 | status = U_MEMORY_ALLOCATION_ERROR; | |
983 | return appendTo; | |
984 | } | |
985 | for (int32_t i = 0; i < measureCount; ++i) { | |
986 | const NumberFormat *nf = cache->getIntegerFormat(); | |
987 | if (i == measureCount - 1) { | |
988 | nf = numberFormat->get(); | |
989 | } | |
990 | formatMeasure( | |
991 | measures[i], | |
992 | *nf, | |
993 | results[i], | |
994 | pos, | |
995 | status); | |
996 | } | |
997 | listFormatter->format(results, measureCount, appendTo, status); | |
2ca993e8 A |
998 | delete [] results; |
999 | return appendTo; | |
1000 | } | |
1001 | ||
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)) { | |
1011 | return appendTo; | |
1012 | } | |
1013 | FieldPositionIteratorHandler handler(posIter, status); | |
1014 | if (measureCount == 0) { | |
1015 | return appendTo; | |
1016 | } | |
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()); | |
1024 | return appendTo; | |
1025 | } | |
1026 | if (width == UMEASFMT_WIDTH_NUMERIC) { | |
1027 | Formattable hms[3]; | |
1028 | int32_t bitMap = toHMS(measures, measureCount, hms, status); | |
1029 | if (bitMap > 0) { | |
1030 | return formatNumeric(hms, bitMap, appendTo, handler, status); | |
1031 | } | |
1032 | } | |
1033 | UnicodeString *results = new UnicodeString[measureCount]; | |
1034 | if (results == NULL) { | |
1035 | status = U_MEMORY_ALLOCATION_ERROR; | |
1036 | return appendTo; | |
1037 | } | |
1038 | FieldPosition *numPositions = new FieldPosition[measureCount]; | |
1039 | if (results == NULL) { | |
1040 | delete [] results; | |
1041 | status = U_MEMORY_ALLOCATION_ERROR; | |
1042 | return appendTo; | |
1043 | } | |
1044 | ||
1045 | for (int32_t i = 0; i < measureCount; ++i) { | |
1046 | const NumberFormat *nf = cache->getIntegerFormat(); | |
1047 | if (i == measureCount - 1) { | |
1048 | nf = numberFormat->get(); | |
1049 | } | |
1050 | numPositions[i].setField(UAMEASFMT_NUMERIC_FIELD_FLAG); | |
1051 | formatMeasure( | |
1052 | measures[i], | |
1053 | *nf, | |
1054 | results[i], | |
1055 | numPositions[i], | |
1056 | status); | |
1057 | } | |
1058 | listFormatter->format(results, measureCount, appendTo, status); | |
1059 | for (int32_t i = 0; i < measureCount; ++i) { | |
1060 | int32_t begin = appendTo.indexOf(results[i]); | |
1061 | if (begin >= 0) { | |
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); | |
1068 | } | |
1069 | } | |
1070 | } | |
1071 | delete [] results; | |
1072 | delete [] numPositions; | |
57a6839d A |
1073 | return appendTo; |
1074 | } | |
1075 | ||
2ca993e8 | 1076 | |
f3c0d7a5 A |
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]; | |
1081 | if (dnam == NULL) { | |
1082 | int32_t fallbackWidth = cache->widthFallback[width]; | |
1083 | dnam = styleToDnam[fallbackWidth]; | |
1084 | } | |
1085 | ||
1086 | UnicodeString result; | |
1087 | if (dnam == NULL) { | |
1088 | result.setToBogus(); | |
1089 | } else { | |
1090 | result.setTo(dnam, -1); | |
1091 | } | |
1092 | return result; | |
1093 | } | |
1094 | ||
57a6839d A |
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)) { | |
1103 | return; | |
1104 | } | |
1105 | const char *name = locale.getName(); | |
1106 | setLocaleIDs(name, name); | |
1107 | ||
b331163b A |
1108 | UnifiedCache::getByLocale(locale, cache, status); |
1109 | if (U_FAILURE(status)) { | |
57a6839d A |
1110 | return; |
1111 | } | |
1112 | ||
b331163b A |
1113 | const SharedPluralRules *pr = PluralRules::createSharedInstance( |
1114 | locale, UPLURAL_TYPE_CARDINAL, status); | |
57a6839d A |
1115 | if (U_FAILURE(status)) { |
1116 | return; | |
1117 | } | |
b331163b A |
1118 | SharedObject::copyPtr(pr, pluralRules); |
1119 | pr->removeRef(); | |
57a6839d | 1120 | if (nf.isNull()) { |
b331163b A |
1121 | const SharedNumberFormat *shared = NumberFormat::createSharedInstance( |
1122 | locale, UNUM_DECIMAL, status); | |
57a6839d A |
1123 | if (U_FAILURE(status)) { |
1124 | return; | |
1125 | } | |
b331163b A |
1126 | SharedObject::copyPtr(shared, numberFormat); |
1127 | shared->removeRef(); | |
57a6839d A |
1128 | } else { |
1129 | adoptNumberFormat(nf.orphan(), status); | |
1130 | if (U_FAILURE(status)) { | |
1131 | return; | |
1132 | } | |
1133 | } | |
1134 | width = w; | |
2ca993e8 A |
1135 | if (stripPatternSpaces) { |
1136 | w = UMEASFMT_WIDTH_NARROW; | |
1137 | } | |
57a6839d A |
1138 | delete listFormatter; |
1139 | listFormatter = ListFormatter::createInstance( | |
1140 | locale, | |
2ca993e8 A |
1141 | listStyles[getRegularWidth(w)], |
1142 | status); | |
1143 | delete listFormatterStd; | |
1144 | listFormatterStd = ListFormatter::createInstance( | |
1145 | locale, | |
1146 | "standard", | |
57a6839d A |
1147 | status); |
1148 | } | |
1149 | ||
1150 | void MeasureFormat::adoptNumberFormat( | |
1151 | NumberFormat *nfToAdopt, UErrorCode &status) { | |
1152 | LocalPointer<NumberFormat> nf(nfToAdopt); | |
1153 | if (U_FAILURE(status)) { | |
1154 | return; | |
1155 | } | |
1156 | SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias()); | |
1157 | if (shared == NULL) { | |
1158 | status = U_MEMORY_ALLOCATION_ERROR; | |
1159 | return; | |
1160 | } | |
1161 | nf.orphan(); | |
1162 | SharedObject::copyPtr(shared, numberFormat); | |
1163 | } | |
1164 | ||
1165 | UBool MeasureFormat::setMeasureFormatLocale(const Locale &locale, UErrorCode &status) { | |
1166 | if (U_FAILURE(status) || locale == getLocale(status)) { | |
1167 | return FALSE; | |
1168 | } | |
1169 | initMeasureFormat(locale, width, NULL, status); | |
1170 | return U_SUCCESS(status); | |
f3c0d7a5 | 1171 | } |
57a6839d | 1172 | |
b331163b A |
1173 | // Apple-specific for now |
1174 | UMeasureFormatWidth MeasureFormat::getWidth() const { | |
1175 | return width; | |
1176 | } | |
1177 | ||
57a6839d A |
1178 | const NumberFormat &MeasureFormat::getNumberFormat() const { |
1179 | return **numberFormat; | |
1180 | } | |
1181 | ||
1182 | const PluralRules &MeasureFormat::getPluralRules() const { | |
1183 | return **pluralRules; | |
1184 | } | |
1185 | ||
1186 | Locale MeasureFormat::getLocale(UErrorCode &status) const { | |
1187 | return Format::getLocale(ULOC_VALID_LOCALE, status); | |
1188 | } | |
1189 | ||
1190 | const char *MeasureFormat::getLocaleID(UErrorCode &status) const { | |
1191 | return Format::getLocaleID(ULOC_VALID_LOCALE, status); | |
1192 | } | |
1193 | ||
2ca993e8 | 1194 | // Apple=specific |
f3c0d7a5 A |
1195 | // now just re-implement using standard getUnitDisplayName |
1196 | // so we no longer use cache->getDisplayName | |
2ca993e8 A |
1197 | UnicodeString &MeasureFormat::getUnitName( |
1198 | const MeasureUnit* unit, | |
1199 | UnicodeString &result ) const { | |
f3c0d7a5 A |
1200 | UErrorCode status = U_ZERO_ERROR; |
1201 | result = getUnitDisplayName(*unit, status); // does not use or set status | |
2ca993e8 A |
1202 | return result; |
1203 | } | |
1204 | ||
1205 | // Apple=specific | |
1206 | UnicodeString &MeasureFormat::getMultipleUnitNames( | |
1207 | const MeasureUnit** units, | |
1208 | int32_t unitCount, | |
1209 | UAMeasureNameListStyle listStyle, | |
1210 | UnicodeString &result ) const { | |
1211 | if (unitCount == 0) { | |
1212 | return result.remove(); | |
1213 | } | |
1214 | if (unitCount == 1) { | |
1215 | return getUnitName(units[0], result); | |
1216 | } | |
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]); | |
1221 | } | |
1222 | UErrorCode status = U_ZERO_ERROR; | |
1223 | if (listStyle == UAMEASNAME_LIST_STANDARD) { | |
1224 | listFormatterStd->format(results, unitCount, result, status); | |
1225 | } else { | |
1226 | listFormatter->format(results, unitCount, result, status); | |
1227 | } | |
1228 | delete [] results; | |
1229 | if (U_SUCCESS(status)) { | |
1230 | return result; | |
1231 | } | |
1232 | } | |
1233 | result.setToBogus(); | |
1234 | return result; | |
1235 | } | |
1236 | ||
57a6839d A |
1237 | UnicodeString &MeasureFormat::formatMeasure( |
1238 | const Measure &measure, | |
1239 | const NumberFormat &nf, | |
1240 | UnicodeString &appendTo, | |
1241 | FieldPosition &pos, | |
1242 | UErrorCode &status) const { | |
1243 | if (U_FAILURE(status)) { | |
1244 | return appendTo; | |
1245 | } | |
1246 | const Formattable& amtNumber = measure.getNumber(); | |
1247 | const MeasureUnit& amtUnit = measure.getUnit(); | |
1248 | if (isCurrency(amtUnit)) { | |
1249 | UChar isoCode[4]; | |
1250 | u_charsToUChars(amtUnit.getSubtype(), isoCode, 4); | |
2ca993e8 | 1251 | return cache->getCurrencyFormat(width)->format( |
57a6839d A |
1252 | new CurrencyAmount(amtNumber, isoCode, status), |
1253 | appendTo, | |
1254 | pos, | |
1255 | status); | |
1256 | } | |
2ca993e8 A |
1257 | UnicodeString formattedNumber; |
1258 | UBool posForFullNumericPart = (pos.getField() == UAMEASFMT_NUMERIC_FIELD_FLAG); | |
1259 | if (posForFullNumericPart) { | |
1260 | pos.setField(FieldPosition::DONT_CARE); | |
1261 | } | |
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()); | |
1268 | } | |
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); | |
1287 | } | |
1288 | } | |
1289 | } | |
1290 | } | |
1291 | } | |
57a6839d | 1292 | } |
2ca993e8 | 1293 | return appendTo; |
57a6839d A |
1294 | } |
1295 | ||
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, | |
2ca993e8 | 1301 | FieldPositionHandler& handler, |
57a6839d A |
1302 | UErrorCode &status) const { |
1303 | if (U_FAILURE(status)) { | |
1304 | return appendTo; | |
1305 | } | |
f3c0d7a5 | 1306 | UDate millis = |
57a6839d A |
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); | |
1310 | switch (bitMap) { | |
1311 | case 5: // hs | |
1312 | case 7: // hms | |
1313 | return formatNumeric( | |
1314 | millis, | |
1315 | cache->getNumericDateFormatters()->hourMinuteSecond, | |
1316 | UDAT_SECOND_FIELD, | |
1317 | hms[2], | |
1318 | appendTo, | |
2ca993e8 | 1319 | handler, |
57a6839d A |
1320 | status); |
1321 | break; | |
1322 | case 6: // ms | |
1323 | return formatNumeric( | |
1324 | millis, | |
1325 | cache->getNumericDateFormatters()->minuteSecond, | |
1326 | UDAT_SECOND_FIELD, | |
1327 | hms[2], | |
1328 | appendTo, | |
2ca993e8 | 1329 | handler, |
57a6839d A |
1330 | status); |
1331 | break; | |
1332 | case 3: // hm | |
1333 | return formatNumeric( | |
1334 | millis, | |
1335 | cache->getNumericDateFormatters()->hourMinute, | |
1336 | UDAT_MINUTE_FIELD, | |
1337 | hms[1], | |
1338 | appendTo, | |
2ca993e8 | 1339 | handler, |
57a6839d A |
1340 | status); |
1341 | break; | |
1342 | default: | |
1343 | status = U_INTERNAL_PROGRAM_ERROR; | |
1344 | return appendTo; | |
1345 | break; | |
1346 | } | |
1347 | return appendTo; | |
1348 | } | |
1349 | ||
1350 | static void appendRange( | |
1351 | const UnicodeString &src, | |
1352 | int32_t start, | |
1353 | int32_t end, | |
1354 | UnicodeString &dest) { | |
1355 | dest.append(src, start, end - start); | |
1356 | } | |
1357 | ||
1358 | static void appendRange( | |
1359 | const UnicodeString &src, | |
1360 | int32_t end, | |
1361 | UnicodeString &dest) { | |
1362 | dest.append(src, end, src.length() - end); | |
1363 | } | |
1364 | ||
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, | |
2ca993e8 | 1372 | FieldPositionHandler& handler, |
57a6839d A |
1373 | UErrorCode &status) const { |
1374 | if (U_FAILURE(status)) { | |
1375 | return appendTo; | |
1376 | } | |
1377 | // Format the smallest amount with this object's NumberFormat | |
1378 | UnicodeString smallestAmountFormatted; | |
1379 | ||
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 | |
1382 | // '0:00:9.3' | |
1383 | FieldPosition intFieldPosition(UNUM_INTEGER_FIELD); | |
1384 | (*numberFormat)->format( | |
1385 | smallestAmount, smallestAmountFormatted, intFieldPosition, status); | |
1386 | if ( | |
1387 | intFieldPosition.getBeginIndex() == 0 && | |
1388 | intFieldPosition.getEndIndex() == 0) { | |
1389 | status = U_INTERNAL_PROGRAM_ERROR; | |
1390 | return appendTo; | |
1391 | } | |
1392 | ||
1393 | // Format time. draft becomes something like '5:30:45' | |
0f5d89e8 | 1394 | // #13606: DateFormat is not thread-safe, but MeasureFormat advertises itself as thread-safe. |
2ca993e8 | 1395 | FieldPositionIterator posIter; |
57a6839d | 1396 | UnicodeString draft; |
0f5d89e8 A |
1397 | static UMutex dateFmtMutex = U_MUTEX_INITIALIZER; |
1398 | umtx_lock(&dateFmtMutex); | |
2ca993e8 | 1399 | dateFmt.format(date, draft, &posIter, status); |
0f5d89e8 | 1400 | umtx_unlock(&dateFmtMutex); |
2ca993e8 A |
1401 | |
1402 | int32_t start = appendTo.length(); | |
1403 | FieldPosition smallestFieldPosition(smallestField); | |
1404 | FieldPosition fp; | |
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; | |
1418 | default: | |
1419 | measField = -1; break; | |
1420 | } | |
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()); | |
1425 | } | |
1426 | } else { | |
1427 | smallestFieldPosition.setBeginIndex(fp.getBeginIndex()); | |
1428 | smallestFieldPosition.setEndIndex(fp.getEndIndex()); | |
1429 | break; | |
1430 | } | |
1431 | } | |
57a6839d A |
1432 | |
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); | |
1441 | appendRange( | |
1442 | smallestAmountFormatted, | |
1443 | 0, | |
1444 | intFieldPosition.getBeginIndex(), | |
1445 | appendTo); | |
1446 | appendRange( | |
1447 | draft, | |
1448 | smallestFieldPosition.getBeginIndex(), | |
1449 | smallestFieldPosition.getEndIndex(), | |
1450 | appendTo); | |
1451 | appendRange( | |
1452 | smallestAmountFormatted, | |
1453 | intFieldPosition.getEndIndex(), | |
1454 | appendTo); | |
1455 | appendRange( | |
1456 | draft, | |
1457 | smallestFieldPosition.getEndIndex(), | |
1458 | appendTo); | |
2ca993e8 A |
1459 | handler.addAttribute(measField, start + smallestFieldPosition.getBeginIndex(), appendTo.length()); |
1460 | handler.addAttribute(measField | UAMEASFMT_NUMERIC_FIELD_FLAG, start + smallestFieldPosition.getBeginIndex(), appendTo.length()); | |
57a6839d A |
1461 | } else { |
1462 | appendTo.append(draft); | |
1463 | } | |
1464 | return appendTo; | |
1465 | } | |
1466 | ||
2ca993e8 A |
1467 | const SimpleFormatter *MeasureFormat::getFormatterOrNull( |
1468 | const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index) const { | |
1469 | width = getRegularWidth(width); | |
0f5d89e8 | 1470 | SimpleFormatter *const (*unitPatterns)[PATTERN_COUNT] = &cache->patterns[unit.getIndex()][0]; |
2ca993e8 A |
1471 | if (unitPatterns[width][index] != NULL) { |
1472 | return unitPatterns[width][index]; | |
57a6839d | 1473 | } |
2ca993e8 A |
1474 | int32_t fallbackWidth = cache->widthFallback[width]; |
1475 | if (fallbackWidth != UMEASFMT_WIDTH_COUNT && unitPatterns[fallbackWidth][index] != NULL) { | |
1476 | return unitPatterns[fallbackWidth][index]; | |
57a6839d | 1477 | } |
57a6839d A |
1478 | return NULL; |
1479 | } | |
1480 | ||
2ca993e8 A |
1481 | const SimpleFormatter *MeasureFormat::getFormatter( |
1482 | const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index, | |
1483 | UErrorCode &errorCode) const { | |
1484 | if (U_FAILURE(errorCode)) { | |
1485 | return NULL; | |
b331163b | 1486 | } |
2ca993e8 A |
1487 | const SimpleFormatter *pattern = getFormatterOrNull(unit, width, index); |
1488 | if (pattern == NULL) { | |
1489 | errorCode = U_MISSING_RESOURCE_ERROR; | |
b331163b | 1490 | } |
2ca993e8 A |
1491 | return pattern; |
1492 | } | |
1493 | ||
1494 | const SimpleFormatter *MeasureFormat::getPluralFormatter( | |
1495 | const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index, | |
1496 | UErrorCode &errorCode) const { | |
1497 | if (U_FAILURE(errorCode)) { | |
1498 | return NULL; | |
b331163b | 1499 | } |
2ca993e8 A |
1500 | if (index != StandardPlural::OTHER) { |
1501 | const SimpleFormatter *pattern = getFormatterOrNull(unit, width, index); | |
1502 | if (pattern != NULL) { | |
1503 | return pattern; | |
1504 | } | |
1505 | } | |
1506 | return getFormatter(unit, width, StandardPlural::OTHER, errorCode); | |
b331163b A |
1507 | } |
1508 | ||
2ca993e8 A |
1509 | const SimpleFormatter *MeasureFormat::getPerFormatter( |
1510 | UMeasureFormatWidth width, | |
b331163b A |
1511 | UErrorCode &status) const { |
1512 | if (U_FAILURE(status)) { | |
1513 | return NULL; | |
1514 | } | |
2ca993e8 A |
1515 | width = getRegularWidth(width); |
1516 | const SimpleFormatter * perFormatters = cache->perFormatters; | |
1517 | if (perFormatters[width].getArgumentLimit() == 2) { | |
1518 | return &perFormatters[width]; | |
b331163b | 1519 | } |
2ca993e8 A |
1520 | int32_t fallbackWidth = cache->widthFallback[width]; |
1521 | if (fallbackWidth != UMEASFMT_WIDTH_COUNT && | |
1522 | perFormatters[fallbackWidth].getArgumentLimit() == 2) { | |
1523 | return &perFormatters[fallbackWidth]; | |
b331163b A |
1524 | } |
1525 | status = U_MISSING_RESOURCE_ERROR; | |
1526 | return NULL; | |
1527 | } | |
1528 | ||
b331163b A |
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)) { | |
1536 | return offset; | |
1537 | } | |
0f5d89e8 | 1538 | const SimpleFormatter *perUnitFormatter = getFormatterOrNull(perUnit, width, PER_UNIT_INDEX); |
b331163b A |
1539 | if (perUnitFormatter != NULL) { |
1540 | const UnicodeString *params[] = {&formatted}; | |
1541 | perUnitFormatter->formatAndAppend( | |
1542 | params, | |
1543 | UPRV_LENGTHOF(params), | |
1544 | appendTo, | |
1545 | &offset, | |
1546 | 1, | |
1547 | status); | |
1548 | return offset; | |
1549 | } | |
2ca993e8 A |
1550 | const SimpleFormatter *perFormatter = getPerFormatter(width, status); |
1551 | const SimpleFormatter *pattern = | |
1552 | getPluralFormatter(perUnit, width, StandardPlural::ONE, status); | |
b331163b A |
1553 | if (U_FAILURE(status)) { |
1554 | return offset; | |
1555 | } | |
2ca993e8 A |
1556 | UnicodeString perUnitString = pattern->getTextWithNoArguments(); |
1557 | perUnitString.trim(); | |
b331163b A |
1558 | const UnicodeString *params[] = {&formatted, &perUnitString}; |
1559 | perFormatter->formatAndAppend( | |
1560 | params, | |
1561 | UPRV_LENGTHOF(params), | |
1562 | appendTo, | |
1563 | &offset, | |
1564 | 1, | |
1565 | status); | |
1566 | return offset; | |
1567 | } | |
1568 | ||
57a6839d A |
1569 | UnicodeString &MeasureFormat::formatMeasuresSlowTrack( |
1570 | const Measure *measures, | |
1571 | int32_t measureCount, | |
1572 | UnicodeString& appendTo, | |
1573 | FieldPosition& pos, | |
1574 | UErrorCode& status) const { | |
1575 | if (U_FAILURE(status)) { | |
1576 | return appendTo; | |
1577 | } | |
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(); | |
1586 | } | |
1587 | if (fieldPositionFoundIndex == -1) { | |
1588 | formatMeasure(measures[i], *nf, results[i], fpos, status); | |
1589 | if (U_FAILURE(status)) { | |
1590 | delete [] results; | |
1591 | return appendTo; | |
1592 | } | |
1593 | if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) { | |
1594 | fieldPositionFoundIndex = i; | |
1595 | } | |
1596 | } else { | |
1597 | formatMeasure(measures[i], *nf, results[i], dontCare, status); | |
1598 | } | |
1599 | } | |
1600 | int32_t offset; | |
1601 | listFormatter->format( | |
1602 | results, | |
1603 | measureCount, | |
1604 | appendTo, | |
1605 | fieldPositionFoundIndex, | |
1606 | offset, | |
1607 | status); | |
1608 | if (U_FAILURE(status)) { | |
1609 | delete [] results; | |
1610 | return appendTo; | |
1611 | } | |
1612 | if (offset != -1) { | |
1613 | pos.setBeginIndex(fpos.getBeginIndex() + offset); | |
1614 | pos.setEndIndex(fpos.getEndIndex() + offset); | |
1615 | } | |
1616 | delete [] results; | |
1617 | return appendTo; | |
1618 | } | |
4388f060 | 1619 | |
374ca955 A |
1620 | MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(const Locale& locale, |
1621 | UErrorCode& ec) { | |
1622 | CurrencyFormat* fmt = NULL; | |
1623 | if (U_SUCCESS(ec)) { | |
1624 | fmt = new CurrencyFormat(locale, ec); | |
1625 | if (U_FAILURE(ec)) { | |
1626 | delete fmt; | |
1627 | fmt = NULL; | |
1628 | } | |
1629 | } | |
1630 | return fmt; | |
1631 | } | |
1632 | ||
1633 | MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(UErrorCode& ec) { | |
1634 | if (U_FAILURE(ec)) { | |
1635 | return NULL; | |
1636 | } | |
1637 | return MeasureFormat::createCurrencyFormat(Locale::getDefault(), ec); | |
1638 | } | |
1639 | ||
1640 | U_NAMESPACE_END | |
1641 | ||
1642 | #endif /* #if !UCONFIG_NO_FORMATTING */ |