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