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}