Tony Mak | 6c4cc67 | 2018-09-17 11:48:50 +0100 | [diff] [blame^] | 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 | |
| 36 | // The macros below are intended to reduce the boilerplate in Create and avoid |
| 37 | // easily introduced copy/paste errors. |
| 38 | #define TC3_CHECK_JNI_PTR(PTR) \ |
| 39 | TC3_DCHECK(PTR); \ |
| 40 | if (!(PTR)) return nullptr; |
| 41 | |
| 42 | #define TC3_GET_CLASS(FIELD, NAME) \ |
| 43 | result->FIELD##_class = MakeGlobalRef(env->FindClass(NAME), env, jvm); \ |
| 44 | TC3_CHECK_JNI_PTR(result->FIELD##_class) |
| 45 | |
| 46 | #define TC3_GET_METHOD(CLASS, FIELD, NAME, SIGNATURE) \ |
| 47 | result->CLASS##_##FIELD = \ |
| 48 | env->GetMethodID(result->CLASS##_class.get(), NAME, SIGNATURE); \ |
| 49 | TC3_CHECK_JNI_PTR(result->CLASS##_##FIELD) |
| 50 | |
| 51 | #define TC3_GET_OPTIONAL_STATIC_METHOD(CLASS, FIELD, NAME, SIGNATURE) \ |
| 52 | result->CLASS##_##FIELD = \ |
| 53 | env->GetStaticMethodID(result->CLASS##_class.get(), NAME, SIGNATURE); \ |
| 54 | env->ExceptionClear(); |
| 55 | |
| 56 | #define TC3_GET_STATIC_METHOD(CLASS, FIELD, NAME, SIGNATURE) \ |
| 57 | result->CLASS##_##FIELD = \ |
| 58 | env->GetStaticMethodID(result->CLASS##_class.get(), NAME, SIGNATURE); \ |
| 59 | TC3_CHECK_JNI_PTR(result->CLASS##_##FIELD) |
| 60 | |
| 61 | #define TC3_GET_STATIC_OBJECT_FIELD(CLASS, FIELD, NAME, SIGNATURE) \ |
| 62 | const jfieldID CLASS##_##FIELD##_field = \ |
| 63 | env->GetStaticFieldID(result->CLASS##_class.get(), NAME, SIGNATURE); \ |
| 64 | TC3_CHECK_JNI_PTR(CLASS##_##FIELD##_field) \ |
| 65 | result->CLASS##_##FIELD = \ |
| 66 | MakeGlobalRef(env->GetStaticObjectField(result->CLASS##_class.get(), \ |
| 67 | CLASS##_##FIELD##_field), \ |
| 68 | env, jvm); \ |
| 69 | TC3_CHECK_JNI_PTR(result->CLASS##_##FIELD) |
| 70 | |
| 71 | #define TC3_GET_STATIC_INT_FIELD(CLASS, FIELD, NAME) \ |
| 72 | const jfieldID CLASS##_##FIELD##_field = \ |
| 73 | env->GetStaticFieldID(result->CLASS##_class.get(), NAME, "I"); \ |
| 74 | TC3_CHECK_JNI_PTR(CLASS##_##FIELD##_field) \ |
| 75 | result->CLASS##_##FIELD = env->GetStaticIntField( \ |
| 76 | result->CLASS##_class.get(), CLASS##_##FIELD##_field); \ |
| 77 | TC3_CHECK_JNI_PTR(result->CLASS##_##FIELD) |
| 78 | |
| 79 | std::unique_ptr<JniCache> JniCache::Create(JNIEnv* env) { |
| 80 | if (env == nullptr) { |
| 81 | return nullptr; |
| 82 | } |
| 83 | JavaVM* jvm = nullptr; |
| 84 | if (JNI_OK != env->GetJavaVM(&jvm) || jvm == nullptr) { |
| 85 | return nullptr; |
| 86 | } |
| 87 | std::unique_ptr<JniCache> result(new JniCache(jvm)); |
| 88 | |
| 89 | // String |
| 90 | TC3_GET_CLASS(string, "java/lang/String"); |
| 91 | TC3_GET_METHOD(string, init_bytes_charset, "<init>", |
| 92 | "([BLjava/lang/String;)V"); |
| 93 | TC3_GET_METHOD(string, code_point_count, "codePointCount", "(II)I"); |
| 94 | TC3_GET_METHOD(string, length, "length", "()I"); |
| 95 | result->string_utf8 = MakeGlobalRef(env->NewStringUTF("UTF-8"), env, jvm); |
| 96 | TC3_CHECK_JNI_PTR(result->string_utf8) |
| 97 | |
| 98 | // Pattern |
| 99 | TC3_GET_CLASS(pattern, "java/util/regex/Pattern"); |
| 100 | TC3_GET_STATIC_METHOD(pattern, compile, "compile", |
| 101 | "(Ljava/lang/String;)Ljava/util/regex/Pattern;"); |
| 102 | TC3_GET_METHOD(pattern, matcher, "matcher", |
| 103 | "(Ljava/lang/CharSequence;)Ljava/util/regex/Matcher;"); |
| 104 | |
| 105 | // Matcher |
| 106 | TC3_GET_CLASS(matcher, "java/util/regex/Matcher"); |
| 107 | TC3_GET_METHOD(matcher, matches, "matches", "()Z"); |
| 108 | TC3_GET_METHOD(matcher, find, "find", "()Z"); |
| 109 | TC3_GET_METHOD(matcher, reset, "reset", "()Ljava/util/regex/Matcher;"); |
| 110 | TC3_GET_METHOD(matcher, start_idx, "start", "(I)I"); |
| 111 | TC3_GET_METHOD(matcher, end_idx, "end", "(I)I"); |
| 112 | TC3_GET_METHOD(matcher, group, "group", "()Ljava/lang/String;"); |
| 113 | TC3_GET_METHOD(matcher, group_idx, "group", "(I)Ljava/lang/String;"); |
| 114 | |
| 115 | // Locale |
| 116 | TC3_GET_CLASS(locale, "java/util/Locale"); |
| 117 | TC3_GET_STATIC_OBJECT_FIELD(locale, us, "US", "Ljava/util/Locale;"); |
| 118 | TC3_GET_METHOD(locale, init_string, "<init>", "(Ljava/lang/String;)V"); |
| 119 | TC3_GET_OPTIONAL_STATIC_METHOD(locale, for_language_tag, "forLanguageTag", |
| 120 | "(Ljava/lang/String;)Ljava/util/Locale;"); |
| 121 | |
| 122 | // BreakIterator |
| 123 | TC3_GET_CLASS(breakiterator, "java/text/BreakIterator"); |
| 124 | TC3_GET_STATIC_METHOD(breakiterator, getwordinstance, "getWordInstance", |
| 125 | "(Ljava/util/Locale;)Ljava/text/BreakIterator;"); |
| 126 | TC3_GET_METHOD(breakiterator, settext, "setText", "(Ljava/lang/String;)V"); |
| 127 | TC3_GET_METHOD(breakiterator, next, "next", "()I"); |
| 128 | |
| 129 | // Integer |
| 130 | TC3_GET_CLASS(integer, "java/lang/Integer"); |
| 131 | TC3_GET_STATIC_METHOD(integer, parse_int, "parseInt", |
| 132 | "(Ljava/lang/String;)I"); |
| 133 | |
| 134 | // Calendar. |
| 135 | TC3_GET_CLASS(calendar, "java/util/Calendar"); |
| 136 | TC3_GET_STATIC_METHOD( |
| 137 | calendar, get_instance, "getInstance", |
| 138 | "(Ljava/util/TimeZone;Ljava/util/Locale;)Ljava/util/Calendar;"); |
| 139 | TC3_GET_METHOD(calendar, get_first_day_of_week, "getFirstDayOfWeek", "()I"); |
| 140 | TC3_GET_METHOD(calendar, get_time_in_millis, "getTimeInMillis", "()J"); |
| 141 | TC3_GET_METHOD(calendar, set_time_in_millis, "setTimeInMillis", "(J)V"); |
| 142 | TC3_GET_METHOD(calendar, add, "add", "(II)V"); |
| 143 | TC3_GET_METHOD(calendar, get, "get", "(I)I"); |
| 144 | TC3_GET_METHOD(calendar, set, "set", "(II)V"); |
| 145 | TC3_GET_STATIC_INT_FIELD(calendar, zone_offset, "ZONE_OFFSET"); |
| 146 | TC3_GET_STATIC_INT_FIELD(calendar, dst_offset, "DST_OFFSET"); |
| 147 | TC3_GET_STATIC_INT_FIELD(calendar, year, "YEAR"); |
| 148 | TC3_GET_STATIC_INT_FIELD(calendar, month, "MONTH"); |
| 149 | TC3_GET_STATIC_INT_FIELD(calendar, day_of_year, "DAY_OF_YEAR"); |
| 150 | TC3_GET_STATIC_INT_FIELD(calendar, day_of_month, "DAY_OF_MONTH"); |
| 151 | TC3_GET_STATIC_INT_FIELD(calendar, day_of_week, "DAY_OF_WEEK"); |
| 152 | TC3_GET_STATIC_INT_FIELD(calendar, hour_of_day, "HOUR_OF_DAY"); |
| 153 | TC3_GET_STATIC_INT_FIELD(calendar, minute, "MINUTE"); |
| 154 | TC3_GET_STATIC_INT_FIELD(calendar, second, "SECOND"); |
| 155 | TC3_GET_STATIC_INT_FIELD(calendar, millisecond, "MILLISECOND"); |
| 156 | TC3_GET_STATIC_INT_FIELD(calendar, sunday, "SUNDAY"); |
| 157 | TC3_GET_STATIC_INT_FIELD(calendar, monday, "MONDAY"); |
| 158 | TC3_GET_STATIC_INT_FIELD(calendar, tuesday, "TUESDAY"); |
| 159 | TC3_GET_STATIC_INT_FIELD(calendar, wednesday, "WEDNESDAY"); |
| 160 | TC3_GET_STATIC_INT_FIELD(calendar, thursday, "THURSDAY"); |
| 161 | TC3_GET_STATIC_INT_FIELD(calendar, friday, "FRIDAY"); |
| 162 | TC3_GET_STATIC_INT_FIELD(calendar, saturday, "SATURDAY"); |
| 163 | |
| 164 | // TimeZone. |
| 165 | TC3_GET_CLASS(timezone, "java/util/TimeZone"); |
| 166 | TC3_GET_STATIC_METHOD(timezone, get_timezone, "getTimeZone", |
| 167 | "(Ljava/lang/String;)Ljava/util/TimeZone;"); |
| 168 | |
| 169 | return result; |
| 170 | } |
| 171 | |
| 172 | #undef TC3_GET_STATIC_INT_FIELD |
| 173 | #undef TC3_GET_STATIC_OBJECT_FIELD |
| 174 | #undef TC3_GET_STATIC_METHOD |
| 175 | #undef TC3_GET_METHOD |
| 176 | #undef TC3_GET_CLASS |
| 177 | #undef TC3_CHECK_JNI_PTR |
| 178 | |
| 179 | JNIEnv* JniCache::GetEnv() const { |
| 180 | void* env; |
| 181 | if (JNI_OK == jvm->GetEnv(&env, JNI_VERSION_1_4)) { |
| 182 | return reinterpret_cast<JNIEnv*>(env); |
| 183 | } else { |
| 184 | TC3_LOG(ERROR) << "JavaICU UniLib used on unattached thread"; |
| 185 | return nullptr; |
| 186 | } |
| 187 | } |
| 188 | |
| 189 | bool JniCache::ExceptionCheckAndClear() const { |
| 190 | JNIEnv* env = GetEnv(); |
| 191 | TC3_CHECK(env != nullptr); |
| 192 | const bool result = env->ExceptionCheck(); |
| 193 | if (result) { |
| 194 | env->ExceptionDescribe(); |
| 195 | env->ExceptionClear(); |
| 196 | } |
| 197 | return result; |
| 198 | } |
| 199 | |
| 200 | ScopedLocalRef<jstring> JniCache::ConvertToJavaString( |
| 201 | const UnicodeText& text) const { |
| 202 | // Create java byte array. |
| 203 | JNIEnv* jenv = GetEnv(); |
| 204 | const ScopedLocalRef<jbyteArray> text_java_utf8( |
| 205 | jenv->NewByteArray(text.size_bytes()), jenv); |
| 206 | if (!text_java_utf8) { |
| 207 | return nullptr; |
| 208 | } |
| 209 | |
| 210 | jenv->SetByteArrayRegion(text_java_utf8.get(), 0, text.size_bytes(), |
| 211 | reinterpret_cast<const jbyte*>(text.data())); |
| 212 | |
| 213 | // Create the string with a UTF-8 charset. |
| 214 | return ScopedLocalRef<jstring>( |
| 215 | reinterpret_cast<jstring>( |
| 216 | jenv->NewObject(string_class.get(), string_init_bytes_charset, |
| 217 | text_java_utf8.get(), string_utf8.get())), |
| 218 | jenv); |
| 219 | } |
| 220 | |
| 221 | } // namespace libtextclassifier3 |