]>
Commit | Line | Data |
---|---|---|
374ca955 A |
1 | /* |
2 | ********************************************************************** | |
2ca993e8 | 3 | * Copyright (c) 2004-2016, International Business Machines |
374ca955 A |
4 | * Corporation and others. All Rights Reserved. |
5 | ********************************************************************** | |
6 | * Author: Alan Liu | |
7 | * Created: April 20, 2004 | |
8 | * Since: ICU 3.0 | |
9 | ********************************************************************** | |
10 | */ | |
57a6839d | 11 | #include "utypeinfo.h" // for 'typeid' to work |
374ca955 A |
12 | #include "unicode/utypes.h" |
13 | ||
14 | #if !UCONFIG_NO_FORMATTING | |
15 | ||
16 | #include "unicode/measfmt.h" | |
57a6839d | 17 | #include "unicode/numfmt.h" |
374ca955 | 18 | #include "currfmt.h" |
57a6839d | 19 | #include "unicode/localpointer.h" |
2ca993e8 A |
20 | #include "resource.h" |
21 | #include "unicode/simpleformatter.h" | |
57a6839d A |
22 | #include "quantityformatter.h" |
23 | #include "unicode/plurrule.h" | |
24 | #include "unicode/decimfmt.h" | |
57a6839d A |
25 | #include "uresimp.h" |
26 | #include "unicode/ures.h" | |
b331163b | 27 | #include "ureslocs.h" |
57a6839d A |
28 | #include "cstring.h" |
29 | #include "mutex.h" | |
30 | #include "ucln_in.h" | |
31 | #include "unicode/listformatter.h" | |
32 | #include "charstr.h" | |
33 | #include "unicode/putil.h" | |
34 | #include "unicode/smpdtfmt.h" | |
b331163b | 35 | #include "uassert.h" |
2ca993e8 A |
36 | #include "unicode/uameasureformat.h" |
37 | #include "fphdlimp.h" | |
57a6839d A |
38 | |
39 | #include "sharednumberformat.h" | |
40 | #include "sharedpluralrules.h" | |
2ca993e8 | 41 | #include "standardplural.h" |
b331163b | 42 | #include "unifiedcache.h" |
57a6839d | 43 | |
2ca993e8 | 44 | #define MEAS_UNIT_COUNT 134 |
57a6839d A |
45 | #define WIDTH_INDEX_COUNT (UMEASFMT_WIDTH_NARROW + 1) |
46 | ||
374ca955 A |
47 | U_NAMESPACE_BEGIN |
48 | ||
57a6839d A |
49 | UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MeasureFormat) |
50 | ||
51 | // Used to format durations like 5:47 or 21:35:42. | |
52 | class NumericDateFormatters : public UMemory { | |
53 | public: | |
54 | // Formats like H:mm | |
55 | SimpleDateFormat hourMinute; | |
56 | ||
57 | // formats like M:ss | |
58 | SimpleDateFormat minuteSecond; | |
59 | ||
60 | // formats like H:mm:ss | |
61 | SimpleDateFormat hourMinuteSecond; | |
62 | ||
63 | // Constructor that takes the actual patterns for hour-minute, | |
64 | // minute-second, and hour-minute-second respectively. | |
65 | NumericDateFormatters( | |
66 | const UnicodeString &hm, | |
67 | const UnicodeString &ms, | |
68 | const UnicodeString &hms, | |
2ca993e8 | 69 | UErrorCode &status) : |
57a6839d | 70 | hourMinute(hm, status), |
2ca993e8 | 71 | minuteSecond(ms, status), |
57a6839d A |
72 | hourMinuteSecond(hms, status) { |
73 | const TimeZone *gmt = TimeZone::getGMT(); | |
74 | hourMinute.setTimeZone(*gmt); | |
75 | minuteSecond.setTimeZone(*gmt); | |
76 | hourMinuteSecond.setTimeZone(*gmt); | |
77 | } | |
78 | private: | |
79 | NumericDateFormatters(const NumericDateFormatters &other); | |
80 | NumericDateFormatters &operator=(const NumericDateFormatters &other); | |
81 | }; | |
82 | ||
2ca993e8 A |
83 | static UMeasureFormatWidth getRegularWidth(UMeasureFormatWidth width) { |
84 | if (width >= WIDTH_INDEX_COUNT) { | |
85 | return UMEASFMT_WIDTH_NARROW; | |
86 | } | |
87 | return width; | |
88 | } | |
89 | ||
90 | /** | |
91 | * Instances contain all MeasureFormat specific data for a particular locale. | |
92 | * This data is cached. It is never copied, but is shared via shared pointers. | |
93 | * | |
94 | * Note: We might change the cache data to have an array[WIDTH_INDEX_COUNT] of | |
95 | * complete sets of unit & per patterns, | |
96 | * to correspond to the resource data and its aliases. | |
97 | * | |
98 | * TODO: Maybe store more sparsely in general, with pointers rather than potentially-empty objects. | |
99 | */ | |
57a6839d A |
100 | class MeasureFormatCacheData : public SharedObject { |
101 | public: | |
2ca993e8 A |
102 | static const int32_t PER_UNIT_INDEX = StandardPlural::COUNT; |
103 | static const int32_t PATTERN_COUNT = PER_UNIT_INDEX + 1; | |
104 | ||
105 | /** | |
106 | * Redirection data from root-bundle, top-level sideways aliases. | |
107 | * - UMEASFMT_WIDTH_COUNT: initial value, just fall back to root | |
108 | * - UMEASFMT_WIDTH_WIDE/SHORT/NARROW: sideways alias for missing data | |
109 | */ | |
110 | UMeasureFormatWidth widthFallback[WIDTH_INDEX_COUNT]; | |
111 | /** Measure unit -> format width -> array of patterns ("{0} meters") (plurals + PER_UNIT_INDEX) */ | |
112 | SimpleFormatter *patterns[MEAS_UNIT_COUNT][WIDTH_INDEX_COUNT][PATTERN_COUNT]; | |
113 | SimpleFormatter perFormatters[WIDTH_INDEX_COUNT]; | |
114 | const UChar * displayNames[MEAS_UNIT_COUNT][WIDTH_INDEX_COUNT]; // Apple-specific for now | |
b331163b | 115 | |
57a6839d | 116 | MeasureFormatCacheData(); |
2ca993e8 A |
117 | virtual ~MeasureFormatCacheData(); |
118 | ||
119 | UBool hasPerFormatter(int32_t width) const { | |
120 | // TODO: Create a more obvious way to test if the per-formatter has been set? | |
121 | // Use pointers, check for NULL? Or add an isValid() method? | |
122 | return perFormatters[width].getArgumentLimit() == 2; | |
123 | } | |
124 | ||
57a6839d A |
125 | void adoptCurrencyFormat(int32_t widthIndex, NumberFormat *nfToAdopt) { |
126 | delete currencyFormats[widthIndex]; | |
127 | currencyFormats[widthIndex] = nfToAdopt; | |
128 | } | |
2ca993e8 A |
129 | const NumberFormat *getCurrencyFormat(UMeasureFormatWidth width) const { |
130 | return currencyFormats[getRegularWidth(width)]; | |
57a6839d A |
131 | } |
132 | void adoptIntegerFormat(NumberFormat *nfToAdopt) { | |
133 | delete integerFormat; | |
134 | integerFormat = nfToAdopt; | |
135 | } | |
136 | const NumberFormat *getIntegerFormat() const { | |
137 | return integerFormat; | |
138 | } | |
139 | void adoptNumericDateFormatters(NumericDateFormatters *formattersToAdopt) { | |
140 | delete numericDateFormatters; | |
141 | numericDateFormatters = formattersToAdopt; | |
142 | } | |
143 | const NumericDateFormatters *getNumericDateFormatters() const { | |
144 | return numericDateFormatters; | |
145 | } | |
2ca993e8 A |
146 | void setDisplayName( // Apple=specific |
147 | int32_t unitIndex, | |
b331163b | 148 | int32_t widthIndex, |
2ca993e8 A |
149 | const UChar *namePtr) { |
150 | if (widthIndex < WIDTH_INDEX_COUNT) { | |
151 | displayNames[unitIndex][widthIndex] = namePtr; | |
152 | } | |
b331163b | 153 | } |
2ca993e8 A |
154 | const UChar * getDisplayName( // Apple=specific |
155 | int32_t unitIndex, | |
156 | int32_t widthIndex) const { | |
157 | return (widthIndex < WIDTH_INDEX_COUNT)? displayNames[unitIndex][widthIndex]: NULL; | |
b331163b | 158 | } |
2ca993e8 | 159 | |
57a6839d A |
160 | private: |
161 | NumberFormat *currencyFormats[WIDTH_INDEX_COUNT]; | |
162 | NumberFormat *integerFormat; | |
163 | NumericDateFormatters *numericDateFormatters; | |
164 | MeasureFormatCacheData(const MeasureFormatCacheData &other); | |
165 | MeasureFormatCacheData &operator=(const MeasureFormatCacheData &other); | |
166 | }; | |
167 | ||
168 | MeasureFormatCacheData::MeasureFormatCacheData() { | |
2ca993e8 A |
169 | for (int32_t i = 0; i < WIDTH_INDEX_COUNT; ++i) { |
170 | widthFallback[i] = UMEASFMT_WIDTH_COUNT; | |
171 | } | |
b331163b | 172 | for (int32_t i = 0; i < UPRV_LENGTHOF(currencyFormats); ++i) { |
57a6839d A |
173 | currencyFormats[i] = NULL; |
174 | } | |
2ca993e8 A |
175 | uprv_memset(patterns, 0, sizeof(patterns)); |
176 | integerFormat = NULL; | |
177 | numericDateFormatters = NULL; | |
178 | // Apple-specific for now: | |
b331163b A |
179 | for (int32_t i = 0; i < MEAS_UNIT_COUNT; ++i) { |
180 | for (int32_t j = 0; j < WIDTH_INDEX_COUNT; ++j) { | |
2ca993e8 | 181 | displayNames[i][j] = NULL; |
b331163b A |
182 | } |
183 | } | |
57a6839d A |
184 | } |
185 | ||
186 | MeasureFormatCacheData::~MeasureFormatCacheData() { | |
b331163b | 187 | for (int32_t i = 0; i < UPRV_LENGTHOF(currencyFormats); ++i) { |
57a6839d A |
188 | delete currencyFormats[i]; |
189 | } | |
b331163b A |
190 | for (int32_t i = 0; i < MEAS_UNIT_COUNT; ++i) { |
191 | for (int32_t j = 0; j < WIDTH_INDEX_COUNT; ++j) { | |
2ca993e8 A |
192 | for (int32_t k = 0; k < PATTERN_COUNT; ++k) { |
193 | delete patterns[i][j][k]; | |
194 | } | |
b331163b A |
195 | } |
196 | } | |
57a6839d A |
197 | delete integerFormat; |
198 | delete numericDateFormatters; | |
199 | } | |
200 | ||
57a6839d A |
201 | static UBool isCurrency(const MeasureUnit &unit) { |
202 | return (uprv_strcmp(unit.getType(), "currency") == 0); | |
203 | } | |
204 | ||
205 | static UBool getString( | |
206 | const UResourceBundle *resource, | |
207 | UnicodeString &result, | |
208 | UErrorCode &status) { | |
209 | int32_t len = 0; | |
210 | const UChar *resStr = ures_getString(resource, &len, &status); | |
211 | if (U_FAILURE(status)) { | |
212 | return FALSE; | |
213 | } | |
214 | result.setTo(TRUE, resStr, len); | |
215 | return TRUE; | |
216 | } | |
217 | ||
2ca993e8 | 218 | namespace { |
57a6839d | 219 | |
2ca993e8 A |
220 | static const UChar g_LOCALE_units[] = { |
221 | 0x2F, 0x4C, 0x4F, 0x43, 0x41, 0x4C, 0x45, 0x2F, | |
222 | 0x75, 0x6E, 0x69, 0x74, 0x73 | |
223 | }; | |
224 | static const UChar gShort[] = { 0x53, 0x68, 0x6F, 0x72, 0x74 }; | |
225 | static const UChar gNarrow[] = { 0x4E, 0x61, 0x72, 0x72, 0x6F, 0x77 }; | |
226 | ||
227 | /** | |
228 | * Sink for enumerating all of the measurement unit display names. | |
229 | * Contains inner sink classes, each one corresponding to a type of resource table. | |
230 | * The outer sink handles the top-level units, unitsNarrow, and unitsShort tables. | |
231 | * | |
232 | * More specific bundles (en_GB) are enumerated before their parents (en_001, en, root): | |
233 | * Only store a value if it is still missing, that is, it has not been overridden. | |
234 | * | |
235 | * C++: Each inner sink class has a reference to the main outer sink. | |
236 | * Java: Use non-static inner classes instead. | |
237 | */ | |
238 | struct UnitDataSink : public ResourceTableSink { | |
239 | /** | |
240 | * Sink for a table of display patterns. For example, | |
241 | * unitsShort/duration/hour contains other{"{0} hrs"}. | |
242 | */ | |
243 | struct UnitPatternSink : public ResourceTableSink { | |
244 | UnitPatternSink(UnitDataSink &sink) : outer(sink) {} | |
245 | ~UnitPatternSink(); | |
246 | ||
247 | void setFormatterIfAbsent(int32_t index, const ResourceValue &value, | |
248 | int32_t minPlaceholders, UErrorCode &errorCode) { | |
249 | SimpleFormatter **patterns = | |
250 | &outer.cacheData.patterns[outer.unitIndex][outer.width][0]; | |
251 | if (U_SUCCESS(errorCode) && patterns[index] == NULL) { | |
252 | patterns[index] = new SimpleFormatter( | |
253 | value.getUnicodeString(errorCode), minPlaceholders, 1, errorCode); | |
254 | if (U_SUCCESS(errorCode) && patterns[index] == NULL) { | |
255 | errorCode = U_MEMORY_ALLOCATION_ERROR; | |
256 | } | |
257 | } | |
57a6839d | 258 | } |
2ca993e8 A |
259 | |
260 | // Apple-specific: | |
261 | void setDisplayNameIfAbsent(const ResourceValue &value, UErrorCode &errorCode) { | |
262 | if (U_SUCCESS(errorCode) && outer.width < WIDTH_INDEX_COUNT) { | |
263 | const UChar ** displayName = &outer.cacheData.displayNames[outer.unitIndex][outer.width]; | |
264 | if (*displayName == NULL && value.getType() == URES_STRING) { | |
265 | int32_t nameLen = 0; | |
266 | *displayName = value.getString(nameLen, errorCode); | |
267 | } | |
268 | } | |
57a6839d | 269 | } |
2ca993e8 A |
270 | |
271 | virtual void put(const char *key, const ResourceValue &value, UErrorCode &errorCode) { | |
272 | if (U_FAILURE(errorCode)) { return; } | |
273 | if (uprv_strcmp(key, "dnam") == 0) { | |
274 | // Apple-specific for now, save name data | |
275 | setDisplayNameIfAbsent(value, errorCode); | |
276 | } else if (uprv_strcmp(key, "per") == 0) { | |
277 | // For example, "{0}/h". | |
278 | setFormatterIfAbsent(MeasureFormatCacheData::PER_UNIT_INDEX, value, 1, errorCode); | |
b331163b | 279 | } else { |
2ca993e8 A |
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 A |
285 | } |
286 | } | |
2ca993e8 A |
287 | UnitDataSink &outer; |
288 | } patternSink; | |
289 | ||
290 | /** | |
291 | * Sink for a table of per-unit tables. For example, | |
292 | * unitsShort/duration contains tables for duration-unit subtypes day & hour. | |
293 | */ | |
294 | struct UnitSubtypeSink : public ResourceTableSink { | |
295 | UnitSubtypeSink(UnitDataSink &sink) : outer(sink) {} | |
296 | ~UnitSubtypeSink(); | |
297 | virtual ResourceTableSink *getOrCreateTableSink( | |
298 | const char *key, int32_t /* initialSize */, UErrorCode &errorCode) { | |
299 | if (U_FAILURE(errorCode)) { return NULL; } | |
300 | outer.unitIndex = MeasureUnit::internalGetIndexForTypeAndSubtype(outer.type, key); | |
301 | if (outer.unitIndex >= 0) { | |
302 | return &outer.patternSink; | |
57a6839d | 303 | } |
2ca993e8 A |
304 | return NULL; |
305 | } | |
306 | UnitDataSink &outer; | |
307 | } subtypeSink; | |
308 | ||
309 | /** | |
310 | * Sink for compound x-per-y display pattern. For example, | |
311 | * unitsShort/compound/per may be "{0}/{1}". | |
312 | */ | |
313 | struct UnitCompoundSink : public ResourceTableSink { | |
314 | UnitCompoundSink(UnitDataSink &sink) : outer(sink) {} | |
315 | ~UnitCompoundSink(); | |
316 | virtual void put(const char *key, const ResourceValue &value, UErrorCode &errorCode) { | |
317 | if (U_SUCCESS(errorCode) && uprv_strcmp(key, "per") == 0) { | |
318 | outer.cacheData.perFormatters[outer.width]. | |
319 | applyPatternMinMaxArguments(value.getUnicodeString(errorCode), 2, 2, errorCode); | |
57a6839d | 320 | } |
2ca993e8 A |
321 | } |
322 | UnitDataSink &outer; | |
323 | } compoundSink; | |
324 | ||
325 | /** | |
326 | * Sink for a table of unit type tables. For example, | |
327 | * unitsShort contains tables for area & duration. | |
328 | * It also contains a table for the compound/per pattern. | |
329 | */ | |
330 | struct UnitTypeSink : public ResourceTableSink { | |
331 | UnitTypeSink(UnitDataSink &sink) : outer(sink) {} | |
332 | ~UnitTypeSink(); | |
333 | virtual ResourceTableSink *getOrCreateTableSink( | |
334 | const char *key, int32_t /* initialSize */, UErrorCode &errorCode) { | |
335 | if (U_FAILURE(errorCode)) { return NULL; } | |
336 | if (uprv_strcmp(key, "currency") == 0) { | |
337 | // Skip. | |
338 | } else if (uprv_strcmp(key, "compound") == 0) { | |
339 | if (!outer.cacheData.hasPerFormatter(outer.width)) { | |
340 | return &outer.compoundSink; | |
341 | } | |
342 | } else { | |
343 | outer.type = key; | |
344 | return &outer.subtypeSink; | |
57a6839d | 345 | } |
2ca993e8 A |
346 | return NULL; |
347 | } | |
348 | UnitDataSink &outer; | |
349 | } typeSink; | |
350 | ||
351 | UnitDataSink(MeasureFormatCacheData &outputData) | |
352 | : patternSink(*this), subtypeSink(*this), compoundSink(*this), typeSink(*this), | |
353 | cacheData(outputData), | |
354 | width(UMEASFMT_WIDTH_COUNT), type(NULL), unitIndex(0) {} | |
355 | ~UnitDataSink(); | |
356 | virtual void put(const char *key, const ResourceValue &value, UErrorCode &errorCode) { | |
357 | // Handle aliases like | |
358 | // units:alias{"/LOCALE/unitsShort"} | |
359 | // which should only occur in the root bundle. | |
360 | if (U_FAILURE(errorCode) || value.getType() != URES_ALIAS) { return; } | |
361 | UMeasureFormatWidth sourceWidth = widthFromKey(key); | |
362 | if (sourceWidth == UMEASFMT_WIDTH_COUNT) { | |
363 | // Alias from something we don't care about. | |
364 | return; | |
365 | } | |
366 | UMeasureFormatWidth targetWidth = widthFromAlias(value, errorCode); | |
367 | if (targetWidth == UMEASFMT_WIDTH_COUNT) { | |
368 | // We do not recognize what to fall back to. | |
369 | errorCode = U_INVALID_FORMAT_ERROR; | |
370 | return; | |
371 | } | |
372 | // Check that we do not fall back to another fallback. | |
373 | if (cacheData.widthFallback[targetWidth] != UMEASFMT_WIDTH_COUNT) { | |
374 | errorCode = U_INVALID_FORMAT_ERROR; | |
375 | return; | |
376 | } | |
377 | cacheData.widthFallback[sourceWidth] = targetWidth; | |
378 | } | |
379 | virtual ResourceTableSink *getOrCreateTableSink( | |
380 | const char *key, int32_t /* initialSize */, UErrorCode &errorCode) { | |
381 | if (U_SUCCESS(errorCode) && (width = widthFromKey(key)) != UMEASFMT_WIDTH_COUNT) { | |
382 | return &typeSink; | |
383 | } | |
384 | return NULL; | |
385 | } | |
386 | ||
387 | static UMeasureFormatWidth widthFromKey(const char *key) { | |
388 | if (uprv_strncmp(key, "units", 5) == 0) { | |
389 | key += 5; | |
390 | if (*key == 0) { | |
391 | return UMEASFMT_WIDTH_WIDE; | |
392 | } else if (uprv_strcmp(key, "Short") == 0) { | |
393 | return UMEASFMT_WIDTH_SHORT; | |
394 | } else if (uprv_strcmp(key, "Narrow") == 0) { | |
395 | return UMEASFMT_WIDTH_NARROW; | |
57a6839d | 396 | } |
2ca993e8 A |
397 | } |
398 | return UMEASFMT_WIDTH_COUNT; | |
399 | } | |
400 | ||
401 | static UMeasureFormatWidth widthFromAlias(const ResourceValue &value, UErrorCode &errorCode) { | |
402 | int32_t length; | |
403 | const UChar *s = value.getAliasString(length, errorCode); | |
404 | // For example: "/LOCALE/unitsShort" | |
405 | if (U_SUCCESS(errorCode) && length >= 13 && u_memcmp(s, g_LOCALE_units, 13) == 0) { | |
406 | s += 13; | |
407 | length -= 13; | |
408 | if (*s == 0) { | |
409 | return UMEASFMT_WIDTH_WIDE; | |
410 | } else if (u_strCompare(s, length, gShort, 5, FALSE) == 0) { | |
411 | return UMEASFMT_WIDTH_SHORT; | |
412 | } else if (u_strCompare(s, length, gNarrow, 6, FALSE) == 0) { | |
413 | return UMEASFMT_WIDTH_NARROW; | |
57a6839d A |
414 | } |
415 | } | |
2ca993e8 A |
416 | return UMEASFMT_WIDTH_COUNT; |
417 | } | |
418 | ||
419 | // Output data. | |
420 | MeasureFormatCacheData &cacheData; | |
421 | ||
422 | // Path to current data. | |
423 | UMeasureFormatWidth width; | |
424 | const char *type; | |
425 | int32_t unitIndex; | |
426 | }; | |
427 | ||
428 | // Virtual destructors must be defined out of line. | |
429 | UnitDataSink::UnitPatternSink::~UnitPatternSink() {} | |
430 | UnitDataSink::UnitSubtypeSink::~UnitSubtypeSink() {} | |
431 | UnitDataSink::UnitCompoundSink::~UnitCompoundSink() {} | |
432 | UnitDataSink::UnitTypeSink::~UnitTypeSink() {} | |
433 | UnitDataSink::~UnitDataSink() {} | |
434 | ||
435 | } // namespace | |
436 | ||
437 | static const UAMeasureUnit indexToUAMsasUnit[] = { | |
438 | // UAMeasureUnit // UAMeasUnit vals # MeasUnit.getIndex() | |
439 | UAMEASUNIT_ACCELERATION_G_FORCE, // (0 << 8) + 0, # 0 | |
440 | UAMEASUNIT_ACCELERATION_METER_PER_SECOND_SQUARED, // (0 << 8) + 1, # 1 | |
441 | UAMEASUNIT_ANGLE_ARC_MINUTE, // (1 << 8) + 1, # 2 | |
442 | UAMEASUNIT_ANGLE_ARC_SECOND, // (1 << 8) + 2, # 3 | |
443 | UAMEASUNIT_ANGLE_DEGREE, // (1 << 8) + 0, # 4 | |
444 | UAMEASUNIT_ANGLE_RADIAN, // (1 << 8) + 3, # 5 | |
445 | UAMEASUNIT_ANGLE_REVOLUTION, // (1 << 8) + 4, # 6 | |
446 | UAMEASUNIT_AREA_ACRE, // (2 << 8) + 4, # 7 | |
447 | UAMEASUNIT_AREA_HECTARE, // (2 << 8) + 5, # 8 | |
448 | UAMEASUNIT_AREA_SQUARE_CENTIMETER, // (2 << 8) + 6, # 9 | |
449 | UAMEASUNIT_AREA_SQUARE_FOOT, // (2 << 8) + 2, # 10 | |
450 | UAMEASUNIT_AREA_SQUARE_INCH, // (2 << 8) + 7, # 11 | |
451 | UAMEASUNIT_AREA_SQUARE_KILOMETER, // (2 << 8) + 1, # 12 | |
452 | UAMEASUNIT_AREA_SQUARE_METER, // (2 << 8) + 0, # 13 | |
453 | UAMEASUNIT_AREA_SQUARE_MILE, // (2 << 8) + 3, # 14 | |
454 | UAMEASUNIT_AREA_SQUARE_YARD, // (2 << 8) + 8, # 15 | |
455 | UAMEASUNIT_CONCENTRATION_KARAT, // (18 << 8) + 0, # 16 | |
456 | UAMEASUNIT_CONCENTRATION_MILLIGRAM_PER_DECILITER, // (18 << 8) + 1, # 17 | |
457 | UAMEASUNIT_CONCENTRATION_MILLIMOLE_PER_LITER, // (18 << 8) + 2, # 18 | |
458 | UAMEASUNIT_CONCENTRATION_PART_PER_MILLION, // (18 << 8) + 3, # 19 | |
459 | UAMEASUNIT_CONSUMPTION_LITER_PER_100_KILOMETERs, // (13 << 8) + 2, # 20 | |
460 | UAMEASUNIT_CONSUMPTION_LITER_PER_KILOMETER, // (13 << 8) + 0, # 21 | |
461 | UAMEASUNIT_CONSUMPTION_MILE_PER_GALLON, // (13 << 8) + 1, # 22 | |
462 | UAMEASUNIT_CONSUMPTION_MILE_PER_GALLON_IMPERIAL, // (13 << 8) + 3, # 23 | |
463 | UAMEASUNIT_DIGITAL_BIT, // (14 << 8) + 0, # 24 | |
464 | UAMEASUNIT_DIGITAL_BYTE, // (14 << 8) + 1, # 25 | |
465 | UAMEASUNIT_DIGITAL_GIGABIT, // (14 << 8) + 2, # 26 | |
466 | UAMEASUNIT_DIGITAL_GIGABYTE, // (14 << 8) + 3, # 27 | |
467 | UAMEASUNIT_DIGITAL_KILOBIT, // (14 << 8) + 4, # 28 | |
468 | UAMEASUNIT_DIGITAL_KILOBYTE, // (14 << 8) + 5, # 29 | |
469 | UAMEASUNIT_DIGITAL_MEGABIT, // (14 << 8) + 6, # 30 | |
470 | UAMEASUNIT_DIGITAL_MEGABYTE, // (14 << 8) + 7, # 31 | |
471 | UAMEASUNIT_DIGITAL_TERABIT, // (14 << 8) + 8, # 32 | |
472 | UAMEASUNIT_DIGITAL_TERABYTE, // (14 << 8) + 9, # 33 | |
473 | UAMEASUNIT_DURATION_CENTURY, // (4 << 8) + 10, # 34 | |
474 | UAMEASUNIT_DURATION_DAY, // (4 << 8) + 3, # 35 | |
475 | UAMEASUNIT_DURATION_HOUR, // (4 << 8) + 4, # 36 | |
476 | UAMEASUNIT_DURATION_MICROSECOND, // (4 << 8) + 8, # 37 | |
477 | UAMEASUNIT_DURATION_MILLISECOND, // (4 << 8) + 7, # 38 | |
478 | UAMEASUNIT_DURATION_MINUTE, // (4 << 8) + 5, # 39 | |
479 | UAMEASUNIT_DURATION_MONTH, // (4 << 8) + 1, # 40 | |
480 | UAMEASUNIT_DURATION_NANOSECOND, // (4 << 8) + 9, # 41 | |
481 | UAMEASUNIT_DURATION_SECOND, // (4 << 8) + 6, # 42 | |
482 | UAMEASUNIT_DURATION_WEEK, // (4 << 8) + 2, # 43 | |
483 | UAMEASUNIT_DURATION_YEAR, // (4 << 8) + 0, # 44 | |
484 | UAMEASUNIT_ELECTRIC_AMPERE, // (15 << 8) + 0, # 45 | |
485 | UAMEASUNIT_ELECTRIC_MILLIAMPERE, // (15 << 8) + 1, # 46 | |
486 | UAMEASUNIT_ELECTRIC_OHM, // (15 << 8) + 2, # 47 | |
487 | UAMEASUNIT_ELECTRIC_VOLT, // (15 << 8) + 3, # 48 | |
488 | UAMEASUNIT_ENERGY_CALORIE, // (12 << 8) + 0, # 49 | |
489 | UAMEASUNIT_ENERGY_FOODCALORIE, // (12 << 8) + 1, # 50 | |
490 | UAMEASUNIT_ENERGY_JOULE, // (12 << 8) + 2, # 51 | |
491 | UAMEASUNIT_ENERGY_KILOCALORIE, // (12 << 8) + 3, # 52 | |
492 | UAMEASUNIT_ENERGY_KILOJOULE, // (12 << 8) + 4, # 53 | |
493 | UAMEASUNIT_ENERGY_KILOWATT_HOUR, // (12 << 8) + 5, # 54 | |
494 | UAMEASUNIT_FREQUENCY_GIGAHERTZ, // (16 << 8) + 3, # 55 | |
495 | UAMEASUNIT_FREQUENCY_HERTZ, // (16 << 8) + 0, # 56 | |
496 | UAMEASUNIT_FREQUENCY_KILOHERTZ, // (16 << 8) + 1, # 57 | |
497 | UAMEASUNIT_FREQUENCY_MEGAHERTZ, // (16 << 8) + 2, # 58 | |
498 | UAMEASUNIT_LENGTH_ASTRONOMICAL_UNIT, // (5 << 8) + 16, # 59 | |
499 | UAMEASUNIT_LENGTH_CENTIMETER, // (5 << 8) + 1, # 60 | |
500 | UAMEASUNIT_LENGTH_DECIMETER, // (5 << 8) + 10, # 61 | |
501 | UAMEASUNIT_LENGTH_FATHOM, // (5 << 8) + 14, # 62 | |
502 | UAMEASUNIT_LENGTH_FOOT, // (5 << 8) + 5, # 63 | |
503 | UAMEASUNIT_LENGTH_FURLONG, // (5 << 8) + 15, # 64 | |
504 | UAMEASUNIT_LENGTH_INCH, // (5 << 8) + 6, # 65 | |
505 | UAMEASUNIT_LENGTH_KILOMETER, // (5 << 8) + 2, # 66 | |
506 | UAMEASUNIT_LENGTH_LIGHT_YEAR, // (5 << 8) + 9, # 67 | |
507 | UAMEASUNIT_LENGTH_METER, // (5 << 8) + 0, # 68 | |
508 | UAMEASUNIT_LENGTH_MICROMETER, // (5 << 8) + 11, # 69 | |
509 | UAMEASUNIT_LENGTH_MILE, // (5 << 8) + 7, # 70 | |
510 | UAMEASUNIT_LENGTH_MILE_SCANDINAVIAN, // (5 << 8) + 18, # 71 | |
511 | UAMEASUNIT_LENGTH_MILLIMETER, // (5 << 8) + 3, # 72 | |
512 | UAMEASUNIT_LENGTH_NANOMETER, // (5 << 8) + 12, # 73 | |
513 | UAMEASUNIT_LENGTH_NAUTICAL_MILE, // (5 << 8) + 13, # 74 | |
514 | UAMEASUNIT_LENGTH_PARSEC, // (5 << 8) + 17, # 75 | |
515 | UAMEASUNIT_LENGTH_PICOMETER, // (5 << 8) + 4, # 76 | |
516 | UAMEASUNIT_LENGTH_YARD, // (5 << 8) + 8, # 77 | |
517 | UAMEASUNIT_LIGHT_LUX, // (17 << 8) + 0, # 78 | |
518 | UAMEASUNIT_MASS_CARAT, // (6 << 8) + 9, # 79 | |
519 | UAMEASUNIT_MASS_GRAM, // (6 << 8) + 0, # 80 | |
520 | UAMEASUNIT_MASS_KILOGRAM, // (6 << 8) + 1, # 81 | |
521 | UAMEASUNIT_MASS_METRIC_TON, // (6 << 8) + 7, # 82 | |
522 | UAMEASUNIT_MASS_MICROGRAM, // (6 << 8) + 5, # 83 | |
523 | UAMEASUNIT_MASS_MILLIGRAM, // (6 << 8) + 6, # 84 | |
524 | UAMEASUNIT_MASS_OUNCE, // (6 << 8) + 2, # 85 | |
525 | UAMEASUNIT_MASS_OUNCE_TROY, // (6 << 8) + 10, # 86 | |
526 | UAMEASUNIT_MASS_POUND, // (6 << 8) + 3, # 87 | |
527 | UAMEASUNIT_MASS_STONE, // (6 << 8) + 4, # 88 | |
528 | UAMEASUNIT_MASS_TON, // (6 << 8) + 8, # 89 | |
529 | UAMEASUNIT_POWER_GIGAWATT, // (7 << 8) + 5, # 90 | |
530 | UAMEASUNIT_POWER_HORSEPOWER, // (7 << 8) + 2, # 91 | |
531 | UAMEASUNIT_POWER_KILOWATT, // (7 << 8) + 1, # 92 | |
532 | UAMEASUNIT_POWER_MEGAWATT, // (7 << 8) + 4, # 93 | |
533 | UAMEASUNIT_POWER_MILLIWATT, // (7 << 8) + 3, # 94 | |
534 | UAMEASUNIT_POWER_WATT, // (7 << 8) + 0, # 95 | |
535 | UAMEASUNIT_PRESSURE_HECTOPASCAL, // (8 << 8) + 0, # 96 | |
536 | UAMEASUNIT_PRESSURE_INCH_HG, // (8 << 8) + 1, # 97 | |
537 | UAMEASUNIT_PRESSURE_MILLIBAR, // (8 << 8) + 2, # 98 | |
538 | UAMEASUNIT_PRESSURE_MILLIMETER_OF_MERCURY, // (8 << 8) + 3, # 99 | |
539 | UAMEASUNIT_PRESSURE_POUND_PER_SQUARE_INCH, // (8 << 8) + 4, # 100 | |
540 | UAMEASUNIT_SPEED_KILOMETER_PER_HOUR, // (9 << 8) + 1, # 101 | |
541 | UAMEASUNIT_SPEED_KNOT, // (9 << 8) + 3, # 102 | |
542 | UAMEASUNIT_SPEED_METER_PER_SECOND, // (9 << 8) + 0, # 103 | |
543 | UAMEASUNIT_SPEED_MILE_PER_HOUR, // (9 << 8) + 2, # 104 | |
544 | UAMEASUNIT_TEMPERATURE_CELSIUS, // (10 << 8) + 0, # 105 | |
545 | UAMEASUNIT_TEMPERATURE_FAHRENHEIT, // (10 << 8) + 1, # 106 | |
546 | UAMEASUNIT_TEMPERATURE_GENERIC, // (10 << 8) + 3, # 107 | |
547 | UAMEASUNIT_TEMPERATURE_KELVIN, // (10 << 8) + 2, # 108 | |
548 | UAMEASUNIT_VOLUME_ACRE_FOOT, // (11 << 8) + 13, # 109 | |
549 | UAMEASUNIT_VOLUME_BUSHEL, // (11 << 8) + 14, # 110 | |
550 | UAMEASUNIT_VOLUME_CENTILITER, // (11 << 8) + 4, # 111 | |
551 | UAMEASUNIT_VOLUME_CUBIC_CENTIMETER, // (11 << 8) + 8, # 112 | |
552 | UAMEASUNIT_VOLUME_CUBIC_FOOT, // (11 << 8) + 11, # 113 | |
553 | UAMEASUNIT_VOLUME_CUBIC_INCH, // (11 << 8) + 10, # 114 | |
554 | UAMEASUNIT_VOLUME_CUBIC_KILOMETER, // (11 << 8) + 1, # 115 | |
555 | UAMEASUNIT_VOLUME_CUBIC_METER, // (11 << 8) + 9, # 116 | |
556 | UAMEASUNIT_VOLUME_CUBIC_MILE, // (11 << 8) + 2, # 117 | |
557 | UAMEASUNIT_VOLUME_CUBIC_YARD, // (11 << 8) + 12, # 118 | |
558 | UAMEASUNIT_VOLUME_CUP, // (11 << 8) + 18, # 119 | |
559 | UAMEASUNIT_VOLUME_CUP_METRIC, // (11 << 8) + 22, # 120 | |
560 | UAMEASUNIT_VOLUME_DECILITER, // (11 << 8) + 5, # 121 | |
561 | UAMEASUNIT_VOLUME_FLUID_OUNCE, // (11 << 8) + 17, # 122 | |
562 | UAMEASUNIT_VOLUME_GALLON, // (11 << 8) + 21, # 123 | |
563 | UAMEASUNIT_VOLUME_GALLON_IMPERIAL, // (11 << 8) + 24, # 124 | |
564 | UAMEASUNIT_VOLUME_HECTOLITER, // (11 << 8) + 6, # 125 | |
565 | UAMEASUNIT_VOLUME_LITER, // (11 << 8) + 0, # 126 | |
566 | UAMEASUNIT_VOLUME_MEGALITER, // (11 << 8) + 7, # 127 | |
567 | UAMEASUNIT_VOLUME_MILLILITER, // (11 << 8) + 3, # 128 | |
568 | UAMEASUNIT_VOLUME_PINT, // (11 << 8) + 19, # 129 | |
569 | UAMEASUNIT_VOLUME_PINT_METRIC, // (11 << 8) + 23, # 130 | |
570 | UAMEASUNIT_VOLUME_QUART, // (11 << 8) + 20, # 131 | |
571 | UAMEASUNIT_VOLUME_TABLESPOON, // (11 << 8) + 16, # 132 | |
572 | UAMEASUNIT_VOLUME_TEASPOON, // (11 << 8) + 15, # 133 | |
573 | }; | |
574 | ||
575 | static UBool loadMeasureUnitData( | |
576 | const UResourceBundle *resource, | |
577 | MeasureFormatCacheData &cacheData, | |
578 | UErrorCode &status) { | |
579 | UnitDataSink sink(cacheData); | |
580 | ures_getAllTableItemsWithFallback(resource, "", sink, status); | |
581 | // Apple-specific, flesh out display names | |
582 | for (int32_t unitIndex = 0; unitIndex < MEAS_UNIT_COUNT; ++unitIndex) { | |
583 | if ( cacheData.getDisplayName(unitIndex, UMEASFMT_WIDTH_WIDE) == NULL ) { | |
584 | cacheData.setDisplayName(unitIndex, UMEASFMT_WIDTH_WIDE, cacheData.getDisplayName(unitIndex, UMEASFMT_WIDTH_SHORT)); | |
585 | } | |
586 | if ( cacheData.getDisplayName(unitIndex, UMEASFMT_WIDTH_SHORT) == NULL ) { | |
587 | cacheData.setDisplayName(unitIndex, UMEASFMT_WIDTH_SHORT, cacheData.getDisplayName(unitIndex, UMEASFMT_WIDTH_WIDE)); | |
588 | } | |
589 | if ( cacheData.getDisplayName(unitIndex, UMEASFMT_WIDTH_NARROW) == NULL ) { | |
590 | cacheData.setDisplayName(unitIndex, UMEASFMT_WIDTH_NARROW, cacheData.getDisplayName(unitIndex, UMEASFMT_WIDTH_SHORT)); | |
591 | } | |
57a6839d | 592 | } |
57a6839d A |
593 | return U_SUCCESS(status); |
594 | } | |
595 | ||
596 | static UnicodeString loadNumericDateFormatterPattern( | |
597 | const UResourceBundle *resource, | |
598 | const char *pattern, | |
599 | UErrorCode &status) { | |
600 | UnicodeString result; | |
601 | if (U_FAILURE(status)) { | |
602 | return result; | |
603 | } | |
604 | CharString chs; | |
605 | chs.append("durationUnits", status) | |
606 | .append("/", status).append(pattern, status); | |
607 | LocalUResourceBundlePointer patternBundle( | |
608 | ures_getByKeyWithFallback( | |
609 | resource, | |
610 | chs.data(), | |
611 | NULL, | |
612 | &status)); | |
613 | if (U_FAILURE(status)) { | |
614 | return result; | |
615 | } | |
616 | getString(patternBundle.getAlias(), result, status); | |
617 | // Replace 'h' with 'H' | |
618 | int32_t len = result.length(); | |
619 | UChar *buffer = result.getBuffer(len); | |
620 | for (int32_t i = 0; i < len; ++i) { | |
621 | if (buffer[i] == 0x68) { // 'h' | |
622 | buffer[i] = 0x48; // 'H' | |
623 | } | |
624 | } | |
625 | result.releaseBuffer(len); | |
626 | return result; | |
627 | } | |
374ca955 | 628 | |
57a6839d A |
629 | static NumericDateFormatters *loadNumericDateFormatters( |
630 | const UResourceBundle *resource, | |
631 | UErrorCode &status) { | |
632 | if (U_FAILURE(status)) { | |
633 | return NULL; | |
634 | } | |
635 | NumericDateFormatters *result = new NumericDateFormatters( | |
636 | loadNumericDateFormatterPattern(resource, "hm", status), | |
637 | loadNumericDateFormatterPattern(resource, "ms", status), | |
638 | loadNumericDateFormatterPattern(resource, "hms", status), | |
639 | status); | |
640 | if (U_FAILURE(status)) { | |
641 | delete result; | |
642 | return NULL; | |
643 | } | |
644 | return result; | |
645 | } | |
646 | ||
b331163b A |
647 | template<> U_I18N_API |
648 | const MeasureFormatCacheData *LocaleCacheKey<MeasureFormatCacheData>::createObject( | |
649 | const void * /*unused*/, UErrorCode &status) const { | |
650 | const char *localeId = fLoc.getName(); | |
651 | LocalUResourceBundlePointer unitsBundle(ures_open(U_ICUDATA_UNIT, localeId, &status)); | |
57a6839d A |
652 | static UNumberFormatStyle currencyStyles[] = { |
653 | UNUM_CURRENCY_PLURAL, UNUM_CURRENCY_ISO, UNUM_CURRENCY}; | |
b331163b | 654 | LocalPointer<MeasureFormatCacheData> result(new MeasureFormatCacheData(), status); |
57a6839d A |
655 | if (U_FAILURE(status)) { |
656 | return NULL; | |
657 | } | |
57a6839d | 658 | if (!loadMeasureUnitData( |
b331163b | 659 | unitsBundle.getAlias(), |
57a6839d A |
660 | *result, |
661 | status)) { | |
662 | return NULL; | |
663 | } | |
664 | result->adoptNumericDateFormatters(loadNumericDateFormatters( | |
b331163b | 665 | unitsBundle.getAlias(), status)); |
57a6839d A |
666 | if (U_FAILURE(status)) { |
667 | return NULL; | |
668 | } | |
669 | ||
670 | for (int32_t i = 0; i < WIDTH_INDEX_COUNT; ++i) { | |
671 | result->adoptCurrencyFormat(i, NumberFormat::createInstance( | |
672 | localeId, currencyStyles[i], status)); | |
673 | if (U_FAILURE(status)) { | |
674 | return NULL; | |
675 | } | |
676 | } | |
677 | NumberFormat *inf = NumberFormat::createInstance( | |
678 | localeId, UNUM_DECIMAL, status); | |
679 | if (U_FAILURE(status)) { | |
680 | return NULL; | |
681 | } | |
682 | inf->setMaximumFractionDigits(0); | |
683 | DecimalFormat *decfmt = dynamic_cast<DecimalFormat *>(inf); | |
684 | if (decfmt != NULL) { | |
685 | decfmt->setRoundingMode(DecimalFormat::kRoundDown); | |
686 | } | |
687 | result->adoptIntegerFormat(inf); | |
b331163b | 688 | result->addRef(); |
57a6839d A |
689 | return result.orphan(); |
690 | } | |
691 | ||
57a6839d A |
692 | static UBool isTimeUnit(const MeasureUnit &mu, const char *tu) { |
693 | return uprv_strcmp(mu.getType(), "duration") == 0 && | |
694 | uprv_strcmp(mu.getSubtype(), tu) == 0; | |
695 | } | |
696 | ||
697 | // Converts a composite measure into hours-minutes-seconds and stores at hms | |
698 | // array. [0] is hours; [1] is minutes; [2] is seconds. Returns a bit map of | |
699 | // units found: 1=hours, 2=minutes, 4=seconds. For example, if measures | |
700 | // contains hours-minutes, this function would return 3. | |
701 | // | |
702 | // If measures cannot be converted into hours, minutes, seconds or if amounts | |
703 | // are negative, or if hours, minutes, seconds are out of order, returns 0. | |
704 | static int32_t toHMS( | |
705 | const Measure *measures, | |
706 | int32_t measureCount, | |
707 | Formattable *hms, | |
708 | UErrorCode &status) { | |
709 | if (U_FAILURE(status)) { | |
710 | return 0; | |
711 | } | |
712 | int32_t result = 0; | |
713 | if (U_FAILURE(status)) { | |
714 | return 0; | |
715 | } | |
716 | // We use copy constructor to ensure that both sides of equality operator | |
717 | // are instances of MeasureUnit base class and not a subclass. Otherwise, | |
718 | // operator== will immediately return false. | |
719 | for (int32_t i = 0; i < measureCount; ++i) { | |
720 | if (isTimeUnit(measures[i].getUnit(), "hour")) { | |
721 | // hour must come first | |
722 | if (result >= 1) { | |
723 | return 0; | |
724 | } | |
725 | hms[0] = measures[i].getNumber(); | |
726 | if (hms[0].getDouble() < 0.0) { | |
727 | return 0; | |
728 | } | |
729 | result |= 1; | |
730 | } else if (isTimeUnit(measures[i].getUnit(), "minute")) { | |
731 | // minute must come after hour | |
732 | if (result >= 2) { | |
733 | return 0; | |
734 | } | |
735 | hms[1] = measures[i].getNumber(); | |
736 | if (hms[1].getDouble() < 0.0) { | |
737 | return 0; | |
738 | } | |
739 | result |= 2; | |
740 | } else if (isTimeUnit(measures[i].getUnit(), "second")) { | |
741 | // second must come after hour and minute | |
742 | if (result >= 4) { | |
743 | return 0; | |
744 | } | |
745 | hms[2] = measures[i].getNumber(); | |
746 | if (hms[2].getDouble() < 0.0) { | |
747 | return 0; | |
748 | } | |
749 | result |= 4; | |
750 | } else { | |
751 | return 0; | |
752 | } | |
753 | } | |
754 | return result; | |
755 | } | |
756 | ||
757 | ||
758 | MeasureFormat::MeasureFormat( | |
759 | const Locale &locale, UMeasureFormatWidth w, UErrorCode &status) | |
760 | : cache(NULL), | |
761 | numberFormat(NULL), | |
762 | pluralRules(NULL), | |
2ca993e8 A |
763 | width((w==UMEASFMT_WIDTH_SHORTER)? UMEASFMT_WIDTH_SHORT: w), |
764 | stripPatternSpaces(w==UMEASFMT_WIDTH_SHORTER), | |
765 | listFormatter(NULL), | |
766 | listFormatterStd(NULL) { | |
767 | initMeasureFormat(locale, (w==UMEASFMT_WIDTH_SHORTER)? UMEASFMT_WIDTH_SHORT: w, NULL, status); | |
57a6839d A |
768 | } |
769 | ||
770 | MeasureFormat::MeasureFormat( | |
771 | const Locale &locale, | |
772 | UMeasureFormatWidth w, | |
773 | NumberFormat *nfToAdopt, | |
2ca993e8 | 774 | UErrorCode &status) |
57a6839d A |
775 | : cache(NULL), |
776 | numberFormat(NULL), | |
777 | pluralRules(NULL), | |
2ca993e8 A |
778 | width((w==UMEASFMT_WIDTH_SHORTER)? UMEASFMT_WIDTH_SHORT: w), |
779 | stripPatternSpaces(w==UMEASFMT_WIDTH_SHORTER), | |
780 | listFormatter(NULL), | |
781 | listFormatterStd(NULL) { | |
782 | initMeasureFormat(locale, (w==UMEASFMT_WIDTH_SHORTER)? UMEASFMT_WIDTH_SHORT: w, nfToAdopt, status); | |
57a6839d A |
783 | } |
784 | ||
785 | MeasureFormat::MeasureFormat(const MeasureFormat &other) : | |
786 | Format(other), | |
787 | cache(other.cache), | |
788 | numberFormat(other.numberFormat), | |
789 | pluralRules(other.pluralRules), | |
790 | width(other.width), | |
2ca993e8 A |
791 | stripPatternSpaces(other.stripPatternSpaces), |
792 | listFormatter(NULL), | |
793 | listFormatterStd(NULL) { | |
57a6839d A |
794 | cache->addRef(); |
795 | numberFormat->addRef(); | |
796 | pluralRules->addRef(); | |
2ca993e8 A |
797 | if (other.listFormatter != NULL) { |
798 | listFormatter = new ListFormatter(*other.listFormatter); | |
799 | } | |
800 | if (other.listFormatterStd != NULL) { | |
801 | listFormatterStd = new ListFormatter(*other.listFormatterStd); | |
802 | } | |
57a6839d A |
803 | } |
804 | ||
805 | MeasureFormat &MeasureFormat::operator=(const MeasureFormat &other) { | |
806 | if (this == &other) { | |
807 | return *this; | |
808 | } | |
809 | Format::operator=(other); | |
810 | SharedObject::copyPtr(other.cache, cache); | |
811 | SharedObject::copyPtr(other.numberFormat, numberFormat); | |
812 | SharedObject::copyPtr(other.pluralRules, pluralRules); | |
813 | width = other.width; | |
2ca993e8 | 814 | stripPatternSpaces = other.stripPatternSpaces; |
57a6839d | 815 | delete listFormatter; |
2ca993e8 A |
816 | if (other.listFormatter != NULL) { |
817 | listFormatter = new ListFormatter(*other.listFormatter); | |
818 | } else { | |
819 | listFormatter = NULL; | |
820 | } | |
821 | delete listFormatterStd; | |
822 | if (other.listFormatterStd != NULL) { | |
823 | listFormatterStd = new ListFormatter(*other.listFormatterStd); | |
824 | } else { | |
825 | listFormatterStd = NULL; | |
826 | } | |
57a6839d A |
827 | return *this; |
828 | } | |
829 | ||
830 | MeasureFormat::MeasureFormat() : | |
831 | cache(NULL), | |
832 | numberFormat(NULL), | |
833 | pluralRules(NULL), | |
2ca993e8 A |
834 | width(UMEASFMT_WIDTH_SHORT), |
835 | stripPatternSpaces(FALSE), | |
836 | listFormatter(NULL), | |
837 | listFormatterStd(NULL) { | |
57a6839d A |
838 | } |
839 | ||
840 | MeasureFormat::~MeasureFormat() { | |
841 | if (cache != NULL) { | |
842 | cache->removeRef(); | |
843 | } | |
844 | if (numberFormat != NULL) { | |
845 | numberFormat->removeRef(); | |
846 | } | |
847 | if (pluralRules != NULL) { | |
848 | pluralRules->removeRef(); | |
849 | } | |
850 | delete listFormatter; | |
2ca993e8 | 851 | delete listFormatterStd; |
57a6839d A |
852 | } |
853 | ||
854 | UBool MeasureFormat::operator==(const Format &other) const { | |
855 | if (this == &other) { // Same object, equal | |
856 | return TRUE; | |
857 | } | |
858 | if (!Format::operator==(other)) { | |
859 | return FALSE; | |
860 | } | |
861 | const MeasureFormat &rhs = static_cast<const MeasureFormat &>(other); | |
862 | ||
863 | // Note: Since the ListFormatter depends only on Locale and width, we | |
864 | // don't have to check it here. | |
865 | ||
866 | // differing widths aren't equivalent | |
2ca993e8 | 867 | if (width != rhs.width || stripPatternSpaces != rhs.stripPatternSpaces) { |
57a6839d A |
868 | return FALSE; |
869 | } | |
870 | // Width the same check locales. | |
871 | // We don't need to check locales if both objects have same cache. | |
872 | if (cache != rhs.cache) { | |
873 | UErrorCode status = U_ZERO_ERROR; | |
874 | const char *localeId = getLocaleID(status); | |
875 | const char *rhsLocaleId = rhs.getLocaleID(status); | |
876 | if (U_FAILURE(status)) { | |
877 | // On failure, assume not equal | |
878 | return FALSE; | |
879 | } | |
880 | if (uprv_strcmp(localeId, rhsLocaleId) != 0) { | |
881 | return FALSE; | |
882 | } | |
883 | } | |
884 | // Locales same, check NumberFormat if shared data differs. | |
885 | return ( | |
886 | numberFormat == rhs.numberFormat || | |
887 | **numberFormat == **rhs.numberFormat); | |
888 | } | |
889 | ||
890 | Format *MeasureFormat::clone() const { | |
891 | return new MeasureFormat(*this); | |
892 | } | |
893 | ||
894 | UnicodeString &MeasureFormat::format( | |
895 | const Formattable &obj, | |
896 | UnicodeString &appendTo, | |
897 | FieldPosition &pos, | |
898 | UErrorCode &status) const { | |
899 | if (U_FAILURE(status)) return appendTo; | |
900 | if (obj.getType() == Formattable::kObject) { | |
901 | const UObject* formatObj = obj.getObject(); | |
902 | const Measure* amount = dynamic_cast<const Measure*>(formatObj); | |
903 | if (amount != NULL) { | |
904 | return formatMeasure( | |
905 | *amount, **numberFormat, appendTo, pos, status); | |
906 | } | |
907 | } | |
908 | status = U_ILLEGAL_ARGUMENT_ERROR; | |
909 | return appendTo; | |
910 | } | |
911 | ||
912 | void MeasureFormat::parseObject( | |
913 | const UnicodeString & /*source*/, | |
914 | Formattable & /*result*/, | |
915 | ParsePosition& /*pos*/) const { | |
916 | return; | |
917 | } | |
918 | ||
b331163b A |
919 | UnicodeString &MeasureFormat::formatMeasurePerUnit( |
920 | const Measure &measure, | |
921 | const MeasureUnit &perUnit, | |
922 | UnicodeString &appendTo, | |
923 | FieldPosition &pos, | |
924 | UErrorCode &status) const { | |
925 | if (U_FAILURE(status)) { | |
926 | return appendTo; | |
927 | } | |
928 | MeasureUnit *resolvedUnit = | |
929 | MeasureUnit::resolveUnitPerUnit(measure.getUnit(), perUnit); | |
930 | if (resolvedUnit != NULL) { | |
931 | Measure newMeasure(measure.getNumber(), resolvedUnit, status); | |
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 | |
57a6839d A |
1077 | void MeasureFormat::initMeasureFormat( |
1078 | const Locale &locale, | |
1079 | UMeasureFormatWidth w, | |
1080 | NumberFormat *nfToAdopt, | |
1081 | UErrorCode &status) { | |
1082 | static const char *listStyles[] = {"unit", "unit-short", "unit-narrow"}; | |
1083 | LocalPointer<NumberFormat> nf(nfToAdopt); | |
1084 | if (U_FAILURE(status)) { | |
1085 | return; | |
1086 | } | |
1087 | const char *name = locale.getName(); | |
1088 | setLocaleIDs(name, name); | |
1089 | ||
b331163b A |
1090 | UnifiedCache::getByLocale(locale, cache, status); |
1091 | if (U_FAILURE(status)) { | |
57a6839d A |
1092 | return; |
1093 | } | |
1094 | ||
b331163b A |
1095 | const SharedPluralRules *pr = PluralRules::createSharedInstance( |
1096 | locale, UPLURAL_TYPE_CARDINAL, status); | |
57a6839d A |
1097 | if (U_FAILURE(status)) { |
1098 | return; | |
1099 | } | |
b331163b A |
1100 | SharedObject::copyPtr(pr, pluralRules); |
1101 | pr->removeRef(); | |
57a6839d | 1102 | if (nf.isNull()) { |
b331163b A |
1103 | const SharedNumberFormat *shared = NumberFormat::createSharedInstance( |
1104 | locale, UNUM_DECIMAL, status); | |
57a6839d A |
1105 | if (U_FAILURE(status)) { |
1106 | return; | |
1107 | } | |
b331163b A |
1108 | SharedObject::copyPtr(shared, numberFormat); |
1109 | shared->removeRef(); | |
57a6839d A |
1110 | } else { |
1111 | adoptNumberFormat(nf.orphan(), status); | |
1112 | if (U_FAILURE(status)) { | |
1113 | return; | |
1114 | } | |
1115 | } | |
1116 | width = w; | |
2ca993e8 A |
1117 | if (stripPatternSpaces) { |
1118 | w = UMEASFMT_WIDTH_NARROW; | |
1119 | } | |
57a6839d A |
1120 | delete listFormatter; |
1121 | listFormatter = ListFormatter::createInstance( | |
1122 | locale, | |
2ca993e8 A |
1123 | listStyles[getRegularWidth(w)], |
1124 | status); | |
1125 | delete listFormatterStd; | |
1126 | listFormatterStd = ListFormatter::createInstance( | |
1127 | locale, | |
1128 | "standard", | |
57a6839d A |
1129 | status); |
1130 | } | |
1131 | ||
1132 | void MeasureFormat::adoptNumberFormat( | |
1133 | NumberFormat *nfToAdopt, UErrorCode &status) { | |
1134 | LocalPointer<NumberFormat> nf(nfToAdopt); | |
1135 | if (U_FAILURE(status)) { | |
1136 | return; | |
1137 | } | |
1138 | SharedNumberFormat *shared = new SharedNumberFormat(nf.getAlias()); | |
1139 | if (shared == NULL) { | |
1140 | status = U_MEMORY_ALLOCATION_ERROR; | |
1141 | return; | |
1142 | } | |
1143 | nf.orphan(); | |
1144 | SharedObject::copyPtr(shared, numberFormat); | |
1145 | } | |
1146 | ||
1147 | UBool MeasureFormat::setMeasureFormatLocale(const Locale &locale, UErrorCode &status) { | |
1148 | if (U_FAILURE(status) || locale == getLocale(status)) { | |
1149 | return FALSE; | |
1150 | } | |
1151 | initMeasureFormat(locale, width, NULL, status); | |
1152 | return U_SUCCESS(status); | |
2ca993e8 | 1153 | } |
57a6839d | 1154 | |
b331163b A |
1155 | // Apple-specific for now |
1156 | UMeasureFormatWidth MeasureFormat::getWidth() const { | |
1157 | return width; | |
1158 | } | |
1159 | ||
57a6839d A |
1160 | const NumberFormat &MeasureFormat::getNumberFormat() const { |
1161 | return **numberFormat; | |
1162 | } | |
1163 | ||
1164 | const PluralRules &MeasureFormat::getPluralRules() const { | |
1165 | return **pluralRules; | |
1166 | } | |
1167 | ||
1168 | Locale MeasureFormat::getLocale(UErrorCode &status) const { | |
1169 | return Format::getLocale(ULOC_VALID_LOCALE, status); | |
1170 | } | |
1171 | ||
1172 | const char *MeasureFormat::getLocaleID(UErrorCode &status) const { | |
1173 | return Format::getLocaleID(ULOC_VALID_LOCALE, status); | |
1174 | } | |
1175 | ||
2ca993e8 A |
1176 | // Apple=specific |
1177 | UnicodeString &MeasureFormat::getUnitName( | |
1178 | const MeasureUnit* unit, | |
1179 | UnicodeString &result ) const { | |
1180 | const UChar * unitName = cache->getDisplayName( unit->getIndex(), getRegularWidth(width) ); | |
1181 | if (unitName != NULL) { | |
1182 | int32_t unitNameLen = u_strlen(unitName); | |
1183 | if (unitNameLen <= 64) { | |
1184 | result.setTo(unitName, unitNameLen); | |
1185 | return result; | |
1186 | } | |
1187 | } | |
1188 | result.setToBogus(); | |
1189 | return result; | |
1190 | } | |
1191 | ||
1192 | // Apple=specific | |
1193 | UnicodeString &MeasureFormat::getMultipleUnitNames( | |
1194 | const MeasureUnit** units, | |
1195 | int32_t unitCount, | |
1196 | UAMeasureNameListStyle listStyle, | |
1197 | UnicodeString &result ) const { | |
1198 | if (unitCount == 0) { | |
1199 | return result.remove(); | |
1200 | } | |
1201 | if (unitCount == 1) { | |
1202 | return getUnitName(units[0], result); | |
1203 | } | |
1204 | UnicodeString *results = new UnicodeString[unitCount]; | |
1205 | if (results != NULL) { | |
1206 | for (int32_t i = 0; i < unitCount; ++i) { | |
1207 | getUnitName(units[i], results[i]); | |
1208 | } | |
1209 | UErrorCode status = U_ZERO_ERROR; | |
1210 | if (listStyle == UAMEASNAME_LIST_STANDARD) { | |
1211 | listFormatterStd->format(results, unitCount, result, status); | |
1212 | } else { | |
1213 | listFormatter->format(results, unitCount, result, status); | |
1214 | } | |
1215 | delete [] results; | |
1216 | if (U_SUCCESS(status)) { | |
1217 | return result; | |
1218 | } | |
1219 | } | |
1220 | result.setToBogus(); | |
1221 | return result; | |
1222 | } | |
1223 | ||
57a6839d A |
1224 | UnicodeString &MeasureFormat::formatMeasure( |
1225 | const Measure &measure, | |
1226 | const NumberFormat &nf, | |
1227 | UnicodeString &appendTo, | |
1228 | FieldPosition &pos, | |
1229 | UErrorCode &status) const { | |
1230 | if (U_FAILURE(status)) { | |
1231 | return appendTo; | |
1232 | } | |
1233 | const Formattable& amtNumber = measure.getNumber(); | |
1234 | const MeasureUnit& amtUnit = measure.getUnit(); | |
1235 | if (isCurrency(amtUnit)) { | |
1236 | UChar isoCode[4]; | |
1237 | u_charsToUChars(amtUnit.getSubtype(), isoCode, 4); | |
2ca993e8 | 1238 | return cache->getCurrencyFormat(width)->format( |
57a6839d A |
1239 | new CurrencyAmount(amtNumber, isoCode, status), |
1240 | appendTo, | |
1241 | pos, | |
1242 | status); | |
1243 | } | |
2ca993e8 A |
1244 | UnicodeString formattedNumber; |
1245 | UBool posForFullNumericPart = (pos.getField() == UAMEASFMT_NUMERIC_FIELD_FLAG); | |
1246 | if (posForFullNumericPart) { | |
1247 | pos.setField(FieldPosition::DONT_CARE); | |
1248 | } | |
1249 | StandardPlural::Form pluralForm = QuantityFormatter::selectPlural( | |
1250 | amtNumber, nf, **pluralRules, formattedNumber, pos, status); | |
1251 | if (posForFullNumericPart) { | |
1252 | pos.setField(UAMEASFMT_NUMERIC_FIELD_FLAG); | |
1253 | pos.setBeginIndex(0); | |
1254 | pos.setEndIndex(formattedNumber.length()); | |
1255 | } | |
1256 | const SimpleFormatter *formatter = getPluralFormatter(amtUnit, width, pluralForm, status); | |
1257 | int32_t cur = appendTo.length(); | |
1258 | QuantityFormatter::format(*formatter, formattedNumber, appendTo, pos, status); | |
1259 | if (stripPatternSpaces) { | |
1260 | const SimpleFormatter *narrowFormatter = getPluralFormatter(amtUnit, UMEASFMT_WIDTH_NARROW, pluralForm, status); | |
1261 | if (U_SUCCESS(status)) { | |
1262 | // Get the narrow pattern with all {n} set to empty string. | |
1263 | // If there are spaces in that, then do not continue to strip spaces | |
1264 | // (i.e. even in the narrowest form this locale keeps spaces). | |
1265 | UnicodeString narrowPatternNoArgs = narrowFormatter->getTextWithNoArguments(); | |
1266 | if (narrowPatternNoArgs.indexOf((UChar)0x0020) == -1 && narrowPatternNoArgs.indexOf((UChar)0x00A0) == -1) { | |
1267 | int32_t end = appendTo.length(); | |
1268 | for (; cur < end; cur++) { | |
1269 | if (appendTo.charAt(cur) == 0x0020) { | |
1270 | appendTo.remove(cur, 1); | |
1271 | if (pos.getBeginIndex() > cur) { | |
1272 | pos.setBeginIndex(pos.getBeginIndex() - 1); | |
1273 | pos.setEndIndex(pos.getEndIndex() - 1); | |
1274 | } | |
1275 | } | |
1276 | } | |
1277 | } | |
1278 | } | |
57a6839d | 1279 | } |
2ca993e8 | 1280 | return appendTo; |
57a6839d A |
1281 | } |
1282 | ||
1283 | // Formats hours-minutes-seconds as 5:37:23 or similar. | |
1284 | UnicodeString &MeasureFormat::formatNumeric( | |
1285 | const Formattable *hms, // always length 3 | |
1286 | int32_t bitMap, // 1=hourset, 2=minuteset, 4=secondset | |
1287 | UnicodeString &appendTo, | |
2ca993e8 | 1288 | FieldPositionHandler& handler, |
57a6839d A |
1289 | UErrorCode &status) const { |
1290 | if (U_FAILURE(status)) { | |
1291 | return appendTo; | |
1292 | } | |
2ca993e8 | 1293 | UDate millis = |
57a6839d A |
1294 | (UDate) (((uprv_trunc(hms[0].getDouble(status)) * 60.0 |
1295 | + uprv_trunc(hms[1].getDouble(status))) * 60.0 | |
1296 | + uprv_trunc(hms[2].getDouble(status))) * 1000.0); | |
1297 | switch (bitMap) { | |
1298 | case 5: // hs | |
1299 | case 7: // hms | |
1300 | return formatNumeric( | |
1301 | millis, | |
1302 | cache->getNumericDateFormatters()->hourMinuteSecond, | |
1303 | UDAT_SECOND_FIELD, | |
1304 | hms[2], | |
1305 | appendTo, | |
2ca993e8 | 1306 | handler, |
57a6839d A |
1307 | status); |
1308 | break; | |
1309 | case 6: // ms | |
1310 | return formatNumeric( | |
1311 | millis, | |
1312 | cache->getNumericDateFormatters()->minuteSecond, | |
1313 | UDAT_SECOND_FIELD, | |
1314 | hms[2], | |
1315 | appendTo, | |
2ca993e8 | 1316 | handler, |
57a6839d A |
1317 | status); |
1318 | break; | |
1319 | case 3: // hm | |
1320 | return formatNumeric( | |
1321 | millis, | |
1322 | cache->getNumericDateFormatters()->hourMinute, | |
1323 | UDAT_MINUTE_FIELD, | |
1324 | hms[1], | |
1325 | appendTo, | |
2ca993e8 | 1326 | handler, |
57a6839d A |
1327 | status); |
1328 | break; | |
1329 | default: | |
1330 | status = U_INTERNAL_PROGRAM_ERROR; | |
1331 | return appendTo; | |
1332 | break; | |
1333 | } | |
1334 | return appendTo; | |
1335 | } | |
1336 | ||
1337 | static void appendRange( | |
1338 | const UnicodeString &src, | |
1339 | int32_t start, | |
1340 | int32_t end, | |
1341 | UnicodeString &dest) { | |
1342 | dest.append(src, start, end - start); | |
1343 | } | |
1344 | ||
1345 | static void appendRange( | |
1346 | const UnicodeString &src, | |
1347 | int32_t end, | |
1348 | UnicodeString &dest) { | |
1349 | dest.append(src, end, src.length() - end); | |
1350 | } | |
1351 | ||
1352 | // Formats time like 5:37:23 | |
1353 | UnicodeString &MeasureFormat::formatNumeric( | |
1354 | UDate date, // Time since epoch 1:30:00 would be 5400000 | |
1355 | const DateFormat &dateFmt, // h:mm, m:ss, or h:mm:ss | |
1356 | UDateFormatField smallestField, // seconds in 5:37:23.5 | |
1357 | const Formattable &smallestAmount, // 23.5 for 5:37:23.5 | |
1358 | UnicodeString &appendTo, | |
2ca993e8 | 1359 | FieldPositionHandler& handler, |
57a6839d A |
1360 | UErrorCode &status) const { |
1361 | if (U_FAILURE(status)) { | |
1362 | return appendTo; | |
1363 | } | |
1364 | // Format the smallest amount with this object's NumberFormat | |
1365 | UnicodeString smallestAmountFormatted; | |
1366 | ||
1367 | // We keep track of the integer part of smallest amount so that | |
1368 | // we can replace it later so that we get '0:00:09.3' instead of | |
1369 | // '0:00:9.3' | |
1370 | FieldPosition intFieldPosition(UNUM_INTEGER_FIELD); | |
1371 | (*numberFormat)->format( | |
1372 | smallestAmount, smallestAmountFormatted, intFieldPosition, status); | |
1373 | if ( | |
1374 | intFieldPosition.getBeginIndex() == 0 && | |
1375 | intFieldPosition.getEndIndex() == 0) { | |
1376 | status = U_INTERNAL_PROGRAM_ERROR; | |
1377 | return appendTo; | |
1378 | } | |
1379 | ||
1380 | // Format time. draft becomes something like '5:30:45' | |
2ca993e8 | 1381 | FieldPositionIterator posIter; |
57a6839d | 1382 | UnicodeString draft; |
2ca993e8 A |
1383 | dateFmt.format(date, draft, &posIter, status); |
1384 | ||
1385 | int32_t start = appendTo.length(); | |
1386 | FieldPosition smallestFieldPosition(smallestField); | |
1387 | FieldPosition fp; | |
1388 | int32_t measField = -1; | |
1389 | while (posIter.next(fp)) { | |
1390 | int32_t dateField = fp.getField(); | |
1391 | switch (dateField) { | |
1392 | case UDAT_HOUR_OF_DAY1_FIELD: | |
1393 | case UDAT_HOUR_OF_DAY0_FIELD: | |
1394 | case UDAT_HOUR1_FIELD: | |
1395 | case UDAT_HOUR0_FIELD: | |
1396 | measField = UAMEASUNIT_DURATION_HOUR; break; | |
1397 | case UDAT_MINUTE_FIELD: | |
1398 | measField = UAMEASUNIT_DURATION_MINUTE; break; | |
1399 | case UDAT_SECOND_FIELD: | |
1400 | measField = UAMEASUNIT_DURATION_SECOND; break; | |
1401 | default: | |
1402 | measField = -1; break; | |
1403 | } | |
1404 | if (dateField != smallestField) { | |
1405 | if (measField >= 0) { | |
1406 | handler.addAttribute(measField, start + fp.getBeginIndex(), start + fp.getEndIndex()); | |
1407 | handler.addAttribute(measField | UAMEASFMT_NUMERIC_FIELD_FLAG, start + fp.getBeginIndex(), start + fp.getEndIndex()); | |
1408 | } | |
1409 | } else { | |
1410 | smallestFieldPosition.setBeginIndex(fp.getBeginIndex()); | |
1411 | smallestFieldPosition.setEndIndex(fp.getEndIndex()); | |
1412 | break; | |
1413 | } | |
1414 | } | |
57a6839d A |
1415 | |
1416 | // If we find field for smallest amount replace it with the formatted | |
1417 | // smallest amount from above taking care to replace the integer part | |
1418 | // with what is in original time. For example, If smallest amount | |
1419 | // is 9.35s and the formatted time is 0:00:09 then 9.35 becomes 09.35 | |
1420 | // and replacing yields 0:00:09.35 | |
1421 | if (smallestFieldPosition.getBeginIndex() != 0 || | |
1422 | smallestFieldPosition.getEndIndex() != 0) { | |
1423 | appendRange(draft, 0, smallestFieldPosition.getBeginIndex(), appendTo); | |
1424 | appendRange( | |
1425 | smallestAmountFormatted, | |
1426 | 0, | |
1427 | intFieldPosition.getBeginIndex(), | |
1428 | appendTo); | |
1429 | appendRange( | |
1430 | draft, | |
1431 | smallestFieldPosition.getBeginIndex(), | |
1432 | smallestFieldPosition.getEndIndex(), | |
1433 | appendTo); | |
1434 | appendRange( | |
1435 | smallestAmountFormatted, | |
1436 | intFieldPosition.getEndIndex(), | |
1437 | appendTo); | |
1438 | appendRange( | |
1439 | draft, | |
1440 | smallestFieldPosition.getEndIndex(), | |
1441 | appendTo); | |
2ca993e8 A |
1442 | handler.addAttribute(measField, start + smallestFieldPosition.getBeginIndex(), appendTo.length()); |
1443 | handler.addAttribute(measField | UAMEASFMT_NUMERIC_FIELD_FLAG, start + smallestFieldPosition.getBeginIndex(), appendTo.length()); | |
57a6839d A |
1444 | } else { |
1445 | appendTo.append(draft); | |
1446 | } | |
1447 | return appendTo; | |
1448 | } | |
1449 | ||
2ca993e8 A |
1450 | const SimpleFormatter *MeasureFormat::getFormatterOrNull( |
1451 | const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index) const { | |
1452 | width = getRegularWidth(width); | |
1453 | SimpleFormatter *const (*unitPatterns)[MeasureFormatCacheData::PATTERN_COUNT] = | |
1454 | &cache->patterns[unit.getIndex()][0]; | |
1455 | if (unitPatterns[width][index] != NULL) { | |
1456 | return unitPatterns[width][index]; | |
57a6839d | 1457 | } |
2ca993e8 A |
1458 | int32_t fallbackWidth = cache->widthFallback[width]; |
1459 | if (fallbackWidth != UMEASFMT_WIDTH_COUNT && unitPatterns[fallbackWidth][index] != NULL) { | |
1460 | return unitPatterns[fallbackWidth][index]; | |
57a6839d | 1461 | } |
57a6839d A |
1462 | return NULL; |
1463 | } | |
1464 | ||
2ca993e8 A |
1465 | const SimpleFormatter *MeasureFormat::getFormatter( |
1466 | const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index, | |
1467 | UErrorCode &errorCode) const { | |
1468 | if (U_FAILURE(errorCode)) { | |
1469 | return NULL; | |
b331163b | 1470 | } |
2ca993e8 A |
1471 | const SimpleFormatter *pattern = getFormatterOrNull(unit, width, index); |
1472 | if (pattern == NULL) { | |
1473 | errorCode = U_MISSING_RESOURCE_ERROR; | |
b331163b | 1474 | } |
2ca993e8 A |
1475 | return pattern; |
1476 | } | |
1477 | ||
1478 | const SimpleFormatter *MeasureFormat::getPluralFormatter( | |
1479 | const MeasureUnit &unit, UMeasureFormatWidth width, int32_t index, | |
1480 | UErrorCode &errorCode) const { | |
1481 | if (U_FAILURE(errorCode)) { | |
1482 | return NULL; | |
b331163b | 1483 | } |
2ca993e8 A |
1484 | if (index != StandardPlural::OTHER) { |
1485 | const SimpleFormatter *pattern = getFormatterOrNull(unit, width, index); | |
1486 | if (pattern != NULL) { | |
1487 | return pattern; | |
1488 | } | |
1489 | } | |
1490 | return getFormatter(unit, width, StandardPlural::OTHER, errorCode); | |
b331163b A |
1491 | } |
1492 | ||
2ca993e8 A |
1493 | const SimpleFormatter *MeasureFormat::getPerFormatter( |
1494 | UMeasureFormatWidth width, | |
b331163b A |
1495 | UErrorCode &status) const { |
1496 | if (U_FAILURE(status)) { | |
1497 | return NULL; | |
1498 | } | |
2ca993e8 A |
1499 | width = getRegularWidth(width); |
1500 | const SimpleFormatter * perFormatters = cache->perFormatters; | |
1501 | if (perFormatters[width].getArgumentLimit() == 2) { | |
1502 | return &perFormatters[width]; | |
b331163b | 1503 | } |
2ca993e8 A |
1504 | int32_t fallbackWidth = cache->widthFallback[width]; |
1505 | if (fallbackWidth != UMEASFMT_WIDTH_COUNT && | |
1506 | perFormatters[fallbackWidth].getArgumentLimit() == 2) { | |
1507 | return &perFormatters[fallbackWidth]; | |
b331163b A |
1508 | } |
1509 | status = U_MISSING_RESOURCE_ERROR; | |
1510 | return NULL; | |
1511 | } | |
1512 | ||
b331163b A |
1513 | int32_t MeasureFormat::withPerUnitAndAppend( |
1514 | const UnicodeString &formatted, | |
1515 | const MeasureUnit &perUnit, | |
1516 | UnicodeString &appendTo, | |
1517 | UErrorCode &status) const { | |
1518 | int32_t offset = -1; | |
1519 | if (U_FAILURE(status)) { | |
1520 | return offset; | |
1521 | } | |
2ca993e8 A |
1522 | const SimpleFormatter *perUnitFormatter = |
1523 | getFormatterOrNull(perUnit, width, MeasureFormatCacheData::PER_UNIT_INDEX); | |
b331163b A |
1524 | if (perUnitFormatter != NULL) { |
1525 | const UnicodeString *params[] = {&formatted}; | |
1526 | perUnitFormatter->formatAndAppend( | |
1527 | params, | |
1528 | UPRV_LENGTHOF(params), | |
1529 | appendTo, | |
1530 | &offset, | |
1531 | 1, | |
1532 | status); | |
1533 | return offset; | |
1534 | } | |
2ca993e8 A |
1535 | const SimpleFormatter *perFormatter = getPerFormatter(width, status); |
1536 | const SimpleFormatter *pattern = | |
1537 | getPluralFormatter(perUnit, width, StandardPlural::ONE, status); | |
b331163b A |
1538 | if (U_FAILURE(status)) { |
1539 | return offset; | |
1540 | } | |
2ca993e8 A |
1541 | UnicodeString perUnitString = pattern->getTextWithNoArguments(); |
1542 | perUnitString.trim(); | |
b331163b A |
1543 | const UnicodeString *params[] = {&formatted, &perUnitString}; |
1544 | perFormatter->formatAndAppend( | |
1545 | params, | |
1546 | UPRV_LENGTHOF(params), | |
1547 | appendTo, | |
1548 | &offset, | |
1549 | 1, | |
1550 | status); | |
1551 | return offset; | |
1552 | } | |
1553 | ||
57a6839d A |
1554 | UnicodeString &MeasureFormat::formatMeasuresSlowTrack( |
1555 | const Measure *measures, | |
1556 | int32_t measureCount, | |
1557 | UnicodeString& appendTo, | |
1558 | FieldPosition& pos, | |
1559 | UErrorCode& status) const { | |
1560 | if (U_FAILURE(status)) { | |
1561 | return appendTo; | |
1562 | } | |
1563 | FieldPosition dontCare(FieldPosition::DONT_CARE); | |
1564 | FieldPosition fpos(pos.getField()); | |
1565 | UnicodeString *results = new UnicodeString[measureCount]; | |
1566 | int32_t fieldPositionFoundIndex = -1; | |
1567 | for (int32_t i = 0; i < measureCount; ++i) { | |
1568 | const NumberFormat *nf = cache->getIntegerFormat(); | |
1569 | if (i == measureCount - 1) { | |
1570 | nf = numberFormat->get(); | |
1571 | } | |
1572 | if (fieldPositionFoundIndex == -1) { | |
1573 | formatMeasure(measures[i], *nf, results[i], fpos, status); | |
1574 | if (U_FAILURE(status)) { | |
1575 | delete [] results; | |
1576 | return appendTo; | |
1577 | } | |
1578 | if (fpos.getBeginIndex() != 0 || fpos.getEndIndex() != 0) { | |
1579 | fieldPositionFoundIndex = i; | |
1580 | } | |
1581 | } else { | |
1582 | formatMeasure(measures[i], *nf, results[i], dontCare, status); | |
1583 | } | |
1584 | } | |
1585 | int32_t offset; | |
1586 | listFormatter->format( | |
1587 | results, | |
1588 | measureCount, | |
1589 | appendTo, | |
1590 | fieldPositionFoundIndex, | |
1591 | offset, | |
1592 | status); | |
1593 | if (U_FAILURE(status)) { | |
1594 | delete [] results; | |
1595 | return appendTo; | |
1596 | } | |
1597 | if (offset != -1) { | |
1598 | pos.setBeginIndex(fpos.getBeginIndex() + offset); | |
1599 | pos.setEndIndex(fpos.getEndIndex() + offset); | |
1600 | } | |
1601 | delete [] results; | |
1602 | return appendTo; | |
1603 | } | |
4388f060 | 1604 | |
374ca955 A |
1605 | MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(const Locale& locale, |
1606 | UErrorCode& ec) { | |
1607 | CurrencyFormat* fmt = NULL; | |
1608 | if (U_SUCCESS(ec)) { | |
1609 | fmt = new CurrencyFormat(locale, ec); | |
1610 | if (U_FAILURE(ec)) { | |
1611 | delete fmt; | |
1612 | fmt = NULL; | |
1613 | } | |
1614 | } | |
1615 | return fmt; | |
1616 | } | |
1617 | ||
1618 | MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(UErrorCode& ec) { | |
1619 | if (U_FAILURE(ec)) { | |
1620 | return NULL; | |
1621 | } | |
1622 | return MeasureFormat::createCurrencyFormat(Locale::getDefault(), ec); | |
1623 | } | |
1624 | ||
1625 | U_NAMESPACE_END | |
1626 | ||
1627 | #endif /* #if !UCONFIG_NO_FORMATTING */ |