8251499: no-placeholder compact number patterns throw IllegalArgumentException
Reviewed-by: joehw, rriggs
This commit is contained in:
parent
93c00472eb
commit
49e7609da2
@ -42,7 +42,6 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
|
||||
/**
|
||||
@ -263,6 +262,12 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||
*/
|
||||
private transient List<Number> divisors;
|
||||
|
||||
/**
|
||||
* List of place holders that represent minimum integer digits at each index
|
||||
* for each count.
|
||||
*/
|
||||
private transient List<Patterns> placeHolderPatterns;
|
||||
|
||||
/**
|
||||
* The {@code DecimalFormatSymbols} object used by this format.
|
||||
* It contains the symbols used to format numbers. For example,
|
||||
@ -459,7 +464,7 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||
|
||||
this.pluralRules = pluralRules;
|
||||
|
||||
// Process compact patterns to extract the prefixes, suffixes and
|
||||
// Process compact patterns to extract the prefixes, suffixes, place holders, and
|
||||
// divisors
|
||||
processCompactPatterns();
|
||||
}
|
||||
@ -589,12 +594,14 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||
|
||||
if (!prefix.isEmpty() || !suffix.isEmpty()) {
|
||||
appendPrefix(result, prefix, delegate);
|
||||
roundedNumber = roundedNumber / divisor;
|
||||
decimalFormat.setDigitList(roundedNumber, isNegative, getMaximumFractionDigits());
|
||||
decimalFormat.subformatNumber(result, delegate, isNegative,
|
||||
false, getMaximumIntegerDigits(), getMinimumIntegerDigits(),
|
||||
getMaximumFractionDigits(), getMinimumFractionDigits());
|
||||
appendSuffix(result, suffix, delegate);
|
||||
if (!placeHolderPatterns.get(compactDataIndex).get(iPart).isEmpty()) {
|
||||
roundedNumber = roundedNumber / divisor;
|
||||
decimalFormat.setDigitList(roundedNumber, isNegative, getMaximumFractionDigits());
|
||||
decimalFormat.subformatNumber(result, delegate, isNegative,
|
||||
false, getMaximumIntegerDigits(), getMinimumIntegerDigits(),
|
||||
getMaximumFractionDigits(), getMinimumFractionDigits());
|
||||
appendSuffix(result, suffix, delegate);
|
||||
}
|
||||
} else {
|
||||
defaultDecimalFormat.doubleSubformat(number, result, delegate, isNegative);
|
||||
}
|
||||
@ -655,25 +662,27 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||
String suffix = getAffix(false, false, isNegative, compactDataIndex, iPart);
|
||||
if (!prefix.isEmpty() || !suffix.isEmpty()) {
|
||||
appendPrefix(result, prefix, delegate);
|
||||
if ((number % divisor == 0)) {
|
||||
number = number / divisor;
|
||||
decimalFormat.setDigitList(number, isNegative, 0);
|
||||
decimalFormat.subformatNumber(result, delegate,
|
||||
isNegative, true, getMaximumIntegerDigits(),
|
||||
getMinimumIntegerDigits(), getMaximumFractionDigits(),
|
||||
getMinimumFractionDigits());
|
||||
} else {
|
||||
// To avoid truncation of fractional part store
|
||||
// the value in double and follow double path instead of
|
||||
// long path
|
||||
double dNumber = (double) number / divisor;
|
||||
decimalFormat.setDigitList(dNumber, isNegative, getMaximumFractionDigits());
|
||||
decimalFormat.subformatNumber(result, delegate,
|
||||
isNegative, false, getMaximumIntegerDigits(),
|
||||
getMinimumIntegerDigits(), getMaximumFractionDigits(),
|
||||
getMinimumFractionDigits());
|
||||
if (!placeHolderPatterns.get(compactDataIndex).get(iPart).isEmpty()) {
|
||||
if ((number % divisor == 0)) {
|
||||
number = number / divisor;
|
||||
decimalFormat.setDigitList(number, isNegative, 0);
|
||||
decimalFormat.subformatNumber(result, delegate,
|
||||
isNegative, true, getMaximumIntegerDigits(),
|
||||
getMinimumIntegerDigits(), getMaximumFractionDigits(),
|
||||
getMinimumFractionDigits());
|
||||
} else {
|
||||
// To avoid truncation of fractional part store
|
||||
// the value in double and follow double path instead of
|
||||
// long path
|
||||
double dNumber = (double) number / divisor;
|
||||
decimalFormat.setDigitList(dNumber, isNegative, getMaximumFractionDigits());
|
||||
decimalFormat.subformatNumber(result, delegate,
|
||||
isNegative, false, getMaximumIntegerDigits(),
|
||||
getMinimumIntegerDigits(), getMaximumFractionDigits(),
|
||||
getMinimumFractionDigits());
|
||||
}
|
||||
appendSuffix(result, suffix, delegate);
|
||||
}
|
||||
appendSuffix(result, suffix, delegate);
|
||||
} else {
|
||||
number = isNegative ? -number : number;
|
||||
defaultDecimalFormat.format(number, result, delegate);
|
||||
@ -748,12 +757,14 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||
String suffix = getAffix(false, false, isNegative, compactDataIndex, iPart);
|
||||
if (!prefix.isEmpty() || !suffix.isEmpty()) {
|
||||
appendPrefix(result, prefix, delegate);
|
||||
number = number.divide(new BigDecimal(divisor.toString()), getRoundingMode());
|
||||
decimalFormat.setDigitList(number, isNegative, getMaximumFractionDigits());
|
||||
decimalFormat.subformatNumber(result, delegate, isNegative,
|
||||
false, getMaximumIntegerDigits(), getMinimumIntegerDigits(),
|
||||
getMaximumFractionDigits(), getMinimumFractionDigits());
|
||||
appendSuffix(result, suffix, delegate);
|
||||
if (!placeHolderPatterns.get(compactDataIndex).get(iPart).isEmpty()) {
|
||||
number = number.divide(new BigDecimal(divisor.toString()), getRoundingMode());
|
||||
decimalFormat.setDigitList(number, isNegative, getMaximumFractionDigits());
|
||||
decimalFormat.subformatNumber(result, delegate, isNegative,
|
||||
false, getMaximumIntegerDigits(), getMinimumIntegerDigits(),
|
||||
getMaximumFractionDigits(), getMinimumFractionDigits());
|
||||
appendSuffix(result, suffix, delegate);
|
||||
}
|
||||
} else {
|
||||
number = isNegative ? number.negate() : number;
|
||||
defaultDecimalFormat.format(number, result, delegate);
|
||||
@ -813,28 +824,30 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||
String suffix = getAffix(false, false, isNegative, compactDataIndex, iPart);
|
||||
if (!prefix.isEmpty() || !suffix.isEmpty()) {
|
||||
appendPrefix(result, prefix, delegate);
|
||||
if (number.mod(new BigInteger(divisor.toString()))
|
||||
.compareTo(BigInteger.ZERO) == 0) {
|
||||
number = number.divide(new BigInteger(divisor.toString()));
|
||||
if (!placeHolderPatterns.get(compactDataIndex).get(iPart).isEmpty()) {
|
||||
if (number.mod(new BigInteger(divisor.toString()))
|
||||
.compareTo(BigInteger.ZERO) == 0) {
|
||||
number = number.divide(new BigInteger(divisor.toString()));
|
||||
|
||||
decimalFormat.setDigitList(number, isNegative, 0);
|
||||
decimalFormat.subformatNumber(result, delegate,
|
||||
isNegative, true, getMaximumIntegerDigits(),
|
||||
getMinimumIntegerDigits(), getMaximumFractionDigits(),
|
||||
getMinimumFractionDigits());
|
||||
} else {
|
||||
// To avoid truncation of fractional part store the value in
|
||||
// BigDecimal and follow BigDecimal path instead of
|
||||
// BigInteger path
|
||||
BigDecimal nDecimal = new BigDecimal(number)
|
||||
.divide(new BigDecimal(divisor.toString()), getRoundingMode());
|
||||
decimalFormat.setDigitList(nDecimal, isNegative, getMaximumFractionDigits());
|
||||
decimalFormat.subformatNumber(result, delegate,
|
||||
isNegative, false, getMaximumIntegerDigits(),
|
||||
getMinimumIntegerDigits(), getMaximumFractionDigits(),
|
||||
getMinimumFractionDigits());
|
||||
decimalFormat.setDigitList(number, isNegative, 0);
|
||||
decimalFormat.subformatNumber(result, delegate,
|
||||
isNegative, true, getMaximumIntegerDigits(),
|
||||
getMinimumIntegerDigits(), getMaximumFractionDigits(),
|
||||
getMinimumFractionDigits());
|
||||
} else {
|
||||
// To avoid truncation of fractional part store the value in
|
||||
// BigDecimal and follow BigDecimal path instead of
|
||||
// BigInteger path
|
||||
BigDecimal nDecimal = new BigDecimal(number)
|
||||
.divide(new BigDecimal(divisor.toString()), getRoundingMode());
|
||||
decimalFormat.setDigitList(nDecimal, isNegative, getMaximumFractionDigits());
|
||||
decimalFormat.subformatNumber(result, delegate,
|
||||
isNegative, false, getMaximumIntegerDigits(),
|
||||
getMinimumIntegerDigits(), getMaximumFractionDigits(),
|
||||
getMinimumFractionDigits());
|
||||
}
|
||||
appendSuffix(result, suffix, delegate);
|
||||
}
|
||||
appendSuffix(result, suffix, delegate);
|
||||
} else {
|
||||
number = isNegative ? number.negate() : number;
|
||||
defaultDecimalFormat.format(number, result, delegate, formatLong);
|
||||
@ -905,8 +918,7 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||
if (!string.isEmpty()) {
|
||||
int start = result.length();
|
||||
result.append(string);
|
||||
for (int counter = 0; counter < positions.size(); counter++) {
|
||||
FieldPosition fp = positions.get(counter);
|
||||
for (FieldPosition fp : positions) {
|
||||
Format.Field attribute = fp.getFieldAttribute();
|
||||
delegate.formatted(attribute, attribute,
|
||||
start + fp.getBeginIndex(),
|
||||
@ -1098,7 +1110,7 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||
* pattern at given {@code patternIndex}
|
||||
*/
|
||||
private Number computeDivisor(String minIntDigits, int patternIndex) {
|
||||
int count = minIntDigits.length() - 1;
|
||||
int count = minIntDigits.length();
|
||||
Number matchedValue;
|
||||
// The divisor value can go above long range, if the compact patterns
|
||||
// goes above index 18, divisor may need to be stored as BigInteger,
|
||||
@ -1109,25 +1121,25 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||
matchedValue = BigInteger.valueOf(RANGE_MULTIPLIER).pow(patternIndex);
|
||||
}
|
||||
Number divisor = matchedValue;
|
||||
if (count != 0) {
|
||||
if (count > 0) {
|
||||
if (matchedValue instanceof BigInteger) {
|
||||
BigInteger bigValue = (BigInteger) matchedValue;
|
||||
if (bigValue.compareTo(BigInteger.valueOf((long) Math.pow(RANGE_MULTIPLIER, count))) < 0) {
|
||||
if (bigValue.compareTo(BigInteger.valueOf((long) Math.pow(RANGE_MULTIPLIER, count - 1))) < 0) {
|
||||
throw new IllegalArgumentException("Invalid Pattern"
|
||||
+ " [" + compactPatterns[patternIndex]
|
||||
+ "]: min integer digits specified exceeds the limit"
|
||||
+ " for the index " + patternIndex);
|
||||
}
|
||||
divisor = bigValue.divide(BigInteger.valueOf((long) Math.pow(RANGE_MULTIPLIER, count)));
|
||||
divisor = bigValue.divide(BigInteger.valueOf((long) Math.pow(RANGE_MULTIPLIER, count - 1)));
|
||||
} else {
|
||||
long longValue = (long) matchedValue;
|
||||
if (longValue < (long) Math.pow(RANGE_MULTIPLIER, count)) {
|
||||
if (longValue < (long) Math.pow(RANGE_MULTIPLIER, count - 1)) {
|
||||
throw new IllegalArgumentException("Invalid Pattern"
|
||||
+ " [" + compactPatterns[patternIndex]
|
||||
+ "]: min integer digits specified exceeds the limit"
|
||||
+ " for the index " + patternIndex);
|
||||
}
|
||||
divisor = longValue / (long) Math.pow(RANGE_MULTIPLIER, count);
|
||||
divisor = longValue / (long) Math.pow(RANGE_MULTIPLIER, count - 1);
|
||||
}
|
||||
}
|
||||
return divisor;
|
||||
@ -1140,7 +1152,7 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||
*
|
||||
*/
|
||||
private static final Pattern PLURALS =
|
||||
Pattern.compile("^\\{(?<plurals>.*)\\}$");
|
||||
Pattern.compile("^\\{(?<plurals>.*)}$");
|
||||
private static final Pattern COUNT_PATTERN =
|
||||
Pattern.compile("(zero|one|two|few|many|other):((' '|[^ ])+)[ ]*");
|
||||
private void processCompactPatterns() {
|
||||
@ -1150,6 +1162,7 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||
positiveSuffixPatterns = new ArrayList<>(size);
|
||||
negativeSuffixPatterns = new ArrayList<>(size);
|
||||
divisors = new ArrayList<>(size);
|
||||
placeHolderPatterns = new ArrayList<>(size);
|
||||
|
||||
for (int index = 0; index < size; index++) {
|
||||
String text = compactPatterns[index];
|
||||
@ -1157,6 +1170,7 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||
negativePrefixPatterns.add(new Patterns());
|
||||
positiveSuffixPatterns.add(new Patterns());
|
||||
negativeSuffixPatterns.add(new Patterns());
|
||||
placeHolderPatterns.add(new Patterns());
|
||||
|
||||
// check if it is the old style
|
||||
Matcher m = text != null ? PLURALS.matcher(text) : null;
|
||||
@ -1198,13 +1212,13 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||
}
|
||||
|
||||
// Patterns for plurals syntax validation
|
||||
private final static String EXPR = "([niftvw]{1})\\s*(([/\\%])\\s*(\\d+))*";
|
||||
private final static String RELATION = "(!{0,1}=)";
|
||||
private final static String EXPR = "([niftvw])\\s*(([/%])\\s*(\\d+))*";
|
||||
private final static String RELATION = "(!?=)";
|
||||
private final static String VALUE_RANGE = "((\\d+)\\.\\.(\\d+)|\\d+)";
|
||||
private final static String CONDITION = EXPR + "\\s*" +
|
||||
RELATION + "\\s*" +
|
||||
VALUE_RANGE + "\\s*" +
|
||||
"(\\,\\s*" + VALUE_RANGE + ")*";
|
||||
"(,\\s*" + VALUE_RANGE + ")*";
|
||||
private final static Pattern PLURALRULES_PATTERN =
|
||||
Pattern.compile("(zero|one|two|few|many):\\s*" +
|
||||
CONDITION +
|
||||
@ -1400,19 +1414,13 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||
}
|
||||
}
|
||||
|
||||
// If no 0s are specified in a non empty pattern, it is invalid
|
||||
if (!pattern.isEmpty() && zeros.isEmpty()) {
|
||||
throw new IllegalArgumentException("Invalid pattern"
|
||||
+ " [" + pattern + "]: all patterns must include digit"
|
||||
+ " placement 0s");
|
||||
}
|
||||
|
||||
// Only if positive affix exists; else put empty strings
|
||||
if (!positivePrefix.isEmpty() || !positiveSuffix.isEmpty()) {
|
||||
positivePrefixPatterns.get(index).put(count, positivePrefix);
|
||||
negativePrefixPatterns.get(index).put(count, negativePrefix);
|
||||
positiveSuffixPatterns.get(index).put(count, positiveSuffix);
|
||||
negativeSuffixPatterns.get(index).put(count, negativeSuffix);
|
||||
placeHolderPatterns.get(index).put(count, zeros);
|
||||
if (divisors.size() <= index) {
|
||||
divisors.add(computeDivisor(zeros, index));
|
||||
}
|
||||
@ -1421,6 +1429,7 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||
negativePrefixPatterns.get(index).put(count, "");
|
||||
positiveSuffixPatterns.get(index).put(count, "");
|
||||
negativeSuffixPatterns.get(index).put(count, "");
|
||||
placeHolderPatterns.get(index).put(count, "");
|
||||
if (divisors.size() <= index) {
|
||||
divisors.add(1L);
|
||||
}
|
||||
@ -1617,14 +1626,18 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||
// Update the position and take compact multiplier
|
||||
// only if it matches the compact prefix, not the default
|
||||
// prefix; else multiplier should be 1
|
||||
if (gotPositive) {
|
||||
position += matchedPosPrefix.length();
|
||||
cnfMultiplier = matchedPosIndex != -1
|
||||
? divisors.get(matchedPosIndex) : 1L;
|
||||
} else if (gotNegative) {
|
||||
position += matchedNegPrefix.length();
|
||||
cnfMultiplier = matchedNegIndex != -1
|
||||
? divisors.get(matchedNegIndex) : 1L;
|
||||
// If there's no number part, no need to go further, just
|
||||
// return the multiplier.
|
||||
if (gotPositive || gotNegative) {
|
||||
position += gotPositive ? matchedPosPrefix.length() : matchedNegPrefix.length();
|
||||
int matchedIndex = gotPositive ? matchedPosIndex : matchedNegIndex;
|
||||
if (matchedIndex != -1) {
|
||||
cnfMultiplier = divisors.get(matchedIndex);
|
||||
if (placeHolderPatterns.get(matchedIndex).get(num).isEmpty()) {
|
||||
pos.index = position;
|
||||
return cnfMultiplier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
digitList.setRoundingMode(getRoundingMode());
|
||||
@ -1708,6 +1721,7 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||
}
|
||||
}
|
||||
|
||||
private static final Pattern DIGITS = Pattern.compile("\\p{Nd}+");
|
||||
/**
|
||||
* Parse the number part in the input text into a number
|
||||
*
|
||||
@ -1715,7 +1729,6 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||
* @param position starting position
|
||||
* @return the number
|
||||
*/
|
||||
private static Pattern DIGITS = Pattern.compile("\\p{Nd}+");
|
||||
private double parseNumberPart(String text, int position) {
|
||||
if (text.startsWith(symbols.getInfinity(), position)) {
|
||||
return Double.POSITIVE_INFINITY;
|
||||
@ -1730,6 +1743,9 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||
.mapToObj(Integer::toString)
|
||||
.collect(Collectors.joining()));
|
||||
}
|
||||
} else {
|
||||
// no numbers. return 1.0 for possible no-placeholder pattern
|
||||
return 1.0;
|
||||
}
|
||||
}
|
||||
return Double.NaN;
|
||||
@ -1821,9 +1837,7 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||
if (!affix.isEmpty() && !affix.equals(defaultAffix)) {
|
||||
// Look ahead only for the longer match than the previous match
|
||||
if (matchedAffix.length() < affix.length()) {
|
||||
if (text.regionMatches(position, affix, 0, affix.length())) {
|
||||
return true;
|
||||
}
|
||||
return text.regionMatches(position, affix, 0, affix.length());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@ -2357,10 +2371,10 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstraction of affix patterns for each "count" tag.
|
||||
* Abstraction of affix or number (represented by zeros) patterns for each "count" tag.
|
||||
*/
|
||||
private final class Patterns {
|
||||
private Map<String, String> patternsMap = new HashMap<>();
|
||||
private final Map<String, String> patternsMap = new HashMap<>();
|
||||
|
||||
void put(String count, String pattern) {
|
||||
patternsMap.put(count, pattern);
|
||||
@ -2373,13 +2387,12 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||
|
||||
Patterns expandAffix() {
|
||||
Patterns ret = new Patterns();
|
||||
patternsMap.entrySet().stream()
|
||||
.forEach(e -> ret.put(e.getKey(), CompactNumberFormat.this.expandAffix(e.getValue())));
|
||||
patternsMap.forEach((key, value) -> ret.put(key, CompactNumberFormat.this.expandAffix(value)));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
private final int getIntegerPart(double number, double divisor) {
|
||||
private int getIntegerPart(double number, double divisor) {
|
||||
return BigDecimal.valueOf(number)
|
||||
.divide(BigDecimal.valueOf(divisor), roundingMode).intValue();
|
||||
}
|
||||
@ -2394,7 +2407,7 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||
if (rulesMap != null) {
|
||||
return rulesMap.entrySet().stream()
|
||||
.filter(e -> matchPluralRule(e.getValue(), input))
|
||||
.map(e -> e.getKey())
|
||||
.map(Map.Entry::getKey)
|
||||
.findFirst()
|
||||
.orElse("other");
|
||||
}
|
||||
@ -2405,14 +2418,12 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||
|
||||
private static boolean matchPluralRule(String condition, double input) {
|
||||
return Arrays.stream(condition.split("or"))
|
||||
.anyMatch(and_condition -> {
|
||||
return Arrays.stream(and_condition.split("and"))
|
||||
.allMatch(r -> relationCheck(r, input));
|
||||
});
|
||||
.anyMatch(and_condition -> Arrays.stream(and_condition.split("and"))
|
||||
.allMatch(r -> relationCheck(r, input)));
|
||||
}
|
||||
|
||||
private final static String NAMED_EXPR = "(?<op>[niftvw]{1})\\s*((?<div>[/\\%])\\s*(?<val>\\d+))*";
|
||||
private final static String NAMED_RELATION = "(?<rel>!{0,1}=)";
|
||||
private final static String NAMED_EXPR = "(?<op>[niftvw])\\s*((?<div>[/%])\\s*(?<val>\\d+))*";
|
||||
private final static String NAMED_RELATION = "(?<rel>!?=)";
|
||||
private final static String NAMED_VALUE_RANGE = "(?<start>\\d+)\\.\\.(?<end>\\d+)|(?<value>\\d+)";
|
||||
private final static Pattern EXPR_PATTERN = Pattern.compile(NAMED_EXPR);
|
||||
private final static Pattern RELATION_PATTERN = Pattern.compile(NAMED_RELATION);
|
||||
@ -2462,7 +2473,7 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||
var conditions =
|
||||
Arrays.stream(relation.substring(rel.end()).split(","));
|
||||
|
||||
if (rel.group("rel").equals("!=")) {
|
||||
if (Objects.equals(rel.group("rel"), "!=")) {
|
||||
return conditions.noneMatch(c -> valOrRangeMatches(c, lop));
|
||||
} else {
|
||||
return conditions.anyMatch(c -> valOrRangeMatches(c, lop));
|
||||
@ -2487,7 +2498,7 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||
ret =input;
|
||||
} else {
|
||||
String op = expr.group("op");
|
||||
if (op.equals("n") || op.equals("i")) {
|
||||
if (Objects.equals(op, "n") || Objects.equals(op, "i")) {
|
||||
ret = input;
|
||||
}
|
||||
|
||||
@ -2495,12 +2506,8 @@ public final class CompactNumberFormat extends NumberFormat {
|
||||
if (divop != null) {
|
||||
String divisor = expr.group("val");
|
||||
switch (divop) {
|
||||
case "%":
|
||||
ret %= Double.parseDouble(divisor);
|
||||
break;
|
||||
case "/":
|
||||
ret /= Double.parseDouble(divisor);
|
||||
break;
|
||||
case "%" -> ret %= Double.parseDouble(divisor);
|
||||
case "/" -> ret /= Double.parseDouble(divisor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -21,11 +21,10 @@
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.ParsePosition;
|
||||
import static org.testng.Assert.*;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
class CompactFormatAndParseHelper {
|
||||
|
||||
@ -47,22 +46,11 @@ class CompactFormatAndParseHelper {
|
||||
}
|
||||
|
||||
if (returnType != null) {
|
||||
assertEquals(number.getClass(), returnType, "Incorrect return type for string" + parseString);
|
||||
assertEquals(number.getClass(), returnType,
|
||||
"Incorrect return type for string '" + parseString + "'");
|
||||
}
|
||||
|
||||
if (number instanceof Double) {
|
||||
assertEquals(number.doubleValue(), (double) expected,
|
||||
"Incorrect parsing of the string '" + parseString + "'");
|
||||
} else if (number instanceof Long) {
|
||||
assertEquals(number.longValue(), (long) expected,
|
||||
"Incorrect parsing of the string '" + parseString + "'");
|
||||
} else if (number instanceof BigDecimal) {
|
||||
BigDecimal num = (BigDecimal) number;
|
||||
assertEquals(num, (BigDecimal) expected,
|
||||
"Incorrect parsing of the string '" + parseString + "'");
|
||||
} else {
|
||||
assertEquals(number, expected, "Incorrect parsing of the string '"
|
||||
+ parseString + "'");
|
||||
}
|
||||
assertEquals(number, expected, "Incorrect parsing of the string '"
|
||||
+ parseString + "'");
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -22,7 +22,7 @@
|
||||
*/
|
||||
/*
|
||||
* @test
|
||||
* @bug 8177552 8217254
|
||||
* @bug 8177552 8217254 8251499
|
||||
* @summary Checks the validity of compact number patterns specified through
|
||||
* CompactNumberFormat constructor
|
||||
* @run testng/othervm TestCompactPatternsValidity
|
||||
@ -67,27 +67,32 @@ public class TestCompactPatternsValidity {
|
||||
"elfu 000;elfu -000", "milioni 0;milioni -0", "milioni 00;milioni -00", "milioni 000;milioni -000"};
|
||||
// Containing both prefix and suffix and positive;negative subpatern
|
||||
private static final String[] COMPACT_PATTERN12 = new String[]{"", "", "H0H;H-0H", "0K;0K-", "00K;-00K", "H0G;-H0G"};
|
||||
// A non empty pattern containing no 0s (min integer digits)
|
||||
private static final String[] COMPACT_PATTERN13 =
|
||||
new String[]{"", "", "", "Thousand", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "BeyondLong"};
|
||||
// A non empty pattern containing no 0s (min integer digits), with plural rules
|
||||
private static final String[] COMPACT_PATTERN14 =
|
||||
new String[]{"", "", "", "{one:Kun other:0' 'Kun}"}; // from Somali in CLDR 38
|
||||
|
||||
@DataProvider(name = "invalidPatterns")
|
||||
Object[][] invalidCompactPatterns() {
|
||||
return new Object[][]{
|
||||
return new Object[][] {
|
||||
// compact patterns
|
||||
// Pattern containing unquoted special character '.'
|
||||
{new String[]{"", "", "", "0K", "00K."}},
|
||||
// Pattern containing invalid single quote
|
||||
{new String[]{"", "", "", "0 'do", "00K"}},
|
||||
{new String[]{"", "", "", "0K", "00 don't"}},
|
||||
// A non empty pattern containing no 0s (min integer digits)
|
||||
{new String[]{"K", "0K", "00K"}},
|
||||
// 0s (min integer digits) exceeding for the range at index 3
|
||||
{new String[]{"", "", "0K", "00000K"}},
|
||||
// null as a compact pattern
|
||||
{new String[]{"", "", null, "00K"}},};
|
||||
{new String[]{"", "", null, "00K"}},
|
||||
};
|
||||
}
|
||||
|
||||
@DataProvider(name = "validPatternsFormat")
|
||||
Object[][] validPatternsFormat() {
|
||||
return new Object[][]{
|
||||
return new Object[][] {
|
||||
// compact patterns, numbers, expected output
|
||||
{COMPACT_PATTERN1, List.of(200, 1000, 3000, 500000), List.of("200", "1K", "3K", "500K")},
|
||||
{COMPACT_PATTERN2, List.of(1, 20, 3000), List.of("1", ".K2", ".K300")},
|
||||
@ -97,19 +102,21 @@ public class TestCompactPatternsValidity {
|
||||
{COMPACT_PATTERN6, List.of(20.99, 1000, 30000), List.of("21", ".1K", ".30K")},
|
||||
{COMPACT_PATTERN7, List.of(100, 1000, new BigInteger("12345678987654321")), List.of("100", "1K,", "12345678987654K,")},
|
||||
{COMPACT_PATTERN8, List.of(new BigInteger("223565686837667632"), new BigDecimal("12322456774334.89766"), 30000, 3456.78),
|
||||
List.of("223566T", "12T", "30K", "3K")},
|
||||
List.of("223566T", "12T", "30K", "3K")},
|
||||
{COMPACT_PATTERN9, List.of(new BigInteger("223566000000000000"), new BigDecimal("12345678987654567"), 30000, 3000),
|
||||
List.of("223,566,000,000,000,000", "12,345,678,987,654,567", "30,000", "3,000")},
|
||||
List.of("223,566,000,000,000,000", "12,345,678,987,654,567", "30,000", "3,000")},
|
||||
{COMPACT_PATTERN10, List.of(new BigInteger("100000000000000000"), new BigInteger("10000000000000000000"), new BigDecimal("555555555555555555555.89766"), 30000),
|
||||
List.of("100L", "10XL", "556XL", "30K")},
|
||||
List.of("100L", "10XL", "556XL", "30K")},
|
||||
{COMPACT_PATTERN11, List.of(20.99, -20.99, 1000, -1000, 30000, -30000, new BigInteger("12345678987654321"), new BigInteger("-12345678987654321")),
|
||||
List.of("21", "-21", "elfu 1", "elfu -1", "elfu 30", "elfu -30", "milioni 12345678988", "milioni -12345678988")},
|
||||
{COMPACT_PATTERN12, List.of(0, 500, -500, 30000, -3000, 5000000), List.of("0", "H5H", "H-5H", "30K", "3K-", "H50G")},};
|
||||
List.of("21", "-21", "elfu 1", "elfu -1", "elfu 30", "elfu -30", "milioni 12345678988", "milioni -12345678988")},
|
||||
{COMPACT_PATTERN12, List.of(0, 500, -500, 30000, -3000, 5000000), List.of("0", "H5H", "H-5H", "30K", "3K-", "H50G")},
|
||||
{COMPACT_PATTERN13, List.of(1000, new BigInteger("10000000000000000000")), List.of("Thousand", "BeyondLong")},
|
||||
};
|
||||
}
|
||||
|
||||
@DataProvider(name = "validPatternsParse")
|
||||
Object[][] validPatternsParse() {
|
||||
return new Object[][]{
|
||||
return new Object[][] {
|
||||
// compact patterns, parse string, expected output
|
||||
{COMPACT_PATTERN1, List.of(".56", "200", ".1K", "3K", "500K"), List.of(0.56, 200L, 100L, 3000L, 500000L)},
|
||||
{COMPACT_PATTERN2, List.of("1", ".K2", ".K300"), List.of(1L, 20L, 3000L)},
|
||||
@ -122,7 +129,25 @@ public class TestCompactPatternsValidity {
|
||||
{COMPACT_PATTERN10, List.of("1L", "100L", "10XL", "556XL", "30K"), List.of(1000000000000000L, 100000000000000000L, 1.0E19, 5.56E20, 30000L)},
|
||||
{COMPACT_PATTERN11, List.of("21", "-21", "100.90", "-100.90", "elfu 1", "elfu -1", "elfu 30", "elfu -30", "milioni 12345678988", "milioni -12345678988"),
|
||||
List.of(21L, -21L, 100.90, -100.90, 1000L, -1000L, 30000L, -30000L, 12345678988000000L, -12345678988000000L)},
|
||||
{COMPACT_PATTERN12, List.of("0", "H5H", "H-5H", "30K", "30K-", "H50G"), List.of(0L, 500L, -500L, 30000L, -30000L, 5000000L)},};
|
||||
{COMPACT_PATTERN12, List.of("0", "H5H", "H-5H", "30K", "30K-", "H50G"), List.of(0L, 500L, -500L, 30000L, -30000L, 5000000L)},
|
||||
{COMPACT_PATTERN13, List.of("Thousand", "BeyondLong"), List.of(1000L, new BigInteger("10000000000000000000"))},
|
||||
};
|
||||
}
|
||||
|
||||
@DataProvider(name = "validPatternsFormatWithPluralRules")
|
||||
Object[][] validPatternsFormatWithPluralRules() {
|
||||
return new Object[][] {
|
||||
// compact patterns, plural rules, numbers, expected output
|
||||
{COMPACT_PATTERN14, "one:n = 1", List.of(1000, 2345), List.of("Kun", "2 Kun")},
|
||||
};
|
||||
}
|
||||
|
||||
@DataProvider(name = "validPatternsParseWithPluralRules")
|
||||
Object[][] validPatternsParseWithPluralRules() {
|
||||
return new Object[][] {
|
||||
// compact patterns, plural rules, parse string, expected output
|
||||
{COMPACT_PATTERN14, "one:n = 1", List.of("Kun", "2 Kun"), List.of(1000L, 2000L)},
|
||||
};
|
||||
}
|
||||
|
||||
@Test(dataProvider = "invalidPatterns",
|
||||
@ -147,7 +172,29 @@ public class TestCompactPatternsValidity {
|
||||
public void testValidPatternsParse(String[] compactPatterns,
|
||||
List<String> parseString, List<Number> numbers) throws ParseException {
|
||||
CompactNumberFormat fmt = new CompactNumberFormat("#,##0.0#",
|
||||
DecimalFormatSymbols.getInstance(Locale.US), compactPatterns);
|
||||
DecimalFormatSymbols.getInstance(Locale.US), compactPatterns);
|
||||
for (int index = 0; index < parseString.size(); index++) {
|
||||
CompactFormatAndParseHelper.testParse(fmt, parseString.get(index),
|
||||
numbers.get(index), null, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "validPatternsFormatWithPluralRules")
|
||||
public void testValidPatternsFormatWithPluralRules(String[] compactPatterns, String pluralRules,
|
||||
List<Object> numbers, List<String> expected) {
|
||||
CompactNumberFormat fmt = new CompactNumberFormat("#,##0.0#",
|
||||
DecimalFormatSymbols.getInstance(Locale.US), compactPatterns, pluralRules);
|
||||
for (int index = 0; index < numbers.size(); index++) {
|
||||
CompactFormatAndParseHelper.testFormat(fmt, numbers.get(index),
|
||||
expected.get(index));
|
||||
}
|
||||
}
|
||||
|
||||
@Test(dataProvider = "validPatternsParseWithPluralRules")
|
||||
public void testValidPatternsParsewithPluralRules(String[] compactPatterns, String pluralRules,
|
||||
List<String> parseString, List<Number> numbers) throws ParseException {
|
||||
CompactNumberFormat fmt = new CompactNumberFormat("#,##0.0#",
|
||||
DecimalFormatSymbols.getInstance(Locale.US), compactPatterns, pluralRules);
|
||||
for (int index = 0; index < parseString.size(); index++) {
|
||||
CompactFormatAndParseHelper.testParse(fmt, parseString.get(index),
|
||||
numbers.get(index), null, null);
|
||||
|
Loading…
x
Reference in New Issue
Block a user