diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin/Suggest.java')
-rw-r--r-- | java/src/com/android/inputmethod/latin/Suggest.java | 360 |
1 files changed, 210 insertions, 150 deletions
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index caa5aac51..69754d769 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -20,7 +20,9 @@ import android.content.Context; import android.text.TextUtils; import android.util.Log; +import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import java.io.File; import java.util.ArrayList; @@ -42,9 +44,8 @@ public class Suggest implements Dictionary.WordCallback { public static final int APPROX_MAX_WORD_LENGTH = 32; public static final int CORRECTION_NONE = 0; - public static final int CORRECTION_BASIC = 1; - public static final int CORRECTION_FULL = 2; - public static final int CORRECTION_FULL_BIGRAM = 3; + public static final int CORRECTION_FULL = 1; + public static final int CORRECTION_FULL_BIGRAM = 2; /** * Words that appear in both bigram and unigram data gets multiplier ranging from @@ -83,8 +84,6 @@ public class Suggest implements Dictionary.WordCallback { private static final boolean DBG = LatinImeLogger.sDBG; - private AutoCorrection mAutoCorrection; - private Dictionary mMainDict; private ContactsDictionary mContactsDict; private WhitelistDictionary mWhiteListDictionary; @@ -100,14 +99,15 @@ public class Suggest implements Dictionary.WordCallback { private int[] mBigramScores = new int[PREF_MAX_BIGRAMS]; private ArrayList<CharSequence> mSuggestions = new ArrayList<CharSequence>(); - ArrayList<CharSequence> mBigramSuggestions = new ArrayList<CharSequence>(); - private CharSequence mTypedWord; + private ArrayList<CharSequence> mBigramSuggestions = new ArrayList<CharSequence>(); + private CharSequence mConsideredWord; // TODO: Remove these member variables by passing more context to addWord() callback method private boolean mIsFirstCharCapitalized; private boolean mIsAllUpperCase; + private int mTrailingSingleQuotesCount; - private int mCorrectionMode = CORRECTION_BASIC; + private static final int MINIMUM_SAFETY_NET_CHAR_LENGTH = 4; public Suggest(final Context context, final int dictionaryResId, final Locale locale) { initAsynchronously(context, dictionaryResId, locale); @@ -116,15 +116,13 @@ public class Suggest implements Dictionary.WordCallback { /* package for test */ Suggest(final Context context, final File dictionary, final long startOffset, final long length, final Flag[] flagArray, final Locale locale) { - initSynchronously(null, DictionaryFactory.createDictionaryForTest(context, dictionary, + initSynchronously(context, DictionaryFactory.createDictionaryForTest(context, dictionary, startOffset, length, flagArray), locale); } private void initWhitelistAndAutocorrectAndPool(final Context context, final Locale locale) { mWhiteListDictionary = new WhitelistDictionary(context, locale); addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_WHITELIST, mWhiteListDictionary); - mAutoCorrection = new AutoCorrection(); - StringBuilderPool.ensureCapacity(mPrefMaxSuggestions, getApproxMaxWordLength()); } private void initAsynchronously(final Context context, final int dictionaryResId, @@ -144,7 +142,7 @@ public class Suggest implements Dictionary.WordCallback { initWhitelistAndAutocorrectAndPool(context, locale); } - private void addOrReplaceDictionary(Map<String, Dictionary> dictionaries, String key, + private static void addOrReplaceDictionary(Map<String, Dictionary> dictionaries, String key, Dictionary dict) { final Dictionary oldDict = (dict == null) ? dictionaries.remove(key) @@ -169,14 +167,6 @@ public class Suggest implements Dictionary.WordCallback { }.start(); } - public int getCorrectionMode() { - return mCorrectionMode; - } - - public void setCorrectionMode(int mode) { - mCorrectionMode = mode; - } - // The main dictionary could have been loaded asynchronously. Don't cache the return value // of this method. public boolean hasMainDictionary() { @@ -191,7 +181,7 @@ public class Suggest implements Dictionary.WordCallback { return mUnigramDictionaries; } - public int getApproxMaxWordLength() { + public static int getApproxMaxWordLength() { return APPROX_MAX_WORD_LENGTH; } @@ -226,44 +216,11 @@ public class Suggest implements Dictionary.WordCallback { mAutoCorrectionThreshold = threshold; } - public boolean isAggressiveAutoCorrectionMode() { - return (mAutoCorrectionThreshold == 0); - } - - /** - * Number of suggestions to generate from the input key sequence. This has - * to be a number between 1 and 100 (inclusive). - * @param maxSuggestions - * @throws IllegalArgumentException if the number is out of range - */ - public void setMaxSuggestions(int maxSuggestions) { - if (maxSuggestions < 1 || maxSuggestions > 100) { - throw new IllegalArgumentException("maxSuggestions must be between 1 and 100"); - } - mPrefMaxSuggestions = maxSuggestions; - mScores = new int[mPrefMaxSuggestions]; - mBigramScores = new int[PREF_MAX_BIGRAMS]; - collectGarbage(mSuggestions, mPrefMaxSuggestions); - StringBuilderPool.ensureCapacity(mPrefMaxSuggestions, getApproxMaxWordLength()); - } - - /** - * Returns a object which represents suggested words that match the list of character codes - * passed in. This object contents will be overwritten the next time this function is called. - * @param wordComposer contains what is currently being typed - * @param prevWordForBigram previous word (used only for bigram) - * @return suggested words object. - */ - public SuggestedWords getSuggestions(final WordComposer wordComposer, - final CharSequence prevWordForBigram, final ProximityInfo proximityInfo) { - return getSuggestedWordBuilder(wordComposer, prevWordForBigram, - proximityInfo).build(); - } - - private CharSequence capitalizeWord(boolean all, boolean first, CharSequence word) { + private static CharSequence capitalizeWord(final boolean all, final boolean first, + final CharSequence word) { if (TextUtils.isEmpty(word) || !(all || first)) return word; final int wordLength = word.length(); - final StringBuilder sb = StringBuilderPool.getStringBuilder(getApproxMaxWordLength()); + final StringBuilder sb = new StringBuilder(getApproxMaxWordLength()); // TODO: Must pay attention to locale when changing case. if (all) { sb.append(word.toString().toUpperCase()); @@ -277,41 +234,73 @@ public class Suggest implements Dictionary.WordCallback { } protected void addBigramToSuggestions(CharSequence bigram) { - // TODO: Try to be a little more shrewd with resource allocation. - // At the moment we copy this object because the StringBuilders are pooled (see - // StringBuilderPool.java) and when we are finished using mSuggestions and - // mBigramSuggestions we will take everything from both and insert them back in the - // pool, so we can't allow the same object to be in both lists at the same time. - final StringBuilder sb = StringBuilderPool.getStringBuilder(getApproxMaxWordLength()); + final StringBuilder sb = new StringBuilder(getApproxMaxWordLength()); sb.append(bigram); mSuggestions.add(sb); } + private static final WordComposer sEmptyWordComposer = new WordComposer(); + public SuggestedWords getBigramPredictions(CharSequence prevWordForBigram) { + LatinImeLogger.onStartSuggestion(prevWordForBigram); + mIsFirstCharCapitalized = false; + mIsAllUpperCase = false; + mTrailingSingleQuotesCount = 0; + mSuggestions = new ArrayList<CharSequence>(mPrefMaxSuggestions); + Arrays.fill(mScores, 0); + + // Treating USER_TYPED as UNIGRAM suggestion for logging now. + LatinImeLogger.onAddSuggestedWord("", Suggest.DIC_USER_TYPED, Dictionary.UNIGRAM); + mConsideredWord = ""; + + Arrays.fill(mBigramScores, 0); + mBigramSuggestions = new ArrayList<CharSequence>(PREF_MAX_BIGRAMS); + + CharSequence lowerPrevWord = prevWordForBigram.toString().toLowerCase(); + if (mMainDict != null && mMainDict.isValidWord(lowerPrevWord)) { + prevWordForBigram = lowerPrevWord; + } + for (final Dictionary dictionary : mBigramDictionaries.values()) { + dictionary.getBigrams(sEmptyWordComposer, prevWordForBigram, this); + } + // Nothing entered: return all bigrams for the previous word + int insertCount = Math.min(mBigramSuggestions.size(), mPrefMaxSuggestions); + for (int i = 0; i < insertCount; ++i) { + addBigramToSuggestions(mBigramSuggestions.get(i)); + } + + StringUtils.removeDupes(mSuggestions); + + return new SuggestedWords(SuggestedWords.getFromCharSequenceList(mSuggestions), + false /* typedWordValid */, + false /* hasAutoCorrectionCandidate */, + false /* allowsToBeAutoCorrected */, + false /* isPunctuationSuggestions */, + false /* isObsoleteSuggestions */); + } + // TODO: cleanup dictionaries looking up and suggestions building with SuggestedWords.Builder - public SuggestedWords.Builder getSuggestedWordBuilder( + public SuggestedWords getSuggestedWords( final WordComposer wordComposer, CharSequence prevWordForBigram, - final ProximityInfo proximityInfo) { + final ProximityInfo proximityInfo, final int correctionMode) { LatinImeLogger.onStartSuggestion(prevWordForBigram); - mAutoCorrection.init(); mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized(); mIsAllUpperCase = wordComposer.isAllUpperCase(); - collectGarbage(mSuggestions, mPrefMaxSuggestions); + mTrailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount(); + mSuggestions = new ArrayList<CharSequence>(mPrefMaxSuggestions); Arrays.fill(mScores, 0); - // Save a lowercase version of the original word - String typedWord = wordComposer.getTypedWord(); - if (typedWord != null) { - // Treating USER_TYPED as UNIGRAM suggestion for logging now. - LatinImeLogger.onAddSuggestedWord(typedWord, Suggest.DIC_USER_TYPED, - Dictionary.DataType.UNIGRAM); - } - mTypedWord = typedWord; + final String typedWord = wordComposer.getTypedWord(); + final String consideredWord = mTrailingSingleQuotesCount > 0 + ? typedWord.substring(0, typedWord.length() - mTrailingSingleQuotesCount) + : typedWord; + // Treating USER_TYPED as UNIGRAM suggestion for logging now. + LatinImeLogger.onAddSuggestedWord(typedWord, Suggest.DIC_USER_TYPED, Dictionary.UNIGRAM); + mConsideredWord = consideredWord; - if (wordComposer.size() <= 1 && (mCorrectionMode == CORRECTION_FULL_BIGRAM - || mCorrectionMode == CORRECTION_BASIC)) { + if (wordComposer.size() <= 1 && (correctionMode == CORRECTION_FULL_BIGRAM)) { // At first character typed, search only the bigrams Arrays.fill(mBigramScores, 0); - collectGarbage(mBigramSuggestions, PREF_MAX_BIGRAMS); + mBigramSuggestions = new ArrayList<CharSequence>(PREF_MAX_BIGRAMS); if (!TextUtils.isEmpty(prevWordForBigram)) { CharSequence lowerPrevWord = prevWordForBigram.toString().toLowerCase(); @@ -321,7 +310,7 @@ public class Suggest implements Dictionary.WordCallback { for (final Dictionary dictionary : mBigramDictionaries.values()) { dictionary.getBigrams(wordComposer, prevWordForBigram, this); } - if (TextUtils.isEmpty(typedWord)) { + if (TextUtils.isEmpty(consideredWord)) { // Nothing entered: return all bigrams for the previous word int insertCount = Math.min(mBigramSuggestions.size(), mPrefMaxSuggestions); for (int i = 0; i < insertCount; ++i) { @@ -329,8 +318,7 @@ public class Suggest implements Dictionary.WordCallback { } } else { // Word entered: return only bigrams that match the first char of the typed word - @SuppressWarnings("null") - final char currentChar = typedWord.charAt(0); + final char currentChar = consideredWord.charAt(0); // TODO: Must pay attention to locale when changing case. final char currentCharUpper = Character.toUpperCase(currentChar); int count = 0; @@ -348,70 +336,126 @@ public class Suggest implements Dictionary.WordCallback { } } else if (wordComposer.size() > 1) { + final WordComposer wordComposerForLookup; + if (mTrailingSingleQuotesCount > 0) { + wordComposerForLookup = new WordComposer(wordComposer); + for (int i = mTrailingSingleQuotesCount - 1; i >= 0; --i) { + wordComposerForLookup.deleteLast(); + } + } else { + wordComposerForLookup = wordComposer; + } // At second character typed, search the unigrams (scores being affected by bigrams) for (final String key : mUnigramDictionaries.keySet()) { // Skip UserUnigramDictionary and WhitelistDictionary to lookup if (key.equals(DICT_KEY_USER_UNIGRAM) || key.equals(DICT_KEY_WHITELIST)) continue; final Dictionary dictionary = mUnigramDictionaries.get(key); - dictionary.getWords(wordComposer, this, proximityInfo); + dictionary.getWords(wordComposerForLookup, this, proximityInfo); } } - final String typedWordString = typedWord == null ? null : typedWord.toString(); - CharSequence whitelistedWord = capitalizeWord(mIsAllUpperCase, mIsFirstCharCapitalized, - mWhiteListDictionary.getWhitelistedWord(typedWordString)); + final CharSequence whitelistedWord = capitalizeWord(mIsAllUpperCase, + mIsFirstCharCapitalized, mWhiteListDictionary.getWhitelistedWord(consideredWord)); - mAutoCorrection.updateAutoCorrectionStatus(mUnigramDictionaries, wordComposer, - mSuggestions, mScores, typedWord, mAutoCorrectionThreshold, mCorrectionMode, - whitelistedWord); + final boolean hasAutoCorrection; + if (CORRECTION_FULL == correctionMode || CORRECTION_FULL_BIGRAM == correctionMode) { + final CharSequence autoCorrection = + AutoCorrection.computeAutoCorrectionWord(mUnigramDictionaries, wordComposer, + mSuggestions, mScores, consideredWord, mAutoCorrectionThreshold, + whitelistedWord); + hasAutoCorrection = (null != autoCorrection); + } else { + hasAutoCorrection = false; + } if (whitelistedWord != null) { - mSuggestions.add(0, whitelistedWord); + if (mTrailingSingleQuotesCount > 0) { + final StringBuilder sb = new StringBuilder(whitelistedWord); + for (int i = mTrailingSingleQuotesCount - 1; i >= 0; --i) { + sb.appendCodePoint(Keyboard.CODE_SINGLE_QUOTE); + } + mSuggestions.add(0, sb.toString()); + } else { + mSuggestions.add(0, whitelistedWord); + } } - if (typedWord != null) { - mSuggestions.add(0, typedWordString); - } - Utils.removeDupes(mSuggestions); + mSuggestions.add(0, typedWord); + StringUtils.removeDupes(mSuggestions); + final ArrayList<SuggestedWordInfo> suggestionsList; if (DBG) { - double normalizedScore = mAutoCorrection.getNormalizedScore(); - ArrayList<SuggestedWords.SuggestedWordInfo> scoreInfoList = - new ArrayList<SuggestedWords.SuggestedWordInfo>(); - scoreInfoList.add(new SuggestedWords.SuggestedWordInfo("+", false)); - for (int i = 0; i < mScores.length; ++i) { - if (normalizedScore > 0) { - final String scoreThreshold = String.format("%d (%4.2f)", mScores[i], - normalizedScore); - scoreInfoList.add( - new SuggestedWords.SuggestedWordInfo(scoreThreshold, false)); - normalizedScore = 0.0; - } else { - final String score = Integer.toString(mScores[i]); - scoreInfoList.add(new SuggestedWords.SuggestedWordInfo(score, false)); - } - } - for (int i = mScores.length; i < mSuggestions.size(); ++i) { - scoreInfoList.add(new SuggestedWords.SuggestedWordInfo("--", false)); - } - return new SuggestedWords.Builder().addWords(mSuggestions, scoreInfoList); + suggestionsList = getSuggestionsInfoListWithDebugInfo(typedWord, mSuggestions, mScores); + } else { + suggestionsList = SuggestedWords.getFromCharSequenceList(mSuggestions); + } + + // TODO: Change this scheme - a boolean is not enough. A whitelisted word may be "valid" + // but still autocorrected from - in the case the whitelist only capitalizes the word. + // The whitelist should be case-insensitive, so it's not possible to be consistent with + // a boolean flag. Right now this is handled with a slight hack in + // WhitelistDictionary#shouldForciblyAutoCorrectFrom. + final boolean allowsToBeAutoCorrected = AutoCorrection.allowsToBeAutoCorrected( + getUnigramDictionaries(), consideredWord, wordComposer.isFirstCharCapitalized()); + + boolean autoCorrectionAvailable = hasAutoCorrection; + if (correctionMode == CORRECTION_FULL || correctionMode == CORRECTION_FULL_BIGRAM) { + autoCorrectionAvailable |= !allowsToBeAutoCorrected; + } + // Don't auto-correct words with multiple capital letter + autoCorrectionAvailable &= !wordComposer.isMostlyCaps(); + if (allowsToBeAutoCorrected && suggestionsList.size() > 1 && mAutoCorrectionThreshold > 0 + && Suggest.shouldBlockAutoCorrectionBySafetyNet(typedWord, + suggestionsList.get(1).mWord)) { + autoCorrectionAvailable = false; } - return new SuggestedWords.Builder().addWords(mSuggestions, null); + return new SuggestedWords(suggestionsList, + !allowsToBeAutoCorrected /* typedWordValid */, + autoCorrectionAvailable /* hasAutoCorrectionCandidate */, + allowsToBeAutoCorrected /* allowsToBeAutoCorrected */, + false /* isPunctuationSuggestions */, + false /* isObsoleteSuggestions */); } - public boolean hasAutoCorrection() { - return mAutoCorrection.hasAutoCorrection(); + // This assumes the scores[] array is at least as long as suggestions.size() - 1. + private static ArrayList<SuggestedWordInfo> getSuggestionsInfoListWithDebugInfo( + final String typedWord, final ArrayList<CharSequence> suggestions, final int[] scores) { + // TODO: this doesn't take into account the fact that removing dupes from mSuggestions + // may have made mScores[] and mSuggestions out of sync. + final CharSequence autoCorrectionSuggestion = suggestions.get(0); + double normalizedScore = BinaryDictionary.calcNormalizedScore( + typedWord, autoCorrectionSuggestion.toString(), scores[0]); + final int suggestionsSize = suggestions.size(); + final ArrayList<SuggestedWordInfo> suggestionsList = + new ArrayList<SuggestedWordInfo>(suggestionsSize); + suggestionsList.add(new SuggestedWordInfo(autoCorrectionSuggestion, "+")); + // Note: i here is the index in mScores[], but the index in mSuggestions is one more + // than i because we added the typed word to mSuggestions without touching mScores. + for (int i = 0; i < scores.length && i < suggestionsSize - 1; ++i) { + final String scoreInfoString; + if (normalizedScore > 0) { + scoreInfoString = String.format("%d (%4.2f)", scores[i], normalizedScore); + normalizedScore = 0.0; + } else { + scoreInfoString = Integer.toString(scores[i]); + } + suggestionsList.add(new SuggestedWordInfo(suggestions.get(i + 1), scoreInfoString)); + } + for (int i = scores.length; i < suggestionsSize; ++i) { + suggestionsList.add(new SuggestedWordInfo(suggestions.get(i), "--")); + } + return suggestionsList; } @Override public boolean addWord(final char[] word, final int offset, final int length, int score, - final int dicTypeId, final Dictionary.DataType dataType) { - Dictionary.DataType dataTypeForLog = dataType; + final int dicTypeId, final int dataType) { + int dataTypeForLog = dataType; final ArrayList<CharSequence> suggestions; final int[] sortedScores; final int prefMaxSuggestions; - if(dataType == Dictionary.DataType.BIGRAM) { + if (dataType == Dictionary.BIGRAM) { suggestions = mBigramSuggestions; sortedScores = mBigramScores; prefMaxSuggestions = PREF_MAX_BIGRAMS; @@ -424,7 +468,7 @@ public class Suggest implements Dictionary.WordCallback { int pos = 0; // Check if it's the same word, only caps are different - if (Utils.equalsIgnoreCase(mTypedWord, word, offset, length)) { + if (StringUtils.equalsIgnoreCase(mConsideredWord, word, offset, length)) { // TODO: remove this surrounding if clause and move this logic to // getSuggestedWordBuilder. if (suggestions.size() > 0) { @@ -433,17 +477,17 @@ public class Suggest implements Dictionary.WordCallback { // frequency to determine the insertion position. This does not ensure strictly // correct ordering, but ensures the top score is on top which is enough for // removing duplicates correctly. - if (Utils.equalsIgnoreCase(currentHighestWord, word, offset, length) + if (StringUtils.equalsIgnoreCase(currentHighestWord, word, offset, length) && score <= sortedScores[0]) { pos = 1; } } } else { - if (dataType == Dictionary.DataType.UNIGRAM) { + if (dataType == Dictionary.UNIGRAM) { // Check if the word was already added before (by bigram data) int bigramSuggestion = searchBigramSuggestion(word,offset,length); if(bigramSuggestion >= 0) { - dataTypeForLog = Dictionary.DataType.BIGRAM; + dataTypeForLog = Dictionary.BIGRAM; // turn freq from bigram into multiplier specified above double multiplier = (((double) mBigramScores[bigramSuggestion]) / MAXIMUM_BIGRAM_FREQUENCY) @@ -474,7 +518,7 @@ public class Suggest implements Dictionary.WordCallback { System.arraycopy(sortedScores, pos, sortedScores, pos + 1, prefMaxSuggestions - pos - 1); sortedScores[pos] = score; - final StringBuilder sb = StringBuilderPool.getStringBuilder(getApproxMaxWordLength()); + final StringBuilder sb = new StringBuilder(getApproxMaxWordLength()); // TODO: Must pay attention to locale when changing case. if (mIsAllUpperCase) { sb.append(new String(word, offset, length).toUpperCase()); @@ -486,12 +530,12 @@ public class Suggest implements Dictionary.WordCallback { } else { sb.append(word, offset, length); } + for (int i = mTrailingSingleQuotesCount - 1; i >= 0; --i) { + sb.appendCodePoint(Keyboard.CODE_SINGLE_QUOTE); + } suggestions.add(pos, sb); if (suggestions.size() > prefMaxSuggestions) { - final CharSequence garbage = suggestions.remove(prefMaxSuggestions); - if (garbage instanceof StringBuilder) { - StringBuilderPool.recycle((StringBuilder)garbage); - } + suggestions.remove(prefMaxSuggestions); } else { LatinImeLogger.onAddSuggestedWord(sb.toString(), dicTypeId, dataTypeForLog); } @@ -502,39 +546,22 @@ public class Suggest implements Dictionary.WordCallback { // TODO This is almost O(n^2). Might need fix. // search whether the word appeared in bigram data int bigramSuggestSize = mBigramSuggestions.size(); - for(int i = 0; i < bigramSuggestSize; i++) { - if(mBigramSuggestions.get(i).length() == length) { + for (int i = 0; i < bigramSuggestSize; i++) { + if (mBigramSuggestions.get(i).length() == length) { boolean chk = true; - for(int j = 0; j < length; j++) { - if(mBigramSuggestions.get(i).charAt(j) != word[offset+j]) { + for (int j = 0; j < length; j++) { + if (mBigramSuggestions.get(i).charAt(j) != word[offset+j]) { chk = false; break; } } - if(chk) return i; + if (chk) return i; } } return -1; } - private void collectGarbage(ArrayList<CharSequence> suggestions, int prefMaxSuggestions) { - int poolSize = StringBuilderPool.getSize(); - int garbageSize = suggestions.size(); - while (poolSize < prefMaxSuggestions && garbageSize > 0) { - final CharSequence garbage = suggestions.get(garbageSize - 1); - if (garbage instanceof StringBuilder) { - StringBuilderPool.recycle((StringBuilder)garbage); - poolSize++; - } - garbageSize--; - } - if (poolSize == prefMaxSuggestions + 1) { - Log.w("Suggest", "String pool got too big: " + poolSize); - } - suggestions.clear(); - } - public void close() { final Set<Dictionary> dictionaries = new HashSet<Dictionary>(); dictionaries.addAll(mUnigramDictionaries.values()); @@ -544,4 +571,37 @@ public class Suggest implements Dictionary.WordCallback { } mMainDict = null; } + + // TODO: Resolve the inconsistencies between the native auto correction algorithms and + // this safety net + public static boolean shouldBlockAutoCorrectionBySafetyNet(final String typedWord, + final CharSequence suggestion) { + // Safety net for auto correction. + // Actually if we hit this safety net, it's a bug. + // If user selected aggressive auto correction mode, there is no need to use the safety + // net. + // If the length of typed word is less than MINIMUM_SAFETY_NET_CHAR_LENGTH, + // we should not use net because relatively edit distance can be big. + final int typedWordLength = typedWord.length(); + if (typedWordLength < Suggest.MINIMUM_SAFETY_NET_CHAR_LENGTH) { + return false; + } + final int maxEditDistanceOfNativeDictionary = + (typedWordLength < 5 ? 2 : typedWordLength / 2) + 1; + final int distance = BinaryDictionary.editDistance(typedWord, suggestion.toString()); + if (DBG) { + Log.d(TAG, "Autocorrected edit distance = " + distance + + ", " + maxEditDistanceOfNativeDictionary); + } + if (distance > maxEditDistanceOfNativeDictionary) { + if (DBG) { + Log.e(TAG, "Safety net: before = " + typedWord + ", after = " + suggestion); + Log.e(TAG, "(Error) The edit distance of this correction exceeds limit. " + + "Turning off auto-correction."); + } + return true; + } else { + return false; + } + } } |