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 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: 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 66 MatcherAccessor(JNIEnv* env, jlong address) { 67 init(env, address); 68 } 69 70 ~MatcherAccessor() { 71 utext_close(mUText); 72 if (mJavaInput) { 73 mEnv->ReleaseStringChars(mJavaInput, mChars); 74 } 75 maybeThrowIcuException(mEnv, "utext_close", mStatus); 76 } 77 78 RegexMatcher* operator->() { 79 return mMatcher; 80 } 81 82 UErrorCode& status() { 83 return mStatus; 84 } 85 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: 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 120 static void Matcher_closeImpl(JNIEnv*, jclass, jlong address) { 121 delete toRegexMatcher(address); 122 } 123 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 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 145 static jint Matcher_groupCountImpl(JNIEnv* env, jclass, jlong addr) { 146 MatcherAccessor matcher(env, addr); 147 return matcher->groupCount(); 148 } 149 150 static jint Matcher_hitEndImpl(JNIEnv* env, jclass, jlong addr) { 151 MatcherAccessor matcher(env, addr); 152 return matcher->hitEnd(); 153 } 154 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 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 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 181 static jint Matcher_requireEndImpl(JNIEnv* env, jclass, jlong addr) { 182 MatcherAccessor matcher(env, addr); 183 return matcher->requireEnd(); 184 } 185 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 191 static void Matcher_useAnchoringBoundsImpl(JNIEnv* env, jclass, jlong addr, jboolean value) { 192 MatcherAccessor matcher(env, addr); 193 matcher->useAnchoringBounds(value); 194 } 195 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 }; 215 void register_java_util_regex_Matcher(JNIEnv* env) { 216 jniRegisterNativeMethods(env, "java/util/regex/Matcher", gMethods, NELEM(gMethods)); 217 } 218