1923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/*
2443c360d0afdbab091994244f045f4756feaf2b4Jean-Baptiste Queru * Copyright (C) 2008 The Android Open Source Project
3e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa *
48aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * Licensed under the Apache License, Version 2.0 (the "License");
58aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * you may not use this file except in compliance with the License.
68aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * You may obtain a copy of the License at
7e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa *
88aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka *      http://www.apache.org/licenses/LICENSE-2.0
9e90b333017c68e888a5e3d351f07ea29036457d0Ken Wakasa *
10923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
118aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * distributed under the License is distributed on an "AS IS" BASIS,
128aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * See the License for the specific language governing permissions and
148aa9963a895f9dd5bb1bc92ab2e4f461e058f87aTadashi G. Takaoka * limitations under the License.
15923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */
16923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
17923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectpackage com.android.inputmethod.latin;
18923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
194d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyangimport android.text.TextUtils;
203979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataokaimport android.util.SparseArray;
21fa086c90760bc2bedf0b74eacb0fed3bf7ebc2b7Tadashi G. Takaoka
22d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagiimport com.android.inputmethod.annotations.UsedForTesting;
23ab72a97d7ce44230a0c824797d1675a5ca354a56Tadashi G. Takaokaimport com.android.inputmethod.keyboard.ProximityInfo;
24bda7eaa63aace64f3d40eae3affaf281591ffa66Jean Chalardimport com.android.inputmethod.latin.SuggestedWords.SuggestedWordInfo;
25a7d2fc6befa1b16883200a653fc01deb4d94944dKen Wakasaimport com.android.inputmethod.latin.settings.NativeSuggestOptions;
26e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.CollectionUtils;
27e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.JniUtils;
28e28eba5074664d5716b8e58b8d0a235746b261ebKen Wakasaimport com.android.inputmethod.latin.utils.StringUtils;
29ab72a97d7ce44230a0c824797d1675a5ca354a56Tadashi G. Takaoka
302e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagiimport java.io.File;
31bda7eaa63aace64f3d40eae3affaf281591ffa66Jean Chalardimport java.util.ArrayList;
32923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Projectimport java.util.Arrays;
3343ebd8a035af31244a2d54fce5d8000a1fbada4csatokimport java.util.Locale;
345ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagiimport java.util.Map;
35923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
36923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project/**
37923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project * Implements a static, compacted, binary dictionary of standard words.
38923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */
396142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi// TODO: All methods which should be locked need to have a suffix "Locked".
40a28a05e971cc242b338331a3b78276fa95188d19Tadashi G. Takaokapublic final class BinaryDictionary extends Dictionary {
41b02ee3d67a1884b6ff59cc16c29a476845c0694fKen Wakasa    private static final String TAG = BinaryDictionary.class.getSimpleName();
42cba93f50c3d46ada773ec49435689dc3e2094385Jean Chalard
436c22439bf80da08576e86c1282afc5cfa431e235Ken Wakasa    // Must be equal to MAX_WORD_LENGTH in native/jni/src/defines.h
44ffcbbaf12788a9fc9398607a548e552d7d2bf05eSatoshi Kataoka    private static final int MAX_WORD_LENGTH = Constants.DICTIONARY_MAX_WORD_LENGTH;
456c22439bf80da08576e86c1282afc5cfa431e235Ken Wakasa    // Must be equal to MAX_RESULTS in native/jni/src/defines.h
465db594abbad2d9e8d2cf1aa6e417aa50ffc5dfc1Ken Wakasa    private static final int MAX_RESULTS = 18;
47459cd6f8ef3eaa561e47dd996ce537770ea8b37aJean Chalard    // The cutoff returned by native for auto-commit confidence.
48459cd6f8ef3eaa561e47dd996ce537770ea8b37aJean Chalard    // Must be equal to CONFIDENCE_TO_AUTO_COMMIT in native/jni/src/defines.h
49459cd6f8ef3eaa561e47dd996ce537770ea8b37aJean Chalard    private static final int CONFIDENCE_TO_AUTO_COMMIT = 1000000;
50923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
5131097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi    @UsedForTesting
5231097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi    public static final String UNIGRAM_COUNT_QUERY = "UNIGRAM_COUNT";
5331097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi    @UsedForTesting
5431097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi    public static final String BIGRAM_COUNT_QUERY = "BIGRAM_COUNT";
55cfb018ba6db78f2b33b54d4811f0bf166db29792Keisuke Kuroyanagi    @UsedForTesting
56cfb018ba6db78f2b33b54d4811f0bf166db29792Keisuke Kuroyanagi    public static final String MAX_UNIGRAM_COUNT_QUERY = "MAX_UNIGRAM_COUNT";
57cfb018ba6db78f2b33b54d4811f0bf166db29792Keisuke Kuroyanagi    @UsedForTesting
58cfb018ba6db78f2b33b54d4811f0bf166db29792Keisuke Kuroyanagi    public static final String MAX_BIGRAM_COUNT_QUERY = "MAX_BIGRAM_COUNT";
5931097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi
605fdcd7d5cd9d39d41568aa1412a4b1a866c05d3aTadashi G. Takaoka    private long mNativeDict;
613979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka    private final Locale mLocale;
624c2767857a02c9cf18a9579aa0391fd09b3fe411Keisuke Kuroyanagi    private final long mDictSize;
63d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi    private final String mDictFilePath;
64f3850e554389dc3012584f9d81a4f2d3d4c89e44Ken Wakasa    private final int[] mInputCodePoints = new int[MAX_WORD_LENGTH];
651e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa    private final int[] mOutputCodePoints = new int[MAX_WORD_LENGTH * MAX_RESULTS];
66f6870cc82ddf394e94155322fcc7e4e2256bea66Ken Wakasa    private final int[] mSpaceIndices = new int[MAX_RESULTS];
676f233f7ba1ee924844154515dda208bb9b34acb5Jean Chalard    private final int[] mOutputScores = new int[MAX_RESULTS];
686931df9c17aaeb04288f937cabf956c1b9eb0cc9Jean Chalard    private final int[] mOutputTypes = new int[MAX_RESULTS];
69bb57090f1da9d1fc5a0eda9b627d3f8c8b25ab42Jean Chalard    // Only one result is ever used
70bb57090f1da9d1fc5a0eda9b627d3f8c8b25ab42Jean Chalard    private final int[] mOutputAutoCommitFirstWordConfidence = new int[1];
71923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
7250db5f0fffdc35e9fe0bed27e7d630d571727e06Keisuke Kuroyanagi    private final NativeSuggestOptions mNativeSuggestOptions = new NativeSuggestOptions();
733979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka
743979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka    private final SparseArray<DicTraverseSession> mDicTraverseSessions =
75ecfbf4625c8afd9cde7b79e0c7846b87e20f79e9Tadashi G. Takaoka            CollectionUtils.newSparseArray();
768ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka
778ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka    // TODO: There should be a way to remove used DicTraverseSession objects from
788ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka    // {@code mDicTraverseSessions}.
79bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    private DicTraverseSession getTraverseSession(final int traverseSessionId) {
808ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka        synchronized(mDicTraverseSessions) {
818ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka            DicTraverseSession traverseSession = mDicTraverseSessions.get(traverseSessionId);
828ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka            if (traverseSession == null) {
833979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka                traverseSession = mDicTraverseSessions.get(traverseSessionId);
843979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka                if (traverseSession == null) {
854c2767857a02c9cf18a9579aa0391fd09b3fe411Keisuke Kuroyanagi                    traverseSession = new DicTraverseSession(mLocale, mNativeDict, mDictSize);
863979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka                    mDicTraverseSessions.put(traverseSessionId, traverseSession);
873979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka                }
883979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka            }
898ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka            return traverseSession;
903979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka        }
913979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka    }
92923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
93923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    /**
944250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard     * Constructor for the binary dictionary. This is supposed to be called from the
954250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard     * dictionary factory.
964250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard     * @param filename the name of the file to read through native code.
974250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard     * @param offset the offset of the dictionary data within the file.
984250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard     * @param length the length of the binary data.
9924aee9100e92dc4c06cdb54487a4922420fa8660Jean Chalard     * @param useFullEditDistance whether to use the full edit distance in suggestions
10005efe576f976f5fa280f8d523f2935c15cbb9bd1Jean Chalard     * @param dictType the dictionary type, as a human-readable string
101981717da4c414caee57ba98596f9bc634a97f74fKeisuke Kuroynagi     * @param isUpdatable whether to open the dictionary file in writable mode.
102923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project     */
103f6870cc82ddf394e94155322fcc7e4e2256bea66Ken Wakasa    public BinaryDictionary(final String filename, final long offset, final long length,
104981717da4c414caee57ba98596f9bc634a97f74fKeisuke Kuroynagi            final boolean useFullEditDistance, final Locale locale, final String dictType,
105981717da4c414caee57ba98596f9bc634a97f74fKeisuke Kuroynagi            final boolean isUpdatable) {
10605efe576f976f5fa280f8d523f2935c15cbb9bd1Jean Chalard        super(dictType);
1073979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka        mLocale = locale;
1084c2767857a02c9cf18a9579aa0391fd09b3fe411Keisuke Kuroyanagi        mDictSize = length;
109d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi        mDictFilePath = filename;
11050db5f0fffdc35e9fe0bed27e7d630d571727e06Keisuke Kuroyanagi        mNativeSuggestOptions.setUseFullEditDistance(useFullEditDistance);
111981717da4c414caee57ba98596f9bc634a97f74fKeisuke Kuroynagi        loadDictionary(filename, offset, length, isUpdatable);
112979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
113979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
114eaef1c500703b4ee378821884c7b108815ed2983Ken Wakasa    static {
115cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka        JniUtils.loadNativeLibrary();
116eaef1c500703b4ee378821884c7b108815ed2983Ken Wakasa    }
117c2bbc6a4499a6da979381fa0e8e6e855a5ac6aa4Jean Chalard
1185ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi    private static native boolean createEmptyDictFileNative(String filePath, long dictVersion,
1195ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi            String[] attributeKeyStringArray, String[] attributeValueStringArray);
120f3b62900c7bcb0d6434f45ec7b467b7b4bad6f9aKeisuke Kuroynagi    private static native long openNative(String sourceDir, long dictOffset, long dictSize,
121f3b62900c7bcb0d6434f45ec7b467b7b4bad6f9aKeisuke Kuroynagi            boolean isUpdatable);
122d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi    private static native void flushNative(long dict, String filePath);
123c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi    private static native boolean needsToRunGCNative(long dict, boolean mindsBlockByGC);
124d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi    private static native void flushWithGCNative(long dict, String filePath);
1255db594abbad2d9e8d2cf1aa6e417aa50ffc5dfc1Ken Wakasa    private static native void closeNative(long dict);
126e0e67373735918c78eaeaf24f127e1d28816aa29Satoshi Kataoka    private static native int getProbabilityNative(long dict, int[] word);
1274d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi    private static native int getBigramProbabilityNative(long dict, int[] word0, int[] word1);
1285db594abbad2d9e8d2cf1aa6e417aa50ffc5dfc1Ken Wakasa    private static native int getSuggestionsNative(long dict, long proximityInfo,
1295db594abbad2d9e8d2cf1aa6e417aa50ffc5dfc1Ken Wakasa            long traverseSession, int[] xCoordinates, int[] yCoordinates, int[] times,
1305db594abbad2d9e8d2cf1aa6e417aa50ffc5dfc1Ken Wakasa            int[] pointerIds, int[] inputCodePoints, int inputSize, int commitPoint,
13150db5f0fffdc35e9fe0bed27e7d630d571727e06Keisuke Kuroyanagi            int[] suggestOptions, int[] prevWordCodePointArray,
13224aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard            int[] outputCodePoints, int[] outputScores, int[] outputIndices, int[] outputTypes,
13324aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard            int[] outputAutoCommitFirstWordConfidence);
1341e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa    private static native float calcNormalizedScoreNative(int[] before, int[] after, int score);
1351e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa    private static native int editDistanceNative(int[] before, int[] after);
1365bf1be71629607e7206e6203489cf742d2f8ed79Keisuke Kuroynagi    private static native void addUnigramWordNative(long dict, int[] word, int probability);
1375bf1be71629607e7206e6203489cf742d2f8ed79Keisuke Kuroynagi    private static native void addBigramWordsNative(long dict, int[] word0, int[] word1,
1385bf1be71629607e7206e6203489cf742d2f8ed79Keisuke Kuroynagi            int probability);
1395bf1be71629607e7206e6203489cf742d2f8ed79Keisuke Kuroynagi    private static native void removeBigramWordsNative(long dict, int[] word0, int[] word1);
1404d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi    private static native int calculateProbabilityNative(long dict, int unigramProbability,
1414d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi            int bigramProbability);
14231097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi    private static native String getPropertyNative(long dict, String query);
143979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
1445ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi    @UsedForTesting
1455ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi    public static boolean createEmptyDictFile(final String filePath, final long dictVersion,
1465ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi            final Map<String, String> attributeMap) {
1475ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi        final String[] keyArray = new String[attributeMap.size()];
1485ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi        final String[] valueArray = new String[attributeMap.size()];
1495ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi        int index = 0;
1505ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi        for (final String key : attributeMap.keySet()) {
1515ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi            keyArray[index] = key;
1525ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi            valueArray[index] = attributeMap.get(key);
1535ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi            index++;
1545ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi        }
1555ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi        return createEmptyDictFileNative(filePath, dictVersion, keyArray, valueArray);
1565ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi    }
1575ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi
158e9f3e182e4b3217282831fd8805958270b4dbba3Satoshi Kataoka    // TODO: Move native dict into session
159bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    private final void loadDictionary(final String path, final long startOffset,
160981717da4c414caee57ba98596f9bc634a97f74fKeisuke Kuroynagi            final long length, final boolean isUpdatable) {
161981717da4c414caee57ba98596f9bc634a97f74fKeisuke Kuroynagi        mNativeDict = openNative(path, startOffset, length, isUpdatable);
162979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    }
163979f8690967ff5409fe18f5085858ccdb8e0ccf1satok
164979f8690967ff5409fe18f5085858ccdb8e0ccf1satok    @Override
165b30d2185f24e3d531f5d46249e7c97391705e469Jean Chalard    public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer,
1662dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard            final String prevWord, final ProximityInfo proximityInfo,
167fe87f5f41744a633a2ed91af9b171bda2637649eKeisuke Kuroyanagi            final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) {
1682dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard        return getSuggestionsWithSessionId(composer, prevWord, proximityInfo, blockOffensiveWords,
169fe87f5f41744a633a2ed91af9b171bda2637649eKeisuke Kuroyanagi                additionalFeaturesOptions, 0 /* sessionId */);
1703979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka    }
1713979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka
1723979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka    @Override
1733979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka    public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer,
1742dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard            final String prevWord, final ProximityInfo proximityInfo,
175fe87f5f41744a633a2ed91af9b171bda2637649eKeisuke Kuroyanagi            final boolean blockOffensiveWords, final int[] additionalFeaturesOptions,
176fe87f5f41744a633a2ed91af9b171bda2637649eKeisuke Kuroyanagi            final int sessionId) {
177b4b93dbf3e0912ba26c5c34abba7a5b94c74138cJean Chalard        if (!isValidDictionary()) return null;
1783979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka
179ac78633be28e8990fc3b3a8de192c80966e746e3Tadashi G. Takaoka        Arrays.fill(mInputCodePoints, Constants.NOT_A_CODE);
180ea98e026f1ad7732279aec6d06107f46ea0af93dJean Chalard        // TODO: toLowerCase in the native code
1815e21ea1a3553000529c288acdf6d6a4b165bedc5Tadashi Takaoka        final int[] prevWordCodePointArray = (null == prevWord)
182bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka                ? null : StringUtils.toCodePointArray(prevWord);
183860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard        final int composerSize = composer.size();
184860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard
185e4e7e5fc82eca67330765510ad0bd29caeb7a1bbJean Chalard        final boolean isGesture = composer.isBatchMode();
186e4e7e5fc82eca67330765510ad0bd29caeb7a1bbJean Chalard        if (composerSize <= 1 || !isGesture) {
187860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard            if (composerSize > MAX_WORD_LENGTH - 1) return null;
188860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard            for (int i = 0; i < composerSize; i++) {
189f3850e554389dc3012584f9d81a4f2d3d4c89e44Ken Wakasa                mInputCodePoints[i] = composer.getCodeAt(i);
190860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard            }
191860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard        }
192860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard
193251f302985bc491f4dd54983e9c69c5dc76cb834Jean Chalard        final InputPointers ips = composer.getInputPointers();
1945db594abbad2d9e8d2cf1aa6e417aa50ffc5dfc1Ken Wakasa        final int inputSize = isGesture ? ips.getPointerSize() : composerSize;
19550db5f0fffdc35e9fe0bed27e7d630d571727e06Keisuke Kuroyanagi        mNativeSuggestOptions.setIsGesture(isGesture);
196fe87f5f41744a633a2ed91af9b171bda2637649eKeisuke Kuroyanagi        mNativeSuggestOptions.setAdditionalFeaturesOptions(additionalFeaturesOptions);
197251f302985bc491f4dd54983e9c69c5dc76cb834Jean Chalard        // proximityInfo and/or prevWordForBigrams may not be null.
198f6870cc82ddf394e94155322fcc7e4e2256bea66Ken Wakasa        final int count = getSuggestionsNative(mNativeDict, proximityInfo.getNativeProximityInfo(),
199f6870cc82ddf394e94155322fcc7e4e2256bea66Ken Wakasa                getTraverseSession(sessionId).getSession(), ips.getXCoordinates(),
200f6870cc82ddf394e94155322fcc7e4e2256bea66Ken Wakasa                ips.getYCoordinates(), ips.getTimes(), ips.getPointerIds(), mInputCodePoints,
20150db5f0fffdc35e9fe0bed27e7d630d571727e06Keisuke Kuroyanagi                inputSize, 0 /* commitPoint */, mNativeSuggestOptions.getOptions(),
20250db5f0fffdc35e9fe0bed27e7d630d571727e06Keisuke Kuroyanagi                prevWordCodePointArray, mOutputCodePoints, mOutputScores, mSpaceIndices,
20324aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard                mOutputTypes, mOutputAutoCommitFirstWordConfidence);
2045f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka        final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
205f5cded1c6cf0f39df13750d4f9f5ba66c1b32964satok        for (int j = 0; j < count; ++j) {
206f5cded1c6cf0f39df13750d4f9f5ba66c1b32964satok            final int start = j * MAX_WORD_LENGTH;
207979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            int len = 0;
2081e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa            while (len < MAX_WORD_LENGTH && mOutputCodePoints[start + len] != 0) {
209f5cded1c6cf0f39df13750d4f9f5ba66c1b32964satok                ++len;
210979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
211979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            if (len > 0) {
21299e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard                final int flags = mOutputTypes[j] & SuggestedWordInfo.KIND_MASK_FLAGS;
2132dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard                if (blockOffensiveWords
214bb61293675234f6e5e1312db70618be985b37a83Jean Chalard                        && 0 != (flags & SuggestedWordInfo.KIND_FLAG_POSSIBLY_OFFENSIVE)
21599e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard                        && 0 == (flags & SuggestedWordInfo.KIND_FLAG_EXACT_MATCH)) {
216bb61293675234f6e5e1312db70618be985b37a83Jean Chalard                    // If we block potentially offensive words, and if the word is possibly
217bb61293675234f6e5e1312db70618be985b37a83Jean Chalard                    // offensive, then we don't output it unless it's also an exact match.
21899e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard                    continue;
21999e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard                }
22099e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard                final int kind = mOutputTypes[j] & SuggestedWordInfo.KIND_MASK_KIND;
22199e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard                final int score = SuggestedWordInfo.KIND_WHITELIST == kind
222926ef06a1ef8a0f8e01baa26c23172b93012d2b1Jean Chalard                        ? SuggestedWordInfo.MAX_SCORE : mOutputScores[j];
22399e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard                // TODO: check that all users of the `kind' parameter are ready to accept
22499e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard                // flags too and pass mOutputTypes[j] instead of kind
2251e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa                suggestions.add(new SuggestedWordInfo(new String(mOutputCodePoints, start, len),
226e8ef09567077211da034a77b457fd5f87e70f6f0Jean Chalard                        score, kind, this /* sourceDict */,
227e8754aba1c8f217e7ca828de25e0506ac58daa99Keisuke Kuroyanagi                        mSpaceIndices[j] /* indexOfTouchPointOfSecondWord */,
22824aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard                        mOutputAutoCommitFirstWordConfidence[0]));
229979f8690967ff5409fe18f5085858ccdb8e0ccf1satok            }
230979f8690967ff5409fe18f5085858ccdb8e0ccf1satok        }
231d82898c5a91f8aa69d5dc594b7a9290b8be1247aJean Chalard        return suggestions;
232923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
233923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
23415f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka    public boolean isValidDictionary() {
2356f4eba814a7f8426617db61f928a965209ebf359Tadashi G. Takaoka        return mNativeDict != 0;
2366f4eba814a7f8426617db61f928a965209ebf359Tadashi G. Takaoka    }
2376f4eba814a7f8426617db61f928a965209ebf359Tadashi G. Takaoka
238bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public static float calcNormalizedScore(final String before, final String after,
239bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka            final int score) {
2401e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa        return calcNormalizedScoreNative(StringUtils.toCodePointArray(before),
2411e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa                StringUtils.toCodePointArray(after), score);
242be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok    }
243be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok
244bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public static int editDistance(final String before, final String after) {
24566d955ad711d4a1248157056a1d3d643af19fd3fSatoshi Kataoka        if (before == null || after == null) {
24666d955ad711d4a1248157056a1d3d643af19fd3fSatoshi Kataoka            throw new IllegalArgumentException();
24766d955ad711d4a1248157056a1d3d643af19fd3fSatoshi Kataoka        }
2481e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa        return editDistanceNative(StringUtils.toCodePointArray(before),
2491e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa                StringUtils.toCodePointArray(after));
250be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok    }
251be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok
252923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
253bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public boolean isValidWord(final String word) {
2544d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi        return getFrequency(word) != NOT_A_PROBABILITY;
255c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka    }
256c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka
257c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka    @Override
258bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka    public int getFrequency(final String word) {
2594d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi        if (word == null) return NOT_A_PROBABILITY;
260bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka        int[] codePoints = StringUtils.toCodePointArray(word);
261e0e67373735918c78eaeaf24f127e1d28816aa29Satoshi Kataoka        return getProbabilityNative(mNativeDict, codePoints);
262923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
263c3df2d6fd27f3a5b84040b59aece3367769f0cb6Amith Yamasani
2644d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang    // TODO: Add a batch process version (isValidBigramMultiple?) to avoid excessive numbers of jni
2654d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang    // calls when checking for changes in an entire dictionary.
266f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi    public boolean isValidBigram(final String word0, final String word1) {
2674d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi        return getBigramProbability(word0, word1) != NOT_A_PROBABILITY;
2684d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi    }
2694d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi
2704d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi    public int getBigramProbability(final String word0, final String word1) {
2714d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi        if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) return NOT_A_PROBABILITY;
272f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        final int[] codePoints0 = StringUtils.toCodePointArray(word0);
2731e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa        final int[] codePoints1 = StringUtils.toCodePointArray(word1);
2744d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi        return getBigramProbabilityNative(mNativeDict, codePoints0, codePoints1);
275f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi    }
276f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi
277f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi    // Add a unigram entry to binary dictionary in native code.
278f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi    public void addUnigramWord(final String word, final int probability) {
279f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        if (TextUtils.isEmpty(word)) {
280f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi            return;
281f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        }
282f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        final int[] codePoints = StringUtils.toCodePointArray(word);
283f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        addUnigramWordNative(mNativeDict, codePoints, probability);
284f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi    }
285f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi
286f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi    // Add a bigram entry to binary dictionary in native code.
287f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi    public void addBigramWords(final String word0, final String word1, final int probability) {
288f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) {
289f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi            return;
290f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        }
291f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        final int[] codePoints0 = StringUtils.toCodePointArray(word0);
292f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        final int[] codePoints1 = StringUtils.toCodePointArray(word1);
293f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        addBigramWordsNative(mNativeDict, codePoints0, codePoints1, probability);
294f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi    }
295f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi
296f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi    // Remove a bigram entry form binary dictionary in native code.
297f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi    public void removeBigramWords(final String word0, final String word1) {
298f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) {
299f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi            return;
300f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        }
301f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        final int[] codePoints0 = StringUtils.toCodePointArray(word0);
302f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        final int[] codePoints1 = StringUtils.toCodePointArray(word1);
303f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi        removeBigramWordsNative(mNativeDict, codePoints0, codePoints1);
3044d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang    }
3054d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang
3066142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi    private void reopen() {
3076142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi        close();
3082e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi        final File dictFile = new File(mDictFilePath);
3092e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi        mNativeDict = openNative(dictFile.getAbsolutePath(), 0 /* startOffset */,
3102e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi                dictFile.length(), true /* isUpdatable */);
311d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi    }
312d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi
3136142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi    public void flush() {
3146142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi        if (!isValidDictionary()) return;
3156142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi        flushNative(mNativeDict, mDictFilePath);
3166142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi        reopen();
3176142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi    }
3186142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi
319d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi    public void flushWithGC() {
320d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi        if (!isValidDictionary()) return;
321d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi        flushWithGCNative(mNativeDict, mDictFilePath);
3226142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi        reopen();
323d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi    }
324d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi
325c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi    /**
326c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi     * Checks whether GC is needed to run or not.
327c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi     * @param mindsBlockByGC Whether to mind operations blocked by GC. We don't need to care about
328c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi     * the blocking in some situations such as in idle time or just before closing.
329c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi     * @return whether GC is needed to run or not.
330c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi     */
331c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi    public boolean needsToRunGC(final boolean mindsBlockByGC) {
332d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi        if (!isValidDictionary()) return false;
333c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi        return needsToRunGCNative(mNativeDict, mindsBlockByGC);
334d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi    }
335d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi
3364d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi    @UsedForTesting
3374d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi    public int calculateProbability(final int unigramProbability, final int bigramProbability) {
3384d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi        if (!isValidDictionary()) return NOT_A_PROBABILITY;
3394d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi        return calculateProbabilityNative(mNativeDict, unigramProbability, bigramProbability);
3404d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi    }
3414d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi
34231097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi    @UsedForTesting
34331097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi    public String getPropertyForTests(String query) {
34431097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi        if (!isValidDictionary()) return "";
34531097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi        return getPropertyNative(mNativeDict, query);
34631097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi    }
34731097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi
34836fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani    @Override
34924aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard    public boolean shouldAutoCommit(final SuggestedWordInfo candidate) {
350459cd6f8ef3eaa561e47dd996ce537770ea8b37aJean Chalard        return candidate.mAutoCommitFirstWordConfidence > CONFIDENCE_TO_AUTO_COMMIT;
35124aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard    }
35224aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard
35324aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard    @Override
3548ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka    public void close() {
3558ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka        synchronized (mDicTraverseSessions) {
3568ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka            final int sessionsSize = mDicTraverseSessions.size();
3578ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka            for (int index = 0; index < sessionsSize; ++index) {
3588ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka                final DicTraverseSession traverseSession = mDicTraverseSessions.valueAt(index);
3598ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka                if (traverseSession != null) {
3608ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka                    traverseSession.close();
3618ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka                }
3623979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka            }
3636142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi            mDicTraverseSessions.clear();
3643979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka        }
3656142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi        closeInternalLocked();
366da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa    }
367da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa
3686142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi    private synchronized void closeInternalLocked() {
369923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        if (mNativeDict != 0) {
370923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            closeNative(mNativeDict);
371923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project            mNativeDict = 0;
372923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project        }
373923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
374923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project
3756142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi    // TODO: Manage BinaryDictionary instances without using WeakReference or something.
376923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    @Override
377923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    protected void finalize() throws Throwable {
378da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa        try {
3796142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi            closeInternalLocked();
380da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa        } finally {
381da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa            super.finalize();
382da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa        }
383923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project    }
384923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project}
385