ICU-13513 A few more minor fixes before merge.
X-SVN-Rev: 40822
This commit is contained in:
parent
8d366b982d
commit
da65d09dd5
@ -34,37 +34,36 @@ public class NumberParserImpl {
|
||||
// TODO: Find a better place for this enum.
|
||||
/** Controls the set of rules for parsing a string. */
|
||||
public static enum ParseMode {
|
||||
/**
|
||||
* Lenient mode should be used if you want to accept malformed user input. It will use
|
||||
* heuristics to attempt to parse through typographical errors in the string.
|
||||
*/
|
||||
LENIENT,
|
||||
/**
|
||||
* Lenient mode should be used if you want to accept malformed user input. It will use heuristics
|
||||
* to attempt to parse through typographical errors in the string.
|
||||
*/
|
||||
LENIENT,
|
||||
|
||||
/**
|
||||
* Strict mode should be used if you want to require that the input is well-formed. More
|
||||
* specifically, it differs from lenient mode in the following ways:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Grouping widths must match the grouping settings. For example, "12,3,45" will fail if
|
||||
* the grouping width is 3, as in the pattern "#,##0".
|
||||
* <li>The string must contain a complete prefix and suffix. For example, if the pattern is
|
||||
* "{#};(#)", then "{123}" or "(123)" would match, but "{123", "123}", and "123" would all
|
||||
* fail. (The latter strings would be accepted in lenient mode.)
|
||||
* <li>Whitespace may not appear at arbitrary places in the string. In lenient mode,
|
||||
* whitespace is allowed to occur arbitrarily before and after prefixes and exponent
|
||||
* separators.
|
||||
* <li>Leading grouping separators are not allowed, as in ",123".
|
||||
* <li>Minus and plus signs can only appear if specified in the pattern. In lenient mode, a
|
||||
* plus or minus sign can always precede a number.
|
||||
* <li>The set of characters that can be interpreted as a decimal or grouping separator is
|
||||
* smaller.
|
||||
* <li><strong>If currency parsing is enabled,</strong> currencies must only appear where
|
||||
* specified in either the current pattern string or in a valid pattern string for the
|
||||
* current locale. For example, if the pattern is "¤0.00", then "$1.23" would match, but
|
||||
* "1.23$" would fail to match.
|
||||
* </ul>
|
||||
*/
|
||||
STRICT,
|
||||
/**
|
||||
* Strict mode should be used if you want to require that the input is well-formed. More
|
||||
* specifically, it differs from lenient mode in the following ways:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Grouping widths must match the grouping settings. For example, "12,3,45" will fail if the
|
||||
* grouping width is 3, as in the pattern "#,##0".
|
||||
* <li>The string must contain a complete prefix and suffix. For example, if the pattern is
|
||||
* "{#};(#)", then "{123}" or "(123)" would match, but "{123", "123}", and "123" would all fail.
|
||||
* (The latter strings would be accepted in lenient mode.)
|
||||
* <li>Whitespace may not appear at arbitrary places in the string. In lenient mode, whitespace
|
||||
* is allowed to occur arbitrarily before and after prefixes and exponent separators.
|
||||
* <li>Leading grouping separators are not allowed, as in ",123".
|
||||
* <li>Minus and plus signs can only appear if specified in the pattern. In lenient mode, a plus
|
||||
* or minus sign can always precede a number.
|
||||
* <li>The set of characters that can be interpreted as a decimal or grouping separator is
|
||||
* smaller.
|
||||
* <li><strong>If currency parsing is enabled,</strong> currencies must only appear where
|
||||
* specified in either the current pattern string or in a valid pattern string for the current
|
||||
* locale. For example, if the pattern is "¤0.00", then "$1.23" would match, but "1.23$" would
|
||||
* fail to match.
|
||||
* </ul>
|
||||
*/
|
||||
STRICT,
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@ -167,20 +166,12 @@ public class NumberParserImpl {
|
||||
AffixPatternProvider patternInfo = new PropertiesAffixPatternProvider(properties);
|
||||
Currency currency = CustomSymbolCurrency.resolve(properties.getCurrency(), locale, symbols);
|
||||
boolean isStrict = properties.getParseMode() == ParseMode.STRICT;
|
||||
boolean decimalSeparatorRequired = properties.getDecimalPatternMatchRequired()
|
||||
? (properties.getDecimalSeparatorAlwaysShown()
|
||||
|| properties.getMaximumFractionDigits() != 0)
|
||||
: false;
|
||||
boolean decimalSeparatorForbidden = properties.getDecimalPatternMatchRequired()
|
||||
? (!properties.getDecimalSeparatorAlwaysShown()
|
||||
&& properties.getMaximumFractionDigits() == 0)
|
||||
: false;
|
||||
Grouper grouper = Grouper.defaults().withProperties(properties);
|
||||
int parseFlags = 0;
|
||||
if (!properties.getParseCaseSensitive()) {
|
||||
parseFlags |= ParsingUtils.PARSE_FLAG_IGNORE_CASE;
|
||||
}
|
||||
if (properties.getParseIntegerOnly() || decimalSeparatorForbidden) {
|
||||
if (properties.getParseIntegerOnly()) {
|
||||
parseFlags |= ParsingUtils.PARSE_FLAG_INTEGER_ONLY;
|
||||
}
|
||||
if (isStrict) {
|
||||
@ -260,8 +251,10 @@ public class NumberParserImpl {
|
||||
if (parseCurrency) {
|
||||
parser.addMatcher(new RequireCurrencyMatcher());
|
||||
}
|
||||
if (decimalSeparatorRequired) {
|
||||
parser.addMatcher(new RequireDecimalSeparatorMatcher());
|
||||
if (properties.getDecimalPatternMatchRequired()) {
|
||||
boolean patternHasDecimalSeparator = properties.getDecimalSeparatorAlwaysShown()
|
||||
|| properties.getMaximumFractionDigits() != 0;
|
||||
parser.addMatcher(RequireDecimalSeparatorMatcher.getInstance(patternHasDecimalSeparator));
|
||||
}
|
||||
if (properties.getMultiplier() != null) {
|
||||
// We need to use a math context in order to prevent non-terminating decimal expansions.
|
||||
|
@ -8,9 +8,23 @@ package com.ibm.icu.impl.number.parse;
|
||||
*/
|
||||
public class RequireDecimalSeparatorMatcher extends ValidationMatcher {
|
||||
|
||||
private static final RequireDecimalSeparatorMatcher A = new RequireDecimalSeparatorMatcher(true);
|
||||
private static final RequireDecimalSeparatorMatcher B = new RequireDecimalSeparatorMatcher(false);
|
||||
|
||||
private final boolean patternHasDecimalSeparator;
|
||||
|
||||
public static RequireDecimalSeparatorMatcher getInstance(boolean patternHasDecimalSeparator) {
|
||||
return patternHasDecimalSeparator ? A : B;
|
||||
}
|
||||
|
||||
private RequireDecimalSeparatorMatcher(boolean patternHasDecimalSeparator) {
|
||||
this.patternHasDecimalSeparator = patternHasDecimalSeparator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postProcess(ParsedNumber result) {
|
||||
if (0 == (result.flags & ParsedNumber.FLAG_HAS_DECIMAL_SEPARATOR)) {
|
||||
boolean parseHasDecimalSeparator = 0 != (result.flags & ParsedNumber.FLAG_HAS_DECIMAL_SEPARATOR);
|
||||
if (parseHasDecimalSeparator != patternHasDecimalSeparator) {
|
||||
result.flags |= ParsedNumber.FLAG_FAIL;
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,8 @@ import java.math.RoundingMode;
|
||||
import java.text.FieldPosition;
|
||||
|
||||
import com.ibm.icu.impl.StandardPlural;
|
||||
import com.ibm.icu.impl.number.DecimalQuantity;
|
||||
import com.ibm.icu.impl.number.RoundingUtils;
|
||||
import com.ibm.icu.text.PluralRules;
|
||||
import com.ibm.icu.text.PluralRules.Operand;
|
||||
import com.ibm.icu.text.UFieldPosition;
|
||||
|
@ -4250,7 +4250,6 @@ public class NumberFormatTest extends TestFmwk {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void TestParseRequiredDecimalPoint() {
|
||||
|
||||
String[] testPattern = { "00.####", "00.0", "00" };
|
||||
@ -5466,7 +5465,6 @@ public class NumberFormatTest extends TestFmwk {
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testParseSubtraction() {
|
||||
// TODO: Is this a case we need to support? It prevents us from automatically parsing
|
||||
// minus signs that appear after the number, like in "12-" vs "-12".
|
||||
@ -5499,6 +5497,7 @@ public class NumberFormatTest extends TestFmwk {
|
||||
assertEquals("Quote should be escapable in padding syntax", "a''12b", result);
|
||||
}
|
||||
|
||||
// TODO: Investigate this test and re-enable if appropriate.
|
||||
@Test
|
||||
@Ignore
|
||||
public void testParseAmbiguousAffixes() {
|
||||
@ -5648,89 +5647,6 @@ public class NumberFormatTest extends TestFmwk {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore
|
||||
public void testParseGroupingMode() {
|
||||
ULocale[] locales = { // GROUPING DECIMAL
|
||||
new ULocale("en-US"), // comma period
|
||||
new ULocale("fr-FR"), // space comma
|
||||
new ULocale("de-CH"), // apostrophe period
|
||||
new ULocale("es-PY") // period comma
|
||||
};
|
||||
String[] inputs = {
|
||||
"12,345.67",
|
||||
"12 345,67",
|
||||
"12'345.67",
|
||||
"12.345,67",
|
||||
"12,345",
|
||||
"12 345",
|
||||
"12'345",
|
||||
"12.345"
|
||||
};
|
||||
BigDecimal[] outputs = {
|
||||
new BigDecimal("12345.67"),
|
||||
new BigDecimal("12345.67"),
|
||||
new BigDecimal("12345.67"),
|
||||
new BigDecimal("12345.67"),
|
||||
new BigDecimal("12345"),
|
||||
new BigDecimal("12345"),
|
||||
new BigDecimal("12345"),
|
||||
new BigDecimal("12345")
|
||||
};
|
||||
int[][] expecteds = {
|
||||
// 0 => works in neither default nor restricted
|
||||
// 1 => works in default but not restricted
|
||||
// 2 => works in restricted but not default (should not happen)
|
||||
// 3 => works in both default and restricted
|
||||
//
|
||||
// C=comma, P=period, S=space, A=apostrophe
|
||||
// C+P S+C A+P P+C C-only S-only A-only P-only
|
||||
{ 3, 0, 1, 0, 3, 1, 1, 0 }, // => en-US
|
||||
{ 0, 3, 0, 1, 0, 3, 3, 1 }, // => fr-FR
|
||||
{ 1, 0, 3, 0, 1, 3, 3, 0 }, // => de-CH
|
||||
{ 0, 1, 0, 3, 0, 1, 1, 3 } // => es-PY
|
||||
};
|
||||
|
||||
for (int i=0; i<locales.length; i++) {
|
||||
ULocale loc = locales[i];
|
||||
DecimalFormat df = (DecimalFormat) NumberFormat.getInstance(loc);
|
||||
df.setParseBigDecimal(true);
|
||||
for (int j=0; j<inputs.length; j++) {
|
||||
String input = inputs[j];
|
||||
BigDecimal output = outputs[j];
|
||||
int expected = expecteds[i][j];
|
||||
|
||||
// TODO(sffc): Uncomment after ICU 60 API proposal
|
||||
//df.setParseGroupingMode(null);
|
||||
//assertEquals("Getter should return null", null, df.getParseGroupingMode());
|
||||
ParsePosition ppos = new ParsePosition(0);
|
||||
Number result = df.parse(input, ppos);
|
||||
boolean actualNull = output.equals(result) && (ppos.getIndex() == input.length());
|
||||
assertEquals("Locale " + loc + ", string \"" + input + "\", DEFAULT, "
|
||||
+ "actual result: " + result + " (ppos: " + ppos.getIndex() + ")",
|
||||
(expected & 1) != 0, actualNull);
|
||||
|
||||
// TODO(sffc): Uncomment after ICU 60 API proposal
|
||||
//df.setParseGroupingMode(GroupingMode.DEFAULT);
|
||||
//assertEquals("Getter should return new value", GroupingMode.DEFAULT, df.getParseGroupingMode());
|
||||
//ppos = new ParsePosition(0);
|
||||
//result = df.parse(input, ppos);
|
||||
//boolean actualDefault = output.equals(result) && (ppos.getIndex() == input.length());
|
||||
//assertEquals("Result from null should be the same as DEFAULT", actualNull, actualDefault);
|
||||
|
||||
// TODO(sffc): Uncomment after ICU 60 API proposal
|
||||
//df.setParseGroupingMode(GroupingMode.RESTRICTED);
|
||||
//assertEquals("Getter should return new value", GroupingMode.RESTRICTED, df.getParseGroupingMode());
|
||||
//ppos = new ParsePosition(0);
|
||||
//result = df.parse(input, ppos);
|
||||
//boolean actualRestricted = output.equals(result) && (ppos.getIndex() == input.length());
|
||||
//assertEquals("Locale " + loc + ", string \"" + input + "\", RESTRICTED, "
|
||||
// + "actual result: " + result + " (ppos: " + ppos.getIndex() + ")",
|
||||
// (expected & 2) != 0, actualRestricted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseNoExponent() throws ParseException {
|
||||
DecimalFormat df = new DecimalFormat();
|
||||
|
Loading…
Reference in New Issue
Block a user