Skip to content

Commit a390b8b

Browse files
author
otaviojava
committed
Unify ECB classes in AbstractECBCurrentRateProvider.java
1 parent 685744e commit a390b8b

File tree

5 files changed

+243
-591
lines changed

5 files changed

+243
-591
lines changed
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
/**
2+
* Copyright (c) 2012, 2014, Credit Suisse (Anatole Tresch), Werner Keil and others by the @author tag.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
package org.javamoney.moneta.convert.internal;
17+
18+
import java.io.InputStream;
19+
import java.math.MathContext;
20+
import java.net.MalformedURLException;
21+
import java.time.LocalDate;
22+
import java.time.ZoneId;
23+
import java.util.Comparator;
24+
import java.util.Date;
25+
import java.util.Map;
26+
import java.util.Objects;
27+
import java.util.concurrent.ConcurrentHashMap;
28+
import java.util.logging.Level;
29+
30+
import javax.money.CurrencyUnit;
31+
import javax.money.MonetaryCurrencies;
32+
import javax.money.convert.ConversionContextBuilder;
33+
import javax.money.convert.ConversionQuery;
34+
import javax.money.convert.ExchangeRate;
35+
import javax.money.convert.ProviderContext;
36+
import javax.money.convert.RateType;
37+
import javax.money.spi.Bootstrap;
38+
import javax.xml.parsers.SAXParser;
39+
import javax.xml.parsers.SAXParserFactory;
40+
41+
import org.javamoney.moneta.ExchangeRateBuilder;
42+
import org.javamoney.moneta.spi.AbstractRateProvider;
43+
import org.javamoney.moneta.spi.DefaultNumberValue;
44+
import org.javamoney.moneta.spi.LoaderService;
45+
import org.javamoney.moneta.spi.LoaderService.LoaderListener;
46+
47+
/**
48+
* Base to all Europe Central Bank implementation.
49+
* @author otaviojava
50+
*/
51+
abstract class AbstractECBCurrentRateProvider extends AbstractRateProvider implements
52+
LoaderListener {
53+
54+
static final String BASE_CURRENCY_CODE = "EUR";
55+
56+
/**
57+
* Base currency of the loaded rates is always EUR.
58+
*/
59+
public static final CurrencyUnit BASE_CURRENCY = MonetaryCurrencies.getCurrency(BASE_CURRENCY_CODE);
60+
61+
/**
62+
* Historic exchange rates, rate timestamp as UTC long.
63+
*/
64+
private final Map<Long, Map<String, ExchangeRate>> historicRates = new ConcurrentHashMap<>();
65+
/**
66+
* Parser factory.
67+
*/
68+
private SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
69+
70+
private Long recentKey;
71+
72+
public AbstractECBCurrentRateProvider(ProviderContext context) throws MalformedURLException{
73+
super(context);
74+
saxParserFactory.setNamespaceAware(false);
75+
saxParserFactory.setValidating(false);
76+
LoaderService loader = Bootstrap.getService(LoaderService.class);
77+
loader.addLoaderListener(this, getDataId());
78+
loader.loadDataAsync(getDataId());
79+
}
80+
81+
public abstract String getDataId();
82+
83+
@Override
84+
public void newDataLoaded(String data, InputStream is){
85+
final int oldSize = this.historicRates.size();
86+
try{
87+
SAXParser parser = saxParserFactory.newSAXParser();
88+
parser.parse(is, new RateReadingHandler(historicRates, getProviderContext()));
89+
recentKey = null;
90+
}
91+
catch(Exception e){
92+
LOGGER.log(Level.FINEST, "Error during data load.", e);
93+
}
94+
int newSize = this.historicRates.size();
95+
LOGGER.info("Loaded " + getDataId() + " exchange rates for days:" + (newSize - oldSize));
96+
}
97+
98+
99+
@Override
100+
public ExchangeRate getExchangeRate(ConversionQuery query){
101+
if(historicRates.isEmpty()){
102+
return null;
103+
}
104+
105+
Long timeStampMillis = getMillisSeconds(query);
106+
ExchangeRateBuilder builder = getBuilder(query, timeStampMillis);
107+
108+
109+
Map<String, ExchangeRate> targets = this.historicRates
110+
.get(timeStampMillis);
111+
if(Objects.isNull(targets)){
112+
return null;
113+
}
114+
ExchangeRate sourceRate = targets.get(query.getBaseCurrency()
115+
.getCurrencyCode());
116+
ExchangeRate target = targets
117+
.get(query.getCurrency().getCurrencyCode());
118+
return createExchangeRate(query, builder, sourceRate, target);
119+
}
120+
121+
private ExchangeRate createExchangeRate(ConversionQuery query,
122+
ExchangeRateBuilder builder, ExchangeRate sourceRate,
123+
ExchangeRate target) {
124+
125+
if(areBothBaseCurrencies(query)){
126+
builder.setFactor(DefaultNumberValue.ONE);
127+
return builder.build();
128+
} else if(BASE_CURRENCY_CODE.equals(query.getCurrency().getCurrencyCode())){
129+
if(Objects.isNull(sourceRate)){
130+
return null;
131+
}
132+
return reverse(sourceRate);
133+
} else if (BASE_CURRENCY_CODE.equals(query.getBaseCurrency()
134+
.getCurrencyCode())) {
135+
return target;
136+
} else{
137+
// Get Conversion base as derived rate: base -> EUR -> term
138+
ExchangeRate rate1 = getExchangeRate(
139+
query.toBuilder().setTermCurrency(MonetaryCurrencies.getCurrency(BASE_CURRENCY_CODE)).build());
140+
ExchangeRate rate2 = getExchangeRate(
141+
query.toBuilder().setBaseCurrency(MonetaryCurrencies.getCurrency(BASE_CURRENCY_CODE))
142+
.setTermCurrency(query.getCurrency()).build());
143+
if(Objects.nonNull(rate1) || Objects.nonNull(rate2)){
144+
builder.setFactor(multiply(rate1.getFactor(), rate2.getFactor()));
145+
builder.setRateChain(rate1, rate2);
146+
return builder.build();
147+
}
148+
return null;
149+
}
150+
}
151+
152+
private boolean areBothBaseCurrencies(ConversionQuery query) {
153+
return BASE_CURRENCY_CODE.equals(query.getBaseCurrency().getCurrencyCode()) &&
154+
BASE_CURRENCY_CODE.equals(query.getCurrency().getCurrencyCode());
155+
}
156+
157+
private Long getMillisSeconds(ConversionQuery query) {
158+
if (Objects.nonNull(query.getTimestamp())) {
159+
LocalDate timeStamp = query.getTimestamp().toLocalDate();
160+
161+
Date date = Date.from(timeStamp.atStartOfDay()
162+
.atZone(ZoneId.systemDefault()).toInstant());
163+
Long timeStampMillis = date.getTime();
164+
return timeStampMillis;
165+
} else {
166+
return getRecentKey();
167+
}
168+
}
169+
170+
private Long getRecentKey() {
171+
if(Objects.isNull(recentKey)) {
172+
Comparator<Long> reversed = Comparator.<Long>naturalOrder().reversed();
173+
recentKey = historicRates.keySet().stream().sorted(reversed).findFirst().get();
174+
}
175+
return recentKey;
176+
}
177+
178+
private ExchangeRateBuilder getBuilder(ConversionQuery query,
179+
Long timeStampMillis) {
180+
ExchangeRateBuilder builder = new ExchangeRateBuilder(
181+
ConversionContextBuilder.create(getProviderContext(), RateType.HISTORIC)
182+
.setTimestampMillis(timeStampMillis).build());
183+
builder.setBase(query.getBaseCurrency());
184+
builder.setTerm(query.getCurrency());
185+
return builder;
186+
}
187+
188+
private ExchangeRate reverse(ExchangeRate rate){
189+
if(Objects.isNull(rate)){
190+
throw new IllegalArgumentException("Rate null is not reversable.");
191+
}
192+
return new ExchangeRateBuilder(rate).setRate(rate).setBase(rate.getCurrency()).setTerm(rate.getBaseCurrency())
193+
.setFactor(divide(DefaultNumberValue.ONE, rate.getFactor(), MathContext.DECIMAL64)).build();
194+
}
195+
196+
}

0 commit comments

Comments
 (0)