/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "AndroidSystemNatives.h" #include <stdlib.h> #include <stdio.h> #include <string.h> #include "unicode/uregex.h" #include "unicode/utypes.h" #include "unicode/parseerr.h" #include <jni.h> #include <JNIHelp.h> static jchar EMPTY_STRING = 0; /** * A data structure that ties together an ICU regular expression and the * character data it refers to (but does not have a copy of), so we can * manage memory properly. */ struct RegExData { // A pointer to the ICU regular expression URegularExpression* regex; // A pointer to (a copy of) the input text that *we* manage jchar* text; }; static void throwPatternSyntaxException(JNIEnv* env, UErrorCode status, jstring pattern, UParseError error) { jclass clazz = env->FindClass("java/util/regex/PatternSyntaxException"); jmethodID method = env->GetMethodID(clazz, "<init>", "(Ljava/lang/String;Ljava/lang/String;I)V"); jstring message = env->NewStringUTF(u_errorName(status)); jthrowable except = (jthrowable)(env->NewObject(clazz, method, message, pattern, error.offset)); env->Throw(except); } static void throwRuntimeException(JNIEnv* env, UErrorCode status) { jniThrowRuntimeException(env, u_errorName(status)); } static void _close(JNIEnv* env, jclass clazz, RegExData* data) { if (data->regex != NULL) { uregex_close(data->regex); } if (data->text != &EMPTY_STRING) { delete[] data->text; } free(data); } static RegExData* open(JNIEnv* env, jclass clazz, jstring pattern, jint flags) { flags = flags | UREGEX_ERROR_ON_UNKNOWN_ESCAPES; RegExData* data = (RegExData*)calloc(sizeof(RegExData), 1); UErrorCode status = U_ZERO_ERROR; UParseError error; error.offset = -1; jchar const * patternRaw; int patternLen = env->GetStringLength(pattern); if (patternLen == 0) { data->regex = uregex_open(&EMPTY_STRING, -1, flags, &error, &status); } else { jchar const * patternRaw = env->GetStringChars(pattern, NULL); data->regex = uregex_open(patternRaw, patternLen, flags, &error, &status); env->ReleaseStringChars(pattern, patternRaw); } if (!U_SUCCESS(status)) { _close(env, clazz, data); throwPatternSyntaxException(env, status, pattern, error); data = NULL; } return data; } static RegExData* _clone(JNIEnv* env, jclass clazz, RegExData* data) { UErrorCode status = U_ZERO_ERROR; URegularExpression* clonedRegex = uregex_clone(data->regex, &status); if (!U_SUCCESS(status)) { throwRuntimeException(env, status); } RegExData* result = (RegExData*)calloc(sizeof(RegExData), 1); result->regex = clonedRegex; return result; } static void setText(JNIEnv* env, jclass clazz, RegExData* data, jstring text) { UErrorCode status = U_ZERO_ERROR; uregex_setText(data->regex, &EMPTY_STRING, 0, &status); if (!U_SUCCESS(status)) { throwRuntimeException(env, status); return; } if (data->text != &EMPTY_STRING) { delete[] data->text; data->text = NULL; } int textLen = env->GetStringLength(text); if (textLen == 0) { data->text = &EMPTY_STRING; } else { data->text = new jchar[textLen + 1]; env->GetStringRegion(text, 0, textLen, data->text); data->text[textLen] = 0; } uregex_setText(data->regex, data->text, textLen, &status); if (!U_SUCCESS(status)) { throwRuntimeException(env, status); } } static jboolean matches(JNIEnv* env, jclass clazz, RegExData* data, jint startIndex) { UErrorCode status = U_ZERO_ERROR; jboolean result = uregex_matches(data->regex, startIndex, &status); if (!U_SUCCESS(status)) { throwRuntimeException(env, status); } return result; } static jboolean lookingAt(JNIEnv* env, jclass clazz, RegExData* data, jint startIndex) { UErrorCode status = U_ZERO_ERROR; jboolean result = uregex_lookingAt(data->regex, startIndex, &status); if (!U_SUCCESS(status)) { throwRuntimeException(env, status); } return result; } static jboolean find(JNIEnv* env, jclass clazz, RegExData* data, jint startIndex) { UErrorCode status = U_ZERO_ERROR; jboolean result = uregex_find(data->regex, startIndex, &status); if (!U_SUCCESS(status)) { throwRuntimeException(env, status); } return result; } static jboolean findNext(JNIEnv* env, jclass clazz, RegExData* data) { UErrorCode status = U_ZERO_ERROR; jboolean result = uregex_findNext(data->regex, &status); if (!U_SUCCESS(status)) { throwRuntimeException(env, status); } return result; } static jint groupCount(JNIEnv* env, jclass clazz, RegExData* data) { UErrorCode status = U_ZERO_ERROR; jint result = uregex_groupCount(data->regex, &status); if (!U_SUCCESS(status)) { throwRuntimeException(env, status); } return result; } static void startEnd(JNIEnv* env, jclass clazz, RegExData* data, jintArray offsets) { UErrorCode status = U_ZERO_ERROR; jint * offsetsRaw = env->GetIntArrayElements(offsets, NULL); int groupCount = uregex_groupCount(data->regex, &status); for (int i = 0; i <= groupCount && U_SUCCESS(status); i++) { offsetsRaw[2 * i + 0] = uregex_start(data->regex, i, &status); offsetsRaw[2 * i + 1] = uregex_end(data->regex, i, &status); } env->ReleaseIntArrayElements(offsets, offsetsRaw, 0); if (!U_SUCCESS(status)) { throwRuntimeException(env, status); } } static void setRegion(JNIEnv* env, jclass clazz, RegExData* data, jint start, jint end) { UErrorCode status = U_ZERO_ERROR; uregex_setRegion(data->regex, start, end, &status); if (!U_SUCCESS(status)) { throwRuntimeException(env, status); } } static jint regionStart(JNIEnv* env, jclass clazz, RegExData* data) { UErrorCode status = U_ZERO_ERROR; int result = uregex_regionStart(data->regex, &status); if (!U_SUCCESS(status)) { throwRuntimeException(env, status); } return result; } static jint regionEnd(JNIEnv* env, jclass clazz, RegExData* data) { UErrorCode status = U_ZERO_ERROR; int result = uregex_regionEnd(data->regex, &status); if (!U_SUCCESS(status)) { throwRuntimeException(env, status); } return result; } static void useTransparentBounds(JNIEnv* env, jclass clazz, RegExData* data, jboolean value) { UErrorCode status = U_ZERO_ERROR; uregex_useTransparentBounds(data->regex, value, &status); if (!U_SUCCESS(status)) { throwRuntimeException(env, status); } } static jboolean hasTransparentBounds(JNIEnv* env, jclass clazz, RegExData* data) { UErrorCode status = U_ZERO_ERROR; jboolean result = uregex_hasTransparentBounds(data->regex, &status); if (!U_SUCCESS(status)) { throwRuntimeException(env, status); } return result; } static void useAnchoringBounds(JNIEnv* env, jclass clazz, RegExData* data, jboolean value) { UErrorCode status = U_ZERO_ERROR; uregex_useAnchoringBounds(data->regex, value, &status); if (!U_SUCCESS(status)) { throwRuntimeException(env, status); } } static jboolean hasAnchoringBounds(JNIEnv* env, jclass clazz, RegExData* data) { UErrorCode status = U_ZERO_ERROR; jboolean result = uregex_hasAnchoringBounds(data->regex, &status); if (!U_SUCCESS(status)) { throwRuntimeException(env, status); } return result; } static jboolean hitEnd(JNIEnv* env, jclass clazz, RegExData* data) { UErrorCode status = U_ZERO_ERROR; jboolean result = uregex_hitEnd(data->regex, &status); if (!U_SUCCESS(status)) { throwRuntimeException(env, status); } return result; } static jboolean requireEnd(JNIEnv* env, jclass clazz, RegExData* data) { UErrorCode status = U_ZERO_ERROR; jboolean result = uregex_requireEnd(data->regex, &status); if (!U_SUCCESS(status)) { throwRuntimeException(env, status); } return result; } static void reset(JNIEnv* env, jclass clazz, RegExData* data, jint position) { UErrorCode status = U_ZERO_ERROR; uregex_reset(data->regex, position, &status); if (!U_SUCCESS(status)) { throwRuntimeException(env, status); } } static JNINativeMethod gMethods[] = { { "open", "(Ljava/lang/String;I)I", (void*)open }, { "clone", "(I)I", (void*)_clone }, { "close", "(I)V", (void*)_close }, { "setText", "(ILjava/lang/String;)V", (void*)setText }, { "matches", "(II)Z", (void*)matches }, { "lookingAt", "(II)Z", (void*)lookingAt }, { "find", "(II)Z", (void*)find }, { "findNext", "(I)Z", (void*)findNext }, { "groupCount", "(I)I", (void*)groupCount }, { "startEnd", "(I[I)V", (void*)startEnd }, { "setRegion", "(III)V", (void*)setRegion }, { "regionStart", "(I)I", (void*)regionStart }, { "regionEnd", "(I)I", (void*)regionEnd }, { "useTransparentBounds", "(IZ)V", (void*)useTransparentBounds }, { "hasTransparentBounds", "(I)Z", (void*)hasTransparentBounds }, { "useAnchoringBounds", "(IZ)V", (void*)useAnchoringBounds }, { "hasAnchoringBounds", "(I)Z", (void*)hasAnchoringBounds }, { "hitEnd", "(I)Z", (void*)hitEnd }, { "requireEnd", "(I)Z", (void*)requireEnd }, { "reset", "(II)V", (void*)reset }, }; int register_com_ibm_icu4jni_regex_NativeRegEx(JNIEnv* env) { return jniRegisterNativeMethods(env, "com/ibm/icu4jni/regex/NativeRegEx", gMethods, NELEM(gMethods)); }