blob: 2643e01ecd355593a73d087d7e565a03efc438c4 [file] [log] [blame]
/*
* 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) {
}
}
}