aboutsummaryrefslogtreecommitdiffstats
path: root/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java')
-rw-r--r--java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java310
1 files changed, 249 insertions, 61 deletions
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index 3f11391ba..b92283c5b 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -20,13 +20,16 @@ import android.content.Context;
import android.os.SystemClock;
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.personalization.DynamicPersonalizationDictionaryWriter;
import com.android.inputmethod.latin.utils.CollectionUtils;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
@@ -78,12 +81,21 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
*/
private final String mFilename;
+ /** Whether to support dynamically updating the dictionary */
+ private final boolean mIsUpdatable;
+
/** Controls access to the shared binary dictionary file across multiple instances. */
private final DictionaryController mSharedDictionaryController;
/** Controls access to the local binary dictionary for this instance. */
private final DictionaryController mLocalDictionaryController = new DictionaryController();
+ /* A extension for a binary dictionary file. */
+ public static final String DICT_FILE_EXTENSION = ".dict";
+
+ private final AtomicReference<AsyncWriteBinaryDictionaryTask> mWaitingTask =
+ new AtomicReference<AsyncWriteBinaryDictionaryTask>();
+
/**
* Abstract method for loading the unigrams and bigrams of a given dictionary in a background
* thread.
@@ -110,6 +122,15 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
return controller;
}
+ private static AbstractDictionaryWriter getDictionaryWriter(final Context context,
+ final String dictType, final boolean isDynamicPersonalizationDictionary) {
+ if (isDynamicPersonalizationDictionary) {
+ return new DynamicPersonalizationDictionaryWriter(context, dictType);
+ } else {
+ return new DictionaryWriter(context, dictType);
+ }
+ }
+
/**
* Creates a new expandable binary dictionary.
*
@@ -117,19 +138,23 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
* @param filename The filename for this binary dictionary. Multiple dictionaries with the same
* filename is supported.
* @param dictType the dictionary type, as a human-readable string
+ * @param isUpdatable whether to support dynamically updating the dictionary. Please note that
+ * dynamic dictionary has negative effects on memory space and computation time.
*/
- public ExpandableBinaryDictionary(
- final Context context, final String filename, final String dictType) {
+ public ExpandableBinaryDictionary(final Context context, final String filename,
+ final String dictType, final boolean isUpdatable) {
super(dictType);
mFilename = filename;
mContext = context;
+ mIsUpdatable = isUpdatable;
mBinaryDictionary = null;
mSharedDictionaryController = getSharedDictionaryController(filename);
- mDictionaryWriter = new DictionaryWriter(context, dictType);
+ // Currently, only dynamic personalization dictionary is updatable.
+ mDictionaryWriter = getDictionaryWriter(context, dictType, isUpdatable);
}
protected static String getFilenameWithLocale(final String name, final String localeStr) {
- return name + "." + localeStr + ".dict";
+ return name + "." + localeStr + DICT_FILE_EXTENSION;
}
/**
@@ -137,6 +162,16 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
*/
@Override
public void close() {
+ closeBinaryDictionary();
+ mLocalDictionaryController.writeLock().lock();
+ try {
+ mDictionaryWriter.close();
+ } finally {
+ mLocalDictionaryController.writeLock().unlock();
+ }
+ }
+
+ protected void closeBinaryDictionary() {
// Ensure that no other threads are accessing the local binary dictionary.
mLocalDictionaryController.writeLock().lock();
try {
@@ -144,7 +179,15 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
mBinaryDictionary.close();
mBinaryDictionary = null;
}
- mDictionaryWriter.close();
+ } finally {
+ mLocalDictionaryController.writeLock().unlock();
+ }
+ }
+
+ protected void clear() {
+ mLocalDictionaryController.writeLock().lock();
+ try {
+ mDictionaryWriter.clear();
} finally {
mLocalDictionaryController.writeLock().unlock();
}
@@ -159,59 +202,95 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
}
/**
- * Sets a word bigram in the dictionary. Used for loading a dictionary.
+ * Adds a word bigram in the dictionary. Used for loading a dictionary.
*/
- protected void setBigram(final String prevWord, final String word, final int frequency) {
- mDictionaryWriter.addBigramWords(prevWord, word, frequency, true /* isValid */);
+ protected void addBigram(final String prevWord, final String word, final int frequency,
+ final long lastModifiedTime) {
+ mDictionaryWriter.addBigramWords(prevWord, word, frequency, true /* isValid */,
+ lastModifiedTime);
}
/**
- * Dynamically adds a word unigram to the dictionary.
+ * Dynamically adds a word unigram to the dictionary. May overwrite an existing entry.
*/
protected void addWordDynamically(final String word, final String shortcutTarget,
final int frequency, final boolean isNotAWord) {
- mLocalDictionaryController.writeLock().lock();
- try {
- mDictionaryWriter.addUnigramWord(word, shortcutTarget, frequency, isNotAWord);
- } finally {
- mLocalDictionaryController.writeLock().unlock();
+ if (!mIsUpdatable) {
+ Log.w(TAG, "addWordDynamically is called for non-updatable dictionary: " + mFilename);
+ return;
+ }
+ // TODO: Use a queue to reflect what needs to be reflected.
+ if (mLocalDictionaryController.writeLock().tryLock()) {
+ try {
+ mDictionaryWriter.addUnigramWord(word, shortcutTarget, frequency, isNotAWord);
+ } finally {
+ mLocalDictionaryController.writeLock().unlock();
+ }
}
}
/**
- * Dynamically sets a word bigram in the dictionary.
+ * Dynamically adds a word bigram in the dictionary. May overwrite an existing entry.
*/
- protected void setBigramDynamically(final String prevWord, final String word,
- final int frequency) {
- mLocalDictionaryController.writeLock().lock();
- try {
- mDictionaryWriter.addBigramWords(prevWord, word, frequency, true /* isValid */);
- } finally {
- mLocalDictionaryController.writeLock().unlock();
+ protected void addBigramDynamically(final String word0, final String word1,
+ final int frequency, final boolean isValid) {
+ if (!mIsUpdatable) {
+ Log.w(TAG, "addBigramDynamically is called for non-updatable dictionary: "
+ + mFilename);
+ return;
+ }
+ // TODO: Use a queue to reflect what needs to be reflected.
+ if (mLocalDictionaryController.writeLock().tryLock()) {
+ try {
+ mDictionaryWriter.addBigramWords(word0, word1, frequency, isValid,
+ 0 /* lastTouchedTime */);
+ } finally {
+ mLocalDictionaryController.writeLock().unlock();
+ }
+ }
+ }
+
+ /**
+ * Dynamically remove a word bigram in the dictionary.
+ */
+ protected void removeBigramDynamically(final String word0, final String word1) {
+ if (!mIsUpdatable) {
+ Log.w(TAG, "removeBigramDynamically is called for non-updatable dictionary: "
+ + mFilename);
+ return;
+ }
+ // TODO: Use a queue to reflect what needs to be reflected.
+ if (mLocalDictionaryController.writeLock().tryLock()) {
+ try {
+ mDictionaryWriter.removeBigramWords(word0, word1);
+ } finally {
+ mLocalDictionaryController.writeLock().unlock();
+ }
}
}
@Override
public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
final String prevWord, final ProximityInfo proximityInfo,
- final boolean blockOffensiveWords) {
+ final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) {
asyncReloadDictionaryIfRequired();
// Write lock because getSuggestions in native updates session status.
if (mLocalDictionaryController.writeLock().tryLock()) {
try {
final ArrayList<SuggestedWordInfo> inMemDictSuggestion =
mDictionaryWriter.getSuggestions(composer, prevWord, proximityInfo,
- blockOffensiveWords);
- if (mBinaryDictionary != null) {
+ blockOffensiveWords, additionalFeaturesOptions);
+ // TODO: Remove checking mIsUpdatable and use native suggestion.
+ if (mBinaryDictionary != null && !mIsUpdatable) {
final ArrayList<SuggestedWordInfo> binarySuggestion =
mBinaryDictionary.getSuggestions(composer, prevWord, proximityInfo,
- blockOffensiveWords);
+ blockOffensiveWords, additionalFeaturesOptions);
if (inMemDictSuggestion == null) {
return binarySuggestion;
} else if (binarySuggestion == null) {
return inMemDictSuggestion;
} else {
- binarySuggestion.addAll(binarySuggestion);
+ binarySuggestion.addAll(inMemDictSuggestion);
return binarySuggestion;
}
} else {
@@ -277,7 +356,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
// Build the new binary dictionary
final BinaryDictionary newBinaryDictionary = new BinaryDictionary(filename, 0, length,
- true /* useFullEditDistance */, null, mDictType, false /* isUpdatable */);
+ true /* useFullEditDistance */, null, mDictType, mIsUpdatable);
if (mBinaryDictionary != null) {
// Ensure all threads accessing the current dictionary have finished before swapping in
@@ -302,9 +381,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
abstract protected boolean needsToReloadBeforeWriting();
/**
- * Generates and writes a new binary dictionary based on the contents of the fusion dictionary.
+ * Writes a new binary dictionary based on the contents of the fusion dictionary.
*/
- private void generateBinaryDictionary() {
+ private void writeBinaryDictionary() {
if (DEBUG) {
Log.d(TAG, "Generating binary dictionary: " + mFilename + " request="
+ mSharedDictionaryController.mLastUpdateRequestTime + " update="
@@ -337,7 +416,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
/**
* Reloads the dictionary if required. Reload will occur asynchronously in a separate thread.
*/
- void asyncReloadDictionaryIfRequired() {
+ public void asyncReloadDictionaryIfRequired() {
if (!isReloadRequired()) return;
if (DEBUG) {
Log.d(TAG, "Starting AsyncReloadDictionaryTask: " + mFilename);
@@ -348,7 +427,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
/**
* Reloads the dictionary if required.
*/
- protected final void syncReloadDictionaryIfRequired() {
+ public final void syncReloadDictionaryIfRequired() {
if (!isReloadRequired()) return;
syncReloadDictionaryInternal();
}
@@ -367,41 +446,47 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
private final void syncReloadDictionaryInternal() {
// Ensure that only one thread attempts to read or write to the shared binary dictionary
// file at the same time.
- mSharedDictionaryController.writeLock().lock();
+ mLocalDictionaryController.writeLock().lock();
try {
- final long time = SystemClock.uptimeMillis();
- final boolean dictionaryFileExists = dictionaryFileExists();
- if (mSharedDictionaryController.isOutOfDate() || !dictionaryFileExists) {
- // If the shared dictionary file does not exist or is out of date, the first
- // instance that acquires the lock will generate a new one.
- if (hasContentChanged() || !dictionaryFileExists) {
- // If the source content has changed or the dictionary does not exist, rebuild
- // the binary dictionary. Empty dictionaries are supported (in the case where
- // loadDictionaryAsync() adds nothing) in order to provide a uniform framework.
+ mSharedDictionaryController.writeLock().lock();
+ try {
+ final long time = SystemClock.uptimeMillis();
+ final boolean dictionaryFileExists = dictionaryFileExists();
+ if (mSharedDictionaryController.isOutOfDate() || !dictionaryFileExists) {
+ // If the shared dictionary file does not exist or is out of date, the first
+ // instance that acquires the lock will generate a new one.
+ if (hasContentChanged() || !dictionaryFileExists) {
+ // If the source content has changed or the dictionary does not exist,
+ // rebuild the binary dictionary. Empty dictionaries are supported (in the
+ // case where loadDictionaryAsync() adds nothing) in order to provide a
+ // uniform framework.
+ mSharedDictionaryController.mLastUpdateTime = time;
+ writeBinaryDictionary();
+ loadBinaryDictionary();
+ } else {
+ // If not, the reload request was unnecessary so revert
+ // LastUpdateRequestTime to LastUpdateTime.
+ mSharedDictionaryController.mLastUpdateRequestTime =
+ mSharedDictionaryController.mLastUpdateTime;
+ }
+ } else if (mBinaryDictionary == null || mLocalDictionaryController.mLastUpdateTime
+ < mSharedDictionaryController.mLastUpdateTime) {
+ // Otherwise, if the local dictionary is older than the shared dictionary, load
+ // the shared dictionary.
+ loadBinaryDictionary();
+ }
+ if (mBinaryDictionary != null && !mBinaryDictionary.isValidDictionary()) {
+ // Binary dictionary is not valid. Regenerate the dictionary file.
mSharedDictionaryController.mLastUpdateTime = time;
- generateBinaryDictionary();
+ writeBinaryDictionary();
loadBinaryDictionary();
- } else {
- // If not, the reload request was unnecessary so revert LastUpdateRequestTime
- // to LastUpdateTime.
- mSharedDictionaryController.mLastUpdateRequestTime =
- mSharedDictionaryController.mLastUpdateTime;
}
- } else if (mBinaryDictionary == null || mLocalDictionaryController.mLastUpdateTime
- < mSharedDictionaryController.mLastUpdateTime) {
- // Otherwise, if the local dictionary is older than the shared dictionary, load the
- // shared dictionary.
- loadBinaryDictionary();
- }
- if (mBinaryDictionary != null && !mBinaryDictionary.isValidDictionary()) {
- // Binary dictionary is not valid. Regenerate the dictionary file.
- mSharedDictionaryController.mLastUpdateTime = time;
- generateBinaryDictionary();
- loadBinaryDictionary();
+ mLocalDictionaryController.mLastUpdateTime = time;
+ } finally {
+ mSharedDictionaryController.writeLock().unlock();
}
- mLocalDictionaryController.mLastUpdateTime = time;
} finally {
- mSharedDictionaryController.writeLock().unlock();
+ mLocalDictionaryController.writeLock().unlock();
}
}
@@ -422,6 +507,68 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
}
/**
+ * Load the dictionary to memory.
+ */
+ protected void asyncLoadDictionaryToMemory() {
+ new AsyncLoadDictionaryToMemoryTask().start();
+ }
+
+ /**
+ * Thread class for asynchronously loading dictionary to memory.
+ */
+ private class AsyncLoadDictionaryToMemoryTask extends Thread {
+ @Override
+ public void run() {
+ mLocalDictionaryController.writeLock().lock();
+ try {
+ mSharedDictionaryController.readLock().lock();
+ try {
+ loadDictionaryAsync();
+ } finally {
+ mSharedDictionaryController.readLock().unlock();
+ }
+ } finally {
+ mLocalDictionaryController.writeLock().unlock();
+ }
+ }
+ }
+
+ /**
+ * Generate binary dictionary using DictionaryWriter.
+ */
+ protected void asyncWriteBinaryDictionary() {
+ final AsyncWriteBinaryDictionaryTask newTask = new AsyncWriteBinaryDictionaryTask();
+ newTask.start();
+ final AsyncWriteBinaryDictionaryTask oldTask = mWaitingTask.getAndSet(newTask);
+ if (oldTask != null) {
+ oldTask.interrupt();
+ }
+ }
+
+ /**
+ * Thread class for asynchronously writing the binary dictionary.
+ */
+ private class AsyncWriteBinaryDictionaryTask extends Thread {
+ @Override
+ public void run() {
+ mSharedDictionaryController.writeLock().lock();
+ try {
+ mLocalDictionaryController.writeLock().lock();
+ try {
+ if (isInterrupted()) {
+ return;
+ }
+ writeBinaryDictionary();
+ } finally {
+ mLocalDictionaryController.writeLock().unlock();
+ }
+ } finally {
+ mSharedDictionaryController.writeLock().unlock();
+ }
+ }
+ }
+
+ /**
* Lock for controlling access to a given binary dictionary and for tracking whether the
* dictionary is out of date. Can be shared across multiple dictionary instances that access the
* same filename.
@@ -434,4 +581,45 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
return (mLastUpdateRequestTime > mLastUpdateTime);
}
}
+
+ /**
+ * Dynamically adds a word unigram to the dictionary for testing with blocking-lock.
+ */
+ @UsedForTesting
+ protected void addWordDynamicallyForTests(final String word, final String shortcutTarget,
+ final int frequency, final boolean isNotAWord) {
+ mLocalDictionaryController.writeLock().lock();
+ try {
+ addWordDynamically(word, shortcutTarget, frequency, isNotAWord);
+ } finally {
+ mLocalDictionaryController.writeLock().unlock();
+ }
+ }
+
+ /**
+ * Dynamically adds a word bigram in the dictionary for testing with blocking-lock.
+ */
+ @UsedForTesting
+ protected void addBigramDynamicallyForTests(final String word0, final String word1,
+ final int frequency, final boolean isValid) {
+ mLocalDictionaryController.writeLock().lock();
+ try {
+ addBigramDynamically(word0, word1, frequency, isValid);
+ } finally {
+ mLocalDictionaryController.writeLock().unlock();
+ }
+ }
+
+ /**
+ * Dynamically remove a word bigram in the dictionary for testing with blocking-lock.
+ */
+ @UsedForTesting
+ protected void removeBigramDynamicallyForTests(final String word0, final String word1) {
+ mLocalDictionaryController.writeLock().lock();
+ try {
+ removeBigramDynamically(word0, word1);
+ } finally {
+ mLocalDictionaryController.writeLock().unlock();
+ }
+ }
}