rt/emul/compact/src/main/java/java/util/Currency.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Fri, 04 Oct 2013 12:01:56 +0200
changeset 1340 41046f76a76a
parent 1334 588d5bf7a560
permissions -rw-r--r--
Somehow implementing the calendar classes so they compile
     1 /*
     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.
     4  *
     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.
    10  *
    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).
    16  *
    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.
    20  *
    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
    23  * questions.
    24  */
    25 
    26 package java.util;
    27 
    28 import java.io.BufferedInputStream;
    29 import java.io.DataInputStream;
    30 import java.io.File;
    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;
    39 
    40 
    41 /**
    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
    45  * currency codes.
    46  * <p>
    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.
    51  * <p>
    52  * Users can supersede the Java runtime currency data by creating a properties
    53  * file named <code>&lt;JAVA_HOME&gt;/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,
    59  * <p>
    60  * <code>
    61  * #Sample currency properties<br>
    62  * JP=JPZ,999,0
    63  * </code>
    64  * <p>
    65  * will supersede the currency data for Japan.
    66  *
    67  * @since 1.4
    68  */
    69 public final class Currency implements Serializable {
    70 
    71     private static final long serialVersionUID = -158308464356906721L;
    72 
    73     /**
    74      * ISO 4217 currency code for this currency.
    75      *
    76      * @serial
    77      */
    78     private final String currencyCode;
    79 
    80     /**
    81      * Default fraction digits for this currency.
    82      * Set from currency data tables.
    83      */
    84     transient private final int defaultFractionDigits;
    85 
    86     /**
    87      * ISO 4217 numeric code for this currency.
    88      * Set from currency data tables.
    89      */
    90     transient private final int numericCode;
    91 
    92 
    93     // class data: instance map
    94 
    95     private static HashMap<String, Currency> instances = new HashMap<String, Currency>(7);
    96     private static HashSet<Currency> available;
    97 
    98 
    99     // Class data: currency data obtained from currency.data file.
   100     // Purpose:
   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
   105     //
   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.
   109     //
   110     // table formats:
   111     // - mainTable:
   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
   136 
   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;
   150 
   151     // handy constants - must match definitions in GenerateCurrencyData
   152     // magic number
   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;
   180 
   181     // Currency data format version
   182     private static final int VALID_FORMAT_VERSION = 1;
   183 
   184     static {
   185         AccessController.doPrivileged(new PrivilegedAction() {
   186             public Object run() {
   187                 String homeDir = System.getProperty("java.home");
   188                 try {
   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");
   196                     }
   197                     formatVersion = dis.readInt();
   198                     if (formatVersion != VALID_FORMAT_VERSION) {
   199                         throw new InternalError("Currency data format is incorrect");
   200                     }
   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);
   215                     dis.close();
   216                 } catch (IOException e) {
   217                     InternalError ie = new InternalError();
   218                     ie.initCause(e);
   219                     throw ie;
   220                 }
   221 
   222                 // look for the properties file for overrides
   223 //                try {
   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)) {
   230 //                            props.load(fr);
   231 //                        }
   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));
   239 //                        }
   240 //                    }
   241 //                } catch (IOException e) {
   242 //                    info("currency.properties is ignored because of an IOException", e);
   243 //                }
   244                 return null;
   245             }
   246         });
   247     }
   248 
   249     /**
   250      * Constants for retrieving localized names from the name providers.
   251      */
   252     private static final int SYMBOL = 0;
   253     private static final int DISPLAYNAME = 1;
   254 
   255 
   256     /**
   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
   259      * given currency.
   260      */
   261     private Currency(String currencyCode, int defaultFractionDigits, int numericCode) {
   262         this.currencyCode = currencyCode;
   263         this.defaultFractionDigits = defaultFractionDigits;
   264         this.numericCode = numericCode;
   265     }
   266 
   267     /**
   268      * Returns the <code>Currency</code> instance for the given currency code.
   269      *
   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.
   275      */
   276     public static Currency getInstance(String currencyCode) {
   277         return getInstance(currencyCode, Integer.MIN_VALUE, 0);
   278     }
   279 
   280     private static Currency getInstance(String currencyCode, int defaultFractionDigits,
   281         int numericCode) {
   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) {
   288                 return instance;
   289             }
   290 
   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();
   297                 }
   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;
   306                 } else {
   307                     // Check for '-' separately so we don't get false hits in the table.
   308                     if (currencyCode.charAt(2) == '-') {
   309                         throw new IllegalArgumentException();
   310                     }
   311                     int index = otherCurrencies.indexOf(currencyCode);
   312                     if (index == -1) {
   313                         throw new IllegalArgumentException();
   314                     }
   315                     defaultFractionDigits = otherCurrenciesDFD[index / 4];
   316                     numericCode = otherCurrenciesNumericCode[index / 4];
   317                 }
   318             }
   319 
   320             instance = new Currency(currencyCode, defaultFractionDigits, numericCode);
   321             instances.put(currencyCode, instance);
   322             return instance;
   323         }
   324     }
   325 
   326     /**
   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.
   334      * <p>
   335      * The method returns <code>null</code> for territories that don't
   336      * have a currency, such as Antarctica.
   337      *
   338      * @param locale the locale for whose country a <code>Currency</code>
   339      * instance is needed
   340      * @return the <code>Currency</code> instance for the country of the given
   341      * locale, or null
   342      * @exception NullPointerException if <code>locale</code> or its country
   343      * code is null
   344      * @exception IllegalArgumentException if the country of the given locale
   345      * is not a supported ISO 3166 country code.
   346      */
   347     public static Currency getInstance(Locale locale) {
   348         String country = locale.getCountry();
   349         if (country == null) {
   350             throw new NullPointerException();
   351         }
   352 
   353         if (country.length() != 2) {
   354             throw new IllegalArgumentException();
   355         }
   356 
   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);
   368         } else {
   369             // special cases
   370             if (tableEntry == INVALID_COUNTRY_ENTRY) {
   371                 throw new IllegalArgumentException();
   372             }
   373             if (tableEntry == COUNTRY_WITHOUT_CURRENCY_ENTRY) {
   374                 return null;
   375             } else {
   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]);
   380                 } else {
   381                     return getInstance(scNewCurrencies[index], scNewCurrenciesDFD[index],
   382                         scNewCurrenciesNumericCode[index]);
   383                 }
   384             }
   385         }
   386     }
   387 
   388     /**
   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.
   393      *
   394      * @return the set of available currencies.  If there is no currency
   395      *    available in the runtime, the returned set is empty.
   396      * @since 1.7
   397      */
   398     public static Set<Currency> getAvailableCurrencies() {
   399         synchronized(Currency.class) {
   400             if (available == null) {
   401                 available = new HashSet<Currency>(256);
   402 
   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();
   413                             sb.append(c1);
   414                             sb.append(c2);
   415                             sb.append(finalChar);
   416                             available.add(getInstance(sb.toString(), defaultFractionDigits, numericCode));
   417                         }
   418                     }
   419                 }
   420 
   421                 // Now add other currencies
   422                 StringTokenizer st = new StringTokenizer(otherCurrencies, "-");
   423                 while (st.hasMoreElements()) {
   424                     available.add(getInstance((String)st.nextElement()));
   425                 }
   426             }
   427         }
   428 
   429         return (Set<Currency>) available.clone();
   430     }
   431 
   432     /**
   433      * Gets the ISO 4217 currency code of this currency.
   434      *
   435      * @return the ISO 4217 currency code of this currency.
   436      */
   437     public String getCurrencyCode() {
   438         return currencyCode;
   439     }
   440 
   441     /**
   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.
   446      *
   447      * @return the symbol of this currency for the default locale
   448      */
   449     public String getSymbol() {
   450         return getSymbol(Locale.getDefault(Locale.Category.DISPLAY));
   451     }
   452 
   453     /**
   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.
   458      *
   459      * @param locale the locale for which a display name for this currency is
   460      * needed
   461      * @return the symbol of this currency for the specified locale
   462      * @exception NullPointerException if <code>locale</code> is null
   463      */
   464     public String getSymbol(Locale locale) {
   465         try {
   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.
   468             /*
   469             LocaleServiceProviderPool pool =
   470                 LocaleServiceProviderPool.getPool(CurrencyNameProvider.class);
   471 
   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
   477                 // method.
   478                 String symbol = pool.getLocalizedObject(
   479                                     CurrencyNameGetter.INSTANCE,
   480                                     locale, (OpenListResourceBundle)null,
   481                                     currencyCode, SYMBOL);
   482                 if (symbol != null) {
   483                     return symbol;
   484                 }
   485             }
   486             */
   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
   491             return currencyCode;
   492         }
   493     }
   494 
   495     /**
   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,
   500      * -1 is returned.
   501      *
   502      * @return the default number of fraction digits used with this currency
   503      */
   504     public int getDefaultFractionDigits() {
   505         return defaultFractionDigits;
   506     }
   507 
   508     /**
   509      * Returns the ISO 4217 numeric code of this currency.
   510      *
   511      * @return the ISO 4217 numeric code of this currency
   512      * @since 1.7
   513      */
   514     public int getNumericCode() {
   515         return numericCode;
   516     }
   517 
   518     /**
   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.
   522      *
   523      * @return the display name of this currency for the default locale
   524      * @since 1.7
   525      */
   526     public String getDisplayName() {
   527         return getDisplayName(Locale.getDefault(Locale.Category.DISPLAY));
   528     }
   529 
   530     /**
   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.
   534      *
   535      * @param locale the locale for which a display name for this currency is
   536      * needed
   537      * @return the display name of this currency for the specified locale
   538      * @exception NullPointerException if <code>locale</code> is null
   539      * @since 1.7
   540      */
   541     public String getDisplayName(Locale locale) {
   542 //        try {
   543 //            OpenListResourceBundle bundle = LocaleData.getCurrencyNames(locale);
   544 //            String result = null;
   545 //            String bundleKey = currencyCode.toLowerCase(Locale.ROOT);
   546 //
   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);
   555 //            }
   556 //
   557 //            if (result == null) {
   558 //                result = bundle.getString(bundleKey);
   559 //            }
   560 //
   561 //            if (result != null) {
   562 //                return result;
   563 //            }
   564 //        } catch (MissingResourceException e) {
   565 //            // fall through
   566 //        }
   567 
   568         // use currency code as symbol of last resort
   569         return currencyCode;
   570     }
   571 
   572     /**
   573      * Returns the ISO 4217 currency code of this currency.
   574      *
   575      * @return the ISO 4217 currency code of this currency
   576      */
   577     public String toString() {
   578         return currencyCode;
   579     }
   580 
   581     /**
   582      * Resolves instances being deserialized to a single instance per currency.
   583      */
   584     private Object readResolve() {
   585         return getInstance(currencyCode);
   586     }
   587 
   588     /**
   589      * Gets the main table entry for the country whose country code consists
   590      * of char1 and char2.
   591      */
   592     private static int getMainTableEntry(char char1, char char2) {
   593         if (char1 < 'A' || char1 > 'Z' || char2 < 'A' || char2 > 'Z') {
   594             throw new IllegalArgumentException();
   595         }
   596         return mainTable[(char1 - 'A') * A_TO_Z + (char2 - 'A')];
   597     }
   598 
   599     /**
   600      * Sets the main table entry for the country whose country code consists
   601      * of char1 and char2.
   602      */
   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();
   606         }
   607         mainTable[(char1 - 'A') * A_TO_Z + (char2 - 'A')] = entry;
   608     }
   609 
   610     /**
   611      * Obtains a localized currency names from a CurrencyNameProvider
   612      * implementation.
   613     private static class CurrencyNameGetter
   614         implements LocaleServiceProviderPool.LocalizedObjectGetter<CurrencyNameProvider,
   615                                                                    String> {
   616         private static final CurrencyNameGetter INSTANCE = new CurrencyNameGetter();
   617 
   618         public String getObject(CurrencyNameProvider currencyNameProvider,
   619                                 Locale locale,
   620                                 String key,
   621                                 Object... params) {
   622             assert params.length == 1;
   623             int type = (Integer)params[0];
   624 
   625             switch(type) {
   626             case SYMBOL:
   627                 return currencyNameProvider.getSymbol(key, locale);
   628             case DISPLAYNAME:
   629                 return currencyNameProvider.getDisplayName(key, locale);
   630             default:
   631                 assert false; // shouldn't happen
   632             }
   633 
   634             return null;
   635         }
   636     }
   637      */
   638 
   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();
   643         }
   644 
   645         return ret;
   646     }
   647 
   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();
   652         }
   653 
   654         return ret;
   655     }
   656 
   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();
   661         }
   662 
   663         return ret;
   664     }
   665 
   666     /**
   667      * Replaces currency data found in the currencydata.properties file
   668      *
   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".
   675      * @throws
   676     private static void replaceCurrencyData(Pattern pattern, String ctry, String curdata) {
   677 
   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.")
   683                 .toString();
   684             info(message, null);
   685             return;
   686         }
   687 
   688         Matcher m = pattern.matcher(curdata);
   689         if (!m.find()) {
   690             // format is not recognized.  ignore the data
   691             String message = new StringBuilder()
   692                 .append("The entry in currency.properties for ")
   693                 .append(ctry)
   694                 .append(" is ignored because the value format is not recognized.")
   695                 .toString();
   696             info(message, null);
   697             return;
   698         }
   699 
   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;
   704 
   705         int index;
   706         for (index = 0; index < scOldCurrencies.length; index++) {
   707             if (scOldCurrencies[index].equals(code)) {
   708                 break;
   709             }
   710         }
   711 
   712         if (index == scOldCurrencies.length) {
   713             // simple case
   714             entry |= (fraction << SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT) |
   715                      (code.charAt(2) - 'A');
   716         } else {
   717             // special case
   718             entry |= SPECIAL_CASE_COUNTRY_MASK |
   719                      (index + SPECIAL_CASE_COUNTRY_INDEX_DELTA);
   720         }
   721         setMainTableEntry(ctry.charAt(0), ctry.charAt(1), entry);
   722     }
   723      */
   724 
   725     private static void info(String message, Throwable t) {
   726         Logger logger = Logger.getLogger("java.util.Currency");
   727         if (logger.isLoggable(Level.INFO)) {
   728             if (t != null) {
   729                 logger.log(Level.INFO, message, t);
   730             } else {
   731                 logger.info(message);
   732             }
   733         }
   734     }
   735 }