• 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 "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