| /** |
| ******************************************************************************* |
| * Copyright (C) 2001-2003, International Business Machines Corporation and * |
| * others. All Rights Reserved. * |
| ******************************************************************************* |
| * |
| * $Source: /xsrl/Nsvn/icu/icu4j/src/com/ibm/icu/util/Currency.java,v $ |
| * $Date: 2004/01/15 22:19:50 $ |
| * $Revision: 1.19 $ |
| * |
| ******************************************************************************* |
| */ |
| package com.ibm.icu.util; |
| |
| import java.io.Serializable; |
| import java.util.Locale; |
| import java.util.MissingResourceException; |
| import java.util.ResourceBundle; |
| |
| import com.ibm.icu.impl.ICULocaleData; |
| import com.ibm.icu.impl.LocaleUtility; |
| |
| /** |
| * 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 |
| */ |
| public class Currency implements Serializable { |
| |
| /** |
| * ISO 4217 3-letter code. |
| */ |
| private String isoCode; |
| |
| /** |
| * Selector for getName() indicating a symbolic name for a |
| * currency, such as "$" for USD. |
| * @draft ICU 2.6 |
| */ |
| public static final int SYMBOL_NAME = 0; |
| |
| /** |
| * Selector for ucurr_getName indicating the long name for a |
| * currency, such as "US Dollar" for USD. |
| * @draft ICU 2.6 |
| */ |
| public static final int LONG_NAME = 1; |
| |
| // begin registry stuff |
| |
| // shim for service code |
| /* package */ static abstract class ServiceShim { |
| abstract Locale[] getAvailableLocales(); |
| abstract Currency createInstance(Locale l); |
| abstract Object registerInstance(Currency c, Locale 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) { |
| e.printStackTrace(); |
| throw new RuntimeException(e.getMessage()); |
| } |
| } |
| return shim; |
| } |
| |
| /** |
| * Returns a currency object for the default currency in the given |
| * locale. |
| * @stable ICU 2.2 |
| */ |
| public static Currency getInstance(Locale locale) { |
| if (shim == null) { |
| return createCurrency(locale); |
| } |
| return shim.createInstance(locale); |
| } |
| |
| /** |
| * Instantiate a currency from a resource bundle found in Locale loc. |
| */ |
| /* package */ static Currency createCurrency(Locale loc) { |
| String country = loc.getCountry(); |
| String variant = loc.getVariant(); |
| if (variant.equals("PREEURO") || variant.equals("EURO")) { |
| country = country + '_' + variant; |
| } |
| ResourceBundle bundle = ICULocaleData.getLocaleElements(new Locale("", "", "")); |
| Object[][] cm = (Object[][]) bundle.getObject("CurrencyMap"); |
| |
| // Do a linear search |
| String curriso = null; |
| for (int i=0; i<cm.length; ++i) { |
| if (country.equals((String) cm[i][0])) { |
| curriso = (String) cm[i][1]; |
| break; |
| } |
| } |
| |
| Currency curr = null; |
| if (curriso != null) { |
| curr = new Currency(curriso); |
| |
| // TODO: Determine valid and actual locale correctly. |
| ULocale uloc = new ULocale(bundle.getLocale()); |
| curr.setLocale(uloc, uloc); |
| } |
| return curr; |
| } |
| |
| /** |
| * Returns a currency object given an ISO 4217 3-letter code. |
| * @stable ICU 2.2 |
| */ |
| public static Currency getInstance(String theISOCode) { |
| return new Currency(theISOCode); |
| } |
| |
| /** |
| * Registers a new currency for the provided locale. The returned object |
| * is a key that can be used to unregister this currency object. |
| * @draft ICU 2.6 |
| */ |
| public static Object registerInstance(Currency currency, Locale locale) { |
| return getShim().registerInstance(currency, locale); |
| } |
| |
| /** |
| * Unregister the currency associated with this key (obtained from |
| * registerInstance). |
| * @draft ICU 2.6 |
| */ |
| public static boolean unregister(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. |
| * @stable ICU 2.2 |
| */ |
| public static Locale[] getAvailableLocales() { |
| if (shim == null) { |
| return ICULocaleData.getAvailableLocales(); |
| } else { |
| return shim.getAvailableLocales(); |
| } |
| } |
| |
| // end registry stuff |
| |
| /** |
| * Return a hashcode for this currency. |
| * @stable ICU 2.2 |
| */ |
| 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 |
| */ |
| public boolean equals(Object rhs) { |
| try { |
| return equals((Currency)rhs); |
| } |
| catch (ClassCastException e) { |
| return false; |
| } |
| } |
| |
| /** |
| * Return true if c is non-null and has the same currency code. |
| * @stable ICU 2.2 |
| */ |
| public boolean equals(Currency c) { |
| if (c == null) return false; |
| if (c == this) return true; |
| return c.getClass() == Currency.class && |
| this.isoCode.equals(c.isoCode); |
| } |
| |
| /** |
| * Returns the ISO 4217 3-letter code for this currency object. |
| * @stable ICU 2.2 |
| */ |
| public String getCurrencyCode() { |
| return isoCode; |
| } |
| |
| /** |
| * 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 |
| * @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. |
| * @draft ICU 2.6 |
| */ |
| public String getName(Locale locale, |
| int nameStyle, |
| boolean[] isChoiceFormat) { |
| |
| // Look up the Currencies resource for the given locale. The |
| // Currencies locale data looks like this: |
| //|en { |
| //| Currencies { |
| //| USD { "US$", "US Dollar" } |
| //| CHF { "Sw F", "Swiss Franc" } |
| //| INR { "=0#Rs|1#Re|1<Rs", "=0#Rupees|1#Rupee|1<Rupees" } |
| //| //... |
| //| } |
| //|} |
| |
| if (nameStyle < 0 || nameStyle > 1) { |
| throw new IllegalArgumentException(); |
| } |
| |
| // In the future, resource bundles may implement multi-level |
| // fallback. That is, if a currency is not found in the en_US |
| // Currencies data, then the en Currencies data will be searched. |
| // Currently, if a Currencies datum exists in en_US and en, the |
| // en_US entry hides that in en. |
| |
| // We want multi-level fallback for this resource, so we implement |
| // it manually. |
| |
| String s = null; |
| |
| // Multi-level resource inheritance fallback loop |
| while (locale != null) { |
| ResourceBundle rb = ICULocaleData.getLocaleElements(locale); |
| // We can't cast this to String[][]; the cast has to happen later |
| try { |
| Object[][] currencies = (Object[][]) rb.getObject("Currencies"); |
| // Do a linear search |
| for (int i=0; i<currencies.length; ++i) { |
| if (isoCode.equals((String) currencies[i][0])) { |
| s = ((String[]) currencies[i][1])[nameStyle]; |
| break; |
| } |
| } |
| } |
| catch (MissingResourceException e) {} |
| |
| // If we've succeeded we're done. Otherwise, try to fallback. |
| // If that fails (because we are already at root) then exit. |
| if (s != null) { |
| break; |
| } |
| locale = LocaleUtility.fallback(locale); |
| } |
| |
| // Determine if this is a ChoiceFormat pattern. One leading mark |
| // indicates a ChoiceFormat. Two indicates a static string that |
| // starts with a mark. In either case, the first mark is ignored, |
| // if present. Marks in the rest of the string have no special |
| // meaning. |
| isChoiceFormat[0] = false; |
| if (s != null) { |
| int i=0; |
| while (i < s.length() && s.charAt(i) == '=' && i < 2) { |
| ++i; |
| } |
| isChoiceFormat[0]= (i == 1); |
| if (i != 0) { |
| // Skip over first mark |
| s = s.substring(1); |
| } |
| return s; |
| } |
| |
| // If we fail to find a match, use the ISO 4217 code |
| return isoCode; |
| } |
| |
| /** |
| * 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() { |
| return (findData())[0].intValue(); |
| } |
| |
| /** |
| * 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() { |
| Integer[] data = findData(); |
| |
| int data1 = data[1].intValue(); // rounding increment |
| |
| // 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 = data[0].intValue(); // fraction digits |
| |
| // 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 |
| */ |
| 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. |
| */ |
| private Currency(String theISOCode) { |
| isoCode = theISOCode; |
| } |
| |
| /** |
| * Internal function to look up currency data. Result is an array of |
| * two Integers. The first is the fraction digits. The second is the |
| * rounding increment, or 0 if none. The rounding increment is in |
| * units of 10^(-fraction_digits). |
| */ |
| private Integer[] findData() { |
| |
| try { |
| // Get CurrencyMeta resource out of root locale file. [This may |
| // move out of the root locale file later; if it does, update this |
| // code.] |
| ResourceBundle root = ICULocaleData.getLocaleElements(""); |
| |
| Object[][] currencyMeta = (Object[][]) root.getObject("CurrencyMeta"); |
| |
| Integer[] i = null; |
| int defaultPos = -1; |
| |
| // Do a linear search for isoCode. At the same time, |
| // record the position of the DEFAULT meta data. If the |
| // meta data becomes large, make this faster. |
| for (int j=0; j<currencyMeta.length; ++j) { |
| Object[] row = currencyMeta[j]; |
| String s = (String) row[0]; |
| int c = isoCode.compareToIgnoreCase(s); |
| if (c == 0) { |
| i = (Integer[]) row[1]; |
| break; |
| } |
| if ("DEFAULT".equalsIgnoreCase(s)) { |
| defaultPos = j; |
| } |
| if (c < 0 && defaultPos >= 0) { |
| break; |
| } |
| } |
| |
| if (i == null && defaultPos >= 0) { |
| i = (Integer[]) currencyMeta[defaultPos][1]; |
| } |
| |
| if (i != null && i.length >= 2) { |
| return i; |
| } |
| } |
| catch (MissingResourceException e) {} |
| |
| // Config/build error; return hard-coded defaults |
| return LAST_RESORT_DATA; |
| } |
| |
| // Default currency meta data of last resort. We try to use the |
| // defaults encoded in the meta data resource bundle. If there is a |
| // configuration/build error and these are not available, we use these |
| // hard-coded defaults (which should be identical). |
| private static final Integer[] LAST_RESORT_DATA = |
| new Integer[] { new Integer(2), new Integer(0) }; |
| |
| // POW10[i] = 10^i |
| private static final int[] POW10 = { 1, 10, 100, 1000, 10000, 100000, |
| 1000000, 10000000, 100000000, 1000000000 }; |
| |
| // -------- BEGIN ULocale boilerplate -------- |
| |
| /** |
| * Return the locale that was used to create this object, or null. |
| * This may may differ from the locale requested at the time of |
| * this object's creation. For example, if an object is created |
| * for locale <tt>en_US_CALIFORNIA</tt>, the actual data may be |
| * drawn from <tt>en</tt> (the <i>actual</i> locale), and |
| * <tt>en_US</tt> may be the most specific locale that exists (the |
| * <i>valid</i> locale). |
| * |
| * <p>Note: This method will be implemented in ICU 3.0; ICU 2.8 |
| * contains a partial preview implementation. The * <i>actual</i> |
| * locale is returned correctly, but the <i>valid</i> locale is |
| * not, in most cases. |
| * @param type type of information requested, either {@link |
| * com.ibm.icu.util.ULocale#VALID_LOCALE} or {@link |
| * com.ibm.icu.util.ULocale#ACTUAL_LOCALE}. |
| * @return the information specified by <i>type</i>, or null if |
| * this object was not constructed from locale data. |
| * @see com.ibm.icu.util.ULocale |
| * @see com.ibm.icu.util.ULocale#VALID_LOCALE |
| * @see com.ibm.icu.util.ULocale#ACTUAL_LOCALE |
| * @draft ICU 2.8 |
| */ |
| public final ULocale getLocale(ULocale.Type type) { |
| return type == ULocale.ACTUAL_LOCALE ? |
| this.actualLocale : this.validLocale; |
| } |
| |
| /** |
| * Set information about the locales that were used to create this |
| * object. If the object was not constructed from locale data, |
| * both arguments should be set to null. Otherwise, neither |
| * should be null. The actual locale must be at the same level or |
| * less specific than the valid locale. This method is intended |
| * for use by factories or other entities that create objects of |
| * this class. |
| * @param valid the most specific locale containing any resource |
| * data, or null |
| * @param actual the locale containing data used to construct this |
| * object, or null |
| * @see com.ibm.icu.util.ULocale |
| * @see com.ibm.icu.util.ULocale#VALID_LOCALE |
| * @see com.ibm.icu.util.ULocale#ACTUAL_LOCALE |
| * @internal |
| */ |
| final void setLocale(ULocale valid, ULocale actual) { |
| // Change the following to an assertion later |
| if ((valid == null) != (actual == null)) { |
| ///CLOVER:OFF |
| throw new IllegalArgumentException(); |
| ///CLOVER:ON |
| } |
| // Another check we could do is that the actual locale is at |
| // the same level or less specific than the valid locale. |
| this.validLocale = valid; |
| this.actualLocale = actual; |
| } |
| |
| /** |
| * The most specific locale containing any resource data, or null. |
| * @see com.ibm.icu.util.ULocale |
| * @internal |
| */ |
| private ULocale validLocale; |
| |
| /** |
| * The locale containing data used to construct this object, or |
| * null. |
| * @see com.ibm.icu.util.ULocale |
| * @internal |
| */ |
| private ULocale actualLocale; |
| |
| // -------- END ULocale boilerplate -------- |
| } |
| |
| //eof |