Combine candidate words and scores

Bug: 5240798
Change-Id: Ie56c1c2cfd7f365e771fee88c1ed15012448feed
This commit is contained in:
satok 2012-04-02 16:33:24 +09:00
parent 01127fad51
commit 7e518d8b83
5 changed files with 119 additions and 94 deletions

View File

@ -16,6 +16,8 @@
package com.android.inputmethod.latin; package com.android.inputmethod.latin;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
@ -30,8 +32,9 @@ public class AutoCorrection {
// Purely static class: can't instantiate. // Purely static class: can't instantiate.
} }
public static CharSequence computeAutoCorrectionWord(HashMap<String, Dictionary> dictionaries, public static CharSequence computeAutoCorrectionWord(
WordComposer wordComposer, ArrayList<CharSequence> suggestions, int[] sortedScores, HashMap<String, Dictionary> dictionaries,
WordComposer wordComposer, ArrayList<SuggestedWordInfo> suggestions,
CharSequence consideredWord, double autoCorrectionThreshold, CharSequence consideredWord, double autoCorrectionThreshold,
CharSequence whitelistedWord) { CharSequence whitelistedWord) {
if (hasAutoCorrectionForWhitelistedWord(whitelistedWord)) { if (hasAutoCorrectionForWhitelistedWord(whitelistedWord)) {
@ -40,8 +43,8 @@ public class AutoCorrection {
dictionaries, wordComposer, suggestions, consideredWord)) { dictionaries, wordComposer, suggestions, consideredWord)) {
return consideredWord; return consideredWord;
} else if (hasAutoCorrectionForBinaryDictionary(wordComposer, suggestions, } else if (hasAutoCorrectionForBinaryDictionary(wordComposer, suggestions,
sortedScores, consideredWord, autoCorrectionThreshold)) { consideredWord, autoCorrectionThreshold)) {
return suggestions.get(0); return suggestions.get(0).mWord;
} }
return null; return null;
} }
@ -89,22 +92,23 @@ public class AutoCorrection {
private static boolean hasAutoCorrectionForConsideredWord( private static boolean hasAutoCorrectionForConsideredWord(
HashMap<String, Dictionary> dictionaries, WordComposer wordComposer, HashMap<String, Dictionary> dictionaries, WordComposer wordComposer,
ArrayList<CharSequence> suggestions, CharSequence consideredWord) { ArrayList<SuggestedWordInfo> suggestions, CharSequence consideredWord) {
if (TextUtils.isEmpty(consideredWord)) return false; if (TextUtils.isEmpty(consideredWord)) return false;
return wordComposer.size() > 1 && suggestions.size() > 0 return wordComposer.size() > 1 && suggestions.size() > 0
&& !allowsToBeAutoCorrected(dictionaries, consideredWord, false); && !allowsToBeAutoCorrected(dictionaries, consideredWord, false);
} }
private static boolean hasAutoCorrectionForBinaryDictionary(WordComposer wordComposer, private static boolean hasAutoCorrectionForBinaryDictionary(WordComposer wordComposer,
ArrayList<CharSequence> suggestions, int[] sortedScores, ArrayList<SuggestedWordInfo> suggestions,
CharSequence consideredWord, double autoCorrectionThreshold) { CharSequence consideredWord, double autoCorrectionThreshold) {
if (wordComposer.size() > 1 && suggestions.size() > 0 && sortedScores.length > 0) { if (wordComposer.size() > 1 && suggestions.size() > 0) {
final CharSequence autoCorrectionSuggestion = suggestions.get(0); final SuggestedWordInfo autoCorrectionSuggestion = suggestions.get(0);
final int autoCorrectionSuggestionScore = sortedScores[0]; //final int autoCorrectionSuggestionScore = sortedScores[0];
final int autoCorrectionSuggestionScore = autoCorrectionSuggestion.mScore;
// TODO: when the normalized score of the first suggestion is nearly equals to // TODO: when the normalized score of the first suggestion is nearly equals to
// the normalized score of the second suggestion, behave less aggressive. // the normalized score of the second suggestion, behave less aggressive.
final double normalizedScore = BinaryDictionary.calcNormalizedScore( final double normalizedScore = BinaryDictionary.calcNormalizedScore(
consideredWord.toString(), autoCorrectionSuggestion.toString(), consideredWord.toString(), autoCorrectionSuggestion.mWord.toString(),
autoCorrectionSuggestionScore); autoCorrectionSuggestionScore);
if (DBG) { if (DBG) {
Log.d(TAG, "Normalized " + consideredWord + "," + autoCorrectionSuggestion + "," Log.d(TAG, "Normalized " + consideredWord + "," + autoCorrectionSuggestion + ","

View File

@ -48,7 +48,6 @@ import android.view.ViewParent;
import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo; import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.EditorInfo; import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputConnection;
import com.android.inputmethod.accessibility.AccessibilityUtils; import com.android.inputmethod.accessibility.AccessibilityUtils;
@ -58,7 +57,6 @@ import com.android.inputmethod.compat.EditorInfoCompatUtils;
import com.android.inputmethod.compat.InputMethodManagerCompatWrapper; import com.android.inputmethod.compat.InputMethodManagerCompatWrapper;
import com.android.inputmethod.compat.InputMethodServiceCompatWrapper; import com.android.inputmethod.compat.InputMethodServiceCompatWrapper;
import com.android.inputmethod.compat.InputMethodSubtypeCompatWrapper; import com.android.inputmethod.compat.InputMethodSubtypeCompatWrapper;
import com.android.inputmethod.compat.InputTypeCompatUtils;
import com.android.inputmethod.compat.SuggestionSpanUtils; import com.android.inputmethod.compat.SuggestionSpanUtils;
import com.android.inputmethod.keyboard.Keyboard; import com.android.inputmethod.keyboard.Keyboard;
import com.android.inputmethod.keyboard.KeyboardActionListener; import com.android.inputmethod.keyboard.KeyboardActionListener;

View File

@ -24,6 +24,7 @@ import android.view.inputmethod.EditorInfo;
import com.android.inputmethod.compat.InputTypeCompatUtils; import com.android.inputmethod.compat.InputTypeCompatUtils;
import com.android.inputmethod.keyboard.internal.KeySpecParser; import com.android.inputmethod.keyboard.internal.KeySpecParser;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.VibratorUtils; import com.android.inputmethod.latin.VibratorUtils;
import java.util.ArrayList; import java.util.ArrayList;
@ -162,7 +163,7 @@ public class SettingsValues {
if (puncs != null) { if (puncs != null) {
for (final String puncSpec : puncs) { for (final String puncSpec : puncs) {
puncList.add(new SuggestedWords.SuggestedWordInfo( puncList.add(new SuggestedWords.SuggestedWordInfo(
KeySpecParser.getLabel(puncSpec))); KeySpecParser.getLabel(puncSpec), SuggestedWordInfo.MAX_SCORE));
} }
} }
return new SuggestedWords(puncList, return new SuggestedWords(puncList,

View File

@ -26,7 +26,6 @@ import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Locale; import java.util.Locale;
@ -93,11 +92,9 @@ public class Suggest implements Dictionary.WordCallback {
private static final int PREF_MAX_BIGRAMS = 60; private static final int PREF_MAX_BIGRAMS = 60;
private double mAutoCorrectionThreshold; private double mAutoCorrectionThreshold;
private int[] mScores = new int[mPrefMaxSuggestions];
private int[] mBigramScores = new int[PREF_MAX_BIGRAMS];
private ArrayList<CharSequence> mSuggestions = new ArrayList<CharSequence>(); private ArrayList<SuggestedWordInfo> mSuggestions = new ArrayList<SuggestedWordInfo>();
private ArrayList<CharSequence> mBigramSuggestions = new ArrayList<CharSequence>(); private ArrayList<SuggestedWordInfo> mBigramSuggestions = new ArrayList<SuggestedWordInfo>();
private CharSequence mConsideredWord; private CharSequence mConsideredWord;
// TODO: Remove these member variables by passing more context to addWord() callback method // TODO: Remove these member variables by passing more context to addWord() callback method
@ -230,10 +227,8 @@ public class Suggest implements Dictionary.WordCallback {
return sb; return sb;
} }
protected void addBigramToSuggestions(CharSequence bigram) { protected void addBigramToSuggestions(SuggestedWordInfo bigram) {
final StringBuilder sb = new StringBuilder(getApproxMaxWordLength()); mSuggestions.add(bigram);
sb.append(bigram);
mSuggestions.add(sb);
} }
private static final WordComposer sEmptyWordComposer = new WordComposer(); private static final WordComposer sEmptyWordComposer = new WordComposer();
@ -242,15 +237,13 @@ public class Suggest implements Dictionary.WordCallback {
mIsFirstCharCapitalized = false; mIsFirstCharCapitalized = false;
mIsAllUpperCase = false; mIsAllUpperCase = false;
mTrailingSingleQuotesCount = 0; mTrailingSingleQuotesCount = 0;
mSuggestions = new ArrayList<CharSequence>(mPrefMaxSuggestions); mSuggestions = new ArrayList<SuggestedWordInfo>(mPrefMaxSuggestions);
Arrays.fill(mScores, 0);
// Treating USER_TYPED as UNIGRAM suggestion for logging now. // Treating USER_TYPED as UNIGRAM suggestion for logging now.
LatinImeLogger.onAddSuggestedWord("", Suggest.DIC_USER_TYPED, Dictionary.UNIGRAM); LatinImeLogger.onAddSuggestedWord("", Suggest.DIC_USER_TYPED, Dictionary.UNIGRAM);
mConsideredWord = ""; mConsideredWord = "";
Arrays.fill(mBigramScores, 0); mBigramSuggestions = new ArrayList<SuggestedWordInfo>(PREF_MAX_BIGRAMS);
mBigramSuggestions = new ArrayList<CharSequence>(PREF_MAX_BIGRAMS);
CharSequence lowerPrevWord = prevWordForBigram.toString().toLowerCase(); CharSequence lowerPrevWord = prevWordForBigram.toString().toLowerCase();
if (mMainDict != null && mMainDict.isValidWord(lowerPrevWord)) { if (mMainDict != null && mMainDict.isValidWord(lowerPrevWord)) {
@ -265,9 +258,9 @@ public class Suggest implements Dictionary.WordCallback {
addBigramToSuggestions(mBigramSuggestions.get(i)); addBigramToSuggestions(mBigramSuggestions.get(i));
} }
StringUtils.removeDupes(mSuggestions); SuggestedWordInfo.removeDups(mSuggestions);
return new SuggestedWords(SuggestedWords.getFromCharSequenceList(mSuggestions), return new SuggestedWords(mSuggestions,
false /* typedWordValid */, false /* typedWordValid */,
false /* hasAutoCorrectionCandidate */, false /* hasAutoCorrectionCandidate */,
false /* allowsToBeAutoCorrected */, false /* allowsToBeAutoCorrected */,
@ -283,8 +276,7 @@ public class Suggest implements Dictionary.WordCallback {
mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized(); mIsFirstCharCapitalized = wordComposer.isFirstCharCapitalized();
mIsAllUpperCase = wordComposer.isAllUpperCase(); mIsAllUpperCase = wordComposer.isAllUpperCase();
mTrailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount(); mTrailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount();
mSuggestions = new ArrayList<CharSequence>(mPrefMaxSuggestions); mSuggestions = new ArrayList<SuggestedWordInfo>(mPrefMaxSuggestions);
Arrays.fill(mScores, 0);
final String typedWord = wordComposer.getTypedWord(); final String typedWord = wordComposer.getTypedWord();
final String consideredWord = mTrailingSingleQuotesCount > 0 final String consideredWord = mTrailingSingleQuotesCount > 0
@ -296,8 +288,7 @@ public class Suggest implements Dictionary.WordCallback {
if (wordComposer.size() <= 1 && (correctionMode == CORRECTION_FULL_BIGRAM)) { if (wordComposer.size() <= 1 && (correctionMode == CORRECTION_FULL_BIGRAM)) {
// At first character typed, search only the bigrams // At first character typed, search only the bigrams
Arrays.fill(mBigramScores, 0); mBigramSuggestions = new ArrayList<SuggestedWordInfo>(PREF_MAX_BIGRAMS);
mBigramSuggestions = new ArrayList<CharSequence>(PREF_MAX_BIGRAMS);
if (!TextUtils.isEmpty(prevWordForBigram)) { if (!TextUtils.isEmpty(prevWordForBigram)) {
CharSequence lowerPrevWord = prevWordForBigram.toString().toLowerCase(); CharSequence lowerPrevWord = prevWordForBigram.toString().toLowerCase();
@ -317,12 +308,14 @@ public class Suggest implements Dictionary.WordCallback {
// Word entered: return only bigrams that match the first char of the typed word // Word entered: return only bigrams that match the first char of the typed word
final char currentChar = consideredWord.charAt(0); final char currentChar = consideredWord.charAt(0);
// TODO: Must pay attention to locale when changing case. // TODO: Must pay attention to locale when changing case.
// TODO: Use codepoint instead of char
final char currentCharUpper = Character.toUpperCase(currentChar); final char currentCharUpper = Character.toUpperCase(currentChar);
int count = 0; int count = 0;
final int bigramSuggestionSize = mBigramSuggestions.size(); final int bigramSuggestionSize = mBigramSuggestions.size();
for (int i = 0; i < bigramSuggestionSize; i++) { for (int i = 0; i < bigramSuggestionSize; i++) {
final CharSequence bigramSuggestion = mBigramSuggestions.get(i); final SuggestedWordInfo bigramSuggestion = mBigramSuggestions.get(i);
final char bigramSuggestionFirstChar = bigramSuggestion.charAt(0); final char bigramSuggestionFirstChar =
(char)bigramSuggestion.codePointAt(0);
if (bigramSuggestionFirstChar == currentChar if (bigramSuggestionFirstChar == currentChar
|| bigramSuggestionFirstChar == currentCharUpper) { || bigramSuggestionFirstChar == currentCharUpper) {
addBigramToSuggestions(bigramSuggestion); addBigramToSuggestions(bigramSuggestion);
@ -359,7 +352,7 @@ public class Suggest implements Dictionary.WordCallback {
if (CORRECTION_FULL == correctionMode || CORRECTION_FULL_BIGRAM == correctionMode) { if (CORRECTION_FULL == correctionMode || CORRECTION_FULL_BIGRAM == correctionMode) {
final CharSequence autoCorrection = final CharSequence autoCorrection =
AutoCorrection.computeAutoCorrectionWord(mUnigramDictionaries, wordComposer, AutoCorrection.computeAutoCorrectionWord(mUnigramDictionaries, wordComposer,
mSuggestions, mScores, consideredWord, mAutoCorrectionThreshold, mSuggestions, consideredWord, mAutoCorrectionThreshold,
whitelistedWord); whitelistedWord);
hasAutoCorrection = (null != autoCorrection); hasAutoCorrection = (null != autoCorrection);
} else { } else {
@ -372,20 +365,22 @@ public class Suggest implements Dictionary.WordCallback {
for (int i = mTrailingSingleQuotesCount - 1; i >= 0; --i) { for (int i = mTrailingSingleQuotesCount - 1; i >= 0; --i) {
sb.appendCodePoint(Keyboard.CODE_SINGLE_QUOTE); sb.appendCodePoint(Keyboard.CODE_SINGLE_QUOTE);
} }
mSuggestions.add(0, sb.toString()); mSuggestions.add(0, new SuggestedWordInfo(
sb.toString(), SuggestedWordInfo.MAX_SCORE));
} else { } else {
mSuggestions.add(0, whitelistedWord); mSuggestions.add(0, new SuggestedWordInfo(
whitelistedWord, SuggestedWordInfo.MAX_SCORE));
} }
} }
mSuggestions.add(0, typedWord); mSuggestions.add(0, new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE));
StringUtils.removeDupes(mSuggestions); SuggestedWordInfo.removeDups(mSuggestions);
final ArrayList<SuggestedWordInfo> suggestionsList; final ArrayList<SuggestedWordInfo> suggestionsList;
if (DBG) { if (DBG) {
suggestionsList = getSuggestionsInfoListWithDebugInfo(typedWord, mSuggestions, mScores); suggestionsList = getSuggestionsInfoListWithDebugInfo(typedWord, mSuggestions);
} else { } else {
suggestionsList = SuggestedWords.getFromCharSequenceList(mSuggestions); suggestionsList = mSuggestions;
} }
// TODO: Change this scheme - a boolean is not enough. A whitelisted word may be "valid" // TODO: Change this scheme - a boolean is not enough. A whitelisted word may be "valid"
@ -415,50 +410,45 @@ public class Suggest implements Dictionary.WordCallback {
false /* isObsoleteSuggestions */); false /* isObsoleteSuggestions */);
} }
// This assumes the scores[] array is at least as long as suggestions.size() - 1.
private static ArrayList<SuggestedWordInfo> getSuggestionsInfoListWithDebugInfo( private static ArrayList<SuggestedWordInfo> getSuggestionsInfoListWithDebugInfo(
final String typedWord, final ArrayList<CharSequence> suggestions, final int[] scores) { final String typedWord, final ArrayList<SuggestedWordInfo> suggestions) {
// TODO: this doesn't take into account the fact that removing dupes from mSuggestions final SuggestedWordInfo typedWordInfo = suggestions.get(0);
// may have made mScores[] and mSuggestions out of sync. typedWordInfo.setDebugString("+");
final CharSequence autoCorrectionSuggestion = suggestions.get(0);
double normalizedScore = BinaryDictionary.calcNormalizedScore( double normalizedScore = BinaryDictionary.calcNormalizedScore(
typedWord, autoCorrectionSuggestion.toString(), scores[0]); typedWord, typedWordInfo.toString(), typedWordInfo.mScore);
final int suggestionsSize = suggestions.size(); final int suggestionsSize = suggestions.size();
final ArrayList<SuggestedWordInfo> suggestionsList = final ArrayList<SuggestedWordInfo> suggestionsList =
new ArrayList<SuggestedWordInfo>(suggestionsSize); new ArrayList<SuggestedWordInfo>(suggestionsSize);
suggestionsList.add(new SuggestedWordInfo(autoCorrectionSuggestion, "+")); suggestionsList.add(typedWordInfo);
// Note: i here is the index in mScores[], but the index in mSuggestions is one more // 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. // than i because we added the typed word to mSuggestions without touching mScores.
for (int i = 0; i < scores.length && i < suggestionsSize - 1; ++i) { for (int i = 0; i < suggestionsSize - 1; ++i) {
final SuggestedWordInfo cur = suggestions.get(i + 1);
final String scoreInfoString; final String scoreInfoString;
if (normalizedScore > 0) { if (normalizedScore > 0) {
scoreInfoString = String.format("%d (%4.2f)", scores[i], normalizedScore); scoreInfoString = String.format("%d (%4.2f)", cur.mScore, normalizedScore);
normalizedScore = 0.0; normalizedScore = 0.0;
} else { } else {
scoreInfoString = Integer.toString(scores[i]); scoreInfoString = Integer.toString(cur.mScore);
} }
suggestionsList.add(new SuggestedWordInfo(suggestions.get(i + 1), scoreInfoString)); cur.setDebugString(scoreInfoString);
} suggestionsList.add(cur);
for (int i = scores.length; i < suggestionsSize; ++i) {
suggestionsList.add(new SuggestedWordInfo(suggestions.get(i), "--"));
} }
return suggestionsList; return suggestionsList;
} }
// TODO: Use codepoint instead of char
@Override @Override
public boolean addWord(final char[] word, final int offset, final int length, int score, public boolean addWord(final char[] word, final int offset, final int length, int score,
final int dicTypeId, final int dataType) { final int dicTypeId, final int dataType) {
int dataTypeForLog = dataType; int dataTypeForLog = dataType;
final ArrayList<CharSequence> suggestions; final ArrayList<SuggestedWordInfo> suggestions;
final int[] sortedScores;
final int prefMaxSuggestions; final int prefMaxSuggestions;
if (dataType == Dictionary.BIGRAM) { if (dataType == Dictionary.BIGRAM) {
suggestions = mBigramSuggestions; suggestions = mBigramSuggestions;
sortedScores = mBigramScores;
prefMaxSuggestions = PREF_MAX_BIGRAMS; prefMaxSuggestions = PREF_MAX_BIGRAMS;
} else { } else {
suggestions = mSuggestions; suggestions = mSuggestions;
sortedScores = mScores;
prefMaxSuggestions = mPrefMaxSuggestions; prefMaxSuggestions = mPrefMaxSuggestions;
} }
@ -469,13 +459,13 @@ public class Suggest implements Dictionary.WordCallback {
// TODO: remove this surrounding if clause and move this logic to // TODO: remove this surrounding if clause and move this logic to
// getSuggestedWordBuilder. // getSuggestedWordBuilder.
if (suggestions.size() > 0) { if (suggestions.size() > 0) {
final String currentHighestWord = suggestions.get(0).toString(); final SuggestedWordInfo currentHighestWord = suggestions.get(0);
// If the current highest word is also equal to typed word, we need to compare // If the current highest word is also equal to typed word, we need to compare
// frequency to determine the insertion position. This does not ensure strictly // 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 // correct ordering, but ensures the top score is on top which is enough for
// removing duplicates correctly. // removing duplicates correctly.
if (StringUtils.equalsIgnoreCase(currentHighestWord, word, offset, length) if (StringUtils.equalsIgnoreCase(currentHighestWord.mWord, word, offset, length)
&& score <= sortedScores[0]) { && score <= currentHighestWord.mScore) {
pos = 1; pos = 1;
} }
} }
@ -486,7 +476,7 @@ public class Suggest implements Dictionary.WordCallback {
if(bigramSuggestion >= 0) { if(bigramSuggestion >= 0) {
dataTypeForLog = Dictionary.BIGRAM; dataTypeForLog = Dictionary.BIGRAM;
// turn freq from bigram into multiplier specified above // turn freq from bigram into multiplier specified above
double multiplier = (((double) mBigramScores[bigramSuggestion]) double multiplier = (((double) mBigramSuggestions.get(bigramSuggestion).mScore)
/ MAXIMUM_BIGRAM_FREQUENCY) / MAXIMUM_BIGRAM_FREQUENCY)
* (BIGRAM_MULTIPLIER_MAX - BIGRAM_MULTIPLIER_MIN) * (BIGRAM_MULTIPLIER_MAX - BIGRAM_MULTIPLIER_MIN)
+ BIGRAM_MULTIPLIER_MIN; + BIGRAM_MULTIPLIER_MIN;
@ -500,10 +490,12 @@ public class Suggest implements Dictionary.WordCallback {
} }
// Check the last one's score and bail // Check the last one's score and bail
if (sortedScores[prefMaxSuggestions - 1] >= score) return true; if (suggestions.size() >= prefMaxSuggestions
while (pos < prefMaxSuggestions) { && suggestions.get(prefMaxSuggestions - 1).mScore >= score) return true;
if (sortedScores[pos] < score while (pos < suggestions.size()) {
|| (sortedScores[pos] == score && length < suggestions.get(pos).length())) { final int curScore = suggestions.get(pos).mScore;
if (curScore < score
|| (curScore == score && length < suggestions.get(pos).codePointCount())) {
break; break;
} }
pos++; pos++;
@ -513,8 +505,6 @@ public class Suggest implements Dictionary.WordCallback {
return true; return true;
} }
System.arraycopy(sortedScores, pos, sortedScores, pos + 1, prefMaxSuggestions - pos - 1);
sortedScores[pos] = score;
final StringBuilder sb = new StringBuilder(getApproxMaxWordLength()); final StringBuilder sb = new StringBuilder(getApproxMaxWordLength());
// TODO: Must pay attention to locale when changing case. // TODO: Must pay attention to locale when changing case.
if (mIsAllUpperCase) { if (mIsAllUpperCase) {
@ -530,7 +520,7 @@ public class Suggest implements Dictionary.WordCallback {
for (int i = mTrailingSingleQuotesCount - 1; i >= 0; --i) { for (int i = mTrailingSingleQuotesCount - 1; i >= 0; --i) {
sb.appendCodePoint(Keyboard.CODE_SINGLE_QUOTE); sb.appendCodePoint(Keyboard.CODE_SINGLE_QUOTE);
} }
suggestions.add(pos, sb); suggestions.add(pos, new SuggestedWordInfo(sb, score));
if (suggestions.size() > prefMaxSuggestions) { if (suggestions.size() > prefMaxSuggestions) {
suggestions.remove(prefMaxSuggestions); suggestions.remove(prefMaxSuggestions);
} else { } else {
@ -539,15 +529,16 @@ public class Suggest implements Dictionary.WordCallback {
return true; return true;
} }
// TODO: Use codepoint instead of char
private int searchBigramSuggestion(final char[] word, final int offset, final int length) { private int searchBigramSuggestion(final char[] word, final int offset, final int length) {
// TODO This is almost O(n^2). Might need fix. // TODO This is almost O(n^2). Might need fix.
// search whether the word appeared in bigram data // search whether the word appeared in bigram data
int bigramSuggestSize = mBigramSuggestions.size(); int bigramSuggestSize = mBigramSuggestions.size();
for (int i = 0; i < bigramSuggestSize; i++) { for (int i = 0; i < bigramSuggestSize; i++) {
if (mBigramSuggestions.get(i).length() == length) { if (mBigramSuggestions.get(i).codePointCount() == length) {
boolean chk = true; boolean chk = true;
for (int j = 0; j < length; j++) { for (int j = 0; j < length; j++) {
if (mBigramSuggestions.get(i).charAt(j) != word[offset+j]) { if (mBigramSuggestions.get(i).codePointAt(j) != word[offset+j]) {
chk = false; chk = false;
break; break;
} }

View File

@ -56,6 +56,10 @@ public class SuggestedWords {
return mSuggestedWordInfoList.get(pos).mWord; return mSuggestedWordInfoList.get(pos).mWord;
} }
public SuggestedWordInfo getWordInfo(int pos) {
return mSuggestedWordInfoList.get(pos);
}
public SuggestedWordInfo getInfo(int pos) { public SuggestedWordInfo getInfo(int pos) {
return mSuggestedWordInfoList.get(pos); return mSuggestedWordInfoList.get(pos);
} }
@ -79,20 +83,12 @@ public class SuggestedWords {
+ " words=" + Arrays.toString(mSuggestedWordInfoList.toArray()); + " words=" + Arrays.toString(mSuggestedWordInfoList.toArray());
} }
public static ArrayList<SuggestedWordInfo> getFromCharSequenceList(
final ArrayList<CharSequence> wordList) {
final ArrayList<SuggestedWordInfo> result = new ArrayList<SuggestedWordInfo>();
for (CharSequence word : wordList) {
if (null != word) result.add(new SuggestedWordInfo(word));
}
return result;
}
public static ArrayList<SuggestedWordInfo> getFromApplicationSpecifiedCompletions( public static ArrayList<SuggestedWordInfo> getFromApplicationSpecifiedCompletions(
final CompletionInfo[] infos) { final CompletionInfo[] infos) {
final ArrayList<SuggestedWordInfo> result = new ArrayList<SuggestedWordInfo>(); final ArrayList<SuggestedWordInfo> result = new ArrayList<SuggestedWordInfo>();
for (CompletionInfo info : infos) { for (CompletionInfo info : infos) {
if (null != info) result.add(new SuggestedWordInfo(info.getText())); if (null != info) result.add(new SuggestedWordInfo(info.getText(),
SuggestedWordInfo.MAX_SCORE));
} }
return result; return result;
} }
@ -103,14 +99,15 @@ public class SuggestedWords {
final CharSequence typedWord, final SuggestedWords previousSuggestions) { final CharSequence typedWord, final SuggestedWords previousSuggestions) {
final ArrayList<SuggestedWordInfo> suggestionsList = new ArrayList<SuggestedWordInfo>(); final ArrayList<SuggestedWordInfo> suggestionsList = new ArrayList<SuggestedWordInfo>();
final HashSet<String> alreadySeen = new HashSet<String>(); final HashSet<String> alreadySeen = new HashSet<String>();
suggestionsList.add(new SuggestedWordInfo(typedWord)); suggestionsList.add(new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE));
alreadySeen.add(typedWord.toString()); alreadySeen.add(typedWord.toString());
final int previousSize = previousSuggestions.size(); final int previousSize = previousSuggestions.size();
for (int pos = 1; pos < previousSize; pos++) { for (int pos = 1; pos < previousSize; pos++) {
final String prevWord = previousSuggestions.getWord(pos).toString(); final SuggestedWordInfo prevWordInfo = previousSuggestions.getWordInfo(pos);
final String prevWord = prevWordInfo.mWord.toString();
// Filter out duplicate suggestion. // Filter out duplicate suggestion.
if (!alreadySeen.contains(prevWord)) { if (!alreadySeen.contains(prevWord)) {
suggestionsList.add(new SuggestedWordInfo(prevWord)); suggestionsList.add(prevWordInfo);
alreadySeen.add(prevWord); alreadySeen.add(prevWord);
} }
} }
@ -118,30 +115,64 @@ public class SuggestedWords {
} }
public static class SuggestedWordInfo { public static class SuggestedWordInfo {
public static final int MAX_SCORE = Integer.MAX_VALUE;
private final String mWordStr;
public final CharSequence mWord; public final CharSequence mWord;
private final String mDebugString; public final int mScore;
public final int mCodePointCount;
private String mDebugString = "";
public SuggestedWordInfo(final CharSequence word) { public SuggestedWordInfo(final CharSequence word, final int score) {
mWordStr = word.toString();
mWord = word; mWord = word;
mDebugString = ""; mScore = score;
mCodePointCount = mWordStr.codePointCount(0, mWordStr.length());
} }
public SuggestedWordInfo(final CharSequence word, final String debugString) {
mWord = word; public void setDebugString(String str) {
if (null == debugString) throw new NullPointerException(""); if (null == str) throw new NullPointerException("Debug info is null");
mDebugString = debugString; mDebugString = str;
} }
public String getDebugString() { public String getDebugString() {
return mDebugString; return mDebugString;
} }
public int codePointCount() {
return mCodePointCount;
}
public int codePointAt(int i) {
return mWordStr.codePointAt(i);
}
@Override @Override
public String toString() { public String toString() {
if (TextUtils.isEmpty(mDebugString)) { if (TextUtils.isEmpty(mDebugString)) {
return mWord.toString(); return mWordStr;
} else { } else {
return mWord.toString() + " (" + mDebugString.toString() + ")"; return mWordStr + " (" + mDebugString.toString() + ")";
}
}
// TODO: Consolidate this method and StringUtils.removeDupes() in the future.
public static void removeDups(ArrayList<SuggestedWordInfo> candidates) {
if (candidates.size() <= 1) {
return;
}
int i = 1;
while(i < candidates.size()) {
final SuggestedWordInfo cur = candidates.get(i);
for (int j = 0; j < i; ++j) {
final SuggestedWordInfo previous = candidates.get(j);
if (TextUtils.equals(cur.mWord, previous.mWord)) {
candidates.remove(cur.mScore < previous.mScore ? i : j);
--i;
break;
}
}
++i;
} }
} }
} }