001/**
002 * Copyright (c) 2012, 2014, Credit Suisse (Anatole Tresch), Werner Keil and others by the @author tag.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005 * use this file except in compliance with the License. You may obtain a copy of
006 * the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013 * License for the specific language governing permissions and limitations under
014 * the License.
015 */
016package org.javamoney.moneta.spi;
017
018import static org.javamoney.moneta.spi.AbstractCurrencyConversion.KEY_SCALE;
019
020import java.math.BigDecimal;
021import java.math.MathContext;
022import java.time.LocalDate;
023import java.time.LocalDateTime;
024import java.util.Objects;
025import java.util.Optional;
026import java.util.logging.Logger;
027
028import javax.money.NumberValue;
029import javax.money.convert.ConversionContext;
030import javax.money.convert.ConversionQuery;
031import javax.money.convert.CurrencyConversion;
032import javax.money.convert.ExchangeRate;
033import javax.money.convert.ExchangeRateProvider;
034import javax.money.convert.ProviderContext;
035import javax.money.convert.RateType;
036
037/**
038 * Abstract base class for {@link ExchangeRateProvider} implementations.
039 *
040 * @author Anatole Tresch
041 * @author Werner Keil
042 */
043public abstract class AbstractRateProvider implements ExchangeRateProvider {
044
045    /**
046     * The {@link ConversionContext} of this provider.
047     */
048    private final ProviderContext context;
049
050    @Deprecated
051    protected final Logger log = Logger.getLogger(getClass().getName());
052
053    /**
054     * Constructor.
055     *
056     * @param providerContext the {@link ProviderContext}, not null.
057     */
058    public AbstractRateProvider(ProviderContext providerContext) {
059        Objects.requireNonNull(providerContext);
060        this.context = providerContext;
061    }
062
063    /*
064     * (non-Javadoc)
065     *
066     * @see javax.money.convert.spi.ExchangeRateProviderSpi#getExchangeRateType
067     * ()
068     */
069    @Override
070    public ProviderContext getContext() {
071        return context;
072    }
073
074    @Override
075    public abstract ExchangeRate getExchangeRate(ConversionQuery conversionQuery);
076
077    @Override
078    public CurrencyConversion getCurrencyConversion(ConversionQuery conversionQuery) {
079        if (getContext().getRateTypes().size() == 1) {
080            return new LazyBoundCurrencyConversion(conversionQuery, this, ConversionContext
081                    .of(getContext().getProviderName(), getContext().getRateTypes().iterator().next()));
082        }
083        return new LazyBoundCurrencyConversion(conversionQuery, this,
084                ConversionContext.of(getContext().getProviderName(), RateType.ANY));
085    }
086
087
088    /**
089     * A protected helper method to multiply 2 {@link NumberValue} types.<br>
090     * If either of the values is <code>null</code> an {@link ArithmeticException} is thrown.
091     *
092     * @param multiplicand the first value to be multiplied
093     * @param multiplier   the second value to be multiplied
094     * @return the result of the multiplication as {@link NumberValue}
095     */
096    protected static NumberValue multiply(NumberValue multiplicand, NumberValue multiplier) {
097        if (Objects.isNull(multiplicand)) {
098            throw new ArithmeticException("The multiplicand cannot be null");
099        }
100        if (Objects.isNull(multiplier)) {
101            throw new ArithmeticException("The multiplier cannot be null");
102        }
103        return new DefaultNumberValue(
104                multiplicand.numberValueExact(BigDecimal.class).multiply(multiplier.numberValue(BigDecimal.class)));
105    }
106
107    /**
108     * A protected helper method to divide 2 {@link NumberValue} types.<br>
109     * If either of the values is <code>null</code> an {@link ArithmeticException} is thrown.
110     *
111     * @param dividend the first value to be divided
112     * @param divisor  the value to be divided by
113     * @return the result of the division as {@link NumberValue}
114     */
115    protected static NumberValue divide(NumberValue dividend, NumberValue divisor) {
116        if (Objects.isNull(dividend)) {
117            throw new ArithmeticException("The dividend cannot be null");
118        }
119        if (Objects.isNull(divisor)) {
120            throw new ArithmeticException("The divisor cannot be null");
121        }
122        return new DefaultNumberValue(
123                dividend.numberValueExact(BigDecimal.class).divide(divisor.numberValue(BigDecimal.class),
124                        MathContext.DECIMAL64));
125    }
126
127    /**
128     * A protected helper method to divide 2 {@link NumberValue} types.<br>
129     * If either of the values is <code>null</code> an {@link ArithmeticException} is thrown.
130     *
131     * @param dividend the first value to be divided
132     * @param divisor  the value to be divided by
133     * @param context  the {@link MathContext} to use
134     * @return the result of the division as {@link NumberValue}
135     */
136    protected static NumberValue divide(NumberValue dividend, NumberValue divisor, MathContext context) {
137        if (Objects.isNull(dividend)) {
138            throw new ArithmeticException("The dividend cannot be null");
139        }
140        if (Objects.isNull(divisor)) {
141            throw new ArithmeticException("The divisor cannot be null");
142        }
143        return new DefaultNumberValue(
144                dividend.numberValueExact(BigDecimal.class).divide(divisor.numberValue(BigDecimal.class), context));
145    }
146
147    protected int getScale(String key) {
148                String string = MonetaryConfig.getConfig().getOrDefault(
149                                key, "-1");
150                if (string.isEmpty()) {
151                        return -1;
152                } else {
153                        try {
154                                return Integer.valueOf(string);
155                        } catch (NumberFormatException e) {
156                                return -1;
157                        }
158                }
159        }
160
161    protected ConversionContext getExchangeContext(String key) {
162                int scale = getScale(key);
163        if(scale < 0) {
164          return ConversionContext.of(this.context.getProviderName(), RateType.HISTORIC);
165        } else {
166                return ConversionContext.of(this.context.getProviderName(), RateType.HISTORIC).toBuilder().set(KEY_SCALE, scale).build();
167        }
168        }
169
170    protected LocalDate[] getQueryDates(ConversionQuery query) {
171
172        if (Objects.nonNull(query.get(LocalDate.class)) || Objects.nonNull(query.get(LocalDateTime.class))) {
173                LocalDate localDate = Optional.ofNullable(query.get(LocalDate.class)).orElseGet(() -> query.get(LocalDateTime.class).toLocalDate());
174                return new LocalDate[]{localDate};
175        } else if(Objects.nonNull(query.get(LocalDate[].class))) {
176                return query.get(LocalDate[].class);
177        }
178        return null;
179    }
180}