Skip to content

Commit c5ffe00

Browse files
committed
JAVAMONEY-97: Merged singletons into single Monetary singleton.
1 parent 13495f7 commit c5ffe00

27 files changed

+1004
-1035
lines changed

src/main/JEP/MoneyAndCurrency.md

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,7 @@ Summarizing the JSR contains the following main artifacts:
7171

7272
* Interfaces for amounts (``MonetaryAmount``), currencies (``CurrencyUnit``), rounding (``MonetaryRounding``),
7373
conversion (``CurrencyConverter, ExchangeRateprovider``) and formatting (``MonetaryAmountFormat``).
74-
* Corresponding singleton accessors (``MonetaryCurrencies, MonetaryAmounts, MonetaryRoundings,
75-
MonetaryConversions, MonetaryFormats``).
74+
* Corresponding singleton accessors (``Monetary, MonetaryConversions, MonetaryFormats``).
7675
* The singleton accessors are backed up by SPI interfaces (``MonetaryCurrenciesSingletonSpi,
7776
MonetaryAmountsSingletonSpi, MonetaryRoundingsSingletonSpi, MonetaryConversionSingletonSpi,
7877
MonetaryFormatsSingletonSpi``).
@@ -116,13 +115,13 @@ To give some first impressions of the API the following sections give some examp
116115

117116
The class ``org.javamoney.moneta.Money`` implements ``MonetaryAmount`` using ``java.math.BigDecimal`` internally:
118117

119-
MonetaryAmountFactory<Money> fact = MonetaryAmounts.getAmountFactory(Money.class);
118+
MonetaryAmountFactory<Money> fact = Monetary.getAmountFactory(Money.class);
120119
Money m = fact.setCurrency("USD").setNumber(200.50).create();
121120

122121
Also a generic MonetaryAmount instance can be accessed using a raw factory (hereby it depends on the
123122
configured default amount factory, which effective type instance is returned):
124123

125-
MonetaryAmount amt = MonetaryAmounts.getDefaultAmountFactory()
124+
MonetaryAmount amt = Monetary.getDefaultAmountFactory()
126125
.setCurrency("USD").setNumber(200.50).create();
127126
128127
Still we can evaluate the effective amount’s type effectively:
@@ -167,7 +166,7 @@ effectively used (by default ``Money`` uses ``MathContext.DECIMAL64``).:
167166

168167
#### Example Configuring a ``MonetaryAmountFactory``, using the RI class ``Money`` as example.
169168

170-
MonetaryAmountFactory<Money> fact = MonetaryAmounts.getAmountFactory(
169+
MonetaryAmountFactory<Money> fact = Monetary.getAmountFactory(
171170
MonetaryAmountFactoryQueryBuilder.of(Money.class)
172171
.set(new MathContext(250, RoundingMode.HALF_EVEN)).build()
173172
);
@@ -222,7 +221,7 @@ the possible amounts to be modeled are limited to an overall precision of 18 and
222221
Beside that the overall handling of ``FastMoney`` is similar to ``Money``. So we could rewrite the former
223222
example by just replacing ``FastMoney`` with ``Money``:
224223

225-
MonetaryAmountFactory<FastMoney> fact = MonetaryAmounts.getAmountFactory(FastMoney.class);
224+
MonetaryAmountFactory<FastMoney> fact = Monetary.getAmountFactory(FastMoney.class);
226225
// Creates an instance of Money with the given MathContext
227226
MonetaryAmount m1 = fact.setCurrency("CHF").setNumber(250.34).create();
228227
FastMoney m2 = fact.setCurrency("CHF").setNumber(250.34).create();
@@ -269,7 +268,7 @@ As an alternate it is also possible to define a ``MonetaryOperator``, which can
269268
}
270269

271270
public <T extends MonetaryAmount> T getTotal(Class<T> amountType){
272-
return MonetaryAmounts.getAmountFactory(amountType).with(total).create();
271+
return Monetary.getAmountFactory(amountType).with(total).create();
273272
}
274273
}
275274

src/main/asciidoc/JavaMoneySpecification.adoc

Lines changed: 66 additions & 63 deletions
Large diffs are not rendered by default.
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* CREDIT SUISSE IS WILLING TO LICENSE THIS SPECIFICATION TO YOU ONLY UPON THE
3+
* CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS AGREEMENT.
4+
* PLEASE READ THE TERMS AND CONDITIONS OF THIS AGREEMENT CAREFULLY. BY
5+
* DOWNLOADING THIS SPECIFICATION, YOU ACCEPT THE TERMS AND CONDITIONS OF THE
6+
* AGREEMENT. IF YOU ARE NOT WILLING TO BE BOUND BY IT, SELECT THE "DECLINE"
7+
* BUTTON AT THE BOTTOM OF THIS PAGE.
8+
*
9+
* Specification: JSR-354 Money and Currency API ("Specification")
10+
*
11+
* Copyright (c) 2012-2013, Credit Suisse All rights reserved.
12+
*/
13+
package javax.money;
14+
15+
import javax.money.spi.Bootstrap;
16+
import javax.money.spi.CurrencyProviderSpi;
17+
import javax.money.spi.MonetaryCurrenciesSingletonSpi;
18+
import java.util.ArrayList;
19+
import java.util.Collections;
20+
import java.util.HashSet;
21+
import java.util.List;
22+
import java.util.Set;
23+
import java.util.logging.Level;
24+
import java.util.logging.Logger;
25+
26+
/**
27+
* Factory singleton for {@link CurrencyUnit} instances as provided by the
28+
* different registered {@link javax.money.spi.CurrencyProviderSpi} instances.
29+
* <p/>
30+
* This class is thread safe.
31+
*
32+
* @author Anatole Tresch
33+
* @version 0.8
34+
*/
35+
final class DefaultMonetaryCurrenciesSingletonSpi implements MonetaryCurrenciesSingletonSpi {
36+
37+
@Override
38+
public Set<CurrencyUnit> getCurrencies(CurrencyQuery query) {
39+
Set<CurrencyUnit> result = new HashSet<>();
40+
for (CurrencyProviderSpi spi : Bootstrap.getServices(CurrencyProviderSpi.class)) {
41+
try {
42+
result.addAll(spi.getCurrencies(query));
43+
} catch (Exception e) {
44+
Logger.getLogger(DefaultMonetaryCurrenciesSingletonSpi.class.getName())
45+
.log(Level.SEVERE, "Error loading currency provider names for " + spi.getClass().getName(),
46+
e);
47+
}
48+
}
49+
return result;
50+
}
51+
52+
/**
53+
* This default implementation simply returns all providers defined in arbitrary order.
54+
*
55+
* @return the default provider chain, never null.
56+
*/
57+
@Override
58+
public List<String> getDefaultProviderChain() {
59+
List<String> list = new ArrayList<>();
60+
list.addAll(getProviderNames());
61+
Collections.sort(list);
62+
return list;
63+
}
64+
65+
/**
66+
* Get the names of the currently loaded providers.
67+
*
68+
* @return the names of the currently loaded providers, never null.
69+
*/
70+
@Override
71+
public Set<String> getProviderNames() {
72+
Set<String> result = new HashSet<>();
73+
for (CurrencyProviderSpi spi : Bootstrap.getServices(CurrencyProviderSpi.class)) {
74+
try {
75+
result.add(spi.getProviderName());
76+
} catch (Exception e) {
77+
Logger.getLogger(DefaultMonetaryCurrenciesSingletonSpi.class.getName())
78+
.log(Level.SEVERE, "Error loading currency provider names for " + spi.getClass().getName(),
79+
e);
80+
}
81+
}
82+
return result;
83+
}
84+
85+
}
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/*
2+
* CREDIT SUISSE IS WILLING TO LICENSE THIS SPECIFICATION TO YOU ONLY UPON THE
3+
* CONDITION THAT YOU ACCEPT ALL OF THE TERMS CONTAINED IN THIS AGREEMENT.
4+
* PLEASE READ THE TERMS AND CONDITIONS OF THIS AGREEMENT CAREFULLY. BY
5+
* DOWNLOADING THIS SPECIFICATION, YOU ACCEPT THE TERMS AND CONDITIONS OF THE
6+
* AGREEMENT. IF YOU ARE NOT WILLING TO BE BOUND BY IT, SELECT THE "DECLINE"
7+
* BUTTON AT THE BOTTOM OF THIS PAGE.
8+
*
9+
* Specification: JSR-354 Money and Currency API ("Specification")
10+
*
11+
* Copyright (c) 2012-2013, Credit Suisse All rights reserved.
12+
*/
13+
package javax.money;
14+
15+
import javax.money.spi.Bootstrap;
16+
import javax.money.spi.MonetaryRoundingsSingletonSpi;
17+
import javax.money.spi.RoundingProviderSpi;
18+
import java.io.Serializable;
19+
import java.util.ArrayList;
20+
import java.util.Collection;
21+
import java.util.Collections;
22+
import java.util.HashSet;
23+
import java.util.List;
24+
import java.util.Set;
25+
import java.util.logging.Level;
26+
import java.util.logging.Logger;
27+
28+
/**
29+
* This class models the accessor for rounding instances, modeled as
30+
* {@link javax.money.MonetaryOperator}.
31+
* <p/>
32+
* This class is thread-safe.
33+
*
34+
* @author Anatole Tresch
35+
* @author Werner Keil
36+
*/
37+
final class DefaultMonetaryRoundingsSingletonSpi implements MonetaryRoundingsSingletonSpi {
38+
39+
/**
40+
* An adaptive rounding instance that transparently looks up the correct
41+
* rounding.
42+
*/
43+
private static final MonetaryRounding DEFAULT_ROUNDING = new DefaultCurrencyRounding();
44+
45+
/**
46+
* Creates an rounding instance using {@link java.math.RoundingMode#UP} rounding.
47+
*
48+
* @return the corresponding {@link MonetaryOperator} implementing the
49+
* rounding.
50+
* @throws MonetaryException if no such rounding could be evaluated.
51+
*/
52+
public RoundingQueryBuilder createRoundingQueryBuilder() {
53+
throw new IllegalStateException("No MonetaryRoundingsSingletonSpi registered.");
54+
}
55+
56+
/**
57+
* Get the default rounding, which delegates rounding dynamically depending on the current currency.
58+
*
59+
* @return the default rounding, never null.
60+
*/
61+
public MonetaryRounding getDefaultRounding() {
62+
return DEFAULT_ROUNDING;
63+
}
64+
65+
66+
/**
67+
* Query all roundings matching the given {@link RoundingQuery}.
68+
*
69+
* @param query the rounding query, not null.
70+
* @return the collection found, not null.
71+
*/
72+
@Override
73+
public Collection<MonetaryRounding> getRoundings(RoundingQuery query) {
74+
List<MonetaryRounding> result = new ArrayList<>();
75+
Collection<String> providerNames = query.getProviderNames();
76+
if (providerNames == null || providerNames.isEmpty()) {
77+
providerNames = getDefaultProviderChain();
78+
}
79+
for (String providerName : providerNames) {
80+
Bootstrap.getServices(RoundingProviderSpi.class).stream()
81+
.filter(prov -> providerName.equals(prov.getProviderName())).forEach(prov -> {
82+
try {
83+
MonetaryRounding r = prov.getRounding(query);
84+
if (r != null) {
85+
result.add(r);
86+
}
87+
} catch (Exception e) {
88+
Logger.getLogger(DefaultMonetaryRoundingsSingletonSpi.class.getName())
89+
.log(Level.SEVERE, "Error loading RoundingProviderSpi from provider: " + prov, e);
90+
}
91+
});
92+
}
93+
return result;
94+
}
95+
96+
/**
97+
* Get the names of all current registered providers.
98+
*
99+
* @return the names of all current registered providers, never null.
100+
*/
101+
@Override
102+
public Set<String> getProviderNames() {
103+
Set<String> result = new HashSet<>();
104+
for (RoundingProviderSpi prov : Bootstrap.getServices(RoundingProviderSpi.class)) {
105+
try {
106+
result.add(prov.getProviderName());
107+
} catch (Exception e) {
108+
Logger.getLogger(Monetary.class.getName())
109+
.log(Level.SEVERE, "Error loading RoundingProviderSpi from provider: " + prov, e);
110+
}
111+
}
112+
return result;
113+
}
114+
115+
/**
116+
* Get the default providers list to be used.
117+
*
118+
* @return the default provider list and ordering, not null.
119+
*/
120+
@Override
121+
public List<String> getDefaultProviderChain() {
122+
List<String> result = new ArrayList<>();
123+
result.addAll(Monetary.getRoundingProviderNames());
124+
Collections.sort(result);
125+
return result;
126+
}
127+
128+
/**
129+
* Allows to access the identifiers of the current defined roundings.
130+
*
131+
* @param providers the providers and ordering to be used. By default providers and ordering as defined in
132+
* #getDefaultProviders is used, not null.
133+
* @return the set of custom rounding ids, never {@code null}.
134+
*/
135+
public Set<String> getRoundingNames(String... providers) {
136+
Set<String> result = new HashSet<>();
137+
String[] providerNames = providers;
138+
if (providerNames.length == 0) {
139+
providerNames = Monetary.getDefaultRoundingProviderChain().toArray(new String[Monetary.getDefaultRoundingProviderChain().size()]);
140+
}
141+
for (String providerName : providerNames) {
142+
for (RoundingProviderSpi prov : Bootstrap.getServices(RoundingProviderSpi.class)) {
143+
try {
144+
if (prov.getProviderName().equals(providerName) || prov.getProviderName().matches(providerName)) {
145+
result.addAll(prov.getRoundingNames());
146+
}
147+
} catch (Exception e) {
148+
Logger.getLogger(DefaultMonetaryRoundingsSingletonSpi.class.getName())
149+
.log(Level.SEVERE, "Error loading RoundingProviderSpi from provider: " + prov, e);
150+
}
151+
}
152+
}
153+
return result;
154+
}
155+
156+
/**
157+
* Default Rounding that rounds a {@link MonetaryAmount} based on the
158+
* amount's {@link CurrencyUnit}.
159+
*
160+
* @author Anatole Tresch
161+
*/
162+
private static final class DefaultCurrencyRounding implements MonetaryRounding, Serializable {
163+
164+
private static final RoundingContext ROUNDING_CONTEXT = RoundingContextBuilder.of("default", "default").build();
165+
166+
@Override
167+
public MonetaryAmount apply(MonetaryAmount amount) {
168+
MonetaryRounding r = Monetary.getRounding(amount.getCurrency());
169+
return r.apply(amount);
170+
}
171+
172+
@Override
173+
public RoundingContext getRoundingContext() {
174+
return ROUNDING_CONTEXT;
175+
}
176+
}
177+
178+
}

0 commit comments

Comments
 (0)