12d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert// © 2016 and later: Unicode, Inc. and others.
22d2bb24f747c65578da13d5b13b82f0669690461Fredrik Roubert// License & terms of use: http://www.unicode.org/copyright.html#License
3bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert/*
4bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert ******************************************************************************
5bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert * Copyright (C) 1996-2011, International Business Machines Corporation and   *
6bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert * others. All Rights Reserved.                                               *
7bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert ******************************************************************************
8bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert *
9bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert ******************************************************************************
10bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert */
11bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
12bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubertpackage com.ibm.icu.impl;
13bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
14bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubertimport java.util.Locale;
15bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
16bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert/**
17bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert * A class to hold utility functions missing from java.util.Locale.
18bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert */
19bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubertpublic class LocaleUtility {
20bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
21bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    /**
22bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * A helper function to convert a string of the form
23bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * aa_BB_CC to a locale object.  Why isn't this in Locale?
24bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     */
25bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    public static Locale getLocaleFromName(String name) {
26bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        String language = "";
27bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        String country = "";
28bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        String variant = "";
29bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
30bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        int i1 = name.indexOf('_');
31bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        if (i1 < 0) {
32bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            language = name;
33bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        } else {
34bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            language = name.substring(0, i1);
35bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            ++i1;
36bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            int i2 = name.indexOf('_', i1);
37bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            if (i2 < 0) {
38bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                country = name.substring(i1);
39bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            } else {
40bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                country = name.substring(i1, i2);
41bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                variant = name.substring(i2+1);
42bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
43bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
44bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
45bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        return new Locale(language, country, variant);
46bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    }
47bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
48bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    /**
49bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * Compare two locale strings of the form aa_BB_CC, and
50bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * return true if parent is a 'strict' fallback of child, that is,
51bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * if child =~ "^parent(_.+)*" (roughly).
52bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     */
53bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    public static boolean isFallbackOf(String parent, String child) {
54bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        if (!child.startsWith(parent)) {
55bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            return false;
56bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
57bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        int i = parent.length();
58bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        return (i == child.length() ||
59bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                child.charAt(i) == '_');
60bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    }
61bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
62bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    /**
63bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * Compare two locales, and return true if the parent is a
64bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * 'strict' fallback of the child (parent string is a fallback
65bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * of child string).
66bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     */
67bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    public static boolean isFallbackOf(Locale parent, Locale child) {
68bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        return isFallbackOf(parent.toString(), child.toString());
69bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    }
70bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
71bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
72bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    /*
73bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * Convenience method that calls canonicalLocaleString(String) with
74bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * locale.toString();
75bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     */
76bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    /*public static String canonicalLocaleString(Locale locale) {
77bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        return canonicalLocaleString(locale.toString());
78bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    }*/
79bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
80bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    /*
81bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * You'd think that Locale canonicalizes, since it munges the
82bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * renamed languages, but it doesn't quite.  It forces the region
83bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * to be upper case but doesn't do anything about the language or
84bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * variant.  Our canonical form is 'lower_UPPER_UPPER'.
85bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     */
86bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    /*public static String canonicalLocaleString(String id) {
87bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        if (id != null) {
88bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            int x = id.indexOf("_");
89bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            if (x == -1) {
90bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                id = id.toLowerCase(Locale.ENGLISH);
91bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            } else {
92bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                StringBuffer buf = new StringBuffer();
93bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                buf.append(id.substring(0, x).toLowerCase(Locale.ENGLISH));
94bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                buf.append(id.substring(x).toUpperCase(Locale.ENGLISH));
95bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
96bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                int len = buf.length();
97bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                int n = len;
98bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                while (--n >= 0 && buf.charAt(n) == '_') {
99bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                }
100bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                if (++n != len) {
101bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                    buf.delete(n, len);
102bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                }
103bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                id = buf.toString();
104bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
105bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
106bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        return id;
107bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    }*/
108bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
109bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    /**
110bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * Fallback from the given locale name by removing the rightmost _-delimited
111bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * element. If there is none, return the root locale ("", "", ""). If this
112bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * is the root locale, return null. NOTE: The string "root" is not
113bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * recognized; do not use it.
114bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     *
115bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     * @return a new Locale that is a fallback from the given locale, or null.
116bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert     */
117bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    public static Locale fallback(Locale loc) {
118bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert
119bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        // Split the locale into parts and remove the rightmost part
120bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        String[] parts = new String[]
121bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            { loc.getLanguage(), loc.getCountry(), loc.getVariant() };
122bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        int i;
123bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        for (i=2; i>=0; --i) {
124bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            if (parts[i].length() != 0) {
125bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                parts[i] = "";
126bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert                break;
127bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            }
128bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
129bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        if (i<0) {
130bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert            return null; // All parts were empty
131bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        }
132bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert        return new Locale(parts[0], parts[1], parts[2]);
133bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert    }
134bd1cbb618dcaa1ac6ba7c77dece35cb79593a5d7Fredrik Roubert}
135