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.java239
1 files changed, 105 insertions, 134 deletions
diff --git a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
index 0774ce203..d3da068bd 100644
--- a/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java
@@ -22,12 +22,7 @@ import android.util.Log;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.keyboard.ProximityInfo;
-import com.android.inputmethod.latin.makedict.DictEncoder;
import com.android.inputmethod.latin.makedict.FormatSpec;
-import com.android.inputmethod.latin.makedict.FusionDictionary;
-import com.android.inputmethod.latin.makedict.FusionDictionary.PtNodeArray;
-import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
-import com.android.inputmethod.latin.makedict.Ver3DictEncoder;
import com.android.inputmethod.latin.personalization.DynamicPersonalizationDictionaryWriter;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.utils.AsyncResultHolder;
@@ -35,10 +30,11 @@ import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.PrioritizedSerialExecutor;
import java.io.File;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
/**
@@ -68,18 +64,21 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
*/
protected static final int MAX_WORD_LENGTH = Constants.DICTIONARY_MAX_WORD_LENGTH;
- private static final FormatSpec.FormatOptions FORMAT_OPTIONS =
- new FormatSpec.FormatOptions(3 /* version */, true /* supportsDynamicUpdate */);
+ private static final int DICTIONARY_FORMAT_VERSION = 3;
+
+ private static final String SUPPORTS_DYNAMIC_UPDATE =
+ FormatSpec.FileHeader.ATTRIBUTE_VALUE_TRUE;
/**
- * A static map of time recorders, each of which records the time of accesses to a single binary
- * dictionary file. The key for this map is the filename and the value is the shared dictionary
- * time recorder associated with that filename.
+ * A static map of update controllers, each of which records the time of accesses to a single
+ * binary dictionary file and tracks whether the file is regenerating. The key for this map is
+ * the filename and the value is the shared dictionary time recorder associated with that
+ * filename.
*/
- private static volatile ConcurrentHashMap<String, DictionaryTimeRecorder>
- sFilenameDictionaryTimeRecorderMap = CollectionUtils.newConcurrentHashMap();
+ private static final ConcurrentHashMap<String, DictionaryUpdateController>
+ sFilenameDictionaryUpdateControllerMap = CollectionUtils.newConcurrentHashMap();
- private static volatile ConcurrentHashMap<String, PrioritizedSerialExecutor>
+ private static final ConcurrentHashMap<String, PrioritizedSerialExecutor>
sFilenameExecutorMap = CollectionUtils.newConcurrentHashMap();
/** The application context. */
@@ -106,13 +105,13 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
private final boolean mIsUpdatable;
// TODO: remove, once dynamic operations is serialized
- /** Records access to the shared binary dictionary file across multiple instances. */
- private final DictionaryTimeRecorder mFilenameDictionaryTimeRecorder;
+ /** Controls updating the shared binary dictionary file across multiple instances. */
+ private final DictionaryUpdateController mFilenameDictionaryUpdateController;
// TODO: remove, once dynamic operations is serialized
- /** Records access to the local binary dictionary for this instance. */
- private final DictionaryTimeRecorder mPerInstanceDictionaryTimeRecorder =
- new DictionaryTimeRecorder();
+ /** Controls updating the local binary dictionary for this instance. */
+ private final DictionaryUpdateController mPerInstanceDictionaryUpdateController =
+ new DictionaryUpdateController();
/* A extension for a binary dictionary file. */
public static final String DICT_FILE_EXTENSION = ".dict";
@@ -134,15 +133,15 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
protected abstract boolean hasContentChanged();
/**
- * Gets the dictionary time recorder for the given filename.
+ * Gets the dictionary update controller for the given filename.
*/
- private static DictionaryTimeRecorder getDictionaryTimeRecorder(
+ private static DictionaryUpdateController getDictionaryUpdateController(
String filename) {
- DictionaryTimeRecorder recorder = sFilenameDictionaryTimeRecorderMap.get(filename);
+ DictionaryUpdateController recorder = sFilenameDictionaryUpdateControllerMap.get(filename);
if (recorder == null) {
- synchronized(sFilenameDictionaryTimeRecorderMap) {
- recorder = new DictionaryTimeRecorder();
- sFilenameDictionaryTimeRecorderMap.put(filename, recorder);
+ synchronized(sFilenameDictionaryUpdateControllerMap) {
+ recorder = new DictionaryUpdateController();
+ sFilenameDictionaryUpdateControllerMap.put(filename, recorder);
}
}
return recorder;
@@ -192,7 +191,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
mContext = context;
mIsUpdatable = isUpdatable;
mBinaryDictionary = null;
- mFilenameDictionaryTimeRecorder = getDictionaryTimeRecorder(filename);
+ mFilenameDictionaryUpdateController = getDictionaryUpdateController(filename);
// Currently, only dynamic personalization dictionary is updatable.
mDictionaryWriter = getDictionaryWriter(context, dictType, isUpdatable);
}
@@ -233,6 +232,13 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
});
}
+ protected Map<String, String> getHeaderAttributeMap() {
+ HashMap<String, String> attributeMap = new HashMap<String, String>();
+ attributeMap.put(FormatSpec.FileHeader.SUPPORTS_DYNAMIC_UPDATE_ATTRIBUTE,
+ SUPPORTS_DYNAMIC_UPDATE);
+ return attributeMap;
+ }
+
protected void clear() {
getExecutor(mFilename).execute(new Runnable() {
@Override
@@ -240,17 +246,8 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE && mDictionaryWriter == null) {
mBinaryDictionary.close();
final File file = new File(mContext.getFilesDir(), mFilename);
- final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
- new FusionDictionary.DictionaryOptions(new HashMap<String,String>(),
- false, false));
- final DictEncoder dictEncoder = new Ver3DictEncoder(file);
- try {
- dictEncoder.writeDictionary(dict, FORMAT_OPTIONS);
- } catch (IOException e) {
- Log.e(TAG, "Exception in creating new dictionary file.", e);
- } catch (UnsupportedFormatException e) {
- Log.e(TAG, "Exception in creating new dictionary file.", e);
- }
+ BinaryDictionary.createEmptyDictFile(file.getAbsolutePath(),
+ DICTIONARY_FORMAT_VERSION, getHeaderAttributeMap());
} else {
mDictionaryWriter.clear();
}
@@ -352,6 +349,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
final boolean blockOffensiveWords, final int[] additionalFeaturesOptions,
final int sessionId) {
reloadDictionaryIfRequired();
+ if (isRegenerating()) {
+ return null;
+ }
final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
final AsyncResultHolder<ArrayList<SuggestedWordInfo>> holder =
new AsyncResultHolder<ArrayList<SuggestedWordInfo>>();
@@ -412,6 +412,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
}
protected boolean isValidWordInner(final String word) {
+ if (isRegenerating()) {
+ return false;
+ }
final AsyncResultHolder<Boolean> holder = new AsyncResultHolder<Boolean>();
getExecutor(mFilename).executePrioritized(new Runnable() {
@Override
@@ -437,7 +440,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
* dictionary exists, this method will generate one.
*/
protected void loadDictionary() {
- mPerInstanceDictionaryTimeRecorder.mLastUpdateRequestTime = SystemClock.uptimeMillis();
+ mPerInstanceDictionaryUpdateController.mLastUpdateRequestTime = SystemClock.uptimeMillis();
reloadDictionaryIfRequired();
}
@@ -448,8 +451,8 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
private void loadBinaryDictionary() {
if (DEBUG) {
Log.d(TAG, "Loading binary dictionary: " + mFilename + " request="
- + mFilenameDictionaryTimeRecorder.mLastUpdateRequestTime + " update="
- + mFilenameDictionaryTimeRecorder.mLastUpdateTime);
+ + mFilenameDictionaryUpdateController.mLastUpdateRequestTime + " update="
+ + mFilenameDictionaryUpdateController.mLastUpdateTime);
}
final File file = new File(mContext.getFilesDir(), mFilename);
@@ -487,8 +490,8 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
private void writeBinaryDictionary() {
if (DEBUG) {
Log.d(TAG, "Generating binary dictionary: " + mFilename + " request="
- + mFilenameDictionaryTimeRecorder.mLastUpdateRequestTime + " update="
- + mFilenameDictionaryTimeRecorder.mLastUpdateTime);
+ + mFilenameDictionaryUpdateController.mLastUpdateRequestTime + " update="
+ + mFilenameDictionaryUpdateController.mLastUpdateTime);
}
if (needsToReloadBeforeWriting()) {
mDictionaryWriter.clear();
@@ -498,17 +501,8 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
if (ENABLE_BINARY_DICTIONARY_DYNAMIC_UPDATE) {
if (mBinaryDictionary == null || !mBinaryDictionary.isValidDictionary()) {
final File file = new File(mContext.getFilesDir(), mFilename);
- final FusionDictionary dict = new FusionDictionary(new PtNodeArray(),
- new FusionDictionary.DictionaryOptions(new HashMap<String,String>(),
- false, false));
- final DictEncoder dictEncoder = new Ver3DictEncoder(file);
- try {
- dictEncoder.writeDictionary(dict, FORMAT_OPTIONS);
- } catch (IOException e) {
- Log.e(TAG, "Exception in creating new dictionary file.", e);
- } catch (UnsupportedFormatException e) {
- Log.e(TAG, "Exception in creating new dictionary file.", e);
- }
+ BinaryDictionary.createEmptyDictFile(file.getAbsolutePath(),
+ DICTIONARY_FORMAT_VERSION, getHeaderAttributeMap());
} else {
if (mBinaryDictionary.needsToRunGC()) {
mBinaryDictionary.flushWithGC();
@@ -531,11 +525,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
*/
protected void setRequiresReload(final boolean requiresRebuild) {
final long time = SystemClock.uptimeMillis();
- mPerInstanceDictionaryTimeRecorder.mLastUpdateRequestTime = time;
- mFilenameDictionaryTimeRecorder.mLastUpdateRequestTime = time;
+ mPerInstanceDictionaryUpdateController.mLastUpdateRequestTime = time;
+ mFilenameDictionaryUpdateController.mLastUpdateRequestTime = time;
if (DEBUG) {
Log.d(TAG, "Reload request: " + mFilename + ": request=" + time + " update="
- + mFilenameDictionaryTimeRecorder.mLastUpdateTime);
+ + mFilenameDictionaryUpdateController.mLastUpdateTime);
}
}
@@ -544,14 +538,26 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
*/
public final void reloadDictionaryIfRequired() {
if (!isReloadRequired()) return;
- reloadDictionary();
+ if (setIsRegeneratingIfNotRegenerating()) {
+ reloadDictionary();
+ }
}
/**
* Returns whether a dictionary reload is required.
*/
private boolean isReloadRequired() {
- return mBinaryDictionary == null || mPerInstanceDictionaryTimeRecorder.isOutOfDate();
+ return mBinaryDictionary == null || mPerInstanceDictionaryUpdateController.isOutOfDate();
+ }
+
+ private boolean isRegenerating() {
+ return mFilenameDictionaryUpdateController.mIsRegenerating.get();
+ }
+
+ // Returns whether the dictionary can be regenerated.
+ private boolean setIsRegeneratingIfNotRegenerating() {
+ return mFilenameDictionaryUpdateController.mIsRegenerating.compareAndSet(
+ false /* expect */ , true /* update */);
}
/**
@@ -564,39 +570,44 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
getExecutor(mFilename).execute(new Runnable() {
@Override
public void run() {
- final long time = SystemClock.uptimeMillis();
- final boolean dictionaryFileExists = dictionaryFileExists();
- if (mFilenameDictionaryTimeRecorder.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.
- mFilenameDictionaryTimeRecorder.mLastUpdateTime = time;
+ try {
+ final long time = SystemClock.uptimeMillis();
+ final boolean dictionaryFileExists = dictionaryFileExists();
+ if (mFilenameDictionaryUpdateController.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.
+ mFilenameDictionaryUpdateController.mLastUpdateTime = time;
+ writeBinaryDictionary();
+ loadBinaryDictionary();
+ } else {
+ // If not, the reload request was unnecessary so revert
+ // LastUpdateRequestTime to LastUpdateTime.
+ mFilenameDictionaryUpdateController.mLastUpdateRequestTime =
+ mFilenameDictionaryUpdateController.mLastUpdateTime;
+ }
+ } else if (mBinaryDictionary == null ||
+ mPerInstanceDictionaryUpdateController.mLastUpdateTime
+ < mFilenameDictionaryUpdateController.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.
+ mFilenameDictionaryUpdateController.mLastUpdateTime = time;
writeBinaryDictionary();
loadBinaryDictionary();
- } else {
- // If not, the reload request was unnecessary so revert
- // LastUpdateRequestTime to LastUpdateTime.
- mFilenameDictionaryTimeRecorder.mLastUpdateRequestTime =
- mFilenameDictionaryTimeRecorder.mLastUpdateTime;
}
- } else if (mBinaryDictionary == null ||
- mPerInstanceDictionaryTimeRecorder.mLastUpdateTime
- < mFilenameDictionaryTimeRecorder.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.
- mFilenameDictionaryTimeRecorder.mLastUpdateTime = time;
- writeBinaryDictionary();
- loadBinaryDictionary();
+ mPerInstanceDictionaryUpdateController.mLastUpdateTime = time;
+ } finally {
+ mFilenameDictionaryUpdateController.mIsRegenerating.set(false);
}
- mPerInstanceDictionaryTimeRecorder.mLastUpdateTime = time;
}
});
}
@@ -636,59 +647,19 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
}
/**
- * Time recorder for tracking whether the dictionary is out of date.
+ * For tracking whether the dictionary is out of date and the dictionary is regenerating.
* Can be shared across multiple dictionary instances that access the same filename.
*/
- private static class DictionaryTimeRecorder {
- private volatile long mLastUpdateTime = 0;
- private volatile long mLastUpdateRequestTime = 0;
+ private static class DictionaryUpdateController {
+ public volatile long mLastUpdateTime = 0;
+ public volatile long mLastUpdateRequestTime = 0;
+ public volatile AtomicBoolean mIsRegenerating = new AtomicBoolean();
- private boolean isOutOfDate() {
+ public boolean isOutOfDate() {
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) {
- getExecutor(mFilename).executePrioritized(new Runnable() {
- @Override
- public void run() {
- addWordDynamically(word, shortcutTarget, frequency, isNotAWord);
- }
- });
- }
-
- /**
- * 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) {
- getExecutor(mFilename).executePrioritized(new Runnable() {
- @Override
- public void run() {
- addBigramDynamically(word0, word1, frequency, isValid);
- }
- });
- }
-
- /**
- * Dynamically remove a word bigram in the dictionary for testing with blocking-lock.
- */
- @UsedForTesting
- protected void removeBigramDynamicallyForTests(final String word0, final String word1) {
- getExecutor(mFilename).executePrioritized(new Runnable() {
- @Override
- public void run() {
- removeBigramDynamically(word0, word1);
- }
- });
- }
-
// TODO: Implement native binary methods once the dynamic dictionary implementation is done.
@UsedForTesting
public boolean isInDictionaryForTests(final String word) {
@@ -701,7 +672,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
holder.set(mBinaryDictionary.isValidWord(word));
} else {
holder.set(((DynamicPersonalizationDictionaryWriter) mDictionaryWriter)
- .isInDictionaryForTests(word));
+ .isInBigramListForTests(word));
}
}
}