From aca8870128caeec74ed4426f3c1e23ab60597453 Mon Sep 17 00:00:00 2001 From: "Tadashi G. Takaoka" Date: Fri, 3 Feb 2012 19:34:47 +0900 Subject: [PATCH] Recursively resolve @string/resource reference in key key spec parsing Change-Id: I9d172605e90e828e00f7c4c8d49548498aa3b50d --- .../keyboard/internal/KeySpecParser.java | 74 ++++++++++++------- tests/res/values/donottranslate.xml | 3 + .../internal/KeySpecParserCsvTests.java | 12 +++ 3 files changed, 61 insertions(+), 28 deletions(-) diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java index adb5f4759..e3c5da456 100644 --- a/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java +++ b/java/src/com/android/inputmethod/keyboard/internal/KeySpecParser.java @@ -43,6 +43,8 @@ import java.util.Arrays; public class KeySpecParser { private static final boolean DEBUG = LatinImeLogger.sDBG; + private static final int MAX_STRING_REFERENCE_INDIRECTION = 10; + // Constants for parsing. private static int COMMA = ','; private static final char ESCAPE_CHAR = '\\'; @@ -274,35 +276,51 @@ public class KeySpecParser { return resId; } - private static String resolveStringResource(String text, Resources res, int packageNameResId) { - final int size = text.length(); - if (size < PREFIX_STRING.length()) { - return text; - } - - StringBuilder sb = null; - for (int pos = 0; pos < size; pos++) { - final char c = text.charAt(pos); - if (c == PREFIX_AT && text.startsWith(PREFIX_STRING, pos)) { - if (sb == null) { - sb = new StringBuilder(text.substring(0, pos)); - } - final int end = searchResourceNameEnd(text, pos + PREFIX_STRING.length()); - final String resName = text.substring(pos + 1, end); - final int resId = getResourceId(res, resName, packageNameResId); - sb.append(res.getString(resId)); - pos = end - 1; - } else if (c == ESCAPE_CHAR) { - 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); + private static String resolveStringResource(String rawText, Resources res, + int packageNameResId) { + 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); } - } - return (sb == null) ? text : sb.toString(); + + final int size = text.length(); + if (size < PREFIX_STRING.length()) { + return text; + } + + sb = null; + for (int pos = 0; pos < size; pos++) { + final char c = text.charAt(pos); + if (c == PREFIX_AT && text.startsWith(PREFIX_STRING, pos)) { + if (sb == null) { + sb = new StringBuilder(text.substring(0, pos)); + } + final int end = searchResourceNameEnd(text, pos + PREFIX_STRING.length()); + final String resName = text.substring(pos + 1, end); + final int resId = getResourceId(res, resName, packageNameResId); + sb.append(res.getString(resId)); + pos = end - 1; + } else if (c == ESCAPE_CHAR) { + 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 text; } private static int searchResourceNameEnd(String text, int start) { diff --git a/tests/res/values/donottranslate.xml b/tests/res/values/donottranslate.xml index d0cde71a5..1ca4451d4 100644 --- a/tests/res/values/donottranslate.xml +++ b/tests/res/values/donottranslate.xml @@ -50,4 +50,7 @@ " \\abc , d\\ef , gh\\i " "ab\\\\,d\\\\\\,,g\\,i" " ab\\\\ , d\\\\\\, , g\\,i " + @string/multiple_chars + x,@string/multiple_chars,y + infinite,@string/infinite_indirection,loop diff --git a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java index a0ce86d1c..e090031e4 100644 --- a/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java +++ b/tests/src/com/android/inputmethod/keyboard/internal/KeySpecParserCsvTests.java @@ -288,4 +288,16 @@ public class KeySpecParserCsvTests extends AndroidTestCase { "abc@string/multiple_labels", "abcabc", "def", "ghi"); } + + public void testParseIndirectReference() { + assertTextArray("Indirect", + "@string/indirect_string", "a", "b", "c"); + assertTextArray("Indirect with literal", + "1,@string/indirect_string_with_literal,2", "1", "x", "a", "b", "c", "y", "2"); + } + + public void testParseInfiniteIndirectReference() { + assertError("Infinite indirection", + "1,@string/infinite_indirection,2", "1", "infinite", "", "loop", "2"); + } }