1/*
2 *******************************************************************************
3 * Copyright (C) 2009-2015, International Business Machines Corporation and    *
4 * others. All Rights Reserved.                                                *
5 *******************************************************************************
6 */
7package com.ibm.icu.impl;
8
9import java.util.ArrayList;
10import java.util.Collections;
11import java.util.Comparator;
12import java.util.HashMap;
13import java.util.HashSet;
14import java.util.Iterator;
15import java.util.List;
16import java.util.Locale;
17import java.util.Map;
18import java.util.Map.Entry;
19import java.util.MissingResourceException;
20import java.util.Set;
21
22import com.ibm.icu.impl.CurrencyData.CurrencyDisplayInfo;
23import com.ibm.icu.impl.locale.AsciiUtil;
24import com.ibm.icu.lang.UCharacter;
25import com.ibm.icu.lang.UScript;
26import com.ibm.icu.text.BreakIterator;
27import com.ibm.icu.text.DisplayContext;
28import com.ibm.icu.text.DisplayContext.Type;
29import com.ibm.icu.text.LocaleDisplayNames;
30import com.ibm.icu.text.MessageFormat;
31import com.ibm.icu.util.ULocale;
32import com.ibm.icu.util.UResourceBundle;
33import com.ibm.icu.util.UResourceBundleIterator;
34
35public class LocaleDisplayNamesImpl extends LocaleDisplayNames {
36    private final ULocale locale;
37    private final DialectHandling dialectHandling;
38    private final DisplayContext capitalization;
39    private final DisplayContext nameLength;
40    private final DataTable langData;
41    private final DataTable regionData;
42    private final MessageFormat separatorFormat;
43    private final MessageFormat format;
44    private final MessageFormat keyTypeFormat;
45    private final char formatOpenParen;
46    private final char formatReplaceOpenParen;
47    private final char formatCloseParen;
48    private final char formatReplaceCloseParen;
49    private final CurrencyDisplayInfo currencyDisplayInfo;
50
51    private static final Cache cache = new Cache();
52
53    /**
54     * Capitalization context usage types for locale display names
55     */
56    private enum CapitalizationContextUsage {
57        LANGUAGE,
58        SCRIPT,
59        TERRITORY,
60        VARIANT,
61        KEY,
62        KEYVALUE
63    }
64    /**
65     * Capitalization transforms. For each usage type, indicates whether to titlecase for
66     * the context specified in capitalization (which we know at construction time).
67     */
68    private boolean[] capitalizationUsage = null;
69    /**
70     * Map from resource key to CapitalizationContextUsage value
71     */
72    private static final Map<String, CapitalizationContextUsage> contextUsageTypeMap;
73    static {
74        contextUsageTypeMap=new HashMap<String, CapitalizationContextUsage>();
75        contextUsageTypeMap.put("languages", CapitalizationContextUsage.LANGUAGE);
76        contextUsageTypeMap.put("script",    CapitalizationContextUsage.SCRIPT);
77        contextUsageTypeMap.put("territory", CapitalizationContextUsage.TERRITORY);
78        contextUsageTypeMap.put("variant",   CapitalizationContextUsage.VARIANT);
79        contextUsageTypeMap.put("key",       CapitalizationContextUsage.KEY);
80        contextUsageTypeMap.put("keyValue",  CapitalizationContextUsage.KEYVALUE);
81    }
82    /**
83     * BreakIterator to use for capitalization
84     */
85    private transient BreakIterator capitalizationBrkIter = null;
86
87
88    public static LocaleDisplayNames getInstance(ULocale locale, DialectHandling dialectHandling) {
89        synchronized (cache) {
90            return cache.get(locale, dialectHandling);
91        }
92    }
93
94    public static LocaleDisplayNames getInstance(ULocale locale, DisplayContext... contexts) {
95        synchronized (cache) {
96            return cache.get(locale, contexts);
97        }
98    }
99
100    public LocaleDisplayNamesImpl(ULocale locale, DialectHandling dialectHandling) {
101        this(locale, (dialectHandling==DialectHandling.STANDARD_NAMES)? DisplayContext.STANDARD_NAMES: DisplayContext.DIALECT_NAMES,
102                DisplayContext.CAPITALIZATION_NONE);
103    }
104
105    public LocaleDisplayNamesImpl(ULocale locale, DisplayContext... contexts) {
106        DialectHandling dialectHandling = DialectHandling.STANDARD_NAMES;
107        DisplayContext capitalization = DisplayContext.CAPITALIZATION_NONE;
108        DisplayContext nameLength = DisplayContext.LENGTH_FULL;
109        for (DisplayContext contextItem : contexts) {
110            switch (contextItem.type()) {
111            case DIALECT_HANDLING:
112                dialectHandling = (contextItem.value()==DisplayContext.STANDARD_NAMES.value())?
113                        DialectHandling.STANDARD_NAMES: DialectHandling.DIALECT_NAMES;
114                break;
115            case CAPITALIZATION:
116                capitalization = contextItem;
117                break;
118            case DISPLAY_LENGTH:
119                nameLength = contextItem;
120                break;
121            default:
122                break;
123            }
124        }
125
126        this.dialectHandling = dialectHandling;
127        this.capitalization = capitalization;
128        this.nameLength = nameLength;
129        this.langData = LangDataTables.impl.get(locale);
130        this.regionData = RegionDataTables.impl.get(locale);
131        this.locale = ULocale.ROOT.equals(langData.getLocale()) ? regionData.getLocale() :
132            langData.getLocale();
133
134        // Note, by going through DataTable, this uses table lookup rather than straight lookup.
135        // That should get us the same data, I think.  This way we don't have to explicitly
136        // load the bundle again.  Using direct lookup didn't seem to make an appreciable
137        // difference in performance.
138        String sep = langData.get("localeDisplayPattern", "separator");
139        if ("separator".equals(sep)) {
140            sep = "{0}, {1}";
141        }
142        this.separatorFormat = new MessageFormat(sep);
143
144        String pattern = langData.get("localeDisplayPattern", "pattern");
145        if ("pattern".equals(pattern)) {
146            pattern = "{0} ({1})";
147        }
148        this.format = new MessageFormat(pattern);
149        if (pattern.contains("(")) {
150            formatOpenParen = '(';
151            formatCloseParen = ')';
152            formatReplaceOpenParen = '[';
153            formatReplaceCloseParen = ']';
154        } else  {
155            formatOpenParen = '(';
156            formatCloseParen = ')';
157            formatReplaceOpenParen = '[';
158            formatReplaceCloseParen = ']';
159        }
160
161        String keyTypePattern = langData.get("localeDisplayPattern", "keyTypePattern");
162        if ("keyTypePattern".equals(keyTypePattern)) {
163            keyTypePattern = "{0}={1}";
164        }
165        this.keyTypeFormat = new MessageFormat(keyTypePattern);
166
167        // Get values from the contextTransforms data if we need them
168        // Also check whether we will need a break iterator (depends on the data)
169        boolean needBrkIter = false;
170        if (capitalization == DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU ||
171                capitalization == DisplayContext.CAPITALIZATION_FOR_STANDALONE) {
172            capitalizationUsage = new boolean[CapitalizationContextUsage.values().length]; // initialized to all false
173            ICUResourceBundle rb = (ICUResourceBundle)UResourceBundle.getBundleInstance(ICUResourceBundle.ICU_BASE_NAME, locale);
174            UResourceBundle contextTransformsBundle = null;
175            try {
176                contextTransformsBundle = (UResourceBundle)rb.getWithFallback("contextTransforms");
177            }
178            catch (MissingResourceException e) {
179                contextTransformsBundle = null; // probably redundant
180            }
181            if (contextTransformsBundle != null) {
182                UResourceBundleIterator ctIterator = contextTransformsBundle.getIterator();
183                while ( ctIterator.hasNext() ) {
184                    UResourceBundle contextTransformUsage = ctIterator.next();
185                    int[] intVector = contextTransformUsage.getIntVector();
186                    if (intVector.length >= 2) {
187                        String usageKey = contextTransformUsage.getKey();
188                        CapitalizationContextUsage usage = contextUsageTypeMap.get(usageKey);
189                        if (usage != null) {
190                            int titlecaseInt = (capitalization == DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU)?
191                                    intVector[0]: intVector[1];
192                                    if (titlecaseInt != 0) {
193                                        capitalizationUsage[usage.ordinal()] = true;
194                                        needBrkIter = true;
195                                    }
196                        }
197                    }
198                }
199            }
200        }
201        // Get a sentence break iterator if we will need it
202        if (needBrkIter || capitalization == DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE) {
203            capitalizationBrkIter = BreakIterator.getSentenceInstance(locale);
204        }
205
206        this.currencyDisplayInfo = CurrencyData.provider.getInstance(locale, false);
207    }
208
209    @Override
210    public ULocale getLocale() {
211        return locale;
212    }
213
214    @Override
215    public DialectHandling getDialectHandling() {
216        return dialectHandling;
217    }
218
219    @Override
220    public DisplayContext getContext(DisplayContext.Type type) {
221        DisplayContext result;
222        switch (type) {
223        case DIALECT_HANDLING:
224            result = (dialectHandling==DialectHandling.STANDARD_NAMES)? DisplayContext.STANDARD_NAMES: DisplayContext.DIALECT_NAMES;
225            break;
226        case CAPITALIZATION:
227            result = capitalization;
228            break;
229        case DISPLAY_LENGTH:
230            result = nameLength;
231            break;
232        default:
233            result = DisplayContext.STANDARD_NAMES; // hmm, we should do something else here
234            break;
235        }
236        return result;
237    }
238
239    private String adjustForUsageAndContext(CapitalizationContextUsage usage, String name) {
240        if (name != null && name.length() > 0 && UCharacter.isLowerCase(name.codePointAt(0)) &&
241                (capitalization==DisplayContext.CAPITALIZATION_FOR_BEGINNING_OF_SENTENCE ||
242                (capitalizationUsage != null && capitalizationUsage[usage.ordinal()]) )) {
243            // Note, won't have capitalizationUsage != null && capitalizationUsage[usage.ordinal()]
244            // unless capitalization is CAPITALIZATION_FOR_UI_LIST_OR_MENU or CAPITALIZATION_FOR_STANDALONE
245            synchronized (this) {
246                if (capitalizationBrkIter == null) {
247                    // should only happen when deserializing, etc.
248                    capitalizationBrkIter = BreakIterator.getSentenceInstance(locale);
249                }
250                return UCharacter.toTitleCase(locale, name, capitalizationBrkIter,
251                        UCharacter.TITLECASE_NO_LOWERCASE | UCharacter.TITLECASE_NO_BREAK_ADJUSTMENT);
252            }
253        }
254        return name;
255    }
256
257    @Override
258    public String localeDisplayName(ULocale locale) {
259        return localeDisplayNameInternal(locale);
260    }
261
262    @Override
263    public String localeDisplayName(Locale locale) {
264        return localeDisplayNameInternal(ULocale.forLocale(locale));
265    }
266
267    @Override
268    public String localeDisplayName(String localeId) {
269        return localeDisplayNameInternal(new ULocale(localeId));
270    }
271
272    // TOTO: implement use of capitalization
273    private String localeDisplayNameInternal(ULocale locale) {
274        // lang
275        // lang (script, country, variant, keyword=value, ...)
276        // script, country, variant, keyword=value, ...
277
278        String resultName = null;
279
280        String lang = locale.getLanguage();
281
282        // Empty basename indicates root locale (keywords are ignored for this).
283        // Our data uses 'root' to access display names for the root locale in the
284        // "Languages" table.
285        if (locale.getBaseName().length() == 0) {
286            lang = "root";
287        }
288        String script = locale.getScript();
289        String country = locale.getCountry();
290        String variant = locale.getVariant();
291
292        boolean hasScript = script.length() > 0;
293        boolean hasCountry = country.length() > 0;
294        boolean hasVariant = variant.length() > 0;
295
296        // always have a value for lang
297        if (dialectHandling == DialectHandling.DIALECT_NAMES) {
298            do { // loop construct is so we can break early out of search
299                if (hasScript && hasCountry) {
300                    String langScriptCountry = lang + '_' + script + '_' + country;
301                    String result = localeIdName(langScriptCountry);
302                    if (!result.equals(langScriptCountry)) {
303                        resultName = result;
304                        hasScript = false;
305                        hasCountry = false;
306                        break;
307                    }
308                }
309                if (hasScript) {
310                    String langScript = lang + '_' + script;
311                    String result = localeIdName(langScript);
312                    if (!result.equals(langScript)) {
313                        resultName = result;
314                        hasScript = false;
315                        break;
316                    }
317                }
318                if (hasCountry) {
319                    String langCountry = lang + '_' + country;
320                    String result = localeIdName(langCountry);
321                    if (!result.equals(langCountry)) {
322                        resultName = result;
323                        hasCountry = false;
324                        break;
325                    }
326                }
327            } while (false);
328        }
329
330        if (resultName == null) {
331            resultName = localeIdName(lang)
332                    .replace(formatOpenParen, formatReplaceOpenParen)
333                    .replace(formatCloseParen, formatReplaceCloseParen);
334        }
335
336        StringBuilder buf = new StringBuilder();
337        if (hasScript) {
338            // first element, don't need appendWithSep
339            buf.append(scriptDisplayNameInContext(script)
340                    .replace(formatOpenParen, formatReplaceOpenParen)
341                    .replace(formatCloseParen, formatReplaceCloseParen));
342        }
343        if (hasCountry) {
344            appendWithSep(regionDisplayName(country)
345                    .replace(formatOpenParen, formatReplaceOpenParen)
346                    .replace(formatCloseParen, formatReplaceCloseParen), buf);
347        }
348        if (hasVariant) {
349            appendWithSep(variantDisplayName(variant)
350                    .replace(formatOpenParen, formatReplaceOpenParen)
351                    .replace(formatCloseParen, formatReplaceCloseParen), buf);
352        }
353
354        Iterator<String> keys = locale.getKeywords();
355        if (keys != null) {
356            while (keys.hasNext()) {
357                String key = keys.next();
358                String value = locale.getKeywordValue(key);
359                String keyDisplayName = keyDisplayName(key)
360                        .replace(formatOpenParen, formatReplaceOpenParen)
361                        .replace(formatCloseParen, formatReplaceCloseParen);
362                String valueDisplayName = keyValueDisplayName(key, value)
363                        .replace(formatOpenParen, formatReplaceOpenParen)
364                        .replace(formatCloseParen, formatReplaceCloseParen);
365                if (!valueDisplayName.equals(value)) {
366                    appendWithSep(valueDisplayName, buf);
367                } else if (!key.equals(keyDisplayName)) {
368                    String keyValue = keyTypeFormat.format(
369                            new String[] { keyDisplayName, valueDisplayName });
370                    appendWithSep(keyValue, buf);
371                } else {
372                    appendWithSep(keyDisplayName, buf)
373                    .append("=")
374                    .append(valueDisplayName);
375                }
376            }
377        }
378
379        String resultRemainder = null;
380        if (buf.length() > 0) {
381            resultRemainder = buf.toString();
382        }
383
384        if (resultRemainder != null) {
385            resultName =  format.format(new Object[] {resultName, resultRemainder});
386        }
387
388        return adjustForUsageAndContext(CapitalizationContextUsage.LANGUAGE, resultName);
389    }
390
391    private String localeIdName(String localeId) {
392        if (nameLength == DisplayContext.LENGTH_SHORT) {
393            String locIdName = langData.get("Languages%short", localeId);
394            if (!locIdName.equals(localeId)) {
395                return locIdName;
396            }
397        }
398        return langData.get("Languages", localeId);
399    }
400
401    @Override
402    public String languageDisplayName(String lang) {
403        // Special case to eliminate non-languages, which pollute our data.
404        if (lang.equals("root") || lang.indexOf('_') != -1) {
405            return lang;
406        }
407        if (nameLength == DisplayContext.LENGTH_SHORT) {
408            String langName = langData.get("Languages%short", lang);
409            if (!langName.equals(lang)) {
410                return adjustForUsageAndContext(CapitalizationContextUsage.LANGUAGE, langName);
411            }
412        }
413        return adjustForUsageAndContext(CapitalizationContextUsage.LANGUAGE, langData.get("Languages", lang));
414    }
415
416    @Override
417    public String scriptDisplayName(String script) {
418        String str = langData.get("Scripts%stand-alone", script);
419        if (str.equals(script)) {
420            if (nameLength == DisplayContext.LENGTH_SHORT) {
421                str = langData.get("Scripts%short", script);
422                if (!str.equals(script)) {
423                    return adjustForUsageAndContext(CapitalizationContextUsage.SCRIPT, str);
424                }
425            }
426            str = langData.get("Scripts", script);
427        }
428        return adjustForUsageAndContext(CapitalizationContextUsage.SCRIPT, str);
429    }
430
431    @Override
432    public String scriptDisplayNameInContext(String script) {
433        if (nameLength == DisplayContext.LENGTH_SHORT) {
434            String scriptName = langData.get("Scripts%short", script);
435            if (!scriptName.equals(script)) {
436                return adjustForUsageAndContext(CapitalizationContextUsage.SCRIPT, scriptName);
437            }
438        }
439        return adjustForUsageAndContext(CapitalizationContextUsage.SCRIPT, langData.get("Scripts", script));
440    }
441
442    @Override
443    public String scriptDisplayName(int scriptCode) {
444        return scriptDisplayName(UScript.getShortName(scriptCode));
445    }
446
447    @Override
448    public String regionDisplayName(String region) {
449        if (nameLength == DisplayContext.LENGTH_SHORT) {
450            String regionName = regionData.get("Countries%short", region);
451            if (!regionName.equals(region)) {
452                return adjustForUsageAndContext(CapitalizationContextUsage.TERRITORY, regionName);
453            }
454        }
455        return adjustForUsageAndContext(CapitalizationContextUsage.TERRITORY, regionData.get("Countries", region));
456    }
457
458    @Override
459    public String variantDisplayName(String variant) {
460        // don't have a resource for short variant names
461        return adjustForUsageAndContext(CapitalizationContextUsage.VARIANT, langData.get("Variants", variant));
462    }
463
464    @Override
465    public String keyDisplayName(String key) {
466        // don't have a resource for short key names
467        return adjustForUsageAndContext(CapitalizationContextUsage.KEY, langData.get("Keys", key));
468    }
469
470    @Override
471    public String keyValueDisplayName(String key, String value) {
472        String keyValueName = null;
473
474        if (key.equals("currency")) {
475            keyValueName = currencyDisplayInfo.getName(AsciiUtil.toUpperString(value));
476            if (keyValueName == null) {
477                keyValueName = value;
478            }
479        } else {
480            if (nameLength == DisplayContext.LENGTH_SHORT) {
481                String tmp = langData.get("Types%short", key, value);
482                if (!tmp.equals(value)) {
483                    keyValueName = tmp;
484                }
485            }
486            if (keyValueName == null) {
487                keyValueName = langData.get("Types", key, value);
488            }
489        }
490
491        return adjustForUsageAndContext(CapitalizationContextUsage.KEYVALUE, keyValueName);
492    }
493
494    @Override
495    public List<UiListItem> getUiListCompareWholeItems(Set<ULocale> localeSet, Comparator<UiListItem> comparator) {
496        DisplayContext capContext = getContext(Type.CAPITALIZATION);
497
498        List<UiListItem> result = new ArrayList<UiListItem>();
499        Map<ULocale,Set<ULocale>> baseToLocales = new HashMap<ULocale,Set<ULocale>>();
500        ULocale.Builder builder = new ULocale.Builder();
501        for (ULocale locOriginal : localeSet) {
502            builder.setLocale(locOriginal); // verify well-formed. We do this here so that we consistently throw exception
503            ULocale loc = ULocale.addLikelySubtags(locOriginal);
504            ULocale base = new ULocale(loc.getLanguage());
505            Set<ULocale> locales = baseToLocales.get(base);
506            if (locales == null) {
507                baseToLocales.put(base, locales = new HashSet<ULocale>());
508            }
509            locales.add(loc);
510        }
511        for (Entry<ULocale, Set<ULocale>> entry : baseToLocales.entrySet()) {
512            ULocale base = entry.getKey();
513            Set<ULocale> values = entry.getValue();
514            if (values.size() == 1) {
515                ULocale locale = values.iterator().next();
516                result.add(newRow(ULocale.minimizeSubtags(locale, ULocale.Minimize.FAVOR_SCRIPT), capContext));
517            } else {
518                Set<String> scripts = new HashSet<String>();
519                Set<String> regions = new HashSet<String>();
520                // need the follow two steps to make sure that unusual scripts or regions are displayed
521                ULocale maxBase = ULocale.addLikelySubtags(base);
522                scripts.add(maxBase.getScript());
523                regions.add(maxBase.getCountry());
524                for (ULocale locale : values) {
525                    scripts.add(locale.getScript());
526                    regions.add(locale.getCountry());
527                }
528                boolean hasScripts = scripts.size() > 1;
529                boolean hasRegions = regions.size() > 1;
530                for (ULocale locale : values) {
531                    ULocale.Builder modified = builder.setLocale(locale);
532                    if (!hasScripts) {
533                        modified.setScript("");
534                    }
535                    if (!hasRegions) {
536                        modified.setRegion("");
537                    }
538                    result.add(newRow(modified.build(), capContext));
539                }
540            }
541        }
542        Collections.sort(result, comparator);
543        return result;
544    }
545
546    private UiListItem newRow(ULocale modified, DisplayContext capContext) {
547        ULocale minimized = ULocale.minimizeSubtags(modified, ULocale.Minimize.FAVOR_SCRIPT);
548        String tempName = modified.getDisplayName(locale);
549        boolean titlecase = capContext == DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU;
550        String nameInDisplayLocale =  titlecase ? UCharacter.toTitleFirst(locale, tempName) : tempName;
551        tempName = modified.getDisplayName(modified);
552        String nameInSelf = capContext == DisplayContext.CAPITALIZATION_FOR_UI_LIST_OR_MENU ? UCharacter.toTitleFirst(modified, tempName) : tempName;
553        return new UiListItem(minimized, modified, nameInDisplayLocale, nameInSelf);
554    }
555
556    public static class DataTable {
557        ULocale getLocale() {
558            return ULocale.ROOT;
559        }
560
561        String get(String tableName, String code) {
562            return get(tableName, null, code);
563        }
564
565        String get(String tableName, String subTableName, String code) {
566            return code;
567        }
568    }
569
570    static class ICUDataTable extends DataTable {
571        private final ICUResourceBundle bundle;
572
573        public ICUDataTable(String path, ULocale locale) {
574            this.bundle = (ICUResourceBundle) UResourceBundle.getBundleInstance(
575                    path, locale.getBaseName());
576        }
577
578        public ULocale getLocale() {
579            return bundle.getULocale();
580        }
581
582        public String get(String tableName, String subTableName, String code) {
583            return ICUResourceTableAccess.getTableString(bundle, tableName, subTableName,
584                    code);
585        }
586    }
587
588    static abstract class DataTables {
589        public abstract DataTable get(ULocale locale);
590        public static DataTables load(String className) {
591            try {
592                return (DataTables) Class.forName(className).newInstance();
593            } catch (Throwable t) {
594                final DataTable NO_OP = new DataTable();
595                return new DataTables() {
596                    public DataTable get(ULocale locale) {
597                        return NO_OP;
598                    }
599                };
600            }
601        }
602    }
603
604    static abstract class ICUDataTables extends DataTables {
605        private final String path;
606
607        protected ICUDataTables(String path) {
608            this.path = path;
609        }
610
611        @Override
612        public DataTable get(ULocale locale) {
613            return new ICUDataTable(path, locale);
614        }
615    }
616
617    static class LangDataTables {
618        static final DataTables impl = DataTables.load("com.ibm.icu.impl.ICULangDataTables");
619    }
620
621    static class RegionDataTables {
622        static final DataTables impl = DataTables.load("com.ibm.icu.impl.ICURegionDataTables");
623    }
624
625    public static enum DataTableType {
626        LANG, REGION;
627    }
628
629    public static boolean haveData(DataTableType type) {
630        switch (type) {
631        case LANG: return LangDataTables.impl instanceof ICUDataTables;
632        case REGION: return RegionDataTables.impl instanceof ICUDataTables;
633        default:
634            throw new IllegalArgumentException("unknown type: " + type);
635        }
636    }
637
638    private StringBuilder appendWithSep(String s, StringBuilder b) {
639        if (b.length() == 0) {
640            b.append(s);
641        } else {
642            String combined = separatorFormat.format(new String[] { b.toString(), s });
643            b.replace(0, b.length(), combined);
644        }
645        return b;
646    }
647
648    private static class Cache {
649        private ULocale locale;
650        private DialectHandling dialectHandling;
651        private DisplayContext capitalization;
652        private DisplayContext nameLength;
653        private LocaleDisplayNames cache;
654        public LocaleDisplayNames get(ULocale locale, DialectHandling dialectHandling) {
655            if (!(dialectHandling == this.dialectHandling && DisplayContext.CAPITALIZATION_NONE == this.capitalization &&
656                    DisplayContext.LENGTH_FULL == this.nameLength && locale.equals(this.locale))) {
657                this.locale = locale;
658                this.dialectHandling = dialectHandling;
659                this.capitalization = DisplayContext.CAPITALIZATION_NONE;
660                this.nameLength = DisplayContext.LENGTH_FULL;
661                this.cache = new LocaleDisplayNamesImpl(locale, dialectHandling);
662            }
663            return cache;
664        }
665        public LocaleDisplayNames get(ULocale locale, DisplayContext... contexts) {
666            DialectHandling dialectHandlingIn = DialectHandling.STANDARD_NAMES;
667            DisplayContext capitalizationIn = DisplayContext.CAPITALIZATION_NONE;
668            DisplayContext nameLengthIn = DisplayContext.LENGTH_FULL;
669            for (DisplayContext contextItem : contexts) {
670                switch (contextItem.type()) {
671                case DIALECT_HANDLING:
672                    dialectHandlingIn = (contextItem.value()==DisplayContext.STANDARD_NAMES.value())?
673                            DialectHandling.STANDARD_NAMES: DialectHandling.DIALECT_NAMES;
674                    break;
675                case CAPITALIZATION:
676                    capitalizationIn = contextItem;
677                    break;
678                case DISPLAY_LENGTH:
679                    nameLengthIn = contextItem;
680                    break;
681                default:
682                    break;
683                }
684            }
685            if (!(dialectHandlingIn == this.dialectHandling && capitalizationIn == this.capitalization &&
686                    nameLengthIn == this.nameLength && locale.equals(this.locale))) {
687                this.locale = locale;
688                this.dialectHandling = dialectHandlingIn;
689                this.capitalization = capitalizationIn;
690                this.nameLength = nameLengthIn;
691                this.cache = new LocaleDisplayNamesImpl(locale, contexts);
692            }
693            return cache;
694        }
695    }
696}
697