1 /*
2 * Copyright (C) 2010 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 #define LOG_TAG "Matcher"
18
19 #include <stdlib.h>
20
21 #include "JNIHelp.h"
22 #include "JniConstants.h"
23 #include "JniException.h"
24 #include "ScopedPrimitiveArray.h"
25 #include "UniquePtr.h"
26 #include "jni.h"
27 #include "unicode/parseerr.h"
28 #include "unicode/regex.h"
29
30 // ICU documentation: http://icu-project.org/apiref/icu4c/classRegexMatcher.html
31
toRegexMatcher(jint addr)32 static RegexMatcher* toRegexMatcher(jint addr) {
33 return reinterpret_cast<RegexMatcher*>(static_cast<uintptr_t>(addr));
34 }
35
36 /**
37 * We use ICU4C's RegexMatcher class, but our input is on the Java heap and potentially moving
38 * around between calls. This wrapper class ensures that our RegexMatcher is always pointing at
39 * the current location of the char[]. Earlier versions of Android simply copied the data to the
40 * native heap, but that's wasteful and hides allocations from the garbage collector.
41 */
42 class MatcherAccessor {
43 public:
MatcherAccessor(JNIEnv * env,jint addr,jstring javaInput,bool reset)44 MatcherAccessor(JNIEnv* env, jint addr, jstring javaInput, bool reset) {
45 init(env, addr);
46
47 mJavaInput = javaInput;
48 mChars = env->GetStringChars(mJavaInput, NULL);
49 if (mChars == NULL) {
50 return;
51 }
52
53 mUText = utext_openUChars(NULL, mChars, env->GetStringLength(mJavaInput), &mStatus);
54 if (mUText == NULL) {
55 return;
56 }
57
58 if (reset) {
59 mMatcher->reset(mUText);
60 } else {
61 mMatcher->refreshInputText(mUText, mStatus);
62 }
63 }
64
MatcherAccessor(JNIEnv * env,jint addr)65 MatcherAccessor(JNIEnv* env, jint addr) {
66 init(env, addr);
67 }
68
~MatcherAccessor()69 ~MatcherAccessor() {
70 utext_close(mUText);
71 if (mJavaInput) {
72 mEnv->ReleaseStringChars(mJavaInput, mChars);
73 }
74 maybeThrowIcuException(mEnv, mStatus);
75 }
76
operator ->()77 RegexMatcher* operator->() {
78 return mMatcher;
79 }
80
status()81 UErrorCode& status() {
82 return mStatus;
83 }
84
updateOffsets(jintArray javaOffsets)85 void updateOffsets(jintArray javaOffsets) {
86 ScopedIntArrayRW offsets(mEnv, javaOffsets);
87 if (offsets.get() == NULL) {
88 return;
89 }
90
91 for (size_t i = 0, groupCount = mMatcher->groupCount(); i <= groupCount; ++i) {
92 offsets[2*i + 0] = mMatcher->start(i, mStatus);
93 offsets[2*i + 1] = mMatcher->end(i, mStatus);
94 }
95 }
96
97 private:
init(JNIEnv * env,jint addr)98 void init(JNIEnv* env, jint addr) {
99 mEnv = env;
100 mJavaInput = NULL;
101 mMatcher = toRegexMatcher(addr);
102 mChars = NULL;
103 mStatus = U_ZERO_ERROR;
104 mUText = NULL;
105 }
106
107 JNIEnv* mEnv;
108 jstring mJavaInput;
109 RegexMatcher* mMatcher;
110 const jchar* mChars;
111 UErrorCode mStatus;
112 UText* mUText;
113
114 // Disallow copy and assignment.
115 MatcherAccessor(const MatcherAccessor&);
116 void operator=(const MatcherAccessor&);
117 };
118
Matcher_closeImpl(JNIEnv *,jclass,jint addr)119 static void Matcher_closeImpl(JNIEnv*, jclass, jint addr) {
120 delete toRegexMatcher(addr);
121 }
122
Matcher_findImpl(JNIEnv * env,jclass,jint addr,jstring javaText,jint startIndex,jintArray offsets)123 static jint Matcher_findImpl(JNIEnv* env, jclass, jint addr, jstring javaText, jint startIndex, jintArray offsets) {
124 MatcherAccessor matcher(env, addr, javaText, false);
125 UBool result = matcher->find(startIndex, matcher.status());
126 if (result) {
127 matcher.updateOffsets(offsets);
128 }
129 return result;
130 }
131
Matcher_findNextImpl(JNIEnv * env,jclass,jint addr,jstring javaText,jintArray offsets)132 static jint Matcher_findNextImpl(JNIEnv* env, jclass, jint addr, jstring javaText, jintArray offsets) {
133 MatcherAccessor matcher(env, addr, javaText, false);
134 if (matcher.status() != U_ZERO_ERROR) {
135 return -1;
136 }
137 UBool result = matcher->find();
138 if (result) {
139 matcher.updateOffsets(offsets);
140 }
141 return result;
142 }
143
Matcher_groupCountImpl(JNIEnv * env,jclass,jint addr)144 static jint Matcher_groupCountImpl(JNIEnv* env, jclass, jint addr) {
145 MatcherAccessor matcher(env, addr);
146 return matcher->groupCount();
147 }
148
Matcher_hitEndImpl(JNIEnv * env,jclass,jint addr)149 static jint Matcher_hitEndImpl(JNIEnv* env, jclass, jint addr) {
150 MatcherAccessor matcher(env, addr);
151 return matcher->hitEnd();
152 }
153
Matcher_lookingAtImpl(JNIEnv * env,jclass,jint addr,jstring javaText,jintArray offsets)154 static jint Matcher_lookingAtImpl(JNIEnv* env, jclass, jint addr, jstring javaText, jintArray offsets) {
155 MatcherAccessor matcher(env, addr, javaText, false);
156 UBool result = matcher->lookingAt(matcher.status());
157 if (result) {
158 matcher.updateOffsets(offsets);
159 }
160 return result;
161 }
162
Matcher_matchesImpl(JNIEnv * env,jclass,jint addr,jstring javaText,jintArray offsets)163 static jint Matcher_matchesImpl(JNIEnv* env, jclass, jint addr, jstring javaText, jintArray offsets) {
164 MatcherAccessor matcher(env, addr, javaText, false);
165 UBool result = matcher->matches(matcher.status());
166 if (result) {
167 matcher.updateOffsets(offsets);
168 }
169 return result;
170 }
171
Matcher_openImpl(JNIEnv * env,jclass,jint patternAddr)172 static jint Matcher_openImpl(JNIEnv* env, jclass, jint patternAddr) {
173 RegexPattern* pattern = reinterpret_cast<RegexPattern*>(static_cast<uintptr_t>(patternAddr));
174 UErrorCode status = U_ZERO_ERROR;
175 RegexMatcher* result = pattern->matcher(status);
176 maybeThrowIcuException(env, status);
177 return static_cast<jint>(reinterpret_cast<uintptr_t>(result));
178 }
179
Matcher_requireEndImpl(JNIEnv * env,jclass,jint addr)180 static jint Matcher_requireEndImpl(JNIEnv* env, jclass, jint addr) {
181 MatcherAccessor matcher(env, addr);
182 return matcher->requireEnd();
183 }
184
Matcher_setInputImpl(JNIEnv * env,jclass,jint addr,jstring javaText,jint start,jint end)185 static void Matcher_setInputImpl(JNIEnv* env, jclass, jint addr, jstring javaText, jint start, jint end) {
186 MatcherAccessor matcher(env, addr, javaText, true);
187 matcher->region(start, end, matcher.status());
188 }
189
Matcher_useAnchoringBoundsImpl(JNIEnv * env,jclass,jint addr,jboolean value)190 static void Matcher_useAnchoringBoundsImpl(JNIEnv* env, jclass, jint addr, jboolean value) {
191 MatcherAccessor matcher(env, addr);
192 matcher->useAnchoringBounds(value);
193 }
194
Matcher_useTransparentBoundsImpl(JNIEnv * env,jclass,jint addr,jboolean value)195 static void Matcher_useTransparentBoundsImpl(JNIEnv* env, jclass, jint addr, jboolean value) {
196 MatcherAccessor matcher(env, addr);
197 matcher->useTransparentBounds(value);
198 }
199
200 static JNINativeMethod gMethods[] = {
201 NATIVE_METHOD(Matcher, closeImpl, "(I)V"),
202 NATIVE_METHOD(Matcher, findImpl, "(ILjava/lang/String;I[I)Z"),
203 NATIVE_METHOD(Matcher, findNextImpl, "(ILjava/lang/String;[I)Z"),
204 NATIVE_METHOD(Matcher, groupCountImpl, "(I)I"),
205 NATIVE_METHOD(Matcher, hitEndImpl, "(I)Z"),
206 NATIVE_METHOD(Matcher, lookingAtImpl, "(ILjava/lang/String;[I)Z"),
207 NATIVE_METHOD(Matcher, matchesImpl, "(ILjava/lang/String;[I)Z"),
208 NATIVE_METHOD(Matcher, openImpl, "(I)I"),
209 NATIVE_METHOD(Matcher, requireEndImpl, "(I)Z"),
210 NATIVE_METHOD(Matcher, setInputImpl, "(ILjava/lang/String;II)V"),
211 NATIVE_METHOD(Matcher, useAnchoringBoundsImpl, "(IZ)V"),
212 NATIVE_METHOD(Matcher, useTransparentBoundsImpl, "(IZ)V"),
213 };
register_java_util_regex_Matcher(JNIEnv * env)214 void register_java_util_regex_Matcher(JNIEnv* env) {
215 jniRegisterNativeMethods(env, "java/util/regex/Matcher", gMethods, NELEM(gMethods));
216 }
217