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