• 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             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