1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html#License
3/*
4 *******************************************************************************
5 * Copyright (C) 2015, International Business Machines Corporation and         *
6 * others. All Rights Reserved.                                                *
7 *******************************************************************************
8 */
9package com.ibm.icu.dev.test.format;
10
11import java.lang.reflect.Field;
12import java.lang.reflect.InvocationTargetException;
13import java.lang.reflect.Method;
14import java.util.HashMap;
15import java.util.HashSet;
16import java.util.Map;
17
18import com.ibm.icu.math.BigDecimal;
19import com.ibm.icu.text.DecimalFormat;
20import com.ibm.icu.text.NumberFormat;
21import com.ibm.icu.util.Currency;
22import com.ibm.icu.util.ULocale;
23
24/**
25 * A representation of a single NumberFormat specification test from a data driven test file.
26 * <p>
27 * The purpose of this class is to hide the details of the data driven test file from the
28 * main testing code.
29 * <p>
30 * This class contains fields describing an attribute of the test that may or may
31 * not be set. The name of each attribute corresponds to the name used in the
32 * data driven test file.
33 * <p>
34 * <b>Adding new attributes</b>
35 * <p>
36 * Each attribute name is lower case. Moreover, for each attribute there is also a
37 * setXXX method for that attribute that is used to initialize the attribute from a
38 * String value read from the data file. For example, there is a setLocale(String) method
39 * for the locale attribute and a setCurrency(String) method for the currency attribute.
40 * In general, for an attribute named abcd, the setter will be setAbcd(String).
41 * This naming rule must be strictly followed or else the test runner will not know how to
42 * initialize instances of this class.
43 * <p>
44 * In addition each attribute is listed in the fieldOrdering static array which specifies
45 * The order that attributes are printed whenever there is a test failure.
46 * <p>
47 * To add a new attribute, first create a public field for it.
48 * Next, add the attribute name to the fieldOrdering array.
49 * Finally, create a setter method for it.
50 *
51 * @author rocketman
52 */
53public class NumberFormatTestData {
54
55    /**
56     * The locale.
57     */
58    public ULocale locale = null;
59
60    /**
61     * The currency.
62     */
63    public Currency currency = null;
64
65    /**
66     * The pattern to initialize the formatter, for example 0.00"
67     */
68    public String pattern = null;
69
70    /**
71     * The value to format as a string. For example 1234.5 would be "1234.5"
72     */
73    public String format = null;
74
75    /**
76     * The formatted value.
77     */
78    public String output = null;
79
80    /**
81     * Field for arbitrary comments.
82     */
83    public String comment = null;
84
85    public Integer minIntegerDigits = null;
86    public Integer maxIntegerDigits = null;
87    public Integer minFractionDigits = null;
88    public Integer maxFractionDigits = null;
89    public Integer minGroupingDigits = null;
90    public Integer useSigDigits = null;
91    public Integer minSigDigits = null;
92    public Integer maxSigDigits = null;
93    public Integer useGrouping = null;
94    public Integer multiplier = null;
95    public Double roundingIncrement = null;
96    public Integer formatWidth = null;
97    public String padCharacter = null;
98    public Integer useScientific = null;
99    public Integer grouping = null;
100    public Integer grouping2 = null;
101    public Integer roundingMode = null;
102    public Currency.CurrencyUsage currencyUsage = null;
103    public Integer minimumExponentDigits = null;
104    public Integer exponentSignAlwaysShown = null;
105    public Integer decimalSeparatorAlwaysShown = null;
106    public Integer padPosition = null;
107    public String positivePrefix = null;
108    public String positiveSuffix = null;
109    public String negativePrefix = null;
110    public String negativeSuffix = null;
111    public String localizedPattern = null;
112    public String toPattern = null;
113    public String toLocalizedPattern = null;
114    public Integer style = null;
115    public String parse = null;
116    public Integer lenient = null;
117    public String plural = null;
118    public Integer parseIntegerOnly = null;
119    public Integer decimalPatternMatchRequired = null;
120    public Integer parseNoExponent = null;
121    public String outputCurrency = null;
122
123
124
125    /**
126     * nothing or empty means that test ought to work for both C and JAVA;
127     * "C" means test is known to fail in C. "J" means test is known to fail in JAVA.
128     * "CJ" means test is known to fail for both languages.
129     */
130    public String breaks = null;
131
132    private static Map<String, Integer> roundingModeMap =
133            new HashMap<String, Integer>();
134
135    static {
136        roundingModeMap.put("ceiling", BigDecimal.ROUND_CEILING);
137        roundingModeMap.put("floor", BigDecimal.ROUND_FLOOR);
138        roundingModeMap.put("down", BigDecimal.ROUND_DOWN);
139        roundingModeMap.put("up", BigDecimal.ROUND_UP);
140        roundingModeMap.put("halfEven", BigDecimal.ROUND_HALF_EVEN);
141        roundingModeMap.put("halfDown", BigDecimal.ROUND_HALF_DOWN);
142        roundingModeMap.put("halfUp", BigDecimal.ROUND_HALF_UP);
143        roundingModeMap.put("unnecessary", BigDecimal.ROUND_UNNECESSARY);
144    }
145
146    private static Map<String, Currency.CurrencyUsage> currencyUsageMap =
147            new HashMap<String, Currency.CurrencyUsage>();
148
149    static {
150        currencyUsageMap.put("standard", Currency.CurrencyUsage.STANDARD);
151        currencyUsageMap.put("cash", Currency.CurrencyUsage.CASH);
152    }
153
154    private static Map<String, Integer> padPositionMap =
155            new HashMap<String, Integer>();
156
157    static {
158        // TODO: Fix so that it doesn't depend on DecimalFormat.
159        padPositionMap.put("beforePrefix", DecimalFormat.PAD_BEFORE_PREFIX);
160        padPositionMap.put("afterPrefix", DecimalFormat.PAD_AFTER_PREFIX);
161        padPositionMap.put("beforeSuffix", DecimalFormat.PAD_BEFORE_SUFFIX);
162        padPositionMap.put("afterSuffix", DecimalFormat.PAD_AFTER_SUFFIX);
163    }
164
165    private static Map<String, Integer> formatStyleMap =
166            new HashMap<String, Integer>();
167
168    static {
169        formatStyleMap.put("decimal", NumberFormat.NUMBERSTYLE);
170        formatStyleMap.put("currency", NumberFormat.CURRENCYSTYLE);
171        formatStyleMap.put("percent", NumberFormat.PERCENTSTYLE);
172        formatStyleMap.put("scientific", NumberFormat.SCIENTIFICSTYLE);
173        formatStyleMap.put("currencyIso", NumberFormat.ISOCURRENCYSTYLE);
174        formatStyleMap.put("currencyPlural", NumberFormat.PLURALCURRENCYSTYLE);
175        formatStyleMap.put("currencyAccounting", NumberFormat.ACCOUNTINGCURRENCYSTYLE);
176        formatStyleMap.put("cashCurrency", NumberFormat.CASHCURRENCYSTYLE);
177    }
178
179    // Add any new fields here. On test failures, fields are printed in the same order they
180    // appear here.
181    private static String[] fieldOrdering = {
182        "locale",
183        "currency",
184        "pattern",
185        "format",
186        "output",
187        "comment",
188        "minIntegerDigits",
189        "maxIntegerDigits",
190        "minFractionDigits",
191        "maxFractionDigits",
192        "minGroupingDigits",
193        "breaks",
194        "useSigDigits",
195        "minSigDigits",
196        "maxSigDigits",
197        "useGrouping",
198        "multiplier",
199        "roundingIncrement",
200        "formatWidth",
201        "padCharacter",
202        "useScientific",
203        "grouping",
204        "grouping2",
205        "roundingMode",
206        "currencyUsage",
207        "minimumExponentDigits",
208        "exponentSignAlwaysShown",
209        "decimalSeparatorAlwaysShown",
210        "padPosition",
211        "positivePrefix",
212        "positiveSuffix",
213        "negativePrefix",
214        "negativeSuffix",
215        "localizedPattern",
216        "toPattern",
217        "toLocalizedPattern",
218        "style",
219        "parse",
220        "lenient",
221        "plural",
222        "parseIntegerOnly",
223        "decimalPatternMatchRequired",
224        "parseNoExponent",
225        "outputCurrency"
226    };
227
228    static {
229        HashSet<String> set = new HashSet<String>();
230        for (String s : fieldOrdering) {
231            if (!set.add(s)) {
232                throw new ExceptionInInitializerError(s + "is a duplicate field.");
233            }
234        }
235    }
236
237    private static <T> T fromString(Map<String, T> map, String key) {
238        T value = map.get(key);
239        if (value == null) {
240            throw new IllegalArgumentException("Bad value: "+ key);
241        }
242        return value;
243    }
244
245    // start field setters.
246    // add setter for each new field in this block.
247
248    public void setLocale(String value) {
249        locale = new ULocale(value);
250    }
251
252    public void setCurrency(String value) {
253        currency = Currency.getInstance(value);
254    }
255
256    public void setPattern(String value) {
257        pattern = value;
258    }
259
260    public void setFormat(String value) {
261        format = value;
262    }
263
264    public void setOutput(String value) {
265        output = value;
266    }
267
268    public void setComment(String value) {
269        comment = value;
270    }
271
272    public void setMinIntegerDigits(String value) {
273        minIntegerDigits = Integer.valueOf(value);
274    }
275
276    public void setMaxIntegerDigits(String value) {
277        maxIntegerDigits = Integer.valueOf(value);
278    }
279
280    public void setMinFractionDigits(String value) {
281        minFractionDigits = Integer.valueOf(value);
282    }
283
284    public void setMaxFractionDigits(String value) {
285        maxFractionDigits = Integer.valueOf(value);
286    }
287
288    public void setMinGroupingDigits(String value) {
289        minGroupingDigits = Integer.valueOf(value);
290    }
291
292    public void setBreaks(String value) {
293        breaks = value;
294    }
295
296    public void setUseSigDigits(String value) {
297        useSigDigits = Integer.valueOf(value);
298    }
299
300    public void setMinSigDigits(String value) {
301        minSigDigits = Integer.valueOf(value);
302    }
303
304    public void setMaxSigDigits(String value) {
305        maxSigDigits = Integer.valueOf(value);
306    }
307
308    public void setUseGrouping(String value) {
309        useGrouping = Integer.valueOf(value);
310    }
311
312    public void setMultiplier(String value) {
313        multiplier = Integer.valueOf(value);
314    }
315
316    public void setRoundingIncrement(String value) {
317        roundingIncrement = Double.valueOf(value);
318    }
319
320    public void setFormatWidth(String value) {
321        formatWidth = Integer.valueOf(value);
322    }
323
324    public void setPadCharacter(String value) {
325        padCharacter = value;
326    }
327
328    public void setUseScientific(String value) {
329        useScientific = Integer.valueOf(value);
330    }
331
332    public void setGrouping(String value) {
333        grouping = Integer.valueOf(value);
334    }
335
336    public void setGrouping2(String value) {
337        grouping2 = Integer.valueOf(value);
338    }
339
340    public void setRoundingMode(String value) {
341        roundingMode = fromString(roundingModeMap, value);
342    }
343
344    public void setCurrencyUsage(String value) {
345        currencyUsage = fromString(currencyUsageMap, value);
346    }
347
348    public void setMinimumExponentDigits(String value) {
349        minimumExponentDigits = Integer.valueOf(value);
350    }
351
352    public void setExponentSignAlwaysShown(String value) {
353        exponentSignAlwaysShown = Integer.valueOf(value);
354    }
355
356    public void setDecimalSeparatorAlwaysShown(String value) {
357        decimalSeparatorAlwaysShown = Integer.valueOf(value);
358    }
359
360    public void setPadPosition(String value) {
361        padPosition = fromString(padPositionMap, value);
362    }
363
364    public void setPositivePrefix(String value) {
365        positivePrefix = value;
366    }
367
368    public void setPositiveSuffix(String value) {
369        positiveSuffix = value;
370    }
371
372    public void setNegativePrefix(String value) {
373        negativePrefix = value;
374    }
375
376    public void setNegativeSuffix(String value) {
377        negativeSuffix = value;
378    }
379
380    public void setLocalizedPattern(String value) {
381        localizedPattern = value;
382    }
383
384    public void setToPattern(String value) {
385        toPattern = value;
386    }
387
388    public void setToLocalizedPattern(String value) {
389        toLocalizedPattern = value;
390    }
391
392    public void setStyle(String value) {
393        style = fromString(formatStyleMap, value);
394    }
395
396    public void setParse(String value) {
397        parse = value;
398    }
399
400    public void setLenient(String value) {
401        lenient = Integer.valueOf(value);
402    }
403
404    public void setPlural(String value) {
405        plural = value;
406    }
407
408    public void setParseIntegerOnly(String value) {
409        parseIntegerOnly = Integer.valueOf(value);
410    }
411
412    public void setDecimalPatternMatchRequired(String value) {
413        decimalPatternMatchRequired = Integer.valueOf(value);
414    }
415
416    public void setParseNoExponent(String value) {
417        parseNoExponent = Integer.valueOf(value);
418    }
419
420    public void setOutputCurrency(String value) {
421        outputCurrency = value;
422    }
423
424    // end field setters.
425
426    // start of field clearers
427    // Add clear methods that can be set in one test and cleared
428    // in the next i.e the breaks field.
429
430    public void clearBreaks() {
431        breaks = null;
432    }
433
434    public void clearUseGrouping() {
435        useGrouping = null;
436    }
437
438    public void clearGrouping2() {
439        grouping2 = null;
440    }
441
442    public void clearGrouping() {
443        grouping = null;
444    }
445
446    public void clearMinGroupingDigits() {
447        minGroupingDigits = null;
448    }
449
450    public void clearUseScientific() {
451        useScientific = null;
452    }
453
454    public void clearDecimalSeparatorAlwaysShown() {
455        decimalSeparatorAlwaysShown = null;
456    }
457
458    // end field clearers
459
460    public void setField(String fieldName, String valueString)
461            throws NoSuchMethodException {
462        Method m = getClass().getMethod(
463                fieldToSetter(fieldName), String.class);
464        try {
465            m.invoke(this, valueString);
466        } catch (IllegalAccessException e) {
467            throw new RuntimeException(e);
468        } catch (InvocationTargetException e) {
469            throw new RuntimeException(e);
470        }
471    }
472
473    public void clearField(String fieldName)
474            throws NoSuchMethodException {
475        Method m = getClass().getMethod(fieldToClearer(fieldName));
476        try {
477            m.invoke(this);
478        } catch (IllegalAccessException e) {
479            throw new RuntimeException(e);
480        } catch (InvocationTargetException e) {
481            throw new RuntimeException(e);
482        }
483    }
484
485    public String toString() {
486        StringBuilder result = new StringBuilder();
487        result.append("{");
488        boolean first = true;
489        for (String fieldName : fieldOrdering) {
490            try {
491                Field field = getClass().getField(fieldName);
492                Object optionalValue = field.get(this);
493                if (optionalValue == null) {
494                    continue;
495                }
496                if (!first) {
497                    result.append(", ");
498                }
499                first = false;
500                result.append(fieldName);
501                result.append(": ");
502                result.append(optionalValue);
503            } catch (NoSuchFieldException e) {
504                throw new RuntimeException(e);
505            } catch (SecurityException e) {
506                throw new RuntimeException(e);
507            } catch (IllegalAccessException e) {
508                throw new RuntimeException(e);
509            }
510        }
511        result.append("}");
512        return result.toString();
513    }
514
515    private static String fieldToSetter(String fieldName) {
516        return "set"
517                + Character.toUpperCase(fieldName.charAt(0))
518                + fieldName.substring(1);
519    }
520
521    private static String fieldToClearer(String fieldName) {
522        return "clear"
523                + Character.toUpperCase(fieldName.charAt(0))
524                + fieldName.substring(1);
525    }
526
527}
528