001/* 002 * JScience - Java(TM) Tools and Libraries for the Advancement of Sciences. 003 * Copyright (C) 2006 - JScience (http://jscience.org/) 004 * All rights reserved. 005 * 006 * Permission to use, copy, modify, and distribute this software is 007 * freely granted, provided that this notice is preserved. 008 */ 009package org.jscience.economics.money; 010 011import javolution.context.LocalContext; 012import javolution.util.LocalMap; 013 014import javax.measure.converter.ConversionException; 015import javax.measure.converter.UnitConverter; 016import javax.measure.unit.DerivedUnit; 017import javax.measure.unit.Unit; 018import javax.measure.unit.UnitFormat; 019 020/** 021 * <p> This class represents a currency {@link javax.measure.unit.Unit Unit}. 022 * Currencies are a special form of {@link DerivedUnit}, conversions 023 * between currencies is possible if their respective exchange rates 024 * have been set and the conversion factor can be changed dynamically.</p> 025 * 026 * <p> Quantities stated in {@link Currency} are usually instances of 027 * {@link Money}.</p> 028 * 029 * <p> By default, the label associated to a currency is its ISO-4217 code 030 * (see the <a href="http://www.bsi-global.com/iso4217currency"> ISO 4217 031 * maintenance agency</a> for a table of currency codes). An application may 032 * change this default using the {@link javax.measure.unit.UnitFormat#label 033 * UnitFormat.label(String)} method. 034 * For example:[code] 035 * UnitFormat.getStandardInstance().label(Currency.EUR, "€"); 036 * UnitFormat.getStandardInstance().label(Currency.GBP, "£"); 037 * UnitFormat.getStandardInstance().label(Currency.JPY, "¥"); 038 * UnitFormat.getStandardInstance().label(Currency.USD, "$"); 039 * [/code]</p> 040 * 041 * @author <a href="mailto:jean-marie@dautelle.com">Jean-Marie Dautelle</a> 042 * @version 3.0, February 13, 2006 043 * @see #setExchangeRate 044 */ 045public class Currency extends DerivedUnit<Money> { 046 047 /** 048 * The Australian Dollar currency unit. 049 */ 050 public static final Currency AUD = new Currency("AUD"); 051 052 /** 053 * The Canadian Dollar currency unit. 054 */ 055 public static final Currency CAD = new Currency("CAD"); 056 057 /** 058 * The China Yan currency. 059 */ 060 public static final Currency CNY = new Currency("CNY"); 061 062 /** 063 * The Euro currency. 064 */ 065 public static final Currency EUR = new Currency("EUR"); 066 067 /** 068 * The British Pound currency. 069 */ 070 public static final Currency GBP = new Currency("GBP"); 071 072 /** 073 * The Japanese Yen currency. 074 */ 075 public static final Currency JPY = new Currency("JPY"); 076 077 /** 078 * The Korean Republic Won currency. 079 */ 080 public static final Currency KRW = new Currency("KRW"); 081 082 /** 083 * The Taiwanese dollar currency. 084 */ 085 public static final Currency TWD = new Currency("TWD"); 086 087 /** 088 * The United State dollar currency. 089 */ 090 public static final Currency USD = new Currency("USD"); 091 092 /** 093 * Holds the reference currency. 094 */ 095 private static final LocalContext.Reference<Currency> 096 REFERENCE = new LocalContext.Reference<Currency>(); 097 098 /** 099 * Holds the exchanges rate to the reference currency. 100 */ 101 private static final LocalMap<String, Double> TO_REFERENCE = 102 new LocalMap<String, Double>(); 103 104 /** 105 * Holds the converter to the {@link Money#BASE_UNIT money base unit}. 106 */ 107 private final Converter _toBaseUnit; 108 109 /** 110 * Creates the currency unit for the given currency code. 111 * See the <a href="http://www.bsi-global.com/iso4217currency"> ISO 4217 112 * maintenance agency</a> for more information, including a table of 113 * currency codes. 114 * 115 * @param code the ISO-4217 code of the currency (e.g. 116 * <code>"EUR", "USD", "JPY"</code>). 117 * @throws IllegalArgumentException if the specified code is not an ISO-4217 118 * code. 119 */ 120 public Currency(String code) { 121 _toBaseUnit = new Converter(code, false); 122 UnitFormat.getInstance().label(this, code); 123 } 124 125 /** 126 * Returns the currency code for this currency. 127 * 128 * @return the ISO-4217 code of the currency 129 * (e.g. <code>"EUR", "USD", "JPY"</code>). 130 */ 131 public String getCode() { 132 return _toBaseUnit._code; 133 } 134 135 /** 136 * Returns the default number of fraction digits used with this currency 137 * unit. For example, the default number of fraction digits for 138 * the {@link Currency#EUR} is 2, while for the {@link Currency#JPY} (Yen) 139 * it's 0. This method can be overriden for custom currencies returning 140 * values different from <code>2</code>. 141 * 142 * @return the default number of fraction digits for this currency. 143 */ 144 public int getDefaultFractionDigits() { 145 return (this.equals(JPY) || (this.equals(KRW))) ? 146 0 : 2; 147 } 148 149 /** 150 * Sets the reference currency (context-local). Changing the 151 * reference currency clears all the exchange rates previously set. 152 * 153 * @param currency the new reference currency. 154 * @see javolution.context.LocalContext 155 */ 156 public static void setReferenceCurrency(Currency currency) { 157 REFERENCE.set(currency); 158 TO_REFERENCE.clear(); 159 TO_REFERENCE.put(currency.getCode(), 1.0); 160 } 161 162 /** 163 * Returns the currency used as reference when setting the exchange rate. 164 * By default, the reference currency is the currency for the default 165 * country locale. 166 * 167 * @return the reference currency. 168 * @see #setExchangeRate 169 */ 170 public static Currency getReferenceCurrency() { 171 return REFERENCE.get(); 172 } 173 174 /** 175 * Sets the exchange rate of this {@link Currency} relatively to 176 * the reference currency. Setting the exchange rate allows 177 * for conversion between {@link Money} stated in different currencies. 178 * For example:<pre> 179 * Currency.setReferenceCurrency(Currency.USD); 180 * Currency.EUR.setExchangeRate(1.17); // 1.0 € = 1.17 $ 181 * </pre> 182 * 183 * @param refAmount the amount stated in the {@link #getReferenceCurrency} 184 * equals to one unit of this {@link Currency}. 185 * @see #getReferenceCurrency 186 */ 187 public void setExchangeRate(double refAmount) { 188 TO_REFERENCE.put(this.getCode(), refAmount); 189 } 190 191 /** 192 * Returns the exchange rate for this {@link Currency}. 193 * 194 * @return the amount stated in the {@link #getReferenceCurrency} 195 * equals to one unit of this {@link Currency}. 196 * @throws ConversionException if the exchange rate has not be set for 197 * this {@link Currency}. 198 */ 199 public double getExchangeRate() { 200 Double refAmount = TO_REFERENCE.get(this.getCode()); 201 if (refAmount == null) 202 throw new ConversionException("Exchange rate not set for " + this.getCode()); 203 return refAmount.doubleValue(); 204 } 205 206 207 @Override 208 public boolean equals(Object obj) { 209 if (this == obj) return true; 210 if (!(obj instanceof Currency)) 211 return false; 212 Currency that = (Currency) obj; 213 return this._toBaseUnit.equals(that._toBaseUnit); 214 } 215 216 @Override 217 public int hashCode() { 218 return _toBaseUnit.hashCode(); 219 } 220 221 @Override 222 public Unit<? super Money> getStandardUnit() { 223 return Money.BASE_UNIT; 224 } 225 226 @Override 227 public UnitConverter toStandardUnit() { 228 return _toBaseUnit; 229 } 230 231 /** 232 * This class represents the currency converters. 233 */ 234 private static class Converter extends UnitConverter { 235 236 String _code; 237 238 boolean _invert; 239 240 private Converter(String code, boolean invert) { 241 _code = code; 242 _invert = invert; 243 } 244 245 @Override 246 public UnitConverter inverse() { 247 return new Converter(_code, !_invert); 248 } 249 250 @Override 251 public double convert(double x) throws ConversionException { 252 Double refAmount = TO_REFERENCE.get(_code); 253 if (refAmount == null) 254 throw new ConversionException("Exchange rate not set for " + _code); 255 return _invert ? x / refAmount.doubleValue() : x * refAmount.doubleValue(); 256 } 257 258 @Override 259 public boolean isLinear() { 260 return true; 261 } 262 263 @Override 264 public boolean equals(Object obj) { 265 if (this == obj) return true; 266 if (!(obj instanceof Converter)) 267 return false; 268 Converter that = (Converter) obj; 269 return this._code.equals(that._code) && (this._invert == that._invert); 270 } 271 272 @Override 273 public int hashCode() { 274 return _invert ? _code.hashCode() : - _code.hashCode(); 275 } 276 277 private static final long serialVersionUID = 1L; 278 } 279 280 private static final long serialVersionUID = 1L; 281}