diff --git a/moneta-convert/moneta-convert-base/pom.xml b/moneta-convert/moneta-convert-base/pom.xml index a3d05f416..9319d9bfd 100644 --- a/moneta-convert/moneta-convert-base/pom.xml +++ b/moneta-convert/moneta-convert-base/pom.xml @@ -5,7 +5,7 @@ org.javamoney.moneta moneta-convert-parent - 1.4.2-SNAPSHOT + 1.4.2 moneta-convert diff --git a/moneta-convert/moneta-convert-ecb/pom.xml b/moneta-convert/moneta-convert-ecb/pom.xml index ec2b44d6d..63bb2f85a 100644 --- a/moneta-convert/moneta-convert-ecb/pom.xml +++ b/moneta-convert/moneta-convert-ecb/pom.xml @@ -5,7 +5,7 @@ org.javamoney.moneta moneta-convert-parent - 1.4.2-SNAPSHOT + 1.4.2 moneta-convert-ecb jar diff --git a/moneta-convert/moneta-convert-imf/pom.xml b/moneta-convert/moneta-convert-imf/pom.xml index eb376ff04..4078d78f6 100644 --- a/moneta-convert/moneta-convert-imf/pom.xml +++ b/moneta-convert/moneta-convert-imf/pom.xml @@ -6,7 +6,7 @@ org.javamoney.moneta moneta-convert-parent - 1.4.2-SNAPSHOT + 1.4.2 moneta-convert-imf jar diff --git a/moneta-convert/pom.xml b/moneta-convert/pom.xml index 4a537b60f..3200affd4 100644 --- a/moneta-convert/pom.xml +++ b/moneta-convert/pom.xml @@ -4,7 +4,7 @@ org.javamoney moneta-parent - 1.4.2-SNAPSHOT + 1.4.2 org.javamoney.moneta moneta-convert-parent diff --git a/moneta-core/pom.xml b/moneta-core/pom.xml index 0f33b5f3a..81d35672f 100644 --- a/moneta-core/pom.xml +++ b/moneta-core/pom.xml @@ -13,7 +13,7 @@ org.javamoney moneta-parent - 1.4.2-SNAPSHOT + 1.4.2 org.javamoney.moneta moneta-core diff --git a/moneta-core/src/main/java/org/javamoney/moneta/Money.java b/moneta-core/src/main/java/org/javamoney/moneta/Money.java index 7aed37f23..89c22fb7a 100644 --- a/moneta-core/src/main/java/org/javamoney/moneta/Money.java +++ b/moneta-core/src/main/java/org/javamoney/moneta/Money.java @@ -1,934 +1,934 @@ -/* - Copyright (c) 2012, 2020, 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; - -import org.javamoney.moneta.ToStringMonetaryAmountFormat.ToStringMonetaryAmountFormatStyle; -import org.javamoney.moneta.format.MonetaryAmountDecimalFormat; -import org.javamoney.moneta.spi.MoneyAmountFactory; -import org.javamoney.moneta.spi.DefaultNumberValue; -import org.javamoney.moneta.spi.MonetaryConfig; -import org.javamoney.moneta.spi.MoneyUtils; - -import javax.money.*; -import javax.money.format.MonetaryAmountFormat; -import java.io.Serializable; -import java.math.BigDecimal; -import java.math.MathContext; -import java.math.RoundingMode; -import java.util.Objects; -import java.util.logging.Level; -import java.util.logging.Logger; - -/** - * Default immutable implementation of {@link MonetaryAmount} based - * on {@link BigDecimal} as numeric representation. - *

- * As required by {@link MonetaryAmount} this class is final, thread-safe, - * immutable and serializable. - *

- * This class can be configured with an arbitrary {@link MonetaryContext}. The - * default {@link MonetaryContext} used models by default the same settings as - * {@link MathContext#DECIMAL64} . This default {@link MonetaryContext} can also - * be reconfigured by adding a file {@code /javamoney.properties} to the - * classpath, with the following content: - *

- *
- * # Default MathContext for Money
- * #-------------------------------
- * # Custom MonetaryContext, overrides default entries from
- * # org.javamoney.moneta.Money.monetaryContext
- * # RoundingMode hereby is optional (default = HALF_EVEN)
- * org.javamoney.moneta.Money.defaults.precision=256
- * org.javamoney.moneta.Money.defaults.roundingMode=HALF_EVEN
- * 
- * - * @author Anatole Tresch - * @author Werner Keil - * @version 1.1 - * @since 1.0 - */ -public final class Money implements MonetaryAmount, Comparable, Serializable { - - /** - * serialVersionUID. - */ - private static final long serialVersionUID = -7565813772046251748L; - - /** - * The default {@link MonetaryContext} applied, if not set explicitly on - * creation. - */ - public static final MonetaryContext DEFAULT_MONETARY_CONTEXT = new DefaultMonetaryContextFactory().getContext(); - - /** - * The currency of this amount. - */ - private final CurrencyUnit currency; - - /** - * the {@link MonetaryContext} used by this instance, e.g. on division. - */ - private final MonetaryContext monetaryContext; - - /** - * The numeric part of this amount. - */ - private final BigDecimal number; - - /** - * Creates a new instance of {@link Money}. - * - * @param currency the currency, not null. - * @param number the amount, not null. - * @throws ArithmeticException If the number exceeds the capabilities of the default - * {@link MonetaryContext}. - */ - private Money(BigDecimal number, CurrencyUnit currency) { - this(number, currency, null); - } - - /** - * Creates a new instance of {@link Money}. - * - * @param currency the currency, not {@code null}. - * @param number the amount, not {@code null}. - * @param monetaryContext the {@link MonetaryContext}, if {@code null}, the default is - * used. - * @throws ArithmeticException If the number exceeds the capabilities of the - * {@link MonetaryContext} used. - */ - private Money(BigDecimal number, CurrencyUnit currency, MonetaryContext monetaryContext) { - Objects.requireNonNull(currency, "Currency is required."); - this.currency = currency; - if (Objects.nonNull(monetaryContext)) { - this.monetaryContext = monetaryContext; - } else { - this.monetaryContext = DEFAULT_MONETARY_CONTEXT; - } - Objects.requireNonNull(number, "Number is required."); - this.number = MoneyUtils.getBigDecimal(number, monetaryContext); - } - - /** - * Returns the amount’s currency, modelled as {@link CurrencyUnit}. - * Implementations may co-variantly change the return type to a more - * specific implementation of {@link CurrencyUnit} if desired. - * - * @return the currency, never {@code null} - * @see javax.money.MonetaryAmount#getCurrency() - */ - @Override - public CurrencyUnit getCurrency() { - return currency; - } - - /** - * Access the {@link MonetaryContext} used by this instance. - * - * @return the {@link MonetaryContext} used, never null. - * @see javax.money.MonetaryAmount#getContext() - */ - @Override - public MonetaryContext getContext() { - return monetaryContext; - } - - /** - * Gets the number representation of the numeric value of this item. - * - * @return The {@link Number} representation matching best. - */ - @Override - public NumberValue getNumber() { - return new DefaultNumberValue(number); - } - - /** - * Method that returns BigDecimal.ZERO, if {@link #isZero()}, and - * {@link #number #stripTrailingZeros()} in all other cases. - * - * @return the stripped number value. - */ - public BigDecimal getNumberStripped() { - if (isZero()) { - return BigDecimal.ZERO; - } - return this.number.stripTrailingZeros(); - } - - /* - * @see java.lang.Comparable#compareTo(java.lang.Object) - */ - @Override - public int compareTo(MonetaryAmount o) { - Objects.requireNonNull(o); - int compare = getCurrency().getCurrencyCode().compareTo(o.getCurrency().getCurrencyCode()); - if (compare == 0) { - compare = this.number.compareTo(Money.from(o).number); - } - return compare; - } - - // Arithmetic Operations - - /* - * (non-Javadoc) - * - * @see MonetaryAmount#abs() - */ - @Override - public Money abs() { - if (this.isPositiveOrZero()) { - return this; - } - return negate(); - } - - /* - * (non-Javadoc) - * - * @see MonetaryAmount#divide(MonetaryAmount) - */ - @Override - public Money divide(long divisor) { - if (divisor == 1L) { - return this; - } - return divide(BigDecimal.valueOf(divisor)); - } - - /* - * (non-Javadoc) - * - * @see MonetaryAmount#divide(MonetaryAmount) - */ - @Override - public Money divide(double divisor) { - if (NumberVerifier.isInfinityAndNotNaN(divisor)) { - return Money.of(0, getCurrency()); - } - if (divisor == 1.0d) { - return this; - } - return divide(new BigDecimal(String.valueOf(divisor))); - } - - /* - * (non-Javadoc) - * - * @see - * MonetaryAmount#divideAndRemainder(MonetaryAmount) - */ - @Override - public Money[] divideAndRemainder(long divisor) { - return divideAndRemainder(BigDecimal.valueOf(divisor)); - } - - /* - * (non-Javadoc) - * - * @see - * MonetaryAmount#divideAndRemainder(MonetaryAmount) - */ - @Override - public Money[] divideAndRemainder(double divisor) { - if (NumberVerifier.isInfinityAndNotNaN(divisor)) { - Money zero = Money.of(0, getCurrency()); - return new Money[]{zero, zero}; - } - return divideAndRemainder(new BigDecimal(String.valueOf(divisor))); - } - - /* - * (non-Javadoc) - * - * @see javax.money.MonetaryAmount#multiply(Number) - */ - @Override - public Money multiply(long multiplicand) { - if (multiplicand == 1L) { - return this; - } - return multiply(BigDecimal.valueOf(multiplicand)); - } - - /* - * (non-Javadoc) - * - * @see javax.money.MonetaryAmount#multiply(Number) - */ - @Override - public Money multiply(double multiplicand) { - NumberVerifier.checkNoInfinityOrNaN(multiplicand); - if (multiplicand == 1.0d) { - return this; - } - return multiply(new BigDecimal(String.valueOf(multiplicand))); - } - - /* - * (non-Javadoc) - * - * @see javax.money.MonetaryAmount#remainder(Number) - */ - @Override - public Money remainder(long divisor) { - return remainder(BigDecimal.valueOf(divisor)); - } - - /* - * (non-Javadoc) - * - * @see javax.money.MonetaryAmount#remainder(Number) - */ - @Override - public Money remainder(double divisor) { - if (NumberVerifier.isInfinityAndNotNaN(divisor)) { - return Money.of(0, getCurrency()); - } - return remainder(new BigDecimal(String.valueOf(divisor))); - } - - /* - * (non-Javadoc) - * - * @see javax.money.MonetaryAmount#isZero() - */ - @Override - public boolean isZero() { - return signum() == 0; - } - - /* - * (non-Javadoc) - * - * @see javax.money.MonetaryAmount#isPositive() - */ - @Override - public boolean isPositive() { - return signum() == 1; - } - - /* - * (non-Javadoc) - * - * @see javax.money.MonetaryAmount#isPositiveOrZero() - */ - @Override - public boolean isPositiveOrZero() { - return signum() >= 0; - } - - /* - * (non-Javadoc) - * - * @see javax.money.MonetaryAmount#isNegative() - */ - @Override - public boolean isNegative() { - return signum() == -1; - } - - /* - * (non-Javadoc) - * - * @see javax.money.MonetaryAmount#isNegativeOrZero() - */ - @Override - public boolean isNegativeOrZero() { - return signum() <= 0; - } - - - /* - * (non-Javadoc) - * - * @see javax.money.MonetaryAmount#query(javax.money.MonetaryQuery) - */ - @Override - public R query(MonetaryQuery query) { - Objects.requireNonNull(query); - try { - return query.queryFrom(this); - } catch (MonetaryException e) { - throw e; - } catch (Exception e) { - throw new MonetaryException("Query failed: " + query, e); - } - } - - /* - * (non-Javadoc) - * - * @see javax.money.MonetaryAmount#with(javax.money.MonetaryOperator) - */ - @Override - public Money with(MonetaryOperator operator) { - Objects.requireNonNull(operator); - try { - return Money.class.cast(operator.apply(this)); - } catch (MonetaryException e) { - throw e; - } catch (Exception e) { - throw new MonetaryException("Operator failed: " + operator, e); - } - } - - /* - * (non-Javadoc) - * - * @see javax.money.MonetaryAmount#add(javax.money.MonetaryAmount) - */ - @Override - public Money add(MonetaryAmount amount) { - MoneyUtils.checkAmountParameter(amount, this.currency); - if (amount.isZero()) { - return this; - } - MathContext mc = MoneyUtils.getMathContext(monetaryContext, RoundingMode.HALF_EVEN); - return new Money(this.number.add(amount.getNumber().numberValue(BigDecimal.class), mc), getCurrency(), monetaryContext); - } - - /* - * (non-Javadoc) - * - * @see javax.money.MonetaryAmount#divide(java.lang.Number) - */ - @Override - public Money divide(Number divisor) { - if (NumberVerifier.isInfinityAndNotNaN(divisor)) { - return Money.of(0, getCurrency()); - } - BigDecimal divisorBD = MoneyUtils.getBigDecimal(divisor); - if (divisorBD.equals(BigDecimal.ONE)) { - return this; - } - MathContext mc = MoneyUtils.getMathContext(monetaryContext, RoundingMode.HALF_EVEN); - int maxScale = monetaryContext.getMaxScale(); - if(maxScale>0){ - return new Money(this.number.divide(divisorBD, maxScale, mc.getRoundingMode()), getCurrency(), monetaryContext); - } - return new Money(this.number.divide(divisorBD, mc), getCurrency(), monetaryContext); - } - - @Override - public Money[] divideAndRemainder(Number divisor) { - if (NumberVerifier.isInfinityAndNotNaN(divisor)) { - Money zero = Money.of(0, getCurrency()); - return new Money[]{zero, zero}; - } - BigDecimal divisorBD = MoneyUtils.getBigDecimal(divisor); - if (divisorBD.equals(BigDecimal.ONE)) { - return new Money[]{this, new Money(BigDecimal.ZERO, getCurrency())}; - } - MathContext mc = MoneyUtils.getMathContext(monetaryContext, RoundingMode.HALF_EVEN); - BigDecimal[] dec = this.number.divideAndRemainder(divisorBD, mc); - return new Money[]{new Money(dec[0], getCurrency(), monetaryContext), new Money(dec[1], getCurrency(), monetaryContext)}; - } - - /* - * (non-Javadoc) - * - * @see - * org.javamoney.moneta.AbstractMoney#divideToIntegralValue(java.lang.Number - * ) - */ - @Override - public Money divideToIntegralValue(long divisor) { - return divideToIntegralValue(MoneyUtils.getBigDecimal(divisor)); - } - - @Override - public Money divideToIntegralValue(double divisor) { - if (NumberVerifier.isInfinityAndNotNaN(divisor)) { - return Money.of(0, getCurrency()); - } - return divideToIntegralValue(MoneyUtils.getBigDecimal(divisor)); - } - - @Override - public Money divideToIntegralValue(Number divisor) { - if (NumberVerifier.isInfinityAndNotNaN(divisor)) { - return Money.of(0, getCurrency()); - } - MathContext mc = MoneyUtils.getMathContext(monetaryContext, RoundingMode.HALF_EVEN); - BigDecimal divisorBD = MoneyUtils.getBigDecimal(divisor); - BigDecimal dec = this.number.divideToIntegralValue(divisorBD, mc); - return new Money(dec, getCurrency(), monetaryContext); - } - - /* - * (non-Javadoc) - * - * @see org.javamoney.moneta.AbstractMoney#multiply(java.lang.Number) - */ - @Override - public Money multiply(Number multiplicand) { - NumberVerifier.checkNoInfinityOrNaN(multiplicand); - BigDecimal multiplicandBD = MoneyUtils.getBigDecimal(multiplicand); - if (multiplicandBD.equals(BigDecimal.ONE)) { - return this; - } - MathContext mc = MoneyUtils.getMathContext(monetaryContext, RoundingMode.HALF_EVEN); - BigDecimal dec = this.number.multiply(multiplicandBD, mc); - return new Money(dec, getCurrency(), monetaryContext); - } - - /* - * (non-Javadoc) - * - * @see javax.money.MonetaryAmount#negate() - */ - @Override - public Money negate() { - return new Money(this.number.negate(), getCurrency()); - } - - /* - * (non-Javadoc) - * - * @see javax.money.MonetaryAmount#plus() - */ - @Override - public Money plus() { - return this; - } - - /* - * (non-Javadoc) - * - * @see javax.money.MonetaryAmount#subtract(javax.money.MonetaryAmount) - */ - @Override - public Money subtract(MonetaryAmount subtrahend) { - MoneyUtils.checkAmountParameter(subtrahend, this.currency); - if (subtrahend.isZero()) { - return this; - } - MathContext mc = MoneyUtils.getMathContext(monetaryContext, RoundingMode.HALF_EVEN); - return new Money(this.number.subtract(subtrahend.getNumber().numberValue(BigDecimal.class), mc), getCurrency(), - monetaryContext); - } - - /* - * (non-Javadoc) - * - * @see javax.money.MonetaryAmount#stripTrailingZeros() - */ - @Override - public Money stripTrailingZeros() { - if (isZero()) { - return new Money(BigDecimal.ZERO, getCurrency()); - } - return new Money(this.number.stripTrailingZeros(), getCurrency(), monetaryContext); - } - - /* - * (non-Javadoc) - * - * @see org.javamoney.moneta.AbstractMoney#remainder(java.math.BigDecimal) - */ - @Override - public Money remainder(Number divisor) { - if (NumberVerifier.isInfinityAndNotNaN(divisor)) { - return new Money(BigDecimal.ZERO, getCurrency()); - } - MathContext mc = MoneyUtils.getMathContext(monetaryContext, RoundingMode.HALF_EVEN); - BigDecimal bd = MoneyUtils.getBigDecimal(divisor); - return new Money(this.number.remainder(bd, mc), getCurrency(), monetaryContext); - } - - /* - * (non-Javadoc) - * - * @see javax.money.MonetaryAmount#scaleByPowerOfTen(int) - */ - @Override - public Money scaleByPowerOfTen(int power) { - return new Money(this.number.scaleByPowerOfTen(power), getCurrency(), - monetaryContext); - } - - /* - * (non-Javadoc) - * - * @see javax.money.MonetaryAmount#signum() - */ - @Override - public int signum() { - return this.number.signum(); - } - - /* - * (non-Javadoc) - * - * @see javax.money.MonetaryAmount#isLessThan(javax.money.MonetaryAmount) - */ - @Override - public boolean isLessThan(MonetaryAmount amount) { - MoneyUtils.checkAmountParameter(amount, this.currency); - return number.stripTrailingZeros() - .compareTo(amount.getNumber().numberValue(BigDecimal.class).stripTrailingZeros()) < 0; - } - - /* - * (non-Javadoc) - * - * @see - * javax.money.MonetaryAmount#isLessThanOrEqualTo(javax.money.MonetaryAmount - * ) - */ - @Override - public boolean isLessThanOrEqualTo(MonetaryAmount amount) { - MoneyUtils.checkAmountParameter(amount, this.currency); - return number.stripTrailingZeros() - .compareTo(amount.getNumber().numberValue(BigDecimal.class).stripTrailingZeros()) <= 0; - } - - /* - * (non-Javadoc) - * - * @see javax.money.MonetaryAmount#isGreaterThan(javax.money.MonetaryAmount) - */ - @Override - public boolean isGreaterThan(MonetaryAmount amount) { - MoneyUtils.checkAmountParameter(amount, this.currency); - return number.stripTrailingZeros() - .compareTo(amount.getNumber().numberValue(BigDecimal.class).stripTrailingZeros()) > 0; - } - - /* - * (non-Javadoc) - * - * @see - * javax.money.MonetaryAmount#isGreaterThanOrEqualTo(javax.money.MonetaryAmount - * ) #see - */ - @Override - public boolean isGreaterThanOrEqualTo(MonetaryAmount amount) { - MoneyUtils.checkAmountParameter(amount, this.currency); - return number.stripTrailingZeros() - .compareTo(amount.getNumber().numberValue(BigDecimal.class).stripTrailingZeros()) >= 0; - } - - /* - * (non-Javadoc) - * - * @see javax.money.MonetaryAmount#isEqualTo(javax.money.MonetaryAmount) - */ - @Override - public boolean isEqualTo(MonetaryAmount amount) { - MoneyUtils.checkAmountParameter(amount, this.currency); - return number.stripTrailingZeros() - .compareTo(amount.getNumber().numberValue(BigDecimal.class).stripTrailingZeros()) == 0; - } - - /* - * (non-Javadoc) - * - * @see javax.money.MonetaryAmount#getFactory() - */ - @Override - public MonetaryAmountFactory getFactory() { - return new MoneyAmountFactory().setAmount(this); - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#equals(java.lang.Object) - */ - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } - if (obj instanceof Money) { - Money other = (Money) obj; - return getCurrency().equals(other.getCurrency()) && - this.number.compareTo(other.number) == 0; - } - return false; - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - try { - MonetaryAmount amount = Monetary.getDefaultRounding().apply(this); - return defaultFormat().format(amount); - }catch(Exception e) { - return getCurrency().getCurrencyCode() + ' ' + number.toPlainString(); - } - } - - /* - * (non-Javadoc) - * - * @see java.lang.Object#hashCode() - */ - @Override - public int hashCode() { - return Objects.hash(getCurrency(), getNumberStripped()); - } - - /** - * Creates a new instance of {@link Money}, using the default - * {@link MonetaryContext}. - * - * @param number numeric value, not {@code null}. - * @param currency currency unit, not {@code null}. - * @return a {@code Money} combining the numeric value and currency unit. - * @throws ArithmeticException If the number exceeds the capabilities of the default - * {@link MonetaryContext} used. - */ - public static Money of(BigDecimal number, CurrencyUnit currency) { - return new Money(number, currency); - } - - /** - * Creates a new instance of {@link Money}, using an explicit - * {@link MonetaryContext}. - * - * @param number numeric value, not {@code null}. - * @param currency currency unit, not {@code null}. - * @param monetaryContext the {@link MonetaryContext} to be used, if {@code null} the - * default {@link MonetaryContext} is used. - * @return a {@code Money} instance based on the monetary context with the - * given numeric value, currency unit. - * @throws ArithmeticException If the number exceeds the capabilities of the - * {@link MonetaryContext} used. - */ - public static Money of(BigDecimal number, CurrencyUnit currency, MonetaryContext monetaryContext) { - return new Money(number, currency, monetaryContext); - } - - /** - * Creates a new instance of {@link Money}, using the default - * {@link MonetaryContext}. - * - * @param currency The target currency, not null. - * @param number The numeric part, not null. - * @return A new instance of {@link Money}. - * @throws ArithmeticException If the number exceeds the capabilities of the default - * {@link MonetaryContext} used. - */ - public static Money of(Number number, CurrencyUnit currency) { - return new Money(MoneyUtils.getBigDecimal(number), currency); - } - - /** - * Creates a new instance of {@link Money}, using an explicit - * {@link MonetaryContext}. - * - * @param currency The target currency, not null. - * @param number The numeric part, not null. - * @param monetaryContext the {@link MonetaryContext} to be used, if {@code null} the - * default {@link MonetaryContext} is used. - * @return A new instance of {@link Money}. - * @throws ArithmeticException If the number exceeds the capabilities of the - * {@link MonetaryContext} used. - */ - public static Money of(Number number, CurrencyUnit currency, MonetaryContext monetaryContext) { - return new Money(MoneyUtils.getBigDecimal(number), currency, monetaryContext); - } - - /** - * Static factory method for creating a new instance of {@link Money}. - * - * @param currencyCode The target currency as ISO currency code. - * @param number The numeric part, not null. - * @return A new instance of {@link Money}. - */ - public static Money of(Number number, String currencyCode) { - return new Money(MoneyUtils.getBigDecimal(number), Monetary.getCurrency(currencyCode)); - } - - /** - * Static factory method for creating a new instance of {@link Money}. - * - * @param currencyCode The target currency as ISO currency code. - * @param number The numeric part, not null. - * @return A new instance of {@link Money}. - */ - public static Money of(BigDecimal number, String currencyCode) { - return new Money(number, Monetary.getCurrency(currencyCode)); - } - - /** - * Static factory method for creating a new instance of {@link Money}. - * - * @param currencyCode The target currency as ISO currency code. - * @param number The numeric part, not null. - * @param monetaryContext the {@link MonetaryContext} to be used, if {@code null} the - * default {@link MonetaryContext} is used. - * @return A new instance of {@link Money}. - */ - public static Money of(Number number, String currencyCode, MonetaryContext monetaryContext) { - return new Money(MoneyUtils.getBigDecimal(number), Monetary.getCurrency(currencyCode), - monetaryContext); - } - - /** - * Static factory method for creating a new instance of {@link Money}. - * - * @param currencyCode The target currency as ISO currency code. - * @param number The numeric part, not null. - * @param monetaryContext the {@link MonetaryContext} to be used, if {@code null} the - * default {@link MonetaryContext} is used. - * @return A new instance of {@link Money}. - */ - public static Money of(BigDecimal number, String currencyCode, MonetaryContext monetaryContext) { - return new Money(number, Monetary.getCurrency(currencyCode), monetaryContext); - } - - /** - * Obtains an instance of {@link Money} representing zero. - * @param currency the currency, not null. - * @return an instance of {@link Money} representing zero. - * @since 1.0.1 - */ - public static Money zero(CurrencyUnit currency) { - return new Money(BigDecimal.ZERO, currency); - } - - /** - * Obtains an instance of {@code Money} from an amount in minor units. - * For example, {@code ofMinor(USD, 1234)} creates the instance {@code USD 12.34}. - * @param currency the currency - * @param amountMinor the amount of money in the minor division of the currency - * @return the Money from minor units - * @throws NullPointerException when the currency is null - * @throws IllegalArgumentException when {@link CurrencyUnit#getDefaultFractionDigits()} is lesser than zero. - * @see CurrencyUnit#getDefaultFractionDigits() - * @since 1.0.1 - */ - public static Money ofMinor(CurrencyUnit currency, long amountMinor) { - return ofMinor(currency, amountMinor, currency.getDefaultFractionDigits()); - } - - /** - * Obtains an instance of {@code Money} from an amount in minor units. - * For example, {@code ofMinor(USD, 1234, 2)} creates the instance {@code USD 12.34}. - * @param currency the currency, not null - * @param amountMinor the amount of money in the minor division of the currency - * @param fractionDigits number of digits - * @return the monetary amount from minor units - * @see CurrencyUnit#getDefaultFractionDigits() - * @see Money#ofMinor(CurrencyUnit, long, int) - * @throws NullPointerException when the currency is null - * @throws IllegalArgumentException when the fractionDigits is negative - * @since 1.0.1 - */ - public static Money ofMinor(CurrencyUnit currency, long amountMinor, int fractionDigits) { - if(fractionDigits < 0) { - throw new IllegalArgumentException("The fractionDigits cannot be negative"); - } - return of(BigDecimal.valueOf(amountMinor, fractionDigits), currency); - } - - /** - * Converts (if necessary) the given {@link MonetaryAmount} to a - * {@link Money} instance. The {@link MonetaryContext} will be adapted as - * necessary, if the precision of the given amount exceeds the capabilities - * of the default {@link MonetaryContext}. - * - * @param amt the amount to be converted - * @return an according Money instance. - */ - public static Money from(MonetaryAmount amt) { - if (amt.getClass() == Money.class) { - return (Money) amt; - } - return Money.of(amt.getNumber().numberValue(BigDecimal.class), amt.getCurrency(), amt.getContext()); - } - - /** - * Obtains an instance of Money from a text string such as 'EUR 25.25'. - * - * @param text the text to parse not null - * @return Money instance - * @throws NumberFormatException if the amount is not a number - * @throws UnknownCurrencyException if the currency cannot be resolved - */ - public static Money parse(CharSequence text) { - return parse(text, defaultFormat()); - } - - /** - * Obtains an instance of Money from a text using specific formatter. - * - * @param text the text to parse not null - * @param formatter the formatter to use not null - * @return Money instance - */ - public static Money parse(CharSequence text, MonetaryAmountFormat formatter) { - return from(formatter.parse(text)); - } - - private static MonetaryAmountFormat defaultFormat() { - String useDefault = MonetaryConfig.getConfig().getOrDefault("org.javamoney.moneta.useJDKdefaultFormat", "false"); - try{ - if(Boolean.parseBoolean(useDefault)){ - Logger.getLogger(Money.class.getName()).info("Using JDK formatter for toString()."); - return MonetaryAmountDecimalFormat.of(); - }else{ - Logger.getLogger(Money.class.getName()).info("Using default formatter for toString()."); - return ToStringMonetaryAmountFormat.of(ToStringMonetaryAmountFormatStyle.MONEY); - } - }catch(Exception e){ - Logger.getLogger(Money.class.getName()).log(Level.WARNING, - "Invalid boolean parameter for 'org.javamoney.moneta.useJDKdefaultFormat', " + - "using default formatter for toString()."); - return ToStringMonetaryAmountFormat.of(ToStringMonetaryAmountFormatStyle.MONEY); - } - } - - /** - * Just to don't break the compatibility. - * Don't use it - * @param number the amount, not null - * @deprecated Will be removed. - */ - @Deprecated - public static boolean isInfinityAndNotNaN(Number number) { - if (Double.class == number.getClass() || Float.class == number.getClass()) { - double dValue = number.doubleValue(); - if (!Double.isNaN(dValue) && Double.isInfinite(dValue)) { - return true; - } - } - return false; - } - - - /** - * Just to don't break the compatibility. - * Don't use it - * @param number the amount, not null. - * @deprecated Will be removed. Use - * @see NumberVerifier#checkNoInfinityOrNaN - */ - @Deprecated - public static void checkNoInfinityOrNaN(Number number) { - NumberVerifier.checkNoInfinityOrNaN(number); - } - -} +/* + Copyright (c) 2012, 2020, 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; + +import org.javamoney.moneta.ToStringMonetaryAmountFormat.ToStringMonetaryAmountFormatStyle; +import org.javamoney.moneta.format.MonetaryAmountDecimalFormat; +import org.javamoney.moneta.spi.MoneyAmountFactory; +import org.javamoney.moneta.spi.DefaultNumberValue; +import org.javamoney.moneta.spi.MonetaryConfig; +import org.javamoney.moneta.spi.MoneyUtils; + +import javax.money.*; +import javax.money.format.MonetaryAmountFormat; +import java.io.Serializable; +import java.math.BigDecimal; +import java.math.MathContext; +import java.math.RoundingMode; +import java.util.Objects; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * Default immutable implementation of {@link MonetaryAmount} based + * on {@link BigDecimal} as numeric representation. + *

+ * As required by {@link MonetaryAmount} this class is final, thread-safe, + * immutable and serializable. + *

+ * This class can be configured with an arbitrary {@link MonetaryContext}. The + * default {@link MonetaryContext} used models by default the same settings as + * {@link MathContext#DECIMAL64} . This default {@link MonetaryContext} can also + * be reconfigured by adding a file {@code /javamoney.properties} to the + * classpath, with the following content: + *

+ *
+ * # Default MathContext for Money
+ * #-------------------------------
+ * # Custom MonetaryContext, overrides default entries from
+ * # org.javamoney.moneta.Money.monetaryContext
+ * # RoundingMode hereby is optional (default = HALF_EVEN)
+ * org.javamoney.moneta.Money.defaults.precision=256
+ * org.javamoney.moneta.Money.defaults.roundingMode=HALF_EVEN
+ * 
+ * + * @author Anatole Tresch + * @author Werner Keil + * @version 1.2 + * @since 1.0 + */ +public final class Money implements MonetaryAmount, Comparable, Serializable { + + /** + * serialVersionUID. + */ + private static final long serialVersionUID = -7565813772046251748L; + + /** + * The default {@link MonetaryContext} applied, if not set explicitly on + * creation. + */ + public static final MonetaryContext DEFAULT_MONETARY_CONTEXT = new DefaultMonetaryContextFactory().getContext(); + + /** + * The currency of this amount. + */ + private final CurrencyUnit currency; + + /** + * the {@link MonetaryContext} used by this instance, e.g. on division. + */ + private final MonetaryContext monetaryContext; + + /** + * The numeric part of this amount. + */ + private final BigDecimal number; + + /** + * Creates a new instance of {@link Money}. + * + * @param currency the currency, not null. + * @param number the amount, not null. + * @throws ArithmeticException If the number exceeds the capabilities of the default + * {@link MonetaryContext}. + */ + private Money(BigDecimal number, CurrencyUnit currency) { + this(number, currency, null); + } + + /** + * Creates a new instance of {@link Money}. + * + * @param currency the currency, not {@code null}. + * @param number the amount, not {@code null}. + * @param monetaryContext the {@link MonetaryContext}, if {@code null}, the default is + * used. + * @throws ArithmeticException If the number exceeds the capabilities of the + * {@link MonetaryContext} used. + */ + private Money(BigDecimal number, CurrencyUnit currency, MonetaryContext monetaryContext) { + Objects.requireNonNull(currency, "Currency is required."); + this.currency = currency; + if (Objects.nonNull(monetaryContext)) { + this.monetaryContext = monetaryContext; + } else { + this.monetaryContext = DEFAULT_MONETARY_CONTEXT; + } + Objects.requireNonNull(number, "Number is required."); + this.number = MoneyUtils.getBigDecimal(number, this.monetaryContext); + } + + /** + * Returns the amount’s currency, modelled as {@link CurrencyUnit}. + * Implementations may co-variantly change the return type to a more + * specific implementation of {@link CurrencyUnit} if desired. + * + * @return the currency, never {@code null} + * @see javax.money.MonetaryAmount#getCurrency() + */ + @Override + public CurrencyUnit getCurrency() { + return currency; + } + + /** + * Access the {@link MonetaryContext} used by this instance. + * + * @return the {@link MonetaryContext} used, never null. + * @see javax.money.MonetaryAmount#getContext() + */ + @Override + public MonetaryContext getContext() { + return monetaryContext; + } + + /** + * Gets the number representation of the numeric value of this item. + * + * @return The {@link Number} representation matching best. + */ + @Override + public NumberValue getNumber() { + return new DefaultNumberValue(number); + } + + /** + * Method that returns BigDecimal.ZERO, if {@link #isZero()}, and + * {@link #number #stripTrailingZeros()} in all other cases. + * + * @return the stripped number value. + */ + public BigDecimal getNumberStripped() { + if (isZero()) { + return BigDecimal.ZERO; + } + return this.number.stripTrailingZeros(); + } + + /* + * @see java.lang.Comparable#compareTo(java.lang.Object) + */ + @Override + public int compareTo(MonetaryAmount o) { + Objects.requireNonNull(o); + int compare = getCurrency().getCurrencyCode().compareTo(o.getCurrency().getCurrencyCode()); + if (compare == 0) { + compare = this.number.compareTo(Money.from(o).number); + } + return compare; + } + + // Arithmetic Operations + + /* + * (non-Javadoc) + * + * @see MonetaryAmount#abs() + */ + @Override + public Money abs() { + if (this.isPositiveOrZero()) { + return this; + } + return negate(); + } + + /* + * (non-Javadoc) + * + * @see MonetaryAmount#divide(MonetaryAmount) + */ + @Override + public Money divide(long divisor) { + if (divisor == 1L) { + return this; + } + return divide(BigDecimal.valueOf(divisor)); + } + + /* + * (non-Javadoc) + * + * @see MonetaryAmount#divide(MonetaryAmount) + */ + @Override + public Money divide(double divisor) { + if (NumberVerifier.isInfinityAndNotNaN(divisor)) { + return Money.of(0, getCurrency()); + } + if (divisor == 1.0d) { + return this; + } + return divide(new BigDecimal(String.valueOf(divisor))); + } + + /* + * (non-Javadoc) + * + * @see + * MonetaryAmount#divideAndRemainder(MonetaryAmount) + */ + @Override + public Money[] divideAndRemainder(long divisor) { + return divideAndRemainder(BigDecimal.valueOf(divisor)); + } + + /* + * (non-Javadoc) + * + * @see + * MonetaryAmount#divideAndRemainder(MonetaryAmount) + */ + @Override + public Money[] divideAndRemainder(double divisor) { + if (NumberVerifier.isInfinityAndNotNaN(divisor)) { + Money zero = Money.of(0, getCurrency()); + return new Money[]{zero, zero}; + } + return divideAndRemainder(new BigDecimal(String.valueOf(divisor))); + } + + /* + * (non-Javadoc) + * + * @see javax.money.MonetaryAmount#multiply(Number) + */ + @Override + public Money multiply(long multiplicand) { + if (multiplicand == 1L) { + return this; + } + return multiply(BigDecimal.valueOf(multiplicand)); + } + + /* + * (non-Javadoc) + * + * @see javax.money.MonetaryAmount#multiply(Number) + */ + @Override + public Money multiply(double multiplicand) { + NumberVerifier.checkNoInfinityOrNaN(multiplicand); + if (multiplicand == 1.0d) { + return this; + } + return multiply(new BigDecimal(String.valueOf(multiplicand))); + } + + /* + * (non-Javadoc) + * + * @see javax.money.MonetaryAmount#remainder(Number) + */ + @Override + public Money remainder(long divisor) { + return remainder(BigDecimal.valueOf(divisor)); + } + + /* + * (non-Javadoc) + * + * @see javax.money.MonetaryAmount#remainder(Number) + */ + @Override + public Money remainder(double divisor) { + if (NumberVerifier.isInfinityAndNotNaN(divisor)) { + return Money.of(0, getCurrency()); + } + return remainder(new BigDecimal(String.valueOf(divisor))); + } + + /* + * (non-Javadoc) + * + * @see javax.money.MonetaryAmount#isZero() + */ + @Override + public boolean isZero() { + return signum() == 0; + } + + /* + * (non-Javadoc) + * + * @see javax.money.MonetaryAmount#isPositive() + */ + @Override + public boolean isPositive() { + return signum() == 1; + } + + /* + * (non-Javadoc) + * + * @see javax.money.MonetaryAmount#isPositiveOrZero() + */ + @Override + public boolean isPositiveOrZero() { + return signum() >= 0; + } + + /* + * (non-Javadoc) + * + * @see javax.money.MonetaryAmount#isNegative() + */ + @Override + public boolean isNegative() { + return signum() == -1; + } + + /* + * (non-Javadoc) + * + * @see javax.money.MonetaryAmount#isNegativeOrZero() + */ + @Override + public boolean isNegativeOrZero() { + return signum() <= 0; + } + + + /* + * (non-Javadoc) + * + * @see javax.money.MonetaryAmount#query(javax.money.MonetaryQuery) + */ + @Override + public R query(MonetaryQuery query) { + Objects.requireNonNull(query); + try { + return query.queryFrom(this); + } catch (MonetaryException e) { + throw e; + } catch (Exception e) { + throw new MonetaryException("Query failed: " + query, e); + } + } + + /* + * (non-Javadoc) + * + * @see javax.money.MonetaryAmount#with(javax.money.MonetaryOperator) + */ + @Override + public Money with(MonetaryOperator operator) { + Objects.requireNonNull(operator); + try { + return Money.class.cast(operator.apply(this)); + } catch (MonetaryException e) { + throw e; + } catch (Exception e) { + throw new MonetaryException("Operator failed: " + operator, e); + } + } + + /* + * (non-Javadoc) + * + * @see javax.money.MonetaryAmount#add(javax.money.MonetaryAmount) + */ + @Override + public Money add(MonetaryAmount amount) { + MoneyUtils.checkAmountParameter(amount, this.currency); + if (amount.isZero()) { + return this; + } + MathContext mc = MoneyUtils.getMathContext(monetaryContext, RoundingMode.HALF_EVEN); + return new Money(this.number.add(amount.getNumber().numberValue(BigDecimal.class), mc), getCurrency(), monetaryContext); + } + + /* + * (non-Javadoc) + * + * @see javax.money.MonetaryAmount#divide(java.lang.Number) + */ + @Override + public Money divide(Number divisor) { + if (NumberVerifier.isInfinityAndNotNaN(divisor)) { + return Money.of(0, getCurrency()); + } + BigDecimal divisorBD = MoneyUtils.getBigDecimal(divisor); + if (divisorBD.equals(BigDecimal.ONE)) { + return this; + } + MathContext mc = MoneyUtils.getMathContext(monetaryContext, RoundingMode.HALF_EVEN); + int maxScale = monetaryContext.getMaxScale(); + if(maxScale>0){ + return new Money(this.number.divide(divisorBD, maxScale, mc.getRoundingMode()), getCurrency(), monetaryContext); + } + return new Money(this.number.divide(divisorBD, mc), getCurrency(), monetaryContext); + } + + @Override + public Money[] divideAndRemainder(Number divisor) { + if (NumberVerifier.isInfinityAndNotNaN(divisor)) { + Money zero = Money.of(0, getCurrency()); + return new Money[]{zero, zero}; + } + BigDecimal divisorBD = MoneyUtils.getBigDecimal(divisor); + if (divisorBD.equals(BigDecimal.ONE)) { + return new Money[]{this, new Money(BigDecimal.ZERO, getCurrency())}; + } + MathContext mc = MoneyUtils.getMathContext(monetaryContext, RoundingMode.HALF_EVEN); + BigDecimal[] dec = this.number.divideAndRemainder(divisorBD, mc); + return new Money[]{new Money(dec[0], getCurrency(), monetaryContext), new Money(dec[1], getCurrency(), monetaryContext)}; + } + + /* + * (non-Javadoc) + * + * @see + * org.javamoney.moneta.AbstractMoney#divideToIntegralValue(java.lang.Number + * ) + */ + @Override + public Money divideToIntegralValue(long divisor) { + return divideToIntegralValue(MoneyUtils.getBigDecimal(divisor)); + } + + @Override + public Money divideToIntegralValue(double divisor) { + if (NumberVerifier.isInfinityAndNotNaN(divisor)) { + return Money.of(0, getCurrency()); + } + return divideToIntegralValue(MoneyUtils.getBigDecimal(divisor)); + } + + @Override + public Money divideToIntegralValue(Number divisor) { + if (NumberVerifier.isInfinityAndNotNaN(divisor)) { + return Money.of(0, getCurrency()); + } + MathContext mc = MoneyUtils.getMathContext(monetaryContext, RoundingMode.HALF_EVEN); + BigDecimal divisorBD = MoneyUtils.getBigDecimal(divisor); + BigDecimal dec = this.number.divideToIntegralValue(divisorBD, mc); + return new Money(dec, getCurrency(), monetaryContext); + } + + /* + * (non-Javadoc) + * + * @see org.javamoney.moneta.AbstractMoney#multiply(java.lang.Number) + */ + @Override + public Money multiply(Number multiplicand) { + NumberVerifier.checkNoInfinityOrNaN(multiplicand); + BigDecimal multiplicandBD = MoneyUtils.getBigDecimal(multiplicand); + if (multiplicandBD.equals(BigDecimal.ONE)) { + return this; + } + MathContext mc = MoneyUtils.getMathContext(monetaryContext, RoundingMode.HALF_EVEN); + BigDecimal dec = this.number.multiply(multiplicandBD, mc); + return new Money(dec, getCurrency(), monetaryContext); + } + + /* + * (non-Javadoc) + * + * @see javax.money.MonetaryAmount#negate() + */ + @Override + public Money negate() { + return new Money(this.number.negate(), getCurrency()); + } + + /* + * (non-Javadoc) + * + * @see javax.money.MonetaryAmount#plus() + */ + @Override + public Money plus() { + return this; + } + + /* + * (non-Javadoc) + * + * @see javax.money.MonetaryAmount#subtract(javax.money.MonetaryAmount) + */ + @Override + public Money subtract(MonetaryAmount subtrahend) { + MoneyUtils.checkAmountParameter(subtrahend, this.currency); + if (subtrahend.isZero()) { + return this; + } + MathContext mc = MoneyUtils.getMathContext(monetaryContext, RoundingMode.HALF_EVEN); + return new Money(this.number.subtract(subtrahend.getNumber().numberValue(BigDecimal.class), mc), getCurrency(), + monetaryContext); + } + + /* + * (non-Javadoc) + * + * @see javax.money.MonetaryAmount#stripTrailingZeros() + */ + @Override + public Money stripTrailingZeros() { + if (isZero()) { + return new Money(BigDecimal.ZERO, getCurrency()); + } + return new Money(this.number.stripTrailingZeros(), getCurrency(), monetaryContext); + } + + /* + * (non-Javadoc) + * + * @see org.javamoney.moneta.AbstractMoney#remainder(java.math.BigDecimal) + */ + @Override + public Money remainder(Number divisor) { + if (NumberVerifier.isInfinityAndNotNaN(divisor)) { + return new Money(BigDecimal.ZERO, getCurrency()); + } + MathContext mc = MoneyUtils.getMathContext(monetaryContext, RoundingMode.HALF_EVEN); + BigDecimal bd = MoneyUtils.getBigDecimal(divisor); + return new Money(this.number.remainder(bd, mc), getCurrency(), monetaryContext); + } + + /* + * (non-Javadoc) + * + * @see javax.money.MonetaryAmount#scaleByPowerOfTen(int) + */ + @Override + public Money scaleByPowerOfTen(int power) { + return new Money(this.number.scaleByPowerOfTen(power), getCurrency(), + monetaryContext); + } + + /* + * (non-Javadoc) + * + * @see javax.money.MonetaryAmount#signum() + */ + @Override + public int signum() { + return this.number.signum(); + } + + /* + * (non-Javadoc) + * + * @see javax.money.MonetaryAmount#isLessThan(javax.money.MonetaryAmount) + */ + @Override + public boolean isLessThan(MonetaryAmount amount) { + MoneyUtils.checkAmountParameter(amount, this.currency); + return number.stripTrailingZeros() + .compareTo(amount.getNumber().numberValue(BigDecimal.class).stripTrailingZeros()) < 0; + } + + /* + * (non-Javadoc) + * + * @see + * javax.money.MonetaryAmount#isLessThanOrEqualTo(javax.money.MonetaryAmount + * ) + */ + @Override + public boolean isLessThanOrEqualTo(MonetaryAmount amount) { + MoneyUtils.checkAmountParameter(amount, this.currency); + return number.stripTrailingZeros() + .compareTo(amount.getNumber().numberValue(BigDecimal.class).stripTrailingZeros()) <= 0; + } + + /* + * (non-Javadoc) + * + * @see javax.money.MonetaryAmount#isGreaterThan(javax.money.MonetaryAmount) + */ + @Override + public boolean isGreaterThan(MonetaryAmount amount) { + MoneyUtils.checkAmountParameter(amount, this.currency); + return number.stripTrailingZeros() + .compareTo(amount.getNumber().numberValue(BigDecimal.class).stripTrailingZeros()) > 0; + } + + /* + * (non-Javadoc) + * + * @see + * javax.money.MonetaryAmount#isGreaterThanOrEqualTo(javax.money.MonetaryAmount + * ) #see + */ + @Override + public boolean isGreaterThanOrEqualTo(MonetaryAmount amount) { + MoneyUtils.checkAmountParameter(amount, this.currency); + return number.stripTrailingZeros() + .compareTo(amount.getNumber().numberValue(BigDecimal.class).stripTrailingZeros()) >= 0; + } + + /* + * (non-Javadoc) + * + * @see javax.money.MonetaryAmount#isEqualTo(javax.money.MonetaryAmount) + */ + @Override + public boolean isEqualTo(MonetaryAmount amount) { + MoneyUtils.checkAmountParameter(amount, this.currency); + return number.stripTrailingZeros() + .compareTo(amount.getNumber().numberValue(BigDecimal.class).stripTrailingZeros()) == 0; + } + + /* + * (non-Javadoc) + * + * @see javax.money.MonetaryAmount#getFactory() + */ + @Override + public MonetaryAmountFactory getFactory() { + return new MoneyAmountFactory().setAmount(this); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof Money) { + Money other = (Money) obj; + return getCurrency().equals(other.getCurrency()) && + this.number.compareTo(other.number) == 0; + } + return false; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + try { + MonetaryAmount amount = Monetary.getDefaultRounding().apply(this); + return defaultFormat().format(amount); + }catch(Exception e) { + return getCurrency().getCurrencyCode() + ' ' + number.toPlainString(); + } + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return Objects.hash(getCurrency(), getNumberStripped()); + } + + /** + * Creates a new instance of {@link Money}, using the default + * {@link MonetaryContext}. + * + * @param number numeric value, not {@code null}. + * @param currency currency unit, not {@code null}. + * @return a {@code Money} combining the numeric value and currency unit. + * @throws ArithmeticException If the number exceeds the capabilities of the default + * {@link MonetaryContext} used. + */ + public static Money of(BigDecimal number, CurrencyUnit currency) { + return new Money(number, currency); + } + + /** + * Creates a new instance of {@link Money}, using an explicit + * {@link MonetaryContext}. + * + * @param number numeric value, not {@code null}. + * @param currency currency unit, not {@code null}. + * @param monetaryContext the {@link MonetaryContext} to be used, if {@code null} the + * default {@link MonetaryContext} is used. + * @return a {@code Money} instance based on the monetary context with the + * given numeric value, currency unit. + * @throws ArithmeticException If the number exceeds the capabilities of the + * {@link MonetaryContext} used. + */ + public static Money of(BigDecimal number, CurrencyUnit currency, MonetaryContext monetaryContext) { + return new Money(number, currency, monetaryContext); + } + + /** + * Creates a new instance of {@link Money}, using the default + * {@link MonetaryContext}. + * + * @param currency The target currency, not null. + * @param number The numeric part, not null. + * @return A new instance of {@link Money}. + * @throws ArithmeticException If the number exceeds the capabilities of the default + * {@link MonetaryContext} used. + */ + public static Money of(Number number, CurrencyUnit currency) { + return new Money(MoneyUtils.getBigDecimal(number), currency); + } + + /** + * Creates a new instance of {@link Money}, using an explicit + * {@link MonetaryContext}. + * + * @param currency The target currency, not null. + * @param number The numeric part, not null. + * @param monetaryContext the {@link MonetaryContext} to be used, if {@code null} the + * default {@link MonetaryContext} is used. + * @return A new instance of {@link Money}. + * @throws ArithmeticException If the number exceeds the capabilities of the + * {@link MonetaryContext} used. + */ + public static Money of(Number number, CurrencyUnit currency, MonetaryContext monetaryContext) { + return new Money(MoneyUtils.getBigDecimal(number), currency, monetaryContext); + } + + /** + * Static factory method for creating a new instance of {@link Money}. + * + * @param currencyCode The target currency as ISO currency code. + * @param number The numeric part, not null. + * @return A new instance of {@link Money}. + */ + public static Money of(Number number, String currencyCode) { + return new Money(MoneyUtils.getBigDecimal(number), Monetary.getCurrency(currencyCode)); + } + + /** + * Static factory method for creating a new instance of {@link Money}. + * + * @param currencyCode The target currency as ISO currency code. + * @param number The numeric part, not null. + * @return A new instance of {@link Money}. + */ + public static Money of(BigDecimal number, String currencyCode) { + return new Money(number, Monetary.getCurrency(currencyCode)); + } + + /** + * Static factory method for creating a new instance of {@link Money}. + * + * @param currencyCode The target currency as ISO currency code. + * @param number The numeric part, not null. + * @param monetaryContext the {@link MonetaryContext} to be used, if {@code null} the + * default {@link MonetaryContext} is used. + * @return A new instance of {@link Money}. + */ + public static Money of(Number number, String currencyCode, MonetaryContext monetaryContext) { + return new Money(MoneyUtils.getBigDecimal(number), Monetary.getCurrency(currencyCode), + monetaryContext); + } + + /** + * Static factory method for creating a new instance of {@link Money}. + * + * @param currencyCode The target currency as ISO currency code. + * @param number The numeric part, not null. + * @param monetaryContext the {@link MonetaryContext} to be used, if {@code null} the + * default {@link MonetaryContext} is used. + * @return A new instance of {@link Money}. + */ + public static Money of(BigDecimal number, String currencyCode, MonetaryContext monetaryContext) { + return new Money(number, Monetary.getCurrency(currencyCode), monetaryContext); + } + + /** + * Obtains an instance of {@link Money} representing zero. + * @param currency the currency, not null. + * @return an instance of {@link Money} representing zero. + * @since 1.0.1 + */ + public static Money zero(CurrencyUnit currency) { + return new Money(BigDecimal.ZERO, currency); + } + + /** + * Obtains an instance of {@code Money} from an amount in minor units. + * For example, {@code ofMinor(USD, 1234)} creates the instance {@code USD 12.34}. + * @param currency the currency + * @param amountMinor the amount of money in the minor division of the currency + * @return the Money from minor units + * @throws NullPointerException when the currency is null + * @throws IllegalArgumentException when {@link CurrencyUnit#getDefaultFractionDigits()} is lesser than zero. + * @see CurrencyUnit#getDefaultFractionDigits() + * @since 1.0.1 + */ + public static Money ofMinor(CurrencyUnit currency, long amountMinor) { + return ofMinor(currency, amountMinor, currency.getDefaultFractionDigits()); + } + + /** + * Obtains an instance of {@code Money} from an amount in minor units. + * For example, {@code ofMinor(USD, 1234, 2)} creates the instance {@code USD 12.34}. + * @param currency the currency, not null + * @param amountMinor the amount of money in the minor division of the currency + * @param fractionDigits number of digits + * @return the monetary amount from minor units + * @see CurrencyUnit#getDefaultFractionDigits() + * @see Money#ofMinor(CurrencyUnit, long, int) + * @throws NullPointerException when the currency is null + * @throws IllegalArgumentException when the fractionDigits is negative + * @since 1.0.1 + */ + public static Money ofMinor(CurrencyUnit currency, long amountMinor, int fractionDigits) { + if(fractionDigits < 0) { + throw new IllegalArgumentException("The fractionDigits cannot be negative"); + } + return of(BigDecimal.valueOf(amountMinor, fractionDigits), currency); + } + + /** + * Converts (if necessary) the given {@link MonetaryAmount} to a + * {@link Money} instance. The {@link MonetaryContext} will be adapted as + * necessary, if the precision of the given amount exceeds the capabilities + * of the default {@link MonetaryContext}. + * + * @param amt the amount to be converted + * @return an according Money instance. + */ + public static Money from(MonetaryAmount amt) { + if (amt.getClass() == Money.class) { + return (Money) amt; + } + return Money.of(amt.getNumber().numberValue(BigDecimal.class), amt.getCurrency(), amt.getContext()); + } + + /** + * Obtains an instance of Money from a text string such as 'EUR 25.25'. + * + * @param text the text to parse not null + * @return Money instance + * @throws NumberFormatException if the amount is not a number + * @throws UnknownCurrencyException if the currency cannot be resolved + */ + public static Money parse(CharSequence text) { + return parse(text, defaultFormat()); + } + + /** + * Obtains an instance of Money from a text using specific formatter. + * + * @param text the text to parse not null + * @param formatter the formatter to use not null + * @return Money instance + */ + public static Money parse(CharSequence text, MonetaryAmountFormat formatter) { + return from(formatter.parse(text)); + } + + private static MonetaryAmountFormat defaultFormat() { + String useDefault = MonetaryConfig.getConfig().getOrDefault("org.javamoney.moneta.useJDKdefaultFormat", "false"); + try{ + if(Boolean.parseBoolean(useDefault)){ + Logger.getLogger(Money.class.getName()).info("Using JDK formatter for toString()."); + return MonetaryAmountDecimalFormat.of(); + }else{ + Logger.getLogger(Money.class.getName()).info("Using default formatter for toString()."); + return ToStringMonetaryAmountFormat.of(ToStringMonetaryAmountFormatStyle.MONEY); + } + }catch(Exception e){ + Logger.getLogger(Money.class.getName()).log(Level.WARNING, + "Invalid boolean parameter for 'org.javamoney.moneta.useJDKdefaultFormat', " + + "using default formatter for toString()."); + return ToStringMonetaryAmountFormat.of(ToStringMonetaryAmountFormatStyle.MONEY); + } + } + + /** + * Just to don't break the compatibility. + * Don't use it + * @param number the amount, not null + * @deprecated Will be removed. + */ + @Deprecated + public static boolean isInfinityAndNotNaN(Number number) { + if (Double.class == number.getClass() || Float.class == number.getClass()) { + double dValue = number.doubleValue(); + if (!Double.isNaN(dValue) && Double.isInfinite(dValue)) { + return true; + } + } + return false; + } + + + /** + * Just to don't break the compatibility. + * Don't use it + * @param number the amount, not null. + * @deprecated Will be removed. Use + * @see NumberVerifier#checkNoInfinityOrNaN + */ + @Deprecated + public static void checkNoInfinityOrNaN(Number number) { + NumberVerifier.checkNoInfinityOrNaN(number); + } + +} diff --git a/moneta-core/src/main/resources/META-INF/services/org.javamoney.moneta.spi.MonetaryConfigProvider b/moneta-core/src/main/resources/META-INF/services/org.javamoney.moneta.spi.MonetaryConfigProvider index 4a130a676..b71877496 100644 --- a/moneta-core/src/main/resources/META-INF/services/org.javamoney.moneta.spi.MonetaryConfigProvider +++ b/moneta-core/src/main/resources/META-INF/services/org.javamoney.moneta.spi.MonetaryConfigProvider @@ -1,15 +1,15 @@ -# 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. -# +# Copyright (c) 2012, 2020 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. +# org.javamoney.moneta.spi.DefaultConfigProvider \ No newline at end of file diff --git a/moneta/pom.xml b/moneta/pom.xml index f28edd5c8..822234aaa 100644 --- a/moneta/pom.xml +++ b/moneta/pom.xml @@ -14,7 +14,7 @@ org.javamoney moneta-parent - 1.4.2-SNAPSHOT + 1.4.2 moneta pom diff --git a/pom.xml b/pom.xml index f793c5706..7df2577c5 100644 --- a/pom.xml +++ b/pom.xml @@ -13,9 +13,9 @@ org.javamoney javamoney-parent - 1.3-SNAPSHOT + 1.3 - 1.4.2-SNAPSHOT + 1.4.2 moneta-parent pom Moneta Parent