diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin')
26 files changed, 648 insertions, 1153 deletions
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java index b20bcd1f9..013f9220a 100644 --- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java @@ -26,12 +26,12 @@ import com.android.inputmethod.latin.makedict.DictionaryHeader; import com.android.inputmethod.latin.makedict.FormatSpec; import com.android.inputmethod.latin.makedict.FusionDictionary.DictionaryOptions; import com.android.inputmethod.latin.makedict.UnsupportedFormatException; +import com.android.inputmethod.latin.makedict.WordProperty; import com.android.inputmethod.latin.settings.NativeSuggestOptions; import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.JniUtils; import com.android.inputmethod.latin.utils.LanguageModelParam; import com.android.inputmethod.latin.utils.StringUtils; -import com.android.inputmethod.latin.utils.WordProperty; import java.io.File; import java.util.ArrayList; @@ -217,9 +217,8 @@ public final class BinaryDictionary extends Dictionary { outAttributeValues.get(i)); attributes.put(attributeKey, attributeValue); } - final boolean hasHistoricalInfo = - attributes.get(DictionaryHeader.HAS_HISTORICAL_INFO_KEY).equals( - DictionaryHeader.ATTRIBUTE_VALUE_TRUE); + final boolean hasHistoricalInfo = DictionaryHeader.ATTRIBUTE_VALUE_TRUE.equals( + attributes.get(DictionaryHeader.HAS_HISTORICAL_INFO_KEY)); return new DictionaryHeader(outHeaderSize[0], new DictionaryOptions(attributes), new FormatSpec.FormatOptions(outFormatVersion[0], hasHistoricalInfo)); } diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java index e68c6b771..259c1372e 100644 --- a/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java +++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitatorForSuggest.java @@ -430,12 +430,19 @@ public class DictionaryFacilitatorForSuggest { public void getSuggestions(final WordComposer composer, final String prevWord, final ProximityInfo proximityInfo, final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, - final int sessionId, final Set<SuggestedWordInfo> suggestionSet) { + final int sessionId, final Set<SuggestedWordInfo> suggestionSet, + final ArrayList<SuggestedWordInfo> rawSuggestions) { for (final String key : mDictionaries.keySet()) { final Dictionary dictionary = mDictionaries.get(key); if (null == dictionary) continue; - suggestionSet.addAll(dictionary.getSuggestionsWithSessionId(composer, prevWord, - proximityInfo, blockOffensiveWords, additionalFeaturesOptions, sessionId)); + final ArrayList<SuggestedWordInfo> dictionarySuggestions = + dictionary.getSuggestionsWithSessionId(composer, prevWord, proximityInfo, + blockOffensiveWords, additionalFeaturesOptions, sessionId); + if (null == dictionarySuggestions) continue; + suggestionSet.addAll(dictionarySuggestions); + if (null != rawSuggestions) { + rawSuggestions.addAll(dictionarySuggestions); + } } } diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index 48a6588a2..c2451ce8d 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -23,13 +23,15 @@ import com.android.inputmethod.annotations.UsedForTesting; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.makedict.DictionaryHeader; import com.android.inputmethod.latin.makedict.FormatSpec; +import com.android.inputmethod.latin.makedict.UnsupportedFormatException; +import com.android.inputmethod.latin.makedict.WordProperty; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.utils.AsyncResultHolder; import com.android.inputmethod.latin.utils.CollectionUtils; +import com.android.inputmethod.latin.utils.CombinedFormatUtils; import com.android.inputmethod.latin.utils.FileUtils; import com.android.inputmethod.latin.utils.LanguageModelParam; import com.android.inputmethod.latin.utils.PrioritizedSerialExecutor; -import com.android.inputmethod.latin.utils.WordProperty; import java.io.File; import java.util.ArrayList; @@ -785,7 +787,14 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { getExecutor(mDictName).execute(new Runnable() { @Override public void run() { - Log.d(TAG, "dictionary=" + mDictName); + Log.d(TAG, "Dump dictionary: " + mDictName); + try { + final DictionaryHeader header = mBinaryDictionary.getHeader(); + Log.d(TAG, CombinedFormatUtils.formatAttributeMap( + header.mDictionaryOptions.mAttributes)); + } catch (final UnsupportedFormatException e) { + Log.d(TAG, "Cannot fetch header information.", e); + } int token = 0; do { final BinaryDictionary.GetNextWordPropertyResult result = diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java index e55c08dae..3b427720a 100644 --- a/java/src/com/android/inputmethod/latin/LatinIME.java +++ b/java/src/com/android/inputmethod/latin/LatinIME.java @@ -620,6 +620,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen ResearchLogger.getInstance().onDestroy(); } unregisterReceiver(mDictionaryPackInstallReceiver); + unregisterReceiver(mDictionaryDumpBroadcastReceiver); PersonalizationDictionarySessionRegistrar.close(this); LatinImeLogger.commit(); LatinImeLogger.onDestroy(); @@ -997,7 +998,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen SuggestedWords.getFromApplicationSpecifiedCompletions( applicationSpecifiedCompletions); final SuggestedWords suggestedWords = new SuggestedWords( - applicationSuggestedWords, + applicationSuggestedWords, null /* rawSuggestions */, false /* typedWordValid */, false /* willAutoCorrect */, false /* isPunctuationSuggestions */, @@ -1417,7 +1418,7 @@ public class LatinIME extends InputMethodService implements KeyboardActionListen ? SuggestedWords.EMPTY : previousSuggestedWords; final ArrayList<SuggestedWords.SuggestedWordInfo> typedWordAndPreviousSuggestions = SuggestedWords.getTypedWordAndPreviousSuggestions(typedWord, oldSuggestedWords); - return new SuggestedWords(typedWordAndPreviousSuggestions, + return new SuggestedWords(typedWordAndPreviousSuggestions, null /* rawSuggestions */, false /* typedWordValid */, false /* hasAutoCorrectionCandidate */, false /* isPunctuationSuggestions */, diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index bb938a99e..5e74d75b0 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -20,6 +20,7 @@ import android.text.TextUtils; import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.define.ProductionFlag; import com.android.inputmethod.latin.utils.AutoCorrectionUtils; import com.android.inputmethod.latin.utils.BoundedTreeSet; import com.android.inputmethod.latin.utils.CollectionUtils; @@ -51,7 +52,6 @@ public final class Suggest { private static final int SUPPRESS_SUGGEST_THRESHOLD = -2000000000; private static final boolean DBG = LatinImeLogger.sDBG; - public final DictionaryFacilitatorForSuggest mDictionaryFacilitator; private float mAutoCorrectionThreshold; @@ -124,9 +124,15 @@ public final class Suggest { } else { wordComposerForLookup = wordComposer; } + final ArrayList<SuggestedWordInfo> rawSuggestions; + if (ProductionFlag.INCLUDE_RAW_SUGGESTIONS) { + rawSuggestions = CollectionUtils.newArrayList(); + } else { + rawSuggestions = null; + } mDictionaryFacilitator.getSuggestions(wordComposerForLookup, prevWordForBigram, proximityInfo, blockOffensiveWords, additionalFeaturesOptions, SESSION_TYPING, - suggestionsSet); + suggestionsSet, rawSuggestions); final String firstSuggestion; final String whitelistedWord; if (suggestionsSet.isEmpty()) { @@ -141,8 +147,6 @@ public final class Suggest { } } - // The word can be auto-corrected if it has a whitelist entry that is not itself, - // or if it's a 2+ characters non-word (i.e. it's not in the dictionary). // We allow auto-correction if we have a whitelisted word, or if the word is not a valid // word of more than 1 char, except if the first suggestion is the same as the typed string // because in this case if it's strong enough to auto-correct that will mistakenly designate @@ -217,7 +221,7 @@ public final class Suggest { suggestionsList = suggestionsContainer; } - callback.onGetSuggestedWords(new SuggestedWords(suggestionsList, + callback.onGetSuggestedWords(new SuggestedWords(suggestionsList, rawSuggestions, // TODO: this first argument is lying. If this is a whitelisted word which is an // actual word, it says typedWordValid = false, which looks wrong. We should either // rename the attribute or change the value. @@ -237,8 +241,15 @@ public final class Suggest { final OnGetSuggestedWordsCallback callback) { final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator, SuggestedWords.MAX_SUGGESTIONS); + final ArrayList<SuggestedWordInfo> rawSuggestions; + if (ProductionFlag.INCLUDE_RAW_SUGGESTIONS) { + rawSuggestions = CollectionUtils.newArrayList(); + } else { + rawSuggestions = null; + } mDictionaryFacilitator.getSuggestions(wordComposer, prevWordForBigram, proximityInfo, - blockOffensiveWords, additionalFeaturesOptions, sessionId, suggestionsSet); + blockOffensiveWords, additionalFeaturesOptions, sessionId, suggestionsSet, + rawSuggestions); for (SuggestedWordInfo wordInfo : suggestionsSet) { LatinImeLogger.onAddSuggestedWord(wordInfo.mWord, wordInfo.mSourceDict.mDictType); } @@ -275,7 +286,7 @@ public final class Suggest { // In the batch input mode, the most relevant suggested word should act as a "typed word" // (typedWordValid=true), not as an "auto correct word" (willAutoCorrect=false). - callback.onGetSuggestedWords(new SuggestedWords(suggestionsContainer, + callback.onGetSuggestedWords(new SuggestedWords(suggestionsContainer, rawSuggestions, true /* typedWordValid */, false /* willAutoCorrect */, false /* isPunctuationSuggestions */, diff --git a/java/src/com/android/inputmethod/latin/SuggestedWords.java b/java/src/com/android/inputmethod/latin/SuggestedWords.java index dbaf822e4..b2efc4a86 100644 --- a/java/src/com/android/inputmethod/latin/SuggestedWords.java +++ b/java/src/com/android/inputmethod/latin/SuggestedWords.java @@ -37,7 +37,7 @@ public final class SuggestedWords { private static final ArrayList<SuggestedWordInfo> EMPTY_WORD_INFO_LIST = CollectionUtils.newArrayList(0); public static final SuggestedWords EMPTY = new SuggestedWords( - EMPTY_WORD_INFO_LIST, false, false, false, false, false); + EMPTY_WORD_INFO_LIST, null /* rawSuggestions */, false, false, false, false, false); public final String mTypedWord; public final boolean mTypedWordValid; @@ -50,25 +50,29 @@ public final class SuggestedWords { public final boolean mIsPrediction; public final int mSequenceNumber; // Sequence number for auto-commit. private final ArrayList<SuggestedWordInfo> mSuggestedWordInfoList; + public final ArrayList<SuggestedWordInfo> mRawSuggestions; public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList, + final ArrayList<SuggestedWordInfo> rawSuggestions, final boolean typedWordValid, final boolean willAutoCorrect, final boolean isPunctuationSuggestions, final boolean isObsoleteSuggestions, final boolean isPrediction) { - this(suggestedWordInfoList, typedWordValid, willAutoCorrect, isPunctuationSuggestions, - isObsoleteSuggestions, isPrediction, NOT_A_SEQUENCE_NUMBER); + this(suggestedWordInfoList, rawSuggestions, typedWordValid, willAutoCorrect, + isPunctuationSuggestions, isObsoleteSuggestions, isPrediction, + NOT_A_SEQUENCE_NUMBER); } public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList, + final ArrayList<SuggestedWordInfo> rawSuggestions, final boolean typedWordValid, final boolean willAutoCorrect, final boolean isPunctuationSuggestions, final boolean isObsoleteSuggestions, final boolean isPrediction, final int sequenceNumber) { - this(suggestedWordInfoList, + this(suggestedWordInfoList, rawSuggestions, suggestedWordInfoList.isEmpty() ? null : suggestedWordInfoList.get(INDEX_OF_TYPED_WORD).mWord, typedWordValid, willAutoCorrect, isPunctuationSuggestions, @@ -76,6 +80,7 @@ public final class SuggestedWords { } public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList, + final ArrayList<SuggestedWordInfo> rawSuggestions, final String typedWord, final boolean typedWordValid, final boolean willAutoCorrect, @@ -84,6 +89,7 @@ public final class SuggestedWords { final boolean isPrediction, final int sequenceNumber) { mSuggestedWordInfoList = suggestedWordInfoList; + mRawSuggestions = rawSuggestions; mTypedWordValid = typedWordValid; mWillAutoCorrect = willAutoCorrect; mIsPunctuationSuggestions = isPunctuationSuggestions; @@ -306,9 +312,9 @@ public final class SuggestedWords { } // We should never autocorrect, so we say the typed word is valid. Also, in this case, // no auto-correction should take place hence willAutoCorrect = false. - return new SuggestedWords(newSuggestions, typedWord, true /* typedWordValid */, - false /* willAutoCorrect */, mIsPunctuationSuggestions, mIsObsoleteSuggestions, - mIsPrediction, NOT_A_SEQUENCE_NUMBER); + return new SuggestedWords(newSuggestions, null /* rawSuggestions */, typedWord, + true /* typedWordValid */, false /* willAutoCorrect */, mIsPunctuationSuggestions, + mIsObsoleteSuggestions, mIsPrediction, NOT_A_SEQUENCE_NUMBER); } // Creates a new SuggestedWordInfo from the currently suggested words that removes all but the @@ -326,7 +332,7 @@ public final class SuggestedWords { info.mSourceDict, SuggestedWordInfo.NOT_AN_INDEX, SuggestedWordInfo.NOT_A_CONFIDENCE)); } - return new SuggestedWords(newSuggestions, mTypedWordValid, + return new SuggestedWords(newSuggestions, null /* rawSuggestions */, mTypedWordValid, mWillAutoCorrect, mIsPunctuationSuggestions, mIsObsoleteSuggestions, mIsPrediction); } diff --git a/java/src/com/android/inputmethod/latin/define/ProductionFlag.java b/java/src/com/android/inputmethod/latin/define/ProductionFlag.java index dc937fb25..e6fa1cdad 100644 --- a/java/src/com/android/inputmethod/latin/define/ProductionFlag.java +++ b/java/src/com/android/inputmethod/latin/define/ProductionFlag.java @@ -29,4 +29,7 @@ public final class ProductionFlag { public static final boolean USES_DEVELOPMENT_ONLY_DIAGNOSTICS_DEBUG = false; public static final boolean IS_HARDWARE_KEYBOARD_SUPPORTED = false; + + // Include all suggestions from all dictionaries in {@link SuggestedWords#mRawSuggestions}. + public static final boolean INCLUDE_RAW_SUGGESTIONS = false; } diff --git a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java index 3ecf5f0fb..6e9050593 100644 --- a/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java +++ b/java/src/com/android/inputmethod/latin/inputlogic/InputLogic.java @@ -1332,7 +1332,8 @@ public final class InputLogic { } else { // We found suggestion spans in the word. We'll create the SuggestedWords out of // them, and make willAutoCorrect false. - final SuggestedWords suggestedWords = new SuggestedWords(suggestions, typedWord, + final SuggestedWords suggestedWords = new SuggestedWords(suggestions, + null /* rawSuggestions */, typedWord, true /* typedWordValid */, false /* willAutoCorrect */, false /* isPunctuationSuggestions */, false /* isObsoleteSuggestions */, false /* isPrediction */, SuggestedWords.NOT_A_SEQUENCE_NUMBER); diff --git a/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java index 5c7c4b8e3..e7d1c98a9 100644 --- a/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/AbstractDictDecoder.java @@ -22,6 +22,7 @@ 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; import java.util.ArrayList; import java.util.HashMap; @@ -223,4 +224,49 @@ public abstract class AbstractDictDecoder implements DictDecoder { public boolean hasValidRawBinaryDictionary() { return checkHeader() == SUCCESS; } + + // Placeholder implementations below. These are actually unused. + @Override + public void openDictBuffer() throws FileNotFoundException, IOException, + UnsupportedFormatException { + } + + @Override + public boolean isDictBufferOpen() { + return false; + } + + @Override + public PtNodeInfo readPtNode(final int ptNodePos, final FormatOptions options) { + return null; + } + + @Override + public void setPosition(int newPos) { + } + + @Override + public int getPosition() { + return 0; + } + + @Override + public int readPtNodeCount() { + return 0; + } + + @Override + public boolean readAndFollowForwardLink() { + return false; + } + + @Override + 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 9f2345962..782ada3f4 100644 --- a/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java +++ b/java/src/com/android/inputmethod/latin/makedict/BinaryDictDecoderUtils.java @@ -606,19 +606,21 @@ public final class BinaryDictDecoderUtils { FusionDictionary newDict = new FusionDictionary(root, fileHeader.mDictionaryOptions); if (null != dict) { - for (final Word w : dict) { - if (w.mIsBlacklistEntry) { - newDict.addBlacklistEntry(w.mWord, w.mShortcutTargets, w.mIsNotAWord); + for (final WordProperty wordProperty : dict) { + if (wordProperty.mIsBlacklistEntry) { + newDict.addBlacklistEntry(wordProperty.mWord, wordProperty.mShortcutTargets, + wordProperty.mIsNotAWord); } else { - newDict.add(w.mWord, w.mFrequency, w.mShortcutTargets, w.mIsNotAWord); + newDict.add(wordProperty.mWord, wordProperty.getProbability(), + wordProperty.mShortcutTargets, wordProperty.mIsNotAWord); } } - for (final Word w : dict) { + for (final WordProperty wordProperty : dict) { // By construction a binary dictionary may not have bigrams pointing to // words that are not also registered as unigrams so we don't have to avoid // them explicitly here. - for (final WeightedString bigram : w.mBigrams) { - newDict.setBigram(w.mWord, bigram.mWord, bigram.getProbability()); + for (final WeightedString bigram : wordProperty.mBigrams) { + newDict.setBigram(wordProperty.mWord, bigram.mWord, bigram.getProbability()); } } } diff --git a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java index ef23acb71..ca4a2e9bb 100644 --- a/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java +++ b/java/src/com/android/inputmethod/latin/makedict/FusionDictionary.java @@ -31,7 +31,7 @@ import java.util.LinkedList; * A dictionary that can fusion heads and tails of words for more compression. */ @UsedForTesting -public final class FusionDictionary implements Iterable<Word> { +public final class FusionDictionary implements Iterable<WordProperty> { private static final boolean DBG = MakedictLog.DBG; private static int CHARACTER_NOT_FOUND_INDEX = -1; @@ -76,8 +76,12 @@ public final class FusionDictionary implements Iterable<Word> { public ProbabilityInfo mProbabilityInfo; public WeightedString(final String word, final int probability) { + this(word, new ProbabilityInfo(probability)); + } + + public WeightedString(final String word, final ProbabilityInfo probabilityInfo) { mWord = word; - mProbabilityInfo = new ProbabilityInfo(probability); + mProbabilityInfo = probabilityInfo; } public int getProbability() { @@ -90,9 +94,7 @@ public final class FusionDictionary implements Iterable<Word> { @Override public int hashCode() { - return Arrays.hashCode(new Object[] { mWord, mProbabilityInfo.mProbability, - mProbabilityInfo.mTimestamp, mProbabilityInfo.mLevel, - mProbabilityInfo.mCount }); + return Arrays.hashCode(new Object[] { mWord, mProbabilityInfo}); } @Override @@ -434,25 +436,25 @@ public final class FusionDictionary implements Iterable<Word> { /** * Helper method to add a new bigram to the dictionary. * - * @param word1 the previous word of the context - * @param word2 the next word of the context + * @param word0 the previous word of the context + * @param word1 the next word of the context * @param frequency the bigram frequency */ - public void setBigram(final String word1, final String word2, final int frequency) { - PtNode ptNode = findWordInTree(mRootNodeArray, word1); - if (ptNode != null) { - final PtNode ptNode2 = findWordInTree(mRootNodeArray, word2); - if (ptNode2 == null) { - add(getCodePoints(word2), 0, null, false /* isNotAWord */, + public void setBigram(final String word0, final String word1, final int frequency) { + PtNode ptNode0 = findWordInTree(mRootNodeArray, word0); + if (ptNode0 != null) { + final PtNode ptNode1 = findWordInTree(mRootNodeArray, word1); + if (ptNode1 == null) { + add(getCodePoints(word1), 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. - ptNode = findWordInTree(mRootNodeArray, word1); + ptNode0 = findWordInTree(mRootNodeArray, word0); } - ptNode.addBigram(word2, frequency); + ptNode0.addBigram(word1, frequency); } else { - throw new RuntimeException("First word of bigram not found"); + throw new RuntimeException("First word of bigram not found " + word0); } } @@ -704,7 +706,7 @@ public final class FusionDictionary implements Iterable<Word> { * * This is purely for convenience. */ - public static final class DictionaryIterator implements Iterator<Word> { + public static final class DictionaryIterator implements Iterator<WordProperty> { private static final class Position { public Iterator<PtNode> pos; public int length; @@ -734,7 +736,7 @@ public final class FusionDictionary implements Iterable<Word> { } @Override - public Word next() { + public WordProperty next() { Position currentPos = mPositions.getLast(); mCurrentString.setLength(currentPos.length); @@ -751,7 +753,7 @@ public final class FusionDictionary implements Iterable<Word> { mPositions.addLast(currentPos); } if (currentPtNode.mFrequency >= 0) { - return new Word(mCurrentString.toString(), currentPtNode.mFrequency, + return new WordProperty(mCurrentString.toString(), currentPtNode.mFrequency, currentPtNode.mShortcutTargets, currentPtNode.mBigrams, currentPtNode.mIsNotAWord, currentPtNode.mIsBlacklistEntry); } @@ -777,7 +779,7 @@ public final class FusionDictionary implements Iterable<Word> { * and say : for (Word w : x) {} */ @Override - public Iterator<Word> iterator() { + public Iterator<WordProperty> iterator() { return new DictionaryIterator(mRootNodeArray.mData); } } diff --git a/java/src/com/android/inputmethod/latin/makedict/ProbabilityInfo.java b/java/src/com/android/inputmethod/latin/makedict/ProbabilityInfo.java index 79f924cc6..dafbc04b9 100644 --- a/java/src/com/android/inputmethod/latin/makedict/ProbabilityInfo.java +++ b/java/src/com/android/inputmethod/latin/makedict/ProbabilityInfo.java @@ -17,7 +17,9 @@ package com.android.inputmethod.latin.makedict; import com.android.inputmethod.latin.BinaryDictionary; -import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; +import com.android.inputmethod.latin.utils.CombinedFormatUtils; + +import java.util.Arrays; public final class ProbabilityInfo { public final int mProbability; @@ -45,19 +47,28 @@ public final class ProbabilityInfo { } @Override + public int hashCode() { + if (hasHistoricalInfo()) { + return Arrays.hashCode(new Object[] { mProbability, mTimestamp, mLevel, mCount }); + } else { + return Arrays.hashCode(new Object[] { mProbability }); + } + } + + @Override public String toString() { - return mTimestamp + ":" + mLevel + ":" + mCount; + return CombinedFormatUtils.formatProbabilityInfo(this); } @Override public boolean equals(Object o) { - if (o == this) return true; - if (!(o instanceof ProbabilityInfo)) return false; - final ProbabilityInfo p = (ProbabilityInfo)o; - if (!hasHistoricalInfo() && !p.hasHistoricalInfo()) { - return mProbability == p.mProbability; - } - return mProbability == p.mProbability && mTimestamp == p.mTimestamp && mLevel == p.mLevel - && mCount == p.mCount; - } + if (o == this) return true; + if (!(o instanceof ProbabilityInfo)) return false; + final ProbabilityInfo p = (ProbabilityInfo)o; + if (!hasHistoricalInfo() && !p.hasHistoricalInfo()) { + return mProbability == p.mProbability; + } + return mProbability == p.mProbability && mTimestamp == p.mTimestamp && mLevel == p.mLevel + && mCount == p.mCount; + } }
\ No newline at end of file diff --git a/java/src/com/android/inputmethod/latin/makedict/SparseTable.java b/java/src/com/android/inputmethod/latin/makedict/SparseTable.java deleted file mode 100644 index 7592a0c13..000000000 --- a/java/src/com/android/inputmethod/latin/makedict/SparseTable.java +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin.makedict; - -import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.latin.utils.CollectionUtils; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Collections; - -/** - * SparseTable is an extensible map from integer to integer. - * This holds one value for every mBlockSize keys, so it uses 1/mBlockSize'th of the full index - * memory. - */ -@UsedForTesting -public class SparseTable { - - /** - * mLookupTable is indexed by terminal ID, containing exactly one entry for every mBlockSize - * terminals. - * It contains at index i = j / mBlockSize the index in each ArrayList in mContentsTables where - * the values for terminals with IDs j to j + mBlockSize - 1 are stored as an mBlockSize-sized - * integer array. - */ - private final ArrayList<Integer> mLookupTable; - private final ArrayList<ArrayList<Integer>> mContentTables; - - private final int mBlockSize; - private final int mContentTableCount; - public static final int NOT_EXIST = -1; - public static final int SIZE_OF_INT_IN_BYTES = 4; - - @UsedForTesting - public SparseTable(final int initialCapacity, final int blockSize, - final int contentTableCount) { - mBlockSize = blockSize; - final int lookupTableSize = initialCapacity / mBlockSize - + (initialCapacity % mBlockSize > 0 ? 1 : 0); - mLookupTable = new ArrayList<Integer>(Collections.nCopies(lookupTableSize, NOT_EXIST)); - mContentTableCount = contentTableCount; - mContentTables = CollectionUtils.newArrayList(); - for (int i = 0; i < mContentTableCount; ++i) { - mContentTables.add(new ArrayList<Integer>()); - } - } - - @UsedForTesting - public SparseTable(final ArrayList<Integer> lookupTable, - final ArrayList<ArrayList<Integer>> contentTables, final int blockSize) { - mBlockSize = blockSize; - mContentTableCount = contentTables.size(); - mLookupTable = lookupTable; - mContentTables = contentTables; - } - - /** - * Converts an byte array to an int array considering each set of 4 bytes is an int stored in - * big-endian. - * The length of byteArray must be a multiple of four. - * Otherwise, IndexOutOfBoundsException will be raised. - */ - @UsedForTesting - private static ArrayList<Integer> convertByteArrayToIntegerArray(final byte[] byteArray) { - final ArrayList<Integer> integerArray = new ArrayList<Integer>(byteArray.length / 4); - for (int i = 0; i < byteArray.length; i += 4) { - int value = 0; - for (int j = i; j < i + 4; ++j) { - value <<= 8; - value |= byteArray[j] & 0xFF; - } - integerArray.add(value); - } - return integerArray; - } - - @UsedForTesting - public int get(final int contentTableIndex, final int index) { - if (!contains(index)) { - return NOT_EXIST; - } - return mContentTables.get(contentTableIndex).get( - mLookupTable.get(index / mBlockSize) + (index % mBlockSize)); - } - - @UsedForTesting - public ArrayList<Integer> getAll(final int index) { - final ArrayList<Integer> ret = CollectionUtils.newArrayList(); - for (int i = 0; i < mContentTableCount; ++i) { - ret.add(get(i, index)); - } - return ret; - } - - @UsedForTesting - public void set(final int contentTableIndex, final int index, final int value) { - if (mLookupTable.get(index / mBlockSize) == NOT_EXIST) { - mLookupTable.set(index / mBlockSize, mContentTables.get(contentTableIndex).size()); - for (int i = 0; i < mContentTableCount; ++i) { - for (int j = 0; j < mBlockSize; ++j) { - mContentTables.get(i).add(NOT_EXIST); - } - } - } - mContentTables.get(contentTableIndex).set( - mLookupTable.get(index / mBlockSize) + (index % mBlockSize), value); - } - - public void remove(final int indexOfContent, final int index) { - set(indexOfContent, index, NOT_EXIST); - } - - @UsedForTesting - public int size() { - return mLookupTable.size() * mBlockSize; - } - - @UsedForTesting - /* package */ int getContentTableSize() { - // This class always has at least one content table. - return mContentTables.get(0).size(); - } - - @UsedForTesting - /* package */ int getLookupTableSize() { - return mLookupTable.size(); - } - - public boolean contains(final int index) { - if (index < 0 || index / mBlockSize >= mLookupTable.size() - || mLookupTable.get(index / mBlockSize) == NOT_EXIST) { - return false; - } - return true; - } - - @UsedForTesting - public void write(final OutputStream lookupOutStream, final OutputStream[] contentOutStreams) - throws IOException { - if (contentOutStreams.length != mContentTableCount) { - throw new RuntimeException(contentOutStreams.length + " streams are given, but the" - + " table has " + mContentTableCount + " content tables."); - } - for (final int index : mLookupTable) { - BinaryDictEncoderUtils.writeUIntToStream(lookupOutStream, index, SIZE_OF_INT_IN_BYTES); - } - - for (int i = 0; i < contentOutStreams.length; ++i) { - for (final int data : mContentTables.get(i)) { - BinaryDictEncoderUtils.writeUIntToStream(contentOutStreams[i], data, - SIZE_OF_INT_IN_BYTES); - } - } - } - - @UsedForTesting - public void writeToFiles(final File lookupTableFile, final File[] contentFiles) - throws IOException { - FileOutputStream lookupTableOutStream = null; - final FileOutputStream[] contentTableOutStreams = new FileOutputStream[mContentTableCount]; - try { - lookupTableOutStream = new FileOutputStream(lookupTableFile); - for (int i = 0; i < contentFiles.length; ++i) { - contentTableOutStreams[i] = new FileOutputStream(contentFiles[i]); - } - write(lookupTableOutStream, contentTableOutStreams); - } finally { - if (lookupTableOutStream != null) { - lookupTableOutStream.close(); - } - for (int i = 0; i < contentTableOutStreams.length; ++i) { - if (contentTableOutStreams[i] != null) { - contentTableOutStreams[i].close(); - } - } - } - } - - private static byte[] readFileToByteArray(final File file) throws IOException { - final byte[] contents = new byte[(int) file.length()]; - FileInputStream inStream = null; - try { - inStream = new FileInputStream(file); - inStream.read(contents); - } finally { - if (inStream != null) { - inStream.close(); - } - } - return contents; - } - - @UsedForTesting - public static SparseTable readFromFiles(final File lookupTableFile, final File[] contentFiles, - final int blockSize) throws IOException { - final ArrayList<ArrayList<Integer>> contentTables = - new ArrayList<ArrayList<Integer>>(contentFiles.length); - for (int i = 0; i < contentFiles.length; ++i) { - contentTables.add(convertByteArrayToIntegerArray(readFileToByteArray(contentFiles[i]))); - } - return new SparseTable(convertByteArrayToIntegerArray(readFileToByteArray(lookupTableFile)), - contentTables, blockSize); - } -} diff --git a/java/src/com/android/inputmethod/latin/makedict/SparseTableContentReader.java b/java/src/com/android/inputmethod/latin/makedict/SparseTableContentReader.java deleted file mode 100644 index 63e1f56f5..000000000 --- a/java/src/com/android/inputmethod/latin/makedict/SparseTableContentReader.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin.makedict; - -import com.android.inputmethod.latin.makedict.BinaryDictDecoderUtils.DictBuffer; -import com.android.inputmethod.latin.makedict.DictDecoder.DictionaryBufferFactory; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; - -/** - * An auxiliary class for reading SparseTable and data written by SparseTableContentWriter. - */ -public class SparseTableContentReader { - - /** - * An interface of a function which is passed to SparseTableContentReader.read. - */ - public interface SparseTableContentReaderInterface { - /** - * Reads data. - * - * @param buffer the DictBuffer. The position of the buffer is set to the head of data. - */ - public void read(final DictBuffer buffer); - } - - protected final int mContentCount; - protected final int mBlockSize; - protected final File mBaseDir; - protected final File mLookupTableFile; - protected final File[] mAddressTableFiles; - protected final File[] mContentFiles; - protected DictBuffer mLookupTableBuffer; - protected final DictBuffer[] mAddressTableBuffers; - private final DictBuffer[] mContentBuffers; - protected final DictionaryBufferFactory mFactory; - - /** - * Sole constructor of SparseTableContentReader. - * - * @param name the name of SparseTable. - * @param blockSize the block size of the content table. - * @param baseDir the directory which contains the files of the content table. - * @param contentFilenames the file names of content files. - * @param contentSuffixes the ids of contents. These ids are used for a suffix of a name of - * address files and content files. - * @param factory the DictionaryBufferFactory which is used for opening the files. - */ - public SparseTableContentReader(final String name, final int blockSize, final File baseDir, - final String[] contentFilenames, final String[] contentSuffixes, - final DictionaryBufferFactory factory) { - if (contentFilenames.length != contentSuffixes.length) { - throw new RuntimeException("The length of contentFilenames and the length of" - + " contentSuffixes are different " + contentFilenames.length + ", " - + contentSuffixes.length); - } - mBlockSize = blockSize; - mBaseDir = baseDir; - mFactory = factory; - mContentCount = contentFilenames.length; - mLookupTableFile = new File(baseDir, name + FormatSpec.LOOKUP_TABLE_FILE_SUFFIX); - mAddressTableFiles = new File[mContentCount]; - mContentFiles = new File[mContentCount]; - for (int i = 0; i < mContentCount; ++i) { - mAddressTableFiles[i] = new File(mBaseDir, - name + FormatSpec.CONTENT_TABLE_FILE_SUFFIX + contentSuffixes[i]); - mContentFiles[i] = new File(mBaseDir, contentFilenames[i] + contentSuffixes[i]); - } - mAddressTableBuffers = new DictBuffer[mContentCount]; - mContentBuffers = new DictBuffer[mContentCount]; - } - - public void openBuffers() throws FileNotFoundException, IOException { - mLookupTableBuffer = mFactory.getDictionaryBuffer(mLookupTableFile); - for (int i = 0; i < mContentCount; ++i) { - mAddressTableBuffers[i] = mFactory.getDictionaryBuffer(mAddressTableFiles[i]); - mContentBuffers[i] = mFactory.getDictionaryBuffer(mContentFiles[i]); - } - } - - /** - * Calls the read() callback of the reader with the appropriate buffer appropriately positioned. - * @param contentNumber the index in the original contentFilenames[] array. - * @param terminalId the terminal ID to read. - * @param reader the reader on which to call the callback. - */ - protected void read(final int contentNumber, final int terminalId, - final SparseTableContentReaderInterface reader) { - if (terminalId < 0 || (terminalId / mBlockSize) * SparseTable.SIZE_OF_INT_IN_BYTES - >= mLookupTableBuffer.limit()) { - return; - } - - mLookupTableBuffer.position((terminalId / mBlockSize) * SparseTable.SIZE_OF_INT_IN_BYTES); - final int indexInAddressTable = mLookupTableBuffer.readInt(); - if (indexInAddressTable == SparseTable.NOT_EXIST) { - return; - } - - mAddressTableBuffers[contentNumber].position(SparseTable.SIZE_OF_INT_IN_BYTES - * ((indexInAddressTable * mBlockSize) + (terminalId % mBlockSize))); - final int address = mAddressTableBuffers[contentNumber].readInt(); - if (address == SparseTable.NOT_EXIST) { - return; - } - - mContentBuffers[contentNumber].position(address); - reader.read(mContentBuffers[contentNumber]); - } -}
\ No newline at end of file diff --git a/java/src/com/android/inputmethod/latin/makedict/SparseTableContentWriter.java b/java/src/com/android/inputmethod/latin/makedict/SparseTableContentWriter.java deleted file mode 100644 index 49f0fd624..000000000 --- a/java/src/com/android/inputmethod/latin/makedict/SparseTableContentWriter.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin.makedict; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; - -/** - * An auxiliary class for writing data associated with SparseTable to files. - */ -public class SparseTableContentWriter { - public interface SparseTableContentWriterInterface { - public void write(final OutputStream outStream) throws IOException; - } - - private final int mContentCount; - private final SparseTable mSparseTable; - private final File mLookupTableFile; - protected final File mBaseDir; - private final File[] mAddressTableFiles; - private final File[] mContentFiles; - protected final OutputStream[] mContentOutStreams; - - /** - * Sole constructor of SparseTableContentWriter. - * - * @param name the name of SparseTable. - * @param initialCapacity the initial capacity of SparseTable. - * @param blockSize the block size of the content table. - * @param baseDir the directory which contains the files of the content table. - * @param contentFilenames the file names of content files. - * @param contentIds the ids of contents. These ids are used for a suffix of a name of address - * files and content files. - */ - public SparseTableContentWriter(final String name, final int initialCapacity, - final int blockSize, final File baseDir, final String[] contentFilenames, - final String[] contentIds) { - if (contentFilenames.length != contentIds.length) { - throw new RuntimeException("The length of contentFilenames and the length of" - + " contentIds are different " + contentFilenames.length + ", " - + contentIds.length); - } - mContentCount = contentFilenames.length; - mSparseTable = new SparseTable(initialCapacity, blockSize, mContentCount); - mLookupTableFile = new File(baseDir, name + FormatSpec.LOOKUP_TABLE_FILE_SUFFIX); - mAddressTableFiles = new File[mContentCount]; - mContentFiles = new File[mContentCount]; - mBaseDir = baseDir; - for (int i = 0; i < mContentCount; ++i) { - mAddressTableFiles[i] = new File(mBaseDir, - name + FormatSpec.CONTENT_TABLE_FILE_SUFFIX + contentIds[i]); - mContentFiles[i] = new File(mBaseDir, contentFilenames[i] + contentIds[i]); - } - mContentOutStreams = new OutputStream[mContentCount]; - } - - public void openStreams() throws FileNotFoundException { - for (int i = 0; i < mContentCount; ++i) { - mContentOutStreams[i] = new FileOutputStream(mContentFiles[i]); - } - } - - protected void write(final int contentIndex, final int index, - final SparseTableContentWriterInterface writer) throws IOException { - mSparseTable.set(contentIndex, index, (int) mContentFiles[contentIndex].length()); - writer.write(mContentOutStreams[contentIndex]); - mContentOutStreams[contentIndex].flush(); - } - - public void closeStreams() throws IOException { - mSparseTable.writeToFiles(mLookupTableFile, mAddressTableFiles); - for (int i = 0; i < mContentCount; ++i) { - mContentOutStreams[i].close(); - } - } -}
\ No newline at end of file diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java index 9ddaaf734..83707480d 100644 --- a/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictDecoder.java @@ -17,20 +17,15 @@ package com.android.inputmethod.latin.makedict; 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.BinaryDictionary; import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; import com.android.inputmethod.latin.utils.CollectionUtils; - -import android.util.Log; +import com.android.inputmethod.latin.utils.FileUtils; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; /** * An implementation of binary dictionary decoder for version 4 binary dictionary. @@ -39,421 +34,74 @@ import java.util.Arrays; public class Ver4DictDecoder extends AbstractDictDecoder { private static final String TAG = Ver4DictDecoder.class.getSimpleName(); - protected static final int FILETYPE_TRIE = 1; - protected static final int FILETYPE_FREQUENCY = 2; - protected static final int FILETYPE_TERMINAL_ADDRESS_TABLE = 3; - protected static final int FILETYPE_BIGRAM_FREQ = 4; - protected static final int FILETYPE_SHORTCUT = 5; - protected static final int FILETYPE_HEADER = 6; - - protected final File mDictDirectory; - protected final DictionaryBufferFactory mBufferFactory; - protected DictBuffer mDictBuffer; - protected DictBuffer mHeaderBuffer; - protected DictBuffer mFrequencyBuffer; - protected DictBuffer mTerminalAddressTableBuffer; - private BigramContentReader mBigramReader; - private ShortcutContentReader mShortcutReader; - - /** - * Raw PtNode info straight out of a trie file in version 4 dictionary. - */ - protected static final class Ver4PtNodeInfo { - public final int mFlags; - public final int[] mCharacters; - public final int mTerminalId; - public final int mChildrenPos; - public final int mParentPos; - public final int mNodeSize; - public int mStartIndexOfCharacters; - public int mEndIndexOfCharacters; // exclusive - - public Ver4PtNodeInfo(final int flags, final int[] characters, final int terminalId, - final int childrenPos, final int parentPos, final int nodeSize) { - mFlags = flags; - mCharacters = characters; - mTerminalId = terminalId; - mChildrenPos = childrenPos; - mParentPos = parentPos; - mNodeSize = nodeSize; - mStartIndexOfCharacters = 0; - mEndIndexOfCharacters = characters.length; - } - } + final File mDictDirectory; + final BinaryDictionary mBinaryDictionary; @UsedForTesting /* package */ Ver4DictDecoder(final File dictDirectory, final int factoryFlag) { - mDictDirectory = dictDirectory; - mDictBuffer = mHeaderBuffer = mFrequencyBuffer = null; - - if ((factoryFlag & MASK_DICTBUFFER) == USE_READONLY_BYTEBUFFER) { - mBufferFactory = new DictionaryBufferFromReadOnlyByteBufferFactory(); - } else if ((factoryFlag & MASK_DICTBUFFER) == USE_BYTEARRAY) { - mBufferFactory = new DictionaryBufferFromByteArrayFactory(); - } else if ((factoryFlag & MASK_DICTBUFFER) == USE_WRITABLE_BYTEBUFFER) { - mBufferFactory = new DictionaryBufferFromWritableByteBufferFactory(); - } else { - mBufferFactory = new DictionaryBufferFromReadOnlyByteBufferFactory(); - } + this(dictDirectory, null /* factory */); } @UsedForTesting /* package */ Ver4DictDecoder(final File dictDirectory, final DictionaryBufferFactory factory) { mDictDirectory = dictDirectory; - mBufferFactory = factory; - mDictBuffer = mHeaderBuffer = mFrequencyBuffer = null; - } - - protected File getFile(final int fileType) throws UnsupportedFormatException { - if (fileType == FILETYPE_TRIE) { - return new File(mDictDirectory, - mDictDirectory.getName() + FormatSpec.TRIE_FILE_EXTENSION); - } else if (fileType == FILETYPE_HEADER) { - return new File(mDictDirectory, - mDictDirectory.getName() + FormatSpec.HEADER_FILE_EXTENSION); - } else if (fileType == FILETYPE_FREQUENCY) { - return new File(mDictDirectory, - mDictDirectory.getName() + FormatSpec.FREQ_FILE_EXTENSION); - } else if (fileType == FILETYPE_TERMINAL_ADDRESS_TABLE) { - return new File(mDictDirectory, - mDictDirectory.getName() + FormatSpec.TERMINAL_ADDRESS_TABLE_FILE_EXTENSION); - } else if (fileType == FILETYPE_BIGRAM_FREQ) { - return new File(mDictDirectory, - mDictDirectory.getName() + FormatSpec.BIGRAM_FILE_EXTENSION - + FormatSpec.BIGRAM_FREQ_CONTENT_ID); - } else if (fileType == FILETYPE_SHORTCUT) { - return new File(mDictDirectory, - mDictDirectory.getName() + FormatSpec.SHORTCUT_FILE_EXTENSION - + FormatSpec.SHORTCUT_CONTENT_ID); - } else { - throw new UnsupportedFormatException("Unsupported kind of file : " + fileType); - } - } - - @Override - public void openDictBuffer() throws FileNotFoundException, IOException, - UnsupportedFormatException { - if (!mDictDirectory.isDirectory()) { - throw new UnsupportedFormatException("Format 4 dictionary needs a directory"); - } - mHeaderBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_HEADER)); - mDictBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_TRIE)); - mFrequencyBuffer = mBufferFactory.getDictionaryBuffer(getFile(FILETYPE_FREQUENCY)); - mTerminalAddressTableBuffer = mBufferFactory.getDictionaryBuffer( - getFile(FILETYPE_TERMINAL_ADDRESS_TABLE)); - mBigramReader = new BigramContentReader(mDictDirectory.getName(), - mDictDirectory, mBufferFactory); - mBigramReader.openBuffers(); - mShortcutReader = new ShortcutContentReader(mDictDirectory.getName(), mDictDirectory, - mBufferFactory); - mShortcutReader.openBuffers(); - } - - @Override - public boolean isDictBufferOpen() { - return mDictBuffer != null; - } - - @UsedForTesting - /* package */ DictBuffer getHeaderBuffer() { - return mHeaderBuffer; - } - - @UsedForTesting - /* package */ DictBuffer getDictBuffer() { - return mDictBuffer; + mBinaryDictionary = new BinaryDictionary(dictDirectory.getAbsolutePath(), + 0 /* offset */, 0 /* length */, true /* useFullEditDistance */, null /* locale */, + "" /* dictType */, true /* isUpdatable */); } @Override public DictionaryHeader readHeader() throws IOException, UnsupportedFormatException { - if (mHeaderBuffer == null) { - openDictBuffer(); - } - mHeaderBuffer.position(0); - final DictionaryHeader header = super.readHeader(mHeaderBuffer); - final int version = header.mFormatOptions.mVersion; - if (version != FormatSpec.VERSION4) { - throw new UnsupportedFormatException("File header has a wrong version : " + version); - } - return header; - } - - /** - * An auxiliary class for reading bigrams. - */ - protected static class BigramContentReader extends SparseTableContentReader { - public BigramContentReader(final String name, final File baseDir, - final DictionaryBufferFactory factory) { - super(name + FormatSpec.BIGRAM_FILE_EXTENSION, - FormatSpec.BIGRAM_ADDRESS_TABLE_BLOCK_SIZE, baseDir, - getContentFilenames(name), getContentIds(), factory); - } - - // TODO: Consolidate this method and BigramContentWriter.getContentFilenames. - protected static String[] getContentFilenames(final String name) { - return new String[] { name + FormatSpec.BIGRAM_FILE_EXTENSION }; - } - - // TODO: Consolidate this method and BigramContentWriter.getContentIds. - protected static String[] getContentIds() { - return new String[] { FormatSpec.BIGRAM_FREQ_CONTENT_ID }; - } - - public ArrayList<PendingAttribute> readTargetsAndFrequencies(final int terminalId, - final DictBuffer terminalAddressTableBuffer, final FormatOptions options) { - final ArrayList<PendingAttribute> bigrams = CollectionUtils.newArrayList(); - read(FormatSpec.BIGRAM_FREQ_CONTENT_INDEX, terminalId, - new SparseTableContentReaderInterface() { - @Override - public void read(final DictBuffer buffer) { - while (bigrams.size() < FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) { - // If bigrams.size() reaches FormatSpec.MAX_BIGRAMS_IN_A_PTNODE, - // remaining bigram entries are ignored. - final int bigramFlags = buffer.readUnsignedByte(); - final int probability; - - if (options.mHasTimestamp) { - probability = buffer.readUnsignedByte(); - // Skip timestamp - buffer.readInt(); - // Skip level - buffer.readUnsignedByte(); - // Skip count - buffer.readUnsignedByte(); - } else { - probability = bigramFlags - & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY; - } - final int targetTerminalId = buffer.readUnsignedInt24(); - terminalAddressTableBuffer.position(targetTerminalId - * FormatSpec.TERMINAL_ADDRESS_TABLE_ADDRESS_SIZE); - final int targetAddress = - terminalAddressTableBuffer.readUnsignedInt24(); - bigrams.add(new PendingAttribute(probability, targetAddress)); - if (0 == (bigramFlags - & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) { - break; - } - } - if (bigrams.size() >= FormatSpec.MAX_BIGRAMS_IN_A_PTNODE) { - throw new RuntimeException("Too many bigrams in a PtNode (" - + bigrams.size() + " but max is " - + FormatSpec.MAX_BIGRAMS_IN_A_PTNODE + ")"); - } - } - }); - if (bigrams.isEmpty()) return null; - return bigrams; - } - } - - /** - * An auxiliary class for reading shortcuts. - */ - protected static class ShortcutContentReader extends SparseTableContentReader { - public ShortcutContentReader(final String name, final File baseDir, - final DictionaryBufferFactory factory) { - super(name + FormatSpec.SHORTCUT_FILE_EXTENSION, - FormatSpec.SHORTCUT_ADDRESS_TABLE_BLOCK_SIZE, baseDir, - new String[] { name + FormatSpec.SHORTCUT_FILE_EXTENSION }, - new String[] { FormatSpec.SHORTCUT_CONTENT_ID }, factory); - } - - public ArrayList<WeightedString> readShortcuts(final int terminalId) { - final ArrayList<WeightedString> shortcuts = CollectionUtils.newArrayList(); - read(FormatSpec.SHORTCUT_CONTENT_INDEX, terminalId, - new SparseTableContentReaderInterface() { - @Override - public void read(final DictBuffer buffer) { - while (true) { - final int flags = buffer.readUnsignedByte(); - final String word = CharEncoding.readString(buffer); - shortcuts.add(new WeightedString(word, - flags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_FREQUENCY)); - if (0 == (flags & FormatSpec.FLAG_BIGRAM_SHORTCUT_ATTR_HAS_NEXT)) { - break; - } - } - } - }); - if (shortcuts.isEmpty()) return null; - return shortcuts; - } - } - - protected static class PtNodeReader extends AbstractDictDecoder.PtNodeReader { - protected static int readFrequency(final DictBuffer frequencyBuffer, final int terminalId, - final FormatOptions formatOptions) { - final int readingPos; - if (formatOptions.mHasTimestamp) { - final int entrySize = FormatSpec.FREQUENCY_AND_FLAGS_SIZE - + FormatSpec.UNIGRAM_TIMESTAMP_SIZE + FormatSpec.UNIGRAM_LEVEL_SIZE - + FormatSpec.UNIGRAM_COUNTER_SIZE; - readingPos = terminalId * entrySize + FormatSpec.FLAGS_IN_FREQ_FILE_SIZE; - } else { - readingPos = terminalId * FormatSpec.FREQUENCY_AND_FLAGS_SIZE - + FormatSpec.FLAGS_IN_FREQ_FILE_SIZE; - } - frequencyBuffer.position(readingPos); - return frequencyBuffer.readUnsignedByte(); - } - - protected static int readTerminalId(final DictBuffer dictBuffer) { - return dictBuffer.readInt(); - } - } - - private final int[] mCharacterBufferForReadingVer4PtNodeInfo - = new int[FormatSpec.MAX_WORD_LENGTH]; - - /** - * Reads PtNode from ptNodePos in the trie file and returns Ver4PtNodeInfo. - * - * @param ptNodePos the position of PtNode. - * @param options the format options. - * @return Ver4PtNodeInfo. - */ - // TODO: Make this buffer thread safe. - // TODO: Support words longer than FormatSpec.MAX_WORD_LENGTH. - protected Ver4PtNodeInfo readVer4PtNodeInfo(final int ptNodePos, final FormatOptions options) { - int readingPos = ptNodePos; - final int flags = PtNodeReader.readPtNodeOptionFlags(mDictBuffer); - readingPos += FormatSpec.PTNODE_FLAGS_SIZE; - - final int parentPos = PtNodeReader.readParentAddress(mDictBuffer, options); - if (BinaryDictIOUtils.supportsDynamicUpdate(options)) { - readingPos += FormatSpec.PARENT_ADDRESS_SIZE; - } - - final int characters[]; - if (0 != (flags & FormatSpec.FLAG_HAS_MULTIPLE_CHARS)) { - int index = 0; - int character = CharEncoding.readChar(mDictBuffer); - readingPos += CharEncoding.getCharSize(character); - while (FormatSpec.INVALID_CHARACTER != character - && index < FormatSpec.MAX_WORD_LENGTH) { - mCharacterBufferForReadingVer4PtNodeInfo[index++] = character; - character = CharEncoding.readChar(mDictBuffer); - readingPos += CharEncoding.getCharSize(character); - } - characters = Arrays.copyOfRange(mCharacterBufferForReadingVer4PtNodeInfo, 0, index); - } else { - final int character = CharEncoding.readChar(mDictBuffer); - readingPos += CharEncoding.getCharSize(character); - characters = new int[] { character }; - } - final int terminalId; - if (0 != (FormatSpec.FLAG_IS_TERMINAL & flags)) { - terminalId = PtNodeReader.readTerminalId(mDictBuffer); - readingPos += FormatSpec.PTNODE_TERMINAL_ID_SIZE; - } else { - terminalId = PtNode.NOT_A_TERMINAL; - } - - int childrenPos = PtNodeReader.readChildrenAddress(mDictBuffer, flags, options); - if (childrenPos != FormatSpec.NO_CHILDREN_ADDRESS) { - childrenPos += readingPos; - } - readingPos += BinaryDictIOUtils.getChildrenAddressSize(flags, options); - - return new Ver4PtNodeInfo(flags, characters, terminalId, childrenPos, parentPos, - readingPos - ptNodePos); - } - - @Override - public PtNodeInfo readPtNode(final int ptNodePos, final FormatOptions options) { - final Ver4PtNodeInfo nodeInfo = readVer4PtNodeInfo(ptNodePos, options); - - final int frequency; - if (0 != (FormatSpec.FLAG_IS_TERMINAL & nodeInfo.mFlags)) { - frequency = PtNodeReader.readFrequency(mFrequencyBuffer, nodeInfo.mTerminalId, options); - } else { - frequency = PtNode.NOT_A_TERMINAL; - } - - final ArrayList<WeightedString> shortcutTargets = mShortcutReader.readShortcuts( - nodeInfo.mTerminalId); - final ArrayList<PendingAttribute> bigrams = mBigramReader.readTargetsAndFrequencies( - nodeInfo.mTerminalId, mTerminalAddressTableBuffer, options); - - return new PtNodeInfo(ptNodePos, ptNodePos + nodeInfo.mNodeSize, nodeInfo.mFlags, - nodeInfo.mCharacters, frequency, nodeInfo.mParentPos, nodeInfo.mChildrenPos, - shortcutTargets, bigrams); - } - - private void deleteDictFiles() { - final File[] files = mDictDirectory.listFiles(); - for (int i = 0; i < files.length; ++i) { - files[i].delete(); - } + return mBinaryDictionary.getHeader(); } @Override public FusionDictionary readDictionaryBinary(final FusionDictionary dict, final boolean deleteDictIfBroken) throws FileNotFoundException, IOException, UnsupportedFormatException { - if (mDictBuffer == null) { - openDictBuffer(); - } - try { - return BinaryDictDecoderUtils.readDictionaryBinary(this, dict); - } catch (IOException e) { - Log.e(TAG, "The dictionary " + mDictDirectory.getName() + " is broken.", e); - if (deleteDictIfBroken) { - deleteDictFiles(); + final DictionaryHeader header = readHeader(); + final FusionDictionary fusionDict = dict != null ? dict : + new FusionDictionary(new FusionDictionary.PtNodeArray(), header.mDictionaryOptions); + int token = 0; + final ArrayList<WordProperty> wordProperties = CollectionUtils.newArrayList(); + do { + final BinaryDictionary.GetNextWordPropertyResult result = + mBinaryDictionary.getNextWordProperty(token); + final WordProperty wordProperty = result.mWordProperty; + if (wordProperty == null) { + if (deleteDictIfBroken) { + mBinaryDictionary.close(); + FileUtils.deleteRecursively(mDictDirectory); + } + return null; } - throw e; - } catch (UnsupportedFormatException e) { - Log.e(TAG, "The dictionary " + mDictDirectory.getName() + " is broken.", e); - if (deleteDictIfBroken) { - deleteDictFiles(); + wordProperties.add(wordProperty); + token = result.mNextToken; + } while (token != 0); + + // 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, + wordProperty.mShortcutTargets, wordProperty.mIsNotAWord); } - throw e; } - } - - @Override - public void setPosition(int newPos) { - mDictBuffer.position(newPos); - } - - @Override - public int getPosition() { - return mDictBuffer.position(); - } - - @Override - public int readPtNodeCount() { - return BinaryDictDecoderUtils.readPtNodeCount(mDictBuffer); - } - - @Override - public boolean readAndFollowForwardLink() { - final int forwardLinkPos = mDictBuffer.position(); - int nextRelativePos = BinaryDictDecoderUtils.readSInt24(mDictBuffer); - if (nextRelativePos != FormatSpec.NO_FORWARD_LINK_ADDRESS) { - final int nextPos = forwardLinkPos + nextRelativePos; - if (nextPos >= 0 && nextPos < mDictBuffer.limit()) { - mDictBuffer.position(nextPos); - return true; + // Insert bigrams to the fusion dictionary. + for (final WordProperty wordProperty : wordProperties) { + if (wordProperty.mBigrams == null) { + continue; + } + final String word0 = wordProperty.mWord; + for (final WeightedString bigram : wordProperty.mBigrams) { + fusionDict.setBigram(word0, bigram.mWord, bigram.getProbability()); } } - return false; - } - - @Override - public boolean hasNextPtNodeArray() { - return mDictBuffer.position() != FormatSpec.NO_FORWARD_LINK_ADDRESS; - } - - @Override - @UsedForTesting - 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); - if ((flags & FormatSpec.FLAG_IS_TERMINAL) != 0) PtNodeReader.readTerminalId(mDictBuffer); - PtNodeReader.readChildrenAddress(mDictBuffer, flags, formatOptions); + return fusionDict; } } diff --git a/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java b/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java index a5b013586..147844fd8 100644 --- a/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java +++ b/java/src/com/android/inputmethod/latin/makedict/Ver4DictEncoder.java @@ -71,27 +71,29 @@ public class Ver4DictEncoder implements DictEncoder { // Somehow createEmptyDictFile returned true, but the file was not created correctly throw new IOException("Cannot create dictionary file"); } - for (final Word word : dict) { + for (final WordProperty wordProperty : dict) { // TODO: switch to addMultipleDictionaryEntries when they support shortcuts - if (null == word.mShortcutTargets || word.mShortcutTargets.isEmpty()) { - binaryDict.addUnigramWord(word.mWord, word.mFrequency, + if (null == wordProperty.mShortcutTargets || wordProperty.mShortcutTargets.isEmpty()) { + binaryDict.addUnigramWord(wordProperty.mWord, wordProperty.getProbability(), null /* shortcutTarget */, 0 /* shortcutProbability */, - word.mIsNotAWord, word.mIsBlacklistEntry, 0 /* timestamp */); + wordProperty.mIsNotAWord, wordProperty.mIsBlacklistEntry, + 0 /* timestamp */); } else { - for (final WeightedString shortcutTarget : word.mShortcutTargets) { - binaryDict.addUnigramWord(word.mWord, word.mFrequency, + for (final WeightedString shortcutTarget : wordProperty.mShortcutTargets) { + binaryDict.addUnigramWord(wordProperty.mWord, wordProperty.getProbability(), shortcutTarget.mWord, shortcutTarget.getProbability(), - word.mIsNotAWord, word.mIsBlacklistEntry, 0 /* timestamp */); + wordProperty.mIsNotAWord, wordProperty.mIsBlacklistEntry, + 0 /* timestamp */); } } if (binaryDict.needsToRunGC(true /* mindsBlockByGC */)) { binaryDict.flushWithGC(); } } - for (final Word word0 : dict) { - if (null == word0.mBigrams) continue; - for (final WeightedString word1 : word0.mBigrams) { - binaryDict.addBigramWords(word0.mWord, word1.mWord, word1.getProbability(), + for (final WordProperty word0Property : dict) { + if (null == word0Property.mBigrams) continue; + for (final WeightedString word1 : word0Property.mBigrams) { + binaryDict.addBigramWords(word0Property.mWord, word1.mWord, word1.getProbability(), 0 /* timestamp */); if (binaryDict.needsToRunGC(true /* mindsBlockByGC */)) { binaryDict.flushWithGC(); diff --git a/java/src/com/android/inputmethod/latin/makedict/Word.java b/java/src/com/android/inputmethod/latin/makedict/Word.java deleted file mode 100644 index 0eabb7bf3..000000000 --- a/java/src/com/android/inputmethod/latin/makedict/Word.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.inputmethod.latin.makedict; - -import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; - -import java.util.ArrayList; -import java.util.Arrays; - -/** - * Utility class for a word with a frequency. - * - * This is chiefly used to iterate a dictionary. - */ -public final class Word implements Comparable<Word> { - public final String mWord; - public final int mFrequency; - public final ArrayList<WeightedString> mShortcutTargets; - public final ArrayList<WeightedString> mBigrams; - public final boolean mIsNotAWord; - public final boolean mIsBlacklistEntry; - - private int mHashCode = 0; - - public Word(final String word, final int frequency, - final ArrayList<WeightedString> shortcutTargets, - final ArrayList<WeightedString> bigrams, - final boolean isNotAWord, final boolean isBlacklistEntry) { - mWord = word; - mFrequency = frequency; - mShortcutTargets = shortcutTargets; - mBigrams = bigrams; - mIsNotAWord = isNotAWord; - mIsBlacklistEntry = isBlacklistEntry; - } - - private static int computeHashCode(Word word) { - return Arrays.hashCode(new Object[] { - word.mWord, - word.mFrequency, - word.mShortcutTargets.hashCode(), - word.mBigrams.hashCode(), - word.mIsNotAWord, - word.mIsBlacklistEntry - }); - } - - /** - * Three-way comparison. - * - * A Word x is greater than a word y if x has a higher frequency. If they have the same - * frequency, they are sorted in lexicographic order. - */ - @Override - public int compareTo(Word w) { - if (mFrequency < w.mFrequency) return 1; - if (mFrequency > w.mFrequency) return -1; - return mWord.compareTo(w.mWord); - } - - /** - * Equality test. - * - * Words are equal if they have the same frequency, the same spellings, and the same - * attributes. - */ - @Override - public boolean equals(Object o) { - if (o == this) return true; - if (!(o instanceof Word)) return false; - Word w = (Word)o; - return mFrequency == w.mFrequency && mWord.equals(w.mWord) - && mShortcutTargets.equals(w.mShortcutTargets) - && mBigrams.equals(w.mBigrams) - && mIsNotAWord == w.mIsNotAWord - && mIsBlacklistEntry == w.mIsBlacklistEntry; - } - - @Override - public int hashCode() { - if (mHashCode == 0) { - mHashCode = computeHashCode(this); - } - return mHashCode; - } -} diff --git a/java/src/com/android/inputmethod/latin/makedict/WordProperty.java b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java new file mode 100644 index 000000000..b93a0a525 --- /dev/null +++ b/java/src/com/android/inputmethod/latin/makedict/WordProperty.java @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.inputmethod.latin.makedict; + +import com.android.inputmethod.annotations.UsedForTesting; +import com.android.inputmethod.latin.BinaryDictionary; +import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; +import com.android.inputmethod.latin.utils.CollectionUtils; +import com.android.inputmethod.latin.utils.CombinedFormatUtils; +import com.android.inputmethod.latin.utils.StringUtils; + +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Utility class for a word with a probability. + * + * This is chiefly used to iterate a dictionary. + */ +public final class WordProperty implements Comparable<WordProperty> { + public final String mWord; + public final ProbabilityInfo mProbabilityInfo; + public final ArrayList<WeightedString> mShortcutTargets; + public final ArrayList<WeightedString> mBigrams; + public final boolean mIsNotAWord; + public final boolean mIsBlacklistEntry; + public final boolean mHasShortcuts; + public final boolean mHasBigrams; + + private int mHashCode = 0; + + public WordProperty(final String word, final int probability, + final ArrayList<WeightedString> shortcutTargets, + final ArrayList<WeightedString> bigrams, + final boolean isNotAWord, final boolean isBlacklistEntry) { + mWord = word; + mProbabilityInfo = new ProbabilityInfo(probability); + mShortcutTargets = shortcutTargets; + mBigrams = bigrams; + mIsNotAWord = isNotAWord; + mIsBlacklistEntry = isBlacklistEntry; + mHasBigrams = bigrams != null && !bigrams.isEmpty(); + mHasShortcuts = shortcutTargets != null && !shortcutTargets.isEmpty(); + } + + private static ProbabilityInfo createProbabilityInfoFromArray(final int[] probabilityInfo) { + return new ProbabilityInfo( + probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_PROBABILITY_INDEX], + probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_TIMESTAMP_INDEX], + probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_LEVEL_INDEX], + probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_COUNT_INDEX]); + } + + // Construct word property using information from native code. + // This represents invalid word when the probability is BinaryDictionary.NOT_A_PROBABILITY. + public WordProperty(final int[] codePoints, final boolean isNotAWord, + final boolean isBlacklisted, final boolean hasBigram, + final boolean hasShortcuts, final int[] probabilityInfo, + final ArrayList<int[]> bigramTargets, final ArrayList<int[]> bigramProbabilityInfo, + final ArrayList<int[]> shortcutTargets, + final ArrayList<Integer> shortcutProbabilities) { + mWord = StringUtils.getStringFromNullTerminatedCodePointArray(codePoints); + mProbabilityInfo = createProbabilityInfoFromArray(probabilityInfo); + mShortcutTargets = CollectionUtils.newArrayList(); + mBigrams = CollectionUtils.newArrayList(); + mIsNotAWord = isNotAWord; + mIsBlacklistEntry = isBlacklisted; + mHasShortcuts = hasShortcuts; + mHasBigrams = hasBigram; + + final int bigramTargetCount = bigramTargets.size(); + for (int i = 0; i < bigramTargetCount; i++) { + final String bigramTargetString = + StringUtils.getStringFromNullTerminatedCodePointArray(bigramTargets.get(i)); + mBigrams.add(new WeightedString(bigramTargetString, + createProbabilityInfoFromArray(bigramProbabilityInfo.get(i)))); + } + + final int shortcutTargetCount = shortcutTargets.size(); + for (int i = 0; i < shortcutTargetCount; i++) { + final String shortcutTargetString = + StringUtils.getStringFromNullTerminatedCodePointArray(shortcutTargets.get(i)); + mShortcutTargets.add( + new WeightedString(shortcutTargetString, shortcutProbabilities.get(i))); + } + } + + public int getProbability() { + return mProbabilityInfo.mProbability; + } + + private static int computeHashCode(WordProperty word) { + return Arrays.hashCode(new Object[] { + word.mWord, + word.mProbabilityInfo, + word.mShortcutTargets.hashCode(), + word.mBigrams.hashCode(), + word.mIsNotAWord, + word.mIsBlacklistEntry + }); + } + + /** + * Three-way comparison. + * + * A Word x is greater than a word y if x has a higher frequency. If they have the same + * frequency, they are sorted in lexicographic order. + */ + @Override + public int compareTo(final WordProperty w) { + if (getProbability() < w.getProbability()) return 1; + if (getProbability() > w.getProbability()) return -1; + return mWord.compareTo(w.mWord); + } + + /** + * Equality test. + * + * Words are equal if they have the same frequency, the same spellings, and the same + * attributes. + */ + @Override + public boolean equals(Object o) { + if (o == this) return true; + if (!(o instanceof WordProperty)) return false; + WordProperty w = (WordProperty)o; + return mProbabilityInfo.equals(w.mProbabilityInfo) && mWord.equals(w.mWord) + && mShortcutTargets.equals(w.mShortcutTargets) && mBigrams.equals(w.mBigrams) + && mIsNotAWord == w.mIsNotAWord && mIsBlacklistEntry == w.mIsBlacklistEntry + && mHasBigrams == w.mHasBigrams && mHasShortcuts && w.mHasBigrams; + } + + @Override + public int hashCode() { + if (mHashCode == 0) { + mHashCode = computeHashCode(this); + } + return mHashCode; + } + + @UsedForTesting + public boolean isValid() { + return getProbability() != BinaryDictionary.NOT_A_PROBABILITY; + } + + @Override + public String toString() { + return CombinedFormatUtils.formatWordProperty(this); + } +} diff --git a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java index fa5ae92e7..c87dd1589 100644 --- a/java/src/com/android/inputmethod/latin/settings/DebugSettings.java +++ b/java/src/com/android/inputmethod/latin/settings/DebugSettings.java @@ -18,6 +18,7 @@ package com.android.inputmethod.latin.settings; import android.content.Intent; import android.content.SharedPreferences; +import android.content.res.Resources; import android.os.Bundle; import android.os.Process; import android.preference.CheckBoxPreference; @@ -32,6 +33,7 @@ import com.android.inputmethod.latin.LatinImeLogger; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.debug.ExternalDictionaryGetterForDebug; import com.android.inputmethod.latin.utils.ApplicationUtils; +import com.android.inputmethod.latin.utils.ResourceUtils; public final class DebugSettings extends PreferenceFragment implements SharedPreferences.OnSharedPreferenceChangeListener { @@ -42,6 +44,14 @@ public final class DebugSettings extends PreferenceFragment public static final String PREF_STATISTICS_LOGGING = "enable_logging"; public static final String PREF_USE_ONLY_PERSONALIZATION_DICTIONARY_FOR_DEBUG = "use_only_personalization_dictionary_for_debug"; + public static final String PREF_KEY_PREVIEW_SHOW_UP_START_SCALE = + "pref_key_preview_show_up_start_scale"; + public static final String PREF_KEY_PREVIEW_DISMISS_END_SCALE = + "pref_key_preview_dismiss_end_scale"; + public static final String PREF_KEY_PREVIEW_SHOW_UP_DURATION = + "pref_key_preview_show_up_duration"; + public static final String PREF_KEY_PREVIEW_DISMISS_DURATION = + "pref_key_preview_dismiss_duration"; private static final String PREF_READ_EXTERNAL_DICTIONARY = "read_external_dictionary"; private static final String PREF_DUMP_CONTACTS_DICT = "dump_contacts_dict"; private static final String PREF_DUMP_USER_DICT = "dump_user_dict"; @@ -101,6 +111,17 @@ public final class DebugSettings extends PreferenceFragment dictDumpPrefClickListener); findPreference(PREF_DUMP_PERSONALIZATION_DICT).setOnPreferenceClickListener( dictDumpPrefClickListener); + final Resources res = getResources(); + setupKeyPreviewAnimationDuration(prefs, res, PREF_KEY_PREVIEW_SHOW_UP_DURATION, + res.getInteger(R.integer.config_key_preview_show_up_duration)); + setupKeyPreviewAnimationDuration(prefs, res, PREF_KEY_PREVIEW_DISMISS_DURATION, + res.getInteger(R.integer.config_key_preview_dismiss_duration)); + setupKeyPreviewAnimationScale(prefs, res, PREF_KEY_PREVIEW_SHOW_UP_START_SCALE, + ResourceUtils.getFloatFromFraction( + res, R.fraction.config_key_preview_show_up_start_scale)); + setupKeyPreviewAnimationScale(prefs, res, PREF_KEY_PREVIEW_DISMISS_END_SCALE, + ResourceUtils.getFloatFromFraction( + res, R.fraction.config_key_preview_dismiss_end_scale)); mServiceNeedsRestart = false; mDebugMode = (CheckBoxPreference) findPreference(PREF_DEBUG_MODE); @@ -180,4 +201,92 @@ public final class DebugSettings extends PreferenceFragment mDebugMode.setSummary(version); } } + + private void setupKeyPreviewAnimationScale(final SharedPreferences sp, final Resources res, + final String prefKey, final float defaultValue) { + final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference(prefKey); + if (pref == null) { + return; + } + pref.setInterface(new SeekBarDialogPreference.ValueProxy() { + private static final float PERCENTAGE_FLOAT = 100.0f; + + private float getValueFromPercentage(final int percentage) { + return percentage / PERCENTAGE_FLOAT; + } + + private int getPercentageFromValue(final float floatValue) { + return (int)(floatValue * PERCENTAGE_FLOAT); + } + + @Override + public void writeValue(final int value, final String key) { + sp.edit().putFloat(key, getValueFromPercentage(value)).apply(); + } + + @Override + public void writeDefaultValue(final String key) { + sp.edit().remove(key).apply(); + } + + @Override + public int readValue(final String key) { + return getPercentageFromValue( + Settings.readKeyPreviewAnimationScale(sp, key, defaultValue)); + } + + @Override + public int readDefaultValue(final String key) { + return getPercentageFromValue(defaultValue); + } + + @Override + public String getValueText(final int value) { + if (value < 0) { + return res.getString(R.string.settings_system_default); + } + return String.format("%d%%", value); + } + + @Override + public void feedbackValue(final int value) {} + }); + } + + private void setupKeyPreviewAnimationDuration(final SharedPreferences sp, final Resources res, + final String prefKey, final int defaultValue) { + final SeekBarDialogPreference pref = (SeekBarDialogPreference)findPreference(prefKey); + if (pref == null) { + return; + } + pref.setInterface(new SeekBarDialogPreference.ValueProxy() { + @Override + public void writeValue(final int value, final String key) { + sp.edit().putInt(key, value).apply(); + } + + @Override + public void writeDefaultValue(final String key) { + sp.edit().remove(key).apply(); + } + + @Override + public int readValue(final String key) { + return Settings.readKeyPreviewAnimationDuration(sp, key, defaultValue); + } + + @Override + public int readDefaultValue(final String key) { + return defaultValue; + } + + @Override + public String getValueText(final int value) { + return res.getString(R.string.abbreviation_unit_milliseconds, value); + } + + @Override + public void feedbackValue(final int value) {} + }); + } } diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java index 9bf269b6e..f0f7de7d4 100644 --- a/java/src/com/android/inputmethod/latin/settings/Settings.java +++ b/java/src/com/android/inputmethod/latin/settings/Settings.java @@ -107,6 +107,9 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static final String PREF_EMOJI_CATEGORY_LAST_TYPED_ID = "emoji_category_last_typed_id"; public static final String PREF_LAST_SHOWN_EMOJI_CATEGORY_ID = "last_shown_emoji_category_id"; + private static final float UNDEFINED_PREFERENCE_VALUE_FLOAT = -1.0f; + private static final int UNDEFINED_PREFERENCE_VALUE_INT = -1; + private Context mContext; private Resources mRes; private SharedPreferences mPrefs; @@ -301,8 +304,10 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static float readKeypressSoundVolume(final SharedPreferences prefs, final Resources res) { - final float volume = prefs.getFloat(PREF_KEYPRESS_SOUND_VOLUME, -1.0f); - return (volume >= 0) ? volume : readDefaultKeypressSoundVolume(res); + final float volume = prefs.getFloat( + PREF_KEYPRESS_SOUND_VOLUME, UNDEFINED_PREFERENCE_VALUE_FLOAT); + return (volume != UNDEFINED_PREFERENCE_VALUE_FLOAT) ? volume + : readDefaultKeypressSoundVolume(res); } public static float readDefaultKeypressSoundVolume(final Resources res) { @@ -312,8 +317,10 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static int readKeyLongpressTimeout(final SharedPreferences prefs, final Resources res) { - final int ms = prefs.getInt(PREF_KEY_LONGPRESS_TIMEOUT, -1); - return (ms >= 0) ? ms : readDefaultKeyLongpressTimeout(res); + final int milliseconds = prefs.getInt( + PREF_KEY_LONGPRESS_TIMEOUT, UNDEFINED_PREFERENCE_VALUE_INT); + return (milliseconds != UNDEFINED_PREFERENCE_VALUE_INT) ? milliseconds + : readDefaultKeyLongpressTimeout(res); } public static int readDefaultKeyLongpressTimeout(final Resources res) { @@ -322,8 +329,10 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang public static int readKeypressVibrationDuration(final SharedPreferences prefs, final Resources res) { - final int ms = prefs.getInt(PREF_VIBRATION_DURATION_SETTINGS, -1); - return (ms >= 0) ? ms : readDefaultKeypressVibrationDuration(res); + final int milliseconds = prefs.getInt( + PREF_VIBRATION_DURATION_SETTINGS, UNDEFINED_PREFERENCE_VALUE_INT); + return (milliseconds != UNDEFINED_PREFERENCE_VALUE_INT) ? milliseconds + : readDefaultKeypressVibrationDuration(res); } public static int readDefaultKeypressVibrationDuration(final Resources res) { @@ -335,6 +344,18 @@ public final class Settings implements SharedPreferences.OnSharedPreferenceChang return prefs.getBoolean(DebugSettings.PREF_USABILITY_STUDY_MODE, true); } + public static float readKeyPreviewAnimationScale(final SharedPreferences prefs, + final String prefKey, final float defaultValue) { + final float fraction = prefs.getFloat(prefKey, UNDEFINED_PREFERENCE_VALUE_FLOAT); + return (fraction != UNDEFINED_PREFERENCE_VALUE_FLOAT) ? fraction : defaultValue; + } + + public static int readKeyPreviewAnimationDuration(final SharedPreferences prefs, + final String prefKey, final int defaultValue) { + final int milliseconds = prefs.getInt(prefKey, UNDEFINED_PREFERENCE_VALUE_INT); + return (milliseconds != UNDEFINED_PREFERENCE_VALUE_INT) ? milliseconds : defaultValue; + } + public static boolean readUseFullscreenMode(final Resources res) { return res.getBoolean(R.bool.config_use_fullscreen_mode); } diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java index 2979544ae..90d3519a4 100644 --- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java +++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java @@ -29,6 +29,7 @@ import com.android.inputmethod.latin.InputAttributes; import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.RichInputMethodManager; import com.android.inputmethod.latin.utils.AsyncResultHolder; +import com.android.inputmethod.latin.utils.ResourceUtils; import com.android.inputmethod.latin.utils.TargetPackageInfoGetterTask; import java.util.Arrays; @@ -93,6 +94,10 @@ public final class SettingsValues { // Debug settings public final boolean mIsInternal; + public final int mKeyPreviewShowUpDuration; + public final int mKeyPreviewDismissDuration; + public final float mKeyPreviewShowUpStartScale; + public final float mKeyPreviewDismissEndScale; public SettingsValues(final Context context, final SharedPreferences prefs, final Resources res, final InputAttributes inputAttributes) { @@ -149,6 +154,20 @@ public final class SettingsValues { AdditionalFeaturesSettingUtils.readAdditionalFeaturesPreferencesIntoArray( prefs, mAdditionalFeaturesSettingValues); mIsInternal = Settings.isInternal(prefs); + mKeyPreviewShowUpDuration = Settings.readKeyPreviewAnimationDuration( + prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_DURATION, + res.getInteger(R.integer.config_key_preview_show_up_duration)); + mKeyPreviewDismissDuration = Settings.readKeyPreviewAnimationDuration( + prefs, DebugSettings.PREF_KEY_PREVIEW_DISMISS_DURATION, + res.getInteger(R.integer.config_key_preview_dismiss_duration)); + mKeyPreviewShowUpStartScale = Settings.readKeyPreviewAnimationScale( + prefs, DebugSettings.PREF_KEY_PREVIEW_SHOW_UP_START_SCALE, + ResourceUtils.getFloatFromFraction( + res, R.fraction.config_key_preview_show_up_start_scale)); + mKeyPreviewDismissEndScale = Settings.readKeyPreviewAnimationScale( + prefs, DebugSettings.PREF_KEY_PREVIEW_DISMISS_END_SCALE, + ResourceUtils.getFloatFromFraction( + res, R.fraction.config_key_preview_dismiss_end_scale)); mUseOnlyPersonalizationDictionaryForDebug = prefs.getBoolean( DebugSettings.PREF_USE_ONLY_PERSONALIZATION_DICTIONARY_FOR_DEBUG, false); mDisplayOrientation = res.getConfiguration().orientation; diff --git a/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java b/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java index 70cb2b285..60ca5baab 100644 --- a/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java +++ b/java/src/com/android/inputmethod/latin/settings/SpacingAndPunctuations.java @@ -75,7 +75,7 @@ public final class SpacingAndPunctuations { final ArrayList<SuggestedWordInfo> puncList = CollectionUtils.newArrayList(); if (puncs != null) { for (final String puncSpec : puncs) { - // TODO: Stop using KeySpceParser.getLabel(). + // TODO: Stop using KeySpecParser.getLabel(). // TODO: Punctuation suggestions should honor RTL languages. puncList.add(new SuggestedWordInfo(KeySpecParser.getLabel(puncSpec), SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_HARDCODED, @@ -84,7 +84,7 @@ public final class SpacingAndPunctuations { SuggestedWordInfo.NOT_A_CONFIDENCE /* autoCommitFirstWordConfidence */)); } } - return new SuggestedWords(puncList, + return new SuggestedWords(puncList, null /* rawSuggestions */, false /* typedWordValid */, false /* hasAutoCorrectionCandidate */, true /* isPunctuationSuggestions */, diff --git a/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java b/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java new file mode 100644 index 000000000..bb7ae2f9b --- /dev/null +++ b/java/src/com/android/inputmethod/latin/utils/CombinedFormatUtils.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.android.inputmethod.latin.utils; + +import com.android.inputmethod.latin.makedict.DictionaryHeader; +import com.android.inputmethod.latin.makedict.ProbabilityInfo; +import com.android.inputmethod.latin.makedict.WordProperty; +import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; + +import java.util.HashMap; + +public class CombinedFormatUtils { + public static final String DICTIONARY_TAG = "dictionary"; + public static final String BIGRAM_TAG = "bigram"; + public static final String SHORTCUT_TAG = "shortcut"; + public static final String PROBABILITY_TAG = "f"; + public static final String HISTORICAL_INFO_TAG = "historicalInfo"; + public static final String HISTORICAL_INFO_SEPARATOR = ":"; + public static final String WORD_TAG = "word"; + public static final String NOT_A_WORD_TAG = "not_a_word"; + public static final String BLACKLISTED_TAG = "blacklisted"; + + public static String formatAttributeMap(final HashMap<String, String> attributeMap) { + final StringBuilder builder = new StringBuilder(); + builder.append(DICTIONARY_TAG + "="); + if (attributeMap.containsKey(DictionaryHeader.DICTIONARY_ID_KEY)) { + builder.append(attributeMap.get(DictionaryHeader.DICTIONARY_ID_KEY)); + } + for (final String key : attributeMap.keySet()) { + if (key.equals(DictionaryHeader.DICTIONARY_ID_KEY)) { + continue; + } + final String value = attributeMap.get(key); + builder.append("," + key + "=" + value); + } + builder.append("\n"); + return builder.toString(); + } + + public static String formatWordProperty(final WordProperty wordProperty) { + final StringBuilder builder = new StringBuilder(); + builder.append(" " + WORD_TAG + "=" + wordProperty.mWord); + builder.append(","); + builder.append(formatProbabilityInfo(wordProperty.mProbabilityInfo)); + if (wordProperty.mIsNotAWord) { + builder.append("," + NOT_A_WORD_TAG + "=true"); + } + if (wordProperty.mIsBlacklistEntry) { + builder.append("," + BLACKLISTED_TAG + "=true"); + } + builder.append("\n"); + if (wordProperty.mShortcutTargets != null) { + for (final WeightedString shortcutTarget : wordProperty.mShortcutTargets) { + builder.append(" " + SHORTCUT_TAG + "=" + shortcutTarget.mWord); + builder.append(","); + builder.append(formatProbabilityInfo(shortcutTarget.mProbabilityInfo)); + builder.append("\n"); + } + } + if (wordProperty.mBigrams != null) { + for (final WeightedString bigram : wordProperty.mBigrams) { + builder.append(" " + BIGRAM_TAG + "=" + bigram.mWord); + builder.append(","); + builder.append(formatProbabilityInfo(bigram.mProbabilityInfo)); + builder.append("\n"); + } + } + return builder.toString(); + } + + public static String formatProbabilityInfo(final ProbabilityInfo probabilityInfo) { + final StringBuilder builder = new StringBuilder(); + builder.append(PROBABILITY_TAG + "=" + probabilityInfo.mProbability); + if (probabilityInfo.hasHistoricalInfo()) { + builder.append(","); + builder.append(HISTORICAL_INFO_TAG + "="); + builder.append(probabilityInfo.mTimestamp); + builder.append(HISTORICAL_INFO_SEPARATOR); + builder.append(probabilityInfo.mLevel); + builder.append(HISTORICAL_INFO_SEPARATOR); + builder.append(probabilityInfo.mCount); + } + return builder.toString(); + } +} diff --git a/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java b/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java index deb28a08d..3a1c24c74 100644 --- a/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java +++ b/java/src/com/android/inputmethod/latin/utils/ResourceUtils.java @@ -260,6 +260,10 @@ public final class ResourceUtils { return dimension >= 0; } + public static float getFloatFromFraction(final Resources res, final int fractionResId) { + return res.getFraction(fractionResId, 1, 1); + } + public static float getFraction(final TypedArray a, final int index, final float defValue) { final TypedValue value = a.peekValue(index); if (value == null || !isFractionValue(value)) { diff --git a/java/src/com/android/inputmethod/latin/utils/WordProperty.java b/java/src/com/android/inputmethod/latin/utils/WordProperty.java deleted file mode 100644 index 37d1102e3..000000000 --- a/java/src/com/android/inputmethod/latin/utils/WordProperty.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -package com.android.inputmethod.latin.utils; - -import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.latin.BinaryDictionary; -import com.android.inputmethod.latin.makedict.FusionDictionary.WeightedString; -import com.android.inputmethod.latin.makedict.ProbabilityInfo; - -import java.util.ArrayList; - -// This has information that belong to a unigram. This class has some detailed attributes such as -// historical information but they have to be checked only for testing purpose. -@UsedForTesting -public class WordProperty { - public final String mCodePoints; - public final boolean mIsNotAWord; - public final boolean mIsBlacklisted; - public final boolean mHasBigrams; - public final boolean mHasShortcuts; - public final ProbabilityInfo mProbabilityInfo; - public final ArrayList<WeightedString> mBigramTargets = CollectionUtils.newArrayList(); - public final ArrayList<ProbabilityInfo> mBigramProbabilityInfo = CollectionUtils.newArrayList(); - public final ArrayList<WeightedString> mShortcutTargets = CollectionUtils.newArrayList(); - - private static ProbabilityInfo createProbabilityInfoFromArray(final int[] probabilityInfo) { - return new ProbabilityInfo( - probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_PROBABILITY_INDEX], - probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_TIMESTAMP_INDEX], - probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_LEVEL_INDEX], - probabilityInfo[BinaryDictionary.FORMAT_WORD_PROPERTY_COUNT_INDEX]); - } - - // This represents invalid word when the probability is BinaryDictionary.NOT_A_PROBABILITY. - public WordProperty(final int[] codePoints, final boolean isNotAWord, - final boolean isBlacklisted, final boolean hasBigram, - final boolean hasShortcuts, final int[] probabilityInfo, - final ArrayList<int[]> bigramTargets, final ArrayList<int[]> bigramProbabilityInfo, - final ArrayList<int[]> shortcutTargets, - final ArrayList<Integer> shortcutProbabilities) { - mCodePoints = StringUtils.getStringFromNullTerminatedCodePointArray(codePoints); - mIsNotAWord = isNotAWord; - mIsBlacklisted = isBlacklisted; - mHasBigrams = hasBigram; - mHasShortcuts = hasShortcuts; - mProbabilityInfo = createProbabilityInfoFromArray(probabilityInfo); - - final int bigramTargetCount = bigramTargets.size(); - for (int i = 0; i < bigramTargetCount; i++) { - final String bigramTargetString = - StringUtils.getStringFromNullTerminatedCodePointArray(bigramTargets.get(i)); - final ProbabilityInfo bigramProbability = - createProbabilityInfoFromArray(bigramProbabilityInfo.get(i)); - mBigramTargets.add( - new WeightedString(bigramTargetString, bigramProbability.mProbability)); - mBigramProbabilityInfo.add(bigramProbability); - } - - final int shortcutTargetCount = shortcutTargets.size(); - for (int i = 0; i < shortcutTargetCount; i++) { - final String shortcutTargetString = - StringUtils.getStringFromNullTerminatedCodePointArray(shortcutTargets.get(i)); - mShortcutTargets.add( - new WeightedString(shortcutTargetString, shortcutProbabilities.get(i))); - } - } - - @UsedForTesting - public boolean isValid() { - return mProbabilityInfo.mProbability != BinaryDictionary.NOT_A_PROBABILITY; - } - - @Override - public String toString() { - // TODO: Move this logic to CombinedInputOutput. - final StringBuffer builder = new StringBuffer(); - builder.append(" word=" + mCodePoints); - builder.append(","); - builder.append("f=" + mProbabilityInfo.mProbability); - if (mIsNotAWord) { - builder.append(","); - builder.append("not_a_word=true"); - } - if (mIsBlacklisted) { - builder.append(","); - builder.append("blacklisted=true"); - } - if (mProbabilityInfo.mTimestamp != BinaryDictionary.NOT_A_VALID_TIMESTAMP) { - builder.append(","); - builder.append("historicalInfo=" + mProbabilityInfo); - } - builder.append("\n"); - for (int i = 0; i < mBigramTargets.size(); i++) { - builder.append(" bigram=" + mBigramTargets.get(i).mWord); - builder.append(","); - builder.append("f=" + mBigramTargets.get(i).getProbability()); - if (mBigramProbabilityInfo.get(i).mTimestamp - != BinaryDictionary.NOT_A_VALID_TIMESTAMP) { - builder.append(","); - builder.append("historicalInfo=" + mBigramProbabilityInfo.get(i)); - } - builder.append("\n"); - } - for (int i = 0; i < mShortcutTargets.size(); i++) { - builder.append(" shortcut=" + mShortcutTargets.get(i).mWord); - builder.append(","); - builder.append("f=" + mShortcutTargets.get(i).getProbability()); - builder.append("\n"); - } - return builder.toString(); - } -}
\ No newline at end of file |