Merge "Dictionary migration in Java side."

This commit is contained in:
Keisuke Kuroyanagi 2014-03-28 08:07:25 +00:00 committed by Android (Google) Code Review
commit 781e3df73b
4 changed files with 150 additions and 1 deletions

View file

@ -28,9 +28,10 @@ import com.android.inputmethod.latin.makedict.FormatSpec;
import com.android.inputmethod.latin.makedict.FormatSpec.DictionaryOptions; import com.android.inputmethod.latin.makedict.FormatSpec.DictionaryOptions;
import com.android.inputmethod.latin.makedict.UnsupportedFormatException; import com.android.inputmethod.latin.makedict.UnsupportedFormatException;
import com.android.inputmethod.latin.makedict.WordProperty; import com.android.inputmethod.latin.makedict.WordProperty;
import com.android.inputmethod.latin.personalization.PersonalizationHelper;
import com.android.inputmethod.latin.settings.NativeSuggestOptions; import com.android.inputmethod.latin.settings.NativeSuggestOptions;
import com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
import com.android.inputmethod.latin.utils.CollectionUtils; import com.android.inputmethod.latin.utils.CollectionUtils;
import com.android.inputmethod.latin.utils.FileUtils;
import com.android.inputmethod.latin.utils.JniUtils; import com.android.inputmethod.latin.utils.JniUtils;
import com.android.inputmethod.latin.utils.LanguageModelParam; import com.android.inputmethod.latin.utils.LanguageModelParam;
import com.android.inputmethod.latin.utils.StringUtils; import com.android.inputmethod.latin.utils.StringUtils;
@ -81,6 +82,8 @@ public final class BinaryDictionary extends Dictionary {
public static final int FORMAT_WORD_PROPERTY_LEVEL_INDEX = 2; public static final int FORMAT_WORD_PROPERTY_LEVEL_INDEX = 2;
public static final int FORMAT_WORD_PROPERTY_COUNT_INDEX = 3; public static final int FORMAT_WORD_PROPERTY_COUNT_INDEX = 3;
public static final String DICT_FILE_NAME_SUFFIX_FOR_MIGRATION = ".migrate";
private long mNativeDict; private long mNativeDict;
private final Locale mLocale; private final Locale mLocale;
private final long mDictSize; private final long mDictSize;
@ -458,6 +461,24 @@ public final class BinaryDictionary extends Dictionary {
return needsToRunGCNative(mNativeDict, mindsBlockByGC); return needsToRunGCNative(mNativeDict, mindsBlockByGC);
} }
public boolean migrateTo(final int newFormatVersion) {
if (!isValidDictionary()) {
return false;
}
final String tmpDictFilePath = mDictFilePath + DICT_FILE_NAME_SUFFIX_FOR_MIGRATION;
// TODO: Implement migrateNative(tmpDictFilePath, newFormatVersion).
close();
final File dictFile = new File(mDictFilePath);
final File tmpDictFile = new File(tmpDictFilePath);
FileUtils.deleteRecursively(dictFile);
if (!BinaryDictionaryUtils.renameDict(tmpDictFile, dictFile)) {
return false;
}
loadDictionary(dictFile.getAbsolutePath(), 0 /* startOffset */,
dictFile.length(), mIsUpdatable);
return true;
}
@UsedForTesting @UsedForTesting
public int calculateProbability(final int unigramProbability, final int bigramProbability) { public int calculateProbability(final int unigramProbability, final int bigramProbability) {
if (!isValidDictionary()) return NOT_A_PROBABILITY; if (!isValidDictionary()) return NOT_A_PROBABILITY;

View file

@ -137,6 +137,11 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
return formatVersion == FormatSpec.VERSION4; return formatVersion == FormatSpec.VERSION4;
} }
private boolean needsToMigrateDictionary(final int formatVersion) {
// TODO: Check version.
return false;
}
public boolean isValidDictionaryLocked() { public boolean isValidDictionaryLocked() {
return mBinaryDictionary.isValidDictionary(); return mBinaryDictionary.isValidDictionary();
} }
@ -477,6 +482,10 @@ abstract public class ExpandableBinaryDictionary extends Dictionary {
if (oldBinaryDictionary != null) { if (oldBinaryDictionary != null) {
oldBinaryDictionary.close(); oldBinaryDictionary.close();
} }
if (mBinaryDictionary.isValidDictionary()
&& needsToMigrateDictionary(mBinaryDictionary.getFormatVersion())) {
mBinaryDictionary.migrateTo(DICTIONARY_FORMAT_VERSION);
}
} }
/** /**

View file

@ -26,6 +26,8 @@ import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public final class BinaryDictionaryUtils { public final class BinaryDictionaryUtils {
private static final String TAG = BinaryDictionaryUtils.class.getSimpleName(); private static final String TAG = BinaryDictionaryUtils.class.getSimpleName();
@ -64,6 +66,31 @@ public final class BinaryDictionaryUtils {
return header; return header;
} }
public static boolean renameDict(final File dictFile, final File newDictFile) {
if (dictFile.isFile()) {
return dictFile.renameTo(newDictFile);
} else if (dictFile.isDirectory()) {
final String dictName = dictFile.getName();
final String newDictName = newDictFile.getName();
if (newDictFile.exists()) {
return false;
}
for (final File file : dictFile.listFiles()) {
if (!file.isFile()) {
continue;
}
final String fileName = file.getName();
final String newFileName = fileName.replaceFirst(
Pattern.quote(dictName), Matcher.quoteReplacement(newDictName));
if (!file.renameTo(new File(dictFile, newFileName))) {
return false;
}
}
return dictFile.renameTo(newDictFile);
}
return false;
}
public static boolean createEmptyDictFile(final String filePath, final long dictVersion, public static boolean createEmptyDictFile(final String filePath, final long dictVersion,
final Locale locale, final Map<String, String> attributeMap) { final Locale locale, final Map<String, String> attributeMap) {
final String[] keyArray = new String[attributeMap.size()]; final String[] keyArray = new String[attributeMap.size()];

View file

@ -0,0 +1,92 @@
/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.inputmethod.latin.utils;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import com.android.inputmethod.latin.BinaryDictionary;
import com.android.inputmethod.latin.makedict.DictionaryHeader;
import com.android.inputmethod.latin.makedict.FormatSpec;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@LargeTest
public class BinaryDictionaryUtilsTests extends AndroidTestCase {
private static final String TEST_DICT_FILE_EXTENSION = ".testDict";
private static final String TEST_LOCALE = "test";
private File createEmptyDictionaryAndGetFile(final String dictId,
final int formatVersion) throws IOException {
if (formatVersion == FormatSpec.VERSION4) {
return createEmptyVer4DictionaryAndGetFile(dictId);
} else {
throw new IOException("Dictionary format version " + formatVersion
+ " is not supported.");
}
}
private File createEmptyVer4DictionaryAndGetFile(final String dictId) throws IOException {
final File file = getDictFile(dictId);
FileUtils.deleteRecursively(file);
Map<String, String> attributeMap = new HashMap<String, String>();
attributeMap.put(DictionaryHeader.DICTIONARY_ID_KEY, dictId);
attributeMap.put(DictionaryHeader.DICTIONARY_VERSION_KEY,
String.valueOf(TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis())));
attributeMap.put(DictionaryHeader.USES_FORGETTING_CURVE_KEY,
DictionaryHeader.ATTRIBUTE_VALUE_TRUE);
attributeMap.put(DictionaryHeader.HAS_HISTORICAL_INFO_KEY,
DictionaryHeader.ATTRIBUTE_VALUE_TRUE);
if (BinaryDictionaryUtils.createEmptyDictFile(file.getAbsolutePath(), FormatSpec.VERSION4,
LocaleUtils.constructLocaleFromString(TEST_LOCALE), attributeMap)) {
return file;
} else {
throw new IOException("Empty dictionary " + file.getAbsolutePath()
+ " cannot be created.");
}
}
private File getDictFile(final String dictId) {
return new File(getContext().getCacheDir(), dictId + TEST_DICT_FILE_EXTENSION);
}
public void testRenameDictionary() {
final int formatVersion = FormatSpec.VERSION4;
File dictFile0 = null;
try {
dictFile0 = createEmptyDictionaryAndGetFile("MoveFromDictionary", formatVersion);
} catch (IOException e) {
fail("IOException while writing an initial dictionary : " + e);
}
final File dictFile1 = getDictFile("MoveToDictionary");
FileUtils.deleteRecursively(dictFile1);
assertTrue(BinaryDictionaryUtils.renameDict(dictFile0, dictFile1));
assertFalse(dictFile0.exists());
assertTrue(dictFile1.exists());
BinaryDictionary binaryDictionary = new BinaryDictionary(dictFile1.getAbsolutePath(),
0 /* offset */, dictFile1.length(), true /* useFullEditDistance */,
Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
assertTrue(binaryDictionary.isValidDictionary());
assertTrue(binaryDictionary.getFormatVersion() == formatVersion);
binaryDictionary.close();
}
}