1// © 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 "ustrfmt.h"
15#include "charstr.h"
16#include "cstring.h"
17#include "cmemory.h"
18#include "digitlst.h"
19
20static NumberFormatTestTuple *gNullPtr = NULL;
21
22#define FIELD_OFFSET(fieldName) ((int32_t) (((char *) &gNullPtr->fieldName) - ((char *) gNullPtr)))
23#define FIELD_FLAG_OFFSET(fieldName) ((int32_t) (((char *) &gNullPtr->fieldName##Flag) - ((char *) gNullPtr)))
24
25#define FIELD_INIT(fieldName, fieldType) {#fieldName, FIELD_OFFSET(fieldName), FIELD_FLAG_OFFSET(fieldName), fieldType}
26
27struct Numberformattesttuple_EnumConversion {
28    const char *str;
29    int32_t value;
30};
31
32static Numberformattesttuple_EnumConversion gRoundingEnum[] = {
33    {"ceiling", DecimalFormat::kRoundCeiling},
34    {"floor", DecimalFormat::kRoundFloor},
35    {"down", DecimalFormat::kRoundDown},
36    {"up", DecimalFormat::kRoundUp},
37    {"halfEven", DecimalFormat::kRoundHalfEven},
38    {"halfDown", DecimalFormat::kRoundHalfDown},
39    {"halfUp", DecimalFormat::kRoundHalfUp},
40    {"unnecessary", DecimalFormat::kRoundUnnecessary}};
41
42static Numberformattesttuple_EnumConversion gCurrencyUsageEnum[] = {
43    {"standard", UCURR_USAGE_STANDARD},
44    {"cash", UCURR_USAGE_CASH}};
45
46static Numberformattesttuple_EnumConversion gPadPositionEnum[] = {
47    {"beforePrefix", DecimalFormat::kPadBeforePrefix},
48    {"afterPrefix", DecimalFormat::kPadAfterPrefix},
49    {"beforeSuffix", DecimalFormat::kPadBeforeSuffix},
50    {"afterSuffix", DecimalFormat::kPadAfterSuffix}};
51
52static Numberformattesttuple_EnumConversion gFormatStyleEnum[] = {
53    {"patternDecimal", UNUM_PATTERN_DECIMAL},
54    {"decimal", UNUM_DECIMAL},
55    {"currency", UNUM_CURRENCY},
56    {"percent", UNUM_PERCENT},
57    {"scientific", UNUM_SCIENTIFIC},
58    {"spellout", UNUM_SPELLOUT},
59    {"ordinal", UNUM_ORDINAL},
60    {"duration", UNUM_DURATION},
61    {"numberingSystem", UNUM_NUMBERING_SYSTEM},
62    {"patternRuleBased", UNUM_PATTERN_RULEBASED},
63    {"currencyIso", UNUM_CURRENCY_ISO},
64    {"currencyPlural", UNUM_CURRENCY_PLURAL},
65    {"currencyAccounting", UNUM_CURRENCY_ACCOUNTING},
66    {"cashCurrency", UNUM_CASH_CURRENCY},
67    {"default", UNUM_DEFAULT},
68    {"ignore", UNUM_IGNORE}};
69
70static int32_t toEnum(
71        const Numberformattesttuple_EnumConversion *table,
72        int32_t tableLength,
73        const UnicodeString &str,
74        UErrorCode &status) {
75    if (U_FAILURE(status)) {
76        return 0;
77    }
78    CharString cstr;
79    cstr.appendInvariantChars(str, status);
80    if (U_FAILURE(status)) {
81        return 0;
82    }
83    for (int32_t i = 0; i < tableLength; ++i) {
84        if (uprv_strcmp(cstr.data(), table[i].str) == 0) {
85            return table[i].value;
86        }
87    }
88    status = U_ILLEGAL_ARGUMENT_ERROR;
89    return 0;
90}
91
92static void fromEnum(
93        const Numberformattesttuple_EnumConversion *table,
94        int32_t tableLength,
95        int32_t val,
96        UnicodeString &appendTo) {
97    for (int32_t i = 0; i < tableLength; ++i) {
98        if (table[i].value == val) {
99            appendTo.append(table[i].str);
100        }
101    }
102}
103
104static void identVal(
105        const UnicodeString &str, void *strPtr, UErrorCode & /*status*/) {
106    *static_cast<UnicodeString *>(strPtr) = str;
107}
108
109static void identStr(
110        const void *strPtr, UnicodeString &appendTo) {
111    appendTo.append(*static_cast<const UnicodeString *>(strPtr));
112}
113
114static void strToLocale(
115        const UnicodeString &str, void *localePtr, UErrorCode &status) {
116    if (U_FAILURE(status)) {
117        return;
118    }
119    CharString localeStr;
120    localeStr.appendInvariantChars(str, status);
121    *static_cast<Locale *>(localePtr) = Locale(localeStr.data());
122}
123
124static void localeToStr(
125        const void *localePtr, UnicodeString &appendTo) {
126    appendTo.append(
127            UnicodeString(
128                    static_cast<const Locale *>(localePtr)->getName()));
129}
130
131static void strToInt(
132        const UnicodeString &str, void *intPtr, UErrorCode &status) {
133    if (U_FAILURE(status)) {
134        return;
135    }
136    int32_t len = str.length();
137    int32_t start = 0;
138    UBool neg = FALSE;
139    if (len > 0 && str[0] == 0x2D) { // negative
140        neg = TRUE;
141        start = 1;
142    }
143    if (start == len) {
144        status = U_ILLEGAL_ARGUMENT_ERROR;
145        return;
146    }
147    int32_t value = 0;
148    for (int32_t i = start; i < len; ++i) {
149        UChar ch = str[i];
150        if (ch < 0x30 || ch > 0x39) {
151            status = U_ILLEGAL_ARGUMENT_ERROR;
152            return;
153        }
154        value = value * 10 - 0x30 + (int32_t) ch;
155    }
156    if (neg) {
157        value = -value;
158    }
159    *static_cast<int32_t *>(intPtr) = value;
160}
161
162static void intToStr(
163        const void *intPtr, UnicodeString &appendTo) {
164    UChar buffer[20];
165    int32_t x = *static_cast<const int32_t *>(intPtr);
166    UBool neg = FALSE;
167    if (x < 0) {
168        neg = TRUE;
169        x = -x;
170    }
171    if (neg) {
172        appendTo.append((UChar)0x2D);
173    }
174    int32_t len = uprv_itou(buffer, UPRV_LENGTHOF(buffer), (uint32_t) 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(localizedPattern, &gStrOps),
329    FIELD_INIT(toPattern, &gStrOps),
330    FIELD_INIT(toLocalizedPattern, &gStrOps),
331    FIELD_INIT(style, &gFormatStyleOps),
332    FIELD_INIT(parse, &gStrOps),
333    FIELD_INIT(lenient, &gIntOps),
334    FIELD_INIT(plural, &gStrOps),
335    FIELD_INIT(parseIntegerOnly, &gIntOps),
336    FIELD_INIT(decimalPatternMatchRequired, &gIntOps),
337    FIELD_INIT(parseNoExponent, &gIntOps),
338    FIELD_INIT(parseCaseSensitive, &gIntOps),
339    FIELD_INIT(outputCurrency, &gStrOps)
340};
341
342UBool
343NumberFormatTestTuple::setField(
344        ENumberFormatTestTupleField fieldId,
345        const UnicodeString &fieldValue,
346        UErrorCode &status) {
347    if (U_FAILURE(status)) {
348        return FALSE;
349    }
350    if (fieldId == kNumberFormatTestTupleFieldCount) {
351        status = U_ILLEGAL_ARGUMENT_ERROR;
352        return FALSE;
353    }
354    gFieldData[fieldId].ops->toValue(
355            fieldValue, getMutableFieldAddress(fieldId), status);
356    if (U_FAILURE(status)) {
357        return FALSE;
358    }
359    setFlag(fieldId, TRUE);
360    return TRUE;
361}
362
363UBool
364NumberFormatTestTuple::clearField(
365        ENumberFormatTestTupleField fieldId,
366        UErrorCode &status) {
367    if (U_FAILURE(status)) {
368        return FALSE;
369    }
370    if (fieldId == kNumberFormatTestTupleFieldCount) {
371        status = U_ILLEGAL_ARGUMENT_ERROR;
372        return FALSE;
373    }
374    setFlag(fieldId, FALSE);
375    return TRUE;
376}
377
378void
379NumberFormatTestTuple::clear() {
380    for (int32_t i = 0; i < kNumberFormatTestTupleFieldCount; ++i) {
381        setFlag(i, FALSE);
382    }
383}
384
385UnicodeString &
386NumberFormatTestTuple::toString(
387        UnicodeString &appendTo) const {
388    appendTo.append("{");
389    UBool first = TRUE;
390    for (int32_t i = 0; i < kNumberFormatTestTupleFieldCount; ++i) {
391        if (!isFlag(i)) {
392            continue;
393        }
394        if (!first) {
395            appendTo.append(", ");
396        }
397        first = FALSE;
398        appendTo.append(gFieldData[i].name);
399        appendTo.append(": ");
400        gFieldData[i].ops->toString(getFieldAddress(i), appendTo);
401    }
402    appendTo.append("}");
403    return appendTo;
404}
405
406ENumberFormatTestTupleField
407NumberFormatTestTuple::getFieldByName(
408        const UnicodeString &name) {
409    CharString buffer;
410    UErrorCode status = U_ZERO_ERROR;
411    buffer.appendInvariantChars(name, status);
412    if (U_FAILURE(status)) {
413        return kNumberFormatTestTupleFieldCount;
414    }
415    int32_t result = -1;
416    for (int32_t i = 0; i < UPRV_LENGTHOF(gFieldData); ++i) {
417        if (uprv_strcmp(gFieldData[i].name, buffer.data()) == 0) {
418            result = i;
419            break;
420        }
421    }
422    if (result == -1) {
423        return kNumberFormatTestTupleFieldCount;
424    }
425    return (ENumberFormatTestTupleField) result;
426}
427
428const void *
429NumberFormatTestTuple::getFieldAddress(int32_t fieldId) const {
430    return reinterpret_cast<const char *>(this) + gFieldData[fieldId].offset;
431}
432
433void *
434NumberFormatTestTuple::getMutableFieldAddress(int32_t fieldId) {
435    return reinterpret_cast<char *>(this) + gFieldData[fieldId].offset;
436}
437
438void
439NumberFormatTestTuple::setFlag(int32_t fieldId, UBool value) {
440    void *flagAddr = reinterpret_cast<char *>(this) + gFieldData[fieldId].flagOffset;
441    *static_cast<UBool *>(flagAddr) = value;
442}
443
444UBool
445NumberFormatTestTuple::isFlag(int32_t fieldId) const {
446    const void *flagAddr = reinterpret_cast<const char *>(this) + gFieldData[fieldId].flagOffset;
447    return *static_cast<const UBool *>(flagAddr);
448}
449
450#endif /* !UCONFIG_NO_FORMATTING */
451