mirror of
https://github.com/unicode-org/icu.git
synced 2025-04-21 04:29:31 +00:00
ICU-13513 A few more compatibility tweaks.
X-SVN-Rev: 40829
This commit is contained in:
parent
73ac780512
commit
82a8f8bc68
8 changed files with 71 additions and 102 deletions
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue