diff options
Diffstat (limited to 'java/src')
16 files changed, 247 insertions, 350 deletions
diff --git a/java/src/com/android/inputmethod/latin/DictionaryWriter.java b/java/src/com/android/inputmethod/latin/DictionaryWriter.java index 84898680e..b931c66d1 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryWriter.java +++ b/java/src/com/android/inputmethod/latin/DictionaryWriter.java @@ -21,6 +21,7 @@ import com.android.inputmethod.latin.makedict.FormatSpec; import com.android.inputmethod.latin.makedict.FusionDictionary; import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray; import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; +import com.android.inputmethod.latin.makedict.ProbabilityInfo; import com.android.inputmethod.latin.makedict.UnsupportedFormatException; import com.android.inputmethod.latin.utils.CollectionUtils; @@ -56,22 +57,23 @@ public class DictionaryWriter extends AbstractDictionaryWriter { // TODO: Create "cache dictionary" to cache fresh words for frequently updated dictionaries, // considering performance regression. @Override - public void addUnigramWord(final String word, final String shortcutTarget, final int frequency, - final int shortcutFreq, final boolean isNotAWord) { + public void addUnigramWord(final String word, final String shortcutTarget, + final int probability, final int shortcutProbability, final boolean isNotAWord) { if (shortcutTarget == null) { - mFusionDictionary.add(word, frequency, null, isNotAWord); + mFusionDictionary.add(word, new ProbabilityInfo(probability), null, isNotAWord); } else { // TODO: Do this in the subclass, with this class taking an arraylist. final ArrayList<WeightedString> shortcutTargets = CollectionUtils.newArrayList(); - shortcutTargets.add(new WeightedString(shortcutTarget, shortcutFreq)); - mFusionDictionary.add(word, frequency, shortcutTargets, isNotAWord); + shortcutTargets.add(new WeightedString(shortcutTarget, shortcutProbability)); + mFusionDictionary.add(word, new ProbabilityInfo(probability), shortcutTargets, + isNotAWord); } } @Override - public void addBigramWords(final String word0, final String word1, final int frequency, + public void addBigramWords(final String word0, final String word1, final int probability, final boolean isValid, final long lastModifiedTime) { - mFusionDictionary.setBigram(word0, word1, frequency); + mFusionDictionary.setBigram(word0, word1, new ProbabilityInfo(probability)); } @Override diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index 3b427720a..432b8a5a9 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -1350,8 +1350,17 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen public void setSuggestedWords(final SuggestedWords suggestedWords, final boolean shouldShow) { mInputLogic.setSuggestedWords(suggestedWords); if (mSuggestionStripView != null) { - mSuggestionStripView.setSuggestions(suggestedWords, - SubtypeLocaleUtils.isRtlLanguage(mSubtypeSwitcher.getCurrentSubtype())); + final boolean showSuggestions; + if (SuggestedWords.EMPTY == suggestedWords + || suggestedWords.mIsPunctuationSuggestions) { + showSuggestions = !mSuggestionStripView.maybeShowImportantNoticeTitle(); + } else { + showSuggestions = true; + } + if (showSuggestions) { + mSuggestionStripView.setSuggestions(suggestedWords, + SubtypeLocaleUtils.isRtlLanguage(mSubtypeSwitcher.getCurrentSubtype())); + } mKeyboardSwitcher.onAutoCorrectionStateChanged(suggestedWords.mWillAutoCorrect); setSuggestionStripShownInternal(shouldShow, true /* needsInputViewShown */); } diff --git a/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java index e7d1c98a9..1bc322724 100644 --- a/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java @@ -20,7 +20,6 @@ import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding; import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer; import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; -import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; import java.io.FileNotFoundException; import java.io.IOException; @@ -117,94 +116,6 @@ public abstract class AbstractDictDecoder implements DictDecoder { } /** - * A utility class for reading a PtNode. - */ - protected static class PtNodeReader { - protected static int readPtNodeOptionFlags(final DictBuffer dictBuffer) { - return dictBuffer.readUnsignedByte(); - } - - protected static int readParentAddress(final DictBuffer dictBuffer, - final FormatOptions formatOptions) { - if (BinaryDictIOUtils.supportsDynamicUpdate(formatOptions)) { - return BinaryDictDecoderUtils.readSInt24(dictBuffer); - } else { - return FormatSpec.NO_PARENT_ADDRESS; - } - } - - protected static int readChildrenAddress(final DictBuffer dictBuffer, final int optionFlags, - final FormatOptions formatOptions) { - if (BinaryDictIOUtils.supportsDynamicUpdate(formatOptions)) { - final int address = BinaryDictDecoderUtils.readSInt24(dictBuffer); - if (address == 0) return FormatSpec.NO_CHILDREN_ADDRESS; - return address; - } else { - switch (optionFlags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) { - case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE: - return dictBuffer.readUnsignedByte(); - case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES: - return dictBuffer.readUnsignedShort(); - case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES: - return dictBuffer.readUnsignedInt24(); - case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS: - default: - return FormatSpec.NO_CHILDREN_ADDRESS; - } - } - } - - // Reads shortcuts and returns the read length. - protected static int readShortcut(final DictBuffer dictBuffer, - final ArrayList<WeightedString> shortcutTargets) { - final int pointerBefore = dictBuffer.position(); - dictBuffer.readUnsignedShort(); // skip the size - while (true) { - final int targetFlags = dictBuffer.readUnsignedByte(); - final String word = CharEncoding.readString(dictBuffer); - shortcutTargets.add(new WeightedString(word, - targetFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY)); - if (0 == (targetFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break; - } - return dictBuffer.position() - pointerBefore; - } - - protected static int readBigramAddresses(final DictBuffer dictBuffer, - final ArrayList<PendingAttribute> bigrams, final int baseAddress) { - int readLength = 0; - int bigramCount = 0; - while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) { - final int bigramFlags = dictBuffer.readUnsignedByte(); - ++readLength; - final int sign = 0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE) - ? 1 : -1; - int bigramAddress = baseAddress + readLength; - switch (bigramFlags & FormatSpec.MASK_BIGRAM_ATTR_ADDRESS_TYPE) { - case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE: - bigramAddress += sign * dictBuffer.readUnsignedByte(); - readLength += 1; - break; - case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES: - bigramAddress += sign * dictBuffer.readUnsignedShort(); - readLength += 2; - break; - case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES: - bigramAddress += sign * dictBuffer.readUnsignedInt24(); - readLength += 3; - break; - default: - throw new RuntimeException("Has bigrams with no address"); - } - bigrams.add(new PendingAttribute( - bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY, - bigramAddress)); - if (0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break; - } - return readLength; - } - } - - /** * Check whether the header contains the expected information. This is a no-error method, * that will return an error code and never throw a checked exception. * @return an error code, either ERROR_* or SUCCESS. @@ -264,9 +175,4 @@ public abstract class AbstractDictDecoder implements DictDecoder { public boolean hasNextPtNodeArray() { return false; } - - @Override - @UsedForTesting - public void skipPtNode(final FormatOptions formatOptions) { - } } diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java index 782ada3f4..fc5788de9 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java @@ -166,15 +166,6 @@ public final class BinaryDictDecoderUtils { return size; } - @UsedForTesting - static int getCharArraySize(final int[] chars, final int start, final int end) { - int size = 0; - for (int i = start; i < end; ++i) { - size += getCharSize(chars[i]); - } - return size; - } - /** * Writes a char array to a byte buffer. * @@ -253,41 +244,6 @@ public final class BinaryDictDecoderUtils { } /** - * Writes an array of code points with our character format to an OutputStream. - * - * This will also write the terminator byte. - * - * @param stream the OutputStream to write to. - * @param codePoints the array of code points - * @return the size written, in bytes. - */ - // TODO: Merge this method with writeCharArray and rename the various write* methods to - // make the difference clear. - @UsedForTesting - static int writeCodePoints(final OutputStream stream, final int[] codePoints, - final int startIndex, final int endIndex) - throws IOException { - int written = 0; - for (int i = startIndex; i < endIndex; ++i) { - final int codePoint = codePoints[i]; - final int charSize = getCharSize(codePoint); - if (1 == charSize) { - stream.write((byte) codePoint); - } else { - stream.write((byte) (0xFF & (codePoint >> 16))); - stream.write((byte) (0xFF & (codePoint >> 8))); - stream.write((byte) (0xFF & codePoint)); - } - written += charSize; - } - if (endIndex - startIndex > 1) { - stream.write(FormatSpec.PTNODE_CHARACTERS_TERMINATOR); - written += FormatSpec.PTNODE_TERMINATOR_SIZE; - } - return written; - } - - /** * Reads a string from a DictBuffer. This is the converse of the above method. */ static String readString(final DictBuffer dictBuffer) { @@ -321,50 +277,6 @@ public final class BinaryDictDecoderUtils { } } - // Input methods: Read a binary dictionary to memory. - // readDictionaryBinary is the public entry point for them. - - static int readSInt24(final DictBuffer dictBuffer) { - final int retval = dictBuffer.readUnsignedInt24(); - final int sign = ((retval & FormatSpec.MSB24) != 0) ? -1 : 1; - return sign * (retval & FormatSpec.SINT24_MAX); - } - - static int readChildrenAddress(final DictBuffer dictBuffer, - final int optionFlags, final FormatOptions options) { - if (options.supportsDynamicUpdate()) { - final int address = dictBuffer.readUnsignedInt24(); - if (address == 0) return FormatSpec.NO_CHILDREN_ADDRESS; - if ((address & FormatSpec.MSB24) != 0) { - return -(address & FormatSpec.SINT24_MAX); - } else { - return address; - } - } - switch (optionFlags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) { - case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE: - return dictBuffer.readUnsignedByte(); - case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES: - return dictBuffer.readUnsignedShort(); - case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES: - return dictBuffer.readUnsignedInt24(); - case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS: - default: - return FormatSpec.NO_CHILDREN_ADDRESS; - } - } - - static int readParentAddress(final DictBuffer dictBuffer, - final FormatOptions formatOptions) { - if (BinaryDictIOUtils.supportsDynamicUpdate(formatOptions)) { - final int parentAddress = dictBuffer.readUnsignedInt24(); - final int sign = ((parentAddress & FormatSpec.MSB24) != 0) ? -1 : 1; - return sign * (parentAddress & FormatSpec.SINT24_MAX); - } else { - return FormatSpec.NO_PARENT_ADDRESS; - } - } - /** * Reads and returns the PtNode count out of a buffer and forwards the pointer. */ @@ -408,7 +320,7 @@ public final class BinaryDictDecoderUtils { private static WeightedString getWordAtPositionWithParentAddress(final DictDecoder dictDecoder, final int pos, final FormatOptions options) { int currentPos = pos; - int frequency = Integer.MIN_VALUE; + ProbabilityInfo probabilityInfo = null; final StringBuilder builder = new StringBuilder(); // the length of the path from the root to the leaf is limited by MAX_WORD_LENGTH for (int count = 0; count < FormatSpec.MAX_WORD_LENGTH; ++count) { @@ -424,13 +336,15 @@ public final class BinaryDictDecoderUtils { MakedictLog.d("Too many jumps - probably a bug"); } } while (BinaryDictIOUtils.isMovedPtNode(currentInfo.mFlags, options)); - if (Integer.MIN_VALUE == frequency) frequency = currentInfo.mFrequency; + if (probabilityInfo == null) { + probabilityInfo = currentInfo.mProbabilityInfo; + } builder.insert(0, new String(currentInfo.mCharacters, 0, currentInfo.mCharacters.length)); if (currentInfo.mParentAddress == FormatSpec.NO_PARENT_ADDRESS) break; currentPos = currentInfo.mParentAddress + currentInfo.mOriginalAddress; } - return new WeightedString(builder.toString(), frequency); + return new WeightedString(builder.toString(), probabilityInfo); } private static WeightedString getWordAtPositionWithoutParentAddress( @@ -448,7 +362,7 @@ public final class BinaryDictDecoderUtils { groupPos = info.mEndAddress; if (info.mOriginalAddress == pos) { builder.append(new String(info.mCharacters, 0, info.mCharacters.length)); - result = new WeightedString(builder.toString(), info.mFrequency); + result = new WeightedString(builder.toString(), info.mProbabilityInfo); break; // and return } if (BinaryDictIOUtils.hasChildrenAddress(info.mChildrenAddress)) { @@ -527,13 +441,13 @@ public final class BinaryDictDecoderUtils { } nodeArrayContents.add( new PtNode(info.mCharacters, shortcutTargets, bigrams, - info.mFrequency, + info.mProbabilityInfo, 0 != (info.mFlags & FormatSpec.FLAG_IS_NOT_A_WORD), 0 != (info.mFlags & FormatSpec.FLAG_IS_BLACKLISTED), children)); } else { nodeArrayContents.add( new PtNode(info.mCharacters, shortcutTargets, bigrams, - info.mFrequency, + info.mProbabilityInfo, 0 != (info.mFlags & FormatSpec.FLAG_IS_NOT_A_WORD), 0 != (info.mFlags & FormatSpec.FLAG_IS_BLACKLISTED))); } @@ -611,7 +525,7 @@ public final class BinaryDictDecoderUtils { newDict.addBlacklistEntry(wordProperty.mWord, wordProperty.mShortcutTargets, wordProperty.mIsNotAWord); } else { - newDict.add(wordProperty.mWord, wordProperty.getProbability(), + newDict.add(wordProperty.mWord, wordProperty.mProbabilityInfo, wordProperty.mShortcutTargets, wordProperty.mIsNotAWord); } } @@ -620,7 +534,7 @@ public final class BinaryDictDecoderUtils { // words that are not also registered as unigrams so we don't have to avoid // them explicitly here. for (final WeightedString bigram : wordProperty.mBigrams) { - newDict.setBigram(wordProperty.mWord, bigram.mWord, bigram.getProbability()); + newDict.setBigram(wordProperty.mWord, bigram.mWord, bigram.mProbabilityInfo); } } } diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java index eff8fc375..9c5d1b9d7 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictEncoderUtils.java @@ -673,7 +673,7 @@ public class BinaryDictEncoderUtils { /* package */ static byte makePtNodeFlags(final PtNode node, final int childrenOffset, final FormatOptions formatOptions) { - return (byte) makePtNodeFlags(node.mChars.length > 1, node.mFrequency >= 0, + return (byte) makePtNodeFlags(node.mChars.length > 1, node.isTerminal(), getByteSize(childrenOffset), node.mShortcutTargets != null && !node.mShortcutTargets.isEmpty(), node.mBigrams != null, node.mIsNotAWord, node.mIsBlacklistEntry, formatOptions); @@ -833,10 +833,10 @@ public class BinaryDictEncoderUtils { + ptNode.mCachedAddressAfterUpdate); } // Sanity checks. - if (DBG && ptNode.mFrequency > FormatSpec.MAX_TERMINAL_FREQUENCY) { + if (DBG && ptNode.getProbability() > FormatSpec.MAX_TERMINAL_FREQUENCY) { throw new RuntimeException("A node has a frequency > " + FormatSpec.MAX_TERMINAL_FREQUENCY - + " : " + ptNode.mFrequency); + + " : " + ptNode.mProbabilityInfo.toString()); } dictEncoder.writePtNode(ptNode, parentPosition, formatOptions, dict); } @@ -871,7 +871,7 @@ public class BinaryDictEncoderUtils { for (final PtNode ptNode : ptNodeArray.mData) { ++ptNodes; if (ptNode.mChars.length > maxRuns) maxRuns = ptNode.mChars.length; - if (ptNode.mFrequency >= 0) { + if (ptNode.isTerminal()) { if (ptNodeArray.mCachedAddressAfterUpdate < firstTerminalAddress) firstTerminalAddress = ptNodeArray.mCachedAddressAfterUpdate; if (ptNodeArray.mCachedAddressAfterUpdate > lastTerminalAddress) diff --git a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java index dea9f2e28..54446df49 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictIOUtils.java @@ -21,7 +21,6 @@ import com.android.inputmethod.latin.Constants; import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding; import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer; import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; -import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode; import com.android.inputmethod.latin.utils.ByteArrayDictBuffer; import java.io.File; @@ -91,21 +90,23 @@ public final class BinaryDictIOUtils { stack.pop(); continue; } - PtNodeInfo info = dictDecoder.readPtNode(p.mAddress, formatOptions); - for (int i = 0; i < info.mCharacters.length; ++i) { - pushedChars[index++] = info.mCharacters[i]; + final PtNodeInfo ptNodeInfo = dictDecoder.readPtNode(p.mAddress, formatOptions); + for (int i = 0; i < ptNodeInfo.mCharacters.length; ++i) { + pushedChars[index++] = ptNodeInfo.mCharacters[i]; } p.mPosition++; - final boolean isMovedPtNode = isMovedPtNode(info.mFlags, + final boolean isMovedPtNode = isMovedPtNode(ptNodeInfo.mFlags, formatOptions); - final boolean isDeletedPtNode = isDeletedPtNode(info.mFlags, + final boolean isDeletedPtNode = isDeletedPtNode(ptNodeInfo.mFlags, formatOptions); - if (!isMovedPtNode && !isDeletedPtNode - && info.mFrequency != FusionDictionary.PtNode.NOT_A_TERMINAL) {// found word - words.put(info.mOriginalAddress, new String(pushedChars, 0, index)); - frequencies.put(info.mOriginalAddress, info.mFrequency); - if (info.mBigrams != null) bigrams.put(info.mOriginalAddress, info.mBigrams); + if (!isMovedPtNode && !isDeletedPtNode && ptNodeInfo.isTerminal()) {// found word + words.put(ptNodeInfo.mOriginalAddress, new String(pushedChars, 0, index)); + frequencies.put( + ptNodeInfo.mOriginalAddress, ptNodeInfo.mProbabilityInfo.mProbability); + if (ptNodeInfo.mBigrams != null) { + bigrams.put(ptNodeInfo.mOriginalAddress, ptNodeInfo.mBigrams); + } } if (p.mPosition == p.mNumOfPtNode) { @@ -127,8 +128,8 @@ public final class BinaryDictIOUtils { p.mAddress = dictDecoder.getPosition(); } - if (!isMovedPtNode && hasChildrenAddress(info.mChildrenAddress)) { - final Position childrenPos = new Position(info.mChildrenAddress, index); + if (!isMovedPtNode && hasChildrenAddress(ptNodeInfo.mChildrenAddress)) { + final Position childrenPos = new Position(ptNodeInfo.mChildrenAddress, index); stack.push(childrenPos); } } @@ -203,8 +204,7 @@ public final class BinaryDictIOUtils { if (same) { // found the PtNode matches the word. if (wordPos + currentInfo.mCharacters.length == wordLen) { - if (currentInfo.mFrequency == PtNode.NOT_A_TERMINAL - || isDeletedNode) { + if (!currentInfo.isTerminal() || isDeletedNode) { return FormatSpec.NOT_VALID_WORD; } else { return ptNodePos; diff --git a/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java index bba1d434f..a93a8bbad 100644 --- a/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/DictDecoder.java @@ -228,8 +228,6 @@ public interface DictDecoder { } } - public void skipPtNode(final FormatOptions formatOptions); - /** * @return whether this decoder has a valid binary dictionary that it can decode. */ diff --git a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java index ca4a2e9bb..8f73b27b5 100644 --- a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java +++ b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java @@ -107,24 +107,26 @@ public final class FusionDictionary implements Iterable<WordProperty> { } /** - * PtNode is a group of characters, with a frequency, shortcut targets, bigrams, and children - * (Pt means Patricia Trie). + * PtNode is a group of characters, with probability information, shortcut targets, bigrams, + * and children (Pt means Patricia Trie). * * This is the central class of the in-memory representation. A PtNode is what can * be seen as a traditional "trie node", except it can hold several characters at the * same time. A PtNode essentially represents one or several characters in the middle * of the trie tree; as such, it can be a terminal, and it can have children. * In this in-memory representation, whether the PtNode is a terminal or not is represented - * in the frequency, where NOT_A_TERMINAL (= -1) means this is not a terminal and any other - * value is the frequency of this terminal. A terminal may have non-null shortcuts and/or - * bigrams, but a non-terminal may not. Moreover, children, if present, are null. + * by mProbabilityInfo. The PtNode is a terminal when the mProbabilityInfo is not null and the + * PtNode is not a terminal when the mProbabilityInfo is null. A terminal may have non-null + * shortcuts and/or bigrams, but a non-terminal may not. Moreover, children, if present, + * are non-null. */ public static final class PtNode { - public static final int NOT_A_TERMINAL = -1; + private static final int NOT_A_TERMINAL = -1; final int mChars[]; ArrayList<WeightedString> mShortcutTargets; ArrayList<WeightedString> mBigrams; - int mFrequency; // NOT_A_TERMINAL == mFrequency indicates this is not a terminal. + // null == mProbabilityInfo indicates this is not a terminal. + ProbabilityInfo mProbabilityInfo; int mTerminalId; // NOT_A_TERMINAL == mTerminalId indicates this is not a terminal. PtNodeArray mChildren; boolean mIsNotAWord; // Only a shortcut @@ -140,11 +142,11 @@ public final class FusionDictionary implements Iterable<WordProperty> { int mCachedAddressAfterUpdate; // The address of this PtNode (after update) public PtNode(final int[] chars, final ArrayList<WeightedString> shortcutTargets, - final ArrayList<WeightedString> bigrams, final int frequency, + final ArrayList<WeightedString> bigrams, final ProbabilityInfo probabilityInfo, final boolean isNotAWord, final boolean isBlacklistEntry) { mChars = chars; - mFrequency = frequency; - mTerminalId = frequency; + mProbabilityInfo = probabilityInfo; + mTerminalId = probabilityInfo == null ? NOT_A_TERMINAL : probabilityInfo.mProbability; mShortcutTargets = shortcutTargets; mBigrams = bigrams; mChildren = null; @@ -153,11 +155,11 @@ public final class FusionDictionary implements Iterable<WordProperty> { } public PtNode(final int[] chars, final ArrayList<WeightedString> shortcutTargets, - final ArrayList<WeightedString> bigrams, final int frequency, + final ArrayList<WeightedString> bigrams, final ProbabilityInfo probabilityInfo, final boolean isNotAWord, final boolean isBlacklistEntry, final PtNodeArray children) { mChars = chars; - mFrequency = frequency; + mProbabilityInfo = probabilityInfo; mShortcutTargets = shortcutTargets; mBigrams = bigrams; mChildren = children; @@ -177,11 +179,15 @@ public final class FusionDictionary implements Iterable<WordProperty> { } public boolean isTerminal() { - return NOT_A_TERMINAL != mFrequency; + return mProbabilityInfo != null; } - public int getFrequency() { - return mFrequency; + public int getProbability() { + if (isTerminal()) { + return mProbabilityInfo.mProbability; + } else { + return NOT_A_TERMINAL; + } } public boolean getIsNotAWord() { @@ -213,18 +219,18 @@ public final class FusionDictionary implements Iterable<WordProperty> { } /** - * Adds a word to the bigram list. Updates the probability if the word already + * Adds a word to the bigram list. Updates the probability information if the word already * exists. */ - public void addBigram(final String word, final int probability) { + public void addBigram(final String word, final ProbabilityInfo probabilityInfo) { if (mBigrams == null) { mBigrams = new ArrayList<WeightedString>(); } WeightedString bigram = getBigram(word); if (bigram != null) { - bigram.setProbability(probability); + bigram.mProbabilityInfo = probabilityInfo; } else { - bigram = new WeightedString(word, probability); + bigram = new WeightedString(word, probabilityInfo); mBigrams.add(bigram); } } @@ -270,12 +276,11 @@ public final class FusionDictionary implements Iterable<WordProperty> { * the existing ones if any. Note: unigram, bigram, and shortcut frequencies are only * updated if they are higher than the existing ones. */ - public void update(final int frequency, final ArrayList<WeightedString> shortcutTargets, + private void update(final ProbabilityInfo probabilityInfo, + final ArrayList<WeightedString> shortcutTargets, final ArrayList<WeightedString> bigrams, final boolean isNotAWord, final boolean isBlacklistEntry) { - if (frequency > mFrequency) { - mFrequency = frequency; - } + mProbabilityInfo = ProbabilityInfo.max(mProbabilityInfo, probabilityInfo); if (shortcutTargets != null) { if (mShortcutTargets == null) { mShortcutTargets = shortcutTargets; @@ -286,8 +291,9 @@ public final class FusionDictionary implements Iterable<WordProperty> { final WeightedString existingShortcut = getShortcut(shortcut.mWord); if (existingShortcut == null) { mShortcutTargets.add(shortcut); - } else if (existingShortcut.getProbability() < shortcut.getProbability()) { - existingShortcut.setProbability(shortcut.getProbability()); + } else { + existingShortcut.mProbabilityInfo = ProbabilityInfo.max( + existingShortcut.mProbabilityInfo, shortcut.mProbabilityInfo); } } } @@ -302,8 +308,9 @@ public final class FusionDictionary implements Iterable<WordProperty> { final WeightedString existingBigram = getBigram(bigram.mWord); if (existingBigram == null) { mBigrams.add(bigram); - } else if (existingBigram.getProbability() < bigram.getProbability()) { - existingBigram.setProbability(bigram.getProbability()); + } else { + existingBigram.mProbabilityInfo = ProbabilityInfo.max( + existingBigram.mProbabilityInfo, bigram.mProbabilityInfo); } } } @@ -393,13 +400,13 @@ public final class FusionDictionary implements Iterable<WordProperty> { * they will be added to the dictionary as necessary. * * @param word the word to add. - * @param frequency the frequency of the word, in the range [0..255]. + * @param probabilityInfo probability information of the word. * @param shortcutTargets a list of shortcut targets for this word, or null. * @param isNotAWord true if this should not be considered a word (e.g. shortcut only) */ - public void add(final String word, final int frequency, + public void add(final String word, final ProbabilityInfo probabilityInfo, final ArrayList<WeightedString> shortcutTargets, final boolean isNotAWord) { - add(getCodePoints(word), frequency, shortcutTargets, isNotAWord, + add(getCodePoints(word), probabilityInfo, shortcutTargets, isNotAWord, false /* isBlacklistEntry */); } @@ -412,7 +419,8 @@ public final class FusionDictionary implements Iterable<WordProperty> { */ public void addBlacklistEntry(final String word, final ArrayList<WeightedString> shortcutTargets, final boolean isNotAWord) { - add(getCodePoints(word), 0, shortcutTargets, isNotAWord, true /* isBlacklistEntry */); + add(getCodePoints(word), new ProbabilityInfo(0), shortcutTargets, isNotAWord, + true /* isBlacklistEntry */); } /** @@ -438,21 +446,22 @@ public final class FusionDictionary implements Iterable<WordProperty> { * * @param word0 the previous word of the context * @param word1 the next word of the context - * @param frequency the bigram frequency + * @param probabilityInfo the bigram probability info */ - public void setBigram(final String word0, final String word1, final int frequency) { + public void setBigram(final String word0, final String word1, + final ProbabilityInfo probabilityInfo) { PtNode ptNode0 = findWordInTree(mRootNodeArray, word0); if (ptNode0 != null) { final PtNode ptNode1 = findWordInTree(mRootNodeArray, word1); if (ptNode1 == null) { - add(getCodePoints(word1), 0, null, false /* isNotAWord */, + add(getCodePoints(word1), new ProbabilityInfo(0), null, false /* isNotAWord */, false /* isBlacklistEntry */); // The PtNode for the first word may have moved by the above insertion, // if word1 and word2 share a common stem that happens not to have been // a cutting point until now. In this case, we need to refresh ptNode. ptNode0 = findWordInTree(mRootNodeArray, word0); } - ptNode0.addBigram(word1, frequency); + ptNode0.addBigram(word1, probabilityInfo); } else { throw new RuntimeException("First word of bigram not found " + word0); } @@ -465,15 +474,15 @@ public final class FusionDictionary implements Iterable<WordProperty> { * an exception is thrown. * * @param word the word, as an int array. - * @param frequency the frequency of the word, in the range [0..255]. + * @param probabilityInfo the probability information of the word. * @param shortcutTargets an optional list of shortcut targets for this word (null if none). * @param isNotAWord true if this is not a word for spellcheking purposes (shortcut only or so) * @param isBlacklistEntry true if this is a blacklisted word, false otherwise */ - private void add(final int[] word, final int frequency, + private void add(final int[] word, final ProbabilityInfo probabilityInfo, final ArrayList<WeightedString> shortcutTargets, final boolean isNotAWord, final boolean isBlacklistEntry) { - assert(frequency >= 0 && frequency <= 255); + assert(probabilityInfo.mProbability <= FormatSpec.MAX_TERMINAL_FREQUENCY); if (word.length >= Constants.DICTIONARY_MAX_WORD_LENGTH) { MakedictLog.w("Ignoring a word that is too long: word.length = " + word.length); return; @@ -501,7 +510,8 @@ public final class FusionDictionary implements Iterable<WordProperty> { // No node at this point to accept the word. Create one. final int insertionIndex = findInsertionIndex(currentNodeArray, word[charIndex]); final PtNode newPtNode = new PtNode(Arrays.copyOfRange(word, charIndex, word.length), - shortcutTargets, null /* bigrams */, frequency, isNotAWord, isBlacklistEntry); + shortcutTargets, null /* bigrams */, probabilityInfo, isNotAWord, + isBlacklistEntry); currentNodeArray.mData.add(insertionIndex, newPtNode); if (DBG) checkStack(currentNodeArray); } else { @@ -511,15 +521,15 @@ public final class FusionDictionary implements Iterable<WordProperty> { // The new word is a prefix of an existing word, but the node on which it // should end already exists as is. Since the old PtNode was not a terminal, // make it one by filling in its frequency and other attributes - currentPtNode.update(frequency, shortcutTargets, null, isNotAWord, + currentPtNode.update(probabilityInfo, shortcutTargets, null, isNotAWord, isBlacklistEntry); } else { // The new word matches the full old word and extends past it. // We only have to create a new node and add it to the end of this. final PtNode newNode = new PtNode( Arrays.copyOfRange(word, charIndex + differentCharIndex, word.length), - shortcutTargets, null /* bigrams */, frequency, isNotAWord, - isBlacklistEntry); + shortcutTargets, null /* bigrams */, probabilityInfo, + isNotAWord, isBlacklistEntry); currentPtNode.mChildren = new PtNodeArray(); currentPtNode.mChildren.mData.add(newNode); } @@ -527,7 +537,7 @@ public final class FusionDictionary implements Iterable<WordProperty> { if (0 == differentCharIndex) { // Exact same word. Update the frequency if higher. This will also add the // new shortcuts to the existing shortcut list if it already exists. - currentPtNode.update(frequency, shortcutTargets, null, + currentPtNode.update(probabilityInfo, shortcutTargets, null, currentPtNode.mIsNotAWord && isNotAWord, currentPtNode.mIsBlacklistEntry || isBlacklistEntry); } else { @@ -537,7 +547,7 @@ public final class FusionDictionary implements Iterable<WordProperty> { final PtNode newOldWord = new PtNode( Arrays.copyOfRange(currentPtNode.mChars, differentCharIndex, currentPtNode.mChars.length), currentPtNode.mShortcutTargets, - currentPtNode.mBigrams, currentPtNode.mFrequency, + currentPtNode.mBigrams, currentPtNode.mProbabilityInfo, currentPtNode.mIsNotAWord, currentPtNode.mIsBlacklistEntry, currentPtNode.mChildren); newChildren.mData.add(newOldWord); @@ -546,16 +556,17 @@ public final class FusionDictionary implements Iterable<WordProperty> { if (charIndex + differentCharIndex >= word.length) { newParent = new PtNode( Arrays.copyOfRange(currentPtNode.mChars, 0, differentCharIndex), - shortcutTargets, null /* bigrams */, frequency, + shortcutTargets, null /* bigrams */, probabilityInfo, isNotAWord, isBlacklistEntry, newChildren); } else { newParent = new PtNode( Arrays.copyOfRange(currentPtNode.mChars, 0, differentCharIndex), - null /* shortcutTargets */, null /* bigrams */, -1, - false /* isNotAWord */, false /* isBlacklistEntry */, newChildren); + null /* shortcutTargets */, null /* bigrams */, + null /* probabilityInfo */, false /* isNotAWord */, + false /* isBlacklistEntry */, newChildren); final PtNode newWord = new PtNode(Arrays.copyOfRange(word, charIndex + differentCharIndex, word.length), - shortcutTargets, null /* bigrams */, frequency, + shortcutTargets, null /* bigrams */, probabilityInfo, isNotAWord, isBlacklistEntry); final int addIndex = word[charIndex + differentCharIndex] > currentPtNode.mChars[differentCharIndex] ? 1 : 0; @@ -617,8 +628,8 @@ public final class FusionDictionary implements Iterable<WordProperty> { private static int findInsertionIndex(final PtNodeArray nodeArray, int character) { final ArrayList<PtNode> data = nodeArray.mData; final PtNode reference = new PtNode(new int[] { character }, - null /* shortcutTargets */, null /* bigrams */, 0, false /* isNotAWord */, - false /* isBlacklistEntry */); + null /* shortcutTargets */, null /* bigrams */, null /* probabilityInfo */, + false /* isNotAWord */, false /* isBlacklistEntry */); int result = Collections.binarySearch(data, reference, PTNODE_COMPARATOR); return result >= 0 ? result : -result - 1; } @@ -752,8 +763,9 @@ public final class FusionDictionary implements Iterable<WordProperty> { currentPos.length = mCurrentString.length(); mPositions.addLast(currentPos); } - if (currentPtNode.mFrequency >= 0) { - return new WordProperty(mCurrentString.toString(), currentPtNode.mFrequency, + if (currentPtNode.isTerminal()) { + return new WordProperty(mCurrentString.toString(), + currentPtNode.mProbabilityInfo, currentPtNode.mShortcutTargets, currentPtNode.mBigrams, currentPtNode.mIsNotAWord, currentPtNode.mIsBlacklistEntry); } diff --git a/java/src/com/android/inputmethod/latin/makedict/ProbabilityInfo.java b/java/src/com/android/inputmethod/latin/makedict/ProbabilityInfo.java index dafbc04b9..9dcd63f0c 100644 --- a/java/src/com/android/inputmethod/latin/makedict/ProbabilityInfo.java +++ b/java/src/com/android/inputmethod/latin/makedict/ProbabilityInfo.java @@ -30,6 +30,21 @@ public final class ProbabilityInfo { public final int mLevel; public final int mCount; + public static ProbabilityInfo max(final ProbabilityInfo probabilityInfo1, + final ProbabilityInfo probabilityInfo2) { + if (probabilityInfo1 == null) { + return probabilityInfo2; + } + if (probabilityInfo2 == null) { + return probabilityInfo1; + } + if (probabilityInfo1.mProbability > probabilityInfo2.mProbability) { + return probabilityInfo1; + } else { + return probabilityInfo2; + } + } + public ProbabilityInfo(final int probability) { this(probability, BinaryDictionary.NOT_A_VALID_TIMESTAMP, 0, 0); } diff --git a/java/src/com/android/inputmethod/latin/makedict/PtNodeInfo.java b/java/src/com/android/inputmethod/latin/makedict/PtNodeInfo.java index 188de7a0f..4760aa8d5 100644 --- a/java/src/com/android/inputmethod/latin/makedict/PtNodeInfo.java +++ b/java/src/com/android/inputmethod/latin/makedict/PtNodeInfo.java @@ -29,24 +29,28 @@ public final class PtNodeInfo { public final int mEndAddress; public final int mFlags; public final int[] mCharacters; - public final int mFrequency; + public final ProbabilityInfo mProbabilityInfo; public final int mChildrenAddress; public final int mParentAddress; public final ArrayList<WeightedString> mShortcutTargets; public final ArrayList<PendingAttribute> mBigrams; public PtNodeInfo(final int originalAddress, final int endAddress, final int flags, - final int[] characters, final int frequency, final int parentAddress, + final int[] characters, final ProbabilityInfo probabilityInfo, final int parentAddress, final int childrenAddress, final ArrayList<WeightedString> shortcutTargets, final ArrayList<PendingAttribute> bigrams) { mOriginalAddress = originalAddress; mEndAddress = endAddress; mFlags = flags; mCharacters = characters; - mFrequency = frequency; + mProbabilityInfo = probabilityInfo; mParentAddress = parentAddress; mChildrenAddress = childrenAddress; mShortcutTargets = shortcutTargets; mBigrams = bigrams; } + + public boolean isTerminal() { + return mProbabilityInfo != null; + } } diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java index d35f780cf..fd6138d41 100644 --- a/java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/Ver2DictDecoder.java @@ -20,7 +20,6 @@ import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.CharEncoding; import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer; import com.android.inputmethod.latin.makedict.FormatSpec.FormatOptions; -import com.android.inputmethod.latin.makedict.FusionDictionary.PtNode; import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; import android.util.Log; @@ -38,10 +37,82 @@ import java.util.Arrays; public class Ver2DictDecoder extends AbstractDictDecoder { private static final String TAG = Ver2DictDecoder.class.getSimpleName(); - protected static class PtNodeReader extends AbstractDictDecoder.PtNodeReader { - private static int readFrequency(final DictBuffer dictBuffer) { + /** + * A utility class for reading a PtNode. + */ + protected static class PtNodeReader { + private static ProbabilityInfo readProbabilityInfo(final DictBuffer dictBuffer) { + // Ver2 dicts don't contain historical information. + return new ProbabilityInfo(dictBuffer.readUnsignedByte()); + } + + protected static int readPtNodeOptionFlags(final DictBuffer dictBuffer) { return dictBuffer.readUnsignedByte(); } + + protected static int readChildrenAddress(final DictBuffer dictBuffer, + final int ptNodeFlags) { + switch (ptNodeFlags & FormatSpec.MASK_CHILDREN_ADDRESS_TYPE) { + case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_ONEBYTE: + return dictBuffer.readUnsignedByte(); + case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_TWOBYTES: + return dictBuffer.readUnsignedShort(); + case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_THREEBYTES: + return dictBuffer.readUnsignedInt24(); + case FormatSpec.FLAG_CHILDREN_ADDRESS_TYPE_NOADDRESS: + default: + return FormatSpec.NO_CHILDREN_ADDRESS; + } + } + + // Reads shortcuts and returns the read length. + protected static int readShortcut(final DictBuffer dictBuffer, + final ArrayList<WeightedString> shortcutTargets) { + final int pointerBefore = dictBuffer.position(); + dictBuffer.readUnsignedShort(); // skip the size + while (true) { + final int targetFlags = dictBuffer.readUnsignedByte(); + final String word = CharEncoding.readString(dictBuffer); + shortcutTargets.add(new WeightedString(word, + targetFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY)); + if (0 == (targetFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break; + } + return dictBuffer.position() - pointerBefore; + } + + protected static int readBigramAddresses(final DictBuffer dictBuffer, + final ArrayList<PendingAttribute> bigrams, final int baseAddress) { + int readLength = 0; + int bigramCount = 0; + while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) { + final int bigramFlags = dictBuffer.readUnsignedByte(); + ++readLength; + final int sign = 0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_ATTR_OFFSET_NEGATIVE) + ? 1 : -1; + int bigramAddress = baseAddress + readLength; + switch (bigramFlags & FormatSpec.MASK_BIGRAM_ATTR_ADDRESS_TYPE) { + case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE: + bigramAddress += sign * dictBuffer.readUnsignedByte(); + readLength += 1; + break; + case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES: + bigramAddress += sign * dictBuffer.readUnsignedShort(); + readLength += 2; + break; + case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES: + bigramAddress += sign * dictBuffer.readUnsignedInt24(); + readLength += 3; + break; + default: + throw new RuntimeException("Has bigrams with no address"); + } + bigrams.add(new PendingAttribute( + bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY, + bigramAddress)); + if (0 == (bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) break; + } + return readLength; + } } protected final File mDictionaryBinaryFile; @@ -95,8 +166,8 @@ public class Ver2DictDecoder extends AbstractDictDecoder { } final DictionaryHeader header = super.readHeader(mDictBuffer); final int version = header.mFormatOptions.mVersion; - if (!(version >= 2 && version <= 3)) { - throw new UnsupportedFormatException("File header has a wrong version : " + version); + if (version != FormatSpec.VERSION2) { + throw new UnsupportedFormatException("File header has a wrong version : " + version); } return header; } @@ -108,12 +179,6 @@ public class Ver2DictDecoder extends AbstractDictDecoder { int addressPointer = ptNodePos; final int flags = PtNodeReader.readPtNodeOptionFlags(mDictBuffer); addressPointer += FormatSpec.PTNODE_FLAGS_SIZE; - - final int parentAddress = PtNodeReader.readParentAddress(mDictBuffer, options); - if (BinaryDictIOUtils.supportsDynamicUpdate(options)) { - addressPointer += FormatSpec.PARENT_ADDRESS_SIZE; - } - final int characters[]; if (0 != (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS)) { int index = 0; @@ -133,14 +198,14 @@ public class Ver2DictDecoder extends AbstractDictDecoder { addressPointer += CharEncoding.getCharSize(character); characters = new int[] { character }; } - final int frequency; + final ProbabilityInfo probabilityInfo; if (0 != (FormatSpec.FLAG_IS_TERMINAL & flags)) { - frequency = PtNodeReader.readFrequency(mDictBuffer); + probabilityInfo = PtNodeReader.readProbabilityInfo(mDictBuffer); addressPointer += FormatSpec.PTNODE_FREQUENCY_SIZE; } else { - frequency = PtNode.NOT_A_TERMINAL; + probabilityInfo = null; } - int childrenAddress = PtNodeReader.readChildrenAddress(mDictBuffer, flags, options); + int childrenAddress = PtNodeReader.readChildrenAddress(mDictBuffer, flags); if (childrenAddress != FormatSpec.NO_CHILDREN_ADDRESS) { childrenAddress += addressPointer; } @@ -166,8 +231,8 @@ public class Ver2DictDecoder extends AbstractDictDecoder { } else { bigrams = null; } - return new PtNodeInfo(ptNodePos, addressPointer, flags, characters, frequency, - parentAddress, childrenAddress, shortcutTargets, bigrams); + return new PtNodeInfo(ptNodePos, addressPointer, flags, characters, probabilityInfo, + FormatSpec.NO_PARENT_ADDRESS, childrenAddress, shortcutTargets, bigrams); } @Override @@ -223,40 +288,4 @@ public class Ver2DictDecoder extends AbstractDictDecoder { public boolean hasNextPtNodeArray() { return mDictBuffer.position() != FormatSpec.NO_FORWARD_LINK_ADDRESS; } - - @Override - public void skipPtNode(final FormatOptions formatOptions) { - final int flags = PtNodeReader.readPtNodeOptionFlags(mDictBuffer); - PtNodeReader.readParentAddress(mDictBuffer, formatOptions); - BinaryDictIOUtils.skipString(mDictBuffer, - (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS) != 0); - PtNodeReader.readChildrenAddress(mDictBuffer, flags, formatOptions); - if ((flags & FormatSpec.FLAG_IS_TERMINAL) != 0) PtNodeReader.readFrequency(mDictBuffer); - if ((flags & FormatSpec.FLAG_HAS_SHORTCUT_TARGETS) != 0) { - final int shortcutsSize = mDictBuffer.readUnsignedShort(); - mDictBuffer.position(mDictBuffer.position() + shortcutsSize - - FormatSpec.PTNODE_SHORTCUT_LIST_SIZE_SIZE); - } - if ((flags & FormatSpec.FLAG_HAS_BIGRAMS) != 0) { - int bigramCount = 0; - while (bigramCount++ < FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) { - final int bigramFlags = mDictBuffer.readUnsignedByte(); - switch (bigramFlags & FormatSpec.MASK_BIGRAM_ATTR_ADDRESS_TYPE) { - case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_ONEBYTE: - mDictBuffer.readUnsignedByte(); - break; - case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_TWOBYTES: - mDictBuffer.readUnsignedShort(); - break; - case FormatSpec.FLAG_BIGRAM_ATTR_ADDRESS_TYPE_THREEBYTES: - mDictBuffer.readUnsignedInt24(); - break; - } - if ((bigramFlags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT) == 0) break; - } - if (bigramCount >= FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) { - throw new RuntimeException("Too many bigrams in a PtNode."); - } - } - } } diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java index 3de083ef3..634f63137 100644 --- a/java/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/Ver2DictEncoder.java @@ -227,7 +227,7 @@ public class Ver2DictEncoder implements DictEncoder { final PtNode target = FusionDictionary.findWordInTree(dict.mRootNodeArray, bigram.mWord); final int addressOfBigram = target.mCachedAddressAfterUpdate; - final int unigramFrequencyForThisWord = target.mFrequency; + final int unigramFrequencyForThisWord = target.getProbability(); final int offset = addressOfBigram - (mPosition + FormatSpec.PTNODE_ATTRIBUTE_FLAGS_SIZE); final int bigramFlags = BinaryDictEncoderUtils.makeBigramFlags(bigramIterator.hasNext(), @@ -251,7 +251,7 @@ public class Ver2DictEncoder implements DictEncoder { writePtNodeFlags(ptNode, formatOptions); writeParentPosition(parentPosition, ptNode, formatOptions); writeCharacters(ptNode.mChars, ptNode.hasSeveralChars()); - writeFrequency(ptNode.mFrequency); + writeFrequency(ptNode.getProbability()); writeChildrenPosition(ptNode, formatOptions); writeShortcuts(ptNode.mShortcutTargets); writeBigrams(ptNode.mBigrams, dict); diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java index 83707480d..1c74852fe 100644 --- a/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java @@ -81,14 +81,11 @@ public class Ver4DictDecoder extends AbstractDictDecoder { // Insert unigrams to the fusion dictionary. for (final WordProperty wordProperty : wordProperties) { - // TODO: Support probability that is -1. - final int probability = wordProperty.getProbability() < 0 ? - 0 : wordProperty.getProbability(); if (wordProperty.mIsBlacklistEntry) { fusionDict.addBlacklistEntry(wordProperty.mWord, wordProperty.mShortcutTargets, wordProperty.mIsNotAWord); } else { - fusionDict.add(wordProperty.mWord, probability, + fusionDict.add(wordProperty.mWord, wordProperty.mProbabilityInfo, wordProperty.mShortcutTargets, wordProperty.mIsNotAWord); } } @@ -99,7 +96,7 @@ public class Ver4DictDecoder extends AbstractDictDecoder { } final String word0 = wordProperty.mWord; for (final WeightedString bigram : wordProperty.mBigrams) { - fusionDict.setBigram(word0, bigram.mWord, bigram.getProbability()); + fusionDict.setBigram(word0, bigram.mWord, bigram.mProbabilityInfo); } } return fusionDict; diff --git a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java index b93a0a525..1fc61e10a 100644 --- a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java +++ b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java @@ -43,12 +43,12 @@ public final class WordProperty implements Comparable<WordProperty> { private int mHashCode = 0; - public WordProperty(final String word, final int probability, + public WordProperty(final String word, final ProbabilityInfo probabilityInfo, final ArrayList<WeightedString> shortcutTargets, final ArrayList<WeightedString> bigrams, final boolean isNotAWord, final boolean isBlacklistEntry) { mWord = word; - mProbabilityInfo = new ProbabilityInfo(probability); + mProbabilityInfo = probabilityInfo; mShortcutTargets = shortcutTargets; mBigrams = bigrams; mIsNotAWord = isNotAWord; diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java index a89f70e1f..4063edccc 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripLayoutHelper.java @@ -504,15 +504,9 @@ final class SuggestionStripLayoutHelper { titleView, infoIcon, null, moreIcon, null); final CharSequence importantNoticeTitle = titleView.getText(); titleView.setTextScaleX(1.0f); // Reset textScaleX. - // When the suggestions strip is displayed first time, stripWidth may be zero. - // Then importantNoticeTitle will be displayed as is without auto text scaleX. - // TODO: Fix the logic to always have a correct value of stripWidth. - if (width > 0) { - // Auto text scaleX to show entire important notice title should be on the strip. - final float titleScaleX = getTextScaleX( - importantNoticeTitle, width, titleView.getPaint()); - titleView.setTextScaleX(titleScaleX); - } + final float titleScaleX = getTextScaleX( + importantNoticeTitle, width, titleView.getPaint()); + titleView.setTextScaleX(titleScaleX); } static void setLayoutWeight(final View v, final float weight, final int height) { @@ -529,7 +523,7 @@ final class SuggestionStripLayoutHelper { final TextPaint paint) { paint.setTextScaleX(1.0f); final int width = getTextWidth(text, paint); - if (width <= maxWidth) { + if (width <= maxWidth || maxWidth <= 0) { return 1.0f; } return maxWidth / (float)width; diff --git a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java index b04a2cb5b..68c891bf3 100644 --- a/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java +++ b/java/src/com/android/inputmethod/latin/suggestions/SuggestionStripView.java @@ -46,6 +46,7 @@ import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.suggestions.MoreSuggestions.MoreSuggestionsListener; import com.android.inputmethod.latin.utils.CollectionUtils; +import com.android.inputmethod.latin.utils.ImportantNoticeUtils; import com.android.inputmethod.research.ResearchLogger; import java.util.ArrayList; @@ -220,11 +221,20 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick return false; } - // TODO: This method should be called after this View has been attached and displayed. - public void showImportantNoticeTitle() { - mLayoutHelper.layoutImportantNotice(mImportantNoticeStrip, getWidth()); + // This method checks if we should show the important notice (checks on permanent storage if + // it has been shown once already or not, and if in the setup wizard). If applicable, it shows + // the notice. In all cases, it returns true if it was shown, false otherwise. + public boolean maybeShowImportantNoticeTitle() { + if (!ImportantNoticeUtils.hasNewImportantNotice(getContext()) + || ImportantNoticeUtils.isInSystemSetupWizard(getContext())) { + return false; + } + final int width = getWidth(); + if (width <= 0) return false; + mLayoutHelper.layoutImportantNotice(mImportantNoticeStrip, width); mStripVisibilityGroup.showImportantNoticeStrip(); mImportantNoticeStrip.setOnClickListener(this); + return true; } public void clear() { @@ -415,4 +425,11 @@ public final class SuggestionStripView extends RelativeLayout implements OnClick super.onDetachedFromWindow(); dismissMoreSuggestionsPanel(); } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + // Called by the framework when the size is known. Show the important notice if applicable. + // This may be overriden by showing suggestions later, if applicable. + maybeShowImportantNoticeTitle(); + } } |