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
JniCache(JavaVM * jvm)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
Create(JNIEnv * env)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
GetEnv() const252 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
ExceptionCheckAndClear() const262 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
ConvertToJavaString(const char * utf8_text,const int utf8_text_size_bytes) const273 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
ConvertToJavaString(StringPiece utf8_text) const294 ScopedLocalRef<jstring> JniCache::ConvertToJavaString(
295 StringPiece utf8_text) const {
296 return ConvertToJavaString(utf8_text.data(), utf8_text.size());
297 }
298
ConvertToJavaString(const UnicodeText & text) const299 ScopedLocalRef<jstring> JniCache::ConvertToJavaString(
300 const UnicodeText& text) const {
301 return ConvertToJavaString(text.data(), text.size_bytes());
302 }
303
304 } // namespace libtextclassifier3
305