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