diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java index 5c288c8e7..144d55220 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java @@ -48,13 +48,10 @@ import java.util.Arrays; public final class KeySpecParser { private static final boolean DEBUG = LatinImeLogger.sDBG; - private static final int MAX_STRING_REFERENCE_INDIRECTION = 10; - // Constants for parsing. private static final char COMMA = ','; private static final char BACKSLASH = '\\'; private static final char VERTICAL_BAR = '|'; - private static final String PREFIX_TEXT = "!text/"; static final String PREFIX_ICON = "!icon/"; private static final String PREFIX_CODE = "!code/"; private static final String PREFIX_HEX = "0x"; @@ -361,68 +358,6 @@ public final class KeySpecParser { } } - public static String resolveTextReference(final String rawText, - final KeyboardTextsSet textsSet) { - if (TextUtils.isEmpty(rawText)) { - return null; - } - int level = 0; - String text = rawText; - StringBuilder sb; - do { - level++; - if (level >= MAX_STRING_REFERENCE_INDIRECTION) { - throw new RuntimeException("too many @string/resource indirection: " + text); - } - - final int prefixLen = PREFIX_TEXT.length(); - final int size = text.length(); - if (size < prefixLen) { - return TextUtils.isEmpty(text) ? null : text; - } - - sb = null; - for (int pos = 0; pos < size; pos++) { - final char c = text.charAt(pos); - if (text.startsWith(PREFIX_TEXT, pos) && textsSet != null) { - if (sb == null) { - sb = new StringBuilder(text.substring(0, pos)); - } - final int end = searchTextNameEnd(text, pos + prefixLen); - final String name = text.substring(pos + prefixLen, end); - sb.append(textsSet.getText(name)); - pos = end - 1; - } else if (c == BACKSLASH) { - if (sb != null) { - // Append both escape character and escaped character. - sb.append(text.substring(pos, Math.min(pos + 2, size))); - } - pos++; - } else if (sb != null) { - sb.append(c); - } - } - - if (sb != null) { - text = sb.toString(); - } - } while (sb != null); - return TextUtils.isEmpty(text) ? null : text; - } - - private static int searchTextNameEnd(final String text, final int start) { - final int size = text.length(); - for (int pos = start; pos < size; pos++) { - final char c = text.charAt(pos); - // Label name should be consisted of [a-zA-Z_0-9]. - if ((c >= 'a' && c <= 'z') || c == '_' || (c >= '0' && c <= '9')) { - continue; - } - return pos; - } - return size; - } - public static int getIntValue(final String[] moreKeys, final String key, final int defaultValue) { if (moreKeys == null) { diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyStyle.java b/java/src/com/android/inputmethod/keyboard/internal/KeyStyle.java index e6a674334..a2242b841 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyStyle.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyStyle.java @@ -32,14 +32,14 @@ public abstract class KeyStyle { protected String parseString(final TypedArray a, final int index) { if (a.hasValue(index)) { - return KeySpecParser.resolveTextReference(a.getString(index), mTextsSet); + return mTextsSet.resolveTextReference(a.getString(index)); } return null; } protected String[] parseStringArray(final TypedArray a, final int index) { if (a.hasValue(index)) { - final String text = KeySpecParser.resolveTextReference(a.getString(index), mTextsSet); + final String text = mTextsSet.resolveTextReference(a.getString(index)); return KeySpecParser.splitKeySpecs(text); } return null; diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java index 4e8eb3ea7..d953a6bb1 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.java @@ -18,6 +18,7 @@ package com.android.inputmethod.keyboard.internal; import android.content.Context; import android.content.res.Resources; +import android.text.TextUtils; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.utils.CollectionUtils; @@ -45,6 +46,10 @@ import java.util.HashMap; * KeyboardTextsSet.java */ public final class KeyboardTextsSet { + private static final String PREFIX_TEXT = "!text/"; + private static final char BACKSLASH = '\\'; + private static final int MAX_STRING_REFERENCE_INDIRECTION = 10; + // Language to texts map. private static final HashMap sLocaleToTextsMap = CollectionUtils.newHashMap(); private static final HashMap sNameToIdsMap = CollectionUtils.newHashMap(); @@ -87,6 +92,67 @@ public final class KeyboardTextsSet { return (text == null) ? LANGUAGE_DEFAULT[id] : text; } + private static int searchTextNameEnd(final String text, final int start) { + final int size = text.length(); + for (int pos = start; pos < size; pos++) { + final char c = text.charAt(pos); + // Label name should be consisted of [a-zA-Z_0-9]. + if ((c >= 'a' && c <= 'z') || c == '_' || (c >= '0' && c <= '9')) { + continue; + } + return pos; + } + return size; + } + + public String resolveTextReference(final String rawText) { + if (TextUtils.isEmpty(rawText)) { + return null; + } + int level = 0; + String text = rawText; + StringBuilder sb; + do { + level++; + if (level >= MAX_STRING_REFERENCE_INDIRECTION) { + throw new RuntimeException("too many @string/resource indirection: " + text); + } + + final int prefixLen = PREFIX_TEXT.length(); + final int size = text.length(); + if (size < prefixLen) { + return TextUtils.isEmpty(text) ? null : text; + } + + sb = null; + for (int pos = 0; pos < size; pos++) { + final char c = text.charAt(pos); + if (text.startsWith(PREFIX_TEXT, pos)) { + if (sb == null) { + sb = new StringBuilder(text.substring(0, pos)); + } + final int end = searchTextNameEnd(text, pos + prefixLen); + final String name = text.substring(pos + prefixLen, end); + sb.append(getText(name)); + pos = end - 1; + } else if (c == BACKSLASH) { + if (sb != null) { + // Append both escape character and escaped character. + sb.append(text.substring(pos, Math.min(pos + 2, size))); + } + pos++; + } else if (sb != null) { + sb.append(c); + } + } + + if (sb != null) { + text = sb.toString(); + } + } while (sb != null); + return TextUtils.isEmpty(text) ? null : text; + } + // These texts' name should be aligned with the @string/ in // values*/strings-action-keys.xml. private static final String[] RESOURCE_NAMES = { diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserSplitTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserSplitTests.java index cbe2d5960..7b7f1758c 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserSplitTests.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserSplitTests.java @@ -92,7 +92,7 @@ public class KeySpecParserSplitTests extends InstrumentationTestCase { private void assertTextArray(final String message, final String value, final String ... expectedArray) { - final String resolvedActual = KeySpecParser.resolveTextReference(value, mTextsSet); + final String resolvedActual = mTextsSet.resolveTextReference(value); final String[] actual = KeySpecParser.splitKeySpecs(resolvedActual); final String[] expected = (expectedArray.length == 0) ? null : expectedArray; assertArrayEquals(message, expected, actual); @@ -117,13 +117,11 @@ public class KeySpecParserSplitTests extends InstrumentationTestCase { private static final String SURROGATE2 = PAIR1 + PAIR2 + PAIR3; public void testResolveNullText() { - assertNull("resolve null", KeySpecParser.resolveTextReference( - null, mTextsSet)); + assertNull("resolve null", mTextsSet.resolveTextReference(null)); } public void testResolveEmptyText() { - assertNull("resolve empty text", KeySpecParser.resolveTextReference( - "!text/empty_string", mTextsSet)); + assertNull("resolve empty text", mTextsSet.resolveTextReference("!text/empty_string")); } public void testSplitZero() { diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java index afb2b0343..0388435eb 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserTests.java @@ -73,7 +73,7 @@ public class KeySpecParserTests extends AndroidTestCase { private void assertParser(String message, String moreKeySpec, String expectedLabel, String expectedOutputText, int expectedIcon, int expectedCode) { - final String labelResolved = KeySpecParser.resolveTextReference(moreKeySpec, mTextsSet); + final String labelResolved = mTextsSet.resolveTextReference(moreKeySpec); final MoreKeySpec spec = new MoreKeySpec(labelResolved, false /* needsToUpperCase */, Locale.US, mCodesSet); assertEquals(message + " [label]", expectedLabel, spec.mLabel); diff --git a/tools/make-keyboard-text/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl b/tools/make-keyboard-text/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl index a8ac981e0..5898bc50b 100644 --- a/tools/make-keyboard-text/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl +++ b/tools/make-keyboard-text/res/com/android/inputmethod/keyboard/internal/KeyboardTextsSet.tmpl @@ -18,6 +18,7 @@ package com.android.inputmethod.keyboard.internal; import android.content.Context; import android.content.res.Resources; +import android.text.TextUtils; import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.utils.CollectionUtils; @@ -45,6 +46,10 @@ import java.util.HashMap; * KeyboardTextsSet.java */ public final class KeyboardTextsSet { + private static final String PREFIX_TEXT = "!text/"; + private static final char BACKSLASH = '\\'; + private static final int MAX_STRING_REFERENCE_INDIRECTION = 10; + // Language to texts map. private static final HashMap sLocaleToTextsMap = CollectionUtils.newHashMap(); private static final HashMap sNameToIdsMap = CollectionUtils.newHashMap(); @@ -87,6 +92,67 @@ public final class KeyboardTextsSet { return (text == null) ? LANGUAGE_DEFAULT[id] : text; } + private static int searchTextNameEnd(final String text, final int start) { + final int size = text.length(); + for (int pos = start; pos < size; pos++) { + final char c = text.charAt(pos); + // Label name should be consisted of [a-zA-Z_0-9]. + if ((c >= 'a' && c <= 'z') || c == '_' || (c >= '0' && c <= '9')) { + continue; + } + return pos; + } + return size; + } + + public String resolveTextReference(final String rawText) { + if (TextUtils.isEmpty(rawText)) { + return null; + } + int level = 0; + String text = rawText; + StringBuilder sb; + do { + level++; + if (level >= MAX_STRING_REFERENCE_INDIRECTION) { + throw new RuntimeException("too many @string/resource indirection: " + text); + } + + final int prefixLen = PREFIX_TEXT.length(); + final int size = text.length(); + if (size < prefixLen) { + return TextUtils.isEmpty(text) ? null : text; + } + + sb = null; + for (int pos = 0; pos < size; pos++) { + final char c = text.charAt(pos); + if (text.startsWith(PREFIX_TEXT, pos)) { + if (sb == null) { + sb = new StringBuilder(text.substring(0, pos)); + } + final int end = searchTextNameEnd(text, pos + prefixLen); + final String name = text.substring(pos + prefixLen, end); + sb.append(getText(name)); + pos = end - 1; + } else if (c == BACKSLASH) { + if (sb != null) { + // Append both escape character and escaped character. + sb.append(text.substring(pos, Math.min(pos + 2, size))); + } + pos++; + } else if (sb != null) { + sb.append(c); + } + } + + if (sb != null) { + text = sb.toString(); + } + } while (sb != null); + return TextUtils.isEmpty(text) ? null : text; + } + // These texts' name should be aligned with the @string/ in // values*/strings-action-keys.xml. private static final String[] RESOURCE_NAMES = {