// © 2016 and later: Unicode, Inc. and others. // License & terms of use: http://www.unicode.org/copyright.html /* ******************************************************************************* * Copyright (C) 2015, International Business Machines Corporation and * * others. All Rights Reserved. * ******************************************************************************* */ #include "numberformattesttuple.h" #if !UCONFIG_NO_FORMATTING #include "unicode/testlog.h" #include "ustrfmt.h" #include "charstr.h" #include "cstring.h" #include "cmemory.h" static NumberFormatTestTuple emptyObject; static NumberFormatTestTuple *gNullPtr = &emptyObject; #define FIELD_OFFSET(fieldName) ((int32_t) (((char *) &gNullPtr->fieldName) - ((char *) gNullPtr))) #define FIELD_FLAG_OFFSET(fieldName) ((int32_t) (((char *) &gNullPtr->fieldName##Flag) - ((char *) gNullPtr))) #define FIELD_INIT(fieldName, fieldType) {#fieldName, FIELD_OFFSET(fieldName), FIELD_FLAG_OFFSET(fieldName), fieldType} struct Numberformattesttuple_EnumConversion { const char *str; int32_t value; }; static Numberformattesttuple_EnumConversion gRoundingEnum[] = { {"ceiling", DecimalFormat::kRoundCeiling}, {"floor", DecimalFormat::kRoundFloor}, {"down", DecimalFormat::kRoundDown}, {"up", DecimalFormat::kRoundUp}, {"halfEven", DecimalFormat::kRoundHalfEven}, {"halfDown", DecimalFormat::kRoundHalfDown}, {"halfUp", DecimalFormat::kRoundHalfUp}, {"unnecessary", DecimalFormat::kRoundUnnecessary}}; static Numberformattesttuple_EnumConversion gCurrencyUsageEnum[] = { {"standard", UCURR_USAGE_STANDARD}, {"cash", UCURR_USAGE_CASH}}; static Numberformattesttuple_EnumConversion gPadPositionEnum[] = { {"beforePrefix", DecimalFormat::kPadBeforePrefix}, {"afterPrefix", DecimalFormat::kPadAfterPrefix}, {"beforeSuffix", DecimalFormat::kPadBeforeSuffix}, {"afterSuffix", DecimalFormat::kPadAfterSuffix}}; static Numberformattesttuple_EnumConversion gFormatStyleEnum[] = { {"patternDecimal", UNUM_PATTERN_DECIMAL}, {"decimal", UNUM_DECIMAL}, {"currency", UNUM_CURRENCY}, {"percent", UNUM_PERCENT}, {"scientific", UNUM_SCIENTIFIC}, {"spellout", UNUM_SPELLOUT}, {"ordinal", UNUM_ORDINAL}, {"duration", UNUM_DURATION}, {"numberingSystem", UNUM_NUMBERING_SYSTEM}, {"patternRuleBased", UNUM_PATTERN_RULEBASED}, {"currencyIso", UNUM_CURRENCY_ISO}, {"currencyPlural", UNUM_CURRENCY_PLURAL}, {"currencyAccounting", UNUM_CURRENCY_ACCOUNTING}, {"cashCurrency", UNUM_CASH_CURRENCY}, {"default", UNUM_DEFAULT}, {"ignore", UNUM_IGNORE}}; static int32_t toEnum( const Numberformattesttuple_EnumConversion *table, int32_t tableLength, const UnicodeString &str, UErrorCode &status) { if (U_FAILURE(status)) { return 0; } CharString cstr; cstr.appendInvariantChars(str, status); if (U_FAILURE(status)) { return 0; } for (int32_t i = 0; i < tableLength; ++i) { if (uprv_strcmp(cstr.data(), table[i].str) == 0) { return table[i].value; } } status = U_ILLEGAL_ARGUMENT_ERROR; return 0; } static void fromEnum( const Numberformattesttuple_EnumConversion *table, int32_t tableLength, int32_t val, UnicodeString &appendTo) { for (int32_t i = 0; i < tableLength; ++i) { if (table[i].value == val) { appendTo.append(table[i].str); } } } static void identVal( const UnicodeString &str, void *strPtr, UErrorCode & /*status*/) { *static_cast(strPtr) = str; } static void identStr( const void *strPtr, UnicodeString &appendTo) { appendTo.append(*static_cast(strPtr)); } static void strToLocale( const UnicodeString &str, void *localePtr, UErrorCode &status) { if (U_FAILURE(status)) { return; } CharString localeStr; localeStr.appendInvariantChars(str, status); *static_cast(localePtr) = Locale(localeStr.data()); } static void localeToStr( const void *localePtr, UnicodeString &appendTo) { appendTo.append( UnicodeString( static_cast(localePtr)->getName())); } static void strToInt( const UnicodeString &str, void *intPtr, UErrorCode &status) { if (U_FAILURE(status)) { return; } int32_t len = str.length(); int32_t start = 0; UBool neg = FALSE; if (len > 0 && str[0] == 0x2D) { // negative neg = TRUE; start = 1; } if (start == len) { status = U_ILLEGAL_ARGUMENT_ERROR; return; } int64_t value = 0; for (int32_t i = start; i < len; ++i) { UChar ch = str[i]; if (ch < 0x30 || ch > 0x39) { status = U_ILLEGAL_ARGUMENT_ERROR; return; } value = value * 10 - 0x30 + (int32_t) ch; } int32_t signedValue = neg ? static_cast(-value) : static_cast(value); *static_cast(intPtr) = signedValue; } static void intToStr( const void *intPtr, UnicodeString &appendTo) { UChar buffer[20]; // int64_t such that all int32_t values can be negated int64_t xSigned = *static_cast(intPtr); uint32_t x; if (xSigned < 0) { appendTo.append((UChar)0x2D); x = static_cast(-xSigned); } else { x = static_cast(xSigned); } int32_t len = uprv_itou(buffer, UPRV_LENGTHOF(buffer), x, 10, 1); appendTo.append(buffer, 0, len); } static void strToDouble( const UnicodeString &str, void *doublePtr, UErrorCode &status) { if (U_FAILURE(status)) { return; } CharString buffer; buffer.appendInvariantChars(str, status); if (U_FAILURE(status)) { return; } *static_cast(doublePtr) = atof(buffer.data()); } static void doubleToStr( const void *doublePtr, UnicodeString &appendTo) { char buffer[256]; double x = *static_cast(doublePtr); sprintf(buffer, "%f", x); appendTo.append(buffer); } static void strToERounding( const UnicodeString &str, void *roundPtr, UErrorCode &status) { int32_t val = toEnum( gRoundingEnum, UPRV_LENGTHOF(gRoundingEnum), str, status); *static_cast(roundPtr) = (DecimalFormat::ERoundingMode) val; } static void eRoundingToStr( const void *roundPtr, UnicodeString &appendTo) { DecimalFormat::ERoundingMode rounding = *static_cast(roundPtr); fromEnum( gRoundingEnum, UPRV_LENGTHOF(gRoundingEnum), rounding, appendTo); } static void strToCurrencyUsage( const UnicodeString &str, void *currencyUsagePtr, UErrorCode &status) { int32_t val = toEnum( gCurrencyUsageEnum, UPRV_LENGTHOF(gCurrencyUsageEnum), str, status); *static_cast(currencyUsagePtr) = (UCurrencyUsage) val; } static void currencyUsageToStr( const void *currencyUsagePtr, UnicodeString &appendTo) { UCurrencyUsage currencyUsage = *static_cast(currencyUsagePtr); fromEnum( gCurrencyUsageEnum, UPRV_LENGTHOF(gCurrencyUsageEnum), currencyUsage, appendTo); } static void strToEPadPosition( const UnicodeString &str, void *padPositionPtr, UErrorCode &status) { int32_t val = toEnum( gPadPositionEnum, UPRV_LENGTHOF(gPadPositionEnum), str, status); *static_cast(padPositionPtr) = (DecimalFormat::EPadPosition) val; } static void ePadPositionToStr( const void *padPositionPtr, UnicodeString &appendTo) { DecimalFormat::EPadPosition padPosition = *static_cast(padPositionPtr); fromEnum( gPadPositionEnum, UPRV_LENGTHOF(gPadPositionEnum), padPosition, appendTo); } static void strToFormatStyle( const UnicodeString &str, void *formatStylePtr, UErrorCode &status) { int32_t val = toEnum( gFormatStyleEnum, UPRV_LENGTHOF(gFormatStyleEnum), str, status); *static_cast(formatStylePtr) = (UNumberFormatStyle) val; } static void formatStyleToStr( const void *formatStylePtr, UnicodeString &appendTo) { UNumberFormatStyle formatStyle = *static_cast(formatStylePtr); fromEnum( gFormatStyleEnum, UPRV_LENGTHOF(gFormatStyleEnum), formatStyle, appendTo); } struct NumberFormatTestTupleFieldOps { void (*toValue)(const UnicodeString &str, void *valPtr, UErrorCode &); void (*toString)(const void *valPtr, UnicodeString &appendTo); }; const NumberFormatTestTupleFieldOps gStrOps = {identVal, identStr}; const NumberFormatTestTupleFieldOps gIntOps = {strToInt, intToStr}; const NumberFormatTestTupleFieldOps gLocaleOps = {strToLocale, localeToStr}; const NumberFormatTestTupleFieldOps gDoubleOps = {strToDouble, doubleToStr}; const NumberFormatTestTupleFieldOps gERoundingOps = {strToERounding, eRoundingToStr}; const NumberFormatTestTupleFieldOps gCurrencyUsageOps = {strToCurrencyUsage, currencyUsageToStr}; const NumberFormatTestTupleFieldOps gEPadPositionOps = {strToEPadPosition, ePadPositionToStr}; const NumberFormatTestTupleFieldOps gFormatStyleOps = {strToFormatStyle, formatStyleToStr}; struct NumberFormatTestTupleFieldData { const char *name; int32_t offset; int32_t flagOffset; const NumberFormatTestTupleFieldOps *ops; }; // Order must correspond to ENumberFormatTestTupleField const NumberFormatTestTupleFieldData gFieldData[] = { FIELD_INIT(locale, &gLocaleOps), FIELD_INIT(currency, &gStrOps), FIELD_INIT(pattern, &gStrOps), FIELD_INIT(format, &gStrOps), FIELD_INIT(output, &gStrOps), FIELD_INIT(comment, &gStrOps), FIELD_INIT(minIntegerDigits, &gIntOps), FIELD_INIT(maxIntegerDigits, &gIntOps), FIELD_INIT(minFractionDigits, &gIntOps), FIELD_INIT(maxFractionDigits, &gIntOps), FIELD_INIT(minGroupingDigits, &gIntOps), FIELD_INIT(breaks, &gStrOps), FIELD_INIT(useSigDigits, &gIntOps), FIELD_INIT(minSigDigits, &gIntOps), FIELD_INIT(maxSigDigits, &gIntOps), FIELD_INIT(useGrouping, &gIntOps), FIELD_INIT(multiplier, &gIntOps), FIELD_INIT(roundingIncrement, &gDoubleOps), FIELD_INIT(formatWidth, &gIntOps), FIELD_INIT(padCharacter, &gStrOps), FIELD_INIT(useScientific, &gIntOps), FIELD_INIT(grouping, &gIntOps), FIELD_INIT(grouping2, &gIntOps), FIELD_INIT(roundingMode, &gERoundingOps), FIELD_INIT(currencyUsage, &gCurrencyUsageOps), FIELD_INIT(minimumExponentDigits, &gIntOps), FIELD_INIT(exponentSignAlwaysShown, &gIntOps), FIELD_INIT(decimalSeparatorAlwaysShown, &gIntOps), FIELD_INIT(padPosition, &gEPadPositionOps), FIELD_INIT(positivePrefix, &gStrOps), FIELD_INIT(positiveSuffix, &gStrOps), FIELD_INIT(negativePrefix, &gStrOps), FIELD_INIT(negativeSuffix, &gStrOps), FIELD_INIT(signAlwaysShown, &gIntOps), FIELD_INIT(localizedPattern, &gStrOps), FIELD_INIT(toPattern, &gStrOps), FIELD_INIT(toLocalizedPattern, &gStrOps), FIELD_INIT(style, &gFormatStyleOps), FIELD_INIT(parse, &gStrOps), FIELD_INIT(lenient, &gIntOps), FIELD_INIT(plural, &gStrOps), FIELD_INIT(parseIntegerOnly, &gIntOps), FIELD_INIT(decimalPatternMatchRequired, &gIntOps), FIELD_INIT(parseNoExponent, &gIntOps), FIELD_INIT(parseCaseSensitive, &gIntOps), FIELD_INIT(outputCurrency, &gStrOps) }; UBool NumberFormatTestTuple::setField( ENumberFormatTestTupleField fieldId, const UnicodeString &fieldValue, UErrorCode &status) { if (U_FAILURE(status)) { return FALSE; } if (fieldId == kNumberFormatTestTupleFieldCount) { status = U_ILLEGAL_ARGUMENT_ERROR; return FALSE; } gFieldData[fieldId].ops->toValue( fieldValue, getMutableFieldAddress(fieldId), status); if (U_FAILURE(status)) { return FALSE; } setFlag(fieldId, TRUE); return TRUE; } UBool NumberFormatTestTuple::clearField( ENumberFormatTestTupleField fieldId, UErrorCode &status) { if (U_FAILURE(status)) { return FALSE; } if (fieldId == kNumberFormatTestTupleFieldCount) { status = U_ILLEGAL_ARGUMENT_ERROR; return FALSE; } setFlag(fieldId, FALSE); return TRUE; } void NumberFormatTestTuple::clear() { for (int32_t i = 0; i < kNumberFormatTestTupleFieldCount; ++i) { setFlag(i, FALSE); } } UnicodeString & NumberFormatTestTuple::toString( UnicodeString &appendTo) const { appendTo.append("{"); UBool first = TRUE; for (int32_t i = 0; i < kNumberFormatTestTupleFieldCount; ++i) { if (!isFlag(i)) { continue; } if (!first) { appendTo.append(", "); } first = FALSE; appendTo.append(gFieldData[i].name); appendTo.append(": "); gFieldData[i].ops->toString(getFieldAddress(i), appendTo); } appendTo.append("}"); return appendTo; } ENumberFormatTestTupleField NumberFormatTestTuple::getFieldByName( const UnicodeString &name) { CharString buffer; UErrorCode status = U_ZERO_ERROR; buffer.appendInvariantChars(name, status); if (U_FAILURE(status)) { return kNumberFormatTestTupleFieldCount; } int32_t result = -1; for (int32_t i = 0; i < UPRV_LENGTHOF(gFieldData); ++i) { if (uprv_strcmp(gFieldData[i].name, buffer.data()) == 0) { result = i; break; } } if (result == -1) { return kNumberFormatTestTupleFieldCount; } return (ENumberFormatTestTupleField) result; } const void * NumberFormatTestTuple::getFieldAddress(int32_t fieldId) const { return reinterpret_cast(this) + gFieldData[fieldId].offset; } void * NumberFormatTestTuple::getMutableFieldAddress(int32_t fieldId) { return reinterpret_cast(this) + gFieldData[fieldId].offset; } void NumberFormatTestTuple::setFlag(int32_t fieldId, UBool value) { void *flagAddr = reinterpret_cast(this) + gFieldData[fieldId].flagOffset; *static_cast(flagAddr) = value; } UBool NumberFormatTestTuple::isFlag(int32_t fieldId) const { const void *flagAddr = reinterpret_cast(this) + gFieldData[fieldId].flagOffset; return *static_cast(flagAddr); } #endif /* !UCONFIG_NO_FORMATTING */