Package com.ibm.icu.util

Source Code of com.ibm.icu.util.Currency$CurrencyStringInfo

/**
*******************************************************************************
* Copyright (C) 2001-2013, International Business Machines Corporation and    *
* others. All Rights Reserved.                                                *
*******************************************************************************
*/
package com.ibm.icu.util;

import java.io.Serializable;
import java.lang.ref.SoftReference;
import java.text.ParsePosition;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.Set;

import com.ibm.icu.impl.ICUCache;
import com.ibm.icu.impl.ICUDebug;
import com.ibm.icu.impl.ICUResourceBundle;
import com.ibm.icu.impl.SimpleCache;
import com.ibm.icu.impl.TextTrieMap;
import com.ibm.icu.text.CurrencyDisplayNames;
import com.ibm.icu.text.CurrencyMetaInfo;
import com.ibm.icu.text.CurrencyMetaInfo.CurrencyDigits;
import com.ibm.icu.text.CurrencyMetaInfo.CurrencyFilter;
import com.ibm.icu.util.ULocale.Category;

/**
* A class encapsulating a currency, as defined by ISO 4217. A <tt>Currency</tt> object can be created given a <tt>Locale</tt> or given an
* ISO 4217 code. Once created, the <tt>Currency</tt> object can return various data necessary to its proper display:
*
* <ul>
* <li>A display symbol, for a specific locale
* <li>The number of fraction digits to display
* <li>A rounding increment
* </ul>
*
* The <tt>DecimalFormat</tt> class uses these data to display currencies.
*
* <p>
* Note: This class deliberately resembles <tt>java.util.Currency</tt> but it has a completely independent implementation, and adds features
* not present in the JDK.
*
* @author Alan Liu
* @stable ICU 2.2
*/
@SuppressWarnings("deprecation")
public class Currency extends MeasureUnit implements Serializable {
  // using serialver from jdk1.4.2_05
  private static final long serialVersionUID = -5839973855554750484L;
  private static final boolean DEBUG = ICUDebug.enabled("currency");

  // Cache to save currency name trie
  private static ICUCache<ULocale, List<TextTrieMap<CurrencyStringInfo>>> CURRENCY_NAME_CACHE = new SimpleCache<ULocale, List<TextTrieMap<CurrencyStringInfo>>>();

  /**
   * ISO 4217 3-letter code.
   */
  private String isoCode;

  /**
   * Selector for getName() indicating a symbolic name for a currency, such as "$" for USD.
   *
   * @stable ICU 2.6
   */
  public static final int SYMBOL_NAME = 0;

  /**
   * Selector for getName() indicating the long name for a currency, such as "US Dollar" for USD.
   *
   * @stable ICU 2.6
   */
  public static final int LONG_NAME = 1;

  /**
   * Selector for getName() indicating the plural long name for a currency, such as "US dollar" for USD in "1 US dollar", and "US dollars"
   * for USD in "2 US dollars".
   *
   * @stable ICU 4.2
   */
  public static final int PLURAL_LONG_NAME = 2;

  // begin registry stuff

  // shim for service code
  /* package */static abstract class ServiceShim {
    abstract ULocale[] getAvailableULocales();

    abstract Locale[] getAvailableLocales();

    abstract Currency createInstance(ULocale l);

    abstract Object registerInstance(Currency c, ULocale l);

    abstract boolean unregister(Object f);
  }

  private static ServiceShim shim;

  private static ServiceShim getShim() {
    // Note: this instantiation is safe on loose-memory-model configurations
    // despite lack of synchronization, since the shim instance has no state--
    // it's all in the class init.  The worst problem is we might instantiate
    // two shim instances, but they'll share the same state so that's ok.
    if (shim == null) {
      try {
        Class<?> cls = Class.forName("com.ibm.icu.util.CurrencyServiceShim");
        shim = (ServiceShim) cls.newInstance();
      } catch (Exception e) {
        if (DEBUG) {
          e.printStackTrace();
        }
        throw new RuntimeException(e.getMessage());
      }
    }
    return shim;
  }

  /**
   * Returns a currency object for the default currency in the given locale.
   *
   * @param locale
   *            the locale
   * @return the currency object for this locale
   * @stable ICU 2.2
   */
  public static Currency getInstance(final Locale locale) {
    return getInstance(ULocale.forLocale(locale));
  }

  /**
   * Returns a currency object for the default currency in the given locale.
   *
   * @stable ICU 3.2
   */
  public static Currency getInstance(final ULocale locale) {
    String currency = locale.getKeywordValue("currency");
    if (currency != null) {
      return getInstance(currency);
    }

    if (shim == null) {
      return createCurrency(locale);
    }

    return shim.createInstance(locale);
  }

  /**
   * Returns an array of Strings which contain the currency identifiers that are valid for the given locale on the given date. If there
   * are no such identifiers, returns null. Returned identifiers are in preference order.
   *
   * @param loc
   *            the locale for which to retrieve currency codes.
   * @param d
   *            the date for which to retrieve currency codes for the given locale.
   * @return The array of ISO currency codes.
   * @stable ICU 4.0
   */
  public static String[] getAvailableCurrencyCodes(final ULocale loc, final Date d) {
    CurrencyFilter filter = CurrencyFilter.onDate(d).withRegion(loc.getCountry());
    List<String> list = getTenderCurrencies(filter);
    // Note: Prior to 4.4 the spec didn't say that we return null if there are no results, but
    // the test assumed it did.  Kept the behavior and amended the spec.
    if (list.isEmpty()) {
      return null;
    }
    return list.toArray(new String[list.size()]);
  }

  /**
   * Returns the set of available currencies. The returned set of currencies contains all of the available currencies, including obsolete
   * ones. The result set can be modified without affecting the available currencies in the runtime.
   *
   * @return The set of available currencies. The returned set could be empty if there is no currency data available.
   *
   * @stable ICU 49
   */
  public static Set<Currency> getAvailableCurrencies() {
    CurrencyMetaInfo info = CurrencyMetaInfo.getInstance();
    List<String> list = info.currencies(CurrencyFilter.all());
    HashSet<Currency> resultSet = new HashSet<Currency>(list.size());
    for (String code : list) {
      resultSet.add(new Currency(code));
    }
    return resultSet;
  }

  private static final String EUR_STR = "EUR";
  private static final ICUCache<ULocale, String> currencyCodeCache = new SimpleCache<ULocale, String>();

  /**
   * Instantiate a currency from resource data.
   */
  /* package */static Currency createCurrency(final ULocale loc) {

    String variant = loc.getVariant();
    if ("EURO".equals(variant)) {
      return new Currency(EUR_STR);
    }

    String code = currencyCodeCache.get(loc);
    if (code == null) {
      String country = loc.getCountry();

      CurrencyMetaInfo info = CurrencyMetaInfo.getInstance();
      List<String> list = info.currencies(CurrencyFilter.onRegion(country));
      if (list.size() > 0) {
        code = list.get(0);
        boolean isPreEuro = "PREEURO".equals(variant);
        if (isPreEuro && EUR_STR.equals(code)) {
          if (list.size() < 2) {
            return null;
          }
          code = list.get(1);
        }
      } else {
        return null;
      }
      currencyCodeCache.put(loc, code);
    }
    return new Currency(code);
  }

  /**
   * Returns a currency object given an ISO 4217 3-letter code.
   *
   * @param theISOCode
   *            the iso code
   * @return the currency for this iso code
   * @throws NullPointerException
   *             if <code>theISOCode</code> is null.
   * @throws IllegalArgumentException
   *             if <code>theISOCode</code> is not a 3-letter alpha code.
   * @stable ICU 2.2
   */
  public static Currency getInstance(final String theISOCode) {
    if (theISOCode == null) {
      throw new NullPointerException("The input currency code is null.");
    }
    if (!isAlpha3Code(theISOCode)) {
      throw new IllegalArgumentException("The input currency code is not 3-letter alphabetic code.");
    }
    return new Currency(theISOCode.toUpperCase(Locale.ENGLISH));
  }

  private static boolean isAlpha3Code(final String code) {
    if (code.length() != 3) {
      return false;
    } else {
      for (int i = 0; i < 3; i++) {
        char ch = code.charAt(i);
        if (ch < 'A' || (ch > 'Z' && ch < 'a') || ch > 'z') {
          return false;
        }
      }
    }
    return true;
  }

  /**
   * Registers a new currency for the provided locale. The returned object is a key that can be used to unregister this currency object.
   *
   * @param currency
   *            the currency to register
   * @param locale
   *            the ulocale under which to register the currency
   * @return a registry key that can be used to unregister this currency
   * @see #unregister
   * @stable ICU 3.2
   */
  public static Object registerInstance(final Currency currency, final ULocale locale) {
    return getShim().registerInstance(currency, locale);
  }

  /**
   * Unregister the currency associated with this key (obtained from registerInstance).
   *
   * @param registryKey
   *            the registry key returned from registerInstance
   * @see #registerInstance
   * @stable ICU 2.6
   */
  public static boolean unregister(final Object registryKey) {
    if (registryKey == null) {
      throw new IllegalArgumentException("registryKey must not be null");
    }
    if (shim == null) {
      return false;
    }
    return shim.unregister(registryKey);
  }

  /**
   * Return an array of the locales for which a currency is defined.
   *
   * @return an array of the available locales
   * @stable ICU 2.2
   */
  public static Locale[] getAvailableLocales() {
    if (shim == null) {
      return ICUResourceBundle.getAvailableLocales();
    } else {
      return shim.getAvailableLocales();
    }
  }

  /**
   * Return an array of the ulocales for which a currency is defined.
   *
   * @return an array of the available ulocales
   * @stable ICU 3.2
   */
  public static ULocale[] getAvailableULocales() {
    if (shim == null) {
      return ICUResourceBundle.getAvailableULocales();
    } else {
      return shim.getAvailableULocales();
    }
  }

  // end registry stuff

  /**
   * Given a key and a locale, returns an array of values for the key for which data exists. If commonlyUsed is true, these are the values
   * that typically are used with this locale, otherwise these are all values for which data exists. This is a common service API.
   * <p>
   * The only supported key is "currency", other values return an empty array.
   * <p>
   * Currency information is based on the region of the locale. If the locale does not indicate a region,
   * {@link ULocale#addLikelySubtags(ULocale)} is used to infer a region, except for the 'und' locale.
   * <p>
   * If commonlyUsed is true, only the currencies known to be in use as of the current date are returned. When there are more than one,
   * these are returned in preference order (typically, this occurs when a country is transitioning to a new currency, and the newer
   * currency is preferred), see <a href="http://unicode.org/reports/tr35/#Supplemental_Currency_Data">Unicode TR#35 Sec. C1</a>. If
   * commonlyUsed is false, all currencies ever used in any locale are returned, in no particular order.
   *
   * @param key
   *            key whose values to look up. the only recognized key is "currency"
   * @param locale
   *            the locale
   * @param commonlyUsed
   *            if true, return only values that are currently used in the locale. Otherwise returns all values.
   * @return an array of values for the given key and the locale. If there is no data, the array will be empty.
   * @stable ICU 4.2
   */
  public static final String[] getKeywordValuesForLocale(final String key, final ULocale locale, final boolean commonlyUsed) {

    // The only keyword we recognize is 'currency'
    if (!"currency".equals(key)) {
      return EMPTY_STRING_ARRAY;
    }

    if (!commonlyUsed) {
      // Behavior change from 4.3.3, no longer sort the currencies
      return getAllTenderCurrencies().toArray(new String[0]);
    }

    // Don't resolve region if the requested locale is 'und', it will resolve to US
    // which we don't want.
    String prefRegion = locale.getCountry();
    if (prefRegion.length() == 0) {
      if (UND.equals(locale)) {
        return EMPTY_STRING_ARRAY;
      }
      ULocale loc = ULocale.addLikelySubtags(locale);
      prefRegion = loc.getCountry();
    }

    CurrencyFilter filter = CurrencyFilter.now().withRegion(prefRegion);

    // currencies are in region's preferred order when we're filtering on region, which
    // matches our spec
    List<String> result = getTenderCurrencies(filter);

    // No fallback anymore (change from 4.3.3)
    if (result.size() == 0) {
      return EMPTY_STRING_ARRAY;
    }

    return result.toArray(new String[result.size()]);
  }

  private static final ULocale UND = new ULocale("und");
  private static final String[] EMPTY_STRING_ARRAY = new String[0];

  /**
   * Return a hashcode for this currency.
   *
   * @stable ICU 2.2
   */
  @Override
  public int hashCode() {
    return isoCode.hashCode();
  }

  /**
   * Return true if rhs is a Currency instance, is non-null, and has the same currency code.
   *
   * @stable ICU 2.2
   */
  @Override
  public boolean equals(final Object rhs) {
    if (rhs == null)
      return false;
    if (rhs == this)
      return true;
    try {
      Currency c = (Currency) rhs;
      return isoCode.equals(c.isoCode);
    } catch (ClassCastException e) {
      return false;
    }
  }

  /**
   * Returns the ISO 4217 3-letter code for this currency object.
   *
   * @stable ICU 2.2
   */
  public String getCurrencyCode() {
    return isoCode;
  }

  /**
   * Returns the ISO 4217 numeric code for this currency object.
   * <p>
   * Note: If the ISO 4217 numeric code is not assigned for the currency or the currency is unknown, this method returns 0.
   * </p>
   *
   * @return The ISO 4217 numeric code of this currency.
   * @stable ICU 49
   */
  public int getNumericCode() {
    int code = 0;
    try {
      UResourceBundle bundle = UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, "currencyNumericCodes",
          ICUResourceBundle.ICU_DATA_CLASS_LOADER);
      UResourceBundle codeMap = bundle.get("codeMap");
      UResourceBundle numCode = codeMap.get(isoCode);
      code = numCode.getInt();
    } catch (MissingResourceException e) {
      // fall through
    }
    return code;
  }

  /**
   * Convenience and compatibility override of getName that requests the symbol name for the default <code>DISPLAY</code> locale.
   *
   * @see #getName
   * @see Category#DISPLAY
   * @stable ICU 3.4
   */
  public String getSymbol() {
    return getSymbol(ULocale.getDefault(Category.DISPLAY));
  }

  /**
   * Convenience and compatibility override of getName that requests the symbol name.
   *
   * @param loc
   *            the Locale for the symbol
   * @see #getName
   * @stable ICU 3.4
   */
  public String getSymbol(final Locale loc) {
    return getSymbol(ULocale.forLocale(loc));
  }

  /**
   * Convenience and compatibility override of getName that requests the symbol name.
   *
   * @param uloc
   *            the ULocale for the symbol
   * @see #getName
   * @stable ICU 3.4
   */
  public String getSymbol(final ULocale uloc) {
    return getName(uloc, SYMBOL_NAME, new boolean[1]);
  }

  /**
   * Returns the display name for the given currency in the given locale. This is a convenient method for getName(ULocale, int,
   * boolean[]);
   *
   * @stable ICU 3.2
   */
  public String getName(final Locale locale, final int nameStyle, final boolean[] isChoiceFormat) {
    return getName(ULocale.forLocale(locale), nameStyle, isChoiceFormat);
  }

  /**
   * Returns the display name for the given currency in the given locale. For example, the display name for the USD currency object in the
   * en_US locale is "$".
   *
   * @param locale
   *            locale in which to display currency
   * @param nameStyle
   *            selector for which kind of name to return. The nameStyle should be either SYMBOL_NAME or LONG_NAME. Otherwise, throw
   *            IllegalArgumentException.
   * @param isChoiceFormat
   *            fill-in; isChoiceFormat[0] is set to true if the returned value is a ChoiceFormat pattern; otherwise it is set to false
   * @return display string for this currency. If the resource data contains no entry for this currency, then the ISO 4217 code is
   *         returned. If isChoiceFormat[0] is true, then the result is a ChoiceFormat pattern. Otherwise it is a static string.
   *         <b>Note:</b> as of ICU 4.4, choice formats are not used, and the value returned in isChoiceFormat is always false.
   *         <p>
   * @throws IllegalArgumentException
   *             if the nameStyle is not SYMBOL_NAME or LONG_NAME.
   * @see #getName(ULocale, int, String, boolean[])
   * @stable ICU 3.2
   */
  public String getName(final ULocale locale, final int nameStyle, final boolean[] isChoiceFormat) {
    if (!(nameStyle == SYMBOL_NAME || nameStyle == LONG_NAME)) {
      throw new IllegalArgumentException("bad name style: " + nameStyle);
    }

    // We no longer support choice format data in names.  Data should not contain
    // choice patterns.
    if (isChoiceFormat != null) {
      isChoiceFormat[0] = false;
    }

    CurrencyDisplayNames names = CurrencyDisplayNames.getInstance(locale);
    return nameStyle == SYMBOL_NAME ? names.getSymbol(isoCode) : names.getName(isoCode);
  }

  /**
   * Returns the display name for the given currency in the given locale. This is a convenience overload of getName(ULocale, int, String,
   * boolean[]);
   *
   * @stable ICU 4.2
   */
  public String getName(final Locale locale, final int nameStyle, final String pluralCount, final boolean[] isChoiceFormat) {
    return getName(ULocale.forLocale(locale), nameStyle, pluralCount, isChoiceFormat);
  }

  /**
   * Returns the display name for the given currency in the given locale. For example, the SYMBOL_NAME for the USD currency object in the
   * en_US locale is "$". The PLURAL_LONG_NAME for the USD currency object when the currency amount is plural is "US dollars", such as in
   * "3.00 US dollars"; while the PLURAL_LONG_NAME for the USD currency object when the currency amount is singular is "US dollar", such
   * as in "1.00 US dollar".
   *
   * @param locale
   *            locale in which to display currency
   * @param nameStyle
   *            selector for which kind of name to return
   * @param pluralCount
   *            plural count string for this locale
   * @param isChoiceFormat
   *            fill-in; isChoiceFormat[0] is set to true if the returned value is a ChoiceFormat pattern; otherwise it is set to false
   * @return display string for this currency. If the resource data contains no entry for this currency, then the ISO 4217 code is
   *         returned. If isChoiceFormat[0] is true, then the result is a ChoiceFormat pattern. Otherwise it is a static string.
   *         <b>Note:</b> as of ICU 4.4, choice formats are not used, and the value returned in isChoiceFormat is always false.
   * @throws IllegalArgumentException
   *             if the nameStyle is not SYMBOL_NAME, LONG_NAME, or PLURAL_LONG_NAME.
   * @stable ICU 4.2
   */
  public String getName(final ULocale locale, final int nameStyle, final String pluralCount, final boolean[] isChoiceFormat) {
    if (nameStyle != PLURAL_LONG_NAME) {
      return getName(locale, nameStyle, isChoiceFormat);
    }

    // We no longer support choice format
    if (isChoiceFormat != null) {
      isChoiceFormat[0] = false;
    }

    CurrencyDisplayNames names = CurrencyDisplayNames.getInstance(locale);
    return names.getPluralName(isoCode, pluralCount);
  }

  /**
   * Returns the display name for this currency in the default locale. If the resource data for the default locale contains no entry for
   * this currency, then the ISO 4217 code is returned.
   * <p>
   * Note: This method was added for JDK compatibility support and equivalent to
   * <code>getName(Locale.getDefault(), LONG_NAME, null)</code>.
   *
   * @return The display name of this currency
   * @see #getDisplayName(Locale)
   * @see #getName(Locale, int, boolean[])
   * @stable ICU 49
   */
  public String getDisplayName() {
    return getName(Locale.getDefault(), LONG_NAME, null);
  }

  /**
   * Returns the display name for this currency in the given locale. If the resource data for the given locale contains no entry for this
   * currency, then the ISO 4217 code is returned.
   * <p>
   * Note: This method was added for JDK compatibility support and equivalent to <code>getName(locale, LONG_NAME, null)</code>.
   *
   * @param locale
   *            locale in which to display currency
   * @return The display name of this currency for the specified locale
   * @see #getDisplayName(Locale)
   * @see #getName(Locale, int, boolean[])
   * @stable ICU 49
   */
  public String getDisplayName(final Locale locale) {
    return getName(locale, LONG_NAME, null);
  }

  /**
   * Attempt to parse the given string as a currency, either as a display name in the given locale, or as a 3-letter ISO 4217 code. If
   * multiple display names match, then the longest one is selected. If both a display name and a 3-letter ISO code match, then the
   * display name is preferred, unless it's length is less than 3.
   *
   * @param locale
   *            the locale of the display names to match
   * @param text
   *            the text to parse
   * @param type
   *            parse against currency type: LONG_NAME only or not
   * @param pos
   *            input-output position; on input, the position within text to match; must have 0 <= pos.getIndex() < text.length(); on
   *            output, the position after the last matched character. If the parse fails, the position in unchanged upon output.
   * @return the ISO 4217 code, as a string, of the best match, or null if there is no match
   *
   * @internal
   * @deprecated This API is ICU internal only.
   */
  @Deprecated
  public static String parse(final ULocale locale, final String text, final int type, final ParsePosition pos) {
    List<TextTrieMap<CurrencyStringInfo>> currencyTrieVec = CURRENCY_NAME_CACHE.get(locale);
    if (currencyTrieVec == null) {
      TextTrieMap<CurrencyStringInfo> currencyNameTrie = new TextTrieMap<CurrencyStringInfo>(true);
      TextTrieMap<CurrencyStringInfo> currencySymbolTrie = new TextTrieMap<CurrencyStringInfo>(false);
      currencyTrieVec = new ArrayList<TextTrieMap<CurrencyStringInfo>>();
      currencyTrieVec.add(currencySymbolTrie);
      currencyTrieVec.add(currencyNameTrie);
      setupCurrencyTrieVec(locale, currencyTrieVec);
      CURRENCY_NAME_CACHE.put(locale, currencyTrieVec);
    }

    int maxLength = 0;
    String isoResult = null;

    // look for the names
    TextTrieMap<CurrencyStringInfo> currencyNameTrie = currencyTrieVec.get(1);
    CurrencyNameResultHandler handler = new CurrencyNameResultHandler();
    currencyNameTrie.find(text, pos.getIndex(), handler);
    List<CurrencyStringInfo> list = handler.getMatchedCurrencyNames();
    if (list != null && list.size() != 0) {
      for (CurrencyStringInfo info : list) {
        String isoCode = info.getISOCode();
        String currencyString = info.getCurrencyString();
        if (currencyString.length() > maxLength) {
          maxLength = currencyString.length();
          isoResult = isoCode;
        }
      }
    }

    if (type != Currency.LONG_NAME) {  // not long name only
      TextTrieMap<CurrencyStringInfo> currencySymbolTrie = currencyTrieVec.get(0);
      handler = new CurrencyNameResultHandler();
      currencySymbolTrie.find(text, pos.getIndex(), handler);
      list = handler.getMatchedCurrencyNames();
      if (list != null && list.size() != 0) {
        for (CurrencyStringInfo info : list) {
          String isoCode = info.getISOCode();
          String currencyString = info.getCurrencyString();
          if (currencyString.length() > maxLength) {
            maxLength = currencyString.length();
            isoResult = isoCode;
          }
        }
      }
    }

    int start = pos.getIndex();
    pos.setIndex(start + maxLength);
    return isoResult;
  }

  private static void setupCurrencyTrieVec(final ULocale locale, final List<TextTrieMap<CurrencyStringInfo>> trieVec) {

    TextTrieMap<CurrencyStringInfo> symTrie = trieVec.get(0);
    TextTrieMap<CurrencyStringInfo> trie = trieVec.get(1);

    CurrencyDisplayNames names = CurrencyDisplayNames.getInstance(locale);
    for (Map.Entry<String, String> e : names.symbolMap().entrySet()) {
      String symbol = e.getKey();
      String isoCode = e.getValue();
      symTrie.put(symbol, new CurrencyStringInfo(isoCode, symbol));
    }
    for (Map.Entry<String, String> e : names.nameMap().entrySet()) {
      String name = e.getKey();
      String isoCode = e.getValue();
      trie.put(name, new CurrencyStringInfo(isoCode, name));
    }
  }

  private static final class CurrencyStringInfo {
    private String isoCode;
    private String currencyString;

    public CurrencyStringInfo(final String isoCode, final String currencyString) {
      this.isoCode = isoCode;
      this.currencyString = currencyString;
    }

    private String getISOCode() {
      return isoCode;
    }

    private String getCurrencyString() {
      return currencyString;
    }
  }

  private static class CurrencyNameResultHandler implements TextTrieMap.ResultHandler<CurrencyStringInfo> {
    private ArrayList<CurrencyStringInfo> resultList;

    public boolean handlePrefixMatch(final int matchLength, final Iterator<CurrencyStringInfo> values) {
      if (resultList == null) {
        resultList = new ArrayList<CurrencyStringInfo>();
      }
      while (values.hasNext()) {
        CurrencyStringInfo item = values.next();
        if (item == null) {
          break;
        }
        int i = 0;
        for (; i < resultList.size(); i++) {
          CurrencyStringInfo tmp = resultList.get(i);
          if (item.getISOCode().equals(tmp.getISOCode())) {
            if (matchLength > tmp.getCurrencyString().length()) {
              resultList.set(i, item);
            }
            break;
          }
        }
        if (i == resultList.size()) {
          // not found in the current list
          resultList.add(item);
        }
      }
      return true;
    }

    List<CurrencyStringInfo> getMatchedCurrencyNames() {
      if (resultList == null || resultList.size() == 0) {
        return null;
      }
      return resultList;
    }
  }

  /**
   * Returns the number of the number of fraction digits that should be displayed for this currency.
   *
   * @return a non-negative number of fraction digits to be displayed
   * @stable ICU 2.2
   */
  public int getDefaultFractionDigits() {
    CurrencyMetaInfo info = CurrencyMetaInfo.getInstance();
    CurrencyDigits digits = info.currencyDigits(isoCode);
    return digits.fractionDigits;
  }

  /**
   * Returns the rounding increment for this currency, or 0.0 if no rounding is done by this currency.
   *
   * @return the non-negative rounding increment, or 0.0 if none
   * @stable ICU 2.2
   */
  public double getRoundingIncrement() {
    CurrencyMetaInfo info = CurrencyMetaInfo.getInstance();
    CurrencyDigits digits = info.currencyDigits(isoCode);

    int data1 = digits.roundingIncrement;

    // If there is no rounding return 0.0 to indicate no rounding.
    // This is the high-runner case, by far.
    if (data1 == 0) {
      return 0.0;
    }

    int data0 = digits.fractionDigits;

    // If the meta data is invalid, return 0.0 to indicate no rounding.
    if (data0 < 0 || data0 >= POW10.length) {
      return 0.0;
    }

    // Return data[1] / 10^(data[0]).  The only actual rounding data,
    // as of this writing, is CHF { 2, 25 }.
    return (double) data1 / POW10[data0];
  }

  /**
   * Returns the ISO 4217 code for this currency.
   *
   * @stable ICU 2.2
   */
  @Override
  public String toString() {
    return isoCode;
  }

  /**
   * Constructs a currency object for the given ISO 4217 3-letter code. This constructor assumes that the code is valid.
   *
   * @param theISOCode
   *            The iso code used to construct the currency.
   * @stable ICU 3.4
   */
  protected Currency(final String theISOCode) {
    isoCode = theISOCode;
  }

  // POW10[i] = 10^i
  private static final int[] POW10 = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };

  private static SoftReference<List<String>> ALL_TENDER_CODES;
  private static SoftReference<Set<String>> ALL_CODES_AS_SET;

  /*
   * Returns an unmodifiable String list including all known tender currency codes.
   */
  private static synchronized List<String> getAllTenderCurrencies() {
    List<String> all = (ALL_TENDER_CODES == null) ? null : ALL_TENDER_CODES.get();
    if (all == null) {
      // Filter out non-tender currencies which have "from" date set to 9999-12-31
      // CurrencyFilter has "to" value set to 9998-12-31 in order to exclude them
      //CurrencyFilter filter = CurrencyFilter.onDateRange(null, new Date(253373299200000L));
      CurrencyFilter filter = CurrencyFilter.all();
      all = Collections.unmodifiableList(getTenderCurrencies(filter));
      ALL_TENDER_CODES = new SoftReference<List<String>>(all);
    }
    return all;
  }

  private static synchronized Set<String> getAllCurrenciesAsSet() {
    Set<String> all = (ALL_CODES_AS_SET == null) ? null : ALL_CODES_AS_SET.get();
    if (all == null) {
      CurrencyMetaInfo info = CurrencyMetaInfo.getInstance();
      all = Collections.unmodifiableSet(new HashSet<String>(info.currencies(CurrencyFilter.all())));
      ALL_CODES_AS_SET = new SoftReference<Set<String>>(all);
    }
    return all;
  }

  /**
   * Queries if the given ISO 4217 3-letter code is available on the specified date range.
   * <p>
   * Note: For checking availability of a currency on a specific date, specify the date on both <code>from</code> and <code>to</code>.
   * When both <code>from</code> and <code>to</code> are null, this method checks if the specified currency is available all time.
   *
   * @param code
   *            The ISO 4217 3-letter code.
   * @param from
   *            The lower bound of the date range, inclusive. When <code>from</code> is null, check the availability of the currency any
   *            date before <code>to</code>
   * @param to
   *            The upper bound of the date range, inclusive. When <code>to</code> is null, check the availability of the currency any
   *            date after <code>from</code>
   * @return true if the given ISO 4217 3-letter code is supported on the specified date range.
   * @throws IllegalArgumentException
   *             when <code>to</code> is before <code>from</code>.
   *
   * @stable ICU 4.6
   */
  public static boolean isAvailable(String code, final Date from, final Date to) {
    if (!isAlpha3Code(code)) {
      return false;
    }

    if (from != null && to != null && from.after(to)) {
      throw new IllegalArgumentException("To is before from");
    }

    code = code.toUpperCase(Locale.ENGLISH);
    boolean isKnown = getAllCurrenciesAsSet().contains(code);
    if (isKnown == false) {
      return false;
    } else if (from == null && to == null) {
      return true;
    }

    // If caller passed a date range, we cannot rely solely on the cache
    CurrencyMetaInfo info = CurrencyMetaInfo.getInstance();
    List<String> allActive = info.currencies(CurrencyFilter.onDateRange(from, to).withCurrency(code));
    return allActive.contains(code);
  }

  /**
   * Returns the list of remaining tender currencies after a filter is applied.
   *
   * @param filter
   *            the filter to apply to the tender currencies
   * @return a list of tender currencies
   */
  private static List<String> getTenderCurrencies(final CurrencyFilter filter) {
    CurrencyMetaInfo info = CurrencyMetaInfo.getInstance();
    return info.currencies(filter.withTender());
  }
}
//eof
TOP

Related Classes of com.ibm.icu.util.Currency$CurrencyStringInfo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.