// Allow implicit conversion from char16_t* to UnicodeString for this file: // Helpful in toString methods and elsewhere. #define UNISTR_FROM_STRING_EXPLICIT
DecimalFormat::DecimalFormat(const UnicodeString& pattern, DecimalFormatSymbols* symbolsToAdopt,
UNumberFormatStyle style, UErrorCode& status)
: DecimalFormat(symbolsToAdopt, status) { if (U_FAILURE(status)) { return; } // If choice is a currency type, ignore the rounding information. if (style == UNumberFormatStyle::UNUM_CURRENCY ||
style == UNumberFormatStyle::UNUM_CURRENCY_ISO ||
style == UNumberFormatStyle::UNUM_CURRENCY_ACCOUNTING ||
style == UNumberFormatStyle::UNUM_CASH_CURRENCY ||
style == UNumberFormatStyle::UNUM_CURRENCY_STANDARD ||
style == UNumberFormatStyle::UNUM_CURRENCY_PLURAL) {
setPropertiesFromPattern(pattern, IGNORE_ROUNDING_ALWAYS, status);
} else {
setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status);
} // Note: in Java, CurrencyPluralInfo is set in NumberFormat.java, but in C++, it is not set there, // so we have to set it here. if (style == UNumberFormatStyle::UNUM_CURRENCY_PLURAL) {
LocalPointer<CurrencyPluralInfo> cpi( new CurrencyPluralInfo(fields->symbols->getLocale(), status),
status); if (U_FAILURE(status)) { return; }
fields->properties.currencyPluralInfo.fPtr.adoptInstead(cpi.orphan());
}
touch(status);
}
DecimalFormat::DecimalFormat(const DecimalFormatSymbols* symbolsToAdopt, UErrorCode& status) { // we must take ownership of symbolsToAdopt, even in a failure case.
LocalPointer<const DecimalFormatSymbols> adoptedSymbols(symbolsToAdopt); if (U_FAILURE(status)) { return;
}
fields = new DecimalFormatFields(); if (fields == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR; return;
} if (adoptedSymbols.isNull()) {
fields->symbols.adoptInsteadAndCheckErrorCode(new DecimalFormatSymbols(status), status);
} else {
fields->symbols.adoptInsteadAndCheckErrorCode(adoptedSymbols.orphan(), status);
} if (U_FAILURE(status)) { delete fields;
fields = nullptr;
}
}
if (fields == nullptr) { // We only get here if an OOM error happened during construction, copy construction, assignment, or modification.
status = U_MEMORY_ALLOCATION_ERROR; return *this;
}
switch (attr) { case UNUM_LENIENT_PARSE:
setLenient(newValue != 0); break;
case UNUM_PARSE_INT_ONLY:
setParseIntegerOnly(newValue != 0); break;
case UNUM_GROUPING_USED:
setGroupingUsed(newValue != 0); break;
case UNUM_DECIMAL_ALWAYS_SHOWN:
setDecimalSeparatorAlwaysShown(newValue != 0); break;
case UNUM_MAX_INTEGER_DIGITS:
setMaximumIntegerDigits(newValue); break;
case UNUM_MIN_INTEGER_DIGITS:
setMinimumIntegerDigits(newValue); break;
case UNUM_INTEGER_DIGITS:
setMinimumIntegerDigits(newValue);
setMaximumIntegerDigits(newValue); break;
case UNUM_MAX_FRACTION_DIGITS:
setMaximumFractionDigits(newValue); break;
case UNUM_MIN_FRACTION_DIGITS:
setMinimumFractionDigits(newValue); break;
case UNUM_FRACTION_DIGITS:
setMinimumFractionDigits(newValue);
setMaximumFractionDigits(newValue); break;
case UNUM_SIGNIFICANT_DIGITS_USED:
setSignificantDigitsUsed(newValue != 0); break;
case UNUM_MAX_SIGNIFICANT_DIGITS:
setMaximumSignificantDigits(newValue); break;
case UNUM_MIN_SIGNIFICANT_DIGITS:
setMinimumSignificantDigits(newValue); break;
case UNUM_MULTIPLIER:
setMultiplier(newValue); break;
case UNUM_SCALE:
setMultiplierScale(newValue); break;
case UNUM_GROUPING_SIZE:
setGroupingSize(newValue); break;
case UNUM_ROUNDING_MODE:
setRoundingMode(static_cast<DecimalFormat::ERoundingMode>(newValue)); break;
case UNUM_FORMAT_WIDTH:
setFormatWidth(newValue); break;
case UNUM_PADDING_POSITION: /** The position at which padding will take place. */
setPadPosition(static_cast<DecimalFormat::EPadPosition>(newValue)); break;
case UNUM_SECONDARY_GROUPING_SIZE:
setSecondaryGroupingSize(newValue); break;
#if UCONFIG_HAVE_PARSEALLINPUT case UNUM_PARSE_ALL_INPUT:
setParseAllInput(static_cast<UNumberFormatAttributeValue>(newValue)); break; #endif
case UNUM_PARSE_NO_EXPONENT:
setParseNoExponent(static_cast<UBool>(newValue)); break;
case UNUM_PARSE_DECIMAL_MARK_REQUIRED:
setDecimalPatternMatchRequired(static_cast<UBool>(newValue)); break;
case UNUM_CURRENCY_USAGE:
setCurrencyUsage(static_cast<UCurrencyUsage>(newValue), &status); break;
case UNUM_MINIMUM_GROUPING_DIGITS:
setMinimumGroupingDigits(newValue); break;
case UNUM_PARSE_CASE_SENSITIVE:
setParseCaseSensitive(static_cast<UBool>(newValue)); break;
case UNUM_SIGN_ALWAYS_SHOWN:
setSignAlwaysShown(static_cast<UBool>(newValue)); break;
case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS:
setFormatFailIfMoreThanMaxDigits(static_cast<UBool>(newValue)); break;
default:
status = U_UNSUPPORTED_ERROR; break;
} return *this;
}
if (fields == nullptr) { // We only get here if an OOM error happened during construction, copy construction, assignment, or modification.
status = U_MEMORY_ALLOCATION_ERROR; return -1;
}
switch (attr) { case UNUM_LENIENT_PARSE: return isLenient();
case UNUM_PARSE_INT_ONLY: return isParseIntegerOnly();
case UNUM_GROUPING_USED: return isGroupingUsed();
case UNUM_DECIMAL_ALWAYS_SHOWN: return isDecimalSeparatorAlwaysShown();
case UNUM_MAX_INTEGER_DIGITS: return getMaximumIntegerDigits();
case UNUM_MIN_INTEGER_DIGITS: return getMinimumIntegerDigits();
case UNUM_INTEGER_DIGITS: // TBD: what should this return? return getMinimumIntegerDigits();
case UNUM_MAX_FRACTION_DIGITS: return getMaximumFractionDigits();
case UNUM_MIN_FRACTION_DIGITS: return getMinimumFractionDigits();
case UNUM_FRACTION_DIGITS: // TBD: what should this return? return getMinimumFractionDigits();
case UNUM_SIGNIFICANT_DIGITS_USED: return areSignificantDigitsUsed();
case UNUM_MAX_SIGNIFICANT_DIGITS: return getMaximumSignificantDigits();
case UNUM_MIN_SIGNIFICANT_DIGITS: return getMinimumSignificantDigits();
case UNUM_MULTIPLIER: return getMultiplier();
case UNUM_SCALE: return getMultiplierScale();
case UNUM_GROUPING_SIZE: return getGroupingSize();
case UNUM_ROUNDING_MODE: return getRoundingMode();
case UNUM_FORMAT_WIDTH: return getFormatWidth();
case UNUM_PADDING_POSITION: return getPadPosition();
case UNUM_SECONDARY_GROUPING_SIZE: return getSecondaryGroupingSize();
case UNUM_PARSE_NO_EXPONENT: return isParseNoExponent();
case UNUM_PARSE_DECIMAL_MARK_REQUIRED: return isDecimalPatternMatchRequired();
case UNUM_CURRENCY_USAGE: return getCurrencyUsage();
case UNUM_MINIMUM_GROUPING_DIGITS: return getMinimumGroupingDigits();
case UNUM_PARSE_CASE_SENSITIVE: return isParseCaseSensitive();
case UNUM_SIGN_ALWAYS_SHOWN: return isSignAlwaysShown();
case UNUM_FORMAT_FAIL_IF_MORE_THAN_MAX_DIGITS: return isFormatFailIfMoreThanMaxDigits();
default:
status = U_UNSUPPORTED_ERROR; break;
}
return -1; /* undefined */
}
void DecimalFormat::setGroupingUsed(UBool enabled) { if (fields == nullptr) { return;
} if (UBOOL_TO_BOOL(enabled) == fields->properties.groupingUsed) { return; }
NumberFormat::setGroupingUsed(enabled); // to set field for compatibility
fields->properties.groupingUsed = enabled;
touchNoError();
}
void DecimalFormat::setParseIntegerOnly(UBool value) { if (fields == nullptr) { return;
} if (UBOOL_TO_BOOL(value) == fields->properties.parseIntegerOnly) { return; }
NumberFormat::setParseIntegerOnly(value); // to set field for compatibility
fields->properties.parseIntegerOnly = value;
touchNoError();
}
void DecimalFormat::setLenient(UBool enable) { if (fields == nullptr) { return;
}
ParseMode mode = enable ? PARSE_MODE_LENIENT : PARSE_MODE_STRICT; if (!fields->properties.parseMode.isNull() && mode == fields->properties.parseMode.getNoError()) { return; }
NumberFormat::setLenient(enable); // to set field for compatibility
fields->properties.parseMode = mode;
touchNoError();
}
DecimalFormat::DecimalFormat(const UnicodeString& pattern, DecimalFormatSymbols* symbolsToAdopt,
UParseError&, UErrorCode& status)
: DecimalFormat(symbolsToAdopt, status) { if (U_FAILURE(status)) { return; } // TODO: What is parseError for?
setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status);
touch(status);
}
DecimalFormat::DecimalFormat(const UnicodeString& pattern, const DecimalFormatSymbols& symbols,
UErrorCode& status)
: DecimalFormat(nullptr, status) { if (U_FAILURE(status)) { return; }
LocalPointer<DecimalFormatSymbols> dfs(new DecimalFormatSymbols(symbols), status); if (U_FAILURE(status)) { // If we failed to allocate DecimalFormatSymbols, then release fields and its members. // We must have a fully complete fields object, we cannot have partially populated members. delete fields;
fields = nullptr;
status = U_MEMORY_ALLOCATION_ERROR; return;
}
fields->symbols.adoptInstead(dfs.orphan());
setPropertiesFromPattern(pattern, IGNORE_ROUNDING_IF_CURRENCY, status);
touch(status);
}
DecimalFormat::DecimalFormat(const DecimalFormat& source) : NumberFormat(source) { // If the object that we are copying from is invalid, no point in going further. if (source.fields == nullptr) { return;
} // Note: it is not safe to copy fields->formatter or fWarehouse directly because fields->formatter might have // dangling pointers to fields inside fWarehouse. The safe thing is to re-construct fields->formatter from // the property bag, despite being somewhat slower.
fields = new DecimalFormatFields(source.fields->properties); if (fields == nullptr) { return; // no way to report an error.
}
UErrorCode status = U_ZERO_ERROR;
fields->symbols.adoptInsteadAndCheckErrorCode(new DecimalFormatSymbols(*source.getDecimalFormatSymbols()), status); // In order to simplify error handling logic in the various getters/setters/etc, we do not allow // any partially populated DecimalFormatFields object. We must have a fully complete fields object // or else we set it to nullptr. if (U_FAILURE(status)) { delete fields;
fields = nullptr; return;
}
touch(status);
}
DecimalFormat& DecimalFormat::operator=(const DecimalFormat& rhs) { // guard against self-assignment if (this == &rhs) { return *this;
} // Make sure both objects are valid. if (fields == nullptr || rhs.fields == nullptr) { return *this; // unfortunately, no way to report an error.
}
fields->properties = rhs.fields->properties;
fields->exportedProperties.clear();
UErrorCode status = U_ZERO_ERROR;
LocalPointer<DecimalFormatSymbols> dfs(new DecimalFormatSymbols(*rhs.getDecimalFormatSymbols()), status); if (U_FAILURE(status)) { // We failed to allocate DecimalFormatSymbols, release fields and its members. // We must have a fully complete fields object, we cannot have partially populated members. delete fields;
fields = nullptr; return *this;
}
fields->symbols.adoptInstead(dfs.orphan());
touch(status);
return *this;
}
DecimalFormat::~DecimalFormat() { if (fields == nullptr) { return; }
UnicodeString& DecimalFormat::format(int64_t number, UnicodeString& appendTo, FieldPosition& pos,
UErrorCode& status) const { if (U_FAILURE(status)) { return appendTo; // don't overwrite status if it's already a failure.
} if (fields == nullptr) { // We only get here if an OOM error happened during construction, copy construction, assignment, or modification.
status = U_MEMORY_ALLOCATION_ERROR;
appendTo.setToBogus(); return appendTo;
} if (pos.getField() == FieldPosition::DONT_CARE && fastFormatInt64(number, appendTo)) { return appendTo;
}
UFormattedNumberData output;
output.quantity.setToLong(number);
fields->formatter.formatImpl(&output, status);
fieldPositionHelper(output, pos, appendTo.length(), status); auto appendable = UnicodeStringAppendable(appendTo);
output.appendTo(appendable, status); return appendTo;
}
UnicodeString&
DecimalFormat::format(int64_t number, UnicodeString& appendTo, FieldPositionIterator* posIter,
UErrorCode& status) const { if (U_FAILURE(status)) { return appendTo; // don't overwrite status if it's already a failure.
} if (fields == nullptr) { // We only get here if an OOM error happened during construction, copy construction, assignment, or modification.
status = U_MEMORY_ALLOCATION_ERROR;
appendTo.setToBogus(); return appendTo;
} if (posIter == nullptr && fastFormatInt64(number, appendTo)) { return appendTo;
}
UFormattedNumberData output;
output.quantity.setToLong(number);
fields->formatter.formatImpl(&output, status);
fieldPositionIteratorHelper(output, posIter, appendTo.length(), status); auto appendable = UnicodeStringAppendable(appendTo);
output.appendTo(appendable, status); return appendTo;
}
UnicodeString&
DecimalFormat::format(StringPiece number, UnicodeString& appendTo, FieldPositionIterator* posIter,
UErrorCode& status) const { if (U_FAILURE(status)) { return appendTo; // don't overwrite status if it's already a failure.
} if (fields == nullptr) { // We only get here if an OOM error happened during construction, copy construction, assignment, or modification.
status = U_MEMORY_ALLOCATION_ERROR;
appendTo.setToBogus(); return appendTo;
}
UFormattedNumberData output;
output.quantity.setToDecNumber(number, status);
fields->formatter.formatImpl(&output, status);
fieldPositionIteratorHelper(output, posIter, appendTo.length(), status); auto appendable = UnicodeStringAppendable(appendTo);
output.appendTo(appendable, status); return appendTo;
}
UnicodeString& DecimalFormat::format(const DecimalQuantity& number, UnicodeString& appendTo,
FieldPositionIterator* posIter, UErrorCode& status) const { if (U_FAILURE(status)) { return appendTo; // don't overwrite status if it's already a failure.
} if (fields == nullptr) { // We only get here if an OOM error happened during construction, copy construction, assignment, or modification.
status = U_MEMORY_ALLOCATION_ERROR;
appendTo.setToBogus(); return appendTo;
}
UFormattedNumberData output;
output.quantity = number;
fields->formatter.formatImpl(&output, status);
fieldPositionIteratorHelper(output, posIter, appendTo.length(), status); auto appendable = UnicodeStringAppendable(appendTo);
output.appendTo(appendable, status); return appendTo;
}
UnicodeString&
DecimalFormat::format(const DecimalQuantity& number, UnicodeString& appendTo, FieldPosition& pos,
UErrorCode& status) const { if (U_FAILURE(status)) { return appendTo; // don't overwrite status if it's already a failure.
} if (fields == nullptr) { // We only get here if an OOM error happened during construction, copy construction, assignment, or modification.
status = U_MEMORY_ALLOCATION_ERROR;
appendTo.setToBogus(); return appendTo;
}
UFormattedNumberData output;
output.quantity = number;
fields->formatter.formatImpl(&output, status);
fieldPositionHelper(output, pos, appendTo.length(), status); auto appendable = UnicodeStringAppendable(appendTo);
output.appendTo(appendable, status); return appendTo;
}
void DecimalFormat::parse(const UnicodeString& text, Formattable& output,
ParsePosition& parsePosition) const { if (fields == nullptr) { return;
} if (parsePosition.getIndex() < 0 || parsePosition.getIndex() >= text.length()) { if (parsePosition.getIndex() == text.length()) { // If there is nothing to parse, it is an error
parsePosition.setErrorIndex(parsePosition.getIndex());
} return;
}
ErrorCode status;
ParsedNumber result; // Note: if this is a currency instance, currencies will be matched despite the fact that we are not in the // parseCurrency method (backwards compatibility)
int32_t startIndex = parsePosition.getIndex(); const NumberParserImpl* parser = getParser(status); if (U_FAILURE(status)) { return; // unfortunately no way to report back the error.
}
parser->parse(text, startIndex, true, result, status); if (U_FAILURE(status)) { return; // unfortunately no way to report back the error.
} // TODO: Do we need to check for fImpl->properties->parseAllInput (UCONFIG_HAVE_PARSEALLINPUT) here? if (result.success()) {
parsePosition.setIndex(result.charEnd);
result.populateFormattable(output, parser->getParseFlags());
} else {
parsePosition.setErrorIndex(startIndex + result.charEnd);
}
}
ErrorCode status;
ParsedNumber result; // Note: if this is a currency instance, currencies will be matched despite the fact that we are not in the // parseCurrency method (backwards compatibility)
int32_t startIndex = parsePosition.getIndex(); const NumberParserImpl* parser = getCurrencyParser(status); if (U_FAILURE(status)) { return nullptr;
}
parser->parse(text, startIndex, true, result, status); if (U_FAILURE(status)) { return nullptr;
} // TODO: Do we need to check for fImpl->properties->parseAllInput (UCONFIG_HAVE_PARSEALLINPUT) here? if (result.success()) {
parsePosition.setIndex(result.charEnd);
Formattable formattable;
result.populateFormattable(formattable, parser->getParseFlags());
LocalPointer<CurrencyAmount> currencyAmount( new CurrencyAmount(formattable, result.currencyCode, status), status); if (U_FAILURE(status)) { return nullptr;
} return currencyAmount.orphan();
} else {
parsePosition.setErrorIndex(startIndex + result.charEnd); return nullptr;
}
}
void DecimalFormat::adoptDecimalFormatSymbols(DecimalFormatSymbols* symbolsToAdopt) { if (symbolsToAdopt == nullptr) { return; // do not allow caller to set fields->symbols to nullptr
} // we must take ownership of symbolsToAdopt, even in a failure case.
LocalPointer<DecimalFormatSymbols> dfs(symbolsToAdopt); if (fields == nullptr) { return;
}
fields->symbols.adoptInstead(dfs.orphan());
touchNoError();
}
void DecimalFormat::setDecimalFormatSymbols(const DecimalFormatSymbols& symbols) { if (fields == nullptr) { return;
}
UErrorCode status = U_ZERO_ERROR;
LocalPointer<DecimalFormatSymbols> dfs(new DecimalFormatSymbols(symbols), status); if (U_FAILURE(status)) { // We failed to allocate DecimalFormatSymbols, release fields and its members. // We must have a fully complete fields object, we cannot have partially populated members. delete fields;
fields = nullptr; return;
}
fields->symbols.adoptInstead(dfs.orphan());
touchNoError();
}
void DecimalFormat::adoptCurrencyPluralInfo(CurrencyPluralInfo* toAdopt) { // TODO: should we guard against nullptr input, like in adoptDecimalFormatSymbols? // we must take ownership of toAdopt, even in a failure case.
LocalPointer<CurrencyPluralInfo> cpi(toAdopt); if (fields == nullptr) { return;
}
fields->properties.currencyPluralInfo.fPtr.adoptInstead(cpi.orphan());
touchNoError();
}
void DecimalFormat::setCurrencyPluralInfo(const CurrencyPluralInfo& info) { if (fields == nullptr) { return;
} if (fields->properties.currencyPluralInfo.fPtr.isNull()) { // Note: clone() can fail with OOM error, but we have no way to report it. :(
fields->properties.currencyPluralInfo.fPtr.adoptInstead(info.clone());
} else {
*fields->properties.currencyPluralInfo.fPtr = info; // copy-assignment operator
}
touchNoError();
}
UnicodeString& DecimalFormat::getPositivePrefix(UnicodeString& result) const { if (fields == nullptr) {
result.setToBogus(); return result;
}
UErrorCode status = U_ZERO_ERROR;
fields->formatter.getAffixImpl(true, false, result, status); if (U_FAILURE(status)) { result.setToBogus(); } return result;
}
UBool DecimalFormat::isSignAlwaysShown() const { // Not much we can do to report an error. if (fields == nullptr) { return DecimalFormatProperties::getDefault().signAlwaysShown;
} return fields->properties.signAlwaysShown;
}
int32_t DecimalFormat::getMultiplier() const { const DecimalFormatProperties *dfp; // Not much we can do to report an error. if (fields == nullptr) { // Fallback to using the default instance of DecimalFormatProperties.
dfp = &(DecimalFormatProperties::getDefault());
} else {
dfp = &fields->properties;
} if (dfp->multiplier != 1) { return dfp->multiplier;
} elseif (dfp->magnitudeMultiplier != 0) { returnstatic_cast<int32_t>(uprv_pow10(dfp->magnitudeMultiplier));
} else { return 1;
}
}
void DecimalFormat::setMultiplier(int32_t multiplier) { if (fields == nullptr) { return;
} if (multiplier == 0) {
multiplier = 1; // one being the benign default value for a multiplier.
}
// Try to convert to a magnitude multiplier first int delta = 0; int value = multiplier; while (value != 1) {
delta++; int temp = value / 10; if (temp * 10 != value) {
delta = -1; break;
}
value = temp;
} if (delta != -1) {
fields->properties.magnitudeMultiplier = delta;
fields->properties.multiplier = 1;
} else {
fields->properties.magnitudeMultiplier = 0;
fields->properties.multiplier = multiplier;
}
touchNoError();
}
int32_t DecimalFormat::getMultiplierScale() const { // Not much we can do to report an error. if (fields == nullptr) { // Fallback to using the default instance of DecimalFormatProperties. return DecimalFormatProperties::getDefault().multiplierScale;
} return fields->properties.multiplierScale;
}
double DecimalFormat::getRoundingIncrement() const { // Not much we can do to report an error. if (fields == nullptr) { // Fallback to using the default instance of DecimalFormatProperties. return DecimalFormatProperties::getDefault().roundingIncrement;
} return fields->exportedProperties.roundingIncrement;
}
ERoundingMode DecimalFormat::getRoundingMode() const { // Not much we can do to report an error. if (fields == nullptr) { // Fallback to using the default instance of DecimalFormatProperties. returnstatic_cast<ERoundingMode>(DecimalFormatProperties::getDefault().roundingMode.getNoError());
} // UNumberFormatRoundingMode and ERoundingMode have the same values. returnstatic_cast<ERoundingMode>(fields->exportedProperties.roundingMode.getNoError());
}
void DecimalFormat::setRoundingMode(ERoundingMode roundingMode) UPRV_NO_SANITIZE_UNDEFINED { if (fields == nullptr) { return; } auto uRoundingMode = static_cast<UNumberFormatRoundingMode>(roundingMode); if (!fields->properties.roundingMode.isNull() && uRoundingMode == fields->properties.roundingMode.getNoError()) { return;
}
NumberFormat::setMaximumIntegerDigits(roundingMode); // to set field for compatibility
fields->properties.roundingMode = uRoundingMode;
touchNoError();
}
int32_t DecimalFormat::getFormatWidth() const { // Not much we can do to report an error. if (fields == nullptr) { // Fallback to using the default instance of DecimalFormatProperties. return DecimalFormatProperties::getDefault().formatWidth;
} return fields->properties.formatWidth;
}
EPadPosition DecimalFormat::getPadPosition() const { if (fields == nullptr || fields->properties.padPosition.isNull()) { return EPadPosition::kPadBeforePrefix;
} else { // UNumberFormatPadPosition and EPadPosition have the same values. returnstatic_cast<EPadPosition>(fields->properties.padPosition.getNoError());
}
}
void DecimalFormat::setPadPosition(EPadPosition padPos) { if (fields == nullptr) { return; } auto uPadPos = static_cast<UNumberFormatPadPosition>(padPos); if (!fields->properties.padPosition.isNull() && uPadPos == fields->properties.padPosition.getNoError()) { return;
}
fields->properties.padPosition = uPadPos;
touchNoError();
}
UBool DecimalFormat::isScientificNotation() const { // Not much we can do to report an error. if (fields == nullptr) { // Fallback to using the default instance of DecimalFormatProperties. return (DecimalFormatProperties::getDefault().minimumExponentDigits != -1);
} return (fields->properties.minimumExponentDigits != -1);
}
int8_t DecimalFormat::getMinimumExponentDigits() const { // Not much we can do to report an error. if (fields == nullptr) { // Fallback to using the default instance of DecimalFormatProperties. returnstatic_cast<int8_t>(DecimalFormatProperties::getDefault().minimumExponentDigits);
} returnstatic_cast<int8_t>(fields->properties.minimumExponentDigits);
}
UBool DecimalFormat::isExponentSignAlwaysShown() const { // Not much we can do to report an error. if (fields == nullptr) { // Fallback to using the default instance of DecimalFormatProperties. return DecimalFormatProperties::getDefault().exponentSignAlwaysShown;
} return fields->properties.exponentSignAlwaysShown;
}
int32_t DecimalFormat::getGroupingSize() const {
int32_t groupingSize; // Not much we can do to report an error. if (fields == nullptr) { // Fallback to using the default instance of DecimalFormatProperties.
groupingSize = DecimalFormatProperties::getDefault().groupingSize;
} else {
groupingSize = fields->properties.groupingSize;
} if (groupingSize < 0) { return 0;
} return groupingSize;
}
int32_t DecimalFormat::getSecondaryGroupingSize() const {
int32_t grouping2; // Not much we can do to report an error. if (fields == nullptr) { // Fallback to using the default instance of DecimalFormatProperties.
grouping2 = DecimalFormatProperties::getDefault().secondaryGroupingSize;
} else {
grouping2 = fields->properties.secondaryGroupingSize;
} if (grouping2 < 0) { return 0;
} return grouping2;
}
int32_t DecimalFormat::getMinimumGroupingDigits() const { // Not much we can do to report an error. if (fields == nullptr) { // Fallback to using the default instance of DecimalFormatProperties. return DecimalFormatProperties::getDefault().minimumGroupingDigits;
} return fields->properties.minimumGroupingDigits;
}
UBool DecimalFormat::isDecimalSeparatorAlwaysShown() const { // Not much we can do to report an error. if (fields == nullptr) { // Fallback to using the default instance of DecimalFormatProperties. return DecimalFormatProperties::getDefault().decimalSeparatorAlwaysShown;
} return fields->properties.decimalSeparatorAlwaysShown;
}
UBool DecimalFormat::isDecimalPatternMatchRequired() const { // Not much we can do to report an error. if (fields == nullptr) { // Fallback to using the default instance of DecimalFormatProperties. return DecimalFormatProperties::getDefault().decimalPatternMatchRequired;
} return fields->properties.decimalPatternMatchRequired;
}
UBool DecimalFormat::isParseNoExponent() const { // Not much we can do to report an error. if (fields == nullptr) { // Fallback to using the default instance of DecimalFormatProperties. return DecimalFormatProperties::getDefault().parseNoExponent;
} return fields->properties.parseNoExponent;
}
UBool DecimalFormat::isParseCaseSensitive() const { // Not much we can do to report an error. if (fields == nullptr) { // Fallback to using the default instance of DecimalFormatProperties. return DecimalFormatProperties::getDefault().parseCaseSensitive;
} return fields->properties.parseCaseSensitive;
}
UBool DecimalFormat::isFormatFailIfMoreThanMaxDigits() const { // Not much we can do to report an error. if (fields == nullptr) { // Fallback to using the default instance of DecimalFormatProperties. return DecimalFormatProperties::getDefault().formatFailIfMoreThanMaxDigits;
} return fields->properties.formatFailIfMoreThanMaxDigits;
}
UnicodeString& DecimalFormat::toPattern(UnicodeString& result) const { if (fields == nullptr) { // We only get here if an OOM error happened during construction, copy construction, assignment, or modification.
result.setToBogus(); return result;
} // Pull some properties from exportedProperties and others from properties // to keep affix patterns intact. In particular, pull rounding properties // so that CurrencyUsage is reflected properly. // TODO: Consider putting this logic in number_patternstring.cpp instead.
ErrorCode localStatus;
DecimalFormatProperties tprops(fields->properties); bool useCurrency = (
!tprops.currency.isNull() ||
!tprops.currencyPluralInfo.fPtr.isNull() ||
!tprops.currencyUsage.isNull() ||
tprops.currencyAsDecimal ||
AffixUtils::hasCurrencySymbols(tprops.positivePrefixPattern, localStatus) ||
AffixUtils::hasCurrencySymbols(tprops.positiveSuffixPattern, localStatus) ||
AffixUtils::hasCurrencySymbols(tprops.negativePrefixPattern, localStatus) ||
AffixUtils::hasCurrencySymbols(tprops.negativeSuffixPattern, localStatus)); if (useCurrency) {
tprops.minimumFractionDigits = fields->exportedProperties.minimumFractionDigits;
tprops.maximumFractionDigits = fields->exportedProperties.maximumFractionDigits;
tprops.roundingIncrement = fields->exportedProperties.roundingIncrement;
}
result = PatternStringUtils::propertiesToPatternString(tprops, localStatus); return result;
}
UnicodeString& DecimalFormat::toLocalizedPattern(UnicodeString& result) const { if (fields == nullptr) { // We only get here if an OOM error happened during construction, copy construction, assignment, or modification.
result.setToBogus(); return result;
}
ErrorCode localStatus;
result = toPattern(result);
result = PatternStringUtils::convertLocalized(result, *getDecimalFormatSymbols(), true, localStatus); return result;
}
void DecimalFormat::applyPattern(const UnicodeString& pattern, UParseError&, UErrorCode& status) { // TODO: What is parseError for?
applyPattern(pattern, status);
}
void DecimalFormat::applyPattern(const UnicodeString& pattern, UErrorCode& status) { // don't overwrite status if it's already a failure. if (U_FAILURE(status)) { return; } if (fields == nullptr) { // We only get here if an OOM error happened during construction, copy construction, assignment, or modification.
status = U_MEMORY_ALLOCATION_ERROR; return;
}
setPropertiesFromPattern(pattern, IGNORE_ROUNDING_NEVER, status);
touch(status);
}
void DecimalFormat::applyLocalizedPattern(const UnicodeString& localizedPattern, UParseError&,
UErrorCode& status) { // TODO: What is parseError for?
applyLocalizedPattern(localizedPattern, status);
}
void DecimalFormat::applyLocalizedPattern(const UnicodeString& localizedPattern, UErrorCode& status) { // don't overwrite status if it's already a failure. if (U_FAILURE(status)) { return; } if (fields == nullptr) { // We only get here if an OOM error happened during construction, copy construction, assignment, or modification.
status = U_MEMORY_ALLOCATION_ERROR; return;
}
UnicodeString pattern = PatternStringUtils::convertLocalized(
localizedPattern, *getDecimalFormatSymbols(), false, status);
applyPattern(pattern, status);
}
void DecimalFormat::setMaximumIntegerDigits(int32_t newValue) { if (fields == nullptr) { return; } if (newValue == fields->properties.maximumIntegerDigits) { return; } // For backwards compatibility, conflicting min/max need to keep the most recent setting.
int32_t min = fields->properties.minimumIntegerDigits; if (min >= 0 && min > newValue) {
fields->properties.minimumIntegerDigits = newValue;
}
fields->properties.maximumIntegerDigits = newValue;
touchNoError();
}
void DecimalFormat::setMinimumIntegerDigits(int32_t newValue) { if (fields == nullptr) { return; } if (newValue == fields->properties.minimumIntegerDigits) { return; } // For backwards compatibility, conflicting min/max need to keep the most recent setting.
int32_t max = fields->properties.maximumIntegerDigits; if (max >= 0 && max < newValue) {
fields->properties.maximumIntegerDigits = newValue;
}
fields->properties.minimumIntegerDigits = newValue;
touchNoError();
}
void DecimalFormat::setMaximumFractionDigits(int32_t newValue) { if (fields == nullptr) { return; } if (newValue == fields->properties.maximumFractionDigits) { return; } // cap for backward compatibility, formerly 340, now 999 if (newValue > kMaxIntFracSig) {
newValue = kMaxIntFracSig;
} // For backwards compatibility, conflicting min/max need to keep the most recent setting.
int32_t min = fields->properties.minimumFractionDigits; if (min >= 0 && min > newValue) {
fields->properties.minimumFractionDigits = newValue;
}
fields->properties.maximumFractionDigits = newValue;
touchNoError();
}
void DecimalFormat::setMinimumFractionDigits(int32_t newValue) { if (fields == nullptr) { return; } if (newValue == fields->properties.minimumFractionDigits) { return; } // For backwards compatibility, conflicting min/max need to keep the most recent setting.
int32_t max = fields->properties.maximumFractionDigits; if (max >= 0 && max < newValue) {
fields->properties.maximumFractionDigits = newValue;
}
fields->properties.minimumFractionDigits = newValue;
touchNoError();
}
int32_t DecimalFormat::getMinimumSignificantDigits() const { // Not much we can do to report an error. if (fields == nullptr) { // Fallback to using the default instance of DecimalFormatProperties. return DecimalFormatProperties::getDefault().minimumSignificantDigits;
} return fields->exportedProperties.minimumSignificantDigits;
}
int32_t DecimalFormat::getMaximumSignificantDigits() const { // Not much we can do to report an error. if (fields == nullptr) { // Fallback to using the default instance of DecimalFormatProperties. return DecimalFormatProperties::getDefault().maximumSignificantDigits;
} return fields->exportedProperties.maximumSignificantDigits;
}
void DecimalFormat::setMinimumSignificantDigits(int32_t value) { if (fields == nullptr) { return; } if (value == fields->properties.minimumSignificantDigits) { return; }
int32_t max = fields->properties.maximumSignificantDigits; if (max >= 0 && max < value) {
fields->properties.maximumSignificantDigits = value;
}
fields->properties.minimumSignificantDigits = value;
touchNoError();
}
void DecimalFormat::setMaximumSignificantDigits(int32_t value) { if (fields == nullptr) { return; } if (value == fields->properties.maximumSignificantDigits) { return; }
int32_t min = fields->properties.minimumSignificantDigits; if (min >= 0 && min > value) {
fields->properties.minimumSignificantDigits = value;
}
fields->properties.maximumSignificantDigits = value;
touchNoError();
}
UBool DecimalFormat::areSignificantDigitsUsed() const { const DecimalFormatProperties* dfp; // Not much we can do to report an error. if (fields == nullptr) { // Fallback to using the default instance of DecimalFormatProperties.
dfp = &(DecimalFormatProperties::getDefault());
} else {
dfp = &fields->properties;
} return dfp->minimumSignificantDigits != -1 || dfp->maximumSignificantDigits != -1;
}
// These are the default values from the old implementation. if (useSignificantDigits) { if (fields->properties.minimumSignificantDigits != -1 ||
fields->properties.maximumSignificantDigits != -1) { return;
}
} else { if (fields->properties.minimumSignificantDigits == -1 &&
fields->properties.maximumSignificantDigits == -1) { return;
}
}
int32_t minSig = useSignificantDigits ? 1 : -1;
int32_t maxSig = useSignificantDigits ? 6 : -1;
fields->properties.minimumSignificantDigits = minSig;
fields->properties.maximumSignificantDigits = maxSig;
touchNoError();
}
void DecimalFormat::setCurrency(const char16_t* theCurrency, UErrorCode& ec) { // don't overwrite ec if it's already a failure. if (U_FAILURE(ec)) { return; } if (fields == nullptr) { // We only get here if an OOM error happened during construction, copy construction, assignment, or modification.
ec = U_MEMORY_ALLOCATION_ERROR; return;
}
CurrencyUnit currencyUnit(theCurrency, ec); if (U_FAILURE(ec)) { return; } if (!fields->properties.currency.isNull() && fields->properties.currency.getNoError() == currencyUnit) { return;
}
NumberFormat::setCurrency(theCurrency, ec); // to set field for compatibility
fields->properties.currency = currencyUnit; // In Java, the DecimalFormatSymbols is mutable. Why not in C++?
LocalPointer<DecimalFormatSymbols> newSymbols(new DecimalFormatSymbols(*getDecimalFormatSymbols()), ec);
newSymbols->setCurrency(currencyUnit.getISOCurrency(), ec);
fields->symbols.adoptInsteadAndCheckErrorCode(newSymbols.orphan(), ec);
touch(ec);
}
void DecimalFormat::setCurrencyUsage(UCurrencyUsage newUsage, UErrorCode* ec) { // don't overwrite ec if it's already a failure. if (U_FAILURE(*ec)) { return; } if (fields == nullptr) { // We only get here if an OOM error happened during construction, copy construction, assignment, or modification.
*ec = U_MEMORY_ALLOCATION_ERROR; return;
} if (!fields->properties.currencyUsage.isNull() && newUsage == fields->properties.currencyUsage.getNoError()) { return;
}
fields->properties.currencyUsage = newUsage;
touch(*ec);
}
UCurrencyUsage DecimalFormat::getCurrencyUsage() const { // CurrencyUsage is not exported, so we have to get it from the input property bag. // TODO: Should we export CurrencyUsage instead? if (fields == nullptr || fields->properties.currencyUsage.isNull()) { return UCURR_USAGE_STANDARD;
} return fields->properties.currencyUsage.getNoError();
}
void
DecimalFormat::formatToDecimalQuantity(double number, DecimalQuantity& output, UErrorCode& status) const { // don't overwrite status if it's already a failure. if (U_FAILURE(status)) { return; } if (fields == nullptr) { // We only get here if an OOM error happened during construction, copy construction, assignment, or modification.
status = U_MEMORY_ALLOCATION_ERROR; return;
}
fields->formatter.formatDouble(number, status).getDecimalQuantity(output, status);
}
void DecimalFormat::formatToDecimalQuantity(const Formattable& number, DecimalQuantity& output,
UErrorCode& status) const { // don't overwrite status if it's already a failure. if (U_FAILURE(status)) { return; } if (fields == nullptr) { // We only get here if an OOM error happened during construction, copy construction, assignment, or modification.
status = U_MEMORY_ALLOCATION_ERROR; return;
}
UFormattedNumberData obj;
number.populateDecimalQuantity(obj.quantity, status);
fields->formatter.formatImpl(&obj, status);
output = std::move(obj.quantity);
}
const number::LocalizedNumberFormatter* DecimalFormat::toNumberFormatter(UErrorCode& status) const { // We sometimes need to return nullptr here (see ICU-20380) if (U_FAILURE(status)) { return nullptr; } if (fields == nullptr) { // We only get here if an OOM error happened during construction, copy construction, assignment, or modification.
status = U_MEMORY_ALLOCATION_ERROR; return nullptr;
} return &fields->formatter;
}
/** Rebuilds the formatter object from the property bag. */ void DecimalFormat::touch(UErrorCode& status) { if (U_FAILURE(status)) { return;
} if (fields == nullptr) { // We only get here if an OOM error happened during construction, copy construction, assignment, or modification. // For regular construction, the caller should have checked the status variable for errors. // For copy construction, there is unfortunately nothing to report the error, so we need to guard against // this possible bad state here and set the status to an error.
status = U_MEMORY_ALLOCATION_ERROR; return;
}
// In C++, fields->symbols (or, if it's null, the DecimalFormatSymbols owned by the underlying LocalizedNumberFormatter) // is the source of truth for the locale. const DecimalFormatSymbols* symbols = getDecimalFormatSymbols();
Locale locale = symbols->getLocale();
// Note: The formatter is relatively cheap to create, and we need it to populate fields->exportedProperties, // so automatically recompute it here. The parser is a bit more expensive and is not needed until the // parse method is called, so defer that until needed. // TODO: Only update the pieces that changed instead of re-computing the whole formatter?
// Since memory has already been allocated for the formatter, we can move assign a stack-allocated object // and don't need to call new. (Which is slower and could possibly fail). // [Note that "symbols" above might point to the DecimalFormatSymbols object owned by fields->formatter. // That's okay, because NumberPropertyMapper::create() will clone it before fields->formatter's assignment // operator deletes it. But it does mean that "symbols" can't be counted on to be good after this line.]
fields->formatter = NumberPropertyMapper::create(
fields->properties, *symbols, fields->warehouse, fields->exportedProperties, status
).locale(locale);
fields->symbols.adoptInstead(nullptr); // the fields->symbols property is only temporary, until we can copy it into a new LocalizedNumberFormatter
// Do this after fields->exportedProperties are set up
setupFastFormat();
// Delete the parsers if they were made previously #ifndef __wasi__ delete fields->atomicParser.exchange(nullptr); delete fields->atomicCurrencyParser.exchange(nullptr); #else delete fields->atomicParser; delete fields->atomicCurrencyParser; #endif
// In order for the getters to work, we need to populate some fields in NumberFormat.
NumberFormat::setCurrency(fields->exportedProperties.currency.get(status).getISOCurrency(), status);
NumberFormat::setMaximumIntegerDigits(fields->exportedProperties.maximumIntegerDigits);
NumberFormat::setMinimumIntegerDigits(fields->exportedProperties.minimumIntegerDigits);
NumberFormat::setMaximumFractionDigits(fields->exportedProperties.maximumFractionDigits);
NumberFormat::setMinimumFractionDigits(fields->exportedProperties.minimumFractionDigits); // fImpl->properties, not fields->exportedProperties, since this information comes from the pattern:
NumberFormat::setGroupingUsed(fields->properties.groupingUsed);
}
void DecimalFormat::setPropertiesFromPattern(const UnicodeString& pattern, int32_t ignoreRounding,
UErrorCode& status) { if (U_SUCCESS(status)) { // Cast workaround to get around putting the enum in the public header file auto actualIgnoreRounding = static_cast<IgnoreRounding>(ignoreRounding);
PatternParser::parseToExistingProperties(pattern, fields->properties, actualIgnoreRounding, status);
}
}
const numparse::impl::NumberParserImpl* DecimalFormat::getParser(UErrorCode& status) const { // TODO: Move this into umutex.h? (similar logic also in numrange_fluent.cpp) // See ICU-20146
if (U_FAILURE(status)) { return nullptr;
}
// First try to get the pre-computed parser #ifndef __wasi__ auto* ptr = fields->atomicParser.load(); #else auto* ptr = fields->atomicParser; #endif if (ptr != nullptr) { return ptr;
}
// Try computing the parser on our own auto* temp = NumberParserImpl::createParserFromProperties(fields->properties, *getDecimalFormatSymbols(), false, status); if (U_FAILURE(status)) { return nullptr;
} if (temp == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR; return nullptr;
}
// Note: ptr starts as nullptr; during compare_exchange, // it is set to what is actually stored in the atomic // if another thread beat us to computing the parser object. auto* nonConstThis = const_cast<DecimalFormat*>(this); #ifndef __wasi__ if (!nonConstThis->fields->atomicParser.compare_exchange_strong(ptr, temp)) { // Another thread beat us to computing the parser delete temp; return ptr;
} else { // Our copy of the parser got stored in the atomic return temp;
} #else
nonConstThis->fields->atomicParser = temp; return temp; #endif
}
// First try to get the pre-computed parser #ifndef __wasi__ auto* ptr = fields->atomicCurrencyParser.load(); #else auto* ptr = fields->atomicCurrencyParser; #endif if (ptr != nullptr) { return ptr;
}
// Try computing the parser on our own auto* temp = NumberParserImpl::createParserFromProperties(fields->properties, *getDecimalFormatSymbols(), true, status); if (temp == nullptr) {
status = U_MEMORY_ALLOCATION_ERROR; // although we may still dereference, call sites should be guarded
}
// Note: ptr starts as nullptr; during compare_exchange, it is set to what is actually stored in the // atomic if another thread beat us to computing the parser object. auto* nonConstThis = const_cast<DecimalFormat*>(this); #ifndef __wasi__ if (!nonConstThis->fields->atomicCurrencyParser.compare_exchange_strong(ptr, temp)) { // Another thread beat us to computing the parser delete temp; return ptr;
} else { // Our copy of the parser got stored in the atomic return temp;
} #else
nonConstThis->fields->atomicCurrencyParser = temp; return temp; #endif
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.