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