7180362: RFE: Implement date cutover functionality for currency.properties file
Reviewed-by: naoto
This commit is contained in:
parent
21cc7bf277
commit
19e3f0756e
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -34,6 +34,8 @@ import java.io.IOException;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.security.AccessController;
|
import java.security.AccessController;
|
||||||
import java.security.PrivilegedAction;
|
import java.security.PrivilegedAction;
|
||||||
|
import java.text.ParseException;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.ConcurrentMap;
|
import java.util.concurrent.ConcurrentMap;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
@ -60,7 +62,14 @@ import sun.util.logging.PlatformLogger;
|
|||||||
* and the ISO 4217 currency data respectively. The value part consists of
|
* 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
|
* 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.
|
* 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,
|
||||||
* <p>
|
* <p>
|
||||||
* <code>
|
* <code>
|
||||||
* #Sample currency properties<br>
|
* #Sample currency properties<br>
|
||||||
@ -69,6 +78,20 @@ import sun.util.logging.PlatformLogger;
|
|||||||
* <p>
|
* <p>
|
||||||
* will supersede the currency data for Japan.
|
* will supersede the currency data for Japan.
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
|
* <code>
|
||||||
|
* #Sample currency properties with cutover date<br>
|
||||||
|
* JP=JPZ,999,0,2014-01-01T00:00:00
|
||||||
|
* </code>
|
||||||
|
* <p>
|
||||||
|
* will supersede the currency data for Japan if {@code Currency} class is loaded after
|
||||||
|
* 1st January 2014 00:00:00 GMT.
|
||||||
|
* <p>
|
||||||
|
* 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
|
* @since 1.4
|
||||||
*/
|
*/
|
||||||
public final class Currency implements Serializable {
|
public final class Currency implements Serializable {
|
||||||
@ -100,7 +123,6 @@ public final class Currency implements Serializable {
|
|||||||
private static ConcurrentMap<String, Currency> instances = new ConcurrentHashMap<>(7);
|
private static ConcurrentMap<String, Currency> instances = new ConcurrentHashMap<>(7);
|
||||||
private static HashSet<Currency> available;
|
private static HashSet<Currency> available;
|
||||||
|
|
||||||
|
|
||||||
// Class data: currency data obtained from currency.data file.
|
// Class data: currency data obtained from currency.data file.
|
||||||
// Purpose:
|
// Purpose:
|
||||||
// - determine valid country codes
|
// - determine valid country codes
|
||||||
@ -235,7 +257,9 @@ public final class Currency implements Serializable {
|
|||||||
}
|
}
|
||||||
Set<String> keys = props.stringPropertyNames();
|
Set<String> keys = props.stringPropertyNames();
|
||||||
Pattern propertiesPattern =
|
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) {
|
for (String key : keys) {
|
||||||
replaceCurrencyData(propertiesPattern,
|
replaceCurrencyData(propertiesPattern,
|
||||||
key.toUpperCase(Locale.ROOT),
|
key.toUpperCase(Locale.ROOT),
|
||||||
@ -645,29 +669,38 @@ public final class Currency implements Serializable {
|
|||||||
* consists of "three-letter alphabet code", "three-digit numeric code",
|
* consists of "three-letter alphabet code", "three-digit numeric code",
|
||||||
* and "one-digit (0,1,2, or 3) default fraction digit".
|
* and "one-digit (0,1,2, or 3) default fraction digit".
|
||||||
* For example, "JPZ,392,0".
|
* 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) {
|
private static void replaceCurrencyData(Pattern pattern, String ctry, String curdata) {
|
||||||
|
|
||||||
if (ctry.length() != 2) {
|
if (ctry.length() != 2) {
|
||||||
// ignore invalid country code
|
// ignore invalid country code
|
||||||
String message = new StringBuilder()
|
info("currency.properties entry for " + ctry +
|
||||||
.append("The entry in currency.properties for ")
|
" is ignored because of the invalid country code.", null);
|
||||||
.append(ctry).append(" is ignored because of the invalid country code.")
|
|
||||||
.toString();
|
|
||||||
info(message, null);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Matcher m = pattern.matcher(curdata);
|
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
|
// format is not recognized. ignore the data
|
||||||
String message = new StringBuilder()
|
// if group(4) date string is null and we've 4 values, bad date value
|
||||||
.append("The entry in currency.properties for ")
|
info("currency.properties entry for " + ctry +
|
||||||
.append(ctry)
|
" ignored because the value format is not recognized.", null);
|
||||||
.append(" is ignored because the value format is not recognized.")
|
return;
|
||||||
.toString();
|
}
|
||||||
info(message, null);
|
|
||||||
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -695,6 +728,26 @@ public final class Currency implements Serializable {
|
|||||||
setMainTableEntry(ctry.charAt(0), ctry.charAt(1), entry);
|
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) {
|
private static void info(String message, Throwable t) {
|
||||||
PlatformLogger logger = PlatformLogger.getLogger("java.util.Currency");
|
PlatformLogger logger = PlatformLogger.getLogger("java.util.Currency");
|
||||||
if (logger.isLoggable(PlatformLogger.INFO)) {
|
if (logger.isLoggable(PlatformLogger.INFO)) {
|
||||||
|
@ -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.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -22,11 +22,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
import java.text.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.regex.*;
|
import java.util.regex.*;
|
||||||
|
|
||||||
public class PropertiesTest {
|
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 ++) {
|
for (int i = 0; i < s.length; i ++) {
|
||||||
if ("-d".equals(s[i])) {
|
if ("-d".equals(s[i])) {
|
||||||
i++;
|
i++;
|
||||||
@ -76,7 +77,7 @@ public class PropertiesTest {
|
|||||||
pw.close();
|
pw.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void compare(String beforeFile, String afterFile) {
|
private static void compare(String beforeFile, String afterFile) throws Exception {
|
||||||
// load file contents
|
// load file contents
|
||||||
Properties before = new Properties();
|
Properties before = new Properties();
|
||||||
Properties after = new Properties();
|
Properties after = new Properties();
|
||||||
@ -114,11 +115,23 @@ public class PropertiesTest {
|
|||||||
// test each replacements
|
// test each replacements
|
||||||
keys = p.stringPropertyNames();
|
keys = p.stringPropertyNames();
|
||||||
Pattern propertiesPattern =
|
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) {
|
for (String key: keys) {
|
||||||
String val = p.getProperty(key);
|
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);
|
String afterVal = after.getProperty(key);
|
||||||
System.out.printf("Testing key: %s, val: %s... ", key, val);
|
System.out.printf("Testing key: %s, val: %s... ", key, val);
|
||||||
|
System.out.println("AfterVal is : " + afterVal);
|
||||||
|
|
||||||
Matcher m = propertiesPattern.matcher(val.toUpperCase(Locale.ROOT));
|
Matcher m = propertiesPattern.matcher(val.toUpperCase(Locale.ROOT));
|
||||||
if (!m.find()) {
|
if (!m.find()) {
|
||||||
@ -131,7 +144,6 @@ public class PropertiesTest {
|
|||||||
// ignore this
|
// ignore this
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
Matcher mAfter = propertiesPattern.matcher(afterVal);
|
Matcher mAfter = propertiesPattern.matcher(afterVal);
|
||||||
mAfter.find();
|
mAfter.find();
|
||||||
|
|
||||||
@ -164,4 +176,29 @@ public class PropertiesTest {
|
|||||||
throw new RuntimeException(sb.toString());
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
#
|
#
|
||||||
# @test
|
# @test
|
||||||
# @bug 6332666
|
# @bug 6332666 7180362
|
||||||
# @summary tests the capability of replacing the currency data with user
|
# @summary tests the capability of replacing the currency data with user
|
||||||
# specified currency properties file
|
# specified currency properties file
|
||||||
# @build PropertiesTest
|
# @build PropertiesTest
|
||||||
|
@ -2,9 +2,19 @@
|
|||||||
# Test data for replacing the currency data
|
# Test data for replacing the currency data
|
||||||
#
|
#
|
||||||
JP=JPZ,123,2
|
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
|
ZZ = ZZZ , 999 , 3
|
||||||
|
NO=EUR ,978 ,2, 2099-01-01T00:00:00
|
||||||
|
|
||||||
# invalid entries
|
# invalid entries
|
||||||
GB=123
|
GB=123
|
||||||
FR=zzzzz.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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user