Optimize KeyboardTextsTable

This change counts all occurrences of each string resource and sort
those in descending order of the occurrence.

Change-Id: I726402157feb0d436a54bd0a7252acd17fd711f9
main
Tadashi G. Takaoka 2014-02-11 23:06:53 +09:00
parent 8dd47029f1
commit 0fe4d00068
4 changed files with 2107 additions and 2002 deletions

View File

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

View File

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

View File

@ -25,6 +25,7 @@ import java.io.LineNumberReader;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Locale;
import java.util.jar.JarFile;
@ -47,8 +48,19 @@ public class MoreKeysResources {
// 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>();
// Sorted languages list. The language is taken from string resource directories
// (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) {
mJar = jar;
@ -65,6 +77,45 @@ public class MoreKeysResources {
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) {
@ -132,19 +183,17 @@ public class MoreKeysResources {
}
private void dumpNames(final PrintStream out) {
final StringResourceMap defaultResMap = mResourcesMap.get(DEFAULT_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++;
final int namesCount = mSortedResourceNames.length;
for (int index = 0; index < namesCount; index++) {
final String name = mSortedResourceNames[index];
final int histogramValue = mNameHistogram.get(name);
out.format(" /* %3d:%2d */ \"%s\",\n", index, histogramValue, name);
}
}
private void dumpDefaultTexts(final PrintStream out) {
final StringResourceMap defaultResMap = mResourcesMap.get(DEFAULT_LANGUAGE_NAME);
final int outputArraySize = dumpTextsInternal(out, defaultResMap, defaultResMap);
defaultResMap.setOutputArraySize(outputArraySize);
final int outputArraySize = dumpTextsInternal(out, mDefaultResourceMap);
mDefaultResourceMap.setOutputArraySize(outputArraySize);
}
private static String getArrayNameForLanguage(final String language) {
@ -152,35 +201,20 @@ public class MoreKeysResources {
}
private void dumpTexts(final PrintStream out) {
final StringResourceMap defaultResMap = mResourcesMap.get(DEFAULT_LANGUAGE_NAME);
final ArrayList<String> allLanguages = new ArrayList<String>();
allLanguages.addAll(mResourcesMap.keySet());
Collections.sort(allLanguages);
for (final String language : allLanguages) {
if (language.equals(DEFAULT_LANGUAGE_NAME)) {
continue;
}
for (final String language : mSortedLanguagesList) {
final StringResourceMap resMap = mResourcesMap.get(language);
if (resMap == mDefaultResourceMap) continue;
out.format(" /* Language %s: %s */\n", language, getLanguageDisplayName(language));
out.format(" private static final String[] " + getArrayNameForLanguage(language)
+ " = {\n");
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");
}
}
final int outputArraySize = dumpTextsInternal(out, resMap, defaultResMap);
final int outputArraySize = dumpTextsInternal(out, resMap);
resMap.setOutputArraySize(outputArraySize);
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) {
for (final String language : mSortedLanguagesList) {
final StringResourceMap resMap = mResourcesMap.get(language);
final Locale locale = LocaleUtils.constructLocaleFromString(language);
final String languageKeyToDump = locale.getCountry().isEmpty()
@ -201,15 +235,17 @@ public class MoreKeysResources {
return locale.getDisplayName(Locale.ENGLISH);
}
private static int dumpTextsInternal(final PrintStream out, final StringResourceMap resMap,
final StringResourceMap defaultResMap) {
private int dumpTextsInternal(final PrintStream out, final StringResourceMap resMap) {
final ArrayInitializerFormatter formatter =
new ArrayInitializerFormatter(out, 100, " ");
new ArrayInitializerFormatter(out, 100, " ", mSortedResourceNames);
int outputArraySize = 0;
boolean successiveNull = false;
for (final StringResource defaultRes : defaultResMap.getResources()) {
if (resMap.contains(defaultRes.mName)) {
final StringResource res = resMap.get(defaultRes.mName);
final int namesCount = mSortedResourceNames.length;
for (int index = 0; index < namesCount; index++) {
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) {
formatter.outCommentLines(addPrefix(" // ", res. mComment));
}
@ -270,7 +306,7 @@ public class MoreKeysResources {
return t;
}
private static void close(Closeable stream) {
private static void close(final Closeable stream) {
try {
if (stream != null) {
stream.close();