[go: nahoru, domu]

LocaleList.java revision 8bca69858aefd2326b1bb7ead75796778ed54e93
1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.util;
18
19import android.annotation.NonNull;
20import android.annotation.Nullable;
21import android.annotation.Size;
22
23import com.android.internal.annotations.GuardedBy;
24
25import java.util.HashSet;
26import java.util.Locale;
27
28// TODO: We don't except too many LocaleLists to exist at the same time, and
29// we need access to the data at native level, so we should pass the data
30// down to the native level, create a map of every list seen there, take a
31// pointer back, and just keep that pointer in the Java-level object, so
32// things could be copied very quickly.
33
34/**
35 * LocaleList is an immutable list of Locales, typically used to keep an
36 * ordered user preferences for locales.
37 */
38public final class LocaleList {
39    private final Locale[] mList;
40    // This is a comma-separated list of the locales in the LocaleList created at construction time,
41    // basically the result of running each locale's toLanguageTag() method and concatenating them
42    // with commas in between.
43    private final String mStringRepresentation;
44
45    private static final Locale[] sEmptyList = new Locale[0];
46    private static final LocaleList sEmptyLocaleList = new LocaleList();
47
48    public Locale get(int location) {
49        return location < mList.length ? mList[location] : null;
50    }
51
52    @Nullable
53    public Locale getPrimary() {
54        return mList.length == 0 ? null : get(0);
55    }
56
57    public boolean isEmpty() {
58        return mList.length == 0;
59    }
60
61    public int size() {
62        return mList.length;
63    }
64
65    @Override
66    public boolean equals(Object other) {
67        if (other == this)
68            return true;
69        if (!(other instanceof LocaleList))
70            return false;
71        final Locale[] otherList = ((LocaleList) other).mList;
72        if (mList.length != otherList.length)
73            return false;
74        for (int i = 0; i < mList.length; ++i) {
75            if (!mList[i].equals(otherList[i]))
76                return false;
77        }
78        return true;
79    }
80
81    @Override
82    public int hashCode() {
83        int result = 1;
84        for (int i = 0; i < mList.length; ++i) {
85            result = 31 * result + mList[i].hashCode();
86        }
87        return result;
88    }
89
90    @Override
91    public String toString() {
92        StringBuilder sb = new StringBuilder();
93        sb.append("[");
94        for (int i = 0; i < mList.length; ++i) {
95            sb.append(mList[i]);
96            if (i < mList.length - 1) {
97                sb.append(',');
98            }
99        }
100        sb.append("]");
101        return sb.toString();
102    }
103
104    @NonNull
105    public String toLanguageTags() {
106        return mStringRepresentation;
107    }
108
109    /**
110     * It is almost always better to call {@link #getEmptyLocaleList()} instead which returns
111     * a pre-constructed empty locale list.
112     */
113    public LocaleList() {
114        mList = sEmptyList;
115        mStringRepresentation = "";
116    }
117
118    /**
119     * @throws NullPointerException if any of the input locales is <code>null</code>.
120     * @throws IllegalArgumentException if any of the input locales repeat.
121     */
122    public LocaleList(@Nullable Locale locale) {
123        if (locale == null) {
124            mList = sEmptyList;
125            mStringRepresentation = "";
126        } else {
127            mList = new Locale[1];
128            mList[0] = (Locale) locale.clone();
129            mStringRepresentation = locale.toLanguageTag();
130        }
131    }
132
133    /**
134     * @throws NullPointerException if any of the input locales is <code>null</code>.
135     * @throws IllegalArgumentException if any of the input locales repeat.
136     */
137    public LocaleList(@Nullable Locale[] list) {
138        if (list == null || list.length == 0) {
139            mList = sEmptyList;
140            mStringRepresentation = "";
141        } else {
142            final Locale[] localeList = new Locale[list.length];
143            final HashSet<Locale> seenLocales = new HashSet<Locale>();
144            final StringBuilder sb = new StringBuilder();
145            for (int i = 0; i < list.length; ++i) {
146                final Locale l = list[i];
147                if (l == null) {
148                    throw new NullPointerException();
149                } else if (seenLocales.contains(l)) {
150                    throw new IllegalArgumentException();
151                } else {
152                    final Locale localeClone = (Locale) l.clone();
153                    localeList[i] = localeClone;
154                    sb.append(localeClone.toLanguageTag());
155                    if (i < list.length - 1) {
156                        sb.append(',');
157                    }
158                    seenLocales.add(localeClone);
159                }
160            }
161            mList = localeList;
162            mStringRepresentation = sb.toString();
163        }
164    }
165
166    public static LocaleList getEmptyLocaleList() {
167        return sEmptyLocaleList;
168    }
169
170    public static LocaleList forLanguageTags(@Nullable String list) {
171        if (list == null || list.equals("")) {
172            return getEmptyLocaleList();
173        } else {
174            final String[] tags = list.split(",");
175            final Locale[] localeArray = new Locale[tags.length];
176            for (int i = 0; i < localeArray.length; ++i) {
177                localeArray[i] = Locale.forLanguageTag(tags[i]);
178            }
179            return new LocaleList(localeArray);
180        }
181    }
182
183    @Nullable
184    public Locale getBestMatch(String[] locales) {
185        // TODO: Fix this to actually do locale negotiation and choose the best match
186        return getPrimary();
187    }
188
189    private final static Object sLock = new Object();
190
191    @GuardedBy("sLock")
192    private static LocaleList sDefaultLocaleList;
193
194    // TODO: fix this to return the default system locale list once we have that
195    @NonNull @Size(min=1)
196    public static LocaleList getDefault() {
197        Locale defaultLocale = Locale.getDefault();
198        synchronized (sLock) {
199            if (sDefaultLocaleList == null || sDefaultLocaleList.size() != 1
200                    || !defaultLocale.equals(sDefaultLocaleList.getPrimary())) {
201                sDefaultLocaleList = new LocaleList(defaultLocale);
202            }
203        }
204        return sDefaultLocaleList;
205    }
206}
207