1/*
2 *******************************************************************************
3 * Copyright (C) 2009-2014, International Business Machines Corporation and    *
4 * others. All Rights Reserved.                                                *
5 *******************************************************************************
6 */
7package com.ibm.icu.text;
8
9import java.io.Serializable;
10import java.util.HashMap;
11import java.util.Iterator;
12import java.util.Locale;
13import java.util.Map;
14
15import com.ibm.icu.impl.CurrencyData;
16import com.ibm.icu.util.ICUCloneNotSupportedException;
17import com.ibm.icu.util.ULocale;
18import com.ibm.icu.util.ULocale.Category;
19
20/**
21 * This class represents the information needed by
22 * DecimalFormat to format currency plural,
23 * such as "3.00 US dollars" or "1.00 US dollar".
24 * DecimalFormat creates for itself an instance of
25 * CurrencyPluralInfo from its locale data.
26 * If you need to change any of these symbols, you can get the
27 * CurrencyPluralInfo object from your
28 * DecimalFormat and modify it.
29 *
30 * Following are the information needed for currency plural format and parse:
31 * locale information,
32 * plural rule of the locale,
33 * currency plural pattern of the locale.
34 *
35 * @stable ICU 4.2
36 */
37
38public class CurrencyPluralInfo implements Cloneable, Serializable {
39    private static final long serialVersionUID = 1;
40
41    /**
42     * Create a CurrencyPluralInfo object for the default <code>FORMAT</code> locale.
43     * @see Category#FORMAT
44     * @stable ICU 4.2
45     */
46    public CurrencyPluralInfo() {
47        initialize(ULocale.getDefault(Category.FORMAT));
48    }
49
50    /**
51     * Create a CurrencyPluralInfo object for the given locale.
52     * @param locale the locale
53     * @stable ICU 4.2
54     */
55    public CurrencyPluralInfo(Locale locale) {
56        initialize(ULocale.forLocale(locale));
57    }
58
59    /**
60     * Create a CurrencyPluralInfo object for the given locale.
61     * @param locale the locale
62     * @stable ICU 4.2
63     */
64    public CurrencyPluralInfo(ULocale locale) {
65        initialize(locale);
66    }
67
68    /**
69     * Gets a CurrencyPluralInfo instance for the default locale.
70     *
71     * @return A CurrencyPluralInfo instance.
72     * @stable ICU 4.2
73     */
74    public static CurrencyPluralInfo getInstance() {
75        return new CurrencyPluralInfo();
76    }
77
78    /**
79     * Gets a CurrencyPluralInfo instance for the given locale.
80     *
81     * @param locale the locale.
82     * @return A CurrencyPluralInfo instance.
83     * @stable ICU 4.2
84     */
85    public static CurrencyPluralInfo getInstance(Locale locale) {
86        return new CurrencyPluralInfo(locale);
87    }
88
89    /**
90     * Gets a CurrencyPluralInfo instance for the given locale.
91     *
92     * @param locale the locale.
93     * @return A CurrencyPluralInfo instance.
94     * @stable ICU 4.2
95     */
96    public static CurrencyPluralInfo getInstance(ULocale locale) {
97        return new CurrencyPluralInfo(locale);
98    }
99
100    /**
101     * Gets plural rules of this locale, used for currency plural format
102     *
103     * @return plural rule
104     * @stable ICU 4.2
105     */
106    public PluralRules getPluralRules() {
107        return pluralRules;
108    }
109
110    /**
111     * Given a plural count, gets currency plural pattern of this locale,
112     * used for currency plural format
113     *
114     * @param  pluralCount currency plural count
115     * @return a currency plural pattern based on plural count
116     * @stable ICU 4.2
117     */
118    public String getCurrencyPluralPattern(String pluralCount) {
119        String currencyPluralPattern = pluralCountToCurrencyUnitPattern.get(pluralCount);
120        if (currencyPluralPattern == null) {
121            // fall back to "other"
122            if (!pluralCount.equals("other")) {
123                currencyPluralPattern = pluralCountToCurrencyUnitPattern.get("other");
124            }
125            if (currencyPluralPattern == null) {
126                // no currencyUnitPatterns defined,
127                // fallback to predefined default.
128                // This should never happen when ICU resource files are
129                // available, since currencyUnitPattern of "other" is always
130                // defined in root.
131                currencyPluralPattern = defaultCurrencyPluralPattern;
132            }
133        }
134        return currencyPluralPattern;
135    }
136
137    /**
138     * Get locale
139     *
140     * @return locale
141     *
142     * @stable ICU 4.2
143     */
144    public ULocale getLocale() {
145        return ulocale;
146    }
147
148    /**
149     * Set plural rules.  These are initially set in the constructor based on the locale,
150     * and usually do not need to be changed.
151     *
152     * @param ruleDescription new plural rule description
153     * @stable ICU 4.2
154     */
155    public void setPluralRules(String ruleDescription) {
156        pluralRules = PluralRules.createRules(ruleDescription);
157    }
158
159    /**
160     * Set currency plural patterns.  These are initially set in the constructor based on the
161     * locale, and usually do not need to be changed.
162     *
163     * @param pluralCount the plural count for which the currency pattern will
164     *                    be overridden.
165     * @param pattern     the new currency plural pattern
166     * @stable ICU 4.2
167     */
168    public void setCurrencyPluralPattern(String pluralCount, String pattern) {
169        pluralCountToCurrencyUnitPattern.put(pluralCount, pattern);
170    }
171
172    /**
173     * Set locale.  This also sets both the plural rules and the currency plural patterns to be
174     * the defaults for the locale.
175     *
176     * @param loc the new locale to set
177     * @stable ICU 4.2
178     */
179    public void setLocale(ULocale loc) {
180        ulocale = loc;
181        initialize(loc);
182    }
183
184    /**
185     * Standard override
186     *
187     * @stable ICU 4.2
188     */
189    public Object clone() {
190        try {
191            CurrencyPluralInfo other = (CurrencyPluralInfo) super.clone();
192            // locale is immutable
193            other.ulocale = (ULocale)ulocale.clone();
194            // plural rule is immutable
195            //other.pluralRules = pluralRules;
196            // clone content
197            //other.pluralCountToCurrencyUnitPattern = pluralCountToCurrencyUnitPattern;
198            other.pluralCountToCurrencyUnitPattern = new HashMap<String, String>();
199            for (String pluralCount : pluralCountToCurrencyUnitPattern.keySet()) {
200                String currencyPattern = pluralCountToCurrencyUnitPattern.get(pluralCount);
201                other.pluralCountToCurrencyUnitPattern.put(pluralCount, currencyPattern);
202            }
203            return other;
204        } catch (CloneNotSupportedException e) {
205            throw new ICUCloneNotSupportedException(e);
206        }
207    }
208
209    /**
210     * Override equals
211     *
212     * @stable ICU 4.2
213     */
214    public boolean equals(Object a) {
215        if (a instanceof CurrencyPluralInfo) {
216            CurrencyPluralInfo other = (CurrencyPluralInfo)a;
217            return pluralRules.equals(other.pluralRules) &&
218                   pluralCountToCurrencyUnitPattern.equals(other.pluralCountToCurrencyUnitPattern);
219        }
220        return false;
221    }
222
223    /**
224     * Mock implementation of hashCode(). This implementation always returns a constant
225     * value. When Java assertion is enabled, this method triggers an assertion failure.
226     * @internal
227     * @deprecated This API is ICU internal only.
228     */
229    @Deprecated
230    public int hashCode() {
231        assert false : "hashCode not designed";
232        return 42;
233    }
234
235    /**
236     * Given a number, returns the keyword of the first rule that applies
237     * to the number.
238     * @internal
239     * @deprecated This API is ICU internal only.
240     */
241    @Deprecated
242    String select(double number) {
243        return pluralRules.select(number);
244    }
245
246    /**
247     * Given a number, returns the keyword of the first rule that applies
248     * to the number.
249     * @internal
250     * @deprecated This API is ICU internal only.
251     */
252    @Deprecated
253    String select(PluralRules.FixedDecimal numberInfo) {
254        return pluralRules.select(numberInfo);
255    }
256
257    /**
258     * Currency plural pattern iterator.
259     *
260     * @return a iterator on the currency plural pattern key set.
261     */
262    Iterator<String> pluralPatternIterator() {
263        return pluralCountToCurrencyUnitPattern.keySet().iterator();
264    }
265
266    private void initialize(ULocale uloc) {
267        ulocale = uloc;
268        pluralRules = PluralRules.forLocale(uloc);
269        setupCurrencyPluralPattern(uloc);
270    }
271
272    private void setupCurrencyPluralPattern(ULocale uloc) {
273        pluralCountToCurrencyUnitPattern = new HashMap<String, String>();
274
275        String numberStylePattern = NumberFormat.getPattern(uloc, NumberFormat.NUMBERSTYLE);
276        // Split the number style pattern into pos and neg if applicable
277        int separatorIndex = numberStylePattern.indexOf(";");
278        String negNumberPattern = null;
279        if (separatorIndex != -1) {
280            negNumberPattern = numberStylePattern.substring(separatorIndex + 1);
281            numberStylePattern = numberStylePattern.substring(0, separatorIndex);
282        }
283        Map<String, String> map = CurrencyData.provider.getInstance(uloc, true).getUnitPatterns();
284        for (Map.Entry<String, String> e : map.entrySet()) {
285            String pluralCount = e.getKey();
286            String pattern = e.getValue();
287
288            // replace {0} with numberStylePattern
289            // and {1} with triple currency sign
290            String patternWithNumber = pattern.replace("{0}", numberStylePattern);
291            String patternWithCurrencySign = patternWithNumber.replace("{1}", tripleCurrencyStr);
292            if (separatorIndex != -1) {
293                String negPattern = pattern;
294                String negWithNumber = negPattern.replace("{0}", negNumberPattern);
295                String negWithCurrSign = negWithNumber.replace("{1}", tripleCurrencyStr);
296                StringBuilder posNegPatterns = new StringBuilder(patternWithCurrencySign);
297                posNegPatterns.append(";");
298                posNegPatterns.append(negWithCurrSign);
299                patternWithCurrencySign = posNegPatterns.toString();
300            }
301            pluralCountToCurrencyUnitPattern.put(pluralCount, patternWithCurrencySign);
302        }
303    }
304
305
306    //-------------------- private data member ---------------------
307    //
308    // triple currency sign char array
309    private static final char[] tripleCurrencySign = {0xA4, 0xA4, 0xA4};
310    // triple currency sign string
311    private static final String tripleCurrencyStr = new String(tripleCurrencySign);
312
313    // default currency plural pattern char array
314    private static final char[] defaultCurrencyPluralPatternChar = {0, '.', '#', '#', ' ', 0xA4, 0xA4, 0xA4};
315    // default currency plural pattern string
316    private static final String defaultCurrencyPluralPattern = new String(defaultCurrencyPluralPatternChar);
317
318    // map from plural count to currency plural pattern, for example
319    // one (plural count) --> {0} {1} (currency plural pattern,
320    // in which {0} is the amount number, and {1} is the currency plural name).
321    private Map<String, String> pluralCountToCurrencyUnitPattern = null;
322
323    /*
324     * The plural rule is used to format currency plural name,
325     * for example: "3.00 US Dollars".
326     * If there are 3 currency signs in the currency pattern,
327     * the 3 currency signs will be replaced by the currency plural name.
328     */
329    private PluralRules pluralRules = null;
330
331    // locale
332    private ULocale ulocale = null;
333}
334