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