com.opengamma.strata.basics.currency.Money.java Source code

Java tutorial

Introduction

Here is the source code for com.opengamma.strata.basics.currency.Money.java

Source

/*
 * Copyright (C) 2017 - present by OpenGamma Inc. and the OpenGamma group of companies
 *
 * Please see distribution for license.
 */
package com.opengamma.strata.basics.currency;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;

import org.joda.convert.FromString;
import org.joda.convert.ToString;

import com.google.common.base.Splitter;
import com.google.common.collect.ComparisonChain;
import com.google.common.math.DoubleMath;
import com.opengamma.strata.collect.ArgChecker;

/**
 * An amount of a currency, rounded to match the currency specifications.
 * <p>
 * This class is similar to {@link CurrencyAmount}, but only exposes the rounded amounts.
 * The rounding is done using {@link BigDecimal}, as BigDecimal.ROUND_HALF_UP. Given this operation,
 * it should be assumed that the numbers are an approximation, and not an exact figure.
 * <p>
 * This class is immutable and thread-safe.
 */
public class Money implements FxConvertible<Money>, Comparable<Money>, Serializable {

    /** Serialization version. */
    private static final long serialVersionUID = 1L;

    /**
     * The currency.
     * <p>
     * For example, in the value 'GBP 12.34' the currency is 'GBP'.
     */
    private final Currency currency;
    /**
     * The amount of the currency.
     * <p>
     * For example, in the value 'GBP 12.34' the amount is 12.34.
     */
    private final BigDecimal amount;

    //-------------------------------------------------------------------------
    /**
     * Obtains an instance of {@code Money} for the specified {@link CurrencyAmount}.
     *
     * @param currencyAmount  the instance of {@link CurrencyAmount} wrapping the currency and amount.
     * @return the currency amount
     */
    public static Money of(CurrencyAmount currencyAmount) {
        return new Money(currencyAmount.getCurrency(), BigDecimal.valueOf(currencyAmount.getAmount()));
    }

    /**
     * Obtains an instance of {@code Money} for the specified currency and amount.
     *
     * @param currency  the currency the amount is in
     * @param amount  the amount of the currency to represent
     * @return the currency amount
     */
    public static Money of(Currency currency, double amount) {
        return new Money(currency, BigDecimal.valueOf(amount));
    }

    /**
     * Obtains an instance of {@code Money} for the specified currency and amount.
     *
     * @param currency  the currency the amount is in
     * @param amount  the amount of the currency to represent, as an instance of {@link BigDecimal}
     * @return the currency amount
     */
    public static Money of(Currency currency, BigDecimal amount) {
        return new Money(currency, amount);
    }

    //-------------------------------------------------------------------------
    /**
     * Parses the string to produce a {@link Money}.
     * <p>
     * This parses the {@code toString} format of '${currency} ${amount}'.
     *
     * @param amountStr  the amount string
     * @return the currency amount
     * @throws IllegalArgumentException if the amount cannot be parsed
     */
    @FromString
    public static Money parse(String amountStr) {
        ArgChecker.notNull(amountStr, "amountStr");
        List<String> split = Splitter.on(' ').splitToList(amountStr);
        if (split.size() != 2) {
            throw new IllegalArgumentException("Unable to parse amount, invalid format: " + amountStr);
        }
        try {
            Currency cur = Currency.parse(split.get(0));
            return new Money(cur, new BigDecimal(split.get(1)));
        } catch (RuntimeException ex) {
            throw new IllegalArgumentException("Unable to parse amount: " + amountStr, ex);
        }
    }

    //-------------------------------------------------------------------------
    /**
     * Creates an instance.
     *
     * @param currency  the currency
     * @param amount  the amount
     */
    private Money(Currency currency, BigDecimal amount) {
        ArgChecker.notNull(currency, "currency");
        ArgChecker.notNull(amount, "amount");
        this.currency = currency;
        this.amount = currency.roundMinorUnits(amount);
    }

    //-------------------------------------------------------------------------
    /**
     * Gets the currency.
     * <p>
     * For example, in the value 'GBP 12.34' the currency is 'GBP'.
     *
     * @return the currency
     */
    public Currency getCurrency() {
        return currency;
    }

    /**
     * Gets the amount of the currency as an instance of {@link BigDecimal}.
     * <p>
     * The amount will be rounded to the currency specifications.
     * <p>
     * For example, in the value 'GBP 12.34' the amount is 12.34.
     *
     * @return the amount
     */
    public BigDecimal getAmount() {
        return amount;
    }

    //-------------------------------------------------------------------------
    /**
     * Converts this amount to an equivalent amount the specified currency.
     * <p>
     * The result will be expressed in terms of the given currency, converting
     * using the specified FX rate.
     * <p>
     * For example, if this represents 'GBP 100' and this method is called with
     * arguments {@code (USD, 1.6)} then the result will be 'USD 160'.
     *
     * @param resultCurrency  the currency of the result
     * @param fxRate  the FX rate from this currency to the result currency
     * @return the converted instance, which should be expressed in the specified currency
     * @throws IllegalArgumentException if the FX is not 1 when no conversion is required
     */
    public Money convertedTo(Currency resultCurrency, BigDecimal fxRate) {
        if (currency.equals(resultCurrency)) {
            if (DoubleMath.fuzzyEquals(fxRate.doubleValue(), 1d, 1e-8)) {
                return this;
            }
            throw new IllegalArgumentException("FX rate must be 1 when no conversion required");
        }
        return Money.of(resultCurrency, amount.multiply(fxRate));
    }

    /**
     * Converts this amount to an equivalent amount in the specified currency.
     * <p>
     * The result will be expressed in terms of the given currency.
     * If conversion is needed, the provider will be used to supply the FX rate.
     *
     * @param resultCurrency  the currency of the result
     * @param rateProvider  the provider of FX rates
     * @return the converted instance, in the specified currency
     * @throws RuntimeException if no FX rate could be found
     */
    @Override
    public Money convertedTo(Currency resultCurrency, FxRateProvider rateProvider) {
        if (currency.equals(resultCurrency)) {
            return this;
        }
        double converted = rateProvider.convert(amount.doubleValue(), currency, resultCurrency);
        return Money.of(resultCurrency, converted);
    }

    //-------------------------------------------------------------------------
    /**
     * Compares this money to another.
     * <p>
     * This compares currencies alphabetically, then by amount.
     *
     * @param other  the other amount
     * @return negative if less, zero if equal, positive if greater
     */
    @Override
    public int compareTo(Money other) {
        return ComparisonChain.start().compare(currency, other.currency).compare(amount, other.amount).result();
    }

    //-------------------------------------------------------------------------
    /**
     * Checks if this money equals another.
     *
     * @param obj  the other amount, null returns false
     * @return true if equal
     */
    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        }
        if (obj != null && obj.getClass() == this.getClass()) {
            Money other = (Money) obj;
            return currency.equals(other.currency) && amount.equals(other.amount);
        }
        return false;
    }

    /**
     * Returns a suitable hash code for the currency.
     *
     * @return the hash code
     */
    @Override
    public int hashCode() {
        return currency.hashCode() * 31 + amount.hashCode();
    }

    //-------------------------------------------------------------------------
    /**
     * Gets the amount as a string.
     * <p>
     * The format is the currency code, followed by a space, followed by the
     * amount: '${currency} ${amount}'.
     *
     * @return the currency amount
     */
    @Override
    @ToString
    public String toString() {
        return currency + " " + amount.toString();
    }

}