< prev index next >
src/java.base/share/classes/java/util/Currency.java
Print this page
@@ -151,37 +151,17 @@
// 0 - simple country, bits 0-4 indicate final char of currency code
// - bits 5-8: fraction digits for simple countries, 0 for special cases
// - bits 0-4: final char for currency code for simple country, or ID of special case
// - special case IDs:
// - 0: country has no currency
- // - other: index into sc* arrays + 1
- // - scCutOverTimes: cut-over time in millis as returned by
- // System.currentTimeMillis for special case countries that are changing
- // currencies; Long.MAX_VALUE for countries that are not changing currencies
- // - scOldCurrencies: old currencies for special case countries
- // - scNewCurrencies: new currencies for special case countries that are
- // changing currencies; null for others
- // - scOldCurrenciesDFD: default fraction digits for old currencies
- // - scNewCurrenciesDFD: default fraction digits for new currencies, 0 for
- // countries that are not changing currencies
- // - otherCurrencies: concatenation of all currency codes that are not the
- // main currency of a simple country, separated by "-"
- // - otherCurrenciesDFD: decimal format digits for currencies in otherCurrencies, same order
+ // - other: index into specialCasesList
static int formatVersion;
static int dataVersion;
static int[] mainTable;
- static long[] scCutOverTimes;
- static String[] scOldCurrencies;
- static String[] scNewCurrencies;
- static int[] scOldCurrenciesDFD;
- static int[] scNewCurrenciesDFD;
- static int[] scOldCurrenciesNumericCode;
- static int[] scNewCurrenciesNumericCode;
- static String otherCurrencies;
- static int[] otherCurrenciesDFD;
- static int[] otherCurrenciesNumericCode;
+ static List<SpecialCaseEntry> specialCasesList;
+ static List<OtherCurrencyEntry> otherCurrenciesList;
// handy constants - must match definitions in GenerateCurrencyData
// magic number
private static final int MAGIC_NUMBER = 0x43757244;
// number of characters from A to Z
@@ -212,11 +192,11 @@
private static final int NUMERIC_CODE_MASK = 0x000FFC00;
// shift count for the numeric code of the currency
private static final int NUMERIC_CODE_SHIFT = 10;
// Currency data format version
- private static final int VALID_FORMAT_VERSION = 2;
+ private static final int VALID_FORMAT_VERSION = 3;
static {
AccessController.doPrivileged(new PrivilegedAction<>() {
@Override
public Void run() {
@@ -234,21 +214,13 @@
throw new InternalError("Currency data format is incorrect");
}
dataVersion = dis.readInt();
mainTable = readIntArray(dis, A_TO_Z * A_TO_Z);
int scCount = dis.readInt();
- scCutOverTimes = readLongArray(dis, scCount);
- scOldCurrencies = readStringArray(dis, scCount);
- scNewCurrencies = readStringArray(dis, scCount);
- scOldCurrenciesDFD = readIntArray(dis, scCount);
- scNewCurrenciesDFD = readIntArray(dis, scCount);
- scOldCurrenciesNumericCode = readIntArray(dis, scCount);
- scNewCurrenciesNumericCode = readIntArray(dis, scCount);
+ specialCasesList = readSpecialCases(dis, scCount);
int ocCount = dis.readInt();
- otherCurrencies = dis.readUTF();
- otherCurrenciesDFD = readIntArray(dis, ocCount);
- otherCurrenciesNumericCode = readIntArray(dis, ocCount);
+ otherCurrenciesList = readOtherCurrencies(dis, ocCount);
}
} catch (IOException e) {
throw new InternalError(e);
}
@@ -327,10 +299,11 @@
if (defaultFractionDigits == Integer.MIN_VALUE) {
// Currency code not internally generated, need to verify first
// A currency code must have 3 characters and exist in the main table
// or in the list of other currencies.
+ boolean found = false;
if (currencyCode.length() != 3) {
throw new IllegalArgumentException();
}
char char1 = currencyCode.charAt(0);
char char2 = currencyCode.charAt(1);
@@ -338,21 +311,27 @@
if ((tableEntry & COUNTRY_TYPE_MASK) == SIMPLE_CASE_COUNTRY_MASK
&& tableEntry != INVALID_COUNTRY_ENTRY
&& currencyCode.charAt(2) - 'A' == (tableEntry & SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK)) {
defaultFractionDigits = (tableEntry & SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK) >> SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT;
numericCode = (tableEntry & NUMERIC_CODE_MASK) >> NUMERIC_CODE_SHIFT;
- } else {
- // Check for '-' separately so we don't get false hits in the table.
- if (currencyCode.charAt(2) == '-') {
- throw new IllegalArgumentException();
+ found = true;
+ } else { //special case
+ int[] fractionAndNumericCode = SpecialCaseEntry.findEntry(currencyCode);
+ if (fractionAndNumericCode != null) {
+ defaultFractionDigits = fractionAndNumericCode[0];
+ numericCode = fractionAndNumericCode[1];
+ found = true;
}
- int index = otherCurrencies.indexOf(currencyCode);
- if (index == -1) {
+ }
+
+ if (!found) {
+ OtherCurrencyEntry ocEntry = OtherCurrencyEntry.findEntry(currencyCode);
+ if (ocEntry == null) {
throw new IllegalArgumentException();
}
- defaultFractionDigits = otherCurrenciesDFD[index / 4];
- numericCode = otherCurrenciesNumericCode[index / 4];
+ defaultFractionDigits = ocEntry.fraction;
+ numericCode = ocEntry.numericCode;
}
}
Currency currencyVal =
new Currency(currencyCode, defaultFractionDigits, numericCode);
@@ -408,17 +387,21 @@
throw new IllegalArgumentException();
}
if (tableEntry == COUNTRY_WITHOUT_CURRENCY_ENTRY) {
return null;
} else {
- int index = (tableEntry & SPECIAL_CASE_COUNTRY_INDEX_MASK) - SPECIAL_CASE_COUNTRY_INDEX_DELTA;
- if (scCutOverTimes[index] == Long.MAX_VALUE || System.currentTimeMillis() < scCutOverTimes[index]) {
- return getInstance(scOldCurrencies[index], scOldCurrenciesDFD[index],
- scOldCurrenciesNumericCode[index]);
+ int index = SpecialCaseEntry.toIndex(tableEntry);
+ SpecialCaseEntry scEntry = specialCasesList.get(index);
+ if (scEntry.cutOverTime == Long.MAX_VALUE
+ || System.currentTimeMillis() < scEntry.cutOverTime) {
+ return getInstance(scEntry.oldCurrency,
+ scEntry.oldCurrencyFraction,
+ scEntry.oldCurrencyNumericCode);
} else {
- return getInstance(scNewCurrencies[index], scNewCurrenciesDFD[index],
- scNewCurrenciesNumericCode[index]);
+ return getInstance(scEntry.newCurrency,
+ scEntry.newCurrencyFraction,
+ scEntry.newCurrencyNumericCode);
}
}
}
}
@@ -449,18 +432,33 @@
StringBuilder sb = new StringBuilder();
sb.append(c1);
sb.append(c2);
sb.append(finalChar);
available.add(getInstance(sb.toString(), defaultFractionDigits, numericCode));
+ } else if ((tableEntry & COUNTRY_TYPE_MASK) == SPECIAL_CASE_COUNTRY_MASK
+ && tableEntry != INVALID_COUNTRY_ENTRY
+ && tableEntry != COUNTRY_WITHOUT_CURRENCY_ENTRY) {
+ int index = SpecialCaseEntry.toIndex(tableEntry);
+ SpecialCaseEntry scEntry = specialCasesList.get(index);
+
+ if (scEntry.cutOverTime == Long.MAX_VALUE
+ || System.currentTimeMillis() < scEntry.cutOverTime) {
+ available.add(getInstance(scEntry.oldCurrency,
+ scEntry.oldCurrencyFraction,
+ scEntry.oldCurrencyNumericCode));
+ } else {
+ available.add(getInstance(scEntry.newCurrency,
+ scEntry.newCurrencyFraction,
+ scEntry.newCurrencyNumericCode));
+ }
}
}
}
// Now add other currencies
- StringTokenizer st = new StringTokenizer(otherCurrencies, "-");
- while (st.hasMoreElements()) {
- available.add(getInstance((String)st.nextElement()));
+ for (OtherCurrencyEntry entry : otherCurrenciesList) {
+ available.add(getInstance(entry.currencyCode));
}
}
}
@SuppressWarnings("unchecked")
@@ -691,26 +689,59 @@
}
return ret;
}
- private static long[] readLongArray(DataInputStream dis, int count) throws IOException {
- long[] ret = new long[count];
- for (int i = 0; i < count; i++) {
- ret[i] = dis.readLong();
- }
+ private static List<SpecialCaseEntry> readSpecialCases(DataInputStream dis,
+ int count)
+ throws IOException {
+
+ List<SpecialCaseEntry> list = new ArrayList<>(count);
+ long cutOverTime;
+ String oldCurrency;
+ String newCurrency;
+ int oldCurrencyFraction;
+ int newCurrencyFraction;
+ int oldCurrencyNumericCode;
+ int newCurrencyNumericCode;
- return ret;
- }
+ for (int i = 0; i < count; i++) {
+ cutOverTime = dis.readLong();
+ oldCurrency = dis.readUTF();
+ newCurrency = dis.readUTF();
+ oldCurrencyFraction = dis.readInt();
+ newCurrencyFraction = dis.readInt();
+ oldCurrencyNumericCode = dis.readInt();
+ newCurrencyNumericCode = dis.readInt();
+ SpecialCaseEntry sc = new SpecialCaseEntry(cutOverTime,
+ oldCurrency, newCurrency,
+ oldCurrencyFraction, newCurrencyFraction,
+ oldCurrencyNumericCode, newCurrencyNumericCode);
+ list.add(sc);
+ }
+ return list;
+ }
+
+ private static List<OtherCurrencyEntry> readOtherCurrencies(DataInputStream dis,
+ int count)
+ throws IOException {
+
+ List<OtherCurrencyEntry> list = new ArrayList<>(count);
+ String currencyCode;
+ int fraction;
+ int numericCode;
- private static String[] readStringArray(DataInputStream dis, int count) throws IOException {
- String[] ret = new String[count];
for (int i = 0; i < count; i++) {
- ret[i] = dis.readUTF();
+ currencyCode = dis.readUTF();
+ fraction = dis.readInt();
+ numericCode = dis.readInt();
+ OtherCurrencyEntry oc = new OtherCurrencyEntry(currencyCode,
+ fraction,
+ numericCode);
+ list.add(oc);
}
-
- return ret;
+ return list;
}
/**
* Replaces currency data found in the currencydata.properties file
*
@@ -764,25 +795,31 @@
" ignored since the fraction is more than " +
SIMPLE_CASE_COUNTRY_MAX_DEFAULT_DIGITS + ":" + curdata, null);
return;
}
- int index;
- for (index = 0; index < scOldCurrencies.length; index++) {
- if (scOldCurrencies[index].equals(code)) {
- break;
- }
+ int index = SpecialCaseEntry.indexOf(code, fraction, numeric);
+
+ /* if a country switches from simple case to special case or
+ * one special case to other special case which is not present
+ * in the sc arrays then insert the new entry in special case arrays
+ */
+ if (index == -1 && (ctry.charAt(0) != code.charAt(0)
+ || ctry.charAt(1) != code.charAt(1))) {
+
+ specialCasesList.add(new SpecialCaseEntry(code, fraction, numeric));
+ index = specialCasesList.size() - 1;
}
- if (index == scOldCurrencies.length) {
+ if (index == -1) {
// simple case
- entry |= (fraction << SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT) |
- (code.charAt(2) - 'A');
+ entry |= (fraction << SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT)
+ | (code.charAt(2) - 'A');
} else {
// special case
- entry |= SPECIAL_CASE_COUNTRY_MASK |
- (index + SPECIAL_CASE_COUNTRY_INDEX_DELTA);
+ entry = SPECIAL_CASE_COUNTRY_MASK
+ | (index + SPECIAL_CASE_COUNTRY_INDEX_DELTA);
}
setMainTableEntry(ctry.charAt(0), ctry.charAt(1), entry);
}
private static boolean isPastCutoverDate(String s) throws ParseException {
@@ -812,7 +849,130 @@
} else {
logger.info(message);
}
}
}
+
+ /* Used to represent a special case currency entry
+ * - cutOverTime: cut-over time in millis as returned by
+ * System.currentTimeMillis for special case countries that are changing
+ * currencies; Long.MAX_VALUE for countries that are not changing currencies
+ * - oldCurrency: old currencies for special case countries
+ * - newCurrency: new currencies for special case countries that are
+ * changing currencies; null for others
+ * - oldCurrencyFraction: default fraction digits for old currencies
+ * - newCurrencyFraction: default fraction digits for new currencies, 0 for
+ * countries that are not changing currencies
+ * - oldCurrencyNumericCode: numeric code for old currencies
+ * - newCurrencyNumericCode: numeric code for new currencies, 0 for countries
+ * that are not changing currencies
+ */
+ private static class SpecialCaseEntry {
+
+ final private long cutOverTime;
+ final private String oldCurrency;
+ final private String newCurrency;
+ final private int oldCurrencyFraction;
+ final private int newCurrencyFraction;
+ final private int oldCurrencyNumericCode;
+ final private int newCurrencyNumericCode;
+
+ private SpecialCaseEntry(long cutOverTime, String oldCurrency, String newCurrency,
+ int oldCurrencyFraction, int newCurrencyFraction,
+ int oldCurrencyNumericCode, int newCurrencyNumericCode) {
+ this.cutOverTime = cutOverTime;
+ this.oldCurrency = oldCurrency;
+ this.newCurrency = newCurrency;
+ this.oldCurrencyFraction = oldCurrencyFraction;
+ this.newCurrencyFraction = newCurrencyFraction;
+ this.oldCurrencyNumericCode = oldCurrencyNumericCode;
+ this.newCurrencyNumericCode = newCurrencyNumericCode;
+ }
+
+ private SpecialCaseEntry(String currencyCode, int fraction,
+ int numericCode) {
+ this(Long.MAX_VALUE, currencyCode, "", fraction, 0, numericCode, 0);
+ }
+
+ //get the index of the special case entry
+ private static int indexOf(String code, int fraction, int numeric) {
+ int size = specialCasesList.size();
+ for (int index = 0; index < size; index++) {
+ SpecialCaseEntry scEntry = specialCasesList.get(index);
+ if (scEntry.oldCurrency.equals(code)
+ && scEntry.oldCurrencyFraction == fraction
+ && scEntry.oldCurrencyNumericCode == numeric
+ && scEntry.cutOverTime == Long.MAX_VALUE) {
+ return index;
+ }
+ }
+ return -1;
+ }
+
+ // get the fraction and numericCode of the sc currencycode
+ private static int[] findEntry(String code) {
+ int[] fractionAndNumericCode = null;
+ int size = specialCasesList.size();
+ for (int index = 0; index < size; index++) {
+ SpecialCaseEntry scEntry = specialCasesList.get(index);
+ if (scEntry.oldCurrency.equals(code) && (scEntry.cutOverTime == Long.MAX_VALUE
+ || System.currentTimeMillis() < scEntry.cutOverTime)) {
+ //consider only when there is no new currency or cutover time is not passed
+ fractionAndNumericCode = new int[2];
+ fractionAndNumericCode[0] = scEntry.oldCurrencyFraction;
+ fractionAndNumericCode[1] = scEntry.oldCurrencyNumericCode;
+ break;
+ } else if (scEntry.newCurrency.equals(code)
+ && System.currentTimeMillis() >= scEntry.cutOverTime) {
+ //consider only if the cutover time is passed
+ fractionAndNumericCode = new int[2];
+ fractionAndNumericCode[0] = scEntry.newCurrencyFraction;
+ fractionAndNumericCode[1] = scEntry.newCurrencyNumericCode;
+ break;
+ }
+ }
+ return fractionAndNumericCode;
+ }
+
+ // convert the special case entry to sc arrays index
+ private static int toIndex(int tableEntry) {
+ return (tableEntry & SPECIAL_CASE_COUNTRY_INDEX_MASK) - SPECIAL_CASE_COUNTRY_INDEX_DELTA;
+ }
+
+ }
+
+ /* Used to represent Other currencies
+ * - currencyCode: currency codes that are not the main currency
+ * of a simple country
+ * - otherCurrenciesDFD: decimal format digits for other currencies
+ * - otherCurrenciesNumericCode: numeric code for other currencies
+ */
+ private static class OtherCurrencyEntry {
+
+ final private String currencyCode;
+ final private int fraction;
+ final private int numericCode;
+
+ private OtherCurrencyEntry(String currencyCode, int fraction,
+ int numericCode) {
+ this.currencyCode = currencyCode;
+ this.fraction = fraction;
+ this.numericCode = numericCode;
+ }
+
+ //get the instance of the other currency code
+ private static OtherCurrencyEntry findEntry(String code) {
+ int size = otherCurrenciesList.size();
+ for (int index = 0; index < size; index++) {
+ OtherCurrencyEntry ocEntry = otherCurrenciesList.get(index);
+ if (ocEntry.currencyCode.equalsIgnoreCase(code)) {
+ return ocEntry;
+ }
+ }
+ return null;
+ }
+
+ }
+
}
+
< prev index next >