Frames | No Frames |
1: /* Currency.java -- Representation of a currency 2: Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc. 3: 4: This file is part of GNU Classpath. 5: 6: GNU Classpath is free software; you can redistribute it and/or modify 7: it under the terms of the GNU General Public License as published by 8: the Free Software Foundation; either version 2, or (at your option) 9: any later version. 10: 11: GNU Classpath is distributed in the hope that it will be useful, but 12: WITHOUT ANY WARRANTY; without even the implied warranty of 13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14: General Public License for more details. 15: 16: You should have received a copy of the GNU General Public License 17: along with GNU Classpath; see the file COPYING. If not, write to the 18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19: 02110-1301 USA. 20: 21: Linking this library statically or dynamically with other modules is 22: making a combined work based on this library. Thus, the terms and 23: conditions of the GNU General Public License cover the whole 24: combination. 25: 26: As a special exception, the copyright holders of this library give you 27: permission to link this library with independent modules to produce an 28: executable, regardless of the license terms of these independent 29: modules, and to copy and distribute the resulting executable under 30: terms of your choice, provided that you also meet, for each linked 31: independent module, the terms and conditions of the license of that 32: module. An independent module is a module which is not derived from 33: or based on this library. If you modify this library, you may extend 34: this exception to your version of the library, but you are not 35: obligated to do so. If you do not wish to do so, delete this 36: exception statement from your version. */ 37: 38: package java.util; 39: 40: import java.io.ObjectStreamException; 41: import java.io.Serializable; 42: import java.text.NumberFormat; 43: 44: /** 45: * Representation of a currency for a particular locale. Each currency 46: * is identified by its ISO 4217 code, and only one instance of this 47: * class exists per currency. As a result, instances are created 48: * via the <code>getInstance()</code> methods rather than by using 49: * a constructor. 50: * 51: * @see java.util.Locale 52: * @author Guilhem Lavaux (guilhem.lavaux@free.fr) 53: * @author Dalibor Topic (robilad@kaffe.org) 54: * @author Bryce McKinlay (mckinlay@redhat.com) 55: * @author Andrew John Hughes (gnu_andrew@member.fsf.org) 56: * @since 1.4 57: */ 58: public final class Currency 59: implements Serializable 60: { 61: /** 62: * For compatability with Sun's JDK 63: */ 64: static final long serialVersionUID = -158308464356906721L; 65: 66: /** 67: * The locale associated with this currency. 68: * 69: * @see #Currency(java.util.Locale) 70: * @see #getInstance(java.util.Locale) 71: * @see #getSymbol(java.util.Locale) 72: * @serial ignored. 73: */ 74: private transient Locale locale; 75: 76: /** 77: * The resource bundle which maps the currency to 78: * a ISO 4217 currency code. 79: * 80: * @see #getCurrencyCode() 81: * @serial ignored. 82: */ 83: private transient ResourceBundle res; 84: 85: /** 86: * The ISO 4217 currency code associated with this 87: * particular instance. 88: * 89: * @see #getCurrencyCode() 90: * @serial the ISO 4217 currency code 91: */ 92: private String currencyCode; 93: 94: /** 95: * A cache of <code>Currency</code> instances to 96: * ensure the singleton nature of this class. The key 97: * is the locale of the currency. 98: * 99: * @see #getInstance(java.util.Locale) 100: * @see #readResolve() 101: * @serial ignored. 102: */ 103: private static transient Map cache; 104: 105: /** 106: * Instantiates the cache. 107: */ 108: static 109: { 110: cache = new HashMap(); 111: } 112: 113: /** 114: * Default constructor for deserialization 115: */ 116: private Currency () 117: { 118: } 119: 120: /** 121: * Constructor to create a <code>Currency</code> object 122: * for a particular <code>Locale</code>. 123: * All components of the given locale, other than the 124: * country code, are ignored. The results of calling this 125: * method may vary over time, as the currency associated with 126: * a particular country changes. For countries without 127: * a given currency (e.g. Antarctica), the result is null. 128: * 129: * @param loc the locale for the new currency. 130: */ 131: private Currency (Locale loc) 132: { 133: this.locale = loc; 134: this.res = ResourceBundle.getBundle ("gnu.java.locale.LocaleInformation", 135: locale, ClassLoader.getSystemClassLoader()); 136: /* Retrieve the ISO4217 currency code */ 137: try 138: { 139: currencyCode = res.getString ("intlCurrencySymbol"); 140: } 141: catch (Exception _) 142: { 143: currencyCode = null; 144: } 145: } 146: 147: /** 148: * Returns the ISO4217 currency code of this currency. 149: * 150: * @return a <code>String</code> containing currency code. 151: */ 152: public String getCurrencyCode () 153: { 154: return currencyCode; 155: } 156: 157: /** 158: * Returns the number of digits which occur after the decimal point 159: * for this particular currency. For example, currencies such 160: * as the U.S. dollar, the Euro and the Great British pound have two 161: * digits following the decimal point to indicate the value which exists 162: * in the associated lower-valued coinage (cents in the case of the first 163: * two, pennies in the latter). Some currencies such as the Japanese 164: * Yen have no digits after the decimal point. In the case of pseudo 165: * currencies, such as IMF Special Drawing Rights, -1 is returned. 166: * 167: * @return the number of digits after the decimal separator for this currency. 168: */ 169: public int getDefaultFractionDigits () 170: { 171: NumberFormat currency = NumberFormat.getCurrencyInstance (locale); 172: 173: return currency.getMaximumFractionDigits(); 174: } 175: 176: /** 177: * Builds a new currency instance for this locale. 178: * All components of the given locale, other than the 179: * country code, are ignored. The results of calling this 180: * method may vary over time, as the currency associated with 181: * a particular country changes. For countries without 182: * a given currency (e.g. Antarctica), the result is null. 183: * 184: * @param locale a <code>Locale</code> instance. 185: * @return a new <code>Currency</code> instance. 186: * @throws NullPointerException if the locale or its 187: * country code is null. 188: * @throws IllegalArgumentException if the country of 189: * the given locale is not a supported ISO3166 code. 190: */ 191: public static Currency getInstance (Locale locale) 192: { 193: /** 194: * The new instance must be the only available instance 195: * for the currency it supports. We ensure this happens, 196: * while maintaining a suitable performance level, by 197: * creating the appropriate object on the first call to 198: * this method, and returning the cached instance on 199: * later calls. 200: */ 201: Currency newCurrency; 202: 203: /* Attempt to get the currency from the cache */ 204: newCurrency = (Currency) cache.get(locale); 205: if (newCurrency == null) 206: { 207: /* Create the currency for this locale */ 208: newCurrency = new Currency (locale); 209: /* Cache it */ 210: cache.put(locale, newCurrency); 211: } 212: /* Return the instance */ 213: return newCurrency; 214: } 215: 216: /** 217: * Builds the currency corresponding to the specified currency code. 218: * 219: * @param currencyCode a string representing a currency code. 220: * @return a new <code>Currency</code> instance. 221: * @throws NullPointerException if currencyCode is null. 222: * @throws IllegalArgumentException if the supplied currency code 223: * is not a supported ISO 4217 code. 224: */ 225: public static Currency getInstance (String currencyCode) 226: { 227: Locale[] allLocales = Locale.getAvailableLocales (); 228: 229: for (int i = 0;i < allLocales.length; i++) 230: { 231: Currency testCurrency = getInstance (allLocales[i]); 232: 233: if (testCurrency.getCurrencyCode() != null && 234: testCurrency.getCurrencyCode().equals(currencyCode)) 235: return testCurrency; 236: } 237: /* 238: * If we get this far, the code is not supported by any of 239: * our locales. 240: */ 241: throw new IllegalArgumentException("The currency code, " + currencyCode + 242: ", is not supported."); 243: } 244: 245: /** 246: * This method returns the symbol which precedes or follows a 247: * value in this particular currency. In cases where there is no 248: * such symbol for the currency, the ISO 4217 currency 249: * code is returned. 250: * 251: * @return the currency symbol, or the ISO 4217 currency code if 252: * one doesn't exist. 253: */ 254: public String getSymbol() 255: { 256: try 257: { 258: /* What does this return if there is no mapping? */ 259: return res.getString ("currencySymbol"); 260: } 261: catch (Exception _) 262: { 263: return null; 264: } 265: } 266: 267: /** 268: * <p> 269: * This method returns the symbol which precedes or follows a 270: * value in this particular currency. The returned value is 271: * the symbol used to denote the currency in the specified locale. 272: * </p> 273: * <p> 274: * For example, a supplied locale may specify a different symbol 275: * for the currency, due to conflicts with its own currency. 276: * This would be the case with the American currency, the dollar. 277: * Locales that also use a dollar-based currency (e.g. Canada, Australia) 278: * need to differentiate the American dollar using 'US$' rather than '$'. 279: * So, supplying one of these locales to <code>getSymbol()</code> would 280: * return this value, rather than the standard '$'. 281: * </p> 282: * <p> 283: * In cases where there is no such symbol for a particular currency, 284: * the ISO 4217 currency code is returned. 285: * </p> 286: * 287: * @param locale the locale to express the symbol in. 288: * @return the currency symbol, or the ISO 4217 currency code if 289: * one doesn't exist. 290: * @throws NullPointerException if the locale is null. 291: */ 292: public String getSymbol(Locale locale) 293: { 294: // TODO. The behaviour is unclear if locale != this.locale. 295: // First we need to implement fully LocaleInformation*.java 296: 297: /* 298: * FIXME: My reading of how this method works has this implementation 299: * as wrong. It should return a value relating to how the specified 300: * locale handles the symbol for this currency. This implementation 301: * seems to just do a variation of getInstance(locale). 302: */ 303: try 304: { 305: ResourceBundle localeResource = 306: ResourceBundle.getBundle ("gnu.java.locale.LocaleInformation", 307: locale, Currency.class.getClassLoader()); 308: 309: if (localeResource.equals(res)) 310: return localeResource.getString ("currencySymbol"); 311: else 312: return localeResource.getString ("intlCurrencySymbol"); 313: } 314: catch (Exception e1) 315: { 316: try 317: { 318: return res.getString ("intlCurrencySymbol"); 319: } 320: catch (Exception e2) 321: { 322: return null; 323: } 324: } 325: } 326: 327: /** 328: * Returns the international ISO4217 currency code of this currency. 329: * 330: * @return a <code>String</code> containing the ISO4217 currency code. 331: */ 332: public String toString() 333: { 334: return getCurrencyCode(); 335: } 336: 337: /** 338: * Resolves the deserialized object to the singleton instance for its 339: * particular currency. The currency code of the deserialized instance 340: * is used to return the correct instance. 341: * 342: * @return the singleton instance for the currency specified by the 343: * currency code of the deserialized object. This replaces 344: * the deserialized object as the returned object from 345: * deserialization. 346: * @throws ObjectStreamException if a problem occurs with deserializing 347: * the object. 348: */ 349: private Object readResolve() 350: throws ObjectStreamException 351: { 352: return getInstance(currencyCode); 353: } 354: 355: }