/* TODO: Is setting the prefix symbol to a positive sign a good idea in all locales? */ static void u_sprintf_set_sign(UNumberFormat *format, const u_sprintf_spec_info *info, UErrorCode *status) { if(info->fShowSign) { if (info->fSpace) { /* Setting UNUM_PLUS_SIGN_SYMBOL affects the exponent too. */ /* unum_setSymbol(format, UNUM_PLUS_SIGN_SYMBOL, gSpaceStr, 1, &status); */ unum_setTextAttribute(format, UNUM_POSITIVE_PREFIX, gSpaceStr, 1, status); } else { UChar plusSymbol[USPRINTF_SYMBOL_BUFFER_SIZE]; int32_t symbolLen; symbolLen = unum_getSymbol(format, UNUM_PLUS_SIGN_SYMBOL, plusSymbol, sizeof(plusSymbol)/sizeof(*plusSymbol), status); unum_setTextAttribute(format, UNUM_POSITIVE_PREFIX, plusSymbol, symbolLen, status); } } }
static void NativeDecimalFormat_setTextAttribute(JNIEnv* env, jclass, jlong addr, jint javaAttr, jstring javaValue) { ScopedStringChars value(env, javaValue); if (value.get() == NULL) { return; } UErrorCode status = U_ZERO_ERROR; UNumberFormatTextAttribute attr = static_cast<UNumberFormatTextAttribute>(javaAttr); unum_setTextAttribute(toUNumberFormat(addr), attr, value.get(), value.size(), &status); maybeThrowIcuException(env, "unum_setTextAttribute", status); }
static bool HHVM_METHOD(NumberFormatter, setTextAttribute, int64_t attr, const String& value) { NUMFMT_GET(obj, this_, false); UErrorCode error = U_ZERO_ERROR; icu::UnicodeString val(u16(value, error)); NUMFMT_CHECK(obj, error, false); unum_setTextAttribute(obj->formatter(), (UNumberFormatTextAttribute)attr, val.getBuffer(), val.length(), &error); NUMFMT_CHECK(obj, error, false); return true; }
//static void NativeDecimalFormat_setTextAttribute(JNIEnv* env, jclass, jint addr, jint javaAttr, jstring javaValue) { JNIEXPORT void JNICALL Java_com_ibm_icu4jni_text_NativeDecimalFormat_setTextAttribute(JNIEnv* env, jclass, jint addr, jint javaAttr, jstring javaValue) { ScopedJavaUnicodeString value(env, javaValue); UnicodeString& s(value.unicodeString()); UErrorCode status = U_ZERO_ERROR; UNumberFormatTextAttribute attr = static_cast<UNumberFormatTextAttribute> (javaAttr); unum_setTextAttribute(toUNumberFormat(addr), attr, s.getBuffer(), s.length(), &status); icu4jni_error(env, status); }
static void TestPrefixSuffix(void) { int32_t pos; UErrorCode status; double result1 = 0.0, result2 = 0.0; UNumberFormat* parser; UChar buffer[4]; static const UChar TEST_NUMBER[] = {0x0024,0x0031,0x0032,0x002E,0x0030,0x0030,0}; /* $12.00 */ static const UChar NEG_PREFIX[] = {0x005B,0}; /* "[" */ static const UChar NEG_SUFFIX[] = {0x005D,0}; /* "]" */ status = U_ZERO_ERROR; parser = unum_open(UNUM_CURRENCY, NULL, -1, "en_US", NULL, &status); if (U_FAILURE(status)) { log_data_err("Error: unum_open returned %s (Are you missing data?)\n", u_errorName(status)); return; } pos = 0; status = U_ZERO_ERROR; result1 = unum_parseDoubleCurrency(parser, TEST_NUMBER, -1, &pos, buffer, &status); unum_setTextAttribute(parser, UNUM_NEGATIVE_SUFFIX, NEG_SUFFIX, -1, &status); unum_setTextAttribute(parser, UNUM_NEGATIVE_PREFIX, NEG_PREFIX, -1, &status); if (U_FAILURE(status)) { log_err("Error: unum_setTextAttribute returned %s\n", u_errorName(status)); return; } pos = 0; result2 = unum_parseDoubleCurrency(parser, TEST_NUMBER, -1, &pos, buffer, &status); if (result1 != result2 || U_FAILURE(status)) { log_err("Error: unum_parseDoubleCurrency didn't return the same value for same string %f %f %s\n", result1, result2, u_errorName(status)); } unum_close(parser); }
static void u_printf_reset_sign(UNumberFormat *format, const u_printf_spec_info *info, UChar *prefixBuffer, int32_t *prefixBufLen, UErrorCode *status) { if(info->fShowSign) { unum_setTextAttribute(format, UNUM_POSITIVE_PREFIX, prefixBuffer, *prefixBufLen, status); } }
void IntlNumberFormat::initializeNumberFormat(ExecState& state, JSValue locales, JSValue optionsValue) { VM& vm = state.vm(); auto scope = DECLARE_THROW_SCOPE(vm); // 11.1.2 InitializeNumberFormat (numberFormat, locales, options) (ECMA-402) // https://tc39.github.io/ecma402/#sec-initializenumberformat auto requestedLocales = canonicalizeLocaleList(state, locales); RETURN_IF_EXCEPTION(scope, void()); JSObject* options; if (optionsValue.isUndefined()) options = constructEmptyObject(&state, state.lexicalGlobalObject()->nullPrototypeObjectStructure()); else { options = optionsValue.toObject(&state); RETURN_IF_EXCEPTION(scope, void()); } HashMap<String, String> opt; String matcher = intlStringOption(state, options, vm.propertyNames->localeMatcher, { "lookup", "best fit" }, "localeMatcher must be either \"lookup\" or \"best fit\"", "best fit"); RETURN_IF_EXCEPTION(scope, void()); opt.add("localeMatcher"_s, matcher); auto& availableLocales = state.jsCallee()->globalObject(vm)->intlNumberFormatAvailableLocales(); auto result = resolveLocale(state, availableLocales, requestedLocales, opt, relevantNumberExtensionKeys, WTF_ARRAY_LENGTH(relevantNumberExtensionKeys), IntlNFInternal::localeData); m_locale = result.get("locale"_s); if (m_locale.isEmpty()) { throwTypeError(&state, scope, "failed to initialize NumberFormat due to invalid locale"_s); return; } m_numberingSystem = result.get("nu"_s); String styleString = intlStringOption(state, options, Identifier::fromString(&vm, "style"), { "decimal", "percent", "currency" }, "style must be either \"decimal\", \"percent\", or \"currency\"", "decimal"); RETURN_IF_EXCEPTION(scope, void()); if (styleString == "decimal") m_style = Style::Decimal; else if (styleString == "percent") m_style = Style::Percent; else if (styleString == "currency") m_style = Style::Currency; else ASSERT_NOT_REACHED(); String currency = intlStringOption(state, options, Identifier::fromString(&vm, "currency"), { }, nullptr, nullptr); RETURN_IF_EXCEPTION(scope, void()); if (!currency.isNull()) { if (currency.length() != 3 || !currency.isAllSpecialCharacters<isASCIIAlpha>()) { throwException(&state, scope, createRangeError(&state, "currency is not a well-formed currency code"_s)); return; } } unsigned currencyDigits = 0; if (m_style == Style::Currency) { if (currency.isNull()) { throwTypeError(&state, scope, "currency must be a string"_s); return; } currency = currency.convertToASCIIUppercase(); m_currency = currency; currencyDigits = computeCurrencyDigits(currency); } String currencyDisplayString = intlStringOption(state, options, Identifier::fromString(&vm, "currencyDisplay"), { "code", "symbol", "name" }, "currencyDisplay must be either \"code\", \"symbol\", or \"name\"", "symbol"); RETURN_IF_EXCEPTION(scope, void()); if (m_style == Style::Currency) { if (currencyDisplayString == "code") m_currencyDisplay = CurrencyDisplay::Code; else if (currencyDisplayString == "symbol") m_currencyDisplay = CurrencyDisplay::Symbol; else if (currencyDisplayString == "name") m_currencyDisplay = CurrencyDisplay::Name; else ASSERT_NOT_REACHED(); } unsigned minimumIntegerDigits = intlNumberOption(state, options, Identifier::fromString(&vm, "minimumIntegerDigits"), 1, 21, 1); RETURN_IF_EXCEPTION(scope, void()); m_minimumIntegerDigits = minimumIntegerDigits; unsigned minimumFractionDigitsDefault = (m_style == Style::Currency) ? currencyDigits : 0; unsigned minimumFractionDigits = intlNumberOption(state, options, Identifier::fromString(&vm, "minimumFractionDigits"), 0, 20, minimumFractionDigitsDefault); RETURN_IF_EXCEPTION(scope, void()); m_minimumFractionDigits = minimumFractionDigits; unsigned maximumFractionDigitsDefault; if (m_style == Style::Currency) maximumFractionDigitsDefault = std::max(minimumFractionDigits, currencyDigits); else if (m_style == Style::Percent) maximumFractionDigitsDefault = minimumFractionDigits; else maximumFractionDigitsDefault = std::max(minimumFractionDigits, 3u); unsigned maximumFractionDigits = intlNumberOption(state, options, Identifier::fromString(&vm, "maximumFractionDigits"), minimumFractionDigits, 20, maximumFractionDigitsDefault); RETURN_IF_EXCEPTION(scope, void()); m_maximumFractionDigits = maximumFractionDigits; JSValue minimumSignificantDigitsValue = options->get(&state, Identifier::fromString(&vm, "minimumSignificantDigits")); RETURN_IF_EXCEPTION(scope, void()); JSValue maximumSignificantDigitsValue = options->get(&state, Identifier::fromString(&vm, "maximumSignificantDigits")); RETURN_IF_EXCEPTION(scope, void()); if (!minimumSignificantDigitsValue.isUndefined() || !maximumSignificantDigitsValue.isUndefined()) { unsigned minimumSignificantDigits = intlDefaultNumberOption(state, minimumSignificantDigitsValue, Identifier::fromString(&vm, "minimumSignificantDigits"), 1, 21, 1); RETURN_IF_EXCEPTION(scope, void()); unsigned maximumSignificantDigits = intlDefaultNumberOption(state, maximumSignificantDigitsValue, Identifier::fromString(&vm, "maximumSignificantDigits"), minimumSignificantDigits, 21, 21); RETURN_IF_EXCEPTION(scope, void()); m_minimumSignificantDigits = minimumSignificantDigits; m_maximumSignificantDigits = maximumSignificantDigits; } bool usesFallback; bool useGrouping = intlBooleanOption(state, options, Identifier::fromString(&vm, "useGrouping"), usesFallback); if (usesFallback) useGrouping = true; RETURN_IF_EXCEPTION(scope, void()); m_useGrouping = useGrouping; UNumberFormatStyle style = UNUM_DEFAULT; switch (m_style) { case Style::Decimal: style = UNUM_DECIMAL; break; case Style::Percent: style = UNUM_PERCENT; break; case Style::Currency: switch (m_currencyDisplay) { case CurrencyDisplay::Code: style = UNUM_CURRENCY_ISO; break; case CurrencyDisplay::Symbol: style = UNUM_CURRENCY; break; case CurrencyDisplay::Name: style = UNUM_CURRENCY_PLURAL; break; default: ASSERT_NOT_REACHED(); } break; default: ASSERT_NOT_REACHED(); } UErrorCode status = U_ZERO_ERROR; m_numberFormat = std::unique_ptr<UNumberFormat, UNumberFormatDeleter>(unum_open(style, nullptr, 0, m_locale.utf8().data(), nullptr, &status)); if (U_FAILURE(status)) { throwTypeError(&state, scope, "failed to initialize NumberFormat"_s); return; } if (m_style == Style::Currency) { unum_setTextAttribute(m_numberFormat.get(), UNUM_CURRENCY_CODE, StringView(m_currency).upconvertedCharacters(), m_currency.length(), &status); if (U_FAILURE(status)) { throwTypeError(&state, scope, "failed to initialize NumberFormat"_s); return; } } if (!m_minimumSignificantDigits) { unum_setAttribute(m_numberFormat.get(), UNUM_MIN_INTEGER_DIGITS, m_minimumIntegerDigits); unum_setAttribute(m_numberFormat.get(), UNUM_MIN_FRACTION_DIGITS, m_minimumFractionDigits); unum_setAttribute(m_numberFormat.get(), UNUM_MAX_FRACTION_DIGITS, m_maximumFractionDigits); } else { unum_setAttribute(m_numberFormat.get(), UNUM_SIGNIFICANT_DIGITS_USED, true); unum_setAttribute(m_numberFormat.get(), UNUM_MIN_SIGNIFICANT_DIGITS, m_minimumSignificantDigits); unum_setAttribute(m_numberFormat.get(), UNUM_MAX_SIGNIFICANT_DIGITS, m_maximumSignificantDigits); } unum_setAttribute(m_numberFormat.get(), UNUM_GROUPING_USED, m_useGrouping); unum_setAttribute(m_numberFormat.get(), UNUM_ROUNDING_MODE, UNUM_ROUND_HALFUP); m_initializedNumberFormat = true; }
/** * Test currency "object" (we use this name to match the other C++ * test name and the Jave name). Actually, test ISO currency code * support in the C API. */ static void TestCurrencyObject(void) { UNumberFormat *currencyFmt; UChar *str=NULL, *res=NULL; int32_t lneed, i; UFieldPosition pos; UErrorCode status = U_ZERO_ERROR; const char* locale[]={ "fr_FR", "fr_FR", }; const char* currency[]={ "", "JPY", }; const char* result[]={ "1\\u202F234,56\\u00A0\\u20AC", "1\\u202F235\\u00A0JPY", }; log_verbose("\nTesting the number format with different currency codes\n"); for(i=0; i < 2; i++) { char cStr[20]={0}; UChar isoCode[16]={0}; currencyFmt = unum_open(UNUM_CURRENCY, NULL,0,locale[i],NULL, &status); if(U_FAILURE(status)){ log_data_err("Error in the construction of number format with style currency: %s (Are you missing data?)\n", myErrorName(status)); } else { if (*currency[i]) { u_uastrcpy(isoCode, currency[i]); unum_setTextAttribute(currencyFmt, UNUM_CURRENCY_CODE, isoCode, u_strlen(isoCode), &status); if(U_FAILURE(status)) { log_err("FAIL: can't set currency code %s\n", myErrorName(status) ); } } unum_getTextAttribute(currencyFmt, UNUM_CURRENCY_CODE, isoCode, sizeof(isoCode), &status); if(U_FAILURE(status)) { log_err("FAIL: can't get currency code %s\n", myErrorName(status) ); } u_UCharsToChars(isoCode,cStr,u_strlen(isoCode)); log_verbose("ISO code %s\n", cStr); if (*currency[i] && uprv_strcmp(cStr, currency[i])) { log_err("FAIL: currency should be %s, but is %s\n", currency[i], cStr); } lneed=0; lneed= unum_formatDouble(currencyFmt, 1234.56, NULL, lneed, NULL, &status); if(status==U_BUFFER_OVERFLOW_ERROR){ status=U_ZERO_ERROR; str=(UChar*)malloc(sizeof(UChar) * (lneed+1) ); pos.field = 0; unum_formatDouble(currencyFmt, 1234.56, str, lneed+1, &pos, &status); } if(U_FAILURE(status)) { log_err("Error in formatting using unum_formatDouble(.....): %s\n", myErrorName(status) ); } else { res=(UChar*)malloc(sizeof(UChar) * (strlen(result[i])+1) ); u_unescape(result[i],res, (int32_t)(strlen(result[i])+1)); if (u_strcmp(str, res) != 0){ log_err("FAIL: Expected %s Got: %s for locale: %s\n", result[i],aescstrdup(str, -1),locale[i]); } } } unum_close(currencyFmt); free(str); free(res); } }
/** * call-seq: * 23874.localize(:decimal, 'es_ES') => 23.874 */ VALUE rb_numeric_localize(int argc, VALUE *argv, VALUE self) { VALUE style, options; UNumberFormatStyle formatStyle; char *locale = NULL; UNumberFormat *format; UErrorCode status; UChar result[256]; /* arguments */ rb_scan_args(argc, argv, "02", &style, &options); if (style == Qnil) { formatStyle = UNUM_DECIMAL; } else { ID style_ID; Check_Type(style, T_SYMBOL); style_ID = SYM2ID(style); if (style_ID == rb_intern("decimal")) { formatStyle = UNUM_DECIMAL; } else if (style_ID == rb_intern("currency")) { formatStyle = UNUM_CURRENCY; } else if (style_ID == rb_intern("percent")) { formatStyle = UNUM_PERCENT; } else if (style_ID == rb_intern("scientific")) { formatStyle = UNUM_SCIENTIFIC; } else if (style_ID == rb_intern("spellout")) { formatStyle = UNUM_SPELLOUT; } else { rb_raise(rb_eArgError, "unsupported format style %s", rb_id2name(style_ID)); } } if (options != Qnil) { VALUE rb_locale; Check_Type(options, T_HASH); rb_locale = rb_hash_aref(options, ID2SYM(rb_intern("locale"))); if (rb_locale != Qnil) { locale = StringValuePtr(rb_locale); } } /* formatter */ status = U_ZERO_ERROR; format = unum_open(formatStyle, NULL, 0, locale, NULL, &status); RAISE_ON_ERROR(status); /* set format attributes */ if (options != Qnil) { VALUE currency, precision, round_mode, round_increment; switch (formatStyle) { case UNUM_CURRENCY: currency = rb_hash_aref(options, ID2SYM(rb_intern("currency"))); if (currency != Qnil) { UChar *uStr; int32_t uStrLen; uStr = u_strFromRString(currency, &uStrLen); status = U_ZERO_ERROR; unum_setTextAttribute(format, UNUM_CURRENCY_CODE, uStr, uStrLen, &status); RAISE_ON_ERROR(status); } case UNUM_DECIMAL: /* precision */ precision = rb_hash_aref(options, ID2SYM(rb_intern("precision"))); if (precision != Qnil) { Check_Type(precision, T_FIXNUM); status = U_ZERO_ERROR; unum_setAttribute(format, UNUM_FRACTION_DIGITS, NUM2INT(precision)); RAISE_ON_ERROR(status); } round_mode = rb_hash_aref(options, ID2SYM(rb_intern("round_mode"))); if (round_mode != Qnil) { ID round_mode_ID; UNumberFormatRoundingMode rounding_mode; Check_Type(round_mode, T_SYMBOL); round_mode_ID = SYM2ID(round_mode); if (round_mode_ID == rb_intern("ceil")) { rounding_mode = UNUM_ROUND_CEILING; } else if (round_mode_ID == rb_intern("floor")) { rounding_mode = UNUM_ROUND_FLOOR; } else if (round_mode_ID == rb_intern("down")) { rounding_mode = UNUM_ROUND_DOWN; } else if (round_mode_ID == rb_intern("up")) { rounding_mode = UNUM_ROUND_UP; } else if (round_mode_ID == rb_intern("halfeven")) { rounding_mode = UNUM_FOUND_HALFEVEN; } else if (round_mode_ID == rb_intern("halfdown")) { rounding_mode = UNUM_ROUND_HALFDOWN; } else if (round_mode_ID == rb_intern("halfup")) { rounding_mode = UNUM_ROUND_HALFUP; } else { rb_raise(rb_eArgError, "unsupported rounding mode '%s'", rb_id2name(round_mode_ID)); } status = U_ZERO_ERROR; unum_setAttribute(format, UNUM_ROUNDING_MODE, rounding_mode); RAISE_ON_ERROR(status); } round_increment = rb_hash_aref(options, ID2SYM(rb_intern("round_increment"))); if (round_increment != Qnil) { Check_Type(round_increment, T_FLOAT); status = U_ZERO_ERROR; unum_setDoubleAttribute(format, UNUM_ROUNDING_INCREMENT, NUM2DBL(round_increment)); RAISE_ON_ERROR(status); } } } /* format */ status = U_ZERO_ERROR; switch (TYPE(self)) { case T_FIXNUM: unum_format(format, NUM2INT(self), result, 256, NULL, &status); break; case T_FLOAT: unum_formatDouble(format, NUM2DBL(self), result, 256, NULL, &status); break; case T_BIGNUM: unum_formatInt64(format, rb_big2ll(self), result, 256, NULL, &status); break; } RAISE_ON_ERROR(status); /* free resources */ unum_close(format); return u_strToRString(result, -1); }