BinaryDictionary.java revision da0ea7603bdfb71a6134617d19535960aca4f40e
1258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani/*
2258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani * Copyright (C) 2008 The Android Open Source Project
3258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani *
4258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani * Licensed under the Apache License, Version 2.0 (the "License");
5258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani * you may not use this file except in compliance with the License.
6258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani * You may obtain a copy of the License at
7258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani *
8258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani *      http://www.apache.org/licenses/LICENSE-2.0
9258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani *
10258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani * Unless required by applicable law or agreed to in writing, software
11258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani * distributed under the License is distributed on an "AS IS" BASIS,
12258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani * See the License for the specific language governing permissions and
14258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani * limitations under the License.
15258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani */
16258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani
17258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasanipackage com.android.inputmethod.latin;
180e8d7d63ba439cc0604af7055679dae3d30fdc48Amith Yamasani
19409297da182267465adbc21cfb75a23e8d678117Dianne Hackbornimport android.text.TextUtils;
20a8a9bd65bf5865d83ef44f54552ca39522bfbcf0Dianne Hackbornimport android.util.Log;
21258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasaniimport android.util.SparseArray;
22258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani
2327bd34d9d9fe99f11b80aa0bbdb402fb47ef4158Jeff Sharkeyimport com.android.inputmethod.annotations.UsedForTesting;
246794458f8626c3be27eac3db3a5c89d94f132675Maggie Benthallimport com.android.inputmethod.keyboard.ProximityInfo;
251bdff9139fd412b36d5d2d783574b6418fcb198aAdrian Roosimport com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
264f58263d02f296430a9653126d28501e95c7bb6cAmith Yamasaniimport com.android.inputmethod.latin.makedict.DictionaryHeader;
274f58263d02f296430a9653126d28501e95c7bb6cAmith Yamasaniimport com.android.inputmethod.latin.makedict.FormatSpec;
281e9c21871e81642669079cd290ef47818a3165bdAmith Yamasaniimport com.android.inputmethod.latin.makedict.FormatSpec.DictionaryOptions;
29258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasaniimport com.android.inputmethod.latin.makedict.UnsupportedFormatException;
301c7c319bb89b9988bfd12afc3e8d89449fd163fcJason Monkimport com.android.inputmethod.latin.makedict.WordProperty;
31258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasaniimport com.android.inputmethod.latin.settings.NativeSuggestOptions;
326794458f8626c3be27eac3db3a5c89d94f132675Maggie Benthallimport com.android.inputmethod.latin.utils.BinaryDictionaryUtils;
336794458f8626c3be27eac3db3a5c89d94f132675Maggie Benthallimport com.android.inputmethod.latin.utils.FileUtils;
341bdff9139fd412b36d5d2d783574b6418fcb198aAdrian Roosimport com.android.inputmethod.latin.utils.JniUtils;
354f58263d02f296430a9653126d28501e95c7bb6cAmith Yamasaniimport com.android.inputmethod.latin.utils.LanguageModelParam;
36258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasaniimport com.android.inputmethod.latin.utils.StringUtils;
37258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani
38258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasaniimport java.io.File;
39258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasaniimport java.util.ArrayList;
40258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasaniimport java.util.Arrays;
41258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasaniimport java.util.HashMap;
42258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasaniimport java.util.Locale;
43258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasaniimport java.util.Map;
44258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani
45258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani/**
46258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani * Implements a static, compacted, binary dictionary of standard words.
47e4cf73437a18c1444055f88a1fcc0d146ec23ac5Amith Yamasani */
4826af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani// TODO: All methods which should be locked need to have a suffix "Locked".
4971e6c697e54a43d357cc25d87a446d140f17396aAmith Yamasanipublic final class BinaryDictionary extends Dictionary {
5026af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    private static final String TAG = BinaryDictionary.class.getSimpleName();
5126af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani
5226af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    // Must be equal to MAX_WORD_LENGTH in native/jni/src/defines.h
53e4cf73437a18c1444055f88a1fcc0d146ec23ac5Amith Yamasani    private static final int MAX_WORD_LENGTH = Constants.DICTIONARY_MAX_WORD_LENGTH;
54e4cf73437a18c1444055f88a1fcc0d146ec23ac5Amith Yamasani    // Must be equal to MAX_RESULTS in native/jni/src/defines.h
55e4cf73437a18c1444055f88a1fcc0d146ec23ac5Amith Yamasani    private static final int MAX_RESULTS = 18;
5671e6c697e54a43d357cc25d87a446d140f17396aAmith Yamasani    // The cutoff returned by native for auto-commit confidence.
57e4cf73437a18c1444055f88a1fcc0d146ec23ac5Amith Yamasani    // Must be equal to CONFIDENCE_TO_AUTO_COMMIT in native/jni/src/defines.h
58e4cf73437a18c1444055f88a1fcc0d146ec23ac5Amith Yamasani    private static final int CONFIDENCE_TO_AUTO_COMMIT = 1000000;
5926af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani
602cb384f42569f36e19ecee60da259d69048fdd85Julia Reynolds    @UsedForTesting
61150514bd03312ab0a890cc040019cfbc73eb077cAmith Yamasani    public static final String UNIGRAM_COUNT_QUERY = "UNIGRAM_COUNT";
6226af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    @UsedForTesting
6326af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    public static final String BIGRAM_COUNT_QUERY = "BIGRAM_COUNT";
6426af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    @UsedForTesting
65e4cf73437a18c1444055f88a1fcc0d146ec23ac5Amith Yamasani    public static final String MAX_UNIGRAM_COUNT_QUERY = "MAX_UNIGRAM_COUNT";
66e4cf73437a18c1444055f88a1fcc0d146ec23ac5Amith Yamasani    @UsedForTesting
67e4cf73437a18c1444055f88a1fcc0d146ec23ac5Amith Yamasani    public static final String MAX_BIGRAM_COUNT_QUERY = "MAX_BIGRAM_COUNT";
6871e6c697e54a43d357cc25d87a446d140f17396aAmith Yamasani
69e4cf73437a18c1444055f88a1fcc0d146ec23ac5Amith Yamasani    public static final int NOT_A_VALID_TIMESTAMP = -1;
70e4cf73437a18c1444055f88a1fcc0d146ec23ac5Amith Yamasani
7126af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    // Format to get unigram flags from native side via getWordPropertyNative().
7271e6c697e54a43d357cc25d87a446d140f17396aAmith Yamasani    private static final int FORMAT_WORD_PROPERTY_OUTPUT_FLAG_COUNT = 4;
7326af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    private static final int FORMAT_WORD_PROPERTY_IS_NOT_A_WORD_INDEX = 0;
7426af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    private static final int FORMAT_WORD_PROPERTY_IS_BLACKLISTED_INDEX = 1;
7526af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    private static final int FORMAT_WORD_PROPERTY_HAS_BIGRAMS_INDEX = 2;
76e4cf73437a18c1444055f88a1fcc0d146ec23ac5Amith Yamasani    private static final int FORMAT_WORD_PROPERTY_HAS_SHORTCUTS_INDEX = 3;
77e4cf73437a18c1444055f88a1fcc0d146ec23ac5Amith Yamasani
78e4cf73437a18c1444055f88a1fcc0d146ec23ac5Amith Yamasani    // Format to get probability and historical info from native side via getWordPropertyNative().
7971e6c697e54a43d357cc25d87a446d140f17396aAmith Yamasani    public static final int FORMAT_WORD_PROPERTY_OUTPUT_PROBABILITY_INFO_COUNT = 4;
80e4cf73437a18c1444055f88a1fcc0d146ec23ac5Amith Yamasani    public static final int FORMAT_WORD_PROPERTY_PROBABILITY_INDEX = 0;
81e4cf73437a18c1444055f88a1fcc0d146ec23ac5Amith Yamasani    public static final int FORMAT_WORD_PROPERTY_TIMESTAMP_INDEX = 1;
8226af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    public static final int FORMAT_WORD_PROPERTY_LEVEL_INDEX = 2;
8371e6c697e54a43d357cc25d87a446d140f17396aAmith Yamasani    public static final int FORMAT_WORD_PROPERTY_COUNT_INDEX = 3;
8426af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani
8526af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    public static final String DICT_FILE_NAME_SUFFIX_FOR_MIGRATION = ".migrate";
8626af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani
87e4cf73437a18c1444055f88a1fcc0d146ec23ac5Amith Yamasani    private long mNativeDict;
88e4cf73437a18c1444055f88a1fcc0d146ec23ac5Amith Yamasani    private final Locale mLocale;
89e4cf73437a18c1444055f88a1fcc0d146ec23ac5Amith Yamasani    private final long mDictSize;
9071e6c697e54a43d357cc25d87a446d140f17396aAmith Yamasani    private final String mDictFilePath;
91e4cf73437a18c1444055f88a1fcc0d146ec23ac5Amith Yamasani    private final boolean mIsUpdatable;
9271e6c697e54a43d357cc25d87a446d140f17396aAmith Yamasani    private boolean mHasUpdated;
93150514bd03312ab0a890cc040019cfbc73eb077cAmith Yamasani
9471e6c697e54a43d357cc25d87a446d140f17396aAmith Yamasani    private final int[] mInputCodePoints = new int[MAX_WORD_LENGTH];
95150514bd03312ab0a890cc040019cfbc73eb077cAmith Yamasani    private final int[] mOutputSuggestionCount = new int[1];
96150514bd03312ab0a890cc040019cfbc73eb077cAmith Yamasani    private final int[] mOutputCodePoints = new int[MAX_WORD_LENGTH * MAX_RESULTS];
9726af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    private final int[] mSpaceIndices = new int[MAX_RESULTS];
9826af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    private final int[] mOutputScores = new int[MAX_RESULTS];
9926af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    private final int[] mOutputTypes = new int[MAX_RESULTS];
1006794458f8626c3be27eac3db3a5c89d94f132675Maggie Benthall    // Only one result is ever used
1016794458f8626c3be27eac3db3a5c89d94f132675Maggie Benthall    private final int[] mOutputAutoCommitFirstWordConfidence = new int[1];
1026794458f8626c3be27eac3db3a5c89d94f132675Maggie Benthall    private final float[] mInputOutputLanguageWeight = new float[1];
10371e6c697e54a43d357cc25d87a446d140f17396aAmith Yamasani
1046794458f8626c3be27eac3db3a5c89d94f132675Maggie Benthall    private final NativeSuggestOptions mNativeSuggestOptions = new NativeSuggestOptions();
105a12fccf57d5ec289793699d9b22ff45daccd3933Maggie Benthall
10626af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    private final SparseArray<DicTraverseSession> mDicTraverseSessions = new SparseArray<>();
107a12fccf57d5ec289793699d9b22ff45daccd3933Maggie Benthall
108a12fccf57d5ec289793699d9b22ff45daccd3933Maggie Benthall    // TODO: There should be a way to remove used DicTraverseSession objects from
10926af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    // {@code mDicTraverseSessions}.
11026af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    private DicTraverseSession getTraverseSession(final int traverseSessionId) {
11126af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        synchronized(mDicTraverseSessions) {
112a12fccf57d5ec289793699d9b22ff45daccd3933Maggie Benthall            DicTraverseSession traverseSession = mDicTraverseSessions.get(traverseSessionId);
113a12fccf57d5ec289793699d9b22ff45daccd3933Maggie Benthall            if (traverseSession == null) {
114a12fccf57d5ec289793699d9b22ff45daccd3933Maggie Benthall                traverseSession = new DicTraverseSession(mLocale, mNativeDict, mDictSize);
115a12fccf57d5ec289793699d9b22ff45daccd3933Maggie Benthall                mDicTraverseSessions.put(traverseSessionId, traverseSession);
116a12fccf57d5ec289793699d9b22ff45daccd3933Maggie Benthall            }
117a12fccf57d5ec289793699d9b22ff45daccd3933Maggie Benthall            return traverseSession;
11826af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        }
1191c4c442e4540b16aed7acc345aea26ab101efbf8Nicolas Prevot    }
120a12fccf57d5ec289793699d9b22ff45daccd3933Maggie Benthall
121150514bd03312ab0a890cc040019cfbc73eb077cAmith Yamasani    /**
12226af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani     * Constructs binary dictionary using existing dictionary file.
12326af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani     * @param filename the name of the file to read through native code.
12426af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani     * @param offset the offset of the dictionary data within the file.
125a12fccf57d5ec289793699d9b22ff45daccd3933Maggie Benthall     * @param length the length of the binary data.
126a12fccf57d5ec289793699d9b22ff45daccd3933Maggie Benthall     * @param useFullEditDistance whether to use the full edit distance in suggestions
127a12fccf57d5ec289793699d9b22ff45daccd3933Maggie Benthall     * @param dictType the dictionary type, as a human-readable string
128a12fccf57d5ec289793699d9b22ff45daccd3933Maggie Benthall     * @param isUpdatable whether to open the dictionary file in writable mode.
129a12fccf57d5ec289793699d9b22ff45daccd3933Maggie Benthall     */
130a12fccf57d5ec289793699d9b22ff45daccd3933Maggie Benthall    public BinaryDictionary(final String filename, final long offset, final long length,
13126af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani            final boolean useFullEditDistance, final Locale locale, final String dictType,
132c34dc7cdeb5cae8ca4c731838aafe90ed4c9a2b8Amith Yamasani            final boolean isUpdatable) {
133c34dc7cdeb5cae8ca4c731838aafe90ed4c9a2b8Amith Yamasani        super(dictType);
13426af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        mLocale = locale;
13526af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        mDictSize = length;
13626af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        mDictFilePath = filename;
137a12fccf57d5ec289793699d9b22ff45daccd3933Maggie Benthall        mIsUpdatable = isUpdatable;
138a12fccf57d5ec289793699d9b22ff45daccd3933Maggie Benthall        mHasUpdated = false;
139a12fccf57d5ec289793699d9b22ff45daccd3933Maggie Benthall        mNativeSuggestOptions.setUseFullEditDistance(useFullEditDistance);
140a12fccf57d5ec289793699d9b22ff45daccd3933Maggie Benthall        loadDictionary(filename, offset, length, isUpdatable);
141a12fccf57d5ec289793699d9b22ff45daccd3933Maggie Benthall    }
142b223f73a69f76f5e32a0bca267f945f464776f9dEmily Bernier
14326af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    /**
144b223f73a69f76f5e32a0bca267f945f464776f9dEmily Bernier     * Constructs binary dictionary on memory.
14526af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani     * @param filename the name of the file used to flush.
14626af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani     * @param useFullEditDistance whether to use the full edit distance in suggestions
14726af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani     * @param dictType the dictionary type, as a human-readable string
148b223f73a69f76f5e32a0bca267f945f464776f9dEmily Bernier     * @param formatVersion the format version of the dictionary
149b223f73a69f76f5e32a0bca267f945f464776f9dEmily Bernier     * @param attributeMap the attributes of the dictionary
150b223f73a69f76f5e32a0bca267f945f464776f9dEmily Bernier     */
151b223f73a69f76f5e32a0bca267f945f464776f9dEmily Bernier    @UsedForTesting
152b223f73a69f76f5e32a0bca267f945f464776f9dEmily Bernier    public BinaryDictionary(final String filename, final boolean useFullEditDistance,
153b223f73a69f76f5e32a0bca267f945f464776f9dEmily Bernier            final Locale locale, final String dictType, final long formatVersion,
154150514bd03312ab0a890cc040019cfbc73eb077cAmith Yamasani            final Map<String, String> attributeMap) {
155150514bd03312ab0a890cc040019cfbc73eb077cAmith Yamasani        super(dictType);
156150514bd03312ab0a890cc040019cfbc73eb077cAmith Yamasani        mLocale = locale;
157150514bd03312ab0a890cc040019cfbc73eb077cAmith Yamasani        mDictSize = 0;
15826af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        mDictFilePath = filename;
15926af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        // On memory dictionary is always updatable.
16026af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        mIsUpdatable = true;
161b223f73a69f76f5e32a0bca267f945f464776f9dEmily Bernier        mHasUpdated = false;
162b223f73a69f76f5e32a0bca267f945f464776f9dEmily Bernier        mNativeSuggestOptions.setUseFullEditDistance(useFullEditDistance);
163b223f73a69f76f5e32a0bca267f945f464776f9dEmily Bernier        final String[] keyArray = new String[attributeMap.size()];
164b223f73a69f76f5e32a0bca267f945f464776f9dEmily Bernier        final String[] valueArray = new String[attributeMap.size()];
165b223f73a69f76f5e32a0bca267f945f464776f9dEmily Bernier        int index = 0;
166d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds        for (final String key : attributeMap.keySet()) {
16726af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani            keyArray[index] = key;
168d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds            valueArray[index] = attributeMap.get(key);
16926af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani            index++;
17026af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        }
17126af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        mNativeDict = createOnMemoryNative(formatVersion, locale.toString(), keyArray, valueArray);
172d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds    }
173d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds
174d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds
175d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds    static {
176d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds        JniUtils.loadNativeLibrary();
177d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds    }
17826af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani
179d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds    private static native long openNative(String sourceDir, long dictOffset, long dictSize,
180298f98fa234f353267ddc9c75d58e8cc542c25f1Nicolas Prevot            boolean isUpdatable);
181298f98fa234f353267ddc9c75d58e8cc542c25f1Nicolas Prevot    private static native long createOnMemoryNative(long formatVersion,
18226af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani            String locale, String[] attributeKeyStringArray, String[] attributeValueStringArray);
18326af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    private static native void getHeaderInfoNative(long dict, int[] outHeaderSize,
18426af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani            int[] outFormatVersion, ArrayList<int[]> outAttributeKeys,
185d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds            ArrayList<int[]> outAttributeValues);
186d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds    private static native boolean flushNative(long dict, String filePath);
187d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds    private static native boolean needsToRunGCNative(long dict, boolean mindsBlockByGC);
188d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds    private static native boolean flushWithGCNative(long dict, String filePath);
189d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds    private static native void closeNative(long dict);
190d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds    private static native int getFormatVersionNative(long dict);
19126af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    private static native int getProbabilityNative(long dict, int[] word);
192c34dc7cdeb5cae8ca4c731838aafe90ed4c9a2b8Amith Yamasani    private static native int getMaxProbabilityOfExactMatchesNative(long dict, int[] word);
193c34dc7cdeb5cae8ca4c731838aafe90ed4c9a2b8Amith Yamasani    private static native int getBigramProbabilityNative(long dict, int[] word0,
19426af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani            boolean isBeginningOfSentence, int[] word1);
19526af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    private static native void getWordPropertyNative(long dict, int[] word,
19626af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani            int[] outCodePoints, boolean[] outFlags, int[] outProbabilityInfo,
197d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds            ArrayList<int[]> outBigramTargets, ArrayList<int[]> outBigramProbabilityInfo,
198d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds            ArrayList<int[]> outShortcutTargets, ArrayList<Integer> outShortcutProbabilities);
199d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds    private static native int getNextWordNative(long dict, int token, int[] outCodePoints);
200d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds    private static native void getSuggestionsNative(long dict, long proximityInfo,
201d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds            long traverseSession, int[] xCoordinates, int[] yCoordinates, int[] times,
202d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds            int[] pointerIds, int[] inputCodePoints, int inputSize, int[] suggestOptions,
203e3e314df4d52881225326d426a76e3e7f1bc40d3Stuart Scott            int[] prevWordCodePointArray, boolean isBeginningOfSentence,
204e3e314df4d52881225326d426a76e3e7f1bc40d3Stuart Scott            int[] outputSuggestionCount, int[] outputCodePoints, int[] outputScores,
205e3e314df4d52881225326d426a76e3e7f1bc40d3Stuart Scott            int[] outputIndices, int[] outputTypes, int[] outputAutoCommitFirstWordConfidence,
206e3e314df4d52881225326d426a76e3e7f1bc40d3Stuart Scott            float[] inOutLanguageWeight);
207e3e314df4d52881225326d426a76e3e7f1bc40d3Stuart Scott    private static native boolean addUnigramWordNative(long dict, int[] word, int probability,
208e3e314df4d52881225326d426a76e3e7f1bc40d3Stuart Scott            int[] shortcutTarget, int shortcutProbability, boolean isBeginningOfSentence,
209e3e314df4d52881225326d426a76e3e7f1bc40d3Stuart Scott            boolean isNotAWord, boolean isBlacklisted, int timestamp);
210e3e314df4d52881225326d426a76e3e7f1bc40d3Stuart Scott    private static native boolean removeUnigramWordNative(long dict, int[] word);
211e3e314df4d52881225326d426a76e3e7f1bc40d3Stuart Scott    private static native boolean addBigramWordsNative(long dict, int[] word0,
212e3e314df4d52881225326d426a76e3e7f1bc40d3Stuart Scott            boolean isBeginningOfSentence, int[] word1, int probability, int timestamp);
213e3e314df4d52881225326d426a76e3e7f1bc40d3Stuart Scott    private static native boolean removeBigramWordsNative(long dict, int[] word0,
214e3e314df4d52881225326d426a76e3e7f1bc40d3Stuart Scott            boolean isBeginningOfSentence, int[] word1);
215e3e314df4d52881225326d426a76e3e7f1bc40d3Stuart Scott    private static native int addMultipleDictionaryEntriesNative(long dict,
216e3e314df4d52881225326d426a76e3e7f1bc40d3Stuart Scott            LanguageModelParam[] languageModelParams, int startIndex);
21726af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    private static native String getPropertyNative(long dict, String query);
218c34dc7cdeb5cae8ca4c731838aafe90ed4c9a2b8Amith Yamasani    private static native boolean isCorruptedNative(long dict);
219c34dc7cdeb5cae8ca4c731838aafe90ed4c9a2b8Amith Yamasani    private static native boolean migrateNative(long dict, String dictFilePath,
220150514bd03312ab0a890cc040019cfbc73eb077cAmith Yamasani            long newFormatVersion);
221150514bd03312ab0a890cc040019cfbc73eb077cAmith Yamasani
22226af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    // TODO: Move native dict into session
22326af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    private final void loadDictionary(final String path, final long startOffset,
22426af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani            final long length, final boolean isUpdatable) {
225d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds        mHasUpdated = false;
226d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds        mNativeDict = openNative(path, startOffset, length, isUpdatable);
227d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds    }
228d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds
229d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds    // TODO: Check isCorrupted() for main dictionaries.
230d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds    public boolean isCorrupted() {
23126af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        if (!isValidDictionary()) {
232c34dc7cdeb5cae8ca4c731838aafe90ed4c9a2b8Amith Yamasani            return false;
233c34dc7cdeb5cae8ca4c731838aafe90ed4c9a2b8Amith Yamasani        }
234150514bd03312ab0a890cc040019cfbc73eb077cAmith Yamasani        if (!isCorruptedNative(mNativeDict)) {
235150514bd03312ab0a890cc040019cfbc73eb077cAmith Yamasani            return false;
23626af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        }
23726af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        // TODO: Record the corruption.
23826af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        Log.e(TAG, "BinaryDictionary (" + mDictFilePath + ") is corrupted.");
239d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds        Log.e(TAG, "locale: " + mLocale);
240d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds        Log.e(TAG, "dict size: " + mDictSize);
241d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds        Log.e(TAG, "updatable: " + mIsUpdatable);
242d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds        return true;
243d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds    }
244d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds
24526af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    public DictionaryHeader getHeader() throws UnsupportedFormatException {
246d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds        if (mNativeDict == 0) {
24726af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani            return null;
24826af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        }
24926af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        final int[] outHeaderSize = new int[1];
250d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds        final int[] outFormatVersion = new int[1];
251d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds        final ArrayList<int[]> outAttributeKeys = new ArrayList<>();
252d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds        final ArrayList<int[]> outAttributeValues = new ArrayList<>();
253d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds        getHeaderInfoNative(mNativeDict, outHeaderSize, outFormatVersion, outAttributeKeys,
254d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds                outAttributeValues);
255d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds        final HashMap<String, String> attributes = new HashMap<>();
25626af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        for (int i = 0; i < outAttributeKeys.size(); i++) {
257c34dc7cdeb5cae8ca4c731838aafe90ed4c9a2b8Amith Yamasani            final String attributeKey = StringUtils.getStringFromNullTerminatedCodePointArray(
258c34dc7cdeb5cae8ca4c731838aafe90ed4c9a2b8Amith Yamasani                    outAttributeKeys.get(i));
259150514bd03312ab0a890cc040019cfbc73eb077cAmith Yamasani            final String attributeValue = StringUtils.getStringFromNullTerminatedCodePointArray(
260150514bd03312ab0a890cc040019cfbc73eb077cAmith Yamasani                    outAttributeValues.get(i));
26126af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani            attributes.put(attributeKey, attributeValue);
26226af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        }
26326af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        final boolean hasHistoricalInfo = DictionaryHeader.ATTRIBUTE_VALUE_TRUE.equals(
264d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds                attributes.get(DictionaryHeader.HAS_HISTORICAL_INFO_KEY));
265d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds        return new DictionaryHeader(outHeaderSize[0], new DictionaryOptions(attributes),
266d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds                new FormatSpec.FormatOptions(outFormatVersion[0], hasHistoricalInfo));
267d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds    }
268d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds
269d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds    @Override
27026af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
271c34dc7cdeb5cae8ca4c731838aafe90ed4c9a2b8Amith Yamasani            final PrevWordsInfo prevWordsInfo, final ProximityInfo proximityInfo,
272c34dc7cdeb5cae8ca4c731838aafe90ed4c9a2b8Amith Yamasani            final boolean blockOffensiveWords, final int[] additionalFeaturesOptions,
273150514bd03312ab0a890cc040019cfbc73eb077cAmith Yamasani            final int sessionId, final float[] inOutLanguageWeight) {
274150514bd03312ab0a890cc040019cfbc73eb077cAmith Yamasani        if (!isValidDictionary()) {
27526af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani            return null;
27626af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        }
27726af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani
278d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds        Arrays.fill(mInputCodePoints, Constants.NOT_A_CODE);
279d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds        // TODO: toLowerCase in the native code
280d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds        final int[] prevWordCodePointArray = (null == prevWordsInfo.mPrevWord)
281d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds                ? null : StringUtils.toCodePointArray(prevWordsInfo.mPrevWord);
282d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds        final InputPointers inputPointers = composer.getInputPointers();
283d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds        final boolean isGesture = composer.isBatchMode();
28426af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        final int inputSize;
285c617f815453b6e70a0165924907e69a0f993e651Julia Reynolds        if (!isGesture) {
286c617f815453b6e70a0165924907e69a0f993e651Julia Reynolds            inputSize = composer.copyCodePointsExceptTrailingSingleQuotesAndReturnCodePointCount(
287c617f815453b6e70a0165924907e69a0f993e651Julia Reynolds                    mInputCodePoints);
288c617f815453b6e70a0165924907e69a0f993e651Julia Reynolds            if (inputSize < 0) {
289c617f815453b6e70a0165924907e69a0f993e651Julia Reynolds                return null;
290c617f815453b6e70a0165924907e69a0f993e651Julia Reynolds            }
291c617f815453b6e70a0165924907e69a0f993e651Julia Reynolds        } else {
292c617f815453b6e70a0165924907e69a0f993e651Julia Reynolds            inputSize = inputPointers.getPointerSize();
293c617f815453b6e70a0165924907e69a0f993e651Julia Reynolds        }
294c617f815453b6e70a0165924907e69a0f993e651Julia Reynolds
29526af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        mNativeSuggestOptions.setIsGesture(isGesture);
29626af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        mNativeSuggestOptions.setBlockOffensiveWords(blockOffensiveWords);
29726af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        mNativeSuggestOptions.setAdditionalFeaturesOptions(additionalFeaturesOptions);
298d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds        if (inOutLanguageWeight != null) {
299d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds            mInputOutputLanguageWeight[0] = inOutLanguageWeight[0];
300d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds        } else {
30136fbc8d6453da438a8ab83352ff1bcfcba5f25b5Julia Reynolds            mInputOutputLanguageWeight[0] = Dictionary.NOT_A_LANGUAGE_WEIGHT;
302d46d0f9dcd72dfaa93a57d07d896def6ce53bbaeJulia Reynolds        }
303394a6cdd987fed79bd040f39e2d3e47d4484bab4Emily Bernier        // proximityInfo and/or prevWordForBigrams may not be null.
30426af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        getSuggestionsNative(mNativeDict, proximityInfo.getNativeProximityInfo(),
305c34dc7cdeb5cae8ca4c731838aafe90ed4c9a2b8Amith Yamasani                getTraverseSession(sessionId).getSession(), inputPointers.getXCoordinates(),
306c34dc7cdeb5cae8ca4c731838aafe90ed4c9a2b8Amith Yamasani                inputPointers.getYCoordinates(), inputPointers.getTimes(),
30726af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani                inputPointers.getPointerIds(), mInputCodePoints, inputSize,
30826af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani                mNativeSuggestOptions.getOptions(), prevWordCodePointArray,
30926af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani                prevWordsInfo.mIsBeginningOfSentence, mOutputSuggestionCount,
310394a6cdd987fed79bd040f39e2d3e47d4484bab4Emily Bernier                mOutputCodePoints, mOutputScores, mSpaceIndices, mOutputTypes,
311394a6cdd987fed79bd040f39e2d3e47d4484bab4Emily Bernier                mOutputAutoCommitFirstWordConfidence, mInputOutputLanguageWeight);
312394a6cdd987fed79bd040f39e2d3e47d4484bab4Emily Bernier        if (inOutLanguageWeight != null) {
313394a6cdd987fed79bd040f39e2d3e47d4484bab4Emily Bernier            inOutLanguageWeight[0] = mInputOutputLanguageWeight[0];
314394a6cdd987fed79bd040f39e2d3e47d4484bab4Emily Bernier        }
315394a6cdd987fed79bd040f39e2d3e47d4484bab4Emily Bernier        final int count = mOutputSuggestionCount[0];
31626af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        final ArrayList<SuggestedWordInfo> suggestions = new ArrayList<>();
317c34dc7cdeb5cae8ca4c731838aafe90ed4c9a2b8Amith Yamasani        for (int j = 0; j < count; ++j) {
318c34dc7cdeb5cae8ca4c731838aafe90ed4c9a2b8Amith Yamasani            final int start = j * MAX_WORD_LENGTH;
31926af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani            int len = 0;
32026af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani            while (len < MAX_WORD_LENGTH && mOutputCodePoints[start + len] != 0) {
32126af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani                ++len;
322394a6cdd987fed79bd040f39e2d3e47d4484bab4Emily Bernier            }
323394a6cdd987fed79bd040f39e2d3e47d4484bab4Emily Bernier            if (len > 0) {
324394a6cdd987fed79bd040f39e2d3e47d4484bab4Emily Bernier                suggestions.add(new SuggestedWordInfo(new String(mOutputCodePoints, start, len),
325394a6cdd987fed79bd040f39e2d3e47d4484bab4Emily Bernier                        mOutputScores[j], mOutputTypes[j], this /* sourceDict */,
326394a6cdd987fed79bd040f39e2d3e47d4484bab4Emily Bernier                        mSpaceIndices[j] /* indexOfTouchPointOfSecondWord */,
327394a6cdd987fed79bd040f39e2d3e47d4484bab4Emily Bernier                        mOutputAutoCommitFirstWordConfidence[0]));
32826af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani            }
329c34dc7cdeb5cae8ca4c731838aafe90ed4c9a2b8Amith Yamasani        }
330c34dc7cdeb5cae8ca4c731838aafe90ed4c9a2b8Amith Yamasani        return suggestions;
33126af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    }
33226af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani
33326af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    public boolean isValidDictionary() {
334394a6cdd987fed79bd040f39e2d3e47d4484bab4Emily Bernier        return mNativeDict != 0;
335394a6cdd987fed79bd040f39e2d3e47d4484bab4Emily Bernier    }
336394a6cdd987fed79bd040f39e2d3e47d4484bab4Emily Bernier
337394a6cdd987fed79bd040f39e2d3e47d4484bab4Emily Bernier    public int getFormatVersion() {
338394a6cdd987fed79bd040f39e2d3e47d4484bab4Emily Bernier        return getFormatVersionNative(mNativeDict);
3399f6c25f57e26f3e2f9c744547a139d14b7d3db5cAmith Yamasani    }
34026af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani
341390989da1967f9d385212cd2e22a50589ce69046Amith Yamasani    @Override
342390989da1967f9d385212cd2e22a50589ce69046Amith Yamasani    public boolean isInDictionary(final String word) {
343150514bd03312ab0a890cc040019cfbc73eb077cAmith Yamasani        return getFrequency(word) != NOT_A_PROBABILITY;
344150514bd03312ab0a890cc040019cfbc73eb077cAmith Yamasani    }
34526af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani
34626af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    @Override
34726af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    public int getFrequency(final String word) {
348390989da1967f9d385212cd2e22a50589ce69046Amith Yamasani        if (TextUtils.isEmpty(word)) return NOT_A_PROBABILITY;
349390989da1967f9d385212cd2e22a50589ce69046Amith Yamasani        int[] codePoints = StringUtils.toCodePointArray(word);
350390989da1967f9d385212cd2e22a50589ce69046Amith Yamasani        return getProbabilityNative(mNativeDict, codePoints);
351390989da1967f9d385212cd2e22a50589ce69046Amith Yamasani    }
352390989da1967f9d385212cd2e22a50589ce69046Amith Yamasani
353390989da1967f9d385212cd2e22a50589ce69046Amith Yamasani    @Override
35426af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    public int getMaxFrequencyOfExactMatches(final String word) {
355c34dc7cdeb5cae8ca4c731838aafe90ed4c9a2b8Amith Yamasani        if (TextUtils.isEmpty(word)) return NOT_A_PROBABILITY;
35626af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        int[] codePoints = StringUtils.toCodePointArray(word);
35726af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        return getMaxProbabilityOfExactMatchesNative(mNativeDict, codePoints);
35826af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    }
3599f6c25f57e26f3e2f9c744547a139d14b7d3db5cAmith Yamasani
3609f6c25f57e26f3e2f9c744547a139d14b7d3db5cAmith Yamasani    @UsedForTesting
3619f6c25f57e26f3e2f9c744547a139d14b7d3db5cAmith Yamasani    public boolean isValidNgram(final PrevWordsInfo prevWordsInfo, final String word) {
362390989da1967f9d385212cd2e22a50589ce69046Amith Yamasani        return getNgramProbability(prevWordsInfo, word) != NOT_A_PROBABILITY;
3639f6c25f57e26f3e2f9c744547a139d14b7d3db5cAmith Yamasani    }
3641c7c319bb89b9988bfd12afc3e8d89449fd163fcJason Monk
3652cc03e5606ad7cd473283898400506d5ac2237baJeff Sharkey    public int getNgramProbability(final PrevWordsInfo prevWordsInfo, final String word) {
3662cc03e5606ad7cd473283898400506d5ac2237baJeff Sharkey        if (!prevWordsInfo.isValid() || TextUtils.isEmpty(word)) {
3672cc03e5606ad7cd473283898400506d5ac2237baJeff Sharkey            return NOT_A_PROBABILITY;
3682cc03e5606ad7cd473283898400506d5ac2237baJeff Sharkey        }
3692cc03e5606ad7cd473283898400506d5ac2237baJeff Sharkey        final int[] codePoints0 = StringUtils.toCodePointArray(prevWordsInfo.mPrevWord);
3702cc03e5606ad7cd473283898400506d5ac2237baJeff Sharkey        final int[] codePoints1 = StringUtils.toCodePointArray(word);
3712cc03e5606ad7cd473283898400506d5ac2237baJeff Sharkey        return getBigramProbabilityNative(mNativeDict, codePoints0,
3722cc03e5606ad7cd473283898400506d5ac2237baJeff Sharkey                prevWordsInfo.mIsBeginningOfSentence, codePoints1);
3732cc03e5606ad7cd473283898400506d5ac2237baJeff Sharkey    }
3742cc03e5606ad7cd473283898400506d5ac2237baJeff Sharkey
3752cc03e5606ad7cd473283898400506d5ac2237baJeff Sharkey    public WordProperty getWordProperty(final String word) {
3762cc03e5606ad7cd473283898400506d5ac2237baJeff Sharkey        if (TextUtils.isEmpty(word)) {
37726af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani            return null;
3781c7c319bb89b9988bfd12afc3e8d89449fd163fcJason Monk        }
3791c7c319bb89b9988bfd12afc3e8d89449fd163fcJason Monk        final int[] codePoints = StringUtils.toCodePointArray(word);
3801c7c319bb89b9988bfd12afc3e8d89449fd163fcJason Monk        final int[] outCodePoints = new int[MAX_WORD_LENGTH];
3811c7c319bb89b9988bfd12afc3e8d89449fd163fcJason Monk        final boolean[] outFlags = new boolean[FORMAT_WORD_PROPERTY_OUTPUT_FLAG_COUNT];
3821c7c319bb89b9988bfd12afc3e8d89449fd163fcJason Monk        final int[] outProbabilityInfo =
3831c7c319bb89b9988bfd12afc3e8d89449fd163fcJason Monk                new int[FORMAT_WORD_PROPERTY_OUTPUT_PROBABILITY_INFO_COUNT];
3841c7c319bb89b9988bfd12afc3e8d89449fd163fcJason Monk        final ArrayList<int[]> outBigramTargets = new ArrayList<>();
3851c7c319bb89b9988bfd12afc3e8d89449fd163fcJason Monk        final ArrayList<int[]> outBigramProbabilityInfo = new ArrayList<>();
386c34dc7cdeb5cae8ca4c731838aafe90ed4c9a2b8Amith Yamasani        final ArrayList<int[]> outShortcutTargets = new ArrayList<>();
387c34dc7cdeb5cae8ca4c731838aafe90ed4c9a2b8Amith Yamasani        final ArrayList<Integer> outShortcutProbabilities = new ArrayList<>();
38826af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        getWordPropertyNative(mNativeDict, codePoints, outCodePoints, outFlags, outProbabilityInfo,
38926af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani                outBigramTargets, outBigramProbabilityInfo, outShortcutTargets,
39026af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani                outShortcutProbabilities);
3911c7c319bb89b9988bfd12afc3e8d89449fd163fcJason Monk        return new WordProperty(codePoints,
3921c7c319bb89b9988bfd12afc3e8d89449fd163fcJason Monk                outFlags[FORMAT_WORD_PROPERTY_IS_NOT_A_WORD_INDEX],
3931c7c319bb89b9988bfd12afc3e8d89449fd163fcJason Monk                outFlags[FORMAT_WORD_PROPERTY_IS_BLACKLISTED_INDEX],
3941c7c319bb89b9988bfd12afc3e8d89449fd163fcJason Monk                outFlags[FORMAT_WORD_PROPERTY_HAS_BIGRAMS_INDEX],
3951c7c319bb89b9988bfd12afc3e8d89449fd163fcJason Monk                outFlags[FORMAT_WORD_PROPERTY_HAS_SHORTCUTS_INDEX], outProbabilityInfo,
396f1939901d2ed0480069f0b23be64f122fce93995Nicolas Prevot                outBigramTargets, outBigramProbabilityInfo, outShortcutTargets,
39726af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani                outShortcutProbabilities);
398f1939901d2ed0480069f0b23be64f122fce93995Nicolas Prevot    }
399f1939901d2ed0480069f0b23be64f122fce93995Nicolas Prevot
400f1939901d2ed0480069f0b23be64f122fce93995Nicolas Prevot    public static class GetNextWordPropertyResult {
40126af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        public WordProperty mWordProperty;
40226af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        public int mNextToken;
40326af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani
404f1939901d2ed0480069f0b23be64f122fce93995Nicolas Prevot        public GetNextWordPropertyResult(final WordProperty wordProperty, final int nextToken) {
405f1939901d2ed0480069f0b23be64f122fce93995Nicolas Prevot            mWordProperty = wordProperty;
406f1939901d2ed0480069f0b23be64f122fce93995Nicolas Prevot            mNextToken = nextToken;
407f1939901d2ed0480069f0b23be64f122fce93995Nicolas Prevot        }
408f1939901d2ed0480069f0b23be64f122fce93995Nicolas Prevot    }
40926af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani
41026af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    /**
41126af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani     * Method to iterate all words in the dictionary for makedict.
41226af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani     * If token is 0, this method newly starts iterating the dictionary.
41326af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani     */
41426af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    public GetNextWordPropertyResult getNextWordProperty(final int token) {
41526af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        final int[] codePoints = new int[MAX_WORD_LENGTH];
41626af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        final int nextToken = getNextWordNative(mNativeDict, token, codePoints);
41726af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        final String word = StringUtils.getStringFromNullTerminatedCodePointArray(codePoints);
41826af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani        return new GetNextWordPropertyResult(getWordProperty(word), nextToken);
41926af829fd70609cf073b56e54e1f78faf83a5e8bAmith Yamasani    }
42053d63dcd04da3c1d5187b29f6530c2dcac9d516cSander Alewijnse
421f3ece36535d4999cf2bfd2175a33da6c3cdf298eBenjamin Franz    // Add a unigram entry to binary dictionary with unigram attributes in native code.
422f3ece36535d4999cf2bfd2175a33da6c3cdf298eBenjamin Franz    public boolean addUnigramEntry(final String word, final int probability,
423f3ece36535d4999cf2bfd2175a33da6c3cdf298eBenjamin Franz            final String shortcutTarget, final int shortcutProbability,
424f3ece36535d4999cf2bfd2175a33da6c3cdf298eBenjamin Franz            final boolean isBeginningOfSentence, final boolean isNotAWord,
425f3ece36535d4999cf2bfd2175a33da6c3cdf298eBenjamin Franz            final boolean isBlacklisted, final int timestamp) {
426f3ece36535d4999cf2bfd2175a33da6c3cdf298eBenjamin Franz        if (word == null || (word.isEmpty() && !isBeginningOfSentence)) {
427f3ece36535d4999cf2bfd2175a33da6c3cdf298eBenjamin Franz            return false;
428f3ece36535d4999cf2bfd2175a33da6c3cdf298eBenjamin Franz        }
429f3ece36535d4999cf2bfd2175a33da6c3cdf298eBenjamin Franz        final int[] codePoints = StringUtils.toCodePointArray(word);
430bff46bac807ae8a9ebdc22c449a8d4f78711b4d2Benjamin Franz        final int[] shortcutTargetCodePoints = (shortcutTarget != null) ?
431bff46bac807ae8a9ebdc22c449a8d4f78711b4d2Benjamin Franz                StringUtils.toCodePointArray(shortcutTarget) : null;
432bff46bac807ae8a9ebdc22c449a8d4f78711b4d2Benjamin Franz        if (!addUnigramWordNative(mNativeDict, codePoints, probability, shortcutTargetCodePoints,
433bff46bac807ae8a9ebdc22c449a8d4f78711b4d2Benjamin Franz                shortcutProbability, isBeginningOfSentence, isNotAWord, isBlacklisted, timestamp)) {
434bff46bac807ae8a9ebdc22c449a8d4f78711b4d2Benjamin Franz            return false;
435bff46bac807ae8a9ebdc22c449a8d4f78711b4d2Benjamin Franz        }
436bff46bac807ae8a9ebdc22c449a8d4f78711b4d2Benjamin Franz        mHasUpdated = true;
437bff46bac807ae8a9ebdc22c449a8d4f78711b4d2Benjamin Franz        return true;
438bff46bac807ae8a9ebdc22c449a8d4f78711b4d2Benjamin Franz    }
439bff46bac807ae8a9ebdc22c449a8d4f78711b4d2Benjamin Franz
440bff46bac807ae8a9ebdc22c449a8d4f78711b4d2Benjamin Franz    // Remove a unigram entry from the binary dictionary in native code.
441bff46bac807ae8a9ebdc22c449a8d4f78711b4d2Benjamin Franz    public boolean removeUnigramEntry(final String word) {
442b501330a1b6ef14ff512a5727f7a01bc423d6fbbFyodor Kupolov        if (TextUtils.isEmpty(word)) {
443b501330a1b6ef14ff512a5727f7a01bc423d6fbbFyodor Kupolov            return false;
444b501330a1b6ef14ff512a5727f7a01bc423d6fbbFyodor Kupolov        }
445b501330a1b6ef14ff512a5727f7a01bc423d6fbbFyodor Kupolov        final int[] codePoints = StringUtils.toCodePointArray(word);
446b501330a1b6ef14ff512a5727f7a01bc423d6fbbFyodor Kupolov        if (!removeUnigramWordNative(mNativeDict, codePoints)) {
447b501330a1b6ef14ff512a5727f7a01bc423d6fbbFyodor Kupolov            return false;
448b501330a1b6ef14ff512a5727f7a01bc423d6fbbFyodor Kupolov        }
449b501330a1b6ef14ff512a5727f7a01bc423d6fbbFyodor Kupolov        mHasUpdated = true;
450b501330a1b6ef14ff512a5727f7a01bc423d6fbbFyodor Kupolov        return true;
451b501330a1b6ef14ff512a5727f7a01bc423d6fbbFyodor Kupolov    }
4529edbda18df025527e18614cf0c45d538a27af30fNicolas Prevot
4539edbda18df025527e18614cf0c45d538a27af30fNicolas Prevot    // Add an n-gram entry to the binary dictionary with timestamp in native code.
4549edbda18df025527e18614cf0c45d538a27af30fNicolas Prevot    public boolean addNgramEntry(final PrevWordsInfo prevWordsInfo, final String word,
4559edbda18df025527e18614cf0c45d538a27af30fNicolas Prevot            final int probability, final int timestamp) {
4569edbda18df025527e18614cf0c45d538a27af30fNicolas Prevot        if (!prevWordsInfo.isValid() || TextUtils.isEmpty(word)) {
4579edbda18df025527e18614cf0c45d538a27af30fNicolas Prevot            return false;
4589edbda18df025527e18614cf0c45d538a27af30fNicolas Prevot        }
4599edbda18df025527e18614cf0c45d538a27af30fNicolas Prevot        final int[] codePoints0 = StringUtils.toCodePointArray(prevWordsInfo.mPrevWord);
4609edbda18df025527e18614cf0c45d538a27af30fNicolas Prevot        final int[] codePoints1 = StringUtils.toCodePointArray(word);
4619edbda18df025527e18614cf0c45d538a27af30fNicolas Prevot        if (!addBigramWordsNative(mNativeDict, codePoints0, prevWordsInfo.mIsBeginningOfSentence,
4629edbda18df025527e18614cf0c45d538a27af30fNicolas Prevot                codePoints1, probability, timestamp)) {
4639edbda18df025527e18614cf0c45d538a27af30fNicolas Prevot            return false;
4649edbda18df025527e18614cf0c45d538a27af30fNicolas Prevot        }
4659edbda18df025527e18614cf0c45d538a27af30fNicolas Prevot        mHasUpdated = true;
4669edbda18df025527e18614cf0c45d538a27af30fNicolas Prevot        return true;
4679edbda18df025527e18614cf0c45d538a27af30fNicolas Prevot    }
46853d63dcd04da3c1d5187b29f6530c2dcac9d516cSander Alewijnse
46953d63dcd04da3c1d5187b29f6530c2dcac9d516cSander Alewijnse    // Remove an n-gram entry from the binary dictionary in native code.
47053d63dcd04da3c1d5187b29f6530c2dcac9d516cSander Alewijnse    public boolean removeNgramEntry(final PrevWordsInfo prevWordsInfo, final String word) {
47153d63dcd04da3c1d5187b29f6530c2dcac9d516cSander Alewijnse        if (!prevWordsInfo.isValid() || TextUtils.isEmpty(word)) {
47253d63dcd04da3c1d5187b29f6530c2dcac9d516cSander Alewijnse            return false;
47353d63dcd04da3c1d5187b29f6530c2dcac9d516cSander Alewijnse        }
47453d63dcd04da3c1d5187b29f6530c2dcac9d516cSander Alewijnse        final int[] codePoints0 = StringUtils.toCodePointArray(prevWordsInfo.mPrevWord);
47553d63dcd04da3c1d5187b29f6530c2dcac9d516cSander Alewijnse        final int[] codePoints1 = StringUtils.toCodePointArray(word);
47653d63dcd04da3c1d5187b29f6530c2dcac9d516cSander Alewijnse        if (!removeBigramWordsNative(mNativeDict, codePoints0, prevWordsInfo.mIsBeginningOfSentence,
47753d63dcd04da3c1d5187b29f6530c2dcac9d516cSander Alewijnse                codePoints1)) {
47853d63dcd04da3c1d5187b29f6530c2dcac9d516cSander Alewijnse            return false;
47953d63dcd04da3c1d5187b29f6530c2dcac9d516cSander Alewijnse        }
480b14ed95647ff7c38869550606396d5b784eeece1Nicolas Prevot        mHasUpdated = true;
481b14ed95647ff7c38869550606396d5b784eeece1Nicolas Prevot        return true;
482b14ed95647ff7c38869550606396d5b784eeece1Nicolas Prevot    }
483b14ed95647ff7c38869550606396d5b784eeece1Nicolas Prevot
48453d63dcd04da3c1d5187b29f6530c2dcac9d516cSander Alewijnse    public void addMultipleDictionaryEntries(final LanguageModelParam[] languageModelParams) {
48553d63dcd04da3c1d5187b29f6530c2dcac9d516cSander Alewijnse        if (!isValidDictionary()) return;
48653d63dcd04da3c1d5187b29f6530c2dcac9d516cSander Alewijnse        int processedParamCount = 0;
487655d0e2029e6ae77a47e922dce4c4989818b8dd1Amith Yamasani        while (processedParamCount < languageModelParams.length) {
488655d0e2029e6ae77a47e922dce4c4989818b8dd1Amith Yamasani            if (needsToRunGC(true /* mindsBlockByGC */)) {
489655d0e2029e6ae77a47e922dce4c4989818b8dd1Amith Yamasani                flushWithGC();
490655d0e2029e6ae77a47e922dce4c4989818b8dd1Amith Yamasani            }
491655d0e2029e6ae77a47e922dce4c4989818b8dd1Amith Yamasani            processedParamCount = addMultipleDictionaryEntriesNative(mNativeDict,
492655d0e2029e6ae77a47e922dce4c4989818b8dd1Amith Yamasani                    languageModelParams, processedParamCount);
493655d0e2029e6ae77a47e922dce4c4989818b8dd1Amith Yamasani            mHasUpdated = true;
49427db46850b708070452c0ce49daf5f79503fbde6Amith Yamasani            if (processedParamCount <= 0) {
49527db46850b708070452c0ce49daf5f79503fbde6Amith Yamasani                return;
4967e99bc02c8e2f44dd92d70bfa6e654297e5286d8Amith Yamasani            }
49727db46850b708070452c0ce49daf5f79503fbde6Amith Yamasani        }
49827db46850b708070452c0ce49daf5f79503fbde6Amith Yamasani    }
49927db46850b708070452c0ce49daf5f79503fbde6Amith Yamasani
50027db46850b708070452c0ce49daf5f79503fbde6Amith Yamasani    private void reopen() {
50127db46850b708070452c0ce49daf5f79503fbde6Amith Yamasani        close();
50227db46850b708070452c0ce49daf5f79503fbde6Amith Yamasani        final File dictFile = new File(mDictFilePath);
503a12fccf57d5ec289793699d9b22ff45daccd3933Maggie Benthall        // WARNING: Because we pass 0 as the offset and file.length() as the length, this can
504258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani        // only be called for actual files. Right now it's only called by the flush() family of
505258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani        // functions, which require an updatable dictionary, so it's okay. But beware.
506258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani        loadDictionary(dictFile.getAbsolutePath(), 0 /* startOffset */,
507258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani                dictFile.length(), mIsUpdatable);
508258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani    }
509258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani
510258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani    // Flush to dict file if the dictionary has been updated.
511258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani    public boolean flush() {
5121a447535cef7e3739d5f763dfe13e568568b9789Kenny Guy        if (!isValidDictionary()) return false;
513e928d7d95dbb64627e6ff3a0572190c555b59d96Amith Yamasani        if (mHasUpdated) {
514258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani            if (!flushNative(mNativeDict, mDictFilePath)) {
5154673e7ea8d1f869910a9c0f9c211d4d27ad50b41Jeff Sharkey                return false;
5161a447535cef7e3739d5f763dfe13e568568b9789Kenny Guy            }
5171a447535cef7e3739d5f763dfe13e568568b9789Kenny Guy            reopen();
5181a447535cef7e3739d5f763dfe13e568568b9789Kenny Guy        }
519258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani        return true;
520258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani    }
5216794458f8626c3be27eac3db3a5c89d94f132675Maggie Benthall
5225760e1786bcc2feac9d1c2c784520aa4d6cf8cb8Amith Yamasani    // Run GC and flush to dict file if the dictionary has been updated.
523be81c800ae6216e30b6008b4c73172b36531c405Jessica Hummel    public boolean flushWithGCIfHasUpdated() {
5245760e1786bcc2feac9d1c2c784520aa4d6cf8cb8Amith Yamasani        if (mHasUpdated) {
525258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani            return flushWithGC();
5266794458f8626c3be27eac3db3a5c89d94f132675Maggie Benthall        }
527258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani        return true;
52879af1dd54c16cde063152922b42c96d72ae9eca8Dianne Hackborn    }
529258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani
530258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani    // Run GC and flush to dict file.
531258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani    public boolean flushWithGC() {
5328832c18d8b63367929c2d394c9c508f56003d400Dianne Hackborn        if (!isValidDictionary()) return false;
5338832c18d8b63367929c2d394c9c508f56003d400Dianne Hackborn        if (!flushWithGCNative(mNativeDict, mDictFilePath)) {
5348832c18d8b63367929c2d394c9c508f56003d400Dianne Hackborn            return false;
535258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani        }
536258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani        reopen();
537258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani        return true;
538258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani    }
539258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani
540258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani    /**
541258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani     * Checks whether GC is needed to run or not.
542258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani     * @param mindsBlockByGC Whether to mind operations blocked by GC. We don't need to care about
543258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani     * the blocking in some situations such as in idle time or just before closing.
544258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani     * @return whether GC is needed to run or not.
545258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani     */
54667a101aa3006aa2011a9d9edaacb2fa6f6f31140Dianne Hackborn    public boolean needsToRunGC(final boolean mindsBlockByGC) {
547e4ab16ad98b183afbf7a21ad7314372de41a8b57Dan Morrill        if (!isValidDictionary()) return false;
548e4ab16ad98b183afbf7a21ad7314372de41a8b57Dan Morrill        return needsToRunGCNative(mNativeDict, mindsBlockByGC);
54967a101aa3006aa2011a9d9edaacb2fa6f6f31140Dianne Hackborn    }
550955d8d69ea6caabce1461dc25b339b9bf9dc61a6Dianne Hackborn
55167a101aa3006aa2011a9d9edaacb2fa6f6f31140Dianne Hackborn    public boolean migrateTo(final int newFormatVersion) {
55267a101aa3006aa2011a9d9edaacb2fa6f6f31140Dianne Hackborn        if (!isValidDictionary()) {
55367a101aa3006aa2011a9d9edaacb2fa6f6f31140Dianne Hackborn            return false;
554e4ab16ad98b183afbf7a21ad7314372de41a8b57Dan Morrill        }
555e4ab16ad98b183afbf7a21ad7314372de41a8b57Dan Morrill        final String tmpDictFilePath = mDictFilePath + DICT_FILE_NAME_SUFFIX_FOR_MIGRATION;
556988ae30ff7729ac0e9a44ee665c7e00f1961e7cdAdam Powell        if (!migrateNative(mNativeDict, tmpDictFilePath, newFormatVersion)) {
557988ae30ff7729ac0e9a44ee665c7e00f1961e7cdAdam Powell            return false;
558e4ab16ad98b183afbf7a21ad7314372de41a8b57Dan Morrill        }
559df2e92a535e19c00edd37318d974dab992ccc2c1Amith Yamasani        close();
560df2e92a535e19c00edd37318d974dab992ccc2c1Amith Yamasani        final File dictFile = new File(mDictFilePath);
5615760e1786bcc2feac9d1c2c784520aa4d6cf8cb8Amith Yamasani        final File tmpDictFile = new File(tmpDictFilePath);
5625760e1786bcc2feac9d1c2c784520aa4d6cf8cb8Amith Yamasani        if (!FileUtils.deleteRecursively(dictFile)) {
5635760e1786bcc2feac9d1c2c784520aa4d6cf8cb8Amith Yamasani            return false;
5645760e1786bcc2feac9d1c2c784520aa4d6cf8cb8Amith Yamasani        }
5655760e1786bcc2feac9d1c2c784520aa4d6cf8cb8Amith Yamasani        if (!BinaryDictionaryUtils.renameDict(tmpDictFile, dictFile)) {
5665760e1786bcc2feac9d1c2c784520aa4d6cf8cb8Amith Yamasani            return false;
5675760e1786bcc2feac9d1c2c784520aa4d6cf8cb8Amith Yamasani        }
5685760e1786bcc2feac9d1c2c784520aa4d6cf8cb8Amith Yamasani        loadDictionary(dictFile.getAbsolutePath(), 0 /* startOffset */,
5695760e1786bcc2feac9d1c2c784520aa4d6cf8cb8Amith Yamasani                dictFile.length(), mIsUpdatable);
5705760e1786bcc2feac9d1c2c784520aa4d6cf8cb8Amith Yamasani        return true;
5715760e1786bcc2feac9d1c2c784520aa4d6cf8cb8Amith Yamasani    }
572e1375908a5f05e5c926e95049970c4505e4dfad9Amith Yamasani
57346bc4ebb87232b39d7b02ac0135c8ccf2c33f233Amith Yamasani    @UsedForTesting
574e1375908a5f05e5c926e95049970c4505e4dfad9Amith Yamasani    public String getPropertyForTest(final String query) {
5752555dafce87e60fae28d71913730abf73e40fcd7Amith Yamasani        if (!isValidDictionary()) return "";
576df2e92a535e19c00edd37318d974dab992ccc2c1Amith Yamasani        return getPropertyNative(mNativeDict, query);
577e1375908a5f05e5c926e95049970c4505e4dfad9Amith Yamasani    }
578df2e92a535e19c00edd37318d974dab992ccc2c1Amith Yamasani
57971e6c697e54a43d357cc25d87a446d140f17396aAmith Yamasani    @Override
580df2e92a535e19c00edd37318d974dab992ccc2c1Amith Yamasani    public boolean shouldAutoCommit(final SuggestedWordInfo candidate) {
58146bc4ebb87232b39d7b02ac0135c8ccf2c33f233Amith Yamasani        return candidate.mAutoCommitFirstWordConfidence > CONFIDENCE_TO_AUTO_COMMIT;
582df2e92a535e19c00edd37318d974dab992ccc2c1Amith Yamasani    }
583df2e92a535e19c00edd37318d974dab992ccc2c1Amith Yamasani
584df2e92a535e19c00edd37318d974dab992ccc2c1Amith Yamasani    @Override
585df2e92a535e19c00edd37318d974dab992ccc2c1Amith Yamasani    public void close() {
586258848d2ae04f447ff1c18023fa76b139fcc0862Amith Yamasani        synchronized (mDicTraverseSessions) {
5871e9c21871e81642669079cd290ef47818a3165bdAmith Yamasani            final int sessionsSize = mDicTraverseSessions.size();
5881e9c21871e81642669079cd290ef47818a3165bdAmith Yamasani            for (int index = 0; index < sessionsSize; ++index) {
5891e9c21871e81642669079cd290ef47818a3165bdAmith Yamasani                final DicTraverseSession traverseSession = mDicTraverseSessions.valueAt(index);
5901e9c21871e81642669079cd290ef47818a3165bdAmith Yamasani                if (traverseSession != null) {
5911e9c21871e81642669079cd290ef47818a3165bdAmith Yamasani                    traverseSession.close();
5921e9c21871e81642669079cd290ef47818a3165bdAmith Yamasani                }
5931e9c21871e81642669079cd290ef47818a3165bdAmith Yamasani            }
5941e9c21871e81642669079cd290ef47818a3165bdAmith Yamasani            mDicTraverseSessions.clear();
5951e9c21871e81642669079cd290ef47818a3165bdAmith Yamasani        }
5961e9c21871e81642669079cd290ef47818a3165bdAmith Yamasani        closeInternalLocked();
5970e8d7d63ba439cc0604af7055679dae3d30fdc48Amith Yamasani    }
5980e8d7d63ba439cc0604af7055679dae3d30fdc48Amith Yamasani
5990e8d7d63ba439cc0604af7055679dae3d30fdc48Amith Yamasani    private synchronized void closeInternalLocked() {
6000e8d7d63ba439cc0604af7055679dae3d30fdc48Amith Yamasani        if (mNativeDict != 0) {
6010e8d7d63ba439cc0604af7055679dae3d30fdc48Amith Yamasani            closeNative(mNativeDict);
6020e8d7d63ba439cc0604af7055679dae3d30fdc48Amith Yamasani            mNativeDict = 0;
6030e8d7d63ba439cc0604af7055679dae3d30fdc48Amith Yamasani        }
6040e8d7d63ba439cc0604af7055679dae3d30fdc48Amith Yamasani    }
6050e8d7d63ba439cc0604af7055679dae3d30fdc48Amith Yamasani
6060e8d7d63ba439cc0604af7055679dae3d30fdc48Amith Yamasani    // TODO: Manage BinaryDictionary instances without using WeakReference or something.
6070e8d7d63ba439cc0604af7055679dae3d30fdc48Amith Yamasani    @Override
6080e8d7d63ba439cc0604af7055679dae3d30fdc48Amith Yamasani    protected void finalize() throws Throwable {
6090e8d7d63ba439cc0604af7055679dae3d30fdc48Amith Yamasani        try {
610a8a9bd65bf5865d83ef44f54552ca39522bfbcf0Dianne Hackborn            closeInternalLocked();
611a8a9bd65bf5865d83ef44f54552ca39522bfbcf0Dianne Hackborn        } finally {
612a8a9bd65bf5865d83ef44f54552ca39522bfbcf0Dianne Hackborn            super.finalize();
613a8a9bd65bf5865d83ef44f54552ca39522bfbcf0Dianne Hackborn        }
614a8a9bd65bf5865d83ef44f54552ca39522bfbcf0Dianne Hackborn    }
615a8a9bd65bf5865d83ef44f54552ca39522bfbcf0Dianne Hackborn}
616a8a9bd65bf5865d83ef44f54552ca39522bfbcf0Dianne Hackborn