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.ConversionQuery;
019import javax.money.convert.CurrencyConversionException;
020import javax.money.convert.ExchangeRate;
021import javax.money.convert.ExchangeRateProvider;
022import javax.money.convert.ProviderContext;
023import javax.money.convert.ProviderContextBuilder;
024import javax.money.convert.RateType;
025
026import java.util.*;
027import java.util.logging.Level;
028import java.util.logging.Logger;
029
030/**
031 * This class implements a {@link ExchangeRateProvider} that delegates calls to
032 * a collection of child {@link ExchangeRateProvider} instance.
033 *
034 * @author Anatole Tresch
035 */
036public class CompoundRateProvider extends AbstractRateProvider {
037    /**
038     * Kery used to store a list of child {@link javax.money.convert.ProviderContext} instances of the providers
039     * contained within this instance.
040     */
041    public static final String CHILD_PROVIDER_CONTEXTS_KEY = "childProviderContexts";
042    /**
043     * The {@link ExchangeRateProvider} instances.
044     */
045    private final List<ExchangeRateProvider> providers = new ArrayList<>();
046
047    /**
048     * Constructor.
049     *
050     * @param providers The collection of child {@link ExchangeRateProvider}
051     *                  instances this class delegates calls to.
052     */
053    public CompoundRateProvider(Iterable<ExchangeRateProvider> providers) {
054        super(createContext(providers));
055        for (ExchangeRateProvider exchangeRateProvider : providers) {
056            addProvider(exchangeRateProvider);
057        }
058    }
059
060    private static ProviderContext createContext(Iterable<ExchangeRateProvider> providers) {
061        Set<RateType> rateTypeSet = new HashSet<>();
062        StringBuilder providerName = new StringBuilder("Compound: ");
063        List<ProviderContext> childContextList = new ArrayList<>();
064        for (ExchangeRateProvider exchangeRateProvider : providers) {
065            childContextList.add(exchangeRateProvider.getContext());
066            providerName.append(exchangeRateProvider.getContext().getProviderName());
067            providerName.append(',');
068            rateTypeSet.addAll(exchangeRateProvider.getContext().getRateTypes());
069        }
070        providerName.setLength(providerName.length() - 1);
071
072        ProviderContextBuilder builder = ProviderContextBuilder.of(providerName.toString(), rateTypeSet);
073        builder.set(CHILD_PROVIDER_CONTEXTS_KEY, childContextList);
074        return builder.build();
075    }
076
077    /**
078     * Add an additional {@link ExchangeRateProvider} to the instance's delegate
079     * list.
080     *
081     * @param prov The {@link ExchangeRateProvider} to be added, not {@code null}
082     *             .
083     * @throws java.lang.NullPointerException if the provider is null.
084     *                                        .
085     */
086    private void addProvider(ExchangeRateProvider prov) {
087        if(prov==null){
088            throw new NullPointerException("ConversionProvider required.");
089        }
090        providers.add(prov);
091    }
092
093    /*
094     * (non-Javadoc)
095     *
096     * @see
097     * ExchangeRateProvider#getExchangeRate(org.javamoney.bp.
098     * CurrencyUnit, CurrencyUnit,
099     * ConversionContext)
100     */
101    @Override
102    public ExchangeRate getExchangeRate(ConversionQuery conversionQuery) {
103        for (ExchangeRateProvider prov : this.providers) {
104            try {
105                if (prov.isAvailable(conversionQuery)) {
106                    ExchangeRate rate = prov.getExchangeRate(conversionQuery);
107                    if (rate!=null) {
108                        return rate;
109                    }
110                }
111            } catch (Exception e) {
112                Logger.getLogger(getClass().getName()).log(Level.WARNING,
113                        "Rate Provider did not return data though at check before data was flagged as available," +
114                                " provider=" + prov.getContext().getProviderName() + ", query=" + conversionQuery);
115            }
116        }
117        throw new CurrencyConversionException(conversionQuery.getBaseCurrency(), conversionQuery.getCurrency(), null,
118                "All delegate prov iders failed to deliver rate, providers=" + this.providers +
119                        ", query=" + conversionQuery);
120    }
121
122
123}