diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin/Suggest.java')
-rw-r--r-- | java/src/com/android/inputmethod/latin/Suggest.java | 177 |
1 files changed, 78 insertions, 99 deletions
diff --git a/java/src/com/android/inputmethod/latin/Suggest.java b/java/src/com/android/inputmethod/latin/Suggest.java index 61a8e2831..a4332936d 100644 --- a/java/src/com/android/inputmethod/latin/Suggest.java +++ b/java/src/com/android/inputmethod/latin/Suggest.java @@ -26,6 +26,7 @@ import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import java.io.File; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.Locale; import java.util.concurrent.ConcurrentHashMap; @@ -34,7 +35,7 @@ import java.util.concurrent.ConcurrentHashMap; * This class loads a dictionary and provides a list of suggestions for a given sequence of * characters. This includes corrections and completions. */ -public class Suggest implements Dictionary.WordCallback { +public class Suggest { public static final String TAG = Suggest.class.getSimpleName(); public static final int APPROX_MAX_WORD_LENGTH = 32; @@ -58,53 +59,61 @@ public class Suggest implements Dictionary.WordCallback { public static final String DICT_KEY_CONTACTS = "contacts"; // User dictionary, the system-managed one. public static final String DICT_KEY_USER = "user"; - // User history dictionary for the unigram map, internal to LatinIME - public static final String DICT_KEY_USER_HISTORY_UNIGRAM = "history_unigram"; - // User history dictionary for the bigram map, internal to LatinIME - public static final String DICT_KEY_USER_HISTORY_BIGRAM = "history_bigram"; + // User history dictionary internal to LatinIME + public static final String DICT_KEY_USER_HISTORY = "history"; public static final String DICT_KEY_WHITELIST ="whitelist"; + // TODO: remove this map. This only serves as backward compatibility with a feature + // that has never been used and has been broken for a while. + private static final HashMap<String, Integer> sDictKeyToDictIndex + = new HashMap<String, Integer>(); + static { + sDictKeyToDictIndex.put(DICT_KEY_MAIN, DIC_MAIN); + sDictKeyToDictIndex.put(DICT_KEY_USER, DIC_USER); + sDictKeyToDictIndex.put(DICT_KEY_USER_HISTORY, DIC_USER_HISTORY); + sDictKeyToDictIndex.put(DICT_KEY_CONTACTS, DIC_CONTACTS); + sDictKeyToDictIndex.put(DICT_KEY_WHITELIST, DIC_WHITELIST); + } private static final boolean DBG = LatinImeLogger.sDBG; private Dictionary mMainDictionary; private ContactsBinaryDictionary mContactsDict; private WhitelistDictionary mWhiteListDictionary; - private final ConcurrentHashMap<String, Dictionary> mUnigramDictionaries = - new ConcurrentHashMap<String, Dictionary>(); - private final ConcurrentHashMap<String, Dictionary> mBigramDictionaries = + private final ConcurrentHashMap<String, Dictionary> mDictionaries = new ConcurrentHashMap<String, Dictionary>(); public static final int MAX_SUGGESTIONS = 18; private float mAutoCorrectionThreshold; - private ArrayList<SuggestedWordInfo> mSuggestions = new ArrayList<SuggestedWordInfo>(); - 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; + // Locale used for upper- and title-casing words + final private Locale mLocale; + private static final int MINIMUM_SAFETY_NET_CHAR_LENGTH = 4; public Suggest(final Context context, final Locale locale) { initAsynchronously(context, locale); + mLocale = locale; } /* package for test */ Suggest(final Context context, final File dictionary, final long startOffset, final long length, final Locale locale) { final Dictionary mainDict = DictionaryFactory.createDictionaryForTest(context, dictionary, startOffset, length /* useFullEditDistance */, false, locale); + mLocale = locale; mMainDictionary = mainDict; - addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_MAIN, mainDict); - addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_MAIN, mainDict); + addOrReplaceDictionary(mDictionaries, DICT_KEY_MAIN, mainDict); initWhitelistAndAutocorrectAndPool(context, locale); } private void initWhitelistAndAutocorrectAndPool(final Context context, final Locale locale) { mWhiteListDictionary = new WhitelistDictionary(context, locale); - addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_WHITELIST, mWhiteListDictionary); + addOrReplaceDictionary(mDictionaries, DICT_KEY_WHITELIST, mWhiteListDictionary); } private void initAsynchronously(final Context context, final Locale locale) { @@ -133,8 +142,7 @@ public class Suggest implements Dictionary.WordCallback { public void run() { final DictionaryCollection newMainDict = DictionaryFactory.createMainDictionaryFromManager(context, locale); - addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_MAIN, newMainDict); - addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_MAIN, newMainDict); + addOrReplaceDictionary(mDictionaries, DICT_KEY_MAIN, newMainDict); mMainDictionary = newMainDict; } }.start(); @@ -155,7 +163,7 @@ public class Suggest implements Dictionary.WordCallback { } public ConcurrentHashMap<String, Dictionary> getUnigramDictionaries() { - return mUnigramDictionaries; + return mDictionaries; } public static int getApproxMaxWordLength() { @@ -167,7 +175,7 @@ public class Suggest implements Dictionary.WordCallback { * before the main dictionary, if set. This refers to the system-managed user dictionary. */ public void setUserDictionary(UserBinaryDictionary userDictionary) { - addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_USER, userDictionary); + addOrReplaceDictionary(mDictionaries, DICT_KEY_USER, userDictionary); } /** @@ -177,15 +185,11 @@ public class Suggest implements Dictionary.WordCallback { */ public void setContactsDictionary(ContactsBinaryDictionary contactsDictionary) { mContactsDict = contactsDictionary; - addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_CONTACTS, contactsDictionary); - addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_CONTACTS, contactsDictionary); + addOrReplaceDictionary(mDictionaries, DICT_KEY_CONTACTS, contactsDictionary); } public void setUserHistoryDictionary(UserHistoryDictionary userHistoryDictionary) { - addOrReplaceDictionary(mUnigramDictionaries, DICT_KEY_USER_HISTORY_UNIGRAM, - userHistoryDictionary); - addOrReplaceDictionary(mBigramDictionaries, DICT_KEY_USER_HISTORY_BIGRAM, - userHistoryDictionary); + addOrReplaceDictionary(mDictionaries, DICT_KEY_USER_HISTORY, userHistoryDictionary); } public void setAutoCorrectionThreshold(float threshold) { @@ -209,39 +213,6 @@ public class Suggest implements Dictionary.WordCallback { return 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<SuggestedWordInfo>(MAX_SUGGESTIONS); - - // Treating USER_TYPED as UNIGRAM suggestion for logging now. - LatinImeLogger.onAddSuggestedWord("", Suggest.DIC_USER_TYPED, Dictionary.UNIGRAM); - mConsideredWord = ""; - - getAllBigrams(prevWordForBigram, sEmptyWordComposer); - - SuggestedWordInfo.removeDups(mSuggestions); - - return new SuggestedWords(mSuggestions, - false /* typedWordValid */, - false /* hasAutoCorrectionCandidate */, - false /* allowsToBeAutoCorrected */, - false /* isPunctuationSuggestions */, - false /* isObsoleteSuggestions */, - true /* isPrediction */); - } - - // Compatibility for tests. TODO: remove this - public SuggestedWords getSuggestedWords( - final WordComposer wordComposer, CharSequence prevWordForBigram, - final ProximityInfo proximityInfo, final boolean isCorrectionEnabled) { - return getSuggestedWords(wordComposer, prevWordForBigram, proximityInfo, - isCorrectionEnabled, false); - } - // TODO: cleanup dictionaries looking up and suggestions building with SuggestedWords.Builder public SuggestedWords getSuggestedWords( final WordComposer wordComposer, CharSequence prevWordForBigram, @@ -251,7 +222,8 @@ public class Suggest implements Dictionary.WordCallback { mIsFirstCharCapitalized = !isPrediction && wordComposer.isFirstCharCapitalized(); mIsAllUpperCase = !isPrediction && wordComposer.isAllUpperCase(); mTrailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount(); - mSuggestions = new ArrayList<SuggestedWordInfo>(MAX_SUGGESTIONS); + final ArrayList<SuggestedWordInfo> suggestions = + new ArrayList<SuggestedWordInfo>(MAX_SUGGESTIONS); final String typedWord = wordComposer.getTypedWord(); final String consideredWord = mTrailingSingleQuotesCount > 0 @@ -259,12 +231,32 @@ public class Suggest implements Dictionary.WordCallback { : 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 && isCorrectionEnabled) { // At first character typed, search only the bigrams if (!TextUtils.isEmpty(prevWordForBigram)) { - getAllBigrams(prevWordForBigram, wordComposer); + final CharSequence lowerPrevWord; + if (StringUtils.hasUpperCase(prevWordForBigram)) { + // TODO: Must pay attention to locale when changing case. + lowerPrevWord = prevWordForBigram.toString().toLowerCase(); + } else { + lowerPrevWord = null; + } + for (final String key : mDictionaries.keySet()) { + final int dicTypeId = sDictKeyToDictIndex.get(key); + final Dictionary dictionary = mDictionaries.get(key); + final ArrayList<SuggestedWordInfo> localSuggestions = + dictionary.getBigrams(wordComposer, prevWordForBigram); + if (null != lowerPrevWord) { + localSuggestions.addAll(dictionary.getBigrams(wordComposer, lowerPrevWord)); + } + for (final SuggestedWordInfo suggestion : localSuggestions) { + final String suggestionStr = suggestion.mWord.toString(); + addWord(suggestionStr.toCharArray(), null, 0, suggestionStr.length(), + suggestion.mScore, dicTypeId, Dictionary.BIGRAM, + suggestions, consideredWord); + } + } } } else if (wordComposer.size() > 1) { final WordComposer wordComposerForLookup; @@ -277,12 +269,20 @@ public class Suggest implements Dictionary.WordCallback { wordComposerForLookup = wordComposer; } // At second character typed, search the unigrams (scores being affected by bigrams) - for (final String key : mUnigramDictionaries.keySet()) { + for (final String key : mDictionaries.keySet()) { // Skip UserUnigramDictionary and WhitelistDictionary to lookup - if (key.equals(DICT_KEY_USER_HISTORY_UNIGRAM) || key.equals(DICT_KEY_WHITELIST)) + if (key.equals(DICT_KEY_USER_HISTORY) || key.equals(DICT_KEY_WHITELIST)) continue; - final Dictionary dictionary = mUnigramDictionaries.get(key); - dictionary.getWords(wordComposerForLookup, prevWordForBigram, this, proximityInfo); + final int dicTypeId = sDictKeyToDictIndex.get(key); + final Dictionary dictionary = mDictionaries.get(key); + final ArrayList<SuggestedWordInfo> localSuggestions = dictionary.getWords( + wordComposerForLookup, prevWordForBigram, proximityInfo); + for (final SuggestedWordInfo suggestion : localSuggestions) { + final String suggestionStr = suggestion.mWord.toString(); + addWord(suggestionStr.toCharArray(), null, 0, suggestionStr.length(), + suggestion.mScore, dicTypeId, Dictionary.UNIGRAM, + suggestions, consideredWord); + } } } @@ -292,8 +292,8 @@ public class Suggest implements Dictionary.WordCallback { final boolean hasAutoCorrection; if (isCorrectionEnabled) { final CharSequence autoCorrection = - AutoCorrection.computeAutoCorrectionWord(mUnigramDictionaries, wordComposer, - mSuggestions, consideredWord, mAutoCorrectionThreshold, + AutoCorrection.computeAutoCorrectionWord(mDictionaries, wordComposer, + suggestions, consideredWord, mAutoCorrectionThreshold, whitelistedWord); hasAutoCorrection = (null != autoCorrection); } else { @@ -306,25 +306,25 @@ public class Suggest implements Dictionary.WordCallback { for (int i = mTrailingSingleQuotesCount - 1; i >= 0; --i) { sb.appendCodePoint(Keyboard.CODE_SINGLE_QUOTE); } - mSuggestions.add(0, new SuggestedWordInfo(sb.toString(), + suggestions.add(0, new SuggestedWordInfo(sb.toString(), SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_WHITELIST)); } else { - mSuggestions.add(0, new SuggestedWordInfo(whitelistedWord, + suggestions.add(0, new SuggestedWordInfo(whitelistedWord, SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_WHITELIST)); } } if (!isPrediction) { - mSuggestions.add(0, new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE, + suggestions.add(0, new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_TYPED)); } - SuggestedWordInfo.removeDups(mSuggestions); + SuggestedWordInfo.removeDups(suggestions); final ArrayList<SuggestedWordInfo> suggestionsList; - if (DBG && !mSuggestions.isEmpty()) { - suggestionsList = getSuggestionsInfoListWithDebugInfo(typedWord, mSuggestions); + if (DBG && !suggestions.isEmpty()) { + suggestionsList = getSuggestionsInfoListWithDebugInfo(typedWord, suggestions); } else { - suggestionsList = mSuggestions; + suggestionsList = suggestions; } // TODO: Change this scheme - a boolean is not enough. A whitelisted word may be "valid" @@ -362,23 +362,6 @@ public class Suggest implements Dictionary.WordCallback { isPrediction); } - /** - * Adds all bigram predictions for prevWord. Also checks the lower case version of prevWord if - * it contains any upper case characters. - */ - private void getAllBigrams(final CharSequence prevWord, final WordComposer wordComposer) { - if (StringUtils.hasUpperCase(prevWord)) { - // TODO: Must pay attention to locale when changing case. - final CharSequence lowerPrevWord = prevWord.toString().toLowerCase(); - for (final Dictionary dictionary : mBigramDictionaries.values()) { - dictionary.getBigrams(wordComposer, lowerPrevWord, this); - } - } - for (final Dictionary dictionary : mBigramDictionaries.values()) { - dictionary.getBigrams(wordComposer, prevWord, this); - } - } - private static ArrayList<SuggestedWordInfo> getSuggestionsInfoListWithDebugInfo( final String typedWord, final ArrayList<SuggestedWordInfo> suggestions) { final SuggestedWordInfo typedWordInfo = suggestions.get(0); @@ -406,19 +389,16 @@ public class Suggest implements Dictionary.WordCallback { } // TODO: Use codepoint instead of char - @Override public boolean addWord(final char[] word, int[] indices, final int offset, final int length, - int score, final int dicTypeId, final int dataType) { + int score, final int dicTypeId, final int dataType, + final ArrayList<SuggestedWordInfo> suggestions, final String consideredWord) { int dataTypeForLog = dataType; - final ArrayList<SuggestedWordInfo> suggestions; - final int prefMaxSuggestions; - suggestions = mSuggestions; - prefMaxSuggestions = MAX_SUGGESTIONS; + final int prefMaxSuggestions = MAX_SUGGESTIONS; int pos = 0; // Check if it's the same word, only caps are different - if (StringUtils.equalsIgnoreCase(mConsideredWord, word, offset, length)) { + if (StringUtils.equalsIgnoreCase(consideredWord, word, offset, length)) { // TODO: remove this surrounding if clause and move this logic to // getSuggestedWordBuilder. if (suggestions.size() > 0) { @@ -476,8 +456,7 @@ public class Suggest implements Dictionary.WordCallback { public void close() { final HashSet<Dictionary> dictionaries = new HashSet<Dictionary>(); - dictionaries.addAll(mUnigramDictionaries.values()); - dictionaries.addAll(mBigramDictionaries.values()); + dictionaries.addAll(mDictionaries.values()); for (final Dictionary dictionary : dictionaries) { dictionary.close(); } |