SuggestedWords.java revision 3d97b95b90a1c40a22981d563e12cfe86f60ae71
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.inputmethod.latin; 18 19import android.text.TextUtils; 20import android.view.inputmethod.CompletionInfo; 21 22import java.util.ArrayList; 23import java.util.Arrays; 24import java.util.HashSet; 25 26public final class SuggestedWords { 27 private static final ArrayList<SuggestedWordInfo> EMPTY_WORD_INFO_LIST = 28 CollectionUtils.newArrayList(0); 29 public static final SuggestedWords EMPTY = new SuggestedWords( 30 EMPTY_WORD_INFO_LIST, false, false, false, false, false); 31 32 public final boolean mTypedWordValid; 33 // Note: this INCLUDES cases where the word will auto-correct to itself. A good definition 34 // of what this flag means would be "the top suggestion is strong enough to auto-correct", 35 // whether this exactly matches the user entry or not. 36 public final boolean mWillAutoCorrect; 37 public final boolean mIsPunctuationSuggestions; 38 public final boolean mIsObsoleteSuggestions; 39 public final boolean mIsPrediction; 40 private final ArrayList<SuggestedWordInfo> mSuggestedWordInfoList; 41 42 public SuggestedWords(final ArrayList<SuggestedWordInfo> suggestedWordInfoList, 43 final boolean typedWordValid, 44 final boolean willAutoCorrect, 45 final boolean isPunctuationSuggestions, 46 final boolean isObsoleteSuggestions, 47 final boolean isPrediction) { 48 mSuggestedWordInfoList = suggestedWordInfoList; 49 mTypedWordValid = typedWordValid; 50 mWillAutoCorrect = willAutoCorrect; 51 mIsPunctuationSuggestions = isPunctuationSuggestions; 52 mIsObsoleteSuggestions = isObsoleteSuggestions; 53 mIsPrediction = isPrediction; 54 } 55 56 public boolean isEmpty() { 57 return mSuggestedWordInfoList.isEmpty(); 58 } 59 60 public int size() { 61 return mSuggestedWordInfoList.size(); 62 } 63 64 public String getWord(int pos) { 65 return mSuggestedWordInfoList.get(pos).mWord; 66 } 67 68 public SuggestedWordInfo getInfo(int pos) { 69 return mSuggestedWordInfoList.get(pos); 70 } 71 72 public boolean willAutoCorrect() { 73 return mWillAutoCorrect; 74 } 75 76 @Override 77 public String toString() { 78 // Pretty-print method to help debug 79 return "SuggestedWords:" 80 + " mTypedWordValid=" + mTypedWordValid 81 + " mWillAutoCorrect=" + mWillAutoCorrect 82 + " mIsPunctuationSuggestions=" + mIsPunctuationSuggestions 83 + " words=" + Arrays.toString(mSuggestedWordInfoList.toArray()); 84 } 85 86 public static ArrayList<SuggestedWordInfo> getFromApplicationSpecifiedCompletions( 87 final CompletionInfo[] infos) { 88 final ArrayList<SuggestedWordInfo> result = CollectionUtils.newArrayList(); 89 for (final CompletionInfo info : infos) { 90 if (info == null) continue; 91 final CharSequence text = info.getText(); 92 if (null == text) continue; 93 final SuggestedWordInfo suggestedWordInfo = new SuggestedWordInfo(text.toString(), 94 SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_APP_DEFINED, 95 Dictionary.TYPE_APPLICATION_DEFINED); 96 result.add(suggestedWordInfo); 97 } 98 return result; 99 } 100 101 // Should get rid of the first one (what the user typed previously) from suggestions 102 // and replace it with what the user currently typed. 103 public static ArrayList<SuggestedWordInfo> getTypedWordAndPreviousSuggestions( 104 final String typedWord, final SuggestedWords previousSuggestions) { 105 final ArrayList<SuggestedWordInfo> suggestionsList = CollectionUtils.newArrayList(); 106 final HashSet<String> alreadySeen = CollectionUtils.newHashSet(); 107 suggestionsList.add(new SuggestedWordInfo(typedWord, SuggestedWordInfo.MAX_SCORE, 108 SuggestedWordInfo.KIND_TYPED, Dictionary.TYPE_USER_TYPED)); 109 alreadySeen.add(typedWord.toString()); 110 final int previousSize = previousSuggestions.size(); 111 for (int pos = 1; pos < previousSize; pos++) { 112 final SuggestedWordInfo prevWordInfo = previousSuggestions.getInfo(pos); 113 final String prevWord = prevWordInfo.mWord; 114 // Filter out duplicate suggestion. 115 if (!alreadySeen.contains(prevWord)) { 116 suggestionsList.add(prevWordInfo); 117 alreadySeen.add(prevWord); 118 } 119 } 120 return suggestionsList; 121 } 122 123 public static final class SuggestedWordInfo { 124 public static final int MAX_SCORE = Integer.MAX_VALUE; 125 public static final int KIND_MASK_KIND = 0xFF; // Mask to get only the kind 126 public static final int KIND_TYPED = 0; // What user typed 127 public static final int KIND_CORRECTION = 1; // Simple correction/suggestion 128 public static final int KIND_COMPLETION = 2; // Completion (suggestion with appended chars) 129 public static final int KIND_WHITELIST = 3; // Whitelisted word 130 public static final int KIND_BLACKLIST = 4; // Blacklisted word 131 public static final int KIND_HARDCODED = 5; // Hardcoded suggestion, e.g. punctuation 132 public static final int KIND_APP_DEFINED = 6; // Suggested by the application 133 public static final int KIND_SHORTCUT = 7; // A shortcut 134 public static final int KIND_PREDICTION = 8; // A prediction (== a suggestion with no input) 135 // KIND_RESUMED: A resumed suggestion (comes from a span, currently this type is used only 136 // in java for re-correction) 137 public static final int KIND_RESUMED = 9; 138 public static final int KIND_OOV_CORRECTION = 10; // Most probable string correction 139 140 public static final int KIND_MASK_FLAGS = 0xFFFFFF00; // Mask to get the flags 141 public static final int KIND_FLAG_POSSIBLY_OFFENSIVE = 0x80000000; 142 public static final int KIND_FLAG_EXACT_MATCH = 0x40000000; 143 144 public final String mWord; 145 public final int mScore; 146 public final int mKind; // one of the KIND_* constants above 147 public final int mCodePointCount; 148 public final String mSourceDict; 149 private String mDebugString = ""; 150 151 public SuggestedWordInfo(final String word, final int score, final int kind, 152 final String sourceDict) { 153 mWord = word; 154 mScore = score; 155 mKind = kind; 156 mSourceDict = sourceDict; 157 mCodePointCount = StringUtils.codePointCount(mWord); 158 } 159 160 161 public void setDebugString(final String str) { 162 if (null == str) throw new NullPointerException("Debug info is null"); 163 mDebugString = str; 164 } 165 166 public String getDebugString() { 167 return mDebugString; 168 } 169 170 public int codePointCount() { 171 return mCodePointCount; 172 } 173 174 public int codePointAt(int i) { 175 return mWord.codePointAt(i); 176 } 177 178 @Override 179 public String toString() { 180 if (TextUtils.isEmpty(mDebugString)) { 181 return mWord; 182 } else { 183 return mWord + " (" + mDebugString + ")"; 184 } 185 } 186 187 // TODO: Consolidate this method and StringUtils.removeDupes() in the future. 188 public static void removeDups(ArrayList<SuggestedWordInfo> candidates) { 189 if (candidates.size() <= 1) { 190 return; 191 } 192 int i = 1; 193 while (i < candidates.size()) { 194 final SuggestedWordInfo cur = candidates.get(i); 195 for (int j = 0; j < i; ++j) { 196 final SuggestedWordInfo previous = candidates.get(j); 197 if (cur.mWord.equals(previous.mWord)) { 198 candidates.remove(cur.mScore < previous.mScore ? i : j); 199 --i; 200 break; 201 } 202 } 203 ++i; 204 } 205 } 206 } 207 208 // SuggestedWords is an immutable object, as much as possible. We must not just remove 209 // words from the member ArrayList as some other parties may expect the object to never change. 210 public SuggestedWords getSuggestedWordsExcludingTypedWord() { 211 final ArrayList<SuggestedWordInfo> newSuggestions = CollectionUtils.newArrayList(); 212 for (int i = 0; i < mSuggestedWordInfoList.size(); ++i) { 213 final SuggestedWordInfo info = mSuggestedWordInfoList.get(i); 214 if (SuggestedWordInfo.KIND_TYPED != info.mKind) { 215 newSuggestions.add(info); 216 } 217 } 218 // We should never autocorrect, so we say the typed word is valid. Also, in this case, 219 // no auto-correction should take place hence willAutoCorrect = false. 220 return new SuggestedWords(newSuggestions, true /* typedWordValid */, 221 false /* willAutoCorrect */, mIsPunctuationSuggestions, mIsObsoleteSuggestions, 222 mIsPrediction); 223 } 224} 225