Home | History | Annotate | Download | only in format
      1 //  2017 and later: Unicode, Inc. and others.
      2 // License & terms of use: http://www.unicode.org/copyright.html#License
      3 package com.ibm.icu.dev.test.format;
      4 
      5 import java.math.BigDecimal;
      6 import java.math.RoundingMode;
      7 import java.text.ParsePosition;
      8 
      9 import org.junit.Test;
     10 
     11 import com.ibm.icu.dev.test.TestUtil;
     12 import com.ibm.icu.impl.number.DecimalFormatProperties;
     13 import com.ibm.icu.impl.number.DecimalFormatProperties.ParseMode;
     14 import com.ibm.icu.impl.number.Padder.PadPosition;
     15 import com.ibm.icu.impl.number.PatternStringParser;
     16 import com.ibm.icu.impl.number.PatternStringUtils;
     17 import com.ibm.icu.impl.number.parse.NumberParserImpl;
     18 import com.ibm.icu.number.LocalizedNumberFormatter;
     19 import com.ibm.icu.number.NumberFormatter;
     20 import com.ibm.icu.text.DecimalFormat;
     21 import com.ibm.icu.text.DecimalFormat.PropertySetter;
     22 import com.ibm.icu.text.DecimalFormatSymbols;
     23 import com.ibm.icu.util.CurrencyAmount;
     24 import com.ibm.icu.util.ULocale;
     25 
     26 public class NumberFormatDataDrivenTest {
     27 
     28     private static ULocale EN = new ULocale("en");
     29 
     30     private static Number toNumber(String s) {
     31         if (s.equals("NaN")) {
     32             return Double.NaN;
     33         } else if (s.equals("-Inf")) {
     34             return Double.NEGATIVE_INFINITY;
     35         } else if (s.equals("Inf")) {
     36             return Double.POSITIVE_INFINITY;
     37         }
     38         return new BigDecimal(s);
     39     }
     40 
     41     /**
     42      * Standard function for comparing expected and actual parse results. Handles NaN, Infinity, and
     43      * failure cases.
     44      */
     45     private static String compareParseResult(String expected, Number actual, ParsePosition ppos) {
     46         if (actual == null && ppos.getIndex() != 0) {
     47             throw new AssertionError("Error: value is null but parse position is not zero");
     48         }
     49         if (ppos.getIndex() == 0) {
     50             return "Parse failed; got " + actual + ", but expected " + expected;
     51         }
     52         if (expected.equals("NaN")) {
     53             if (!Double.isNaN(actual.doubleValue())) {
     54                 return "Expected NaN, but got: " + actual;
     55             }
     56             return null;
     57         } else if (expected.equals("Inf")) {
     58             if (!Double.isInfinite(actual.doubleValue())
     59                     || Double.compare(actual.doubleValue(), 0.0) < 0) {
     60                 return "Expected Inf, but got: " + actual;
     61             }
     62             return null;
     63         } else if (expected.equals("-Inf")) {
     64             if (!Double.isInfinite(actual.doubleValue())
     65                     || Double.compare(actual.doubleValue(), 0.0) > 0) {
     66                 return "Expected -Inf, but got: " + actual;
     67             }
     68             return null;
     69         } else if (expected.equals("fail")) {
     70             return null;
     71         } else if (actual.toString().equals("Infinity")) {
     72             return "Expected " + expected + ", but got Infinity";
     73         } else {
     74             BigDecimal expectedDecimal = new BigDecimal(expected);
     75             BigDecimal actualDecimal;
     76             try {
     77                 actualDecimal = new BigDecimal(actual.toString());
     78             } catch (NumberFormatException e) {
     79                 throw new AssertionError("Could not convert to BigDecimal: " + actual.toString() + " - " + e.getMessage());
     80             }
     81             if (expectedDecimal.compareTo(actualDecimal) != 0) {
     82                 return "Expected: " + expected + ", got: " + actual;
     83             } else {
     84                 return null;
     85             }
     86         }
     87     }
     88 
     89     /**
     90      * Standard function for comparing expected and actual parse-currency results. Handles failure cases.
     91      * Does not currently handle NaN or Infinity because there are no parse-currency cases with NaN or
     92      * Infinity.
     93      */
     94     private static String compareParseCurrencyResult(
     95             String expected,
     96             String expectedCurrency,
     97             CurrencyAmount actual,
     98             ParsePosition ppos) {
     99         if (ppos.getIndex() == 0 || actual.getCurrency().getCurrencyCode().equals("XXX")) {
    100             return "Parse failed; got " + actual + ", but expected " + expected;
    101         }
    102         if (expected.equals("fail")) {
    103             return null;
    104         }
    105         BigDecimal expectedNumber = new BigDecimal(expected);
    106         if (expectedNumber.compareTo(new BigDecimal(actual.getNumber().toString())) != 0) {
    107             return "Wrong number: Expected: " + expectedNumber + ", got: " + actual;
    108         }
    109         if (!expectedCurrency.equals(actual.getCurrency().toString())) {
    110             return "Wrong currency: Expected: " + expectedCurrency + ", got: " + actual;
    111         }
    112         return null;
    113     }
    114 
    115     /**
    116      * Main ICU4J DecimalFormat data-driven test.
    117      */
    118     private DataDrivenNumberFormatTestUtility.CodeUnderTest ICU4J = new DataDrivenNumberFormatTestUtility.CodeUnderTest() {
    119         @Override
    120         public Character Id() {
    121             return 'J';
    122         }
    123 
    124         @Override
    125         public String format(DataDrivenNumberFormatTestData tuple) {
    126             DecimalFormat fmt = createDecimalFormat(tuple);
    127             String actual = fmt.format(toNumber(tuple.format));
    128             String expected = tuple.output;
    129             if (!expected.equals(actual)) {
    130                 return "Expected " + expected + ", got " + actual;
    131             }
    132             return null;
    133         }
    134 
    135         @Override
    136         public String toPattern(DataDrivenNumberFormatTestData tuple) {
    137             DecimalFormat fmt = createDecimalFormat(tuple);
    138             StringBuilder result = new StringBuilder();
    139             if (tuple.toPattern != null) {
    140                 String expected = tuple.toPattern;
    141                 String actual = fmt.toPattern();
    142                 if (!expected.equals(actual)) {
    143                     result.append("Expected toPattern=" + expected + ", got " + actual);
    144                 }
    145             }
    146             if (tuple.toLocalizedPattern != null) {
    147                 String expected = tuple.toLocalizedPattern;
    148                 String actual = fmt.toLocalizedPattern();
    149                 if (!expected.equals(actual)) {
    150                     result.append("Expected toLocalizedPattern=" + expected + ", got " + actual);
    151                 }
    152             }
    153             return result.length() == 0 ? null : result.toString();
    154         }
    155 
    156         @Override
    157         public String parse(DataDrivenNumberFormatTestData tuple) {
    158             DecimalFormat fmt = createDecimalFormat(tuple);
    159             ParsePosition ppos = new ParsePosition(0);
    160             Number actual = fmt.parse(tuple.parse, ppos);
    161             return compareParseResult(tuple.output, actual, ppos);
    162         }
    163 
    164         @Override
    165         public String parseCurrency(DataDrivenNumberFormatTestData tuple) {
    166             DecimalFormat fmt = createDecimalFormat(tuple);
    167             ParsePosition ppos = new ParsePosition(0);
    168             CurrencyAmount actual = fmt.parseCurrency(tuple.parse, ppos);
    169             return compareParseCurrencyResult(tuple.output, tuple.outputCurrency, actual, ppos);
    170         }
    171 
    172         /**
    173          * @param tuple
    174          * @return
    175          */
    176         private DecimalFormat createDecimalFormat(DataDrivenNumberFormatTestData tuple) {
    177             DecimalFormat fmt = new DecimalFormat(tuple.pattern == null ? "0" : tuple.pattern,
    178                     new DecimalFormatSymbols(tuple.locale == null ? EN : tuple.locale));
    179             adjustDecimalFormat(tuple, fmt);
    180             return fmt;
    181         }
    182 
    183         /**
    184          * @param tuple
    185          * @param fmt
    186          */
    187         private void adjustDecimalFormat(DataDrivenNumberFormatTestData tuple, DecimalFormat fmt) {
    188             if (tuple.minIntegerDigits != null) {
    189                 fmt.setMinimumIntegerDigits(tuple.minIntegerDigits);
    190             }
    191             if (tuple.maxIntegerDigits != null) {
    192                 fmt.setMaximumIntegerDigits(tuple.maxIntegerDigits);
    193             }
    194             if (tuple.minFractionDigits != null) {
    195                 fmt.setMinimumFractionDigits(tuple.minFractionDigits);
    196             }
    197             if (tuple.maxFractionDigits != null) {
    198                 fmt.setMaximumFractionDigits(tuple.maxFractionDigits);
    199             }
    200             if (tuple.currency != null) {
    201                 fmt.setCurrency(tuple.currency);
    202             }
    203             if (tuple.minGroupingDigits != null) {
    204                 fmt.setMinimumGroupingDigits(tuple.minGroupingDigits);
    205             }
    206             if (tuple.useSigDigits != null) {
    207                 fmt.setSignificantDigitsUsed(tuple.useSigDigits != 0);
    208             }
    209             if (tuple.minSigDigits != null) {
    210                 fmt.setMinimumSignificantDigits(tuple.minSigDigits);
    211             }
    212             if (tuple.maxSigDigits != null) {
    213                 fmt.setMaximumSignificantDigits(tuple.maxSigDigits);
    214             }
    215             if (tuple.useGrouping != null) {
    216                 fmt.setGroupingUsed(tuple.useGrouping != 0);
    217             }
    218             if (tuple.multiplier != null) {
    219                 fmt.setMultiplier(tuple.multiplier);
    220             }
    221             if (tuple.roundingIncrement != null) {
    222                 fmt.setRoundingIncrement(tuple.roundingIncrement.doubleValue());
    223             }
    224             if (tuple.formatWidth != null) {
    225                 fmt.setFormatWidth(tuple.formatWidth);
    226             }
    227             if (tuple.padCharacter != null && tuple.padCharacter.length() > 0) {
    228                 fmt.setPadCharacter(tuple.padCharacter.charAt(0));
    229             }
    230             if (tuple.useScientific != null) {
    231                 fmt.setScientificNotation(tuple.useScientific != 0);
    232             }
    233             if (tuple.grouping != null) {
    234                 fmt.setGroupingSize(tuple.grouping);
    235             }
    236             if (tuple.grouping2 != null) {
    237                 fmt.setSecondaryGroupingSize(tuple.grouping2);
    238             }
    239             if (tuple.roundingMode != null) {
    240                 fmt.setRoundingMode(tuple.roundingMode);
    241             }
    242             if (tuple.currencyUsage != null) {
    243                 fmt.setCurrencyUsage(tuple.currencyUsage);
    244             }
    245             if (tuple.minimumExponentDigits != null) {
    246                 fmt.setMinimumExponentDigits(tuple.minimumExponentDigits.byteValue());
    247             }
    248             if (tuple.exponentSignAlwaysShown != null) {
    249                 fmt.setExponentSignAlwaysShown(tuple.exponentSignAlwaysShown != 0);
    250             }
    251             if (tuple.decimalSeparatorAlwaysShown != null) {
    252                 fmt.setDecimalSeparatorAlwaysShown(tuple.decimalSeparatorAlwaysShown != 0);
    253             }
    254             if (tuple.padPosition != null) {
    255                 fmt.setPadPosition(tuple.padPosition);
    256             }
    257             if (tuple.positivePrefix != null) {
    258                 fmt.setPositivePrefix(tuple.positivePrefix);
    259             }
    260             if (tuple.positiveSuffix != null) {
    261                 fmt.setPositiveSuffix(tuple.positiveSuffix);
    262             }
    263             if (tuple.negativePrefix != null) {
    264                 fmt.setNegativePrefix(tuple.negativePrefix);
    265             }
    266             if (tuple.negativeSuffix != null) {
    267                 fmt.setNegativeSuffix(tuple.negativeSuffix);
    268             }
    269             if (tuple.signAlwaysShown != null) {
    270                 fmt.setSignAlwaysShown(tuple.signAlwaysShown != 0);
    271             }
    272             if (tuple.localizedPattern != null) {
    273                 fmt.applyLocalizedPattern(tuple.localizedPattern);
    274             }
    275             int lenient = tuple.lenient == null ? 1 : tuple.lenient.intValue();
    276             fmt.setParseStrict(lenient == 0);
    277             if (tuple.parseIntegerOnly != null) {
    278                 fmt.setParseIntegerOnly(tuple.parseIntegerOnly != 0);
    279             }
    280             if (tuple.parseCaseSensitive != null) {
    281                 fmt.setParseCaseSensitive(tuple.parseCaseSensitive != 0);
    282             }
    283             if (tuple.decimalPatternMatchRequired != null) {
    284                 fmt.setDecimalPatternMatchRequired(tuple.decimalPatternMatchRequired != 0);
    285             }
    286             if (tuple.parseNoExponent != null) {
    287                 fmt.setParseNoExponent(tuple.parseNoExponent != 0);
    288             }
    289         }
    290     };
    291 
    292     /**
    293      * Backwards-compatibility test: snapshot of DecimalFormat from ICU 58.
    294      */
    295     // Android patch: Android can't access DecimalFormat_ICU58 for testing (b/33448125).
    296     // That class lived in a package under test and relied on package access, but
    297     // 1.) Android Compatibility Test Suite (CTS) run tests with a different ClassLoader,
    298     //     preventing package access, and
    299     // 2.) By default, the OpenJDK 9 toolchain won't compile non-libcore code that in
    300     //     libcore packages (see http://b/68224249).
    301     /*
    302     private DataDrivenNumberFormatTestUtility.CodeUnderTest ICU58 = new DataDrivenNumberFormatTestUtility.CodeUnderTest() {
    303         @Override
    304         public Character Id() {
    305             return 'H';
    306         }
    307 
    308         @Override
    309         public String format(DataDrivenNumberFormatTestData tuple) {
    310             DecimalFormat_ICU58 fmt = createDecimalFormat(tuple);
    311             String actual = fmt.format(toNumber(tuple.format));
    312             String expected = tuple.output;
    313             if (!expected.equals(actual)) {
    314                 return "Expected " + expected + ", got " + actual;
    315             }
    316             return null;
    317         }
    318 
    319         @Override
    320         public String toPattern(DataDrivenNumberFormatTestData tuple) {
    321             DecimalFormat_ICU58 fmt = createDecimalFormat(tuple);
    322             StringBuilder result = new StringBuilder();
    323             if (tuple.toPattern != null) {
    324                 String expected = tuple.toPattern;
    325                 String actual = fmt.toPattern();
    326                 if (!expected.equals(actual)) {
    327                     result.append("Expected toPattern=" + expected + ", got " + actual);
    328                 }
    329             }
    330             if (tuple.toLocalizedPattern != null) {
    331                 String expected = tuple.toLocalizedPattern;
    332                 String actual = fmt.toLocalizedPattern();
    333                 if (!expected.equals(actual)) {
    334                     result.append("Expected toLocalizedPattern=" + expected + ", got " + actual);
    335                 }
    336             }
    337             return result.length() == 0 ? null : result.toString();
    338         }
    339 
    340         @Override
    341         public String parse(DataDrivenNumberFormatTestData tuple) {
    342             DecimalFormat_ICU58 fmt = createDecimalFormat(tuple);
    343             ParsePosition ppos = new ParsePosition(0);
    344             Number actual = fmt.parse(tuple.parse, ppos);
    345             return compareParseResult(tuple.output, actual, ppos);
    346         }
    347 
    348         @Override
    349         public String parseCurrency(DataDrivenNumberFormatTestData tuple) {
    350             DecimalFormat_ICU58 fmt = createDecimalFormat(tuple);
    351             ParsePosition ppos = new ParsePosition(0);
    352             CurrencyAmount actual = fmt.parseCurrency(tuple.parse, ppos);
    353             return compareParseCurrencyResult(tuple.output, tuple.outputCurrency, actual, ppos);
    354         }
    355 
    356         /**
    357          * @param tuple
    358          * @return
    359          *
    360         private DecimalFormat_ICU58 createDecimalFormat(DataDrivenNumberFormatTestData tuple) {
    361 
    362             DecimalFormat_ICU58 fmt = new DecimalFormat_ICU58(
    363                     tuple.pattern == null ? "0" : tuple.pattern,
    364                     new DecimalFormatSymbols(tuple.locale == null ? EN : tuple.locale));
    365             adjustDecimalFormat(tuple, fmt);
    366             return fmt;
    367         }
    368 
    369         /**
    370          * @param tuple
    371          * @param fmt
    372          *
    373         private void adjustDecimalFormat(DataDrivenNumberFormatTestData tuple, DecimalFormat_ICU58 fmt) {
    374             if (tuple.minIntegerDigits != null) {
    375                 fmt.setMinimumIntegerDigits(tuple.minIntegerDigits);
    376             }
    377             if (tuple.maxIntegerDigits != null) {
    378                 fmt.setMaximumIntegerDigits(tuple.maxIntegerDigits);
    379             }
    380             if (tuple.minFractionDigits != null) {
    381                 fmt.setMinimumFractionDigits(tuple.minFractionDigits);
    382             }
    383             if (tuple.maxFractionDigits != null) {
    384                 fmt.setMaximumFractionDigits(tuple.maxFractionDigits);
    385             }
    386             if (tuple.currency != null) {
    387                 fmt.setCurrency(tuple.currency);
    388             }
    389             if (tuple.minGroupingDigits != null) {
    390                 // Oops we don't support this.
    391             }
    392             if (tuple.useSigDigits != null) {
    393                 fmt.setSignificantDigitsUsed(tuple.useSigDigits != 0);
    394             }
    395             if (tuple.minSigDigits != null) {
    396                 fmt.setMinimumSignificantDigits(tuple.minSigDigits);
    397             }
    398             if (tuple.maxSigDigits != null) {
    399                 fmt.setMaximumSignificantDigits(tuple.maxSigDigits);
    400             }
    401             if (tuple.useGrouping != null) {
    402                 fmt.setGroupingUsed(tuple.useGrouping != 0);
    403             }
    404             if (tuple.multiplier != null) {
    405                 fmt.setMultiplier(tuple.multiplier);
    406             }
    407             if (tuple.roundingIncrement != null) {
    408                 fmt.setRoundingIncrement(tuple.roundingIncrement.doubleValue());
    409             }
    410             if (tuple.formatWidth != null) {
    411                 fmt.setFormatWidth(tuple.formatWidth);
    412             }
    413             if (tuple.padCharacter != null && tuple.padCharacter.length() > 0) {
    414                 fmt.setPadCharacter(tuple.padCharacter.charAt(0));
    415             }
    416             if (tuple.useScientific != null) {
    417                 fmt.setScientificNotation(tuple.useScientific != 0);
    418             }
    419             if (tuple.grouping != null) {
    420                 fmt.setGroupingSize(tuple.grouping);
    421             }
    422             if (tuple.grouping2 != null) {
    423                 fmt.setSecondaryGroupingSize(tuple.grouping2);
    424             }
    425             if (tuple.roundingMode != null) {
    426                 fmt.setRoundingMode(tuple.roundingMode);
    427             }
    428             if (tuple.currencyUsage != null) {
    429                 fmt.setCurrencyUsage(tuple.currencyUsage);
    430             }
    431             if (tuple.minimumExponentDigits != null) {
    432                 fmt.setMinimumExponentDigits(tuple.minimumExponentDigits.byteValue());
    433             }
    434             if (tuple.exponentSignAlwaysShown != null) {
    435                 fmt.setExponentSignAlwaysShown(tuple.exponentSignAlwaysShown != 0);
    436             }
    437             if (tuple.decimalSeparatorAlwaysShown != null) {
    438                 fmt.setDecimalSeparatorAlwaysShown(tuple.decimalSeparatorAlwaysShown != 0);
    439             }
    440             if (tuple.padPosition != null) {
    441                 fmt.setPadPosition(tuple.padPosition);
    442             }
    443             if (tuple.positivePrefix != null) {
    444                 fmt.setPositivePrefix(tuple.positivePrefix);
    445             }
    446             if (tuple.positiveSuffix != null) {
    447                 fmt.setPositiveSuffix(tuple.positiveSuffix);
    448             }
    449             if (tuple.negativePrefix != null) {
    450                 fmt.setNegativePrefix(tuple.negativePrefix);
    451             }
    452             if (tuple.negativeSuffix != null) {
    453                 fmt.setNegativeSuffix(tuple.negativeSuffix);
    454             }
    455             if (tuple.signAlwaysShown != null) {
    456                 // Not supported.
    457             }
    458             if (tuple.localizedPattern != null) {
    459                 fmt.applyLocalizedPattern(tuple.localizedPattern);
    460             }
    461             int lenient = tuple.lenient == null ? 1 : tuple.lenient.intValue();
    462             fmt.setParseStrict(lenient == 0);
    463             if (tuple.parseIntegerOnly != null) {
    464                 fmt.setParseIntegerOnly(tuple.parseIntegerOnly != 0);
    465             }
    466             if (tuple.parseCaseSensitive != null) {
    467                 // Not supported.
    468             }
    469             if (tuple.decimalPatternMatchRequired != null) {
    470                 fmt.setDecimalPatternMatchRequired(tuple.decimalPatternMatchRequired != 0);
    471             }
    472             if (tuple.parseNoExponent != null) {
    473                 // Oops, not supported for now
    474             }
    475         }
    476     };
    477     */
    478     // Android patch end.
    479 
    480     /**
    481      * Test of available JDK APIs.
    482      */
    483     private DataDrivenNumberFormatTestUtility.CodeUnderTest JDK = new DataDrivenNumberFormatTestUtility.CodeUnderTest() {
    484         @Override
    485         public Character Id() {
    486             return 'K';
    487         }
    488 
    489         @Override
    490         public String format(DataDrivenNumberFormatTestData tuple) {
    491             java.text.DecimalFormat fmt = createDecimalFormat(tuple);
    492             String actual = fmt.format(toNumber(tuple.format));
    493             String expected = tuple.output;
    494             if (!expected.equals(actual)) {
    495                 return "Expected " + expected + ", got " + actual;
    496             }
    497             return null;
    498         }
    499 
    500         @Override
    501         public String toPattern(DataDrivenNumberFormatTestData tuple) {
    502             java.text.DecimalFormat fmt = createDecimalFormat(tuple);
    503             StringBuilder result = new StringBuilder();
    504             if (tuple.toPattern != null) {
    505                 String expected = tuple.toPattern;
    506                 String actual = fmt.toPattern();
    507                 if (!expected.equals(actual)) {
    508                     result.append("Expected toPattern=" + expected + ", got " + actual);
    509                 }
    510             }
    511             if (tuple.toLocalizedPattern != null) {
    512                 String expected = tuple.toLocalizedPattern;
    513                 String actual = fmt.toLocalizedPattern();
    514                 if (!expected.equals(actual)) {
    515                     result.append("Expected toLocalizedPattern=" + expected + ", got " + actual);
    516                 }
    517             }
    518             return result.length() == 0 ? null : result.toString();
    519         }
    520 
    521         @Override
    522         public String parse(DataDrivenNumberFormatTestData tuple) {
    523             java.text.DecimalFormat fmt = createDecimalFormat(tuple);
    524             ParsePosition ppos = new ParsePosition(0);
    525             Number actual = fmt.parse(tuple.parse, ppos);
    526             return compareParseResult(tuple.output, actual, ppos);
    527         }
    528 
    529         /**
    530          * @param tuple
    531          * @return
    532          */
    533         private java.text.DecimalFormat createDecimalFormat(DataDrivenNumberFormatTestData tuple) {
    534             java.text.DecimalFormat fmt = new java.text.DecimalFormat(
    535                     tuple.pattern == null ? "0" : tuple.pattern,
    536                     new java.text.DecimalFormatSymbols(
    537                             (tuple.locale == null ? EN : tuple.locale).toLocale()));
    538             adjustDecimalFormat(tuple, fmt);
    539             return fmt;
    540         }
    541 
    542         /**
    543          * @param tuple
    544          * @param fmt
    545          */
    546         private void adjustDecimalFormat(
    547                 DataDrivenNumberFormatTestData tuple,
    548                 java.text.DecimalFormat fmt) {
    549             if (tuple.minIntegerDigits != null) {
    550                 fmt.setMinimumIntegerDigits(tuple.minIntegerDigits);
    551             }
    552             if (tuple.maxIntegerDigits != null) {
    553                 fmt.setMaximumIntegerDigits(tuple.maxIntegerDigits);
    554             }
    555             if (tuple.minFractionDigits != null) {
    556                 fmt.setMinimumFractionDigits(tuple.minFractionDigits);
    557             }
    558             if (tuple.maxFractionDigits != null) {
    559                 fmt.setMaximumFractionDigits(tuple.maxFractionDigits);
    560             }
    561             if (tuple.currency != null) {
    562                 fmt.setCurrency(java.util.Currency.getInstance(tuple.currency.toString()));
    563             }
    564             if (tuple.minGroupingDigits != null) {
    565                 // Oops we don't support this.
    566             }
    567             if (tuple.useSigDigits != null) {
    568                 // Oops we don't support this
    569             }
    570             if (tuple.minSigDigits != null) {
    571                 // Oops we don't support this
    572             }
    573             if (tuple.maxSigDigits != null) {
    574                 // Oops we don't support this
    575             }
    576             if (tuple.useGrouping != null) {
    577                 fmt.setGroupingUsed(tuple.useGrouping != 0);
    578             }
    579             if (tuple.multiplier != null) {
    580                 fmt.setMultiplier(tuple.multiplier);
    581             }
    582             if (tuple.roundingIncrement != null) {
    583                 // Not supported
    584             }
    585             if (tuple.formatWidth != null) {
    586                 // Not supported
    587             }
    588             if (tuple.padCharacter != null && tuple.padCharacter.length() > 0) {
    589                 // Not supported
    590             }
    591             if (tuple.useScientific != null) {
    592                 // Not supported
    593             }
    594             if (tuple.grouping != null) {
    595                 fmt.setGroupingSize(tuple.grouping);
    596             }
    597             if (tuple.grouping2 != null) {
    598                 // Not supported
    599             }
    600             if (tuple.roundingMode != null) {
    601                 // Not supported
    602             }
    603             if (tuple.currencyUsage != null) {
    604                 // Not supported
    605             }
    606             if (tuple.minimumExponentDigits != null) {
    607                 // Not supported
    608             }
    609             if (tuple.exponentSignAlwaysShown != null) {
    610                 // Not supported
    611             }
    612             if (tuple.decimalSeparatorAlwaysShown != null) {
    613                 fmt.setDecimalSeparatorAlwaysShown(tuple.decimalSeparatorAlwaysShown != 0);
    614             }
    615             if (tuple.padPosition != null) {
    616                 // Not supported
    617             }
    618             if (tuple.positivePrefix != null) {
    619                 fmt.setPositivePrefix(tuple.positivePrefix);
    620             }
    621             if (tuple.positiveSuffix != null) {
    622                 fmt.setPositiveSuffix(tuple.positiveSuffix);
    623             }
    624             if (tuple.negativePrefix != null) {
    625                 fmt.setNegativePrefix(tuple.negativePrefix);
    626             }
    627             if (tuple.negativeSuffix != null) {
    628                 fmt.setNegativeSuffix(tuple.negativeSuffix);
    629             }
    630             if (tuple.signAlwaysShown != null) {
    631                 // Not supported.
    632             }
    633             if (tuple.localizedPattern != null) {
    634                 fmt.applyLocalizedPattern(tuple.localizedPattern);
    635             }
    636 
    637             // lenient parsing not supported by JDK
    638             if (tuple.parseIntegerOnly != null) {
    639                 fmt.setParseIntegerOnly(tuple.parseIntegerOnly != 0);
    640             }
    641             if (tuple.parseCaseSensitive != null) {
    642                 // Not supported.
    643             }
    644             if (tuple.decimalPatternMatchRequired != null) {
    645                 // Oops, not supported
    646             }
    647             if (tuple.parseNoExponent != null) {
    648                 // Oops, not supported for now
    649             }
    650         }
    651     };
    652 
    653     static void propertiesFromTuple(
    654             DataDrivenNumberFormatTestData tuple,
    655             DecimalFormatProperties properties) {
    656         if (tuple.minIntegerDigits != null) {
    657             properties.setMinimumIntegerDigits(tuple.minIntegerDigits);
    658         }
    659         if (tuple.maxIntegerDigits != null) {
    660             properties.setMaximumIntegerDigits(tuple.maxIntegerDigits);
    661         }
    662         if (tuple.minFractionDigits != null) {
    663             properties.setMinimumFractionDigits(tuple.minFractionDigits);
    664         }
    665         if (tuple.maxFractionDigits != null) {
    666             properties.setMaximumFractionDigits(tuple.maxFractionDigits);
    667         }
    668         if (tuple.currency != null) {
    669             properties.setCurrency(tuple.currency);
    670         }
    671         if (tuple.minGroupingDigits != null) {
    672             properties.setMinimumGroupingDigits(tuple.minGroupingDigits);
    673         }
    674         if (tuple.useSigDigits != null) {
    675             // TODO
    676         }
    677         if (tuple.minSigDigits != null) {
    678             properties.setMinimumSignificantDigits(tuple.minSigDigits);
    679         }
    680         if (tuple.maxSigDigits != null) {
    681             properties.setMaximumSignificantDigits(tuple.maxSigDigits);
    682         }
    683         if (tuple.useGrouping != null) {
    684             properties.setGroupingUsed(tuple.useGrouping > 0);
    685         }
    686         if (tuple.multiplier != null) {
    687             properties.setMultiplier(new BigDecimal(tuple.multiplier));
    688         }
    689         if (tuple.roundingIncrement != null) {
    690             properties.setRoundingIncrement(new BigDecimal(tuple.roundingIncrement.toString()));
    691         }
    692         if (tuple.formatWidth != null) {
    693             properties.setFormatWidth(tuple.formatWidth);
    694         }
    695         if (tuple.padCharacter != null && tuple.padCharacter.length() > 0) {
    696             properties.setPadString(tuple.padCharacter.toString());
    697         }
    698         if (tuple.useScientific != null) {
    699             properties.setMinimumExponentDigits(tuple.useScientific != 0 ? 1 : -1);
    700         }
    701         if (tuple.grouping != null) {
    702             properties.setGroupingSize(tuple.grouping);
    703         }
    704         if (tuple.grouping2 != null) {
    705             properties.setSecondaryGroupingSize(tuple.grouping2);
    706         }
    707         if (tuple.roundingMode != null) {
    708             properties.setRoundingMode(RoundingMode.valueOf(tuple.roundingMode));
    709         }
    710         if (tuple.currencyUsage != null) {
    711             properties.setCurrencyUsage(tuple.currencyUsage);
    712         }
    713         if (tuple.minimumExponentDigits != null) {
    714             properties.setMinimumExponentDigits(tuple.minimumExponentDigits.byteValue());
    715         }
    716         if (tuple.exponentSignAlwaysShown != null) {
    717             properties.setExponentSignAlwaysShown(tuple.exponentSignAlwaysShown != 0);
    718         }
    719         if (tuple.decimalSeparatorAlwaysShown != null) {
    720             properties.setDecimalSeparatorAlwaysShown(tuple.decimalSeparatorAlwaysShown != 0);
    721         }
    722         if (tuple.padPosition != null) {
    723             properties.setPadPosition(PadPosition.fromOld(tuple.padPosition));
    724         }
    725         if (tuple.positivePrefix != null) {
    726             properties.setPositivePrefix(tuple.positivePrefix);
    727         }
    728         if (tuple.positiveSuffix != null) {
    729             properties.setPositiveSuffix(tuple.positiveSuffix);
    730         }
    731         if (tuple.negativePrefix != null) {
    732             properties.setNegativePrefix(tuple.negativePrefix);
    733         }
    734         if (tuple.negativeSuffix != null) {
    735             properties.setNegativeSuffix(tuple.negativeSuffix);
    736         }
    737         if (tuple.signAlwaysShown != null) {
    738             properties.setSignAlwaysShown(tuple.signAlwaysShown != 0);
    739         }
    740         if (tuple.localizedPattern != null) {
    741             DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(tuple.locale);
    742             String converted = PatternStringUtils
    743                     .convertLocalized(tuple.localizedPattern, symbols, false);
    744             PatternStringParser.parseToExistingProperties(converted, properties);
    745         }
    746         if (tuple.lenient != null) {
    747             properties.setParseMode(tuple.lenient == 0 ? ParseMode.STRICT : ParseMode.LENIENT);
    748         }
    749         if (tuple.parseIntegerOnly != null) {
    750             properties.setParseIntegerOnly(tuple.parseIntegerOnly != 0);
    751         }
    752         if (tuple.parseCaseSensitive != null) {
    753             properties.setParseCaseSensitive(tuple.parseCaseSensitive != 0);
    754         }
    755         if (tuple.decimalPatternMatchRequired != null) {
    756             properties.setDecimalPatternMatchRequired(tuple.decimalPatternMatchRequired != 0);
    757         }
    758         if (tuple.parseNoExponent != null) {
    759             properties.setParseNoExponent(tuple.parseNoExponent != 0);
    760         }
    761     }
    762 
    763     /**
    764      * Same as ICU4J, but bypasses the DecimalFormat wrapper and goes directly to the
    765      * DecimalFormatProperties.
    766      */
    767     private DataDrivenNumberFormatTestUtility.CodeUnderTest ICU4J_Properties = new DataDrivenNumberFormatTestUtility.CodeUnderTest() {
    768 
    769         @Override
    770         public Character Id() {
    771             return 'P';
    772         }
    773 
    774         /**
    775          * Runs a single formatting test. On success, returns null. On failure, returns the error. This
    776          * implementation just returns null. Subclasses should override.
    777          *
    778          * @param tuple
    779          *            contains the parameters of the format test.
    780          */
    781         @Override
    782         public String format(DataDrivenNumberFormatTestData tuple) {
    783             String pattern = (tuple.pattern == null) ? "0" : tuple.pattern;
    784             ULocale locale = (tuple.locale == null) ? ULocale.ENGLISH : tuple.locale;
    785             DecimalFormatProperties properties = PatternStringParser.parseToProperties(pattern,
    786                     tuple.currency != null ? PatternStringParser.IGNORE_ROUNDING_ALWAYS
    787                             : PatternStringParser.IGNORE_ROUNDING_NEVER);
    788             propertiesFromTuple(tuple, properties);
    789             DecimalFormatSymbols symbols = DecimalFormatSymbols.getInstance(locale);
    790             LocalizedNumberFormatter fmt = NumberFormatter.fromDecimalFormat(properties, symbols, null)
    791                     .locale(locale);
    792             Number number = toNumber(tuple.format);
    793             String expected = tuple.output;
    794             String actual = fmt.format(number).toString();
    795             if (!expected.equals(actual)) {
    796                 return "Expected \"" + expected + "\", got \"" + actual + "\"";
    797             }
    798             return null;
    799         }
    800 
    801         /**
    802          * Runs a single toPattern test. On success, returns null. On failure, returns the error. This
    803          * implementation just returns null. Subclasses should override.
    804          *
    805          * @param tuple
    806          *            contains the parameters of the format test.
    807          */
    808         @Override
    809         public String toPattern(DataDrivenNumberFormatTestData tuple) {
    810             String pattern = (tuple.pattern == null) ? "0" : tuple.pattern;
    811             final DecimalFormatProperties properties;
    812             DecimalFormat df;
    813             try {
    814                 properties = PatternStringParser.parseToProperties(pattern,
    815                         tuple.currency != null ? PatternStringParser.IGNORE_ROUNDING_ALWAYS
    816                                 : PatternStringParser.IGNORE_ROUNDING_NEVER);
    817                 propertiesFromTuple(tuple, properties);
    818                 // TODO: Use PatternString.propertiesToString() directly. (How to deal with
    819                 // CurrencyUsage?)
    820                 df = new DecimalFormat();
    821                 df.setProperties(new PropertySetter() {
    822                     @Override
    823                     public void set(DecimalFormatProperties props) {
    824                         props.copyFrom(properties);
    825                     }
    826                 });
    827             } catch (IllegalArgumentException e) {
    828                 e.printStackTrace();
    829                 return e.getLocalizedMessage();
    830             }
    831 
    832             if (tuple.toPattern != null) {
    833                 String expected = tuple.toPattern;
    834                 String actual = df.toPattern();
    835                 if (!expected.equals(actual)) {
    836                     return "Expected toPattern='" + expected + "'; got '" + actual + "'";
    837                 }
    838             }
    839             if (tuple.toLocalizedPattern != null) {
    840                 String expected = tuple.toLocalizedPattern;
    841                 String actual = PatternStringUtils.propertiesToPatternString(properties);
    842                 if (!expected.equals(actual)) {
    843                     return "Expected toLocalizedPattern='" + expected + "'; got '" + actual + "'";
    844                 }
    845             }
    846             return null;
    847         }
    848 
    849         @Override
    850         public String parse(DataDrivenNumberFormatTestData tuple) {
    851             String pattern = (tuple.pattern == null) ? "0" : tuple.pattern;
    852             DecimalFormatProperties properties;
    853             ParsePosition ppos = new ParsePosition(0);
    854             Number actual;
    855             try {
    856                 properties = PatternStringParser.parseToProperties(pattern,
    857                         tuple.currency != null ? PatternStringParser.IGNORE_ROUNDING_ALWAYS
    858                                 : PatternStringParser.IGNORE_ROUNDING_NEVER);
    859                 propertiesFromTuple(tuple, properties);
    860                 actual = NumberParserImpl.parseStatic(tuple.parse,
    861                         ppos,
    862                         properties,
    863                         DecimalFormatSymbols.getInstance(tuple.locale));
    864             } catch (IllegalArgumentException e) {
    865                 return "parse exception: " + e.getMessage();
    866             }
    867             return compareParseResult(tuple.output, actual, ppos);
    868         }
    869 
    870         @Override
    871         public String parseCurrency(DataDrivenNumberFormatTestData tuple) {
    872             String pattern = (tuple.pattern == null) ? "0" : tuple.pattern;
    873             DecimalFormatProperties properties;
    874             ParsePosition ppos = new ParsePosition(0);
    875             CurrencyAmount actual;
    876             try {
    877                 properties = PatternStringParser.parseToProperties(pattern,
    878                         tuple.currency != null ? PatternStringParser.IGNORE_ROUNDING_ALWAYS
    879                                 : PatternStringParser.IGNORE_ROUNDING_NEVER);
    880                 propertiesFromTuple(tuple, properties);
    881                 actual = NumberParserImpl.parseStaticCurrency(tuple.parse,
    882                         ppos,
    883                         properties,
    884                         DecimalFormatSymbols.getInstance(tuple.locale));
    885             } catch (IllegalArgumentException e) {
    886                 e.printStackTrace();
    887                 return "parse exception: " + e.getMessage();
    888             }
    889             return compareParseCurrencyResult(tuple.output, tuple.outputCurrency, actual, ppos);
    890         }
    891     };
    892 
    893     @Test
    894     public void TestNoUnknownIDs() {
    895         DataDrivenNumberFormatTestUtility.checkNoUnknownIDs("numberformattestspecification.txt", "CHJKP");
    896     }
    897 
    898     @Test
    899     public void TestDataDrivenICU4J() {
    900         DataDrivenNumberFormatTestUtility
    901                 .runFormatSuiteIncludingKnownFailures("numberformattestspecification.txt", ICU4J);
    902     }
    903 
    904     // Android patch: Android can't access DecimalFormat_ICU58 for testing (b/33448125).
    905     /*
    906     @Test
    907     public void TestDataDrivenICU58() {
    908         // Android can't access DecimalFormat_ICU58 for testing (ticket #13283).
    909         if (TestUtil.getJavaVendor() == TestUtil.JavaVendor.Android)
    910             return;
    911 
    912         DataDrivenNumberFormatTestUtility
    913                 .runFormatSuiteIncludingKnownFailures("numberformattestspecification.txt", ICU58);
    914     }
    915     */
    916     // Android patch end.
    917 
    918     @Test
    919     public void TestDataDrivenJDK() {
    920         // #13373: Since not all JDK implementations are the same, test only whitelisted JDKs
    921         // with known behavior. The JDK version should be occasionally updated.
    922         org.junit.Assume.assumeTrue(TestUtil.getJavaRuntimeName() == TestUtil.JavaRuntimeName.OpenJDK
    923                 && TestUtil.getJavaVersion() == 8);
    924 
    925         DataDrivenNumberFormatTestUtility
    926                 .runFormatSuiteIncludingKnownFailures("numberformattestspecification.txt", JDK);
    927     }
    928 
    929     @Test
    930     public void TestDataDrivenICU4JProperties() {
    931         DataDrivenNumberFormatTestUtility
    932                 .runFormatSuiteIncludingKnownFailures("numberformattestspecification.txt", ICU4J_Properties);
    933     }
    934 }
    935