]> granicus.if.org Git - icu/commitdiff
ICU-13513 A few more minor fixes before merge.
authorShane Carr <shane@unicode.org>
Tue, 30 Jan 2018 01:21:56 +0000 (01:21 +0000)
committerShane Carr <shane@unicode.org>
Tue, 30 Jan 2018 01:21:56 +0000 (01:21 +0000)
X-SVN-Rev: 40822

icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/NumberParserImpl.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/RequireDecimalSeparatorMatcher.java
icu4j/main/tests/core/src/com/ibm/icu/dev/impl/number/DecimalQuantity_SimpleStorage.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatTest.java

index 005ba8e8ea509b77da6b34ff933c8c7a0bad9ff5..59bcdd09a50ceccc14abc1522e3a90e52b8ec97f 100644 (file)
@@ -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,
-
-      /**
-       * 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,
+        /**
+         * 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,
     }
 
     @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.
index 0bbe5b2972ca8f4684414bdb1cad1c7810dad989..6c536e8c091a6e733116c80b9b802e523f7f450b 100644 (file)
@@ -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;
         }
     }
index 406a21094468c628e2ac6c668d846f903ebb595a..c4db104e5f60bcdd4ea7bd8412e0bb42ac5b336a 100644 (file)
@@ -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;
index 13b4d9112847d07142428df0c3638fb4ac491567..524ac2ce4ea98334b4b625d73ba295a113c50841 100644 (file)
@@ -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();