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 javax.money.convert.*;
019import java.util.*;
020
021/**
022 * This class implements a {@link ExchangeRateProvider} that delegates calls to
023 * a collection of child {@link ExchangeRateProvider} instance.
024 *
025 * @author Anatole Tresch
026 */
027public class CompoundRateProvider extends AbstractRateProvider {
028    /**
029     * Kery used to store a list of child {@link javax.money.convert.ProviderContext} instances of the providers
030     * contained within this instance.
031     */
032    public static final String CHILD_PROVIDER_CONTEXTS_KEY = "childProviderContexts";
033    /**
034     * The {@link ExchangeRateProvider} instances.
035     */
036    private final List<ExchangeRateProvider> providers = new ArrayList<>();
037
038    /**
039     * Constructor.
040     *
041     * @param providers The collection of child {@link ExchangeRateProvider}
042     *                  instances this class delegates calls to.
043     */
044    public CompoundRateProvider(Iterable<ExchangeRateProvider> providers) {
045        super(createContext(providers));
046        for (ExchangeRateProvider exchangeRateProvider : providers) {
047            addProvider(exchangeRateProvider);
048        }
049    }
050
051    private static ProviderContext createContext(Iterable<ExchangeRateProvider> providers) {
052        Set<RateType> rateTypeSet = new HashSet<>();
053        StringBuilder providerName = new StringBuilder("Compound: ");
054        List<ProviderContext> childContextList = new ArrayList<>();
055        for (ExchangeRateProvider exchangeRateProvider : providers) {
056            childContextList.add(exchangeRateProvider.getContext());
057            providerName.append(exchangeRateProvider.getContext().getProviderName());
058            providerName.append(',');
059            rateTypeSet.addAll(exchangeRateProvider.getContext().getRateTypes());
060        }
061        providerName.setLength(providerName.length() - 1);
062
063        ProviderContextBuilder builder = ProviderContextBuilder.of(providerName.toString(), rateTypeSet);
064        builder.set(CHILD_PROVIDER_CONTEXTS_KEY, childContextList);
065        return builder.build();
066    }
067
068    /**
069     * Add an additional {@link ExchangeRateProvider} to the instance's delegate
070     * list.
071     *
072     * @param prov The {@link ExchangeRateProvider} to be added, not {@code null}
073     *             .
074     * @throws java.lang.NullPointerException if the provider is null.
075     *                                        .
076     */
077    private void addProvider(ExchangeRateProvider prov) {
078        providers.add(Optional.ofNullable(prov)
079                .orElseThrow(() -> new NullPointerException("ConversionProvider required.")));
080    }
081
082    /*
083     * (non-Javadoc)
084     *
085     * @see
086     * javax.money.convert.ExchangeRateProvider#getExchangeRate(javax.money.
087     * CurrencyUnit, javax.money.CurrencyUnit,
088     * javax.money.convert.ConversionContext)
089     */
090    @Override
091    public ExchangeRate getExchangeRate(ConversionQuery conversionQuery) {
092        for (ExchangeRateProvider prov : this.providers) {
093            if (prov.isAvailable(conversionQuery)) {
094                ExchangeRate rate = prov.getExchangeRate(conversionQuery);
095                if (Objects.nonNull(rate)) {
096                    return rate;
097                }
098            }
099        }
100        return null;
101    }
102
103
104}