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