org.javamoney.moneta.convert.imf.IMFRateReadingHandler Maven / Gradle / Ivy
The newest version!
/*
Copyright (c) 2012, 2015, Credit Suisse (Anatole Tresch), Werner Keil and others by the @author tag.
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy of
the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under
the License.
*/
package org.javamoney.moneta.convert.imf;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.ParseException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.logging.Logger;
import javax.money.CurrencyUnit;
import javax.money.convert.ConversionContextBuilder;
import javax.money.convert.ExchangeRate;
import javax.money.convert.ProviderContext;
import javax.money.convert.RateType;
import org.javamoney.moneta.convert.ExchangeRateBuilder;
import org.javamoney.moneta.spi.DefaultNumberValue;
class IMFRateReadingHandler {
private static final Logger LOG = Logger
.getLogger(IMFRateReadingHandler.class.getName());
private final Map currenciesByName;
private final ProviderContext context;
IMFRateReadingHandler(Map currenciesByName, ProviderContext context) {
this.currenciesByName = currenciesByName;
this.context = context;
}
RateIMFResult read(InputStream inputStream) throws IOException,
ParseException {
Map> currencyToSdr = new HashMap<>();
Map> sdrToCurrency = new HashMap<>();
BufferedReader reader = new BufferedReader(new InputStreamReader(
inputStream));
String line = reader.readLine();
boolean isCurrencyToSdr = true;
// SDRs per Currency unit (2)
//
// Currency January 31, 2013 January 30, 2013 January 29, 2013
// January 28, 2013 January 25, 2013
// Euro 0.8791080000 0.8789170000 0.8742470000 0.8752180000
// 0.8768020000
// Currency units per SDR(3)
//
// Currency January 31, 2013 January 30, 2013 January 29, 2013
// January 28, 2013 January 25, 2013
// Euro 1.137520 1.137760 1.143840 1.142570 1.140510
List timestamps = null;
while (Objects.nonNull(line)) {
if (line.trim().isEmpty()) {
line = reader.readLine();
continue;
}
if(line.contains("Request Rejected")){
throw new IOException("Request has been rejected by IMF server.");
}
if (line.startsWith("SDRs per Currency unit")) {
isCurrencyToSdr = false;
line = reader.readLine();
continue;
} else if (line.startsWith("Currency units per SDR")) {
isCurrencyToSdr = true;
line = reader.readLine();
continue;
} else if (line.startsWith("Currency")) {
timestamps = readTimestamps(line);
line = reader.readLine();
continue;
}
String[] parts = line.split("\\t");
CurrencyUnit currency = currenciesByName.get(parts[0].toLowerCase(Locale.ENGLISH));
if (Objects.isNull(currency)) {
LOG.finest(() -> "Uninterpretable data from IMF data feed: "
+ parts[0]);
line = reader.readLine();
continue;
}
saveExchangeRate(currencyToSdr, sdrToCurrency, isCurrencyToSdr,
timestamps, currency, parseValues(parts));
line = reader.readLine();
}
// Cast is save, since contained DefaultExchangeRate is Comparable!
sortResult(currencyToSdr, sdrToCurrency);
return new RateIMFResult(currencyToSdr, sdrToCurrency);
}
@SuppressWarnings("unchecked")
private void sortResult(
Map> newCurrencyToSdr,
Map> newSdrToCurrency) {
newSdrToCurrency.values().forEach(
(c) -> Collections.sort(List.class.cast(c)));
newCurrencyToSdr.values().forEach(
(c) -> Collections.sort(List.class.cast(c)));
newSdrToCurrency.forEach((c, l) -> LOG.finest(() -> "SDR -> "
+ c.getCurrencyCode() + ": " + l));
newCurrencyToSdr.forEach((c, l) -> LOG.finest(() -> c
.getCurrencyCode() + " -> SDR: " + l));
}
private List readTimestamps(String line) {
// Currency May 01, 2013 April 30, 2013 April 29, 2013 April 26, 2013
// April 25, 2013
DateTimeFormatter sdf = DateTimeFormatter.ofPattern("MMMM dd, uuuu")
.withLocale(Locale.ENGLISH);
String[] parts = line.split("\\\t");
List dates = new ArrayList<>(parts.length);
for (int i = 1; i < parts.length; i++) {
dates.add(LocalDate.parse(parts[i], sdf));
}
return dates;
}
private void saveExchangeRate(
Map> currencyToSdr,
Map> sdrToCurrency,
boolean isCurrencyToSdr, List timestamps,
CurrencyUnit currency, Double[] values) {
for (int index = 0; index < values.length; index++) {
if (Objects.isNull(values[index])
|| Objects.isNull(getLocalDateFromTS(timestamps, index))) {
continue;
}
LocalDate fromTS = getLocalDateFromTS(timestamps, index);
RateType rateType = getRateType(fromTS);
if (isCurrencyToSdr) {
ExchangeRate rate = new ExchangeRateBuilder(
ConversionContextBuilder.create(context, rateType)
.set(fromTS).build()).setBase(currency)
.setTerm(IMFAbstractRateProvider.SDR)
.setFactor(new DefaultNumberValue(1D / values[index]))
.build();
List rates = currencyToSdr.computeIfAbsent(
currency, c -> new ArrayList<>(5));
rates.add(rate);
} else {
ExchangeRate rate = new ExchangeRateBuilder(
ConversionContextBuilder.create(context, rateType)
.set(fromTS).build()).setBase(IMFAbstractRateProvider.SDR)
.setTerm(currency)
.setFactor(DefaultNumberValue.of(1D / values[index]))
.build();
List rates = sdrToCurrency.computeIfAbsent(
currency, (c) -> new ArrayList<>(5));
rates.add(rate);
}
}
}
private Double[] parseValues(String[] parts) throws ParseException {
ArrayList result = new ArrayList<>();
int index = 0;
for (String part : parts) {
if(index == 0) {
index++;
continue;
}
if (part.isEmpty() || "NA".equals(part)) {
index++;
result.add(null);
continue;
}
index++;
result.add(Double.valueOf(part.trim().replace(",", "")));
}
return result.toArray(new Double[parts.length - 1]);
}
private LocalDate getLocalDateFromTS(List timestamps, int index) {
return timestamps != null ? timestamps.get(index) : null;
}
private RateType getRateType(LocalDate fromTS) {
RateType rateType = RateType.HISTORIC;
if (fromTS.equals(LocalDate.now())) {
rateType = RateType.DEFERRED;
}
return rateType;
}
class RateIMFResult {
private final Map> currencyToSdr;
private final Map> sdrToCurrency;
RateIMFResult(Map> currencyToSdr,
Map> sdrToCurrency) {
this.currencyToSdr = currencyToSdr;
this.sdrToCurrency = sdrToCurrency;
}
public Map> getCurrencyToSdr() {
return currencyToSdr;
}
public Map> getSdrToCurrency() {
return sdrToCurrency;
}
}
@Override
public String toString() {
return IMFRateReadingHandler.class.getName() + '{' + " currenciesByName: " + currenciesByName + ',' +
" context: " + context + '}';
}
}