BinaryDictionary.java revision 459cd6f8ef3eaa561e47dd996ce537770ea8b37a
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"; 5531097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi 565fdcd7d5cd9d39d41568aa1412a4b1a866c05d3aTadashi G. Takaoka private long mNativeDict; 573979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka private final Locale mLocale; 584c2767857a02c9cf18a9579aa0391fd09b3fe411Keisuke Kuroyanagi private final long mDictSize; 59d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi private final String mDictFilePath; 60f3850e554389dc3012584f9d81a4f2d3d4c89e44Ken Wakasa private final int[] mInputCodePoints = new int[MAX_WORD_LENGTH]; 611e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa private final int[] mOutputCodePoints = new int[MAX_WORD_LENGTH * MAX_RESULTS]; 62f6870cc82ddf394e94155322fcc7e4e2256bea66Ken Wakasa private final int[] mSpaceIndices = new int[MAX_RESULTS]; 636f233f7ba1ee924844154515dda208bb9b34acb5Jean Chalard private final int[] mOutputScores = new int[MAX_RESULTS]; 646931df9c17aaeb04288f937cabf956c1b9eb0cc9Jean Chalard private final int[] mOutputTypes = new int[MAX_RESULTS]; 65bb57090f1da9d1fc5a0eda9b627d3f8c8b25ab42Jean Chalard // Only one result is ever used 66bb57090f1da9d1fc5a0eda9b627d3f8c8b25ab42Jean Chalard private final int[] mOutputAutoCommitFirstWordConfidence = new int[1]; 67923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 6850db5f0fffdc35e9fe0bed27e7d630d571727e06Keisuke Kuroyanagi private final NativeSuggestOptions mNativeSuggestOptions = new NativeSuggestOptions(); 693979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka 703979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka private final SparseArray<DicTraverseSession> mDicTraverseSessions = 71ecfbf4625c8afd9cde7b79e0c7846b87e20f79e9Tadashi G. Takaoka CollectionUtils.newSparseArray(); 728ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka 738ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka // TODO: There should be a way to remove used DicTraverseSession objects from 748ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka // {@code mDicTraverseSessions}. 75bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka private DicTraverseSession getTraverseSession(final int traverseSessionId) { 768ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka synchronized(mDicTraverseSessions) { 778ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka DicTraverseSession traverseSession = mDicTraverseSessions.get(traverseSessionId); 788ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka if (traverseSession == null) { 793979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka traverseSession = mDicTraverseSessions.get(traverseSessionId); 803979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka if (traverseSession == null) { 814c2767857a02c9cf18a9579aa0391fd09b3fe411Keisuke Kuroyanagi traverseSession = new DicTraverseSession(mLocale, mNativeDict, mDictSize); 823979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka mDicTraverseSessions.put(traverseSessionId, traverseSession); 833979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka } 843979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka } 858ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka return traverseSession; 863979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka } 873979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka } 88923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 89923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project /** 904250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard * Constructor for the binary dictionary. This is supposed to be called from the 914250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard * dictionary factory. 924250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard * @param filename the name of the file to read through native code. 934250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard * @param offset the offset of the dictionary data within the file. 944250eb27f54f8fedc388fe4825b0646a88778744Jean Chalard * @param length the length of the binary data. 9524aee9100e92dc4c06cdb54487a4922420fa8660Jean Chalard * @param useFullEditDistance whether to use the full edit distance in suggestions 9605efe576f976f5fa280f8d523f2935c15cbb9bd1Jean Chalard * @param dictType the dictionary type, as a human-readable string 97981717da4c414caee57ba98596f9bc634a97f74fKeisuke Kuroynagi * @param isUpdatable whether to open the dictionary file in writable mode. 98923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project */ 99f6870cc82ddf394e94155322fcc7e4e2256bea66Ken Wakasa public BinaryDictionary(final String filename, final long offset, final long length, 100981717da4c414caee57ba98596f9bc634a97f74fKeisuke Kuroynagi final boolean useFullEditDistance, final Locale locale, final String dictType, 101981717da4c414caee57ba98596f9bc634a97f74fKeisuke Kuroynagi final boolean isUpdatable) { 10205efe576f976f5fa280f8d523f2935c15cbb9bd1Jean Chalard super(dictType); 1033979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka mLocale = locale; 1044c2767857a02c9cf18a9579aa0391fd09b3fe411Keisuke Kuroyanagi mDictSize = length; 105d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi mDictFilePath = filename; 10650db5f0fffdc35e9fe0bed27e7d630d571727e06Keisuke Kuroyanagi mNativeSuggestOptions.setUseFullEditDistance(useFullEditDistance); 107981717da4c414caee57ba98596f9bc634a97f74fKeisuke Kuroynagi loadDictionary(filename, offset, length, isUpdatable); 108979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 109979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 110eaef1c500703b4ee378821884c7b108815ed2983Ken Wakasa static { 111cc8c8b99bd0463f5977dea82f5e2379ea1dd4e73Tadashi G. Takaoka JniUtils.loadNativeLibrary(); 112eaef1c500703b4ee378821884c7b108815ed2983Ken Wakasa } 113c2bbc6a4499a6da979381fa0e8e6e855a5ac6aa4Jean Chalard 1145ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi private static native boolean createEmptyDictFileNative(String filePath, long dictVersion, 1155ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi String[] attributeKeyStringArray, String[] attributeValueStringArray); 116f3b62900c7bcb0d6434f45ec7b467b7b4bad6f9aKeisuke Kuroynagi private static native long openNative(String sourceDir, long dictOffset, long dictSize, 117f3b62900c7bcb0d6434f45ec7b467b7b4bad6f9aKeisuke Kuroynagi boolean isUpdatable); 118d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi private static native void flushNative(long dict, String filePath); 119c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi private static native boolean needsToRunGCNative(long dict, boolean mindsBlockByGC); 120d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi private static native void flushWithGCNative(long dict, String filePath); 1215db594abbad2d9e8d2cf1aa6e417aa50ffc5dfc1Ken Wakasa private static native void closeNative(long dict); 122e0e67373735918c78eaeaf24f127e1d28816aa29Satoshi Kataoka private static native int getProbabilityNative(long dict, int[] word); 1234d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi private static native int getBigramProbabilityNative(long dict, int[] word0, int[] word1); 1245db594abbad2d9e8d2cf1aa6e417aa50ffc5dfc1Ken Wakasa private static native int getSuggestionsNative(long dict, long proximityInfo, 1255db594abbad2d9e8d2cf1aa6e417aa50ffc5dfc1Ken Wakasa long traverseSession, int[] xCoordinates, int[] yCoordinates, int[] times, 1265db594abbad2d9e8d2cf1aa6e417aa50ffc5dfc1Ken Wakasa int[] pointerIds, int[] inputCodePoints, int inputSize, int commitPoint, 12750db5f0fffdc35e9fe0bed27e7d630d571727e06Keisuke Kuroyanagi int[] suggestOptions, int[] prevWordCodePointArray, 12824aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard int[] outputCodePoints, int[] outputScores, int[] outputIndices, int[] outputTypes, 12924aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard int[] outputAutoCommitFirstWordConfidence); 1301e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa private static native float calcNormalizedScoreNative(int[] before, int[] after, int score); 1311e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa private static native int editDistanceNative(int[] before, int[] after); 1325bf1be71629607e7206e6203489cf742d2f8ed79Keisuke Kuroynagi private static native void addUnigramWordNative(long dict, int[] word, int probability); 1335bf1be71629607e7206e6203489cf742d2f8ed79Keisuke Kuroynagi private static native void addBigramWordsNative(long dict, int[] word0, int[] word1, 1345bf1be71629607e7206e6203489cf742d2f8ed79Keisuke Kuroynagi int probability); 1355bf1be71629607e7206e6203489cf742d2f8ed79Keisuke Kuroynagi private static native void removeBigramWordsNative(long dict, int[] word0, int[] word1); 1364d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi private static native int calculateProbabilityNative(long dict, int unigramProbability, 1374d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi int bigramProbability); 13831097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi private static native String getPropertyNative(long dict, String query); 139979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 1405ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi @UsedForTesting 1415ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi public static boolean createEmptyDictFile(final String filePath, final long dictVersion, 1425ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi final Map<String, String> attributeMap) { 1435ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi final String[] keyArray = new String[attributeMap.size()]; 1445ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi final String[] valueArray = new String[attributeMap.size()]; 1455ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi int index = 0; 1465ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi for (final String key : attributeMap.keySet()) { 1475ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi keyArray[index] = key; 1485ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi valueArray[index] = attributeMap.get(key); 1495ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi index++; 1505ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi } 1515ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi return createEmptyDictFileNative(filePath, dictVersion, keyArray, valueArray); 1525ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi } 1535ef6209656c51df0f0542d2a75c2df93c8d0f027Keisuke Kuroyanagi 154e9f3e182e4b3217282831fd8805958270b4dbba3Satoshi Kataoka // TODO: Move native dict into session 155bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka private final void loadDictionary(final String path, final long startOffset, 156981717da4c414caee57ba98596f9bc634a97f74fKeisuke Kuroynagi final long length, final boolean isUpdatable) { 157981717da4c414caee57ba98596f9bc634a97f74fKeisuke Kuroynagi mNativeDict = openNative(path, startOffset, length, isUpdatable); 158979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 159979f8690967ff5409fe18f5085858ccdb8e0ccf1satok 160979f8690967ff5409fe18f5085858ccdb8e0ccf1satok @Override 161b30d2185f24e3d531f5d46249e7c97391705e469Jean Chalard public ArrayList<SuggestedWordInfo> getSuggestions(final WordComposer composer, 1622dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard final String prevWord, final ProximityInfo proximityInfo, 163fe87f5f41744a633a2ed91af9b171bda2637649eKeisuke Kuroyanagi final boolean blockOffensiveWords, final int[] additionalFeaturesOptions) { 1642dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard return getSuggestionsWithSessionId(composer, prevWord, proximityInfo, blockOffensiveWords, 165fe87f5f41744a633a2ed91af9b171bda2637649eKeisuke Kuroyanagi additionalFeaturesOptions, 0 /* sessionId */); 1663979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka } 1673979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka 1683979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka @Override 1693979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka public ArrayList<SuggestedWordInfo> getSuggestionsWithSessionId(final WordComposer composer, 1702dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard final String prevWord, final ProximityInfo proximityInfo, 171fe87f5f41744a633a2ed91af9b171bda2637649eKeisuke Kuroyanagi final boolean blockOffensiveWords, final int[] additionalFeaturesOptions, 172fe87f5f41744a633a2ed91af9b171bda2637649eKeisuke Kuroyanagi final int sessionId) { 173b4b93dbf3e0912ba26c5c34abba7a5b94c74138cJean Chalard if (!isValidDictionary()) return null; 1743979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka 175ac78633be28e8990fc3b3a8de192c80966e746e3Tadashi G. Takaoka Arrays.fill(mInputCodePoints, Constants.NOT_A_CODE); 176ea98e026f1ad7732279aec6d06107f46ea0af93dJean Chalard // TODO: toLowerCase in the native code 1775e21ea1a3553000529c288acdf6d6a4b165bedc5Tadashi Takaoka final int[] prevWordCodePointArray = (null == prevWord) 178bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka ? null : StringUtils.toCodePointArray(prevWord); 179860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard final int composerSize = composer.size(); 180860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard 181e4e7e5fc82eca67330765510ad0bd29caeb7a1bbJean Chalard final boolean isGesture = composer.isBatchMode(); 182e4e7e5fc82eca67330765510ad0bd29caeb7a1bbJean Chalard if (composerSize <= 1 || !isGesture) { 183860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard if (composerSize > MAX_WORD_LENGTH - 1) return null; 184860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard for (int i = 0; i < composerSize; i++) { 185f3850e554389dc3012584f9d81a4f2d3d4c89e44Ken Wakasa mInputCodePoints[i] = composer.getCodeAt(i); 186860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard } 187860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard } 188860a9f85ff7f2753b7e1bed2e00f86de8eca68e1Jean Chalard 189251f302985bc491f4dd54983e9c69c5dc76cb834Jean Chalard final InputPointers ips = composer.getInputPointers(); 1905db594abbad2d9e8d2cf1aa6e417aa50ffc5dfc1Ken Wakasa final int inputSize = isGesture ? ips.getPointerSize() : composerSize; 19150db5f0fffdc35e9fe0bed27e7d630d571727e06Keisuke Kuroyanagi mNativeSuggestOptions.setIsGesture(isGesture); 192fe87f5f41744a633a2ed91af9b171bda2637649eKeisuke Kuroyanagi mNativeSuggestOptions.setAdditionalFeaturesOptions(additionalFeaturesOptions); 193251f302985bc491f4dd54983e9c69c5dc76cb834Jean Chalard // proximityInfo and/or prevWordForBigrams may not be null. 194f6870cc82ddf394e94155322fcc7e4e2256bea66Ken Wakasa final int count = getSuggestionsNative(mNativeDict, proximityInfo.getNativeProximityInfo(), 195f6870cc82ddf394e94155322fcc7e4e2256bea66Ken Wakasa getTraverseSession(sessionId).getSession(), ips.getXCoordinates(), 196f6870cc82ddf394e94155322fcc7e4e2256bea66Ken Wakasa ips.getYCoordinates(), ips.getTimes(), ips.getPointerIds(), mInputCodePoints, 19750db5f0fffdc35e9fe0bed27e7d630d571727e06Keisuke Kuroyanagi inputSize, 0 /* commitPoint */, mNativeSuggestOptions.getOptions(), 19850db5f0fffdc35e9fe0bed27e7d630d571727e06Keisuke Kuroyanagi prevWordCodePointArray, mOutputCodePoints, mOutputScores, mSpaceIndices, 19924aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard mOutputTypes, mOutputAutoCommitFirstWordConfidence); 2005f282ea9e4a4590fcbab6e27d5fca7dacbb40a6aTadashi G. Takaoka final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList(); 201f5cded1c6cf0f39df13750d4f9f5ba66c1b32964satok for (int j = 0; j < count; ++j) { 202f5cded1c6cf0f39df13750d4f9f5ba66c1b32964satok final int start = j * MAX_WORD_LENGTH; 203979f8690967ff5409fe18f5085858ccdb8e0ccf1satok int len = 0; 2041e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa while (len < MAX_WORD_LENGTH && mOutputCodePoints[start + len] != 0) { 205f5cded1c6cf0f39df13750d4f9f5ba66c1b32964satok ++len; 206979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 207979f8690967ff5409fe18f5085858ccdb8e0ccf1satok if (len > 0) { 20899e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard final int flags = mOutputTypes[j] & SuggestedWordInfo.KIND_MASK_FLAGS; 2092dbb5957e3c8354fa9bcb1e08c7ce81387b7fe25Jean Chalard if (blockOffensiveWords 210bb61293675234f6e5e1312db70618be985b37a83Jean Chalard && 0 != (flags & SuggestedWordInfo.KIND_FLAG_POSSIBLY_OFFENSIVE) 21199e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard && 0 == (flags & SuggestedWordInfo.KIND_FLAG_EXACT_MATCH)) { 212bb61293675234f6e5e1312db70618be985b37a83Jean Chalard // If we block potentially offensive words, and if the word is possibly 213bb61293675234f6e5e1312db70618be985b37a83Jean Chalard // offensive, then we don't output it unless it's also an exact match. 21499e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard continue; 21599e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard } 21699e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard final int kind = mOutputTypes[j] & SuggestedWordInfo.KIND_MASK_KIND; 21799e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard final int score = SuggestedWordInfo.KIND_WHITELIST == kind 218926ef06a1ef8a0f8e01baa26c23172b93012d2b1Jean Chalard ? SuggestedWordInfo.MAX_SCORE : mOutputScores[j]; 21999e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard // TODO: check that all users of the `kind' parameter are ready to accept 22099e998286d71cf698d0a809a29b15d1a231ebbb1Jean Chalard // flags too and pass mOutputTypes[j] instead of kind 2211e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa suggestions.add(new SuggestedWordInfo(new String(mOutputCodePoints, start, len), 222e8ef09567077211da034a77b457fd5f87e70f6f0Jean Chalard score, kind, this /* sourceDict */, 223e8754aba1c8f217e7ca828de25e0506ac58daa99Keisuke Kuroyanagi mSpaceIndices[j] /* indexOfTouchPointOfSecondWord */, 22424aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard mOutputAutoCommitFirstWordConfidence[0])); 225979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 226979f8690967ff5409fe18f5085858ccdb8e0ccf1satok } 227d82898c5a91f8aa69d5dc594b7a9290b8be1247aJean Chalard return suggestions; 228923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 229923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 23015f6d4ae34664ea3d92827a2c3003198c0bac70bTadashi G. Takaoka public boolean isValidDictionary() { 2316f4eba814a7f8426617db61f928a965209ebf359Tadashi G. Takaoka return mNativeDict != 0; 2326f4eba814a7f8426617db61f928a965209ebf359Tadashi G. Takaoka } 2336f4eba814a7f8426617db61f928a965209ebf359Tadashi G. Takaoka 234bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka public static float calcNormalizedScore(final String before, final String after, 235bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka final int score) { 2361e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa return calcNormalizedScoreNative(StringUtils.toCodePointArray(before), 2371e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa StringUtils.toCodePointArray(after), score); 238be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok } 239be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok 240bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka public static int editDistance(final String before, final String after) { 24166d955ad711d4a1248157056a1d3d643af19fd3fSatoshi Kataoka if (before == null || after == null) { 24266d955ad711d4a1248157056a1d3d643af19fd3fSatoshi Kataoka throw new IllegalArgumentException(); 24366d955ad711d4a1248157056a1d3d643af19fd3fSatoshi Kataoka } 2441e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa return editDistanceNative(StringUtils.toCodePointArray(before), 2451e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa StringUtils.toCodePointArray(after)); 246be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok } 247be0cf72253f15bff6abdeaa79f60a56f06ab7b86satok 248923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project @Override 249bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka public boolean isValidWord(final String word) { 2504d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi return getFrequency(word) != NOT_A_PROBABILITY; 251c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka } 252c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka 253c88f61215c5b9ca6e0cc3f776e3b7da19eec9caeSatoshi Kataoka @Override 254bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka public int getFrequency(final String word) { 2554d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi if (word == null) return NOT_A_PROBABILITY; 256bc464e2952e102219f0b977fc1e9140ad5bd03e4Tadashi G. Takaoka int[] codePoints = StringUtils.toCodePointArray(word); 257e0e67373735918c78eaeaf24f127e1d28816aa29Satoshi Kataoka return getProbabilityNative(mNativeDict, codePoints); 258923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 259c3df2d6fd27f3a5b84040b59aece3367769f0cb6Amith Yamasani 2604d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang // TODO: Add a batch process version (isValidBigramMultiple?) to avoid excessive numbers of jni 2614d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang // calls when checking for changes in an entire dictionary. 262f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi public boolean isValidBigram(final String word0, final String word1) { 2634d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi return getBigramProbability(word0, word1) != NOT_A_PROBABILITY; 2644d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi } 2654d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi 2664d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi public int getBigramProbability(final String word0, final String word1) { 2674d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) return NOT_A_PROBABILITY; 268f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi final int[] codePoints0 = StringUtils.toCodePointArray(word0); 2691e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa final int[] codePoints1 = StringUtils.toCodePointArray(word1); 2704d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi return getBigramProbabilityNative(mNativeDict, codePoints0, codePoints1); 271f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi } 272f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi 273f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi // Add a unigram entry to binary dictionary in native code. 274f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi public void addUnigramWord(final String word, final int probability) { 275f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi if (TextUtils.isEmpty(word)) { 276f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi return; 277f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi } 278f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi final int[] codePoints = StringUtils.toCodePointArray(word); 279f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi addUnigramWordNative(mNativeDict, codePoints, probability); 280f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi } 281f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi 282f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi // Add a bigram entry to binary dictionary in native code. 283f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi public void addBigramWords(final String word0, final String word1, final int probability) { 284f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) { 285f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi return; 286f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi } 287f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi final int[] codePoints0 = StringUtils.toCodePointArray(word0); 288f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi final int[] codePoints1 = StringUtils.toCodePointArray(word1); 289f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi addBigramWordsNative(mNativeDict, codePoints0, codePoints1, probability); 290f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi } 291f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi 292f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi // Remove a bigram entry form binary dictionary in native code. 293f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi public void removeBigramWords(final String word0, final String word1) { 294f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi if (TextUtils.isEmpty(word0) || TextUtils.isEmpty(word1)) { 295f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi return; 296f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi } 297f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi final int[] codePoints0 = StringUtils.toCodePointArray(word0); 298f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi final int[] codePoints1 = StringUtils.toCodePointArray(word1); 299f8d2796724b67ffb7e02c033ae15183a0d58febeKeisuke Kuroynagi removeBigramWordsNative(mNativeDict, codePoints0, codePoints1); 3004d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang } 3014d289d39aeae21064f63d958974816ceee3e9fdeTom Ouyang 3026142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi private void reopen() { 3036142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi close(); 3042e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi final File dictFile = new File(mDictFilePath); 3052e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi mNativeDict = openNative(dictFile.getAbsolutePath(), 0 /* startOffset */, 3062e58670da9687fd1fd28c322e03343957d11568cKeisuke Kuroyanagi dictFile.length(), true /* isUpdatable */); 307d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi } 308d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi 3096142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi public void flush() { 3106142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi if (!isValidDictionary()) return; 3116142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi flushNative(mNativeDict, mDictFilePath); 3126142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi reopen(); 3136142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi } 3146142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi 315d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi public void flushWithGC() { 316d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi if (!isValidDictionary()) return; 317d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi flushWithGCNative(mNativeDict, mDictFilePath); 3186142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi reopen(); 319d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi } 320d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi 321c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi /** 322c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi * Checks whether GC is needed to run or not. 323c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi * @param mindsBlockByGC Whether to mind operations blocked by GC. We don't need to care about 324c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi * the blocking in some situations such as in idle time or just before closing. 325c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi * @return whether GC is needed to run or not. 326c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi */ 327c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi public boolean needsToRunGC(final boolean mindsBlockByGC) { 328d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi if (!isValidDictionary()) return false; 329c18510049a3422c88ed3ab3bbc64944c94a611fdKeisuke Kuroyanagi return needsToRunGCNative(mNativeDict, mindsBlockByGC); 330d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi } 331d0246277fde27e9c40a270e206f1d106811e847fKeisuke Kuroyanagi 3324d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi @UsedForTesting 3334d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi public int calculateProbability(final int unigramProbability, final int bigramProbability) { 3344d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi if (!isValidDictionary()) return NOT_A_PROBABILITY; 3354d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi return calculateProbabilityNative(mNativeDict, unigramProbability, bigramProbability); 3364d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi } 3374d02a2d44db94985c9f079cdd58c7c51d3e557eeKeisuke Kuroyanagi 33831097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi @UsedForTesting 33931097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi public String getPropertyForTests(String query) { 34031097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi if (!isValidDictionary()) return ""; 34131097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi return getPropertyNative(mNativeDict, query); 34231097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi } 34331097a57cc6f8022abc0ea56f27147399f41b630Keisuke Kuroyanagi 34436fcf25833f7c8876cbc8286e0c159b052dc2626Amith Yamasani @Override 34524aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard public boolean shouldAutoCommit(final SuggestedWordInfo candidate) { 346459cd6f8ef3eaa561e47dd996ce537770ea8b37aJean Chalard return candidate.mAutoCommitFirstWordConfidence > CONFIDENCE_TO_AUTO_COMMIT; 34724aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard } 34824aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard 34924aad5a4d545e743fe43953c1a9d8141c022d355Jean Chalard @Override 3508ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka public void close() { 3518ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka synchronized (mDicTraverseSessions) { 3528ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka final int sessionsSize = mDicTraverseSessions.size(); 3538ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka for (int index = 0; index < sessionsSize; ++index) { 3548ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka final DicTraverseSession traverseSession = mDicTraverseSessions.valueAt(index); 3558ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka if (traverseSession != null) { 3568ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka traverseSession.close(); 3578ce351a8275f0ad73cdd642e8b46a430b072e8efTadashi G. Takaoka } 3583979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka } 3596142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi mDicTraverseSessions.clear(); 3603979f060f0650cbc117eee0307d05fb0be78c6f2Satoshi Kataoka } 3616142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi closeInternalLocked(); 362da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa } 363da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa 3646142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi private synchronized void closeInternalLocked() { 365923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project if (mNativeDict != 0) { 366923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project closeNative(mNativeDict); 367923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project mNativeDict = 0; 368923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 369923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 370923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project 3716142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi // TODO: Manage BinaryDictionary instances without using WeakReference or something. 372923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project @Override 373923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project protected void finalize() throws Throwable { 374da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa try { 3756142068a3311e4f828bb2acb0e4f9469c29a083fKeisuke Kuroyanagi closeInternalLocked(); 376da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa } finally { 377da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa super.finalize(); 378da50e1e98dadc3733c615dfb8d87fe8b4688c782Ken Wakasa } 379923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project } 380923bf41f853a544fd0d71fbf7dc90359ec35981The Android Open Source Project} 381