# HG changeset patch # User mullan # Date 1347283090 14400 # Node ID 07980df92cb0ab852d9307006f3cb3858e393ab2 # Parent 5b29e3921008539f2524d262468d8a9ed9bbe934# Parent e47746f0eeb9f5429cb31b96aab17c758586f6f7 Merge diff -r e47746f0eeb9 -r 07980df92cb0 jdk/src/share/classes/java/util/Currency.java --- a/jdk/src/share/classes/java/util/Currency.java Mon Sep 10 09:00:00 2012 -0400 +++ b/jdk/src/share/classes/java/util/Currency.java Mon Sep 10 09:18:10 2012 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -34,6 +34,8 @@ import java.io.Serializable; import java.security.AccessController; import java.security.PrivilegedAction; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.regex.Pattern; @@ -60,7 +62,14 @@ * and the ISO 4217 currency data respectively. The value part consists of * three ISO 4217 values of a currency, i.e., an alphabetic code, a numeric * code, and a minor unit. Those three ISO 4217 values are separated by commas. - * The lines which start with '#'s are considered comment lines. For example, + * The lines which start with '#'s are considered comment lines. An optional UTC + * timestamp may be specified per currency entry if users need to specify a + * cutover date indicating when the new data comes into effect. The timestamp is + * appended to the end of the currency properties and uses a comma as a separator. + * If a UTC datestamp is present and valid, the JRE will only use the new currency + * properties if the current UTC date is later than the date specified at class + * loading time. The format of the timestamp must be of ISO 8601 format : + * {@code 'yyyy-MM-dd'T'HH:mm:ss'}. For example, *

* * #Sample currency properties
@@ -69,6 +78,20 @@ *

* will supersede the currency data for Japan. * + *

+ * + * #Sample currency properties with cutover date
+ * JP=JPZ,999,0,2014-01-01T00:00:00 + *
+ *

+ * will supersede the currency data for Japan if {@code Currency} class is loaded after + * 1st January 2014 00:00:00 GMT. + *

+ * Where syntactically malformed entries are encountered, the entry is ignored + * and the remainder of entries in file are processed. For instances where duplicate + * country code entries exist, the behavior of the Currency information for that + * {@code Currency} is undefined and the remainder of entries in file are processed. + * * @since 1.4 */ public final class Currency implements Serializable { @@ -100,7 +123,6 @@ private static ConcurrentMap instances = new ConcurrentHashMap<>(7); private static HashSet available; - // Class data: currency data obtained from currency.data file. // Purpose: // - determine valid country codes @@ -235,7 +257,9 @@ } Set keys = props.stringPropertyNames(); Pattern propertiesPattern = - Pattern.compile("([A-Z]{3})\\s*,\\s*(\\d{3})\\s*,\\s*([0-3])"); + Pattern.compile("([A-Z]{3})\\s*,\\s*(\\d{3})\\s*,\\s*" + + "([0-3])\\s*,?\\s*(\\d{4}-\\d{2}-\\d{2}T\\d{2}:" + + "\\d{2}:\\d{2})?"); for (String key : keys) { replaceCurrencyData(propertiesPattern, key.toUpperCase(Locale.ROOT), @@ -645,29 +669,38 @@ * consists of "three-letter alphabet code", "three-digit numeric code", * and "one-digit (0,1,2, or 3) default fraction digit". * For example, "JPZ,392,0". - * @throws + * An optional UTC date can be appended to the string (comma separated) + * to allow a currency change take effect after date specified. + * For example, "JP=JPZ,999,0,2014-01-01T00:00:00" has no effect unless + * UTC time is past 1st January 2014 00:00:00 GMT. */ private static void replaceCurrencyData(Pattern pattern, String ctry, String curdata) { if (ctry.length() != 2) { // ignore invalid country code - String message = new StringBuilder() - .append("The entry in currency.properties for ") - .append(ctry).append(" is ignored because of the invalid country code.") - .toString(); - info(message, null); + info("currency.properties entry for " + ctry + + " is ignored because of the invalid country code.", null); return; } Matcher m = pattern.matcher(curdata); - if (!m.find()) { + if (!m.find() || (m.group(4) == null && countOccurrences(curdata, ',') >= 3)) { // format is not recognized. ignore the data - String message = new StringBuilder() - .append("The entry in currency.properties for ") - .append(ctry) - .append(" is ignored because the value format is not recognized.") - .toString(); - info(message, null); + // if group(4) date string is null and we've 4 values, bad date value + info("currency.properties entry for " + ctry + + " ignored because the value format is not recognized.", null); + return; + } + + try { + if (m.group(4) != null && !isPastCutoverDate(m.group(4))) { + info("currency.properties entry for " + ctry + + " ignored since cutover date has not passed :" + curdata, null); + return; + } + } catch (IndexOutOfBoundsException | NullPointerException | ParseException ex) { + info("currency.properties entry for " + ctry + + " ignored since exception encountered :" + ex.getMessage(), null); return; } @@ -695,6 +728,26 @@ setMainTableEntry(ctry.charAt(0), ctry.charAt(1), entry); } + private static boolean isPastCutoverDate(String s) + throws IndexOutOfBoundsException, NullPointerException, ParseException { + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ROOT); + format.setTimeZone(TimeZone.getTimeZone("UTC")); + format.setLenient(false); + long time = format.parse(s.trim()).getTime(); + return System.currentTimeMillis() > time; + + } + + private static int countOccurrences(String value, char match) { + int count = 0; + for (char c : value.toCharArray()) { + if (c == match) { + ++count; + } + } + return count; + } + private static void info(String message, Throwable t) { PlatformLogger logger = PlatformLogger.getLogger("java.util.Currency"); if (logger.isLoggable(PlatformLogger.INFO)) { diff -r e47746f0eeb9 -r 07980df92cb0 jdk/test/java/util/Currency/PropertiesTest.java --- a/jdk/test/java/util/Currency/PropertiesTest.java Mon Sep 10 09:00:00 2012 -0400 +++ b/jdk/test/java/util/Currency/PropertiesTest.java Mon Sep 10 09:18:10 2012 -0400 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,11 +22,12 @@ */ import java.io.*; +import java.text.*; import java.util.*; import java.util.regex.*; public class PropertiesTest { - public static void main(String[] s) { + public static void main(String[] s) throws Exception { for (int i = 0; i < s.length; i ++) { if ("-d".equals(s[i])) { i++; @@ -76,7 +77,7 @@ pw.close(); } - private static void compare(String beforeFile, String afterFile) { + private static void compare(String beforeFile, String afterFile) throws Exception { // load file contents Properties before = new Properties(); Properties after = new Properties(); @@ -114,11 +115,23 @@ // test each replacements keys = p.stringPropertyNames(); Pattern propertiesPattern = - Pattern.compile("([A-Z]{3})\\s*,\\s*(\\d{3})\\s*,\\s*([0-3])"); + Pattern.compile("([A-Z]{3})\\s*,\\s*(\\d{3})\\s*,\\s*" + + "([0-3])\\s*,?\\s*(\\d{4}-\\d{2}-\\d{2}T\\d{2}:" + + "\\d{2}:\\d{2})?"); for (String key: keys) { String val = p.getProperty(key); + try { + if (countOccurrences(val, ',') == 3 && !isPastCutoverDate(val)) { + System.out.println("Skipping since date is in future"); + continue; // skip since date in future (no effect) + } + } catch (ParseException pe) { + // swallow - currency class should not honour this value + continue; + } String afterVal = after.getProperty(key); System.out.printf("Testing key: %s, val: %s... ", key, val); + System.out.println("AfterVal is : " + afterVal); Matcher m = propertiesPattern.matcher(val.toUpperCase(Locale.ROOT)); if (!m.find()) { @@ -131,7 +144,6 @@ // ignore this continue; } - Matcher mAfter = propertiesPattern.matcher(afterVal); mAfter.find(); @@ -164,4 +176,29 @@ throw new RuntimeException(sb.toString()); } } + + private static boolean isPastCutoverDate(String s) + throws IndexOutOfBoundsException, NullPointerException, ParseException { + String dateString = s.substring(s.lastIndexOf(',')+1, s.length()).trim(); + SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.ROOT); + format.setTimeZone(TimeZone.getTimeZone("GMT")); + format.setLenient(false); + + long time = format.parse(dateString).getTime(); + if (System.currentTimeMillis() - time >= 0L) { + return true; + } else { + return false; + } + } + + private static int countOccurrences(String value, char match) { + int count = 0; + for (char c : value.toCharArray()) { + if (c == match) { + ++count; + } + } + return count; + } } diff -r e47746f0eeb9 -r 07980df92cb0 jdk/test/java/util/Currency/PropertiesTest.sh --- a/jdk/test/java/util/Currency/PropertiesTest.sh Mon Sep 10 09:00:00 2012 -0400 +++ b/jdk/test/java/util/Currency/PropertiesTest.sh Mon Sep 10 09:18:10 2012 -0400 @@ -1,7 +1,7 @@ #!/bin/sh # # @test -# @bug 6332666 +# @bug 6332666 7180362 # @summary tests the capability of replacing the currency data with user # specified currency properties file # @build PropertiesTest diff -r e47746f0eeb9 -r 07980df92cb0 jdk/test/java/util/Currency/currency.properties --- a/jdk/test/java/util/Currency/currency.properties Mon Sep 10 09:00:00 2012 -0400 +++ b/jdk/test/java/util/Currency/currency.properties Mon Sep 10 09:18:10 2012 -0400 @@ -2,9 +2,19 @@ # Test data for replacing the currency data # JP=JPZ,123,2 -US=euR,978,2 +ES=ESD,877,2 +US=euR,978,2,2001-01-01T00:00:00 +CM=IED,111,2, 2004-01-01T00:70:00 +SB=EUR,111,2, 2099-01-01T00:00:00 ZZ = ZZZ , 999 , 3 +NO=EUR ,978 ,2, 2099-01-01T00:00:00 # invalid entries GB=123 FR=zzzzz.123 +DE=2009-01-01T00:00:00,EUR,111,2 +IE=euR,111,2,#testcomment +=euR,111,2, 2099-01-01-00-00-00 +FM=DED,194,2,eeee-01-01T00:00:00 +PE=EUR ,978 ,2, 20399-01-01T00:00:00 +MX=SSS,493,2,2001-01-01-00-00-00