BinaryDictionary.java revision dfca51726e9dc9a35f462dee39331823eafa07c9
1/* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.inputmethod.latin; 18 19import android.text.TextUtils; 20import android.util.Log; 21import android.util.SparseArray; 22 23import com.android.inputmethod.annotations.UsedForTesting; 24import com.android.inputmethod.keyboard.ProximityInfo; 25import com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo; 26import com.android.inputmethod.latin.makedict.DictionaryHeader; 27import com.android.inputmethod.latin.makedict.FormatSpec; 28import com.android.inputmethod.latin.makedict.FormatSpec.DictionaryOptions; 29import com.android.inputmethod.latin.makedict.UnsupportedFormatException; 30import com.android.inputmethod.latin.makedict.WordProperty; 31import com.android.inputmethod.latin.settings.NativeSuggestOptions; 32import com.android.inputmethod.latin.utils.BinaryDictionaryUtils; 33import com.android.inputmethod.latin.utils.FileUtils; 34import com.android.inputmethod.latin.utils.JniUtils; 35import com.android.inputmethod.latin.utils.LanguageModelParam; 36import com.android.inputmethod.latin.utils.StringUtils; 37 38import java.io.File; 39import java.util.ArrayList; 40import java.util.Arrays; 41import java.util.HashMap; 42import java.util.Locale; 43import java.util.Map; 44 45/** 46 * Implements a static, compacted, binary dictionary of standard words. 47 */ 48// TODO: All methods which should be locked need to have a suffix "Locked". 49public final class BinaryDictionary extends Dictionary { 50 private static final String TAG = BinaryDictionary.class.getSimpleName(); 51 52 // Must be equal to MAX_WORD_LENGTH in native/jni/src/defines.h 53 private static final int MAX_WORD_LENGTH = Constants.DICTIONARY_MAX_WORD_LENGTH; 54 // Must be equal to MAX_RESULTS in native/jni/src/defines.h 55 private static final int MAX_RESULTS = 18; 56 // The cutoff returned by native for auto-commit confidence. 57 // Must be equal to CONFIDENCE_TO_AUTO_COMMIT in native/jni/src/defines.h 58 private static final int CONFIDENCE_TO_AUTO_COMMIT = 1000000; 59 60 @UsedForTesting 61 public static final String UNIGRAM_COUNT_QUERY = "UNIGRAM_COUNT"; 62 @UsedForTesting 63 public static final String BIGRAM_COUNT_QUERY = "BIGRAM_COUNT"; 64 @UsedForTesting 65 public static final String MAX_UNIGRAM_COUNT_QUERY = "MAX_UNIGRAM_COUNT"; 66 @UsedForTesting 67 public static final String MAX_BIGRAM_COUNT_QUERY = "MAX_BIGRAM_COUNT"; 68 69 public static final int NOT_A_VALID_TIMESTAMP = -1; 70 71 // Format to get unigram flags from native side via getWordPropertyNative(). 72 private static final int FORMAT_WORD_PROPERTY_OUTPUT_FLAG_COUNT = 4; 73 private static final int FORMAT_WORD_PROPERTY_IS_NOT_A_WORD_INDEX = 0; 74 private static final int FORMAT_WORD_PROPERTY_IS_BLACKLISTED_INDEX = 1; 75 private static final int FORMAT_WORD_PROPERTY_HAS_BIGRAMS_INDEX = 2; 76 private static final int FORMAT_WORD_PROPERTY_HAS_SHORTCUTS_INDEX = 3; 77 78 // Format to get probability and historical info from native side via getWordPropertyNative(). 79 public static final int FORMAT_WORD_PROPERTY_OUTPUT_PROBABILITY_INFO_COUNT = 4; 80 public static final int FORMAT_WORD_PROPERTY_PROBABILITY_INDEX = 0; 81 public static final int FORMAT_WORD_PROPERTY_TIMESTAMP_INDEX = 1; 82 public static final int FORMAT_WORD_PROPERTY_LEVEL_INDEX = 2; 83 public static final int FORMAT_WORD_PROPERTY_COUNT_INDEX = 3; 84 85 public static final String DICT_FILE_NAME_SUFFIX_FOR_MIGRATION = ".migrate"; 86 87 private long mNativeDict; 88 private final Locale mLocale; 89 private final long mDictSize; 90 private final String mDictFilePath; 91 private final boolean mIsUpdatable; 92 private boolean mHasUpdated; 93 94 private final int[] mInputCodePoints = new int[MAX_WORD_LENGTH]; 95 private final int[] mOutputSuggestionCount = new int[1]; 96 private final int[] mOutputCodePoints = new int[MAX_WORD_LENGTH * MAX_RESULTS]; 97 private final int[] mSpaceIndices = new int[MAX_RESULTS]; 98 private final int[] mOutputScores = new int[MAX_RESULTS]; 99 private final int[] mOutputTypes = new int[MAX_RESULTS]; 100 // Only one result is ever used 101 private final int[] mOutputAutoCommitFirstWordConfidence = new int[1]; 102 private final float[] mInputOutputLanguageWeight = new float[1]; 103 104 private final NativeSuggestOptions mNativeSuggestOptions = new NativeSuggestOptions(); 105 106 private final SparseArray<DicTraverseSession> mDicTraverseSessions = new SparseArray<>(); 107 108 // TODO: There should be a way to remove used DicTraverseSession objects from 109 // {@code mDicTraverseSessions}. 110 private DicTraverseSession getTraverseSession(final int traverseSessionId) { 111 synchronized(mDicTraverseSessions) { 112 DicTraverseSession traverseSession = mDicTraverseSessions.get(traverseSessionId); 113 if (traverseSession == null) { 114 traverseSession = new DicTraverseSession(mLocale, mNativeDict, mDictSize); 115 mDicTraverseSessions.put(traverseSessionId, traverseSession); 116 } 117 return traverseSession; 118 } 119 } 120 121 /** 122 * Constructs binary dictionary using existing dictionary file. 123 * @param filename the name of the file to read through native code. 124 * @param offset the offset of the dictionary data within the file. 125 * @param length the length of the binary data. 126 * @param useFullEditDistance whether to use the full edit distance in suggestions 127 * @param dictType the dictionary type, as a human-readable string 128 * @param isUpdatable whether to open the dictionary file in writable mode. 129 */ 130 public BinaryDictionary(final String filename, final long offset, final long length, 131 final boolean useFullEditDistance, final Locale locale, final String dictType, 132 final boolean isUpdatable) { 133 super(dictType); 134 mLocale = locale; 135 mDictSize = length; 136 mDictFilePath = filename; 137 mIsUpdatable = isUpdatable; 138 mHasUpdated = false; 139 mNativeSuggestOptions.setUseFullEditDistance(useFullEditDistance); 140 loadDictionary(filename, offset, length, isUpdatable); 141 } 142 143 /** 144 * Constructs binary dictionary on memory. 145 * @param filename the name of the file used to flush. 146 * @param useFullEditDistance whether to use the full edit distance in suggestions 147 * @param dictType the dictionary type, as a human-readable string 148 * @param formatVersion the format version of the dictionary 149 * @param attributeMap the attributes of the dictionary 150 */ 151 @UsedForTesting 152 public BinaryDictionary(final String filename, final boolean useFullEditDistance, 153 final Locale locale, final String dictType, final long formatVersion, 154 final Map<String, String> attributeMap) { 155 super(dictType); 156 mLocale = locale; 157 mDictSize = 0; 158 mDictFilePath = filename; 159 // On memory dictionary is always updatable. 160 mIsUpdatable = true; 161 mHasUpdated = false; 162 mNativeSuggestOptions.setUseFullEditDistance(useFullEditDistance); 163 final String[] keyArray = new String[attributeMap.size()]; 164 final String[] valueArray = new String[attributeMap.size()]; 165 int index = 0; 166 for (final String key : attributeMap.keySet()) { 167 keyArray[index] = key; 168 valueArray[index] = attributeMap.get(key); 169 index++; 170 } 171 mNativeDict = createOnMemoryNative(formatVersion, locale.toString(), keyArray, valueArray); 172 } 173 174 175 static { 176 JniUtils.loadNativeLibrary(); 177 } 178 179 private static native long openNative(String sourceDir, long dictOffset, long dictSize, 180 boolean isUpdatable); 181 private static native long createOnMemoryNative(long formatVersion, 182 String locale, String[] attributeKeyStringArray, String[] attributeValueStringArray); 183 private static native void getHeaderInfoNative(long dict, int[] outHeaderSize, 184 int[] outFormatVersion, ArrayList<int[]> outAttributeKeys, 185 ArrayList<int[]> outAttributeValues); 186 private static native boolean flushNative(long dict, String filePath); 187 private static native boolean needsToRunGCNative(long dict, boolean mindsBlockByGC); 188 private static native boolean flushWithGCNative(long dict, String filePath); 189 private static native void closeNative(long dict); 190 private static native int getFormatVersionNative(long dict); 191 private static native int getProbabilityNative(long dict, int[] word); 192 private static native int getBigramProbabilityNative(long dict, int[] word0, 193 boolean isBeginningOfSentence, int[] word1); 194 private static native void getWordPropertyNative(long dict, int[] word, 195 int[] outCodePoints, boolean[] outFlags, int[] outProbabilityInfo, 196 ArrayList<int[]> outBigramTargets, ArrayList<int[]> outBigramProbabilityInfo, 197 ArrayList<int[]> outShortcutTargets, ArrayList<Integer> outShortcutProbabilities); 198 private static native int getNextWordNative(long dict, int token, int[] outCodePoints); 199 private static native void getSuggestionsNative(long dict, long proximityInfo, 200 long traverseSession, int[] xCoordinates, int[] yCoordinates, int[] times, 201 int[] pointerIds, int[] inputCodePoints, int inputSize, int[] suggestOptions, 202 int[] prevWordCodePointArray, boolean isBeginningOfSentence, 203 int[] outputSuggestionCount, int[] outputCodePoints, int[] outputScores, 204 int[] outputIndices, int[] outputTypes, int[] outputAutoCommitFirstWordConfidence, 205 float[] inOutLanguageWeight); 206 private static native boolean addUnigramWordNative(long dict, int[] word, int probability, 207 int[] shortcutTarget, int shortcutProbability, boolean isBeginningOfSentence, 208 boolean isNotAWord, boolean isBlacklisted, int timestamp); 209 private static native boolean addBigramWordsNative(long dict, int[] word0, 210 boolean isBeginningOfSentence, int[] word1, int probability, int timestamp); 211 private static native boolean removeBigramWordsNative(long dict, int[] word0, 212 boolean isBeginningOfSentence, int[] word1); 213 private static native int addMultipleDictionaryEntriesNative(long dict, 214 LanguageModelParam[] languageModelParams, int startIndex); 215 private static native String getPropertyNative(long dict, String query); 216 private static native boolean isCorruptedNative(long dict); 217 private static native boolean migrateNative(long dict, String dictFilePath, 218 long newFormatVersion); 219 220 // TODO: Move native dict into session 221 private final void loadDictionary(final String path, final long startOffset, 222 final long length, final boolean isUpdatable) { 223 mHasUpdated = false; 224 mNativeDict = openNative(path, startOffset, length, isUpdatable); 225 } 226 227 // TODO: Check isCorrupted() for main dictionaries. 228 public boolean isCorrupted() { 229 if (!isValidDictionary()) { 230 return false; 231 } 232 if (!isCorruptedNative(mNativeDict)) { 233 return false; 234 } 235 // TODO: Record the corruption. 236 Log.e(TAG, "BinaryDictionary (" + mDictFilePath + ") is corrupted."); 237 Log.e(TAG, "locale: " + mLocale); 238 Log.e(TAG, "dict size: " + mDictSize); 239 Log.e(TAG, "updatable: " + mIsUpdatable); 240 return true; 241 } 242 243 public DictionaryHeader getHeader() throws UnsupportedFormatException { 244 if (mNativeDict == 0) { 245 return null; 246 } 247 final int[] outHeaderSize = new int[1]; 248 final int[] outFormatVersion = new int[1]; 249 final ArrayList<int[]> outAttributeKeys = new ArrayList<>(); 250 final ArrayList<int[]> outAttributeValues = new ArrayList<>(); 251 getHeaderInfoNative(mNativeDict, outHeaderSize, outFormatVersion, outAttributeKeys, 252 outAttributeValues); 253 final HashMap<String, String> attributes = new HashMap<>(); 254 for (int i = 0; i < outAttributeKeys.size(); i++) { 255 final String attributeKey = StringUtils.getStringFromNullTerminatedCodePointArray( 256 outAttributeKeys.get(i)); 257 final String attributeValue = StringUtils.getStringFromNullTerminatedCodePointArray( 258 outAttributeValues.get(i)); 259 attributes.put(attributeKey, attributeValue); 260 } 261 final boolean hasHistoricalInfo = DictionaryHeader.ATTRIBUTE_VALUE_TRUE.equals( 262 attributes.get(DictionaryHeader.HAS_HISTORICAL_INFO_KEY)); 263 return new DictionaryHeader(outHeaderSize[0], new DictionaryOptions(attributes), 264 new FormatSpec.FormatOptions(outFormatVersion[0], hasHistoricalInfo)); 265 } 266 267 @Override 268 public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, 269 final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo, 270 final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, 271 final int sessionId, final float[] inOutLanguageWeight) { 272 if (!isValidDictionary()) { 273 return null; 274 } 275 276 Arrays.fill(mInputCodePoints, Constants.NOT_A_CODE); 277 // TODO: toLowerCase in the native code 278 final int[] prevWordCodePointArray = (null == prevWordsInfo.mPrevWord) 279 ? null : StringUtils.toCodePointArray(prevWordsInfo.mPrevWord); 280 final InputPointers inputPointers = composer.getInputPointers(); 281 final boolean isGesture = composer.isBatchMode(); 282 final int inputSize; 283 if (!isGesture) { 284 inputSize = composer.copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount( 285 mInputCodePoints); 286 if (inputSize < 0) { 287 return null; 288 } 289 } else { 290 inputSize = inputPointers.getPointerSize(); 291 } 292 293 mNativeSuggestOptions.setIsGesture(isGesture); 294 mNativeSuggestOptions.setAdditionalFeaturesOptions(additionalFeaturesOptions); 295 if (inOutLanguageWeight != null) { 296 mInputOutputLanguageWeight[0] = inOutLanguageWeight[0]; 297 } else { 298 mInputOutputLanguageWeight[0] = Dictionary.NOT_A_LANGUAGE_WEIGHT; 299 } 300 // proximityInfo and/or prevWordForBigrams may not be null. 301 getSuggestionsNative(mNativeDict, proximityInfo.getNativeProximityInfo(), 302 getTraverseSession(sessionId).getSession(), inputPointers.getXCoordinates(), 303 inputPointers.getYCoordinates(), inputPointers.getTimes(), 304 inputPointers.getPointerIds(), mInputCodePoints, inputSize, 305 mNativeSuggestOptions.getOptions(), prevWordCodePointArray, 306 prevWordsInfo.mIsBeginningOfSentence, mOutputSuggestionCount, 307 mOutputCodePoints, mOutputScores, mSpaceIndices, mOutputTypes, 308 mOutputAutoCommitFirstWordConfidence, mInputOutputLanguageWeight); 309 if (inOutLanguageWeight != null) { 310 inOutLanguageWeight[0] = mInputOutputLanguageWeight[0]; 311 } 312 final int count = mOutputSuggestionCount[0]; 313 final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<>(); 314 for (int j = 0; j < count; ++j) { 315 final int start = j * MAX_WORD_LENGTH; 316 int len = 0; 317 while (len < MAX_WORD_LENGTH && mOutputCodePoints[start + len] != 0) { 318 ++len; 319 } 320 if (len > 0) { 321 final int flags = mOutputTypes[j] & SuggestedWordInfo.KIND_MASK_FLAGS; 322 if (blockOffensiveWords 323 && 0 != (flags & SuggestedWordInfo.KIND_FLAG_POSSIBLY_OFFENSIVE) 324 && 0 == (flags & SuggestedWordInfo.KIND_FLAG_EXACT_MATCH)) { 325 // If we block potentially offensive words, and if the word is possibly 326 // offensive, then we don't output it unless it's also an exact match. 327 continue; 328 } 329 suggestions.add(new SuggestedWordInfo(new String(mOutputCodePoints, start, len), 330 mOutputScores[j], mOutputTypes[j], this /* sourceDict */, 331 mSpaceIndices[j] /* indexOfTouchPointOfSecondWord */, 332 mOutputAutoCommitFirstWordConfidence[0])); 333 } 334 } 335 return suggestions; 336 } 337 338 public boolean isValidDictionary() { 339 return mNativeDict != 0; 340 } 341 342 public int getFormatVersion() { 343 return getFormatVersionNative(mNativeDict); 344 } 345 346 @Override 347 public boolean isValidWord(final String word) { 348 return getFrequency(word) != NOT_A_PROBABILITY; 349 } 350 351 @Override 352 public int getFrequency(final String word) { 353 if (word == null) return NOT_A_PROBABILITY; 354 int[] codePoints = StringUtils.toCodePointArray(word); 355 return getProbabilityNative(mNativeDict, codePoints); 356 } 357 358 @UsedForTesting 359 public boolean isValidNgram(final PrevWordsInfo prevWordsInfo, final String word) { 360 return getNgramProbability(prevWordsInfo, word) != NOT_A_PROBABILITY; 361 } 362 363 public int getNgramProbability(final PrevWordsInfo prevWordsInfo, final String word) { 364 if (!prevWordsInfo.isValid() || TextUtils.isEmpty(word)) { 365 return NOT_A_PROBABILITY; 366 } 367 final int[] codePoints0 = StringUtils.toCodePointArray(prevWordsInfo.mPrevWord); 368 final int[] codePoints1 = StringUtils.toCodePointArray(word); 369 return getBigramProbabilityNative(mNativeDict, codePoints0, 370 prevWordsInfo.mIsBeginningOfSentence, codePoints1); 371 } 372 373 public WordProperty getWordProperty(final String word) { 374 if (TextUtils.isEmpty(word)) { 375 return null; 376 } 377 final int[] codePoints = StringUtils.toCodePointArray(word); 378 final int[] outCodePoints = new int[MAX_WORD_LENGTH]; 379 final boolean[] outFlags = new boolean[FORMAT_WORD_PROPERTY_OUTPUT_FLAG_COUNT]; 380 final int[] outProbabilityInfo = 381 new int[FORMAT_WORD_PROPERTY_OUTPUT_PROBABILITY_INFO_COUNT]; 382 final ArrayList<int[]> outBigramTargets = new ArrayList<>(); 383 final ArrayList<int[]> outBigramProbabilityInfo = new ArrayList<>(); 384 final ArrayList<int[]> outShortcutTargets = new ArrayList<>(); 385 final ArrayList<Integer> outShortcutProbabilities = new ArrayList<>(); 386 getWordPropertyNative(mNativeDict, codePoints, outCodePoints, outFlags, outProbabilityInfo, 387 outBigramTargets, outBigramProbabilityInfo, outShortcutTargets, 388 outShortcutProbabilities); 389 return new WordProperty(codePoints, 390 outFlags[FORMAT_WORD_PROPERTY_IS_NOT_A_WORD_INDEX], 391 outFlags[FORMAT_WORD_PROPERTY_IS_BLACKLISTED_INDEX], 392 outFlags[FORMAT_WORD_PROPERTY_HAS_BIGRAMS_INDEX], 393 outFlags[FORMAT_WORD_PROPERTY_HAS_SHORTCUTS_INDEX], outProbabilityInfo, 394 outBigramTargets, outBigramProbabilityInfo, outShortcutTargets, 395 outShortcutProbabilities); 396 } 397 398 public static class GetNextWordPropertyResult { 399 public WordProperty mWordProperty; 400 public int mNextToken; 401 402 public GetNextWordPropertyResult(final WordProperty wordProperty, final int nextToken) { 403 mWordProperty = wordProperty; 404 mNextToken = nextToken; 405 } 406 } 407 408 /** 409 * Method to iterate all words in the dictionary for makedict. 410 * If token is 0, this method newly starts iterating the dictionary. 411 */ 412 public GetNextWordPropertyResult getNextWordProperty(final int token) { 413 final int[] codePoints = new int[MAX_WORD_LENGTH]; 414 final int nextToken = getNextWordNative(mNativeDict, token, codePoints); 415 final String word = StringUtils.getStringFromNullTerminatedCodePointArray(codePoints); 416 return new GetNextWordPropertyResult(getWordProperty(word), nextToken); 417 } 418 419 // Add a unigram entry to binary dictionary with unigram attributes in native code. 420 public boolean addUnigramEntry(final String word, final int probability, 421 final String shortcutTarget, final int shortcutProbability, 422 final boolean isBeginningOfSentence, final boolean isNotAWord, 423 final boolean isBlacklisted, final int timestamp) { 424 if (word == null || (word.isEmpty() && !isBeginningOfSentence)) { 425 return false; 426 } 427 final int[] codePoints = StringUtils.toCodePointArray(word); 428 final int[] shortcutTargetCodePoints = (shortcutTarget != null) ? 429 StringUtils.toCodePointArray(shortcutTarget) : null; 430 if (!addUnigramWordNative(mNativeDict, codePoints, probability, shortcutTargetCodePoints, 431 shortcutProbability, isBeginningOfSentence, isNotAWord, isBlacklisted, timestamp)) { 432 return false; 433 } 434 mHasUpdated = true; 435 return true; 436 } 437 438 // Add an n-gram entry to the binary dictionary with timestamp in native code. 439 public boolean addNgramEntry(final PrevWordsInfo prevWordsInfo, final String word, 440 final int probability, final int timestamp) { 441 if (!prevWordsInfo.isValid() || TextUtils.isEmpty(word)) { 442 return false; 443 } 444 final int[] codePoints0 = StringUtils.toCodePointArray(prevWordsInfo.mPrevWord); 445 final int[] codePoints1 = StringUtils.toCodePointArray(word); 446 if (!addBigramWordsNative(mNativeDict, codePoints0, prevWordsInfo.mIsBeginningOfSentence, 447 codePoints1, probability, timestamp)) { 448 return false; 449 } 450 mHasUpdated = true; 451 return true; 452 } 453 454 // Remove an n-gram entry from the binary dictionary in native code. 455 public boolean removeNgramEntry(final PrevWordsInfo prevWordsInfo, final String word) { 456 if (!prevWordsInfo.isValid() || TextUtils.isEmpty(word)) { 457 return false; 458 } 459 final int[] codePoints0 = StringUtils.toCodePointArray(prevWordsInfo.mPrevWord); 460 final int[] codePoints1 = StringUtils.toCodePointArray(word); 461 if (!removeBigramWordsNative(mNativeDict, codePoints0, prevWordsInfo.mIsBeginningOfSentence, 462 codePoints1)) { 463 return false; 464 } 465 mHasUpdated = true; 466 return true; 467 } 468 469 public void addMultipleDictionaryEntries(final LanguageModelParam[] languageModelParams) { 470 if (!isValidDictionary()) return; 471 int processedParamCount = 0; 472 while (processedParamCount < languageModelParams.length) { 473 if (needsToRunGC(true /* mindsBlockByGC */)) { 474 flushWithGC(); 475 } 476 processedParamCount = addMultipleDictionaryEntriesNative(mNativeDict, 477 languageModelParams, processedParamCount); 478 mHasUpdated = true; 479 if (processedParamCount <= 0) { 480 return; 481 } 482 } 483 } 484 485 private void reopen() { 486 close(); 487 final File dictFile = new File(mDictFilePath); 488 // WARNING: Because we pass 0 as the offset and file.length() as the length, this can 489 // only be called for actual files. Right now it's only called by the flush() family of 490 // functions, which require an updatable dictionary, so it's okay. But beware. 491 loadDictionary(dictFile.getAbsolutePath(), 0 /* startOffset */, 492 dictFile.length(), mIsUpdatable); 493 } 494 495 // Flush to dict file if the dictionary has been updated. 496 public boolean flush() { 497 if (!isValidDictionary()) return false; 498 if (mHasUpdated) { 499 if (!flushNative(mNativeDict, mDictFilePath)) { 500 return false; 501 } 502 reopen(); 503 } 504 return true; 505 } 506 507 // Run GC and flush to dict file if the dictionary has been updated. 508 public boolean flushWithGCIfHasUpdated() { 509 if (mHasUpdated) { 510 return flushWithGC(); 511 } 512 return true; 513 } 514 515 // Run GC and flush to dict file. 516 public boolean flushWithGC() { 517 if (!isValidDictionary()) return false; 518 if (!flushWithGCNative(mNativeDict, mDictFilePath)) { 519 return false; 520 } 521 reopen(); 522 return true; 523 } 524 525 /** 526 * Checks whether GC is needed to run or not. 527 * @param mindsBlockByGC Whether to mind operations blocked by GC. We don't need to care about 528 * the blocking in some situations such as in idle time or just before closing. 529 * @return whether GC is needed to run or not. 530 */ 531 public boolean needsToRunGC(final boolean mindsBlockByGC) { 532 if (!isValidDictionary()) return false; 533 return needsToRunGCNative(mNativeDict, mindsBlockByGC); 534 } 535 536 public boolean migrateTo(final int newFormatVersion) { 537 if (!isValidDictionary()) { 538 return false; 539 } 540 final String tmpDictFilePath = mDictFilePath + DICT_FILE_NAME_SUFFIX_FOR_MIGRATION; 541 if (!migrateNative(mNativeDict, tmpDictFilePath, newFormatVersion)) { 542 return false; 543 } 544 close(); 545 final File dictFile = new File(mDictFilePath); 546 final File tmpDictFile = new File(tmpDictFilePath); 547 if (!FileUtils.deleteRecursively(dictFile)) { 548 return false; 549 } 550 if (!BinaryDictionaryUtils.renameDict(tmpDictFile, dictFile)) { 551 return false; 552 } 553 loadDictionary(dictFile.getAbsolutePath(), 0 /* startOffset */, 554 dictFile.length(), mIsUpdatable); 555 return true; 556 } 557 558 @UsedForTesting 559 public String getPropertyForTest(final String query) { 560 if (!isValidDictionary()) return ""; 561 return getPropertyNative(mNativeDict, query); 562 } 563 564 @Override 565 public boolean shouldAutoCommit(final SuggestedWordInfo candidate) { 566 return candidate.mAutoCommitFirstWordConfidence > CONFIDENCE_TO_AUTO_COMMIT; 567 } 568 569 @Override 570 public void close() { 571 synchronized (mDicTraverseSessions) { 572 final int sessionsSize = mDicTraverseSessions.size(); 573 for (int index = 0; index < sessionsSize; ++index) { 574 final DicTraverseSession traverseSession = mDicTraverseSessions.valueAt(index); 575 if (traverseSession != null) { 576 traverseSession.close(); 577 } 578 } 579 mDicTraverseSessions.clear(); 580 } 581 closeInternalLocked(); 582 } 583 584 private synchronized void closeInternalLocked() { 585 if (mNativeDict != 0) { 586 closeNative(mNativeDict); 587 mNativeDict = 0; 588 } 589 } 590 591 // TODO: Manage BinaryDictionary instances without using WeakReference or something. 592 @Override 593 protected void finalize() throws Throwable { 594 try { 595 closeInternalLocked(); 596 } finally { 597 super.finalize(); 598 } 599 } 600} 601