001/*
002 * CREDIT SUISSE IS WILLING TO LICENSE THIS SPECIFICATION TO YOU ONLY UPON THE
003 * CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS AGREEMENT.
004 * PLEASE READ THE TERMS AND CONDITIONS OF THIS AGREEMENT CAREFULLY. BY
005 * DOWNLOADING THIS SPECIFICATION, YOU ACCEPT THE TERMS AND CONDITIONS OF THE
006 * AGREEMENT. IF YOU ARE NOT WILLING TO BE BOUND BY IT, SELECT THE "DECLINE"
007 * BUTTON AT THE BOTTOM OF THIS PAGE. Specification: JSR-354 Money and Currency
008 * API ("Specification") Copyright (c) 2012-2014, Credit Suisse All rights
009 * reserved.
010 */
011package org.javamoney.moneta.spi.base;
012
013import javax.money.CurrencyUnit;
014import javax.money.Monetary;
015import javax.money.convert.ConversionQuery;
016import javax.money.convert.ConversionQueryBuilder;
017import javax.money.convert.CurrencyConversion;
018import javax.money.convert.ExchangeRate;
019import javax.money.convert.ExchangeRateProvider;
020import java.util.Objects;
021
022/**
023 * This interface defines access to the exchange rates provided by a provider.
024 * The provider and its capabilities can be defined in arbitrary detail by the
025 * corresponding {@link javax.money.convert.ProviderContext}.
026 * Instances of this class must only provide conversion data for exact one provider, identified by
027 * {@link javax.money.convert.ProviderContext#getProviderName()}.
028 *
029 * When accessing ExchangeRateProvider instances or {@link javax.money.convert.CurrencyConversion} instances from the
030 * {@link javax.money.convert.MonetaryConversions}
031 * in many cases a chain of providers will be returned. It is the responsibility of the implementation code assembling
032 * the chain to
033 * establish a well defined coordination mechanism for evaluating the correct result. By default the first provider
034 * in the chain that returns a non null result determines the final result of a call. Nevertheless adapting the
035 * {@link javax.money.spi.MonetaryConversionsSingletonSpi} allows
036 * to implement also alternate strategies, e.g. honoring different priorities of providers as well.
037 * <p>
038 * Implementations of this interface are required to be thread save.
039 * <p>
040 * Implementations of this class must neither be immutable nor serializable.
041 *
042 * @author Anatole Tresch
043 * @author Werner Keil
044 */
045public abstract class BaseExchangeRateProvider implements ExchangeRateProvider{
046
047    /**
048     * Checks if an {@link javax.money.convert.ExchangeRate} between two {@link javax.money.CurrencyUnit} is
049     * available from this provider. This method should check, if a given rate
050     * is <i>currently</i> defined.
051     *
052     * @param conversionQuery the required {@link javax.money.convert.ConversionQuery}, not {@code null}
053     * @return {@code true}, if such an {@link javax.money.convert.ExchangeRate} is currently
054     * defined.
055     */
056    public boolean isAvailable(ConversionQuery conversionQuery){
057        Objects.requireNonNull(conversionQuery);
058        try{
059            return conversionQuery.getProviderNames().isEmpty() ||
060                    conversionQuery.getProviderNames().contains(getContext().getProviderName());
061        }
062        catch(Exception e){
063            return false;
064        }
065    }
066
067
068    /**
069     * Access a {@link javax.money.convert.ExchangeRate} using the given currencies. The
070     * {@link javax.money.convert.ExchangeRate} may be, depending on the data provider, eal-time or
071     * deferred. This method should return the rate that is <i>currently</i>
072     * valid.
073     *
074     * @param base base {@link javax.money.CurrencyUnit}, not {@code null}
075     * @param term term {@link javax.money.CurrencyUnit}, not {@code null}
076     * @throws javax.money.convert.CurrencyConversionException If no such rate is available.
077     */
078    public ExchangeRate getExchangeRate(CurrencyUnit base, CurrencyUnit term){
079        Objects.requireNonNull(base, "Base Currency is null");
080        Objects.requireNonNull(term, "Term Currency is null");
081        return getExchangeRate(ConversionQueryBuilder.of().setBaseCurrency(base).setTermCurrency(term).build());
082    }
083
084    /**
085     * Access a {@link javax.money.convert.CurrencyConversion} that can be applied as a
086     * {@link javax.money.MonetaryOperator} to an amount.
087     *
088     * @param term term {@link javax.money.CurrencyUnit}, not {@code null}
089     * @return a new instance of a corresponding {@link javax.money.convert.CurrencyConversion},
090     * never {@code null}.
091     */
092    public CurrencyConversion getCurrencyConversion(CurrencyUnit term){
093        return getCurrencyConversion(ConversionQueryBuilder.of().setTermCurrency(term).build());
094    }
095
096    /**
097     * Checks if an {@link javax.money.convert.ExchangeRate} between two {@link javax.money.CurrencyUnit} is
098     * available from this provider. This method should check, if a given rate
099     * is <i>currently</i> defined.
100     *
101     * @param base the base {@link javax.money.CurrencyUnit}
102     * @param term the term {@link javax.money.CurrencyUnit}
103     * @return {@code true}, if such an {@link javax.money.convert.ExchangeRate} is currently
104     * defined.
105     */
106    public boolean isAvailable(CurrencyUnit base, CurrencyUnit term){
107        return isAvailable(ConversionQueryBuilder.of().setBaseCurrency(base).setTermCurrency(term).build());
108    }
109
110
111    /**
112     * Checks if an {@link javax.money.convert.ExchangeRate} between two {@link javax.money.CurrencyUnit} is
113     * available from this provider. This method should check, if a given rate
114     * is <i>currently</i> defined.
115     *
116     * @param baseCode the base currency code
117     * @param termCode the terminal/target currency code
118     * @return {@code true}, if such an {@link javax.money.convert.ExchangeRate} is currently
119     * defined.
120     * @throws javax.money.MonetaryException if one of the currency codes passed is not valid.
121     */
122    public boolean isAvailable(String baseCode, String termCode){
123        return isAvailable(Monetary.getCurrency(baseCode), Monetary.getCurrency(termCode));
124    }
125
126
127    /**
128     * Access a {@link javax.money.convert.ExchangeRate} using the given currencies. The
129     * {@link javax.money.convert.ExchangeRate} may be, depending on the data provider, eal-time or
130     * deferred. This method should return the rate that is <i>currently</i>
131     * valid.
132     *
133     * @param baseCode base currency code, not {@code null}
134     * @param termCode term/target currency code, not {@code null}
135     * @return the matching {@link javax.money.convert.ExchangeRate}.
136     * @throws javax.money.convert.CurrencyConversionException If no such rate is available.
137     * @throws javax.money.MonetaryException           if one of the currency codes passed is not valid.
138     */
139    public ExchangeRate getExchangeRate(String baseCode, String termCode){
140        return getExchangeRate(Monetary.getCurrency(baseCode), Monetary.getCurrency(termCode));
141    }
142
143
144    /**
145     * The method reverses the {@link javax.money.convert.ExchangeRate} to a rate mapping from term
146     * to base {@link javax.money.CurrencyUnit}. Hereby the factor must <b>not</b> be
147     * recalculated as {@code 1/oldFactor}, since typically reverse rates are
148     * not symmetric in most cases.
149     *
150     * @return the matching reversed {@link javax.money.convert.ExchangeRate}, or {@code null}, if
151     * the rate cannot be reversed.
152     */
153    public ExchangeRate getReversed(ExchangeRate rate){
154        ConversionQuery reverseQuery = rate.getContext().toQueryBuilder().setBaseCurrency(rate.getCurrency())
155                .setTermCurrency(rate.getBaseCurrency()).build();
156        if(isAvailable(reverseQuery)){
157            return getExchangeRate(reverseQuery);
158        }
159        return null;
160    }
161
162
163    /**
164     * Access a {@link javax.money.convert.CurrencyConversion} that can be applied as a
165     * {@link javax.money.MonetaryOperator} to an amount.
166     *
167     * @param termCode terminal/target currency code, not {@code null}
168     * @return a new instance of a corresponding {@link javax.money.convert.CurrencyConversion},
169     * never {@code null}.
170     * @throws javax.money.MonetaryException if one of the currency codes passed is not valid.
171     */
172    public CurrencyConversion getCurrencyConversion(String termCode){
173        return getCurrencyConversion(Monetary.getCurrency(termCode));
174    }
175
176}