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