Package org.jugile.util

Source Code of org.jugile.util.Money

package org.jugile.util;

/*

Copyright (C) 2007-2011 Jukka Rahkonen  email: jukka.rahkonen@iki.fi

This library 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 2.1 of the License, or (at your option) any later version.

This library 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 this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

*/

import java.io.Serializable;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

/**
* "No man can serve two masters: for either he will hate the one, and love the other;
*  or else he will hold to the one, and despise the other.
*  Ye cannot serve God and mammon." (Math. 6:24)
*
* ==========
*
* Immutable Money class for all money type handling consistently.
*
* Money should not be any kind of decimal number. No BigDecimal or any other
* floating point number. Money should be long integer, representing cents.
* Sometimes prices can have even fragments of cents and they can be presented
* as BigDecimal. Money is an accountable amount of money that can be part of
* money transaction. (RAHKOJUK)
*
* Btw, BigDecimal is buggy. (2009). Try how long does it take to run this simple loop:
* BigDecimal bd = new BigDecimal("1.0"); for (int i = 0; i < 1000; i++) { bd =
* bd.multiply(bd); } System.out.println("result: " + bd); (RAHKOJUK)
*
* @author jukka.rahkonen@iki.fi
*/
public final class Money extends Jugile implements Serializable, Comparable<Money> {
  private static final long serialVersionUID = 1L;
  private static final Locale LOCALE_FI = new Locale("fi", "FI");

  public enum Currency {
    EUR, FIM,
  }

  private final long cents;      // immutable
  private final Currency currency;   // immutable

  public Money(long cents) {
    this.cents = cents;
    this.currency = Currency.EUR;
  }

  public Money(long cents, Currency c) {
    this.cents = cents;
    this.currency = c;
  }

  public Money(double d) {
    this.cents = toCents(d);
    this.currency = Currency.EUR;
  }

  public Money(double d, Currency c) {
    this.cents = toCents(d);
    this.currency = c;
  }

  public Money(String str) {
    cents = parse(str);
    currency = Currency.EUR;
  }

  public Money(String str, Currency c) {
    cents = parse(str);
    currency = c;
  }

  public Money(BigDecimal bd) {
    if (bd == null)
      cents = 0;
    else
      cents = toCents(bd.doubleValue());
    currency = Currency.EUR;
  }

  public Money(BigDecimal bd, Currency c) {
    if (bd == null)
      cents = 0;
    else
      cents = toCents(bd.doubleValue());
    currency = c;
  }

  public long getCents() {
    return cents;
  }
 
  public Currency getCurrency() {
    return currency;
  }

  public BigDecimal getBigDecimal() {
    BigDecimal c = new BigDecimal(cents);
    return c.divide(new BigDecimal(100));
  }

  /**
   * Get the integer value without cents. eg. 1,55 euros return 1.
   */
  public int getInteger() {
    return (int)cents/100;
  }

  /**
   * Get rounded value to nearest integer without cents. eg. 1,49 euros return
   * 1.
   */
  public int getRoundedInteger() {
    return (int) Math.round(nextUp(cents / 100.0));
  }

  public String toString() {
    if (cents == 0)
      return "0,00";
        DecimalFormatSymbols symbols = new DecimalFormatSymbols(LOCALE_FI);
        symbols.setGroupingSeparator(' ');
        DecimalFormat f = new DecimalFormat("#,##0.00", symbols);
        f.setDecimalSeparatorAlwaysShown(true);
        f.setMaximumFractionDigits(2);
        f.setMinimumFractionDigits(2);
        f.setMinimumIntegerDigits(1);
        return f.format(cents / 100.0);
    }

  public String toStringIntegerValueWithCurrency() {
    String c = "€";
    if (currency == Currency.FIM)
      c = "mk";
    return getInteger()+ " " + c;
  }
 
  public String toStringWithCurrency() {
    String c = "€";
    if (currency == Currency.FIM)
      c = "mk";
    return toString() + " " + c;
  }

  private long parse(String str) {
    if (str == null || str.trim().length() == 0) return 0;
    // remove all whites
    str = str.trim();
    str = str.replaceAll("\\s", "");
    str = str.replaceAll(" ", ""); // some other white space (utf8?)
    str = str.replaceAll(",", ".");
    double d = Double.parseDouble(str);
    return toCents(d);
  }

  /**
   * Does the correct rounding for positive double values to cents.
   */
  public static long toCents(double d) {
    // nextUp is needed for getting 1.005 correctly rounded to 1.01
    return Math.round(nextUp(d * 100.0));
  }

  public Money plus(Money m) {
    assertSameCurrency(m);
    return new Money(cents + m.cents, currency);
  }

  public Money minus(Money m) {
    assertSameCurrency(m);
    return new Money(cents - m.cents, currency);
  }

  public Money neg() {
    return new Money(-this.cents, currency);
  }

  public int compareTo(Money m) {
    assertSameCurrency(m);
    return (new Long(cents)).compareTo(m.cents);
  }

  public Money mult(double d) {
    return new Money(toCents((cents / 100.0) * d), currency);
  }

  public Money divBy(double d) {
    if (d == 0)
      throw new RuntimeException("divided by zero");
    return new Money(toCents((cents / 100.0) / d), currency);
  }

  /**
   * Common case for dividing Money to some parts where rounded amounts of
   * parts must equals to original sum.
   *
   * @param numberOfParts
   *            number of parts to divide to.
   * @return List of Money parts. Last one has rounding correction (modulo).
   */
  public List<Money> divToParts(int numberOfParts) {
    if (numberOfParts == 0)
      throw new RuntimeException("divided by zero");
    List<Money> res = new ArrayList<Money>();
    Money a = divBy(numberOfParts);
    long modulo = cents - numberOfParts * a.cents;
    for (int i = 0; i < numberOfParts; i++) {
      Money m = new Money(a.cents);
      if (i == (numberOfParts - 1))
        m = new Money(a.cents + modulo);
      res.add(m);
    }
    return res;
  }

  /**
   * @param d
   * @return next double towards positive infinity
   */
  private static double nextUp(double d) {
    if (Double.isNaN(d) || d == (1.0D / 0.0D)) {
      return d;
    } else {
      d += 0.0D;
      return Double.longBitsToDouble(Double.doubleToRawLongBits(d)
          + (d < 0.0D ? -1L : 1L));
    }
  }

  private void assertSameCurrency(Money m) {
    if (m.currency != currency)
      throw new RuntimeException("currency mismatch");
  }


  public boolean equals(Money m) {
    if (m == null) return false;
    return m.cents == cents;
  }
   
}
TOP

Related Classes of org.jugile.util.Money

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.