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 options->enable_add_contact_intent, options->enable_search_intent,
279 &remote_action_templates)) {
280 return {Status::UNKNOWN};
281 }
282
283 TC3_ASSIGN_OR_RETURN(
284 remote_action_templates_result,
285 model_context->template_handler()->RemoteActionTemplatesToJObjectArray(
286 remote_action_templates));
287 }
288
289 return JniHelper::NewObject(
290 env, result_class, result_class_constructor, row_string.get(),
291 static_cast<jfloat>(classification_result.score),
292 row_datetime_parse.get(), serialized_knowledge_result.get(),
293 contact_name.get(), contact_given_name.get(), contact_family_name.get(),
294 contact_nickname.get(), contact_email_address.get(),
295 contact_phone_number.get(), contact_account_type.get(),
296 contact_account_name.get(), contact_id.get(), app_name.get(),
297 app_package_name.get(), extras.get(), serialized_entity_data.get(),
298 remote_action_templates_result.get(), classification_result.duration_ms,
299 classification_result.numeric_value,
300 classification_result.numeric_double_value);
301 }
302
303 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)304 ClassificationResultsWithIntentsToJObjectArray(
305 JNIEnv* env, const AnnotatorJniContext* model_context, jobject app_context,
306 const jstring device_locales, const ClassificationOptions* options,
307 const std::string& context, const CodepointSpan& selection_indices,
308 const std::vector<ClassificationResult>& classification_result,
309 bool generate_intents) {
310 TC3_ASSIGN_OR_RETURN(
311 ScopedLocalRef<jclass> result_class,
312 JniHelper::FindClass(env, TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR
313 "$ClassificationResult"));
314
315 TC3_ASSIGN_OR_RETURN(
316 ScopedLocalRef<jclass> datetime_parse_class,
317 JniHelper::FindClass(env, TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR
318 "$DatetimeResult"));
319
320 TC3_ASSIGN_OR_RETURN(
321 const jmethodID result_class_constructor,
322 JniHelper::GetMethodID(
323 env, result_class.get(), "<init>",
324 "(Ljava/lang/String;FL" TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR
325 "$DatetimeResult;"
326 "[B"
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 "Ljava/lang/String;"
338 "[L" TC3_PACKAGE_PATH "" TC3_NAMED_VARIANT_CLASS_NAME_STR ";"
339 "[B"
340 "[L" TC3_PACKAGE_PATH "" TC3_REMOTE_ACTION_TEMPLATE_CLASS_NAME_STR ";"
341 "JJD)V"));
342 TC3_ASSIGN_OR_RETURN(const jmethodID datetime_parse_class_constructor,
343 JniHelper::GetMethodID(env, datetime_parse_class.get(),
344 "<init>", "(JI)V"));
345
346 TC3_ASSIGN_OR_RETURN(
347 ScopedLocalRef<jobjectArray> results,
348 JniHelper::NewObjectArray(env, classification_result.size(),
349 result_class.get()));
350
351 for (int i = 0; i < classification_result.size(); i++) {
352 TC3_ASSIGN_OR_RETURN(
353 ScopedLocalRef<jobject> result,
354 ClassificationResultWithIntentsToJObject(
355 env, model_context, app_context, result_class.get(),
356 result_class_constructor, datetime_parse_class.get(),
357 datetime_parse_class_constructor, device_locales, options, context,
358 selection_indices, classification_result[i],
359 generate_intents && (i == 0)));
360 TC3_RETURN_IF_ERROR(
361 JniHelper::SetObjectArrayElement(env, results.get(), i, result.get()));
362 }
363 return results;
364 }
365
ClassificationResultsToJObjectArray(JNIEnv * env,const AnnotatorJniContext * model_context,const std::vector<ClassificationResult> & classification_result)366 StatusOr<ScopedLocalRef<jobjectArray>> ClassificationResultsToJObjectArray(
367 JNIEnv* env, const AnnotatorJniContext* model_context,
368 const std::vector<ClassificationResult>& classification_result) {
369 return ClassificationResultsWithIntentsToJObjectArray(
370 env, model_context,
371 /*(unused) app_context=*/nullptr,
372 /*(unused) device_locale=*/nullptr,
373 /*(unusued) options=*/nullptr,
374 /*(unused) selection_text=*/"",
375 /*(unused) selection_indices=*/{kInvalidIndex, kInvalidIndex},
376 classification_result,
377 /*generate_intents=*/false);
378 }
379
ConvertIndicesBMPUTF8(const std::string & utf8_str,const std::pair<int,int> & orig_indices,bool from_utf8)380 std::pair<int, int> ConvertIndicesBMPUTF8(
381 const std::string& utf8_str, const std::pair<int, int>& orig_indices,
382 bool from_utf8) {
383 const libtextclassifier3::UnicodeText unicode_str =
384 libtextclassifier3::UTF8ToUnicodeText(utf8_str, /*do_copy=*/false);
385
386 int unicode_index = 0;
387 int bmp_index = 0;
388
389 const int* source_index;
390 const int* target_index;
391 if (from_utf8) {
392 source_index = &unicode_index;
393 target_index = &bmp_index;
394 } else {
395 source_index = &bmp_index;
396 target_index = &unicode_index;
397 }
398
399 std::pair<int, int> result = std::make_pair(-1, -1);
400 std::function<void()> assign_indices_fn = [&result, &orig_indices,
401 &source_index, &target_index]() {
402 if (orig_indices.first == *source_index) {
403 result.first = *target_index;
404 }
405
406 if (orig_indices.second == *source_index) {
407 result.second = *target_index;
408 }
409 };
410
411 for (auto it = unicode_str.begin(); it != unicode_str.end();
412 ++it, ++unicode_index, ++bmp_index) {
413 assign_indices_fn();
414
415 // There is 1 extra character in the input for each UTF8 character > 0xFFFF.
416 if (*it > 0xFFFF) {
417 ++bmp_index;
418 }
419 }
420 assign_indices_fn();
421
422 return result;
423 }
424
425 } // namespace
426
ConvertIndicesBMPToUTF8(const std::string & utf8_str,const std::pair<int,int> & bmp_indices)427 CodepointSpan ConvertIndicesBMPToUTF8(const std::string& utf8_str,
428 const std::pair<int, int>& bmp_indices) {
429 const std::pair<int, int> utf8_indices =
430 ConvertIndicesBMPUTF8(utf8_str, bmp_indices, /*from_utf8=*/false);
431 return CodepointSpan(utf8_indices.first, utf8_indices.second);
432 }
433
ConvertIndicesUTF8ToBMP(const std::string & utf8_str,const CodepointSpan & utf8_indices)434 std::pair<int, int> ConvertIndicesUTF8ToBMP(const std::string& utf8_str,
435 const CodepointSpan& utf8_indices) {
436 return ConvertIndicesBMPUTF8(
437 utf8_str, std::make_pair(utf8_indices.first, utf8_indices.second),
438 /*from_utf8=*/true);
439 }
440
GetLocalesFromMmap(JNIEnv * env,libtextclassifier3::ScopedMmap * mmap)441 StatusOr<ScopedLocalRef<jstring>> GetLocalesFromMmap(
442 JNIEnv* env, libtextclassifier3::ScopedMmap* mmap) {
443 if (!mmap->handle().ok()) {
444 return JniHelper::NewStringUTF(env, "");
445 }
446 const Model* model = libtextclassifier3::ViewModel(
447 mmap->handle().start(), mmap->handle().num_bytes());
448 if (!model || !model->locales()) {
449 return JniHelper::NewStringUTF(env, "");
450 }
451
452 return JniHelper::NewStringUTF(env, model->locales()->c_str());
453 }
454
GetVersionFromMmap(JNIEnv * env,libtextclassifier3::ScopedMmap * mmap)455 jint GetVersionFromMmap(JNIEnv* env, libtextclassifier3::ScopedMmap* mmap) {
456 if (!mmap->handle().ok()) {
457 return 0;
458 }
459 const Model* model = libtextclassifier3::ViewModel(
460 mmap->handle().start(), mmap->handle().num_bytes());
461 if (!model) {
462 return 0;
463 }
464 return model->version();
465 }
466
GetNameFromMmap(JNIEnv * env,libtextclassifier3::ScopedMmap * mmap)467 StatusOr<ScopedLocalRef<jstring>> GetNameFromMmap(
468 JNIEnv* env, libtextclassifier3::ScopedMmap* mmap) {
469 if (!mmap->handle().ok()) {
470 return JniHelper::NewStringUTF(env, "");
471 }
472 const Model* model = libtextclassifier3::ViewModel(
473 mmap->handle().start(), mmap->handle().num_bytes());
474 if (!model || !model->name()) {
475 return JniHelper::NewStringUTF(env, "");
476 }
477 return JniHelper::NewStringUTF(env, model->name()->c_str());
478 }
479
480 } // namespace libtextclassifier3
481
482 using libtextclassifier3::AnnotatorJniContext;
483 using libtextclassifier3::ClassificationResultsToJObjectArray;
484 using libtextclassifier3::ClassificationResultsWithIntentsToJObjectArray;
485 using libtextclassifier3::ConvertIndicesBMPToUTF8;
486 using libtextclassifier3::ConvertIndicesUTF8ToBMP;
487 using libtextclassifier3::FromJavaAnnotationOptions;
488 using libtextclassifier3::FromJavaClassificationOptions;
489 using libtextclassifier3::FromJavaInputFragment;
490 using libtextclassifier3::FromJavaSelectionOptions;
491 using libtextclassifier3::InputFragment;
492 using libtextclassifier3::JStringToUtf8String;
493
TC3_JNI_METHOD(jlong,TC3_ANNOTATOR_CLASS_NAME,nativeNewAnnotator)494 TC3_JNI_METHOD(jlong, TC3_ANNOTATOR_CLASS_NAME, nativeNewAnnotator)
495 (JNIEnv* env, jobject clazz, jint fd) {
496 std::shared_ptr<libtextclassifier3::JniCache> jni_cache(
497 libtextclassifier3::JniCache::Create(env));
498 #ifdef TC3_USE_JAVAICU
499 return reinterpret_cast<jlong>(AnnotatorJniContext::Create(
500 jni_cache,
501 Annotator::FromFileDescriptor(
502 fd, std::unique_ptr<UniLib>(new UniLib(jni_cache)),
503 std::unique_ptr<CalendarLib>(new CalendarLib(jni_cache)))));
504 #else
505 return reinterpret_cast<jlong>(AnnotatorJniContext::Create(
506 jni_cache, Annotator::FromFileDescriptor(fd)));
507 #endif
508 }
509
TC3_JNI_METHOD(jlong,TC3_ANNOTATOR_CLASS_NAME,nativeNewAnnotatorFromPath)510 TC3_JNI_METHOD(jlong, TC3_ANNOTATOR_CLASS_NAME, nativeNewAnnotatorFromPath)
511 (JNIEnv* env, jobject clazz, jstring path) {
512 TC3_ASSIGN_OR_RETURN_0(const std::string path_str,
513 JStringToUtf8String(env, path));
514 std::shared_ptr<libtextclassifier3::JniCache> jni_cache(
515 libtextclassifier3::JniCache::Create(env));
516 #ifdef TC3_USE_JAVAICU
517 return reinterpret_cast<jlong>(AnnotatorJniContext::Create(
518 jni_cache,
519 Annotator::FromPath(
520 path_str, std::unique_ptr<UniLib>(new UniLib(jni_cache)),
521 std::unique_ptr<CalendarLib>(new CalendarLib(jni_cache)))));
522 #else
523 return reinterpret_cast<jlong>(
524 AnnotatorJniContext::Create(jni_cache, Annotator::FromPath(path_str)));
525 #endif
526 }
527
TC3_JNI_METHOD(jlong,TC3_ANNOTATOR_CLASS_NAME,nativeNewAnnotatorWithOffset)528 TC3_JNI_METHOD(jlong, TC3_ANNOTATOR_CLASS_NAME, nativeNewAnnotatorWithOffset)
529 (JNIEnv* env, jobject clazz, jint fd, jlong offset, jlong size) {
530 std::shared_ptr<libtextclassifier3::JniCache> jni_cache(
531 libtextclassifier3::JniCache::Create(env));
532 #ifdef TC3_USE_JAVAICU
533 return reinterpret_cast<jlong>(AnnotatorJniContext::Create(
534 jni_cache,
535 Annotator::FromFileDescriptor(
536 fd, offset, size, std::unique_ptr<UniLib>(new UniLib(jni_cache)),
537 std::unique_ptr<CalendarLib>(new CalendarLib(jni_cache)))));
538 #else
539 return reinterpret_cast<jlong>(AnnotatorJniContext::Create(
540 jni_cache, Annotator::FromFileDescriptor(fd, offset, size)));
541 #endif
542 }
543
TC3_JNI_METHOD(jboolean,TC3_ANNOTATOR_CLASS_NAME,nativeInitializeKnowledgeEngine)544 TC3_JNI_METHOD(jboolean, TC3_ANNOTATOR_CLASS_NAME,
545 nativeInitializeKnowledgeEngine)
546 (JNIEnv* env, jobject thiz, jlong ptr, jbyteArray serialized_config) {
547 if (!ptr) {
548 return false;
549 }
550
551 Annotator* model = reinterpret_cast<AnnotatorJniContext*>(ptr)->model();
552
553 TC3_ASSIGN_OR_RETURN_FALSE(
554 const std::string serialized_config_string,
555 libtextclassifier3::JByteArrayToString(env, serialized_config));
556
557 return model->InitializeKnowledgeEngine(serialized_config_string);
558 }
559
TC3_JNI_METHOD(jboolean,TC3_ANNOTATOR_CLASS_NAME,nativeInitializeContactEngine)560 TC3_JNI_METHOD(jboolean, TC3_ANNOTATOR_CLASS_NAME,
561 nativeInitializeContactEngine)
562 (JNIEnv* env, jobject thiz, jlong ptr, jbyteArray serialized_config) {
563 if (!ptr) {
564 return false;
565 }
566
567 Annotator* model = reinterpret_cast<AnnotatorJniContext*>(ptr)->model();
568
569 TC3_ASSIGN_OR_RETURN_FALSE(
570 const std::string serialized_config_string,
571 libtextclassifier3::JByteArrayToString(env, serialized_config));
572
573 return model->InitializeContactEngine(serialized_config_string);
574 }
575
TC3_JNI_METHOD(jboolean,TC3_ANNOTATOR_CLASS_NAME,nativeInitializeInstalledAppEngine)576 TC3_JNI_METHOD(jboolean, TC3_ANNOTATOR_CLASS_NAME,
577 nativeInitializeInstalledAppEngine)
578 (JNIEnv* env, jobject thiz, jlong ptr, jbyteArray serialized_config) {
579 if (!ptr) {
580 return false;
581 }
582
583 Annotator* model = reinterpret_cast<AnnotatorJniContext*>(ptr)->model();
584
585 TC3_ASSIGN_OR_RETURN_FALSE(
586 const std::string serialized_config_string,
587 libtextclassifier3::JByteArrayToString(env, serialized_config));
588
589 return model->InitializeInstalledAppEngine(serialized_config_string);
590 }
591
TC3_JNI_METHOD(jboolean,TC3_ANNOTATOR_CLASS_NAME,nativeInitializePersonNameEngine)592 TC3_JNI_METHOD(jboolean, TC3_ANNOTATOR_CLASS_NAME,
593 nativeInitializePersonNameEngine)
594 (JNIEnv* env, jobject thiz, jlong ptr, jint fd, jlong offset, jlong size) {
595 if (!ptr) {
596 return false;
597 }
598
599 Annotator* model = reinterpret_cast<AnnotatorJniContext*>(ptr)->model();
600
601 return model->InitializePersonNameEngineFromFileDescriptor(fd, offset, size);
602 }
603
TC3_JNI_METHOD(void,TC3_ANNOTATOR_CLASS_NAME,nativeSetLangId)604 TC3_JNI_METHOD(void, TC3_ANNOTATOR_CLASS_NAME, nativeSetLangId)
605 (JNIEnv* env, jobject thiz, jlong annotator_ptr, jlong lang_id_ptr) {
606 if (!annotator_ptr) {
607 return;
608 }
609 Annotator* model =
610 reinterpret_cast<AnnotatorJniContext*>(annotator_ptr)->model();
611 libtextclassifier3::mobile::lang_id::LangId* lang_id_model =
612 reinterpret_cast<libtextclassifier3::mobile::lang_id::LangId*>(lang_id_ptr);
613 model->SetLangId(lang_id_model);
614 }
615
TC3_JNI_METHOD(jlong,TC3_ANNOTATOR_CLASS_NAME,nativeGetNativeModelPtr)616 TC3_JNI_METHOD(jlong, TC3_ANNOTATOR_CLASS_NAME, nativeGetNativeModelPtr)
617 (JNIEnv* env, jobject thiz, jlong ptr) {
618 if (!ptr) {
619 return 0L;
620 }
621 return reinterpret_cast<jlong>(
622 reinterpret_cast<AnnotatorJniContext*>(ptr)->model());
623 }
624
TC3_JNI_METHOD(jintArray,TC3_ANNOTATOR_CLASS_NAME,nativeSuggestSelection)625 TC3_JNI_METHOD(jintArray, TC3_ANNOTATOR_CLASS_NAME, nativeSuggestSelection)
626 (JNIEnv* env, jobject thiz, jlong ptr, jstring context, jint selection_begin,
627 jint selection_end, jobject options) {
628 if (!ptr) {
629 return nullptr;
630 }
631 const Annotator* model = reinterpret_cast<AnnotatorJniContext*>(ptr)->model();
632 TC3_ASSIGN_OR_RETURN_NULL(const std::string context_utf8,
633 JStringToUtf8String(env, context));
634 const CodepointSpan input_indices =
635 ConvertIndicesBMPToUTF8(context_utf8, {selection_begin, selection_end});
636 TC3_ASSIGN_OR_RETURN_NULL(
637 libtextclassifier3::SelectionOptions selection_options,
638 FromJavaSelectionOptions(env, options));
639 CodepointSpan selection =
640 model->SuggestSelection(context_utf8, input_indices, selection_options);
641 const std::pair<int, int> selection_bmp =
642 ConvertIndicesUTF8ToBMP(context_utf8, selection);
643
644 TC3_ASSIGN_OR_RETURN_NULL(ScopedLocalRef<jintArray> result,
645 JniHelper::NewIntArray(env, 2));
646 TC3_RETURN_NULL_IF_ERROR(JniHelper::SetIntArrayRegion(
647 env, result.get(), 0, 1, &(selection_bmp.first)));
648 TC3_RETURN_NULL_IF_ERROR(JniHelper::SetIntArrayRegion(
649 env, result.get(), 1, 1, &(selection_bmp.second)));
650 return result.release();
651 }
652
TC3_JNI_METHOD(jobjectArray,TC3_ANNOTATOR_CLASS_NAME,nativeClassifyText)653 TC3_JNI_METHOD(jobjectArray, TC3_ANNOTATOR_CLASS_NAME, nativeClassifyText)
654 (JNIEnv* env, jobject thiz, jlong ptr, jstring context, jint selection_begin,
655 jint selection_end, jobject options, jobject app_context,
656 jstring device_locales) {
657 if (!ptr) {
658 return nullptr;
659 }
660 const AnnotatorJniContext* model_context =
661 reinterpret_cast<AnnotatorJniContext*>(ptr);
662
663 TC3_ASSIGN_OR_RETURN_NULL(const std::string context_utf8,
664 JStringToUtf8String(env, context));
665 const CodepointSpan input_indices =
666 ConvertIndicesBMPToUTF8(context_utf8, {selection_begin, selection_end});
667 TC3_ASSIGN_OR_RETURN_NULL(
668 const libtextclassifier3::ClassificationOptions classification_options,
669 FromJavaClassificationOptions(env, options));
670 const std::vector<ClassificationResult> classification_result =
671 model_context->model()->ClassifyText(context_utf8, input_indices,
672 classification_options);
673
674 ScopedLocalRef<jobjectArray> result;
675 if (app_context != nullptr) {
676 TC3_ASSIGN_OR_RETURN_NULL(
677 result, ClassificationResultsWithIntentsToJObjectArray(
678 env, model_context, app_context, device_locales,
679 &classification_options, context_utf8, input_indices,
680 classification_result,
681 /*generate_intents=*/true));
682
683 } else {
684 TC3_ASSIGN_OR_RETURN_NULL(
685 result, ClassificationResultsToJObjectArray(env, model_context,
686 classification_result));
687 }
688
689 return result.release();
690 }
691
TC3_JNI_METHOD(jobjectArray,TC3_ANNOTATOR_CLASS_NAME,nativeAnnotate)692 TC3_JNI_METHOD(jobjectArray, TC3_ANNOTATOR_CLASS_NAME, nativeAnnotate)
693 (JNIEnv* env, jobject thiz, jlong ptr, jstring context, jobject options) {
694 if (!ptr) {
695 return nullptr;
696 }
697 const AnnotatorJniContext* model_context =
698 reinterpret_cast<AnnotatorJniContext*>(ptr);
699 TC3_ASSIGN_OR_RETURN_NULL(const std::string context_utf8,
700 JStringToUtf8String(env, context));
701 TC3_ASSIGN_OR_RETURN_NULL(
702 libtextclassifier3::AnnotationOptions annotation_options,
703 FromJavaAnnotationOptions(env, options));
704 const std::vector<AnnotatedSpan> annotations =
705 model_context->model()->Annotate(context_utf8, annotation_options);
706
707 TC3_ASSIGN_OR_RETURN_NULL(
708 ScopedLocalRef<jclass> result_class,
709 JniHelper::FindClass(
710 env, TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR "$AnnotatedSpan"));
711
712 TC3_ASSIGN_OR_RETURN_NULL(
713 jmethodID result_class_constructor,
714 JniHelper::GetMethodID(
715 env, result_class.get(), "<init>",
716 "(II[L" TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR
717 "$ClassificationResult;)V"));
718
719 TC3_ASSIGN_OR_RETURN_NULL(
720 ScopedLocalRef<jobjectArray> results,
721 JniHelper::NewObjectArray(env, annotations.size(), result_class.get()));
722
723 for (int i = 0; i < annotations.size(); ++i) {
724 const std::pair<int, int> span_bmp =
725 ConvertIndicesUTF8ToBMP(context_utf8, annotations[i].span);
726
727 TC3_ASSIGN_OR_RETURN_NULL(
728 ScopedLocalRef<jobjectArray> classification_results,
729 ClassificationResultsToJObjectArray(env, model_context,
730 annotations[i].classification));
731
732 TC3_ASSIGN_OR_RETURN_NULL(
733 ScopedLocalRef<jobject> result,
734 JniHelper::NewObject(env, result_class.get(), result_class_constructor,
735 static_cast<jint>(span_bmp.first),
736 static_cast<jint>(span_bmp.second),
737 classification_results.get()));
738 if (!JniHelper::SetObjectArrayElement(env, results.get(), i, result.get())
739 .ok()) {
740 return nullptr;
741 }
742 }
743 return results.release();
744 }
745
TC3_JNI_METHOD(jobject,TC3_ANNOTATOR_CLASS_NAME,nativeAnnotateStructuredInput)746 TC3_JNI_METHOD(jobject, TC3_ANNOTATOR_CLASS_NAME, nativeAnnotateStructuredInput)
747 (JNIEnv* env, jobject thiz, jlong ptr, jobjectArray jinput_fragments,
748 jobject options) {
749 if (!ptr) {
750 return nullptr;
751 }
752 const AnnotatorJniContext* model_context =
753 reinterpret_cast<AnnotatorJniContext*>(ptr);
754
755 std::vector<InputFragment> string_fragments;
756 TC3_ASSIGN_OR_RETURN_NULL(jsize input_size,
757 JniHelper::GetArrayLength(env, jinput_fragments));
758 for (int i = 0; i < input_size; ++i) {
759 TC3_ASSIGN_OR_RETURN_NULL(
760 ScopedLocalRef<jobject> jfragment,
761 JniHelper::GetObjectArrayElement<jobject>(env, jinput_fragments, i));
762 TC3_ASSIGN_OR_RETURN_NULL(InputFragment fragment,
763 FromJavaInputFragment(env, jfragment.get()));
764 string_fragments.push_back(std::move(fragment));
765 }
766
767 TC3_ASSIGN_OR_RETURN_NULL(
768 libtextclassifier3::AnnotationOptions annotation_options,
769 FromJavaAnnotationOptions(env, options));
770 const StatusOr<Annotations> annotations_or =
771 model_context->model()->AnnotateStructuredInput(string_fragments,
772 annotation_options);
773 if (!annotations_or.ok()) {
774 TC3_LOG(ERROR) << "Annotation of structured input failed with error: "
775 << annotations_or.status().error_message();
776 return nullptr;
777 }
778
779 Annotations annotations = std::move(annotations_or.ValueOrDie());
780 TC3_ASSIGN_OR_RETURN_NULL(
781 ScopedLocalRef<jclass> annotations_class,
782 JniHelper::FindClass(
783 env, TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR "$Annotations"));
784
785 TC3_ASSIGN_OR_RETURN_NULL(
786 jmethodID annotations_class_constructor,
787 JniHelper::GetMethodID(
788 env, annotations_class.get(), "<init>",
789 "([[L" TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR
790 "$AnnotatedSpan;[L" TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR
791 "$ClassificationResult;)V"));
792
793 TC3_ASSIGN_OR_RETURN_NULL(
794 ScopedLocalRef<jclass> span_class,
795 JniHelper::FindClass(
796 env, TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR "$AnnotatedSpan"));
797
798 TC3_ASSIGN_OR_RETURN_NULL(
799 jmethodID span_class_constructor,
800 JniHelper::GetMethodID(
801 env, span_class.get(), "<init>",
802 "(II[L" TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR
803 "$ClassificationResult;)V"));
804
805 TC3_ASSIGN_OR_RETURN_NULL(
806 ScopedLocalRef<jclass> span_class_array,
807 JniHelper::FindClass(env,
808 "[L" TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR
809 "$AnnotatedSpan;"));
810
811 TC3_ASSIGN_OR_RETURN_NULL(
812 ScopedLocalRef<jobjectArray> annotated_spans,
813 JniHelper::NewObjectArray(env, input_size, span_class_array.get()));
814
815 for (int fragment_index = 0;
816 fragment_index < annotations.annotated_spans.size(); ++fragment_index) {
817 TC3_ASSIGN_OR_RETURN_NULL(
818 ScopedLocalRef<jobjectArray> jfragmentAnnotations,
819 JniHelper::NewObjectArray(
820 env, annotations.annotated_spans[fragment_index].size(),
821 span_class.get()));
822 for (int annotation_index = 0;
823 annotation_index < annotations.annotated_spans[fragment_index].size();
824 ++annotation_index) {
825 const std::pair<int, int> span_bmp = ConvertIndicesUTF8ToBMP(
826 string_fragments[fragment_index].text,
827 annotations.annotated_spans[fragment_index][annotation_index].span);
828 TC3_ASSIGN_OR_RETURN_NULL(
829 ScopedLocalRef<jobjectArray> classification_results,
830 ClassificationResultsToJObjectArray(
831 env, model_context,
832 annotations.annotated_spans[fragment_index][annotation_index]
833 .classification));
834 TC3_ASSIGN_OR_RETURN_NULL(
835 ScopedLocalRef<jobject> single_annotation,
836 JniHelper::NewObject(env, span_class.get(), span_class_constructor,
837 static_cast<jint>(span_bmp.first),
838 static_cast<jint>(span_bmp.second),
839 classification_results.get()));
840
841 if (!JniHelper::SetObjectArrayElement(env, jfragmentAnnotations.get(),
842 annotation_index,
843 single_annotation.get())
844 .ok()) {
845 return nullptr;
846 }
847 }
848
849 if (!JniHelper::SetObjectArrayElement(env, annotated_spans.get(),
850 fragment_index,
851 jfragmentAnnotations.get())
852 .ok()) {
853 return nullptr;
854 }
855 }
856
857 TC3_ASSIGN_OR_RETURN_NULL(
858 ScopedLocalRef<jobjectArray> topicality_results,
859 ClassificationResultsToJObjectArray(env, model_context,
860 annotations.topicality_results));
861
862 TC3_ASSIGN_OR_RETURN_NULL(
863 ScopedLocalRef<jobject> annotations_result,
864 JniHelper::NewObject(env, annotations_class.get(),
865 annotations_class_constructor, annotated_spans.get(),
866 topicality_results.get()));
867
868 return annotations_result.release();
869 }
870
TC3_JNI_METHOD(jbyteArray,TC3_ANNOTATOR_CLASS_NAME,nativeLookUpKnowledgeEntity)871 TC3_JNI_METHOD(jbyteArray, TC3_ANNOTATOR_CLASS_NAME,
872 nativeLookUpKnowledgeEntity)
873 (JNIEnv* env, jobject thiz, jlong ptr, jstring id) {
874 if (!ptr) {
875 return nullptr;
876 }
877 const Annotator* model = reinterpret_cast<AnnotatorJniContext*>(ptr)->model();
878 TC3_ASSIGN_OR_RETURN_NULL(const std::string id_utf8,
879 JStringToUtf8String(env, id));
880 auto serialized_knowledge_result_so = model->LookUpKnowledgeEntity(id_utf8);
881 if (!serialized_knowledge_result_so.ok()) {
882 return nullptr;
883 }
884 std::string serialized_knowledge_result =
885 serialized_knowledge_result_so.ValueOrDie();
886
887 TC3_ASSIGN_OR_RETURN_NULL(
888 ScopedLocalRef<jbyteArray> result,
889 JniHelper::NewByteArray(env, serialized_knowledge_result.size()));
890 TC3_RETURN_NULL_IF_ERROR(JniHelper::SetByteArrayRegion(
891 env, result.get(), 0, serialized_knowledge_result.size(),
892 reinterpret_cast<const jbyte*>(serialized_knowledge_result.data())));
893 return result.release();
894 }
895
TC3_JNI_METHOD(void,TC3_ANNOTATOR_CLASS_NAME,nativeCloseAnnotator)896 TC3_JNI_METHOD(void, TC3_ANNOTATOR_CLASS_NAME, nativeCloseAnnotator)
897 (JNIEnv* env, jobject thiz, jlong ptr) {
898 const AnnotatorJniContext* context =
899 reinterpret_cast<AnnotatorJniContext*>(ptr);
900 if (context != nullptr && context->model()) {
901 context->model()->CleanUpContactEngine();
902 }
903 delete context;
904 }
905
TC3_JNI_METHOD(jstring,TC3_ANNOTATOR_CLASS_NAME,nativeGetLanguage)906 TC3_JNI_METHOD(jstring, TC3_ANNOTATOR_CLASS_NAME, nativeGetLanguage)
907 (JNIEnv* env, jobject clazz, jint fd) {
908 TC3_LOG(WARNING) << "Using deprecated getLanguage().";
909 return TC3_JNI_METHOD_NAME(TC3_ANNOTATOR_CLASS_NAME, nativeGetLocales)(
910 env, clazz, fd);
911 }
912
TC3_JNI_METHOD(jstring,TC3_ANNOTATOR_CLASS_NAME,nativeGetLocales)913 TC3_JNI_METHOD(jstring, TC3_ANNOTATOR_CLASS_NAME, nativeGetLocales)
914 (JNIEnv* env, jobject clazz, jint fd) {
915 const std::unique_ptr<libtextclassifier3::ScopedMmap> mmap(
916 new libtextclassifier3::ScopedMmap(fd));
917 TC3_ASSIGN_OR_RETURN_NULL(ScopedLocalRef<jstring> value,
918 GetLocalesFromMmap(env, mmap.get()));
919 return value.release();
920 }
921
TC3_JNI_METHOD(jstring,TC3_ANNOTATOR_CLASS_NAME,nativeGetLocalesWithOffset)922 TC3_JNI_METHOD(jstring, TC3_ANNOTATOR_CLASS_NAME, nativeGetLocalesWithOffset)
923 (JNIEnv* env, jobject clazz, jint fd, jlong offset, jlong size) {
924 const std::unique_ptr<libtextclassifier3::ScopedMmap> mmap(
925 new libtextclassifier3::ScopedMmap(fd, offset, size));
926 TC3_ASSIGN_OR_RETURN_NULL(ScopedLocalRef<jstring> value,
927 GetLocalesFromMmap(env, mmap.get()));
928 return value.release();
929 }
930
TC3_JNI_METHOD(jint,TC3_ANNOTATOR_CLASS_NAME,nativeGetVersion)931 TC3_JNI_METHOD(jint, TC3_ANNOTATOR_CLASS_NAME, nativeGetVersion)
932 (JNIEnv* env, jobject clazz, jint fd) {
933 const std::unique_ptr<libtextclassifier3::ScopedMmap> mmap(
934 new libtextclassifier3::ScopedMmap(fd));
935 return GetVersionFromMmap(env, mmap.get());
936 }
937
TC3_JNI_METHOD(jint,TC3_ANNOTATOR_CLASS_NAME,nativeGetVersionWithOffset)938 TC3_JNI_METHOD(jint, TC3_ANNOTATOR_CLASS_NAME, nativeGetVersionWithOffset)
939 (JNIEnv* env, jobject clazz, jint fd, jlong offset, jlong size) {
940 const std::unique_ptr<libtextclassifier3::ScopedMmap> mmap(
941 new libtextclassifier3::ScopedMmap(fd, offset, size));
942 return GetVersionFromMmap(env, mmap.get());
943 }
944
TC3_JNI_METHOD(jstring,TC3_ANNOTATOR_CLASS_NAME,nativeGetName)945 TC3_JNI_METHOD(jstring, TC3_ANNOTATOR_CLASS_NAME, nativeGetName)
946 (JNIEnv* env, jobject clazz, jint fd) {
947 const std::unique_ptr<libtextclassifier3::ScopedMmap> mmap(
948 new libtextclassifier3::ScopedMmap(fd));
949 TC3_ASSIGN_OR_RETURN_NULL(ScopedLocalRef<jstring> value,
950 GetNameFromMmap(env, mmap.get()));
951 return value.release();
952 }
953
TC3_JNI_METHOD(jstring,TC3_ANNOTATOR_CLASS_NAME,nativeGetNameWithOffset)954 TC3_JNI_METHOD(jstring, TC3_ANNOTATOR_CLASS_NAME, nativeGetNameWithOffset)
955 (JNIEnv* env, jobject clazz, jint fd, jlong offset, jlong size) {
956 const std::unique_ptr<libtextclassifier3::ScopedMmap> mmap(
957 new libtextclassifier3::ScopedMmap(fd, offset, size));
958 TC3_ASSIGN_OR_RETURN_NULL(ScopedLocalRef<jstring> value,
959 GetNameFromMmap(env, mmap.get()));
960 return value.release();
961 }
962