2 * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
28 import java.io.BufferedInputStream;
29 import java.io.DataInputStream;
31 import java.io.FileInputStream;
32 import java.io.FileReader;
33 import java.io.IOException;
34 import java.io.Serializable;
35 import java.security.AccessController;
36 import java.security.PrivilegedAction;
37 import java.util.logging.Level;
38 import java.util.regex.Pattern;
39 import java.util.regex.Matcher;
40 import java.util.spi.CurrencyNameProvider;
41 import java.util.spi.LocaleServiceProvider;
42 import sun.util.LocaleServiceProviderPool;
43 import sun.util.logging.PlatformLogger;
44 import sun.util.resources.LocaleData;
45 import sun.util.resources.OpenListResourceBundle;
49 * Represents a currency. Currencies are identified by their ISO 4217 currency
50 * codes. Visit the <a href="http://www.iso.org/iso/en/prods-services/popstds/currencycodes.html">
51 * ISO web site</a> for more information, including a table of
54 * The class is designed so that there's never more than one
55 * <code>Currency</code> instance for any given currency. Therefore, there's
56 * no public constructor. You obtain a <code>Currency</code> instance using
57 * the <code>getInstance</code> methods.
59 * Users can supersede the Java runtime currency data by creating a properties
60 * file named <code><JAVA_HOME>/lib/currency.properties</code>. The contents
61 * of the properties file are key/value pairs of the ISO 3166 country codes
62 * and the ISO 4217 currency data respectively. The value part consists of
63 * three ISO 4217 values of a currency, i.e., an alphabetic code, a numeric
64 * code, and a minor unit. Those three ISO 4217 values are separated by commas.
65 * The lines which start with '#'s are considered comment lines. For example,
68 * #Sample currency properties<br>
72 * will supersede the currency data for Japan.
76 public final class Currency implements Serializable {
78 private static final long serialVersionUID = -158308464356906721L;
81 * ISO 4217 currency code for this currency.
85 private final String currencyCode;
88 * Default fraction digits for this currency.
89 * Set from currency data tables.
91 transient private final int defaultFractionDigits;
94 * ISO 4217 numeric code for this currency.
95 * Set from currency data tables.
97 transient private final int numericCode;
100 // class data: instance map
102 private static HashMap<String, Currency> instances = new HashMap<String, Currency>(7);
103 private static HashSet<Currency> available;
106 // Class data: currency data obtained from currency.data file.
108 // - determine valid country codes
109 // - determine valid currency codes
110 // - map country codes to currency codes
111 // - obtain default fraction digits for currency codes
113 // sc = special case; dfd = default fraction digits
114 // Simple countries are those where the country code is a prefix of the
115 // currency code, and there are no known plans to change the currency.
119 // - maps country code to 32-bit int
120 // - 26*26 entries, corresponding to [A-Z]*[A-Z]
121 // - \u007F -> not valid country
122 // - bits 18-31: unused
123 // - bits 8-17: numeric code (0 to 1023)
124 // - bit 7: 1 - special case, bits 0-4 indicate which one
125 // 0 - simple country, bits 0-4 indicate final char of currency code
126 // - bits 5-6: fraction digits for simple countries, 0 for special cases
127 // - bits 0-4: final char for currency code for simple country, or ID of special case
128 // - special case IDs:
129 // - 0: country has no currency
130 // - other: index into sc* arrays + 1
131 // - scCutOverTimes: cut-over time in millis as returned by
132 // System.currentTimeMillis for special case countries that are changing
133 // currencies; Long.MAX_VALUE for countries that are not changing currencies
134 // - scOldCurrencies: old currencies for special case countries
135 // - scNewCurrencies: new currencies for special case countries that are
136 // changing currencies; null for others
137 // - scOldCurrenciesDFD: default fraction digits for old currencies
138 // - scNewCurrenciesDFD: default fraction digits for new currencies, 0 for
139 // countries that are not changing currencies
140 // - otherCurrencies: concatenation of all currency codes that are not the
141 // main currency of a simple country, separated by "-"
142 // - otherCurrenciesDFD: decimal format digits for currencies in otherCurrencies, same order
144 static int formatVersion;
145 static int dataVersion;
146 static int[] mainTable;
147 static long[] scCutOverTimes;
148 static String[] scOldCurrencies;
149 static String[] scNewCurrencies;
150 static int[] scOldCurrenciesDFD;
151 static int[] scNewCurrenciesDFD;
152 static int[] scOldCurrenciesNumericCode;
153 static int[] scNewCurrenciesNumericCode;
154 static String otherCurrencies;
155 static int[] otherCurrenciesDFD;
156 static int[] otherCurrenciesNumericCode;
158 // handy constants - must match definitions in GenerateCurrencyData
160 private static final int MAGIC_NUMBER = 0x43757244;
161 // number of characters from A to Z
162 private static final int A_TO_Z = ('Z' - 'A') + 1;
163 // entry for invalid country codes
164 private static final int INVALID_COUNTRY_ENTRY = 0x007F;
165 // entry for countries without currency
166 private static final int COUNTRY_WITHOUT_CURRENCY_ENTRY = 0x0080;
167 // mask for simple case country entries
168 private static final int SIMPLE_CASE_COUNTRY_MASK = 0x0000;
169 // mask for simple case country entry final character
170 private static final int SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK = 0x001F;
171 // mask for simple case country entry default currency digits
172 private static final int SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK = 0x0060;
173 // shift count for simple case country entry default currency digits
174 private static final int SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT = 5;
175 // mask for special case country entries
176 private static final int SPECIAL_CASE_COUNTRY_MASK = 0x0080;
177 // mask for special case country index
178 private static final int SPECIAL_CASE_COUNTRY_INDEX_MASK = 0x001F;
179 // delta from entry index component in main table to index into special case tables
180 private static final int SPECIAL_CASE_COUNTRY_INDEX_DELTA = 1;
181 // mask for distinguishing simple and special case countries
182 private static final int COUNTRY_TYPE_MASK = SIMPLE_CASE_COUNTRY_MASK | SPECIAL_CASE_COUNTRY_MASK;
183 // mask for the numeric code of the currency
184 private static final int NUMERIC_CODE_MASK = 0x0003FF00;
185 // shift count for the numeric code of the currency
186 private static final int NUMERIC_CODE_SHIFT = 8;
188 // Currency data format version
189 private static final int VALID_FORMAT_VERSION = 1;
192 AccessController.doPrivileged(new PrivilegedAction() {
193 public Object run() {
194 String homeDir = System.getProperty("java.home");
196 String dataFile = homeDir + File.separator +
197 "lib" + File.separator + "currency.data";
198 DataInputStream dis = new DataInputStream(
199 new BufferedInputStream(
200 new FileInputStream(dataFile)));
201 if (dis.readInt() != MAGIC_NUMBER) {
202 throw new InternalError("Currency data is possibly corrupted");
204 formatVersion = dis.readInt();
205 if (formatVersion != VALID_FORMAT_VERSION) {
206 throw new InternalError("Currency data format is incorrect");
208 dataVersion = dis.readInt();
209 mainTable = readIntArray(dis, A_TO_Z * A_TO_Z);
210 int scCount = dis.readInt();
211 scCutOverTimes = readLongArray(dis, scCount);
212 scOldCurrencies = readStringArray(dis, scCount);
213 scNewCurrencies = readStringArray(dis, scCount);
214 scOldCurrenciesDFD = readIntArray(dis, scCount);
215 scNewCurrenciesDFD = readIntArray(dis, scCount);
216 scOldCurrenciesNumericCode = readIntArray(dis, scCount);
217 scNewCurrenciesNumericCode = readIntArray(dis, scCount);
218 int ocCount = dis.readInt();
219 otherCurrencies = dis.readUTF();
220 otherCurrenciesDFD = readIntArray(dis, ocCount);
221 otherCurrenciesNumericCode = readIntArray(dis, ocCount);
223 } catch (IOException e) {
224 InternalError ie = new InternalError();
229 // look for the properties file for overrides
231 File propFile = new File(homeDir + File.separator +
232 "lib" + File.separator +
233 "currency.properties");
234 if (propFile.exists()) {
235 Properties props = new Properties();
236 try (FileReader fr = new FileReader(propFile)) {
239 Set<String> keys = props.stringPropertyNames();
240 Pattern propertiesPattern =
241 Pattern.compile("([A-Z]{3})\\s*,\\s*(\\d{3})\\s*,\\s*([0-3])");
242 for (String key : keys) {
243 replaceCurrencyData(propertiesPattern,
244 key.toUpperCase(Locale.ROOT),
245 props.getProperty(key).toUpperCase(Locale.ROOT));
248 } catch (IOException e) {
249 info("currency.properties is ignored because of an IOException", e);
257 * Constants for retrieving localized names from the name providers.
259 private static final int SYMBOL = 0;
260 private static final int DISPLAYNAME = 1;
264 * Constructs a <code>Currency</code> instance. The constructor is private
265 * so that we can insure that there's never more than one instance for a
268 private Currency(String currencyCode, int defaultFractionDigits, int numericCode) {
269 this.currencyCode = currencyCode;
270 this.defaultFractionDigits = defaultFractionDigits;
271 this.numericCode = numericCode;
275 * Returns the <code>Currency</code> instance for the given currency code.
277 * @param currencyCode the ISO 4217 code of the currency
278 * @return the <code>Currency</code> instance for the given currency code
279 * @exception NullPointerException if <code>currencyCode</code> is null
280 * @exception IllegalArgumentException if <code>currencyCode</code> is not
281 * a supported ISO 4217 code.
283 public static Currency getInstance(String currencyCode) {
284 return getInstance(currencyCode, Integer.MIN_VALUE, 0);
287 private static Currency getInstance(String currencyCode, int defaultFractionDigits,
289 synchronized (instances) {
290 // Try to look up the currency code in the instances table.
291 // This does the null pointer check as a side effect.
292 // Also, if there already is an entry, the currencyCode must be valid.
293 Currency instance = instances.get(currencyCode);
294 if (instance != null) {
298 if (defaultFractionDigits == Integer.MIN_VALUE) {
299 // Currency code not internally generated, need to verify first
300 // A currency code must have 3 characters and exist in the main table
301 // or in the list of other currencies.
302 if (currencyCode.length() != 3) {
303 throw new IllegalArgumentException();
305 char char1 = currencyCode.charAt(0);
306 char char2 = currencyCode.charAt(1);
307 int tableEntry = getMainTableEntry(char1, char2);
308 if ((tableEntry & COUNTRY_TYPE_MASK) == SIMPLE_CASE_COUNTRY_MASK
309 && tableEntry != INVALID_COUNTRY_ENTRY
310 && currencyCode.charAt(2) - 'A' == (tableEntry & SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK)) {
311 defaultFractionDigits = (tableEntry & SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK) >> SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT;
312 numericCode = (tableEntry & NUMERIC_CODE_MASK) >> NUMERIC_CODE_SHIFT;
314 // Check for '-' separately so we don't get false hits in the table.
315 if (currencyCode.charAt(2) == '-') {
316 throw new IllegalArgumentException();
318 int index = otherCurrencies.indexOf(currencyCode);
320 throw new IllegalArgumentException();
322 defaultFractionDigits = otherCurrenciesDFD[index / 4];
323 numericCode = otherCurrenciesNumericCode[index / 4];
327 instance = new Currency(currencyCode, defaultFractionDigits, numericCode);
328 instances.put(currencyCode, instance);
334 * Returns the <code>Currency</code> instance for the country of the
335 * given locale. The language and variant components of the locale
336 * are ignored. The result may vary over time, as countries change their
337 * currencies. For example, for the original member countries of the
338 * European Monetary Union, the method returns the old national currencies
339 * until December 31, 2001, and the Euro from January 1, 2002, local time
340 * of the respective countries.
342 * The method returns <code>null</code> for territories that don't
343 * have a currency, such as Antarctica.
345 * @param locale the locale for whose country a <code>Currency</code>
347 * @return the <code>Currency</code> instance for the country of the given
349 * @exception NullPointerException if <code>locale</code> or its country
351 * @exception IllegalArgumentException if the country of the given locale
352 * is not a supported ISO 3166 country code.
354 public static Currency getInstance(Locale locale) {
355 String country = locale.getCountry();
356 if (country == null) {
357 throw new NullPointerException();
360 if (country.length() != 2) {
361 throw new IllegalArgumentException();
364 char char1 = country.charAt(0);
365 char char2 = country.charAt(1);
366 int tableEntry = getMainTableEntry(char1, char2);
367 if ((tableEntry & COUNTRY_TYPE_MASK) == SIMPLE_CASE_COUNTRY_MASK
368 && tableEntry != INVALID_COUNTRY_ENTRY) {
369 char finalChar = (char) ((tableEntry & SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK) + 'A');
370 int defaultFractionDigits = (tableEntry & SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK) >> SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT;
371 int numericCode = (tableEntry & NUMERIC_CODE_MASK) >> NUMERIC_CODE_SHIFT;
372 StringBuffer sb = new StringBuffer(country);
373 sb.append(finalChar);
374 return getInstance(sb.toString(), defaultFractionDigits, numericCode);
377 if (tableEntry == INVALID_COUNTRY_ENTRY) {
378 throw new IllegalArgumentException();
380 if (tableEntry == COUNTRY_WITHOUT_CURRENCY_ENTRY) {
383 int index = (tableEntry & SPECIAL_CASE_COUNTRY_INDEX_MASK) - SPECIAL_CASE_COUNTRY_INDEX_DELTA;
384 if (scCutOverTimes[index] == Long.MAX_VALUE || System.currentTimeMillis() < scCutOverTimes[index]) {
385 return getInstance(scOldCurrencies[index], scOldCurrenciesDFD[index],
386 scOldCurrenciesNumericCode[index]);
388 return getInstance(scNewCurrencies[index], scNewCurrenciesDFD[index],
389 scNewCurrenciesNumericCode[index]);
396 * Gets the set of available currencies. The returned set of currencies
397 * contains all of the available currencies, which may include currencies
398 * that represent obsolete ISO 4217 codes. The set can be modified
399 * without affecting the available currencies in the runtime.
401 * @return the set of available currencies. If there is no currency
402 * available in the runtime, the returned set is empty.
405 public static Set<Currency> getAvailableCurrencies() {
406 synchronized(Currency.class) {
407 if (available == null) {
408 available = new HashSet<Currency>(256);
410 // Add simple currencies first
411 for (char c1 = 'A'; c1 <= 'Z'; c1 ++) {
412 for (char c2 = 'A'; c2 <= 'Z'; c2 ++) {
413 int tableEntry = getMainTableEntry(c1, c2);
414 if ((tableEntry & COUNTRY_TYPE_MASK) == SIMPLE_CASE_COUNTRY_MASK
415 && tableEntry != INVALID_COUNTRY_ENTRY) {
416 char finalChar = (char) ((tableEntry & SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK) + 'A');
417 int defaultFractionDigits = (tableEntry & SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK) >> SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT;
418 int numericCode = (tableEntry & NUMERIC_CODE_MASK) >> NUMERIC_CODE_SHIFT;
419 StringBuilder sb = new StringBuilder();
422 sb.append(finalChar);
423 available.add(getInstance(sb.toString(), defaultFractionDigits, numericCode));
428 // Now add other currencies
429 StringTokenizer st = new StringTokenizer(otherCurrencies, "-");
430 while (st.hasMoreElements()) {
431 available.add(getInstance((String)st.nextElement()));
436 return (Set<Currency>) available.clone();
440 * Gets the ISO 4217 currency code of this currency.
442 * @return the ISO 4217 currency code of this currency.
444 public String getCurrencyCode() {
449 * Gets the symbol of this currency for the default locale.
450 * For example, for the US Dollar, the symbol is "$" if the default
451 * locale is the US, while for other locales it may be "US$". If no
452 * symbol can be determined, the ISO 4217 currency code is returned.
454 * @return the symbol of this currency for the default locale
456 public String getSymbol() {
457 return getSymbol(Locale.getDefault(Locale.Category.DISPLAY));
461 * Gets the symbol of this currency for the specified locale.
462 * For example, for the US Dollar, the symbol is "$" if the specified
463 * locale is the US, while for other locales it may be "US$". If no
464 * symbol can be determined, the ISO 4217 currency code is returned.
466 * @param locale the locale for which a display name for this currency is
468 * @return the symbol of this currency for the specified locale
469 * @exception NullPointerException if <code>locale</code> is null
471 public String getSymbol(Locale locale) {
473 // Check whether a provider can provide an implementation that's closer
474 // to the requested locale than what the Java runtime itself can provide.
475 LocaleServiceProviderPool pool =
476 LocaleServiceProviderPool.getPool(CurrencyNameProvider.class);
478 if (pool.hasProviders()) {
479 // Assuming that all the country locales include necessary currency
480 // symbols in the Java runtime's resources, so there is no need to
481 // examine whether Java runtime's currency resource bundle is missing
482 // names. Therefore, no resource bundle is provided for calling this
484 String symbol = pool.getLocalizedObject(
485 CurrencyNameGetter.INSTANCE,
486 locale, (OpenListResourceBundle)null,
487 currencyCode, SYMBOL);
488 if (symbol != null) {
493 ResourceBundle bundle = LocaleData.getCurrencyNames(locale);
494 return bundle.getString(currencyCode);
495 } catch (MissingResourceException e) {
496 // use currency code as symbol of last resort
502 * Gets the default number of fraction digits used with this currency.
503 * For example, the default number of fraction digits for the Euro is 2,
504 * while for the Japanese Yen it's 0.
505 * In the case of pseudo-currencies, such as IMF Special Drawing Rights,
508 * @return the default number of fraction digits used with this currency
510 public int getDefaultFractionDigits() {
511 return defaultFractionDigits;
515 * Returns the ISO 4217 numeric code of this currency.
517 * @return the ISO 4217 numeric code of this currency
520 public int getNumericCode() {
525 * Gets the name that is suitable for displaying this currency for
526 * the default locale. If there is no suitable display name found
527 * for the default locale, the ISO 4217 currency code is returned.
529 * @return the display name of this currency for the default locale
532 public String getDisplayName() {
533 return getDisplayName(Locale.getDefault(Locale.Category.DISPLAY));
537 * Gets the name that is suitable for displaying this currency for
538 * the specified locale. If there is no suitable display name found
539 * for the specified locale, the ISO 4217 currency code is returned.
541 * @param locale the locale for which a display name for this currency is
543 * @return the display name of this currency for the specified locale
544 * @exception NullPointerException if <code>locale</code> is null
547 public String getDisplayName(Locale locale) {
549 OpenListResourceBundle bundle = LocaleData.getCurrencyNames(locale);
550 String result = null;
551 String bundleKey = currencyCode.toLowerCase(Locale.ROOT);
553 // Check whether a provider can provide an implementation that's closer
554 // to the requested locale than what the Java runtime itself can provide.
555 LocaleServiceProviderPool pool =
556 LocaleServiceProviderPool.getPool(CurrencyNameProvider.class);
557 if (pool.hasProviders()) {
558 result = pool.getLocalizedObject(
559 CurrencyNameGetter.INSTANCE,
560 locale, bundleKey, bundle, currencyCode, DISPLAYNAME);
563 if (result == null) {
564 result = bundle.getString(bundleKey);
567 if (result != null) {
570 } catch (MissingResourceException e) {
574 // use currency code as symbol of last resort
579 * Returns the ISO 4217 currency code of this currency.
581 * @return the ISO 4217 currency code of this currency
583 public String toString() {
588 * Resolves instances being deserialized to a single instance per currency.
590 private Object readResolve() {
591 return getInstance(currencyCode);
595 * Gets the main table entry for the country whose country code consists
596 * of char1 and char2.
598 private static int getMainTableEntry(char char1, char char2) {
599 if (char1 < 'A' || char1 > 'Z' || char2 < 'A' || char2 > 'Z') {
600 throw new IllegalArgumentException();
602 return mainTable[(char1 - 'A') * A_TO_Z + (char2 - 'A')];
606 * Sets the main table entry for the country whose country code consists
607 * of char1 and char2.
609 private static void setMainTableEntry(char char1, char char2, int entry) {
610 if (char1 < 'A' || char1 > 'Z' || char2 < 'A' || char2 > 'Z') {
611 throw new IllegalArgumentException();
613 mainTable[(char1 - 'A') * A_TO_Z + (char2 - 'A')] = entry;
617 * Obtains a localized currency names from a CurrencyNameProvider
620 private static class CurrencyNameGetter
621 implements LocaleServiceProviderPool.LocalizedObjectGetter<CurrencyNameProvider,
623 private static final CurrencyNameGetter INSTANCE = new CurrencyNameGetter();
625 public String getObject(CurrencyNameProvider currencyNameProvider,
629 assert params.length == 1;
630 int type = (Integer)params[0];
634 return currencyNameProvider.getSymbol(key, locale);
636 return currencyNameProvider.getDisplayName(key, locale);
638 assert false; // shouldn't happen
645 private static int[] readIntArray(DataInputStream dis, int count) throws IOException {
646 int[] ret = new int[count];
647 for (int i = 0; i < count; i++) {
648 ret[i] = dis.readInt();
654 private static long[] readLongArray(DataInputStream dis, int count) throws IOException {
655 long[] ret = new long[count];
656 for (int i = 0; i < count; i++) {
657 ret[i] = dis.readLong();
663 private static String[] readStringArray(DataInputStream dis, int count) throws IOException {
664 String[] ret = new String[count];
665 for (int i = 0; i < count; i++) {
666 ret[i] = dis.readUTF();
673 * Replaces currency data found in the currencydata.properties file
675 * @param pattern regex pattern for the properties
676 * @param ctry country code
677 * @param data currency data. This is a comma separated string that
678 * consists of "three-letter alphabet code", "three-digit numeric code",
679 * and "one-digit (0,1,2, or 3) default fraction digit".
680 * For example, "JPZ,392,0".
683 private static void replaceCurrencyData(Pattern pattern, String ctry, String curdata) {
685 if (ctry.length() != 2) {
686 // ignore invalid country code
687 String message = new StringBuilder()
688 .append("The entry in currency.properties for ")
689 .append(ctry).append(" is ignored because of the invalid country code.")
695 Matcher m = pattern.matcher(curdata);
697 // format is not recognized. ignore the data
698 String message = new StringBuilder()
699 .append("The entry in currency.properties for ")
701 .append(" is ignored because the value format is not recognized.")
707 String code = m.group(1);
708 int numeric = Integer.parseInt(m.group(2));
709 int fraction = Integer.parseInt(m.group(3));
710 int entry = numeric << NUMERIC_CODE_SHIFT;
713 for (index = 0; index < scOldCurrencies.length; index++) {
714 if (scOldCurrencies[index].equals(code)) {
719 if (index == scOldCurrencies.length) {
721 entry |= (fraction << SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT) |
722 (code.charAt(2) - 'A');
725 entry |= SPECIAL_CASE_COUNTRY_MASK |
726 (index + SPECIAL_CASE_COUNTRY_INDEX_DELTA);
728 setMainTableEntry(ctry.charAt(0), ctry.charAt(1), entry);
731 private static void info(String message, Throwable t) {
732 PlatformLogger logger = PlatformLogger.getLogger("java.util.Currency");
733 if (logger.isLoggable(PlatformLogger.INFO)) {
735 logger.info(message, t);
737 logger.info(message);