--- old/src/java.base/share/classes/java/text/CompactNumberFormat.java 2020-08-17 14:28:30.000000000 -0700 +++ new/src/java.base/share/classes/java/text/CompactNumberFormat.java 2020-08-17 14:28:29.000000000 -0700 @@ -42,7 +42,6 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; -import java.util.stream.Stream; /** @@ -264,6 +263,12 @@ private transient List divisors; /** + * List of place holders that represent minimum integer digits at each index + * for each count. + */ + private transient List placeHolders; + + /** * The {@code DecimalFormatSymbols} object used by this format. * It contains the symbols used to format numbers. For example, * the grouping separator, decimal separator, and so on. @@ -459,7 +464,7 @@ 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 @@ 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 (!placeHolders.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 @@ 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 (!placeHolders.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 @@ 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 (!placeHolders.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 @@ 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())); - - 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()); + if (!placeHolders.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()); + } + appendSuffix(result, suffix, delegate); } - appendSuffix(result, suffix, delegate); } else { number = isNegative ? number.negate() : number; defaultDecimalFormat.format(number, result, delegate, formatLong); @@ -905,8 +918,7 @@ 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 @@ * 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 @@ 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 @@ * */ private static final Pattern PLURALS = - Pattern.compile("^\\{(?.*)\\}$"); + Pattern.compile("^\\{(?.*)}$"); private static final Pattern COUNT_PATTERN = Pattern.compile("(zero|one|two|few|many|other):((' '|[^ ])+)[ ]*"); private void processCompactPatterns() { @@ -1150,6 +1162,7 @@ positiveSuffixPatterns = new ArrayList<>(size); negativeSuffixPatterns = new ArrayList<>(size); divisors = new ArrayList<>(size); + placeHolders = new ArrayList<>(size); for (int index = 0; index < size; index++) { String text = compactPatterns[index]; @@ -1157,6 +1170,7 @@ negativePrefixPatterns.add(new Patterns()); positiveSuffixPatterns.add(new Patterns()); negativeSuffixPatterns.add(new Patterns()); + placeHolders.add(new Patterns()); // check if it is the old style Matcher m = text != null ? PLURALS.matcher(text) : null; @@ -1198,13 +1212,13 @@ } // 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 @@ } } - // 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); + placeHolders.get(index).put(count, zeros); if (divisors.size() <= index) { divisors.add(computeDivisor(zeros, index)); } @@ -1421,6 +1429,7 @@ negativePrefixPatterns.get(index).put(count, ""); positiveSuffixPatterns.get(index).put(count, ""); negativeSuffixPatterns.get(index).put(count, ""); + placeHolders.get(index).put(count, ""); if (divisors.size() <= index) { divisors.add(1L); } @@ -1617,14 +1626,26 @@ // Update the position and take compact multiplier // only if it matches the compact prefix, not the default // prefix; else multiplier should be 1 + // If there's no number part, no need to go further, just + // return the multiplier. if (gotPositive) { position += matchedPosPrefix.length(); - cnfMultiplier = matchedPosIndex != -1 - ? divisors.get(matchedPosIndex) : 1L; + if (matchedPosIndex != -1) { + cnfMultiplier = divisors.get(matchedPosIndex); + if (placeHolders.get(matchedPosIndex).get(num).isEmpty()) { + pos.index = position; + return cnfMultiplier; + } + } } else if (gotNegative) { position += matchedNegPrefix.length(); - cnfMultiplier = matchedNegIndex != -1 - ? divisors.get(matchedNegIndex) : 1L; + if (matchedNegIndex != -1) { + cnfMultiplier = divisors.get(matchedNegIndex); + if (placeHolders.get(matchedNegIndex).get(num).isEmpty()) { + pos.index = position; + return cnfMultiplier; + } + } } digitList.setRoundingMode(getRoundingMode()); @@ -1708,6 +1729,7 @@ } } + private static final Pattern DIGITS = Pattern.compile("\\p{Nd}+"); /** * Parse the number part in the input text into a number * @@ -1715,7 +1737,6 @@ * @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 +1751,9 @@ .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 +1845,7 @@ 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 +2379,10 @@ } /** - * 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 patternsMap = new HashMap<>(); + private final Map patternsMap = new HashMap<>(); void put(String count, String pattern) { patternsMap.put(count, pattern); @@ -2373,13 +2395,12 @@ 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 +2415,7 @@ 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 +2426,12 @@ 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 = "(?[niftvw]{1})\\s*((?
[/\\%])\\s*(?\\d+))*"; - private final static String NAMED_RELATION = "(?!{0,1}=)"; + private final static String NAMED_EXPR = "(?[niftvw])\\s*((?
[/%])\\s*(?\\d+))*"; + private final static String NAMED_RELATION = "(?!?=)"; private final static String NAMED_VALUE_RANGE = "(?\\d+)\\.\\.(?\\d+)|(?\\d+)"; private final static Pattern EXPR_PATTERN = Pattern.compile(NAMED_EXPR); private final static Pattern RELATION_PATTERN = Pattern.compile(NAMED_RELATION); @@ -2462,7 +2481,7 @@ 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 +2506,7 @@ 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 +2514,8 @@ 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); } } } --- old/test/jdk/java/text/Format/CompactNumberFormat/CompactFormatAndParseHelper.java 2020-08-17 14:28:31.000000000 -0700 +++ new/test/jdk/java/text/Format/CompactNumberFormat/CompactFormatAndParseHelper.java 2020-08-17 14:28:31.000000000 -0700 @@ -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 @@ } 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 + "'"); } } --- old/test/jdk/java/text/Format/CompactNumberFormat/TestCompactPatternsValidity.java 2020-08-17 14:28:32.000000000 -0700 +++ new/test/jdk/java/text/Format/CompactNumberFormat/TestCompactPatternsValidity.java 2020-08-17 14:28:32.000000000 -0700 @@ -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,62 +67,72 @@ "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"}; + 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[][]{ - // 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")}, - {COMPACT_PATTERN3, List.of(100.99, 1000, 30000), List.of("101", "1K", "30K.")}, - {COMPACT_PATTERN4, List.of(0.0, 500, -500, 30000, 5000000), List.of("0", "H5H", "-H5H", "30K", "H50G")}, - {COMPACT_PATTERN5, List.of(100, 1000, 30000), List.of("100", "1K", "K3")}, - {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")}, - {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")}, - {COMPACT_PATTERN10, List.of(new BigInteger("100000000000000000"), new BigInteger("10000000000000000000"), new BigDecimal("555555555555555555555.89766"), 30000), - 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")},}; + return new Object[][] { + // compact patterns, pluralRules, numbers, expected output + {COMPACT_PATTERN1, null, List.of(200, 1000, 3000, 500000), List.of("200", "1K", "3K", "500K")}, + {COMPACT_PATTERN2, null, List.of(1, 20, 3000), List.of("1", ".K2", ".K300")}, + {COMPACT_PATTERN3, null, List.of(100.99, 1000, 30000), List.of("101", "1K", "30K.")}, + {COMPACT_PATTERN4, null, List.of(0.0, 500, -500, 30000, 5000000), List.of("0", "H5H", "-H5H", "30K", "H50G")}, + {COMPACT_PATTERN5, null, List.of(100, 1000, 30000), List.of("100", "1K", "K3")}, + {COMPACT_PATTERN6, null, List.of(20.99, 1000, 30000), List.of("21", ".1K", ".30K")}, + {COMPACT_PATTERN7, null, List.of(100, 1000, new BigInteger("12345678987654321")), List.of("100", "1K,", "12345678987654K,")}, + {COMPACT_PATTERN8, null, List.of(new BigInteger("223565686837667632"), new BigDecimal("12322456774334.89766"), 30000, 3456.78), + List.of("223566T", "12T", "30K", "3K")}, + {COMPACT_PATTERN9, null, 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")}, + {COMPACT_PATTERN10, null, List.of(new BigInteger("100000000000000000"), new BigInteger("10000000000000000000"), new BigDecimal("555555555555555555555.89766"), 30000), + List.of("100L", "10XL", "556XL", "30K")}, + {COMPACT_PATTERN11, null, 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, null, List.of(0, 500, -500, 30000, -3000, 5000000), List.of("0", "H5H", "H-5H", "30K", "3K-", "H50G")}, + {COMPACT_PATTERN13, null, List.of(1000, new BigInteger("10000000000000000000")), List.of("Thousand", "BeyondLong")}, + {COMPACT_PATTERN14, "one:n = 1", List.of(1000, 2345), List.of("Kun", "2 Kun")}, + }; } @DataProvider(name = "validPatternsParse") Object[][] validPatternsParse() { - 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)}, - {COMPACT_PATTERN3, List.of("101", "1K", "30K."), List.of(101L, 1000L, 30000L)}, - {COMPACT_PATTERN4, List.of("0", "H5H", "-H5H", "30K", "H50G"), List.of(0L, 500L, -500L, 30000L, 5000000L)}, - {COMPACT_PATTERN5, List.of("100", "1K", "K3"), List.of(100L, 1000L, 30000L)}, - {COMPACT_PATTERN6, List.of("21", ".1K", ".30K"), List.of(21L, 1000L, 30000L)}, - {COMPACT_PATTERN7, List.of("100", "1K,", "12345678987654K,"), List.of(100L, 1000L, 12345678987654000L)}, - {COMPACT_PATTERN8, List.of("223566T", "12T", "30K", "3K"), List.of(223566000000000000L, 12000000000000L, 30000L, 3000L)}, - {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"), + return new Object[][] { + // compact patterns, plural rules, parse string, expected output + {COMPACT_PATTERN1, null, List.of(".56", "200", ".1K", "3K", "500K"), List.of(0.56, 200L, 100L, 3000L, 500000L)}, + {COMPACT_PATTERN2, null, List.of("1", ".K2", ".K300"), List.of(1L, 20L, 3000L)}, + {COMPACT_PATTERN3, null, List.of("101", "1K", "30K."), List.of(101L, 1000L, 30000L)}, + {COMPACT_PATTERN4, null, List.of("0", "H5H", "-H5H", "30K", "H50G"), List.of(0L, 500L, -500L, 30000L, 5000000L)}, + {COMPACT_PATTERN5, null, List.of("100", "1K", "K3"), List.of(100L, 1000L, 30000L)}, + {COMPACT_PATTERN6, null, List.of("21", ".1K", ".30K"), List.of(21L, 1000L, 30000L)}, + {COMPACT_PATTERN7, null, List.of("100", "1K,", "12345678987654K,"), List.of(100L, 1000L, 12345678987654000L)}, + {COMPACT_PATTERN8, null, List.of("223566T", "12T", "30K", "3K"), List.of(223566000000000000L, 12000000000000L, 30000L, 3000L)}, + {COMPACT_PATTERN10, null, List.of("1L", "100L", "10XL", "556XL", "30K"), List.of(1000000000000000L, 100000000000000000L, 1.0E19, 5.56E20, 30000L)}, + {COMPACT_PATTERN11, null, 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, null, List.of("0", "H5H", "H-5H", "30K", "30K-", "H50G"), List.of(0L, 500L, -500L, 30000L, -30000L, 5000000L)}, + {COMPACT_PATTERN13, null, List.of("Thousand", "BeyondLong"), List.of(1000L, new BigInteger("10000000000000000000"))}, + {COMPACT_PATTERN14,"one:n = 1", List.of("Kun", "2 Kun"), List.of(1000L, 2000L)}, + }; } @Test(dataProvider = "invalidPatterns", @@ -133,9 +143,12 @@ } @Test(dataProvider = "validPatternsFormat") - public void testValidPatternsFormat(String[] compactPatterns, + public void testValidPatternsFormat(String[] compactPatterns, String pluralRules, List numbers, List expected) { - CompactNumberFormat fmt = new CompactNumberFormat("#,##0.0#", + CompactNumberFormat fmt = pluralRules != null ? + new CompactNumberFormat("#,##0.0#", + DecimalFormatSymbols.getInstance(Locale.US), compactPatterns, pluralRules) : + new CompactNumberFormat("#,##0.0#", DecimalFormatSymbols.getInstance(Locale.US), compactPatterns); for (int index = 0; index < numbers.size(); index++) { CompactFormatAndParseHelper.testFormat(fmt, numbers.get(index), @@ -144,10 +157,13 @@ } @Test(dataProvider = "validPatternsParse") - public void testValidPatternsParse(String[] compactPatterns, + public void testValidPatternsParse(String[] compactPatterns, String pluralRules, List parseString, List numbers) throws ParseException { - CompactNumberFormat fmt = new CompactNumberFormat("#,##0.0#", - DecimalFormatSymbols.getInstance(Locale.US), compactPatterns); + CompactNumberFormat fmt = pluralRules != null ? + new CompactNumberFormat("#,##0.0#", + DecimalFormatSymbols.getInstance(Locale.US), compactPatterns, pluralRules) : + new CompactNumberFormat("#,##0.0#", + DecimalFormatSymbols.getInstance(Locale.US), compactPatterns); for (int index = 0; index < parseString.size(); index++) { CompactFormatAndParseHelper.testParse(fmt, parseString.get(index), numbers.get(index), null, null);