• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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