Suggest.java revision 87d06afc66db68f0b30b36593095511314793517
1923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/* 2443c360d0afdbab091994244f045f4756feaf2b4Jean-Baptiste Queru * Copyright (C) 2008 The Android Open Source Project 3e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa * 48aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License"); 58aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * you may not use this file except in compliance with the License. 68aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * You may obtain a copy of the License at 7e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa * 88aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * http://www.apache.org/licenses/LICENSE-2.0 9e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa * 10923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Unless required by applicable law or agreed to in writing, software 118aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS, 128aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 138aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * See the License for the specific language governing permissions and 148aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * limitations under the License. 15923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 16923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 17923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectpackage com.android.inputmethod.latin; 18923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 19923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.content.Context; 20923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport android.text.TextUtils; 21923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 2215f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaokaimport com.android.inputmethod.annotations.UsedForTesting; 23043f7841985916717f4fa821fe3e423daf3ff2f5Jean Chalardimport com.android.inputmethod.keyboard.ProximityInfo; 24def4551c2a570e7f575b2e9303506d790c2f335fJean Chalardimport com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; 2587d06afc66db68f0b30b36593095511314793517Satoshi Kataokaimport com.android.inputmethod.latin.personalization.UserHistoryPredictionDictionary; 26b03447e1af950888d901fccbd2cc3e3b4a11ef98Ken Wakasaimport com.android.inputmethod.latin.utils.AutoCorrectionUtils; 27e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.BoundedTreeSet; 28e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.CollectionUtils; 29e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.StringUtils; 30043f7841985916717f4fa821fe3e423daf3ff2f5Jean Chalard 31fa086c90760bc2bedf0b74eacb0fed3bf7ebc2b7Tadashi G. Takaokaimport java.util.ArrayList; 329da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalardimport java.util.Comparator; 33c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaokaimport java.util.HashSet; 34cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalardimport java.util.Locale; 351b06b59e28743b713947947437ea5b312477f808Jean Chalardimport java.util.concurrent.ConcurrentHashMap; 36fa086c90760bc2bedf0b74eacb0fed3bf7ebc2b7Tadashi G. Takaoka 37923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/** 38e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa * This class loads a dictionary and provides a list of suggestions for a given sequence of 39923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * characters. This includes corrections and completions. 40923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 41a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class Suggest { 4282411d47ba7e8133ed2390c6920945e139a738cesatok public static final String TAG = Suggest.class.getSimpleName(); 43cdbbea735f590784791f0c1fe33a514c4e864836satok 44f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka // Session id for 45bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka // {@link #getSuggestedWords(WordComposer,String,ProximityInfo,boolean,int)}. 46f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka public static final int SESSION_TYPING = 0; 47f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka public static final int SESSION_GESTURE = 1; 48f035649cb612be8b80892c510bbc137a615719b4Tadashi G. Takaoka 495475e92b3fb33dd7d6b021ddcbe1ca593112b5c8Jean Chalard // TODO: rename this to CORRECTION_OFF 50923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project public static final int CORRECTION_NONE = 0; 515475e92b3fb33dd7d6b021ddcbe1ca593112b5c8Jean Chalard // TODO: rename this to CORRECTION_ON 524606de117b7541125f3f15bd6b50d77ed20e5132Jean Chalard public static final int CORRECTION_FULL = 1; 53979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 54a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard // Close to -2**31 55a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard private static final int SUPPRESS_SUGGEST_THRESHOLD = -2000000000; 56a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard 57369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka public interface SuggestInitializationListener { 58369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka public void onUpdateMainDictionaryAvailability(boolean isMainDictionaryAvailable); 59369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka } 60369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka 618553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard private static final boolean DBG = LatinImeLogger.sDBG; 6282411d47ba7e8133ed2390c6920945e139a738cesatok 636080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge private Dictionary mMainDictionary; 6467fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard private ContactsBinaryDictionary mContactsDict; 656234be1fe76740c458781b633f4ac66edd8ea84fJean Chalard private final ConcurrentHashMap<String, Dictionary> mDictionaries = 665f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka CollectionUtils.newConcurrentHashMap(); 6701a4ebcd88f8a7001aac2f7f45293ceab717a30dJean Chalard @UsedForTesting 6801a4ebcd88f8a7001aac2f7f45293ceab717a30dJean Chalard private boolean mIsCurrentlyWaitingForMainDictionary = false; 69979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 700998c48ac30294b7b6b70257b390962e930b59e1Jean Chalard public static final int MAX_SUGGESTIONS = 18; 7134386e698876c0f29b2d5b54b44db1e32b562c47Amith Yamasani 720028ed3627ff4f37a62a80f3b2c857e373cd5090satok private float mAutoCorrectionThreshold; 73979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 748e17f6d93a3b079eab41450539b9890763fb6e3fJean Chalard // Locale used for upper- and title-casing words 75d91268ad9fb69b4733044b4e466e1d33f6c4725fJean Chalard public final Locale mLocale; 768e17f6d93a3b079eab41450539b9890763fb6e3fJean Chalard 77369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka public Suggest(final Context context, final Locale locale, 78369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka final SuggestInitializationListener listener) { 7979eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka initAsynchronously(context, locale, listener); 808e17f6d93a3b079eab41450539b9890763fb6e3fJean Chalard mLocale = locale; 81979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 82979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 8315f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka @UsedForTesting 841562fc91f015616f900b82bb44e6f1493be92c5aJean Chalard Suggest(final AssetFileAddress[] dictionaryList, final Locale locale) { 851562fc91f015616f900b82bb44e6f1493be92c5aJean Chalard final Dictionary mainDict = DictionaryFactory.createDictionaryForTest(dictionaryList, 861562fc91f015616f900b82bb44e6f1493be92c5aJean Chalard false /* useFullEditDistance */, locale); 878e17f6d93a3b079eab41450539b9890763fb6e3fJean Chalard mLocale = locale; 886080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge mMainDictionary = mainDict; 89d8f0caa406a0ca1df488baeb3af05528085755b7Jean Chalard addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_MAIN, mainDict); 9033e0b1e79e464ac48a09433bbfcbb17ded620452Tadashi G. Takaoka } 9133e0b1e79e464ac48a09433bbfcbb17ded620452Tadashi G. Takaoka 9279eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka private void initAsynchronously(final Context context, final Locale locale, 9379eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka final SuggestInitializationListener listener) { 9479eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka resetMainDict(context, locale, listener); 953af9f05f2916e376f265974c820c369a6c63a780Jean Chalard } 963af9f05f2916e376f265974c820c369a6c63a780Jean Chalard 971b06b59e28743b713947947437ea5b312477f808Jean Chalard private static void addOrReplaceDictionary( 981b06b59e28743b713947947437ea5b312477f808Jean Chalard final ConcurrentHashMap<String, Dictionary> dictionaries, 991b06b59e28743b713947947437ea5b312477f808Jean Chalard final String key, final Dictionary dict) { 1003439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka final Dictionary oldDict = (dict == null) 1013439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka ? dictionaries.remove(key) 1023439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka : dictionaries.put(key, dict); 1033439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka if (oldDict != null && dict != oldDict) { 1043439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka oldDict.close(); 1053439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka } 1063439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka } 1073439c72639d50921a87ab6f9d3aa1bf941aef8d2Tadashi G. Takaoka 10879eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka public void resetMainDict(final Context context, final Locale locale, 10979eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka final SuggestInitializationListener listener) { 11001a4ebcd88f8a7001aac2f7f45293ceab717a30dJean Chalard mIsCurrentlyWaitingForMainDictionary = true; 1116080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge mMainDictionary = null; 11279eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka if (listener != null) { 11379eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka listener.onUpdateMainDictionaryAvailability(hasMainDictionary()); 114369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka } 1153af9f05f2916e376f265974c820c369a6c63a780Jean Chalard new Thread("InitializeBinaryDictionary") { 116904baab25a4c6ec5d9c4bf7e562154e3f544d296satok @Override 1173af9f05f2916e376f265974c820c369a6c63a780Jean Chalard public void run() { 118f0e12a969974987f1b97929886c6ebe6a685c538Jean Chalard final DictionaryCollection newMainDict = 119f0e12a969974987f1b97929886c6ebe6a685c538Jean Chalard DictionaryFactory.createMainDictionaryFromManager(context, locale); 120d8f0caa406a0ca1df488baeb3af05528085755b7Jean Chalard addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_MAIN, newMainDict); 1216080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge mMainDictionary = newMainDict; 12279eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka if (listener != null) { 12379eefda0d3ab5e03c2d012ed8ea1898004781871Tadashi G. Takaoka listener.onUpdateMainDictionaryAvailability(hasMainDictionary()); 124369e54cc338d8115d63138663b882f56208d7ec3Tadashi G. Takaoka } 12501a4ebcd88f8a7001aac2f7f45293ceab717a30dJean Chalard mIsCurrentlyWaitingForMainDictionary = false; 1263af9f05f2916e376f265974c820c369a6c63a780Jean Chalard } 1273af9f05f2916e376f265974c820c369a6c63a780Jean Chalard }.start(); 128cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard } 129cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard 130c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa // The main dictionary could have been loaded asynchronously. Don't cache the return value 131c769ef4dd17ff9561e99528624f74b9072a09fbbKen Wakasa // of this method. 132e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani public boolean hasMainDictionary() { 1336080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge return null != mMainDictionary && mMainDictionary.isInitialized(); 1346080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge } 1356080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge 13601a4ebcd88f8a7001aac2f7f45293ceab717a30dJean Chalard @UsedForTesting 13701a4ebcd88f8a7001aac2f7f45293ceab717a30dJean Chalard public boolean isCurrentlyWaitingForMainDictionary() { 13801a4ebcd88f8a7001aac2f7f45293ceab717a30dJean Chalard return mIsCurrentlyWaitingForMainDictionary; 13901a4ebcd88f8a7001aac2f7f45293ceab717a30dJean Chalard } 14001a4ebcd88f8a7001aac2f7f45293ceab717a30dJean Chalard 1416080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge public Dictionary getMainDictionary() { 1426080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge return mMainDictionary; 143e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani } 144e8f1edefeb2375a253d742c7f95e8d91677c7073Amith Yamasani 14567fd0c240d7c37b06e05333347fd17acf59fadf8Jean Chalard public ContactsBinaryDictionary getContactsDictionary() { 14614051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard return mContactsDict; 14714051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard } 14814051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard 1491b06b59e28743b713947947437ea5b312477f808Jean Chalard public ConcurrentHashMap<String, Dictionary> getUnigramDictionaries() { 1506234be1fe76740c458781b633f4ac66edd8ea84fJean Chalard return mDictionaries; 151bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok } 152bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok 153923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 154923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Sets an optional user dictionary resource to be loaded. The user dictionary is consulted 155f4223452119f9ff8b52f026f7ef92d961736dc51Jean Chalard * before the main dictionary, if set. This refers to the system-managed user dictionary. 156923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 157bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka public void setUserDictionary(final UserBinaryDictionary userDictionary) { 158d8f0caa406a0ca1df488baeb3af05528085755b7Jean Chalard addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_USER, userDictionary); 159923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 1602bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer 1612bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer /** 162699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard * Sets an optional contacts dictionary resource to be loaded. It is also possible to remove 163699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard * the contacts dictionary by passing null to this method. In this case no contacts dictionary 164699094f9b6e0a4621e8b3cfab70b59c0c7c086bbJean Chalard * won't be used. 1652bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer */ 166bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka public void setContactsDictionary(final ContactsBinaryDictionary contactsDictionary) { 16714051e2b5343db4b0531b7b4b806da0c09d6e251Jean Chalard mContactsDict = contactsDictionary; 168d8f0caa406a0ca1df488baeb3af05528085755b7Jean Chalard addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_CONTACTS, contactsDictionary); 1692bed1531c2c9bd48096bfa97dd1a39e04bd15e7bEric Fischer } 170e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa 17187d06afc66db68f0b30b36593095511314793517Satoshi Kataoka public void setUserHistoryPredictionDictionary( 17287d06afc66db68f0b30b36593095511314793517Satoshi Kataoka final UserHistoryPredictionDictionary userHistoryPredictionDictionary) { 17387d06afc66db68f0b30b36593095511314793517Satoshi Kataoka addOrReplaceDictionary(mDictionaries, Dictionary.TYPE_USER_HISTORY, 17487d06afc66db68f0b30b36593095511314793517Satoshi Kataoka userHistoryPredictionDictionary); 175979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 176979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 1770028ed3627ff4f37a62a80f3b2c857e373cd5090satok public void setAutoCorrectionThreshold(float threshold) { 1781b1f7f907f6c7d6e849c88ca06c3608bc84d7c5fTadashi G. Takaoka mAutoCorrectionThreshold = threshold; 179b1abda8d62d654e876c4f781a07d724922c736e4Mitsuhiro Shimoda } 180b1abda8d62d654e876c4f781a07d724922c736e4Mitsuhiro Shimoda 181bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka public SuggestedWords getSuggestedWords(final WordComposer wordComposer, 182bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka final String prevWordForBigram, final ProximityInfo proximityInfo, 1832dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard final boolean blockOffensiveWords, final boolean isCorrectionEnabled, 1842dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard final int sessionId) { 185979f8690967ff5409fe18f5085858ccdb8e0ccf1satok LatinImeLogger.onStartSuggestion(prevWordForBigram); 186d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka if (wordComposer.isBatchMode()) { 1873979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka return getSuggestedWordsForBatchInput( 1882dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard wordComposer, prevWordForBigram, proximityInfo, blockOffensiveWords, sessionId); 189d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka } else { 190d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka return getSuggestedWordsForTypingInput(wordComposer, prevWordForBigram, proximityInfo, 1912dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard blockOffensiveWords, isCorrectionEnabled); 192d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka } 193d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka } 194d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka 195d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka // Retrieves suggestions for the typing input. 196bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka private SuggestedWords getSuggestedWordsForTypingInput(final WordComposer wordComposer, 197bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka final String prevWordForBigram, final ProximityInfo proximityInfo, 1982dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard final boolean blockOffensiveWords, final boolean isCorrectionEnabled) { 19910abf10c1fd3782389cbec1aec7b91855a7b5154Jean Chalard final int trailingSingleQuotesCount = wordComposer.trailingSingleQuotesCount(); 2002d2e3480338b97b55f1a22bf2bfe89c52ba866e2Jean Chalard final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator, 2012d2e3480338b97b55f1a22bf2bfe89c52ba866e2Jean Chalard MAX_SUGGESTIONS); 2021b62ff1a3d61cd44ab88acdfcbdf0fc70a7e1b10Amith Yamasani 203c83359f9746ca6f0269a1a7017b585c1a5cab9b8Jean Chalard final String typedWord = wordComposer.getTypedWord(); 20410abf10c1fd3782389cbec1aec7b91855a7b5154Jean Chalard final String consideredWord = trailingSingleQuotesCount > 0 20510abf10c1fd3782389cbec1aec7b91855a7b5154Jean Chalard ? typedWord.substring(0, typedWord.length() - trailingSingleQuotesCount) 206117fc18ed46496c81596f8207bba30a09c7317d1Jean Chalard : typedWord; 207d8f0caa406a0ca1df488baeb3af05528085755b7Jean Chalard LatinImeLogger.onAddSuggestedWord(typedWord, Dictionary.TYPE_USER_TYPED); 208979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 209c677b0071d51a277413079b30f2215605637aa6bJean Chalard final WordComposer wordComposerForLookup; 210c677b0071d51a277413079b30f2215605637aa6bJean Chalard if (trailingSingleQuotesCount > 0) { 211c677b0071d51a277413079b30f2215605637aa6bJean Chalard wordComposerForLookup = new WordComposer(wordComposer); 212c677b0071d51a277413079b30f2215605637aa6bJean Chalard for (int i = trailingSingleQuotesCount - 1; i >= 0; --i) { 213c677b0071d51a277413079b30f2215605637aa6bJean Chalard wordComposerForLookup.deleteLast(); 214c677b0071d51a277413079b30f2215605637aa6bJean Chalard } 215c677b0071d51a277413079b30f2215605637aa6bJean Chalard } else { 216c677b0071d51a277413079b30f2215605637aa6bJean Chalard wordComposerForLookup = wordComposer; 217c677b0071d51a277413079b30f2215605637aa6bJean Chalard } 218d8afa2fbe13adf9f512fd294056a884a0edb0573Jean Chalard 219d8afa2fbe13adf9f512fd294056a884a0edb0573Jean Chalard for (final String key : mDictionaries.keySet()) { 220d8afa2fbe13adf9f512fd294056a884a0edb0573Jean Chalard final Dictionary dictionary = mDictionaries.get(key); 221d8afa2fbe13adf9f512fd294056a884a0edb0573Jean Chalard suggestionsSet.addAll(dictionary.getSuggestions( 2222dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard wordComposerForLookup, prevWordForBigram, proximityInfo, blockOffensiveWords)); 223923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 2249f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok 225bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka final String whitelistedWord; 2267b258e512dc2a8c821eb9f435e5719b8a967b441Jean Chalard if (suggestionsSet.isEmpty()) { 2277b258e512dc2a8c821eb9f435e5719b8a967b441Jean Chalard whitelistedWord = null; 2287b258e512dc2a8c821eb9f435e5719b8a967b441Jean Chalard } else if (SuggestedWordInfo.KIND_WHITELIST != suggestionsSet.first().mKind) { 2297b258e512dc2a8c821eb9f435e5719b8a967b441Jean Chalard whitelistedWord = null; 2307b258e512dc2a8c821eb9f435e5719b8a967b441Jean Chalard } else { 2317b258e512dc2a8c821eb9f435e5719b8a967b441Jean Chalard whitelistedWord = suggestionsSet.first().mWord; 2327b258e512dc2a8c821eb9f435e5719b8a967b441Jean Chalard } 2337b258e512dc2a8c821eb9f435e5719b8a967b441Jean Chalard 2348c06a468e0bffa5cedc7d782be4754da70d9a657Jean Chalard // The word can be auto-corrected if it has a whitelist entry that is not itself, 2358c06a468e0bffa5cedc7d782be4754da70d9a657Jean Chalard // or if it's a 2+ characters non-word (i.e. it's not in the dictionary). 236caed149b67be378adf49f3db16a2cfbb8dd15d84Jean Chalard final boolean allowsToBeAutoCorrected = (null != whitelistedWord 237caed149b67be378adf49f3db16a2cfbb8dd15d84Jean Chalard && !whitelistedWord.equals(consideredWord)) 238b03447e1af950888d901fccbd2cc3e3b4a11ef98Ken Wakasa || (consideredWord.length() > 1 && !AutoCorrectionUtils.isValidWord(this, 2398c06a468e0bffa5cedc7d782be4754da70d9a657Jean Chalard consideredWord, wordComposer.isFirstCharCapitalized())); 240caed149b67be378adf49f3db16a2cfbb8dd15d84Jean Chalard 24167af2a24157ead953607bdfd585fba3a7e6bf50cJean Chalard final boolean hasAutoCorrection; 242966efe48891cbdd364d94f1e72fa0435ab8f2b77Jean Chalard // TODO: using isCorrectionEnabled here is not very good. It's probably useless, because 243966efe48891cbdd364d94f1e72fa0435ab8f2b77Jean Chalard // any attempt to do auto-correction is already shielded with a test for this flag; at the 244966efe48891cbdd364d94f1e72fa0435ab8f2b77Jean Chalard // same time, it feels wrong that the SuggestedWord object includes information about 245966efe48891cbdd364d94f1e72fa0435ab8f2b77Jean Chalard // the current settings. It may also be useful to know, when the setting is off, whether 246966efe48891cbdd364d94f1e72fa0435ab8f2b77Jean Chalard // the word *would* have been auto-corrected. 2472549b4978e5b0460d0f34a5e4016374ac2198753Jean Chalard if (!isCorrectionEnabled || !allowsToBeAutoCorrected || !wordComposer.isComposingWord() 248e7c471a52f38c48cd38e412d88901bddb6f903a9Jean Chalard || suggestionsSet.isEmpty() || wordComposer.hasDigits() 2492549b4978e5b0460d0f34a5e4016374ac2198753Jean Chalard || wordComposer.isMostlyCaps() || wordComposer.isResumed() 2502549b4978e5b0460d0f34a5e4016374ac2198753Jean Chalard || !hasMainDictionary()) { 25190d300c770b1697af5b715e55fa87d97e22588d2Jean Chalard // If we don't have a main dictionary, we never want to auto-correct. The reason for 25290d300c770b1697af5b715e55fa87d97e22588d2Jean Chalard // this is, the user may have a contact whose name happens to match a valid word in 25390d300c770b1697af5b715e55fa87d97e22588d2Jean Chalard // their language, and it will unexpectedly auto-correct. For example, if the user 25490d300c770b1697af5b715e55fa87d97e22588d2Jean Chalard // types in English with no dictionary and has a "Will" in their contact list, "will" 25590d300c770b1697af5b715e55fa87d97e22588d2Jean Chalard // would always auto-correct to "Will" which is unwanted. Hence, no main dict => no 25690d300c770b1697af5b715e55fa87d97e22588d2Jean Chalard // auto-correct. 257ea578f6b1dbcf04ffcc9c673f72a38ed2cfecdfcJean Chalard hasAutoCorrection = false; 25894b20c90d86aa042c2f361597665045271956decJean Chalard } else { 259b03447e1af950888d901fccbd2cc3e3b4a11ef98Ken Wakasa hasAutoCorrection = AutoCorrectionUtils.suggestionExceedsAutoCorrectionThreshold( 2601343d27de30c4010c54576d6c8bbb052c7630cbeJean Chalard suggestionsSet.first(), consideredWord, mAutoCorrectionThreshold); 26194b20c90d86aa042c2f361597665045271956decJean Chalard } 2629f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok 263ed83d4b14366b9799bf94c3f3486dc14ebd15d0fJean Chalard final ArrayList<SuggestedWordInfo> suggestionsContainer = 2645f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka CollectionUtils.newArrayList(suggestionsSet); 2655110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard final int suggestionsCount = suggestionsContainer.size(); 266f5b55cb70c9d6012e1aa2b201c4785530afab168Jean Chalard final boolean isFirstCharCapitalized = wordComposer.isFirstCharCapitalized(); 267f5b55cb70c9d6012e1aa2b201c4785530afab168Jean Chalard final boolean isAllUpperCase = wordComposer.isAllUpperCase(); 2685110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard if (isFirstCharCapitalized || isAllUpperCase || 0 != trailingSingleQuotesCount) { 2695110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard for (int i = 0; i < suggestionsCount; ++i) { 2705110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard final SuggestedWordInfo wordInfo = suggestionsContainer.get(i); 2715110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo( 2725110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard wordInfo, mLocale, isAllUpperCase, isFirstCharCapitalized, 2735110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard trailingSingleQuotesCount); 2745110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard suggestionsContainer.set(i, transformedWordInfo); 2755110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard } 2765110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard } 2775110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard 2785110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard for (int i = 0; i < suggestionsCount; ++i) { 279ed83d4b14366b9799bf94c3f3486dc14ebd15d0fJean Chalard final SuggestedWordInfo wordInfo = suggestionsContainer.get(i); 2805110e2cb5115bc7d8337a63427b895eeb74c9cd5Jean Chalard LatinImeLogger.onAddSuggestedWord(wordInfo.mWord.toString(), wordInfo.mSourceDict); 281bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok } 282bcfce3b3b9dbd4f5db736948b74bd820fc639a08satok 283f89a75134b03bd2675c85249a184c09f83c6f80cJean Chalard if (!TextUtils.isEmpty(typedWord)) { 284bed514bd902d9736edcbfe03d37d8cced2bb03a3Jean Chalard suggestionsContainer.add(0, new SuggestedWordInfo(typedWord, 28524eec0fa680f97e64d1fa0df754acbad95ed9a76Jean Chalard SuggestedWordInfo.MAX_SCORE, SuggestedWordInfo.KIND_TYPED, 28624eec0fa680f97e64d1fa0df754acbad95ed9a76Jean Chalard Dictionary.TYPE_USER_TYPED)); 28728eeb35d149468514a65379e9d0d1672cf26981eJean Chalard } 288bed514bd902d9736edcbfe03d37d8cced2bb03a3Jean Chalard SuggestedWordInfo.removeDups(suggestionsContainer); 2899f67e12a0e3f77985fb8bafe0db4c00e32317b9asatok 290def4551c2a570e7f575b2e9303506d790c2f335fJean Chalard final ArrayList<SuggestedWordInfo> suggestionsList; 291bed514bd902d9736edcbfe03d37d8cced2bb03a3Jean Chalard if (DBG && !suggestionsContainer.isEmpty()) { 292bed514bd902d9736edcbfe03d37d8cced2bb03a3Jean Chalard suggestionsList = getSuggestionsInfoListWithDebugInfo(typedWord, suggestionsContainer); 293ed9986824e1339855376771ad29fae4de921a029Jean Chalard } else { 294bed514bd902d9736edcbfe03d37d8cced2bb03a3Jean Chalard suggestionsList = suggestionsContainer; 2958553b5ec315660ab53dd9234e64e1e39ea09ec0fJean Chalard } 296ed9986824e1339855376771ad29fae4de921a029Jean Chalard 2974a08b2f0e4d0ee7f1d89b4eb3c77c37d987584eaJean Chalard return new SuggestedWords(suggestionsList, 298b3cfde2cbb96951b1202c70b9961f340bdf495d0Jean Chalard // TODO: this first argument is lying. If this is a whitelisted word which is an 299b3cfde2cbb96951b1202c70b9961f340bdf495d0Jean Chalard // actual word, it says typedWordValid = false, which looks wrong. We should either 300b3cfde2cbb96951b1202c70b9961f340bdf495d0Jean Chalard // rename the attribute or change the value. 30102f1c1534c2060aaea7a9a020ce87f6c5ff5d8e0Jean Chalard !allowsToBeAutoCorrected /* typedWordValid */, 3022549b4978e5b0460d0f34a5e4016374ac2198753Jean Chalard hasAutoCorrection, /* willAutoCorrect */ 30303a35170751a635332c00bf6c272a0127a255cf6Jean Chalard false /* isPunctuationSuggestions */, 3040142b997bf18f5d07e83b3fd403f0b3ea4736040satok false /* isObsoleteSuggestions */, 305f5b55cb70c9d6012e1aa2b201c4785530afab168Jean Chalard !wordComposer.isComposingWord() /* isPrediction */); 306923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 307923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 308d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka // Retrieves suggestions for the batch input. 309bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka private SuggestedWords getSuggestedWordsForBatchInput(final WordComposer wordComposer, 310bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka final String prevWordForBigram, final ProximityInfo proximityInfo, 3112dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard final boolean blockOffensiveWords, final int sessionId) { 312d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka final BoundedTreeSet suggestionsSet = new BoundedTreeSet(sSuggestedWordInfoComparator, 313d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka MAX_SUGGESTIONS); 314d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka 315d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka // At second character typed, search the unigrams (scores being affected by bigrams) 316d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka for (final String key : mDictionaries.keySet()) { 31746fc768e54e3d52003645494552f9e686f28f20fJean Chalard // Skip User history dictionary for lookup 31846fc768e54e3d52003645494552f9e686f28f20fJean Chalard // TODO: The user history dictionary should just override getSuggestionsWithSessionId 31946fc768e54e3d52003645494552f9e686f28f20fJean Chalard // to make sure it doesn't return anything and we should remove this test 32046fc768e54e3d52003645494552f9e686f28f20fJean Chalard if (key.equals(Dictionary.TYPE_USER_HISTORY)) { 321d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka continue; 322d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka } 323d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka final Dictionary dictionary = mDictionaries.get(key); 3242dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard suggestionsSet.addAll(dictionary.getSuggestionsWithSessionId(wordComposer, 3252dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard prevWordForBigram, proximityInfo, blockOffensiveWords, sessionId)); 326d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka } 327d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka 32887cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka for (SuggestedWordInfo wordInfo : suggestionsSet) { 329bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka LatinImeLogger.onAddSuggestedWord(wordInfo.mWord, wordInfo.mSourceDict); 33087cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka } 33187cecf7db61536d9f7ec07fe198d37a11b6c8407Satoshi Kataoka 332d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka final ArrayList<SuggestedWordInfo> suggestionsContainer = 3335f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka CollectionUtils.newArrayList(suggestionsSet); 334eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang final int suggestionsCount = suggestionsContainer.size(); 3351eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard final boolean isFirstCharCapitalized = wordComposer.wasShiftedNoLock(); 3361eba97d92fb5caa4f23425837b6680ccc2a23ae8Jean Chalard final boolean isAllUpperCase = wordComposer.isAllUpperCase(); 337eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang if (isFirstCharCapitalized || isAllUpperCase) { 338eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang for (int i = 0; i < suggestionsCount; ++i) { 339eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang final SuggestedWordInfo wordInfo = suggestionsContainer.get(i); 340eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang final SuggestedWordInfo transformedWordInfo = getTransformedSuggestedWordInfo( 341eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang wordInfo, mLocale, isAllUpperCase, isFirstCharCapitalized, 342eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang 0 /* trailingSingleQuotesCount */); 343eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang suggestionsContainer.set(i, transformedWordInfo); 344eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang } 345eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang } 346d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka 347d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard if (suggestionsContainer.size() > 1 && TextUtils.equals(suggestionsContainer.get(0).mWord, 348d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard wordComposer.getRejectedBatchModeSuggestion())) { 349d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard final SuggestedWordInfo rejected = suggestionsContainer.remove(0); 350d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard suggestionsContainer.add(1, rejected); 351d40f3f6bc1bcf07800fbee0468fe90d307ca28bbJean Chalard } 352d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka SuggestedWordInfo.removeDups(suggestionsContainer); 353a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard 354a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard // For some reason some suggestions with MIN_VALUE are making their way here. 355a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard // TODO: Find a more robust way to detect distractors. 356a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard for (int i = suggestionsContainer.size() - 1; i >= 0; --i) { 357a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard if (suggestionsContainer.get(i).mScore < SUPPRESS_SUGGEST_THRESHOLD) { 358a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard suggestionsContainer.remove(i); 359a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard } 360a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard } 361a5a2f3e3c77ebf2e1bb74b08c8587c15b9711ac8Jean Chalard 362eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang // In the batch input mode, the most relevant suggested word should act as a "typed word" 363eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang // (typedWordValid=true), not as an "auto correct word" (willAutoCorrect=false). 364d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka return new SuggestedWords(suggestionsContainer, 365d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka true /* typedWordValid */, 366eea34598bf63f670f47d7b3f37b6436921e5fe02Tom Ouyang false /* willAutoCorrect */, 367d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka false /* isPunctuationSuggestions */, 368d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka false /* isObsoleteSuggestions */, 369d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka false /* isPrediction */); 370d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka } 371d82dcdc9246b340c4b355e34efc6079f3278efa6Tadashi G. Takaoka 3720d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard private static ArrayList<SuggestedWordInfo> getSuggestionsInfoListWithDebugInfo( 3737e518d8b8358c96b94b900f0917cdc5fd8190ce1satok final String typedWord, final ArrayList<SuggestedWordInfo> suggestions) { 3747e518d8b8358c96b94b900f0917cdc5fd8190ce1satok final SuggestedWordInfo typedWordInfo = suggestions.get(0); 3757e518d8b8358c96b94b900f0917cdc5fd8190ce1satok typedWordInfo.setDebugString("+"); 3760d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard final int suggestionsSize = suggestions.size(); 3770d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard final ArrayList<SuggestedWordInfo> suggestionsList = 3785f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka CollectionUtils.newArrayList(suggestionsSize); 3797e518d8b8358c96b94b900f0917cdc5fd8190ce1satok suggestionsList.add(typedWordInfo); 3800d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard // Note: i here is the index in mScores[], but the index in mSuggestions is one more 3810d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard // than i because we added the typed word to mSuggestions without touching mScores. 3827e518d8b8358c96b94b900f0917cdc5fd8190ce1satok for (int i = 0; i < suggestionsSize - 1; ++i) { 3837e518d8b8358c96b94b900f0917cdc5fd8190ce1satok final SuggestedWordInfo cur = suggestions.get(i + 1); 3840028ed3627ff4f37a62a80f3b2c857e373cd5090satok final float normalizedScore = BinaryDictionary.calcNormalizedScore( 385db1939dbaa1de59eaf5693e2c89b02b323e9aac8satok typedWord, cur.toString(), cur.mScore); 3860d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard final String scoreInfoString; 3870d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard if (normalizedScore > 0) { 38894027c7201a376107a35ec78cd21db1905662601Tadashi G. Takaoka scoreInfoString = String.format( 38994027c7201a376107a35ec78cd21db1905662601Tadashi G. Takaoka Locale.ROOT, "%d (%4.2f)", cur.mScore, normalizedScore); 3900d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard } else { 3917e518d8b8358c96b94b900f0917cdc5fd8190ce1satok scoreInfoString = Integer.toString(cur.mScore); 3920d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard } 3937e518d8b8358c96b94b900f0917cdc5fd8190ce1satok cur.setDebugString(scoreInfoString); 3947e518d8b8358c96b94b900f0917cdc5fd8190ce1satok suggestionsList.add(cur); 3950d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard } 3960d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard return suggestionsList; 3970d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard } 3980d0f182959600d83c376e6b844337ea45e5ddbbfJean Chalard 399a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaoka private static final class SuggestedWordInfoComparator 400a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaoka implements Comparator<SuggestedWordInfo> { 4019da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard // This comparator ranks the word info with the higher frequency first. That's because 4029da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard // that's the order we want our elements in. 4039da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard @Override 4049da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard public int compare(final SuggestedWordInfo o1, final SuggestedWordInfo o2) { 4059da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard if (o1.mScore > o2.mScore) return -1; 4069da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard if (o1.mScore < o2.mScore) return 1; 4079da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard if (o1.mCodePointCount < o2.mCodePointCount) return -1; 4089da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard if (o1.mCodePointCount > o2.mCodePointCount) return 1; 409bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka return o1.mWord.compareTo(o2.mWord); 4109da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard } 4119da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard } 4129da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard private static final SuggestedWordInfoComparator sSuggestedWordInfoComparator = 4139da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard new SuggestedWordInfoComparator(); 4149da0027b386c23b83c2f9b0121bc15fa15306e3aJean Chalard 415ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard private static SuggestedWordInfo getTransformedSuggestedWordInfo( 416ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard final SuggestedWordInfo wordInfo, final Locale locale, final boolean isAllUpperCase, 417ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard final boolean isFirstCharCapitalized, final int trailingSingleQuotesCount) { 4189011b89f4ea0d73f1ad78b2dd0a6557b950fddd9Jean Chalard final StringBuilder sb = new StringBuilder(wordInfo.mWord.length()); 419ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard if (isAllUpperCase) { 420bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka sb.append(wordInfo.mWord.toUpperCase(locale)); 421ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard } else if (isFirstCharCapitalized) { 42299b93d17d53c2d587c45373831b327f7851ec0a8Jean Chalard sb.append(StringUtils.capitalizeFirstCodePoint(wordInfo.mWord, locale)); 423ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard } else { 424ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard sb.append(wordInfo.mWord); 425ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard } 426ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard for (int i = trailingSingleQuotesCount - 1; i >= 0; --i) { 427240871ecafde7834ebb4270cd7758fc904a5f3a7Tadashi G. Takaoka sb.appendCodePoint(Constants.CODE_SINGLE_QUOTE); 428ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard } 429bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka return new SuggestedWordInfo(sb.toString(), wordInfo.mScore, wordInfo.mKind, 430bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka wordInfo.mSourceDict); 431ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard } 432ec8b27fe49bd0a149cf7dcd36d1b0d966b03a3b5Jean Chalard 43336fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani public void close() { 4345f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka final HashSet<Dictionary> dictionaries = CollectionUtils.newHashSet(); 4356234be1fe76740c458781b633f4ac66edd8ea84fJean Chalard dictionaries.addAll(mDictionaries.values()); 436c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka for (final Dictionary dictionary : dictionaries) { 437c2c44f94e705e74598ec944ab51f3bd13eb50dbfTadashi G. Takaoka dictionary.close(); 43836fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani } 4396080f6878b10916013a8a5e1d5f58f8041452c56Kurt Partridge mMainDictionary = null; 44036fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani } 441923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project} 442