• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.inputmethod.latin.maketext;
18 
19 import java.io.Closeable;
20 import java.io.File;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.InputStreamReader;
24 import java.io.LineNumberReader;
25 import java.io.PrintStream;
26 import java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.Locale;
30 import java.util.jar.JarFile;
31 
32 public class MoreKeysResources {
33     private static final String TEXT_RESOURCE_NAME = "donottranslate-more-keys.xml";
34 
35     private static final String JAVA_TEMPLATE = "KeyboardTextsSet.tmpl";
36     private static final String MARK_NAMES = "@NAMES@";
37     private static final String MARK_DEFAULT_TEXTS = "@DEFAULT_TEXTS@";
38     private static final String MARK_TEXTS = "@TEXTS@";
39     private static final String MARK_LANGUAGES_AND_TEXTS = "@LANGUAGES_AND_TEXTS@";
40     private static final String DEFAUT_LANGUAGE_NAME = "DEFAULT";
41     private static final String ARRAY_NAME_FOR_LANGUAGE = "LANGUAGE_%s";
42     private static final String EMPTY_STRING_VAR = "EMPTY";
43 
44     private static final String NO_LANGUAGE_CODE = "zz";
45     private static final String NO_LANGUAGE_DISPLAY_NAME = "No language";
46 
47     private final JarFile mJar;
48     // Language to string resources map.
49     private final HashMap<String, StringResourceMap> mResourcesMap =
50             new HashMap<String, StringResourceMap>();
51     // Name to id map.
52     private final HashMap<String, Integer> mNameToIdMap = new HashMap<String,Integer>();
53 
MoreKeysResources(final JarFile jar)54     public MoreKeysResources(final JarFile jar) {
55         mJar = jar;
56         final ArrayList<String> resources = JarUtils.getNameListing(jar, TEXT_RESOURCE_NAME);
57         for (final String name : resources) {
58             final String dirName = name.substring(0, name.lastIndexOf('/'));
59             final int pos = dirName.lastIndexOf('/');
60             final String parentName = (pos >= 0) ? dirName.substring(pos + 1) : dirName;
61             final String language = getLanguageFromResDir(parentName);
62             final InputStream stream = JarUtils.openResource(name);
63             try {
64                 mResourcesMap.put(language, new StringResourceMap(stream));
65             } finally {
66                 close(stream);
67             }
68         }
69     }
70 
getLanguageFromResDir(final String dirName)71     private static String getLanguageFromResDir(final String dirName) {
72         final int languagePos = dirName.indexOf('-');
73         if (languagePos < 0) {
74             // Default resource.
75             return DEFAUT_LANGUAGE_NAME;
76         }
77         final String language = dirName.substring(languagePos + 1);
78         final int countryPos = language.indexOf("-r");
79         if (countryPos < 0) {
80             return language;
81         }
82         return language.replace("-r", "_");
83     }
84 
writeToJava(final String outDir)85     public void writeToJava(final String outDir) {
86         final ArrayList<String> list = JarUtils.getNameListing(mJar, JAVA_TEMPLATE);
87         if (list.isEmpty())
88             throw new RuntimeException("Can't find java template " + JAVA_TEMPLATE);
89         if (list.size() > 1)
90             throw new RuntimeException("Found multiple java template " + JAVA_TEMPLATE);
91         final String template = list.get(0);
92         final String javaPackage = template.substring(0, template.lastIndexOf('/'));
93         PrintStream ps = null;
94         LineNumberReader lnr = null;
95         try {
96             if (outDir == null) {
97                 ps = System.out;
98             } else {
99                 final File outPackage = new File(outDir, javaPackage);
100                 final File outputFile = new File(outPackage,
101                         JAVA_TEMPLATE.replace(".tmpl", ".java"));
102                 outPackage.mkdirs();
103                 ps = new PrintStream(outputFile, "UTF-8");
104             }
105             lnr = new LineNumberReader(new InputStreamReader(JarUtils.openResource(template)));
106             inflateTemplate(lnr, ps);
107         } catch (IOException e) {
108             throw new RuntimeException(e);
109         } finally {
110             close(lnr);
111             close(ps);
112         }
113     }
114 
inflateTemplate(final LineNumberReader in, final PrintStream out)115     private void inflateTemplate(final LineNumberReader in, final PrintStream out)
116             throws IOException {
117         String line;
118         while ((line = in.readLine()) != null) {
119             if (line.contains(MARK_NAMES)) {
120                 dumpNames(out);
121             } else if (line.contains(MARK_DEFAULT_TEXTS)) {
122                 dumpDefaultTexts(out);
123             } else if (line.contains(MARK_TEXTS)) {
124                 dumpTexts(out);
125             } else if (line.contains(MARK_LANGUAGES_AND_TEXTS)) {
126                 dumpLanguageMap(out);
127             } else {
128                 out.println(line);
129             }
130         }
131     }
132 
dumpNames(final PrintStream out)133     private void dumpNames(final PrintStream out) {
134         final StringResourceMap defaultResMap = mResourcesMap.get(DEFAUT_LANGUAGE_NAME);
135         int id = 0;
136         for (final StringResource res : defaultResMap.getResources()) {
137             out.format("        /* %2d */ \"%s\",\n", id, res.mName);
138             mNameToIdMap.put(res.mName, id);
139             id++;
140         }
141     }
142 
dumpDefaultTexts(final PrintStream out)143     private void dumpDefaultTexts(final PrintStream out) {
144         final StringResourceMap defaultResMap = mResourcesMap.get(DEFAUT_LANGUAGE_NAME);
145         dumpTextsInternal(out, defaultResMap, defaultResMap);
146     }
147 
dumpTexts(final PrintStream out)148     private void dumpTexts(final PrintStream out) {
149         final StringResourceMap defaultResMap = mResourcesMap.get(DEFAUT_LANGUAGE_NAME);
150         final ArrayList<String> allLanguages = new ArrayList<String>();
151         allLanguages.addAll(mResourcesMap.keySet());
152         Collections.sort(allLanguages);
153         for (final String language : allLanguages) {
154             if (language.equals(DEFAUT_LANGUAGE_NAME)) {
155                 continue;
156             }
157             out.format("    /* Language %s: %s */\n", language, getLanguageDisplayName(language));
158             out.format("    private static final String[] " + ARRAY_NAME_FOR_LANGUAGE + " = {\n",
159                     language);
160             final StringResourceMap resMap = mResourcesMap.get(language);
161             for (final StringResource res : resMap.getResources()) {
162                 if (!defaultResMap.contains(res.mName)) {
163                     throw new RuntimeException(res.mName + " in " + language
164                             + " doesn't have default resource");
165                 }
166             }
167             dumpTextsInternal(out, resMap, defaultResMap);
168             out.format("    };\n\n");
169         }
170     }
171 
dumpLanguageMap(final PrintStream out)172     private void dumpLanguageMap(final PrintStream out) {
173         final ArrayList<String> allLanguages = new ArrayList<String>();
174         allLanguages.addAll(mResourcesMap.keySet());
175         Collections.sort(allLanguages);
176         for (final String language : allLanguages) {
177             out.format("        \"%s\", " + ARRAY_NAME_FOR_LANGUAGE + ", /* %s */\n",
178                     language, language, getLanguageDisplayName(language));
179         }
180     }
181 
getLanguageDisplayName(final String language)182     private static String getLanguageDisplayName(final String language) {
183         if (language.equals(NO_LANGUAGE_CODE)) {
184             return NO_LANGUAGE_DISPLAY_NAME;
185         } else {
186             return new Locale(language).getDisplayLanguage();
187         }
188     }
189 
dumpTextsInternal(final PrintStream out, final StringResourceMap resMap, final StringResourceMap defaultResMap)190     private static void dumpTextsInternal(final PrintStream out, final StringResourceMap resMap,
191             final StringResourceMap defaultResMap) {
192         final ArrayInitializerFormatter formatter =
193                 new ArrayInitializerFormatter(out, 100, "        ");
194         boolean successiveNull = false;
195         for (final StringResource defaultRes : defaultResMap.getResources()) {
196             if (resMap.contains(defaultRes.mName)) {
197                 final StringResource res = resMap.get(defaultRes.mName);
198                 if (res.mComment != null) {
199                     formatter.outCommentLines(addPrefix("        // ", res. mComment));
200                 }
201                 final String escaped = escapeNonAscii(res.mValue);
202                 if (escaped.length() == 0) {
203                     formatter.outElement(EMPTY_STRING_VAR + ",");
204                 } else {
205                     formatter.outElement(String.format("\"%s\",", escaped));
206                 }
207                 successiveNull = false;
208             } else {
209                 formatter.outElement("null,");
210                 successiveNull = true;
211             }
212         }
213         if (!successiveNull) {
214             formatter.flush();
215         }
216     }
217 
addPrefix(final String prefix, final String lines)218     private static String addPrefix(final String prefix, final String lines) {
219         final StringBuilder sb = new StringBuilder();
220         for (final String line : lines.split("\n")) {
221             sb.append(prefix + line.trim() + "\n");
222         }
223         return sb.toString();
224     }
225 
escapeNonAscii(final String text)226     private static String escapeNonAscii(final String text) {
227         final StringBuilder sb = new StringBuilder();
228         final int length = text.length();
229         for (int i = 0; i < length; i++) {
230             final char c = text.charAt(i);
231             if (c >= ' ' && c < 0x7f) {
232                 sb.append(c);
233             } else {
234                 sb.append(String.format("\\u%04X", (int)c));
235             }
236         }
237         return replaceIncompatibleEscape(sb.toString());
238     }
239 
replaceIncompatibleEscape(final String text)240     private static String replaceIncompatibleEscape(final String text) {
241         String t = text;
242         t = replaceAll(t, "\\?", "?");
243         t = replaceAll(t, "\\@", "@");
244         t = replaceAll(t, "@string/", "!text/");
245         return t;
246     }
247 
replaceAll(final String text, final String target, final String replace)248     private static String replaceAll(final String text, final String target, final String replace) {
249         String t = text;
250         while (t.indexOf(target) >= 0) {
251             t = t.replace(target, replace);
252         }
253         return t;
254     }
255 
close(Closeable stream)256     private static void close(Closeable stream) {
257         try {
258             if (stream != null) {
259                 stream.close();
260             }
261         } catch (IOException e) {
262         }
263     }
264 }
265