mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-07 22:44:49 +00:00
ICU-13634 In accordance with ICU-TC meeting, changing percent parsing behavior to be closer to that of ICU 60.
X-SVN-Rev: 41222
This commit is contained in:
parent
6c1714870f
commit
af0f8e62e4
11 changed files with 161 additions and 9 deletions
|
@ -150,6 +150,23 @@ NumberParserImpl::createParserFromProperties(const number::impl::DecimalFormatPr
|
|||
parser->addMatcher(parser->fLocalMatchers.currency = {currencySymbols, symbols, status});
|
||||
}
|
||||
|
||||
///////////////
|
||||
/// PERCENT ///
|
||||
///////////////
|
||||
|
||||
// ICU-TC meeting, April 11, 2018: accept percent/permille only if it is in the pattern,
|
||||
// and to maintain regressive behavior, divide by 100 even if no percent sign is present.
|
||||
if (affixProvider->containsSymbolType(AffixPatternType::TYPE_PERCENT, status)) {
|
||||
parser->addMatcher(parser->fLocalMatchers.percent = {symbols});
|
||||
// causes number to be always scaled by 100:
|
||||
parser->addMatcher(parser->fLocalValidators.percentFlags = {ResultFlags::FLAG_PERCENT});
|
||||
}
|
||||
if (affixProvider->containsSymbolType(AffixPatternType::TYPE_PERMILLE, status)) {
|
||||
parser->addMatcher(parser->fLocalMatchers.permille = {symbols});
|
||||
// causes number to be always scaled by 1000:
|
||||
parser->addMatcher(parser->fLocalValidators.permilleFlags = {ResultFlags::FLAG_PERMILLE});
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
/// OTHER STANDARD MATCHERS ///
|
||||
///////////////////////////////
|
||||
|
@ -157,8 +174,6 @@ NumberParserImpl::createParserFromProperties(const number::impl::DecimalFormatPr
|
|||
if (!isStrict) {
|
||||
parser->addMatcher(parser->fLocalMatchers.plusSign = {symbols, false});
|
||||
parser->addMatcher(parser->fLocalMatchers.minusSign = {symbols, false});
|
||||
parser->addMatcher(parser->fLocalMatchers.percent = {symbols});
|
||||
parser->addMatcher(parser->fLocalMatchers.permille = {symbols});
|
||||
}
|
||||
parser->addMatcher(parser->fLocalMatchers.nan = {symbols});
|
||||
parser->addMatcher(parser->fLocalMatchers.infinity = {symbols});
|
||||
|
|
|
@ -80,6 +80,8 @@ class NumberParserImpl : public MutableMatcherCollection {
|
|||
RequireExponentValidator exponent;
|
||||
RequireNumberValidator number;
|
||||
MultiplierParseHandler multiplier;
|
||||
FlagHandler percentFlags;
|
||||
FlagHandler permilleFlags;
|
||||
} fLocalValidators;
|
||||
|
||||
explicit NumberParserImpl(parse_flags_t parseFlags);
|
||||
|
|
|
@ -80,4 +80,16 @@ UnicodeString RequireNumberValidator::toString() const {
|
|||
}
|
||||
|
||||
|
||||
FlagHandler::FlagHandler(result_flags_t flags)
|
||||
: fFlags(flags) {}
|
||||
|
||||
void FlagHandler::postProcess(ParsedNumber& result) const {
|
||||
result.flags |= fFlags;
|
||||
}
|
||||
|
||||
UnicodeString FlagHandler::toString() const {
|
||||
return u"<Flags>";
|
||||
}
|
||||
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
|
|
@ -97,6 +97,24 @@ class MultiplierParseHandler : public ValidationMatcher, public UMemory {
|
|||
};
|
||||
|
||||
|
||||
/**
|
||||
* Unconditionally applies a given set of flags to the ParsedNumber in the post-processing step.
|
||||
*/
|
||||
class FlagHandler : public ValidationMatcher, public UMemory {
|
||||
public:
|
||||
FlagHandler() = default;
|
||||
|
||||
FlagHandler(result_flags_t flags);
|
||||
|
||||
void postProcess(ParsedNumber& result) const U_OVERRIDE;
|
||||
|
||||
UnicodeString toString() const U_OVERRIDE;
|
||||
|
||||
private:
|
||||
result_flags_t fFlags;
|
||||
};
|
||||
|
||||
|
||||
} // namespace impl
|
||||
} // namespace numparse
|
||||
U_NAMESPACE_END
|
||||
|
|
|
@ -655,6 +655,7 @@ void NumberFormatTest::runIndexedTest( int32_t index, UBool exec, const char* &n
|
|||
TESTCASE_AUTO(Test11735_ExceptionIssue);
|
||||
TESTCASE_AUTO(Test11035_FormatCurrencyAmount);
|
||||
TESTCASE_AUTO(Test11318_DoubleConversion);
|
||||
TESTCASE_AUTO(TestParsePercentRegression);
|
||||
TESTCASE_AUTO_END;
|
||||
}
|
||||
|
||||
|
@ -9029,4 +9030,35 @@ void NumberFormatTest::Test11318_DoubleConversion() {
|
|||
assertEquals("Should render all digits", u"999,999,999,999,999.9", appendTo);
|
||||
}
|
||||
|
||||
void NumberFormatTest::TestParsePercentRegression() {
|
||||
IcuTestErrorCode status(*this, "TestParsePercentRegression");
|
||||
LocalPointer<DecimalFormat> df1((DecimalFormat*) NumberFormat::createInstance("en", status));
|
||||
LocalPointer<DecimalFormat> df2((DecimalFormat*) NumberFormat::createPercentInstance("en", status));
|
||||
df1->setLenient(TRUE);
|
||||
df2->setLenient(TRUE);
|
||||
|
||||
{
|
||||
ParsePosition ppos;
|
||||
Formattable result;
|
||||
df1->parse("50%", result, ppos);
|
||||
assertEquals("df1 should accept a number but not the percent sign", 2, ppos.getIndex());
|
||||
assertEquals("df1 should return the number as 50", 50.0, result.getDouble(status));
|
||||
}
|
||||
{
|
||||
ParsePosition ppos;
|
||||
Formattable result;
|
||||
df2->parse("50%", result, ppos);
|
||||
assertEquals("df2 should accept the percent sign", 3, ppos.getIndex());
|
||||
assertEquals("df2 should return the number as 0.5", 0.5, result.getDouble(status));
|
||||
}
|
||||
{
|
||||
ParsePosition ppos;
|
||||
Formattable result;
|
||||
df2->parse("50", result, ppos);
|
||||
assertEquals("df2 should return the number as 0.5 even though the percent sign is missing",
|
||||
0.5,
|
||||
result.getDouble(status));
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* #if !UCONFIG_NO_FORMATTING */
|
||||
|
|
|
@ -222,6 +222,7 @@ class NumberFormatTest: public CalendarTimeZoneTest {
|
|||
void Test11735_ExceptionIssue();
|
||||
void Test11035_FormatCurrencyAmount();
|
||||
void Test11318_DoubleConversion();
|
||||
void TestParsePercentRegression();
|
||||
|
||||
private:
|
||||
UBool testFormattableAsUFormattable(const char *file, int line, Formattable &f);
|
||||
|
|
|
@ -1543,8 +1543,8 @@ begin
|
|||
parse output breaks
|
||||
55% 0.55
|
||||
// J and K get null
|
||||
// P requires the symbol to be present and gets 55
|
||||
55 0.55 CJKP
|
||||
// C and P scale by 100 even if the percent sign is not present
|
||||
55 0.55 JK
|
||||
|
||||
test trailing grouping separators in pattern
|
||||
// This test is for #13115
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
// © 2018 and later: Unicode, Inc. and others.
|
||||
// License & terms of use: http://www.unicode.org/copyright.html#License
|
||||
package com.ibm.icu.impl.number.parse;
|
||||
|
||||
/**
|
||||
* Unconditionally applies a given set of flags to the ParsedNumber in the post-processing step.
|
||||
*/
|
||||
public class FlagHandler extends ValidationMatcher {
|
||||
|
||||
public static final FlagHandler PERCENT = new FlagHandler(ParsedNumber.FLAG_PERCENT);
|
||||
public static final FlagHandler PERMILLE = new FlagHandler(ParsedNumber.FLAG_PERMILLE);
|
||||
|
||||
private final int flags;
|
||||
|
||||
private FlagHandler(int flags) {
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcess(ParsedNumber result) {
|
||||
result.flags |= flags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "<FlagsHandler " + Integer.toHexString(flags) + ">";
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ import java.util.List;
|
|||
|
||||
import com.ibm.icu.impl.StringSegment;
|
||||
import com.ibm.icu.impl.number.AffixPatternProvider;
|
||||
import com.ibm.icu.impl.number.AffixUtils;
|
||||
import com.ibm.icu.impl.number.CurrencyPluralInfoAffixProvider;
|
||||
import com.ibm.icu.impl.number.CustomSymbolCurrency;
|
||||
import com.ibm.icu.impl.number.DecimalFormatProperties;
|
||||
|
@ -195,6 +196,23 @@ public class NumberParserImpl {
|
|||
parser.addMatcher(CombinedCurrencyMatcher.getInstance(currency, symbols));
|
||||
}
|
||||
|
||||
///////////////
|
||||
/// PERCENT ///
|
||||
///////////////
|
||||
|
||||
// ICU-TC meeting, April 11, 2018: accept percent/permille only if it is in the pattern,
|
||||
// and to maintain regressive behavior, divide by 100 even if no percent sign is present.
|
||||
if (affixProvider.containsSymbolType(AffixUtils.TYPE_PERCENT)) {
|
||||
parser.addMatcher(PercentMatcher.getInstance(symbols));
|
||||
// causes number to be always scaled by 100:
|
||||
parser.addMatcher(FlagHandler.PERCENT);
|
||||
}
|
||||
if (affixProvider.containsSymbolType(AffixUtils.TYPE_PERMILLE)) {
|
||||
parser.addMatcher(PermilleMatcher.getInstance(symbols));
|
||||
// causes number to be always scaled by 1000:
|
||||
parser.addMatcher(FlagHandler.PERMILLE);
|
||||
}
|
||||
|
||||
///////////////////////////////
|
||||
/// OTHER STANDARD MATCHERS ///
|
||||
///////////////////////////////
|
||||
|
@ -202,8 +220,6 @@ public class NumberParserImpl {
|
|||
if (!isStrict) {
|
||||
parser.addMatcher(PlusSignMatcher.getInstance(symbols, false));
|
||||
parser.addMatcher(MinusSignMatcher.getInstance(symbols, false));
|
||||
parser.addMatcher(PercentMatcher.getInstance(symbols));
|
||||
parser.addMatcher(PermilleMatcher.getInstance(symbols));
|
||||
}
|
||||
parser.addMatcher(NanMatcher.getInstance(symbols, parseFlags));
|
||||
parser.addMatcher(InfinityMatcher.getInstance(symbols));
|
||||
|
|
|
@ -1545,8 +1545,8 @@ begin
|
|||
parse output breaks
|
||||
55% 0.55
|
||||
// J and K get null
|
||||
// P requires the symbol to be present and gets 55
|
||||
55 0.55 JKP
|
||||
// C and P scale by 100 even if the percent sign is not present
|
||||
55 0.55 JK
|
||||
|
||||
test trailing grouping separators in pattern
|
||||
// This test is for #13115
|
||||
|
|
|
@ -5637,7 +5637,7 @@ public class NumberFormatTest extends TestFmwk {
|
|||
assertEquals("Should consume the trailing bidi since it is in the symbol", 5, ppos.getIndex());
|
||||
ppos.setIndex(0);
|
||||
result = df.parse("-42a\u200E ", ppos);
|
||||
assertEquals("Should not parse as percent", new Long(-42), result);
|
||||
assertEquals("Should parse as percent", -0.42, result.doubleValue());
|
||||
assertEquals("Should not consume the trailing bidi or whitespace", 4, ppos.getIndex());
|
||||
|
||||
// A few more cases based on the docstring:
|
||||
|
@ -6075,4 +6075,32 @@ public class NumberFormatTest extends TestFmwk {
|
|||
DecimalFormat df = new DecimalFormat("-0", DecimalFormatSymbols.getInstance(ULocale.ENGLISH));
|
||||
expect2(df, -5, "--5");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParsePercentRegression() {
|
||||
DecimalFormat df1 = (DecimalFormat) NumberFormat.getInstance(ULocale.ENGLISH);
|
||||
DecimalFormat df2 = (DecimalFormat) NumberFormat.getPercentInstance(ULocale.ENGLISH);
|
||||
df1.setParseStrict(false);
|
||||
df2.setParseStrict(false);
|
||||
|
||||
{
|
||||
ParsePosition ppos = new ParsePosition(0);
|
||||
Number result = df1.parse("50%", ppos);
|
||||
assertEquals("df1 should accept a number but not the percent sign", 2, ppos.getIndex());
|
||||
assertEquals("df1 should return the number as 50", 50.0, result.doubleValue());
|
||||
}
|
||||
{
|
||||
ParsePosition ppos = new ParsePosition(0);
|
||||
Number result = df2.parse("50%", ppos);
|
||||
assertEquals("df2 should accept the percent sign", 3, ppos.getIndex());
|
||||
assertEquals("df2 should return the number as 0.5", 0.5, result.doubleValue());
|
||||
}
|
||||
{
|
||||
ParsePosition ppos = new ParsePosition(0);
|
||||
Number result = df2.parse("50", ppos);
|
||||
assertEquals("df2 should return the number as 0.5 even though the percent sign is missing",
|
||||
0.5,
|
||||
result.doubleValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue