1ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang/* 2ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang * Copyright (C) 2012 The Android Open Source Project 3ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang * 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 7ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang * 88aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * http://www.apache.org/licenses/LICENSE-2.0 9ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang * 108aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * 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. 15ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang */ 16ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 17ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyangpackage com.android.inputmethod.latin; 18ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 19ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyangimport android.content.Context; 20ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyangimport android.os.SystemClock; 21ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyangimport android.util.Log; 22ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 23c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagiimport com.android.inputmethod.annotations.UsedForTesting; 24ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyangimport com.android.inputmethod.keyboard.ProximityInfo; 252e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagiimport com.android.inputmethod.latin.makedict.FormatSpec; 2687a72f50c23a4ef357ae623eabc2af16d02466aeKeisuke Kuroyanagiimport com.android.inputmethod.latin.personalization.DynamicPersonalizationDictionaryWriter; 272e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagiimport com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; 28ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanadaimport com.android.inputmethod.latin.utils.AsyncResultHolder; 29e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.CollectionUtils; 30ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanadaimport com.android.inputmethod.latin.utils.PrioritizedSerialExecutor; 31ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 32ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyangimport java.io.File; 33f6adff6227a15af105dbf39c57213a24bf16780bTom Ouyangimport java.util.ArrayList; 342e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagiimport java.util.HashMap; 355ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagiimport java.util.Map; 36ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanadaimport java.util.concurrent.ConcurrentHashMap; 37e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagiimport java.util.concurrent.atomic.AtomicBoolean; 386e04d6593239e841f5dac0d3f32d613967c11e22Keisuke Kuroyanagiimport java.util.concurrent.atomic.AtomicReference; 39ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 40ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang/** 41ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang * Abstract base class for an expandable dictionary that can be created and updated dynamically 42ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang * during runtime. When updated it automatically generates a new binary dictionary to handle future 43ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang * queries in native code. This binary dictionary is written to internal storage, and potentially 44ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang * shared across multiple ExpandableBinaryDictionary instances. Updates to each dictionary filename 45ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang * are controlled across multiple instances to ensure that only one instance can update the same 46ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang * dictionary at the same time. 47ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang */ 48ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyangabstract public class ExpandableBinaryDictionary extends Dictionary { 49ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 50ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang /** Used for Log actions from this class */ 51ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang private static final String TAG = ExpandableBinaryDictionary.class.getSimpleName(); 52ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 53ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang /** Whether to print debug output to log */ 54ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang private static boolean DEBUG = false; 55ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 562e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi // TODO: Remove. 57e531c2241eb8d5a1462c43ce0deffaf6c769cc23Keisuke Kuroyanagi /** Whether to call binary dictionary dynamically updating methods. */ 582e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi public static boolean ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE = true; 59e531c2241eb8d5a1462c43ce0deffaf6c769cc23Keisuke Kuroyanagi 60ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada private static final int TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS = 100; 61ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada 62ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang /** 6396b22200beb98fd1a6288f4cf53e38611a09cdd0Ken Wakasa * The maximum length of a word in this dictionary. 64ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang */ 65ffcbbaf12788a9fc9398607a548e552d7d2bf05eSatoshi Kataoka protected static final int MAX_WORD_LENGTH = Constants.DICTIONARY_MAX_WORD_LENGTH; 66ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 675ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi private static final int DICTIONARY_FORMAT_VERSION = 3; 685ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi 695ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi private static final String SUPPORTS_DYNAMIC_UPDATE = 705ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE; 712e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi 72ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang /** 73e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi * A static map of update controllers, each of which records the time of accesses to a single 74e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi * binary dictionary file and tracks whether the file is regenerating. The key for this map is 75e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi * the filename and the value is the shared dictionary time recorder associated with that 76e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi * filename. 77ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang */ 78e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi private static final ConcurrentHashMap<String, DictionaryUpdateController> 79e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi sFilenameDictionaryUpdateControllerMap = CollectionUtils.newConcurrentHashMap(); 80ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada 81e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi private static final ConcurrentHashMap<String, PrioritizedSerialExecutor> 82ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada sFilenameExecutorMap = CollectionUtils.newConcurrentHashMap(); 83ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 84ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang /** The application context. */ 85ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang protected final Context mContext; 86ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 87ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang /** 88ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang * The binary dictionary generated dynamically from the fusion dictionary. This is used to 89ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang * answer unigram and bigram queries. 90ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang */ 91ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang private BinaryDictionary mBinaryDictionary; 92ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 93e531c2241eb8d5a1462c43ce0deffaf6c769cc23Keisuke Kuroyanagi // TODO: Remove and handle dictionaries in native code. 94edd1992ed329a84f0e9ef7056fda99f78eeb92b4Keisuke Kuroynagi /** The in-memory dictionary used to generate the binary dictionary. */ 95e5a35711b854aedeeea2f45105b941b9deee49bcSatoshi Kataoka protected AbstractDictionaryWriter mDictionaryWriter; 96ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 97ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang /** 98ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang * The name of this dictionary, used as the filename for storing the binary dictionary. Multiple 99ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang * dictionary instances with the same filename is supported, with access controlled by 100ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada * DictionaryTimeRecorder. 101ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang */ 102ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang private final String mFilename; 103ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 104c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi /** Whether to support dynamically updating the dictionary */ 105c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi private final boolean mIsUpdatable; 106c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi 10737e0fd2ff04a2a87e421abea8bc407bd312dbfc6Keisuke Kuroyanagi // TODO: remove, once dynamic operations is serialized 108e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi /** Controls updating the shared binary dictionary file across multiple instances. */ 109e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi private final DictionaryUpdateController mFilenameDictionaryUpdateController; 110ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 11137e0fd2ff04a2a87e421abea8bc407bd312dbfc6Keisuke Kuroyanagi // TODO: remove, once dynamic operations is serialized 112e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi /** Controls updating the local binary dictionary for this instance. */ 113e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi private final DictionaryUpdateController mPerInstanceDictionaryUpdateController = 114e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi new DictionaryUpdateController(); 115ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 1168aaae56cf6694ec75043be56f1c7812a343b24d5Yuichiro Hanada /* A extension for a binary dictionary file. */ 1178aaae56cf6694ec75043be56f1c7812a343b24d5Yuichiro Hanada public static final String DICT_FILE_EXTENSION = ".dict"; 1188aaae56cf6694ec75043be56f1c7812a343b24d5Yuichiro Hanada 119ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada private final AtomicReference<Runnable> mUnfinishedFlushingTask = 120ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada new AtomicReference<Runnable>(); 1216e04d6593239e841f5dac0d3f32d613967c11e22Keisuke Kuroyanagi 122ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang /** 123ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang * Abstract method for loading the unigrams and bigrams of a given dictionary in a background 124ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang * thread. 125ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang */ 126ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang protected abstract void loadDictionaryAsync(); 127ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 128ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang /** 1294d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang * Indicates that the source dictionary content has changed and a rebuild of the binary file is 1304d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang * required. If it returns false, the next reload will only read the current binary dictionary 1314d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang * from file. Note that the shared binary dictionary is locked when this is called. 1324d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang */ 1334d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang protected abstract boolean hasContentChanged(); 1344d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang 1354d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang /** 136e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi * Gets the dictionary update controller for the given filename. 137ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang */ 138e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi private static DictionaryUpdateController getDictionaryUpdateController( 139ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang String filename) { 140e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi DictionaryUpdateController recorder = sFilenameDictionaryUpdateControllerMap.get(filename); 141ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada if (recorder == null) { 142e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi synchronized(sFilenameDictionaryUpdateControllerMap) { 143e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi recorder = new DictionaryUpdateController(); 144e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi sFilenameDictionaryUpdateControllerMap.put(filename, recorder); 145ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada } 146ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada } 147ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada return recorder; 148ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada } 149ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada 150ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada /** 151ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada * Gets the executor for the given filename. 152ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada */ 153ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada private static PrioritizedSerialExecutor getExecutor(final String filename) { 154ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada PrioritizedSerialExecutor executor = sFilenameExecutorMap.get(filename); 155ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada if (executor == null) { 156ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada synchronized(sFilenameExecutorMap) { 157ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada executor = new PrioritizedSerialExecutor(); 158ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada sFilenameExecutorMap.put(filename, executor); 159ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada } 160ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang } 161ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada return executor; 162ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang } 163ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 164c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi private static AbstractDictionaryWriter getDictionaryWriter(final Context context, 16587a72f50c23a4ef357ae623eabc2af16d02466aeKeisuke Kuroyanagi final String dictType, final boolean isDynamicPersonalizationDictionary) { 16687a72f50c23a4ef357ae623eabc2af16d02466aeKeisuke Kuroyanagi if (isDynamicPersonalizationDictionary) { 1672e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { 1682e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi return null; 1692e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi } else { 1702e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi return new DynamicPersonalizationDictionaryWriter(context, dictType); 1712e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi } 172c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi } else { 173c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi return new DictionaryWriter(context, dictType); 174c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi } 175c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi } 176c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi 177ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang /** 178ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang * Creates a new expandable binary dictionary. 179ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang * 180ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang * @param context The application context of the parent. 181ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang * @param filename The filename for this binary dictionary. Multiple dictionaries with the same 182ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang * filename is supported. 18305efe576f976f5fa280f8d523f2935c15cbb9bd1Jean Chalard * @param dictType the dictionary type, as a human-readable string 184c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi * @param isUpdatable whether to support dynamically updating the dictionary. Please note that 185c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi * dynamic dictionary has negative effects on memory space and computation time. 186ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang */ 187c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi public ExpandableBinaryDictionary(final Context context, final String filename, 188c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi final String dictType, final boolean isUpdatable) { 18905efe576f976f5fa280f8d523f2935c15cbb9bd1Jean Chalard super(dictType); 190ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang mFilename = filename; 191ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang mContext = context; 192c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi mIsUpdatable = isUpdatable; 193ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang mBinaryDictionary = null; 194e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi mFilenameDictionaryUpdateController = getDictionaryUpdateController(filename); 19587a72f50c23a4ef357ae623eabc2af16d02466aeKeisuke Kuroyanagi // Currently, only dynamic personalization dictionary is updatable. 196c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi mDictionaryWriter = getDictionaryWriter(context, dictType, isUpdatable); 197ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang } 198ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 199f6adff6227a15af105dbf39c57213a24bf16780bTom Ouyang protected static String getFilenameWithLocale(final String name, final String localeStr) { 2008aaae56cf6694ec75043be56f1c7812a343b24d5Yuichiro Hanada return name + "." + localeStr + DICT_FILE_EXTENSION; 201f6adff6227a15af105dbf39c57213a24bf16780bTom Ouyang } 202f6adff6227a15af105dbf39c57213a24bf16780bTom Ouyang 203ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang /** 204ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang * Closes and cleans up the binary dictionary. 205ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang */ 206ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang @Override 207ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang public void close() { 208ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada getExecutor(mFilename).execute(new Runnable() { 209ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada @Override 210ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada public void run() { 211ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada if (mBinaryDictionary!= null) { 212ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada mBinaryDictionary.close(); 213ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada mBinaryDictionary = null; 214ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada } 2152e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi if (mDictionaryWriter != null) { 2162e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi mDictionaryWriter.close(); 2172e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi } 218ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada } 219ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada }); 220c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi } 221c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi 222c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi protected void closeBinaryDictionary() { 223ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang // Ensure that no other threads are accessing the local binary dictionary. 224ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada getExecutor(mFilename).execute(new Runnable() { 225ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada @Override 226ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada public void run() { 227ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada if (mBinaryDictionary != null) { 228ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada mBinaryDictionary.close(); 229ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada mBinaryDictionary = null; 230ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada } 231ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang } 232ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada }); 233ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang } 234ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 2355ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi protected Map<String, String> getHeaderAttributeMap() { 2365ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi HashMap<String, String> attributeMap = new HashMap<String, String>(); 2375ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi attributeMap.put(FormatSpec.FileHeader.SUPPORTS_DYNAMIC_UPDATE_ATTRIBUTE, 2385ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi SUPPORTS_DYNAMIC_UPDATE); 2395ed30a7660048ef4bf78077e77554c97786eae2bKeisuke Kuroyanagi attributeMap.put(FormatSpec.FileHeader.DICTIONARY_ID_ATTRIBUTE, mFilename); 2405ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi return attributeMap; 2415ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi } 2425ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi 2436e04d6593239e841f5dac0d3f32d613967c11e22Keisuke Kuroyanagi protected void clear() { 244ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada getExecutor(mFilename).execute(new Runnable() { 245ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada @Override 246ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada public void run() { 2472e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE && mDictionaryWriter == null) { 2482e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi mBinaryDictionary.close(); 2492e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi final File file = new File(mContext.getFilesDir(), mFilename); 2505ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi BinaryDictionary.createEmptyDictFile(file.getAbsolutePath(), 2515ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi DICTIONARY_FORMAT_VERSION, getHeaderAttributeMap()); 25211f7cae094720c3ab47e6c18772b1fc44e9e5372Keisuke Kuroyanagi mBinaryDictionary = new BinaryDictionary( 25311f7cae094720c3ab47e6c18772b1fc44e9e5372Keisuke Kuroyanagi file.getAbsolutePath(), 0 /* offset */, file.length(), 25411f7cae094720c3ab47e6c18772b1fc44e9e5372Keisuke Kuroyanagi true /* useFullEditDistance */, null, mDictType, mIsUpdatable); 2552e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi } else { 2562e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi mDictionaryWriter.clear(); 2572e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi } 258ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada } 259ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada }); 2606e04d6593239e841f5dac0d3f32d613967c11e22Keisuke Kuroyanagi } 2616e04d6593239e841f5dac0d3f32d613967c11e22Keisuke Kuroyanagi 262ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang /** 263edd1992ed329a84f0e9ef7056fda99f78eeb92b4Keisuke Kuroynagi * Adds a word unigram to the dictionary. Used for loading a dictionary. 264f3204eebb19f0f8fae9d6d81e7e2b430f29829a0Jean Chalard * @param word The word to add. 265f3204eebb19f0f8fae9d6d81e7e2b430f29829a0Jean Chalard * @param shortcutTarget A shortcut target for this word, or null if none. 266f3204eebb19f0f8fae9d6d81e7e2b430f29829a0Jean Chalard * @param frequency The frequency for this unigram. 267f3204eebb19f0f8fae9d6d81e7e2b430f29829a0Jean Chalard * @param shortcutFreq The frequency of the shortcut (0~15, with 15 = whitelist). Ignored 268f3204eebb19f0f8fae9d6d81e7e2b430f29829a0Jean Chalard * if shortcutTarget is null. 269f3204eebb19f0f8fae9d6d81e7e2b430f29829a0Jean Chalard * @param isNotAWord true if this is not a word, i.e. shortcut only. 270ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang */ 271edd1992ed329a84f0e9ef7056fda99f78eeb92b4Keisuke Kuroynagi protected void addWord(final String word, final String shortcutTarget, 272f3204eebb19f0f8fae9d6d81e7e2b430f29829a0Jean Chalard final int frequency, final int shortcutFreq, final boolean isNotAWord) { 273f3204eebb19f0f8fae9d6d81e7e2b430f29829a0Jean Chalard mDictionaryWriter.addUnigramWord(word, shortcutTarget, frequency, shortcutFreq, isNotAWord); 274ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang } 275ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 276ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang /** 277c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi * Adds a word bigram in the dictionary. Used for loading a dictionary. 278ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang */ 279c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi protected void addBigram(final String prevWord, final String word, final int frequency, 280c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi final long lastModifiedTime) { 281c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi mDictionaryWriter.addBigramWords(prevWord, word, frequency, true /* isValid */, 282c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi lastModifiedTime); 283edd1992ed329a84f0e9ef7056fda99f78eeb92b4Keisuke Kuroynagi } 284edd1992ed329a84f0e9ef7056fda99f78eeb92b4Keisuke Kuroynagi 28511f7cae094720c3ab47e6c18772b1fc44e9e5372Keisuke Kuroyanagi /** 28611f7cae094720c3ab47e6c18772b1fc44e9e5372Keisuke Kuroyanagi * Check whether GC is needed and run GC if required. 28711f7cae094720c3ab47e6c18772b1fc44e9e5372Keisuke Kuroyanagi */ 288f36a97ab3abf7fb3766ed6ff553a2b6501d0908fKeisuke Kuroyanagi protected void runGCIfRequired(final boolean mindsBlockByGC) { 28903cb8f751a7f35e9159c724a2d25528b86287b57Keisuke Kuroyanagi if (!ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) return; 29011f7cae094720c3ab47e6c18772b1fc44e9e5372Keisuke Kuroyanagi getExecutor(mFilename).execute(new Runnable() { 29111f7cae094720c3ab47e6c18772b1fc44e9e5372Keisuke Kuroyanagi @Override 29211f7cae094720c3ab47e6c18772b1fc44e9e5372Keisuke Kuroyanagi public void run() { 29311f7cae094720c3ab47e6c18772b1fc44e9e5372Keisuke Kuroyanagi runGCIfRequiredInternalLocked(mindsBlockByGC); 29411f7cae094720c3ab47e6c18772b1fc44e9e5372Keisuke Kuroyanagi } 29511f7cae094720c3ab47e6c18772b1fc44e9e5372Keisuke Kuroyanagi }); 29611f7cae094720c3ab47e6c18772b1fc44e9e5372Keisuke Kuroyanagi } 29711f7cae094720c3ab47e6c18772b1fc44e9e5372Keisuke Kuroyanagi 29811f7cae094720c3ab47e6c18772b1fc44e9e5372Keisuke Kuroyanagi private void runGCIfRequiredInternalLocked(final boolean mindsBlockByGC) { 29911f7cae094720c3ab47e6c18772b1fc44e9e5372Keisuke Kuroyanagi if (!ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) return; 30011f7cae094720c3ab47e6c18772b1fc44e9e5372Keisuke Kuroyanagi // Calls to needsToRunGC() need to be serialized. 301f36a97ab3abf7fb3766ed6ff553a2b6501d0908fKeisuke Kuroyanagi if (mBinaryDictionary.needsToRunGC(mindsBlockByGC)) { 30203cb8f751a7f35e9159c724a2d25528b86287b57Keisuke Kuroyanagi if (setIsRegeneratingIfNotRegenerating()) { 30311f7cae094720c3ab47e6c18772b1fc44e9e5372Keisuke Kuroyanagi // Run GC after currently existing time sensitive operations. 30411f7cae094720c3ab47e6c18772b1fc44e9e5372Keisuke Kuroyanagi getExecutor(mFilename).executePrioritized(new Runnable() { 30503cb8f751a7f35e9159c724a2d25528b86287b57Keisuke Kuroyanagi @Override 30603cb8f751a7f35e9159c724a2d25528b86287b57Keisuke Kuroyanagi public void run() { 30703cb8f751a7f35e9159c724a2d25528b86287b57Keisuke Kuroyanagi try { 30803cb8f751a7f35e9159c724a2d25528b86287b57Keisuke Kuroyanagi mBinaryDictionary.flushWithGC(); 30903cb8f751a7f35e9159c724a2d25528b86287b57Keisuke Kuroyanagi } finally { 31003cb8f751a7f35e9159c724a2d25528b86287b57Keisuke Kuroyanagi mFilenameDictionaryUpdateController.mIsRegenerating.set(false); 31103cb8f751a7f35e9159c724a2d25528b86287b57Keisuke Kuroyanagi } 31203cb8f751a7f35e9159c724a2d25528b86287b57Keisuke Kuroyanagi } 31303cb8f751a7f35e9159c724a2d25528b86287b57Keisuke Kuroyanagi }); 31403cb8f751a7f35e9159c724a2d25528b86287b57Keisuke Kuroyanagi } 31503cb8f751a7f35e9159c724a2d25528b86287b57Keisuke Kuroyanagi } 31603cb8f751a7f35e9159c724a2d25528b86287b57Keisuke Kuroyanagi } 31703cb8f751a7f35e9159c724a2d25528b86287b57Keisuke Kuroyanagi 318edd1992ed329a84f0e9ef7056fda99f78eeb92b4Keisuke Kuroynagi /** 319c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi * Dynamically adds a word unigram to the dictionary. May overwrite an existing entry. 320edd1992ed329a84f0e9ef7056fda99f78eeb92b4Keisuke Kuroynagi */ 321edd1992ed329a84f0e9ef7056fda99f78eeb92b4Keisuke Kuroynagi protected void addWordDynamically(final String word, final String shortcutTarget, 322f3204eebb19f0f8fae9d6d81e7e2b430f29829a0Jean Chalard final int frequency, final int shortcutFreq, final boolean isNotAWord) { 323c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi if (!mIsUpdatable) { 324c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi Log.w(TAG, "addWordDynamically is called for non-updatable dictionary: " + mFilename); 325c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi return; 326c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi } 327ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada getExecutor(mFilename).execute(new Runnable() { 328ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada @Override 329ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada public void run() { 330e531c2241eb8d5a1462c43ce0deffaf6c769cc23Keisuke Kuroyanagi if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { 33111f7cae094720c3ab47e6c18772b1fc44e9e5372Keisuke Kuroyanagi runGCIfRequiredInternalLocked(true /* mindsBlockByGC */); 332e531c2241eb8d5a1462c43ce0deffaf6c769cc23Keisuke Kuroyanagi mBinaryDictionary.addUnigramWord(word, frequency); 3332e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi } else { 3342e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi // TODO: Remove. 335f3204eebb19f0f8fae9d6d81e7e2b430f29829a0Jean Chalard mDictionaryWriter.addUnigramWord(word, shortcutTarget, frequency, shortcutFreq, 336f3204eebb19f0f8fae9d6d81e7e2b430f29829a0Jean Chalard isNotAWord); 337e531c2241eb8d5a1462c43ce0deffaf6c769cc23Keisuke Kuroyanagi } 338c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi } 339ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada }); 340ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang } 341ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 342ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang /** 343c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi * Dynamically adds a word bigram in the dictionary. May overwrite an existing entry. 344ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang */ 345c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi protected void addBigramDynamically(final String word0, final String word1, 346c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi final int frequency, final boolean isValid) { 347c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi if (!mIsUpdatable) { 348c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi Log.w(TAG, "addBigramDynamically is called for non-updatable dictionary: " 349c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi + mFilename); 350c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi return; 351c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi } 352ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada getExecutor(mFilename).execute(new Runnable() { 353ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada @Override 354ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada public void run() { 355e531c2241eb8d5a1462c43ce0deffaf6c769cc23Keisuke Kuroyanagi if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { 35611f7cae094720c3ab47e6c18772b1fc44e9e5372Keisuke Kuroyanagi runGCIfRequiredInternalLocked(true /* mindsBlockByGC */); 357e531c2241eb8d5a1462c43ce0deffaf6c769cc23Keisuke Kuroyanagi mBinaryDictionary.addBigramWords(word0, word1, frequency); 3582e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi } else { 3592e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi // TODO: Remove. 3602e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi mDictionaryWriter.addBigramWords(word0, word1, frequency, isValid, 3612e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi 0 /* lastTouchedTime */); 362e531c2241eb8d5a1462c43ce0deffaf6c769cc23Keisuke Kuroyanagi } 363c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi } 364ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada }); 365c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi } 366c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi 367c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi /** 368c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi * Dynamically remove a word bigram in the dictionary. 369c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi */ 370c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi protected void removeBigramDynamically(final String word0, final String word1) { 371c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi if (!mIsUpdatable) { 372c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi Log.w(TAG, "removeBigramDynamically is called for non-updatable dictionary: " 373c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi + mFilename); 374c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi return; 375c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi } 376ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada getExecutor(mFilename).execute(new Runnable() { 377ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada @Override 378ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada public void run() { 379e531c2241eb8d5a1462c43ce0deffaf6c769cc23Keisuke Kuroyanagi if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { 38011f7cae094720c3ab47e6c18772b1fc44e9e5372Keisuke Kuroyanagi runGCIfRequiredInternalLocked(true /* mindsBlockByGC */); 381e531c2241eb8d5a1462c43ce0deffaf6c769cc23Keisuke Kuroyanagi mBinaryDictionary.removeBigramWords(word0, word1); 3822e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi } else { 3832e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi // TODO: Remove. 3842e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi mDictionaryWriter.removeBigramWords(word0, word1); 385e531c2241eb8d5a1462c43ce0deffaf6c769cc23Keisuke Kuroyanagi } 386c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi } 387ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada }); 388ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang } 389ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 390ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang @Override 39140f66795a21b857276fd0601fd9bb54e58c947eeKeisuke Kuroyanagi public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer, 3922dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard final String prevWord, final ProximityInfo proximityInfo, 39340f66795a21b857276fd0601fd9bb54e58c947eeKeisuke Kuroyanagi final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, 39440f66795a21b857276fd0601fd9bb54e58c947eeKeisuke Kuroyanagi final int sessionId) { 395ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada reloadDictionaryIfRequired(); 396e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi if (isRegenerating()) { 397e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi return null; 398e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi } 399ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList(); 400ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada final AsyncResultHolder<ArrayList<SuggestedWordInfo>> holder = 401ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada new AsyncResultHolder<ArrayList<SuggestedWordInfo>>(); 402ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada getExecutor(mFilename).executePrioritized(new Runnable() { 403ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada @Override 404ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada public void run() { 4052e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { 4062e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi if (mBinaryDictionary == null) { 4072e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi holder.set(null); 4082e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi return; 4092e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi } 410edd1992ed329a84f0e9ef7056fda99f78eeb92b4Keisuke Kuroynagi final ArrayList<SuggestedWordInfo> binarySuggestion = 41140f66795a21b857276fd0601fd9bb54e58c947eeKeisuke Kuroyanagi mBinaryDictionary.getSuggestionsWithSessionId(composer, prevWord, 41240f66795a21b857276fd0601fd9bb54e58c947eeKeisuke Kuroyanagi proximityInfo, blockOffensiveWords, additionalFeaturesOptions, 41340f66795a21b857276fd0601fd9bb54e58c947eeKeisuke Kuroyanagi sessionId); 4142e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi holder.set(binarySuggestion); 4152e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi } else { 4162e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi final ArrayList<SuggestedWordInfo> inMemDictSuggestion = 4172e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi composer.isBatchMode() ? null : 4182e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi mDictionaryWriter.getSuggestionsWithSessionId(composer, 4192e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi prevWord, proximityInfo, blockOffensiveWords, 4202e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi additionalFeaturesOptions, sessionId); 4212e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi // TODO: Remove checking mIsUpdatable and use native suggestion. 4222e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi if (mBinaryDictionary != null && !mIsUpdatable) { 4232e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi final ArrayList<SuggestedWordInfo> binarySuggestion = 4242e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi mBinaryDictionary.getSuggestionsWithSessionId(composer, prevWord, 4252e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi proximityInfo, blockOffensiveWords, 4262e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi additionalFeaturesOptions, sessionId); 4272e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi if (inMemDictSuggestion == null) { 4282e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi holder.set(binarySuggestion); 4292e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi } else if (binarySuggestion == null) { 4302e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi holder.set(inMemDictSuggestion); 4312e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi } else { 4322e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi binarySuggestion.addAll(inMemDictSuggestion); 4332e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi holder.set(binarySuggestion); 4342e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi } 435edd1992ed329a84f0e9ef7056fda99f78eeb92b4Keisuke Kuroynagi } else { 4362e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi holder.set(inMemDictSuggestion); 437edd1992ed329a84f0e9ef7056fda99f78eeb92b4Keisuke Kuroynagi } 438b30d2185f24e3d531f5d46249e7c97391705e469Jean Chalard } 439b30d2185f24e3d531f5d46249e7c97391705e469Jean Chalard } 440ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada }); 441ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada return holder.get(null, TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS); 442b30d2185f24e3d531f5d46249e7c97391705e469Jean Chalard } 443b30d2185f24e3d531f5d46249e7c97391705e469Jean Chalard 444ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang @Override 44540f66795a21b857276fd0601fd9bb54e58c947eeKeisuke Kuroyanagi public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, 44640f66795a21b857276fd0601fd9bb54e58c947eeKeisuke Kuroyanagi final String prevWord, final ProximityInfo proximityInfo, 44740f66795a21b857276fd0601fd9bb54e58c947eeKeisuke Kuroyanagi final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) { 44840f66795a21b857276fd0601fd9bb54e58c947eeKeisuke Kuroyanagi return getSuggestionsWithSessionId(composer, prevWord, proximityInfo, blockOffensiveWords, 44940f66795a21b857276fd0601fd9bb54e58c947eeKeisuke Kuroyanagi additionalFeaturesOptions, 0 /* sessionId */); 45040f66795a21b857276fd0601fd9bb54e58c947eeKeisuke Kuroyanagi } 45140f66795a21b857276fd0601fd9bb54e58c947eeKeisuke Kuroyanagi 45240f66795a21b857276fd0601fd9bb54e58c947eeKeisuke Kuroyanagi @Override 453bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka public boolean isValidWord(final String word) { 454ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada reloadDictionaryIfRequired(); 455ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang return isValidWordInner(word); 456ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang } 457ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 458bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka protected boolean isValidWordInner(final String word) { 459e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi if (isRegenerating()) { 460e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi return false; 461e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi } 462ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<Boolean>(); 463ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada getExecutor(mFilename).executePrioritized(new Runnable() { 464ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada @Override 465ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada public void run() { 466ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada holder.set(isValidWordLocked(word)); 467ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang } 468ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada }); 469ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada return holder.get(false, TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS); 470ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang } 471ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 472bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka protected boolean isValidWordLocked(final String word) { 4734d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (mBinaryDictionary == null) return false; 4744d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang return mBinaryDictionary.isValidWord(word); 4754d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 4764d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang 477bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka protected boolean isValidBigramLocked(final String word1, final String word2) { 4784d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang if (mBinaryDictionary == null) return false; 4794d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang return mBinaryDictionary.isValidBigram(word1, word2); 4804d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 4814d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang 482ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang /** 483ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang * Load the current binary dictionary from internal storage in a background thread. If no binary 484ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang * dictionary exists, this method will generate one. 485ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang */ 486ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang protected void loadDictionary() { 487e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi mPerInstanceDictionaryUpdateController.mLastUpdateRequestTime = SystemClock.uptimeMillis(); 488ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada reloadDictionaryIfRequired(); 489ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang } 490ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 491ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang /** 492ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang * Loads the current binary dictionary from internal storage. Assumes the dictionary file 493ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang * exists. 494ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang */ 495edd1992ed329a84f0e9ef7056fda99f78eeb92b4Keisuke Kuroynagi private void loadBinaryDictionary() { 496ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang if (DEBUG) { 4971ed017ef0e271ed3f3c212def6cc6ba95b14e780Tom Ouyang Log.d(TAG, "Loading binary dictionary: " + mFilename + " request=" 498e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi + mFilenameDictionaryUpdateController.mLastUpdateRequestTime + " update=" 499e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi + mFilenameDictionaryUpdateController.mLastUpdateTime); 500ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang } 501ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 502ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang final File file = new File(mContext.getFilesDir(), mFilename); 503ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang final String filename = file.getAbsolutePath(); 504ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang final long length = file.length(); 505ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 506ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang // Build the new binary dictionary 50711f7cae094720c3ab47e6c18772b1fc44e9e5372Keisuke Kuroyanagi final BinaryDictionary newBinaryDictionary = new BinaryDictionary(filename, 0 /* offset */, 50811f7cae094720c3ab47e6c18772b1fc44e9e5372Keisuke Kuroyanagi length, true /* useFullEditDistance */, null, mDictType, mIsUpdatable); 509ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 5102e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi // Ensure all threads accessing the current dictionary have finished before 5112e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi // swapping in the new one. 5122e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi // TODO: Ensure multi-thread assignment of mBinaryDictionary. 513ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada final BinaryDictionary oldBinaryDictionary = mBinaryDictionary; 514ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada getExecutor(mFilename).executePrioritized(new Runnable() { 515ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada @Override 516ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada public void run() { 5170b1c08bf5aad0c6775acb1acb7048191854851abKeisuke Kuroynagi mBinaryDictionary = newBinaryDictionary; 518ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada if (oldBinaryDictionary != null) { 519ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada oldBinaryDictionary.close(); 520ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada } 5210b1c08bf5aad0c6775acb1acb7048191854851abKeisuke Kuroynagi } 522ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada }); 523ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang } 524ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 525ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang /** 526edd1992ed329a84f0e9ef7056fda99f78eeb92b4Keisuke Kuroynagi * Abstract method for checking if it is required to reload the dictionary before writing 527edd1992ed329a84f0e9ef7056fda99f78eeb92b4Keisuke Kuroynagi * a binary dictionary. 528edd1992ed329a84f0e9ef7056fda99f78eeb92b4Keisuke Kuroynagi */ 529edd1992ed329a84f0e9ef7056fda99f78eeb92b4Keisuke Kuroynagi abstract protected boolean needsToReloadBeforeWriting(); 530edd1992ed329a84f0e9ef7056fda99f78eeb92b4Keisuke Kuroynagi 531edd1992ed329a84f0e9ef7056fda99f78eeb92b4Keisuke Kuroynagi /** 532c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi * Writes a new binary dictionary based on the contents of the fusion dictionary. 533ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang */ 534c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi private void writeBinaryDictionary() { 535ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang if (DEBUG) { 5361ed017ef0e271ed3f3c212def6cc6ba95b14e780Tom Ouyang Log.d(TAG, "Generating binary dictionary: " + mFilename + " request=" 537e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi + mFilenameDictionaryUpdateController.mLastUpdateRequestTime + " update=" 538e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi + mFilenameDictionaryUpdateController.mLastUpdateTime); 539ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang } 540edd1992ed329a84f0e9ef7056fda99f78eeb92b4Keisuke Kuroynagi if (needsToReloadBeforeWriting()) { 541edd1992ed329a84f0e9ef7056fda99f78eeb92b4Keisuke Kuroynagi mDictionaryWriter.clear(); 542edd1992ed329a84f0e9ef7056fda99f78eeb92b4Keisuke Kuroynagi loadDictionaryAsync(); 5435ed30a7660048ef4bf78077e77554c97786eae2bKeisuke Kuroyanagi mDictionaryWriter.write(mFilename, getHeaderAttributeMap()); 5442e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi } else { 5452e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { 5462e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi if (mBinaryDictionary == null || !mBinaryDictionary.isValidDictionary()) { 5472e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi final File file = new File(mContext.getFilesDir(), mFilename); 5485ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi BinaryDictionary.createEmptyDictFile(file.getAbsolutePath(), 5495ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi DICTIONARY_FORMAT_VERSION, getHeaderAttributeMap()); 5502e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi } else { 551c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi if (mBinaryDictionary.needsToRunGC(false /* mindsBlockByGC */)) { 5522e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi mBinaryDictionary.flushWithGC(); 5532e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi } else { 5542e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi mBinaryDictionary.flush(); 5552e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi } 5562e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi } 5572e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi } else { 5585ed30a7660048ef4bf78077e77554c97786eae2bKeisuke Kuroyanagi mDictionaryWriter.write(mFilename, getHeaderAttributeMap()); 5592e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi } 560ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang } 561ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang } 562ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 563ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang /** 5644d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang * Marks that the dictionary is out of date and requires a reload. 5654d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang * 5664d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang * @param requiresRebuild Indicates that the source dictionary content has changed and a rebuild 5674d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang * of the binary file is required. If not true, the next reload process will only read 5684d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang * the current binary dictionary from file. 569ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang */ 5704d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang protected void setRequiresReload(final boolean requiresRebuild) { 5714d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang final long time = SystemClock.uptimeMillis(); 572e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi mPerInstanceDictionaryUpdateController.mLastUpdateRequestTime = time; 573e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi mFilenameDictionaryUpdateController.mLastUpdateRequestTime = time; 574ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang if (DEBUG) { 5751ed017ef0e271ed3f3c212def6cc6ba95b14e780Tom Ouyang Log.d(TAG, "Reload request: " + mFilename + ": request=" + time + " update=" 576e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi + mFilenameDictionaryUpdateController.mLastUpdateTime); 577ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang } 578ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang } 579ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 580ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang /** 5811ed017ef0e271ed3f3c212def6cc6ba95b14e780Tom Ouyang * Reloads the dictionary if required. 582ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang */ 583ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada public final void reloadDictionaryIfRequired() { 5841ed017ef0e271ed3f3c212def6cc6ba95b14e780Tom Ouyang if (!isReloadRequired()) return; 585e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi if (setIsRegeneratingIfNotRegenerating()) { 586e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi reloadDictionary(); 587e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi } 5881ed017ef0e271ed3f3c212def6cc6ba95b14e780Tom Ouyang } 5891ed017ef0e271ed3f3c212def6cc6ba95b14e780Tom Ouyang 5901ed017ef0e271ed3f3c212def6cc6ba95b14e780Tom Ouyang /** 5911ed017ef0e271ed3f3c212def6cc6ba95b14e780Tom Ouyang * Returns whether a dictionary reload is required. 5921ed017ef0e271ed3f3c212def6cc6ba95b14e780Tom Ouyang */ 5931ed017ef0e271ed3f3c212def6cc6ba95b14e780Tom Ouyang private boolean isReloadRequired() { 594e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi return mBinaryDictionary == null || mPerInstanceDictionaryUpdateController.isOutOfDate(); 595e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi } 596e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi 597e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi private boolean isRegenerating() { 598e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi return mFilenameDictionaryUpdateController.mIsRegenerating.get(); 599e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi } 600e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi 601e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi // Returns whether the dictionary can be regenerated. 602e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi private boolean setIsRegeneratingIfNotRegenerating() { 603e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi return mFilenameDictionaryUpdateController.mIsRegenerating.compareAndSet( 604e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi false /* expect */ , true /* update */); 6051ed017ef0e271ed3f3c212def6cc6ba95b14e780Tom Ouyang } 606ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 6071ed017ef0e271ed3f3c212def6cc6ba95b14e780Tom Ouyang /** 6081ed017ef0e271ed3f3c212def6cc6ba95b14e780Tom Ouyang * Reloads the dictionary. Access is controlled on a per dictionary file basis and supports 6091ed017ef0e271ed3f3c212def6cc6ba95b14e780Tom Ouyang * concurrent calls from multiple instances that share the same dictionary file. 6101ed017ef0e271ed3f3c212def6cc6ba95b14e780Tom Ouyang */ 611ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada private final void reloadDictionary() { 612ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang // Ensure that only one thread attempts to read or write to the shared binary dictionary 613ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang // file at the same time. 614ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada getExecutor(mFilename).execute(new Runnable() { 615ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada @Override 616ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada public void run() { 617e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi try { 618e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi final long time = SystemClock.uptimeMillis(); 619e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi final boolean dictionaryFileExists = dictionaryFileExists(); 620e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi if (mFilenameDictionaryUpdateController.isOutOfDate() 621e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi || !dictionaryFileExists) { 622e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi // If the shared dictionary file does not exist or is out of date, the 623e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi // first instance that acquires the lock will generate a new one. 624e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi if (hasContentChanged() || !dictionaryFileExists) { 625e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi // If the source content has changed or the dictionary does not exist, 626e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi // rebuild the binary dictionary. Empty dictionaries are supported (in 627e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi // the case where loadDictionaryAsync() adds nothing) in order to 628e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi // provide a uniform framework. 629e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi mFilenameDictionaryUpdateController.mLastUpdateTime = time; 630e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi writeBinaryDictionary(); 631e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi loadBinaryDictionary(); 632e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi } else { 633e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi // If not, the reload request was unnecessary so revert 634e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi // LastUpdateRequestTime to LastUpdateTime. 635e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi mFilenameDictionaryUpdateController.mLastUpdateRequestTime = 636e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi mFilenameDictionaryUpdateController.mLastUpdateTime; 637e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi } 638e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi } else if (mBinaryDictionary == null || 639e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi mPerInstanceDictionaryUpdateController.mLastUpdateTime 640e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi < mFilenameDictionaryUpdateController.mLastUpdateTime) { 641e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi // Otherwise, if the local dictionary is older than the shared dictionary, 642e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi // load the shared dictionary. 643e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi loadBinaryDictionary(); 644e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi } 645e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi if (mBinaryDictionary != null && !mBinaryDictionary.isValidDictionary()) { 646e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi // Binary dictionary is not valid. Regenerate the dictionary file. 647e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi mFilenameDictionaryUpdateController.mLastUpdateTime = time; 648c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi writeBinaryDictionary(); 649c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi loadBinaryDictionary(); 650c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi } 651e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi mPerInstanceDictionaryUpdateController.mLastUpdateTime = time; 652e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi } finally { 653e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi mFilenameDictionaryUpdateController.mIsRegenerating.set(false); 6544d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 655a62b5b22eff2c1842fe1e0a4ea949e1e004de40bKeisuke Kuroynagi } 656ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada }); 657ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang } 658ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 6594d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang // TODO: cache the file's existence so that we avoid doing a disk access each time. 660ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang private boolean dictionaryFileExists() { 661ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang final File file = new File(mContext.getFilesDir(), mFilename); 662ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang return file.exists(); 663ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang } 664ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 665ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang /** 6666e04d6593239e841f5dac0d3f32d613967c11e22Keisuke Kuroyanagi * Load the dictionary to memory. 6676e04d6593239e841f5dac0d3f32d613967c11e22Keisuke Kuroyanagi */ 6686e04d6593239e841f5dac0d3f32d613967c11e22Keisuke Kuroyanagi protected void asyncLoadDictionaryToMemory() { 669ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada getExecutor(mFilename).executePrioritized(new Runnable() { 670ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada @Override 671ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada public void run() { 6722e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi if (!ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { 6732e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi loadDictionaryAsync(); 6742e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi } 6756e04d6593239e841f5dac0d3f32d613967c11e22Keisuke Kuroyanagi } 676ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada }); 6776e04d6593239e841f5dac0d3f32d613967c11e22Keisuke Kuroyanagi } 6786e04d6593239e841f5dac0d3f32d613967c11e22Keisuke Kuroyanagi 6796e04d6593239e841f5dac0d3f32d613967c11e22Keisuke Kuroyanagi /** 6806e04d6593239e841f5dac0d3f32d613967c11e22Keisuke Kuroyanagi * Generate binary dictionary using DictionaryWriter. 6816e04d6593239e841f5dac0d3f32d613967c11e22Keisuke Kuroyanagi */ 6822e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi protected void asyncFlashAllBinaryDictionary() { 683ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada final Runnable newTask = new Runnable() { 684ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada @Override 685ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada public void run() { 686ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada writeBinaryDictionary(); 6876e04d6593239e841f5dac0d3f32d613967c11e22Keisuke Kuroyanagi } 688ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada }; 689ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada final Runnable oldTask = mUnfinishedFlushingTask.getAndSet(newTask); 690ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada getExecutor(mFilename).replaceAndExecute(oldTask, newTask); 6916e04d6593239e841f5dac0d3f32d613967c11e22Keisuke Kuroyanagi } 6926e04d6593239e841f5dac0d3f32d613967c11e22Keisuke Kuroyanagi 6936e04d6593239e841f5dac0d3f32d613967c11e22Keisuke Kuroyanagi /** 694e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi * For tracking whether the dictionary is out of date and the dictionary is regenerating. 695ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada * Can be shared across multiple dictionary instances that access the same filename. 696ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang */ 697e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi private static class DictionaryUpdateController { 698e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi public volatile long mLastUpdateTime = 0; 699e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi public volatile long mLastUpdateRequestTime = 0; 700e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi public volatile AtomicBoolean mIsRegenerating = new AtomicBoolean(); 701ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang 702e74d4a184bbd06ddb607f81147ed827b9dd1ba17Keisuke Kuroyanagi public boolean isOutOfDate() { 703ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang return (mLastUpdateRequestTime > mLastUpdateTime); 704ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang } 705ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang } 706c8db6f21e936b819a0b818f44eae0d2bc44433c9Keisuke Kuroyanagi 707e5a35711b854aedeeea2f45105b941b9deee49bcSatoshi Kataoka // TODO: Implement native binary methods once the dynamic dictionary implementation is done. 708e5a35711b854aedeeea2f45105b941b9deee49bcSatoshi Kataoka @UsedForTesting 709e5a35711b854aedeeea2f45105b941b9deee49bcSatoshi Kataoka public boolean isInDictionaryForTests(final String word) { 710ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<Boolean>(); 711ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada getExecutor(mFilename).executePrioritized(new Runnable() { 712ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada @Override 713ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada public void run() { 714ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada if (mDictType == Dictionary.TYPE_USER_HISTORY) { 7152e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) { 7162e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi holder.set(mBinaryDictionary.isValidWord(word)); 7172e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi } else { 7182e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi holder.set(((DynamicPersonalizationDictionaryWriter) mDictionaryWriter) 719a328f538c34ad2dafdfa53642085cb1072224d80Yuichiro Hanada .isInBigramListForTests(word)); 7202e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi } 721ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada } 722e5a35711b854aedeeea2f45105b941b9deee49bcSatoshi Kataoka } 723ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada }); 724ef073f402407b19f5be90ddf68beb874945e82beYuichiro Hanada return holder.get(false, TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS); 725e5a35711b854aedeeea2f45105b941b9deee49bcSatoshi Kataoka } 726a099a3e341d8de0512c8bb8f4dbe352456f2a4a4Yuichiro Hanada 727a099a3e341d8de0512c8bb8f4dbe352456f2a4a4Yuichiro Hanada @UsedForTesting 728a099a3e341d8de0512c8bb8f4dbe352456f2a4a4Yuichiro Hanada public void shutdownExecutorForTests() { 729a099a3e341d8de0512c8bb8f4dbe352456f2a4a4Yuichiro Hanada getExecutor(mFilename).shutdown(); 730a099a3e341d8de0512c8bb8f4dbe352456f2a4a4Yuichiro Hanada } 731a099a3e341d8de0512c8bb8f4dbe352456f2a4a4Yuichiro Hanada 732a099a3e341d8de0512c8bb8f4dbe352456f2a4a4Yuichiro Hanada @UsedForTesting 733a099a3e341d8de0512c8bb8f4dbe352456f2a4a4Yuichiro Hanada public boolean isTerminatedForTests() { 734a099a3e341d8de0512c8bb8f4dbe352456f2a4a4Yuichiro Hanada return getExecutor(mFilename).isTerminated(); 735a099a3e341d8de0512c8bb8f4dbe352456f2a4a4Yuichiro Hanada } 736ecd2ac93bc321fdd932930c43851a92859d4775dTom Ouyang} 737