1 /*
2 * Copyright (C) 2018 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 // JNI wrapper for the Annotator.
18
19 #include "annotator/annotator_jni.h"
20
21 #include <jni.h>
22
23 #include <type_traits>
24 #include <utility>
25 #include <vector>
26
27 #include "annotator/annotator.h"
28 #include "annotator/annotator_jni_common.h"
29 #include "annotator/knowledge/knowledge-engine-types.h"
30 #include "annotator/types.h"
31 #include "utils/base/integral_types.h"
32 #include "utils/base/status_macros.h"
33 #include "utils/base/statusor.h"
34 #include "utils/calendar/calendar.h"
35 #include "utils/intents/intent-generator.h"
36 #include "utils/intents/jni.h"
37 #include "utils/intents/remote-action-template.h"
38 #include "utils/java/jni-cache.h"
39 #include "utils/java/jni-helper.h"
40 #include "utils/memory/mmap.h"
41 #include "utils/strings/stringpiece.h"
42 #include "utils/utf8/unilib.h"
43
44 #ifdef TC3_UNILIB_JAVAICU
45 #ifndef TC3_CALENDAR_JAVAICU
46 #error Inconsistent usage of Java ICU components
47 #else
48 #define TC3_USE_JAVAICU
49 #endif
50 #endif
51
52 using libtextclassifier3::AnnotatedSpan;
53 using libtextclassifier3::Annotations;
54 using libtextclassifier3::Annotator;
55 using libtextclassifier3::ClassificationResult;
56 using libtextclassifier3::CodepointSpan;
57 using libtextclassifier3::JniHelper;
58 using libtextclassifier3::Model;
59 using libtextclassifier3::ScopedLocalRef;
60 using libtextclassifier3::StatusOr;
61 // When using the Java's ICU, CalendarLib and UniLib need to be instantiated
62 // with a JavaVM pointer from JNI. When using a standard ICU the pointer is
63 // not needed and the objects are instantiated implicitly.
64 #ifdef TC3_USE_JAVAICU
65 using libtextclassifier3::CalendarLib;
66 using libtextclassifier3::UniLib;
67 #endif
68
69 namespace libtextclassifier3 {
70
71 using libtextclassifier3::CodepointSpan;
72
73 namespace {
74 class AnnotatorJniContext {
75 public:
Create(const std::shared_ptr<libtextclassifier3::JniCache> & jni_cache,std::unique_ptr<Annotator> model)76 static AnnotatorJniContext* Create(
77 const std::shared_ptr<libtextclassifier3::JniCache>& jni_cache,
78 std::unique_ptr<Annotator> model) {
79 if (jni_cache == nullptr || model == nullptr) {
80 return nullptr;
81 }
82 // Intent generator will be null if the options are not specified.
83 std::unique_ptr<IntentGenerator> intent_generator =
84 IntentGenerator::Create(model->model()->intent_options(),
85 model->model()->resources(), jni_cache);
86 TC3_ASSIGN_OR_RETURN_NULL(
87 std::unique_ptr<RemoteActionTemplatesHandler> template_handler,
88 libtextclassifier3::RemoteActionTemplatesHandler::Create(jni_cache));
89
90 return new AnnotatorJniContext(jni_cache, std::move(model),
91 std::move(intent_generator),
92 std::move(template_handler));
93 }
94
jni_cache() const95 std::shared_ptr<libtextclassifier3::JniCache> jni_cache() const {
96 return jni_cache_;
97 }
98
model() const99 Annotator* model() const { return model_.get(); }
100
101 // NOTE: Intent generator will be null if the options are not specified in
102 // the model.
intent_generator() const103 IntentGenerator* intent_generator() const { return intent_generator_.get(); }
104
template_handler() const105 RemoteActionTemplatesHandler* template_handler() const {
106 return template_handler_.get();
107 }
108
109 private:
AnnotatorJniContext(const std::shared_ptr<libtextclassifier3::JniCache> & jni_cache,std::unique_ptr<Annotator> model,std::unique_ptr<IntentGenerator> intent_generator,std::unique_ptr<RemoteActionTemplatesHandler> template_handler)110 AnnotatorJniContext(
111 const std::shared_ptr<libtextclassifier3::JniCache>& jni_cache,
112 std::unique_ptr<Annotator> model,
113 std::unique_ptr<IntentGenerator> intent_generator,
114 std::unique_ptr<RemoteActionTemplatesHandler> template_handler)
115 : jni_cache_(jni_cache),
116 model_(std::move(model)),
117 intent_generator_(std::move(intent_generator)),
118 template_handler_(std::move(template_handler)) {}
119
120 std::shared_ptr<libtextclassifier3::JniCache> jni_cache_;
121 std::unique_ptr<Annotator> model_;
122 std::unique_ptr<IntentGenerator> intent_generator_;
123 std::unique_ptr<RemoteActionTemplatesHandler> template_handler_;
124 };
125
ClassificationResultWithIntentsToJObject(JNIEnv * env,const AnnotatorJniContext * model_context,jobject app_context,jclass result_class,jmethodID result_class_constructor,jclass datetime_parse_class,jmethodID datetime_parse_class_constructor,const jstring device_locales,const ClassificationOptions * options,const std::string & context,const CodepointSpan & selection_indices,const ClassificationResult & classification_result,bool generate_intents)126 StatusOr<ScopedLocalRef<jobject>> ClassificationResultWithIntentsToJObject(
127 JNIEnv* env, const AnnotatorJniContext* model_context, jobject app_context,
128 jclass result_class, jmethodID result_class_constructor,
129 jclass datetime_parse_class, jmethodID datetime_parse_class_constructor,
130 const jstring device_locales, const ClassificationOptions* options,
131 const std::string& context, const CodepointSpan& selection_indices,
132 const ClassificationResult& classification_result, bool generate_intents) {
133 TC3_ASSIGN_OR_RETURN(
134 ScopedLocalRef<jstring> row_string,
135 JniHelper::NewStringUTF(env, classification_result.collection.c_str()));
136
137 ScopedLocalRef<jobject> row_datetime_parse;
138 if (classification_result.datetime_parse_result.IsSet()) {
139 TC3_ASSIGN_OR_RETURN(
140 row_datetime_parse,
141 JniHelper::NewObject(
142 env, datetime_parse_class, datetime_parse_class_constructor,
143 classification_result.datetime_parse_result.time_ms_utc,
144 classification_result.datetime_parse_result.granularity));
145 }
146
147 ScopedLocalRef<jbyteArray> serialized_knowledge_result;
148 const std::string& serialized_knowledge_result_string =
149 classification_result.serialized_knowledge_result;
150 if (!serialized_knowledge_result_string.empty()) {
151 TC3_ASSIGN_OR_RETURN(serialized_knowledge_result,
152 JniHelper::NewByteArray(
153 env, serialized_knowledge_result_string.size()));
154 TC3_RETURN_IF_ERROR(JniHelper::SetByteArrayRegion(
155 env, serialized_knowledge_result.get(), 0,
156 serialized_knowledge_result_string.size(),
157 reinterpret_cast<const jbyte*>(
158 serialized_knowledge_result_string.data())));
159 }
160
161 ScopedLocalRef<jstring> contact_name;
162 if (!classification_result.contact_name.empty()) {
163 TC3_ASSIGN_OR_RETURN(contact_name,
164 JniHelper::NewStringUTF(
165 env, classification_result.contact_name.c_str()));
166 }
167
168 ScopedLocalRef<jstring> contact_given_name;
169 if (!classification_result.contact_given_name.empty()) {
170 TC3_ASSIGN_OR_RETURN(
171 contact_given_name,
172 JniHelper::NewStringUTF(
173 env, classification_result.contact_given_name.c_str()));
174 }
175
176 ScopedLocalRef<jstring> contact_family_name;
177 if (!classification_result.contact_family_name.empty()) {
178 TC3_ASSIGN_OR_RETURN(
179 contact_family_name,
180 JniHelper::NewStringUTF(
181 env, classification_result.contact_family_name.c_str()));
182 }
183
184 ScopedLocalRef<jstring> contact_nickname;
185 if (!classification_result.contact_nickname.empty()) {
186 TC3_ASSIGN_OR_RETURN(
187 contact_nickname,
188 JniHelper::NewStringUTF(
189 env, classification_result.contact_nickname.c_str()));
190 }
191
192 ScopedLocalRef<jstring> contact_email_address;
193 if (!classification_result.contact_email_address.empty()) {
194 TC3_ASSIGN_OR_RETURN(
195 contact_email_address,
196 JniHelper::NewStringUTF(
197 env, classification_result.contact_email_address.c_str()));
198 }
199
200 ScopedLocalRef<jstring> contact_phone_number;
201 if (!classification_result.contact_phone_number.empty()) {
202 TC3_ASSIGN_OR_RETURN(
203 contact_phone_number,
204 JniHelper::NewStringUTF(
205 env, classification_result.contact_phone_number.c_str()));
206 }
207
208 ScopedLocalRef<jstring> contact_account_type;
209 if (!classification_result.contact_account_type.empty()) {
210 TC3_ASSIGN_OR_RETURN(
211 contact_account_type,
212 JniHelper::NewStringUTF(
213 env, classification_result.contact_account_type.c_str()));
214 }
215
216 ScopedLocalRef<jstring> contact_account_name;
217 if (!classification_result.contact_account_name.empty()) {
218 TC3_ASSIGN_OR_RETURN(
219 contact_account_name,
220 JniHelper::NewStringUTF(
221 env, classification_result.contact_account_name.c_str()));
222 }
223
224 ScopedLocalRef<jstring> contact_id;
225 if (!classification_result.contact_id.empty()) {
226 TC3_ASSIGN_OR_RETURN(
227 contact_id,
228 JniHelper::NewStringUTF(env, classification_result.contact_id.c_str()));
229 }
230
231 ScopedLocalRef<jstring> app_name;
232 if (!classification_result.app_name.empty()) {
233 TC3_ASSIGN_OR_RETURN(
234 app_name,
235 JniHelper::NewStringUTF(env, classification_result.app_name.c_str()));
236 }
237
238 ScopedLocalRef<jstring> app_package_name;
239 if (!classification_result.app_package_name.empty()) {
240 TC3_ASSIGN_OR_RETURN(
241 app_package_name,
242 JniHelper::NewStringUTF(
243 env, classification_result.app_package_name.c_str()));
244 }
245
246 ScopedLocalRef<jobjectArray> extras;
247 if (model_context->model()->entity_data_schema() != nullptr &&
248 !classification_result.serialized_entity_data.empty()) {
249 TC3_ASSIGN_OR_RETURN(
250 extras,
251 model_context->template_handler()->EntityDataAsNamedVariantArray(
252 model_context->model()->entity_data_schema(),
253 classification_result.serialized_entity_data));
254 }
255
256 ScopedLocalRef<jbyteArray> serialized_entity_data;
257 if (!classification_result.serialized_entity_data.empty()) {
258 TC3_ASSIGN_OR_RETURN(
259 serialized_entity_data,
260 JniHelper::NewByteArray(
261 env, classification_result.serialized_entity_data.size()));
262 TC3_RETURN_IF_ERROR(JniHelper::SetByteArrayRegion(
263 env, serialized_entity_data.get(), 0,
264 classification_result.serialized_entity_data.size(),
265 reinterpret_cast<const jbyte*>(
266 classification_result.serialized_entity_data.data())));
267 }
268
269 ScopedLocalRef<jobjectArray> remote_action_templates_result;
270 // Only generate RemoteActionTemplate for the top classification result
271 // as classifyText does not need RemoteAction from other results anyway.
272 if (generate_intents && model_context->intent_generator() != nullptr) {
273 std::vector<RemoteActionTemplate> remote_action_templates;
274 if (!model_context->intent_generator()->GenerateIntents(
275 device_locales, classification_result,
276 options->reference_time_ms_utc, context, selection_indices,
277 app_context, model_context->model()->entity_data_schema(),
278 &remote_action_templates)) {
279 return {Status::UNKNOWN};
280 }
281
282 TC3_ASSIGN_OR_RETURN(
283 remote_action_templates_result,
284 model_context->template_handler()->RemoteActionTemplatesToJObjectArray(
285 remote_action_templates));
286 }
287
288 return JniHelper::NewObject(
289 env, result_class, result_class_constructor, row_string.get(),
290 static_cast<jfloat>(classification_result.score),
291 row_datetime_parse.get(), serialized_knowledge_result.get(),
292 contact_name.get(), contact_given_name.get(), contact_family_name.get(),
293 contact_nickname.get(), contact_email_address.get(),
294 contact_phone_number.get(), contact_account_type.get(),
295 contact_account_name.get(), contact_id.get(), app_name.get(),
296 app_package_name.get(), extras.get(), serialized_entity_data.get(),
297 remote_action_templates_result.get(), classification_result.duration_ms,
298 classification_result.numeric_value,
299 classification_result.numeric_double_value);
300 }
301
302 StatusOr<ScopedLocalRef<jobjectArray>>
ClassificationResultsWithIntentsToJObjectArray(JNIEnv * env,const AnnotatorJniContext * model_context,jobject app_context,const jstring device_locales,const ClassificationOptions * options,const std::string & context,const CodepointSpan & selection_indices,const std::vector<ClassificationResult> & classification_result,bool generate_intents)303 ClassificationResultsWithIntentsToJObjectArray(
304 JNIEnv* env, const AnnotatorJniContext* model_context, jobject app_context,
305 const jstring device_locales, const ClassificationOptions* options,
306 const std::string& context, const CodepointSpan& selection_indices,
307 const std::vector<ClassificationResult>& classification_result,
308 bool generate_intents) {
309 TC3_ASSIGN_OR_RETURN(
310 ScopedLocalRef<jclass> result_class,
311 JniHelper::FindClass(env, TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR
312 "$ClassificationResult"));
313
314 TC3_ASSIGN_OR_RETURN(
315 ScopedLocalRef<jclass> datetime_parse_class,
316 JniHelper::FindClass(env, TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR
317 "$DatetimeResult"));
318
319 TC3_ASSIGN_OR_RETURN(
320 const jmethodID result_class_constructor,
321 JniHelper::GetMethodID(
322 env, result_class.get(), "<init>",
323 "(Ljava/lang/String;FL" TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR
324 "$DatetimeResult;"
325 "[B"
326 "Ljava/lang/String;"
327 "Ljava/lang/String;"
328 "Ljava/lang/String;"
329 "Ljava/lang/String;"
330 "Ljava/lang/String;"
331 "Ljava/lang/String;"
332 "Ljava/lang/String;"
333 "Ljava/lang/String;"
334 "Ljava/lang/String;"
335 "Ljava/lang/String;"
336 "Ljava/lang/String;"
337 "[L" TC3_PACKAGE_PATH "" TC3_NAMED_VARIANT_CLASS_NAME_STR ";"
338 "[B"
339 "[L" TC3_PACKAGE_PATH "" TC3_REMOTE_ACTION_TEMPLATE_CLASS_NAME_STR ";"
340 "JJD)V"));
341 TC3_ASSIGN_OR_RETURN(const jmethodID datetime_parse_class_constructor,
342 JniHelper::GetMethodID(env, datetime_parse_class.get(),
343 "<init>", "(JI)V"));
344
345 TC3_ASSIGN_OR_RETURN(
346 ScopedLocalRef<jobjectArray> results,
347 JniHelper::NewObjectArray(env, classification_result.size(),
348 result_class.get()));
349
350 for (int i = 0; i < classification_result.size(); i++) {
351 TC3_ASSIGN_OR_RETURN(
352 ScopedLocalRef<jobject> result,
353 ClassificationResultWithIntentsToJObject(
354 env, model_context, app_context, result_class.get(),
355 result_class_constructor, datetime_parse_class.get(),
356 datetime_parse_class_constructor, device_locales, options, context,
357 selection_indices, classification_result[i],
358 generate_intents && (i == 0)));
359 TC3_RETURN_IF_ERROR(
360 JniHelper::SetObjectArrayElement(env, results.get(), i, result.get()));
361 }
362 return results;
363 }
364
ClassificationResultsToJObjectArray(JNIEnv * env,const AnnotatorJniContext * model_context,const std::vector<ClassificationResult> & classification_result)365 StatusOr<ScopedLocalRef<jobjectArray>> ClassificationResultsToJObjectArray(
366 JNIEnv* env, const AnnotatorJniContext* model_context,
367 const std::vector<ClassificationResult>& classification_result) {
368 return ClassificationResultsWithIntentsToJObjectArray(
369 env, model_context,
370 /*(unused) app_context=*/nullptr,
371 /*(unused) device_locale=*/nullptr,
372 /*(unusued) options=*/nullptr,
373 /*(unused) selection_text=*/"",
374 /*(unused) selection_indices=*/{kInvalidIndex, kInvalidIndex},
375 classification_result,
376 /*generate_intents=*/false);
377 }
378
ConvertIndicesBMPUTF8(const std::string & utf8_str,const std::pair<int,int> & orig_indices,bool from_utf8)379 std::pair<int, int> ConvertIndicesBMPUTF8(
380 const std::string& utf8_str, const std::pair<int, int>& orig_indices,
381 bool from_utf8) {
382 const libtextclassifier3::UnicodeText unicode_str =
383 libtextclassifier3::UTF8ToUnicodeText(utf8_str, /*do_copy=*/false);
384
385 int unicode_index = 0;
386 int bmp_index = 0;
387
388 const int* source_index;
389 const int* target_index;
390 if (from_utf8) {
391 source_index = &unicode_index;
392 target_index = &bmp_index;
393 } else {
394 source_index = &bmp_index;
395 target_index = &unicode_index;
396 }
397
398 std::pair<int, int> result = std::make_pair(-1, -1);
399 std::function<void()> assign_indices_fn = [&result, &orig_indices,
400 &source_index, &target_index]() {
401 if (orig_indices.first == *source_index) {
402 result.first = *target_index;
403 }
404
405 if (orig_indices.second == *source_index) {
406 result.second = *target_index;
407 }
408 };
409
410 for (auto it = unicode_str.begin(); it != unicode_str.end();
411 ++it, ++unicode_index, ++bmp_index) {
412 assign_indices_fn();
413
414 // There is 1 extra character in the input for each UTF8 character > 0xFFFF.
415 if (*it > 0xFFFF) {
416 ++bmp_index;
417 }
418 }
419 assign_indices_fn();
420
421 return result;
422 }
423
424 } // namespace
425
ConvertIndicesBMPToUTF8(const std::string & utf8_str,const std::pair<int,int> & bmp_indices)426 CodepointSpan ConvertIndicesBMPToUTF8(const std::string& utf8_str,
427 const std::pair<int, int>& bmp_indices) {
428 const std::pair<int, int> utf8_indices =
429 ConvertIndicesBMPUTF8(utf8_str, bmp_indices, /*from_utf8=*/false);
430 return CodepointSpan(utf8_indices.first, utf8_indices.second);
431 }
432
ConvertIndicesUTF8ToBMP(const std::string & utf8_str,const CodepointSpan & utf8_indices)433 std::pair<int, int> ConvertIndicesUTF8ToBMP(const std::string& utf8_str,
434 const CodepointSpan& utf8_indices) {
435 return ConvertIndicesBMPUTF8(
436 utf8_str, std::make_pair(utf8_indices.first, utf8_indices.second),
437 /*from_utf8=*/true);
438 }
439
GetLocalesFromMmap(JNIEnv * env,libtextclassifier3::ScopedMmap * mmap)440 StatusOr<ScopedLocalRef<jstring>> GetLocalesFromMmap(
441 JNIEnv* env, libtextclassifier3::ScopedMmap* mmap) {
442 if (!mmap->handle().ok()) {
443 return JniHelper::NewStringUTF(env, "");
444 }
445 const Model* model = libtextclassifier3::ViewModel(
446 mmap->handle().start(), mmap->handle().num_bytes());
447 if (!model || !model->locales()) {
448 return JniHelper::NewStringUTF(env, "");
449 }
450
451 return JniHelper::NewStringUTF(env, model->locales()->c_str());
452 }
453
GetVersionFromMmap(JNIEnv * env,libtextclassifier3::ScopedMmap * mmap)454 jint GetVersionFromMmap(JNIEnv* env, libtextclassifier3::ScopedMmap* mmap) {
455 if (!mmap->handle().ok()) {
456 return 0;
457 }
458 const Model* model = libtextclassifier3::ViewModel(
459 mmap->handle().start(), mmap->handle().num_bytes());
460 if (!model) {
461 return 0;
462 }
463 return model->version();
464 }
465
GetNameFromMmap(JNIEnv * env,libtextclassifier3::ScopedMmap * mmap)466 StatusOr<ScopedLocalRef<jstring>> GetNameFromMmap(
467 JNIEnv* env, libtextclassifier3::ScopedMmap* mmap) {
468 if (!mmap->handle().ok()) {
469 return JniHelper::NewStringUTF(env, "");
470 }
471 const Model* model = libtextclassifier3::ViewModel(
472 mmap->handle().start(), mmap->handle().num_bytes());
473 if (!model || !model->name()) {
474 return JniHelper::NewStringUTF(env, "");
475 }
476 return JniHelper::NewStringUTF(env, model->name()->c_str());
477 }
478
479 } // namespace libtextclassifier3
480
481 using libtextclassifier3::AnnotatorJniContext;
482 using libtextclassifier3::ClassificationResultsToJObjectArray;
483 using libtextclassifier3::ClassificationResultsWithIntentsToJObjectArray;
484 using libtextclassifier3::ConvertIndicesBMPToUTF8;
485 using libtextclassifier3::ConvertIndicesUTF8ToBMP;
486 using libtextclassifier3::FromJavaAnnotationOptions;
487 using libtextclassifier3::FromJavaClassificationOptions;
488 using libtextclassifier3::FromJavaInputFragment;
489 using libtextclassifier3::FromJavaSelectionOptions;
490 using libtextclassifier3::InputFragment;
491 using libtextclassifier3::JStringToUtf8String;
492
TC3_JNI_METHOD(jlong,TC3_ANNOTATOR_CLASS_NAME,nativeNewAnnotator)493 TC3_JNI_METHOD(jlong, TC3_ANNOTATOR_CLASS_NAME, nativeNewAnnotator)
494 (JNIEnv* env, jobject clazz, jint fd) {
495 std::shared_ptr<libtextclassifier3::JniCache> jni_cache(
496 libtextclassifier3::JniCache::Create(env));
497 #ifdef TC3_USE_JAVAICU
498 return reinterpret_cast<jlong>(AnnotatorJniContext::Create(
499 jni_cache,
500 Annotator::FromFileDescriptor(
501 fd, std::unique_ptr<UniLib>(new UniLib(jni_cache)),
502 std::unique_ptr<CalendarLib>(new CalendarLib(jni_cache)))));
503 #else
504 return reinterpret_cast<jlong>(AnnotatorJniContext::Create(
505 jni_cache, Annotator::FromFileDescriptor(fd)));
506 #endif
507 }
508
TC3_JNI_METHOD(jlong,TC3_ANNOTATOR_CLASS_NAME,nativeNewAnnotatorFromPath)509 TC3_JNI_METHOD(jlong, TC3_ANNOTATOR_CLASS_NAME, nativeNewAnnotatorFromPath)
510 (JNIEnv* env, jobject clazz, jstring path) {
511 TC3_ASSIGN_OR_RETURN_0(const std::string path_str,
512 JStringToUtf8String(env, path));
513 std::shared_ptr<libtextclassifier3::JniCache> jni_cache(
514 libtextclassifier3::JniCache::Create(env));
515 #ifdef TC3_USE_JAVAICU
516 return reinterpret_cast<jlong>(AnnotatorJniContext::Create(
517 jni_cache,
518 Annotator::FromPath(
519 path_str, std::unique_ptr<UniLib>(new UniLib(jni_cache)),
520 std::unique_ptr<CalendarLib>(new CalendarLib(jni_cache)))));
521 #else
522 return reinterpret_cast<jlong>(
523 AnnotatorJniContext::Create(jni_cache, Annotator::FromPath(path_str)));
524 #endif
525 }
526
TC3_JNI_METHOD(jlong,TC3_ANNOTATOR_CLASS_NAME,nativeNewAnnotatorWithOffset)527 TC3_JNI_METHOD(jlong, TC3_ANNOTATOR_CLASS_NAME, nativeNewAnnotatorWithOffset)
528 (JNIEnv* env, jobject clazz, jint fd, jlong offset, jlong size) {
529 std::shared_ptr<libtextclassifier3::JniCache> jni_cache(
530 libtextclassifier3::JniCache::Create(env));
531 #ifdef TC3_USE_JAVAICU
532 return reinterpret_cast<jlong>(AnnotatorJniContext::Create(
533 jni_cache,
534 Annotator::FromFileDescriptor(
535 fd, offset, size, std::unique_ptr<UniLib>(new UniLib(jni_cache)),
536 std::unique_ptr<CalendarLib>(new CalendarLib(jni_cache)))));
537 #else
538 return reinterpret_cast<jlong>(AnnotatorJniContext::Create(
539 jni_cache, Annotator::FromFileDescriptor(fd, offset, size)));
540 #endif
541 }
542
TC3_JNI_METHOD(jboolean,TC3_ANNOTATOR_CLASS_NAME,nativeInitializeKnowledgeEngine)543 TC3_JNI_METHOD(jboolean, TC3_ANNOTATOR_CLASS_NAME,
544 nativeInitializeKnowledgeEngine)
545 (JNIEnv* env, jobject thiz, jlong ptr, jbyteArray serialized_config) {
546 if (!ptr) {
547 return false;
548 }
549
550 Annotator* model = reinterpret_cast<AnnotatorJniContext*>(ptr)->model();
551
552 TC3_ASSIGN_OR_RETURN_FALSE(
553 const std::string serialized_config_string,
554 libtextclassifier3::JByteArrayToString(env, serialized_config));
555
556 return model->InitializeKnowledgeEngine(serialized_config_string);
557 }
558
TC3_JNI_METHOD(jboolean,TC3_ANNOTATOR_CLASS_NAME,nativeInitializeContactEngine)559 TC3_JNI_METHOD(jboolean, TC3_ANNOTATOR_CLASS_NAME,
560 nativeInitializeContactEngine)
561 (JNIEnv* env, jobject thiz, jlong ptr, jbyteArray serialized_config) {
562 if (!ptr) {
563 return false;
564 }
565
566 Annotator* model = reinterpret_cast<AnnotatorJniContext*>(ptr)->model();
567
568 TC3_ASSIGN_OR_RETURN_FALSE(
569 const std::string serialized_config_string,
570 libtextclassifier3::JByteArrayToString(env, serialized_config));
571
572 return model->InitializeContactEngine(serialized_config_string);
573 }
574
TC3_JNI_METHOD(jboolean,TC3_ANNOTATOR_CLASS_NAME,nativeInitializeInstalledAppEngine)575 TC3_JNI_METHOD(jboolean, TC3_ANNOTATOR_CLASS_NAME,
576 nativeInitializeInstalledAppEngine)
577 (JNIEnv* env, jobject thiz, jlong ptr, jbyteArray serialized_config) {
578 if (!ptr) {
579 return false;
580 }
581
582 Annotator* model = reinterpret_cast<AnnotatorJniContext*>(ptr)->model();
583
584 TC3_ASSIGN_OR_RETURN_FALSE(
585 const std::string serialized_config_string,
586 libtextclassifier3::JByteArrayToString(env, serialized_config));
587
588 return model->InitializeInstalledAppEngine(serialized_config_string);
589 }
590
TC3_JNI_METHOD(jboolean,TC3_ANNOTATOR_CLASS_NAME,nativeInitializePersonNameEngine)591 TC3_JNI_METHOD(jboolean, TC3_ANNOTATOR_CLASS_NAME,
592 nativeInitializePersonNameEngine)
593 (JNIEnv* env, jobject thiz, jlong ptr, jint fd, jlong offset, jlong size) {
594 if (!ptr) {
595 return false;
596 }
597
598 Annotator* model = reinterpret_cast<AnnotatorJniContext*>(ptr)->model();
599
600 return model->InitializePersonNameEngineFromFileDescriptor(fd, offset, size);
601 }
602
TC3_JNI_METHOD(void,TC3_ANNOTATOR_CLASS_NAME,nativeSetLangId)603 TC3_JNI_METHOD(void, TC3_ANNOTATOR_CLASS_NAME, nativeSetLangId)
604 (JNIEnv* env, jobject thiz, jlong annotator_ptr, jlong lang_id_ptr) {
605 if (!annotator_ptr) {
606 return;
607 }
608 Annotator* model =
609 reinterpret_cast<AnnotatorJniContext*>(annotator_ptr)->model();
610 libtextclassifier3::mobile::lang_id::LangId* lang_id_model =
611 reinterpret_cast<libtextclassifier3::mobile::lang_id::LangId*>(lang_id_ptr);
612 model->SetLangId(lang_id_model);
613 }
614
TC3_JNI_METHOD(jlong,TC3_ANNOTATOR_CLASS_NAME,nativeGetNativeModelPtr)615 TC3_JNI_METHOD(jlong, TC3_ANNOTATOR_CLASS_NAME, nativeGetNativeModelPtr)
616 (JNIEnv* env, jobject thiz, jlong ptr) {
617 if (!ptr) {
618 return 0L;
619 }
620 return reinterpret_cast<jlong>(
621 reinterpret_cast<AnnotatorJniContext*>(ptr)->model());
622 }
623
TC3_JNI_METHOD(jintArray,TC3_ANNOTATOR_CLASS_NAME,nativeSuggestSelection)624 TC3_JNI_METHOD(jintArray, TC3_ANNOTATOR_CLASS_NAME, nativeSuggestSelection)
625 (JNIEnv* env, jobject thiz, jlong ptr, jstring context, jint selection_begin,
626 jint selection_end, jobject options) {
627 if (!ptr) {
628 return nullptr;
629 }
630 const Annotator* model = reinterpret_cast<AnnotatorJniContext*>(ptr)->model();
631 TC3_ASSIGN_OR_RETURN_NULL(const std::string context_utf8,
632 JStringToUtf8String(env, context));
633 const CodepointSpan input_indices =
634 ConvertIndicesBMPToUTF8(context_utf8, {selection_begin, selection_end});
635 TC3_ASSIGN_OR_RETURN_NULL(
636 libtextclassifier3::SelectionOptions selection_options,
637 FromJavaSelectionOptions(env, options));
638 CodepointSpan selection =
639 model->SuggestSelection(context_utf8, input_indices, selection_options);
640 const std::pair<int, int> selection_bmp =
641 ConvertIndicesUTF8ToBMP(context_utf8, selection);
642
643 TC3_ASSIGN_OR_RETURN_NULL(ScopedLocalRef<jintArray> result,
644 JniHelper::NewIntArray(env, 2));
645 TC3_RETURN_NULL_IF_ERROR(JniHelper::SetIntArrayRegion(
646 env, result.get(), 0, 1, &(selection_bmp.first)));
647 TC3_RETURN_NULL_IF_ERROR(JniHelper::SetIntArrayRegion(
648 env, result.get(), 1, 1, &(selection_bmp.second)));
649 return result.release();
650 }
651
TC3_JNI_METHOD(jobjectArray,TC3_ANNOTATOR_CLASS_NAME,nativeClassifyText)652 TC3_JNI_METHOD(jobjectArray, TC3_ANNOTATOR_CLASS_NAME, nativeClassifyText)
653 (JNIEnv* env, jobject thiz, jlong ptr, jstring context, jint selection_begin,
654 jint selection_end, jobject options, jobject app_context,
655 jstring device_locales) {
656 if (!ptr) {
657 return nullptr;
658 }
659 const AnnotatorJniContext* model_context =
660 reinterpret_cast<AnnotatorJniContext*>(ptr);
661
662 TC3_ASSIGN_OR_RETURN_NULL(const std::string context_utf8,
663 JStringToUtf8String(env, context));
664 const CodepointSpan input_indices =
665 ConvertIndicesBMPToUTF8(context_utf8, {selection_begin, selection_end});
666 TC3_ASSIGN_OR_RETURN_NULL(
667 const libtextclassifier3::ClassificationOptions classification_options,
668 FromJavaClassificationOptions(env, options));
669 const std::vector<ClassificationResult> classification_result =
670 model_context->model()->ClassifyText(context_utf8, input_indices,
671 classification_options);
672
673 ScopedLocalRef<jobjectArray> result;
674 if (app_context != nullptr) {
675 TC3_ASSIGN_OR_RETURN_NULL(
676 result, ClassificationResultsWithIntentsToJObjectArray(
677 env, model_context, app_context, device_locales,
678 &classification_options, context_utf8, input_indices,
679 classification_result,
680 /*generate_intents=*/true));
681
682 } else {
683 TC3_ASSIGN_OR_RETURN_NULL(
684 result, ClassificationResultsToJObjectArray(env, model_context,
685 classification_result));
686 }
687
688 return result.release();
689 }
690
TC3_JNI_METHOD(jobjectArray,TC3_ANNOTATOR_CLASS_NAME,nativeAnnotate)691 TC3_JNI_METHOD(jobjectArray, TC3_ANNOTATOR_CLASS_NAME, nativeAnnotate)
692 (JNIEnv* env, jobject thiz, jlong ptr, jstring context, jobject options) {
693 if (!ptr) {
694 return nullptr;
695 }
696 const AnnotatorJniContext* model_context =
697 reinterpret_cast<AnnotatorJniContext*>(ptr);
698 TC3_ASSIGN_OR_RETURN_NULL(const std::string context_utf8,
699 JStringToUtf8String(env, context));
700 TC3_ASSIGN_OR_RETURN_NULL(
701 libtextclassifier3::AnnotationOptions annotation_options,
702 FromJavaAnnotationOptions(env, options));
703 const std::vector<AnnotatedSpan> annotations =
704 model_context->model()->Annotate(context_utf8, annotation_options);
705
706 TC3_ASSIGN_OR_RETURN_NULL(
707 ScopedLocalRef<jclass> result_class,
708 JniHelper::FindClass(
709 env, TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR "$AnnotatedSpan"));
710
711 TC3_ASSIGN_OR_RETURN_NULL(
712 jmethodID result_class_constructor,
713 JniHelper::GetMethodID(
714 env, result_class.get(), "<init>",
715 "(II[L" TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR
716 "$ClassificationResult;)V"));
717
718 TC3_ASSIGN_OR_RETURN_NULL(
719 ScopedLocalRef<jobjectArray> results,
720 JniHelper::NewObjectArray(env, annotations.size(), result_class.get()));
721
722 for (int i = 0; i < annotations.size(); ++i) {
723 const std::pair<int, int> span_bmp =
724 ConvertIndicesUTF8ToBMP(context_utf8, annotations[i].span);
725
726 TC3_ASSIGN_OR_RETURN_NULL(
727 ScopedLocalRef<jobjectArray> classification_results,
728 ClassificationResultsToJObjectArray(env, model_context,
729 annotations[i].classification));
730
731 TC3_ASSIGN_OR_RETURN_NULL(
732 ScopedLocalRef<jobject> result,
733 JniHelper::NewObject(env, result_class.get(), result_class_constructor,
734 static_cast<jint>(span_bmp.first),
735 static_cast<jint>(span_bmp.second),
736 classification_results.get()));
737 if (!JniHelper::SetObjectArrayElement(env, results.get(), i, result.get())
738 .ok()) {
739 return nullptr;
740 }
741 }
742 return results.release();
743 }
744
TC3_JNI_METHOD(jobject,TC3_ANNOTATOR_CLASS_NAME,nativeAnnotateStructuredInput)745 TC3_JNI_METHOD(jobject, TC3_ANNOTATOR_CLASS_NAME, nativeAnnotateStructuredInput)
746 (JNIEnv* env, jobject thiz, jlong ptr, jobjectArray jinput_fragments,
747 jobject options) {
748 if (!ptr) {
749 return nullptr;
750 }
751 const AnnotatorJniContext* model_context =
752 reinterpret_cast<AnnotatorJniContext*>(ptr);
753
754 std::vector<InputFragment> string_fragments;
755 TC3_ASSIGN_OR_RETURN_NULL(jsize input_size,
756 JniHelper::GetArrayLength(env, jinput_fragments));
757 for (int i = 0; i < input_size; ++i) {
758 TC3_ASSIGN_OR_RETURN_NULL(
759 ScopedLocalRef<jobject> jfragment,
760 JniHelper::GetObjectArrayElement<jobject>(env, jinput_fragments, i));
761 TC3_ASSIGN_OR_RETURN_NULL(InputFragment fragment,
762 FromJavaInputFragment(env, jfragment.get()));
763 string_fragments.push_back(std::move(fragment));
764 }
765
766 TC3_ASSIGN_OR_RETURN_NULL(
767 libtextclassifier3::AnnotationOptions annotation_options,
768 FromJavaAnnotationOptions(env, options));
769 const StatusOr<Annotations> annotations_or =
770 model_context->model()->AnnotateStructuredInput(string_fragments,
771 annotation_options);
772 if (!annotations_or.ok()) {
773 TC3_LOG(ERROR) << "Annotation of structured input failed with error: "
774 << annotations_or.status().error_message();
775 return nullptr;
776 }
777
778 Annotations annotations = std::move(annotations_or.ValueOrDie());
779 TC3_ASSIGN_OR_RETURN_NULL(
780 ScopedLocalRef<jclass> annotations_class,
781 JniHelper::FindClass(
782 env, TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR "$Annotations"));
783
784 TC3_ASSIGN_OR_RETURN_NULL(
785 jmethodID annotations_class_constructor,
786 JniHelper::GetMethodID(
787 env, annotations_class.get(), "<init>",
788 "([[L" TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR
789 "$AnnotatedSpan;[L" TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR
790 "$ClassificationResult;)V"));
791
792 TC3_ASSIGN_OR_RETURN_NULL(
793 ScopedLocalRef<jclass> span_class,
794 JniHelper::FindClass(
795 env, TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR "$AnnotatedSpan"));
796
797 TC3_ASSIGN_OR_RETURN_NULL(
798 jmethodID span_class_constructor,
799 JniHelper::GetMethodID(
800 env, span_class.get(), "<init>",
801 "(II[L" TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR
802 "$ClassificationResult;)V"));
803
804 TC3_ASSIGN_OR_RETURN_NULL(
805 ScopedLocalRef<jclass> span_class_array,
806 JniHelper::FindClass(env,
807 "[L" TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR
808 "$AnnotatedSpan;"));
809
810 TC3_ASSIGN_OR_RETURN_NULL(
811 ScopedLocalRef<jobjectArray> annotated_spans,
812 JniHelper::NewObjectArray(env, input_size, span_class_array.get()));
813
814 for (int fragment_index = 0;
815 fragment_index < annotations.annotated_spans.size(); ++fragment_index) {
816 TC3_ASSIGN_OR_RETURN_NULL(
817 ScopedLocalRef<jobjectArray> jfragmentAnnotations,
818 JniHelper::NewObjectArray(
819 env, annotations.annotated_spans[fragment_index].size(),
820 span_class.get()));
821 for (int annotation_index = 0;
822 annotation_index < annotations.annotated_spans[fragment_index].size();
823 ++annotation_index) {
824 const std::pair<int, int> span_bmp = ConvertIndicesUTF8ToBMP(
825 string_fragments[fragment_index].text,
826 annotations.annotated_spans[fragment_index][annotation_index].span);
827 TC3_ASSIGN_OR_RETURN_NULL(
828 ScopedLocalRef<jobjectArray> classification_results,
829 ClassificationResultsToJObjectArray(
830 env, model_context,
831 annotations.annotated_spans[fragment_index][annotation_index]
832 .classification));
833 TC3_ASSIGN_OR_RETURN_NULL(
834 ScopedLocalRef<jobject> single_annotation,
835 JniHelper::NewObject(env, span_class.get(), span_class_constructor,
836 static_cast<jint>(span_bmp.first),
837 static_cast<jint>(span_bmp.second),
838 classification_results.get()));
839
840 if (!JniHelper::SetObjectArrayElement(env, jfragmentAnnotations.get(),
841 annotation_index,
842 single_annotation.get())
843 .ok()) {
844 return nullptr;
845 }
846 }
847
848 if (!JniHelper::SetObjectArrayElement(env, annotated_spans.get(),
849 fragment_index,
850 jfragmentAnnotations.get())
851 .ok()) {
852 return nullptr;
853 }
854 }
855
856 TC3_ASSIGN_OR_RETURN_NULL(
857 ScopedLocalRef<jobjectArray> topicality_results,
858 ClassificationResultsToJObjectArray(env, model_context,
859 annotations.topicality_results));
860
861 TC3_ASSIGN_OR_RETURN_NULL(
862 ScopedLocalRef<jobject> annotations_result,
863 JniHelper::NewObject(env, annotations_class.get(),
864 annotations_class_constructor, annotated_spans.get(),
865 topicality_results.get()));
866
867 return annotations_result.release();
868 }
869
TC3_JNI_METHOD(jbyteArray,TC3_ANNOTATOR_CLASS_NAME,nativeLookUpKnowledgeEntity)870 TC3_JNI_METHOD(jbyteArray, TC3_ANNOTATOR_CLASS_NAME,
871 nativeLookUpKnowledgeEntity)
872 (JNIEnv* env, jobject thiz, jlong ptr, jstring id) {
873 if (!ptr) {
874 return nullptr;
875 }
876 const Annotator* model = reinterpret_cast<AnnotatorJniContext*>(ptr)->model();
877 TC3_ASSIGN_OR_RETURN_NULL(const std::string id_utf8,
878 JStringToUtf8String(env, id));
879 auto serialized_knowledge_result_so = model->LookUpKnowledgeEntity(id_utf8);
880 if (!serialized_knowledge_result_so.ok()) {
881 return nullptr;
882 }
883 std::string serialized_knowledge_result =
884 serialized_knowledge_result_so.ValueOrDie();
885
886 TC3_ASSIGN_OR_RETURN_NULL(
887 ScopedLocalRef<jbyteArray> result,
888 JniHelper::NewByteArray(env, serialized_knowledge_result.size()));
889 TC3_RETURN_NULL_IF_ERROR(JniHelper::SetByteArrayRegion(
890 env, result.get(), 0, serialized_knowledge_result.size(),
891 reinterpret_cast<const jbyte*>(serialized_knowledge_result.data())));
892 return result.release();
893 }
894
TC3_JNI_METHOD(void,TC3_ANNOTATOR_CLASS_NAME,nativeCloseAnnotator)895 TC3_JNI_METHOD(void, TC3_ANNOTATOR_CLASS_NAME, nativeCloseAnnotator)
896 (JNIEnv* env, jobject thiz, jlong ptr) {
897 const AnnotatorJniContext* context =
898 reinterpret_cast<AnnotatorJniContext*>(ptr);
899 delete context;
900 }
901
TC3_JNI_METHOD(jstring,TC3_ANNOTATOR_CLASS_NAME,nativeGetLanguage)902 TC3_JNI_METHOD(jstring, TC3_ANNOTATOR_CLASS_NAME, nativeGetLanguage)
903 (JNIEnv* env, jobject clazz, jint fd) {
904 TC3_LOG(WARNING) << "Using deprecated getLanguage().";
905 return TC3_JNI_METHOD_NAME(TC3_ANNOTATOR_CLASS_NAME, nativeGetLocales)(
906 env, clazz, fd);
907 }
908
TC3_JNI_METHOD(jstring,TC3_ANNOTATOR_CLASS_NAME,nativeGetLocales)909 TC3_JNI_METHOD(jstring, TC3_ANNOTATOR_CLASS_NAME, nativeGetLocales)
910 (JNIEnv* env, jobject clazz, jint fd) {
911 const std::unique_ptr<libtextclassifier3::ScopedMmap> mmap(
912 new libtextclassifier3::ScopedMmap(fd));
913 TC3_ASSIGN_OR_RETURN_NULL(ScopedLocalRef<jstring> value,
914 GetLocalesFromMmap(env, mmap.get()));
915 return value.release();
916 }
917
TC3_JNI_METHOD(jstring,TC3_ANNOTATOR_CLASS_NAME,nativeGetLocalesWithOffset)918 TC3_JNI_METHOD(jstring, TC3_ANNOTATOR_CLASS_NAME, nativeGetLocalesWithOffset)
919 (JNIEnv* env, jobject clazz, jint fd, jlong offset, jlong size) {
920 const std::unique_ptr<libtextclassifier3::ScopedMmap> mmap(
921 new libtextclassifier3::ScopedMmap(fd, offset, size));
922 TC3_ASSIGN_OR_RETURN_NULL(ScopedLocalRef<jstring> value,
923 GetLocalesFromMmap(env, mmap.get()));
924 return value.release();
925 }
926
TC3_JNI_METHOD(jint,TC3_ANNOTATOR_CLASS_NAME,nativeGetVersion)927 TC3_JNI_METHOD(jint, TC3_ANNOTATOR_CLASS_NAME, nativeGetVersion)
928 (JNIEnv* env, jobject clazz, jint fd) {
929 const std::unique_ptr<libtextclassifier3::ScopedMmap> mmap(
930 new libtextclassifier3::ScopedMmap(fd));
931 return GetVersionFromMmap(env, mmap.get());
932 }
933
TC3_JNI_METHOD(jint,TC3_ANNOTATOR_CLASS_NAME,nativeGetVersionWithOffset)934 TC3_JNI_METHOD(jint, TC3_ANNOTATOR_CLASS_NAME, nativeGetVersionWithOffset)
935 (JNIEnv* env, jobject clazz, jint fd, jlong offset, jlong size) {
936 const std::unique_ptr<libtextclassifier3::ScopedMmap> mmap(
937 new libtextclassifier3::ScopedMmap(fd, offset, size));
938 return GetVersionFromMmap(env, mmap.get());
939 }
940
TC3_JNI_METHOD(jstring,TC3_ANNOTATOR_CLASS_NAME,nativeGetName)941 TC3_JNI_METHOD(jstring, TC3_ANNOTATOR_CLASS_NAME, nativeGetName)
942 (JNIEnv* env, jobject clazz, jint fd) {
943 const std::unique_ptr<libtextclassifier3::ScopedMmap> mmap(
944 new libtextclassifier3::ScopedMmap(fd));
945 TC3_ASSIGN_OR_RETURN_NULL(ScopedLocalRef<jstring> value,
946 GetNameFromMmap(env, mmap.get()));
947 return value.release();
948 }
949
TC3_JNI_METHOD(jstring,TC3_ANNOTATOR_CLASS_NAME,nativeGetNameWithOffset)950 TC3_JNI_METHOD(jstring, TC3_ANNOTATOR_CLASS_NAME, nativeGetNameWithOffset)
951 (JNIEnv* env, jobject clazz, jint fd, jlong offset, jlong size) {
952 const std::unique_ptr<libtextclassifier3::ScopedMmap> mmap(
953 new libtextclassifier3::ScopedMmap(fd, offset, size));
954 TC3_ASSIGN_OR_RETURN_NULL(ScopedLocalRef<jstring> value,
955 GetNameFromMmap(env, mmap.get()));
956 return value.release();
957 }
958