1 /* 2 * Copyright (C) 2013 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 17 package com.android.inputmethod.keyboard.tools; 18 19 import java.util.HashMap; 20 import java.util.Locale; 21 22 /** 23 * A class to help with handling Locales in string form. 24 * 25 * This is a subset of com/android/inputmethod/latin/utils/LocaleUtils.java in order to use 26 * for the make-keyboard-text tool. 27 */ 28 public final class LocaleUtils { 29 public static final Locale DEFAULT_LOCALE = Locale.ROOT; 30 private static final String DEFAULT_LOCALE_CODE = "DEFAULT"; 31 public static final String NO_LANGUAGE_LOCALE_CODE = "zz"; 32 public static final String NO_LANGUAGE_LOCALE_DISPLAY_NAME = "Alphabet"; 33 LocaleUtils()34 private LocaleUtils() { 35 // Intentional empty constructor for utility class. 36 } 37 38 private static final HashMap<String, Locale> sLocaleCache = new HashMap<>(); 39 40 private static final int INDEX_LANGUAGE = 0; 41 private static final int INDEX_SCRIPT = 1; 42 private static final int INDEX_REGION = 2; 43 private static final int ELEMENT_LIMIT = INDEX_REGION + 1; 44 45 /** 46 * Creates a locale from a string specification. 47 * 48 * Locale string is: language(_script)?(_region)? 49 * where: language := [a-zA-Z]{2,3} 50 * script := [a-zA-Z]{4} 51 * region := [a-zA-Z]{2,3}|[0-9]{3} 52 */ constructLocaleFromString(final String localeStr)53 public static Locale constructLocaleFromString(final String localeStr) { 54 if (localeStr == null) { 55 return null; 56 } 57 synchronized (sLocaleCache) { 58 if (sLocaleCache.containsKey(localeStr)) { 59 return sLocaleCache.get(localeStr); 60 } 61 boolean hasRegion = false; 62 final Locale.Builder builder = new Locale.Builder(); 63 final String[] localeElements = localeStr.split("_", ELEMENT_LIMIT); 64 if (localeElements.length > INDEX_LANGUAGE) { 65 final String text = localeElements[INDEX_LANGUAGE]; 66 if (isValidLanguage(text)) { 67 builder.setLanguage(text); 68 } else { 69 throw new RuntimeException("Unknown locale format: " + localeStr); 70 } 71 } 72 if (localeElements.length > INDEX_SCRIPT) { 73 final String text = localeElements[INDEX_SCRIPT]; 74 if (isValidScript(text)) { 75 builder.setScript(text); 76 } else if (isValidRegion(text)) { 77 builder.setRegion(text); 78 hasRegion = true; 79 } else { 80 throw new RuntimeException("Unknown locale format: " + localeStr); 81 } 82 } 83 if (localeElements.length > INDEX_REGION) { 84 final String text = localeElements[INDEX_REGION]; 85 if (!hasRegion && isValidRegion(text)) { 86 builder.setRegion(text); 87 } else { 88 throw new RuntimeException("Unknown locale format: " + localeStr); 89 } 90 } 91 final Locale locale = builder.build(); 92 sLocaleCache.put(localeStr, locale); 93 return locale; 94 } 95 } 96 97 private static final int MIN_LENGTH_OF_LANGUAGE = 2; 98 private static final int MAX_LENGTH_OF_LANGUAGE = 2; 99 private static final int LENGTH_OF_SCRIPT = 4; 100 private static final int MIN_LENGTH_OF_REGION = 2; 101 private static final int MAX_LENGTH_OF_REGION = 2; 102 private static final int LENGTH_OF_AREA_CODE = 3; 103 isValidLanguage(final String text)104 private static boolean isValidLanguage(final String text) { 105 return isAlphabetSequence(text, MIN_LENGTH_OF_LANGUAGE, MAX_LENGTH_OF_LANGUAGE); 106 } 107 isValidScript(final String text)108 private static boolean isValidScript(final String text) { 109 return isAlphabetSequence(text, LENGTH_OF_SCRIPT, LENGTH_OF_SCRIPT); 110 } 111 isValidRegion(final String text)112 private static boolean isValidRegion(final String text) { 113 return isAlphabetSequence(text, MIN_LENGTH_OF_REGION, MAX_LENGTH_OF_REGION) 114 || isDigitSequence(text, LENGTH_OF_AREA_CODE, LENGTH_OF_AREA_CODE); 115 } 116 isAlphabetSequence(final String text, final int lower, final int upper)117 private static boolean isAlphabetSequence(final String text, final int lower, final int upper) { 118 final int length = text.length(); 119 if (length < lower || length > upper) { 120 return false; 121 } 122 for (int index = 0; index < length; index++) { 123 if (!isAsciiAlphabet(text.charAt(index))) { 124 return false; 125 } 126 } 127 return true; 128 } 129 isDigitSequence(final String text, final int lower, final int upper)130 private static boolean isDigitSequence(final String text, final int lower, final int upper) { 131 final int length = text.length(); 132 if (length < lower || length > upper) { 133 return false; 134 } 135 for (int index = 0; index < length; ++index) { 136 if (!isAsciiDigit(text.charAt(index))) { 137 return false; 138 } 139 } 140 return true; 141 } 142 isAsciiAlphabet(char c)143 private static boolean isAsciiAlphabet(char c) { 144 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); 145 } 146 isAsciiDigit(char c)147 private static boolean isAsciiDigit(char c) { 148 return c >= '0' && c <= '9'; 149 } 150 getLocaleCode(final Locale locale)151 public static String getLocaleCode(final Locale locale) { 152 if (locale == DEFAULT_LOCALE) { 153 return DEFAULT_LOCALE_CODE; 154 } 155 return locale.toString(); 156 } 157 getLocaleDisplayName(final Locale locale)158 public static String getLocaleDisplayName(final Locale locale) { 159 if (locale == DEFAULT_LOCALE) { 160 return DEFAULT_LOCALE_CODE; 161 } 162 if (locale.getLanguage().equals(NO_LANGUAGE_LOCALE_CODE)) { 163 return NO_LANGUAGE_LOCALE_DISPLAY_NAME; 164 } 165 return locale.getDisplayName(Locale.ENGLISH); 166 } 167 } 168