]> granicus.if.org Git - icu/commitdiff
ICU-13513 A few more compatibility tweaks.
authorShane Carr <shane@unicode.org>
Tue, 30 Jan 2018 06:08:15 +0000 (06:08 +0000)
committerShane Carr <shane@unicode.org>
Tue, 30 Jan 2018 06:08:15 +0000 (06:08 +0000)
X-SVN-Rev: 40829

icu4j/main/classes/core/src/com/ibm/icu/impl/Utility.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/AffixUtils.java
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/MutablePatternModifier.java
icu4j/main/classes/core/src/com/ibm/icu/impl/number/parse/AffixMatcher.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/number/AffixUtilsTest.java

index 061956379f7757ea36e14ac21f3ad599a233a748..111e898152a237e8e850afe2c7d31c3b89c90a30 100644 (file)
@@ -1848,4 +1848,19 @@ public final class Utility {
     public static String toString(Object o) {
         return o == null ? "null" : o.toString();
     }
+
+    /**
+     * This implementation is equivalent to Java 8+ {@link Math#addExact(int, int)}
+     * @param x the first value
+     * @param y the second value
+     * @return the result
+     */
+    public static int addExact(int x, int y) {
+        int r = x + y;
+        // HD 2-12 Overflow iff both arguments have the opposite sign of the result
+        if (((x ^ r) & (y ^ r)) < 0) {
+            throw new ArithmeticException("integer overflow");
+        }
+        return r;
+    }
 }
index 19ff1f52f5a663122ae48ad961f709e5c401b2f0..746cbd0a73528245857eb350cf3b47e3adb82d19 100644 (file)
@@ -331,28 +331,35 @@ public class AffixUtils {
     }
 
     /**
-     * Sames as {@link #unescape}, but only calculates the code point count. More efficient than
-     * {@link #unescape} if you only need the length but not the string itself.
+     * Sames as {@link #unescape}, but only calculates the length or code point count. More efficient
+     * than {@link #unescape} if you only need the length but not the string itself.
      *
      * @param affixPattern
      *            The original string to be unescaped.
+     * @param lengthOrCount
+     *            true to count length (UTF-16 code units); false to count code points
      * @param provider
      *            An object to generate locale symbols.
      * @return The number of code points in the unescaped string.
      */
-    public static int unescapedCodePointCount(CharSequence affixPattern, SymbolProvider provider) {
+    public static int unescapedCount(
+            CharSequence affixPattern,
+            boolean lengthOrCount,
+            SymbolProvider provider) {
         int length = 0;
         long tag = 0L;
         while (hasNext(tag, affixPattern)) {
             tag = nextToken(tag, affixPattern);
             int typeOrCp = getTypeOrCp(tag);
             if (typeOrCp == TYPE_CURRENCY_OVERFLOW) {
+                // U+FFFD is one char
                 length += 1;
             } else if (typeOrCp < 0) {
                 CharSequence symbol = provider.getSymbol(typeOrCp);
-                length += Character.codePointCount(symbol, 0, symbol.length());
+                length += lengthOrCount ? symbol.length()
+                        : Character.codePointCount(symbol, 0, symbol.length());
             } else {
-                length += 1;
+                length += lengthOrCount ? Character.charCount(typeOrCp) : 1;
             }
         }
         return length;
@@ -429,44 +436,6 @@ public class AffixUtils {
         return new String(chars);
     }
 
-    /**
-     * Appends a new affix pattern with all symbols and code points in the given "ignorables" UnicodeSet
-     * trimmed from the beginning and end. Similar to calling unescape with a symbol provider that always
-     * returns the empty string.
-     *
-     * <p>
-     * Accepts and returns a StringBuilder, allocating it only if necessary.
-     */
-    public static StringBuilder trimSymbolsAndIgnorables(
-            CharSequence affixPattern,
-            UnicodeSet ignorables,
-            StringBuilder sb) {
-        assert affixPattern != null;
-        long tag = 0L;
-        int trailingIgnorables = 0;
-        while (hasNext(tag, affixPattern)) {
-            tag = nextToken(tag, affixPattern);
-            int typeOrCp = getTypeOrCp(tag);
-            if (typeOrCp >= 0) {
-                if (!ignorables.contains(typeOrCp)) {
-                    if (sb == null) {
-                        // Lazy-initialize the StringBuilder
-                        sb = new StringBuilder();
-                    }
-                    sb.appendCodePoint(typeOrCp);
-                    trailingIgnorables = 0;
-                } else if (sb != null && sb.length() > 0) {
-                    sb.appendCodePoint(typeOrCp);
-                    trailingIgnorables += Character.charCount(typeOrCp);
-                }
-            }
-        }
-        if (trailingIgnorables > 0) {
-            sb.setLength(sb.length() - trailingIgnorables);
-        }
-        return sb;
-    }
-
     /**
      * Returns whether the given affix pattern contains only symbols and ignorables as defined by the
      * given ignorables set.
@@ -488,6 +457,9 @@ public class AffixUtils {
         return true;
     }
 
+    /**
+     * Iterates over the affix pattern, calling the TokenConsumer for each token.
+     */
     public static void iterateWithConsumer(CharSequence affixPattern, TokenConsumer consumer) {
         assert affixPattern != null;
         long tag = 0L;
@@ -510,7 +482,7 @@ public class AffixUtils {
      *         (never negative), or -1 if there were no more tokens in the affix pattern.
      * @see #hasNext
      */
-    public static long nextToken(long tag, CharSequence patternString) {
+    private static long nextToken(long tag, CharSequence patternString) {
         int offset = getOffset(tag);
         int state = getState(tag);
         for (; offset < patternString.length();) {
@@ -662,7 +634,7 @@ public class AffixUtils {
      *            The affix pattern.
      * @return true if there are more tokens to consume; false otherwise.
      */
-    public static boolean hasNext(long tag, CharSequence string) {
+    private static boolean hasNext(long tag, CharSequence string) {
         assert tag >= 0;
         int state = getState(tag);
         int offset = getOffset(tag);
@@ -688,7 +660,7 @@ public class AffixUtils {
      * @return If less than zero, a symbol type corresponding to one of the <code>TYPE_</code> constants,
      *         such as {@link #TYPE_MINUS_SIGN}. If greater than or equal to zero, a literal code point.
      */
-    public static int getTypeOrCp(long tag) {
+    private static int getTypeOrCp(long tag) {
         assert tag >= 0;
         int type = getType(tag);
         return (type == TYPE_CODEPOINT) ? getCodePoint(tag) : -type;
@@ -715,19 +687,19 @@ public class AffixUtils {
         return tag;
     }
 
-    static int getOffset(long tag) {
+    private static int getOffset(long tag) {
         return (int) (tag & 0xffffffff);
     }
 
-    static int getType(long tag) {
+    private static int getType(long tag) {
         return (int) ((tag >>> 32) & 0xf);
     }
 
-    static int getState(long tag) {
+    private static int getState(long tag) {
         return (int) ((tag >>> 36) & 0xf);
     }
 
-    static int getCodePoint(long tag) {
+    private static int getCodePoint(long tag) {
         return (int) (tag >>> 40);
     }
 }
index c3e9734c7cbe4a8db79e05cfceca1c2d6c033aca..1a5d6f73ad6ce38096cd82a97724e6c3d0a1b1de 100644 (file)
@@ -78,11 +78,6 @@ 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 4be22a664a719c4b66aec8d8049d2053acc77749..2e1b416db36721d3c5f4e2a739f57ed203b24400 100644 (file)
@@ -8,6 +8,7 @@ import java.math.MathContext;
 import java.text.FieldPosition;
 
 import com.ibm.icu.impl.StandardPlural;
+import com.ibm.icu.impl.Utility;
 import com.ibm.icu.text.PluralRules;
 import com.ibm.icu.text.PluralRules.Operand;
 import com.ibm.icu.text.UFieldPosition;
@@ -204,9 +205,8 @@ public abstract class DecimalQuantity_AbstractBCD implements DecimalQuantity {
     @Override
     public void adjustMagnitude(int delta) {
         if (precision != 0) {
-            // TODO: Math.addExact is not in 1.6 or 1.7
-            scale = Math.addExact(scale, delta);
-            origDelta = Math.addExact(origDelta, delta);
+            scale = Utility.addExact(scale, delta);
+            origDelta = Utility.addExact(origDelta, delta);
         }
     }
 
@@ -858,15 +858,6 @@ 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 81021ea99c1787d3e2adde10af81552ed8a1ec5f..cc894bebfd94212953ed43bc8c36fed4f1da9554 100644 (file)
@@ -106,7 +106,7 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, MicroPr
             Currency currency,
             UnitWidth unitWidth,
             PluralRules rules) {
-        // assert (rules != null) == needsPlurals();
+        assert (rules != null) == needsPlurals();
         this.symbols = symbols;
         this.currency = currency;
         this.unitWidth = unitWidth;
@@ -282,19 +282,19 @@ public class MutablePatternModifier implements Modifier, SymbolProvider, MicroPr
 
     @Override
     public int getPrefixLength() {
-        // Enter and exit CharSequence Mode to get the length.
+        // Render the affix to get the length
         prepareAffix(true);
-        int result = AffixUtils.unescapedCodePointCount(currentAffix, this); // prefix length
+        int result = AffixUtils.unescapedCount(currentAffix, true, this); // prefix length
         return result;
     }
 
     @Override
     public int getCodePointCount() {
-        // Enter and exit CharSequence Mode to get the length.
+        // Render the affixes to get the length
         prepareAffix(true);
-        int result = AffixUtils.unescapedCodePointCount(currentAffix, this); // prefix length
+        int result = AffixUtils.unescapedCount(currentAffix, false, this); // prefix length
         prepareAffix(false);
-        result += AffixUtils.unescapedCodePointCount(currentAffix, this); // suffix length
+        result += AffixUtils.unescapedCount(currentAffix, false, this); // suffix length
         return result;
     }
 
index 12f3fa3c8354420991067f2af6e7c94d585065f1..58bf69ac7d249fbd017a7e9afb521590202ce26b 100644 (file)
@@ -5,9 +5,9 @@ package com.ibm.icu.impl.number.parse;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
-import java.util.Objects;
 
 import com.ibm.icu.impl.StandardPlural;
+import com.ibm.icu.impl.Utility;
 import com.ibm.icu.impl.number.AffixPatternProvider;
 import com.ibm.icu.impl.number.AffixUtils;
 import com.ibm.icu.impl.number.PatternStringUtils;
@@ -124,7 +124,7 @@ public class AffixMatcher implements NumberParseMatcher {
             if (signum == 1) {
                 posPrefix = prefix;
                 posSuffix = suffix;
-            } else if (Objects.equals(prefix, posPrefix) && Objects.equals(suffix, posSuffix)) {
+            } else if (Utility.equals(prefix, posPrefix) && Utility.equals(suffix, posSuffix)) {
                 // Skip adding these matchers (we already have equivalents)
                 continue;
             }
@@ -137,10 +137,10 @@ public class AffixMatcher implements NumberParseMatcher {
             matchers.add(getInstance(prefix, suffix, flags));
             if (includeUnpaired && prefix != null && suffix != null) {
                 // The following if statements are designed to prevent adding two identical matchers.
-                if (signum == 1 || !Objects.equals(prefix, posPrefix)) {
+                if (signum == 1 || !Utility.equals(prefix, posPrefix)) {
                     matchers.add(getInstance(prefix, null, flags));
                 }
-                if (signum == 1 || !Objects.equals(suffix, posSuffix)) {
+                if (signum == 1 || !Utility.equals(suffix, posSuffix)) {
                     matchers.add(getInstance(null, suffix, flags));
                 }
             }
@@ -255,14 +255,14 @@ public class AffixMatcher implements NumberParseMatcher {
             return false;
         }
         AffixMatcher other = (AffixMatcher) _other;
-        return Objects.equals(prefix, other.prefix)
-                && Objects.equals(suffix, other.suffix)
+        return Utility.equals(prefix, other.prefix)
+                && Utility.equals(suffix, other.suffix)
                 && flags == other.flags;
     }
 
     @Override
     public int hashCode() {
-        return Objects.hashCode(prefix) ^ Objects.hashCode(suffix) ^ flags;
+        return Utility.hashCode(prefix) ^ Utility.hashCode(suffix) ^ flags;
     }
 
     @Override
index 3383fad20b2a36447e0a3873cd6a3673a5890671..b910a9ca739905cd8b7f04aa76e4170b29b6fe36 100644 (file)
@@ -9,7 +9,6 @@ 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;
@@ -413,11 +412,6 @@ 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
index f9944fd96d7f334e36150840ba5d720b26dc6c44..6696daa2bea27f09954cc937c729f59676e10851 100644 (file)
@@ -76,6 +76,7 @@ public class AffixUtilsTest {
         Object[][] cases = {
                 { "", false, 0, "" },
                 { "abc", false, 3, "abc" },
+                { "📺", false, 1, "📺" },
                 { "-", false, 1, "−" },
                 { "-!", false, 2, "−!" },
                 { "+", false, 1, "\u061C+" },
@@ -120,8 +121,13 @@ public class AffixUtilsTest {
             String actual = unescapeWithDefaults(input);
             assertEquals("Output on <" + input + ">", output, actual);
 
-            int ulength = AffixUtils.unescapedCodePointCount(input, DEFAULT_SYMBOL_PROVIDER);
+            int ulength = AffixUtils.unescapedCount(input, true, DEFAULT_SYMBOL_PROVIDER);
             assertEquals("Unescaped length on <" + input + ">", output.length(), ulength);
+
+            int ucpcount = AffixUtils.unescapedCount(input, false, DEFAULT_SYMBOL_PROVIDER);
+            assertEquals("Unescaped length on <" + input + ">",
+                    output.codePointCount(0, output.length()),
+                    ucpcount);
         }
     }
 
@@ -211,24 +217,20 @@ public class AffixUtilsTest {
 
     @Test
     public void testWithoutSymbolsOrIgnorables() {
-        String[][] cases = {
-                { "", "" },
-                { "-", "" },
-                { " ", "" },
-                { "'-'", "-" },
-                { " a + b ", "a  b" },
-                { "-a+b%c‰d¤e¤¤f¤¤¤g¤¤¤¤h¤¤¤¤¤i", "abcdefghi" }, };
+        Object[][] cases = {
+                { "", true },
+                { "-", true },
+                { " ", true },
+                { "'-'", false },
+                { " a + b ", false },
+                { "-a+b%c‰d¤e¤¤f¤¤¤g¤¤¤¤h¤¤¤¤¤i", false }, };
 
         UnicodeSet ignorables = new UnicodeSet("[:whitespace:]");
-        StringBuilder sb = new StringBuilder();
-        for (String[] cas : cases) {
-            String input = cas[0];
-            String expected = cas[1];
-            sb.setLength(0);
-            AffixUtils.trimSymbolsAndIgnorables(input, ignorables, sb);
-            assertEquals("Removing symbols from: " + input, expected, sb.toString());
+        for (Object[] cas : cases) {
+            String input = (String) cas[0];
+            boolean expected = (Boolean) cas[1];
             assertEquals("Contains only symbols and ignorables: " + input,
-                    sb.length() == 0,
+                    expected,
                     AffixUtils.containsOnlySymbolsAndIgnorables(input, ignorables));
         }
     }