1 /* 2 * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 import java.io.*; 25 import java.text.*; 26 import java.util.*; 27 import java.util.regex.*; 28 import java.util.stream.Collectors; 29 30 public class PropertiesTest { main(String[] args)31 public static void main(String[] args) throws Exception { 32 if (args.length == 2 && args[0].equals("-d")) { 33 dump(args[1]); 34 } else if (args.length == 4 && args[0].equals("-c")) { 35 compare(args[1], args[2], args[3]); 36 } else if (args.length == 1 && args[0].equals("bug7102969")) { 37 bug7102969(); 38 } else if (args.length == 1 && args[0].equals("bug8157138")) { 39 bug8157138(); 40 } else if (args.length == 1 && args[0].equals("bug8190904")) { 41 bug8190904(); 42 } else { 43 System.err.println("Usage: java PropertiesTest -d <dumpfile>"); 44 System.err.println(" java PropertiesTest -c <beforedump> <afterdump> <propsfile>"); 45 System.err.println(" java PropertiesTest bug[JBS bug id number] e.g. bug7102969"); 46 System.exit(-1); 47 } 48 } 49 dump(String outfile)50 private static void dump(String outfile) { 51 File f = new File(outfile); 52 PrintWriter pw; 53 try { 54 f.createNewFile(); 55 pw = new PrintWriter(f); 56 } catch (Exception fnfe) { 57 throw new RuntimeException(fnfe); 58 } 59 for (char c1 = 'A'; c1 <= 'Z'; c1++) { 60 for (char c2 = 'A'; c2 <= 'Z'; c2++) { 61 String ctry = new StringBuilder().append(c1).append(c2).toString(); 62 try { 63 Currency c = Currency.getInstance(new Locale("", ctry)); 64 if (c != null) { 65 pw.printf(Locale.ROOT, "%s=%s,%03d,%1d\n", 66 ctry, 67 c.getCurrencyCode(), 68 c.getNumericCode(), 69 c.getDefaultFractionDigits()); 70 } 71 } catch (IllegalArgumentException iae) { 72 // invalid country code 73 continue; 74 } 75 } 76 } 77 pw.flush(); 78 pw.close(); 79 } 80 compare(String beforeFile, String afterFile, String propsFile)81 private static void compare(String beforeFile, String afterFile, String propsFile) 82 throws IOException 83 { 84 // load file contents 85 Properties before = new Properties(); 86 try (Reader reader = new FileReader(beforeFile)) { 87 before.load(reader); 88 } 89 Properties after = new Properties(); 90 try (Reader reader = new FileReader(afterFile)) { 91 after.load(reader); 92 } 93 94 // remove the same contents from the 'after' properties 95 Set<String> keys = before.stringPropertyNames(); 96 for (String key: keys) { 97 String beforeVal = before.getProperty(key); 98 String afterVal = after.getProperty(key); 99 System.out.printf("Removing country: %s. before: %s, after: %s", key, beforeVal, afterVal); 100 if (beforeVal.equals(afterVal)) { 101 after.remove(key); 102 System.out.printf(" --- removed\n"); 103 } else { 104 System.out.printf(" --- NOT removed\n"); 105 } 106 } 107 108 // now look at the currency.properties 109 Properties p = new Properties(); 110 try (Reader reader = new FileReader(propsFile)) { 111 p.load(reader); 112 } 113 114 // test each replacements 115 keys = p.stringPropertyNames(); 116 Pattern propertiesPattern = 117 Pattern.compile("([A-Z]{3})\\s*,\\s*(\\d{3})\\s*,\\s*" + 118 "(\\d+)\\s*,?\\s*(\\d{4}-\\d{2}-\\d{2}T\\d{2}:" + 119 "\\d{2}:\\d{2})?"); 120 for (String key: keys) { 121 String val = p.getProperty(key); 122 try { 123 if (val.chars().map(c -> c == ',' ? 1 : 0).sum() >= 3 124 && !isPastCutoverDate(val)) { 125 System.out.println("Skipping " + key + " since date is in future"); 126 continue; // skip since date in future (no effect) 127 } 128 } catch (ParseException pe) { 129 // swallow - currency class should not honour this value 130 continue; 131 } 132 String afterVal = after.getProperty(key); 133 System.out.printf("Testing key: %s, val: %s... ", key, val); 134 System.out.println("AfterVal is : " + afterVal); 135 136 if (afterVal == null) { 137 System.out.println("Testing key " + key + " is ignored" 138 + " because of the inconsistent numeric code and/or" 139 + " dfd for the given currency code: "+val); 140 continue; 141 } 142 143 Matcher m = propertiesPattern.matcher(val.toUpperCase(Locale.ROOT)); 144 if (!m.find()) { 145 // format is not recognized. 146 System.out.printf("Format is not recognized.\n"); 147 if (afterVal != null) { 148 throw new RuntimeException("Currency data replacement for "+key+" failed: It was incorrectly altered to "+afterVal); 149 } 150 151 // ignore this 152 continue; 153 } 154 155 String code = m.group(1); 156 int numeric = Integer.parseInt(m.group(2)); 157 int fraction = Integer.parseInt(m.group(3)); 158 if (fraction > 9) { 159 System.out.println("Skipping since the fraction is greater than 9"); 160 continue; 161 } 162 163 Matcher mAfter = propertiesPattern.matcher(afterVal); 164 mAfter.find(); 165 166 String codeAfter = mAfter.group(1); 167 int numericAfter = Integer.parseInt(mAfter.group(2)); 168 int fractionAfter = Integer.parseInt(mAfter.group(3)); 169 if (code.equals(codeAfter) && 170 (numeric == numericAfter)&& 171 (fraction == fractionAfter)) { 172 after.remove(key); 173 } else { 174 throw new RuntimeException("Currency data replacement for "+key+" failed: actual: (alphacode: "+codeAfter+", numcode: "+numericAfter+", fraction: "+fractionAfter+"), expected: (alphacode: "+code+", numcode: "+numeric+", fraction: "+fraction+")"); 175 } 176 System.out.printf("Success!\n"); 177 } 178 if (!after.isEmpty()) { 179 180 keys = after.stringPropertyNames(); 181 for (String key : keys) { 182 String modified = after.getProperty(key); 183 if(!p.containsValue(modified)) { 184 throw new RuntimeException("Unnecessary modification was" 185 + " made to county: "+ key + " with currency value:" 186 + " " + modified); 187 } else { 188 System.out.println(key + " modified by an entry in" 189 + " currency.properties with currency value " 190 + modified); 191 } 192 } 193 } 194 } 195 bug7102969()196 private static void bug7102969() { 197 // check the correct overriding of special case entries 198 Currency cur = Currency.getInstance(new Locale("", "JP")); 199 if (!cur.getCurrencyCode().equals("ABC")) { 200 throw new RuntimeException("[Expected: ABC as currency code of JP, found: " 201 + cur.getCurrencyCode() + "]"); 202 } 203 204 /* check if the currency instance is returned by 205 * getAvailableCurrencies() method 206 */ 207 if (!Currency.getAvailableCurrencies().contains(cur)) { 208 throw new RuntimeException("[The Currency instance [" 209 + cur.getCurrencyCode() + ", " 210 + cur.getNumericCode() + ", " 211 + cur.getDefaultFractionDigits() 212 + "] is not available in the currencies list]"); 213 } 214 215 } 216 bug8157138()217 private static void bug8157138() { 218 219 /* check the currencies which exist only as a special case are 220 * accessible i.e. it should not throw IllegalArgumentException 221 */ 222 try { 223 Currency.getInstance("MAD"); 224 } catch (IllegalArgumentException ex) { 225 throw new RuntimeException("Test Failed: " 226 + "special case currency instance MAD not found" 227 + " via Currency.getInstance(\"MAD\")"); 228 } 229 230 try { 231 Currency.getInstance("ABC"); 232 } catch (IllegalArgumentException ex) { 233 throw new RuntimeException("Test Failed: " 234 + "special case currency instance ABC not found" 235 + " via Currency.getInstance(\"ABC\")"); 236 } 237 238 /* check the currency value is returned by getAvailableCurrencies() 239 * method 240 */ 241 List<Currency> list = Currency.getAvailableCurrencies().stream() 242 .filter(cur -> cur.getCurrencyCode().equals("MAD")) 243 .collect(Collectors.toList()); 244 245 if (list.isEmpty()) { 246 throw new RuntimeException("Test Failed: " 247 + "special case currency instance MAD not found" 248 + " in Currency.getAvailableCurrencies() list"); 249 } 250 251 list = Currency.getAvailableCurrencies().stream() 252 .filter(cur -> cur.getCurrencyCode().equals("ABC")) 253 .collect(Collectors.toList()); 254 255 if (list.isEmpty()) { 256 throw new RuntimeException("Test Failed: " 257 + "special case currency instance ABC not found" 258 + " in Currency.getAvailableCurrencies() list"); 259 } 260 261 } 262 bug8190904()263 private static void bug8190904() { 264 // should throw IllegalArgumentException as currency code 265 // does not exist as valid ISO 4217 code and failed to load 266 // from currency.properties file because of inconsistent numeric/dfd 267 try { 268 Currency.getInstance("MCC"); 269 throw new RuntimeException("[FAILED: Should throw" 270 + " IllegalArgumentException for invalid currency code]"); 271 } catch (IllegalArgumentException ex) { 272 // expected to throw IllegalArgumentException 273 } 274 275 // should keep the XOF instance as XOF,952,0, as the XOF entries in 276 // currency.properties IT=XOF,952,1, XY=XOF,955,0 are ignored because 277 // of inconsistency in numeric code and/or dfd 278 checkCurrencyInstance("XOF", 952, 0); 279 // property entry "AS=USD,841,2" should change all occurences 280 // of USD with USD,841,2 281 checkCurrencyInstance("USD", 841, 2); 282 } 283 284 /** 285 * Test the numeric code and fraction of the Currency instance obtained 286 * by given currencyCode, with the expected numericCode and fraction 287 */ checkCurrencyInstance(String currencyCode, int numericCode, int fraction)288 private static void checkCurrencyInstance(String currencyCode, 289 int numericCode, int fraction) { 290 Currency cur = Currency.getInstance(currencyCode); 291 if (cur.getNumericCode() != numericCode 292 || cur.getDefaultFractionDigits() != fraction) { 293 throw new RuntimeException("[FAILED: Incorrect numeric code or" 294 + " dfd for currency code: " + currencyCode + "]"); 295 } 296 } 297 isPastCutoverDate(String s)298 private static boolean isPastCutoverDate(String s) 299 throws IndexOutOfBoundsException, NullPointerException, ParseException { 300 String dateString = s.substring(s.lastIndexOf(',')+1, s.length()).trim(); 301 SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ROOT); 302 format.setTimeZone(TimeZone.getTimeZone("GMT")); 303 format.setLenient(false); 304 305 long time = format.parse(dateString).getTime(); 306 if (System.currentTimeMillis() - time >= 0L) { 307 return true; 308 } else { 309 return false; 310 } 311 } 312 313 } 314