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