blob: 513dc41a828ff82ce0b9d4a15db2a859684b0bcb [file] [log] [blame]
The Android Open Source Projectadc854b2009-03-03 19:28:47 -08001/*
2 * Copyright (C) 2007 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
Elliott Hughes757a7942010-04-16 14:14:28 -070017#define LOG_TAG "NativeRegEx"
18
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080019#include <stdlib.h>
20#include <stdio.h>
21#include <string.h>
22
23#include "unicode/uregex.h"
24#include "unicode/utypes.h"
25#include "unicode/parseerr.h"
26
27#include <jni.h>
28#include <JNIHelp.h>
29
30static jchar EMPTY_STRING = 0;
31
32/**
33 * A data structure that ties together an ICU regular expression and the
34 * character data it refers to (but does not have a copy of), so we can
35 * manage memory properly.
36 */
Elliott Hughes80e88aa2009-09-09 18:15:23 -070037struct RegExData {
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080038 // A pointer to the ICU regular expression
39 URegularExpression* regex;
40 // A pointer to (a copy of) the input text that *we* manage
41 jchar* text;
Elliott Hughes80e88aa2009-09-09 18:15:23 -070042};
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080043
44static void throwPatternSyntaxException(JNIEnv* env, UErrorCode status,
45 jstring pattern, UParseError error)
46{
47 jclass clazz = env->FindClass("java/util/regex/PatternSyntaxException");
48 jmethodID method = env->GetMethodID(clazz, "<init>",
49 "(Ljava/lang/String;Ljava/lang/String;I)V");
50
51 jstring message = env->NewStringUTF(u_errorName(status));
52 jthrowable except = (jthrowable)(env->NewObject(clazz, method, message,
53 pattern, error.offset));
54 env->Throw(except);
55}
56
Elliott Hughesda4f31d2010-01-28 13:43:39 -080057static void throwRuntimeException(JNIEnv* env, UErrorCode status) {
58 jniThrowRuntimeException(env, u_errorName(status));
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080059}
60
Brian Carlstrom44e0e562010-05-06 23:44:16 -070061static void _close(JNIEnv*, jclass, RegExData* data)
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080062{
63 if (data->regex != NULL) {
64 uregex_close(data->regex);
65 }
66
Elliott Hughes80e88aa2009-09-09 18:15:23 -070067 if (data->text != &EMPTY_STRING) {
68 delete[] data->text;
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080069 }
70
71 free(data);
72}
73
74static RegExData* open(JNIEnv* env, jclass clazz, jstring pattern, jint flags)
75{
76 flags = flags | UREGEX_ERROR_ON_UNKNOWN_ESCAPES;
77
78 RegExData* data = (RegExData*)calloc(sizeof(RegExData), 1);
79
80 UErrorCode status = U_ZERO_ERROR;
81 UParseError error;
82 error.offset = -1;
83
The Android Open Source Projectadc854b2009-03-03 19:28:47 -080084 int patternLen = env->GetStringLength(pattern);
85 if (patternLen == 0) {
86 data->regex = uregex_open(&EMPTY_STRING, -1, flags, &error, &status);
87 } else {
88 jchar const * patternRaw = env->GetStringChars(pattern, NULL);
89 data->regex = uregex_open(patternRaw, patternLen, flags, &error,
90 &status);
91 env->ReleaseStringChars(pattern, patternRaw);
92 }
93
94 if (!U_SUCCESS(status)) {
95 _close(env, clazz, data);
96 throwPatternSyntaxException(env, status, pattern, error);
97 data = NULL;
98 }
99
100 return data;
101}
102
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700103static RegExData* _clone(JNIEnv* env, jclass, RegExData* data)
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800104{
105 UErrorCode status = U_ZERO_ERROR;
106
107 URegularExpression* clonedRegex = uregex_clone(data->regex, &status);
108 if (!U_SUCCESS(status)) {
109 throwRuntimeException(env, status);
110 }
111
112 RegExData* result = (RegExData*)calloc(sizeof(RegExData), 1);
113 result->regex = clonedRegex;
114
115 return result;
116}
117
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700118static void setText(JNIEnv* env, jclass, RegExData* data, jstring text)
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800119{
120 UErrorCode status = U_ZERO_ERROR;
121
122 uregex_setText(data->regex, &EMPTY_STRING, 0, &status);
123 if (!U_SUCCESS(status)) {
124 throwRuntimeException(env, status);
125 return;
126 }
127
Elliott Hughes80e88aa2009-09-09 18:15:23 -0700128 if (data->text != &EMPTY_STRING) {
129 delete[] data->text;
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800130 data->text = NULL;
131 }
132
133 int textLen = env->GetStringLength(text);
134 if (textLen == 0) {
135 data->text = &EMPTY_STRING;
136 } else {
Elliott Hughes80e88aa2009-09-09 18:15:23 -0700137 data->text = new jchar[textLen + 1];
138 env->GetStringRegion(text, 0, textLen, data->text);
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800139 data->text[textLen] = 0;
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800140 }
141
142 uregex_setText(data->regex, data->text, textLen, &status);
143 if (!U_SUCCESS(status)) {
144 throwRuntimeException(env, status);
145 }
146}
147
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700148static jboolean matches(JNIEnv* env, jclass, RegExData* data,
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800149 jint startIndex)
150{
151 UErrorCode status = U_ZERO_ERROR;
152
153 jboolean result = uregex_matches(data->regex, startIndex, &status);
154 if (!U_SUCCESS(status)) {
155 throwRuntimeException(env, status);
156 }
157
158 return result;
159}
160
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700161static jboolean lookingAt(JNIEnv* env, jclass, RegExData* data,
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800162 jint startIndex)
163{
164 UErrorCode status = U_ZERO_ERROR;
165
166 jboolean result = uregex_lookingAt(data->regex, startIndex, &status);
167 if (!U_SUCCESS(status)) {
168 throwRuntimeException(env, status);
169 }
170
171 return result;
172}
173
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700174static jboolean find(JNIEnv* env, jclass, RegExData* data,
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800175 jint startIndex)
176{
177 UErrorCode status = U_ZERO_ERROR;
178
179 jboolean result = uregex_find(data->regex, startIndex, &status);
180 if (!U_SUCCESS(status)) {
181 throwRuntimeException(env, status);
182 }
183
184 return result;
185}
186
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700187static jboolean findNext(JNIEnv* env, jclass, RegExData* data)
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800188{
189 UErrorCode status = U_ZERO_ERROR;
190
191 jboolean result = uregex_findNext(data->regex, &status);
192 if (!U_SUCCESS(status)) {
193 throwRuntimeException(env, status);
194 }
195
196 return result;
197}
198
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700199static jint groupCount(JNIEnv* env, jclass, RegExData* data)
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800200{
201 UErrorCode status = U_ZERO_ERROR;
202
203 jint result = uregex_groupCount(data->regex, &status);
204 if (!U_SUCCESS(status)) {
205 throwRuntimeException(env, status);
206 }
207
208 return result;
209}
210
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700211static void startEnd(JNIEnv* env, jclass, RegExData* data,
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800212 jintArray offsets)
213{
214 UErrorCode status = U_ZERO_ERROR;
215
216 jint * offsetsRaw = env->GetIntArrayElements(offsets, NULL);
217
218 int groupCount = uregex_groupCount(data->regex, &status);
219 for (int i = 0; i <= groupCount && U_SUCCESS(status); i++) {
220 offsetsRaw[2 * i + 0] = uregex_start(data->regex, i, &status);
221 offsetsRaw[2 * i + 1] = uregex_end(data->regex, i, &status);
222 }
223
224 env->ReleaseIntArrayElements(offsets, offsetsRaw, 0);
225
226 if (!U_SUCCESS(status)) {
227 throwRuntimeException(env, status);
228 }
229}
230
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700231static void setRegion(JNIEnv* env, jclass, RegExData* data, jint start,
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800232 jint end)
233{
234 UErrorCode status = U_ZERO_ERROR;
235 uregex_setRegion(data->regex, start, end, &status);
236 if (!U_SUCCESS(status)) {
237 throwRuntimeException(env, status);
238 }
239}
240
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700241static jint regionStart(JNIEnv* env, jclass, RegExData* data)
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800242{
243 UErrorCode status = U_ZERO_ERROR;
244 int result = uregex_regionStart(data->regex, &status);
245 if (!U_SUCCESS(status)) {
246 throwRuntimeException(env, status);
247 }
248 return result;
249}
250
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700251static jint regionEnd(JNIEnv* env, jclass, RegExData* data)
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800252{
253 UErrorCode status = U_ZERO_ERROR;
254 int result = uregex_regionEnd(data->regex, &status);
255 if (!U_SUCCESS(status)) {
256 throwRuntimeException(env, status);
257 }
258 return result;
259}
260
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700261static void useTransparentBounds(JNIEnv* env, jclass, RegExData* data,
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800262 jboolean value)
263{
264 UErrorCode status = U_ZERO_ERROR;
265 uregex_useTransparentBounds(data->regex, value, &status);
266 if (!U_SUCCESS(status)) {
267 throwRuntimeException(env, status);
268 }
269}
270
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700271static jboolean hasTransparentBounds(JNIEnv* env, jclass, RegExData* data)
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800272{
273 UErrorCode status = U_ZERO_ERROR;
274 jboolean result = uregex_hasTransparentBounds(data->regex, &status);
275 if (!U_SUCCESS(status)) {
276 throwRuntimeException(env, status);
277 }
278 return result;
279}
280
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700281static void useAnchoringBounds(JNIEnv* env, jclass, RegExData* data,
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800282 jboolean value)
283{
284 UErrorCode status = U_ZERO_ERROR;
285 uregex_useAnchoringBounds(data->regex, value, &status);
286 if (!U_SUCCESS(status)) {
287 throwRuntimeException(env, status);
288 }
289}
290
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700291static jboolean hasAnchoringBounds(JNIEnv* env, jclass, RegExData* data)
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800292{
293 UErrorCode status = U_ZERO_ERROR;
294 jboolean result = uregex_hasAnchoringBounds(data->regex, &status);
295 if (!U_SUCCESS(status)) {
296 throwRuntimeException(env, status);
297 }
298 return result;
299}
300
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700301static jboolean hitEnd(JNIEnv* env, jclass, RegExData* data)
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800302{
303 UErrorCode status = U_ZERO_ERROR;
304 jboolean result = uregex_hitEnd(data->regex, &status);
305 if (!U_SUCCESS(status)) {
306 throwRuntimeException(env, status);
307 }
308 return result;
309}
310
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700311static jboolean requireEnd(JNIEnv* env, jclass, RegExData* data)
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800312{
313 UErrorCode status = U_ZERO_ERROR;
314 jboolean result = uregex_requireEnd(data->regex, &status);
315 if (!U_SUCCESS(status)) {
316 throwRuntimeException(env, status);
317 }
318 return result;
319}
320
Brian Carlstrom44e0e562010-05-06 23:44:16 -0700321static void reset(JNIEnv* env, jclass, RegExData* data, jint position)
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800322{
323 UErrorCode status = U_ZERO_ERROR;
324 uregex_reset(data->regex, position, &status);
325 if (!U_SUCCESS(status)) {
326 throwRuntimeException(env, status);
327 }
328}
329
Elliott Hughes1698d142010-01-22 18:22:53 -0800330static JNINativeMethod gMethods[] = {
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800331 { "open", "(Ljava/lang/String;I)I", (void*)open },
332 { "clone", "(I)I", (void*)_clone },
333 { "close", "(I)V", (void*)_close },
334 { "setText", "(ILjava/lang/String;)V", (void*)setText },
335 { "matches", "(II)Z", (void*)matches },
336 { "lookingAt", "(II)Z", (void*)lookingAt },
337 { "find", "(II)Z", (void*)find },
338 { "findNext", "(I)Z", (void*)findNext },
339 { "groupCount", "(I)I", (void*)groupCount },
340 { "startEnd", "(I[I)V", (void*)startEnd },
341 { "setRegion", "(III)V", (void*)setRegion },
342 { "regionStart", "(I)I", (void*)regionStart },
343 { "regionEnd", "(I)I", (void*)regionEnd },
344 { "useTransparentBounds", "(IZ)V", (void*)useTransparentBounds },
345 { "hasTransparentBounds", "(I)Z", (void*)hasTransparentBounds },
346 { "useAnchoringBounds", "(IZ)V", (void*)useAnchoringBounds },
347 { "hasAnchoringBounds", "(I)Z", (void*)hasAnchoringBounds },
348 { "hitEnd", "(I)Z", (void*)hitEnd },
349 { "requireEnd", "(I)Z", (void*)requireEnd },
Elliott Hughes1698d142010-01-22 18:22:53 -0800350 { "reset", "(II)V", (void*)reset },
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800351};
Elliott Hughes1698d142010-01-22 18:22:53 -0800352int register_com_ibm_icu4jni_regex_NativeRegEx(JNIEnv* env) {
353 return jniRegisterNativeMethods(env, "com/ibm/icu4jni/regex/NativeRegEx",
354 gMethods, NELEM(gMethods));
The Android Open Source Projectadc854b2009-03-03 19:28:47 -0800355}