diff options
author | 2013-08-13 12:34:01 +0900 | |
---|---|---|
committer | 2013-08-13 15:33:14 +0900 | |
commit | ca6acfdd6b3400ad6e29d45c29b0ec40ea92a968 (patch) | |
tree | 4f412f403c7566a0f7b650299a92e4b4f64ea81b /tools/make-keyboard-text/src | |
parent | 0adc8a2ad3630aa01984b4b6ccb2b7ca94cf8948 (diff) | |
download | latinime-ca6acfdd6b3400ad6e29d45c29b0ec40ea92a968.tar.gz latinime-ca6acfdd6b3400ad6e29d45c29b0ec40ea92a968.tar.xz latinime-ca6acfdd6b3400ad6e29d45c29b0ec40ea92a968.zip |
Rename maketext tool to make-keyboard-text
Change-Id: Icceda22aec75f9e3602da8775c0e94b110283575
Diffstat (limited to 'tools/make-keyboard-text/src')
6 files changed, 667 insertions, 0 deletions
diff --git a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/ArrayInitializerFormatter.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/ArrayInitializerFormatter.java new file mode 100644 index 000000000..331003e67 --- /dev/null +++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/ArrayInitializerFormatter.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2012 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.tools; + +import java.io.PrintStream; + +public class ArrayInitializerFormatter { + private final PrintStream mOut; + private final int mMaxWidth; + private final String mIndent; + + private int mCurrentIndex = 0; + private String mFixedElement; + private final StringBuilder mBuffer = new StringBuilder(); + private int mBufferedLen; + private int mBufferedIndex = Integer.MIN_VALUE; + + public ArrayInitializerFormatter(PrintStream out, int width, String indent) { + mOut = out; + mMaxWidth = width - indent.length(); + mIndent = indent; + } + + public void flush() { + if (mBuffer.length() == 0) { + return; + } + final int lastIndex = mCurrentIndex - 1; + if (mBufferedIndex == lastIndex) { + mOut.format("%s/* %d */ %s\n", mIndent, mBufferedIndex, mBuffer); + } else if (mBufferedIndex == lastIndex - 1) { + final String[] elements = mBuffer.toString().split(" "); + mOut.format("%s/* %d */ %s\n" + + "%s/* %d */ %s\n", + mIndent, mBufferedIndex, elements[0], + mIndent, lastIndex, elements[1]); + } else { + mOut.format("%s/* %d~ */\n" + + "%s%s\n" + + "%s/* ~%d */\n", mIndent, mBufferedIndex, + mIndent, mBuffer, + mIndent, lastIndex); + } + mBuffer.setLength(0); + mBufferedLen = 0; + } + + public void outCommentLines(String lines) { + flush(); + mOut.print(lines); + mFixedElement = null; + } + + public void outElement(String element) { + if (!element.equals(mFixedElement)) { + flush(); + mBufferedIndex = mCurrentIndex; + } + final int nextLen = mBufferedLen + " ".length() + element.length(); + if (mBufferedLen != 0 && nextLen < mMaxWidth) { + mBuffer.append(' '); + mBuffer.append(element); + mBufferedLen = nextLen; + } else { + if (mBufferedLen != 0) { + mBuffer.append('\n'); + mBuffer.append(mIndent); + } + mBuffer.append(element); + mBufferedLen = element.length(); + } + mCurrentIndex++; + mFixedElement = element; + } +} diff --git a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/JarUtils.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/JarUtils.java new file mode 100644 index 000000000..a74096e79 --- /dev/null +++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/JarUtils.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2012 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.tools; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URL; +import java.net.URLDecoder; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +public final class JarUtils { + private JarUtils() { + // This utility class is not publicly instantiable. + } + + public static JarFile getJarFile(final Class<?> mainClass) { + final String mainClassPath = "/" + mainClass.getName().replace('.', '/') + ".class"; + final URL resUrl = mainClass.getResource(mainClassPath); + if (!resUrl.getProtocol().equals("jar")) { + throw new RuntimeException("Should run as jar"); + } + final String path = resUrl.getPath(); + if (!path.startsWith("file:")) { + throw new RuntimeException("Unknown jar path: " + path); + } + final String jarPath = path.substring("file:".length(), path.indexOf('!')); + try { + return new JarFile(URLDecoder.decode(jarPath, "UTF-8")); + } catch (UnsupportedEncodingException e) { + } catch (IOException e) { + } + return null; + } + + public static InputStream openResource(final String name) { + return JarUtils.class.getResourceAsStream("/" + name); + } + + public interface JarFilter { + public boolean accept(String dirName, String name); + } + + public static ArrayList<String> getNameListing(final JarFile jar, final JarFilter filter) { + final ArrayList<String> result = new ArrayList<String>(); + final Enumeration<JarEntry> entries = jar.entries(); + while (entries.hasMoreElements()) { + final JarEntry entry = entries.nextElement(); + final String path = entry.getName(); + final int pos = path.lastIndexOf('/'); + final String dirName = (pos >= 0) ? path.substring(0, pos) : ""; + final String name = (pos >= 0) ? path.substring(pos + 1) : path; + if (filter.accept(dirName, name)) { + result.add(path); + } + } + return result; + } + + public static ArrayList<String> getNameListing(final JarFile jar, final String filterName) { + return getNameListing(jar, new JarFilter() { + @Override + public boolean accept(final String dirName, final String name) { + return name.equals(filterName); + } + }); + } +} diff --git a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MakeKeyboardText.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MakeKeyboardText.java new file mode 100644 index 000000000..36a03f8dc --- /dev/null +++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MakeKeyboardText.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2012 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.tools; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.NoSuchElementException; +import java.util.jar.JarFile; + +public class MakeKeyboardText { + static class Options { + private static final String OPTION_JAVA = "-java"; + + public final String mJava; + + public static void usage(String message) { + if (message != null) { + System.err.println(message); + } + System.err.println("usage: make-keyboard-text " + OPTION_JAVA + " <java_output_dir>"); + System.exit(1); + } + + public Options(final String[] argsArray) { + final LinkedList<String> args = new LinkedList<String>(Arrays.asList(argsArray)); + String arg = null; + String java = null; + try { + while (!args.isEmpty()) { + arg = args.removeFirst(); + if (arg.equals(OPTION_JAVA)) { + java = args.removeFirst(); + } else { + usage("Unknown option: " + arg); + } + } + } catch (NoSuchElementException e) { + usage("Option " + arg + " needs argument"); + } + + mJava = java; + } + } + + public static void main(final String[] args) { + final Options options = new Options(args); + final JarFile jar = JarUtils.getJarFile(MakeKeyboardText.class); + final MoreKeysResources resources = new MoreKeysResources(jar); + resources.writeToJava(options.mJava); + } +} diff --git a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MoreKeysResources.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MoreKeysResources.java new file mode 100644 index 000000000..2643e01ec --- /dev/null +++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/MoreKeysResources.java @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2012 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.tools; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Locale; +import java.util.jar.JarFile; + +public class MoreKeysResources { + private static final String TEXT_RESOURCE_NAME = "donottranslate-more-keys.xml"; + + private static final String JAVA_TEMPLATE = "KeyboardTextsSet.tmpl"; + private static final String MARK_NAMES = "@NAMES@"; + private static final String MARK_DEFAULT_TEXTS = "@DEFAULT_TEXTS@"; + private static final String MARK_TEXTS = "@TEXTS@"; + private static final String MARK_LANGUAGES_AND_TEXTS = "@LANGUAGES_AND_TEXTS@"; + private static final String DEFAUT_LANGUAGE_NAME = "DEFAULT"; + private static final String ARRAY_NAME_FOR_LANGUAGE = "LANGUAGE_%s"; + private static final String EMPTY_STRING_VAR = "EMPTY"; + + private static final String NO_LANGUAGE_CODE = "zz"; + private static final String NO_LANGUAGE_DISPLAY_NAME = "Alphabet"; + + private final JarFile mJar; + // Language to string resources map. + private final HashMap<String, StringResourceMap> mResourcesMap = + new HashMap<String, StringResourceMap>(); + // Name to id map. + private final HashMap<String, Integer> mNameToIdMap = new HashMap<String,Integer>(); + + public MoreKeysResources(final JarFile jar) { + mJar = jar; + final ArrayList<String> resources = JarUtils.getNameListing(jar, TEXT_RESOURCE_NAME); + for (final String name : resources) { + final String dirName = name.substring(0, name.lastIndexOf('/')); + final int pos = dirName.lastIndexOf('/'); + final String parentName = (pos >= 0) ? dirName.substring(pos + 1) : dirName; + final String language = getLanguageFromResDir(parentName); + final InputStream stream = JarUtils.openResource(name); + try { + mResourcesMap.put(language, new StringResourceMap(stream)); + } finally { + close(stream); + } + } + } + + private static String getLanguageFromResDir(final String dirName) { + final int languagePos = dirName.indexOf('-'); + if (languagePos < 0) { + // Default resource. + return DEFAUT_LANGUAGE_NAME; + } + final String language = dirName.substring(languagePos + 1); + final int countryPos = language.indexOf("-r"); + if (countryPos < 0) { + return language; + } + return language.replace("-r", "_"); + } + + public void writeToJava(final String outDir) { + final ArrayList<String> list = JarUtils.getNameListing(mJar, JAVA_TEMPLATE); + if (list.isEmpty()) + throw new RuntimeException("Can't find java template " + JAVA_TEMPLATE); + if (list.size() > 1) + throw new RuntimeException("Found multiple java template " + JAVA_TEMPLATE); + final String template = list.get(0); + final String javaPackage = template.substring(0, template.lastIndexOf('/')); + PrintStream ps = null; + LineNumberReader lnr = null; + try { + if (outDir == null) { + ps = System.out; + } else { + final File outPackage = new File(outDir, javaPackage); + final File outputFile = new File(outPackage, + JAVA_TEMPLATE.replace(".tmpl", ".java")); + outPackage.mkdirs(); + ps = new PrintStream(outputFile, "UTF-8"); + } + lnr = new LineNumberReader(new InputStreamReader(JarUtils.openResource(template))); + inflateTemplate(lnr, ps); + } catch (IOException e) { + throw new RuntimeException(e); + } finally { + close(lnr); + close(ps); + } + } + + private void inflateTemplate(final LineNumberReader in, final PrintStream out) + throws IOException { + String line; + while ((line = in.readLine()) != null) { + if (line.contains(MARK_NAMES)) { + dumpNames(out); + } else if (line.contains(MARK_DEFAULT_TEXTS)) { + dumpDefaultTexts(out); + } else if (line.contains(MARK_TEXTS)) { + dumpTexts(out); + } else if (line.contains(MARK_LANGUAGES_AND_TEXTS)) { + dumpLanguageMap(out); + } else { + out.println(line); + } + } + } + + private void dumpNames(final PrintStream out) { + final StringResourceMap defaultResMap = mResourcesMap.get(DEFAUT_LANGUAGE_NAME); + int id = 0; + for (final StringResource res : defaultResMap.getResources()) { + out.format(" /* %2d */ \"%s\",\n", id, res.mName); + mNameToIdMap.put(res.mName, id); + id++; + } + } + + private void dumpDefaultTexts(final PrintStream out) { + final StringResourceMap defaultResMap = mResourcesMap.get(DEFAUT_LANGUAGE_NAME); + dumpTextsInternal(out, defaultResMap, defaultResMap); + } + + private void dumpTexts(final PrintStream out) { + final StringResourceMap defaultResMap = mResourcesMap.get(DEFAUT_LANGUAGE_NAME); + final ArrayList<String> allLanguages = new ArrayList<String>(); + allLanguages.addAll(mResourcesMap.keySet()); + Collections.sort(allLanguages); + for (final String language : allLanguages) { + if (language.equals(DEFAUT_LANGUAGE_NAME)) { + continue; + } + out.format(" /* Language %s: %s */\n", language, getLanguageDisplayName(language)); + out.format(" private static final String[] " + ARRAY_NAME_FOR_LANGUAGE + " = {\n", + language); + final StringResourceMap resMap = mResourcesMap.get(language); + for (final StringResource res : resMap.getResources()) { + if (!defaultResMap.contains(res.mName)) { + throw new RuntimeException(res.mName + " in " + language + + " doesn't have default resource"); + } + } + dumpTextsInternal(out, resMap, defaultResMap); + out.format(" };\n\n"); + } + } + + private void dumpLanguageMap(final PrintStream out) { + final ArrayList<String> allLanguages = new ArrayList<String>(); + allLanguages.addAll(mResourcesMap.keySet()); + Collections.sort(allLanguages); + for (final String language : allLanguages) { + out.format(" \"%s\", " + ARRAY_NAME_FOR_LANGUAGE + ", /* %s */\n", + language, language, getLanguageDisplayName(language)); + } + } + + private static String getLanguageDisplayName(final String language) { + if (language.equals(NO_LANGUAGE_CODE)) { + return NO_LANGUAGE_DISPLAY_NAME; + } else { + return new Locale(language).getDisplayLanguage(); + } + } + + private static void dumpTextsInternal(final PrintStream out, final StringResourceMap resMap, + final StringResourceMap defaultResMap) { + final ArrayInitializerFormatter formatter = + new ArrayInitializerFormatter(out, 100, " "); + boolean successiveNull = false; + for (final StringResource defaultRes : defaultResMap.getResources()) { + if (resMap.contains(defaultRes.mName)) { + final StringResource res = resMap.get(defaultRes.mName); + if (res.mComment != null) { + formatter.outCommentLines(addPrefix(" // ", res. mComment)); + } + final String escaped = escapeNonAscii(res.mValue); + if (escaped.length() == 0) { + formatter.outElement(EMPTY_STRING_VAR + ","); + } else { + formatter.outElement(String.format("\"%s\",", escaped)); + } + successiveNull = false; + } else { + formatter.outElement("null,"); + successiveNull = true; + } + } + if (!successiveNull) { + formatter.flush(); + } + } + + private static String addPrefix(final String prefix, final String lines) { + final StringBuilder sb = new StringBuilder(); + for (final String line : lines.split("\n")) { + sb.append(prefix + line.trim() + "\n"); + } + return sb.toString(); + } + + private static String escapeNonAscii(final String text) { + final StringBuilder sb = new StringBuilder(); + final int length = text.length(); + for (int i = 0; i < length; i++) { + final char c = text.charAt(i); + if (c >= ' ' && c < 0x7f) { + sb.append(c); + } else { + sb.append(String.format("\\u%04X", (int)c)); + } + } + return replaceIncompatibleEscape(sb.toString()); + } + + private static String replaceIncompatibleEscape(final String text) { + String t = text; + t = replaceAll(t, "\\?", "?"); + t = replaceAll(t, "\\@", "@"); + t = replaceAll(t, "@string/", "!text/"); + return t; + } + + private static String replaceAll(final String text, final String target, final String replace) { + String t = text; + while (t.indexOf(target) >= 0) { + t = t.replace(target, replace); + } + return t; + } + + private static void close(Closeable stream) { + try { + if (stream != null) { + stream.close(); + } + } catch (IOException e) { + } + } +} diff --git a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/StringResource.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/StringResource.java new file mode 100644 index 000000000..a49b8fe70 --- /dev/null +++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/StringResource.java @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2012 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.tools; + +public class StringResource { + public final String mName; + public final String mValue; + public final String mComment; + + public StringResource(final String name, final String value, final String comment) { + mName = name; + mValue = value; + mComment = comment; + } +} diff --git a/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/StringResourceMap.java b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/StringResourceMap.java new file mode 100644 index 000000000..cc7ff6a9c --- /dev/null +++ b/tools/make-keyboard-text/src/com/android/inputmethod/keyboard/tools/StringResourceMap.java @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2012 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.tools; + +import org.xml.sax.Attributes; +import org.xml.sax.SAXException; +import org.xml.sax.SAXParseException; +import org.xml.sax.ext.DefaultHandler2; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; + +public class StringResourceMap { + // String resource list. + private final List<StringResource> mResources; + // Name to string resource map. + private final Map<String, StringResource> mResourcesMap; + + public StringResourceMap(final InputStream is) { + final StringResourceHandler handler = new StringResourceHandler(); + final SAXParserFactory factory = SAXParserFactory.newInstance(); + factory.setNamespaceAware(true); + try { + final SAXParser parser = factory.newSAXParser(); + // In order to get comment tag. + parser.setProperty("http://xml.org/sax/properties/lexical-handler", handler); + parser.parse(is, handler); + } catch (ParserConfigurationException e) { + } catch (SAXParseException e) { + throw new RuntimeException(e.getMessage() + " at line " + e.getLineNumber() + + ", column " + e.getColumnNumber()); + } catch (SAXException e) { + throw new RuntimeException(e.getMessage()); + } catch (IOException e) { + } + + mResources = Collections.unmodifiableList(handler.mResources); + final HashMap<String,StringResource> map = new HashMap<String,StringResource>(); + for (final StringResource res : mResources) { + map.put(res.mName, res); + } + mResourcesMap = map; + } + + public List<StringResource> getResources() { + return mResources; + } + + public boolean contains(final String name) { + return mResourcesMap.containsKey(name); + } + + public StringResource get(final String name) { + return mResourcesMap.get(name); + } + + static class StringResourceHandler extends DefaultHandler2 { + private static final String TAG_RESOURCES = "resources"; + private static final String TAG_STRING = "string"; + private static final String ATTR_NAME = "name"; + + final ArrayList<StringResource> mResources = new ArrayList<StringResource>(); + + private String mName; + private final StringBuilder mValue = new StringBuilder(); + private final StringBuilder mComment = new StringBuilder(); + + private void init() { + mName = null; + mComment.setLength(0); + } + + @Override + public void comment(char[] ch, int start, int length) { + mComment.append(ch, start, length); + if (ch[start + length - 1] != '\n') { + mComment.append('\n'); + } + } + + @Override + public void startElement(String uri, String localName, String qName, Attributes attr) { + if (TAG_RESOURCES.equals(localName)) { + init(); + } else if (TAG_STRING.equals(localName)) { + mName = attr.getValue(ATTR_NAME); + mValue.setLength(0); + } + } + + @Override + public void characters(char[] ch, int start, int length) { + mValue.append(ch, start, length); + } + + @Override + public void endElement(String uri, String localName, String qName) throws SAXException { + if (TAG_STRING.equals(localName)) { + if (mName == null) + throw new SAXException(TAG_STRING + " doesn't have name"); + final String comment = mComment.length() > 0 ? mComment.toString() : null; + String value = mValue.toString(); + if (value.startsWith("\"") && value.endsWith("\"")) { + // Trim surroundings double quote. + value = value.substring(1, value.length() - 1); + } + mResources.add(new StringResource(mName, value, comment)); + init(); + } + } + } +} |