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 #include <type_traits>
23 #include <vector>
24 
25 #include "annotator/annotator.h"
26 #include "annotator/annotator_jni_common.h"
27 #include "annotator/types.h"
28 #include "utils/base/integral_types.h"
29 #include "utils/calendar/calendar.h"
30 #include "utils/intents/intent-generator.h"
31 #include "utils/intents/jni.h"
32 #include "utils/java/jni-cache.h"
33 #include "utils/java/scoped_local_ref.h"
34 #include "utils/java/string_utils.h"
35 #include "utils/memory/mmap.h"
36 #include "utils/strings/stringpiece.h"
37 #include "utils/utf8/unilib.h"
38 
39 #ifdef TC3_UNILIB_JAVAICU
40 #ifndef TC3_CALENDAR_JAVAICU
41 #error Inconsistent usage of Java ICU components
42 #else
43 #define TC3_USE_JAVAICU
44 #endif
45 #endif
46 
47 using libtextclassifier3::AnnotatedSpan;
48 using libtextclassifier3::Annotator;
49 using libtextclassifier3::ClassificationResult;
50 using libtextclassifier3::CodepointSpan;
51 using libtextclassifier3::Model;
52 using libtextclassifier3::ScopedLocalRef;
53 // When using the Java's ICU, CalendarLib and UniLib need to be instantiated
54 // with a JavaVM pointer from JNI. When using a standard ICU the pointer is
55 // not needed and the objects are instantiated implicitly.
56 #ifdef TC3_USE_JAVAICU
57 using libtextclassifier3::CalendarLib;
58 using libtextclassifier3::UniLib;
59 #endif
60 
61 namespace libtextclassifier3 {
62 
63 using libtextclassifier3::CodepointSpan;
64 
65 namespace {
66 class AnnotatorJniContext {
67  public:
Create(const std::shared_ptr<libtextclassifier3::JniCache> & jni_cache,std::unique_ptr<Annotator> model)68   static AnnotatorJniContext* Create(
69       const std::shared_ptr<libtextclassifier3::JniCache>& jni_cache,
70       std::unique_ptr<Annotator> model) {
71     if (jni_cache == nullptr || model == nullptr) {
72       return nullptr;
73     }
74     std::unique_ptr<IntentGenerator> intent_generator =
75         IntentGenerator::Create(model->model()->intent_options(),
76                                 model->model()->resources(), jni_cache);
77     std::unique_ptr<RemoteActionTemplatesHandler> template_handler =
78         libtextclassifier3::RemoteActionTemplatesHandler::Create(jni_cache);
79     if (template_handler == nullptr) {
80       return nullptr;
81     }
82     return new AnnotatorJniContext(jni_cache, std::move(model),
83                                    std::move(intent_generator),
84                                    std::move(template_handler));
85   }
86 
jni_cache() const87   std::shared_ptr<libtextclassifier3::JniCache> jni_cache() const {
88     return jni_cache_;
89   }
90 
model() const91   Annotator* model() const { return model_.get(); }
92 
intent_generator() const93   IntentGenerator* intent_generator() const { return intent_generator_.get(); }
94 
template_handler() const95   RemoteActionTemplatesHandler* template_handler() const {
96     return template_handler_.get();
97   }
98 
99  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)100   AnnotatorJniContext(
101       const std::shared_ptr<libtextclassifier3::JniCache>& jni_cache,
102       std::unique_ptr<Annotator> model,
103       std::unique_ptr<IntentGenerator> intent_generator,
104       std::unique_ptr<RemoteActionTemplatesHandler> template_handler)
105       : jni_cache_(jni_cache),
106         model_(std::move(model)),
107         intent_generator_(std::move(intent_generator)),
108         template_handler_(std::move(template_handler)) {}
109 
110   std::shared_ptr<libtextclassifier3::JniCache> jni_cache_;
111   std::unique_ptr<Annotator> model_;
112   std::unique_ptr<IntentGenerator> intent_generator_;
113   std::unique_ptr<RemoteActionTemplatesHandler> template_handler_;
114 };
115 
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)116 jobject ClassificationResultWithIntentsToJObject(
117     JNIEnv* env, const AnnotatorJniContext* model_context, jobject app_context,
118     jclass result_class, jmethodID result_class_constructor,
119     jclass datetime_parse_class, jmethodID datetime_parse_class_constructor,
120     const jstring device_locales, const ClassificationOptions* options,
121     const std::string& context, const CodepointSpan& selection_indices,
122     const ClassificationResult& classification_result, bool generate_intents) {
123   jstring row_string =
124       env->NewStringUTF(classification_result.collection.c_str());
125 
126   jobject row_datetime_parse = nullptr;
127   if (classification_result.datetime_parse_result.IsSet()) {
128     row_datetime_parse =
129         env->NewObject(datetime_parse_class, datetime_parse_class_constructor,
130                        classification_result.datetime_parse_result.time_ms_utc,
131                        classification_result.datetime_parse_result.granularity);
132   }
133 
134   jbyteArray serialized_knowledge_result = nullptr;
135   const std::string& serialized_knowledge_result_string =
136       classification_result.serialized_knowledge_result;
137   if (!serialized_knowledge_result_string.empty()) {
138     serialized_knowledge_result =
139         env->NewByteArray(serialized_knowledge_result_string.size());
140     env->SetByteArrayRegion(serialized_knowledge_result, 0,
141                             serialized_knowledge_result_string.size(),
142                             reinterpret_cast<const jbyte*>(
143                                 serialized_knowledge_result_string.data()));
144   }
145 
146   jstring contact_name = nullptr;
147   if (!classification_result.contact_name.empty()) {
148     contact_name =
149         env->NewStringUTF(classification_result.contact_name.c_str());
150   }
151 
152   jstring contact_given_name = nullptr;
153   if (!classification_result.contact_given_name.empty()) {
154     contact_given_name =
155         env->NewStringUTF(classification_result.contact_given_name.c_str());
156   }
157 
158   jstring contact_nickname = nullptr;
159   if (!classification_result.contact_nickname.empty()) {
160     contact_nickname =
161         env->NewStringUTF(classification_result.contact_nickname.c_str());
162   }
163 
164   jstring contact_email_address = nullptr;
165   if (!classification_result.contact_email_address.empty()) {
166     contact_email_address =
167         env->NewStringUTF(classification_result.contact_email_address.c_str());
168   }
169 
170   jstring contact_phone_number = nullptr;
171   if (!classification_result.contact_phone_number.empty()) {
172     contact_phone_number =
173         env->NewStringUTF(classification_result.contact_phone_number.c_str());
174   }
175 
176   jstring contact_id = nullptr;
177   if (!classification_result.contact_id.empty()) {
178     contact_id = env->NewStringUTF(classification_result.contact_id.c_str());
179   }
180 
181   jstring app_name = nullptr;
182   if (!classification_result.app_name.empty()) {
183     app_name = env->NewStringUTF(classification_result.app_name.c_str());
184   }
185 
186   jstring app_package_name = nullptr;
187   if (!classification_result.app_package_name.empty()) {
188     app_package_name =
189         env->NewStringUTF(classification_result.app_package_name.c_str());
190   }
191 
192   jobject extras = nullptr;
193   if (model_context->model()->entity_data_schema() != nullptr &&
194       !classification_result.serialized_entity_data.empty()) {
195     extras = model_context->template_handler()->EntityDataAsNamedVariantArray(
196         model_context->model()->entity_data_schema(),
197         classification_result.serialized_entity_data);
198   }
199 
200   jbyteArray serialized_entity_data = nullptr;
201   if (!classification_result.serialized_entity_data.empty()) {
202     serialized_entity_data =
203         env->NewByteArray(classification_result.serialized_entity_data.size());
204     env->SetByteArrayRegion(
205         serialized_entity_data, 0,
206         classification_result.serialized_entity_data.size(),
207         reinterpret_cast<const jbyte*>(
208             classification_result.serialized_entity_data.data()));
209   }
210 
211   jobject remote_action_templates_result = nullptr;
212   // Only generate RemoteActionTemplate for the top classification result
213   // as classifyText does not need RemoteAction from other results anyway.
214   if (generate_intents && model_context->intent_generator() != nullptr) {
215     std::vector<RemoteActionTemplate> remote_action_templates;
216     if (model_context->intent_generator()->GenerateIntents(
217             device_locales, classification_result,
218             options->reference_time_ms_utc, context, selection_indices,
219             app_context, model_context->model()->entity_data_schema(),
220             &remote_action_templates)) {
221       remote_action_templates_result =
222           model_context->template_handler()
223               ->RemoteActionTemplatesToJObjectArray(remote_action_templates);
224     }
225   }
226 
227   return env->NewObject(
228       result_class, result_class_constructor, row_string,
229       static_cast<jfloat>(classification_result.score), row_datetime_parse,
230       serialized_knowledge_result, contact_name, contact_given_name,
231       contact_nickname, contact_email_address, contact_phone_number, contact_id,
232       app_name, app_package_name, extras, serialized_entity_data,
233       remote_action_templates_result, classification_result.duration_ms,
234       classification_result.numeric_value);
235 }
236 
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)237 jobjectArray ClassificationResultsWithIntentsToJObjectArray(
238     JNIEnv* env, const AnnotatorJniContext* model_context, jobject app_context,
239     const jstring device_locales, const ClassificationOptions* options,
240     const std::string& context, const CodepointSpan& selection_indices,
241     const std::vector<ClassificationResult>& classification_result,
242     bool generate_intents) {
243   const ScopedLocalRef<jclass> result_class(
244       env->FindClass(TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR
245                      "$ClassificationResult"),
246       env);
247   if (!result_class) {
248     TC3_LOG(ERROR) << "Couldn't find ClassificationResult class.";
249     return nullptr;
250   }
251   const ScopedLocalRef<jclass> datetime_parse_class(
252       env->FindClass(TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR
253                      "$DatetimeResult"),
254       env);
255   if (!datetime_parse_class) {
256     TC3_LOG(ERROR) << "Couldn't find DatetimeResult class.";
257     return nullptr;
258   }
259 
260   const jmethodID result_class_constructor = env->GetMethodID(
261       result_class.get(), "<init>",
262       "(Ljava/lang/String;FL" TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR
263       "$DatetimeResult;[BLjava/lang/String;Ljava/lang/String;Ljava/lang/String;"
264       "Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;"
265       "Ljava/lang/String;[L" TC3_PACKAGE_PATH TC3_NAMED_VARIANT_CLASS_NAME_STR
266       ";[B[L" TC3_PACKAGE_PATH TC3_REMOTE_ACTION_TEMPLATE_CLASS_NAME_STR
267       ";JJ)V");
268   const jmethodID datetime_parse_class_constructor =
269       env->GetMethodID(datetime_parse_class.get(), "<init>", "(JI)V");
270 
271   const jobjectArray results = env->NewObjectArray(classification_result.size(),
272                                                    result_class.get(), nullptr);
273   for (int i = 0; i < classification_result.size(); i++) {
274     jobject result = ClassificationResultWithIntentsToJObject(
275         env, model_context, app_context, result_class.get(),
276         result_class_constructor, datetime_parse_class.get(),
277         datetime_parse_class_constructor, device_locales, options, context,
278         selection_indices, classification_result[i],
279         generate_intents && (i == 0));
280     env->SetObjectArrayElement(results, i, result);
281     env->DeleteLocalRef(result);
282   }
283   return results;
284 }
285 
ClassificationResultsToJObjectArray(JNIEnv * env,const AnnotatorJniContext * model_context,const std::vector<ClassificationResult> & classification_result)286 jobjectArray ClassificationResultsToJObjectArray(
287     JNIEnv* env, const AnnotatorJniContext* model_context,
288     const std::vector<ClassificationResult>& classification_result) {
289   return ClassificationResultsWithIntentsToJObjectArray(
290       env, model_context,
291       /*(unused) app_context=*/nullptr,
292       /*(unused) devide_locale=*/nullptr,
293       /*(unusued) options=*/nullptr,
294       /*(unused) selection_text=*/"",
295       /*(unused) selection_indices=*/{kInvalidIndex, kInvalidIndex},
296       classification_result,
297       /*generate_intents=*/false);
298 }
299 
ConvertIndicesBMPUTF8(const std::string & utf8_str,CodepointSpan orig_indices,bool from_utf8)300 CodepointSpan ConvertIndicesBMPUTF8(const std::string& utf8_str,
301                                     CodepointSpan orig_indices,
302                                     bool from_utf8) {
303   const libtextclassifier3::UnicodeText unicode_str =
304       libtextclassifier3::UTF8ToUnicodeText(utf8_str, /*do_copy=*/false);
305 
306   int unicode_index = 0;
307   int bmp_index = 0;
308 
309   const int* source_index;
310   const int* target_index;
311   if (from_utf8) {
312     source_index = &unicode_index;
313     target_index = &bmp_index;
314   } else {
315     source_index = &bmp_index;
316     target_index = &unicode_index;
317   }
318 
319   CodepointSpan result{-1, -1};
320   std::function<void()> assign_indices_fn = [&result, &orig_indices,
321                                              &source_index, &target_index]() {
322     if (orig_indices.first == *source_index) {
323       result.first = *target_index;
324     }
325 
326     if (orig_indices.second == *source_index) {
327       result.second = *target_index;
328     }
329   };
330 
331   for (auto it = unicode_str.begin(); it != unicode_str.end();
332        ++it, ++unicode_index, ++bmp_index) {
333     assign_indices_fn();
334 
335     // There is 1 extra character in the input for each UTF8 character > 0xFFFF.
336     if (*it > 0xFFFF) {
337       ++bmp_index;
338     }
339   }
340   assign_indices_fn();
341 
342   return result;
343 }
344 
345 }  // namespace
346 
ConvertIndicesBMPToUTF8(const std::string & utf8_str,CodepointSpan bmp_indices)347 CodepointSpan ConvertIndicesBMPToUTF8(const std::string& utf8_str,
348                                       CodepointSpan bmp_indices) {
349   return ConvertIndicesBMPUTF8(utf8_str, bmp_indices, /*from_utf8=*/false);
350 }
351 
ConvertIndicesUTF8ToBMP(const std::string & utf8_str,CodepointSpan utf8_indices)352 CodepointSpan ConvertIndicesUTF8ToBMP(const std::string& utf8_str,
353                                       CodepointSpan utf8_indices) {
354   return ConvertIndicesBMPUTF8(utf8_str, utf8_indices, /*from_utf8=*/true);
355 }
356 
GetLocalesFromMmap(JNIEnv * env,libtextclassifier3::ScopedMmap * mmap)357 jstring GetLocalesFromMmap(JNIEnv* env, libtextclassifier3::ScopedMmap* mmap) {
358   if (!mmap->handle().ok()) {
359     return env->NewStringUTF("");
360   }
361   const Model* model = libtextclassifier3::ViewModel(
362       mmap->handle().start(), mmap->handle().num_bytes());
363   if (!model || !model->locales()) {
364     return env->NewStringUTF("");
365   }
366   return env->NewStringUTF(model->locales()->c_str());
367 }
368 
GetVersionFromMmap(JNIEnv * env,libtextclassifier3::ScopedMmap * mmap)369 jint GetVersionFromMmap(JNIEnv* env, libtextclassifier3::ScopedMmap* mmap) {
370   if (!mmap->handle().ok()) {
371     return 0;
372   }
373   const Model* model = libtextclassifier3::ViewModel(
374       mmap->handle().start(), mmap->handle().num_bytes());
375   if (!model) {
376     return 0;
377   }
378   return model->version();
379 }
380 
GetNameFromMmap(JNIEnv * env,libtextclassifier3::ScopedMmap * mmap)381 jstring GetNameFromMmap(JNIEnv* env, libtextclassifier3::ScopedMmap* mmap) {
382   if (!mmap->handle().ok()) {
383     return env->NewStringUTF("");
384   }
385   const Model* model = libtextclassifier3::ViewModel(
386       mmap->handle().start(), mmap->handle().num_bytes());
387   if (!model || !model->name()) {
388     return env->NewStringUTF("");
389   }
390   return env->NewStringUTF(model->name()->c_str());
391 }
392 
393 }  // namespace libtextclassifier3
394 
395 using libtextclassifier3::AnnotatorJniContext;
396 using libtextclassifier3::ClassificationResultsToJObjectArray;
397 using libtextclassifier3::ClassificationResultsWithIntentsToJObjectArray;
398 using libtextclassifier3::ConvertIndicesBMPToUTF8;
399 using libtextclassifier3::ConvertIndicesUTF8ToBMP;
400 using libtextclassifier3::FromJavaAnnotationOptions;
401 using libtextclassifier3::FromJavaClassificationOptions;
402 using libtextclassifier3::FromJavaSelectionOptions;
403 using libtextclassifier3::ToStlString;
404 
TC3_JNI_METHOD(jlong,TC3_ANNOTATOR_CLASS_NAME,nativeNewAnnotator)405 TC3_JNI_METHOD(jlong, TC3_ANNOTATOR_CLASS_NAME, nativeNewAnnotator)
406 (JNIEnv* env, jobject thiz, jint fd) {
407   std::shared_ptr<libtextclassifier3::JniCache> jni_cache(
408       libtextclassifier3::JniCache::Create(env));
409 #ifdef TC3_USE_JAVAICU
410   return reinterpret_cast<jlong>(AnnotatorJniContext::Create(
411       jni_cache,
412       Annotator::FromFileDescriptor(
413           fd, std::unique_ptr<UniLib>(new UniLib(jni_cache)),
414           std::unique_ptr<CalendarLib>(new CalendarLib(jni_cache)))));
415 #else
416   return reinterpret_cast<jlong>(AnnotatorJniContext::Create(
417       jni_cache, Annotator::FromFileDescriptor(fd)));
418 #endif
419 }
420 
TC3_JNI_METHOD(jlong,TC3_ANNOTATOR_CLASS_NAME,nativeNewAnnotatorFromPath)421 TC3_JNI_METHOD(jlong, TC3_ANNOTATOR_CLASS_NAME, nativeNewAnnotatorFromPath)
422 (JNIEnv* env, jobject thiz, jstring path) {
423   const std::string path_str = ToStlString(env, path);
424   std::shared_ptr<libtextclassifier3::JniCache> jni_cache(
425       libtextclassifier3::JniCache::Create(env));
426 #ifdef TC3_USE_JAVAICU
427   return reinterpret_cast<jlong>(AnnotatorJniContext::Create(
428       jni_cache,
429       Annotator::FromPath(
430           path_str, std::unique_ptr<UniLib>(new UniLib(jni_cache)),
431           std::unique_ptr<CalendarLib>(new CalendarLib(jni_cache)))));
432 #else
433   return reinterpret_cast<jlong>(
434       AnnotatorJniContext::Create(jni_cache, Annotator::FromPath(path_str)));
435 #endif
436 }
437 
TC3_JNI_METHOD(jlong,TC3_ANNOTATOR_CLASS_NAME,nativeNewAnnotatorFromAssetFileDescriptor)438 TC3_JNI_METHOD(jlong, TC3_ANNOTATOR_CLASS_NAME,
439                nativeNewAnnotatorFromAssetFileDescriptor)
440 (JNIEnv* env, jobject thiz, jobject afd, jlong offset, jlong size) {
441   std::shared_ptr<libtextclassifier3::JniCache> jni_cache(
442       libtextclassifier3::JniCache::Create(env));
443   const jint fd = libtextclassifier3::GetFdFromAssetFileDescriptor(env, afd);
444 #ifdef TC3_USE_JAVAICU
445   return reinterpret_cast<jlong>(AnnotatorJniContext::Create(
446       jni_cache,
447       Annotator::FromFileDescriptor(
448           fd, offset, size, std::unique_ptr<UniLib>(new UniLib(jni_cache)),
449           std::unique_ptr<CalendarLib>(new CalendarLib(jni_cache)))));
450 #else
451   return reinterpret_cast<jlong>(AnnotatorJniContext::Create(
452       jni_cache, Annotator::FromFileDescriptor(fd, offset, size)));
453 #endif
454 }
455 
TC3_JNI_METHOD(jboolean,TC3_ANNOTATOR_CLASS_NAME,nativeInitializeKnowledgeEngine)456 TC3_JNI_METHOD(jboolean, TC3_ANNOTATOR_CLASS_NAME,
457                nativeInitializeKnowledgeEngine)
458 (JNIEnv* env, jobject thiz, jlong ptr, jbyteArray serialized_config) {
459   if (!ptr) {
460     return false;
461   }
462 
463   Annotator* model = reinterpret_cast<AnnotatorJniContext*>(ptr)->model();
464 
465   std::string serialized_config_string;
466   const int length = env->GetArrayLength(serialized_config);
467   serialized_config_string.resize(length);
468   env->GetByteArrayRegion(serialized_config, 0, length,
469                           reinterpret_cast<jbyte*>(const_cast<char*>(
470                               serialized_config_string.data())));
471 
472   return model->InitializeKnowledgeEngine(serialized_config_string);
473 }
474 
TC3_JNI_METHOD(jboolean,TC3_ANNOTATOR_CLASS_NAME,nativeInitializeContactEngine)475 TC3_JNI_METHOD(jboolean, TC3_ANNOTATOR_CLASS_NAME,
476                nativeInitializeContactEngine)
477 (JNIEnv* env, jobject thiz, jlong ptr, jbyteArray serialized_config) {
478   if (!ptr) {
479     return false;
480   }
481 
482   Annotator* model = reinterpret_cast<AnnotatorJniContext*>(ptr)->model();
483 
484   std::string serialized_config_string;
485   const int length = env->GetArrayLength(serialized_config);
486   serialized_config_string.resize(length);
487   env->GetByteArrayRegion(serialized_config, 0, length,
488                           reinterpret_cast<jbyte*>(const_cast<char*>(
489                               serialized_config_string.data())));
490 
491   return model->InitializeContactEngine(serialized_config_string);
492 }
493 
TC3_JNI_METHOD(jboolean,TC3_ANNOTATOR_CLASS_NAME,nativeInitializeInstalledAppEngine)494 TC3_JNI_METHOD(jboolean, TC3_ANNOTATOR_CLASS_NAME,
495                nativeInitializeInstalledAppEngine)
496 (JNIEnv* env, jobject thiz, jlong ptr, jbyteArray serialized_config) {
497   if (!ptr) {
498     return false;
499   }
500 
501   Annotator* model = reinterpret_cast<AnnotatorJniContext*>(ptr)->model();
502 
503   std::string serialized_config_string;
504   const int length = env->GetArrayLength(serialized_config);
505   serialized_config_string.resize(length);
506   env->GetByteArrayRegion(serialized_config, 0, length,
507                           reinterpret_cast<jbyte*>(const_cast<char*>(
508                               serialized_config_string.data())));
509 
510   return model->InitializeInstalledAppEngine(serialized_config_string);
511 }
512 
TC3_JNI_METHOD(jlong,TC3_ANNOTATOR_CLASS_NAME,nativeGetNativeModelPtr)513 TC3_JNI_METHOD(jlong, TC3_ANNOTATOR_CLASS_NAME, nativeGetNativeModelPtr)
514 (JNIEnv* env, jobject thiz, jlong ptr) {
515   if (!ptr) {
516     return 0L;
517   }
518   return reinterpret_cast<jlong>(
519       reinterpret_cast<AnnotatorJniContext*>(ptr)->model());
520 }
521 
TC3_JNI_METHOD(jintArray,TC3_ANNOTATOR_CLASS_NAME,nativeSuggestSelection)522 TC3_JNI_METHOD(jintArray, TC3_ANNOTATOR_CLASS_NAME, nativeSuggestSelection)
523 (JNIEnv* env, jobject thiz, jlong ptr, jstring context, jint selection_begin,
524  jint selection_end, jobject options) {
525   if (!ptr) {
526     return nullptr;
527   }
528   const Annotator* model = reinterpret_cast<AnnotatorJniContext*>(ptr)->model();
529   const std::string context_utf8 = ToStlString(env, context);
530   CodepointSpan input_indices =
531       ConvertIndicesBMPToUTF8(context_utf8, {selection_begin, selection_end});
532   CodepointSpan selection = model->SuggestSelection(
533       context_utf8, input_indices, FromJavaSelectionOptions(env, options));
534   selection = ConvertIndicesUTF8ToBMP(context_utf8, selection);
535 
536   jintArray result = env->NewIntArray(2);
537   env->SetIntArrayRegion(result, 0, 1, &(std::get<0>(selection)));
538   env->SetIntArrayRegion(result, 1, 1, &(std::get<1>(selection)));
539   return result;
540 }
541 
TC3_JNI_METHOD(jobjectArray,TC3_ANNOTATOR_CLASS_NAME,nativeClassifyText)542 TC3_JNI_METHOD(jobjectArray, TC3_ANNOTATOR_CLASS_NAME, nativeClassifyText)
543 (JNIEnv* env, jobject thiz, jlong ptr, jstring context, jint selection_begin,
544  jint selection_end, jobject options, jobject app_context,
545  jstring device_locales) {
546   if (!ptr) {
547     return nullptr;
548   }
549   const AnnotatorJniContext* model_context =
550       reinterpret_cast<AnnotatorJniContext*>(ptr);
551 
552   const std::string context_utf8 = ToStlString(env, context);
553   const CodepointSpan input_indices =
554       ConvertIndicesBMPToUTF8(context_utf8, {selection_begin, selection_end});
555   const libtextclassifier3::ClassificationOptions classification_options =
556       FromJavaClassificationOptions(env, options);
557   const std::vector<ClassificationResult> classification_result =
558       model_context->model()->ClassifyText(context_utf8, input_indices,
559                                            classification_options);
560   if (app_context != nullptr) {
561     return ClassificationResultsWithIntentsToJObjectArray(
562         env, model_context, app_context, device_locales,
563         &classification_options, context_utf8, input_indices,
564         classification_result,
565         /*generate_intents=*/true);
566   }
567   return ClassificationResultsToJObjectArray(env, model_context,
568                                              classification_result);
569 }
570 
TC3_JNI_METHOD(jobjectArray,TC3_ANNOTATOR_CLASS_NAME,nativeAnnotate)571 TC3_JNI_METHOD(jobjectArray, TC3_ANNOTATOR_CLASS_NAME, nativeAnnotate)
572 (JNIEnv* env, jobject thiz, jlong ptr, jstring context, jobject options) {
573   if (!ptr) {
574     return nullptr;
575   }
576   const AnnotatorJniContext* model_context =
577       reinterpret_cast<AnnotatorJniContext*>(ptr);
578   const std::string context_utf8 = ToStlString(env, context);
579   const std::vector<AnnotatedSpan> annotations =
580       model_context->model()->Annotate(context_utf8,
581                                        FromJavaAnnotationOptions(env, options));
582 
583   jclass result_class = env->FindClass(
584       TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR "$AnnotatedSpan");
585   if (!result_class) {
586     TC3_LOG(ERROR) << "Couldn't find result class: "
587                    << TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR
588         "$AnnotatedSpan";
589     return nullptr;
590   }
591 
592   jmethodID result_class_constructor =
593       env->GetMethodID(result_class, "<init>",
594                        "(II[L" TC3_PACKAGE_PATH TC3_ANNOTATOR_CLASS_NAME_STR
595                        "$ClassificationResult;)V");
596 
597   jobjectArray results =
598       env->NewObjectArray(annotations.size(), result_class, nullptr);
599 
600   for (int i = 0; i < annotations.size(); ++i) {
601     CodepointSpan span_bmp =
602         ConvertIndicesUTF8ToBMP(context_utf8, annotations[i].span);
603     jobject result = env->NewObject(
604         result_class, result_class_constructor,
605         static_cast<jint>(span_bmp.first), static_cast<jint>(span_bmp.second),
606         ClassificationResultsToJObjectArray(env, model_context,
607                                             annotations[i].classification));
608     env->SetObjectArrayElement(results, i, result);
609     env->DeleteLocalRef(result);
610   }
611   env->DeleteLocalRef(result_class);
612   return results;
613 }
614 
TC3_JNI_METHOD(jbyteArray,TC3_ANNOTATOR_CLASS_NAME,nativeLookUpKnowledgeEntity)615 TC3_JNI_METHOD(jbyteArray, TC3_ANNOTATOR_CLASS_NAME,
616                nativeLookUpKnowledgeEntity)
617 (JNIEnv* env, jobject thiz, jlong ptr, jstring id) {
618   if (!ptr) {
619     return nullptr;
620   }
621   const Annotator* model = reinterpret_cast<AnnotatorJniContext*>(ptr)->model();
622   const std::string id_utf8 = ToStlString(env, id);
623   std::string serialized_knowledge_result;
624   if (!model->LookUpKnowledgeEntity(id_utf8, &serialized_knowledge_result)) {
625     return nullptr;
626   }
627   jbyteArray result = env->NewByteArray(serialized_knowledge_result.size());
628   env->SetByteArrayRegion(
629       result, 0, serialized_knowledge_result.size(),
630       reinterpret_cast<const jbyte*>(serialized_knowledge_result.data()));
631   return result;
632 }
633 
TC3_JNI_METHOD(void,TC3_ANNOTATOR_CLASS_NAME,nativeCloseAnnotator)634 TC3_JNI_METHOD(void, TC3_ANNOTATOR_CLASS_NAME, nativeCloseAnnotator)
635 (JNIEnv* env, jobject thiz, jlong ptr) {
636   const AnnotatorJniContext* context =
637       reinterpret_cast<AnnotatorJniContext*>(ptr);
638   delete context;
639 }
640 
TC3_JNI_METHOD(jstring,TC3_ANNOTATOR_CLASS_NAME,nativeGetLanguage)641 TC3_JNI_METHOD(jstring, TC3_ANNOTATOR_CLASS_NAME, nativeGetLanguage)
642 (JNIEnv* env, jobject clazz, jint fd) {
643   TC3_LOG(WARNING) << "Using deprecated getLanguage().";
644   return TC3_JNI_METHOD_NAME(TC3_ANNOTATOR_CLASS_NAME, nativeGetLocales)(
645       env, clazz, fd);
646 }
647 
TC3_JNI_METHOD(jstring,TC3_ANNOTATOR_CLASS_NAME,nativeGetLocales)648 TC3_JNI_METHOD(jstring, TC3_ANNOTATOR_CLASS_NAME, nativeGetLocales)
649 (JNIEnv* env, jobject clazz, jint fd) {
650   const std::unique_ptr<libtextclassifier3::ScopedMmap> mmap(
651       new libtextclassifier3::ScopedMmap(fd));
652   return GetLocalesFromMmap(env, mmap.get());
653 }
654 
TC3_JNI_METHOD(jstring,TC3_ANNOTATOR_CLASS_NAME,nativeGetLocalesFromAssetFileDescriptor)655 TC3_JNI_METHOD(jstring, TC3_ANNOTATOR_CLASS_NAME,
656                nativeGetLocalesFromAssetFileDescriptor)
657 (JNIEnv* env, jobject thiz, jobject afd, jlong offset, jlong size) {
658   const jint fd = libtextclassifier3::GetFdFromAssetFileDescriptor(env, afd);
659   const std::unique_ptr<libtextclassifier3::ScopedMmap> mmap(
660       new libtextclassifier3::ScopedMmap(fd, offset, size));
661   return GetLocalesFromMmap(env, mmap.get());
662 }
663 
TC3_JNI_METHOD(jint,TC3_ANNOTATOR_CLASS_NAME,nativeGetVersion)664 TC3_JNI_METHOD(jint, TC3_ANNOTATOR_CLASS_NAME, nativeGetVersion)
665 (JNIEnv* env, jobject clazz, jint fd) {
666   const std::unique_ptr<libtextclassifier3::ScopedMmap> mmap(
667       new libtextclassifier3::ScopedMmap(fd));
668   return GetVersionFromMmap(env, mmap.get());
669 }
670 
TC3_JNI_METHOD(jint,TC3_ANNOTATOR_CLASS_NAME,nativeGetVersionFromAssetFileDescriptor)671 TC3_JNI_METHOD(jint, TC3_ANNOTATOR_CLASS_NAME,
672                nativeGetVersionFromAssetFileDescriptor)
673 (JNIEnv* env, jobject thiz, jobject afd, jlong offset, jlong size) {
674   const jint fd = libtextclassifier3::GetFdFromAssetFileDescriptor(env, afd);
675   const std::unique_ptr<libtextclassifier3::ScopedMmap> mmap(
676       new libtextclassifier3::ScopedMmap(fd, offset, size));
677   return GetVersionFromMmap(env, mmap.get());
678 }
679 
TC3_JNI_METHOD(jstring,TC3_ANNOTATOR_CLASS_NAME,nativeGetName)680 TC3_JNI_METHOD(jstring, TC3_ANNOTATOR_CLASS_NAME, nativeGetName)
681 (JNIEnv* env, jobject clazz, jint fd) {
682   const std::unique_ptr<libtextclassifier3::ScopedMmap> mmap(
683       new libtextclassifier3::ScopedMmap(fd));
684   return GetNameFromMmap(env, mmap.get());
685 }
686 
TC3_JNI_METHOD(jstring,TC3_ANNOTATOR_CLASS_NAME,nativeGetNameFromAssetFileDescriptor)687 TC3_JNI_METHOD(jstring, TC3_ANNOTATOR_CLASS_NAME,
688                nativeGetNameFromAssetFileDescriptor)
689 (JNIEnv* env, jobject thiz, jobject afd, jlong offset, jlong size) {
690   const jint fd = libtextclassifier3::GetFdFromAssetFileDescriptor(env, afd);
691   const std::unique_ptr<libtextclassifier3::ScopedMmap> mmap(
692       new libtextclassifier3::ScopedMmap(fd, offset, size));
693   return GetNameFromMmap(env, mmap.get());
694 }
695