blob: 0cf50b4a065f5c4c5496f67f0905f4d576fb6f28 [file] [log] [blame]
Frank Tang3e05d9d2021-11-08 14:04:04 -08001// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html
3/*
4*******************************************************************************
5* Copyright (C) 2015, International Business Machines Corporation and *
6* others. All Rights Reserved. *
7*******************************************************************************
8*/
9
10#include "numberformattesttuple.h"
11
12#if !UCONFIG_NO_FORMATTING
13
14#include "unicode/testlog.h"
15#include "ustrfmt.h"
16#include "charstr.h"
17#include "cstring.h"
18#include "cmemory.h"
19
20static NumberFormatTestTuple emptyObject;
21
22static NumberFormatTestTuple *gNullPtr = &emptyObject;
23
24#define FIELD_OFFSET(fieldName) ((int32_t) (((char *) &gNullPtr->fieldName) - ((char *) gNullPtr)))
25#define FIELD_FLAG_OFFSET(fieldName) ((int32_t) (((char *) &gNullPtr->fieldName##Flag) - ((char *) gNullPtr)))
26
27#define FIELD_INIT(fieldName, fieldType) {#fieldName, FIELD_OFFSET(fieldName), FIELD_FLAG_OFFSET(fieldName), fieldType}
28
29struct Numberformattesttuple_EnumConversion {
30 const char *str;
31 int32_t value;
32};
33
34static Numberformattesttuple_EnumConversion gRoundingEnum[] = {
35 {"ceiling", DecimalFormat::kRoundCeiling},
36 {"floor", DecimalFormat::kRoundFloor},
37 {"down", DecimalFormat::kRoundDown},
38 {"up", DecimalFormat::kRoundUp},
39 {"halfEven", DecimalFormat::kRoundHalfEven},
40 {"halfDown", DecimalFormat::kRoundHalfDown},
41 {"halfUp", DecimalFormat::kRoundHalfUp},
42 {"unnecessary", DecimalFormat::kRoundUnnecessary}};
43
44static Numberformattesttuple_EnumConversion gCurrencyUsageEnum[] = {
45 {"standard", UCURR_USAGE_STANDARD},
46 {"cash", UCURR_USAGE_CASH}};
47
48static Numberformattesttuple_EnumConversion gPadPositionEnum[] = {
49 {"beforePrefix", DecimalFormat::kPadBeforePrefix},
50 {"afterPrefix", DecimalFormat::kPadAfterPrefix},
51 {"beforeSuffix", DecimalFormat::kPadBeforeSuffix},
52 {"afterSuffix", DecimalFormat::kPadAfterSuffix}};
53
54static Numberformattesttuple_EnumConversion gFormatStyleEnum[] = {
55 {"patternDecimal", UNUM_PATTERN_DECIMAL},
56 {"decimal", UNUM_DECIMAL},
57 {"currency", UNUM_CURRENCY},
58 {"percent", UNUM_PERCENT},
59 {"scientific", UNUM_SCIENTIFIC},
60 {"spellout", UNUM_SPELLOUT},
61 {"ordinal", UNUM_ORDINAL},
62 {"duration", UNUM_DURATION},
63 {"numberingSystem", UNUM_NUMBERING_SYSTEM},
64 {"patternRuleBased", UNUM_PATTERN_RULEBASED},
65 {"currencyIso", UNUM_CURRENCY_ISO},
66 {"currencyPlural", UNUM_CURRENCY_PLURAL},
67 {"currencyAccounting", UNUM_CURRENCY_ACCOUNTING},
68 {"cashCurrency", UNUM_CASH_CURRENCY},
69 {"default", UNUM_DEFAULT},
70 {"ignore", UNUM_IGNORE}};
71
72static int32_t toEnum(
73 const Numberformattesttuple_EnumConversion *table,
74 int32_t tableLength,
75 const UnicodeString &str,
76 UErrorCode &status) {
77 if (U_FAILURE(status)) {
78 return 0;
79 }
80 CharString cstr;
81 cstr.appendInvariantChars(str, status);
82 if (U_FAILURE(status)) {
83 return 0;
84 }
85 for (int32_t i = 0; i < tableLength; ++i) {
86 if (uprv_strcmp(cstr.data(), table[i].str) == 0) {
87 return table[i].value;
88 }
89 }
90 status = U_ILLEGAL_ARGUMENT_ERROR;
91 return 0;
92}
93
94static void fromEnum(
95 const Numberformattesttuple_EnumConversion *table,
96 int32_t tableLength,
97 int32_t val,
98 UnicodeString &appendTo) {
99 for (int32_t i = 0; i < tableLength; ++i) {
100 if (table[i].value == val) {
101 appendTo.append(table[i].str);
102 }
103 }
104}
105
106static void identVal(
107 const UnicodeString &str, void *strPtr, UErrorCode & /*status*/) {
108 *static_cast<UnicodeString *>(strPtr) = str;
109}
110
111static void identStr(
112 const void *strPtr, UnicodeString &appendTo) {
113 appendTo.append(*static_cast<const UnicodeString *>(strPtr));
114}
115
116static void strToLocale(
117 const UnicodeString &str, void *localePtr, UErrorCode &status) {
118 if (U_FAILURE(status)) {
119 return;
120 }
121 CharString localeStr;
122 localeStr.appendInvariantChars(str, status);
123 *static_cast<Locale *>(localePtr) = Locale(localeStr.data());
124}
125
126static void localeToStr(
127 const void *localePtr, UnicodeString &appendTo) {
128 appendTo.append(
129 UnicodeString(
130 static_cast<const Locale *>(localePtr)->getName()));
131}
132
133static void strToInt(
134 const UnicodeString &str, void *intPtr, UErrorCode &status) {
135 if (U_FAILURE(status)) {
136 return;
137 }
138 int32_t len = str.length();
139 int32_t start = 0;
Frank Tang1f164ee2022-11-08 12:31:27 -0800140 UBool neg = false;
Frank Tang3e05d9d2021-11-08 14:04:04 -0800141 if (len > 0 && str[0] == 0x2D) { // negative
Frank Tang1f164ee2022-11-08 12:31:27 -0800142 neg = true;
Frank Tang3e05d9d2021-11-08 14:04:04 -0800143 start = 1;
144 }
145 if (start == len) {
146 status = U_ILLEGAL_ARGUMENT_ERROR;
147 return;
148 }
149 int64_t value = 0;
150 for (int32_t i = start; i < len; ++i) {
151 UChar ch = str[i];
152 if (ch < 0x30 || ch > 0x39) {
153 status = U_ILLEGAL_ARGUMENT_ERROR;
154 return;
155 }
156 value = value * 10 - 0x30 + (int32_t) ch;
157 }
158 int32_t signedValue = neg ? static_cast<int32_t>(-value) : static_cast<int32_t>(value);
159 *static_cast<int32_t *>(intPtr) = signedValue;
160}
161
162static void intToStr(
163 const void *intPtr, UnicodeString &appendTo) {
164 UChar buffer[20];
165 // int64_t such that all int32_t values can be negated
166 int64_t xSigned = *static_cast<const int32_t *>(intPtr);
167 uint32_t x;
168 if (xSigned < 0) {
169 appendTo.append((UChar)0x2D);
170 x = static_cast<uint32_t>(-xSigned);
171 } else {
172 x = static_cast<uint32_t>(xSigned);
173 }
174 int32_t len = uprv_itou(buffer, UPRV_LENGTHOF(buffer), x, 10, 1);
175 appendTo.append(buffer, 0, len);
176}
177
178static void strToDouble(
179 const UnicodeString &str, void *doublePtr, UErrorCode &status) {
180 if (U_FAILURE(status)) {
181 return;
182 }
183 CharString buffer;
184 buffer.appendInvariantChars(str, status);
185 if (U_FAILURE(status)) {
186 return;
187 }
188 *static_cast<double *>(doublePtr) = atof(buffer.data());
189}
190
191static void doubleToStr(
192 const void *doublePtr, UnicodeString &appendTo) {
193 char buffer[256];
194 double x = *static_cast<const double *>(doublePtr);
195 sprintf(buffer, "%f", x);
196 appendTo.append(buffer);
197}
198
199static void strToERounding(
200 const UnicodeString &str, void *roundPtr, UErrorCode &status) {
201 int32_t val = toEnum(
202 gRoundingEnum, UPRV_LENGTHOF(gRoundingEnum), str, status);
203 *static_cast<DecimalFormat::ERoundingMode *>(roundPtr) = (DecimalFormat::ERoundingMode) val;
204}
205
206static void eRoundingToStr(
207 const void *roundPtr, UnicodeString &appendTo) {
208 DecimalFormat::ERoundingMode rounding =
209 *static_cast<const DecimalFormat::ERoundingMode *>(roundPtr);
210 fromEnum(
211 gRoundingEnum,
212 UPRV_LENGTHOF(gRoundingEnum),
213 rounding,
214 appendTo);
215}
216
217static void strToCurrencyUsage(
218 const UnicodeString &str, void *currencyUsagePtr, UErrorCode &status) {
219 int32_t val = toEnum(
220 gCurrencyUsageEnum, UPRV_LENGTHOF(gCurrencyUsageEnum), str, status);
221 *static_cast<UCurrencyUsage *>(currencyUsagePtr) = (UCurrencyUsage) val;
222}
223
224static void currencyUsageToStr(
225 const void *currencyUsagePtr, UnicodeString &appendTo) {
226 UCurrencyUsage currencyUsage =
227 *static_cast<const UCurrencyUsage *>(currencyUsagePtr);
228 fromEnum(
229 gCurrencyUsageEnum,
230 UPRV_LENGTHOF(gCurrencyUsageEnum),
231 currencyUsage,
232 appendTo);
233}
234
235static void strToEPadPosition(
236 const UnicodeString &str, void *padPositionPtr, UErrorCode &status) {
237 int32_t val = toEnum(
238 gPadPositionEnum, UPRV_LENGTHOF(gPadPositionEnum), str, status);
239 *static_cast<DecimalFormat::EPadPosition *>(padPositionPtr) =
240 (DecimalFormat::EPadPosition) val;
241}
242
243static void ePadPositionToStr(
244 const void *padPositionPtr, UnicodeString &appendTo) {
245 DecimalFormat::EPadPosition padPosition =
246 *static_cast<const DecimalFormat::EPadPosition *>(padPositionPtr);
247 fromEnum(
248 gPadPositionEnum,
249 UPRV_LENGTHOF(gPadPositionEnum),
250 padPosition,
251 appendTo);
252}
253
254static void strToFormatStyle(
255 const UnicodeString &str, void *formatStylePtr, UErrorCode &status) {
256 int32_t val = toEnum(
257 gFormatStyleEnum, UPRV_LENGTHOF(gFormatStyleEnum), str, status);
258 *static_cast<UNumberFormatStyle *>(formatStylePtr) = (UNumberFormatStyle) val;
259}
260
261static void formatStyleToStr(
262 const void *formatStylePtr, UnicodeString &appendTo) {
263 UNumberFormatStyle formatStyle =
264 *static_cast<const UNumberFormatStyle *>(formatStylePtr);
265 fromEnum(
266 gFormatStyleEnum,
267 UPRV_LENGTHOF(gFormatStyleEnum),
268 formatStyle,
269 appendTo);
270}
271
272struct NumberFormatTestTupleFieldOps {
273 void (*toValue)(const UnicodeString &str, void *valPtr, UErrorCode &);
274 void (*toString)(const void *valPtr, UnicodeString &appendTo);
275};
276
277const NumberFormatTestTupleFieldOps gStrOps = {identVal, identStr};
278const NumberFormatTestTupleFieldOps gIntOps = {strToInt, intToStr};
279const NumberFormatTestTupleFieldOps gLocaleOps = {strToLocale, localeToStr};
280const NumberFormatTestTupleFieldOps gDoubleOps = {strToDouble, doubleToStr};
281const NumberFormatTestTupleFieldOps gERoundingOps = {strToERounding, eRoundingToStr};
282const NumberFormatTestTupleFieldOps gCurrencyUsageOps = {strToCurrencyUsage, currencyUsageToStr};
283const NumberFormatTestTupleFieldOps gEPadPositionOps = {strToEPadPosition, ePadPositionToStr};
284const NumberFormatTestTupleFieldOps gFormatStyleOps = {strToFormatStyle, formatStyleToStr};
285
286struct NumberFormatTestTupleFieldData {
287 const char *name;
288 int32_t offset;
289 int32_t flagOffset;
290 const NumberFormatTestTupleFieldOps *ops;
291};
292
293// Order must correspond to ENumberFormatTestTupleField
294const NumberFormatTestTupleFieldData gFieldData[] = {
295 FIELD_INIT(locale, &gLocaleOps),
296 FIELD_INIT(currency, &gStrOps),
297 FIELD_INIT(pattern, &gStrOps),
298 FIELD_INIT(format, &gStrOps),
299 FIELD_INIT(output, &gStrOps),
300 FIELD_INIT(comment, &gStrOps),
301 FIELD_INIT(minIntegerDigits, &gIntOps),
302 FIELD_INIT(maxIntegerDigits, &gIntOps),
303 FIELD_INIT(minFractionDigits, &gIntOps),
304 FIELD_INIT(maxFractionDigits, &gIntOps),
305 FIELD_INIT(minGroupingDigits, &gIntOps),
306 FIELD_INIT(breaks, &gStrOps),
307 FIELD_INIT(useSigDigits, &gIntOps),
308 FIELD_INIT(minSigDigits, &gIntOps),
309 FIELD_INIT(maxSigDigits, &gIntOps),
310 FIELD_INIT(useGrouping, &gIntOps),
311 FIELD_INIT(multiplier, &gIntOps),
312 FIELD_INIT(roundingIncrement, &gDoubleOps),
313 FIELD_INIT(formatWidth, &gIntOps),
314 FIELD_INIT(padCharacter, &gStrOps),
315 FIELD_INIT(useScientific, &gIntOps),
316 FIELD_INIT(grouping, &gIntOps),
317 FIELD_INIT(grouping2, &gIntOps),
318 FIELD_INIT(roundingMode, &gERoundingOps),
319 FIELD_INIT(currencyUsage, &gCurrencyUsageOps),
320 FIELD_INIT(minimumExponentDigits, &gIntOps),
321 FIELD_INIT(exponentSignAlwaysShown, &gIntOps),
322 FIELD_INIT(decimalSeparatorAlwaysShown, &gIntOps),
323 FIELD_INIT(padPosition, &gEPadPositionOps),
324 FIELD_INIT(positivePrefix, &gStrOps),
325 FIELD_INIT(positiveSuffix, &gStrOps),
326 FIELD_INIT(negativePrefix, &gStrOps),
327 FIELD_INIT(negativeSuffix, &gStrOps),
328 FIELD_INIT(signAlwaysShown, &gIntOps),
329 FIELD_INIT(localizedPattern, &gStrOps),
330 FIELD_INIT(toPattern, &gStrOps),
331 FIELD_INIT(toLocalizedPattern, &gStrOps),
332 FIELD_INIT(style, &gFormatStyleOps),
333 FIELD_INIT(parse, &gStrOps),
334 FIELD_INIT(lenient, &gIntOps),
335 FIELD_INIT(plural, &gStrOps),
336 FIELD_INIT(parseIntegerOnly, &gIntOps),
337 FIELD_INIT(decimalPatternMatchRequired, &gIntOps),
338 FIELD_INIT(parseNoExponent, &gIntOps),
339 FIELD_INIT(parseCaseSensitive, &gIntOps),
340 FIELD_INIT(outputCurrency, &gStrOps)
341};
342
343UBool
344NumberFormatTestTuple::setField(
345 ENumberFormatTestTupleField fieldId,
346 const UnicodeString &fieldValue,
347 UErrorCode &status) {
348 if (U_FAILURE(status)) {
Frank Tang1f164ee2022-11-08 12:31:27 -0800349 return false;
Frank Tang3e05d9d2021-11-08 14:04:04 -0800350 }
351 if (fieldId == kNumberFormatTestTupleFieldCount) {
352 status = U_ILLEGAL_ARGUMENT_ERROR;
Frank Tang1f164ee2022-11-08 12:31:27 -0800353 return false;
Frank Tang3e05d9d2021-11-08 14:04:04 -0800354 }
355 gFieldData[fieldId].ops->toValue(
356 fieldValue, getMutableFieldAddress(fieldId), status);
357 if (U_FAILURE(status)) {
Frank Tang1f164ee2022-11-08 12:31:27 -0800358 return false;
Frank Tang3e05d9d2021-11-08 14:04:04 -0800359 }
Frank Tang1f164ee2022-11-08 12:31:27 -0800360 setFlag(fieldId, true);
361 return true;
Frank Tang3e05d9d2021-11-08 14:04:04 -0800362}
363
364UBool
365NumberFormatTestTuple::clearField(
366 ENumberFormatTestTupleField fieldId,
367 UErrorCode &status) {
368 if (U_FAILURE(status)) {
Frank Tang1f164ee2022-11-08 12:31:27 -0800369 return false;
Frank Tang3e05d9d2021-11-08 14:04:04 -0800370 }
371 if (fieldId == kNumberFormatTestTupleFieldCount) {
372 status = U_ILLEGAL_ARGUMENT_ERROR;
Frank Tang1f164ee2022-11-08 12:31:27 -0800373 return false;
Frank Tang3e05d9d2021-11-08 14:04:04 -0800374 }
Frank Tang1f164ee2022-11-08 12:31:27 -0800375 setFlag(fieldId, false);
376 return true;
Frank Tang3e05d9d2021-11-08 14:04:04 -0800377}
378
379void
380NumberFormatTestTuple::clear() {
381 for (int32_t i = 0; i < kNumberFormatTestTupleFieldCount; ++i) {
Frank Tang1f164ee2022-11-08 12:31:27 -0800382 setFlag(i, false);
Frank Tang3e05d9d2021-11-08 14:04:04 -0800383 }
384}
385
386UnicodeString &
387NumberFormatTestTuple::toString(
388 UnicodeString &appendTo) const {
389 appendTo.append("{");
Frank Tang1f164ee2022-11-08 12:31:27 -0800390 UBool first = true;
Frank Tang3e05d9d2021-11-08 14:04:04 -0800391 for (int32_t i = 0; i < kNumberFormatTestTupleFieldCount; ++i) {
392 if (!isFlag(i)) {
393 continue;
394 }
395 if (!first) {
396 appendTo.append(", ");
397 }
Frank Tang1f164ee2022-11-08 12:31:27 -0800398 first = false;
Frank Tang3e05d9d2021-11-08 14:04:04 -0800399 appendTo.append(gFieldData[i].name);
400 appendTo.append(": ");
401 gFieldData[i].ops->toString(getFieldAddress(i), appendTo);
402 }
403 appendTo.append("}");
404 return appendTo;
405}
406
407ENumberFormatTestTupleField
408NumberFormatTestTuple::getFieldByName(
409 const UnicodeString &name) {
410 CharString buffer;
411 UErrorCode status = U_ZERO_ERROR;
412 buffer.appendInvariantChars(name, status);
413 if (U_FAILURE(status)) {
414 return kNumberFormatTestTupleFieldCount;
415 }
416 int32_t result = -1;
417 for (int32_t i = 0; i < UPRV_LENGTHOF(gFieldData); ++i) {
418 if (uprv_strcmp(gFieldData[i].name, buffer.data()) == 0) {
419 result = i;
420 break;
421 }
422 }
423 if (result == -1) {
424 return kNumberFormatTestTupleFieldCount;
425 }
426 return (ENumberFormatTestTupleField) result;
427}
428
429const void *
430NumberFormatTestTuple::getFieldAddress(int32_t fieldId) const {
431 return reinterpret_cast<const char *>(this) + gFieldData[fieldId].offset;
432}
433
434void *
435NumberFormatTestTuple::getMutableFieldAddress(int32_t fieldId) {
436 return reinterpret_cast<char *>(this) + gFieldData[fieldId].offset;
437}
438
439void
440NumberFormatTestTuple::setFlag(int32_t fieldId, UBool value) {
441 void *flagAddr = reinterpret_cast<char *>(this) + gFieldData[fieldId].flagOffset;
442 *static_cast<UBool *>(flagAddr) = value;
443}
444
445UBool
446NumberFormatTestTuple::isFlag(int32_t fieldId) const {
447 const void *flagAddr = reinterpret_cast<const char *>(this) + gFieldData[fieldId].flagOffset;
448 return *static_cast<const UBool *>(flagAddr);
449}
450
451#endif /* !UCONFIG_NO_FORMATTING */