Save This Page
Home » openjdk-7 » java » util » [javadoc | source]
    1   /*
    2    * Copyright 2000-2005 Sun Microsystems, Inc.  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.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any 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   import java.util.regex.Pattern;
   40   import java.util.regex.Matcher;
   41   import java.util.spi.CurrencyNameProvider;
   42   import java.util.spi.LocaleServiceProvider;
   43   import sun.util.LocaleServiceProviderPool;
   44   import sun.util.resources.LocaleData;
   45   import sun.util.resources.OpenListResourceBundle;
   46   
   47   
   48   /**
   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
   52    * currency codes.
   53    * <p>
   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.
   58    * <p>
   59    * Users can supersede the Java runtime currency data by creating a properties
   60    * file named <code>&lt;JAVA_HOME&gt;/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,
   66    * <p>
   67    * <code>
   68    * #Sample currency properties<br>
   69    * JP=JPZ,999,0
   70    * </code>
   71    * <p>
   72    * will supersede the currency data for Japan.
   73    *
   74    * @since 1.4
   75    */
   76   public final class Currency implements Serializable {
   77   
   78       private static final long serialVersionUID = -158308464356906721L;
   79   
   80       /**
   81        * ISO 4217 currency code for this currency.
   82        *
   83        * @serial
   84        */
   85       private final String currencyCode;
   86   
   87       /**
   88        * Default fraction digits for this currency.
   89        * Set from currency data tables.
   90        */
   91       transient private final int defaultFractionDigits;
   92   
   93       /**
   94        * ISO 4217 numeric code for this currency.
   95        * Set from currency data tables.
   96        */
   97       transient private final int numericCode;
   98   
   99   
  100       // class data: instance map
  101   
  102       private static HashMap<String, Currency> instances = new HashMap<String, Currency>(7);
  103       private static HashSet<Currency> available;
  104   
  105   
  106       // Class data: currency data obtained from currency.data file.
  107       // Purpose:
  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
  112       //
  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.
  116       //
  117       // table formats:
  118       // - mainTable:
  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
  143   
  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;
  157   
  158       // handy constants - must match definitions in GenerateCurrencyData
  159       // magic number
  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;
  187   
  188       // Currency data format version
  189       private static final int VALID_FORMAT_VERSION = 1;
  190   
  191       static {
  192           AccessController.doPrivileged(new PrivilegedAction() {
  193               public Object run() {
  194                   String homeDir = System.getProperty("java.home");
  195                   try {
  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");
  203                       }
  204                       formatVersion = dis.readInt();
  205                       if (formatVersion != VALID_FORMAT_VERSION) {
  206                           throw new InternalError("Currency data format is incorrect");
  207                       }
  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);
  222                       dis.close();
  223                   } catch (IOException e) {
  224                       InternalError ie = new InternalError();
  225                       ie.initCause(e);
  226                       throw ie;
  227                   }
  228   
  229                   // look for the properties file for overrides
  230                   try {
  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                           props.load(new FileReader(propFile));
  237                           Set<String> keys = props.stringPropertyNames();
  238                           Pattern propertiesPattern =
  239                               Pattern.compile("([A-Z]{3})\\s*,\\s*(\\d{3})\\s*,\\s*([0-3])");
  240                           for (String key : keys) {
  241                              replaceCurrencyData(propertiesPattern,
  242                                  key.toUpperCase(Locale.ROOT),
  243                                  props.getProperty(key).toUpperCase(Locale.ROOT));
  244                           }
  245                       }
  246                   } catch (IOException e) {
  247                       log(Level.INFO, "currency.properties is ignored because of an IOException", e);
  248                   }
  249                   return null;
  250               }
  251           });
  252       }
  253   
  254       /**
  255        * Constants for retrieving localized names from the name providers.
  256        */
  257       private static final int SYMBOL = 0;
  258       private static final int DISPLAYNAME = 1;
  259   
  260   
  261       /**
  262        * Constructs a <code>Currency</code> instance. The constructor is private
  263        * so that we can insure that there's never more than one instance for a
  264        * given currency.
  265        */
  266       private Currency(String currencyCode, int defaultFractionDigits, int numericCode) {
  267           this.currencyCode = currencyCode;
  268           this.defaultFractionDigits = defaultFractionDigits;
  269           this.numericCode = numericCode;
  270       }
  271   
  272       /**
  273        * Returns the <code>Currency</code> instance for the given currency code.
  274        *
  275        * @param currencyCode the ISO 4217 code of the currency
  276        * @return the <code>Currency</code> instance for the given currency code
  277        * @exception NullPointerException if <code>currencyCode</code> is null
  278        * @exception IllegalArgumentException if <code>currencyCode</code> is not
  279        * a supported ISO 4217 code.
  280        */
  281       public static Currency getInstance(String currencyCode) {
  282           return getInstance(currencyCode, Integer.MIN_VALUE, 0);
  283       }
  284   
  285       private static Currency getInstance(String currencyCode, int defaultFractionDigits,
  286           int numericCode) {
  287           synchronized (instances) {
  288               // Try to look up the currency code in the instances table.
  289               // This does the null pointer check as a side effect.
  290               // Also, if there already is an entry, the currencyCode must be valid.
  291               Currency instance = instances.get(currencyCode);
  292               if (instance != null) {
  293                   return instance;
  294               }
  295   
  296               if (defaultFractionDigits == Integer.MIN_VALUE) {
  297                   // Currency code not internally generated, need to verify first
  298                   // A currency code must have 3 characters and exist in the main table
  299                   // or in the list of other currencies.
  300                   if (currencyCode.length() != 3) {
  301                       throw new IllegalArgumentException();
  302                   }
  303                   char char1 = currencyCode.charAt(0);
  304                   char char2 = currencyCode.charAt(1);
  305                   int tableEntry = getMainTableEntry(char1, char2);
  306                   if ((tableEntry & COUNTRY_TYPE_MASK) == SIMPLE_CASE_COUNTRY_MASK
  307                           && tableEntry != INVALID_COUNTRY_ENTRY
  308                           && currencyCode.charAt(2) - 'A' == (tableEntry & SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK)) {
  309                       defaultFractionDigits = (tableEntry & SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK) >> SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT;
  310                       numericCode = (tableEntry & NUMERIC_CODE_MASK) >> NUMERIC_CODE_SHIFT;
  311                   } else {
  312                       // Check for '-' separately so we don't get false hits in the table.
  313                       if (currencyCode.charAt(2) == '-') {
  314                           throw new IllegalArgumentException();
  315                       }
  316                       int index = otherCurrencies.indexOf(currencyCode);
  317                       if (index == -1) {
  318                           throw new IllegalArgumentException();
  319                       }
  320                       defaultFractionDigits = otherCurrenciesDFD[index / 4];
  321                       numericCode = otherCurrenciesNumericCode[index / 4];
  322                   }
  323               }
  324   
  325               instance = new Currency(currencyCode, defaultFractionDigits, numericCode);
  326               instances.put(currencyCode, instance);
  327               return instance;
  328           }
  329       }
  330   
  331       /**
  332        * Returns the <code>Currency</code> instance for the country of the
  333        * given locale. The language and variant components of the locale
  334        * are ignored. The result may vary over time, as countries change their
  335        * currencies. For example, for the original member countries of the
  336        * European Monetary Union, the method returns the old national currencies
  337        * until December 31, 2001, and the Euro from January 1, 2002, local time
  338        * of the respective countries.
  339        * <p>
  340        * The method returns <code>null</code> for territories that don't
  341        * have a currency, such as Antarctica.
  342        *
  343        * @param locale the locale for whose country a <code>Currency</code>
  344        * instance is needed
  345        * @return the <code>Currency</code> instance for the country of the given
  346        * locale, or null
  347        * @exception NullPointerException if <code>locale</code> or its country
  348        * code is null
  349        * @exception IllegalArgumentException if the country of the given locale
  350        * is not a supported ISO 3166 country code.
  351        */
  352       public static Currency getInstance(Locale locale) {
  353           String country = locale.getCountry();
  354           if (country == null) {
  355               throw new NullPointerException();
  356           }
  357   
  358           if (country.length() != 2) {
  359               throw new IllegalArgumentException();
  360           }
  361   
  362           char char1 = country.charAt(0);
  363           char char2 = country.charAt(1);
  364           int tableEntry = getMainTableEntry(char1, char2);
  365           if ((tableEntry & COUNTRY_TYPE_MASK) == SIMPLE_CASE_COUNTRY_MASK
  366                       && tableEntry != INVALID_COUNTRY_ENTRY) {
  367               char finalChar = (char) ((tableEntry & SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK) + 'A');
  368               int defaultFractionDigits = (tableEntry & SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK) >> SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT;
  369               int numericCode = (tableEntry & NUMERIC_CODE_MASK) >> NUMERIC_CODE_SHIFT;
  370               StringBuffer sb = new StringBuffer(country);
  371               sb.append(finalChar);
  372               return getInstance(sb.toString(), defaultFractionDigits, numericCode);
  373           } else {
  374               // special cases
  375               if (tableEntry == INVALID_COUNTRY_ENTRY) {
  376                   throw new IllegalArgumentException();
  377               }
  378               if (tableEntry == COUNTRY_WITHOUT_CURRENCY_ENTRY) {
  379                   return null;
  380               } else {
  381                   int index = (tableEntry & SPECIAL_CASE_COUNTRY_INDEX_MASK) - SPECIAL_CASE_COUNTRY_INDEX_DELTA;
  382                   if (scCutOverTimes[index] == Long.MAX_VALUE || System.currentTimeMillis() < scCutOverTimes[index]) {
  383                       return getInstance(scOldCurrencies[index], scOldCurrenciesDFD[index],
  384                           scOldCurrenciesNumericCode[index]);
  385                   } else {
  386                       return getInstance(scNewCurrencies[index], scNewCurrenciesDFD[index],
  387                           scNewCurrenciesNumericCode[index]);
  388                   }
  389               }
  390           }
  391       }
  392   
  393       /**
  394        * Gets the set of available currencies.  The returned set of currencies
  395        * contains all of the available currencies, which may include currencies
  396        * that represent obsolete ISO 4217 codes.  The set can be modified
  397        * without affecting the available currencies in the runtime.
  398        *
  399        * @return the set of available currencies.  If there is no currency
  400        *    available in the runtime, the returned set is empty.
  401        * @since 1.7
  402        */
  403       public static Set<Currency> getAvailableCurrencies() {
  404           synchronized(Currency.class) {
  405               if (available == null) {
  406                   available = new HashSet<Currency>(256);
  407   
  408                   // Add simple currencies first
  409                   for (char c1 = 'A'; c1 <= 'Z'; c1 ++) {
  410                       for (char c2 = 'A'; c2 <= 'Z'; c2 ++) {
  411                           int tableEntry = getMainTableEntry(c1, c2);
  412                           if ((tableEntry & COUNTRY_TYPE_MASK) == SIMPLE_CASE_COUNTRY_MASK
  413                                && tableEntry != INVALID_COUNTRY_ENTRY) {
  414                               char finalChar = (char) ((tableEntry & SIMPLE_CASE_COUNTRY_FINAL_CHAR_MASK) + 'A');
  415                               int defaultFractionDigits = (tableEntry & SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_MASK) >> SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT;
  416                               int numericCode = (tableEntry & NUMERIC_CODE_MASK) >> NUMERIC_CODE_SHIFT;
  417                               StringBuilder sb = new StringBuilder();
  418                               sb.append(c1);
  419                               sb.append(c2);
  420                               sb.append(finalChar);
  421                               available.add(getInstance(sb.toString(), defaultFractionDigits, numericCode));
  422                           }
  423                       }
  424                   }
  425   
  426                   // Now add other currencies
  427                   StringTokenizer st = new StringTokenizer(otherCurrencies, "-");
  428                   while (st.hasMoreElements()) {
  429                       available.add(getInstance((String)st.nextElement()));
  430                   }
  431               }
  432           }
  433   
  434           return (Set<Currency>) available.clone();
  435       }
  436   
  437       /**
  438        * Gets the ISO 4217 currency code of this currency.
  439        *
  440        * @return the ISO 4217 currency code of this currency.
  441        */
  442       public String getCurrencyCode() {
  443           return currencyCode;
  444       }
  445   
  446       /**
  447        * Gets the symbol of this currency for the default locale.
  448        * For example, for the US Dollar, the symbol is "$" if the default
  449        * locale is the US, while for other locales it may be "US$". If no
  450        * symbol can be determined, the ISO 4217 currency code is returned.
  451        *
  452        * @return the symbol of this currency for the default locale
  453        */
  454       public String getSymbol() {
  455           return getSymbol(Locale.getDefault());
  456       }
  457   
  458       /**
  459        * Gets the symbol of this currency for the specified locale.
  460        * For example, for the US Dollar, the symbol is "$" if the specified
  461        * locale is the US, while for other locales it may be "US$". If no
  462        * symbol can be determined, the ISO 4217 currency code is returned.
  463        *
  464        * @param locale the locale for which a display name for this currency is
  465        * needed
  466        * @return the symbol of this currency for the specified locale
  467        * @exception NullPointerException if <code>locale</code> is null
  468        */
  469       public String getSymbol(Locale locale) {
  470           try {
  471               // Check whether a provider can provide an implementation that's closer
  472               // to the requested locale than what the Java runtime itself can provide.
  473               LocaleServiceProviderPool pool =
  474                   LocaleServiceProviderPool.getPool(CurrencyNameProvider.class);
  475   
  476               if (pool.hasProviders()) {
  477                   // Assuming that all the country locales include necessary currency
  478                   // symbols in the Java runtime's resources,  so there is no need to
  479                   // examine whether Java runtime's currency resource bundle is missing
  480                   // names.  Therefore, no resource bundle is provided for calling this
  481                   // method.
  482                   String symbol = pool.getLocalizedObject(
  483                                       CurrencyNameGetter.INSTANCE,
  484                                       locale, (OpenListResourceBundle)null,
  485                                       currencyCode, SYMBOL);
  486                   if (symbol != null) {
  487                       return symbol;
  488                   }
  489               }
  490   
  491               ResourceBundle bundle = LocaleData.getCurrencyNames(locale);
  492               return bundle.getString(currencyCode);
  493           } catch (MissingResourceException e) {
  494               // use currency code as symbol of last resort
  495               return currencyCode;
  496           }
  497       }
  498   
  499       /**
  500        * Gets the default number of fraction digits used with this currency.
  501        * For example, the default number of fraction digits for the Euro is 2,
  502        * while for the Japanese Yen it's 0.
  503        * In the case of pseudo-currencies, such as IMF Special Drawing Rights,
  504        * -1 is returned.
  505        *
  506        * @return the default number of fraction digits used with this currency
  507        */
  508       public int getDefaultFractionDigits() {
  509           return defaultFractionDigits;
  510       }
  511   
  512       /**
  513        * Returns the ISO 4217 numeric code of this currency.
  514        *
  515        * @return the ISO 4217 numeric code of this currency
  516        * @since 1.7
  517        */
  518       public int getNumericCode() {
  519           return numericCode;
  520       }
  521   
  522       /**
  523        * Gets the name that is suitable for displaying this currency for
  524        * the default locale.  If there is no suitable display name found
  525        * for the default locale, the ISO 4217 currency code is returned.
  526        *
  527        * @return the display name of this currency for the default locale
  528        * @since 1.7
  529        */
  530       public String getDisplayName() {
  531           return getDisplayName(Locale.getDefault());
  532       }
  533   
  534       /**
  535        * Gets the name that is suitable for displaying this currency for
  536        * the specified locale.  If there is no suitable display name found
  537        * for the specified locale, the ISO 4217 currency code is returned.
  538        *
  539        * @param locale the locale for which a display name for this currency is
  540        * needed
  541        * @return the display name of this currency for the specified locale
  542        * @exception NullPointerException if <code>locale</code> is null
  543        * @since 1.7
  544        */
  545       public String getDisplayName(Locale locale) {
  546           try {
  547               OpenListResourceBundle bundle = LocaleData.getCurrencyNames(locale);
  548               String result = null;
  549               String bundleKey = currencyCode.toLowerCase(Locale.ROOT);
  550   
  551               // Check whether a provider can provide an implementation that's closer
  552               // to the requested locale than what the Java runtime itself can provide.
  553               LocaleServiceProviderPool pool =
  554                   LocaleServiceProviderPool.getPool(CurrencyNameProvider.class);
  555               if (pool.hasProviders()) {
  556                   result = pool.getLocalizedObject(
  557                                       CurrencyNameGetter.INSTANCE,
  558                                       locale, bundleKey, bundle, currencyCode, DISPLAYNAME);
  559               }
  560   
  561               if (result == null) {
  562                   result = bundle.getString(bundleKey);
  563               }
  564   
  565               if (result != null) {
  566                   return result;
  567               }
  568           } catch (MissingResourceException e) {
  569               // fall through
  570           }
  571   
  572           // use currency code as symbol of last resort
  573           return currencyCode;
  574       }
  575   
  576       /**
  577        * Returns the ISO 4217 currency code of this currency.
  578        *
  579        * @return the ISO 4217 currency code of this currency
  580        */
  581       public String toString() {
  582           return currencyCode;
  583       }
  584   
  585       /**
  586        * Resolves instances being deserialized to a single instance per currency.
  587        */
  588       private Object readResolve() {
  589           return getInstance(currencyCode);
  590       }
  591   
  592       /**
  593        * Gets the main table entry for the country whose country code consists
  594        * of char1 and char2.
  595        */
  596       private static int getMainTableEntry(char char1, char char2) {
  597           if (char1 < 'A' || char1 > 'Z' || char2 < 'A' || char2 > 'Z') {
  598               throw new IllegalArgumentException();
  599           }
  600           return mainTable[(char1 - 'A') * A_TO_Z + (char2 - 'A')];
  601       }
  602   
  603       /**
  604        * Sets the main table entry for the country whose country code consists
  605        * of char1 and char2.
  606        */
  607       private static void setMainTableEntry(char char1, char char2, int entry) {
  608           if (char1 < 'A' || char1 > 'Z' || char2 < 'A' || char2 > 'Z') {
  609               throw new IllegalArgumentException();
  610           }
  611           mainTable[(char1 - 'A') * A_TO_Z + (char2 - 'A')] = entry;
  612       }
  613   
  614       /**
  615        * Obtains a localized currency names from a CurrencyNameProvider
  616        * implementation.
  617        */
  618       private static class CurrencyNameGetter
  619           implements LocaleServiceProviderPool.LocalizedObjectGetter<CurrencyNameProvider,
  620                                                                      String> {
  621           private static final CurrencyNameGetter INSTANCE = new CurrencyNameGetter();
  622   
  623           public String getObject(CurrencyNameProvider currencyNameProvider,
  624                                   Locale locale,
  625                                   String key,
  626                                   Object... params) {
  627               assert params.length == 1;
  628               int type = (Integer)params[0];
  629   
  630               switch(type) {
  631               case SYMBOL:
  632                   return currencyNameProvider.getSymbol(key, locale);
  633               case DISPLAYNAME:
  634                   return currencyNameProvider.getDisplayName(key, locale);
  635               default:
  636                   assert false; // shouldn't happen
  637               }
  638   
  639               return null;
  640           }
  641       }
  642   
  643       private static int[] readIntArray(DataInputStream dis, int count) throws IOException {
  644           int[] ret = new int[count];
  645           for (int i = 0; i < count; i++) {
  646               ret[i] = dis.readInt();
  647           }
  648   
  649           return ret;
  650       }
  651   
  652       private static long[] readLongArray(DataInputStream dis, int count) throws IOException {
  653           long[] ret = new long[count];
  654           for (int i = 0; i < count; i++) {
  655               ret[i] = dis.readLong();
  656           }
  657   
  658           return ret;
  659       }
  660   
  661       private static String[] readStringArray(DataInputStream dis, int count) throws IOException {
  662           String[] ret = new String[count];
  663           for (int i = 0; i < count; i++) {
  664               ret[i] = dis.readUTF();
  665           }
  666   
  667           return ret;
  668       }
  669   
  670       /**
  671        * Replaces currency data found in the currencydata.properties file
  672        *
  673        * @param pattern regex pattern for the properties
  674        * @param ctry country code
  675        * @param data currency data.  This is a comma separated string that
  676        *    consists of "three-letter alphabet code", "three-digit numeric code",
  677        *    and "one-digit (0,1,2, or 3) default fraction digit".
  678        *    For example, "JPZ,392,0".
  679        * @throws
  680        */
  681       private static void replaceCurrencyData(Pattern pattern, String ctry, String curdata) {
  682   
  683           if (ctry.length() != 2) {
  684               // ignore invalid country code
  685               String message = new StringBuilder()
  686                   .append("The entry in currency.properties for ")
  687                   .append(ctry).append(" is ignored because of the invalid country code.")
  688                   .toString();
  689               log(Level.INFO, message, null);
  690               return;
  691           }
  692   
  693           Matcher m = pattern.matcher(curdata);
  694           if (!m.find()) {
  695               // format is not recognized.  ignore the data
  696               String message = new StringBuilder()
  697                   .append("The entry in currency.properties for ")
  698                   .append(ctry)
  699                   .append(" is ignored because the value format is not recognized.")
  700                   .toString();
  701               log(Level.INFO, message, null);
  702               return;
  703           }
  704   
  705           String code = m.group(1);
  706           int numeric = Integer.parseInt(m.group(2));
  707           int fraction = Integer.parseInt(m.group(3));
  708           int entry = numeric << NUMERIC_CODE_SHIFT;
  709   
  710           int index;
  711           for (index = 0; index < scOldCurrencies.length; index++) {
  712               if (scOldCurrencies[index].equals(code)) {
  713                   break;
  714               }
  715           }
  716   
  717           if (index == scOldCurrencies.length) {
  718               // simple case
  719               entry |= (fraction << SIMPLE_CASE_COUNTRY_DEFAULT_DIGITS_SHIFT) |
  720                        (code.charAt(2) - 'A');
  721           } else {
  722               // special case
  723               entry |= SPECIAL_CASE_COUNTRY_MASK |
  724                        (index + SPECIAL_CASE_COUNTRY_INDEX_DELTA);
  725           }
  726           setMainTableEntry(ctry.charAt(0), ctry.charAt(1), entry);
  727       }
  728   
  729       private static void log(Level level, String message, Throwable t) {
  730           Logger logger = Logger.getLogger("java.util.Currency");
  731           if (logger.isLoggable(level)) {
  732               if (t != null) {
  733                   logger.log(level, message, t);
  734               } else {
  735                   logger.log(level, message);
  736               }
  737           }
  738       }
  739   }

Save This Page
Home » openjdk-7 » java » util » [javadoc | source]