/*******************************************************************************
* Copyright (c) 2005-2010, G. Weirich and Elexis
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* G. Weirich - initial implementation
*
*******************************************************************************/
package ch.rgw.tools;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import java.util.Locale;
/**
* A class representing Money (as you might have guessed) Simplifies and standardizes calculations
* with money (rounding, converting to and from strings etc.) The accepted formats of the string
* representation of an amount depends on the current locale by default the format of can be set to
* another locale if necessary (globally) The term "Amount" means here always the x.xx form of Money
*
* @author gerry
*
*/
public class Money extends Number implements Comparable<Money> {
private final static String formatPattern = "#,##0.00";
private final static DecimalFormatSymbols symbols = new DecimalFormatSymbols();
private static final long serialVersionUID = 7466555366749958L;
private static NumberFormat nf = NumberFormat.getInstance(Locale.getDefault());
private int cents; // The value of this money
private double frac; // What rests after rounding
/**
* Create empty Money
*/
public Money(){
cents = 0;
}
/** Double your wealth */
public Money(Money money){
cents = money.cents;
frac = money.frac;
}
/** Create Money with some cents */
public Money(int cent){
cents = cent;
}
/** Create Money with a specified amount */
public Money(double amount){
cents = (int) Math.round(amount * 100.0);
}
/**
* Create Money with a specified amount as String This might fail if the string doesn't conform
* to the current locale's standard currency format
*
* @throws ParseException
*/
public Money(String val) throws ParseException{
addAmount(checkInput(val).doubleValue());
}
/**
* Parse an amount given as string This might fail if the string doesn't conform to the current
* locale's standard currency format. Note: In Switzerland, Separator is '.' while in Germany,
* Austria and Others, it is ','. So if some Computers in the network have an OS locale setting
* of Switzerland and others a german, amounts are entered differently. And even more: The
* MacOSX does not use swiss layout at all but sees always ',' as separator. So if Macintosh,
* Windows and Linux PC's are in he same network, we must make sure, that the decimal comma or
* dot is always honored correctly.
*
* @param val
* an amount
* @return a Number representing the mount
* @throws ParseException
*/
public static Number checkInput(String rawValue) throws ParseException{
Number num;
if (StringTool.isNothing(rawValue)) {
num = new Double(0.0);
} else {
String val = rawValue.trim();
num = parse(val);
}
return num;
}
private static Double parse(String raw) throws ParseException{
// try ch format 123'456.789
ParsePosition parsePosition = new ParsePosition(0);
symbols.setDecimalSeparator('.');
symbols.setGroupingSeparator('\'');
DecimalFormat format = new DecimalFormat(formatPattern, symbols);
Number ret = format.parse(raw, parsePosition);
if (ret != null && parsePosition.getIndex() == raw.length())
return ret.doubleValue();
// try de format 123.456,789
parsePosition = new ParsePosition(0);
symbols.setDecimalSeparator(',');
symbols.setGroupingSeparator('.');
format = new DecimalFormat(formatPattern, symbols);
ret = format.parse(raw, parsePosition);
if (ret != null && parsePosition.getIndex() == raw.length())
return ret.doubleValue();
// the string could not be parsed by the known formats
throw new ParseException(raw, 1);
}
/** Add some cents */
public void addCent(String cents){
String cleanValue = cents != null ? cents.trim() : "0";
this.cents += Integer.parseInt(cleanValue);
}
/** Add some cents */
public void addCent(int cents){
this.cents += cents;
}
/** Add some money as x.xx */
public void addAmount(double amount){
cents += Math.round(amount * 100.0);
}
/** Add some money as "x.xx" */
public void addAmount(String amount) throws ParseException{
addAmount(checkInput(amount).doubleValue());
}
/** Add even more (or less) Money */
public Money addMoney(Money money){
cents += money.cents;
frac += money.frac;
if (Math.abs(frac) >= 1.0) {
cents += Math.signum(frac);
frac -= Math.signum(frac);
}
return this;
}
/** Reduce your wealth */
public Money subtractMoney(Money money){
cents -= money.cents;
frac -= money.frac;
if (frac < 0) {
frac += 1;
cents -= 1;
}
return this;
}
/** Return all the cents (but keep them anyway :-) */
public int getCents(){
return cents;
}
/** return the collected amount as x.xx */
public double getAmount(){
return cents / 100.0;
}
/** return the cents */
public String getCentsAsString(){
return Integer.toString(getCents());
}
/** return the amount as "x.xx" */
public String getAmountAsString(){
nf.setMinimumFractionDigits(2);
nf.setMaximumFractionDigits(2);
String frm = nf.format(getAmount());
return frm;
}
/** Round the collected amount to the nearest 0.05 */
public Money roundTo5(){
frac = cents;
double sum = cents / 100.0;
cents = (int) (5 * Math.round(sum * 20.0));
frac -= cents;
return this;
}
/** Return what was over or missing after rounding */
public double getFrac(){
return frac;
}
public String getFracAsString(){
return Double.toString(frac);
}
/**
* Multiply your wealth
*
* @param factor
*/
public Money multiply(double factor){
cents = (int) Math.round(cents * factor);
return this;
}
/**
* Turn wealth into dept and vice versa
*
* @return multiply(-1.0)
*/
public Money negate(){
return multiply(-1.0);
}
/**
* Are you broke?
*
* @return true if you are broke
*/
public boolean isZero(){
return (cents == 0);
}
public boolean isMoreThan(Money other){
return cents > other.cents;
}
/**
* Are you in dept?
*
* @return true if you are
*/
public boolean isNegative(){
if (cents < 0) {
return true;
} else if (cents == 0) {
return frac < 0;
} else {
return false;
}
}
/**
* Is it worth any effort?
*
* @return true if not.
*/
public boolean isNeglectable(){
return Math.abs(cents) < 3;
}
@Override
public String toString(){
return getAmountAsString();
}
/**
* Set a different locale for handling Money
*
* @param locale
*/
public static void setLocale(Locale locale){
nf = NumberFormat.getInstance(locale);
}
@Override
public boolean equals(Object obj){
if (!(obj instanceof Money)) {
return false;
}
Money other = (Money) obj;
return cents == other.cents;
}
@Override
public int hashCode(){
return cents;
}
public int compareTo(Money other){
return this.cents - other.getCents();
}
@Override
public double doubleValue(){
return this.getAmount();
}
@Override
public float floatValue(){
return new Float(this.getAmount());
}
@Override
public int intValue(){
return this.getCents();
}
@Override
public long longValue(){
return (long) this.getCents();
}
public static char getSeparator(){
double d = 1.0 / 2.0;
String ds = NumberFormat.getCurrencyInstance().format(d);
return ds.charAt(1);
}
}