Android Open Source - gnucash-android Money






From Project

Back to project page gnucash-android.

License

The source code is released under:

Apache License

If you think the Android project gnucash-android listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright (c) 2012 - 2014 Ngewi Fet <ngewif@gmail.com>
 */*from  w w w. jav a  2 s.c om*/
 * 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.gnucash.android.model;

import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Currency;
import java.util.Locale;

/**
 * Money represents a money amount and a corresponding currency.
 * Money internally uses {@link BigDecimal} to represent the amounts, which enables it 
 * to maintain high precision afforded by BigDecimal. Money objects are immutable and
 * most operations return new Money objects.
 * Money String constructors should not be passed any locale-formatted numbers. Only
 * {@link Locale#US} is supported e.g. "2.45" will be parsed as 2.45 meanwhile 
 * "2,45" will be parsed to 245 although that could be a decimal in {@link Locale#GERMAN}
 * 
 * @author Ngewi Fet<ngewif@gmail.com>
 *
 */
public final class Money implements Comparable<Money>{

  /**
   * Currency of the account
   */
  private Currency mCurrency;
  
  /**
   * Amount value held by this object
   */
  private BigDecimal mAmount;
  
  /**
   * Default rounding mode for Money objects
   * Defaults to {@link RoundingMode#HALF_EVEN}
   */
  private static final RoundingMode DEFAULT_ROUNDING_MODE = RoundingMode.HALF_EVEN;
  
  /**
   * Number of decimal places to limit the fractions to when performing operations
   * Defaults to 2 decimal places
   */
  private static final int DEFAULT_DECIMAL_PLACES = 2;
  
  /**
   * Rounding mode to be applied when performing operations
   * Defaults to {@link #DEFAULT_ROUNDING_MODE}
   */
  protected RoundingMode ROUNDING_MODE = DEFAULT_ROUNDING_MODE;
  
  /**
   * Number of decimal places to limit fractions to in arithmetic operations
   * Defaults to {@link #DEFAULT_DECIMAL_PLACES}
   */
  protected int DECIMAL_PLACES = DEFAULT_DECIMAL_PLACES;

  /**
   * Default currency code (according ISO 4217) 
   * This is typically initialized to the currency of the device default locale,
   * otherwise US dollars are used
   */
  public static String DEFAULT_CURRENCY_CODE   = "USD";

    /**
     * A zero instance with the currency of the default locale.
     * This can be used anywhere where a starting amount is required without having to create a new object
     */
    private static final Money sDefaultZero = Money.createZeroInstance(Currency.getInstance(Locale.getDefault()).getCurrencyCode());

    /**
     * Returns a Money instance initialized to the local currency and value 0
     * @return Money instance of value 0 in locale currency
     */
    public static Money getZeroInstance(){
        return sDefaultZero;
    }

  /**
   * Default constructor
   * Initializes the object with an amount of 0 and currency set to the device default locale
   */
  public Money() {
    init();
  }
  
  /**
   * Overloaded constructor
   * @param amount {@link BigDecimal} value of the money instance
   * @param currency {@link Currency} associated with the <code>amount</code>
   */
  public Money(BigDecimal amount, Currency currency){    
    this.mAmount = amount;
    this.mCurrency = currency;
  }
  
  /**
   * Overloaded constructor.
   * Accepts strings as arguments and parses them to create the Money object
   * @param amount Numrical value of the Money
   * @param currencyCode Currency code as specified by ISO 4217
   */
  public Money(String amount, String currencyCode){
    setAmount(amount);
    setCurrency(Currency.getInstance(currencyCode));
  }
  
  /**
   * Overloaded constructor
   * Accepts <code>context</code> options for rounding mode during operations on this money object
   * @param amount {@link BigDecimal} value of the money instance
   * @param currency {@link Currency} associated with the <code>amount</code>
   * @param context {@link MathContext} specifying rounding mode during operations
   */
  public Money(BigDecimal amount, Currency currency, MathContext context){
    setAmount(amount);
    setCurrency(currency);
    ROUNDING_MODE = context.getRoundingMode();
    DECIMAL_PLACES = context.getPrecision();
  }
  
  /**
   * Overloaded constructor. 
   * Initializes the currency to that specified by {@link Money#DEFAULT_CURRENCY_CODE}
   * @param amount Value associated with this Money object
   */
  public Money(String amount){
    init();
    setAmount(parseToDecimal(amount));
  }

  /**
   * Overloaded constructor. 
   * Initializes the currency to that specified by {@link Money#DEFAULT_CURRENCY_CODE}
   * @param amount Value associated with this Money object
   */
  public Money(BigDecimal amount){
    init();
    setAmount(amount);
  }

    /**
     * Copy constructor.
     * Creates a new Money object which is a clone of <code>money</code>
     * @param money Money instance to be cloned
     */
    public Money(Money money){
        setAmount(money.asBigDecimal());
        setCurrency(money.getCurrency());
    }

    /**
     * Creates a new Money instance with 0 amount and the <code>currencyCode</code>
     * @param currencyCode Currency to use for this money instance
     * @return Money object with value 0 and currency <code>currencyCode</code>
     */
    public static Money createZeroInstance(String currencyCode){
        return new Money("0", currencyCode);
    }

  /**
   * Initializes the amount and currency to their default values
   * @see {@link Money#DEFAULT_CURRENCY_CODE}, {@link #DEFAULT_ROUNDING_MODE}, {@link #DEFAULT_DECIMAL_PLACES}
   */
  private void init(){
    mCurrency = Currency.getInstance(Money.DEFAULT_CURRENCY_CODE);
    mAmount = new BigDecimal(0).setScale(DEFAULT_DECIMAL_PLACES, DEFAULT_ROUNDING_MODE);
  }

  /**
   * Returns the currency of the money object
   * @return {@link Currency} of the money value
   */
  public Currency getCurrency() {
    return mCurrency;
  }

  /**
   * Returns a new <code>Money</code> object the currency specified by <code>currency</code> 
   * and the same value as this one. No value exchange between the currencies is performed.
   * @param currency {@link Currency} to assign to new <code>Money</code> object
   * @return {@link Money} object with same value as current object, but with new <code>currency</code>
   */
  public Money withCurrency(Currency currency){
    return new Money(mAmount, currency);
  }
  
  /**
   * Sets the currency of the money object.
   * No currency value conversion is performed. The old value for the amount is not changed
   * This method is only used internally when creating the Money object. 
   * Money objects are immutable and hence this method should not be called out of a constructor
   * @param currency {@link Currency} to assign to the Money object  
   */
  private void setCurrency(Currency currency) {
    //TODO: Consider doing a conversion of the value as well in the future
    this.mCurrency = currency;
  }

  /**
   * Returns the amount represented by this Money object
   * @return {@link BigDecimal} valure of amount in object
   */
  public BigDecimal asBigDecimal() {
    return mAmount;
  }
  
  /**
   * Returns the amount this object
   * @return Double value of the amount in the object
   */
  public double asDouble(){
    return mAmount.doubleValue();
  }
  
  /**
   * An alias for {@link #toPlainString()}
   * @return Money formatted as a string (excludes the currency)
   */
  public String asString(){
    return toPlainString();
  }
  
  /**
   * Returns a string representation of the Money object formatted according to 
   * the <code>locale</code> and includes the currency symbol. 
   * The output precision is limited to {@link #DECIMAL_PLACES}.
   * @param locale Locale to use when formatting the object
   * @return String containing formatted Money representation
   */
  public String formattedString(Locale locale){
    NumberFormat formatter = NumberFormat.getInstance(locale);
    formatter.setMinimumFractionDigits(DECIMAL_PLACES);
    formatter.setMaximumFractionDigits(DECIMAL_PLACES);
    return formatter.format(asDouble()) + " " + mCurrency.getSymbol(locale);
  }

    /**
     * Equivalent to calling formattedString(Locale.getDefault())
     * @return String formatted Money representation in default locale
     */
    public String formattedString(){
        return formattedString(Locale.getDefault());
    }

  /**
   * Returns a new Money object whose amount is the negated value of this object amount.
   * The original <code>Money</code> object remains unchanged.
   * @return Negated <code>Money</code> object
   */
  public Money negate(){
    return new Money(mAmount.negate(), mCurrency);
  }
  
  /**
   * Sets the amount value of this <code>Money</code> object
   * @param amount {@link BigDecimal} amount to be set
   */
  private void setAmount(BigDecimal amount) {  
    mAmount = amount.setScale(DECIMAL_PLACES, ROUNDING_MODE);
  }
  
  /**
   * Sets the amount value of this <code>Money</code> object
   * The <code>amount</code> is parsed by the {@link BigDecimal} constructor
   * @param amount {@link String} amount to be set
   */
  private void setAmount(String amount){
    setAmount(parseToDecimal(amount));
  }  
  
  /**
   * Returns a new <code>Money</code> object whose value is the sum of the values of 
   * this object and <code>addend</code>.
   * 
   * @param addend Second operand in the addition.
   * @return Money object whose value is the sum of this object and <code>money</code>
   * @throws IllegalArgumentException if the <code>Money</code> objects to be added have different Currencies
   */
  public Money add(Money addend){
    if (!mCurrency.equals(addend.mCurrency))
      throw new IllegalArgumentException("Only Money with same currency can be added");
    
    BigDecimal bigD = mAmount.add(addend.mAmount);
    return new Money(bigD, mCurrency);
  }

  /**
   * Returns a new <code>Money</code> object whose value is the difference of the values of 
   * this object and <code>subtrahend</code>.
   * This object is the minuend and the parameter is the subtrahend
   * @param subtrahend Second operand in the subtraction.
   * @return Money object whose value is the difference of this object and <code>subtrahend</code>
   * @throws IllegalArgumentException if the <code>Money</code> objects to be added have different Currencies
   */
  public Money subtract(Money subtrahend){
    if (!mCurrency.equals(subtrahend.mCurrency))
      throw new IllegalArgumentException("Operation can only be performed on money with same currency");
    
    BigDecimal bigD = mAmount.subtract(subtrahend.mAmount);    
    return new Money(bigD, mCurrency);
  }
  
  /**
   * Returns a new <code>Money</code> object whose value is the quotient of the values of 
   * this object and <code>divisor</code>.
   * This object is the dividend and <code>divisor</code> is the divisor
   * @param divisor Second operand in the division.
   * @return Money object whose value is the quotient of this object and <code>divisor</code>
   * @throws IllegalArgumentException if the <code>Money</code> objects to be added have different Currencies
   */
  public Money divide(Money divisor){
    if (!mCurrency.equals(divisor.mCurrency))
      throw new IllegalArgumentException("Operation can only be performed on money with same currency");
    
    BigDecimal bigD = mAmount.divide(divisor.mAmount);    
    return new Money(bigD, mCurrency);
  }
  
  /**
   * Returns a new <code>Money</code> object whose value is the quotient of the division of this objects 
   * value by the factor <code>divisor</code>
   * @param divisor Second operand in the addition.
   * @return Money object whose value is the quotient of this object and <code>divisor</code>
   */
  public Money divide(int divisor){
    Money moneyDiv = new Money(new BigDecimal(divisor), mCurrency);
    return divide(moneyDiv);
  }
  
  /**
   * Returns a new <code>Money</code> object whose value is the product of the values of 
   * this object and <code>money</code>.
   * 
   * @param money Second operand in the multiplication.
   * @return Money object whose value is the product of this object and <code>money</code>
   * @throws IllegalArgumentException if the <code>Money</code> objects to be added have different Currencies
   */
  public Money multiply(Money money){
    if (!mCurrency.equals(money.mCurrency))
      throw new IllegalArgumentException("Operation can only be performed on money with same currency");
    
    BigDecimal bigD = mAmount.multiply(money.mAmount);    
    return new Money(bigD, mCurrency);
  }
  
  /**
   * Returns a new <code>Money</code> object whose value is the product of the division of this objects 
   * value by the factor <code>multiplier</code>
   * @param multiplier Factor to multiply the amount by.
   * @return Money object whose value is the product of this objects values and <code>multiplier</code>
   */
  public Money multiply(int multiplier){
    Money moneyFactor = new Money(new BigDecimal(multiplier), mCurrency);
    return multiply(moneyFactor);
  }
  
  /**
   * Returns true if the amount held by this Money object is negative
   * @return <code>true</code> if the amount is negative, <code>false</code> otherwise.
   */
  public boolean isNegative(){
    return mAmount.compareTo(new BigDecimal(0)) == -1;
  }
  
  /**
   * Returns the string representation of the amount (without currency) of the Money object
   * @return String representation of the amount (without currency) of the Money object
   */
  public String toPlainString(){
    return mAmount.setScale(DECIMAL_PLACES, ROUNDING_MODE).toPlainString();
  }
  
  /**
   * Returns the string representation of the Money object (value + currency) formatted according
   * to the default locale
   * @return String representation of the amount formatted with default locale
   */
  @Override
  public String toString() {
    return formattedString(Locale.getDefault());
  }
    
  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result + ((mAmount == null) ? 0 : mAmount.hashCode());
    result = prime * result
        + ((mCurrency == null) ? 0 : mCurrency.hashCode());
    return result;
  }

  /**
   * Two Money objects are only equal if their amount (value) and currencies are equal
   * @param obj Object to compare with
   * @return <code>true</code> if the objects are equal, <code>false</code> otherwise
   */
  @Override
  public boolean equals(Object obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    Money other = (Money) obj;
    if (mAmount == null) {
      if (other.mAmount != null)
        return false;
    } else if (!mAmount.equals(other.mAmount))
      return false;
    if (mCurrency == null) {
      if (other.mCurrency != null)
        return false;
    } else if (!mCurrency.equals(other.mCurrency))
      return false;
    return true;
  }

  @Override
  public int compareTo(Money another) {
    if (!mCurrency.equals(another.mCurrency))
      throw new IllegalArgumentException("Cannot compare different currencies yet");
    return mAmount.compareTo(another.mAmount);
  }

  /**
   * Parses a Locale specific string into a number using format for {@link Locale#US}
   * @param amountString Formatted String amount
   * @return String amount formatted in the default locale
   */
  public static BigDecimal parseToDecimal(String amountString){  
    char separator = new DecimalFormatSymbols(Locale.US).getGroupingSeparator();
    amountString = amountString.replace(Character.toString(separator), "");
    NumberFormat formatter = NumberFormat.getInstance(Locale.US);    
    if (formatter instanceof DecimalFormat) {
         ((DecimalFormat)formatter).setParseBigDecimal(true);         
     }
    BigDecimal result = null; //new BigDecimal(0);
    try {
      result = (BigDecimal) formatter.parse(amountString);
      
    } catch (ParseException e) {
      e.printStackTrace();    
    }
    return result;    
  }

    /**
     * Returns a new instance of {@link Money} object with the absolute value of the current object
     * @return Money object with absolute value of this instance
     */
    public Money absolute() {
        return new Money(mAmount.abs(), mCurrency);
    }
}




Java Source Code List

org.gnucash.android.app.GnuCashApplication.java
org.gnucash.android.db.AccountsDbAdapter.java
org.gnucash.android.db.DatabaseAdapter.java
org.gnucash.android.db.DatabaseCursorLoader.java
org.gnucash.android.db.DatabaseHelper.java
org.gnucash.android.db.DatabaseSchema.java
org.gnucash.android.db.MigrationHelper.java
org.gnucash.android.db.SplitsDbAdapter.java
org.gnucash.android.db.TransactionsDbAdapter.java
org.gnucash.android.export.ExportDialogFragment.java
org.gnucash.android.export.ExportFormat.java
org.gnucash.android.export.ExportParams.java
org.gnucash.android.export.ExporterAsyncTask.java
org.gnucash.android.export.Exporter.java
org.gnucash.android.export.ofx.OfxExporter.java
org.gnucash.android.export.ofx.OfxHelper.java
org.gnucash.android.export.qif.QifExporter.java
org.gnucash.android.export.qif.QifHelper.java
org.gnucash.android.export.xml.GncXmlExporter.java
org.gnucash.android.export.xml.GncXmlHelper.java
org.gnucash.android.importer.GncXmlHandler.java
org.gnucash.android.importer.GncXmlImporter.java
org.gnucash.android.importer.ImportAsyncTask.java
org.gnucash.android.model.AccountType.java
org.gnucash.android.model.Account.java
org.gnucash.android.model.Money.java
org.gnucash.android.model.Split.java
org.gnucash.android.model.TransactionType.java
org.gnucash.android.model.Transaction.java
org.gnucash.android.receivers.AccountCreator.java
org.gnucash.android.receivers.TransactionAppWidgetProvider.java
org.gnucash.android.receivers.TransactionRecorder.java
org.gnucash.android.ui.UxArgument.java
org.gnucash.android.ui.account.AccountFormFragment.java
org.gnucash.android.ui.account.AccountsActivity.java
org.gnucash.android.ui.account.AccountsListFragment.java
org.gnucash.android.ui.colorpicker.ColorPickerDialog.java
org.gnucash.android.ui.colorpicker.ColorPickerPalette.java
org.gnucash.android.ui.colorpicker.ColorPickerSwatch.java
org.gnucash.android.ui.colorpicker.ColorSquare.java
org.gnucash.android.ui.colorpicker.ColorStateDrawable.java
org.gnucash.android.ui.colorpicker.HsvColorComparator.java
org.gnucash.android.ui.passcode.KeyboardFragment.java
org.gnucash.android.ui.passcode.PassLockActivity.java
org.gnucash.android.ui.passcode.PasscodeLockScreenActivity.java
org.gnucash.android.ui.passcode.PasscodePreferenceActivity.java
org.gnucash.android.ui.settings.AboutPreferenceFragment.java
org.gnucash.android.ui.settings.AccountPreferencesFragment.java
org.gnucash.android.ui.settings.DeleteAllAccountsConfirmationDialog.java
org.gnucash.android.ui.settings.DeleteAllTransacationsConfirmationDialog.java
org.gnucash.android.ui.settings.GeneralPreferenceFragment.java
org.gnucash.android.ui.settings.PasscodePreferenceFragment.java
org.gnucash.android.ui.settings.SettingsActivity.java
org.gnucash.android.ui.settings.TransactionsPreferenceFragment.java
org.gnucash.android.ui.transaction.ScheduledTransactionsListFragment.java
org.gnucash.android.ui.transaction.TransactionFormFragment.java
org.gnucash.android.ui.transaction.TransactionsActivity.java
org.gnucash.android.ui.transaction.TransactionsListFragment.java
org.gnucash.android.ui.transaction.dialog.BulkMoveDialogFragment.java
org.gnucash.android.ui.transaction.dialog.DatePickerDialogFragment.java
org.gnucash.android.ui.transaction.dialog.SplitEditorDialogFragment.java
org.gnucash.android.ui.transaction.dialog.TimePickerDialogFragment.java
org.gnucash.android.ui.transaction.dialog.TransactionsDeleteConfirmationDialogFragment.java
org.gnucash.android.ui.util.AccountBalanceTask.java
org.gnucash.android.ui.util.AmountInputFormatter.java
org.gnucash.android.ui.util.CheckableLinearLayout.java
org.gnucash.android.ui.util.OnAccountClickedListener.java
org.gnucash.android.ui.util.OnTransactionClickedListener.java
org.gnucash.android.ui.util.Refreshable.java
org.gnucash.android.ui.util.TaskDelegate.java
org.gnucash.android.ui.util.TransactionTypeToggleButton.java
org.gnucash.android.ui.widget.WidgetConfigurationActivity.java
org.gnucash.android.util.QualifiedAccountNameCursorAdapter.java