Merge "Prepare ExpandableBinaryDictionary to make it updatable"

main
Keisuke Kuroyanagi 2013-08-19 07:20:16 +00:00 committed by Android (Google) Code Review
commit c8ac8da4e4
6 changed files with 175 additions and 60 deletions

View File

@ -42,8 +42,10 @@ abstract public class AbstractDictionaryWriter extends Dictionary {
abstract public void addUnigramWord(final String word, final String shortcutTarget, abstract public void addUnigramWord(final String word, final String shortcutTarget,
final int frequency, final boolean isNotAWord); final int frequency, final boolean isNotAWord);
// TODO: Remove lastModifiedTime after making binary dictionary support forgetting curve.
abstract public void addBigramWords(final String word0, final String word1, abstract public void addBigramWords(final String word0, final String word1,
final int frequency, final boolean isValid); final int frequency, final boolean isValid,
final long lastModifiedTime);
abstract public void removeBigramWords(final String word0, final String word1); abstract public void removeBigramWords(final String word0, final String word1);

View File

@ -70,7 +70,8 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
private final boolean mUseFirstLastBigrams; private final boolean mUseFirstLastBigrams;
public ContactsBinaryDictionary(final Context context, final Locale locale) { public ContactsBinaryDictionary(final Context context, final Locale locale) {
super(context, getFilenameWithLocale(NAME, locale.toString()), Dictionary.TYPE_CONTACTS); super(context, getFilenameWithLocale(NAME, locale.toString()), Dictionary.TYPE_CONTACTS,
false /* isUpdatable */);
mLocale = locale; mLocale = locale;
mUseFirstLastBigrams = useFirstLastBigramsForLocale(locale); mUseFirstLastBigrams = useFirstLastBigramsForLocale(locale);
registerObserver(context); registerObserver(context);
@ -208,7 +209,8 @@ public class ContactsBinaryDictionary extends ExpandableBinaryDictionary {
false /* isNotAWord */); false /* isNotAWord */);
if (!TextUtils.isEmpty(prevWord)) { if (!TextUtils.isEmpty(prevWord)) {
if (mUseFirstLastBigrams) { if (mUseFirstLastBigrams) {
super.setBigram(prevWord, word, FREQUENCY_FOR_CONTACTS_BIGRAM); super.addBigram(prevWord, word, FREQUENCY_FOR_CONTACTS_BIGRAM,
0 /* lastModifiedTime */);
} }
} }
prevWord = word; prevWord = word;

View File

@ -75,7 +75,7 @@ public class DictionaryWriter extends AbstractDictionaryWriter {
@Override @Override
public void addBigramWords(final String word0, final String word1, final int frequency, public void addBigramWords(final String word0, final String word1, final int frequency,
final boolean isValid) { final boolean isValid, final long lastModifiedTime) {
mFusionDictionary.setBigram(word0, word1, frequency); mFusionDictionary.setBigram(word0, word1, frequency);
} }

View File

@ -20,6 +20,7 @@ import android.content.Context;
import android.os.SystemClock; import android.os.SystemClock;
import android.util.Log; import android.util.Log;
import com.android.inputmethod.annotations.UsedForTesting;
import com.android.inputmethod.keyboard.ProximityInfo; import com.android.inputmethod.keyboard.ProximityInfo;
import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.CollectionUtils;
@ -78,6 +79,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
*/ */
private final String mFilename; 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. */ /** Controls access to the shared binary dictionary file across multiple instances. */
private final DictionaryController mSharedDictionaryController; private final DictionaryController mSharedDictionaryController;
@ -113,6 +117,16 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
return controller; return controller;
} }
private static AbstractDictionaryWriter getDictionaryWriter(final Context context,
final String dictType, final boolean isUpdatable) {
if (isUpdatable) {
// TODO: Employ dynamically updatable DictionaryWriter.
return new DictionaryWriter(context, dictType);
} else {
return new DictionaryWriter(context, dictType);
}
}
/** /**
* Creates a new expandable binary dictionary. * Creates a new expandable binary dictionary.
* *
@ -120,15 +134,18 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
* @param filename The filename for this binary dictionary. Multiple dictionaries with the same * @param filename The filename for this binary dictionary. Multiple dictionaries with the same
* filename is supported. * filename is supported.
* @param dictType the dictionary type, as a human-readable string * @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( public ExpandableBinaryDictionary(final Context context, final String filename,
final Context context, final String filename, final String dictType) { final String dictType, final boolean isUpdatable) {
super(dictType); super(dictType);
mFilename = filename; mFilename = filename;
mContext = context; mContext = context;
mIsUpdatable = isUpdatable;
mBinaryDictionary = null; mBinaryDictionary = null;
mSharedDictionaryController = getSharedDictionaryController(filename); mSharedDictionaryController = getSharedDictionaryController(filename);
mDictionaryWriter = new DictionaryWriter(context, dictType); mDictionaryWriter = getDictionaryWriter(context, dictType, isUpdatable);
} }
protected static String getFilenameWithLocale(final String name, final String localeStr) { protected static String getFilenameWithLocale(final String name, final String localeStr) {
@ -140,6 +157,16 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
*/ */
@Override @Override
public void close() { 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. // Ensure that no other threads are accessing the local binary dictionary.
mLocalDictionaryController.writeLock().lock(); mLocalDictionaryController.writeLock().lock();
try { try {
@ -147,7 +174,6 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
mBinaryDictionary.close(); mBinaryDictionary.close();
mBinaryDictionary = null; mBinaryDictionary = null;
} }
mDictionaryWriter.close();
} finally { } finally {
mLocalDictionaryController.writeLock().unlock(); mLocalDictionaryController.writeLock().unlock();
} }
@ -162,35 +188,70 @@ 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) { protected void addBigram(final String prevWord, final String word, final int frequency,
mDictionaryWriter.addBigramWords(prevWord, word, frequency, true /* isValid */); 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, protected void addWordDynamically(final String word, final String shortcutTarget,
final int frequency, final boolean isNotAWord) { final int frequency, final boolean isNotAWord) {
mLocalDictionaryController.writeLock().lock(); if (!mIsUpdatable) {
try { Log.w(TAG, "addWordDynamically is called for non-updatable dictionary: " + mFilename);
mDictionaryWriter.addUnigramWord(word, shortcutTarget, frequency, isNotAWord); return;
} finally { }
mLocalDictionaryController.writeLock().unlock(); // 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, protected void addBigramDynamically(final String word0, final String word1,
final int frequency) { final int frequency, final boolean isValid) {
mLocalDictionaryController.writeLock().lock(); if (!mIsUpdatable) {
try { Log.w(TAG, "addBigramDynamically is called for non-updatable dictionary: "
mDictionaryWriter.addBigramWords(prevWord, word, frequency, true /* isValid */); + mFilename);
} finally { return;
mLocalDictionaryController.writeLock().unlock(); }
// 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();
}
} }
} }
@ -280,7 +341,7 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
// Build the new binary dictionary // Build the new binary dictionary
final BinaryDictionary newBinaryDictionary = new BinaryDictionary(filename, 0, length, final BinaryDictionary newBinaryDictionary = new BinaryDictionary(filename, 0, length,
true /* useFullEditDistance */, null, mDictType, false /* isUpdatable */); true /* useFullEditDistance */, null, mDictType, mIsUpdatable);
if (mBinaryDictionary != null) { if (mBinaryDictionary != null) {
// Ensure all threads accessing the current dictionary have finished before swapping in // Ensure all threads accessing the current dictionary have finished before swapping in
@ -305,9 +366,9 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
abstract protected boolean needsToReloadBeforeWriting(); 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) { if (DEBUG) {
Log.d(TAG, "Generating binary dictionary: " + mFilename + " request=" Log.d(TAG, "Generating binary dictionary: " + mFilename + " request="
+ mSharedDictionaryController.mLastUpdateRequestTime + " update=" + mSharedDictionaryController.mLastUpdateRequestTime + " update="
@ -370,41 +431,47 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
private final void syncReloadDictionaryInternal() { private final void syncReloadDictionaryInternal() {
// Ensure that only one thread attempts to read or write to the shared binary dictionary // Ensure that only one thread attempts to read or write to the shared binary dictionary
// file at the same time. // file at the same time.
mSharedDictionaryController.writeLock().lock(); mLocalDictionaryController.writeLock().lock();
try { try {
final long time = SystemClock.uptimeMillis(); mSharedDictionaryController.writeLock().lock();
final boolean dictionaryFileExists = dictionaryFileExists(); try {
if (mSharedDictionaryController.isOutOfDate() || !dictionaryFileExists) { final long time = SystemClock.uptimeMillis();
// If the shared dictionary file does not exist or is out of date, the first final boolean dictionaryFileExists = dictionaryFileExists();
// instance that acquires the lock will generate a new one. if (mSharedDictionaryController.isOutOfDate() || !dictionaryFileExists) {
if (hasContentChanged() || !dictionaryFileExists) { // If the shared dictionary file does not exist or is out of date, the first
// If the source content has changed or the dictionary does not exist, rebuild // instance that acquires the lock will generate a new one.
// the binary dictionary. Empty dictionaries are supported (in the case where if (hasContentChanged() || !dictionaryFileExists) {
// loadDictionaryAsync() adds nothing) in order to provide a uniform framework. // If the source content has changed or the dictionary does not exist,
mSharedDictionaryController.mLastUpdateTime = time; // rebuild the binary dictionary. Empty dictionaries are supported (in the
generateBinaryDictionary(); // 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(); loadBinaryDictionary();
} else {
// If not, the reload request was unnecessary so revert LastUpdateRequestTime
// to LastUpdateTime.
mSharedDictionaryController.mLastUpdateRequestTime =
mSharedDictionaryController.mLastUpdateTime;
} }
} else if (mBinaryDictionary == null || mLocalDictionaryController.mLastUpdateTime if (mBinaryDictionary != null && !mBinaryDictionary.isValidDictionary()) {
< mSharedDictionaryController.mLastUpdateTime) { // Binary dictionary is not valid. Regenerate the dictionary file.
// Otherwise, if the local dictionary is older than the shared dictionary, load the mSharedDictionaryController.mLastUpdateTime = time;
// shared dictionary. writeBinaryDictionary();
loadBinaryDictionary(); loadBinaryDictionary();
}
mLocalDictionaryController.mLastUpdateTime = time;
} finally {
mSharedDictionaryController.writeLock().unlock();
} }
if (mBinaryDictionary != null && !mBinaryDictionary.isValidDictionary()) {
// Binary dictionary is not valid. Regenerate the dictionary file.
mSharedDictionaryController.mLastUpdateTime = time;
generateBinaryDictionary();
loadBinaryDictionary();
}
mLocalDictionaryController.mLastUpdateTime = time;
} finally { } finally {
mSharedDictionaryController.writeLock().unlock(); mLocalDictionaryController.writeLock().unlock();
} }
} }
@ -437,4 +504,45 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
return (mLastUpdateRequestTime > mLastUpdateTime); 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();
}
}
} }

View File

@ -75,7 +75,8 @@ public class UserBinaryDictionary extends ExpandableBinaryDictionary {
public UserBinaryDictionary(final Context context, final String locale, public UserBinaryDictionary(final Context context, final String locale,
final boolean alsoUseMoreRestrictiveLocales) { final boolean alsoUseMoreRestrictiveLocales) {
super(context, getFilenameWithLocale(NAME, locale), Dictionary.TYPE_USER); super(context, getFilenameWithLocale(NAME, locale), Dictionary.TYPE_USER,
false /* isUpdatable */);
if (null == locale) throw new NullPointerException(); // Catch the error earlier if (null == locale) throw new NullPointerException(); // Catch the error earlier
if (SubtypeLocaleUtils.NO_LANGUAGE.equals(locale)) { if (SubtypeLocaleUtils.NO_LANGUAGE.equals(locale)) {
// If we don't have a locale, insert into the "all locales" user dictionary. // If we don't have a locale, insert into the "all locales" user dictionary.

View File

@ -36,7 +36,9 @@ public class PersonalizationDictionary extends ExpandableBinaryDictionary {
// Singleton // Singleton
private PersonalizationDictionary(final Context context, final String locale) { private PersonalizationDictionary(final Context context, final String locale) {
super(context, getFilenameWithLocale(NAME, locale), Dictionary.TYPE_PERSONALIZATION); // TODO: Make isUpdatable true.
super(context, getFilenameWithLocale(NAME, locale), Dictionary.TYPE_PERSONALIZATION,
false /* isUpdatable */);
mLocale = locale; mLocale = locale;
} }