Merge "Optimize KeyboardTextsTable"

This commit is contained in:
Tadashi G. Takaoka 2014-02-13 08:22:58 +00:00 committed by Android (Google) Code Review
commit 0af6d988a7
4 changed files with 2107 additions and 2002 deletions

View File

@ -63,6 +63,7 @@ public final class KeyboardTextsTable {
} }
private static final String[] NAMES = { private static final String[] NAMES = {
// /* index:histogram */ "name",
/* @NAMES@ */ /* @NAMES@ */
}; };

View File

@ -22,17 +22,22 @@ public class ArrayInitializerFormatter {
private final PrintStream mOut; private final PrintStream mOut;
private final int mMaxWidth; private final int mMaxWidth;
private final String mIndent; private final String mIndent;
// String resource names array; indexed by {@link #CurrentIndex} and
// {@link #mStartIndexOfBuffer}.
private final String[] mResourceNames;
private int mCurrentIndex = 0; private int mCurrentIndex = 0;
private String mFixedElement; private String mLastElement;
private final StringBuilder mBuffer = new StringBuilder(); private final StringBuilder mBuffer = new StringBuilder();
private int mBufferedLen; private int mBufferedLen;
private int mBufferedIndex = Integer.MIN_VALUE; private int mStartIndexOfBuffer = Integer.MIN_VALUE;
public ArrayInitializerFormatter(final PrintStream out, final int width, final String indent) { public ArrayInitializerFormatter(final PrintStream out, final int width, final String indent,
final String[] resourceNames) {
mOut = out; mOut = out;
mMaxWidth = width - indent.length(); mMaxWidth = width - indent.length();
mIndent = indent; mIndent = indent;
mResourceNames = resourceNames;
} }
public int getCurrentIndex() { public int getCurrentIndex() {
@ -44,20 +49,24 @@ public class ArrayInitializerFormatter {
return; return;
} }
final int lastIndex = mCurrentIndex - 1; final int lastIndex = mCurrentIndex - 1;
if (mBufferedIndex == lastIndex) { if (mStartIndexOfBuffer == lastIndex) {
mOut.format("%s/* %d */ %s\n", mIndent, mBufferedIndex, mBuffer); mOut.format("%s/* %s */ %s\n",
} else if (mBufferedIndex == lastIndex - 1) { mIndent, mResourceNames[mStartIndexOfBuffer], mBuffer);
final String[] elements = mBuffer.toString().split(" "); } else if (mStartIndexOfBuffer == lastIndex - 1) {
mOut.format("%s/* %d */ %s\n" final String startElement = mBuffer.toString()
+ "%s/* %d */ %s\n", .substring(0, mBuffer.length() - mLastElement.length())
mIndent, mBufferedIndex, elements[0], .trim();
mIndent, lastIndex, elements[1]); mOut.format("%s/* %s */ %s\n"
+ "%s/* %s */ %s\n",
mIndent, mResourceNames[mStartIndexOfBuffer], startElement,
mIndent, mResourceNames[lastIndex], mLastElement);
} else { } else {
mOut.format("%s/* %d~ */\n" mOut.format("%s/* %s ~ */\n"
+ "%s%s\n" + "%s%s\n"
+ "%s/* ~%d */\n", mIndent, mBufferedIndex, + "%s/* ~ %s */\n",
mIndent, mResourceNames[mStartIndexOfBuffer],
mIndent, mBuffer, mIndent, mBuffer,
mIndent, lastIndex); mIndent, mResourceNames[lastIndex]);
} }
mBuffer.setLength(0); mBuffer.setLength(0);
mBufferedLen = 0; mBufferedLen = 0;
@ -66,20 +75,22 @@ public class ArrayInitializerFormatter {
public void outCommentLines(final String lines) { public void outCommentLines(final String lines) {
flush(); flush();
mOut.print(lines); mOut.print(lines);
mFixedElement = null; mLastElement = null;
} }
public void outElement(final String element) { public void outElement(final String element) {
if (!element.equals(mFixedElement)) { if (!element.equals(mLastElement)) {
flush(); flush();
mBufferedIndex = mCurrentIndex; mStartIndexOfBuffer = mCurrentIndex;
} }
final int nextLen = mBufferedLen + " ".length() + element.length(); final int nextLen = mBufferedLen + " ".length() + element.length();
if (mBufferedLen != 0 && nextLen < mMaxWidth) { if (mBufferedLen != 0 && nextLen < mMaxWidth) {
// Element can fit in the current line.
mBuffer.append(' '); mBuffer.append(' ');
mBuffer.append(element); mBuffer.append(element);
mBufferedLen = nextLen; mBufferedLen = nextLen;
} else { } else {
// Element should be on the next line.
if (mBufferedLen != 0) { if (mBufferedLen != 0) {
mBuffer.append('\n'); mBuffer.append('\n');
mBuffer.append(mIndent); mBuffer.append(mIndent);
@ -88,6 +99,6 @@ public class ArrayInitializerFormatter {
mBufferedLen = element.length(); mBufferedLen = element.length();
} }
mCurrentIndex++; mCurrentIndex++;
mFixedElement = element; mLastElement = element;
} }
} }

View File

@ -25,6 +25,7 @@ import java.io.LineNumberReader;
import java.io.PrintStream; import java.io.PrintStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.Locale; import java.util.Locale;
import java.util.jar.JarFile; import java.util.jar.JarFile;
@ -47,8 +48,19 @@ public class MoreKeysResources {
// Language to string resources map. // Language to string resources map.
private final HashMap<String, StringResourceMap> mResourcesMap = private final HashMap<String, StringResourceMap> mResourcesMap =
new HashMap<String, StringResourceMap>(); new HashMap<String, StringResourceMap>();
// Name to id map. // Sorted languages list. The language is taken from string resource directories
private final HashMap<String, Integer> mNameToIdMap = new HashMap<String,Integer>(); // (values-<language>/) or {@link #DEFAULT_LANGUAGE_NAME} for the default string resource
// directory (values/).
private final ArrayList<String> mSortedLanguagesList = new ArrayList<String>();
// Default string resources map.
private final StringResourceMap mDefaultResourceMap;
// Histogram of string resource names. This is used to sort {@link #mSortedResourceNames}.
private final HashMap<String, Integer> mNameHistogram = new HashMap<String, Integer>();
// Sorted string resource names array; Descending order of histogram count.
// The string resource name is specified as an attribute "name" in string resource files.
// The string resource can be accessed by specifying name "!text/<name>"
// via {@link KeyboardTextsSet#getText(String)}.
private final String[] mSortedResourceNames;
public MoreKeysResources(final JarFile jar) { public MoreKeysResources(final JarFile jar) {
mJar = jar; mJar = jar;
@ -65,6 +77,45 @@ public class MoreKeysResources {
close(stream); close(stream);
} }
} }
mDefaultResourceMap = mResourcesMap.get(DEFAULT_LANGUAGE_NAME);
mSortedLanguagesList.addAll(mResourcesMap.keySet());
Collections.sort(mSortedLanguagesList);
// Initialize name histogram and names list.
final HashMap<String, Integer> nameHistogram = mNameHistogram;
final ArrayList<String> resourceNamesList = new ArrayList<String>();
for (final StringResource res : mDefaultResourceMap.getResources()) {
nameHistogram.put(res.mName, 0); // Initialize histogram value.
resourceNamesList.add(res.mName);
}
// Make name histogram.
for (final String language : mResourcesMap.keySet()) {
final StringResourceMap resMap = mResourcesMap.get(language);
if (resMap == mDefaultResourceMap) continue;
for (final StringResource res : resMap.getResources()) {
if (!mDefaultResourceMap.contains(res.mName)) {
throw new RuntimeException(res.mName + " in " + language
+ " doesn't have default resource");
}
final int histogramValue = nameHistogram.get(res.mName);
nameHistogram.put(res.mName, histogramValue + 1);
}
}
// Sort names list.
Collections.sort(resourceNamesList, new Comparator<String>() {
@Override
public int compare(final String leftName, final String rightName) {
final int leftCount = nameHistogram.get(leftName);
final int rightCount = nameHistogram.get(rightName);
// Descending order of histogram count.
if (leftCount > rightCount) return -1;
if (leftCount < rightCount) return 1;
// TODO: Add further criteria to order the same histogram value names to be able to
// minimize footprints of string resources arrays.
return 0;
}
});
mSortedResourceNames = resourceNamesList.toArray(new String[resourceNamesList.size()]);
} }
private static String getLanguageFromResDir(final String dirName) { private static String getLanguageFromResDir(final String dirName) {
@ -132,19 +183,17 @@ public class MoreKeysResources {
} }
private void dumpNames(final PrintStream out) { private void dumpNames(final PrintStream out) {
final StringResourceMap defaultResMap = mResourcesMap.get(DEFAULT_LANGUAGE_NAME); final int namesCount = mSortedResourceNames.length;
int id = 0; for (int index = 0; index < namesCount; index++) {
for (final StringResource res : defaultResMap.getResources()) { final String name = mSortedResourceNames[index];
out.format(" /* %2d */ \"%s\",\n", id, res.mName); final int histogramValue = mNameHistogram.get(name);
mNameToIdMap.put(res.mName, id); out.format(" /* %3d:%2d */ \"%s\",\n", index, histogramValue, name);
id++;
} }
} }
private void dumpDefaultTexts(final PrintStream out) { private void dumpDefaultTexts(final PrintStream out) {
final StringResourceMap defaultResMap = mResourcesMap.get(DEFAULT_LANGUAGE_NAME); final int outputArraySize = dumpTextsInternal(out, mDefaultResourceMap);
final int outputArraySize = dumpTextsInternal(out, defaultResMap, defaultResMap); mDefaultResourceMap.setOutputArraySize(outputArraySize);
defaultResMap.setOutputArraySize(outputArraySize);
} }
private static String getArrayNameForLanguage(final String language) { private static String getArrayNameForLanguage(final String language) {
@ -152,35 +201,20 @@ public class MoreKeysResources {
} }
private void dumpTexts(final PrintStream out) { private void dumpTexts(final PrintStream out) {
final StringResourceMap defaultResMap = mResourcesMap.get(DEFAULT_LANGUAGE_NAME); for (final String language : mSortedLanguagesList) {
final ArrayList<String> allLanguages = new ArrayList<String>(); final StringResourceMap resMap = mResourcesMap.get(language);
allLanguages.addAll(mResourcesMap.keySet()); if (resMap == mDefaultResourceMap) continue;
Collections.sort(allLanguages);
for (final String language : allLanguages) {
if (language.equals(DEFAULT_LANGUAGE_NAME)) {
continue;
}
out.format(" /* Language %s: %s */\n", language, getLanguageDisplayName(language)); out.format(" /* Language %s: %s */\n", language, getLanguageDisplayName(language));
out.format(" private static final String[] " + getArrayNameForLanguage(language) out.format(" private static final String[] " + getArrayNameForLanguage(language)
+ " = {\n"); + " = {\n");
final StringResourceMap resMap = mResourcesMap.get(language); final int outputArraySize = dumpTextsInternal(out, resMap);
for (final StringResource res : resMap.getResources()) {
if (!defaultResMap.contains(res.mName)) {
throw new RuntimeException(res.mName + " in " + language
+ " doesn't have default resource");
}
}
final int outputArraySize = dumpTextsInternal(out, resMap, defaultResMap);
resMap.setOutputArraySize(outputArraySize); resMap.setOutputArraySize(outputArraySize);
out.format(" };\n\n"); out.format(" };\n\n");
} }
} }
private void dumpLanguageMap(final PrintStream out) { private void dumpLanguageMap(final PrintStream out) {
final ArrayList<String> allLanguages = new ArrayList<String>(); for (final String language : mSortedLanguagesList) {
allLanguages.addAll(mResourcesMap.keySet());
Collections.sort(allLanguages);
for (final String language : allLanguages) {
final StringResourceMap resMap = mResourcesMap.get(language); final StringResourceMap resMap = mResourcesMap.get(language);
final Locale locale = LocaleUtils.constructLocaleFromString(language); final Locale locale = LocaleUtils.constructLocaleFromString(language);
final String languageKeyToDump = locale.getCountry().isEmpty() final String languageKeyToDump = locale.getCountry().isEmpty()
@ -201,15 +235,17 @@ public class MoreKeysResources {
return locale.getDisplayName(Locale.ENGLISH); return locale.getDisplayName(Locale.ENGLISH);
} }
private static int dumpTextsInternal(final PrintStream out, final StringResourceMap resMap, private int dumpTextsInternal(final PrintStream out, final StringResourceMap resMap) {
final StringResourceMap defaultResMap) {
final ArrayInitializerFormatter formatter = final ArrayInitializerFormatter formatter =
new ArrayInitializerFormatter(out, 100, " "); new ArrayInitializerFormatter(out, 100, " ", mSortedResourceNames);
int outputArraySize = 0; int outputArraySize = 0;
boolean successiveNull = false; boolean successiveNull = false;
for (final StringResource defaultRes : defaultResMap.getResources()) { final int namesCount = mSortedResourceNames.length;
if (resMap.contains(defaultRes.mName)) { for (int index = 0; index < namesCount; index++) {
final StringResource res = resMap.get(defaultRes.mName); final String name = mSortedResourceNames[index];
final StringResource res = resMap.get(name);
if (res != null) {
// TODO: Check whether the resource value is equal to the default.
if (res.mComment != null) { if (res.mComment != null) {
formatter.outCommentLines(addPrefix(" // ", res. mComment)); formatter.outCommentLines(addPrefix(" // ", res. mComment));
} }
@ -270,7 +306,7 @@ public class MoreKeysResources {
return t; return t;
} }
private static void close(Closeable stream) { private static void close(final Closeable stream) {
try { try {
if (stream != null) { if (stream != null) {
stream.close(); stream.close();