/**
* Copyright (C) 2009 - 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.util.Locale;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.joda.convert.FromString;
import org.joda.convert.ToString;
import com.google.common.base.CharMatcher;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.opengamma.strata.collect.ArgChecker;
/**
* A unit of currency.
* <p>
* This class represents a unit of currency such as the British Pound, Euro or US Dollar.
* The currency is represented by a three letter code, intended to be ISO-4217.
* <p>
* It is recommended to define currencies in advance using the {@code Currency.ini} file.
* Standard configuration includes many commonly used currencies.
* <p>
* Only currencies listed in configuration will be returned by {@link #getAvailableCurrencies()}.
* If a currency is requested that is not defined in configuration, it will still be created,
* however it will have the default value of zero for the minor units and 'USD' for
* the triangulation currency.
* <p>
* This class is immutable and thread-safe.
*/
public final class Currency
implements Comparable<Currency>, Serializable {
/** Serialization version. */
private static final long serialVersionUID = 1L;
/**
* The matcher for the code.
*/
static final CharMatcher CODE_MATCHER = CharMatcher.inRange('A', 'Z');
/**
* The configured instances.
*/
private static final ImmutableMap<String, Currency> CONFIGURED =
CurrencyDataLoader.loadCurrencies(false);
/**
* A cache of dynamically created instances, initialized with some historic currencies.
*/
private static final ConcurrentMap<String, Currency> DYNAMIC =
new ConcurrentHashMap<>(CurrencyDataLoader.loadCurrencies(true));
// a selection of commonly traded, stable currencies
/**
* The currency 'USD' - United States Dollar.
*/
public static final Currency USD = of("USD");
/**
* The currency 'EUR' - Euro.
*/
public static final Currency EUR = of("EUR");
/**
* The currency 'JPY' - Japanese Yen.
*/
public static final Currency JPY = of("JPY");
/**
* The currency 'GBP' - British pound.
*/
public static final Currency GBP = of("GBP");
/**
* The currency 'CHF' - Swiss Franc.
*/
public static final Currency CHF = of("CHF");
/**
* The currency 'AUD' - Australian Dollar.
*/
public static final Currency AUD = of("AUD");
/**
* The currency 'CAD' - Canadian Dollar.
*/
public static final Currency CAD = of("CAD");
/**
* The currency 'NZD' - New Zealand Dollar.
*/
public static final Currency NZD = of("NZD");
// a selection of other currencies
/**
* The currency 'AED' - UAE Dirham.
*/
public static final Currency AED = of("AED");
/**
* The currency 'ARS' - Argentine Peso.
*/
public static final Currency ARS = of("ARS");
/**
* The currency 'BGN' - Bulgarian Lev.
*/
public static final Currency BGN = of("BGN");
/**
* The currency 'BHD' - Bahraini Dinar.
*/
public static final Currency BHD = of("BHD");
/**
* The currency 'BRL' - Brazil Dollar.
*/
public static final Currency BRL = of("BRL");
/**
* The currency 'CLP' - Chilean Peso.
*/
public static final Currency CLP = of("CLP");
/**
* The currency 'CNY' - Chinese Yuan.
*/
public static final Currency CNY = of("CNY");
/**
* The currency 'COP' - Colombian Peso.
*/
public static final Currency COP = of("COP");
/**
* The currency 'CZK' - Czeck Krona.
*/
public static final Currency CZK = of("CZK");
/**
* The currency 'DKK' - Danish Krone.
*/
public static final Currency DKK = of("DKK");
/**
* The currency 'EGP' - Egyptian Pound.
*/
public static final Currency EGP = of("EGP");
/**
* The currency 'HKD' - Hong Kong Dollar.
*/
public static final Currency HKD = of("HKD");
/**
* The currency 'HRK' - Croatian Kuna.
*/
public static final Currency HRK = of("HRK");
/**
* The currency 'HUF' = Hugarian Forint.
*/
public static final Currency HUF = of("HUF");
/**
* The currency 'IDR' = Indonesian Rupiah.
*/
public static final Currency IDR = of("IDR");
/**
* The currency 'ILS' = Israeli Shekel.
*/
public static final Currency ILS = of("ILS");
/**
* The currency 'INR' = Indian Rupee.
*/
public static final Currency INR = of("INR");
/**
* The currency 'ISK' = Icelandic Krone.
*/
public static final Currency ISK = of("ISK");
/**
* The currency 'KRW' = South Korean Won.
*/
public static final Currency KRW = of("KRW");
/**
* The currency 'MXN' - Mexican Peso.
*/
public static final Currency MXN = of("MXN");
/**
* The currency 'MYR' - Malaysian Ringgit.
*/
public static final Currency MYR = of("MYR");
/**
* The currency 'NOK' - Norwegian Krone.
*/
public static final Currency NOK = of("NOK");
/**
* The currency 'PEN' - Peruvian Nuevo Sol.
*/
public static final Currency PEN = of("PEN");
/**
* The currency 'PHP' - Philippine Peso.
*/
public static final Currency PHP = of("PHP");
/**
* The currency 'PKR' - Pakistani Rupee.
*/
public static final Currency PKR = of("PKR");
/**
* The currency 'PLN' - Polish Zloty.
*/
public static final Currency PLN = of("PLN");
/**
* The currency 'RON' - Romanian New Leu.
*/
public static final Currency RON = of("RON");
/**
* The currency 'RUB' - Russian Ruble.
*/
public static final Currency RUB = of("RUB");
/**
* The currency 'SAR' - Saudi Riyal.
*/
public static final Currency SAR = of("SAR");
/**
* The currency 'SEK' - Swedish Krona.
*/
public static final Currency SEK = of("SEK");
/**
* The currency 'SGD' - Singapore Dollar.
*/
public static final Currency SGD = of("SGD");
/**
* The currency 'THB' - Thai Baht.
*/
public static final Currency THB = of("THB");
/**
* The currency 'TRY' - Turkish Lira.
*/
public static final Currency TRY = of("TRY");
/**
* The currency 'TWD' - New Taiwan Dollar.
*/
public static final Currency TWD = of("TWD");
/**
* The currency 'UAH' - Ukrainian Hryvnia.
*/
public static final Currency UAH = of("UAH");
/**
* The currency 'ZAR' - South African Rand.
*/
public static final Currency ZAR = of("ZAR");
// special cases
/**
* The currency 'XXX' - No applicable currency.
*/
public static final Currency XXX = of("XXX");
/**
* The currency 'XAG' - Silver (troy ounce).
*/
public static final Currency XAG = of("XAG");
/**
* The currency 'XAU' - Gold (troy ounce).
*/
public static final Currency XAU = of("XAU");
/**
* The currency 'XPD' - Paladium (troy ounce).
*/
public static final Currency XPD = of("XPD");
/**
* The currency 'XPT' - Platinum (troy ounce).
*/
public static final Currency XPT = of("XPT");
/**
* The currency code.
*/
private final String code;
/**
* The number of fraction digits, such as 2 for cents in the dollar.
*/
private final transient int minorUnitDigits;
/**
* The triangulation currency.
* Due to initialization ordering, cannot guarantee that USD/EUR is loaded first, so this must be a string.
*/
private final transient String triangulationCurrency;
/**
* The cached hash code.
*/
private final transient int cachedHashCode;
//-------------------------------------------------------------------------
/**
* Obtains the set of configured currencies.
* <p>
* This contains all the currencies that have been defined in configuration.
* Any currency instances that have been dynamically created are not included.
*
* @return an immutable set containing all registered currencies
*/
public static Set<Currency> getAvailableCurrencies() {
return ImmutableSet.copyOf(CONFIGURED.values());
}
//-------------------------------------------------------------------------
/**
* Obtains an instance for the specified ISO-4217 three letter currency code.
* <p>
* A currency is uniquely identified by ISO-4217 three letter code.
* Currencies should be defined in configuration before they can be used.
* If the requested currency is not defined in configuration, it will still be created,
* however it will have the default value of zero for the minor units and 'USD' for
* the triangulation currency.
*
* @param currencyCode the three letter currency code, ASCII and upper case
* @return the singleton instance
* @throws IllegalArgumentException if the currency code is invalid
*/
@FromString
public static Currency of(String currencyCode) {
ArgChecker.notNull(currencyCode, "currencyCode");
Currency currency = CONFIGURED.get(currencyCode);
if (currency == null) {
return addCode(currencyCode);
}
return currency;
}
// add code
private static Currency addCode(String currencyCode) {
ArgChecker.matches(CODE_MATCHER, 3, 3, currencyCode, "currencyCode", "[A-Z][A-Z][A-Z]");
return DYNAMIC.computeIfAbsent(currencyCode, code -> new Currency(code, 0, "USD"));
}
//-------------------------------------------------------------------------
/**
* Parses a string to obtain a {@code Currency}.
* <p>
* The parse is identical to {@link #of(String)} except that it will convert
* letters to upper case first.
* If the requested currency is not defined in configuration, it will still be created,
* however it will have the default value of zero for the minor units and 'USD' for
* the triangulation currency.
*
* @param currencyCode the three letter currency code, ASCII
* @return the singleton instance
* @throws IllegalArgumentException if the currency code is invalid
*/
public static Currency parse(String currencyCode) {
ArgChecker.notNull(currencyCode, "currencyCode");
return of(currencyCode.toUpperCase(Locale.ENGLISH));
}
//-------------------------------------------------------------------------
/**
* Restricted constructor, called only by {@code CurrencyProperties}.
*
* @param code the three letter currency code, validated
* @param fractionDigits the number of fraction digits, validated
* @param triangulationCurrency the triangulation currency
*/
Currency(String code, int fractionDigits, String triangulationCurrency) {
this.code = code;
this.minorUnitDigits = fractionDigits;
this.triangulationCurrency = triangulationCurrency;
// total universe is (26 * 26 * 26) codes, which can provide a unique hash code
this.cachedHashCode = ((code.charAt(0) - 64) << 16) + ((code.charAt(1) - 64) << 8) + (code.charAt(2) - 64);
}
/**
* Ensure singleton on deserialization.
*
* @return the singleton
*/
private Object readResolve() {
return Currency.of(code);
}
//-------------------------------------------------------------------------
/**
* Gets the three letter ISO code.
*
* @return the three letter ISO code
*/
public String getCode() {
return code;
}
/**
* Gets the number of digits in the minor unit.
* <p>
* For example, 'USD' will return 2, indicating that there are two digits,
* corresponding to cents in the dollar.
*
* @return the number of fraction digits
*/
public int getMinorUnitDigits() {
return minorUnitDigits;
}
/**
* Gets the preferred triangulation currency.
* <p>
* When obtaining a market quote for a currency, the triangulation currency
* is used if no direct rate can be found.
* For example, there is no direct rate for 'CZK/SGD'. Instead 'CZK' might be defined to
* triangulate via 'EUR' and 'SGD' with 'USD'. Since the three rates, 'CZK/EUR', 'EUR/USD'
* and 'USD/SGD' can be obtained, a rate can be determined for 'CZK/SGD'.
* Note that most currencies triangulate via 'USD'.
*
* @return the triangulation currency
*/
public Currency getTriangulationCurrency() {
return Currency.of(triangulationCurrency);
}
//-------------------------------------------------------------------------
/**
* Compares this currency to another.
* <p>
* The comparison sorts alphabetically by the three letter currency code.
*
* @param other the other currency
* @return negative if less, zero if equal, positive if greater
*/
@Override
public int compareTo(Currency other) {
// hash code is unique and ordered so can be used for compareTo
return cachedHashCode - other.cachedHashCode;
}
/**
* Checks if this currency equals another currency.
* <p>
* The comparison checks the three letter currency code.
*
* @param obj the other currency, null returns false
* @return true if equal
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof Currency) {
return equals((Currency) obj);
}
return false;
}
// called by CurrencyAmount
boolean equals(Currency other) {
// hash code is unique so can be used for equals
return other.cachedHashCode == cachedHashCode;
}
/**
* Returns a suitable hash code for the currency.
*
* @return the hash code
*/
@Override
public int hashCode() {
return cachedHashCode;
}
//-------------------------------------------------------------------------
/**
* Returns a string representation of the currency, which is the three letter code.
*
* @return the three letter currency code
*/
@Override
@ToString
public String toString() {
return code;
}
}