affixProvider = new CurrencyPluralInfoAffixProvider(properties.getCurrencyPluralInfo(), properties);
}
Currency currency = CustomSymbolCurrency.resolve(properties.getCurrency(), locale, symbols);
- boolean isStrict = properties.getParseMode() == ParseMode.STRICT;
+ ParseMode parseMode = properties.getParseMode();
+ if (parseMode == null) {
+ parseMode = ParseMode.LENIENT;
+ }
Grouper grouper = Grouper.forProperties(properties);
int parseFlags = 0;
if (!properties.getParseCaseSensitive()) {
if (properties.getSignAlwaysShown()) {
parseFlags |= ParsingUtils.PARSE_FLAG_PLUS_SIGN_ALLOWED;
}
- if (isStrict) {
+ if (parseMode == ParseMode.JAVA_COMPATIBILITY) {
+ parseFlags |= ParsingUtils.PARSE_FLAG_STRICT_SEPARATORS;
+ parseFlags |= ParsingUtils.PARSE_FLAG_USE_FULL_AFFIXES;
+ parseFlags |= ParsingUtils.PARSE_FLAG_EXACT_AFFIX;
+ parseFlags |= ParsingUtils.PARSE_FLAG_JAVA_COMPATIBILITY_IGNORABLES;
+ } else if (parseMode == ParseMode.STRICT) {
parseFlags |= ParsingUtils.PARSE_FLAG_STRICT_GROUPING_SIZE;
parseFlags |= ParsingUtils.PARSE_FLAG_STRICT_SEPARATORS;
parseFlags |= ParsingUtils.PARSE_FLAG_USE_FULL_AFFIXES;
// 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 (!isStrict && affixProvider.containsSymbolType(AffixUtils.TYPE_PERCENT)) {
+ if (parseMode == ParseMode.LENIENT && affixProvider.containsSymbolType(AffixUtils.TYPE_PERCENT)) {
parser.addMatcher(PercentMatcher.getInstance(symbols));
}
- if (!isStrict && affixProvider.containsSymbolType(AffixUtils.TYPE_PERMILLE)) {
+ if (parseMode == ParseMode.LENIENT && affixProvider.containsSymbolType(AffixUtils.TYPE_PERMILLE)) {
parser.addMatcher(PermilleMatcher.getInstance(symbols));
}
/// OTHER STANDARD MATCHERS ///
///////////////////////////////
- if (!isStrict) {
+ if (parseMode == ParseMode.LENIENT) {
parser.addMatcher(PlusSignMatcher.getInstance(symbols, false));
parser.addMatcher(MinusSignMatcher.getInstance(symbols, false));
}
//////////////////
parser.addMatcher(new RequireNumberValidator());
- if (isStrict) {
+ if (parseMode != ParseMode.LENIENT) {
parser.addMatcher(new RequireAffixValidator());
}
if (parseCurrency) {
--- /dev/null
+// © 2019 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html#License
+package com.ibm.icu.dev.test.format;
+
+import static org.junit.Assert.assertEquals;
+
+import java.text.ParsePosition;
+import java.util.Locale;
+import java.util.Objects;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import com.ibm.icu.dev.test.TestUtil;
+import com.ibm.icu.impl.number.DecimalFormatProperties.ParseMode;
+import com.ibm.icu.text.DecimalFormat;
+import com.ibm.icu.text.DecimalFormatSymbols;
+import com.ibm.icu.util.ULocale;
+
+/**
+ * Test for {@link DecimalFormat} in {@link ParseMode#JAVA_COMPATIBILITY} mode.
+ */
+@RunWith(JUnit4.class)
+public class NumberFormatJavaCompatilityTest {
+
+ @Test
+ public void testIgnoreables() {
+ // Test bidi characters
+ assertParseError("0", "\u200e1");
+ assertParsed("0", "1\u200e", 1);
+ assertParseError("0%", "\u200e1%");
+ }
+
+ @Test
+ public void testParseGroupingSeparator() {
+ // Test that grouping separator is optional when the group separator is specified
+ assertParsed("#,##0", "9,999", 9999);
+ assertParsed("#,##0", "9999", 9999);
+ assertParsed("#,###0", "9,9999", 99999);
+
+ // Test that grouping size doesn't affect parsing at all
+ assertParsed("#,##0", "9,9999", 99999);
+ assertParsed("#,###0", "99,999", 99999);
+
+ assertParsed("###0", "9999", 9999);
+ assertParsed("###0", "99999", 99999);
+
+ // Test that grouping separator must not be present when the group separator is NOT specified
+ // Only the 1st character in front of separator , should be consumed.
+ assertParsed("###0", "9,9999", 9);
+ assertParsed("###0", "9,999", 9);
+ }
+
+ @Test
+ public void testParseScienificNotation() {
+ assertParsed("0.###E0", "1E-3", 0.001);
+ assertParsed("0.###E0", "1E0", 1);
+ assertParsed("0.###E0", "1E3", 1000);
+ assertParsed("0.###E0", "1.111E3", 1111);
+ assertParsed("0.###E0", "1.1E3", 1100);
+
+ // "0.###E0" is engineering notation, i.e. the exponent should be a multiple of 3
+ // for formatting. But it shouldn't affect parsing.
+ assertParsed("0.###E0", "1E1", 10);
+
+ // Test that exponent is not required for parsing
+ assertParsed("0.###E0", "1.1", 1.1);
+ assertParsed("0.###E0", "1100", 1100);
+
+ // Test that the max of fraction, integer or signficant digits don't affect parsing
+ // Note that the max of signficant digits is 4 = min integer digits (1)
+ // + max fraction digits (3)
+ assertParsed("0.###E0", "1111.4E3", 1111400);
+ assertParsed("0.###E0", "1111.9999E3", 1111999.9);
+ }
+
+ private void assertParseError(String pattern, String input) {
+ assertParsed(pattern, input, null);
+ }
+
+ private void assertParsed(String pattern, String input, Number expected) {
+ assertParsedICU4J(pattern, input, expected);
+
+ // Skip the OpenJDK test if the runtime is not OpenJDK
+ if (TestUtil.getJavaRuntimeName() != TestUtil.JavaRuntimeName.OpenJDK) {
+ return;
+ }
+
+ assertParsedOpenJDK(pattern, input, expected);
+ }
+
+ private void assertParsedICU4J(String pattern, String input, Number expected) {
+ DecimalFormat df = new DecimalFormat(pattern, new DecimalFormatSymbols(ULocale.US));
+ df.setParseStrictMode(ParseMode.JAVA_COMPATIBILITY);
+ ParsePosition ppos = new ParsePosition(0);
+ Number actual = df.parse(input, ppos);
+ assertEquals(String.format("pattern: %s input: %s", pattern, input),
+ Objects.toString(expected), Objects.toString(actual));
+ }
+
+ private void assertParsedOpenJDK(String pattern, String input, Number expected) {
+ java.text.DecimalFormat df = new java.text.DecimalFormat(pattern,
+ new java.text.DecimalFormatSymbols(Locale.US));
+ ParsePosition ppos = new ParsePosition(0);
+ Number actual = df.parse(input, ppos);
+ assertEquals(String.format("pattern: %s input: %s", pattern, input),
+ Objects.toString(expected), Objects.toString(actual));
+ }
+
+}