diff options
Diffstat (limited to 'java/src')
13 files changed, 192 insertions, 153 deletions
diff --git a/java/src/com/android/inputmethod/keyboard/Keyboard.java b/java/src/com/android/inputmethod/keyboard/Keyboard.java index 962379016..bc48b85ef 100644 --- a/java/src/com/android/inputmethod/keyboard/Keyboard.java +++ b/java/src/com/android/inputmethod/keyboard/Keyboard.java @@ -278,6 +278,7 @@ public class Keyboard { } } + // TODO: Remove this method. public void setEnabled(boolean enabled) { mEnabled = enabled; } @@ -616,29 +617,10 @@ public class Keyboard { mParams = params; - setTouchPositionCorrectionData(context, params); - params.GRID_WIDTH = res.getInteger(R.integer.config_keyboard_grid_width); params.GRID_HEIGHT = res.getInteger(R.integer.config_keyboard_grid_height); } - private static void setTouchPositionCorrectionData(Context context, Params params) { - final TypedArray a = context.obtainStyledAttributes( - null, R.styleable.Keyboard, R.attr.keyboardStyle, 0); - params.mThemeId = a.getInt(R.styleable.Keyboard_themeId, 0); - final int resourceId = a.getResourceId( - R.styleable.Keyboard_touchPositionCorrectionData, 0); - a.recycle(); - if (resourceId == 0) { - if (LatinImeLogger.sDBG) - Log.e(BUILDER_TAG, "touchPositionCorrectionData is not defined"); - return; - } - - final String[] data = context.getResources().getStringArray(resourceId); - params.mTouchPositionCorrection.load(data); - } - public void setAutoGenerate(KeyboardSet.KeysCache keysCache) { mParams.mKeysCache = keysCache; } @@ -660,6 +642,7 @@ public class Keyboard { return this; } + // TODO: Remove this method. public void setTouchPositionCorrectionEnabled(boolean enabled) { mParams.mTouchPositionCorrection.setEnabled(enabled); } @@ -771,6 +754,15 @@ public class Keyboard { R.styleable.Keyboard_Key_maxMoreKeysColumn, 5); params.mIconsSet.loadIcons(keyboardAttr); + + params.mThemeId = keyboardAttr.getInt(R.styleable.Keyboard_themeId, 0); + final int resourceId = keyboardAttr.getResourceId( + R.styleable.Keyboard_touchPositionCorrectionData, 0); + params.mTouchPositionCorrection.setEnabled(resourceId != 0); + if (resourceId != 0) { + final String[] data = mResources.getStringArray(resourceId); + params.mTouchPositionCorrection.load(data); + } } finally { keyAttr.recycle(); keyboardAttr.recycle(); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java index 52096c843..bb11a9b77 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSet.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSet.java @@ -63,8 +63,6 @@ public class KeyboardSet { new HashMap<KeyboardId, SoftReference<Keyboard>>(); private static final KeysCache sKeysCache = new KeysCache(); - private static final EditorInfo EMPTY_EDITOR_INFO = new EditorInfo(); - public static class KeyboardSetException extends RuntimeException { public final KeyboardId mKeyboardId; public KeyboardSetException(Throwable cause, KeyboardId keyboardId) { @@ -209,6 +207,8 @@ public class KeyboardSet { private final Params mParams = new Params(); + private static final EditorInfo EMPTY_EDITOR_INFO = new EditorInfo(); + public Builder(Context context, EditorInfo editorInfo) { mContext = context; mPackageName = context.getPackageName(); @@ -229,15 +229,13 @@ public class KeyboardSet { } // TODO: Use InputMethodSubtype object as argument. - public Builder setSubtype(Locale inputLocale, boolean asciiCapable, - boolean touchPositionCorrectionEnabled) { + public Builder setSubtype(Locale inputLocale, boolean asciiCapable) { final boolean deprecatedForceAscii = StringUtils.inPrivateImeOptions( mPackageName, LatinIME.IME_OPTION_FORCE_ASCII, mEditorInfo); final boolean forceAscii = EditorInfoCompatUtils.hasFlagForceAscii( mParams.mEditorInfo.imeOptions) || deprecatedForceAscii; mParams.mLocale = (forceAscii && !asciiCapable) ? Locale.US : inputLocale; - mParams.mTouchPositionCorrectionEnabled = touchPositionCorrectionEnabled; return this; } @@ -255,6 +253,10 @@ public class KeyboardSet { return this; } + public void setTouchPositionCorrectionEnabled(boolean enabled) { + mParams.mTouchPositionCorrectionEnabled = enabled; + } + public KeyboardSet build() { if (mParams.mOrientation == Configuration.ORIENTATION_UNDEFINED) throw new RuntimeException("Screen geometry is not specified"); diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java index 42dd6206c..93d8704de 100644 --- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java +++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java @@ -140,9 +140,7 @@ public class KeyboardSwitcher implements KeyboardState.SwitchActions { builder.setSubtype( mSubtypeSwitcher.getInputLocale(), mSubtypeSwitcher.currentSubtypeContainsExtraValueKey( - LatinIME.SUBTYPE_EXTRA_VALUE_ASCII_CAPABLE), - mSubtypeSwitcher.currentSubtypeContainsExtraValueKey( - LatinIME.SUBTYPE_EXTRA_VALUE_SUPPORT_TOUCH_POSITION_CORRECTION)); + LatinIME.SUBTYPE_EXTRA_VALUE_ASCII_CAPABLE)); builder.setOptions( settingsValues.isVoiceKeyEnabled(editorInfo), settingsValues.isVoiceKeyOnMain(), diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index 9909638d4..c43683f2d 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -40,14 +40,13 @@ public class BinaryDictionary extends Dictionary { public static final int MAX_WORDS = 18; private static final String TAG = "BinaryDictionary"; - private static final int MAX_PROXIMITY_CHARS_SIZE = ProximityInfo.MAX_PROXIMITY_CHARS_SIZE; private static final int MAX_BIGRAMS = 60; private static final int TYPED_LETTER_MULTIPLIER = 2; private int mDicTypeId; private long mNativeDict; - private final int[] mInputCodes = new int[MAX_WORD_LENGTH * MAX_PROXIMITY_CHARS_SIZE]; + private final int[] mInputCodes = new int[MAX_WORD_LENGTH]; private final char[] mOutputChars = new char[MAX_WORD_LENGTH * MAX_WORDS]; private final char[] mOutputChars_bigrams = new char[MAX_WORD_LENGTH * MAX_BIGRAMS]; private final int[] mScores = new int[MAX_WORDS]; @@ -111,8 +110,7 @@ public class BinaryDictionary extends Dictionary { } private native long openNative(String sourceDir, long dictOffset, long dictSize, - int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength, - int maxWords, int maxAlternatives); + int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength, int maxWords); private native void closeNative(long dict); private native boolean isValidWordNative(long dict, char[] word, int wordLength); private native int getSuggestionsNative(long dict, long proximityInfo, int[] xCoordinates, @@ -120,7 +118,7 @@ public class BinaryDictionary extends Dictionary { int[] scores); private native int getBigramsNative(long dict, char[] prevWord, int prevWordLength, int[] inputCodes, int inputCodesLength, char[] outputChars, int[] scores, - int maxWordLength, int maxBigrams, int maxAlternatives); + int maxWordLength, int maxBigrams); private static native double calcNormalizedScoreNative( char[] before, int beforeLength, char[] after, int afterLength, int score); private static native int editDistanceNative( @@ -128,8 +126,7 @@ public class BinaryDictionary extends Dictionary { private final void loadDictionary(String path, long startOffset, long length) { mNativeDict = openNative(path, startOffset, length, - TYPED_LETTER_MULTIPLIER, FULL_WORD_SCORE_MULTIPLIER, - MAX_WORD_LENGTH, MAX_WORDS, MAX_PROXIMITY_CHARS_SIZE); + TYPED_LETTER_MULTIPLIER, FULL_WORD_SCORE_MULTIPLIER, MAX_WORD_LENGTH, MAX_WORDS); } @Override @@ -148,8 +145,7 @@ public class BinaryDictionary extends Dictionary { } int count = getBigramsNative(mNativeDict, chars, chars.length, mInputCodes, codesSize, - mOutputChars_bigrams, mBigramScores, MAX_WORD_LENGTH, MAX_BIGRAMS, - MAX_PROXIMITY_CHARS_SIZE); + mOutputChars_bigrams, mBigramScores, MAX_WORD_LENGTH, MAX_BIGRAMS); if (count > MAX_BIGRAMS) { count = MAX_BIGRAMS; } diff --git a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java index f8de029bd..098913bef 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableDictionary.java @@ -28,17 +28,12 @@ import java.util.LinkedList; * be searched for suggestions and valid words. */ public class ExpandableDictionary extends Dictionary { - /** - * There is difference between what java and native code can handle. - * It uses 32 because Java stack overflows when greater value is used. - */ - protected static final int MAX_WORD_LENGTH = 32; // Bigram frequency is a fixed point number with 1 meaning 1.2 and 255 meaning 1.8. protected static final int BIGRAM_MAX_FREQUENCY = 255; private Context mContext; - private char[] mWordBuilder = new char[MAX_WORD_LENGTH]; + private char[] mWordBuilder = new char[BinaryDictionary.MAX_WORD_LENGTH]; private int mDicTypeId; private int mMaxDepth; private int mInputLength; @@ -113,7 +108,7 @@ public class ExpandableDictionary extends Dictionary { public ExpandableDictionary(Context context, int dicTypeId) { mContext = context; clearDictionary(); - mCodes = new int[MAX_WORD_LENGTH][]; + mCodes = new int[BinaryDictionary.MAX_WORD_LENGTH][]; mDicTypeId = dicTypeId; } @@ -151,10 +146,13 @@ public class ExpandableDictionary extends Dictionary { } public int getMaxWordLength() { - return MAX_WORD_LENGTH; + return BinaryDictionary.MAX_WORD_LENGTH; } public void addWord(String word, int frequency) { + if (word.length() >= BinaryDictionary.MAX_WORD_LENGTH) { + return; + } addWordRec(mRoots, word, 0, frequency, null); } @@ -201,6 +199,9 @@ public class ExpandableDictionary extends Dictionary { // Currently updating contacts, don't return any results. if (mUpdatingDictionary) return; } + if (codes.size() >= BinaryDictionary.MAX_WORD_LENGTH) { + return; + } getWordsInner(codes, callback, proximityInfo); } @@ -488,7 +489,7 @@ public class ExpandableDictionary extends Dictionary { } // Local to reverseLookUp, but do not allocate each time. - private final char[] mLookedUpString = new char[MAX_WORD_LENGTH]; + private final char[] mLookedUpString = new char[BinaryDictionary.MAX_WORD_LENGTH]; /** * reverseLookUp retrieves the full word given a list of terminal nodes and adds those words @@ -502,15 +503,15 @@ public class ExpandableDictionary extends Dictionary { for (NextWord nextWord : terminalNodes) { node = nextWord.mWord; freq = nextWord.getFrequency(); - int index = MAX_WORD_LENGTH; + int index = BinaryDictionary.MAX_WORD_LENGTH; do { --index; mLookedUpString[index] = node.mCode; node = node.mParent; } while (node != null); - callback.addWord(mLookedUpString, index, MAX_WORD_LENGTH - index, freq, mDicTypeId, - Dictionary.BIGRAM); + callback.addWord(mLookedUpString, index, BinaryDictionary.MAX_WORD_LENGTH - index, + freq, mDicTypeId, Dictionary.BIGRAM); } } diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 0669ee668..86c153958 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -125,12 +125,6 @@ public class LatinIME extends InputMethodServiceCompatWrapper implements Keyboar public static final String SUBTYPE_EXTRA_VALUE_ASCII_CAPABLE = "AsciiCapable"; /** - * The subtype extra value used to indicate that the subtype keyboard layout supports touch - * position correction. - */ - public static final String SUBTYPE_EXTRA_VALUE_SUPPORT_TOUCH_POSITION_CORRECTION = - "SupportTouchPositionCorrection"; - /** * The subtype extra value used to indicate that the subtype keyboard layout should be loaded * from the specified locale. */ diff --git a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java index db2cdf967..62525c205 100644 --- a/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java +++ b/java/src/com/android/inputmethod/latin/UserHistoryDictionary.java @@ -247,8 +247,8 @@ public class UserHistoryDictionary extends ExpandableDictionary { // to recursive lookup if (null == word1) { super.addWord(word2, frequency); - } else if (word1.length() < MAX_WORD_LENGTH - && word2.length() < MAX_WORD_LENGTH) { + } else if (word1.length() < BinaryDictionary.MAX_WORD_LENGTH + && word2.length() < BinaryDictionary.MAX_WORD_LENGTH) { super.setBigram(word1, word2, frequency); } cursor.moveToNext(); diff --git a/java/src/com/android/inputmethod/latin/WordComposer.java b/java/src/com/android/inputmethod/latin/WordComposer.java index 29a7e4816..555a49ef4 100644 --- a/java/src/com/android/inputmethod/latin/WordComposer.java +++ b/java/src/com/android/inputmethod/latin/WordComposer.java @@ -104,7 +104,11 @@ public class WordComposer { return size() > 0; } + // TODO: make sure that the index should not exceed MAX_WORD_LENGTH public int getCodeAt(int index) { + if (index >= BinaryDictionary.MAX_WORD_LENGTH) { + return -1; + } return mPrimaryKeyCodes[index]; } @@ -123,7 +127,6 @@ public class WordComposer { // TODO: remove input keyDetector public void add(int primaryCode, int x, int y, KeyDetector keyDetector) { - final int[] codes; final int keyX; final int keyY; if (null == keyDetector @@ -131,17 +134,13 @@ public class WordComposer { || y == KeyboardActionListener.SUGGESTION_STRIP_COORDINATE || x == KeyboardActionListener.NOT_A_TOUCH_COORDINATE || y == KeyboardActionListener.NOT_A_TOUCH_COORDINATE) { - codes = new int[] { primaryCode }; keyX = x; keyY = y; } else { - final Key key = keyDetector.detectHitKey(x, y); - // TODO: Pass an integer instead of an integer array - codes = new int[] { key != null ? key.mCode : NOT_A_CODE }; keyX = keyDetector.getTouchX(x); keyY = keyDetector.getTouchY(y); } - add(primaryCode, codes, keyX, keyY); + add(primaryCode, keyX, keyY); } /** @@ -149,12 +148,13 @@ public class WordComposer { * the array containing unicode for adjacent keys, sorted by reducing probability/proximity. * @param codes the array of unicode values */ - private void add(int primaryCode, int[] codes, int keyX, int keyY) { + private void add(int primaryCode, int keyX, int keyY) { final int newIndex = size(); mTypedWord.appendCodePoint(primaryCode); refreshSize(); - mPrimaryKeyCodes[newIndex] = codes[0]; if (newIndex < BinaryDictionary.MAX_WORD_LENGTH) { + mPrimaryKeyCodes[newIndex] = primaryCode >= Keyboard.CODE_SPACE + ? Character.toLowerCase(primaryCode) : primaryCode; mXCoordinates[newIndex] = keyX; mYCoordinates[newIndex] = keyY; } @@ -177,13 +177,11 @@ public class WordComposer { if (key.mCode == codePoint) { final int x = key.mX + key.mWidth / 2; final int y = key.mY + key.mHeight / 2; - // TODO: Pass an integer instead of an integer array - add(codePoint, new int[] { key.mCode }, x, y); + add(codePoint, x, y); return; } } - add(codePoint, new int[] { codePoint }, - WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE); + add(codePoint, WordComposer.NOT_A_COORDINATE, WordComposer.NOT_A_COORDINATE); } /** diff --git a/java/src/com/android/inputmethod/latin/define/JniLibName.java b/java/src/com/android/inputmethod/latin/define/JniLibName.java index 3e94a3c07..e23e1a968 100644 --- a/java/src/com/android/inputmethod/latin/define/JniLibName.java +++ b/java/src/com/android/inputmethod/latin/define/JniLibName.java @@ -16,6 +16,10 @@ package com.android.inputmethod.latin.define; -public class JniLibName { +public final class JniLibName { + private JniLibName() { + // This class is not publicly instantiable. + } + public static final String JNI_LIB_NAME = "jni_latinime"; } diff --git a/java/src/com/android/inputmethod/latin/define/ProductionFlag.java b/java/src/com/android/inputmethod/latin/define/ProductionFlag.java index cfb1d09cc..de2057669 100644 --- a/java/src/com/android/inputmethod/latin/define/ProductionFlag.java +++ b/java/src/com/android/inputmethod/latin/define/ProductionFlag.java @@ -16,6 +16,10 @@ package com.android.inputmethod.latin.define; -public class ProductionFlag { +public final class ProductionFlag { + private ProductionFlag() { + // This class is not publicly instantiable. + } + public static final boolean IS_EXPERIMENTAL = false; } diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java index 42dd4df34..af7f863ee 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictInputOutput.java @@ -47,7 +47,6 @@ public class BinaryDictInputOutput { * s | has a terminal ? 1 bit, 1 = yes, 0 = no : FLAG_IS_TERMINAL * | has shortcut targets ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_SHORTCUT_TARGETS * | has bigrams ? 1 bit, 1 = yes, 0 = no : FLAG_HAS_BIGRAMS - * | is shortcut only ? 1 bit, 1 = yes, 0 = no : FLAG_IS_SHORTCUT_ONLY * * c | IF FLAG_HAS_MULTIPLE_CHARS * h | char, char, char, char n * (1 or 3 bytes) : use CharGroupInfo for i/o helpers @@ -74,7 +73,7 @@ public class BinaryDictInputOutput { * dress * * | IF FLAG_IS_TERMINAL && FLAG_HAS_SHORTCUT_TARGETS - * | shortcut targets address list + * | shortcut string list * | IF FLAG_IS_TERMINAL && FLAG_HAS_BIGRAMS * | bigrams address list * @@ -89,7 +88,7 @@ public class BinaryDictInputOutput { * the BMP. Also everything in the iso-latin-1 charset is only 1 byte, except control * characters which should never happen anyway (and still work, but take 3 bytes). * - * bigram and shortcut address list is: + * bigram address list is: * <flags> = | hasNext = 1 bit, 1 = yes, 0 = no : FLAG_ATTRIBUTE_HAS_NEXT * | addressSign = 1 bit, : FLAG_ATTRIBUTE_OFFSET_NEGATIVE * | 1 = must take -address, 0 = must take +address @@ -107,8 +106,16 @@ public class BinaryDictInputOutput { * | read 3 bytes, add top 4 bits * | END * | if (FLAG_ATTRIBUTE_OFFSET_NEGATIVE) then address = -address - * if (FLAG_ATTRIBUTE_HAS_NET) goto bigram_and_shortcut_address_list_is + * if (FLAG_ATTRIBUTE_HAS_NEXT) goto bigram_and_shortcut_address_list_is * + * shortcut string list is: + * <byte size> = GROUP_SHORTCUT_LIST_SIZE_SIZE bytes, big-endian: size of the list, in bytes. + * <flags> = | hasNext = 1 bit, 1 = yes, 0 = no : FLAG_ATTRIBUTE_HAS_NEXT + * | reserved = 3 bits, must be 0 + * | 4 bits : frequency : mask with FLAG_ATTRIBUTE_FREQUENCY + * <shortcut> = | string of characters at the char format described above, with the terminator + * | used to signal the end of the string. + * if (FLAG_ATTRIBUTE_HAS_NEXT goto flags */ private static final int VERSION_1_MAGIC_NUMBER = 0x78B1; @@ -136,7 +143,6 @@ public class BinaryDictInputOutput { private static final int FLAG_IS_TERMINAL = 0x10; private static final int FLAG_HAS_SHORTCUT_TARGETS = 0x08; private static final int FLAG_HAS_BIGRAMS = 0x04; - private static final int FLAG_IS_SHORTCUT_ONLY = 0x02; private static final int FLAG_ATTRIBUTE_HAS_NEXT = 0x80; private static final int FLAG_ATTRIBUTE_OFFSET_NEGATIVE = 0x40; @@ -154,6 +160,7 @@ public class BinaryDictInputOutput { private static final int GROUP_MAX_ADDRESS_SIZE = 3; private static final int GROUP_ATTRIBUTE_FLAGS_SIZE = 1; private static final int GROUP_ATTRIBUTE_MAX_ADDRESS_SIZE = 3; + private static final int GROUP_SHORTCUT_LIST_SIZE_SIZE = 2; private static final int NO_CHILDREN_ADDRESS = Integer.MIN_VALUE; private static final int INVALID_CHARACTER = -1; @@ -215,25 +222,66 @@ public class BinaryDictInputOutput { /** * Writes a char array to a byte buffer. * - * @param characters the character array to write. + * @param codePoints the code point array to write. * @param buffer the byte buffer to write to. * @param index the index in buffer to write the character array to. * @return the index after the last character. */ - private static int writeCharArray(int[] characters, byte[] buffer, int index) { - for (int character : characters) { - if (1 == getCharSize(character)) { - buffer[index++] = (byte)character; + private static int writeCharArray(final int[] codePoints, final byte[] buffer, int index) { + for (int codePoint : codePoints) { + if (1 == getCharSize(codePoint)) { + buffer[index++] = (byte)codePoint; } else { - buffer[index++] = (byte)(0xFF & (character >> 16)); - buffer[index++] = (byte)(0xFF & (character >> 8)); - buffer[index++] = (byte)(0xFF & character); + buffer[index++] = (byte)(0xFF & (codePoint >> 16)); + buffer[index++] = (byte)(0xFF & (codePoint >> 8)); + buffer[index++] = (byte)(0xFF & codePoint); } } return index; } /** + * Writes a string with our character format to a byte buffer. + * + * This will also write the terminator byte. + * + * @param buffer the byte buffer to write to. + * @param origin the offset to write from. + * @param word the string to write. + * @return the size written, in bytes. + */ + private static int writeString(final byte[] buffer, final int origin, + final String word) { + final int length = word.length(); + int index = origin; + for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) { + final int codePoint = word.codePointAt(i); + if (1 == getCharSize(codePoint)) { + buffer[index++] = (byte)codePoint; + } else { + buffer[index++] = (byte)(0xFF & (codePoint >> 16)); + buffer[index++] = (byte)(0xFF & (codePoint >> 8)); + buffer[index++] = (byte)(0xFF & codePoint); + } + } + buffer[index++] = GROUP_CHARACTERS_TERMINATOR; + return index - origin; + } + + /** + * Reads a string from a RandomAccessFile. This is the converse of the above method. + */ + private static String readString(final RandomAccessFile source) throws IOException { + final StringBuilder s = new StringBuilder(); + int character = readChar(source); + while (character != INVALID_CHARACTER) { + s.appendCodePoint(character); + character = readChar(source); + } + return s.toString(); + } + + /** * Reads a character from the file. * * This follows the character format documented earlier in this source file. @@ -294,6 +342,36 @@ public class BinaryDictInputOutput { } /** + * Compute the size of a shortcut in bytes. + */ + private static int getShortcutSize(final WeightedString shortcut) { + int size = GROUP_ATTRIBUTE_FLAGS_SIZE; + final String word = shortcut.mWord; + final int length = word.length(); + for (int i = 0; i < length; i = word.offsetByCodePoints(i, 1)) { + final int codePoint = word.codePointAt(i); + size += CharEncoding.getCharSize(codePoint); + } + size += GROUP_TERMINATOR_SIZE; + return size; + } + + /** + * Compute the size of a shortcut list in bytes. + * + * This is known in advance and does not change according to position in the file + * like address lists do. + */ + private static int getShortcutListSize(final ArrayList<WeightedString> shortcutList) { + if (null == shortcutList) return 0; + int size = GROUP_SHORTCUT_LIST_SIZE_SIZE; + for (final WeightedString shortcut : shortcutList) { + size += getShortcutSize(shortcut); + } + return size; + } + + /** * Compute the maximum size of a CharGroup, assuming 3-byte addresses for everything. * * @param group the CharGroup to compute the size of. @@ -304,10 +382,7 @@ public class BinaryDictInputOutput { // If terminal, one byte for the frequency if (group.isTerminal()) size += GROUP_FREQUENCY_SIZE; size += GROUP_MAX_ADDRESS_SIZE; // For children address - if (null != group.mShortcutTargets) { - size += (GROUP_ATTRIBUTE_FLAGS_SIZE + GROUP_ATTRIBUTE_MAX_ADDRESS_SIZE) - * group.mShortcutTargets.size(); - } + size += getShortcutListSize(group.mShortcutTargets); if (null != group.mBigrams) { size += (GROUP_ATTRIBUTE_FLAGS_SIZE + GROUP_ATTRIBUTE_MAX_ADDRESS_SIZE) * group.mBigrams.size(); @@ -339,13 +414,6 @@ public class BinaryDictInputOutput { } /** - * Helper method to find out if a character info is a shortcut only. - */ - private static boolean isShortcutOnly(final CharGroupInfo info) { - return 0 != (info.mFlags & FLAG_IS_SHORTCUT_ONLY); - } - - /** * Compute the size, in bytes, that an address will occupy. * * This can be used either for children addresses (which are always positive) or for @@ -430,15 +498,7 @@ public class BinaryDictInputOutput { final int offset = group.mChildren.mCachedAddress - offsetBasePoint; groupSize += getByteSize(offset); } - if (null != group.mShortcutTargets) { - for (WeightedString target : group.mShortcutTargets) { - final int offsetBasePoint = groupSize + node.mCachedAddress + size - + GROUP_FLAGS_SIZE; - final int addressOfTarget = findAddressOfWord(dict, target.mWord); - final int offset = addressOfTarget - offsetBasePoint; - groupSize += getByteSize(offset) + GROUP_FLAGS_SIZE; - } - } + groupSize += getShortcutListSize(group.mShortcutTargets); if (null != group.mBigrams) { for (WeightedString bigram : group.mBigrams) { final int offsetBasePoint = groupSize + node.mCachedAddress + size @@ -555,7 +615,7 @@ public class BinaryDictInputOutput { * @param address the address to write. * @return the size in bytes the address actually took. */ - private static int writeVariableAddress(byte[] buffer, int index, int address) { + private static int writeVariableAddress(final byte[] buffer, int index, final int address) { switch (getByteSize(address)) { case 1: buffer[index++] = (byte)address; @@ -610,9 +670,6 @@ public class BinaryDictInputOutput { } flags |= FLAG_HAS_BIGRAMS; } - if (group.mIsShortcutOnly) { - flags |= FLAG_IS_SHORTCUT_ONLY; - } return flags; } @@ -646,6 +703,17 @@ public class BinaryDictInputOutput { } /** + * Makes the flag value for a shortcut. + * + * @param more whether there are more attributes after this one. + * @param frequency the frequency of the attribute, 0..15 + * @return the flags + */ + private static final int makeShortcutFlags(final boolean more, final int frequency) { + return (more ? FLAG_ATTRIBUTE_HAS_NEXT : 0) + (frequency & FLAG_ATTRIBUTE_FREQUENCY); + } + + /** * Write a node to memory. The node is expected to have its final position cached. * * This can be an empty map, but the more is inside the faster the lookups will be. It can @@ -675,7 +743,8 @@ public class BinaryDictInputOutput { for (int i = 0; i < groupCount; ++i) { CharGroup group = node.mData.get(i); if (index != group.mCachedAddress) throw new RuntimeException("Bug: write index is not " - + "the same as the cached address of the group"); + + "the same as the cached address of the group : " + + index + " <> " + group.mCachedAddress); groupAddress += GROUP_FLAGS_SIZE + getGroupCharactersSize(group); // Sanity checks. if (group.mFrequency > MAX_TERMINAL_FREQUENCY) { @@ -700,19 +769,26 @@ public class BinaryDictInputOutput { // Write shortcuts if (null != group.mShortcutTargets) { + final int indexOfShortcutByteSize = index; + index += GROUP_SHORTCUT_LIST_SIZE_SIZE; + groupAddress += GROUP_SHORTCUT_LIST_SIZE_SIZE; final Iterator shortcutIterator = group.mShortcutTargets.iterator(); while (shortcutIterator.hasNext()) { final WeightedString target = (WeightedString)shortcutIterator.next(); - final int addressOfTarget = findAddressOfWord(dict, target.mWord); ++groupAddress; - final int offset = addressOfTarget - groupAddress; - int shortcutFlags = makeAttributeFlags(shortcutIterator.hasNext(), offset, + int shortcutFlags = makeShortcutFlags(shortcutIterator.hasNext(), target.mFrequency); buffer[index++] = (byte)shortcutFlags; - final int shortcutShift = writeVariableAddress(buffer, index, Math.abs(offset)); + final int shortcutShift = CharEncoding.writeString(buffer, index, target.mWord); index += shortcutShift; groupAddress += shortcutShift; } + final int shortcutByteSize = index - indexOfShortcutByteSize; + if (shortcutByteSize > 0xFFFF) { + throw new RuntimeException("Shortcut list too large"); + } + buffer[indexOfShortcutByteSize] = (byte)(shortcutByteSize >> 8); + buffer[indexOfShortcutByteSize + 1] = (byte)(shortcutByteSize & 0xFF); } // Write bigrams if (null != group.mBigrams) { @@ -932,36 +1008,19 @@ public class BinaryDictInputOutput { childrenAddress = NO_CHILDREN_ADDRESS; break; } - ArrayList<PendingAttribute> shortcutTargets = null; + ArrayList<WeightedString> shortcutTargets = null; if (0 != (flags & FLAG_HAS_SHORTCUT_TARGETS)) { - shortcutTargets = new ArrayList<PendingAttribute>(); + final long pointerBefore = source.getFilePointer(); + shortcutTargets = new ArrayList<WeightedString>(); + source.readUnsignedShort(); // Skip the size while (true) { final int targetFlags = source.readUnsignedByte(); - ++addressPointer; - final int sign = 0 == (targetFlags & FLAG_ATTRIBUTE_OFFSET_NEGATIVE) ? 1 : -1; - int targetAddress = addressPointer; - switch (targetFlags & MASK_ATTRIBUTE_ADDRESS_TYPE) { - case FLAG_ATTRIBUTE_ADDRESS_TYPE_ONEBYTE: - targetAddress += sign * source.readUnsignedByte(); - addressPointer += 1; - break; - case FLAG_ATTRIBUTE_ADDRESS_TYPE_TWOBYTES: - targetAddress += sign * source.readUnsignedShort(); - addressPointer += 2; - break; - case FLAG_ATTRIBUTE_ADDRESS_TYPE_THREEBYTES: - final int offset = ((source.readUnsignedByte() << 16) - + source.readUnsignedShort()); - targetAddress += sign * offset; - addressPointer += 3; - break; - default: - throw new RuntimeException("Has shortcut targets with no address"); - } - shortcutTargets.add(new PendingAttribute(targetFlags & FLAG_ATTRIBUTE_FREQUENCY, - targetAddress)); + final String word = CharEncoding.readString(source); + shortcutTargets.add(new WeightedString(word, + targetFlags & FLAG_ATTRIBUTE_FREQUENCY)); if (0 == (targetFlags & FLAG_ATTRIBUTE_HAS_NEXT)) break; } + addressPointer += (source.getFilePointer() - pointerBefore); } ArrayList<PendingAttribute> bigrams = null; if (0 != (flags & FLAG_HAS_BIGRAMS)) { @@ -1086,14 +1145,7 @@ public class BinaryDictInputOutput { int groupOffset = nodeOrigin + getGroupCountSize(count); for (int i = count; i > 0; --i) { CharGroupInfo info = readCharGroup(source, groupOffset); - ArrayList<WeightedString> shortcutTargets = null; - if (null != info.mShortcutTargets) { - shortcutTargets = new ArrayList<WeightedString>(); - for (PendingAttribute target : info.mShortcutTargets) { - final String word = getWordAtAddress(source, headerSize, target.mAddress); - shortcutTargets.add(new WeightedString(word, target.mFrequency)); - } - } + ArrayList<WeightedString> shortcutTargets = info.mShortcutTargets; ArrayList<WeightedString> bigrams = null; if (null != info.mBigrams) { bigrams = new ArrayList<WeightedString>(); @@ -1112,11 +1164,11 @@ public class BinaryDictInputOutput { } nodeContents.add( new CharGroup(info.mCharacters, shortcutTargets, bigrams, info.mFrequency, - children, isShortcutOnly(info))); + children, false)); } else { nodeContents.add( new CharGroup(info.mCharacters, shortcutTargets, bigrams, info.mFrequency, - isShortcutOnly(info))); + false)); } groupOffset = info.mEndAddress; } diff --git a/java/src/com/android/inputmethod/latin/makedict/CharGroupInfo.java b/java/src/com/android/inputmethod/latin/makedict/CharGroupInfo.java index 444b11732..ef7dbb251 100644 --- a/java/src/com/android/inputmethod/latin/makedict/CharGroupInfo.java +++ b/java/src/com/android/inputmethod/latin/makedict/CharGroupInfo.java @@ -16,6 +16,8 @@ package com.android.inputmethod.latin.makedict; +import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; + import java.util.ArrayList; /** @@ -29,12 +31,12 @@ public class CharGroupInfo { public final int[] mCharacters; public final int mFrequency; public final int mChildrenAddress; - public final ArrayList<PendingAttribute> mShortcutTargets; + public final ArrayList<WeightedString> mShortcutTargets; public final ArrayList<PendingAttribute> mBigrams; public CharGroupInfo(final int originalAddress, final int endAddress, final int flags, final int[] characters, final int frequency, final int childrenAddress, - final ArrayList<PendingAttribute> shortcutTargets, + final ArrayList<WeightedString> shortcutTargets, final ArrayList<PendingAttribute> bigrams) { mOriginalAddress = originalAddress; mEndAddress = endAddress; diff --git a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java index 64fcd7f1a..d3ffb47ad 100644 --- a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java +++ b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java @@ -311,9 +311,6 @@ public class FusionDictionary implements Iterable<Word> { public void add(final String word, final int frequency, final ArrayList<WeightedString> shortcutTargets, final ArrayList<WeightedString> bigrams) { - if (null != shortcutTargets) { - addNeutralWords(shortcutTargets); - } if (null != bigrams) { addNeutralWords(bigrams); } @@ -350,7 +347,6 @@ public class FusionDictionary implements Iterable<Word> { if (null == shortcutTargets) { throw new RuntimeException("Can't add a shortcut without targets"); } - addNeutralWords(shortcutTargets); add(getCodePoints(word), frequency, shortcutTargets, null, true /* isShortcutOnly */); } |