From: Andrew John Hughes 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;
+      }
   }
 
   /**
@@ -65,18 +151,20 @@ public final class Currency implements Serializable
    */
   public String getCurrencyCode ()
   {
-    try
-      {
-	return res.getString ("intlCurrencySymbol");
-      }
-    catch (Exception _)
-      {
-	return null;
-      }
+    return currencyCode;
   }
 
   /**
-   * @return number of digits after decimal separator for this currency.
+   * 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 ()
   {
@@ -87,48 +175,87 @@ public final class Currency implements Serializable
     
   /**
    * 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)
   {
-    return new Currency (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[] all_locales = Locale.getAvailableLocales ();
+    Locale[] allLocales = Locale.getAvailableLocales ();
     
-    for (int i=0;i
+   * 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. + * @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 res = + ResourceBundle localeResource = ResourceBundle.getBundle ("gnu.java.locale.LocaleInformation", locale, Currency.class.getClassLoader()); - if (res.equals(this.res)) - return res.getString ("currencySymbol"); + if (localeResource.equals(res)) + return localeResource.getString ("currencySymbol"); else - return res.getString ("intlCurrencySymbol"); + return localeResource.getString ("intlCurrencySymbol"); } catch (Exception e1) { @@ -178,13 +331,25 @@ public final class Currency implements Serializable */ public String toString() { - try - { - return res.getString ("intlCurrencySymbol"); - } - catch (Exception _) - { - return "(unknown currency)"; - } + 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); + } + }