blob: e2a6676606655c7eada5af7d46bf88ee731a0e7a [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "utils/java/jni-cache.h"
#include "utils/base/logging.h"
namespace libtextclassifier3 {
JniCache::JniCache(JavaVM* jvm)
: jvm(jvm),
string_class(nullptr, jvm),
string_utf8(nullptr, jvm),
pattern_class(nullptr, jvm),
matcher_class(nullptr, jvm),
locale_class(nullptr, jvm),
locale_us(nullptr, jvm),
breakiterator_class(nullptr, jvm),
integer_class(nullptr, jvm),
calendar_class(nullptr, jvm),
timezone_class(nullptr, jvm),
urlencoder_class(nullptr, jvm)
#ifdef __ANDROID__
,
context_class(nullptr, jvm),
uri_class(nullptr, jvm),
usermanager_class(nullptr, jvm),
bundle_class(nullptr, jvm)
#endif
{
}
// The macros below are intended to reduce the boilerplate in Create and avoid
// easily introduced copy/paste errors.
#define TC3_CHECK_JNI_PTR(PTR) TC3_CHECK((PTR) != nullptr)
#define TC3_CHECK_JNI_RESULT(RESULT) TC3_CHECK(RESULT)
#define TC3_GET_CLASS(FIELD, NAME) \
result->FIELD##_class = MakeGlobalRef(env->FindClass(NAME), env, jvm); \
TC3_CHECK_JNI_PTR(result->FIELD##_class) << "Error finding class: " << NAME;
#define TC3_GET_OPTIONAL_CLASS(FIELD, NAME) \
{ \
jclass clazz = env->FindClass(NAME); \
if (clazz != nullptr) { \
result->FIELD##_class = MakeGlobalRef(clazz, env, jvm); \
} \
env->ExceptionClear(); \
}
#define TC3_GET_METHOD(CLASS, FIELD, NAME, SIGNATURE) \
result->CLASS##_##FIELD = \
env->GetMethodID(result->CLASS##_class.get(), NAME, SIGNATURE); \
TC3_CHECK_JNI_RESULT(result->CLASS##_##FIELD) \
<< "Error finding method: " << NAME;
#define TC3_GET_OPTIONAL_METHOD(CLASS, FIELD, NAME, SIGNATURE) \
if (result->CLASS##_class != nullptr) { \
result->CLASS##_##FIELD = \
env->GetMethodID(result->CLASS##_class.get(), NAME, SIGNATURE); \
env->ExceptionClear(); \
}
#define TC3_GET_OPTIONAL_STATIC_METHOD(CLASS, FIELD, NAME, SIGNATURE) \
if (result->CLASS##_class != nullptr) { \
result->CLASS##_##FIELD = \
env->GetStaticMethodID(result->CLASS##_class.get(), NAME, SIGNATURE); \
env->ExceptionClear(); \
}
#define TC3_GET_STATIC_METHOD(CLASS, FIELD, NAME, SIGNATURE) \
result->CLASS##_##FIELD = \
env->GetStaticMethodID(result->CLASS##_class.get(), NAME, SIGNATURE); \
TC3_CHECK_JNI_RESULT(result->CLASS##_##FIELD) \
<< "Error finding method: " << NAME;
#define TC3_GET_STATIC_OBJECT_FIELD(CLASS, FIELD, NAME, SIGNATURE) \
const jfieldID CLASS##_##FIELD##_field = \
env->GetStaticFieldID(result->CLASS##_class.get(), NAME, SIGNATURE); \
TC3_CHECK_JNI_RESULT(CLASS##_##FIELD##_field) \
<< "Error finding field id: " << NAME; \
result->CLASS##_##FIELD = \
MakeGlobalRef(env->GetStaticObjectField(result->CLASS##_class.get(), \
CLASS##_##FIELD##_field), \
env, jvm); \
TC3_CHECK_JNI_RESULT(result->CLASS##_##FIELD) \
<< "Error finding field: " << NAME;
#define TC3_GET_STATIC_INT_FIELD(CLASS, FIELD, NAME) \
const jfieldID CLASS##_##FIELD##_field = \
env->GetStaticFieldID(result->CLASS##_class.get(), NAME, "I"); \
TC3_CHECK_JNI_RESULT(CLASS##_##FIELD##_field) \
<< "Error finding field id: " << NAME; \
result->CLASS##_##FIELD = env->GetStaticIntField( \
result->CLASS##_class.get(), CLASS##_##FIELD##_field); \
TC3_CHECK_JNI_RESULT(result->CLASS##_##FIELD) \
<< "Error finding field: " << NAME;
std::unique_ptr<JniCache> JniCache::Create(JNIEnv* env) {
if (env == nullptr) {
return nullptr;
}
JavaVM* jvm = nullptr;
if (JNI_OK != env->GetJavaVM(&jvm) || jvm == nullptr) {
return nullptr;
}
std::unique_ptr<JniCache> result(new JniCache(jvm));
// String
TC3_GET_CLASS(string, "java/lang/String");
TC3_GET_METHOD(string, init_bytes_charset, "<init>",
"([BLjava/lang/String;)V");
TC3_GET_METHOD(string, code_point_count, "codePointCount", "(II)I");
TC3_GET_METHOD(string, length, "length", "()I");
result->string_utf8 = MakeGlobalRef(env->NewStringUTF("UTF-8"), env, jvm);
TC3_CHECK_JNI_PTR(result->string_utf8);
// Pattern
TC3_GET_CLASS(pattern, "java/util/regex/Pattern");
TC3_GET_STATIC_METHOD(pattern, compile, "compile",
"(Ljava/lang/String;)Ljava/util/regex/Pattern;");
TC3_GET_METHOD(pattern, matcher, "matcher",
"(Ljava/lang/CharSequence;)Ljava/util/regex/Matcher;");
// Matcher
TC3_GET_CLASS(matcher, "java/util/regex/Matcher");
TC3_GET_METHOD(matcher, matches, "matches", "()Z");
TC3_GET_METHOD(matcher, find, "find", "()Z");
TC3_GET_METHOD(matcher, reset, "reset", "()Ljava/util/regex/Matcher;");
TC3_GET_METHOD(matcher, start_idx, "start", "(I)I");
TC3_GET_METHOD(matcher, end_idx, "end", "(I)I");
TC3_GET_METHOD(matcher, group, "group", "()Ljava/lang/String;");
TC3_GET_METHOD(matcher, group_idx, "group", "(I)Ljava/lang/String;");
// Locale
TC3_GET_CLASS(locale, "java/util/Locale");
TC3_GET_STATIC_OBJECT_FIELD(locale, us, "US", "Ljava/util/Locale;");
TC3_GET_METHOD(locale, init_string, "<init>", "(Ljava/lang/String;)V");
TC3_GET_OPTIONAL_STATIC_METHOD(locale, for_language_tag, "forLanguageTag",
"(Ljava/lang/String;)Ljava/util/Locale;");
// BreakIterator
TC3_GET_CLASS(breakiterator, "java/text/BreakIterator");
TC3_GET_STATIC_METHOD(breakiterator, getwordinstance, "getWordInstance",
"(Ljava/util/Locale;)Ljava/text/BreakIterator;");
TC3_GET_METHOD(breakiterator, settext, "setText", "(Ljava/lang/String;)V");
TC3_GET_METHOD(breakiterator, next, "next", "()I");
// Integer
TC3_GET_CLASS(integer, "java/lang/Integer");
TC3_GET_STATIC_METHOD(integer, parse_int, "parseInt",
"(Ljava/lang/String;)I");
// Calendar.
TC3_GET_CLASS(calendar, "java/util/Calendar");
TC3_GET_STATIC_METHOD(
calendar, get_instance, "getInstance",
"(Ljava/util/TimeZone;Ljava/util/Locale;)Ljava/util/Calendar;");
TC3_GET_METHOD(calendar, get_first_day_of_week, "getFirstDayOfWeek", "()I");
TC3_GET_METHOD(calendar, get_time_in_millis, "getTimeInMillis", "()J");
TC3_GET_METHOD(calendar, set_time_in_millis, "setTimeInMillis", "(J)V");
TC3_GET_METHOD(calendar, add, "add", "(II)V");
TC3_GET_METHOD(calendar, get, "get", "(I)I");
TC3_GET_METHOD(calendar, set, "set", "(II)V");
TC3_GET_STATIC_INT_FIELD(calendar, zone_offset, "ZONE_OFFSET");
TC3_GET_STATIC_INT_FIELD(calendar, dst_offset, "DST_OFFSET");
TC3_GET_STATIC_INT_FIELD(calendar, year, "YEAR");
TC3_GET_STATIC_INT_FIELD(calendar, month, "MONTH");
TC3_GET_STATIC_INT_FIELD(calendar, day_of_year, "DAY_OF_YEAR");
TC3_GET_STATIC_INT_FIELD(calendar, day_of_month, "DAY_OF_MONTH");
TC3_GET_STATIC_INT_FIELD(calendar, day_of_week, "DAY_OF_WEEK");
TC3_GET_STATIC_INT_FIELD(calendar, hour_of_day, "HOUR_OF_DAY");
TC3_GET_STATIC_INT_FIELD(calendar, minute, "MINUTE");
TC3_GET_STATIC_INT_FIELD(calendar, second, "SECOND");
TC3_GET_STATIC_INT_FIELD(calendar, millisecond, "MILLISECOND");
TC3_GET_STATIC_INT_FIELD(calendar, sunday, "SUNDAY");
TC3_GET_STATIC_INT_FIELD(calendar, monday, "MONDAY");
TC3_GET_STATIC_INT_FIELD(calendar, tuesday, "TUESDAY");
TC3_GET_STATIC_INT_FIELD(calendar, wednesday, "WEDNESDAY");
TC3_GET_STATIC_INT_FIELD(calendar, thursday, "THURSDAY");
TC3_GET_STATIC_INT_FIELD(calendar, friday, "FRIDAY");
TC3_GET_STATIC_INT_FIELD(calendar, saturday, "SATURDAY");
// TimeZone.
TC3_GET_CLASS(timezone, "java/util/TimeZone");
TC3_GET_STATIC_METHOD(timezone, get_timezone, "getTimeZone",
"(Ljava/lang/String;)Ljava/util/TimeZone;");
// URLEncoder.
TC3_GET_CLASS(urlencoder, "java/net/URLEncoder");
TC3_GET_STATIC_METHOD(
urlencoder, encode, "encode",
"(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
#ifdef __ANDROID__
// Context.
TC3_GET_CLASS(context, "android/content/Context");
TC3_GET_METHOD(context, get_package_name, "getPackageName",
"()Ljava/lang/String;");
TC3_GET_METHOD(context, get_system_service, "getSystemService",
"(Ljava/lang/String;)Ljava/lang/Object;");
// Uri.
TC3_GET_CLASS(uri, "android/net/Uri");
TC3_GET_STATIC_METHOD(uri, parse, "parse",
"(Ljava/lang/String;)Landroid/net/Uri;");
TC3_GET_METHOD(uri, get_scheme, "getScheme", "()Ljava/lang/String;");
// UserManager.
TC3_GET_OPTIONAL_CLASS(usermanager, "android/os/UserManager");
TC3_GET_OPTIONAL_METHOD(usermanager, get_user_restrictions,
"getUserRestrictions", "()Landroid/os/Bundle;");
// Bundle.
TC3_GET_CLASS(bundle, "android/os/Bundle");
TC3_GET_METHOD(bundle, get_boolean, "getBoolean", "(Ljava/lang/String;)Z");
#endif
return result;
}
#undef TC3_GET_STATIC_INT_FIELD
#undef TC3_GET_STATIC_OBJECT_FIELD
#undef TC3_GET_STATIC_METHOD
#undef TC3_GET_METHOD
#undef TC3_GET_CLASS
#undef TC3_CHECK_JNI_PTR
JNIEnv* JniCache::GetEnv() const {
void* env;
if (JNI_OK == jvm->GetEnv(&env, JNI_VERSION_1_4)) {
return reinterpret_cast<JNIEnv*>(env);
} else {
TC3_LOG(ERROR) << "JavaICU UniLib used on unattached thread";
return nullptr;
}
}
bool JniCache::ExceptionCheckAndClear() const {
JNIEnv* env = GetEnv();
TC3_CHECK(env != nullptr);
const bool result = env->ExceptionCheck();
if (result) {
env->ExceptionDescribe();
env->ExceptionClear();
}
return result;
}
ScopedLocalRef<jstring> JniCache::ConvertToJavaString(
const UnicodeText& text) const {
// Create java byte array.
JNIEnv* jenv = GetEnv();
const ScopedLocalRef<jbyteArray> text_java_utf8(
jenv->NewByteArray(text.size_bytes()), jenv);
if (!text_java_utf8) {
return nullptr;
}
jenv->SetByteArrayRegion(text_java_utf8.get(), 0, text.size_bytes(),
reinterpret_cast<const jbyte*>(text.data()));
// Create the string with a UTF-8 charset.
return ScopedLocalRef<jstring>(
reinterpret_cast<jstring>(
jenv->NewObject(string_class.get(), string_init_bytes_charset,
text_java_utf8.get(), string_utf8.get())),
jenv);
}
} // namespace libtextclassifier3