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.convert.imf; 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.convert.imf.IMFRateReadingHandler.RateIMFResult; 040import org.javamoney.moneta.spi.loader.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 Fund") 055 .set("days", 0) 056 .set("User-Agent", "Chrome/51.0.2704.103") 057 .build(); 058 059 060 private final List<YearMonth> cachedHistoric = new ArrayList<>(); 061 public IMFHistoricRateProvider() { 062 super(CONTEXT); 063 LoaderService loader = Bootstrap.getService(LoaderService.class); 064 loader.addLoaderListener(this, DATA_ID); 065 try { 066 loader.loadData(DATA_ID); 067 } catch (IOException e) { 068 LOG.log(Level.WARNING, "Error loading initial data from IMF provider...", e); 069 } 070 } 071 072 @Override 073 public ExchangeRate getExchangeRate(ConversionQuery conversionQuery) { 074 LocalDate[] times = getQueryDates(conversionQuery); 075 if(Objects.isNull(times)) { 076 return super.getExchangeRate(conversionQuery); 077 } 078 079 for (YearMonth yearMonth : Stream.of(times).map(YearMonth::from) 080 .collect(Collectors.toSet())) { 081 082 if(!cachedHistoric.contains(yearMonth)){ 083 Map<IMFHistoricalType, InputStream> resources = IMFRemoteSearch.INSTANCE.getResources(yearMonth, 084 getContext().get("User-Agent", String.class)); 085 loadFromRemote(resources); 086 cachedHistoric.add(yearMonth); 087 } 088 } 089 return super.getExchangeRate(conversionQuery); 090 } 091 092 private void loadFromRemote(Map<IMFHistoricalType, InputStream> resources) { 093 try { 094 for(IMFHistoricalType type: resources.keySet()) { 095 RateIMFResult result = handler.read(resources.get(type)); 096 combine(result.getSdrToCurrency(), this.sdrToCurrency); 097 combine(result.getCurrencyToSdr(), this.currencyToSdr); 098 } 099 } catch (Exception e) { 100 LOG.log(Level.SEVERE, "Error", e); 101 } 102 } 103 104 private Map<CurrencyUnit, List<ExchangeRate>> combine(Map<CurrencyUnit, List<ExchangeRate>> source, Map<CurrencyUnit, List<ExchangeRate>> destination) { 105 for(CurrencyUnit currency: source.keySet()) { 106 destination.putIfAbsent(currency, new ArrayList<>()); 107 List<ExchangeRate> rates = source.get(currency); 108 destination.merge(currency, rates, IMFHistoricRateProvider::merge); 109 } 110 return destination; 111 } 112 private static List<ExchangeRate> merge(List<ExchangeRate> ratesA, List<ExchangeRate> ratesB) { 113 ratesA.addAll(ratesB); 114 ratesA.sort(COMPARATOR_EXCHANGE_BY_LOCAL_DATE.reversed()); 115 return ratesA; 116 } 117}