From 59b501a05078e5a9de7cdace19c51ca693076a17 Mon Sep 17 00:00:00 2001 From: Jean Chalard Date: Fri, 2 Sep 2011 14:36:04 +0900 Subject: [PATCH] Only set LOOKS_LIKE_TYPO if the max score meets a threshold Bug: 5240463 Change-Id: I51e85edae57789d638aa1e12b82e6a75c49d33c7 --- java/res/values/config.xml | 2 + .../AndroidSpellCheckerService.java | 107 ++++++++++++++---- 2 files changed, 86 insertions(+), 23 deletions(-) diff --git a/java/res/values/config.xml b/java/res/values/config.xml index 1ffbbacbe..d9ff8d7b9 100644 --- a/java/res/values/config.xml +++ b/java/res/values/config.xml @@ -80,6 +80,8 @@ will be subject to auto-correction. --> 0 + + 0.11 0 false diff --git a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java index a6a5b6dd6..85c142f1e 100644 --- a/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java +++ b/java/src/com/android/inputmethod/latin/spellcheck/AndroidSpellCheckerService.java @@ -34,6 +34,7 @@ import com.android.inputmethod.latin.Dictionary.WordCallback; import com.android.inputmethod.latin.DictionaryCollection; import com.android.inputmethod.latin.DictionaryFactory; import com.android.inputmethod.latin.LocaleUtils; +import com.android.inputmethod.latin.R; import com.android.inputmethod.latin.SynchronouslyLoadedUserDictionary; import com.android.inputmethod.latin.UserDictionary; import com.android.inputmethod.latin.Utils; @@ -62,18 +63,38 @@ public class AndroidSpellCheckerService extends SpellCheckerService { private Map mUserDictionaries = Collections.synchronizedMap(new TreeMap()); + private double mTypoThreshold; + + @Override public void onCreate() { + super.onCreate(); + mTypoThreshold = Double.parseDouble(getString(R.string.spellchecker_typo_threshold_value)); + } + @Override public Session createSession() { - return new AndroidSpellCheckerSession(); + return new AndroidSpellCheckerSession(this); } private static class SuggestionsGatherer implements WordCallback { + public static class Result { + public final String[] mSuggestions; + public final boolean mLooksLikeTypo; + public Result(final String[] gatheredSuggestions, final boolean looksLikeTypo) { + mSuggestions = gatheredSuggestions; + mLooksLikeTypo = looksLikeTypo; + } + } + private final int DEFAULT_SUGGESTION_LENGTH = 16; private final ArrayList mSuggestions; private final int[] mScores; private final int mMaxLength; private int mLength = 0; - private boolean mSeenSuggestions = false; + + // The two following attributes are only ever filled if the requested max length + // is 0 (or less, which is treated the same). + private String mBestSuggestion = null; + private int mBestScore = Integer.MIN_VALUE; // As small as possible SuggestionsGatherer(final int maxLength) { mMaxLength = maxLength; @@ -89,14 +110,26 @@ public class AndroidSpellCheckerService extends SpellCheckerService { // if it doesn't. See documentation for binarySearch. final int insertIndex = positionIndex >= 0 ? positionIndex : -positionIndex - 1; - mSeenSuggestions = true; if (mLength < mMaxLength) { final int copyLen = mLength - insertIndex; ++mLength; System.arraycopy(mScores, insertIndex, mScores, insertIndex + 1, copyLen); mSuggestions.add(insertIndex, new String(word, wordOffset, wordLength)); } else { - if (insertIndex == 0) return true; + if (insertIndex == 0) { + // If the maxLength is 0 (should never be less, but if it is, it's treated as 0) + // then we need to keep track of the best suggestion in mBestScore and + // mBestSuggestion. This is so that we know whether the best suggestion makes + // the score cutoff, since we need to know that to return a meaningful + // looksLikeTypo. + if (0 >= mMaxLength) { + if (score > mBestScore) { + mBestScore = score; + mBestSuggestion = new String(word, wordOffset, wordLength); + } + } + return true; + } System.arraycopy(mScores, 1, mScores, 0, insertIndex); mSuggestions.add(insertIndex, new String(word, wordOffset, wordLength)); mSuggestions.remove(0); @@ -106,20 +139,41 @@ public class AndroidSpellCheckerService extends SpellCheckerService { return true; } - public String[] getGatheredSuggestions() { - if (!mSeenSuggestions) return null; - if (0 == mLength) return EMPTY_STRING_ARRAY; - - if (DBG) { - if (mLength != mSuggestions.size()) { - Log.e(TAG, "Suggestion size is not the same as stored mLength"); + public Result getResults(final CharSequence originalText, final double threshold) { + final String[] gatheredSuggestions; + final boolean looksLikeTypo; + if (0 == mLength) { + // Either we found no suggestions, or we found some BUT the max length was 0. + // If we found some mBestSuggestion will not be null. If it is null, then + // we found none, regardless of the max length. + if (null == mBestSuggestion) { + gatheredSuggestions = null; + looksLikeTypo = false; + } else { + gatheredSuggestions = EMPTY_STRING_ARRAY; + final double normalizedScore = + Utils.calcNormalizedScore(originalText, mBestSuggestion, mBestScore); + looksLikeTypo = (normalizedScore > threshold); } + } else { + if (DBG) { + if (mLength != mSuggestions.size()) { + Log.e(TAG, "Suggestion size is not the same as stored mLength"); + } + } + Collections.reverse(mSuggestions); + Utils.removeDupes(mSuggestions); + // This returns a String[], while toArray() returns an Object[] which cannot be cast + // into a String[]. + gatheredSuggestions = mSuggestions.toArray(EMPTY_STRING_ARRAY); + + final int bestScore = mScores[0]; + final CharSequence bestSuggestion = mSuggestions.get(0); + final double normalizedScore = + Utils.calcNormalizedScore(originalText, bestSuggestion, bestScore); + looksLikeTypo = (normalizedScore > threshold); } - Collections.reverse(mSuggestions); - Utils.removeDupes(mSuggestions); - // This returns a String[], while toArray() returns an Object[] which cannot be cast - // into a String[]. - return mSuggestions.toArray(EMPTY_STRING_ARRAY); + return new Result(gatheredSuggestions, looksLikeTypo); } } @@ -164,16 +218,22 @@ public class AndroidSpellCheckerService extends SpellCheckerService { return new DictAndProximity(dictionaryCollection, proximityInfo); } - private class AndroidSpellCheckerSession extends Session { + private static class AndroidSpellCheckerSession extends Session { // Immutable, but need the locale which is not available in the constructor yet - DictionaryPool mDictionaryPool; + private DictionaryPool mDictionaryPool; // Likewise - Locale mLocale; + private Locale mLocale; + + private final AndroidSpellCheckerService mService; + + AndroidSpellCheckerSession(final AndroidSpellCheckerService service) { + mService = service; + } @Override public void onCreate() { final String localeString = getLocale(); - mDictionaryPool = getDictionaryPool(localeString); + mDictionaryPool = mService.getDictionaryPool(localeString); mLocale = LocaleUtils.constructLocaleFromString(localeString); } @@ -242,13 +302,14 @@ public class AndroidSpellCheckerService extends SpellCheckerService { return EMPTY_SUGGESTIONS_INFO; } - final String[] suggestions = suggestionsGatherer.getGatheredSuggestions(); + final SuggestionsGatherer.Result result = + suggestionsGatherer.getResults(text, mService.mTypoThreshold); final int flags = (isInDict ? SuggestionsInfo.RESULT_ATTR_IN_THE_DICTIONARY : 0) - | (null != suggestions + | (result.mLooksLikeTypo ? SuggestionsInfo.RESULT_ATTR_LOOKS_LIKE_TYPO : 0); - return new SuggestionsInfo(flags, suggestions); + return new SuggestionsInfo(flags, result.mSuggestions); } } }