]> granicus.if.org Git - icu/commitdiff
ICU-13513 Down to line 916 in the data-driven test file.
authorShane Carr <shane@unicode.org>
Thu, 14 Dec 2017 07:04:41 +0000 (07:04 +0000)
committerShane Carr <shane@unicode.org>
Thu, 14 Dec 2017 07:04:41 +0000 (07:04 +0000)
X-SVN-Rev: 40734

17 files changed:
icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/DecimalQuantity_AbstractBCD.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/AffixMatcher.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/DecimalMatcher.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/MinusSignMatcher.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/NumberParserImpl.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/ParsedNumber.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/PlusSignMatcher.java [new file with mode: 0644]
icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/RequireAffixMatcher.java [new file with mode: 0644]
icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/RequireExponentMatcher.java [moved from icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/StrictMatcher.java with 70% similarity]
icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/RequirementsMatcher.java [new file with mode: 0644]
icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/ScientificMatcher.java
icu4j/main/tests/core/src/com/ibm/icu/dev/data/numberformattestspecification.txt
icu4j/main/tests/core/src/com/ibm/icu/dev/test/format/NumberFormatDataDrivenTest.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/DecimalQuantityTest.java
icu4j/main/tests/core/src/com/ibm/icu/dev/test/number/NumberParserTest.java
icu4j/main/tests/core/src/com/ibm/icu/impl/number/DecimalQuantity_SimpleStorage.java

index 368dcfbec17409892adf1b3b4dfb9115b1abb6ad..a5decc3469f244a230aa711f2eeda722853ffddc 100644 (file)
@@ -68,6 +68,11 @@ public interface DecimalQuantity extends PluralRules.IFixedDecimal {
    */
   public void roundToInfinity();
 
+  /**
+   * Truncates the decimals from this DecimalQuantity. Equivalent to calling roundToMagnitude(0, FLOOR)
+   */
+  void truncate();
+
   /**
    * Multiply the internal value.
    *
index b442cf1f3ee5b03f278ab9e12075136e9711ac0d..25025ba01e0ef21587266dc14daff2971b00a57e 100644 (file)
@@ -785,6 +785,15 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
     }
   }
 
+  @Override
+  public void truncate() {
+      if (scale < 0) {
+          shiftRight(-scale);
+          scale = 0;
+          compact();
+      }
+  }
+
   /**
    * Appends a digit, optionally with one or more leading zeros, to the end of the value represented
    * by this DecimalQuantity.
index a9671589a31ef4b382678e8faa0e9d0ed85591df..2ff153cc5b834488ea8d4830fe73e90c3348c32e 100644 (file)
@@ -2,6 +2,7 @@
 // License & terms of use: http://www.unicode.org/copyright.html#License
 package com.ibm.icu.impl.number.parse;
 
+import java.util.Collection;
 import java.util.Comparator;
 import java.util.Set;
 import java.util.TreeSet;
@@ -47,6 +48,7 @@ public class AffixMatcher implements NumberParseMatcher {
     public static void generateFromPatternModifier(
             MutablePatternModifier patternModifier,
             int flags,
+            boolean includeUnpaired,
             NumberParserImpl output) {
 
         // Store the matchers in a TreeSet to ensure both uniqueness and order.
@@ -64,11 +66,11 @@ public class AffixMatcher implements NumberParseMatcher {
             if (patternModifier.needsPlurals()) {
                 for (StandardPlural plural : StandardPlural.VALUES) {
                     patternModifier.setNumberProperties(isNegative, plural);
-                    matchers.add(getInstance(patternModifier, flags, nsb));
+                    AffixMatcher.createAndAppendTo(matchers, patternModifier, flags, nsb, includeUnpaired);
                 }
             } else {
                 patternModifier.setNumberProperties(isNegative, null);
-                matchers.add(getInstance(patternModifier, flags, nsb));
+                AffixMatcher.createAndAppendTo(matchers, patternModifier, flags, nsb, includeUnpaired);
             }
 
             if (isNegative) {
@@ -84,13 +86,17 @@ public class AffixMatcher implements NumberParseMatcher {
     }
 
     /**
-     * Constructs an AffixMatcher from the given MutablePatternModifier and flags. The NumberStringBuilder is used as a
-     * temporary object only.
+     * Constructs one or more AffixMatchers from the given MutablePatternModifier and flags, appending them to the given
+     * collection. The NumberStringBuilder is used as a temporary object only.
+     *
+     * @param includeUnpaired If true, create additional AffixMatchers with an unpaired prefix or suffix.
      */
-    private static AffixMatcher getInstance(
+    private static void createAndAppendTo(
+            Collection<AffixMatcher> appendTo,
             MutablePatternModifier patternModifier,
             int flags,
-            NumberStringBuilder nsb) {
+            NumberStringBuilder nsb,
+            boolean includeUnpaired) {
         // TODO: Make this more efficient (avoid the substrings and things)
         nsb.clear();
         patternModifier.apply(nsb, 0, 0);
@@ -98,7 +104,11 @@ public class AffixMatcher implements NumberParseMatcher {
         String full = nsb.toString();
         String prefix = full.substring(0, prefixLength);
         String suffix = full.substring(prefixLength);
-        return new AffixMatcher(prefix, suffix, flags);
+        appendTo.add(new AffixMatcher(prefix, suffix, flags));
+        if (includeUnpaired && !prefix.isEmpty() && !suffix.isEmpty()) {
+            appendTo.add(new AffixMatcher(prefix, "", flags));
+            appendTo.add(new AffixMatcher("", suffix, flags));
+        }
     }
 
     private AffixMatcher(String prefix, String suffix, int flags) {
@@ -147,6 +157,10 @@ public class AffixMatcher implements NumberParseMatcher {
     public void postProcess(ParsedNumber result) {
         // Check to see if our affix is the one that was matched. If so, set the flags in the result.
         if (prefix.equals(orEmpty(result.prefix)) && suffix.equals(orEmpty(result.suffix))) {
+            // Fill in the result prefix and suffix with non-null values (empty string).
+            // Used by strict mode to determine whether an entire affix pair was matched.
+            result.prefix = prefix;
+            result.suffix = suffix;
             result.flags |= flags;
         }
     }
index 8cbe29397ac6e764981bdedc77c463da4b2839c5..09165b43feca2e395ac6d92768e289ac4efe1428 100644 (file)
@@ -2,10 +2,7 @@
 // License & terms of use: http://www.unicode.org/copyright.html#License
 package com.ibm.icu.impl.number.parse;
 
-import java.math.RoundingMode;
-
 import com.ibm.icu.impl.number.DecimalQuantity_DualStorageBCD;
-import com.ibm.icu.impl.number.RoundingUtils;
 import com.ibm.icu.lang.UCharacter;
 import com.ibm.icu.text.DecimalFormatSymbols;
 import com.ibm.icu.text.UnicodeSet;
@@ -30,8 +27,10 @@ public class DecimalMatcher implements NumberParseMatcher {
 
     public static DecimalMatcher getInstance(DecimalFormatSymbols symbols) {
         String groupingSeparator = symbols.getGroupingSeparatorString();
-        UnicodeSet groupingSet = UNISET_COMMA_LIKE.contains(groupingSeparator) ? UNISET_COMMA_LIKE.cloneAsThawed().addAll(UNISET_OTHER_GROUPING_SEPARATORS).freeze()
-                : UNISET_PERIOD_LIKE.contains(groupingSeparator) ? UNISET_PERIOD_LIKE.cloneAsThawed().addAll(UNISET_OTHER_GROUPING_SEPARATORS).freeze()
+        UnicodeSet groupingSet = UNISET_COMMA_LIKE.contains(groupingSeparator)
+                ? UNISET_COMMA_LIKE.cloneAsThawed().addAll(UNISET_OTHER_GROUPING_SEPARATORS).freeze()
+                : UNISET_PERIOD_LIKE.contains(groupingSeparator)
+                        ? UNISET_PERIOD_LIKE.cloneAsThawed().addAll(UNISET_OTHER_GROUPING_SEPARATORS).freeze()
                         : UNISET_OTHER_GROUPING_SEPARATORS.contains(groupingSeparator)
                                 ? UNISET_OTHER_GROUPING_SEPARATORS
                                 : new UnicodeSet().addAll(groupingSeparator).freeze();
@@ -45,8 +44,10 @@ public class DecimalMatcher implements NumberParseMatcher {
     }
 
     public static DecimalMatcher getExponentInstance(DecimalFormatSymbols symbols) {
-        return new DecimalMatcher(symbols
-                .getDigitStrings(), new UnicodeSet("[,]").freeze(), new UnicodeSet("[.]").freeze(), true);
+        return new DecimalMatcher(symbols.getDigitStrings(),
+                new UnicodeSet("[,]").freeze(),
+                new UnicodeSet("[.]").freeze(),
+                true);
     }
 
     private final String[] digitStrings;
@@ -55,8 +56,9 @@ public class DecimalMatcher implements NumberParseMatcher {
     private final UnicodeSet separatorSet;
     public boolean requireGroupingMatch = false;
     public boolean groupingEnabled = true;
-    private final int grouping1 = 3;
-    private final int grouping2 = 3;
+    public int grouping1 = 3;
+    public int grouping2 = 3;
+    public boolean integerOnly = false;
     private final boolean isScientific;
 
     private DecimalMatcher(
@@ -82,10 +84,11 @@ public class DecimalMatcher implements NumberParseMatcher {
             return false;
         }
 
+        int initialOffset = segment.getOffset();
         int currGroup = 0;
         int separator = -1;
         int lastSeparatorOffset = segment.getOffset();
-        int scientificAdjustment = 0;
+        int exponent = 0;
         boolean hasPartialPrefix = false;
         boolean seenBothSeparators = false;
         while (segment.length() > 0) {
@@ -118,7 +121,7 @@ public class DecimalMatcher implements NumberParseMatcher {
             // If found, save it in the DecimalQuantity or scientific adjustment.
             if (digit >= 0) {
                 if (isScientific) {
-                    scientificAdjustment = digit + scientificAdjustment * 10;
+                    exponent = digit + exponent * 10;
                 } else {
                     if (result.quantity == null) {
                         result.quantity = new DecimalQuantity_DualStorageBCD();
@@ -163,15 +166,30 @@ public class DecimalMatcher implements NumberParseMatcher {
         }
 
         if (isScientific) {
-            result.quantity.adjustMagnitude(scientificAdjustment);
+            result.quantity.adjustMagnitude(exponent);
         } else if (seenBothSeparators || (separator != -1 && decimalUniSet.contains(separator))) {
+            // The final separator was a decimal separator.
+            result.quantity.adjustMagnitude(-currGroup);
+            if (integerOnly) {
+                result.quantity.truncate();
+                segment.setOffset(lastSeparatorOffset);
+            }
+        } else if (separator != -1 && !groupingEnabled) {
+            // The final separator was a grouping separator, but we aren't accepting grouping.
+            // Reset the offset to immediately before that grouping separator.
             result.quantity.adjustMagnitude(-currGroup);
-        } else if (separator != -1
-                && ((requireGroupingMatch && groupingUniSet.contains(separator) && currGroup != grouping1)
-                        || !groupingEnabled)) {
+            result.quantity.truncate();
+            segment.setOffset(lastSeparatorOffset);
+        } else if (separator != -1 && requireGroupingMatch && groupingUniSet.contains(separator)
+                && currGroup != grouping1) {
+            // The final separator was a grouping separator, and we have a mismatched grouping size.
+            // Reset the offset to the beginning of the number.
+            // TODO
             result.quantity.adjustMagnitude(-currGroup);
-            result.quantity.roundToMagnitude(0, RoundingUtils.mathContextUnlimited(RoundingMode.FLOOR));
+            result.quantity.truncate();
             segment.setOffset(lastSeparatorOffset);
+//            result.quantity = null;
+//            segment.setOffset(initialOffset);
         }
 
         return segment.length() == 0 || hasPartialPrefix || segment.isLeadingSurrogate();
index 707fd44e55bec54343575662e34642402fddda37..1e4e4f1abc8aeaa80f5f7aac57e0b714849fb17c 100644 (file)
@@ -11,6 +11,7 @@ import com.ibm.icu.text.UnicodeSet;
 public class MinusSignMatcher extends SymbolMatcher {
 
     public MinusSignMatcher() {
+        // FIXME
         super("-", new UnicodeSet("[-_]"));
     }
 
index c15db2f808f920f8e62a3a247f7d433cc0f88e74..08202f44d01d573e83c70e9d4a75c8b2d97fa882 100644 (file)
@@ -32,7 +32,7 @@ public class NumberParserImpl {
         // Temporary frontend for testing.
 
         NumberParserImpl parser = new NumberParserImpl();
-        ULocale locale = ULocale.ENGLISH;
+        ULocale locale = new ULocale("en_IN");
         DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
 
         MutablePatternModifier mod = new MutablePatternModifier(false);
@@ -47,15 +47,18 @@ public class NumberParserImpl {
         if (provider.containsSymbolType(AffixUtils.TYPE_PERMILLE)) {
             flags |= ParsedNumber.FLAG_PERMILLE;
         }
-        AffixMatcher.generateFromPatternModifier(mod, flags, parser);
+        AffixMatcher.generateFromPatternModifier(mod, flags, true, parser);
 
+        parser.addMatcher(WhitespaceMatcher.getInstance());
         DecimalMatcher decimalMatcher = DecimalMatcher.getInstance(symbols);
         decimalMatcher.requireGroupingMatch = strictGrouping;
+        decimalMatcher.grouping1 = 3;
+        decimalMatcher.grouping2 = 2;
         parser.addMatcher(decimalMatcher);
-        parser.addMatcher(WhitespaceMatcher.getInstance());
         parser.addMatcher(new MinusSignMatcher());
         parser.addMatcher(new ScientificMatcher(symbols));
         parser.addMatcher(new CurrencyMatcher(locale));
+        parser.addMatcher(new RequirementsMatcher());
 
         parser.freeze();
         return parser;
@@ -64,20 +67,27 @@ public class NumberParserImpl {
     public static Number parseStatic(String input,
       ParsePosition ppos,
       DecimalFormatProperties properties,
-      DecimalFormatSymbols symbols) {
-        NumberParserImpl parser = createParserFromProperties(properties, symbols);
+      DecimalFormatSymbols symbols,
+      boolean parseCurrency) {
+        NumberParserImpl parser = createParserFromProperties(properties, symbols, parseCurrency);
         ParsedNumber result = new ParsedNumber();
         parser.parse(input, true, result);
         ppos.setIndex(result.charsConsumed);
-        return result.getDouble();
+        if (result.charsConsumed > 0) {
+            return result.getDouble();
+        } else {
+            return null;
+        }
     }
 
     public static NumberParserImpl createParserFromProperties(
             DecimalFormatProperties properties,
-            DecimalFormatSymbols symbols) {
+            DecimalFormatSymbols symbols,
+            boolean parseCurrency) {
         NumberParserImpl parser = new NumberParserImpl();
         ULocale locale = symbols.getULocale();
         Currency currency = CustomSymbolCurrency.resolve(properties.getCurrency(), locale, symbols);
+        boolean isStrict = properties.getParseMode() == ParseMode.STRICT;
 
         //////////////////////
         /// AFFIX MATCHERS ///
@@ -100,25 +110,40 @@ public class NumberParserImpl {
             flags |= ParsedNumber.FLAG_PERMILLE;
         }
 
-        AffixMatcher.generateFromPatternModifier(mod, flags, parser);
+        AffixMatcher.generateFromPatternModifier(mod, flags, !isStrict, parser);
 
         ///////////////////////////////
         /// OTHER STANDARD MATCHERS ///
         ///////////////////////////////
 
+        if (!isStrict) {
+            parser.addMatcher(WhitespaceMatcher.getInstance());
+            parser.addMatcher(new PlusSignMatcher());
+        }
+        parser.addMatcher(new MinusSignMatcher());
         DecimalMatcher decimalMatcher = DecimalMatcher.getInstance(symbols);
         decimalMatcher.groupingEnabled = properties.getGroupingSize() > 0;
-        decimalMatcher.requireGroupingMatch = properties.getParseMode() == ParseMode.STRICT;
+        decimalMatcher.requireGroupingMatch = isStrict;
+        decimalMatcher.grouping1 = properties.getGroupingSize();
+        decimalMatcher.grouping2 = properties.getSecondaryGroupingSize();
+        decimalMatcher.integerOnly = properties.getParseIntegerOnly();
         parser.addMatcher(decimalMatcher);
-        parser.addMatcher(WhitespaceMatcher.getInstance());
-        parser.addMatcher(new MinusSignMatcher());
         parser.addMatcher(new ScientificMatcher(symbols));
+        parser.addMatcher(new RequirementsMatcher());
+        if (isStrict) {
+            parser.addMatcher(new RequireAffixMatcher());
+        }
+        if (isStrict && properties.getMinimumExponentDigits() > 0) {
+            parser.addMatcher(new RequireExponentMatcher());
+        }
 
         ////////////////////////
         /// CURRENCY MATCHER ///
         ////////////////////////
 
-        parser.addMatcher(new CurrencyMatcher(locale));
+        if (parseCurrency) {
+            parser.addMatcher(new CurrencyMatcher(locale));
+        }
 
         parser.freeze();
         return parser;
index ae847a28fd7addb777239f53662574ad2c23f025..b3bc662106b58569bac089ff5628508a840b32fb 100644 (file)
@@ -12,38 +12,39 @@ import com.ibm.icu.impl.number.DecimalQuantity_DualStorageBCD;
  */
 public class ParsedNumber {
 
-    public DecimalQuantity_DualStorageBCD quantity = null;
+    public DecimalQuantity_DualStorageBCD quantity;
 
     /**
      * The number of chars accepted during parsing. This is NOT necessarily the same as the StringSegment offset; "weak"
      * chars, like whitespace, change the offset, but the charsConsumed is not touched until a "strong" char is
      * encountered.
      */
-    public int charsConsumed = 0;
+    public int charsConsumed;
 
     /**
      * Boolean flags (see constants below).
      */
-    public int flags = 0;
+    public int flags;
 
     /**
      * The prefix string that got consumed.
      */
-    public String prefix = null;
+    public String prefix;
 
     /**
      * The suffix string that got consumed.
      */
-    public String suffix = null;
+    public String suffix;
 
     /**
      * The currency that got consumed.
      */
-    public String currencyCode = null;
+    public String currencyCode;
 
     public static final int FLAG_NEGATIVE = 0x0001;
     public static final int FLAG_PERCENT = 0x0002;
     public static final int FLAG_PERMILLE = 0x0004;
+    public static final int FLAG_HAS_EXPONENT = 0x0008;
 
     /** A Comparator that favors ParsedNumbers with the most chars consumed. */
     public static final Comparator<ParsedNumber> COMPARATOR = new Comparator<ParsedNumber>() {
@@ -53,11 +54,24 @@ public class ParsedNumber {
         }
     };
 
+    public ParsedNumber() {
+        clear();
+    }
+
     /**
-     * @param other
+     * Clears the data from this ParsedNumber, in effect failing the current parse.
      */
+    public void clear() {
+        quantity = null;
+        charsConsumed = 0;
+        flags = 0;
+        prefix = null;
+        suffix = null;
+        currencyCode = null;
+    }
+
     public void copyFrom(ParsedNumber other) {
-        quantity = other.quantity;
+        quantity = other.quantity == null ? null : (DecimalQuantity_DualStorageBCD) other.quantity.createCopy();
         charsConsumed = other.charsConsumed;
         flags = other.flags;
         prefix = other.prefix;
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/PlusSignMatcher.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/PlusSignMatcher.java
new file mode 100644 (file)
index 0000000..b9cdff8
--- /dev/null
@@ -0,0 +1,33 @@
+// © 2017 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html#License
+package com.ibm.icu.impl.number.parse;
+
+import com.ibm.icu.text.UnicodeSet;
+
+/**
+ * @author sffc
+ *
+ */
+public class PlusSignMatcher extends SymbolMatcher {
+
+    public PlusSignMatcher() {
+        // FIXME
+        super("+", new UnicodeSet("[+]"));
+    }
+
+    @Override
+    protected boolean isDisabled(ParsedNumber result) {
+        return false;
+    }
+
+    @Override
+    protected void accept(ParsedNumber result) {
+        // No-op
+    }
+
+    @Override
+    public String toString() {
+        return "<PlusSignMatcher>";
+    }
+
+}
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/RequireAffixMatcher.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/RequireAffixMatcher.java
new file mode 100644 (file)
index 0000000..3398753
--- /dev/null
@@ -0,0 +1,24 @@
+// © 2017 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html#License
+package com.ibm.icu.impl.number.parse;
+
+/**
+ * @author sffc
+ *
+ */
+public class RequireAffixMatcher implements NumberParseMatcher {
+
+    @Override
+    public boolean match(StringSegment segment, ParsedNumber result) {
+        return false;
+    }
+
+    @Override
+    public void postProcess(ParsedNumber result) {
+        if ((result.prefix == null) != (result.suffix == null)) {
+            // We saw a prefix or a suffix but not both. Fail the parse.
+            result.clear();
+        }
+    }
+
+}
similarity index 70%
rename from icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/StrictMatcher.java
rename to icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/RequireExponentMatcher.java
index 0f9b3905852da1a33f360600165888cfce1a2bd6..fff8558a0f445ad0c2df8daa6324de175359e6a7 100644 (file)
@@ -6,7 +6,7 @@ package com.ibm.icu.impl.number.parse;
  * @author sffc
  *
  */
-public class StrictMatcher implements NumberParseMatcher {
+public class RequireExponentMatcher implements NumberParseMatcher {
 
     @Override
     public boolean match(StringSegment segment, ParsedNumber result) {
@@ -15,8 +15,8 @@ public class StrictMatcher implements NumberParseMatcher {
 
     @Override
     public void postProcess(ParsedNumber result) {
-        if (result.prefix == null && result.suffix == null) {
-            // Do something?
+        if (0 == (result.flags & ParsedNumber.FLAG_HAS_EXPONENT)) {
+            result.clear();
         }
     }
 
diff --git a/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/RequirementsMatcher.java b/icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/RequirementsMatcher.java
new file mode 100644 (file)
index 0000000..088cf03
--- /dev/null
@@ -0,0 +1,24 @@
+// © 2017 and later: Unicode, Inc. and others.
+// License & terms of use: http://www.unicode.org/copyright.html#License
+package com.ibm.icu.impl.number.parse;
+
+/**
+ * @author sffc
+ *
+ */
+public class RequirementsMatcher implements NumberParseMatcher {
+
+    @Override
+    public boolean match(StringSegment segment, ParsedNumber result) {
+        return false;
+    }
+
+    @Override
+    public void postProcess(ParsedNumber result) {
+        // Require that a number is matched.
+        if (result.quantity == null) {
+            result.clear();
+        }
+    }
+
+}
index 2b4549289dffa34df14d1206637584e73764941e..bf58e976403837a06feaca22cae5dcce3879db55 100644 (file)
@@ -33,9 +33,9 @@ public class ScientificMatcher implements NumberParseMatcher {
             // Full exponent separator match; allow a sign, and then try to match digits.
             segment.adjustOffset(overlap1);
             int overlap2 = segment.getCommonPrefixLength(minusSignString);
-            boolean sign = false;
+            boolean minusSign = false;
             if (overlap2 == minusSignString.length()) {
-                sign = true;
+                minusSign = true;
                 segment.adjustOffset(overlap2);
             } else if (overlap2 == segment.length()) {
                 // Partial sign match
@@ -45,11 +45,18 @@ public class ScientificMatcher implements NumberParseMatcher {
             int digitsOffset = segment.getOffset();
             int oldMagnitude = result.quantity.getMagnitude();
             boolean digitsReturnValue = exponentMatcher.match(segment, result);
-            if (result.quantity.getMagnitude() != oldMagnitude && sign) {
-                result.quantity.adjustMagnitude(2*(oldMagnitude - result.quantity.getMagnitude()));
-            }
-            if (segment.getOffset() == digitsOffset) {
-                // No digits were matched; un-match the exponent separator.
+            if (segment.getOffset() != digitsOffset) {
+                // At least one exponent digit was matched.
+                result.flags |= ParsedNumber.FLAG_HAS_EXPONENT;
+                if (minusSign) {
+                    // Switch the magnitude adjustment from positive to negative. Extracting the exponent from the
+                    // change in the quantity's magnitude feels a bit like a hack; better would be for the
+                    // DecimalMatcher to somehow return the exponent directly.
+                    int exponent = result.quantity.getMagnitude() - oldMagnitude;
+                    result.quantity.adjustMagnitude(-2 * exponent);
+                }
+            } else {
+                // No exponent digits were matched; un-match the exponent separator.
                 segment.adjustOffset(-overlap1);
             }
             return digitsReturnValue;
index 96868920c68aa29b3f880ed9ad948ad57fcaf5d4..c210c71fff0c0eae4cb374eefcc651e9575358a1 100644 (file)
@@ -736,7 +736,7 @@ parse       output  breaks
 (5,347.25)     -5347.25
 // J requires prefix and suffix for lenient parsing, but C doesn't
 5,347.25       5347.25 JK
-(5,347.25      -5347.25        JP
+(5,347.25      -5347.25        J
 // S is successful at parsing this as -5347.25 in lenient mode
 -5,347.25      -5347.25        CJK
 +3.52E4        35200
@@ -750,8 +750,7 @@ parse       output  breaks
 (34,,25 E-1)   -342.5  CJK
 // Spaces are not allowed after exponent symbol
 // C parses up to the E but J bails
-// P does not make the number negative
-(34  25E -1)   -3425   JKP
+(34  25E -1)   -3425   JK
 +3.52EE4       3.52
 +1,234,567.8901        1234567.8901
 +1,23,4567.8901        1234567.8901
@@ -776,19 +775,18 @@ parse     output  breaks
 (  19 45 )     -1945   JK
 (,,19,45)      -1945
 // C parses to the space, but J bails
-// P makes the number positive
-(,,19 45)      -19     JP
+(,,19 45)      -19     J
 // J bails b/c comma different separator than space. C doesn't treat leading spaces
 // as a separator.
-(  19,45)      -1945   JKP
+(  19,45)      -1945   JK
 // J bails. Doesn't allow trailing separators when there is prefix and suffix.
 (,,19,45,)     -1945   J
 // J bails on next 4 because J doesn't allow letters inside prefix and suffix.
 // C will parse up to the letter.
-(,,19,45,d1)   -1945   JP
-(,,19,45d1)    -1945   JP
-(  19 45 d1)   -1945   JKP
-(  19 45d1)    -1945   JKP
+(,,19,45,d1)   -1945   J
+(,,19,45d1)    -1945   J
+(  19 45 d1)   -1945   JK
+(  19 45d1)    -1945   JK
 // J does allow trailing separator before a decimal point
 (19,45,.25)    -1945.25
 // 2nd decimal points are ignored
@@ -831,17 +829,16 @@ parse     output  breaks
 (65347.25)     -65347.25
 (65,347.25)    -65347.25
 // JDK does allow separators in the wrong place and parses as -5347.25
-(53,47.25)     fail    KP
+(53,47.25)     fail    K
 // strict requires prefix or suffix, except in C
-65,347.25      fail    
+65,347.25      fail    P
 +3.52E4        35200
 (34.8E-3)      -0.0348
 (3425E-1)      -342.5
 // Strict doesn't allow separators in sci notation.
 (63,425)       -63425
-// JDK and S allow separators in sci notation and parses as -342.5
-// C passes
-(63,425E-1)    fail    CKS
+// J does not allow grouping separators in scientific notation.
+(63,425E-1)    -6342.5 J
 // Both prefix and suffix needed for strict.
 // JDK accepts this and parses as -342.5
 (3425E-1       fail    K
@@ -855,10 +852,11 @@ parse     output  breaks
 // have no separators at all.
 +12,345.67     12345.67
 // JDK doesn't require separators to be in the right place.
-+1,23,4567.8901        fail    K
-+1,234,567.8901        fail    K
-+1234,567.8901 fail    K
-+1,234567.8901 fail    K
+// P stops after reading an unexpected grouping separator instead of failing.
++1,23,4567.8901        fail    KP
++1,234,567.8901        fail    KP
++1234,567.8901 fail    KP
++1,234567.8901 fail    KP
 +1234567.8901  1234567.8901
 // Minimum grouping is not satisfied below, but that's ok
 // because minimum grouping is optional.
index 2f5b23a21a662457d966afd438793524007aca56..ef4e2f8c70aa9b54439cf0755762b49f84051071 100644 (file)
@@ -600,7 +600,8 @@ public class NumberFormatDataDrivenTest {
                     actual = NumberParserImpl.parseStatic(tuple.parse,
                             ppos,
                             properties,
-                            DecimalFormatSymbols.getInstance(tuple.locale));
+                            DecimalFormatSymbols.getInstance(tuple.locale),
+                            false);
                 } catch (IllegalArgumentException e) {
                     return "parse exception: " + e.getMessage();
                 }
index 786ea1a4ecfe9be47bd0beaab2a602c50b11e0e0..5ceea86d5ceba0d76935efec30ed75e3e234f812 100644 (file)
@@ -174,6 +174,7 @@ public class DecimalQuantityTest extends TestFmwk {
   private static final MathContext MATH_CONTEXT_HALF_EVEN =
       new MathContext(0, RoundingMode.HALF_EVEN);
   private static final MathContext MATH_CONTEXT_CEILING = new MathContext(0, RoundingMode.CEILING);
+  private static final MathContext MATH_CONTEXT_FLOOR = new MathContext(0, RoundingMode.FLOOR);
   private static final MathContext MATH_CONTEXT_PRECISION =
       new MathContext(3, RoundingMode.HALF_UP);
 
@@ -195,6 +196,18 @@ public class DecimalQuantityTest extends TestFmwk {
     q0.roundToMagnitude(-1, MATH_CONTEXT_PRECISION);
     q1.roundToMagnitude(-1, MATH_CONTEXT_PRECISION);
     testDecimalQuantityBehavior(q0, q1);
+
+    q0 = rq0.createCopy();
+    q1 = rq1.createCopy();
+    q0.roundToMagnitude(0, MATH_CONTEXT_FLOOR);
+    q1.truncate();
+    testDecimalQuantityBehavior(q0, q1);
+
+    q0 = rq0.createCopy();
+    q1 = rq1.createCopy();
+    q0.truncate();
+    q1.roundToMagnitude(0, MATH_CONTEXT_FLOOR);
+    testDecimalQuantityBehavior(q0, q1);
   }
 
   private static void testDecimalQuantityRoundingInterval(DecimalQuantity rq0, DecimalQuantity rq1) {
index fcc58aedae4cfcbdaddb7c6d207163fe85c3df5e..5759c96857b1a8b1a514dbc97e830d8eb5daa8ae 100644 (file)
@@ -36,7 +36,8 @@ public class NumberParserTest {
                 { 3, " 𝟱𝟭𝟰𝟮𝟯", "0", 11, 51423. },
                 { 3, "𝟱𝟭𝟰𝟮𝟯 ", "0", 10, 51423. },
                 { 7, "𝟱𝟭,𝟰𝟮𝟯", "0", 11, 51423. },
-                { 7, "𝟳𝟴,𝟵𝟱𝟭,𝟰𝟮𝟯", "0", 18, 78951423. },
+                { 7, "𝟳,𝟴𝟵,𝟱𝟭,𝟰𝟮𝟯", "0", 19, 78951423. },
+                { 4, "𝟳𝟴,𝟵𝟱𝟭,𝟰𝟮𝟯", "0", 11, 78951. },
                 { 7, "𝟳𝟴,𝟵𝟱𝟭.𝟰𝟮𝟯", "0", 18, 78951.423 },
                 { 7, "𝟳𝟴,𝟬𝟬𝟬", "0", 11, 78000. },
                 { 7, "𝟳𝟴,𝟬𝟬𝟬.𝟬𝟬𝟬", "0", 18, 78000. },
@@ -49,6 +50,10 @@ public class NumberParserTest {
                 { 3, "a 𝟱𝟭𝟰𝟮𝟯 b", "a0b", 14, 51423. },
                 { 3, "-a 𝟱𝟭𝟰𝟮𝟯 b", "a0b", 15, -51423. },
                 { 3, "a -𝟱𝟭𝟰𝟮𝟯 b", "a0b", 15, -51423. },
+                { 3, "𝟱𝟭𝟰𝟮𝟯", "0;(0)", 10, 51423. },
+                { 3, "(𝟱𝟭𝟰𝟮𝟯)", "0;(0)", 12, -51423. },
+                { 3, "𝟱𝟭𝟰𝟮𝟯)", "0;(0)", 11, -51423. },
+                { 3, "(𝟱𝟭𝟰𝟮𝟯", "0;(0)", 11, -51423. },
                 { 1, "a40b", "a0'0b'", 3, 40. }, // greedy code path thinks "40" is the number
                 { 2, "a40b", "a0'0b'", 4, 4. }, // slow code path find the suffix "0b"
                 { 3, "𝟱.𝟭𝟰𝟮E𝟯", "0", 12, 5142. },
index e078020c3fbf46c87cbe2d71da133e1ac99374c6..c78265381aa61348aa1368adc36ce3ef451730fc 100644 (file)
@@ -411,6 +411,11 @@ public class DecimalQuantity_SimpleStorage implements DecimalQuantity {
     // noop
   }
 
+  @Override
+  public void truncate() {
+      roundToMagnitude(0, RoundingUtils.mathContextUnlimited(RoundingMode.FLOOR));
+  }
+
   /**
    * Multiply the internal number by the specified multiplicand. This method forces the internal
    * representation into a BigDecimal. If you are multiplying by a power of 10, use {@link