ICU-13513 A few more compatibility tweaks.

X-SVN-Rev: 40829
This commit is contained in:
Shane Carr 2018-01-30 06:08:15 +00:00
parent 73ac780512
commit 82a8f8bc68
8 changed files with 71 additions and 102 deletions

View 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;
}
}

View 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);
}
}

View 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.
*

View 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.

View 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;
}

View 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

View 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

View 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));
}
}