getInstance() methods rather than by using
 * a constructor.
 *
 * @see java.util.Locale
 * @author Guilhem Lavaux  Currency instances to
   * ensure the singleton nature of this class.  The key
   * is the locale of the currency.
   *
   * @see #getInstance(java.util.Locale)
   * @see #readResolve()
   * @serial ignored.
   */
  private transient static Map cache;
  /**
   * Instantiates the cache.
   */
  static
  {
    cache = new HashMap();
  }
  /**
   * Default constructor for deserialization
   */
  private Currency ()
  {
  }
  /**
   * Constructor to create a Currency object
   * for a particular Locale.
   * All components of the given locale, other than the
   * country code, are ignored.  The results of calling this
   * method may vary over time, as the currency associated with
   * a particular country changes.  For countries without
   * a given currency (e.g. Antarctica), the result is null. 
   *
   * @param loc the locale for the new currency.
   */
  private Currency (Locale loc)
  {
    this.locale = loc;
    this.res = ResourceBundle.getBundle ("gnu.java.locale.LocaleInformation", 
      locale, ClassLoader.getSystemClassLoader());
    /* Retrieve the ISO4217 currency code */
    try
      {
	currencyCode = res.getString ("intlCurrencySymbol");
      }
    catch (Exception _)
      {
	currencyCode = null;
      }
  }
  /**
   * Returns the ISO4217 currency code of this currency.
   *
   * @return a String containing currency code.
   */
  public String getCurrencyCode ()
  {
    return currencyCode;
  }
  /**
   * Returns the number of digits which occur after the decimal point
   * for this particular currency.  For example, currencies such
   * as the U.S. dollar, the Euro and the Great British pound have two
   * digits following the decimal point to indicate the value which exists
   * in the associated lower-valued coinage (cents in the case of the first
   * two, pennies in the latter).  Some currencies such as the Japanese
   * Yen have no digits after the decimal point.  In the case of pseudo
   * currencies, such as IMF Special Drawing Rights, -1 is returned.
   *
   * @return the number of digits after the decimal separator for this currency.
   */   
  public int getDefaultFractionDigits ()
  {
    NumberFormat currency = NumberFormat.getCurrencyInstance (locale);
    
    return currency.getMaximumFractionDigits();
  }
    
  /**
   * Builds a new currency instance for this locale.
   * All components of the given locale, other than the
   * country code, are ignored.  The results of calling this
   * method may vary over time, as the currency associated with
   * a particular country changes.  For countries without
   * a given currency (e.g. Antarctica), the result is null. 
   *
   * @param locale a Locale instance.
   * @return a new Currency instance.
   * @throws NullPointerException if the locale or its
   *         country code is null.
   * @throws IllegalArgumentException if the country of
   *         the given locale is not a supported ISO3166 code.
   */ 
  public static Currency getInstance (Locale locale)
  {
    /**
     * The new instance must be the only available instance
     * for the currency it supports.  We ensure this happens,
     * while maintaining a suitable performance level, by
     * creating the appropriate object on the first call to
     * this method, and returning the cached instance on
     * later calls.
     */
    Currency newCurrency;
    /* Attempt to get the currency from the cache */
    newCurrency = (Currency) cache.get(locale);
    if (newCurrency == null)
      {
        /* Create the currency for this locale */
        newCurrency = new Currency (locale);
        /* Cache it */
        cache.put(locale, newCurrency);
      }
    /* Return the instance */
    return newCurrency;
  }
  /**
   * Builds the currency corresponding to the specified currency code.
   *
   * @param currencyCode a string representing a currency code.
   * @return a new Currency instance.
   * @throws NullPointerException if currencyCode is null.
   * @throws IllegalArgumentException if the supplied currency code
   *         is not a supported ISO 4217 code.
   */
  public static Currency getInstance (String currencyCode)
  {
    Locale[] allLocales = Locale.getAvailableLocales ();
    
    for (int i = 0;i < allLocales.length; i++)
      {
	Currency testCurrency = getInstance (allLocales[i]);
	
	if (testCurrency.getCurrencyCode() != null &&
	    testCurrency.getCurrencyCode().equals(currencyCode))
	  return testCurrency;
      }
    /* 
     * If we get this far, the code is not supported by any of
     * our locales.
     */
    throw new IllegalArgumentException("The currency code, " + currencyCode +
                                       ", is not supported.");
  }
  /**
   * This method returns the symbol which precedes or follows a
   * value in this particular currency.  In cases where there is no
   * such symbol for the currency, the ISO 4217 currency
   * code is returned.
   *
   * @return the currency symbol, or the ISO 4217 currency code if
   *         one doesn't exist.
   */
  public String getSymbol()
  {
    try
      {
        /* What does this return if there is no mapping? */
	return res.getString ("currencySymbol");
      }
    catch (Exception _)
      {
	return null;
      }
  }
  /**
   * * This method returns the symbol which precedes or follows a * value in this particular currency. The returned value is * the symbol used to denote the currency in the specified locale. *
*
   * For example, a supplied locale may specify a different symbol
   * for the currency, due to conflicts with its own currency.
   * This would be the case with the American currency, the dollar.
   * Locales that also use a dollar-based currency (e.g. Canada, Australia)
   * need to differentiate the American dollar using 'US$' rather than '$'.
   * So, supplying one of these locales to getSymbol() would
   * return this value, rather than the standard '$'.
   * 
* In cases where there is no such symbol for a particular currency, * the ISO 4217 currency code is returned. *
* * @param locale the locale to express the symbol in. * @return the currency symbol, or the ISO 4217 currency code if * one doesn't exist. * @throws NullPointerException if the locale is null. */ public String getSymbol(Locale locale) { // TODO. The behaviour is unclear if locale != this.locale. // First we need to implement fully LocaleInformation*.java /* * FIXME: My reading of how this method works has this implementation * as wrong. It should return a value relating to how the specified * locale handles the symbol for this currency. This implementation * seems to just do a variation of getInstance(locale). */ try { ResourceBundle localeResource = ResourceBundle.getBundle ("gnu.java.locale.LocaleInformation", locale, Currency.class.getClassLoader()); if (localeResource.equals(res)) return localeResource.getString ("currencySymbol"); else return localeResource.getString ("intlCurrencySymbol"); } catch (Exception e1) { try { return res.getString ("intlCurrencySymbol"); } catch (Exception e2) { return null; } } } /** * Returns the international ISO4217 currency code of this currency. * * @return aString containing the ISO4217 currency code.
   */
  public String toString()
  {
    return getCurrencyCode();
  }
  /**
   * Resolves the deserialized object to the singleton instance for its
   * particular currency.  The currency code of the deserialized instance
   * is used to return the correct instance.
   *
   * @return the singleton instance for the currency specified by the
   *         currency code of the deserialized object.  This replaces
   *         the deserialized object as the returned object from
   *         deserialization.
   * @throws ObjectStreamException if a problem occurs with deserializing
   *         the object.
   */
  private Object readResolve()
    throws ObjectStreamException
  {
    return getInstance(currencyCode);
  }
}