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.logging.Logger;
42 * Represents a currency. Currencies are identified by their ISO 4217 currency
43 * codes. Visit the <a href="http://www.iso.org/iso/en/prods-services/popstds/currencycodes.html">
44 * ISO web site</a> for more information, including a table of
47 * The class is designed so that there's never more than one
48 * <code>Currency</code> instance for any given currency. Therefore, there's
49 * no public constructor. You obtain a <code>Currency</code> instance using
50 * the <code>getInstance</code> methods.
52 * Users can supersede the Java runtime currency data by creating a properties
53 * file named <code><JAVA_HOME>/lib/currency.properties</code>. The contents
54 * of the properties file are key/value pairs of the ISO 3166 country codes
55 * and the ISO 4217 currency data respectively. The value part consists of
56 * three ISO 4217 values of a currency, i.e., an alphabetic code, a numeric
57 * code, and a minor unit. Those three ISO 4217 values are separated by commas.
58 * The lines which start with '#'s are considered comment lines. For example,
61 * #Sample currency properties<br>
65 * will supersede the currency data for Japan.
69 public final class Currency implements Serializable {
71 private static final long serialVersionUID = -158308464356906721L;
74 * ISO 4217 currency code for this currency.
78 private final String currencyCode;
81 * Default fraction digits for this currency.
82 * Set from currency data tables.
84 transient private final int defaultFractionDigits;
87 * ISO 4217 numeric code for this currency.
88 * Set from currency data tables.
90 transient private final int numericCode;
93 // class data: instance map
95 private static HashMap<String, Currency> instances = new HashMap<String, Currency>(7);
96 private static HashSet<Currency> available;
99 // Class data: currency data obtained from currency.data file.
101 // - determine valid country codes
102 // - determine valid currency codes
103 // - map country codes to currency codes
104 // - obtain default fraction digits for currency codes
106 // sc = special case; dfd = default fraction digits
107 // Simple countries are those where the country code is a prefix of the
108 // currency code, and there are no known plans to change the currency.
112 // - maps country code to 32-bit int
113 // - 26*26 entries, corresponding to [A-Z]*[A-Z]
114 // - \u007F -> not valid country
115 // - bits 18-31: unused
116 // - bits 8-17: numeric code (0 to 1023)
117 // - bit 7: 1 - special case, bits 0-4 indicate which one
118 // 0 - simple country, bits 0-4 indicate final char of currency code
119 // - bits 5-6: fraction digits for simple countries, 0 for special cases
120 // - bits 0-4: final char for currency code for simple country, or ID of special case
121 // - special case IDs:
122 // - 0: country has no currency
123 // - other: index into sc* arrays + 1
124 // - scCutOverTimes: cut-over time in millis as returned by
125 // System.currentTimeMillis for special case countries that are changing
126 // currencies; Long.MAX_VALUE for countries that are not changing currencies
127 // - scOldCurrencies: old currencies for special case countries
128 // - scNewCurrencies: new currencies for special case countries that are
129 // changing currencies; null for others
130 // - scOldCurrenciesDFD: default fraction digits for old currencies
131 // - scNewCurrenciesDFD: default fraction digits for new currencies, 0 for
132 // countries that are not changing currencies
133 // - otherCurrencies: concatenation of all currency codes that are not the
134 // main currency of a simple country, separated by "-"
135 // - otherCurrenciesDFD: decimal format digits for currencies in otherCurrencies, same order
137 static int formatVersion;
138 static int dataVersion;
139 static int[] mainTable;
140 static long[] scCutOverTimes;
141 static String[] scOldCurrencies;
142 static String[] scNewCurrencies;
143 static int[] scOldCurrenciesDFD;
144 static int[] scNewCurrenciesDFD;
145 static int[] scOldCurrenciesNumericCode;
146 static int[] scNewCurrenciesNumericCode;
147 static String otherCurrencies;
148 static int[] otherCurrenciesDFD;
149 static int[] otherCurrenciesNumericCode;
151 // handy constants - must match definitions in GenerateCurrencyData
153 private static final int MAGIC_NUMBER = 0x43757244;
154 // number of characters from A to Z
155 private static final int A_TO_Z = ('Z' - 'A') + 1;
156 // entry for invalid country codes
157 private static final int INVALID_COUNTRY_ENTRY = 0x007F;
158 // entry for countries without currency
159 private static final int COUNTRY_WITHOUT_CURRENCY_ENTRY = 0x0080;
160 // mask for simple case country entries
161 private static final int SIMPLE_CASE_COUNTRY_MASK = 0x0000;
162 // mask for simple case country entry final character
163 private static final int SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK = 0x001F;
164 // mask for simple case country entry default currency digits
165 private static final int SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK = 0x0060;
166 // shift count for simple case country entry default currency digits
167 private static final int SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT = 5;
168 // mask for special case country entries
169 private static final int SPECIAL_CASE_COUNTRY_MASK = 0x0080;
170 // mask for special case country index
171 private static final int SPECIAL_CASE_COUNTRY_INDEX_MASK = 0x001F;
172 // delta from entry index component in main table to index into special case tables
173 private static final int SPECIAL_CASE_COUNTRY_INDEX_DELTA = 1;
174 // mask for distinguishing simple and special case countries
175 private static final int COUNTRY_TYPE_MASK = SIMPLE_CASE_COUNTRY_MASK | SPECIAL_CASE_COUNTRY_MASK;
176 // mask for the numeric code of the currency
177 private static final int NUMERIC_CODE_MASK = 0x0003FF00;
178 // shift count for the numeric code of the currency
179 private static final int NUMERIC_CODE_SHIFT = 8;
181 // Currency data format version
182 private static final int VALID_FORMAT_VERSION = 1;
185 AccessController.doPrivileged(new PrivilegedAction() {
186 public Object run() {
187 String homeDir = System.getProperty("java.home");
189 String dataFile = homeDir + File.separator +
190 "lib" + File.separator + "currency.data";
191 DataInputStream dis = new DataInputStream(
192 new BufferedInputStream(
193 new FileInputStream(dataFile)));
194 if (dis.readInt() != MAGIC_NUMBER) {
195 throw new InternalError("Currency data is possibly corrupted");
197 formatVersion = dis.readInt();
198 if (formatVersion != VALID_FORMAT_VERSION) {
199 throw new InternalError("Currency data format is incorrect");
201 dataVersion = dis.readInt();
202 mainTable = readIntArray(dis, A_TO_Z * A_TO_Z);
203 int scCount = dis.readInt();
204 scCutOverTimes = readLongArray(dis, scCount);
205 scOldCurrencies = readStringArray(dis, scCount);
206 scNewCurrencies = readStringArray(dis, scCount);
207 scOldCurrenciesDFD = readIntArray(dis, scCount);
208 scNewCurrenciesDFD = readIntArray(dis, scCount);
209 scOldCurrenciesNumericCode = readIntArray(dis, scCount);
210 scNewCurrenciesNumericCode = readIntArray(dis, scCount);
211 int ocCount = dis.readInt();
212 otherCurrencies = dis.readUTF();
213 otherCurrenciesDFD = readIntArray(dis, ocCount);
214 otherCurrenciesNumericCode = readIntArray(dis, ocCount);
216 } catch (IOException e) {
217 InternalError ie = new InternalError();
222 // look for the properties file for overrides
224 File propFile = new File(homeDir + File.separator +
225 "lib" + File.separator +
226 "currency.properties");
227 // if (propFile.exists()) {
228 // Properties props = new Properties();
229 // try (FileReader fr = new FileReader(propFile)) {
232 // Set<String> keys = props.stringPropertyNames();
233 // Pattern propertiesPattern =
234 // Pattern.compile("([A-Z]{3})\\s*,\\s*(\\d{3})\\s*,\\s*([0-3])");
235 // for (String key : keys) {
236 // replaceCurrencyData(propertiesPattern,
237 // key.toUpperCase(Locale.ROOT),
238 // props.getProperty(key).toUpperCase(Locale.ROOT));
241 // } catch (IOException e) {
242 // info("currency.properties is ignored because of an IOException", e);
250 * Constants for retrieving localized names from the name providers.
252 private static final int SYMBOL = 0;
253 private static final int DISPLAYNAME = 1;
257 * Constructs a <code>Currency</code> instance. The constructor is private
258 * so that we can insure that there's never more than one instance for a
261 private Currency(String currencyCode, int defaultFractionDigits, int numericCode) {
262 this.currencyCode = currencyCode;
263 this.defaultFractionDigits = defaultFractionDigits;
264 this.numericCode = numericCode;
268 * Returns the <code>Currency</code> instance for the given currency code.
270 * @param currencyCode the ISO 4217 code of the currency
271 * @return the <code>Currency</code> instance for the given currency code
272 * @exception NullPointerException if <code>currencyCode</code> is null
273 * @exception IllegalArgumentException if <code>currencyCode</code> is not
274 * a supported ISO 4217 code.
276 public static Currency getInstance(String currencyCode) {
277 return getInstance(currencyCode, Integer.MIN_VALUE, 0);
280 private static Currency getInstance(String currencyCode, int defaultFractionDigits,
282 synchronized (instances) {
283 // Try to look up the currency code in the instances table.
284 // This does the null pointer check as a side effect.
285 // Also, if there already is an entry, the currencyCode must be valid.
286 Currency instance = instances.get(currencyCode);
287 if (instance != null) {
291 if (defaultFractionDigits == Integer.MIN_VALUE) {
292 // Currency code not internally generated, need to verify first
293 // A currency code must have 3 characters and exist in the main table
294 // or in the list of other currencies.
295 if (currencyCode.length() != 3) {
296 throw new IllegalArgumentException();
298 char char1 = currencyCode.charAt(0);
299 char char2 = currencyCode.charAt(1);
300 int tableEntry = getMainTableEntry(char1, char2);
301 if ((tableEntry & COUNTRY_TYPE_MASK) == SIMPLE_CASE_COUNTRY_MASK
302 && tableEntry != INVALID_COUNTRY_ENTRY
303 && currencyCode.charAt(2) - 'A' == (tableEntry & SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK)) {
304 defaultFractionDigits = (tableEntry & SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK) >> SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT;
305 numericCode = (tableEntry & NUMERIC_CODE_MASK) >> NUMERIC_CODE_SHIFT;
307 // Check for '-' separately so we don't get false hits in the table.
308 if (currencyCode.charAt(2) == '-') {
309 throw new IllegalArgumentException();
311 int index = otherCurrencies.indexOf(currencyCode);
313 throw new IllegalArgumentException();
315 defaultFractionDigits = otherCurrenciesDFD[index / 4];
316 numericCode = otherCurrenciesNumericCode[index / 4];
320 instance = new Currency(currencyCode, defaultFractionDigits, numericCode);
321 instances.put(currencyCode, instance);
327 * Returns the <code>Currency</code> instance for the country of the
328 * given locale. The language and variant components of the locale
329 * are ignored. The result may vary over time, as countries change their
330 * currencies. For example, for the original member countries of the
331 * European Monetary Union, the method returns the old national currencies
332 * until December 31, 2001, and the Euro from January 1, 2002, local time
333 * of the respective countries.
335 * The method returns <code>null</code> for territories that don't
336 * have a currency, such as Antarctica.
338 * @param locale the locale for whose country a <code>Currency</code>
340 * @return the <code>Currency</code> instance for the country of the given
342 * @exception NullPointerException if <code>locale</code> or its country
344 * @exception IllegalArgumentException if the country of the given locale
345 * is not a supported ISO 3166 country code.
347 public static Currency getInstance(Locale locale) {
348 String country = locale.getCountry();
349 if (country == null) {
350 throw new NullPointerException();
353 if (country.length() != 2) {
354 throw new IllegalArgumentException();
357 char char1 = country.charAt(0);
358 char char2 = country.charAt(1);
359 int tableEntry = getMainTableEntry(char1, char2);
360 if ((tableEntry & COUNTRY_TYPE_MASK) == SIMPLE_CASE_COUNTRY_MASK
361 && tableEntry != INVALID_COUNTRY_ENTRY) {
362 char finalChar = (char) ((tableEntry & SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK) + 'A');
363 int defaultFractionDigits = (tableEntry & SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK) >> SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT;
364 int numericCode = (tableEntry & NUMERIC_CODE_MASK) >> NUMERIC_CODE_SHIFT;
365 StringBuffer sb = new StringBuffer(country);
366 sb.append(finalChar);
367 return getInstance(sb.toString(), defaultFractionDigits, numericCode);
370 if (tableEntry == INVALID_COUNTRY_ENTRY) {
371 throw new IllegalArgumentException();
373 if (tableEntry == COUNTRY_WITHOUT_CURRENCY_ENTRY) {
376 int index = (tableEntry & SPECIAL_CASE_COUNTRY_INDEX_MASK) - SPECIAL_CASE_COUNTRY_INDEX_DELTA;
377 if (scCutOverTimes[index] == Long.MAX_VALUE || System.currentTimeMillis() < scCutOverTimes[index]) {
378 return getInstance(scOldCurrencies[index], scOldCurrenciesDFD[index],
379 scOldCurrenciesNumericCode[index]);
381 return getInstance(scNewCurrencies[index], scNewCurrenciesDFD[index],
382 scNewCurrenciesNumericCode[index]);
389 * Gets the set of available currencies. The returned set of currencies
390 * contains all of the available currencies, which may include currencies
391 * that represent obsolete ISO 4217 codes. The set can be modified
392 * without affecting the available currencies in the runtime.
394 * @return the set of available currencies. If there is no currency
395 * available in the runtime, the returned set is empty.
398 public static Set<Currency> getAvailableCurrencies() {
399 synchronized(Currency.class) {
400 if (available == null) {
401 available = new HashSet<Currency>(256);
403 // Add simple currencies first
404 for (char c1 = 'A'; c1 <= 'Z'; c1 ++) {
405 for (char c2 = 'A'; c2 <= 'Z'; c2 ++) {
406 int tableEntry = getMainTableEntry(c1, c2);
407 if ((tableEntry & COUNTRY_TYPE_MASK) == SIMPLE_CASE_COUNTRY_MASK
408 && tableEntry != INVALID_COUNTRY_ENTRY) {
409 char finalChar = (char) ((tableEntry & SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK) + 'A');
410 int defaultFractionDigits = (tableEntry & SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK) >> SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT;
411 int numericCode = (tableEntry & NUMERIC_CODE_MASK) >> NUMERIC_CODE_SHIFT;
412 StringBuilder sb = new StringBuilder();
415 sb.append(finalChar);
416 available.add(getInstance(sb.toString(), defaultFractionDigits, numericCode));
421 // Now add other currencies
422 StringTokenizer st = new StringTokenizer(otherCurrencies, "-");
423 while (st.hasMoreElements()) {
424 available.add(getInstance((String)st.nextElement()));
429 return (Set<Currency>) available.clone();
433 * Gets the ISO 4217 currency code of this currency.
435 * @return the ISO 4217 currency code of this currency.
437 public String getCurrencyCode() {
442 * Gets the symbol of this currency for the default locale.
443 * For example, for the US Dollar, the symbol is "$" if the default
444 * locale is the US, while for other locales it may be "US$". If no
445 * symbol can be determined, the ISO 4217 currency code is returned.
447 * @return the symbol of this currency for the default locale
449 public String getSymbol() {
450 return getSymbol(Locale.getDefault(Locale.Category.DISPLAY));
454 * Gets the symbol of this currency for the specified locale.
455 * For example, for the US Dollar, the symbol is "$" if the specified
456 * locale is the US, while for other locales it may be "US$". If no
457 * symbol can be determined, the ISO 4217 currency code is returned.
459 * @param locale the locale for which a display name for this currency is
461 * @return the symbol of this currency for the specified locale
462 * @exception NullPointerException if <code>locale</code> is null
464 public String getSymbol(Locale locale) {
466 // Check whether a provider can provide an implementation that's closer
467 // to the requested locale than what the Java runtime itself can provide.
469 LocaleServiceProviderPool pool =
470 LocaleServiceProviderPool.getPool(CurrencyNameProvider.class);
472 if (pool.hasProviders()) {
473 // Assuming that all the country locales include necessary currency
474 // symbols in the Java runtime's resources, so there is no need to
475 // examine whether Java runtime's currency resource bundle is missing
476 // names. Therefore, no resource bundle is provided for calling this
478 String symbol = pool.getLocalizedObject(
479 CurrencyNameGetter.INSTANCE,
480 locale, (OpenListResourceBundle)null,
481 currencyCode, SYMBOL);
482 if (symbol != null) {
487 ResourceBundle bundle = null; //LocaleData.getCurrencyNames(locale);
488 return bundle.getString(currencyCode);
489 } catch (MissingResourceException e) {
490 // use currency code as symbol of last resort
496 * Gets the default number of fraction digits used with this currency.
497 * For example, the default number of fraction digits for the Euro is 2,
498 * while for the Japanese Yen it's 0.
499 * In the case of pseudo-currencies, such as IMF Special Drawing Rights,
502 * @return the default number of fraction digits used with this currency
504 public int getDefaultFractionDigits() {
505 return defaultFractionDigits;
509 * Returns the ISO 4217 numeric code of this currency.
511 * @return the ISO 4217 numeric code of this currency
514 public int getNumericCode() {
519 * Gets the name that is suitable for displaying this currency for
520 * the default locale. If there is no suitable display name found
521 * for the default locale, the ISO 4217 currency code is returned.
523 * @return the display name of this currency for the default locale
526 public String getDisplayName() {
527 return getDisplayName(Locale.getDefault(Locale.Category.DISPLAY));
531 * Gets the name that is suitable for displaying this currency for
532 * the specified locale. If there is no suitable display name found
533 * for the specified locale, the ISO 4217 currency code is returned.
535 * @param locale the locale for which a display name for this currency is
537 * @return the display name of this currency for the specified locale
538 * @exception NullPointerException if <code>locale</code> is null
541 public String getDisplayName(Locale locale) {
543 // OpenListResourceBundle bundle = LocaleData.getCurrencyNames(locale);
544 // String result = null;
545 // String bundleKey = currencyCode.toLowerCase(Locale.ROOT);
547 // // Check whether a provider can provide an implementation that's closer
548 // // to the requested locale than what the Java runtime itself can provide.
549 // LocaleServiceProviderPool pool =
550 // LocaleServiceProviderPool.getPool(CurrencyNameProvider.class);
551 // if (pool.hasProviders()) {
552 // result = pool.getLocalizedObject(
553 // CurrencyNameGetter.INSTANCE,
554 // locale, bundleKey, bundle, currencyCode, DISPLAYNAME);
557 // if (result == null) {
558 // result = bundle.getString(bundleKey);
561 // if (result != null) {
564 // } catch (MissingResourceException e) {
568 // use currency code as symbol of last resort
573 * Returns the ISO 4217 currency code of this currency.
575 * @return the ISO 4217 currency code of this currency
577 public String toString() {
582 * Resolves instances being deserialized to a single instance per currency.
584 private Object readResolve() {
585 return getInstance(currencyCode);
589 * Gets the main table entry for the country whose country code consists
590 * of char1 and char2.
592 private static int getMainTableEntry(char char1, char char2) {
593 if (char1 < 'A' || char1 > 'Z' || char2 < 'A' || char2 > 'Z') {
594 throw new IllegalArgumentException();
596 return mainTable[(char1 - 'A') * A_TO_Z + (char2 - 'A')];
600 * Sets the main table entry for the country whose country code consists
601 * of char1 and char2.
603 private static void setMainTableEntry(char char1, char char2, int entry) {
604 if (char1 < 'A' || char1 > 'Z' || char2 < 'A' || char2 > 'Z') {
605 throw new IllegalArgumentException();
607 mainTable[(char1 - 'A') * A_TO_Z + (char2 - 'A')] = entry;
611 * Obtains a localized currency names from a CurrencyNameProvider
613 private static class CurrencyNameGetter
614 implements LocaleServiceProviderPool.LocalizedObjectGetter<CurrencyNameProvider,
616 private static final CurrencyNameGetter INSTANCE = new CurrencyNameGetter();
618 public String getObject(CurrencyNameProvider currencyNameProvider,
622 assert params.length == 1;
623 int type = (Integer)params[0];
627 return currencyNameProvider.getSymbol(key, locale);
629 return currencyNameProvider.getDisplayName(key, locale);
631 assert false; // shouldn't happen
639 private static int[] readIntArray(DataInputStream dis, int count) throws IOException {
640 int[] ret = new int[count];
641 for (int i = 0; i < count; i++) {
642 ret[i] = dis.readInt();
648 private static long[] readLongArray(DataInputStream dis, int count) throws IOException {
649 long[] ret = new long[count];
650 for (int i = 0; i < count; i++) {
651 ret[i] = dis.readLong();
657 private static String[] readStringArray(DataInputStream dis, int count) throws IOException {
658 String[] ret = new String[count];
659 for (int i = 0; i < count; i++) {
660 ret[i] = dis.readUTF();
667 * Replaces currency data found in the currencydata.properties file
669 * @param pattern regex pattern for the properties
670 * @param ctry country code
671 * @param data currency data. This is a comma separated string that
672 * consists of "three-letter alphabet code", "three-digit numeric code",
673 * and "one-digit (0,1,2, or 3) default fraction digit".
674 * For example, "JPZ,392,0".
676 private static void replaceCurrencyData(Pattern pattern, String ctry, String curdata) {
678 if (ctry.length() != 2) {
679 // ignore invalid country code
680 String message = new StringBuilder()
681 .append("The entry in currency.properties for ")
682 .append(ctry).append(" is ignored because of the invalid country code.")
688 Matcher m = pattern.matcher(curdata);
690 // format is not recognized. ignore the data
691 String message = new StringBuilder()
692 .append("The entry in currency.properties for ")
694 .append(" is ignored because the value format is not recognized.")
700 String code = m.group(1);
701 int numeric = Integer.parseInt(m.group(2));
702 int fraction = Integer.parseInt(m.group(3));
703 int entry = numeric << NUMERIC_CODE_SHIFT;
706 for (index = 0; index < scOldCurrencies.length; index++) {
707 if (scOldCurrencies[index].equals(code)) {
712 if (index == scOldCurrencies.length) {
714 entry |= (fraction << SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT) |
715 (code.charAt(2) - 'A');
718 entry |= SPECIAL_CASE_COUNTRY_MASK |
719 (index + SPECIAL_CASE_COUNTRY_INDEX_DELTA);
721 setMainTableEntry(ctry.charAt(0), ctry.charAt(1), entry);
725 private static void info(String message, Throwable t) {
726 Logger logger = Logger.getLogger("java.util.Currency");
727 if (logger.isLoggable(Level.INFO)) {
729 logger.log(Level.INFO, message, t);
731 logger.info(message);