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}