Merge "Refactor KeyboardTextsSet class"
commit
1e40af5ebe
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 = {
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 = {
|
||||||
|
|
Loading…
Reference in New Issue