Home | History | Annotate | Download | only in native
      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 "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 
     32 static icu::RegexMatcher* toRegexMatcher(jlong address) {
     33     return reinterpret_cast<icu::RegexMatcher*>(static_cast<uintptr_t>(address));
     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:
     44     MatcherAccessor(JNIEnv* env, jlong address, jstring javaInput, bool reset) {
     45         init(env, address);
     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 
     65     MatcherAccessor(JNIEnv* env, jlong address) {
     66         init(env, address);
     67     }
     68 
     69     ~MatcherAccessor() {
     70         utext_close(mUText);
     71         if (mJavaInput) {
     72             mEnv->ReleaseStringChars(mJavaInput, mChars);
     73         }
     74         maybeThrowIcuException(mEnv, "utext_close", mStatus);
     75     }
     76 
     77     icu::RegexMatcher* operator->() {
     78         return mMatcher;
     79     }
     80 
     81     UErrorCode& status() {
     82         return mStatus;
     83     }
     84 
     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:
     98     void init(JNIEnv* env, jlong address) {
     99         mEnv = env;
    100         mJavaInput = NULL;
    101         mMatcher = toRegexMatcher(address);
    102         mChars = NULL;
    103         mStatus = U_ZERO_ERROR;
    104         mUText = NULL;
    105     }
    106 
    107     JNIEnv* mEnv;
    108     jstring mJavaInput;
    109     icu::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 
    119 static void Matcher_closeImpl(JNIEnv*, jclass, jlong address) {
    120     delete toRegexMatcher(address);
    121 }
    122 
    123 static jint Matcher_findImpl(JNIEnv* env, jclass, jlong 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 
    132 static jint Matcher_findNextImpl(JNIEnv* env, jclass, jlong 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 
    144 static jint Matcher_groupCountImpl(JNIEnv* env, jclass, jlong addr) {
    145     MatcherAccessor matcher(env, addr);
    146     return matcher->groupCount();
    147 }
    148 
    149 static jint Matcher_hitEndImpl(JNIEnv* env, jclass, jlong addr) {
    150     MatcherAccessor matcher(env, addr);
    151     return matcher->hitEnd();
    152 }
    153 
    154 static jint Matcher_lookingAtImpl(JNIEnv* env, jclass, jlong 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 
    163 static jint Matcher_matchesImpl(JNIEnv* env, jclass, jlong 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 
    172 static jlong Matcher_openImpl(JNIEnv* env, jclass, jlong patternAddr) {
    173     icu::RegexPattern* pattern = reinterpret_cast<icu::RegexPattern*>(static_cast<uintptr_t>(patternAddr));
    174     UErrorCode status = U_ZERO_ERROR;
    175     icu::RegexMatcher* result = pattern->matcher(status);
    176     maybeThrowIcuException(env, "RegexPattern::matcher", status);
    177     return reinterpret_cast<uintptr_t>(result);
    178 }
    179 
    180 static jint Matcher_requireEndImpl(JNIEnv* env, jclass, jlong addr) {
    181     MatcherAccessor matcher(env, addr);
    182     return matcher->requireEnd();
    183 }
    184 
    185 static void Matcher_setInputImpl(JNIEnv* env, jclass, jlong addr, jstring javaText, jint start, jint end) {
    186     MatcherAccessor matcher(env, addr, javaText, true);
    187     matcher->region(start, end, matcher.status());
    188 }
    189 
    190 static void Matcher_useAnchoringBoundsImpl(JNIEnv* env, jclass, jlong addr, jboolean value) {
    191     MatcherAccessor matcher(env, addr);
    192     matcher->useAnchoringBounds(value);
    193 }
    194 
    195 static void Matcher_useTransparentBoundsImpl(JNIEnv* env, jclass, jlong addr, jboolean value) {
    196     MatcherAccessor matcher(env, addr);
    197     matcher->useTransparentBounds(value);
    198 }
    199 
    200 static JNINativeMethod gMethods[] = {
    201     NATIVE_METHOD(Matcher, closeImpl, "(J)V"),
    202     NATIVE_METHOD(Matcher, findImpl, "(JLjava/lang/String;I[I)Z"),
    203     NATIVE_METHOD(Matcher, findNextImpl, "(JLjava/lang/String;[I)Z"),
    204     NATIVE_METHOD(Matcher, groupCountImpl, "(J)I"),
    205     NATIVE_METHOD(Matcher, hitEndImpl, "(J)Z"),
    206     NATIVE_METHOD(Matcher, lookingAtImpl, "(JLjava/lang/String;[I)Z"),
    207     NATIVE_METHOD(Matcher, matchesImpl, "(JLjava/lang/String;[I)Z"),
    208     NATIVE_METHOD(Matcher, openImpl, "(J)J"),
    209     NATIVE_METHOD(Matcher, requireEndImpl, "(J)Z"),
    210     NATIVE_METHOD(Matcher, setInputImpl, "(JLjava/lang/String;II)V"),
    211     NATIVE_METHOD(Matcher, useAnchoringBoundsImpl, "(JZ)V"),
    212     NATIVE_METHOD(Matcher, useTransparentBoundsImpl, "(JZ)V"),
    213 };
    214 void register_java_util_regex_Matcher(JNIEnv* env) {
    215     jniRegisterNativeMethods(env, "java/util/regex/Matcher", gMethods, NELEM(gMethods));
    216 }
    217