Merge "Dictionary migration in Java side."
This commit is contained in:
commit
781e3df73b
4 changed files with 150 additions and 1 deletions
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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()];
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue