Refactor KeyboardTextsSet class

Change-Id: I0b48c85aa2c291e1e7ee25d61c558700fc677e29
main
Tadashi G. Takaoka 2014-01-31 15:08:49 +09:00
parent d245f6c9e2
commit 0bae2ab460
6 changed files with 138 additions and 73 deletions

View File

@ -48,13 +48,10 @@ import java.util.Arrays;
public final class KeySpecParser { public final class KeySpecParser {
private static final boolean DEBUG = LatinImeLogger.sDBG; private static final boolean DEBUG = LatinImeLogger.sDBG;
private static final int MAX_STRING_REFERENCE_INDIRECTION = 10;
// Constants for parsing. // Constants for parsing.
private static final char COMMA = ','; private static final char COMMA = ',';
private static final char BACKSLASH = '\\'; private static final char BACKSLASH = '\\';
private static final char VERTICAL_BAR = '|'; private static final char VERTICAL_BAR = '|';
private static final String PREFIX_TEXT = "!text/";
static final String PREFIX_ICON = "!icon/"; static final String PREFIX_ICON = "!icon/";
private static final String PREFIX_CODE = "!code/"; private static final String PREFIX_CODE = "!code/";
private static final String PREFIX_HEX = "0x"; 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, public static int getIntValue(final String[] moreKeys, final String key,
final int defaultValue) { final int defaultValue) {
if (moreKeys == null) { if (moreKeys == null) {

View File

@ -32,14 +32,14 @@ public abstract class KeyStyle {
protected String parseString(final TypedArray a, final int index) { protected String parseString(final TypedArray a, final int index) {
if (a.hasValue(index)) { if (a.hasValue(index)) {
return KeySpecParser.resolveTextReference(a.getString(index), mTextsSet); return mTextsSet.resolveTextReference(a.getString(index));
} }
return null; return null;
} }
protected String[] parseStringArray(final TypedArray a, final int index) { protected String[] parseStringArray(final TypedArray a, final int index) {
if (a.hasValue(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 KeySpecParser.splitKeySpecs(text);
} }
return null; return null;

View File

@ -18,6 +18,7 @@ package com.android.inputmethod.keyboard.internal;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.text.TextUtils;
import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.CollectionUtils;
@ -45,6 +46,10 @@ import java.util.HashMap;
* KeyboardTextsSet.java * KeyboardTextsSet.java
*/ */
public final class KeyboardTextsSet { 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. // Language to texts map.
private static final HashMap<String, String[]> sLocaleToTextsMap = CollectionUtils.newHashMap(); private static final HashMap<String, String[]> sLocaleToTextsMap = CollectionUtils.newHashMap();
private static final HashMap<String, Integer> sNameToIdsMap = CollectionUtils.newHashMap(); private static final HashMap<String, Integer> sNameToIdsMap = CollectionUtils.newHashMap();
@ -87,6 +92,67 @@ public final class KeyboardTextsSet {
return (text == null) ? LANGUAGE_DEFAULT[id] : text; 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/<name> in // These texts' name should be aligned with the @string/<name> in
// values*/strings-action-keys.xml. // values*/strings-action-keys.xml.
private static final String[] RESOURCE_NAMES = { private static final String[] RESOURCE_NAMES = {

View File

@ -92,7 +92,7 @@ public class KeySpecParserSplitTests extends InstrumentationTestCase {
private void assertTextArray(final String message, final String value, private void assertTextArray(final String message, final String value,
final String ... expectedArray) { final String ... expectedArray) {
final String resolvedActual = KeySpecParser.resolveTextReference(value, mTextsSet); final String resolvedActual = mTextsSet.resolveTextReference(value);
final String[] actual = KeySpecParser.splitKeySpecs(resolvedActual); final String[] actual = KeySpecParser.splitKeySpecs(resolvedActual);
final String[] expected = (expectedArray.length == 0) ? null : expectedArray; final String[] expected = (expectedArray.length == 0) ? null : expectedArray;
assertArrayEquals(message, expected, actual); assertArrayEquals(message, expected, actual);
@ -117,13 +117,11 @@ public class KeySpecParserSplitTests extends InstrumentationTestCase {
private static final String SURROGATE2 = PAIR1 + PAIR2 + PAIR3; private static final String SURROGATE2 = PAIR1 + PAIR2 + PAIR3;
public void testResolveNullText() { public void testResolveNullText() {
assertNull("resolve null", KeySpecParser.resolveTextReference( assertNull("resolve null", mTextsSet.resolveTextReference(null));
null, mTextsSet));
} }
public void testResolveEmptyText() { public void testResolveEmptyText() {
assertNull("resolve empty text", KeySpecParser.resolveTextReference( assertNull("resolve empty text", mTextsSet.resolveTextReference("!text/empty_string"));
"!text/empty_string", mTextsSet));
} }
public void testSplitZero() { public void testSplitZero() {

View File

@ -73,7 +73,7 @@ public class KeySpecParserTests extends AndroidTestCase {
private void assertParser(String message, String moreKeySpec, String expectedLabel, private void assertParser(String message, String moreKeySpec, String expectedLabel,
String expectedOutputText, int expectedIcon, int expectedCode) { 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 */, final MoreKeySpec spec = new MoreKeySpec(labelResolved, false /* needsToUpperCase */,
Locale.US, mCodesSet); Locale.US, mCodesSet);
assertEquals(message + " [label]", expectedLabel, spec.mLabel); assertEquals(message + " [label]", expectedLabel, spec.mLabel);

View File

@ -18,6 +18,7 @@ package com.android.inputmethod.keyboard.internal;
import android.content.Context; import android.content.Context;
import android.content.res.Resources; import android.content.res.Resources;
import android.text.TextUtils;
import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.CollectionUtils;
@ -45,6 +46,10 @@ import java.util.HashMap;
* KeyboardTextsSet.java * KeyboardTextsSet.java
*/ */
public final class KeyboardTextsSet { 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. // Language to texts map.
private static final HashMap<String, String[]> sLocaleToTextsMap = CollectionUtils.newHashMap(); private static final HashMap<String, String[]> sLocaleToTextsMap = CollectionUtils.newHashMap();
private static final HashMap<String, Integer> sNameToIdsMap = CollectionUtils.newHashMap(); private static final HashMap<String, Integer> sNameToIdsMap = CollectionUtils.newHashMap();
@ -87,6 +92,67 @@ public final class KeyboardTextsSet {
return (text == null) ? LANGUAGE_DEFAULT[id] : text; 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/<name> in // These texts' name should be aligned with the @string/<name> in
// values*/strings-action-keys.xml. // values*/strings-action-keys.xml.
private static final String[] RESOURCE_NAMES = { private static final String[] RESOURCE_NAMES = {