001/*
002 * Copyright (c) 2012, 2014, Credit Suisse (Anatole Tresch), Werner Keil. Licensed under the Apache
003 * License, Version 2.0 (the "License"); you may not use this file except in compliance with the
004 * License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
005 * Unless required by applicable law or agreed to in writing, software distributed under the License
006 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
007 * or implied. See the License for the specific language governing permissions and limitations under
008 * the License.
009 */
010package org.javamoney.moneta.spi;
011
012import java.math.BigDecimal;
013import java.math.MathContext;
014import java.util.Objects;
015import java.util.logging.Logger;
016
017import javax.money.CurrencyUnit;
018import javax.money.MonetaryCurrencies;
019import javax.money.NumberValue;
020import javax.money.convert.ConversionContext;
021import javax.money.convert.CurrencyConversion;
022import javax.money.convert.CurrencyConversionException;
023import javax.money.convert.ExchangeRate;
024import javax.money.convert.ExchangeRateProvider;
025import javax.money.convert.ProviderContext;
026
027/**
028 * Abstract base class for {@link ExchangeRateProvider} implementations.
029 * 
030 * @author Anatole Tresch
031 * @author Werner Keil
032 *
033 */
034public abstract class AbstractRateProvider implements ExchangeRateProvider {
035
036        /** The logger used. */
037        protected final Logger LOGGER = Logger.getLogger(getClass().getName());
038
039        /** The {@link ConversionContext} of this provider. */
040        private ProviderContext providerContext;
041
042        /**
043         * Constructor.
044         * 
045         * @param providerContext
046         *            the {@link ProviderContext}, not null.
047         */
048        public AbstractRateProvider(ProviderContext providerContext) {
049                Objects.requireNonNull(providerContext);
050                this.providerContext = providerContext;
051        }
052
053        protected abstract ExchangeRate getExchangeRateInternal(CurrencyUnit base,
054                        CurrencyUnit term, ConversionContext context);
055
056        /*
057         * (non-Javadoc)
058         * 
059         * @see javax.money.convert.spi.ExchangeRateProviderSpi#getExchangeRateType
060         * ()
061         */
062        @Override
063        public ProviderContext getProviderContext() {
064                return providerContext;
065        }
066
067        /*
068         * (non-Javadoc)
069         * 
070         * @see
071         * javax.money.convert.ExchangeRateProvider#isAvailable(javax.money.CurrencyUnit
072         * , javax.money.CurrencyUnit)
073         */
074        @Override
075        public boolean isAvailable(CurrencyUnit src, CurrencyUnit target) {
076                return getExchangeRate(src, target) != null;
077        }
078
079        /*
080         * (non-Javadoc)
081         * 
082         * @see
083         * javax.money.convert.ExchangeRateProvider#getExchangeRate(javax.money.
084         * CurrencyUnit, javax.money.CurrencyUnit)
085         */
086        @Override
087        public ExchangeRate getExchangeRate(CurrencyUnit source, CurrencyUnit target) {
088                return getExchangeRate(source, target, ConversionContext.of());
089        }
090
091        /*
092         * (non-Javadoc)
093         * 
094         * @see
095         * javax.money.convert.ExchangeRateProvider#getReversed(javax.money.convert
096         * .ExchangeRate)
097         */
098        @Override
099        public ExchangeRate getReversed(ExchangeRate rate) {
100                if (isAvailable(rate.getTerm(), rate.getBase(),
101                                rate.getConversionContext())) {
102                        return getExchangeRate(rate.getTerm(), rate.getBase(),
103                                        rate.getConversionContext());
104                }
105                return null;
106        }
107
108        /*
109         * (non-Javadoc)
110         * 
111         * @see
112         * javax.money.convert.ExchangeRateProvider#getCurrencyConversion(javax.
113         * money.CurrencyUnit)
114         */
115        @Override
116        public CurrencyConversion getCurrencyConversion(CurrencyUnit termCurrency) {
117                return new LazyBoundCurrencyConversion(termCurrency, this,
118                                ConversionContext.of());
119        }
120
121        /*
122         * (non-Javadoc)
123         * 
124         * @see
125         * javax.money.convert.ExchangeRateProvider#getCurrencyConversion(javax.
126         * money.CurrencyUnit, javax.money.convert.ConversionContext)
127         */
128        @Override
129        public CurrencyConversion getCurrencyConversion(CurrencyUnit term,
130                        ConversionContext conversionContext) {
131                return new LazyBoundCurrencyConversion(term, this, conversionContext);
132        }
133
134        /*
135         * (non-Javadoc)
136         * 
137         * @see
138         * javax.money.convert.ExchangeRateProvider#isAvailable(java.lang.String,
139         * java.lang.String)
140         */
141        @Override
142        public boolean isAvailable(String baseCode, String termCode) {
143                return isAvailable(MonetaryCurrencies.getCurrency(baseCode),
144                                MonetaryCurrencies.getCurrency(termCode),
145                                ConversionContext.of());
146        }
147
148        /*
149         * (non-Javadoc)
150         * 
151         * @see
152         * javax.money.convert.ExchangeRateProvider#getExchangeRate(java.lang.String
153         * , java.lang.String)
154         */
155        @Override
156        public ExchangeRate getExchangeRate(String baseCode, String termCode) {
157                return getExchangeRate(MonetaryCurrencies.getCurrency(baseCode),
158                                MonetaryCurrencies.getCurrency(termCode),
159                                ConversionContext.of());
160        }
161
162        /*
163         * (non-Javadoc)
164         * 
165         * @see
166         * javax.money.convert.ExchangeRateProvider#getCurrencyConversion(java.lang
167         * .String)
168         */
169        @Override
170        public CurrencyConversion getCurrencyConversion(String termCode) {
171                return getCurrencyConversion(MonetaryCurrencies.getCurrency(termCode));
172        }
173
174        /*
175         * (non-Javadoc)
176         * 
177         * @see
178         * javax.money.convert.ExchangeRateProvider#getCurrencyConversion(java.lang
179         * .String, javax.money.convert.ConversionContext)
180         */
181        @Override
182        public CurrencyConversion getCurrencyConversion(String termCode,
183                        ConversionContext conversionContext) {
184                return getCurrencyConversion(MonetaryCurrencies.getCurrency(termCode),
185                                conversionContext);
186        }
187
188        /*
189         * (non-Javadoc)
190         * 
191         * @see
192         * javax.money.convert.ExchangeRateProvider#isAvailable(java.lang.String,
193         * java.lang.String, javax.money.convert.ConversionContext)
194         */
195        @Override
196        public boolean isAvailable(String baseCode, String termCode,
197                        ConversionContext conversionContext) {
198                return isAvailable(MonetaryCurrencies.getCurrency(baseCode),
199                                MonetaryCurrencies.getCurrency(termCode), conversionContext);
200        }
201
202        /*
203         * (non-Javadoc)
204         * 
205         * @see
206         * javax.money.convert.ExchangeRateProvider#getExchangeRate(java.lang.String
207         * , java.lang.String, javax.money.convert.ConversionContext)
208         */
209        @Override
210        public ExchangeRate getExchangeRate(String baseCode, String termCode,
211                        ConversionContext conversionContext) {
212                return getExchangeRate(MonetaryCurrencies.getCurrency(baseCode),
213                                MonetaryCurrencies.getCurrency(termCode), conversionContext);
214        }
215
216        /*
217         * (non-Javadoc)
218         * 
219         * @see
220         * org.javamoney.moneta.convert.internal.AbstractRateProvider#isAvailable
221         * (javax.money.CurrencyUnit, javax.money.CurrencyUnit,
222         * javax.money.convert.ConversionContext)
223         */
224        @Override
225        public boolean isAvailable(CurrencyUnit base, CurrencyUnit term,
226                        ConversionContext conversionContext) {
227                return getExchangeRateInternal(base, term,
228                                conversionContext) != null;
229        }
230
231        /*
232         * (non-Javadoc)
233         * 
234         * @see
235         * org.javamoney.moneta.convert.internal.AbstractRateProvider#getExchangeRate
236         * (javax.money.CurrencyUnit, javax.money.CurrencyUnit,
237         * javax.money.convert.ConversionContext)
238         */
239        @Override
240        public ExchangeRate getExchangeRate(CurrencyUnit base, CurrencyUnit term,
241                        ConversionContext conversionContext) {
242                ExchangeRate rate = getExchangeRateInternal(base, term,
243                                conversionContext);
244                if (rate == null) {
245                        throw new CurrencyConversionException(base, term, conversionContext);
246                }
247                return rate;
248        }
249        
250        /**
251         * A protected helper method to multiply 2 {@link NumberValue} types.<br>
252         * If either of the values is <code>null</code> an {@link ArithmeticException} is thrown.
253         * 
254         * @param multiplicand the first value to be multiplied
255         * @param multiplier the second value to be multiplied
256         * @return the result of the multiplication as {@link NumberValue}
257         */
258        protected static final NumberValue multiply(NumberValue multiplicand, NumberValue multiplier) {
259                if (multiplicand == null) {
260                        throw new ArithmeticException("The multiplicand cannot be null");
261                }
262                if (multiplier == null) {
263                        throw new ArithmeticException("The multiplier cannot be null");
264                }
265                return new DefaultNumberValue(
266                                multiplicand.numberValue(BigDecimal.class).multiply(
267                                                multiplier.numberValue(BigDecimal.class))); // TODO should we use numberValueExact?
268        }
269        
270        /**
271         * A protected helper method to divide 2 {@link NumberValue} types.<br>
272         * If either of the values is <code>null</code> an {@link ArithmeticException} is thrown.
273         * 
274         * @param dividend the first value to be divided
275         * @param divisor the value to be divided by
276         * @return the result of the division as {@link NumberValue}
277         */
278        protected static final NumberValue divide(NumberValue dividend, NumberValue divisor) {
279                if (dividend == null) {
280                        throw new ArithmeticException("The dividend cannot be null");
281                }
282                if (divisor == null) {
283                        throw new ArithmeticException("The divisor cannot be null");
284                }
285                return new DefaultNumberValue(
286                                dividend.numberValue(BigDecimal.class).divide(
287                                                divisor.numberValue(BigDecimal.class))); // TODO should we use numberValueExact?
288        }
289        
290        /**
291         * A protected helper method to divide 2 {@link NumberValue} types.<br>
292         * If either of the values is <code>null</code> an {@link ArithmeticException} is thrown.
293         * 
294         * @param dividend the first value to be divided
295         * @param divisor the value to be divided by
296         * @param context the {@link MathContext} to use
297         * @return the result of the division as {@link NumberValue}
298         */
299        protected static final NumberValue divide(NumberValue dividend, NumberValue divisor, MathContext context) {
300                if (dividend == null) {
301                        throw new ArithmeticException("The dividend cannot be null");
302                }
303                if (divisor == null) {
304                        throw new ArithmeticException("The divisor cannot be null");
305                }
306                return new DefaultNumberValue(
307                                dividend.numberValue(BigDecimal.class).divide(
308                                                divisor.numberValue(BigDecimal.class), context)); // TODO should we use numberValueExact?
309        }
310}