Skip to content

Commit 2771a85

Browse files
committed
Merged changes.
2 parents 061779a + 340dac4 commit 2771a85

File tree

9 files changed

+544
-675
lines changed

9 files changed

+544
-675
lines changed

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@
1919
<parent>
2020
<groupId>org.javamoney</groupId>
2121
<artifactId>javamoney-parent</artifactId>
22-
<version>0.5</version>
22+
<version>1.0-RC2</version>
2323
</parent>
2424

2525
<artifactId>moneta</artifactId>
26-
<version>1.0-RC2</version>
26+
<version>1.0-SNAPSHOT</version>
2727
<packaging>jar</packaging>
2828

2929
<name>Moneta (JSR 354 RI)</name>
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
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.CurrencyConversionException;
35+
import javax.money.convert.ExchangeRate;
36+
import javax.money.convert.ProviderContext;
37+
import javax.money.convert.RateType;
38+
import javax.money.spi.Bootstrap;
39+
import javax.xml.parsers.SAXParser;
40+
import javax.xml.parsers.SAXParserFactory;
41+
42+
import org.javamoney.moneta.ExchangeRateBuilder;
43+
import org.javamoney.moneta.spi.AbstractRateProvider;
44+
import org.javamoney.moneta.spi.DefaultNumberValue;
45+
import org.javamoney.moneta.spi.LoaderService;
46+
import org.javamoney.moneta.spi.LoaderService.LoaderListener;
47+
48+
/**
49+
* Base to all Europe Central Bank implementation.
50+
* @author otaviojava
51+
*/
52+
abstract class AbstractECBCurrentRateProvider extends AbstractRateProvider implements
53+
LoaderListener {
54+
55+
static final String BASE_CURRENCY_CODE = "EUR";
56+
57+
/**
58+
* Base currency of the loaded rates is always EUR.
59+
*/
60+
public static final CurrencyUnit BASE_CURRENCY = MonetaryCurrencies.getCurrency(BASE_CURRENCY_CODE);
61+
62+
/**
63+
* Historic exchange rates, rate timestamp as UTC long.
64+
*/
65+
private final Map<Long, Map<String, ExchangeRate>> historicRates = new ConcurrentHashMap<>();
66+
/**
67+
* Parser factory.
68+
*/
69+
private SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
70+
71+
private Long recentKey;
72+
73+
public AbstractECBCurrentRateProvider(ProviderContext context) throws MalformedURLException{
74+
super(context);
75+
saxParserFactory.setNamespaceAware(false);
76+
saxParserFactory.setValidating(false);
77+
LoaderService loader = Bootstrap.getService(LoaderService.class);
78+
loader.addLoaderListener(this, getDataId());
79+
loader.loadDataAsync(getDataId());
80+
}
81+
82+
public abstract String getDataId();
83+
84+
@Override
85+
public void newDataLoaded(String data, InputStream is){
86+
final int oldSize = this.historicRates.size();
87+
try{
88+
SAXParser parser = saxParserFactory.newSAXParser();
89+
parser.parse(is, new RateReadingHandler(historicRates, getProviderContext()));
90+
recentKey = null;
91+
}
92+
catch(Exception e){
93+
LOGGER.log(Level.FINEST, "Error during data load.", e);
94+
}
95+
int newSize = this.historicRates.size();
96+
LOGGER.info("Loaded " + getDataId() + " exchange rates for days:" + (newSize - oldSize));
97+
}
98+
99+
100+
@Override
101+
public ExchangeRate getExchangeRate(ConversionQuery query){
102+
Objects.requireNonNull(query);
103+
if(historicRates.isEmpty()){
104+
return null;
105+
}
106+
107+
Long timeStampMillis = getMillisSeconds(query);
108+
ExchangeRateBuilder builder = getBuilder(query, timeStampMillis);
109+
110+
111+
Map<String, ExchangeRate> targets = this.historicRates
112+
.get(timeStampMillis);
113+
if(Objects.isNull(targets)){
114+
return null;
115+
}
116+
ExchangeRate sourceRate = targets.get(query.getBaseCurrency()
117+
.getCurrencyCode());
118+
ExchangeRate target = targets
119+
.get(query.getCurrency().getCurrencyCode());
120+
return createExchangeRate(query, builder, sourceRate, target);
121+
}
122+
123+
private ExchangeRate createExchangeRate(ConversionQuery query,
124+
ExchangeRateBuilder builder, ExchangeRate sourceRate,
125+
ExchangeRate target) {
126+
127+
if(areBothBaseCurrencies(query)){
128+
builder.setFactor(DefaultNumberValue.ONE);
129+
return builder.build();
130+
} else if(BASE_CURRENCY_CODE.equals(query.getCurrency().getCurrencyCode())){
131+
if(Objects.isNull(sourceRate)){
132+
return null;
133+
}
134+
return reverse(sourceRate);
135+
} else if (BASE_CURRENCY_CODE.equals(query.getBaseCurrency()
136+
.getCurrencyCode())) {
137+
return target;
138+
} else{
139+
// Get Conversion base as derived rate: base -> EUR -> term
140+
ExchangeRate rate1 = getExchangeRate(
141+
query.toBuilder().setTermCurrency(MonetaryCurrencies.getCurrency(BASE_CURRENCY_CODE)).build());
142+
ExchangeRate rate2 = getExchangeRate(
143+
query.toBuilder().setBaseCurrency(MonetaryCurrencies.getCurrency(BASE_CURRENCY_CODE))
144+
.setTermCurrency(query.getCurrency()).build());
145+
if(Objects.nonNull(rate1) && Objects.nonNull(rate2)){
146+
builder.setFactor(multiply(rate1.getFactor(), rate2.getFactor()));
147+
builder.setRateChain(rate1, rate2);
148+
return builder.build();
149+
}
150+
throw new CurrencyConversionException(query.getBaseCurrency(),
151+
query.getCurrency(), sourceRate.getConversionContext());
152+
}
153+
}
154+
155+
private boolean areBothBaseCurrencies(ConversionQuery query) {
156+
return BASE_CURRENCY_CODE.equals(query.getBaseCurrency().getCurrencyCode()) &&
157+
BASE_CURRENCY_CODE.equals(query.getCurrency().getCurrencyCode());
158+
}
159+
160+
private Long getMillisSeconds(ConversionQuery query) {
161+
if (Objects.nonNull(query.getTimestamp())) {
162+
LocalDate timeStamp = query.getTimestamp().toLocalDate();
163+
164+
Date date = Date.from(timeStamp.atStartOfDay()
165+
.atZone(ZoneId.systemDefault()).toInstant());
166+
Long timeStampMillis = date.getTime();
167+
return timeStampMillis;
168+
} else {
169+
return getRecentKey();
170+
}
171+
}
172+
173+
private Long getRecentKey() {
174+
if(Objects.isNull(recentKey)) {
175+
Comparator<Long> reversed = Comparator.<Long>naturalOrder().reversed();
176+
recentKey = historicRates.keySet().stream().sorted(reversed).findFirst().get();
177+
}
178+
return recentKey;
179+
}
180+
181+
private ExchangeRateBuilder getBuilder(ConversionQuery query,
182+
Long timeStampMillis) {
183+
ExchangeRateBuilder builder = new ExchangeRateBuilder(
184+
ConversionContextBuilder.create(getProviderContext(), RateType.HISTORIC)
185+
.setTimestampMillis(timeStampMillis).build());
186+
builder.setBase(query.getBaseCurrency());
187+
builder.setTerm(query.getCurrency());
188+
return builder;
189+
}
190+
191+
private ExchangeRate reverse(ExchangeRate rate){
192+
if(Objects.isNull(rate)){
193+
throw new IllegalArgumentException("Rate null is not reversable.");
194+
}
195+
return new ExchangeRateBuilder(rate).setRate(rate).setBase(rate.getCurrency()).setTerm(rate.getBaseCurrency())
196+
.setFactor(divide(DefaultNumberValue.ONE, rate.getFactor(), MathContext.DECIMAL64)).build();
197+
}
198+
199+
}

0 commit comments

Comments
 (0)