Skip to content

Commit 3baaf02

Browse files
committed
Implemented special handling of Double.NaN, Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY.
Fixed bugs preventing reverse parsing of default monetary amount formats that are not separated by whitespaces, e.g. 'CHF123.45'.
1 parent 58b3a78 commit 3baaf02

File tree

7 files changed

+140
-18
lines changed

7 files changed

+140
-18
lines changed

src/main/java/org/javamoney/moneta/FastMoney.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,9 @@ private void checkAmountParameter(MonetaryAmount amount) {
322322
*/
323323
@Override
324324
public FastMoney divide(Number divisor) {
325+
if (Money.isInfinityAndNotNaN(divisor)) {
326+
return new FastMoney(0L, getCurrency());
327+
}
325328
checkNumber(divisor);
326329
if (isOne(divisor)) {
327330
return this;
@@ -335,6 +338,10 @@ public FastMoney divide(Number divisor) {
335338
*/
336339
@Override
337340
public FastMoney[] divideAndRemainder(Number divisor) {
341+
if (Money.isInfinityAndNotNaN(divisor)) {
342+
FastMoney zero = new FastMoney(0L, getCurrency());
343+
return new FastMoney[]{zero, zero};
344+
}
338345
checkNumber(divisor);
339346
if (isOne(divisor)) {
340347
return new FastMoney[]{this, FastMoney.of(0, getCurrency())};
@@ -350,6 +357,9 @@ public FastMoney[] divideAndRemainder(Number divisor) {
350357
*/
351358
@Override
352359
public FastMoney divideToIntegralValue(Number divisor) {
360+
if (Money.isInfinityAndNotNaN(divisor)) {
361+
return new FastMoney(0L, getCurrency());
362+
}
353363
checkNumber(divisor);
354364
if (isOne(divisor)) {
355365
return this;
@@ -360,6 +370,7 @@ public FastMoney divideToIntegralValue(Number divisor) {
360370

361371
@Override
362372
public FastMoney multiply(Number multiplicand) {
373+
Money.checkNoInfinityOrNaN(multiplicand);
363374
checkNumber(multiplicand);
364375
if (isOne(multiplicand)) {
365376
return this;
@@ -729,6 +740,7 @@ private BigDecimal getBigDecimal() {
729740

730741
@Override
731742
public FastMoney multiply(double amount) {
743+
Money.checkNoInfinityOrNaN(amount);
732744
if (amount == 1.0) {
733745
return this;
734746
}
@@ -748,6 +760,9 @@ public FastMoney divide(long amount) {
748760

749761
@Override
750762
public FastMoney divide(double number) {
763+
if (Money.isInfinityAndNotNaN(number)) {
764+
return new FastMoney(0L, getCurrency());
765+
}
751766
if (number == 1.0d) {
752767
return this;
753768
}
@@ -761,6 +776,9 @@ public FastMoney remainder(long number) {
761776

762777
@Override
763778
public FastMoney remainder(double amount) {
779+
if (Money.isInfinityAndNotNaN(amount)) {
780+
return new FastMoney(0L, getCurrency());
781+
}
764782
return remainder(new BigDecimal(String.valueOf(amount)));
765783
}
766784

@@ -771,6 +789,12 @@ public FastMoney[] divideAndRemainder(long amount) {
771789

772790
@Override
773791
public FastMoney[] divideAndRemainder(double amount) {
792+
if (Money.isInfinityAndNotNaN(amount)) {
793+
FastMoney zero = new FastMoney(0L, getCurrency());
794+
return new FastMoney[]{zero, zero};
795+
} else if (amount == Double.NaN) {
796+
throw new ArithmeticException("Not a number: NaN.");
797+
}
774798
return divideAndRemainder(new BigDecimal(String.valueOf(amount)));
775799
}
776800

@@ -800,6 +824,9 @@ public FastMoney divideToIntegralValue(long divisor) {
800824

801825
@Override
802826
public FastMoney divideToIntegralValue(double divisor) {
827+
if (Money.isInfinityAndNotNaN(divisor)) {
828+
return new FastMoney(0L, getCurrency());
829+
}
803830
if (divisor == 1.0) {
804831
return this;
805832
}

src/main/java/org/javamoney/moneta/Money.java

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,9 @@ public Money divide(long divisor){
222222
*/
223223
@Override
224224
public Money divide(double divisor){
225+
if (isInfinityAndNotNaN(divisor)) {
226+
return Money.of(0, getCurrency());
227+
}
225228
if(divisor == 1.0){
226229
return this;
227230
}
@@ -250,6 +253,10 @@ public Money[] divideAndRemainder(long divisor){
250253
*/
251254
@Override
252255
public Money[] divideAndRemainder(double divisor){
256+
if (isInfinityAndNotNaN(divisor)) {
257+
Money zero = Money.of(0, getCurrency());
258+
return new Money[]{zero, zero};
259+
}
253260
return divideAndRemainder(new BigDecimal(String.valueOf(divisor)));
254261
}
255262

@@ -273,6 +280,7 @@ public Money multiply(long multiplicand){
273280
*/
274281
@Override
275282
public Money multiply(double multiplicand){
283+
checkNoInfinityOrNaN(multiplicand);
276284
if(multiplicand == 1.0d){
277285
return this;
278286
}
@@ -299,6 +307,9 @@ public Money remainder(long divisor){
299307
*/
300308
@Override
301309
public Money remainder(double divisor){
310+
if (isInfinityAndNotNaN(divisor)) {
311+
return Money.of(0, getCurrency());
312+
}
302313
return remainder(new BigDecimal(String.valueOf(divisor)));
303314
}
304315

@@ -412,6 +423,9 @@ public Money add(MonetaryAmount amount){
412423
*/
413424
@Override
414425
public Money divide(Number divisor){
426+
if (isInfinityAndNotNaN(divisor)) {
427+
return Money.of(0, getCurrency());
428+
}
415429
BigDecimal divisorBD = MoneyUtils.getBigDecimal(divisor);
416430
if(divisorBD.equals(BigDecimal.ONE)){
417431
return this;
@@ -423,6 +437,10 @@ public Money divide(Number divisor){
423437

424438
@Override
425439
public Money[] divideAndRemainder(Number divisor){
440+
if (isInfinityAndNotNaN(divisor)) {
441+
Money zero = Money.of(0, getCurrency());
442+
return new Money[]{zero, zero};
443+
}
426444
BigDecimal divisorBD = MoneyUtils.getBigDecimal(divisor);
427445
if(divisorBD.equals(BigDecimal.ONE)){
428446
return new Money[]{this, new Money(BigDecimal.ZERO, getCurrency())};
@@ -445,11 +463,17 @@ public Money divideToIntegralValue(long divisor){
445463

446464
@Override
447465
public Money divideToIntegralValue(double divisor){
466+
if (isInfinityAndNotNaN(divisor)) {
467+
return Money.of(0, getCurrency());
468+
}
448469
return divideToIntegralValue(MoneyUtils.getBigDecimal(divisor));
449470
}
450471

451472
@Override
452473
public Money divideToIntegralValue(Number divisor){
474+
if (isInfinityAndNotNaN(divisor)) {
475+
return Money.of(0, getCurrency());
476+
}
453477
BigDecimal divisorBD = MoneyUtils.getBigDecimal(divisor);
454478
BigDecimal dec = this.number.divideToIntegralValue(divisorBD);
455479
return new Money(dec, getCurrency());
@@ -462,6 +486,7 @@ public Money divideToIntegralValue(Number divisor){
462486
*/
463487
@Override
464488
public Money multiply(Number multiplicand){
489+
checkNoInfinityOrNaN(multiplicand);
465490
BigDecimal multiplicandBD = MoneyUtils.getBigDecimal(multiplicand);
466491
if(multiplicandBD.equals(BigDecimal.ONE)){
467492
return this;
@@ -524,6 +549,9 @@ public Money stripTrailingZeros(){
524549
*/
525550
@Override
526551
public Money remainder(Number divisor){
552+
if (isInfinityAndNotNaN(divisor)) {
553+
return new Money(BigDecimal.ZERO, getCurrency());
554+
}
527555
BigDecimal bd = MoneyUtils.getBigDecimal(divisor);
528556
return new Money(this.number.remainder(bd), getCurrency());
529557
}
@@ -810,4 +838,27 @@ public static Money parse(CharSequence text, MonetaryAmountFormat formatter) {
810838

811839
private static ToStringMonetaryAmountFormat DEFAULT_FORMATTER = ToStringMonetaryAmountFormat
812840
.of(ToStringMonetaryAmountFormatStyle.MONEY);
841+
842+
public static void checkNoInfinityOrNaN(Number number) {
843+
if (Double.class == number.getClass() || Float.class == number.getClass()) {
844+
double dValue = number.doubleValue();
845+
if (Double.isNaN(dValue)) {
846+
throw new ArithmeticException("Not a valid input: NaN.");
847+
} else if (Double.isInfinite(dValue)) {
848+
throw new ArithmeticException("Not a valid input: INFINITY: " + dValue);
849+
}
850+
}
851+
}
852+
853+
public static boolean isInfinityAndNotNaN(Number number) {
854+
if (Double.class == number.getClass() || Float.class == number.getClass()) {
855+
double dValue = number.doubleValue();
856+
if (Double.isNaN(dValue)) {
857+
throw new ArithmeticException("Not a valid input: NaN.");
858+
} else if (Double.isInfinite(dValue)) {
859+
return true;
860+
}
861+
}
862+
return false;
863+
}
813864
}

src/main/java/org/javamoney/moneta/RoundedMoney.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -817,6 +817,7 @@ public RoundedMoney multiply(long amount) {
817817

818818
@Override
819819
public RoundedMoney multiply(double amount) {
820+
Money.checkNoInfinityOrNaN(amount);
820821
if (amount == 1.0d) {
821822
return this;
822823
}
@@ -833,7 +834,9 @@ public RoundedMoney divide(long amount) {
833834

834835
@Override
835836
public RoundedMoney divide(double amount) {
836-
837+
if (Money.isInfinityAndNotNaN(amount)) {
838+
return new RoundedMoney(0L, getCurrency(), this.monetaryContext, this.rounding);
839+
}
837840
if (amount == 1.0d) {
838841
return this;
839842
}
@@ -847,6 +850,9 @@ public RoundedMoney remainder(long amount) {
847850

848851
@Override
849852
public RoundedMoney remainder(double amount) {
853+
if (Money.isInfinityAndNotNaN(amount)) {
854+
return new RoundedMoney(0L, getCurrency(), this.monetaryContext, this.rounding);
855+
}
850856
return remainder(MoneyUtils.getBigDecimal(amount));
851857
}
852858

@@ -857,6 +863,10 @@ public RoundedMoney[] divideAndRemainder(long amount) {
857863

858864
@Override
859865
public RoundedMoney[] divideAndRemainder(double amount) {
866+
if (Money.isInfinityAndNotNaN(amount)) {
867+
RoundedMoney zero = new RoundedMoney(0L, getCurrency(), this.monetaryContext, this.rounding);
868+
return new RoundedMoney[]{zero, zero};
869+
}
860870
return divideAndRemainder(MoneyUtils.getBigDecimal(amount));
861871
}
862872

src/main/java/org/javamoney/moneta/format/internal/CurrencyToken.java

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,18 @@
1515
*/
1616
package org.javamoney.moneta.format.internal;
1717

18-
import java.io.IOException;
19-
import java.util.Currency;
20-
import java.util.Locale;
21-
import java.util.Objects;
22-
import java.util.ResourceBundle;
18+
import org.javamoney.moneta.format.CurrencyStyle;
2319

2420
import javax.money.CurrencyUnit;
2521
import javax.money.MonetaryAmount;
2622
import javax.money.MonetaryCurrencies;
27-
28-
import org.javamoney.moneta.format.CurrencyStyle;
29-
3023
import javax.money.MonetaryException;
3124
import javax.money.format.MonetaryParseException;
32-
33-
import org.javamoney.moneta.format.CurrencyStyle;
25+
import java.io.IOException;
26+
import java.util.Currency;
27+
import java.util.Locale;
28+
import java.util.Objects;
29+
import java.util.ResourceBundle;
3430

3531
/**
3632
* Implements a {@link FormatToken} that adds a localizable {@link String}, read
@@ -186,8 +182,15 @@ public void parse(ParseContext context)
186182
CurrencyUnit cur;
187183
switch (style) {
188184
case CODE:
189-
cur = MonetaryCurrencies.getCurrency(token);
190-
context.consume(token);
185+
if (!MonetaryCurrencies.isCurrencyAvailable(token)) {
186+
// Perhaps blank is missing between currency code and number...
187+
String subCurrency = parseCurrencyCode(token);
188+
cur = MonetaryCurrencies.getCurrency(subCurrency);
189+
context.consume(subCurrency);
190+
} else {
191+
cur = MonetaryCurrencies.getCurrency(token);
192+
context.consume(token);
193+
}
191194
break;
192195
case SYMBOL:
193196
if (token.startsWith("$")) {
@@ -201,8 +204,8 @@ public void parse(ParseContext context)
201204
context.consume("£");
202205
} else {
203206
cur = MonetaryCurrencies.getCurrency(token);
207+
context.consume(token);
204208
}
205-
context.consume(token);
206209
context.setParsedCurrency(cur);
207210
break;
208211
case NAME:
@@ -218,6 +221,25 @@ public void parse(ParseContext context)
218221
}
219222
}
220223

224+
/**
225+
* Tries to split up a letter based first part, e.g. for evaluating a ISO currency code from an input as
226+
* 'CHF100.34'.
227+
* @param token the input token
228+
* @return the first letter based part, or the full token.
229+
*/
230+
private String parseCurrencyCode(String token) {
231+
StringBuilder b = new StringBuilder();
232+
int letterIndex = 0;
233+
for (char ch : token.toCharArray()) {
234+
if (Character.isLetter(ch)) {
235+
letterIndex++;
236+
} else {
237+
return token.substring(0, letterIndex);
238+
}
239+
}
240+
return token;
241+
}
242+
221243
/**
222244
* Prints the {@link CurrencyUnit} of the given {@link MonetaryAmount} to
223245
* the given {@link Appendable}.

src/main/java/org/javamoney/moneta/spi/ConvertBigDecimal.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,13 @@ BigDecimal getDecimal(Number num) {
4141
FLUCTUAGE {
4242
@Override
4343
BigDecimal getDecimal(Number num) {
44-
return new BigDecimal(num.toString());
45-
}
44+
double d = num.doubleValue();
45+
if (d == Double.NaN || d == Double.POSITIVE_INFINITY || d == Double.NEGATIVE_INFINITY) {
46+
throw new ArithmeticException("NaN, POSITIVE_INFINITY and NEGATIVE_INFINITY cannot be used as " +
47+
"parameters for monetary operations.");
48+
}
49+
return new BigDecimal(num.toString());
50+
}
4651
},
4752
/** Conversion from BigInteger. */
4853
BIGINTEGER {

src/main/java/org/javamoney/moneta/spi/MoneyUtils.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,13 @@ public static BigDecimal getBigDecimal(long num) {
6262
* @return the corresponding {@link BigDecimal}
6363
*/
6464
public static BigDecimal getBigDecimal(double num) {
65+
if (num == Double.NaN) {
66+
throw new ArithmeticException("Invalid input Double.NaN.");
67+
} else if (num == Double.POSITIVE_INFINITY) {
68+
throw new ArithmeticException("Invalid input Double.POSITIVE_INFINITY.");
69+
} else if (num == Double.NEGATIVE_INFINITY) {
70+
throw new ArithmeticException("Invalid input Double.NEGATIVE_INFINITY.");
71+
}
6572
return new BigDecimal(String.valueOf(num));
6673
}
6774

src/test/java/org/javamoney/moneta/function/MonetaryFunctionsAgregatorTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ public void shouldMaxCorretly() {
105105
}
106106

107107
@Test
108-
public void shouldMaxExchangeCorretly() {
109-
Stream<MonetaryAmount> stream = Stream.of(Money.of(7, EURO), Money.of(9, BRAZILIAN_REAL), Money.of(8, DOLLAR));
108+
public void shouldMaxExchangeCorrectly() {
109+
Stream<MonetaryAmount> stream = Stream.of(Money.of(7, EURO), Money.of(9, BRAZILIAN_REAL), Money.of(8, DOLLAR));
110110
MonetaryAmount max = stream.reduce(max(provider)).get();
111111
Assert.assertEquals(Money.of(7, EURO), max);
112112
}

0 commit comments

Comments
 (0)