aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--java/res/xml/method.xml6
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsTable.java2
-rw-r--r--java/src/com/android/inputmethod/latin/BinaryDictionary.java13
-rw-r--r--java/src/com/android/inputmethod/latin/WordComposer.java43
-rw-r--r--java/src/com/android/inputmethod/latin/utils/StringUtils.java33
-rw-r--r--native/jni/NativeFileList.mk1
-rw-r--r--native/jni/tests/suggest/core/dictionary/bloom_filter_test.cpp80
-rw-r--r--tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java4
-rw-r--r--tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanish419.java36
-rw-r--r--tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java64
-rw-r--r--tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/JarUtils.java15
-rw-r--r--tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/LocaleUtils.java129
-rw-r--r--tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MoreKeysResources.java21
-rw-r--r--tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/StringResourceMap.java5
14 files changed, 386 insertions, 66 deletions
diff --git a/java/res/xml/method.xml b/java/res/xml/method.xml
index 1bef3c254..6d8f78773 100644
--- a/java/res/xml/method.xml
+++ b/java/res/xml/method.xml
@@ -40,7 +40,7 @@
eo: Esperanto/spanish
es: Spanish/spanish
es_US: Spanish (United States)/spanish
- (es_419: Spanish (Latin America)/qwerty)
+ es_419: Spanish (Latin America)/spanish
et_EE: Estonian (Estonia)/nordic
eu_ES: Basque (Spain)/spanish
fa: Persian/farsi
@@ -248,16 +248,14 @@
android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
- <!--
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
- android:subtypeId="0x623f9286"
+ android:subtypeId="0xa23e5d19"
android:imeSubtypeLocale="es_419"
android:imeSubtypeMode="keyboard"
android:imeSubtypeExtraValue="KeyboardLayoutSet=spanish,AsciiCapable,EmojiCapable"
android:isAsciiCapable="true"
/>
- -->
<subtype android:icon="@drawable/ic_ime_switcher_dark"
android:label="@string/subtype_generic"
android:subtypeId="0xec2d3955"
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/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 7b37777f5..5e36d9703 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -247,7 +247,9 @@ public final class BinaryDictionary extends Dictionary {
final String prevWord, final ProximityInfo proximityInfo,
final boolean blockOffensiveWords, final int[] additionalFeaturesOptions,
final int sessionId, final float[] inOutLanguageWeight) {
- if (!isValidDictionary()) return null;
+ if (!isValidDictionary()) {
+ return null;
+ }
Arrays.fill(mInputCodePoints, Constants.NOT_A_CODE);
// TODO: toLowerCase in the native code
@@ -257,12 +259,11 @@ public final class BinaryDictionary extends Dictionary {
final boolean isGesture = composer.isBatchMode();
final int inputSize;
if (!isGesture) {
- final int composerSize = composer.sizeWithoutTrailingSingleQuotes();
- if (composerSize > MAX_WORD_LENGTH - 1) return null;
- for (int i = 0; i < composerSize; i++) {
- mInputCodePoints[i] = composer.getCodeAt(i);
+ inputSize = composer.copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount(
+ mInputCodePoints, MAX_WORD_LENGTH);
+ if (inputSize < 0) {
+ return null;
}
- inputSize = composerSize;
} else {
inputSize = inputPointers.getPointerSize();
}
diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java
index 81d642ff2..02f18cdd3 100644
--- a/java/src/com/android/inputmethod/latin/WordComposer.java
+++ b/java/src/com/android/inputmethod/latin/WordComposer.java
@@ -131,29 +131,42 @@ public final class WordComposer {
return mCodePointSize;
}
- public boolean isSingleLetter() {
- return size() == 1;
+ /**
+ * Copy the code points in the typed word to a destination array of ints.
+ *
+ * If the array is too small to hold the code points in the typed word, nothing is copied and
+ * -1 is returned.
+ *
+ * @param destination the array of ints.
+ * @param maxSize the size of the array.
+ * @return the number of copied code points.
+ */
+ public int copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount(
+ final int[] destination, final int maxSize) {
+ int i = mTypedWordCache.length() - 1;
+ while (i >= 0 && mTypedWordCache.charAt(i) == Constants.CODE_SINGLE_QUOTE) {
+ --i;
+ }
+ if (i < 0) {
+ // The string is empty or contains only single quotes.
+ return 0;
+ }
+ final int codePointSize = Character.codePointCount(mTypedWordCache, 0, i);
+ if (codePointSize > maxSize) {
+ return -1;
+ }
+ return StringUtils.copyCodePointsAndReturnCodePointCount(destination, mTypedWordCache, 0,
+ i + 1, true /* downCase */);
}
- // When the composition contains trailing quotes, we don't pass them to the suggestion engine.
- // This is because "'tgis'" should be corrected to "'this'", but we can't afford to consider
- // single quotes as separators because of their very common use as apostrophes.
- public int sizeWithoutTrailingSingleQuotes() {
- return size() - mTrailingSingleQuotesCount;
+ public boolean isSingleLetter() {
+ return size() == 1;
}
public final boolean isComposingWord() {
return size() > 0;
}
- // TODO: make sure that the index should not exceed MAX_WORD_LENGTH
- public int getCodeAt(int index) {
- if (index >= MAX_WORD_LENGTH) {
- return -1;
- }
- return mPrimaryKeyCodes[index];
- }
-
public InputPointers getInputPointers() {
return mInputPointers;
}
diff --git a/java/src/com/android/inputmethod/latin/utils/StringUtils.java b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
index accbc8b7b..374badc19 100644
--- a/java/src/com/android/inputmethod/latin/utils/StringUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/StringUtils.java
@@ -191,13 +191,42 @@ public final class StringUtils {
}
final int[] codePoints =
new int[Character.codePointCount(charSequence, startIndex, endIndex)];
+ copyCodePointsAndReturnCodePointCount(codePoints, charSequence, startIndex, endIndex,
+ false /* downCase */);
+ return codePoints;
+ }
+
+ /**
+ * Copies the codepoints in a CharSequence to an int array.
+ *
+ * This method assumes there is enough space in the array to store the code points. The size
+ * can be measured with Character#codePointCount(CharSequence, int, int) before passing to this
+ * method. If the int array is too small, an ArrayIndexOutOfBoundsException will be thrown.
+ * Also, this method makes no effort to be thread-safe. Do not modify the CharSequence while
+ * this method is running, or the behavior is undefined.
+ * This method can optionally downcase code points before copying them, but it pays no attention
+ * to locale while doing so.
+ *
+ * @param destination the int array.
+ * @param charSequence the CharSequence.
+ * @param startIndex the start index inside the string in java chars, inclusive.
+ * @param endIndex the end index inside the string in java chars, exclusive.
+ * @param downCase if this is true, code points will be downcased before being copied.
+ * @return the number of copied code points.
+ */
+ public static int copyCodePointsAndReturnCodePointCount(final int[] destination,
+ final CharSequence charSequence, final int startIndex, final int endIndex,
+ final boolean downCase) {
int destIndex = 0;
for (int index = startIndex; index < endIndex;
index = Character.offsetByCodePoints(charSequence, index, 1)) {
- codePoints[destIndex] = Character.codePointAt(charSequence, index);
+ final int codePoint = Character.codePointAt(charSequence, index);
+ // TODO: stop using this, as it's not aware of the locale and does not always do
+ // the right thing.
+ destination[destIndex] = downCase ? Character.toLowerCase(codePoint) : codePoint;
destIndex++;
}
- return codePoints;
+ return destIndex;
}
public static int[] toSortedCodePointArray(final String string) {
diff --git a/native/jni/NativeFileList.mk b/native/jni/NativeFileList.mk
index 1230150ea..70a6638fb 100644
--- a/native/jni/NativeFileList.mk
+++ b/native/jni/NativeFileList.mk
@@ -102,4 +102,5 @@ LATIN_IME_CORE_SRC_FILES := \
LATIN_IME_CORE_TEST_FILES := \
defines_test.cpp \
suggest/core/layout/normal_distribution_2d_test.cpp \
+ suggest/core/dictionary/bloom_filter_test.cpp \
utils/autocorrection_threshold_utils_test.cpp
diff --git a/native/jni/tests/suggest/core/dictionary/bloom_filter_test.cpp b/native/jni/tests/suggest/core/dictionary/bloom_filter_test.cpp
new file mode 100644
index 000000000..b62021784
--- /dev/null
+++ b/native/jni/tests/suggest/core/dictionary/bloom_filter_test.cpp
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "suggest/core/dictionary/bloom_filter.h"
+
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <cstdlib>
+#include <functional>
+#include <random>
+#include <unordered_set>
+#include <vector>
+
+namespace latinime {
+namespace {
+
+TEST(BloomFilterTest, TestFilter) {
+ static const int TEST_RANDOM_DATA_MAX = 65536;
+ static const int ELEMENT_COUNT = 1000;
+ std::vector<int> elements;
+
+ // Initialize data set with random integers.
+ {
+ // Use the uniform integer distribution [0, TEST_RANDOM_DATA_MAX].
+ std::uniform_int_distribution<int> distribution(0, TEST_RANDOM_DATA_MAX);
+ auto randomNumberGenerator = std::bind(distribution, std::mt19937());
+ for (int i = 0; i < ELEMENT_COUNT; ++i) {
+ elements.push_back(randomNumberGenerator());
+ }
+ }
+
+ // Make sure BloomFilter contains nothing by default.
+ BloomFilter bloomFilter;
+ for (const int elem : elements) {
+ ASSERT_FALSE(bloomFilter.isInFilter(elem));
+ }
+
+ // Copy some of the test vector into bloom filter.
+ std::unordered_set<int> elementsThatHaveBeenSetInFilter;
+ {
+ // Use the uniform integer distribution [0, 1].
+ std::uniform_int_distribution<int> distribution(0, 1);
+ auto randomBitGenerator = std::bind(distribution, std::mt19937());
+ for (const int elem : elements) {
+ if (randomBitGenerator() == 0) {
+ bloomFilter.setInFilter(elem);
+ elementsThatHaveBeenSetInFilter.insert(elem);
+ }
+ }
+ }
+
+ for (const int elem : elements) {
+ const bool existsInFilter = bloomFilter.isInFilter(elem);
+ const bool hasBeenSetInFilter =
+ elementsThatHaveBeenSetInFilter.find(elem) != elementsThatHaveBeenSetInFilter.end();
+ if (hasBeenSetInFilter) {
+ EXPECT_TRUE(existsInFilter) << "elem: " << elem;
+ }
+ if (!existsInFilter) {
+ EXPECT_FALSE(hasBeenSetInFilter) << "elem: " << elem;
+ }
+ }
+}
+
+} // namespace
+} // namespace latinime
diff --git a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java
index 57d295058..18390b2cd 100644
--- a/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java
+++ b/tests/src/com/android/inputmethod/keyboard/KeyboardLayoutSetSubtypesCountTests.java
@@ -25,8 +25,8 @@ import java.util.ArrayList;
@SmallTest
public class KeyboardLayoutSetSubtypesCountTests extends KeyboardLayoutSetTestsBase {
- private static final int NUMBER_OF_SUBTYPES = 68;
- private static final int NUMBER_OF_ASCII_CAPABLE_SUBTYPES = 43;
+ private static final int NUMBER_OF_SUBTYPES = 69;
+ private static final int NUMBER_OF_ASCII_CAPABLE_SUBTYPES = 44;
private static final int NUMBER_OF_PREDEFINED_ADDITIONAL_SUBTYPES = 2;
private static String toString(final ArrayList<InputMethodSubtype> subtypeList) {
diff --git a/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanish419.java b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanish419.java
new file mode 100644
index 000000000..75aad136f
--- /dev/null
+++ b/tests/src/com/android/inputmethod/keyboard/layout/tests/TestsSpanish419.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.inputmethod.keyboard.layout.tests;
+
+import android.test.suitebuilder.annotation.SmallTest;
+
+import com.android.inputmethod.keyboard.layout.LayoutBase;
+import com.android.inputmethod.keyboard.layout.Spanish;
+
+import java.util.Locale;
+
+/**
+ * es_419: Spanish (Latin America)/spanish
+ */
+@SmallTest
+public class TestsSpanish419 extends LayoutTestsBase {
+ private static final Locale LOCALE = new Locale("es", "419");
+ private static final LayoutBase LAYOUT = new Spanish(new SpanishCustomizer(LOCALE));
+
+ @Override
+ LayoutBase getLayout() { return LAYOUT; }
+}
diff --git a/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java b/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java
index e55c32bd0..2a4ead383 100644
--- a/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java
+++ b/tests/src/com/android/inputmethod/latin/utils/StringAndJsonUtilsTests.java
@@ -308,4 +308,68 @@ public class StringAndJsonUtilsTests extends AndroidTestCase {
assertEquals(objs[i], newObjArray.get(i));
}
}
+
+ public void testToCodePointArray() {
+ final String STR_WITH_SUPPLEMENTARY_CHAR = "abcde\uD861\uDED7fgh\u0000\u2002\u2003\u3000xx";
+ final int[] EXPECTED_RESULT = new int[] { 'a', 'b', 'c', 'd', 'e', 0x286D7, 'f', 'g', 'h',
+ 0, 0x2002, 0x2003, 0x3000, 'x', 'x'};
+ final int[] codePointArray = StringUtils.toCodePointArray(STR_WITH_SUPPLEMENTARY_CHAR, 0,
+ STR_WITH_SUPPLEMENTARY_CHAR.length());
+ assertEquals("toCodePointArray, size matches", codePointArray.length,
+ EXPECTED_RESULT.length);
+ for (int i = 0; i < EXPECTED_RESULT.length; ++i) {
+ assertEquals("toCodePointArray position " + i, codePointArray[i], EXPECTED_RESULT[i]);
+ }
+ }
+
+ public void testCopyCodePointsAndReturnCodePointCount() {
+ final String STR_WITH_SUPPLEMENTARY_CHAR = "AbcDE\uD861\uDED7fGh\u0000\u2002\u3000あx";
+ final int[] EXPECTED_RESULT = new int[] { 'A', 'b', 'c', 'D', 'E', 0x286D7,
+ 'f', 'G', 'h', 0, 0x2002, 0x3000, 'あ', 'x'};
+ final int[] EXPECTED_RESULT_DOWNCASE = new int[] { 'a', 'b', 'c', 'd', 'e', 0x286D7,
+ 'f', 'g', 'h', 0, 0x2002, 0x3000, 'あ', 'x'};
+
+ int[] codePointArray = new int[50];
+ int codePointCount = StringUtils.copyCodePointsAndReturnCodePointCount(codePointArray,
+ STR_WITH_SUPPLEMENTARY_CHAR, 0,
+ STR_WITH_SUPPLEMENTARY_CHAR.length(), false /* downCase */);
+ assertEquals("copyCodePointsAndReturnCodePointCount, size matches", codePointCount,
+ EXPECTED_RESULT.length);
+ for (int i = 0; i < codePointCount; ++i) {
+ assertEquals("copyCodePointsAndReturnCodePointCount position " + i, codePointArray[i],
+ EXPECTED_RESULT[i]);
+ }
+
+ codePointCount = StringUtils.copyCodePointsAndReturnCodePointCount(codePointArray,
+ STR_WITH_SUPPLEMENTARY_CHAR, 0,
+ STR_WITH_SUPPLEMENTARY_CHAR.length(), true /* downCase */);
+ assertEquals("copyCodePointsAndReturnCodePointCount downcase, size matches", codePointCount,
+ EXPECTED_RESULT_DOWNCASE.length);
+ for (int i = 0; i < codePointCount; ++i) {
+ assertEquals("copyCodePointsAndReturnCodePointCount position " + i, codePointArray[i],
+ EXPECTED_RESULT_DOWNCASE[i]);
+ }
+
+ final int JAVA_CHAR_COUNT = 8;
+ final int CODEPOINT_COUNT = 7;
+ codePointCount = StringUtils.copyCodePointsAndReturnCodePointCount(codePointArray,
+ STR_WITH_SUPPLEMENTARY_CHAR, 0, JAVA_CHAR_COUNT, false /* downCase */);
+ assertEquals("copyCodePointsAndReturnCodePointCount, size matches", codePointCount,
+ CODEPOINT_COUNT);
+ for (int i = 0; i < codePointCount; ++i) {
+ assertEquals("copyCodePointsAndReturnCodePointCount position " + i, codePointArray[i],
+ EXPECTED_RESULT[i]);
+ }
+
+ boolean exceptionHappened = false;
+ codePointArray = new int[5];
+ try {
+ codePointCount = StringUtils.copyCodePointsAndReturnCodePointCount(codePointArray,
+ STR_WITH_SUPPLEMENTARY_CHAR, 0, JAVA_CHAR_COUNT, false /* downCase */);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ exceptionHappened = true;
+ }
+ assertTrue("copyCodePointsAndReturnCodePointCount throws when array is too small",
+ exceptionHappened);
+ }
}
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-<locale>/)
- // 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<String, Locale> sLocaleCache = new HashMap<String, Locale>();
+ 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);
+ }
+ 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);
+ }
}
- 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]);
+ 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);
+ }
}
- if (retval != null) {
- sLocaleCache.put(localeStr, 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;
+ }
+ }
+
+ 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 retval;
}
+ return true;
}
- public static String getLocaleDisplayName(final String localeString) {
- if (localeString.equals(NO_LANGUAGE_LOCALE_CODE)) {
+ 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<String, Integer> 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<StringResource> mResources;
// Name to string resource map.