• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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