]> git.saurik.com Git - apple/icu.git/blame - icuSources/i18n/measfmt.cpp
ICU-62123.0.1.tar.gz
[apple/icu.git] / icuSources / i18n / measfmt.cpp
CommitLineData
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
48U_NAMESPACE_BEGIN
49
0f5d89e8
A
50static constexpr int32_t PER_UNIT_INDEX = StandardPlural::COUNT;
51static constexpr int32_t PATTERN_COUNT = PER_UNIT_INDEX + 1;
52static constexpr int32_t MEAS_UNIT_COUNT = 138; // see assertion in MeasureFormatCacheData constructor
53static constexpr int32_t WIDTH_INDEX_COUNT = UMEASFMT_WIDTH_NARROW + 1;
54
57a6839d
A
55UOBJECT_DEFINE_RTTI_IMPLEMENTATION(MeasureFormat)
56
57// Used to format durations like 5:47 or 21:35:42.
58class NumericDateFormatters : public UMemory {
59public:
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 }
84private:
85 NumericDateFormatters(const NumericDateFormatters &other);
86 NumericDateFormatters &operator=(const NumericDateFormatters &other);
87};
88
2ca993e8
A
89static 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
106class MeasureFormatCacheData : public SharedObject {
107public:
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 151private:
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
160MeasureFormatCacheData::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
173MeasureFormatCacheData::~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
189static UBool isCurrency(const MeasureUnit &unit) {
190 return (uprv_strcmp(unit.getType(), "currency") == 0);
191}
192
193static 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 206namespace {
57a6839d 207
2ca993e8
A
208static const UChar g_LOCALE_units[] = {
209 0x2F, 0x4C, 0x4F, 0x43, 0x41, 0x4C, 0x45, 0x2F,
210 0x75, 0x6E, 0x69, 0x74, 0x73
211};
212static const UChar gShort[] = { 0x53, 0x68, 0x6F, 0x72, 0x74 };
213static 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
226struct 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
437UnitDataSink::~UnitDataSink() {}
438
439} // namespace
440
441static 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
580static 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
589static 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
622static 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
640template<> U_I18N_API
641const 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
691static 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.
703static 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
757MeasureFormat::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
769MeasureFormat::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
784MeasureFormat::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
804MeasureFormat &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
829MeasureFormat::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
839MeasureFormat::~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
853UBool 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
889Format *MeasureFormat::clone() const {
890 return new MeasureFormat(*this);
891}
892
893UnicodeString &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
911void MeasureFormat::parseObject(
912 const UnicodeString & /*source*/,
913 Formattable & /*result*/,
914 ParsePosition& /*pos*/) const {
915 return;
916}
917
b331163b
A
918UnicodeString &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
953UnicodeString &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&
1004UnicodeString &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
1077UnicodeString 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
1095void 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
1150void 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
1165UBool 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
1174UMeasureFormatWidth MeasureFormat::getWidth() const {
1175 return width;
1176}
1177
57a6839d
A
1178const NumberFormat &MeasureFormat::getNumberFormat() const {
1179 return **numberFormat;
1180}
1181
1182const PluralRules &MeasureFormat::getPluralRules() const {
1183 return **pluralRules;
1184}
1185
1186Locale MeasureFormat::getLocale(UErrorCode &status) const {
1187 return Format::getLocale(ULOC_VALID_LOCALE, status);
1188}
1189
1190const 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
1197UnicodeString &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
1206UnicodeString &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
1237UnicodeString &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.
1297UnicodeString &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
1350static 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
1358static 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
1366UnicodeString &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
1467const 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
1481const 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
1494const 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
1509const 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
1529int32_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
1569UnicodeString &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
1620MeasureFormat* 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
1633MeasureFormat* U_EXPORT2 MeasureFormat::createCurrencyFormat(UErrorCode& ec) {
1634 if (U_FAILURE(ec)) {
1635 return NULL;
1636 }
1637 return MeasureFormat::createCurrencyFormat(Locale::getDefault(), ec);
1638}
1639
1640U_NAMESPACE_END
1641
1642#endif /* #if !UCONFIG_NO_FORMATTING */