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