1// © 2016 and later: Unicode, Inc. and others.
2// License & terms of use: http://www.unicode.org/copyright.html#License
3/*
4 **************************************************************************************
5 * Copyright (C) 2009-2016, International Business Machines Corporation,
6 * Google, Inc. and others. All Rights Reserved.
7 **************************************************************************************
8 */
9package com.ibm.icu.util;
10
11import java.util.MissingResourceException;
12
13import com.ibm.icu.impl.ICUData;
14import com.ibm.icu.impl.ICUResourceBundle;
15import com.ibm.icu.text.UnicodeSet;
16import com.ibm.icu.util.ULocale.Category;
17
18/**
19 * A class for accessing miscellaneous data in the locale bundles
20 * @author ram
21 * @stable ICU 2.8
22 */
23public final class LocaleData {
24
25    //    private static final String EXEMPLAR_CHARS      = "ExemplarCharacters";
26    private static final String MEASUREMENT_SYSTEM  = "MeasurementSystem";
27    private static final String PAPER_SIZE          = "PaperSize";
28    private static final String LOCALE_DISPLAY_PATTERN  = "localeDisplayPattern";
29    private static final String PATTERN             = "pattern";
30    private static final String SEPARATOR           = "separator";
31    private boolean noSubstitute;
32    private ICUResourceBundle bundle;
33    private ICUResourceBundle langBundle;
34
35    /**
36     * EXType for {@link #getExemplarSet(int, int)}.
37     * Corresponds to the 'main' (aka 'standard') CLDR exemplars in
38     * <a href="http://www.unicode.org/reports/tr35/tr35-general.html#Character_Elements">
39     *   http://www.unicode.org/reports/tr35/tr35-general.html#Character_Elements</a>.
40     * @stable ICU 3.4
41     */
42    public static final int ES_STANDARD = 0;
43
44    /**
45     * EXType for {@link #getExemplarSet(int, int)}.
46     * Corresponds to the 'auxiliary' CLDR exemplars in
47     * <a href="http://www.unicode.org/reports/tr35/tr35-general.html#Character_Elements">
48     *   http://www.unicode.org/reports/tr35/tr35-general.html#Character_Elements</a>.
49     * @stable ICU 3.4
50     */
51    public static final int ES_AUXILIARY = 1;
52
53    /**
54     * EXType for {@link #getExemplarSet(int, int)}.
55     * Corresponds to the 'index' CLDR exemplars in
56     * <a href="http://www.unicode.org/reports/tr35/tr35-general.html#Character_Elements">
57     *   http://www.unicode.org/reports/tr35/tr35-general.html#Character_Elements</a>.
58     * @stable ICU 4.4
59     */
60    public static final int ES_INDEX = 2;
61
62    /**
63     * EXType for {@link #getExemplarSet(int, int)}.
64     * Corresponds to the 'currencySymbol' CLDR exemplars in
65     * <a href="http://www.unicode.org/reports/tr35/tr35-general.html#Character_Elements">
66     *   http://www.unicode.org/reports/tr35/tr35-general.html#Character_Elements</a>.
67     * Note: This type is no longer supported.
68     * @deprecated ICU 51
69     */
70    @Deprecated
71    public static final int ES_CURRENCY = 3;
72
73    /**
74     * Corresponds to the 'punctuation' CLDR exemplars in
75     * <a href="http://www.unicode.org/reports/tr35/tr35-general.html#Character_Elements">
76     *   http://www.unicode.org/reports/tr35/tr35-general.html#Character_Elements</a>.
77     * EXType for {@link #getExemplarSet(int, int)}.
78     * @stable ICU 49
79     */
80    public static final int ES_PUNCTUATION = 4;
81
82    /**
83     * Count of EXTypes for {@link #getExemplarSet(int, int)}.
84     * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420.
85     */
86    @Deprecated
87    public static final int ES_COUNT = 5;
88
89    /**
90     * Delimiter type for {@link #getDelimiter(int)}.
91     * @stable ICU 3.4
92     */
93    public static final int QUOTATION_START = 0;
94
95    /**
96     * Delimiter type for {@link #getDelimiter(int)}.
97     * @stable ICU 3.4
98     */
99    public static final int QUOTATION_END = 1;
100
101    /**
102     * Delimiter type for {@link #getDelimiter(int)}.
103     * @stable ICU 3.4
104     */
105    public static final int ALT_QUOTATION_START = 2;
106
107    /**
108     * Delimiter type for {@link #getDelimiter(int)}.
109     * @stable ICU 3.4
110     */
111    public static final int ALT_QUOTATION_END = 3;
112
113    /**
114     * Count of delimiter types for {@link #getDelimiter(int)}.
115     * @deprecated ICU 58 The numeric value may change over time, see ICU ticket #12420.
116     */
117    @Deprecated
118    public static final int DELIMITER_COUNT = 4;
119
120    // private constructor to prevent default construction
121    ///CLOVER:OFF
122    private LocaleData(){}
123    ///CLOVER:ON
124
125    /**
126     * Returns the set of exemplar characters for a locale. Equivalent to calling {@link #getExemplarSet(ULocale, int, int)} with
127     * the extype == {@link #ES_STANDARD}.
128     *
129     * @param locale    Locale for which the exemplar character set
130     *                  is to be retrieved.
131     * @param options   Bitmask for options to apply to the exemplar pattern.
132     *                  Specify zero to retrieve the exemplar set as it is
133     *                  defined in the locale data.  Specify
134     *                  UnicodeSet.CASE to retrieve a case-folded exemplar
135     *                  set.  See {@link UnicodeSet#applyPattern(String,
136     *                  int)} for a complete list of valid options.  The
137     *                  IGNORE_SPACE bit is always set, regardless of the
138     *                  value of 'options'.
139     * @return          The set of exemplar characters for the given locale.
140     * @stable ICU 3.0
141     */
142    public static UnicodeSet getExemplarSet(ULocale locale, int options) {
143        return LocaleData.getInstance(locale).getExemplarSet(options, ES_STANDARD);
144    }
145
146    /**
147     * Returns the set of exemplar characters for a locale.
148     * Equivalent to calling new LocaleData(locale).{@link #getExemplarSet(int, int)}.
149     *
150     * @param locale    Locale for which the exemplar character set
151     *                  is to be retrieved.
152     * @param options   Bitmask for options to apply to the exemplar pattern.
153     *                  Specify zero to retrieve the exemplar set as it is
154     *                  defined in the locale data.  Specify
155     *                  UnicodeSet.CASE to retrieve a case-folded exemplar
156     *                  set.  See {@link UnicodeSet#applyPattern(String,
157     *                  int)} for a complete list of valid options.  The
158     *                  IGNORE_SPACE bit is always set, regardless of the
159     *                  value of 'options'.
160     * @param extype    The type of exemplar character set to retrieve.
161     * @return          The set of exemplar characters for the given locale.
162     * @stable ICU 3.0
163     */
164    public static UnicodeSet getExemplarSet(ULocale locale, int options, int extype) {
165        return LocaleData.getInstance(locale).getExemplarSet(options, extype);
166    }
167
168    /**
169     * Returns the set of exemplar characters for a locale.
170     *
171     * @param options   Bitmask for options to apply to the exemplar pattern.
172     *                  Specify zero to retrieve the exemplar set as it is
173     *                  defined in the locale data.  Specify
174     *                  UnicodeSet.CASE to retrieve a case-folded exemplar
175     *                  set.  See {@link UnicodeSet#applyPattern(String,
176     *                  int)} for a complete list of valid options.  The
177     *                  IGNORE_SPACE bit is always set, regardless of the
178     *                  value of 'options'.
179     * @param extype    The type of exemplar set to be retrieved,
180     *                  ES_STANDARD, ES_INDEX, ES_AUXILIARY, or ES_PUNCTUATION
181     * @return          The set of exemplar characters for the given locale.
182     *                  If there is nothing available for the locale,
183     *                  then null is returned if {@link #getNoSubstitute()} is true, otherwise the
184     *                  root value is returned (which may be UnicodeSet.EMPTY).
185     * @exception       RuntimeException if the extype is invalid.
186     * @stable ICU 3.4
187     */
188    public UnicodeSet getExemplarSet(int options, int extype) {
189        String [] exemplarSetTypes = {
190                "ExemplarCharacters",
191                "AuxExemplarCharacters",
192                "ExemplarCharactersIndex",
193                "ExemplarCharactersCurrency",
194                "ExemplarCharactersPunctuation"
195        };
196
197        if (extype == ES_CURRENCY) {
198            // currency symbol exemplar is no longer available
199            return noSubstitute ? null : UnicodeSet.EMPTY;
200        }
201
202        try{
203            final String aKey = exemplarSetTypes[extype]; // will throw an out-of-bounds exception
204            ICUResourceBundle stringBundle = (ICUResourceBundle) bundle.get(aKey);
205
206            if (noSubstitute && !bundle.isRoot() && stringBundle.isRoot()) {
207                return null;
208            }
209            String unicodeSetPattern = stringBundle.getString();
210            return new UnicodeSet(unicodeSetPattern, UnicodeSet.IGNORE_SPACE | options);
211        } catch (ArrayIndexOutOfBoundsException aiooe) {
212            throw new IllegalArgumentException(aiooe);
213        } catch (Exception ex){
214            return noSubstitute ? null : UnicodeSet.EMPTY;
215        }
216    }
217
218    /**
219     * Gets the LocaleData object associated with the ULocale specified in locale
220     *
221     * @param locale    Locale with thich the locale data object is associated.
222     * @return          A locale data object.
223     * @stable ICU 3.4
224     */
225    public static final LocaleData getInstance(ULocale locale) {
226        LocaleData ld = new LocaleData();
227        ld.bundle = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, locale);
228        ld.langBundle = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUData.ICU_LANG_BASE_NAME, locale);
229        ld.noSubstitute = false;
230        return ld;
231    }
232
233    /**
234     * Gets the LocaleData object associated with the default <code>FORMAT</code> locale
235     *
236     * @return          A locale data object.
237     * @see Category#FORMAT
238     * @stable ICU 3.4
239     */
240    public static final LocaleData getInstance() {
241        return LocaleData.getInstance(ULocale.getDefault(Category.FORMAT));
242    }
243
244    /**
245     * Sets the "no substitute" behavior of this locale data object.
246     *
247     * @param setting   Value for the no substitute behavior.  If TRUE,
248     *                  methods of this locale data object will return
249     *                  an error when no data is available for that method,
250     *                  given the locale ID supplied to the constructor.
251     * @stable ICU 3.4
252     */
253    public void setNoSubstitute(boolean setting) {
254        noSubstitute = setting;
255    }
256
257    /**
258     * Gets the "no substitute" behavior of this locale data object.
259     *
260     * @return          Value for the no substitute behavior.  If TRUE,
261     *                  methods of this locale data object will return
262     *                  an error when no data is available for that method,
263     *                  given the locale ID supplied to the constructor.
264     * @stable ICU 3.4
265     */
266    public boolean getNoSubstitute() {
267        return noSubstitute;
268    }
269
270    private static final String [] DELIMITER_TYPES = {
271        "quotationStart",
272        "quotationEnd",
273        "alternateQuotationStart",
274        "alternateQuotationEnd"
275    };
276
277    /**
278     * Retrieves a delimiter string from the locale data.
279     *
280     * @param type      The type of delimiter string desired.  Currently,
281     *                  the valid choices are QUOTATION_START, QUOTATION_END,
282     *                  ALT_QUOTATION_START, or ALT_QUOTATION_END.
283     * @return          The desired delimiter string.
284     * @stable ICU 3.4
285     */
286    public String getDelimiter(int type) {
287        ICUResourceBundle delimitersBundle = (ICUResourceBundle) bundle.get("delimiters");
288        // Only some of the quotation marks may be here. So we make sure that we do a multilevel fallback.
289        ICUResourceBundle stringBundle = delimitersBundle.getWithFallback(DELIMITER_TYPES[type]);
290
291        if (noSubstitute && !bundle.isRoot() && stringBundle.isRoot()) {
292            return null;
293        }
294        return stringBundle.getString();
295    }
296
297    /**
298     * Utility for getMeasurementSystem and getPaperSize
299     */
300    private static UResourceBundle measurementTypeBundleForLocale(ULocale locale, String measurementType){
301        // Much of this is taken from getCalendarType in impl/CalendarUtil.java
302        UResourceBundle measTypeBundle = null;
303        String region = ULocale.getRegionForSupplementalData(locale, true);
304        try {
305            UResourceBundle rb = UResourceBundle.getBundleInstance(
306                    ICUData.ICU_BASE_NAME,
307                    "supplementalData",
308                    ICUResourceBundle.ICU_DATA_CLASS_LOADER);
309            UResourceBundle measurementData = rb.get("measurementData");
310            UResourceBundle measDataBundle = null;
311            try {
312                measDataBundle = measurementData.get(region);
313                measTypeBundle = measDataBundle.get(measurementType);
314            } catch (MissingResourceException mre) {
315                // use "001" as fallback
316                measDataBundle = measurementData.get("001");
317                measTypeBundle = measDataBundle.get(measurementType);
318            }
319        } catch (MissingResourceException mre) {
320            // fall through
321        }
322        return measTypeBundle;
323    }
324
325
326    /**
327     * Enumeration for representing the measurement systems.
328     * @stable ICU 2.8
329     */
330    public static final class MeasurementSystem{
331        /**
332         * Measurement system specified by Le Syst&#x00E8;me International d'Unit&#x00E9;s (SI)
333         * otherwise known as Metric system.
334         * @stable ICU 2.8
335         */
336        public static final MeasurementSystem SI = new MeasurementSystem();
337
338        /**
339         * Measurement system followed in the United States of America.
340         * @stable ICU 2.8
341         */
342        public static final MeasurementSystem US = new MeasurementSystem();
343
344        /**
345         * Mix of metric and imperial units used in Great Britain.
346         * @stable ICU 55
347         */
348        public static final MeasurementSystem UK = new MeasurementSystem();
349
350        private MeasurementSystem() {}
351    }
352
353    /**
354     * Returns the measurement system used in the locale specified by the locale.
355     *
356     * @param locale      The locale for which the measurement system to be retrieved.
357     * @return MeasurementSystem the measurement system used in the locale.
358     * @stable ICU 3.0
359     */
360    public static final MeasurementSystem getMeasurementSystem(ULocale locale){
361        UResourceBundle sysBundle = measurementTypeBundleForLocale(locale, MEASUREMENT_SYSTEM);
362
363        switch (sysBundle.getInt()) {
364        case 0: return MeasurementSystem.SI;
365        case 1: return MeasurementSystem.US;
366        case 2: return MeasurementSystem.UK;
367        default:
368            // return null if the object is null or is not an instance
369            // of integer indicating an error
370            return null;
371        }
372    }
373
374    /**
375     * A class that represents the size of letter head
376     * used in the country
377     * @stable ICU 2.8
378     */
379    public static final class PaperSize{
380        private int height;
381        private int width;
382
383        private PaperSize(int h, int w){
384            height = h;
385            width = w;
386        }
387        /**
388         * Retruns the height of the paper
389         * @return the height
390         * @stable ICU 2.8
391         */
392        public int getHeight(){
393            return height;
394        }
395        /**
396         * Returns the width of the paper
397         * @return the width
398         * @stable ICU 2.8
399         */
400        public int getWidth(){
401            return width;
402        }
403    }
404
405    /**
406     * Returns the size of paper used in the locale. The paper sizes returned are always in
407     * <em>milli-meters</em>.
408     * @param locale The locale for which the measurement system to be retrieved.
409     * @return The paper size used in the locale
410     * @stable ICU 3.0
411     */
412    public static final PaperSize getPaperSize(ULocale locale){
413        UResourceBundle obj = measurementTypeBundleForLocale(locale, PAPER_SIZE);
414        int[] size = obj.getIntVector();
415        return new PaperSize(size[0], size[1]);
416    }
417
418    /**
419     * Returns LocaleDisplayPattern for this locale, e.g., {0}({1})
420     * @return locale display pattern as a String.
421     * @stable ICU 4.2
422     */
423    public String getLocaleDisplayPattern() {
424        ICUResourceBundle locDispBundle = (ICUResourceBundle) langBundle.get(LOCALE_DISPLAY_PATTERN);
425        String localeDisplayPattern = locDispBundle.getStringWithFallback(PATTERN);
426        return localeDisplayPattern;
427    }
428
429    /**
430     * Returns LocaleDisplaySeparator for this locale.
431     * @return locale display separator as a char.
432     * @stable ICU 4.2
433     */
434    public String getLocaleSeparator() {
435        String sub0 = "{0}";
436        String sub1 = "{1}";
437        ICUResourceBundle locDispBundle = (ICUResourceBundle) langBundle.get(LOCALE_DISPLAY_PATTERN);
438        String  localeSeparator = locDispBundle.getStringWithFallback(SEPARATOR);
439        int index0 = localeSeparator.indexOf(sub0);
440        int index1 = localeSeparator.indexOf(sub1);
441        if (index0 >= 0 && index1 >= 0 && index0 <= index1) {
442            return localeSeparator.substring(index0 + sub0.length(), index1);
443        }
444        return localeSeparator;
445    }
446
447    private static VersionInfo gCLDRVersion = null;
448
449    /**
450     * Returns the current CLDR version
451     * @stable ICU 4.2
452     */
453    public static VersionInfo getCLDRVersion() {
454        // fetching this data should be idempotent.
455        if(gCLDRVersion == null) {
456            // from ZoneMeta.java
457            UResourceBundle supplementalDataBundle = UResourceBundle.getBundleInstance(ICUData.ICU_BASE_NAME, "supplementalData", ICUResourceBundle.ICU_DATA_CLASS_LOADER);
458            UResourceBundle cldrVersionBundle = supplementalDataBundle.get("cldrVersion");
459            gCLDRVersion = VersionInfo.getInstance(cldrVersionBundle.getString());
460        }
461        return gCLDRVersion;
462    }
463}
464