diff options
Diffstat (limited to 'java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java')
-rw-r--r-- | java/src/com/android/inputmethod/latin/ExpandableBinaryDictionary.java | 239 |
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)); } } } |