/*
* @(#)Money.java
*
* Copyright 2009 Instituto Superior Tecnico
* Founding Authors: João Figueiredo, Luis Cruz, Paulo Abrantes, Susana Fernandes
*
* https://fenix-ashes.ist.utl.pt/
*
* This file is part of the Bennu Web Application Infrastructure.
*
* The Bennu Web Application Infrastructure is free software: you can
* redistribute it and/or modify it under the terms of the GNU Lesser General
* Public License as published by the Free Software Foundation, either version
* 3 of the License, or (at your option) any later version.
*
* Bennu is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Bennu. If not, see <http://www.gnu.org/licenses/>.
*
*/
package module.finance.util;
import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.util.Currency;
import java.util.Locale;
import org.fenixedu.commons.i18n.I18N;
/**
*
* @author João Neves
* @author Luis Cruz
*
*/
public class Money implements Serializable, Comparable<Money> {
private static final long serialVersionUID = -6802128424337363522L;
private static final String SEPERATOR = ":";
public static final Money ZERO = new Money("0");
private final Currency currency;
private final BigDecimal value;
protected Money(final Currency currency, final BigDecimal value) {
if (currency == null || value == null) {
throw new IllegalArgumentException("error.wrong.init.money.args");
}
this.currency = currency;
this.value = value;
}
public Money(final BigDecimal value) {
this(Currency.getInstance(Locale.getDefault()), value);
}
public Money(final String value) {
this(Currency.getInstance(Locale.getDefault()), new BigDecimal(value));
}
private Money newMoney(BigDecimal value) {
return new Money(getCurrency(), value);
}
public String serialize() {
final StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(getCurrency().getCurrencyCode());
stringBuilder.append(SEPERATOR);
stringBuilder.append(getValue().toString());
return stringBuilder.toString();
}
public Money percentage(final BigDecimal percentage) {
return newMoney(valuePercentage(percentage));
}
private BigDecimal valuePercentage(final BigDecimal percentage) {
return getValue().multiply(percentage.divide(new BigDecimal(100)));
}
public Money addPercentage(final BigDecimal percentage) {
return newMoney(getValue().add(valuePercentage(percentage)));
}
public Money subtractPercentage(final BigDecimal percentage) {
return newMoney(getValue().subtract(valuePercentage(percentage)));
}
public Money addAndRound(final Money money) {
checkCurreny(money);
return newMoney(getValue().add(money.getRoundedValue()));
}
public Money add(final Money money) {
checkCurreny(money);
return newMoney(getValue().add(money.getValue()));
}
public Money subtract(final Money money) {
checkCurreny(money);
return newMoney(getValue().subtract(money.getValue()));
}
public Money multiply(final BigDecimal mult) {
return newMoney(getValue().multiply(mult));
}
public Money multiplyAndRound(final BigDecimal mult) {
return newMoney(getValue().multiply(mult).setScale(getScale(), RoundingMode.HALF_EVEN));
}
public Money multiply(final long mult) {
return multiply(BigDecimal.valueOf(mult));
}
public Money divideAndRound(final BigDecimal divisor) {
return newMoney(getValue().divide(divisor, getScale(), RoundingMode.HALF_EVEN));
}
public boolean isPositive() {
return getValue().signum() == 1;
}
public boolean isZero() {
return getValue().signum() == 0;
}
public boolean isNegative() {
return getValue().signum() == -1;
}
public boolean isLessThan(final Money money) {
return this.compareTo(money) < 0;
}
public boolean isGreaterThan(final Money money) {
return this.compareTo(money) > 0;
}
public boolean isLessThanOrEqual(final Money money) {
return this.compareTo(money) <= 0;
}
public boolean isGreaterThanOrEqual(final Money money) {
return this.compareTo(money) >= 0;
}
protected void checkCurreny(Money money) {
if (!this.getCurrency().equals(money.getCurrency())) {
throw new IllegalArgumentException("error.diferent.currencies");
}
}
private int getScale() {
return getCurrency().getDefaultFractionDigits();
}
public Money[] allocate(int n) {
Money lowResult = newMoney(getValue().divide(BigDecimal.valueOf(n), getScale(), RoundingMode.FLOOR));
BigDecimal remainder = getValue().subtract(lowResult.getValue().multiply(BigDecimal.valueOf(n)));
BigDecimal addingUnit = BigDecimal.valueOf(1).movePointLeft(getScale());
Money[] results = new Money[n];
for (int i = 0; i < n; i++) {
if (remainder.compareTo(BigDecimal.ZERO) > 0) {
results[i] = lowResult.add(newMoney(addingUnit));
remainder = remainder.subtract(addingUnit);
} else {
results[i] = lowResult;
}
}
return results;
}
public static Money deserialize(final String serializedMoney) {
final int seperatorIndex = serializedMoney.indexOf(SEPERATOR);
final String currencyCode = serializedMoney.substring(0, seperatorIndex);
final String valueString = serializedMoney.substring(seperatorIndex + 1);
final BigDecimal value = new BigDecimal(valueString);
return new Money(Currency.getInstance(currencyCode), value);
}
public Currency getCurrency() {
return currency;
}
public BigDecimal getValue() {
return value;
}
@Override
public int compareTo(Money money) {
checkCurreny(money);
return getValue().compareTo(money.getValue());
}
@Override
public int hashCode() {
return getValue().hashCode();
}
@Override
public boolean equals(Object obj) {
return (obj instanceof Money) && equals((Money) obj);
}
public boolean equals(Money money) {
return getRoundedValue().compareTo(money.getRoundedValue()) == 0 && getCurrency().equals(money.getCurrency());
}
private NumberFormat getCurrencyFormat() {
NumberFormat numberFormat = NumberFormat.getCurrencyInstance(I18N.getLocale());
numberFormat.setCurrency(getCurrency());
return numberFormat;
}
public BigDecimal getRoundedValue() {
return getValue().setScale(getCurrencyFormat().getMaximumFractionDigits(), RoundingMode.HALF_EVEN);
}
public String toFormatString() {
return getCurrencyFormat().format(getRoundedValue().doubleValue());
}
public String toFormatStringWithoutCurrency() {
NumberFormat numberInstance = NumberFormat.getNumberInstance(I18N.getLocale());
numberInstance.setMinimumFractionDigits(getCurrencyFormat().getMaximumFractionDigits());
return numberInstance.format(getRoundedValue().doubleValue());
}
public Money round() {
return new Money(getRoundedValue());
}
public String exportAsString() {
return serialize();
}
public static Money importFromString(String string) {
return deserialize(string);
}
}