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