aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod/keyboard/internal/PopupCharactersParser.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com/android/inputmethod/keyboard/internal/PopupCharactersParser.java')
-rw-r--r--java/src/com/android/inputmethod/keyboard/internal/PopupCharactersParser.java177
1 files changed, 177 insertions, 0 deletions
diff --git a/java/src/com/android/inputmethod/keyboard/internal/PopupCharactersParser.java b/java/src/com/android/inputmethod/keyboard/internal/PopupCharactersParser.java
new file mode 100644
index 000000000..3b9a08322
--- /dev/null
+++ b/java/src/com/android/inputmethod/keyboard/internal/PopupCharactersParser.java
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2010 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.internal;
+
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.latin.R;
+
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils;
+
+/**
+ * String parser of popupCharacters attribute of Key.
+ * The string is comma separated texts each of which represents one popup key.
+ * Each popup key text is one of the following:
+ * - A single letter (Letter)
+ * - Label optionally followed by keyOutputText or code (keyLabel|keyOutputText).
+ * - Icon followed by keyOutputText or code (@drawable/icon|@integer/key_code)
+ * Special character, comma ',' backslash '\', and bar '|' can be escaped by '\'
+ * character.
+ * Note that the character '@' and '\' are also parsed by XML parser and CSV parser as well.
+ */
+public class PopupCharactersParser {
+ private static final char ESCAPE = '\\';
+ private static final String LABEL_END = "|";
+ private static final String PREFIX_AT = "@";
+ private static final String PREFIX_ICON = PREFIX_AT + "drawable/";
+ private static final String PREFIX_CODE = PREFIX_AT + "integer/";
+
+ private PopupCharactersParser() {
+ // Intentional empty constructor for utility class.
+ }
+
+ private static boolean hasIcon(String popupSpec) {
+ if (popupSpec.startsWith(PREFIX_ICON)) {
+ final int end = indexOfLabelEnd(popupSpec, 0);
+ if (end > 0)
+ return true;
+ throw new PopupCharactersParserError("outputText or code not specified: " + popupSpec);
+ }
+ return false;
+ }
+
+ private static boolean hasCode(String popupSpec) {
+ final int end = indexOfLabelEnd(popupSpec, 0);
+ if (end > 0 && end + 1 < popupSpec.length()
+ && popupSpec.substring(end + 1).startsWith(PREFIX_CODE)) {
+ return true;
+ }
+ return false;
+ }
+
+ private static String parseEscape(String text) {
+ if (text.indexOf(ESCAPE) < 0)
+ return text;
+ final int length = text.length();
+ final StringBuilder sb = new StringBuilder();
+ for (int pos = 0; pos < length; pos++) {
+ final char c = text.charAt(pos);
+ if (c == ESCAPE && pos + 1 < length) {
+ sb.append(text.charAt(++pos));
+ } else {
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ }
+
+ private static int indexOfLabelEnd(String popupSpec, int start) {
+ if (popupSpec.indexOf(ESCAPE, start) < 0) {
+ final int end = popupSpec.indexOf(LABEL_END, start);
+ if (end == 0)
+ throw new PopupCharactersParserError(LABEL_END + " at " + start + ": " + popupSpec);
+ return end;
+ }
+ final int length = popupSpec.length();
+ for (int pos = start; pos < length; pos++) {
+ final char c = popupSpec.charAt(pos);
+ if (c == ESCAPE && pos + 1 < length) {
+ pos++;
+ } else if (popupSpec.startsWith(LABEL_END, pos)) {
+ return pos;
+ }
+ }
+ return -1;
+ }
+
+ public static String getLabel(String popupSpec) {
+ if (hasIcon(popupSpec))
+ return null;
+ final int end = indexOfLabelEnd(popupSpec, 0);
+ final String label = (end > 0) ? parseEscape(popupSpec.substring(0, end))
+ : parseEscape(popupSpec);
+ if (TextUtils.isEmpty(label))
+ throw new PopupCharactersParserError("Empty label: " + popupSpec);
+ return label;
+ }
+
+ public static String getOutputText(String popupSpec) {
+ if (hasCode(popupSpec))
+ return null;
+ final int end = indexOfLabelEnd(popupSpec, 0);
+ if (end > 0) {
+ if (indexOfLabelEnd(popupSpec, end + 1) >= 0)
+ throw new PopupCharactersParserError("Multiple " + LABEL_END + ": "
+ + popupSpec);
+ final String outputText = parseEscape(popupSpec.substring(end + LABEL_END.length()));
+ if (!TextUtils.isEmpty(outputText))
+ return outputText;
+ throw new PopupCharactersParserError("Empty outputText: " + popupSpec);
+ }
+ final String label = getLabel(popupSpec);
+ if (label == null)
+ throw new PopupCharactersParserError("Empty label: " + popupSpec);
+ // Code is automatically generated for one letter label. See {@link getCode()}.
+ if (label.length() == 1)
+ return null;
+ return label;
+ }
+
+ public static int getCode(Resources res, String popupSpec) {
+ if (hasCode(popupSpec)) {
+ final int end = indexOfLabelEnd(popupSpec, 0);
+ if (indexOfLabelEnd(popupSpec, end + 1) >= 0)
+ throw new PopupCharactersParserError("Multiple " + LABEL_END + ": " + popupSpec);
+ final int resId = getResourceId(res,
+ popupSpec.substring(end + LABEL_END.length() + PREFIX_AT.length()));
+ final int code = res.getInteger(resId);
+ return code;
+ }
+ if (indexOfLabelEnd(popupSpec, 0) > 0)
+ return Keyboard.CODE_DUMMY;
+ final String label = getLabel(popupSpec);
+ // Code is automatically generated for one letter label.
+ if (label != null && label.length() == 1)
+ return label.charAt(0);
+ return Keyboard.CODE_DUMMY;
+ }
+
+ public static Drawable getIcon(Resources res, String popupSpec) {
+ if (hasIcon(popupSpec)) {
+ int end = popupSpec.indexOf(LABEL_END, PREFIX_ICON.length() + 1);
+ int resId = getResourceId(res, popupSpec.substring(PREFIX_AT.length(), end));
+ return res.getDrawable(resId);
+ }
+ return null;
+ }
+
+ private static int getResourceId(Resources res, String name) {
+ String packageName = res.getResourcePackageName(R.string.english_ime_name);
+ int resId = res.getIdentifier(name, null, packageName);
+ if (resId == 0)
+ throw new PopupCharactersParserError("Unknown resource: " + name);
+ return resId;
+ }
+
+ @SuppressWarnings("serial")
+ public static class PopupCharactersParserError extends RuntimeException {
+ public PopupCharactersParserError(String message) {
+ super(message);
+ }
+ }
+}