001/**
002 * Copyright (c) 2012, 2015, 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.internal.convert;
017
018import java.io.IOException;
019import java.io.InputStream;
020import java.time.LocalDate;
021import java.time.YearMonth;
022import java.util.ArrayList;
023import java.util.List;
024import java.util.Map;
025import java.util.Objects;
026import java.util.logging.Level;
027import java.util.logging.Logger;
028import java.util.stream.Collectors;
029import java.util.stream.Stream;
030
031import javax.money.CurrencyUnit;
032import javax.money.convert.ConversionQuery;
033import javax.money.convert.ExchangeRate;
034import javax.money.convert.ProviderContext;
035import javax.money.convert.ProviderContextBuilder;
036import javax.money.convert.RateType;
037import javax.money.spi.Bootstrap;
038
039import org.javamoney.moneta.internal.convert.IMFRateReadingHandler.RateIMFResult;
040import org.javamoney.moneta.spi.LoaderService;
041
042/**
043 * Find by historic from IMF
044 * @author otaviojava
045 * @since 1.0.1
046 */
047public class IMFHistoricRateProvider extends IMFAbstractRateProvider {
048
049    private static final Logger LOG = Logger.getLogger(IMFHistoricRateProvider.class.getName());
050
051    private static final String DATA_ID = IMFHistoricRateProvider.class.getSimpleName();
052
053        private static final ProviderContext CONTEXT = ProviderContextBuilder.of("IMF-HIST", RateType.HISTORIC)
054                    .set("providerDescription", "Historic International Monetary Fond").set("days", 0).build();
055
056
057        private final List<YearMonth> cachedHistoric = new ArrayList<>();
058        public IMFHistoricRateProvider() {
059                super(CONTEXT);
060                 LoaderService loader = Bootstrap.getService(LoaderService.class);
061                loader.addLoaderListener(this, DATA_ID);
062                try {
063                    loader.loadData(DATA_ID);
064                } catch (IOException e) {
065                        LOG.log(Level.WARNING, "Error loading initial data from IMF provider...", e);
066                }
067        }
068
069        @Override
070        public ExchangeRate getExchangeRate(ConversionQuery conversionQuery) {
071                LocalDate[] times = getQueryDates(conversionQuery);
072                if(Objects.isNull(times)) {
073                        return super.getExchangeRate(conversionQuery);
074                }
075
076                for (YearMonth yearMonth : Stream.of(times).map(YearMonth::from)
077                                .collect(Collectors.toSet())) {
078
079                        if(!cachedHistoric.contains(yearMonth)){
080                                Map<IMFHistoricalType, InputStream> resources = IMFRemoteSearch.INSTANCE.getResources(yearMonth);
081                                loadFromRemote(resources);
082                                cachedHistoric.add(yearMonth);
083                        }
084                }
085                return super.getExchangeRate(conversionQuery);
086        }
087
088        private void loadFromRemote(Map<IMFHistoricalType, InputStream> resources) {
089                try {
090                        for(IMFHistoricalType type: resources.keySet()) {
091                                RateIMFResult result = handler.read(resources.get(type));
092                                combine(result.getSdrToCurrency(), this.sdrToCurrency);
093                                combine(result.getCurrencyToSdr(), this.currencyToSdr);
094                        }
095            } catch (Exception e) {
096                LOG.log(Level.SEVERE, "Error", e);
097            }
098        }
099
100        private Map<CurrencyUnit, List<ExchangeRate>> combine(Map<CurrencyUnit, List<ExchangeRate>> source, Map<CurrencyUnit, List<ExchangeRate>> destination) {
101                for(CurrencyUnit currency: source.keySet()) {
102                        destination.putIfAbsent(currency, new ArrayList<>());
103                        List<ExchangeRate> rates = source.get(currency);
104                        destination.merge(currency, rates, IMFHistoricRateProvider::merge);
105                }
106                return destination;
107        }
108        private static List<ExchangeRate> merge(List<ExchangeRate> ratesA, List<ExchangeRate> ratesB) {
109                ratesA.addAll(ratesB);
110                ratesA.sort(COMPARATOR_EXCHANGE_BY_LOCAL_DATE.reversed());
111                return ratesA;
112        }
113}