diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java')
-rw-r--r-- | java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java | 267 |
1 files changed, 152 insertions, 115 deletions
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java index d24f80a45..b47eaa9bb 100644 --- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java +++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java @@ -20,19 +20,20 @@ import android.content.Context; import android.util.Log; import com.android.inputmethod.annotations.UsedForTesting; -import com.android.inputmethod.keyboard.ProximityInfo; +import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; +import com.android.inputmethod.latin.common.ComposedData; +import com.android.inputmethod.latin.common.Constants; import com.android.inputmethod.latin.makedict.DictionaryHeader; import com.android.inputmethod.latin.makedict.FormatSpec; import com.android.inputmethod.latin.makedict.UnsupportedFormatException; import com.android.inputmethod.latin.makedict.WordProperty; -import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.settings.SettingsValuesForSuggestion; import com.android.inputmethod.latin.utils.AsyncResultHolder; import com.android.inputmethod.latin.utils.CombinedFormatUtils; import com.android.inputmethod.latin.utils.DistracterFilter; import com.android.inputmethod.latin.utils.ExecutorUtils; import com.android.inputmethod.latin.utils.FileUtils; -import com.android.inputmethod.latin.utils.LanguageModelParam; +import com.android.inputmethod.latin.utils.WordInputEventForPersonalization; import java.io.File; import java.util.ArrayList; @@ -47,11 +48,16 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.annotation.Nonnull; +import javax.annotation.Nullable; /** * Abstract base class for an expandable dictionary that can be created and updated dynamically * during runtime. When updated it automatically generates a new binary dictionary to handle future * queries in native code. This binary dictionary is written to internal storage. + * + * A class that extends this abstract class must have a static factory method named + * getDictionary(Context context, Locale locale, File dictFile, String dictNamePrefix) + * @see DictionaryFacilitator#getSubDict(String,Context,Locale,File,String) */ abstract public class ExpandableBinaryDictionary extends Dictionary { private static final boolean DEBUG = false; @@ -64,9 +70,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { private static final int TIMEOUT_FOR_READ_OPS_IN_MILLISECONDS = 100; - private static final int DEFAULT_MAX_UNIGRAM_COUNT = 10000; - private static final int DEFAULT_MAX_BIGRAM_COUNT = 10000; - /** * The maximum length of a word in this dictionary. */ @@ -110,14 +113,15 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { */ protected abstract void loadInitialContentsLocked(); - private boolean matchesExpectedBinaryDictFormatVersionForThisType(final int formatVersion) { + static boolean matchesExpectedBinaryDictFormatVersionForThisType(final int formatVersion) { return formatVersion == FormatSpec.VERSION4; } - private boolean needsToMigrateDictionary(final int formatVersion) { + private static boolean needsToMigrateDictionary(final int formatVersion) { // When we bump up the dictionary format version, the old version should be added to here // for supporting migration. Note that native code has to support reading such formats. - return formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING; + return formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING + || formatVersion == FormatSpec.VERSION402; } public boolean isValidDictionaryLocked() { @@ -162,7 +166,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { asyncExecuteTaskWithLock(mLock.writeLock(), mDictName /* executorName */, task); } - private void asyncExecuteTaskWithLock(final Lock lock, final String executorName, + private static void asyncExecuteTaskWithLock(final Lock lock, final String executorName, final Runnable task) { asyncPreCheckAndExecuteTaskWithLock(lock, null /* preCheckTask */, executorName, task); } @@ -175,8 +179,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } // Execute task with lock when the result of preCheckTask is true or preCheckTask is null. - private void asyncPreCheckAndExecuteTaskWithLock(final Lock lock, + private static void asyncPreCheckAndExecuteTaskWithLock(final Lock lock, final Callable<Boolean> preCheckTask, final String executorName, final Runnable task) { + final String tag = TAG; ExecutorUtils.getExecutor(executorName).execute(new Runnable() { @Override public void run() { @@ -186,7 +191,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { return; } } catch (final Exception e) { - Log.e(TAG, "The pre check task throws an exception.", e); + Log.e(tag, "The pre check task throws an exception.", e); return; } } @@ -200,6 +205,18 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { }); } + @Nullable + BinaryDictionary getBinaryDictionary() { + return mBinaryDictionary; + } + + void closeBinaryDictionary() { + if (mBinaryDictionary != null) { + mBinaryDictionary.close(); + mBinaryDictionary = null; + } + } + /** * Closes and cleans up the binary dictionary. */ @@ -208,10 +225,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { asyncExecuteTaskWithWriteLock(new Runnable() { @Override public void run() { - if (mBinaryDictionary != null) { - mBinaryDictionary.close(); - mBinaryDictionary = null; - } + closeBinaryDictionary(); } }); } @@ -225,10 +239,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { attributeMap.put(DictionaryHeader.DICTIONARY_LOCALE_KEY, mLocale.toString()); attributeMap.put(DictionaryHeader.DICTIONARY_VERSION_KEY, String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()))); - attributeMap.put(DictionaryHeader.MAX_UNIGRAM_COUNT_KEY, - String.valueOf(DEFAULT_MAX_UNIGRAM_COUNT)); - attributeMap.put(DictionaryHeader.MAX_BIGRAM_COUNT_KEY, - String.valueOf(DEFAULT_MAX_BIGRAM_COUNT)); return attributeMap; } @@ -241,14 +251,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { }); } - private void removeBinaryDictionaryLocked() { - if (mBinaryDictionary != null) { - mBinaryDictionary.close(); - } + void removeBinaryDictionaryLocked() { + closeBinaryDictionary(); if (mDictFile.exists() && !FileUtils.deleteRecursively(mDictFile)) { Log.e(TAG, "Can't remove a file: " + mDictFile.getName()); } - mBinaryDictionary = null; } private void openBinaryDictionaryLocked() { @@ -257,7 +264,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { true /* useFullEditDistance */, mLocale, mDictType, true /* isUpdatable */); } - private void createOnMemoryBinaryDictionaryLocked() { + void createOnMemoryBinaryDictionaryLocked() { mBinaryDictionary = new BinaryDictionary( mDictFile.getAbsolutePath(), true /* useFullEditDistance */, mLocale, mDictType, DICTIONARY_FORMAT_VERSION, getHeaderAttributeMap()); @@ -280,7 +287,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { asyncExecuteTaskWithWriteLock(new Runnable() { @Override public void run() { - if (mBinaryDictionary == null) { + if (getBinaryDictionary() == null) { return; } runGCIfRequiredLocked(mindsBlockByGC); @@ -298,24 +305,24 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { @Nonnull final Runnable updateTask, @Nonnull final String word, @Nonnull final DistracterFilter distracterFilter) { reloadDictionaryIfRequired(); - asyncPreCheckAndExecuteTaskWithWriteLock( - new Callable<Boolean>() { - @Override - public Boolean call() throws Exception { - return !distracterFilter.isDistracterToWordsInDictionaries( - NgramContext.EMPTY_PREV_WORDS_INFO, word, mLocale); - } - }, - new Runnable() { - @Override - public void run() { - if (mBinaryDictionary == null) { - return; - } - runGCIfRequiredLocked(true /* mindsBlockByGC */); - updateTask.run(); - } - }); + final Callable<Boolean> preCheckTask = new Callable<Boolean>() { + @Override + public Boolean call() throws Exception { + return !distracterFilter.isDistracterToWordsInDictionaries( + NgramContext.EMPTY_PREV_WORDS_INFO, word, mLocale); + } + }; + final Runnable task = new Runnable() { + @Override + public void run() { + if (getBinaryDictionary() == null) { + return; + } + runGCIfRequiredLocked(true /* mindsBlockByGC */); + updateTask.run(); + } + }; + asyncPreCheckAndExecuteTaskWithWriteLock(preCheckTask, task); } /** @@ -323,22 +330,22 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { */ public void addUnigramEntryWithCheckingDistracter(final String word, final int frequency, final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord, - final boolean isBlacklisted, final int timestamp, + final boolean isPossiblyOffensive, final int timestamp, @Nonnull final DistracterFilter distracterFilter) { updateDictionaryWithWriteLockIfWordIsNotADistracter(new Runnable() { @Override public void run() { addUnigramLocked(word, frequency, shortcutTarget, shortcutFreq, - isNotAWord, isBlacklisted, timestamp); + isNotAWord, isPossiblyOffensive, timestamp); } }, word, distracterFilter); } protected void addUnigramLocked(final String word, final int frequency, final String shortcutTarget, final int shortcutFreq, final boolean isNotAWord, - final boolean isBlacklisted, final int timestamp) { + final boolean isPossiblyOffensive, final int timestamp) { if (!mBinaryDictionary.addUnigramEntry(word, frequency, shortcutTarget, shortcutFreq, - false /* isBeginningOfSentence */, isNotAWord, isBlacklisted, timestamp)) { + false /* isBeginningOfSentence */, isNotAWord, isPossiblyOffensive, timestamp)) { Log.e(TAG, "Cannot add unigram entry. word: " + word); } } @@ -351,11 +358,12 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { asyncExecuteTaskWithWriteLock(new Runnable() { @Override public void run() { - if (mBinaryDictionary == null) { + final BinaryDictionary binaryDictionary = getBinaryDictionary(); + if (binaryDictionary == null) { return; } runGCIfRequiredLocked(true /* mindsBlockByGC */); - if (!mBinaryDictionary.removeUnigramEntry(word)) { + if (!binaryDictionary.removeUnigramEntry(word)) { if (DEBUG) { Log.i(TAG, "Cannot remove unigram entry: " + word); } @@ -373,7 +381,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { asyncExecuteTaskWithWriteLock(new Runnable() { @Override public void run() { - if (mBinaryDictionary == null) { + if (getBinaryDictionary() == null) { return; } runGCIfRequiredLocked(true /* mindsBlockByGC */); @@ -402,11 +410,12 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { asyncExecuteTaskWithWriteLock(new Runnable() { @Override public void run() { - if (mBinaryDictionary == null) { + final BinaryDictionary binaryDictionary = getBinaryDictionary(); + if (binaryDictionary == null) { return; } runGCIfRequiredLocked(true /* mindsBlockByGC */); - if (!mBinaryDictionary.removeNgramEntry(ngramContext, word)) { + if (!binaryDictionary.removeNgramEntry(ngramContext, word)) { if (DEBUG) { Log.i(TAG, "Cannot remove n-gram entry."); Log.i(TAG, " NgramContext: " + ngramContext + ", word: " + word); @@ -425,7 +434,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { updateDictionaryWithWriteLockIfWordIsNotADistracter(new Runnable() { @Override public void run() { - if (!mBinaryDictionary.updateEntriesForWordWithNgramContext(ngramContext, word, + final BinaryDictionary binaryDictionary = getBinaryDictionary(); + if (binaryDictionary == null) { + return; + } + if (!binaryDictionary.updateEntriesForWordWithNgramContext(ngramContext, word, isValidWord, count, timestamp)) { if (DEBUG) { Log.e(TAG, "Cannot update counter. word: " + word @@ -436,27 +449,28 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { }, word, distracterFilter); } - public interface AddMultipleDictionaryEntriesCallback { + public interface UpdateEntriesForInputEventsCallback { public void onFinished(); } /** - * Dynamically add multiple entries to the dictionary. + * Dynamically update entries according to input events. */ - public void addMultipleDictionaryEntriesDynamically( - @Nonnull final ArrayList<LanguageModelParam> languageModelParams, - final AddMultipleDictionaryEntriesCallback callback) { + public void updateEntriesForInputEvents( + @Nonnull final ArrayList<WordInputEventForPersonalization> inputEvents, + final UpdateEntriesForInputEventsCallback callback) { reloadDictionaryIfRequired(); asyncExecuteTaskWithWriteLock(new Runnable() { @Override public void run() { try { - if (mBinaryDictionary == null) { + final BinaryDictionary binaryDictionary = getBinaryDictionary(); + if (binaryDictionary == null) { return; } - mBinaryDictionary.addMultipleDictionaryEntries( - languageModelParams.toArray( - new LanguageModelParam[languageModelParams.size()])); + binaryDictionary.updateEntriesForInputEvents( + inputEvents.toArray( + new WordInputEventForPersonalization[inputEvents.size()])); } finally { if (callback != null) { callback.onFinished(); @@ -467,8 +481,8 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { } @Override - public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, - final NgramContext ngramContext, final ProximityInfo proximityInfo, + public ArrayList<SuggestedWordInfo> getSuggestions(final ComposedData composedData, + final NgramContext ngramContext, final long proximityInfoHandle, final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId, final float weightForLocale, final float[] inOutWeightOfLangModelVsSpatialModel) { reloadDictionaryIfRequired(); @@ -481,9 +495,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { return null; } final ArrayList<SuggestedWordInfo> suggestions = - mBinaryDictionary.getSuggestions(composer, ngramContext, proximityInfo, - settingsValuesForSuggestion, sessionId, weightForLocale, - inOutWeightOfLangModelVsSpatialModel); + mBinaryDictionary.getSuggestions(composedData, ngramContext, + proximityInfoHandle, settingsValuesForSuggestion, sessionId, + weightForLocale, inOutWeightOfLangModelVsSpatialModel); if (mBinaryDictionary.isCorrupted()) { Log.i(TAG, "Dictionary (" + mDictName +") is corrupted. " + "Remove and regenerate it."); @@ -562,7 +576,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { * Loads the current binary dictionary from internal storage. Assumes the dictionary file * exists. */ - private void loadBinaryDictionaryLocked() { + void loadBinaryDictionaryLocked() { if (DBG_STRESS_TEST) { // Test if this class does not cause problems when it takes long time to load binary // dictionary. @@ -590,7 +604,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { /** * Create a new binary dictionary and load initial contents. */ - private void createNewDictionaryLocked() { + void createNewDictionaryLocked() { removeBinaryDictionaryLocked(); createOnMemoryBinaryDictionaryLocked(); loadInitialContentsLocked(); @@ -606,6 +620,14 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { mNeedsToRecreate = true; } + void clearNeedsToRecreate() { + mNeedsToRecreate = false; + } + + boolean isNeededToRecreate() { + return mNeedsToRecreate; + } + /** * Load the current binary dictionary from internal storage. If the dictionary file doesn't * exists or needs to be regenerated, the new dictionary file will be asynchronously generated. @@ -628,35 +650,39 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { * Reloads the dictionary. Access is controlled on a per dictionary file basis. */ private final void asyncReloadDictionary() { - if (mIsReloading.compareAndSet(false, true)) { - asyncExecuteTaskWithWriteLock(new Runnable() { - @Override - public void run() { - try { - if (!mDictFile.exists() || mNeedsToRecreate) { - // If the dictionary file does not exist or contents have been updated, - // generate a new one. + final AtomicBoolean isReloading = mIsReloading; + if (!isReloading.compareAndSet(false, true)) { + return; + } + final File dictFile = mDictFile; + asyncExecuteTaskWithWriteLock(new Runnable() { + @Override + public void run() { + try { + if (!dictFile.exists() || isNeededToRecreate()) { + // If the dictionary file does not exist or contents have been updated, + // generate a new one. + createNewDictionaryLocked(); + } else if (getBinaryDictionary() == null) { + // Otherwise, load the existing dictionary. + loadBinaryDictionaryLocked(); + final BinaryDictionary binaryDictionary = getBinaryDictionary(); + if (binaryDictionary != null && !(isValidDictionaryLocked() + // TODO: remove the check below + && matchesExpectedBinaryDictFormatVersionForThisType( + binaryDictionary.getFormatVersion()))) { + // Binary dictionary or its format version is not valid. Regenerate + // the dictionary file. createNewDictionaryLocked will remove the + // existing files if appropriate. createNewDictionaryLocked(); - } else if (mBinaryDictionary == null) { - // Otherwise, load the existing dictionary. - loadBinaryDictionaryLocked(); - if (mBinaryDictionary != null && !(isValidDictionaryLocked() - // TODO: remove the check below - && matchesExpectedBinaryDictFormatVersionForThisType( - mBinaryDictionary.getFormatVersion()))) { - // Binary dictionary or its format version is not valid. Regenerate - // the dictionary file. createNewDictionaryLocked will remove the - // existing files if appropriate. - createNewDictionaryLocked(); - } } - mNeedsToRecreate = false; - } finally { - mIsReloading.set(false); } + clearNeedsToRecreate(); + } finally { + isReloading.set(false); } - }); - } + } + }); } /** @@ -666,19 +692,20 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { asyncExecuteTaskWithWriteLock(new Runnable() { @Override public void run() { - if (mBinaryDictionary == null) { + final BinaryDictionary binaryDictionary = getBinaryDictionary(); + if (binaryDictionary == null) { return; } - if (mBinaryDictionary.needsToRunGC(false /* mindsBlockByGC */)) { - mBinaryDictionary.flushWithGC(); + if (binaryDictionary.needsToRunGC(false /* mindsBlockByGC */)) { + binaryDictionary.flushWithGC(); } else { - mBinaryDictionary.flush(); + binaryDictionary.flush(); } } }); } - private static int parseEntryCount(final String entryCountStr) { + static int parseEntryCount(final String entryCountStr) { int entryCount; try { entryCount = Integer.parseInt(entryCountStr); @@ -690,23 +717,27 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { public DictionaryStats getDictionaryStats() { reloadDictionaryIfRequired(); + final String dictName = mDictName; + final File dictFile = mDictFile; final AsyncResultHolder<DictionaryStats> result = new AsyncResultHolder<>(); - asyncExecuteTaskWithLock(mLock.readLock(), mDictName /* executorName */, new Runnable() { + asyncExecuteTaskWithLock(mLock.readLock(), dictName /* executorName */, new Runnable() { @Override public void run() { - if (mBinaryDictionary == null) { - result.set(new DictionaryStats(mLocale, mDictName, mDictFile, + final BinaryDictionary binaryDictionary = getBinaryDictionary(); + if (binaryDictionary == null) { + result.set(new DictionaryStats(mLocale, dictName, dictFile, DictionaryStats.NOT_AN_ENTRY_COUNT, DictionaryStats.NOT_AN_ENTRY_COUNT)); + return; } final int unigramCount = parseEntryCount( - mBinaryDictionary.getPropertyForGettingStats( + binaryDictionary.getPropertyForGettingStats( BinaryDictionary.MAX_UNIGRAM_COUNT_QUERY)); // TODO: Get dedicated entry counts for bigram, trigram, and so on. - final int ngramCount = parseEntryCount(mBinaryDictionary.getPropertyForGettingStats( + final int ngramCount = parseEntryCount(binaryDictionary.getPropertyForGettingStats( BinaryDictionary.MAX_BIGRAM_COUNT_QUERY)); // TODO: Get more information from dictionary. - result.set(new DictionaryStats(mLocale, mDictName, mDictFile, unigramCount, + result.set(new DictionaryStats(mLocale, dictName, dictFile, unigramCount, ngramCount)); } }); @@ -738,28 +769,34 @@ abstract public class ExpandableBinaryDictionary extends Dictionary { public void dumpAllWordsForDebug() { reloadDictionaryIfRequired(); + final String tag = TAG; + final String dictName = mDictName; asyncExecuteTaskWithLock(mLock.readLock(), "dumpAllWordsForDebug", new Runnable() { @Override public void run() { - Log.d(TAG, "Dump dictionary: " + mDictName + " for " + mLocale); + Log.d(tag, "Dump dictionary: " + dictName + " for " + mLocale); + final BinaryDictionary binaryDictionary = getBinaryDictionary(); + if (binaryDictionary == null) { + return; + } try { - final DictionaryHeader header = mBinaryDictionary.getHeader(); - Log.d(TAG, "Format version: " + mBinaryDictionary.getFormatVersion()); - Log.d(TAG, CombinedFormatUtils.formatAttributeMap( + final DictionaryHeader header = binaryDictionary.getHeader(); + Log.d(tag, "Format version: " + binaryDictionary.getFormatVersion()); + Log.d(tag, CombinedFormatUtils.formatAttributeMap( header.mDictionaryOptions.mAttributes)); } catch (final UnsupportedFormatException e) { - Log.d(TAG, "Cannot fetch header information.", e); + Log.d(tag, "Cannot fetch header information.", e); } int token = 0; do { final BinaryDictionary.GetNextWordPropertyResult result = - mBinaryDictionary.getNextWordProperty(token); + binaryDictionary.getNextWordProperty(token); final WordProperty wordProperty = result.mWordProperty; if (wordProperty == null) { - Log.d(TAG, " dictionary is empty."); + Log.d(tag, " dictionary is empty."); break; } - Log.d(TAG, wordProperty.toString()); + Log.d(tag, wordProperty.toString()); token = result.mNextToken; } while (token != 0); } |