ICU-13634 Refactoring affix-getting code to avoid the need to save the micro-props. Other assorted minor changes.

X-SVN-Rev: 41216
This commit is contained in:
Shane Carr 2018-04-11 11:10:52 +00:00
parent cd92fa2c88
commit 2c6bf0d77e
14 changed files with 129 additions and 112 deletions

View file

@ -564,7 +564,7 @@ void DecimalFormat::setCurrencyPluralInfo(const CurrencyPluralInfo& info) {
UnicodeString& DecimalFormat::getPositivePrefix(UnicodeString& result) const {
ErrorCode localStatus;
result = fFormatter->formatInt(1, localStatus).getPrefix(localStatus);
fFormatter->getAffix(true, false, result, localStatus);
return result;
}
@ -575,7 +575,7 @@ void DecimalFormat::setPositivePrefix(const UnicodeString& newValue) {
UnicodeString& DecimalFormat::getNegativePrefix(UnicodeString& result) const {
ErrorCode localStatus;
result = fFormatter->formatInt(-1, localStatus).getPrefix(localStatus);
fFormatter->getAffix(true, true, result, localStatus);
return result;
}
@ -586,7 +586,7 @@ void DecimalFormat::setNegativePrefix(const UnicodeString& newValue) {
UnicodeString& DecimalFormat::getPositiveSuffix(UnicodeString& result) const {
ErrorCode localStatus;
result = fFormatter->formatInt(1, localStatus).getSuffix(localStatus);
fFormatter->getAffix(false, false, result, localStatus);
return result;
}
@ -597,7 +597,7 @@ void DecimalFormat::setPositiveSuffix(const UnicodeString& newValue) {
UnicodeString& DecimalFormat::getNegativeSuffix(UnicodeString& result) const {
ErrorCode localStatus;
result = fFormatter->formatInt(-1, localStatus).getSuffix(localStatus);
fFormatter->getAffix(false, true, result, localStatus);
return result;
}
@ -682,7 +682,12 @@ void DecimalFormat::setFormatWidth(int32_t width) {
}
UnicodeString DecimalFormat::getPadCharacterString() const {
return fProperties->padString;
if (fProperties->padString.isBogus()) {
// Readonly-alias the static string kFallbackPaddingString
return {TRUE, kFallbackPaddingString, -1};
} else {
return fProperties->padString;
}
}
void DecimalFormat::setPadCharacter(const UnicodeString& padChar) {

View file

@ -748,7 +748,9 @@ CharString *Formattable::internalGetCharString(UErrorCode &status) {
// Older ICUs called uprv_decNumberToString here, which is not exactly the same as
// DecimalQuantity::toScientificString(). The biggest difference is that uprv_decNumberToString does
// not print scientific notation for magnitudes greater than -5 and smaller than some amount (+5?).
if (std::abs(fDecimalQuantity->getMagnitude()) < 5) {
if (fDecimalQuantity->isZero()) {
fDecimalStr->append("0", -1, status);
} else if (std::abs(fDecimalQuantity->getMagnitude()) < 5) {
fDecimalStr->appendInvariantChars(fDecimalQuantity->toPlainString(), status);
} else {
fDecimalStr->appendInvariantChars(fDecimalQuantity->toScientificString(), status);

View file

@ -677,6 +677,24 @@ void LocalizedNumberFormatter::formatImpl(impl::UFormattedNumberData* results, U
}
}
void LocalizedNumberFormatter::getAffix(bool isPrefix, bool isNegative, UnicodeString& result,
UErrorCode& status) const {
NumberStringBuilder nsb;
DecimalQuantity dq;
if (isNegative) {
dq.setToInt(-1);
} else {
dq.setToInt(1);
}
int prefixLength = NumberFormatterImpl::getPrefixSuffix(fMacros, dq, nsb, status);
result.remove();
if (isPrefix) {
result.append(nsb.toTempUnicodeString().tempSubStringBetween(0, prefixLength));
} else {
result.append(nsb.toTempUnicodeString().tempSubStringBetween(prefixLength, nsb.length()));
}
}
const impl::NumberFormatterImpl* LocalizedNumberFormatter::getCompiled() const {
return fCompiled;
}
@ -731,24 +749,6 @@ void FormattedNumber::getDecimalQuantity(DecimalQuantity& output, UErrorCode& st
output = fResults->quantity;
}
const UnicodeString FormattedNumber::getPrefix(UErrorCode& status) const {
if (fResults == nullptr) {
status = fErrorCode;
return {};
}
// FIXME
return {};
}
const UnicodeString FormattedNumber::getSuffix(UErrorCode& status) const {
if (fResults == nullptr) {
status = fErrorCode;
return {};
}
// FIXME
return {};
}
FormattedNumber::~FormattedNumber() {
delete fResults;
}

View file

@ -137,6 +137,12 @@ void NumberFormatterImpl::applyStatic(const MacroProps& macros, DecimalQuantity&
impl.applyUnsafe(inValue, outString, status);
}
int32_t NumberFormatterImpl::getPrefixSuffix(const MacroProps& macros, DecimalQuantity& inValue,
NumberStringBuilder& outString, UErrorCode& status) {
NumberFormatterImpl impl(macros, false, status);
return impl.getPrefixSuffixUnsafe(inValue, outString, status);
}
// NOTE: C++ SPECIFIC DIFFERENCE FROM JAVA:
// The "safe" apply method uses a new MicroProps. In the MicroPropsGenerator, fMicros is copied into the new instance.
// The "unsafe" method simply re-uses fMicros, eliminating the extra copy operation.
@ -159,6 +165,18 @@ void NumberFormatterImpl::applyUnsafe(DecimalQuantity& inValue, NumberStringBuil
microsToString(fMicros, inValue, outString, status);
}
int32_t
NumberFormatterImpl::getPrefixSuffixUnsafe(DecimalQuantity& inValue, NumberStringBuilder& outString,
UErrorCode& status) {
if (U_FAILURE(status)) { return 0; }
fMicroPropsGenerator->processQuantity(inValue, fMicros, status);
if (U_FAILURE(status)) { return 0; }
// #13453: DecimalFormat wants the affixes from the pattern only (modMiddle).
fMicros.modMiddle->apply(outString, 0, 0, status);
if (U_FAILURE(status)) { return 0; }
return fMicros.modMiddle->getPrefixLength(status);
}
NumberFormatterImpl::NumberFormatterImpl(const MacroProps& macros, bool safe, UErrorCode& status) {
fMicroPropsGenerator = macrosToMicroGenerator(macros, safe, status);
}

View file

@ -37,6 +37,15 @@ class NumberFormatterImpl : public UMemory {
applyStatic(const MacroProps &macros, DecimalQuantity &inValue, NumberStringBuilder &outString,
UErrorCode &status);
/**
* Prints only the prefix and suffix; used for DecimalFormat getters.
*
* @return The index into the output at which the prefix ends and the suffix starts; in other words,
* the prefix length.
*/
static int32_t getPrefixSuffix(const MacroProps& macros, DecimalQuantity& inValue,
NumberStringBuilder& outString, UErrorCode& status);
/**
* Evaluates the "safe" MicroPropsGenerator created by "fromMacros".
*/
@ -70,6 +79,9 @@ class NumberFormatterImpl : public UMemory {
void applyUnsafe(DecimalQuantity &inValue, NumberStringBuilder &outString, UErrorCode &status);
int32_t getPrefixSuffixUnsafe(DecimalQuantity& inValue, NumberStringBuilder& outString,
UErrorCode& status);
/**
* If rulesPtr is non-null, return it. Otherwise, return a PluralRules owned by this object for the
* specified locale, creating it if necessary.

View file

@ -16,8 +16,6 @@ using namespace icu::number::impl;
namespace {
static UChar32 kFallbackPadChar = 0x0020;
int32_t
addPaddingHelper(UChar32 paddingCp, int32_t requiredPadding, NumberStringBuilder &string, int32_t index,
UErrorCode &status) {
@ -55,7 +53,7 @@ Padder Padder::forProperties(const DecimalFormatProperties& properties) {
if (properties.padString.length() > 0) {
padCp = properties.padString.char32At(0);
} else {
padCp = kFallbackPadChar;
padCp = kFallbackPaddingString[0];
}
return {padCp, properties.formatWidth, properties.padPosition.getOrDefault(UNUM_PAD_BEFORE_PREFIX)};
}

View file

@ -46,7 +46,7 @@ class DecimalQuantity;
#if U_PLATFORM == U_PF_OS400
#define UNUM_INTERNAL_STACKARRAY_SIZE 144
#else
#define UNUM_INTERNAL_STACKARRAY_SIZE 128
#define UNUM_INTERNAL_STACKARRAY_SIZE 80
#endif
/**

View file

@ -2191,6 +2191,11 @@ class U_I18N_API LocalizedNumberFormatter
*/
FormattedNumber formatDecimalQuantity(const impl::DecimalQuantity& dq, UErrorCode& status) const;
/** Internal method for DecimalFormat compatibility.
* @internal
*/
void getAffix(bool isPrefix, bool isNegative, UnicodeString& result, UErrorCode& status) const;
/**
* Internal method for testing.
* @internal
@ -2250,6 +2255,7 @@ class U_I18N_API LocalizedNumberFormatter
*
* @param results
* The results object. This method will mutate it to save the results.
* @internal
*/
void formatImpl(impl::UFormattedNumberData *results, UErrorCode &status) const;
@ -2355,12 +2361,6 @@ class U_I18N_API FormattedNumber : public UMemory {
*/
void getDecimalQuantity(impl::DecimalQuantity& output, UErrorCode& status) const;
/** @internal */
const UnicodeString getPrefix(UErrorCode& status) const;
/** @internal */
const UnicodeString getSuffix(UErrorCode& status) const;
#endif
// Don't allow copying of FormattedNumber, but moving is okay.

View file

@ -637,7 +637,6 @@ void NumberFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &n
TESTCASE_AUTO(TestNumberFormatTestTuple);
TESTCASE_AUTO(TestDataDriven);
TESTCASE_AUTO(TestDoubleLimit11439);
TESTCASE_AUTO(TestFastPathConsistent11524);
TESTCASE_AUTO(TestGetAffixes);
TESTCASE_AUTO(TestToPatternScientific11648);
TESTCASE_AUTO(TestBenchmark);
@ -1586,7 +1585,8 @@ NumberFormatTest::TestLenientParse(void)
if (U_FAILURE(status) ||n.getType() != Formattable::kDouble ||
n.getDouble() != 0.25) {
errln((UnicodeString)"Lenient parse failed for \"" + (UnicodeString) lenientPercentTestCases[t]
+ (UnicodeString) "\"; error code = " + u_errorName(status));
+ (UnicodeString) "\"; error code = " + u_errorName(status)
+ "; got: " + n.getDouble(status));
status = U_ZERO_ERROR;
}
}
@ -1600,7 +1600,8 @@ NumberFormatTest::TestLenientParse(void)
if (U_FAILURE(status) ||n.getType() != Formattable::kDouble ||
n.getDouble() != -0.25) {
errln((UnicodeString)"Lenient parse failed for \"" + (UnicodeString) lenientNegativePercentTestCases[t]
+ (UnicodeString) "\"; error code = " + u_errorName(status));
+ (UnicodeString) "\"; error code = " + u_errorName(status)
+ "; got: " + n.getDouble(status));
status = U_ZERO_ERROR;
}
}
@ -7983,19 +7984,14 @@ void NumberFormatTest::Test10468ApplyPattern() {
return;
}
if (fmt.getPadCharacterString() != UnicodeString("a")) {
errln("Padding character should be 'a'.");
return;
}
assertEquals("Padding character should be 'a'.", u"a", fmt.getPadCharacterString());
// Padding char of fmt ought to be '*' since that is the default and no
// explicit padding char is specified in the new pattern.
fmt.applyPattern("AA#,##0.00ZZ", status);
// Oops this still prints 'a' even though we changed the pattern.
if (fmt.getPadCharacterString() != UnicodeString(" ")) {
errln("applyPattern did not clear padding character.");
}
assertEquals("applyPattern did not clear padding character.", u" ", fmt.getPadCharacterString());
}
void NumberFormatTest::TestRoundingScientific10542() {
@ -8275,7 +8271,7 @@ void NumberFormatTest::TestCurrencyUsage() {
UnicodeString original;
fmt->format(agent,original);
assertEquals("Test Currency Usage 1", UnicodeString("PKR124"), original);
assertEquals("Test Currency Usage 1", UnicodeString("PKR\u00A0124"), original);
// test the getter here
UCurrencyUsage curUsage = fmt->getCurrencyUsage();
@ -8295,7 +8291,7 @@ void NumberFormatTest::TestCurrencyUsage() {
UnicodeString cash_currency;
fmt->format(agent,cash_currency);
assertEquals("Test Currency Usage 2", UnicodeString("PKR124"), cash_currency);
assertEquals("Test Currency Usage 2", UnicodeString("PKR\u00A0124"), cash_currency);
delete fmt;
}
@ -8355,7 +8351,7 @@ void NumberFormatTest::TestCurrencyUsage() {
UnicodeString PKR_changed;
fmt->format(agent, PKR_changed);
assertEquals("Test Currency Usage 6", UnicodeString("PKR124"), PKR_changed);
assertEquals("Test Currency Usage 6", UnicodeString("PKR\u00A0124"), PKR_changed);
delete fmt;
}
}
@ -8460,21 +8456,6 @@ void NumberFormatTest::TestDoubleLimit11439() {
}
}
void NumberFormatTest::TestFastPathConsistent11524() {
UErrorCode status = U_ZERO_ERROR;
NumberFormat *fmt = NumberFormat::createInstance("en", status);
if (U_FAILURE(status) || fmt == NULL) {
dataerrln("Failed call to NumberFormat::createInstance() - %s", u_errorName(status));
return;
}
fmt->setMaximumIntegerDigits(INT32_MIN);
UnicodeString appendTo;
assertEquals("", "0", fmt->format((int32_t)123, appendTo));
appendTo.remove();
assertEquals("", "0", fmt->format((int32_t)12345, appendTo));
delete fmt;
}
void NumberFormatTest::TestGetAffixes() {
UErrorCode status = U_ZERO_ERROR;
DecimalFormatSymbols sym("en_US", status);

View file

@ -1420,7 +1420,7 @@ NaN NaN K
-1E-99999999999999 -0.0
1E2147483648 Inf K
1E2147483647 Inf K
1E2147483646 1E2147483646
1E2147483646 1E+2147483646
1E-2147483649 0
1E-2147483648 0
// C and P return zero here

View file

@ -9,7 +9,6 @@ import java.text.FieldPosition;
import java.util.Arrays;
import com.ibm.icu.impl.number.DecimalQuantity;
import com.ibm.icu.impl.number.MicroProps;
import com.ibm.icu.impl.number.NumberStringBuilder;
import com.ibm.icu.text.PluralRules.IFixedDecimal;
import com.ibm.icu.util.ICUUncheckedIOException;
@ -23,14 +22,12 @@ import com.ibm.icu.util.ICUUncheckedIOException;
* @see NumberFormatter
*/
public class FormattedNumber {
NumberStringBuilder nsb;
DecimalQuantity fq;
MicroProps micros;
final NumberStringBuilder nsb;
final DecimalQuantity fq;
FormattedNumber(NumberStringBuilder nsb, DecimalQuantity fq, MicroProps micros) {
FormattedNumber(NumberStringBuilder nsb, DecimalQuantity fq) {
this.nsb = nsb;
this.fq = fq;
this.micros = micros;
}
/**
@ -141,34 +138,6 @@ public class FormattedNumber {
return fq.toBigDecimal();
}
/**
* @internal
* @deprecated This API is ICU internal only. Use {@link #populateFieldPosition} or
* {@link #getFieldIterator} for similar functionality.
*/
@Deprecated
public String getPrefix() {
NumberStringBuilder temp = new NumberStringBuilder();
// #13453: DecimalFormat wants the affixes from the pattern only (modMiddle).
micros.modMiddle.apply(temp, 0, 0);
int prefixLength = micros.modMiddle.getPrefixLength();
return temp.subSequence(0, prefixLength).toString();
}
/**
* @internal
* @deprecated This API is ICU internal only. Use {@link #populateFieldPosition} or
* {@link #getFieldIterator} for similar functionality.
*/
@Deprecated
public String getSuffix() {
NumberStringBuilder temp = new NumberStringBuilder();
// #13453: DecimalFormat wants the affixes from the pattern only (modMiddle).
int length = micros.modMiddle.apply(temp, 0, 0);
int prefixLength = micros.modMiddle.getPrefixLength();
return temp.subSequence(prefixLength, length).toString();
}
/**
* @internal
* @deprecated This API is ICU internal only.

View file

@ -9,7 +9,6 @@ import com.ibm.icu.impl.Utility;
import com.ibm.icu.impl.number.DecimalQuantity;
import com.ibm.icu.impl.number.DecimalQuantity_DualStorageBCD;
import com.ibm.icu.impl.number.MacroProps;
import com.ibm.icu.impl.number.MicroProps;
import com.ibm.icu.impl.number.NumberStringBuilder;
import com.ibm.icu.math.BigDecimal;
import com.ibm.icu.util.CurrencyAmount;
@ -137,16 +136,33 @@ public class LocalizedNumberFormatter extends NumberFormatterSettings<LocalizedN
// Further benchmarking is required.
long currentCount = callCount.incrementAndGet(this);
NumberStringBuilder string = new NumberStringBuilder();
MicroProps micros;
if (currentCount == macros.threshold.longValue()) {
compiled = NumberFormatterImpl.fromMacros(macros);
micros = compiled.apply(fq, string);
compiled.apply(fq, string);
} else if (compiled != null) {
micros = compiled.apply(fq, string);
compiled.apply(fq, string);
} else {
micros = NumberFormatterImpl.applyStatic(macros, fq, string);
NumberFormatterImpl.applyStatic(macros, fq, string);
}
return new FormattedNumber(string, fq);
}
/**
* @internal
* @deprecated This API is ICU internal only. Use {@link FormattedNumber#populateFieldPosition} or
* {@link FormattedNumber#getFieldIterator} for similar functionality.
*/
@Deprecated
public String getAffix(boolean isPrefix, boolean isNegative) {
MacroProps macros = resolve();
NumberStringBuilder nsb = new NumberStringBuilder();
DecimalQuantity dq = new DecimalQuantity_DualStorageBCD(isNegative ? -1 : 1);
int prefixLength = NumberFormatterImpl.getPrefixSuffix(macros, dq, nsb);
if (isPrefix) {
return nsb.subSequence(0, prefixLength).toString();
} else {
return nsb.subSequence(prefixLength, nsb.length()).toString();
}
return new FormattedNumber(string, fq, micros);
}
@Override

View file

@ -48,14 +48,30 @@ class NumberFormatterImpl {
/**
* Builds and evaluates an "unsafe" MicroPropsGenerator, which is cheaper but can be used only once.
*/
public static MicroProps applyStatic(
public static void applyStatic(
MacroProps macros,
DecimalQuantity inValue,
NumberStringBuilder outString) {
MicroPropsGenerator microPropsGenerator = macrosToMicroGenerator(macros, false);
MicroProps micros = microPropsGenerator.processQuantity(inValue);
microsToString(micros, inValue, outString);
return micros;
}
/**
* Prints only the prefix and suffix; used for DecimalFormat getters.
*
* @return The index into the output at which the prefix ends and the suffix starts; in other words,
* the prefix length.
*/
public static int getPrefixSuffix(
MacroProps macros,
DecimalQuantity inValue,
NumberStringBuilder output) {
MicroPropsGenerator microPropsGenerator = macrosToMicroGenerator(macros, false);
MicroProps micros = microPropsGenerator.processQuantity(inValue);
// #13453: DecimalFormat wants the affixes from the pattern only (modMiddle).
micros.modMiddle.apply(output, 0, 0);
return micros.modMiddle.getPrefixLength();
}
private static final Currency DEFAULT_CURRENCY = Currency.getInstance("XXX");
@ -66,10 +82,9 @@ class NumberFormatterImpl {
this.microPropsGenerator = microPropsGenerator;
}
public MicroProps apply(DecimalQuantity inValue, NumberStringBuilder outString) {
public void apply(DecimalQuantity inValue, NumberStringBuilder outString) {
MicroProps micros = microPropsGenerator.processQuantity(inValue);
microsToString(micros, inValue, outString);
return micros;
}
//////////

View file

@ -15,6 +15,7 @@ import java.text.ParsePosition;
import com.ibm.icu.impl.number.AffixUtils;
import com.ibm.icu.impl.number.DecimalFormatProperties;
import com.ibm.icu.impl.number.DecimalFormatProperties.ParseMode;
import com.ibm.icu.impl.number.Padder;
import com.ibm.icu.impl.number.Padder.PadPosition;
import com.ibm.icu.impl.number.PatternStringParser;
import com.ibm.icu.impl.number.PatternStringUtils;
@ -915,7 +916,7 @@ public class DecimalFormat extends NumberFormat {
* @stable ICU 2.0
*/
public synchronized String getPositivePrefix() {
return formatter.format(1).getPrefix();
return formatter.getAffix(true, false);
}
/**
@ -952,7 +953,7 @@ public class DecimalFormat extends NumberFormat {
* @stable ICU 2.0
*/
public synchronized String getNegativePrefix() {
return formatter.format(-1).getPrefix();
return formatter.getAffix(true, true);
}
/**
@ -989,7 +990,7 @@ public class DecimalFormat extends NumberFormat {
* @stable ICU 2.0
*/
public synchronized String getPositiveSuffix() {
return formatter.format(1).getSuffix();
return formatter.getAffix(false, false);
}
/**
@ -1026,7 +1027,7 @@ public class DecimalFormat extends NumberFormat {
* @stable ICU 2.0
*/
public synchronized String getNegativeSuffix() {
return formatter.format(-1).getSuffix();
return formatter.getAffix(false, true);
}
/**
@ -1686,7 +1687,7 @@ public class DecimalFormat extends NumberFormat {
public synchronized char getPadCharacter() {
CharSequence paddingString = properties.getPadString();
if (paddingString == null) {
return '.'; // TODO: Is this the correct behavior?
return Padder.FALLBACK_PADDING_STRING.charAt(0);
} else {
return paddingString.charAt(0);
}