1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17 package com.android.inputmethod.latin; 18 19 import android.content.Context; 20 21 import com.android.inputmethod.keyboard.ProximityInfo; 22 23 import java.util.Arrays; 24 25 /** 26 * Implements a static, compacted, binary dictionary of standard words. 27 */ 28 public class BinaryDictionary extends Dictionary { 29 30 public static final String DICTIONARY_PACK_AUTHORITY = 31 "com.android.inputmethod.latin.dictionarypack"; 32 33 /** 34 * There is a difference between what java and native code can handle. 35 * This value should only be used in BinaryDictionary.java 36 * It is necessary to keep it at this value because some languages e.g. German have 37 * really long words. 38 */ 39 public static final int MAX_WORD_LENGTH = 48; 40 public static final int MAX_WORDS = 18; 41 42 private static final String TAG = "BinaryDictionary"; 43 private static final int MAX_PROXIMITY_CHARS_SIZE = ProximityInfo.MAX_PROXIMITY_CHARS_SIZE; 44 private static final int MAX_BIGRAMS = 60; 45 46 private static final int TYPED_LETTER_MULTIPLIER = 2; 47 48 private int mDicTypeId; 49 private int mNativeDict; 50 private final int[] mInputCodes = new int[MAX_WORD_LENGTH * MAX_PROXIMITY_CHARS_SIZE]; 51 private final char[] mOutputChars = new char[MAX_WORD_LENGTH * MAX_WORDS]; 52 private final char[] mOutputChars_bigrams = new char[MAX_WORD_LENGTH * MAX_BIGRAMS]; 53 private final int[] mScores = new int[MAX_WORDS]; 54 private final int[] mBigramScores = new int[MAX_BIGRAMS]; 55 56 public static final Flag FLAG_REQUIRES_GERMAN_UMLAUT_PROCESSING = 57 new Flag(R.bool.config_require_umlaut_processing, 0x1); 58 59 // FULL_EDIT_DISTANCE is a flag that forces the dictionary to use full words 60 // when computing edit distance, instead of the default behavior of stopping 61 // the evaluation at the size the user typed. 62 public static final Flag FLAG_USE_FULL_EDIT_DISTANCE = new Flag(0x2); 63 64 // Can create a new flag from extravalue : 65 // public static final Flag FLAG_MYFLAG = 66 // new Flag("my_flag", 0x02); 67 68 // ALL_CONFIG_FLAGS is a collection of flags that enable reading all flags from configuration. 69 // This is but a mask - it does not mean the flags will be on, only that the configuration 70 // will be read for this particular flag. 71 public static final Flag[] ALL_CONFIG_FLAGS = { 72 // Here should reside all flags that trigger some special processing 73 // These *must* match the definition in UnigramDictionary enum in 74 // unigram_dictionary.h so please update both at the same time. 75 // Please note that flags created with a resource are of type CONFIG while flags 76 // created with a string are of type EXTRAVALUE. These behave like masks, and the 77 // actual value will be read from the configuration/extra value at run time for 78 // the configuration at dictionary creation time. 79 FLAG_REQUIRES_GERMAN_UMLAUT_PROCESSING, 80 }; 81 82 private int mFlags = 0; 83 84 /** 85 * Constructor for the binary dictionary. This is supposed to be called from the 86 * dictionary factory. 87 * All implementations should pass null into flagArray, except for testing purposes. 88 * @param context the context to access the environment from. 89 * @param filename the name of the file to read through native code. 90 * @param offset the offset of the dictionary data within the file. 91 * @param length the length of the binary data. 92 * @param flagArray the flags to limit the dictionary to, or null for default. 93 */ BinaryDictionary(final Context context, final String filename, final long offset, final long length, Flag[] flagArray)94 public BinaryDictionary(final Context context, 95 final String filename, final long offset, final long length, Flag[] flagArray) { 96 // Note: at the moment a binary dictionary is always of the "main" type. 97 // Initializing this here will help transitioning out of the scheme where 98 // the Suggest class knows everything about every single dictionary. 99 mDicTypeId = Suggest.DIC_MAIN; 100 // TODO: Stop relying on the state of SubtypeSwitcher, get it as a parameter 101 mFlags = Flag.initFlags(null == flagArray ? ALL_CONFIG_FLAGS : flagArray, context, 102 SubtypeSwitcher.getInstance()); 103 loadDictionary(filename, offset, length); 104 } 105 106 static { Utils.loadNativeLibrary()107 Utils.loadNativeLibrary(); 108 } 109 openNative(String sourceDir, long dictOffset, long dictSize, int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength, int maxWords, int maxAlternatives)110 private native int openNative(String sourceDir, long dictOffset, long dictSize, 111 int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength, 112 int maxWords, int maxAlternatives); closeNative(int dict)113 private native void closeNative(int dict); isValidWordNative(int nativeData, char[] word, int wordLength)114 private native boolean isValidWordNative(int nativeData, char[] word, int wordLength); getSuggestionsNative(int dict, int proximityInfo, int[] xCoordinates, int[] yCoordinates, int[] inputCodes, int codesSize, int flags, char[] outputChars, int[] scores)115 private native int getSuggestionsNative(int dict, int proximityInfo, int[] xCoordinates, 116 int[] yCoordinates, int[] inputCodes, int codesSize, int flags, char[] outputChars, 117 int[] scores); getBigramsNative(int dict, char[] prevWord, int prevWordLength, int[] inputCodes, int inputCodesLength, char[] outputChars, int[] scores, int maxWordLength, int maxBigrams, int maxAlternatives)118 private native int getBigramsNative(int dict, char[] prevWord, int prevWordLength, 119 int[] inputCodes, int inputCodesLength, char[] outputChars, int[] scores, 120 int maxWordLength, int maxBigrams, int maxAlternatives); 121 loadDictionary(String path, long startOffset, long length)122 private final void loadDictionary(String path, long startOffset, long length) { 123 mNativeDict = openNative(path, startOffset, length, 124 TYPED_LETTER_MULTIPLIER, FULL_WORD_SCORE_MULTIPLIER, 125 MAX_WORD_LENGTH, MAX_WORDS, MAX_PROXIMITY_CHARS_SIZE); 126 } 127 128 @Override getBigrams(final WordComposer codes, final CharSequence previousWord, final WordCallback callback)129 public void getBigrams(final WordComposer codes, final CharSequence previousWord, 130 final WordCallback callback) { 131 if (mNativeDict == 0) return; 132 133 char[] chars = previousWord.toString().toCharArray(); 134 Arrays.fill(mOutputChars_bigrams, (char) 0); 135 Arrays.fill(mBigramScores, 0); 136 137 int codesSize = codes.size(); 138 if (codesSize <= 0) { 139 // Do not return bigrams from BinaryDictionary when nothing was typed. 140 // Only use user-history bigrams (or whatever other bigram dictionaries decide). 141 return; 142 } 143 Arrays.fill(mInputCodes, -1); 144 int[] alternatives = codes.getCodesAt(0); 145 System.arraycopy(alternatives, 0, mInputCodes, 0, 146 Math.min(alternatives.length, MAX_PROXIMITY_CHARS_SIZE)); 147 148 int count = getBigramsNative(mNativeDict, chars, chars.length, mInputCodes, codesSize, 149 mOutputChars_bigrams, mBigramScores, MAX_WORD_LENGTH, MAX_BIGRAMS, 150 MAX_PROXIMITY_CHARS_SIZE); 151 152 for (int j = 0; j < count; ++j) { 153 if (mBigramScores[j] < 1) break; 154 final int start = j * MAX_WORD_LENGTH; 155 int len = 0; 156 while (len < MAX_WORD_LENGTH && mOutputChars_bigrams[start + len] != 0) { 157 ++len; 158 } 159 if (len > 0) { 160 callback.addWord(mOutputChars_bigrams, start, len, mBigramScores[j], 161 mDicTypeId, DataType.BIGRAM); 162 } 163 } 164 } 165 166 // proximityInfo may not be null. 167 @Override getWords(final WordComposer codes, final WordCallback callback, final ProximityInfo proximityInfo)168 public void getWords(final WordComposer codes, final WordCallback callback, 169 final ProximityInfo proximityInfo) { 170 final int count = getSuggestions(codes, proximityInfo, mOutputChars, mScores); 171 172 for (int j = 0; j < count; ++j) { 173 if (mScores[j] < 1) break; 174 final int start = j * MAX_WORD_LENGTH; 175 int len = 0; 176 while (len < MAX_WORD_LENGTH && mOutputChars[start + len] != 0) { 177 ++len; 178 } 179 if (len > 0) { 180 callback.addWord(mOutputChars, start, len, mScores[j], mDicTypeId, 181 DataType.UNIGRAM); 182 } 183 } 184 } 185 isValidDictionary()186 /* package for test */ boolean isValidDictionary() { 187 return mNativeDict != 0; 188 } 189 190 // proximityInfo may not be null. getSuggestions(final WordComposer codes, final ProximityInfo proximityInfo, char[] outputChars, int[] scores)191 /* package for test */ int getSuggestions(final WordComposer codes, 192 final ProximityInfo proximityInfo, char[] outputChars, int[] scores) { 193 if (!isValidDictionary()) return -1; 194 195 final int codesSize = codes.size(); 196 // Won't deal with really long words. 197 if (codesSize > MAX_WORD_LENGTH - 1) return -1; 198 199 Arrays.fill(mInputCodes, WordComposer.NOT_A_CODE); 200 for (int i = 0; i < codesSize; i++) { 201 int[] alternatives = codes.getCodesAt(i); 202 System.arraycopy(alternatives, 0, mInputCodes, i * MAX_PROXIMITY_CHARS_SIZE, 203 Math.min(alternatives.length, MAX_PROXIMITY_CHARS_SIZE)); 204 } 205 Arrays.fill(outputChars, (char) 0); 206 Arrays.fill(scores, 0); 207 208 return getSuggestionsNative( 209 mNativeDict, proximityInfo.getNativeProximityInfo(), 210 codes.getXCoordinates(), codes.getYCoordinates(), mInputCodes, codesSize, 211 mFlags, outputChars, scores); 212 } 213 214 @Override isValidWord(CharSequence word)215 public boolean isValidWord(CharSequence word) { 216 if (word == null) return false; 217 char[] chars = word.toString().toCharArray(); 218 return isValidWordNative(mNativeDict, chars, chars.length); 219 } 220 221 @Override close()222 public synchronized void close() { 223 closeInternal(); 224 } 225 closeInternal()226 private void closeInternal() { 227 if (mNativeDict != 0) { 228 closeNative(mNativeDict); 229 mNativeDict = 0; 230 } 231 } 232 233 @Override finalize()234 protected void finalize() throws Throwable { 235 try { 236 closeInternal(); 237 } finally { 238 super.finalize(); 239 } 240 } 241 } 242