All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opentripplanner.transit.model.basic.Money Maven / Gradle / Ivy

The newest version!
package org.opentripplanner.transit.model.basic;

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 java.util.Objects;
import java.util.function.Function;
import org.opentripplanner.utils.lang.IntUtils;

/**
 * Represents an amount of money.
 */
public class Money implements Comparable, Serializable {

  public static final Currency USD = Currency.getInstance("USD");
  public static final Money ZERO_USD = Money.usDollars(0);
  private final Currency currency;
  private final int amount;

  /**
   *
   * @param currency The currency of the money amount
   * @param minorUnitAmount The amount in the smaller currency unit, so for 1.50 EUR pass 150.
   */
  private Money(Currency currency, int minorUnitAmount) {
    this.currency = Objects.requireNonNull(currency);
    this.amount = minorUnitAmount;
  }

  /**
   * Creates a Euro money object.
   * @param amount Amount in fractional euro, so 1.5 for 1.50 EUR
   */
  public static Money euros(float amount) {
    return Money.ofFractionalAmount(Currency.getInstance("EUR"), amount);
  }

  /**
   * Creates a US dollar money object.
   * @param amount Amount in fractional dollars, so 1.5 for 1.50 USD
   */
  public static Money usDollars(float amount) {
    return Money.ofFractionalAmount(USD, amount);
  }

  /**
   * Take two money instances and return the higher one.
   */
  public static Money max(Money first, Money second) {
    if (!first.currency.equals(second.currency)) {
      throw new RuntimeException("Can't operate on %s and %s as the currencies are not equal.");
    }
    if (first.greaterThan(second)) {
      return first;
    } else {
      return second;
    }
  }

  /**
   * Take a fractional amount of money, ie 1.5 and convert it to amount using the number of default
   * fraction digits of the currency.
   */
  public static Money ofFractionalAmount(Currency currency, float fractionalAmount) {
    Objects.requireNonNull(currency);
    var fractionDigits = currency.getDefaultFractionDigits();
    int amount = IntUtils.round(fractionalAmount * Math.pow(10, fractionDigits));
    return new Money(currency, amount);
  }

  /**
   * Does this instance contain a non-zero amount.
   */
  public boolean isPositive() {
    return amount > 0f;
  }

  /**
   * Is the mount in this instance zero.
   */
  public boolean isZero() {
    return amount == 0f;
  }

  @Override
  public int compareTo(Money m) {
    if (m.currency != currency) {
      throw new RuntimeException("Can't compare " + m.currency + " to " + currency);
    }
    return amount - m.amount;
  }

  /**
   * The amount in the major currency unit, so USD 3.10 is represented as 3.1 (not 310!).
   */
  public BigDecimal fractionalAmount() {
    int fractionDigits = currency.getDefaultFractionDigits();
    var divisor = BigDecimal.valueOf(Math.pow(10, fractionDigits));
    return new BigDecimal(amount)
      .setScale(fractionDigits, RoundingMode.HALF_UP)
      .divide(divisor, RoundingMode.HALF_UP);
  }

  /**
   * The amount in the minor currency unit, so 1.50 EUR will be represented as 150.
   * 

* If the currency doesn't have a minor unit (like Japanese Yen) it is just the amount. */ public int minorUnitAmount() { return amount; } public String localize(Locale loc) { NumberFormat nf = NumberFormat.getCurrencyInstance(loc); nf.setCurrency(currency); nf.setMaximumFractionDigits(currency.getDefaultFractionDigits()); return nf.format(fractionalAmount()); } @Override public String toString() { return localize(Locale.ENGLISH); } /** * Subtract the money amount from this instance and return the result. */ public Money minus(Money other) { return op(other, o -> new Money(currency, amount - o.amount)); } /** * Add another money amount to this instance and return it. */ public Money plus(Money other) { return op(other, o -> new Money(currency, amount + o.amount)); } /** * Returns half this instance's amount * Amounts in minor currency unit is rounded to nearest integer, so $0.99/2 becomes $0.50 */ public Money half() { return new Money(currency, IntUtils.round(amount / 2f)); } /** * Returns the instance rounded down to the nearest multiple of 5 cents * So $0.14 becomes $0.10 */ public Money roundDownToNearestFiveMinorUnits() { int rounded = (this.minorUnitAmount() / 5) * 5; return new Money(currency, rounded); } /** * Multiplies the amount with the multiplicator. */ public Money times(int multiplicator) { return new Money(this.currency, amount * multiplicator); } /** * Does this instance represent a larger amount than the one passed in? */ public boolean greaterThan(Money other) { return booleanOp(other, amount > other.amount); } /** * Does this instance represent a smaller amount than the one passed in? */ public boolean lessThan(Money other) { return booleanOp(other, amount < other.amount); } /** * The currency of this instance. */ public Currency currency() { return currency; } private boolean booleanOp(Money other, boolean result) { checkCurrencyOrThrow(other); return result; } private Money op(Money other, Function op) { checkCurrencyOrThrow(other); return op.apply(other); } private void checkCurrencyOrThrow(Money other) { if (!currency.equals(other.currency)) { throw new IllegalArgumentException( "Cannot perform operations on %s and %s because they have unequal currency.".formatted( this, other ) ); } } @Override public boolean equals(Object obj) { if (obj instanceof Money other) { return other.amount == this.amount && other.currency == this.currency; } else { return false; } } @Override public int hashCode() { return Objects.hash(currency, amount); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy