/**
* Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.util.money;
import java.io.Serializable;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.joda.beans.Bean;
import org.joda.beans.BeanBuilder;
import org.joda.beans.BeanDefinition;
import org.joda.beans.ImmutableBean;
import org.joda.beans.ImmutableConstructor;
import org.joda.beans.JodaBeanUtils;
import org.joda.beans.MetaProperty;
import org.joda.beans.Property;
import org.joda.beans.PropertyDefinition;
import org.joda.beans.impl.direct.DirectFieldsBeanBuilder;
import org.joda.beans.impl.direct.DirectMetaBean;
import org.joda.beans.impl.direct.DirectMetaProperty;
import org.joda.beans.impl.direct.DirectMetaPropertyMap;
import org.joda.convert.FromString;
import org.joda.convert.ToString;
import com.opengamma.util.ArgumentChecker;
/**
* An amount of a currency.
* <p>
* This class represents a {@code double} amount associated with a currency.
* It is specifically named "CurrencyAmount" and not "Money" to indicate that
* it simply holds a currency and an amount. By contrast, naming it "Money"
* would imply it was a suitable choice for accounting purposes, which it is not.
* <p>
* This design approach has been chosen primarily for performance reasons.
* Using a {@code BigDecimal} is markedly slower.
* <p>
* A {@code double} is a 64 bit floating point value suitable for most calculations.
* Floating point maths is
* <a href="http://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html">inexact</a>
* due to the conflict between binary and decimal arithmetic.
* As such, there is the potential for data loss at the margins.
* For example, adding the {@code double} values {@code 0.1d} and {@code 0.2d}
* results in {@code 0.30000000000000004} rather than {@code 0.3}.
* As can be seen, the level of error is small, hence providing this class is
* used appropriately, the use of {@code double} is acceptable.
* For example, using this class to provide a meaningful result type after
* calculations have completed would be an appropriate use.
* <p>
* This class is immutable and thread-safe.
*/
@BeanDefinition(builderScope = "private")
public final class CurrencyAmount implements ImmutableBean, Serializable {
/** Serialization version. */
private static final long serialVersionUID = 1L;
/**
* The currency.
* For example, in the value 'GBP 12.34' the currency is 'GBP'.
*/
@PropertyDefinition(get = "manual", validate = "notNull")
private final Currency _currency;
/**
* The amount of the currency.
* For example, in the value 'GBP 12.34' the amount is 12.34.
*/
@PropertyDefinition(get = "manual")
private final double _amount;
/**
* Obtains an instance of {@code CurrencyAmount} for the specified currency and amount.
*
* @param currency the currency the amount is in, not null
* @param amount the amount of the currency to represent
* @return the currency amount, not null
*/
public static CurrencyAmount of(final Currency currency, final double amount) {
return new CurrencyAmount(currency, amount);
}
/**
* Obtains an instance of {@code CurrencyAmount} for the specified ISO-4217
* three letter currency code and amount.
* <p>
* A currency is uniquely identified by ISO-4217 three letter code.
* This method creates the currency if it is not known.
*
* @param currencyCode the three letter currency code, ASCII and upper case, not null
* @param amount the amount of the currency to represent
* @return the currency amount, not null
* @throws IllegalArgumentException if the currency code is invalid
*/
public static CurrencyAmount of(final String currencyCode, final double amount) {
return of(Currency.of(currencyCode), amount);
}
/**
* Parses the string to produce a {@code CurrencyAmount}.
* <p>
* This parses the {@code toString} format of '${currency} ${amount}'.
*
* @param amountStr the amount string, not null
* @return the currency amount
* @throws IllegalArgumentException if the amount cannot be parsed
*/
@FromString
public static CurrencyAmount parse(final String amountStr) {
ArgumentChecker.notNull(amountStr, "amountStr");
String[] parts = StringUtils.split(amountStr, ' ');
if (parts.length != 2) {
throw new IllegalArgumentException("Unable to parse amount, invalid format: " + amountStr);
}
try {
Currency cur = Currency.parse(parts[0]);
double amount = Double.parseDouble(parts[1]);
return new CurrencyAmount(cur, amount);
} catch (RuntimeException ex) {
throw new IllegalArgumentException("Unable to parse amount: " + amountStr, ex);
}
}
//-------------------------------------------------------------------------
/**
* Creates an instance.
*
* @param currency the currency, not null
* @param amount the amount
*/
@ImmutableConstructor
private CurrencyAmount(final Currency currency, final double amount) {
ArgumentChecker.notNull(currency, "currency");
_currency = currency;
_amount = amount;
}
//-------------------------------------------------------------------------
/**
* Gets the currency.
* For example, in the value 'GBP 12.34' the currency is 'GBP'.
*
* @return the currency, not null
*/
public Currency getCurrency() {
return _currency;
}
/**
* Gets the amount of the currency.
* For example, in the value 'GBP 12.34' the amount is 12.34.
*
* @return the amount
*/
public double getAmount() {
return _amount;
}
//-------------------------------------------------------------------------
/**
* Returns a copy of this {@code CurrencyAmount} with the specified amount added.
* <p>
* This adds the specified amount to this monetary amount, returning a new object.
* The addition simply uses standard {@code double} arithmetic.
* <p>
* This instance is immutable and unaffected by this method.
*
* @param amountToAdd the amount to add, in the same currency, not null
* @return an amount based on this with the specified amount added, not null
* @throws IllegalArgumentException if the currencies are not equal
*/
public CurrencyAmount plus(final CurrencyAmount amountToAdd) {
ArgumentChecker.notNull(amountToAdd, "amountToAdd");
ArgumentChecker.isTrue(amountToAdd.getCurrency().equals(_currency), "Unable to add amounts in different currencies");
return plus(amountToAdd.getAmount());
}
/**
* Returns a copy of this {@code CurrencyAmount} with the specified amount added.
* <p>
* This adds the specified amount to this monetary amount, returning a new object.
* The addition simply uses standard {@code double} arithmetic.
* <p>
* This instance is immutable and unaffected by this method.
*
* @param amountToAdd the amount to add, in the same currency
* @return an amount based on this with the specified amount added, not null
*/
public CurrencyAmount plus(final double amountToAdd) {
return new CurrencyAmount(_currency, _amount + amountToAdd);
}
/**
* Returns a copy of this {@code CurrencyAmount} with the amount multiplied.
* <p>
* This takes this amount and multiplies it by the specified value.
* The multiplication simply uses standard {@code double} arithmetic.
* <p>
* This instance is immutable and unaffected by this method.
*
* @param valueToMultiplyBy the scalar amount to multiply by
* @return an amount based on this with the amount multiplied, not null
*/
public CurrencyAmount multipliedBy(final double valueToMultiplyBy) {
return new CurrencyAmount(_currency, _amount * valueToMultiplyBy);
}
//-----------------------------------------------------------------------
/**
* 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, not null
*/
@Override
@ToString
public String toString() {
return _currency + " " + _amount;
}
//------------------------- AUTOGENERATED START -------------------------
///CLOVER:OFF
/**
* The meta-bean for {@code CurrencyAmount}.
* @return the meta-bean, not null
*/
public static CurrencyAmount.Meta meta() {
return CurrencyAmount.Meta.INSTANCE;
}
static {
JodaBeanUtils.registerMetaBean(CurrencyAmount.Meta.INSTANCE);
}
@Override
public CurrencyAmount.Meta metaBean() {
return CurrencyAmount.Meta.INSTANCE;
}
@Override
public <R> Property<R> property(String propertyName) {
return metaBean().<R>metaProperty(propertyName).createProperty(this);
}
@Override
public Set<String> propertyNames() {
return metaBean().metaPropertyMap().keySet();
}
//-----------------------------------------------------------------------
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj != null && obj.getClass() == this.getClass()) {
CurrencyAmount other = (CurrencyAmount) obj;
return JodaBeanUtils.equal(getCurrency(), other.getCurrency()) &&
JodaBeanUtils.equal(getAmount(), other.getAmount());
}
return false;
}
@Override
public int hashCode() {
int hash = getClass().hashCode();
hash = hash * 31 + JodaBeanUtils.hashCode(getCurrency());
hash = hash * 31 + JodaBeanUtils.hashCode(getAmount());
return hash;
}
//-----------------------------------------------------------------------
/**
* The meta-bean for {@code CurrencyAmount}.
*/
public static final class Meta extends DirectMetaBean {
/**
* The singleton instance of the meta-bean.
*/
static final Meta INSTANCE = new Meta();
/**
* The meta-property for the {@code currency} property.
*/
private final MetaProperty<Currency> _currency = DirectMetaProperty.ofImmutable(
this, "currency", CurrencyAmount.class, Currency.class);
/**
* The meta-property for the {@code amount} property.
*/
private final MetaProperty<Double> _amount = DirectMetaProperty.ofImmutable(
this, "amount", CurrencyAmount.class, Double.TYPE);
/**
* The meta-properties.
*/
private final Map<String, MetaProperty<?>> _metaPropertyMap$ = new DirectMetaPropertyMap(
this, null,
"currency",
"amount");
/**
* Restricted constructor.
*/
private Meta() {
}
@Override
protected MetaProperty<?> metaPropertyGet(String propertyName) {
switch (propertyName.hashCode()) {
case 575402001: // currency
return _currency;
case -1413853096: // amount
return _amount;
}
return super.metaPropertyGet(propertyName);
}
@Override
public BeanBuilder<? extends CurrencyAmount> builder() {
return new CurrencyAmount.Builder();
}
@Override
public Class<? extends CurrencyAmount> beanType() {
return CurrencyAmount.class;
}
@Override
public Map<String, MetaProperty<?>> metaPropertyMap() {
return _metaPropertyMap$;
}
//-----------------------------------------------------------------------
/**
* The meta-property for the {@code currency} property.
* @return the meta-property, not null
*/
public MetaProperty<Currency> currency() {
return _currency;
}
/**
* The meta-property for the {@code amount} property.
* @return the meta-property, not null
*/
public MetaProperty<Double> amount() {
return _amount;
}
//-----------------------------------------------------------------------
@Override
protected Object propertyGet(Bean bean, String propertyName, boolean quiet) {
switch (propertyName.hashCode()) {
case 575402001: // currency
return ((CurrencyAmount) bean).getCurrency();
case -1413853096: // amount
return ((CurrencyAmount) bean).getAmount();
}
return super.propertyGet(bean, propertyName, quiet);
}
@Override
protected void propertySet(Bean bean, String propertyName, Object newValue, boolean quiet) {
metaProperty(propertyName);
if (quiet) {
return;
}
throw new UnsupportedOperationException("Property cannot be written: " + propertyName);
}
}
//-----------------------------------------------------------------------
/**
* The bean-builder for {@code CurrencyAmount}.
*/
private static final class Builder extends DirectFieldsBeanBuilder<CurrencyAmount> {
private Currency _currency;
private double _amount;
/**
* Restricted constructor.
*/
private Builder() {
}
//-----------------------------------------------------------------------
@Override
public Object get(String propertyName) {
switch (propertyName.hashCode()) {
case 575402001: // currency
return _currency;
case -1413853096: // amount
return _amount;
default:
throw new NoSuchElementException("Unknown property: " + propertyName);
}
}
@Override
public Builder set(String propertyName, Object newValue) {
switch (propertyName.hashCode()) {
case 575402001: // currency
this._currency = (Currency) newValue;
break;
case -1413853096: // amount
this._amount = (Double) newValue;
break;
default:
throw new NoSuchElementException("Unknown property: " + propertyName);
}
return this;
}
@Override
public Builder set(MetaProperty<?> property, Object value) {
super.set(property, value);
return this;
}
@Override
public Builder setString(String propertyName, String value) {
setString(meta().metaProperty(propertyName), value);
return this;
}
@Override
public Builder setString(MetaProperty<?> property, String value) {
super.setString(property, value);
return this;
}
@Override
public Builder setAll(Map<String, ? extends Object> propertyValueMap) {
super.setAll(propertyValueMap);
return this;
}
@Override
public CurrencyAmount build() {
return new CurrencyAmount(
_currency,
_amount);
}
//-----------------------------------------------------------------------
@Override
public String toString() {
StringBuilder buf = new StringBuilder(96);
buf.append("CurrencyAmount.Builder{");
buf.append("currency").append('=').append(JodaBeanUtils.toString(_currency)).append(',').append(' ');
buf.append("amount").append('=').append(JodaBeanUtils.toString(_amount));
buf.append('}');
return buf.toString();
}
}
///CLOVER:ON
//-------------------------- AUTOGENERATED END --------------------------
}