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 and    *
6  * others. All Rights Reserved.                                                *
7  *******************************************************************************
8  */
9 package com.ibm.icu.text;
10 
11 import java.lang.reflect.InvocationTargetException;
12 import java.lang.reflect.Method;
13 import java.util.Collections;
14 import java.util.Comparator;
15 import java.util.List;
16 import java.util.Locale;
17 import java.util.Set;
18 
19 import com.ibm.icu.impl.ICUConfig;
20 import com.ibm.icu.lang.UScript;
21 import com.ibm.icu.text.DisplayContext.Type;
22 import com.ibm.icu.util.IllformedLocaleException;
23 import com.ibm.icu.util.ULocale;
24 
25 /**
26  * Returns display names of ULocales and components of ULocales. For
27  * more information on language, script, region, variant, key, and
28  * values, see {@link com.ibm.icu.util.ULocale}.
29  * @stable ICU 4.4
30  */
31 public abstract class LocaleDisplayNames {
32     /**
33      * Enum used in {@link #getInstance(ULocale, DialectHandling)}.
34      * @stable ICU 4.4
35      */
36     public enum DialectHandling {
37         /**
38          * Use standard names when generating a locale name,
39          * e.g. en_GB displays as 'English (United Kingdom)'.
40          * @stable ICU 4.4
41          */
42         STANDARD_NAMES,
43         /**
44          * Use dialect names when generating a locale name,
45          * e.g. en_GB displays as 'British English'.
46          * @stable ICU 4.4
47          */
48         DIALECT_NAMES
49     }
50 
51     // factory methods
52     /**
53      * Convenience overload of {@link #getInstance(ULocale, DialectHandling)} that specifies
54      * STANDARD dialect handling.
55      * @param locale the display locale
56      * @return a LocaleDisplayNames instance
57      * @stable ICU 4.4
58      */
getInstance(ULocale locale)59     public static LocaleDisplayNames getInstance(ULocale locale) {
60         return getInstance(locale, DialectHandling.STANDARD_NAMES);
61     };
62 
63     /**
64      * Convenience overload of {@link #getInstance(Locale, DisplayContext...)} that specifies
65      * {@link DisplayContext#STANDARD_NAMES}.
66      * @param locale the display {@link java.util.Locale}
67      * @return a LocaleDisplayNames instance
68      * @stable ICU 54
69      */
getInstance(Locale locale)70     public static LocaleDisplayNames getInstance(Locale locale) {
71         return getInstance(ULocale.forLocale(locale));
72     };
73 
74     /**
75      * Returns an instance of LocaleDisplayNames that returns names formatted for the provided locale,
76      * using the provided dialectHandling.
77      * @param locale the display locale
78      * @param dialectHandling how to select names for locales
79      * @return a LocaleDisplayNames instance
80      * @stable ICU 4.4
81      */
getInstance(ULocale locale, DialectHandling dialectHandling)82     public static LocaleDisplayNames getInstance(ULocale locale, DialectHandling dialectHandling) {
83         LocaleDisplayNames result = null;
84         if (FACTORY_DIALECTHANDLING != null) {
85             try {
86                 result = (LocaleDisplayNames) FACTORY_DIALECTHANDLING.invoke(null,
87                         locale, dialectHandling);
88             } catch (InvocationTargetException e) {
89                 // fall through
90             } catch (IllegalAccessException e) {
91                 // fall through
92             }
93         }
94         if (result == null) {
95             result = new LastResortLocaleDisplayNames(locale, dialectHandling);
96         }
97         return result;
98     }
99 
100     /**
101      * Returns an instance of LocaleDisplayNames that returns names formatted for the provided locale,
102      * using the provided DisplayContext settings
103      * @param locale the display locale
104      * @param contexts one or more context settings (e.g. for dialect
105      *              handling, capitalization, etc.
106      * @return a LocaleDisplayNames instance
107      * @stable ICU 51
108      */
getInstance(ULocale locale, DisplayContext... contexts)109     public static LocaleDisplayNames getInstance(ULocale locale, DisplayContext... contexts) {
110         LocaleDisplayNames result = null;
111         if (FACTORY_DISPLAYCONTEXT != null) {
112             try {
113                 result = (LocaleDisplayNames) FACTORY_DISPLAYCONTEXT.invoke(null,
114                         locale, contexts);
115             } catch (InvocationTargetException e) {
116                 // fall through
117             } catch (IllegalAccessException e) {
118                 // fall through
119             }
120         }
121         if (result == null) {
122             result = new LastResortLocaleDisplayNames(locale, contexts);
123         }
124         return result;
125     }
126 
127     /**
128      * Returns an instance of LocaleDisplayNames that returns names formatted for the provided
129      * {@link java.util.Locale}, using the provided DisplayContext settings
130      * @param locale the display {@link java.util.Locale}
131      * @param contexts one or more context settings (e.g. for dialect
132      *              handling, capitalization, etc.
133      * @return a LocaleDisplayNames instance
134      * @stable ICU 54
135      */
getInstance(Locale locale, DisplayContext... contexts)136     public static LocaleDisplayNames getInstance(Locale locale, DisplayContext... contexts) {
137         return getInstance(ULocale.forLocale(locale), contexts);
138     }
139 
140     // getters for state
141     /**
142      * Returns the locale used to determine the display names. This is not necessarily the same
143      * locale passed to {@link #getInstance}.
144      * @return the display locale
145      * @stable ICU 4.4
146      */
getLocale()147     public abstract ULocale getLocale();
148 
149     /**
150      * Returns the dialect handling used in the display names.
151      * @return the dialect handling enum
152      * @stable ICU 4.4
153      */
getDialectHandling()154     public abstract DialectHandling getDialectHandling();
155 
156     /**
157      * Returns the current value for a specified DisplayContext.Type.
158      * @param type the DisplayContext.Type whose value to return
159      * @return the current DisplayContext setting for the specified type
160      * @stable ICU 51
161      */
getContext(DisplayContext.Type type)162     public abstract DisplayContext getContext(DisplayContext.Type type);
163 
164     // names for entire locales
165     /**
166      * Returns the display name of the provided ulocale.
167      * When no display names are available for all or portions
168      * of the original locale ID, those portions may be
169      * used directly (possibly in a more canonical form) as
170      * part of the  returned display name.
171      * @param locale the locale whose display name to return
172      * @return the display name of the provided locale
173      * @stable ICU 4.4
174      */
localeDisplayName(ULocale locale)175     public abstract String localeDisplayName(ULocale locale);
176 
177     /**
178      * Returns the display name of the provided locale.
179      * When no display names are available for all or portions
180      * of the original locale ID, those portions may be
181      * used directly (possibly in a more canonical form) as
182      * part of the  returned display name.
183      * @param locale the locale whose display name to return
184      * @return the display name of the provided locale
185      * @stable ICU 4.4
186      */
localeDisplayName(Locale locale)187     public abstract String localeDisplayName(Locale locale);
188 
189     /**
190      * Returns the display name of the provided locale id.
191      * When no display names are available for all or portions
192      * of the original locale ID, those portions may be
193      * used directly (possibly in a more canonical form) as
194      * part of the  returned display name.
195      * @param localeId the id of the locale whose display name to return
196      * @return the display name of the provided locale
197      * @stable ICU 4.4
198      */
localeDisplayName(String localeId)199     public abstract String localeDisplayName(String localeId);
200 
201     // names for components of a locale id
202     /**
203      * Returns the display name of the provided language code.
204      * @param lang the language code
205      * @return the display name of the provided language code
206      * @stable ICU 4.4
207      */
languageDisplayName(String lang)208     public abstract String languageDisplayName(String lang);
209 
210     /**
211      * Returns the display name of the provided script code.
212      * @param script the script code
213      * @return the display name of the provided script code
214      * @stable ICU 4.4
215      */
scriptDisplayName(String script)216     public abstract String scriptDisplayName(String script);
217 
218     /**
219      * Returns the display name of the provided script code
220      * when used in the context of a full locale name.
221      * @param script the script code
222      * @return the display name of the provided script code
223      * @internal ICU 49
224      * @deprecated This API is ICU internal only.
225      */
226     @Deprecated
scriptDisplayNameInContext(String script)227     public String scriptDisplayNameInContext(String script) {
228         return scriptDisplayName(script);
229     }
230 
231     /**
232      * Returns the display name of the provided script code.  See
233      * {@link com.ibm.icu.lang.UScript} for recognized script codes.
234      * @param scriptCode the script code number
235      * @return the display name of the provided script code
236      * @stable ICU 4.4
237      */
scriptDisplayName(int scriptCode)238     public abstract String scriptDisplayName(int scriptCode);
239 
240     /**
241      * Returns the display name of the provided region code.
242      * @param region the region code
243      * @return the display name of the provided region code
244      * @stable ICU 4.4
245      */
regionDisplayName(String region)246     public abstract String regionDisplayName(String region);
247 
248     /**
249      * Returns the display name of the provided variant.
250      * @param variant the variant string
251      * @return the display name of the provided variant
252      * @stable ICU 4.4
253      */
variantDisplayName(String variant)254     public abstract String variantDisplayName(String variant);
255 
256     /**
257      * Returns the display name of the provided locale key.
258      * @param key the locale key name
259      * @return the display name of the provided locale key
260      * @stable ICU 4.4
261      */
keyDisplayName(String key)262     public abstract String keyDisplayName(String key);
263 
264     /**
265      * Returns the display name of the provided value (used with the provided key).
266      * @param key the locale key name
267      * @param value the locale key's value
268      * @return the display name of the provided value
269      * @stable ICU 4.4
270      */
keyValueDisplayName(String key, String value)271     public abstract String keyValueDisplayName(String key, String value);
272 
273 
274     /**
275      * Return a list of information used to construct a UI list of locale names.
276      * @param collator how to collate—should normally be Collator.getInstance(getDisplayLocale())
277      * @param inSelf if true, compares the nameInSelf, otherwise the nameInDisplayLocale.
278      * Set depending on which field (displayLocale vs self) is to show up in the UI.
279      * If both are to show up in the UI, then it should be the one used for the primary sort order.
280      * @param localeSet a list of locales to present in a UI list. The casing uses the settings in the LocaleDisplayNames instance.
281      * @return an ordered list of UiListItems.
282      * @throws IllformedLocaleException if any of the locales in localeSet are malformed.
283      * @stable ICU 55
284      */
getUiList(Set<ULocale> localeSet, boolean inSelf, Comparator<Object> collator)285     public List<UiListItem> getUiList(Set<ULocale> localeSet, boolean inSelf, Comparator<Object> collator) {
286         return getUiListCompareWholeItems(localeSet, UiListItem.getComparator(collator, inSelf));
287     }
288 
289     /**
290      * Return a list of information used to construct a UI list of locale names, providing more access to control the sorting.
291      * Normally use getUiList instead.
292      * @param comparator how to sort the UiListItems in the result.
293      * @param localeSet a list of locales to present in a UI list. The casing uses the settings in the LocaleDisplayNames instance.
294      * @return an ordered list of UiListItems.
295      * @throws IllformedLocaleException if any of the locales in localeSet are malformed.
296      * @stable ICU 55
297      */
getUiListCompareWholeItems(Set<ULocale> localeSet, Comparator<UiListItem> comparator)298     public abstract List<UiListItem> getUiListCompareWholeItems(Set<ULocale> localeSet, Comparator<UiListItem> comparator);
299 
300     /**
301      * Struct-like class used to return information for constructing a UI list, each corresponding to a locale.
302      * @stable ICU 55
303      */
304     public static class UiListItem {
305         /**
306          * Returns the minimized locale for an input locale, such as sr-Cyrl → sr
307          * @stable ICU 55
308          */
309         public final ULocale minimized;
310         /**
311          * Returns the modified locale for an input locale, such as sr → sr-Cyrl, where there is also an sr-Latn in the list
312          * @stable ICU 55
313          */
314         public final ULocale modified;
315         /**
316          * Returns the name of the modified locale in the display locale, such as "Englisch (VS)" (for 'en-US', where the display locale is 'de').
317          * @stable ICU 55
318          */
319         public final String nameInDisplayLocale;
320         /**
321          * Returns the name of the modified locale in itself, such as "English (US)" (for 'en-US').
322          * @stable ICU 55
323          */
324         public final String nameInSelf;
325 
326         /**
327          * Constructor, normally only called internally.
328          * @param minimized locale for an input locale
329          * @param modified modified for an input locale
330          * @param nameInDisplayLocale name of the modified locale in the display locale
331          * @param nameInSelf name of the modified locale in itself
332          * @stable ICU 55
333          */
UiListItem(ULocale minimized, ULocale modified, String nameInDisplayLocale, String nameInSelf)334         public UiListItem(ULocale minimized, ULocale modified, String nameInDisplayLocale, String nameInSelf) {
335             this.minimized = minimized;
336             this.modified = modified;
337             this.nameInDisplayLocale = nameInDisplayLocale;
338             this.nameInSelf = nameInSelf;
339         }
340 
341         /**
342          * {@inheritDoc}
343          *
344          * @stable ICU 55
345          */
346         @Override
equals(Object obj)347         public boolean equals(Object obj) {
348             if (this == obj) {
349                 return true;
350             }
351             if (obj == null || !(obj instanceof UiListItem)) {
352                 return false;
353             }
354             UiListItem other = (UiListItem)obj;
355             return nameInDisplayLocale.equals(other.nameInDisplayLocale)
356                     && nameInSelf.equals(other.nameInSelf)
357                     && minimized.equals(other.minimized)
358                     && modified.equals(other.modified);
359         }
360 
361         /**
362          * {@inheritDoc}
363          *
364          * @stable ICU 55
365          */
366         @Override
hashCode()367         public int hashCode() {
368             return modified.hashCode() ^ nameInDisplayLocale.hashCode();
369         }
370 
371         /**
372          * {@inheritDoc}
373          *
374          * @stable ICU 55
375          */
376         @Override
toString()377         public String toString() {
378             return "{" + minimized + ", " + modified + ", " + nameInDisplayLocale + ", " + nameInSelf  + "}";
379         }
380 
381         /**
382          * Return a comparator that compares the locale names for the display locale or the in-self names,
383          * depending on an input parameter.
384          * @param inSelf if true, compares the nameInSelf, otherwise the nameInDisplayLocale
385          * @param comparator (meant for strings, but because Java Collator doesn't have &lt;String&gt;...)
386          * @return UiListItem comparator
387          * @stable ICU 55
388          */
getComparator(Comparator<Object> comparator, boolean inSelf)389         public static Comparator<UiListItem> getComparator(Comparator<Object> comparator, boolean inSelf) {
390             return new UiListItemComparator(comparator, inSelf);
391         }
392 
393         private static class UiListItemComparator implements Comparator<UiListItem> {
394             private final Comparator<Object> collator;
395             private final boolean useSelf;
UiListItemComparator(Comparator<Object> collator, boolean useSelf)396             UiListItemComparator(Comparator<Object> collator, boolean useSelf) {
397                 this.collator = collator;
398                 this.useSelf = useSelf;
399             }
400             @Override
compare(UiListItem o1, UiListItem o2)401             public int compare(UiListItem o1, UiListItem o2) {
402                 int result = useSelf ? collator.compare(o1.nameInSelf, o2.nameInSelf)
403                         : collator.compare(o1.nameInDisplayLocale, o2.nameInDisplayLocale);
404                 return result != 0 ? result : o1.modified.compareTo(o2.modified); // just in case
405             }
406         }
407     }
408     /**
409      * Sole constructor.  (For invocation by subclass constructors,
410      * typically implicit.)
411      * @internal
412      * @deprecated This API is ICU internal only.
413      */
414     @Deprecated
LocaleDisplayNames()415     protected LocaleDisplayNames() {
416     }
417 
418     private static final Method FACTORY_DIALECTHANDLING;
419     private static final Method FACTORY_DISPLAYCONTEXT;
420 
421     static {
422         String implClassName = ICUConfig.get("com.ibm.icu.text.LocaleDisplayNames.impl", "com.ibm.icu.impl.LocaleDisplayNamesImpl");
423 
424         Method factoryDialectHandling = null;
425         Method factoryDisplayContext = null;
426 
427         try {
428             Class<?> implClass = Class.forName(implClassName);
429             try {
430                 factoryDialectHandling = implClass.getMethod("getInstance",
431                         ULocale.class, DialectHandling.class);
432             } catch (NoSuchMethodException e) {
433             }
434             try {
435                 factoryDisplayContext = implClass.getMethod("getInstance",
436                         ULocale.class, DisplayContext[].class);
437             } catch (NoSuchMethodException e) {
438             }
439 
440         } catch (ClassNotFoundException e) {
441             // fallback to last resort impl
442         }
443 
444         FACTORY_DIALECTHANDLING = factoryDialectHandling;
445         FACTORY_DISPLAYCONTEXT = factoryDisplayContext;
446     }
447 
448     /**
449      * Minimum implementation of LocaleDisplayNames
450      */
451     private static class LastResortLocaleDisplayNames extends LocaleDisplayNames {
452 
453         private ULocale locale;
454         private DisplayContext[] contexts;
455 
LastResortLocaleDisplayNames(ULocale locale, DialectHandling dialectHandling)456         private LastResortLocaleDisplayNames(ULocale locale, DialectHandling dialectHandling) {
457             this.locale = locale;
458             DisplayContext context = (dialectHandling == DialectHandling.DIALECT_NAMES) ?
459                     DisplayContext.DIALECT_NAMES : DisplayContext.STANDARD_NAMES;
460             this.contexts = new DisplayContext[] {context};
461         }
462 
LastResortLocaleDisplayNames(ULocale locale, DisplayContext... contexts)463         private LastResortLocaleDisplayNames(ULocale locale, DisplayContext... contexts) {
464             this.locale = locale;
465             this.contexts = new DisplayContext[contexts.length];
466             System.arraycopy(contexts, 0, this.contexts, 0, contexts.length);
467         }
468 
469         @Override
getLocale()470         public ULocale getLocale() {
471             return locale;
472         }
473 
474         @Override
getDialectHandling()475         public DialectHandling getDialectHandling() {
476             DialectHandling result = DialectHandling.STANDARD_NAMES;
477             for (DisplayContext context : contexts) {
478                 if (context.type() == DisplayContext.Type.DIALECT_HANDLING) {
479                     if (context.value() == DisplayContext.DIALECT_NAMES.ordinal()) {
480                         result = DialectHandling.DIALECT_NAMES;
481                         break;
482                     }
483                 }
484             }
485             return result;
486         }
487 
488         @Override
getContext(Type type)489         public DisplayContext getContext(Type type) {
490             DisplayContext result = DisplayContext.STANDARD_NAMES;  // final fallback
491             for (DisplayContext context : contexts) {
492                 if (context.type() == type) {
493                     result = context;
494                     break;
495                 }
496             }
497             return result;
498         }
499 
500         @Override
localeDisplayName(ULocale locale)501         public String localeDisplayName(ULocale locale) {
502             return locale.getName();
503         }
504 
505         @Override
localeDisplayName(Locale locale)506         public String localeDisplayName(Locale locale) {
507             return ULocale.forLocale(locale).getName();
508         }
509 
510         @Override
localeDisplayName(String localeId)511         public String localeDisplayName(String localeId) {
512             return new ULocale(localeId).getName();
513         }
514 
515         @Override
languageDisplayName(String lang)516         public String languageDisplayName(String lang) {
517             return lang;
518         }
519 
520         @Override
scriptDisplayName(String script)521         public String scriptDisplayName(String script) {
522             return script;
523         }
524 
525         @Override
scriptDisplayName(int scriptCode)526         public String scriptDisplayName(int scriptCode) {
527             return UScript.getShortName(scriptCode);
528         }
529 
530         @Override
regionDisplayName(String region)531         public String regionDisplayName(String region) {
532             return region;
533         }
534 
535         @Override
variantDisplayName(String variant)536         public String variantDisplayName(String variant) {
537             return variant;
538         }
539 
540         @Override
keyDisplayName(String key)541         public String keyDisplayName(String key) {
542             return key;
543         }
544 
545         @Override
keyValueDisplayName(String key, String value)546         public String keyValueDisplayName(String key, String value) {
547             return value;
548         }
549 
550         @Override
getUiListCompareWholeItems(Set<ULocale> localeSet, Comparator<UiListItem> comparator)551         public List<UiListItem> getUiListCompareWholeItems(Set<ULocale> localeSet, Comparator<UiListItem> comparator) {
552             return Collections.emptyList();
553         }
554     }
555 }
556