From d317796207d9c9443669ff94aac63c4193ec0e6f Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Tue, 8 Apr 2014 19:20:23 +0900 Subject: [PATCH] Use Locale to process text resources Change-Id: Ic1c4e1776071332e02c368055157124bb539d14e --- .../keyboard/internal/KeyboardTextsTable.java | 2 +- .../inputmethod/keyboard/tools/JarUtils.java | 15 +- .../keyboard/tools/LocaleUtils.java | 129 +++++++++++++++--- .../keyboard/tools/MoreKeysResources.java | 21 +-- .../keyboard/tools/StringResourceMap.java | 5 +- 5 files changed, 135 insertions(+), 37 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java index 1bd98332f..f0356df79 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java @@ -3656,7 +3656,7 @@ public final class KeyboardTextsTable { private static final Object[] LOCALES_AND_TEXTS = { // "locale", TEXT_ARRAY, /* numberOfNonNullText/lengthOf_TEXT_ARRAY localeName */ - "DEFAULT", TEXTS_DEFAULT, /* 168/168 default */ + "DEFAULT", TEXTS_DEFAULT, /* 168/168 DEFAULT */ "af" , TEXTS_af, /* 7/ 12 Afrikaans */ "ar" , TEXTS_ar, /* 55/107 Arabic */ "az_AZ" , TEXTS_az_AZ, /* 8/ 17 Azerbaijani (Azerbaijan) */ diff --git a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/JarUtils.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/JarUtils.java index b892df236..c947a63bf 100644 --- a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/JarUtils.java +++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/JarUtils.java @@ -24,6 +24,7 @@ import java.net.URL; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Enumeration; +import java.util.Locale; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -86,23 +87,23 @@ public final class JarUtils { } // The locale is taken from string resource jar entry name (values-/) - // or {@link LocaleUtils#DEFAULT_LOCALE_KEY} for the default string resource + // or {@link LocaleUtils#DEFAULT_LOCALE} for the default string resource // directory (values/). - public static String getLocaleFromEntryName(final String jarEntryName) { + public static Locale getLocaleFromEntryName(final String jarEntryName) { final String dirName = jarEntryName.substring(0, jarEntryName.lastIndexOf('/')); final int pos = dirName.lastIndexOf('/'); final String parentName = (pos >= 0) ? dirName.substring(pos + 1) : dirName; final int localePos = parentName.indexOf('-'); if (localePos < 0) { // Default resource name. - return LocaleUtils.DEFAULT_LOCALE_KEY; + return LocaleUtils.DEFAULT_LOCALE; } - final String locale = parentName.substring(localePos + 1); - final int regionPos = locale.indexOf("-r"); + final String localeStr = parentName.substring(localePos + 1); + final int regionPos = localeStr.indexOf("-r"); if (regionPos < 0) { - return locale; + return LocaleUtils.constructLocaleFromString(localeStr); } - return locale.replace("-r", "_"); + return LocaleUtils.constructLocaleFromString(localeStr.replace("-r", "_")); } public static void close(final Closeable stream) { diff --git a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/LocaleUtils.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/LocaleUtils.java index d0f8b4292..0dfa37667 100644 --- a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/LocaleUtils.java +++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/LocaleUtils.java @@ -26,7 +26,8 @@ import java.util.Locale; * for the make-keyboard-text tool. */ public final class LocaleUtils { - public static final String DEFAULT_LOCALE_KEY = "DEFAULT"; + public static final Locale DEFAULT_LOCALE = Locale.ROOT; + private static final String DEFAULT_LOCALE_CODE = "DEFAULT"; public static final String NO_LANGUAGE_LOCALE_CODE = "zz"; public static final String NO_LANGUAGE_LOCALE_DISPLAY_NAME = "Alphabet"; @@ -36,39 +37,131 @@ public final class LocaleUtils { private static final HashMap sLocaleCache = new HashMap(); + private static final int INDEX_LANGUAGE = 0; + private static final int INDEX_SCRIPT = 1; + private static final int INDEX_REGION = 2; + private static final int ELEMENT_LIMIT = INDEX_REGION + 1; + /** * Creates a locale from a string specification. + * + * Locale string is: language(_script)?(_region)? + * where: language := [a-zA-Z]{2,3} + * script := [a-zA-Z]{4} + * region := [a-zA-Z]{2,3}|[0-9]{3} */ public static Locale constructLocaleFromString(final String localeStr) { if (localeStr == null) { return null; } synchronized (sLocaleCache) { - Locale retval = sLocaleCache.get(localeStr); - if (retval != null) { - return retval; + if (sLocaleCache.containsKey(localeStr)) { + return sLocaleCache.get(localeStr); } - final String[] localeParams = localeStr.split("_", 3); - // TODO: Use JDK 7 Locale.Builder to handle a script name. - if (localeParams.length == 1) { - retval = new Locale(localeParams[0]); - } else if (localeParams.length == 2) { - retval = new Locale(localeParams[0], localeParams[1]); - } else if (localeParams.length == 3) { - retval = new Locale(localeParams[0], localeParams[1], localeParams[2]); + boolean hasRegion = false; + final Locale.Builder builder = new Locale.Builder(); + final String[] localeElements = localeStr.split("_", ELEMENT_LIMIT); + if (localeElements.length > INDEX_LANGUAGE) { + final String text = localeElements[INDEX_LANGUAGE]; + if (isValidLanguage(text)) { + builder.setLanguage(text); + } else { + throw new RuntimeException("Unknown locale format: " + localeStr); + } } - if (retval != null) { - sLocaleCache.put(localeStr, retval); + if (localeElements.length > INDEX_SCRIPT) { + final String text = localeElements[INDEX_SCRIPT]; + if (isValidScript(text)) { + builder.setScript(text); + } else if (isValidRegion(text)) { + builder.setRegion(text); + hasRegion = true; + } else { + throw new RuntimeException("Unknown locale format: " + localeStr); + } } - return retval; + if (localeElements.length > INDEX_REGION) { + final String text = localeElements[INDEX_REGION]; + if (!hasRegion && isValidRegion(text)) { + builder.setRegion(text); + } else { + throw new RuntimeException("Unknown locale format: " + localeStr); + } + } + final Locale locale = builder.build(); + sLocaleCache.put(localeStr, locale); + return locale; } } - public static String getLocaleDisplayName(final String localeString) { - if (localeString.equals(NO_LANGUAGE_LOCALE_CODE)) { + private static final int MIN_LENGTH_OF_LANGUAGE = 2; + private static final int MAX_LENGTH_OF_LANGUAGE = 2; + private static final int LENGTH_OF_SCRIPT = 4; + private static final int MIN_LENGTH_OF_REGION = 2; + private static final int MAX_LENGTH_OF_REGION = 2; + private static final int LENGTH_OF_AREA_CODE = 3; + + private static boolean isValidLanguage(final String text) { + return isAlphabetSequence(text, MIN_LENGTH_OF_LANGUAGE, MAX_LENGTH_OF_LANGUAGE); + } + + private static boolean isValidScript(final String text) { + return isAlphabetSequence(text, LENGTH_OF_SCRIPT, LENGTH_OF_SCRIPT); + } + + private static boolean isValidRegion(final String text) { + return isAlphabetSequence(text, MIN_LENGTH_OF_REGION, MAX_LENGTH_OF_REGION) + || isDigitSequence(text, LENGTH_OF_AREA_CODE, LENGTH_OF_AREA_CODE); + } + + private static boolean isAlphabetSequence(final String text, final int lower, final int upper) { + final int length = text.length(); + if (length < lower || length > upper) { + return false; + } + for (int index = 0; index < length; index++) { + if (!isAsciiAlphabet(text.charAt(index))) { + return false; + } + } + return true; + } + + private static boolean isDigitSequence(final String text, final int lower, final int upper) { + final int length = text.length(); + if (length < lower || length > upper) { + return false; + } + for (int index = 0; index < length; ++index) { + if (!isAsciiDigit(text.charAt(index))) { + return false; + } + } + return true; + } + + private static boolean isAsciiAlphabet(char c) { + return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); + } + + private static boolean isAsciiDigit(char c) { + return c >= '0' && c <= '9'; + } + + public static String getLocaleCode(final Locale locale) { + if (locale == DEFAULT_LOCALE) { + return DEFAULT_LOCALE_CODE; + } + return locale.toString(); + } + + public static String getLocaleDisplayName(final Locale locale) { + if (locale == DEFAULT_LOCALE) { + return DEFAULT_LOCALE_CODE; + } + if (locale.getLanguage().equals(NO_LANGUAGE_LOCALE_CODE)) { return NO_LANGUAGE_LOCALE_DISPLAY_NAME; } - final Locale locale = constructLocaleFromString(localeString); return locale.getDisplayName(Locale.ENGLISH); } } diff --git a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MoreKeysResources.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MoreKeysResources.java index c1a9753cc..c8cb4acec 100644 --- a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MoreKeysResources.java +++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MoreKeysResources.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; +import java.util.Locale; import java.util.TreeMap; import java.util.jar.JarFile; @@ -60,9 +61,10 @@ public class MoreKeysResources { jar, TEXT_RESOURCE_NAME); for (final String entryName : resourceEntryNames) { final StringResourceMap resMap = new StringResourceMap(entryName); - mResourcesMap.put(resMap.mLocale, resMap); + mResourcesMap.put(LocaleUtils.getLocaleCode(resMap.mLocale), resMap); } - mDefaultResourceMap = mResourcesMap.get(LocaleUtils.DEFAULT_LOCALE_KEY); + mDefaultResourceMap = mResourcesMap.get( + LocaleUtils.getLocaleCode(LocaleUtils.DEFAULT_LOCALE)); // Initialize name histogram and names list. final HashMap nameHistogram = mNameHistogram; @@ -165,13 +167,13 @@ public class MoreKeysResources { mDefaultResourceMap.setOutputArraySize(outputArraySize); } - private static String getArrayNameForLocale(final String locale) { - return TEXTS_ARRAY_NAME_PREFIX + locale; + private static String getArrayNameForLocale(final Locale locale) { + return TEXTS_ARRAY_NAME_PREFIX + LocaleUtils.getLocaleCode(locale); } private void dumpTexts(final PrintStream out) { for (final StringResourceMap resMap : mResourcesMap.values()) { - final String locale = resMap.mLocale; + final Locale locale = resMap.mLocale; if (resMap == mDefaultResourceMap) continue; out.format(" /* Locale %s: %s */\n", locale, LocaleUtils.getLocaleDisplayName(locale)); @@ -185,10 +187,11 @@ public class MoreKeysResources { private void dumpLocalesMap(final PrintStream out) { for (final StringResourceMap resMap : mResourcesMap.values()) { - final String locale = resMap.mLocale; - final String localeToDump = locale.equals(LocaleUtils.DEFAULT_LOCALE_KEY) - ? String.format("\"%s\"", locale) - : String.format("\"%s\"%s", locale, " ".substring(locale.length())); + final Locale locale = resMap.mLocale; + final String localeStr = LocaleUtils.getLocaleCode(locale); + final String localeToDump = (locale == LocaleUtils.DEFAULT_LOCALE) + ? String.format("\"%s\"", localeStr) + : String.format("\"%s\"%s", localeStr, " ".substring(localeStr.length())); out.format(" %s, %-12s /* %3d/%3d %s */\n", localeToDump, getArrayNameForLocale(locale) + ",", resMap.getResources().size(), resMap.getOutputArraySize(), diff --git a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/StringResourceMap.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/StringResourceMap.java index d7e76ad77..6a79268e5 100644 --- a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/StringResourceMap.java +++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/StringResourceMap.java @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; +import java.util.Locale; import java.util.Map; import javax.xml.parsers.ParserConfigurationException; @@ -34,8 +35,8 @@ import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; public class StringResourceMap { - // Locale name. - public final String mLocale; + // Locale of this string resource map. + public final Locale mLocale; // String resource list. private final List mResources; // Name to string resource map.