Coverage Report - org.joda.money.CurrencyUnit
 
Classes in this File Line Coverage Branch Coverage Complexity
CurrencyUnit
94%
114/121
94%
55/58
2.69
 
 1  
 /*
 2  
  *  Copyright 2009-2013 Stephen Colebourne
 3  
  *
 4  
  *  Licensed under the Apache License, Version 2.0 (the "License");
 5  
  *  you may not use this file except in compliance with the License.
 6  
  *  You may obtain a copy of the License at
 7  
  *
 8  
  *      http://www.apache.org/licenses/LICENSE-2.0
 9  
  *
 10  
  *  Unless required by applicable law or agreed to in writing, software
 11  
  *  distributed under the License is distributed on an "AS IS" BASIS,
 12  
  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  *  See the License for the specific language governing permissions and
 14  
  *  limitations under the License.
 15  
  */
 16  
 package org.joda.money;
 17  
 
 18  
 import java.io.InvalidObjectException;
 19  
 import java.io.ObjectInputStream;
 20  
 import java.io.Serializable;
 21  
 import java.util.ArrayList;
 22  
 import java.util.Collections;
 23  
 import java.util.Currency;
 24  
 import java.util.HashSet;
 25  
 import java.util.List;
 26  
 import java.util.Locale;
 27  
 import java.util.Map.Entry;
 28  
 import java.util.Set;
 29  
 import java.util.concurrent.ConcurrentHashMap;
 30  
 import java.util.concurrent.ConcurrentMap;
 31  
 
 32  
 import org.joda.convert.FromString;
 33  
 import org.joda.convert.ToString;
 34  
 
 35  
 /**
 36  
  * A unit of currency.
 37  
  * <p>
 38  
  * This class represents a unit of currency such as the British Pound, Euro
 39  
  * or US Dollar.
 40  
  * <p>
 41  
  * The set of loaded currencies is provided by an instance of {@link CurrencyUnitDataProvider}.
 42  
  * The provider used is determined by the system property {@code org.joda.money.CurrencyUnitDataProvider}
 43  
  * which should be the fully qualified class name of the provider. The default provider loads the first
 44  
  * resource named {@code /org/joda/money/MoneyData.csv} on the classpath.
 45  
  * <p>
 46  
  * This class is immutable and thread-safe.
 47  
  */
 48  6842
 public final class CurrencyUnit implements Comparable<CurrencyUnit>, Serializable {
 49  
 
 50  
     /**
 51  
      * The serialisation version.
 52  
      */
 53  
     private static final long serialVersionUID = 327835287287L;
 54  
     /**
 55  
      * Map of registered currencies by text code.
 56  
      */
 57  1
     private static final ConcurrentMap<String, CurrencyUnit> currenciesByCode = new ConcurrentHashMap<String, CurrencyUnit>();
 58  
     /**
 59  
      * Map of registered currencies by numeric code.
 60  
      */
 61  1
     private static final ConcurrentMap<Integer, CurrencyUnit> currenciesByNumericCode = new ConcurrentHashMap<Integer, CurrencyUnit>();
 62  
     /**
 63  
      * Map of registered currencies by country.
 64  
      */
 65  1
     private static final ConcurrentMap<String, CurrencyUnit> currenciesByCountry = new ConcurrentHashMap<String, CurrencyUnit>();
 66  
     static {
 67  
         // load one data provider by system property
 68  
         try {
 69  
             try {
 70  1
                 String clsName = System.getProperty(
 71  
                         "org.joda.money.CurrencyUnitDataProvider", "org.joda.money.DefaultCurrencyUnitDataProvider");
 72  1
                 Class<? extends CurrencyUnitDataProvider> cls =
 73  
                         CurrencyUnit.class.getClassLoader().loadClass(clsName).asSubclass(CurrencyUnitDataProvider.class);
 74  1
                 cls.newInstance().registerCurrencies();
 75  0
             } catch (SecurityException ex) {
 76  0
                 new DefaultCurrencyUnitDataProvider().registerCurrencies();
 77  1
             }
 78  0
         } catch (RuntimeException ex) {
 79  0
             throw ex;
 80  0
         } catch (Exception ex) {
 81  0
             throw new RuntimeException(ex.toString(), ex);
 82  1
         }
 83  
     }
 84  
 
 85  
     // a selection of commonly traded, stable currencies
 86  
     /**
 87  
      * The currency 'USD' - United States Dollar.
 88  
      */
 89  1
     public static final CurrencyUnit USD = of("USD");
 90  
     /**
 91  
      * The currency 'EUR' - Euro.
 92  
      */
 93  1
     public static final CurrencyUnit EUR = of("EUR");
 94  
     /**
 95  
      * The currency 'JPY' - Japanese Yen.
 96  
      */
 97  1
     public static final CurrencyUnit JPY = of("JPY");
 98  
     /**
 99  
      * The currency 'GBP' - British pound.
 100  
      */
 101  1
     public static final CurrencyUnit GBP = of("GBP");
 102  
     /**
 103  
      * The currency 'CHF' - Swiss Franc.
 104  
      */
 105  1
     public static final CurrencyUnit CHF = of("CHF");
 106  
     /**
 107  
      * The currency 'AUD' - Australian Dollar.
 108  
      */
 109  1
     public static final CurrencyUnit AUD = of("AUD");
 110  
     /**
 111  
      * The currency 'CAD' - Canadian Dollar.
 112  
      */
 113  1
     public static final CurrencyUnit CAD = of("CAD");
 114  
 
 115  
     /**
 116  
      * The currency code, not null.
 117  
      */
 118  
     private final String code;
 119  
     /**
 120  
      * The numeric currency code.
 121  
      */
 122  
     private final short numericCode;
 123  
     /**
 124  
      * The number of decimal places.
 125  
      */
 126  
     private final short decimalPlaces;
 127  
 
 128  
     //-----------------------------------------------------------------------
 129  
     /**
 130  
      * Registers a currency allowing it to be used.
 131  
      * <p>
 132  
      * This class only permits known currencies to be returned.
 133  
      * To achieve this, all currencies have to be registered in advance.
 134  
      * <p>
 135  
      * Since this method is public, it is possible to add currencies in
 136  
      * application code. It is recommended to do this only at startup, however
 137  
      * it is safe to do so later as the internal implementation is thread-safe.
 138  
      *
 139  
      * @param currencyCode  the currency code, not null
 140  
      * @param numericCurrencyCode  the numeric currency code, -1 if none
 141  
      * @param decimalPlaces  the number of decimal places that the currency
 142  
      *  normally has, from 0 to 9 (normally 0, 2 or 3), or -1 for a pseudo-currency
 143  
      * @param countryCodes  the country codes to register the currency under, not null
 144  
      * @return the new instance, never null
 145  
      * @throws IllegalArgumentException if the code is already registered, or the
 146  
      *  specified data is invalid
 147  
      */
 148  
     public static synchronized CurrencyUnit registerCurrency(
 149  
             String currencyCode, int numericCurrencyCode, int decimalPlaces, List<String> countryCodes) {
 150  9
         return registerCurrency(currencyCode, numericCurrencyCode, decimalPlaces, countryCodes, false);
 151  
     }
 152  
 
 153  
     /**
 154  
      * Registers a currency allowing it to be used, allowing replacement.
 155  
      * <p>
 156  
      * This class only permits known currencies to be returned.
 157  
      * To achieve this, all currencies have to be registered in advance.
 158  
      * <p>
 159  
      * Since this method is public, it is possible to add currencies in
 160  
      * application code. It is recommended to do this only at startup, however
 161  
      * it is safe to do so later as the internal implementation is thread-safe.
 162  
      * <p>
 163  
      * This method uses a flag to determine whether the registered currency
 164  
      * must be new, or can replace an existing currency.
 165  
      *
 166  
      * @param currencyCode  the currency code, not null
 167  
      * @param numericCurrencyCode  the numeric currency code, -1 if none
 168  
      * @param decimalPlaces  the number of decimal places that the currency
 169  
      *  normally has, from 0 to 9 (normally 0, 2 or 3), or -1 for a pseudo-currency
 170  
      * @param countryCodes  the country codes to register the currency under,
 171  
      *  use of ISO-3166 is recommended, not null
 172  
      * @param force  true to register forcefully, replacing any existing matching currency,
 173  
      *  false to validate that there is no existing matching currency
 174  
      * @return the new instance, never null
 175  
      * @throws IllegalArgumentException if the code is already registered and {@code force} is false;
 176  
      *  or if the specified data is invalid
 177  
      */
 178  
     public static synchronized CurrencyUnit registerCurrency(
 179  
                     String currencyCode, int numericCurrencyCode, int decimalPlaces, List<String> countryCodes, boolean force) {
 180  181
         MoneyUtils.checkNotNull(currencyCode, "Currency code must not be null");
 181  180
         if (numericCurrencyCode < -1 || numericCurrencyCode > 999) {
 182  2
             throw new IllegalArgumentException("Invalid numeric code");
 183  
         }
 184  178
         if (decimalPlaces < -1 || decimalPlaces > 9) {
 185  2
             throw new IllegalArgumentException("Invalid number of decimal places");
 186  
         }
 187  176
         MoneyUtils.checkNotNull(countryCodes, "Country codes must not be null");
 188  
         
 189  176
         CurrencyUnit currency = new CurrencyUnit(currencyCode, (short) numericCurrencyCode, (short) decimalPlaces);
 190  176
         if (force) {
 191  172
             currenciesByCode.remove(currencyCode);
 192  172
             currenciesByNumericCode.remove(numericCurrencyCode);
 193  172
             for (String countryCode : countryCodes) {
 194  256
                 currenciesByCountry.remove(countryCode);
 195  
             }
 196  
         } else {
 197  4
             if (currenciesByCode.containsKey(currencyCode) || currenciesByNumericCode.containsKey(numericCurrencyCode)) {
 198  2
                 throw new IllegalArgumentException("Currency already registered: " + currencyCode);
 199  
             }
 200  2
             for (String countryCode : countryCodes) {
 201  2
                 if (currenciesByCountry.containsKey(countryCode)) {
 202  1
                     throw new IllegalArgumentException("Currency already registered for country: " + countryCode);
 203  
                 }
 204  
             }
 205  
         }
 206  172
         currenciesByCode.putIfAbsent(currencyCode, currency);
 207  172
         if (numericCurrencyCode >= 0) {
 208  170
             currenciesByNumericCode.putIfAbsent(numericCurrencyCode, currency);
 209  
         }
 210  172
         for (String countryCode : countryCodes) {
 211  256
             currenciesByCountry.put(countryCode, currency);
 212  
         }
 213  172
         return currenciesByCode.get(currencyCode);
 214  
     }
 215  
 
 216  
     /**
 217  
      * Gets the list of all registered currencies.
 218  
      * <p>
 219  
      * This class only permits known currencies to be returned, thus this list is
 220  
      * the complete list of valid singleton currencies. The list may change after
 221  
      * application startup, however this isn't recommended.
 222  
      *
 223  
      * @return the sorted, independent, list of all registered currencies, never null
 224  
      */
 225  
     public static List<CurrencyUnit> registeredCurrencies() {
 226  6
         ArrayList<CurrencyUnit> list = new ArrayList<CurrencyUnit>(currenciesByCode.values());
 227  6
         Collections.sort(list);
 228  6
         return list;
 229  
     }
 230  
 
 231  
     //-----------------------------------------------------------------------
 232  
     /**
 233  
      * Obtains an instance of {@code CurrencyUnit} matching the specified JDK currency.
 234  
      * <p>
 235  
      * This converts the JDK currency instance to a currency unit using the code.
 236  
      *
 237  
      * @param currency  the currency, not null
 238  
      * @return the singleton instance, never null
 239  
      * @throws IllegalCurrencyException if the currency is unknown
 240  
      */
 241  
     public static CurrencyUnit of(Currency currency) {
 242  2
         MoneyUtils.checkNotNull(currency, "Currency must not be null");
 243  1
         return of(currency.getCurrencyCode());
 244  
     }
 245  
 
 246  
     /**
 247  
      * Obtains an instance of {@code CurrencyUnit} for the specified ISO-4217 three letter currency code.
 248  
      * <p>
 249  
      * A currency is uniquely identified by ISO-4217 three letter code.
 250  
      *
 251  
      * @param currencyCode  the currency code, not null
 252  
      * @return the singleton instance, never null
 253  
      * @throws IllegalCurrencyException if the currency is unknown
 254  
      */
 255  
     @FromString
 256  
     public static CurrencyUnit of(String currencyCode) {
 257  230
         MoneyUtils.checkNotNull(currencyCode, "Currency code must not be null");
 258  228
         CurrencyUnit currency = currenciesByCode.get(currencyCode);
 259  228
         if (currency == null) {
 260  9
             throw new IllegalCurrencyException("Unknown currency '" + currencyCode + '\'');
 261  
         }
 262  219
         return currency;
 263  
     }
 264  
 
 265  
     /**
 266  
      * Obtains an instance of {@code CurrencyUnit} for the specified ISO-4217 numeric currency code.
 267  
      * <p>
 268  
      * The numeric code is an alternative to the three letter code.
 269  
      * This method is lenient and does not require the string to be left padded with zeroes.
 270  
      *
 271  
      * @param numericCurrencyCode  the currency code, not null
 272  
      * @return the singleton instance, never null
 273  
      * @throws IllegalCurrencyException if the currency is unknown
 274  
      */
 275  
     public static CurrencyUnit ofNumericCode(String numericCurrencyCode) {
 276  19
         MoneyUtils.checkNotNull(numericCurrencyCode, "Currency code must not be null");
 277  18
         switch (numericCurrencyCode.length()) {
 278  
             case 1:
 279  2
                 return ofNumericCode(numericCurrencyCode.charAt(0) - '0');
 280  
             case 2:
 281  3
                 return ofNumericCode((numericCurrencyCode.charAt(0) - '0') * 10 +
 282  
                                       numericCurrencyCode.charAt(1) - '0');
 283  
             case 3:
 284  9
                 return ofNumericCode((numericCurrencyCode.charAt(0) - '0') * 100 +
 285  
                                      (numericCurrencyCode.charAt(1) - '0') * 10 +
 286  
                                       numericCurrencyCode.charAt(2) - '0');
 287  
             default:
 288  4
                 throw new IllegalCurrencyException("Unknown currency '" + numericCurrencyCode + '\'');
 289  
         }
 290  
     }
 291  
 
 292  
     /**
 293  
      * Obtains an instance of {@code CurrencyUnit} for the specified ISO-4217 numeric currency code.
 294  
      * <p>
 295  
      * The numeric code is an alternative to the three letter code.
 296  
      *
 297  
      * @param numericCurrencyCode  the numeric currency code, not null
 298  
      * @return the singleton instance, never null
 299  
      * @throws IllegalCurrencyException if the currency is unknown
 300  
      */
 301  
     public static CurrencyUnit ofNumericCode(int numericCurrencyCode) {
 302  20
         CurrencyUnit currency = currenciesByNumericCode.get(numericCurrencyCode);
 303  20
         if (currency == null) {
 304  7
             throw new IllegalCurrencyException("Unknown currency '" + numericCurrencyCode + '\'');
 305  
         }
 306  13
         return currency;
 307  
     }
 308  
 
 309  
     /**
 310  
      * Obtains an instance of {@code CurrencyUnit} for the specified locale.
 311  
      * <p>
 312  
      * Only the country is used from the locale.
 313  
      *
 314  
      * @param locale  the locale, not null
 315  
      * @return the singleton instance, never null
 316  
      * @throws IllegalCurrencyException if the currency is unknown
 317  
      */
 318  
     public static CurrencyUnit of(Locale locale) {
 319  6
         MoneyUtils.checkNotNull(locale, "Locale must not be null");
 320  4
         CurrencyUnit currency = currenciesByCountry.get(locale.getCountry());
 321  4
         if (currency == null) {
 322  2
             throw new IllegalCurrencyException("Unknown currency for locale '" + locale + '\'');
 323  
         }
 324  2
         return currency;
 325  
     }
 326  
 
 327  
     /**
 328  
      * Obtains an instance of {@code CurrencyUnit} for the specified ISO-3166 country code.
 329  
      * <p>
 330  
      * Country codes should generally be in upper case.
 331  
      * This method is case sensitive.
 332  
      *
 333  
      * @param countryCode  the country code, typically ISO-3166, not null
 334  
      * @return the singleton instance, never null
 335  
      * @throws IllegalCurrencyException if the currency is unknown
 336  
      */
 337  
     public static CurrencyUnit ofCountry(String countryCode) {
 338  4
         MoneyUtils.checkNotNull(countryCode, "Country code must not be null");
 339  3
         CurrencyUnit currency = currenciesByCountry.get(countryCode);
 340  3
         if (currency == null) {
 341  1
             throw new IllegalCurrencyException("Unknown currency for country '" + countryCode + '\'');
 342  
         }
 343  2
         return currency;
 344  
     }
 345  
 
 346  
     //-----------------------------------------------------------------------
 347  
     /**
 348  
      * Obtains an instance of {@code CurrencyUnit} for the specified currency code.
 349  
      * <p>
 350  
      * This method exists to match the API of {@link Currency}.
 351  
      *
 352  
      * @param currencyCode  the currency code, not null
 353  
      * @return the singleton instance, never null
 354  
      * @throws IllegalCurrencyException if the currency is unknown
 355  
      */
 356  
     public static CurrencyUnit getInstance(String currencyCode) {
 357  3
         return CurrencyUnit.of(currencyCode);
 358  
     }
 359  
 
 360  
     /**
 361  
      * Obtains an instance of {@code CurrencyUnit} for the specified locale.
 362  
      * <p>
 363  
      * This method exists to match the API of {@link Currency}.
 364  
      *
 365  
      * @param locale  the locale, not null
 366  
      * @return the singleton instance, never null
 367  
      * @throws IllegalCurrencyException if the currency is unknown
 368  
      */
 369  
     public static CurrencyUnit getInstance(Locale locale) {
 370  3
         return CurrencyUnit.of(locale);
 371  
     }
 372  
 
 373  
     //-----------------------------------------------------------------------
 374  
     /**
 375  
      * Constructor, creating a new currency instance.
 376  
      * 
 377  
      * @param code  the currency code, not null
 378  
      * @param numericCurrencyCode  the numeric currency code, -1 if none
 379  
      * @param decimalPlaces  the decimal places, not null
 380  
      */
 381  183
     CurrencyUnit(String code, short numericCurrencyCode, short decimalPlaces) {
 382  183
         assert code != null : "Joda-Money bug: Currency code must not be null";
 383  182
         this.code = code;
 384  182
         this.numericCode = numericCurrencyCode;
 385  182
         this.decimalPlaces = decimalPlaces;
 386  182
     }
 387  
 
 388  
     /**
 389  
      * Block malicious data streams.
 390  
      * 
 391  
      * @param ois  the input stream, not null
 392  
      * @throws InvalidObjectException
 393  
      */
 394  
     private void readObject(ObjectInputStream ois) throws InvalidObjectException {
 395  0
         throw new InvalidObjectException("Serialization delegate required");
 396  
     }
 397  
 
 398  
     /**
 399  
      * Uses a serialization delegate.
 400  
      * 
 401  
      * @return the replacing object, never null
 402  
      */
 403  
     private Object writeReplace() {
 404  3
         return new Ser(Ser.CURRENCY_UNIT, this);
 405  
     }
 406  
 
 407  
     //-----------------------------------------------------------------------
 408  
     /**
 409  
      * Gets the ISO-4217 three letter currency code.
 410  
      * <p>
 411  
      * Each currency is uniquely identified by a three-letter code.
 412  
      * 
 413  
      * @return the three letter ISO-4217 currency code, never null
 414  
      */
 415  
     public String getCode() {
 416  585
         return code;
 417  
     }
 418  
 
 419  
     /**
 420  
      * Gets the ISO-4217 numeric currency code.
 421  
      * <p>
 422  
      * The numeric code is an alternative to the standard three letter code.
 423  
      * 
 424  
      * @return the numeric currency code
 425  
      */
 426  
     public int getNumericCode() {
 427  20
         return numericCode;
 428  
     }
 429  
 
 430  
     /**
 431  
      * Gets the ISO-4217 numeric currency code as a three digit string.
 432  
      * <p>
 433  
      * This formats the numeric code as a three digit string prefixed by zeroes if necessary.
 434  
      * If there is no valid code, then an empty string is returned.
 435  
      * 
 436  
      * @return the three digit numeric currency code, empty is no code, never null
 437  
      */
 438  
     public String getNumeric3Code() {
 439  5
         if (numericCode < 0) {
 440  1
             return "";
 441  
         }
 442  4
         String str = Integer.toString(numericCode);
 443  4
         if (str.length() == 1) {
 444  1
             return "00" + str;
 445  
         }
 446  3
         if (str.length() == 2) {
 447  1
             return "0" + str;
 448  
         }
 449  2
         return str;
 450  
     }
 451  
 
 452  
     /**
 453  
      * Gets the country codes applicable to this currency.
 454  
      * <p>
 455  
      * A currency is typically valid in one or more countries.
 456  
      * The codes are typically defined by ISO-3166.
 457  
      * An empty set indicates that no the currency is not associated with a country code.
 458  
      * 
 459  
      * @return the country codes, may be empty, not null
 460  
      */
 461  
     public Set<String> getCountryCodes() {
 462  3
         Set<String> countryCodes = new HashSet<String>();
 463  3
         for (Entry<String, CurrencyUnit> entry : currenciesByCountry.entrySet()) {
 464  699
             if (this.equals(entry.getValue())) {
 465  29
                 countryCodes.add(entry.getKey());
 466  
             }
 467  
         }
 468  3
         return countryCodes;
 469  
     }
 470  
 
 471  
     //-----------------------------------------------------------------------
 472  
     /**
 473  
      * Gets the number of decimal places typically used by this currency.
 474  
      * <p>
 475  
      * Different currencies have different numbers of decimal places by default.
 476  
      * For example, 'GBP' has 2 decimal places, but 'JPY' has zero.
 477  
      * Pseudo-currencies will return zero.
 478  
      * <p>
 479  
      * See also {@link #getDefaultFractionDigits()}.
 480  
      * 
 481  
      * @return the decimal places, from 0 to 9 (normally 0, 2 or 3)
 482  
      */
 483  
     public int getDecimalPlaces() {
 484  489
         return decimalPlaces < 0 ? 0 : decimalPlaces;
 485  
     }
 486  
 
 487  
     /**
 488  
      * Checks if this is a pseudo-currency.
 489  
      * 
 490  
      * @return true if this is a pseudo-currency
 491  
      */
 492  
     public boolean isPseudoCurrency() {
 493  3
         return decimalPlaces < 0;
 494  
     }
 495  
 
 496  
     //-----------------------------------------------------------------------
 497  
     /**
 498  
      * Gets the ISO-4217 three-letter currency code.
 499  
      * <p>
 500  
      * This method matches the API of {@link Currency}.
 501  
      * 
 502  
      * @return the currency code, never null
 503  
      */
 504  
     public String getCurrencyCode() {
 505  1
         return code;
 506  
     }
 507  
 
 508  
     /**
 509  
      * Gets the number of fractional digits typically used by this currency.
 510  
      * <p>
 511  
      * Different currencies have different numbers of fractional digits by default.
 512  
      * For example, 'GBP' has 2 fractional digits, but 'JPY' has zero.
 513  
      * Pseudo-currencies are indicated by -1.
 514  
      * <p>
 515  
      * This method matches the API of {@link Currency}.
 516  
      * The alternative method {@link #getDecimalPlaces()} may be more useful.
 517  
      * 
 518  
      * @return the fractional digits, from 0 to 9 (normally 0, 2 or 3), or -1 for pseudo-currencies
 519  
      */
 520  
     public int getDefaultFractionDigits() {
 521  18
         return decimalPlaces;
 522  
     }
 523  
 
 524  
     //-----------------------------------------------------------------------
 525  
     /**
 526  
      * Gets the symbol for this locale from the JDK.
 527  
      * <p>
 528  
      * If this currency doesn't have a JDK equivalent, then the currency code
 529  
      * is returned.
 530  
      * <p>
 531  
      * This method matches the API of {@link Currency}.
 532  
      * 
 533  
      * @return the JDK currency instance, never null
 534  
      */
 535  
     public String getSymbol() {
 536  
         try {
 537  4
             return Currency.getInstance(code).getSymbol();
 538  1
         } catch (IllegalArgumentException ex) {
 539  1
             return code;
 540  
         }
 541  
     }
 542  
 
 543  
     /**
 544  
      * Gets the symbol for this locale from the JDK.
 545  
      * <p>
 546  
      * If this currency doesn't have a JDK equivalent, then the currency code
 547  
      * is returned.
 548  
      * <p>
 549  
      * This method matches the API of {@link Currency}.
 550  
      * 
 551  
      * @param locale  the locale to get the symbol for, not null
 552  
      * @return the JDK currency instance, never null
 553  
      */
 554  
     public String getSymbol(Locale locale) {
 555  8
         MoneyUtils.checkNotNull(locale, "Locale must not be null");
 556  
         try {
 557  8
             return Currency.getInstance(code).getSymbol(locale);
 558  1
         } catch (IllegalArgumentException ex) {
 559  1
             return code;
 560  
         }
 561  
     }
 562  
 
 563  
     //-----------------------------------------------------------------------
 564  
     /**
 565  
      * Gets the JDK currency instance equivalent to this currency.
 566  
      * <p>
 567  
      * This attempts to convert a {@code CurrencyUnit} to a JDK {@code Currency}.
 568  
      * 
 569  
      * @return the JDK currency instance, never null
 570  
      * @throws IllegalArgumentException if no matching currency exists in the JDK
 571  
      */
 572  
     public Currency toCurrency() {
 573  1
         return Currency.getInstance(code);
 574  
     }
 575  
 
 576  
     //-----------------------------------------------------------------------
 577  
     /**
 578  
      * Compares this currency to another by alphabetical comparison of the code.
 579  
      * 
 580  
      * @param other  the other currency, not null
 581  
      * @return negative if earlier alphabetically, 0 if equal, positive if greater alphabetically
 582  
      */
 583  
     public int compareTo(CurrencyUnit other) {
 584  6851
         return code.compareTo(other.code);
 585  
     }
 586  
 
 587  
     /**
 588  
      * Checks if this currency equals another currency.
 589  
      * <p>
 590  
      * The comparison checks the 3 letter currency code.
 591  
      * 
 592  
      * @param obj  the other currency, null returns false
 593  
      * @return true if equal
 594  
      */
 595  
     @Override
 596  
     public boolean equals(Object obj) {
 597  1403
         if (obj == this) {
 598  673
             return true;
 599  
         }
 600  730
         if (obj instanceof CurrencyUnit) {
 601  725
             return code.equals(((CurrencyUnit) obj).code);
 602  
         }
 603  5
         return false;
 604  
     }
 605  
 
 606  
     /**
 607  
      * Returns a suitable hash code for the currency.
 608  
      * 
 609  
      * @return the hash code
 610  
      */
 611  
     @Override
 612  
     public int hashCode() {
 613  6
         return code.hashCode();
 614  
     }
 615  
 
 616  
     //-----------------------------------------------------------------------
 617  
     /**
 618  
      * Gets the currency code as a string.
 619  
      * 
 620  
      * @return the currency code, never null
 621  
      */
 622  
     @Override
 623  
     @ToString
 624  
     public String toString() {
 625  434
         return code;
 626  
     }
 627  
 
 628  
 }