/* * Copyright (C) 2011 4th Line GmbH, Switzerland * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.fourthline.konto.shared; import org.fourthline.konto.shared.entity.MonetaryUnit; import java.io.Serializable; import java.math.BigDecimal; import java.math.RoundingMode; /** * @author Christian Bauer */ public class MonetaryAmount implements Serializable { public static final char SEPARATOR_THOUSANDS = '\''; public static final char SEPARATOR_FRACTION = '.'; // Well that depends on toString() of BigDecimal protected MonetaryUnit unit; protected BigDecimal value; public MonetaryAmount() { } public MonetaryAmount(MonetaryAmount other) { this(other.getUnit(), other.getValue()); } public MonetaryAmount(MonetaryUnit unit) { this(unit, 0); } public MonetaryAmount(MonetaryUnit unit, BigDecimal value) { this(unit, value == null ? "0" : value.toString()); } public MonetaryAmount(MonetaryUnit unit, String value) { if (unit == null) throw new NullPointerException("MonetaryUnit is required"); this.unit = unit; this.value = new BigDecimal(value) .setScale(unit.getFractionDigits(), unit.getRoundingMode()); } public MonetaryAmount(MonetaryUnit unit, int value) { if (unit == null) throw new NullPointerException("MonetaryUnit is required"); this.unit = unit; this.value = new BigDecimal(value) .setScale(unit.getFractionDigits(), unit.getRoundingMode()); } public MonetaryUnit getUnit() { return unit; } public BigDecimal getValue() { return value; } public MonetaryAmount abs() { return newInstance(getUnit(), getValue().abs()); } public MonetaryAmount subtract(MonetaryAmount amount) { return newInstance(getUnit(), getValue().subtract(amount.getValue())); } public MonetaryAmount add(MonetaryAmount amount) { return newInstance(getUnit(), getValue().add(amount.getValue())); } public MonetaryAmount divide(MonetaryAmount amount) { return newInstance( getUnit(), getValue().divide( amount.getValue(), getUnit().getFractionDigits(), getUnit().getRoundingMode() ) ); } public MonetaryAmount negate() { return newInstance(getUnit(), getValue().negate()); } public int signum() { return getValue().signum(); } public String getString() { return getValue().toString(); } public String getReportString() { return getReportString(false, false, false); } public String getReportString(boolean prefix, boolean suffix) { return getReportString(prefix, suffix, false); } public String getReportString(boolean prefix, boolean suffix, boolean emptyIfZero) { return getReportString(prefix, suffix, emptyIfZero, false); } public String getReportString(boolean prefix, boolean suffix, boolean emptyIfZero, boolean roundFractions) { if (emptyIfZero && signum() == 0) return ""; String text = roundFractions ? new BigDecimal(getValue().toString()).setScale(0, RoundingMode.HALF_UP).toString() : getString(); // We have no regex, so it's kinda ugly if you want "thousands" seperators StringBuilder sb = new StringBuilder(); boolean inFraction = text.contains("" + SEPARATOR_FRACTION); int thousands = inFraction ? 0 : 1; char[] chars = text.toCharArray(); for (int i = chars.length - 1; i >= 0; i--) { char c = chars[i]; sb.append(c); if (c == SEPARATOR_FRACTION) inFraction = false; if (!inFraction) { if (thousands == 3) { thousands = 0; sb.append(SEPARATOR_THOUSANDS); } thousands++; } } if (sb.charAt(sb.length() - 1) == SEPARATOR_THOUSANDS) sb.deleteCharAt(sb.length() - 1); if (sb.charAt(sb.length() - 1) == '-' && sb.charAt(sb.length() - 2) == SEPARATOR_THOUSANDS) sb.deleteCharAt(sb.length() - 2); // GWT doesn't emulate sb.reverse() char[] rev = sb.toString().toCharArray(); StringBuilder revsb = new StringBuilder(); for (int i = rev.length - 1; i >= 0; i--) { revsb.append(rev[i]); } StringBuilder sb2 = new StringBuilder(); if (prefix) sb2.append(getUnit().getPrefix()).append(" "); sb2.append(revsb.toString()); if (suffix) sb2.append(" ").append(getUnit().getCurrencyCode()); return sb2.toString(); } public boolean requiresCurrencyExchange(MonetaryUnit monetaryUnit) { String currencyCode = getUnit().getCurrencyCode(); return !currencyCode.equals(monetaryUnit.getCurrencyCode()); } protected MonetaryAmount newInstance(MonetaryUnit unit, BigDecimal value) { return new MonetaryAmount(unit, value); } @Override public String toString() { return getValue() + " " + getUnit(); } }