Home | History | Annotate | Download | only in java
      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 #include "utils/java/jni-cache.h"
     18 
     19 #include "utils/base/logging.h"
     20 
     21 namespace libtextclassifier3 {
     22 
     23 JniCache::JniCache(JavaVM* jvm)
     24     : jvm(jvm),
     25       string_class(nullptr, jvm),
     26       string_utf8(nullptr, jvm),
     27       pattern_class(nullptr, jvm),
     28       matcher_class(nullptr, jvm),
     29       locale_class(nullptr, jvm),
     30       locale_us(nullptr, jvm),
     31       breakiterator_class(nullptr, jvm),
     32       integer_class(nullptr, jvm),
     33       calendar_class(nullptr, jvm),
     34       timezone_class(nullptr, jvm),
     35       urlencoder_class(nullptr, jvm)
     36 #ifdef __ANDROID__
     37       ,
     38       context_class(nullptr, jvm),
     39       uri_class(nullptr, jvm),
     40       usermanager_class(nullptr, jvm),
     41       bundle_class(nullptr, jvm),
     42       resources_class(nullptr, jvm)
     43 #endif
     44 {
     45 }
     46 
     47 // The macros below are intended to reduce the boilerplate in Create and avoid
     48 // easily introduced copy/paste errors.
     49 #define TC3_CHECK_JNI_PTR(PTR) TC3_CHECK((PTR) != nullptr)
     50 #define TC3_CHECK_JNI_RESULT(RESULT) TC3_CHECK(RESULT)
     51 
     52 #define TC3_GET_CLASS(FIELD, NAME)                                       \
     53   result->FIELD##_class = MakeGlobalRef(env->FindClass(NAME), env, jvm); \
     54   TC3_CHECK_JNI_PTR(result->FIELD##_class) << "Error finding class: " << NAME;
     55 
     56 #define TC3_GET_OPTIONAL_CLASS(FIELD, NAME)                   \
     57   {                                                           \
     58     jclass clazz = env->FindClass(NAME);                      \
     59     if (clazz != nullptr) {                                   \
     60       result->FIELD##_class = MakeGlobalRef(clazz, env, jvm); \
     61     }                                                         \
     62     env->ExceptionClear();                                    \
     63   }
     64 
     65 #define TC3_GET_METHOD(CLASS, FIELD, NAME, SIGNATURE)                 \
     66   result->CLASS##_##FIELD =                                           \
     67       env->GetMethodID(result->CLASS##_class.get(), NAME, SIGNATURE); \
     68   TC3_CHECK_JNI_RESULT(result->CLASS##_##FIELD)                       \
     69       << "Error finding method: " << NAME;
     70 
     71 #define TC3_GET_OPTIONAL_METHOD(CLASS, FIELD, NAME, SIGNATURE)          \
     72   if (result->CLASS##_class != nullptr) {                               \
     73     result->CLASS##_##FIELD =                                           \
     74         env->GetMethodID(result->CLASS##_class.get(), NAME, SIGNATURE); \
     75     env->ExceptionClear();                                              \
     76   }
     77 
     78 #define TC3_GET_OPTIONAL_STATIC_METHOD(CLASS, FIELD, NAME, SIGNATURE)         \
     79   if (result->CLASS##_class != nullptr) {                                     \
     80     result->CLASS##_##FIELD =                                                 \
     81         env->GetStaticMethodID(result->CLASS##_class.get(), NAME, SIGNATURE); \
     82     env->ExceptionClear();                                                    \
     83   }
     84 
     85 #define TC3_GET_STATIC_METHOD(CLASS, FIELD, NAME, SIGNATURE)                \
     86   result->CLASS##_##FIELD =                                                 \
     87       env->GetStaticMethodID(result->CLASS##_class.get(), NAME, SIGNATURE); \
     88   TC3_CHECK_JNI_RESULT(result->CLASS##_##FIELD)                             \
     89       << "Error finding method: " << NAME;
     90 
     91 #define TC3_GET_STATIC_OBJECT_FIELD(CLASS, FIELD, NAME, SIGNATURE)         \
     92   const jfieldID CLASS##_##FIELD##_field =                                 \
     93       env->GetStaticFieldID(result->CLASS##_class.get(), NAME, SIGNATURE); \
     94   TC3_CHECK_JNI_RESULT(CLASS##_##FIELD##_field)                            \
     95       << "Error finding field id: " << NAME;                               \
     96   result->CLASS##_##FIELD =                                                \
     97       MakeGlobalRef(env->GetStaticObjectField(result->CLASS##_class.get(), \
     98                                               CLASS##_##FIELD##_field),    \
     99                     env, jvm);                                             \
    100   TC3_CHECK_JNI_RESULT(result->CLASS##_##FIELD)                            \
    101       << "Error finding field: " << NAME;
    102 
    103 #define TC3_GET_STATIC_INT_FIELD(CLASS, FIELD, NAME)                 \
    104   const jfieldID CLASS##_##FIELD##_field =                           \
    105       env->GetStaticFieldID(result->CLASS##_class.get(), NAME, "I"); \
    106   TC3_CHECK_JNI_RESULT(CLASS##_##FIELD##_field)                      \
    107       << "Error finding field id: " << NAME;                         \
    108   result->CLASS##_##FIELD = env->GetStaticIntField(                  \
    109       result->CLASS##_class.get(), CLASS##_##FIELD##_field);         \
    110   TC3_CHECK_JNI_RESULT(result->CLASS##_##FIELD)                      \
    111       << "Error finding field: " << NAME;
    112 
    113 std::unique_ptr<JniCache> JniCache::Create(JNIEnv* env) {
    114   if (env == nullptr) {
    115     return nullptr;
    116   }
    117   JavaVM* jvm = nullptr;
    118   if (JNI_OK != env->GetJavaVM(&jvm) || jvm == nullptr) {
    119     return nullptr;
    120   }
    121   std::unique_ptr<JniCache> result(new JniCache(jvm));
    122 
    123   // String
    124   TC3_GET_CLASS(string, "java/lang/String");
    125   TC3_GET_METHOD(string, init_bytes_charset, "<init>",
    126                  "([BLjava/lang/String;)V");
    127   TC3_GET_METHOD(string, code_point_count, "codePointCount", "(II)I");
    128   TC3_GET_METHOD(string, length, "length", "()I");
    129   result->string_utf8 = MakeGlobalRef(env->NewStringUTF("UTF-8"), env, jvm);
    130   TC3_CHECK_JNI_PTR(result->string_utf8);
    131 
    132   // Pattern
    133   TC3_GET_CLASS(pattern, "java/util/regex/Pattern");
    134   TC3_GET_STATIC_METHOD(pattern, compile, "compile",
    135                         "(Ljava/lang/String;)Ljava/util/regex/Pattern;");
    136   TC3_GET_METHOD(pattern, matcher, "matcher",
    137                  "(Ljava/lang/CharSequence;)Ljava/util/regex/Matcher;");
    138 
    139   // Matcher
    140   TC3_GET_CLASS(matcher, "java/util/regex/Matcher");
    141   TC3_GET_METHOD(matcher, matches, "matches", "()Z");
    142   TC3_GET_METHOD(matcher, find, "find", "()Z");
    143   TC3_GET_METHOD(matcher, reset, "reset", "()Ljava/util/regex/Matcher;");
    144   TC3_GET_METHOD(matcher, start_idx, "start", "(I)I");
    145   TC3_GET_METHOD(matcher, end_idx, "end", "(I)I");
    146   TC3_GET_METHOD(matcher, group, "group", "()Ljava/lang/String;");
    147   TC3_GET_METHOD(matcher, group_idx, "group", "(I)Ljava/lang/String;");
    148 
    149   // Locale
    150   TC3_GET_CLASS(locale, "java/util/Locale");
    151   TC3_GET_STATIC_OBJECT_FIELD(locale, us, "US", "Ljava/util/Locale;");
    152   TC3_GET_METHOD(locale, init_string, "<init>", "(Ljava/lang/String;)V");
    153   TC3_GET_OPTIONAL_STATIC_METHOD(locale, for_language_tag, "forLanguageTag",
    154                                  "(Ljava/lang/String;)Ljava/util/Locale;");
    155 
    156   // BreakIterator
    157   TC3_GET_CLASS(breakiterator, "java/text/BreakIterator");
    158   TC3_GET_STATIC_METHOD(breakiterator, getwordinstance, "getWordInstance",
    159                         "(Ljava/util/Locale;)Ljava/text/BreakIterator;");
    160   TC3_GET_METHOD(breakiterator, settext, "setText", "(Ljava/lang/String;)V");
    161   TC3_GET_METHOD(breakiterator, next, "next", "()I");
    162 
    163   // Integer
    164   TC3_GET_CLASS(integer, "java/lang/Integer");
    165   TC3_GET_STATIC_METHOD(integer, parse_int, "parseInt",
    166                         "(Ljava/lang/String;)I");
    167 
    168   // Calendar.
    169   TC3_GET_CLASS(calendar, "java/util/Calendar");
    170   TC3_GET_STATIC_METHOD(
    171       calendar, get_instance, "getInstance",
    172       "(Ljava/util/TimeZone;Ljava/util/Locale;)Ljava/util/Calendar;");
    173   TC3_GET_METHOD(calendar, get_first_day_of_week, "getFirstDayOfWeek", "()I");
    174   TC3_GET_METHOD(calendar, get_time_in_millis, "getTimeInMillis", "()J");
    175   TC3_GET_METHOD(calendar, set_time_in_millis, "setTimeInMillis", "(J)V");
    176   TC3_GET_METHOD(calendar, add, "add", "(II)V");
    177   TC3_GET_METHOD(calendar, get, "get", "(I)I");
    178   TC3_GET_METHOD(calendar, set, "set", "(II)V");
    179   TC3_GET_STATIC_INT_FIELD(calendar, zone_offset, "ZONE_OFFSET");
    180   TC3_GET_STATIC_INT_FIELD(calendar, dst_offset, "DST_OFFSET");
    181   TC3_GET_STATIC_INT_FIELD(calendar, year, "YEAR");
    182   TC3_GET_STATIC_INT_FIELD(calendar, month, "MONTH");
    183   TC3_GET_STATIC_INT_FIELD(calendar, day_of_year, "DAY_OF_YEAR");
    184   TC3_GET_STATIC_INT_FIELD(calendar, day_of_month, "DAY_OF_MONTH");
    185   TC3_GET_STATIC_INT_FIELD(calendar, day_of_week, "DAY_OF_WEEK");
    186   TC3_GET_STATIC_INT_FIELD(calendar, hour_of_day, "HOUR_OF_DAY");
    187   TC3_GET_STATIC_INT_FIELD(calendar, minute, "MINUTE");
    188   TC3_GET_STATIC_INT_FIELD(calendar, second, "SECOND");
    189   TC3_GET_STATIC_INT_FIELD(calendar, millisecond, "MILLISECOND");
    190   TC3_GET_STATIC_INT_FIELD(calendar, sunday, "SUNDAY");
    191   TC3_GET_STATIC_INT_FIELD(calendar, monday, "MONDAY");
    192   TC3_GET_STATIC_INT_FIELD(calendar, tuesday, "TUESDAY");
    193   TC3_GET_STATIC_INT_FIELD(calendar, wednesday, "WEDNESDAY");
    194   TC3_GET_STATIC_INT_FIELD(calendar, thursday, "THURSDAY");
    195   TC3_GET_STATIC_INT_FIELD(calendar, friday, "FRIDAY");
    196   TC3_GET_STATIC_INT_FIELD(calendar, saturday, "SATURDAY");
    197 
    198   // TimeZone.
    199   TC3_GET_CLASS(timezone, "java/util/TimeZone");
    200   TC3_GET_STATIC_METHOD(timezone, get_timezone, "getTimeZone",
    201                         "(Ljava/lang/String;)Ljava/util/TimeZone;");
    202 
    203   // URLEncoder.
    204   TC3_GET_CLASS(urlencoder, "java/net/URLEncoder");
    205   TC3_GET_STATIC_METHOD(
    206       urlencoder, encode, "encode",
    207       "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
    208 
    209 #ifdef __ANDROID__
    210   // Context.
    211   TC3_GET_CLASS(context, "android/content/Context");
    212   TC3_GET_METHOD(context, get_package_name, "getPackageName",
    213                  "()Ljava/lang/String;");
    214   TC3_GET_METHOD(context, get_system_service, "getSystemService",
    215                  "(Ljava/lang/String;)Ljava/lang/Object;");
    216 
    217   // Uri.
    218   TC3_GET_CLASS(uri, "android/net/Uri");
    219   TC3_GET_STATIC_METHOD(uri, parse, "parse",
    220                         "(Ljava/lang/String;)Landroid/net/Uri;");
    221   TC3_GET_METHOD(uri, get_scheme, "getScheme", "()Ljava/lang/String;");
    222   TC3_GET_METHOD(uri, get_host, "getHost", "()Ljava/lang/String;");
    223 
    224   // UserManager.
    225   TC3_GET_OPTIONAL_CLASS(usermanager, "android/os/UserManager");
    226   TC3_GET_OPTIONAL_METHOD(usermanager, get_user_restrictions,
    227                           "getUserRestrictions", "()Landroid/os/Bundle;");
    228 
    229   // Bundle.
    230   TC3_GET_CLASS(bundle, "android/os/Bundle");
    231   TC3_GET_METHOD(bundle, get_boolean, "getBoolean", "(Ljava/lang/String;)Z");
    232 
    233   // String resources.
    234   TC3_GET_CLASS(resources, "android/content/res/Resources");
    235   TC3_GET_STATIC_METHOD(resources, get_system, "getSystem",
    236                         "()Landroid/content/res/Resources;");
    237   TC3_GET_METHOD(resources, get_identifier, "getIdentifier",
    238                  "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)I");
    239   TC3_GET_METHOD(resources, get_string, "getString", "(I)Ljava/lang/String;");
    240 #endif
    241 
    242   return result;
    243 }
    244 
    245 #undef TC3_GET_STATIC_INT_FIELD
    246 #undef TC3_GET_STATIC_OBJECT_FIELD
    247 #undef TC3_GET_STATIC_METHOD
    248 #undef TC3_GET_METHOD
    249 #undef TC3_GET_CLASS
    250 #undef TC3_CHECK_JNI_PTR
    251 
    252 JNIEnv* JniCache::GetEnv() const {
    253   void* env;
    254   if (JNI_OK == jvm->GetEnv(&env, JNI_VERSION_1_4)) {
    255     return reinterpret_cast<JNIEnv*>(env);
    256   } else {
    257     TC3_LOG(ERROR) << "JavaICU UniLib used on unattached thread";
    258     return nullptr;
    259   }
    260 }
    261 
    262 bool JniCache::ExceptionCheckAndClear() const {
    263   JNIEnv* env = GetEnv();
    264   TC3_CHECK(env != nullptr);
    265   const bool result = env->ExceptionCheck();
    266   if (result) {
    267     env->ExceptionDescribe();
    268     env->ExceptionClear();
    269   }
    270   return result;
    271 }
    272 
    273 ScopedLocalRef<jstring> JniCache::ConvertToJavaString(
    274     const char* utf8_text, const int utf8_text_size_bytes) const {
    275   // Create java byte array.
    276   JNIEnv* jenv = GetEnv();
    277   const ScopedLocalRef<jbyteArray> text_java_utf8(
    278       jenv->NewByteArray(utf8_text_size_bytes), jenv);
    279   if (!text_java_utf8) {
    280     return nullptr;
    281   }
    282 
    283   jenv->SetByteArrayRegion(text_java_utf8.get(), 0, utf8_text_size_bytes,
    284                            reinterpret_cast<const jbyte*>(utf8_text));
    285 
    286   // Create the string with a UTF-8 charset.
    287   return ScopedLocalRef<jstring>(
    288       reinterpret_cast<jstring>(
    289           jenv->NewObject(string_class.get(), string_init_bytes_charset,
    290                           text_java_utf8.get(), string_utf8.get())),
    291       jenv);
    292 }
    293 
    294 ScopedLocalRef<jstring> JniCache::ConvertToJavaString(
    295     StringPiece utf8_text) const {
    296   return ConvertToJavaString(utf8_text.data(), utf8_text.size());
    297 }
    298 
    299 ScopedLocalRef<jstring> JniCache::ConvertToJavaString(
    300     const UnicodeText& text) const {
    301   return ConvertToJavaString(text.data(), text.size_bytes());
    302 }
    303 
    304 }  // namespace libtextclassifier3
    305