1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #define LOG_TAG "LatinIME: jni: BinaryDictionary" 18 19 #include "com_android_inputmethod_latin_BinaryDictionary.h" 20 21 #include <cstring> // for memset() 22 23 #include "defines.h" 24 #include "jni.h" 25 #include "jni_common.h" 26 #include "suggest/core/dictionary/dictionary.h" 27 #include "suggest/core/suggest_options.h" 28 #include "suggest/policyimpl/dictionary/dictionary_structure_with_buffer_policy_factory.h" 29 #include "suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h" 30 #include "utils/autocorrection_threshold_utils.h" 31 32 namespace latinime { 33 34 class ProximityInfo; 35 36 // TODO: Move to makedict. 37 static jboolean latinime_BinaryDictionary_createEmptyDictFile(JNIEnv *env, jclass clazz, 38 jstring filePath, jlong dictVersion, jobjectArray attributeKeyStringArray, 39 jobjectArray attributeValueStringArray) { 40 const jsize filePathUtf8Length = env->GetStringUTFLength(filePath); 41 char filePathChars[filePathUtf8Length + 1]; 42 env->GetStringUTFRegion(filePath, 0, env->GetStringLength(filePath), filePathChars); 43 filePathChars[filePathUtf8Length] = '\0'; 44 45 const int keyCount = env->GetArrayLength(attributeKeyStringArray); 46 const int valueCount = env->GetArrayLength(attributeValueStringArray); 47 if (keyCount != valueCount) { 48 return false; 49 } 50 51 HeaderReadWriteUtils::AttributeMap attributeMap; 52 for (int i = 0; i < keyCount; i++) { 53 jstring keyString = static_cast<jstring>( 54 env->GetObjectArrayElement(attributeKeyStringArray, i)); 55 const jsize keyUtf8Length = env->GetStringUTFLength(keyString); 56 char keyChars[keyUtf8Length + 1]; 57 env->GetStringUTFRegion(keyString, 0, env->GetStringLength(keyString), keyChars); 58 keyChars[keyUtf8Length] = '\0'; 59 HeaderReadWriteUtils::AttributeMap::key_type key; 60 HeaderReadWriteUtils::insertCharactersIntoVector(keyChars, &key); 61 62 jstring valueString = static_cast<jstring>( 63 env->GetObjectArrayElement(attributeValueStringArray, i)); 64 const jsize valueUtf8Length = env->GetStringUTFLength(valueString); 65 char valueChars[valueUtf8Length + 1]; 66 env->GetStringUTFRegion(valueString, 0, env->GetStringLength(valueString), valueChars); 67 valueChars[valueUtf8Length] = '\0'; 68 HeaderReadWriteUtils::AttributeMap::mapped_type value; 69 HeaderReadWriteUtils::insertCharactersIntoVector(valueChars, &value); 70 attributeMap[key] = value; 71 } 72 73 return DictFileWritingUtils::createEmptyDictFile(filePathChars, static_cast<int>(dictVersion), 74 &attributeMap); 75 } 76 77 static jlong latinime_BinaryDictionary_open(JNIEnv *env, jclass clazz, jstring sourceDir, 78 jlong dictOffset, jlong dictSize, jboolean isUpdatable) { 79 PROF_OPEN; 80 PROF_START(66); 81 const jsize sourceDirUtf8Length = env->GetStringUTFLength(sourceDir); 82 if (sourceDirUtf8Length <= 0) { 83 AKLOGE("DICT: Can't get sourceDir string"); 84 return 0; 85 } 86 char sourceDirChars[sourceDirUtf8Length + 1]; 87 env->GetStringUTFRegion(sourceDir, 0, env->GetStringLength(sourceDir), sourceDirChars); 88 sourceDirChars[sourceDirUtf8Length] = '\0'; 89 DictionaryStructureWithBufferPolicy *const dictionaryStructureWithBufferPolicy = 90 DictionaryStructureWithBufferPolicyFactory::newDictionaryStructureWithBufferPolicy( 91 sourceDirChars, static_cast<int>(dictOffset), static_cast<int>(dictSize), 92 isUpdatable == JNI_TRUE); 93 if (!dictionaryStructureWithBufferPolicy) { 94 return 0; 95 } 96 97 Dictionary *const dictionary = new Dictionary(env, dictionaryStructureWithBufferPolicy); 98 PROF_END(66); 99 PROF_CLOSE; 100 return reinterpret_cast<jlong>(dictionary); 101 } 102 103 static void latinime_BinaryDictionary_flush(JNIEnv *env, jclass clazz, jlong dict, 104 jstring filePath) { 105 Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); 106 if (!dictionary) return; 107 const jsize filePathUtf8Length = env->GetStringUTFLength(filePath); 108 char filePathChars[filePathUtf8Length + 1]; 109 env->GetStringUTFRegion(filePath, 0, env->GetStringLength(filePath), filePathChars); 110 filePathChars[filePathUtf8Length] = '\0'; 111 dictionary->flush(filePathChars); 112 } 113 114 static bool latinime_BinaryDictionary_needsToRunGC(JNIEnv *env, jclass clazz, 115 jlong dict, jboolean mindsBlockByGC) { 116 Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); 117 if (!dictionary) return false; 118 return dictionary->needsToRunGC(mindsBlockByGC == JNI_TRUE); 119 } 120 121 static void latinime_BinaryDictionary_flushWithGC(JNIEnv *env, jclass clazz, jlong dict, 122 jstring filePath) { 123 Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); 124 if (!dictionary) return; 125 const jsize filePathUtf8Length = env->GetStringUTFLength(filePath); 126 char filePathChars[filePathUtf8Length + 1]; 127 env->GetStringUTFRegion(filePath, 0, env->GetStringLength(filePath), filePathChars); 128 filePathChars[filePathUtf8Length] = '\0'; 129 dictionary->flushWithGC(filePathChars); 130 } 131 132 static void latinime_BinaryDictionary_close(JNIEnv *env, jclass clazz, jlong dict) { 133 Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); 134 if (!dictionary) return; 135 delete dictionary; 136 } 137 138 static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jclass clazz, jlong dict, 139 jlong proximityInfo, jlong dicTraverseSession, jintArray xCoordinatesArray, 140 jintArray yCoordinatesArray, jintArray timesArray, jintArray pointerIdsArray, 141 jintArray inputCodePointsArray, jint inputSize, jint commitPoint, jintArray suggestOptions, 142 jintArray prevWordCodePointsForBigrams, jintArray outputCodePointsArray, 143 jintArray scoresArray, jintArray spaceIndicesArray, jintArray outputTypesArray, 144 jintArray outputAutoCommitFirstWordConfidenceArray) { 145 Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); 146 if (!dictionary) return 0; 147 ProximityInfo *pInfo = reinterpret_cast<ProximityInfo *>(proximityInfo); 148 DicTraverseSession *traverseSession = 149 reinterpret_cast<DicTraverseSession *>(dicTraverseSession); 150 151 // Input values 152 int xCoordinates[inputSize]; 153 int yCoordinates[inputSize]; 154 int times[inputSize]; 155 int pointerIds[inputSize]; 156 const jsize inputCodePointsLength = env->GetArrayLength(inputCodePointsArray); 157 int inputCodePoints[inputCodePointsLength]; 158 const jsize prevWordCodePointsLength = 159 prevWordCodePointsForBigrams ? env->GetArrayLength(prevWordCodePointsForBigrams) : 0; 160 int prevWordCodePointsInternal[prevWordCodePointsLength]; 161 int *prevWordCodePoints = 0; 162 env->GetIntArrayRegion(xCoordinatesArray, 0, inputSize, xCoordinates); 163 env->GetIntArrayRegion(yCoordinatesArray, 0, inputSize, yCoordinates); 164 env->GetIntArrayRegion(timesArray, 0, inputSize, times); 165 env->GetIntArrayRegion(pointerIdsArray, 0, inputSize, pointerIds); 166 env->GetIntArrayRegion(inputCodePointsArray, 0, inputCodePointsLength, inputCodePoints); 167 if (prevWordCodePointsForBigrams) { 168 env->GetIntArrayRegion(prevWordCodePointsForBigrams, 0, prevWordCodePointsLength, 169 prevWordCodePointsInternal); 170 prevWordCodePoints = prevWordCodePointsInternal; 171 } 172 173 const jsize numberOfOptions = env->GetArrayLength(suggestOptions); 174 int options[numberOfOptions]; 175 env->GetIntArrayRegion(suggestOptions, 0, numberOfOptions, options); 176 SuggestOptions givenSuggestOptions(options, numberOfOptions); 177 178 // Output values 179 /* By the way, let's check the output array length here to make sure */ 180 const jsize outputCodePointsLength = env->GetArrayLength(outputCodePointsArray); 181 if (outputCodePointsLength != (MAX_WORD_LENGTH * MAX_RESULTS)) { 182 AKLOGE("Invalid outputCodePointsLength: %d", outputCodePointsLength); 183 ASSERT(false); 184 return 0; 185 } 186 const jsize scoresLength = env->GetArrayLength(scoresArray); 187 if (scoresLength != MAX_RESULTS) { 188 AKLOGE("Invalid scoresLength: %d", scoresLength); 189 ASSERT(false); 190 return 0; 191 } 192 int outputCodePoints[outputCodePointsLength]; 193 int scores[scoresLength]; 194 const jsize spaceIndicesLength = env->GetArrayLength(spaceIndicesArray); 195 int spaceIndices[spaceIndicesLength]; 196 const jsize outputTypesLength = env->GetArrayLength(outputTypesArray); 197 int outputTypes[outputTypesLength]; 198 const jsize outputAutoCommitFirstWordConfidenceLength = 199 env->GetArrayLength(outputAutoCommitFirstWordConfidenceArray); 200 // We only use the first result, as obviously we will only ever autocommit the first one 201 ASSERT(outputAutoCommitFirstWordConfidenceLength == 1); 202 int outputAutoCommitFirstWordConfidence[outputAutoCommitFirstWordConfidenceLength]; 203 memset(outputCodePoints, 0, sizeof(outputCodePoints)); 204 memset(scores, 0, sizeof(scores)); 205 memset(spaceIndices, 0, sizeof(spaceIndices)); 206 memset(outputTypes, 0, sizeof(outputTypes)); 207 memset(outputAutoCommitFirstWordConfidence, 0, sizeof(outputAutoCommitFirstWordConfidence)); 208 209 int count; 210 if (givenSuggestOptions.isGesture() || inputSize > 0) { 211 count = dictionary->getSuggestions(pInfo, traverseSession, xCoordinates, yCoordinates, 212 times, pointerIds, inputCodePoints, inputSize, prevWordCodePoints, 213 prevWordCodePointsLength, commitPoint, &givenSuggestOptions, outputCodePoints, 214 scores, spaceIndices, outputTypes, outputAutoCommitFirstWordConfidence); 215 } else { 216 count = dictionary->getBigrams(prevWordCodePoints, prevWordCodePointsLength, 217 outputCodePoints, scores, outputTypes); 218 } 219 220 // Copy back the output values 221 env->SetIntArrayRegion(outputCodePointsArray, 0, outputCodePointsLength, outputCodePoints); 222 env->SetIntArrayRegion(scoresArray, 0, scoresLength, scores); 223 env->SetIntArrayRegion(spaceIndicesArray, 0, spaceIndicesLength, spaceIndices); 224 env->SetIntArrayRegion(outputTypesArray, 0, outputTypesLength, outputTypes); 225 env->SetIntArrayRegion(outputAutoCommitFirstWordConfidenceArray, 0, 226 outputAutoCommitFirstWordConfidenceLength, outputAutoCommitFirstWordConfidence); 227 228 return count; 229 } 230 231 static jint latinime_BinaryDictionary_getProbability(JNIEnv *env, jclass clazz, jlong dict, 232 jintArray word) { 233 Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); 234 if (!dictionary) return NOT_A_PROBABILITY; 235 const jsize wordLength = env->GetArrayLength(word); 236 int codePoints[wordLength]; 237 env->GetIntArrayRegion(word, 0, wordLength, codePoints); 238 return dictionary->getProbability(codePoints, wordLength); 239 } 240 241 static jint latinime_BinaryDictionary_getBigramProbability(JNIEnv *env, jclass clazz, 242 jlong dict, jintArray word0, jintArray word1) { 243 Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); 244 if (!dictionary) return JNI_FALSE; 245 const jsize word0Length = env->GetArrayLength(word0); 246 const jsize word1Length = env->GetArrayLength(word1); 247 int word0CodePoints[word0Length]; 248 int word1CodePoints[word1Length]; 249 env->GetIntArrayRegion(word0, 0, word0Length, word0CodePoints); 250 env->GetIntArrayRegion(word1, 0, word1Length, word1CodePoints); 251 return dictionary->getBigramProbability(word0CodePoints, word0Length, word1CodePoints, 252 word1Length); 253 } 254 255 static jfloat latinime_BinaryDictionary_calcNormalizedScore(JNIEnv *env, jclass clazz, 256 jintArray before, jintArray after, jint score) { 257 jsize beforeLength = env->GetArrayLength(before); 258 jsize afterLength = env->GetArrayLength(after); 259 int beforeCodePoints[beforeLength]; 260 int afterCodePoints[afterLength]; 261 env->GetIntArrayRegion(before, 0, beforeLength, beforeCodePoints); 262 env->GetIntArrayRegion(after, 0, afterLength, afterCodePoints); 263 return AutocorrectionThresholdUtils::calcNormalizedScore(beforeCodePoints, beforeLength, 264 afterCodePoints, afterLength, score); 265 } 266 267 static jint latinime_BinaryDictionary_editDistance(JNIEnv *env, jclass clazz, jintArray before, 268 jintArray after) { 269 jsize beforeLength = env->GetArrayLength(before); 270 jsize afterLength = env->GetArrayLength(after); 271 int beforeCodePoints[beforeLength]; 272 int afterCodePoints[afterLength]; 273 env->GetIntArrayRegion(before, 0, beforeLength, beforeCodePoints); 274 env->GetIntArrayRegion(after, 0, afterLength, afterCodePoints); 275 return AutocorrectionThresholdUtils::editDistance(beforeCodePoints, beforeLength, 276 afterCodePoints, afterLength); 277 } 278 279 static void latinime_BinaryDictionary_addUnigramWord(JNIEnv *env, jclass clazz, jlong dict, 280 jintArray word, jint probability) { 281 Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); 282 if (!dictionary) { 283 return; 284 } 285 jsize wordLength = env->GetArrayLength(word); 286 int codePoints[wordLength]; 287 env->GetIntArrayRegion(word, 0, wordLength, codePoints); 288 dictionary->addUnigramWord(codePoints, wordLength, probability); 289 } 290 291 static void latinime_BinaryDictionary_addBigramWords(JNIEnv *env, jclass clazz, jlong dict, 292 jintArray word0, jintArray word1, jint probability) { 293 Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); 294 if (!dictionary) { 295 return; 296 } 297 jsize word0Length = env->GetArrayLength(word0); 298 int word0CodePoints[word0Length]; 299 env->GetIntArrayRegion(word0, 0, word0Length, word0CodePoints); 300 jsize word1Length = env->GetArrayLength(word1); 301 int word1CodePoints[word1Length]; 302 env->GetIntArrayRegion(word1, 0, word1Length, word1CodePoints); 303 dictionary->addBigramWords(word0CodePoints, word0Length, word1CodePoints, 304 word1Length, probability); 305 } 306 307 static void latinime_BinaryDictionary_removeBigramWords(JNIEnv *env, jclass clazz, jlong dict, 308 jintArray word0, jintArray word1) { 309 Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); 310 if (!dictionary) { 311 return; 312 } 313 jsize word0Length = env->GetArrayLength(word0); 314 int word0CodePoints[word0Length]; 315 env->GetIntArrayRegion(word0, 0, word0Length, word0CodePoints); 316 jsize word1Length = env->GetArrayLength(word1); 317 int word1CodePoints[word1Length]; 318 env->GetIntArrayRegion(word1, 0, word1Length, word1CodePoints); 319 dictionary->removeBigramWords(word0CodePoints, word0Length, word1CodePoints, 320 word1Length); 321 } 322 323 static int latinime_BinaryDictionary_calculateProbabilityNative(JNIEnv *env, jclass clazz, 324 jlong dict, jint unigramProbability, jint bigramProbability) { 325 Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); 326 if (!dictionary) { 327 return NOT_A_PROBABILITY; 328 } 329 return dictionary->getDictionaryStructurePolicy()->getProbability(unigramProbability, 330 bigramProbability); 331 } 332 333 static jstring latinime_BinaryDictionary_getProperty(JNIEnv *env, jclass clazz, jlong dict, 334 jstring query) { 335 Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict); 336 if (!dictionary) { 337 return env->NewStringUTF(""); 338 } 339 const jsize queryUtf8Length = env->GetStringUTFLength(query); 340 char queryChars[queryUtf8Length + 1]; 341 env->GetStringUTFRegion(query, 0, env->GetStringLength(query), queryChars); 342 queryChars[queryUtf8Length] = '\0'; 343 static const int GET_PROPERTY_RESULT_LENGTH = 100; 344 char resultChars[GET_PROPERTY_RESULT_LENGTH]; 345 resultChars[0] = '\0'; 346 dictionary->getProperty(queryChars, resultChars, GET_PROPERTY_RESULT_LENGTH); 347 return env->NewStringUTF(resultChars); 348 } 349 350 static const JNINativeMethod sMethods[] = { 351 { 352 const_cast<char *>("createEmptyDictFileNative"), 353 const_cast<char *>("(Ljava/lang/String;J[Ljava/lang/String;[Ljava/lang/String;)Z"), 354 reinterpret_cast<void *>(latinime_BinaryDictionary_createEmptyDictFile) 355 }, 356 { 357 const_cast<char *>("openNative"), 358 const_cast<char *>("(Ljava/lang/String;JJZ)J"), 359 reinterpret_cast<void *>(latinime_BinaryDictionary_open) 360 }, 361 { 362 const_cast<char *>("closeNative"), 363 const_cast<char *>("(J)V"), 364 reinterpret_cast<void *>(latinime_BinaryDictionary_close) 365 }, 366 { 367 const_cast<char *>("flushNative"), 368 const_cast<char *>("(JLjava/lang/String;)V"), 369 reinterpret_cast<void *>(latinime_BinaryDictionary_flush) 370 }, 371 { 372 const_cast<char *>("needsToRunGCNative"), 373 const_cast<char *>("(JZ)Z"), 374 reinterpret_cast<void *>(latinime_BinaryDictionary_needsToRunGC) 375 }, 376 { 377 const_cast<char *>("flushWithGCNative"), 378 const_cast<char *>("(JLjava/lang/String;)V"), 379 reinterpret_cast<void *>(latinime_BinaryDictionary_flushWithGC) 380 }, 381 { 382 const_cast<char *>("getSuggestionsNative"), 383 const_cast<char *>("(JJJ[I[I[I[I[III[I[I[I[I[I[I[I)I"), 384 reinterpret_cast<void *>(latinime_BinaryDictionary_getSuggestions) 385 }, 386 { 387 const_cast<char *>("getProbabilityNative"), 388 const_cast<char *>("(J[I)I"), 389 reinterpret_cast<void *>(latinime_BinaryDictionary_getProbability) 390 }, 391 { 392 const_cast<char *>("getBigramProbabilityNative"), 393 const_cast<char *>("(J[I[I)I"), 394 reinterpret_cast<void *>(latinime_BinaryDictionary_getBigramProbability) 395 }, 396 { 397 const_cast<char *>("calcNormalizedScoreNative"), 398 const_cast<char *>("([I[II)F"), 399 reinterpret_cast<void *>(latinime_BinaryDictionary_calcNormalizedScore) 400 }, 401 { 402 const_cast<char *>("editDistanceNative"), 403 const_cast<char *>("([I[I)I"), 404 reinterpret_cast<void *>(latinime_BinaryDictionary_editDistance) 405 }, 406 { 407 const_cast<char *>("addUnigramWordNative"), 408 const_cast<char *>("(J[II)V"), 409 reinterpret_cast<void *>(latinime_BinaryDictionary_addUnigramWord) 410 }, 411 { 412 const_cast<char *>("addBigramWordsNative"), 413 const_cast<char *>("(J[I[II)V"), 414 reinterpret_cast<void *>(latinime_BinaryDictionary_addBigramWords) 415 }, 416 { 417 const_cast<char *>("removeBigramWordsNative"), 418 const_cast<char *>("(J[I[I)V"), 419 reinterpret_cast<void *>(latinime_BinaryDictionary_removeBigramWords) 420 }, 421 { 422 const_cast<char *>("calculateProbabilityNative"), 423 const_cast<char *>("(JII)I"), 424 reinterpret_cast<void *>(latinime_BinaryDictionary_calculateProbabilityNative) 425 }, 426 { 427 const_cast<char *>("getPropertyNative"), 428 const_cast<char *>("(JLjava/lang/String;)Ljava/lang/String;"), 429 reinterpret_cast<void *>(latinime_BinaryDictionary_getProperty) 430 } 431 }; 432 433 int register_BinaryDictionary(JNIEnv *env) { 434 const char *const kClassPathName = "com/android/inputmethod/latin/BinaryDictionary"; 435 return registerNativeMethods(env, kClassPathName, sMethods, NELEMS(sMethods)); 436 } 437 } // namespace latinime 438