1 /* 2 * Copyright (C) 2006 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 "NativeBreakIterator" 18 19 #include "IcuUtilities.h" 20 #include "JNIHelp.h" 21 #include "JniConstants.h" 22 #include "JniException.h" 23 #include "ScopedIcuLocale.h" 24 #include "ScopedUtfChars.h" 25 #include "unicode/brkiter.h" 26 #include "unicode/putil.h" 27 #include <stdlib.h> 28 29 // ICU documentation: http://icu-project.org/apiref/icu4c/classBreakIterator.html 30 31 static BreakIterator* toBreakIterator(jlong address) { 32 return reinterpret_cast<BreakIterator*>(static_cast<uintptr_t>(address)); 33 } 34 35 /** 36 * We use ICU4C's BreakIterator class, but our input is on the Java heap and potentially moving 37 * around between calls. This wrapper class ensures that our RegexMatcher is always pointing at 38 * the current location of the char[]. Earlier versions of Android simply copied the data to the 39 * native heap, but that's wasteful and hides allocations from the garbage collector. 40 */ 41 class BreakIteratorAccessor { 42 public: 43 BreakIteratorAccessor(JNIEnv* env, jlong address, jstring javaInput, bool reset) { 44 init(env, address); 45 mJavaInput = javaInput; 46 47 if (mJavaInput == NULL) { 48 return; 49 } 50 51 mChars = env->GetStringChars(mJavaInput, NULL); 52 if (mChars == NULL) { 53 return; 54 } 55 56 mUText = utext_openUChars(NULL, mChars, env->GetStringLength(mJavaInput), &mStatus); 57 if (mUText == NULL) { 58 return; 59 } 60 61 if (reset) { 62 mBreakIterator->setText(mUText, mStatus); 63 } else { 64 mBreakIterator->refreshInputText(mUText, mStatus); 65 } 66 } 67 68 BreakIteratorAccessor(JNIEnv* env, jlong address) { 69 init(env, address); 70 } 71 72 ~BreakIteratorAccessor() { 73 utext_close(mUText); 74 if (mJavaInput) { 75 mEnv->ReleaseStringChars(mJavaInput, mChars); 76 } 77 maybeThrowIcuException(mEnv, "utext_close", mStatus); 78 } 79 80 BreakIterator* operator->() { 81 return mBreakIterator; 82 } 83 84 UErrorCode& status() { 85 return mStatus; 86 } 87 88 private: 89 void init(JNIEnv* env, jlong address) { 90 mEnv = env; 91 mJavaInput = NULL; 92 mBreakIterator = toBreakIterator(address); 93 mChars = NULL; 94 mStatus = U_ZERO_ERROR; 95 mUText = NULL; 96 } 97 98 JNIEnv* mEnv; 99 jstring mJavaInput; 100 BreakIterator* mBreakIterator; 101 const jchar* mChars; 102 UErrorCode mStatus; 103 UText* mUText; 104 105 // Disallow copy and assignment. 106 BreakIteratorAccessor(const BreakIteratorAccessor&); 107 void operator=(const BreakIteratorAccessor&); 108 }; 109 110 #define MAKE_BREAK_ITERATOR_INSTANCE(F) \ 111 ScopedIcuLocale icuLocale(env, javaLocaleName); \ 112 if (!icuLocale.valid()) { \ 113 return 0; \ 114 } \ 115 UErrorCode status = U_ZERO_ERROR; \ 116 BreakIterator* it = F(icuLocale.locale(), status); \ 117 if (maybeThrowIcuException(env, "ubrk_open", status)) { \ 118 return 0; \ 119 } \ 120 return reinterpret_cast<uintptr_t>(it) 121 122 static jlong NativeBreakIterator_cloneImpl(JNIEnv* env, jclass, jlong address) { 123 BreakIteratorAccessor it(env, address); 124 return reinterpret_cast<uintptr_t>(it->clone()); 125 } 126 127 static void NativeBreakIterator_closeImpl(JNIEnv*, jclass, jlong address) { 128 delete toBreakIterator(address); 129 } 130 131 static jint NativeBreakIterator_currentImpl(JNIEnv* env, jclass, jlong address, jstring javaInput) { 132 BreakIteratorAccessor it(env, address, javaInput, false); 133 return it->current(); 134 } 135 136 static jint NativeBreakIterator_firstImpl(JNIEnv* env, jclass, jlong address, jstring javaInput) { 137 BreakIteratorAccessor it(env, address, javaInput, false); 138 return it->first(); 139 } 140 141 static jint NativeBreakIterator_followingImpl(JNIEnv* env, jclass, jlong address, jstring javaInput, jint offset) { 142 BreakIteratorAccessor it(env, address, javaInput, false); 143 return it->following(offset); 144 } 145 146 static jlong NativeBreakIterator_getCharacterInstanceImpl(JNIEnv* env, jclass, jstring javaLocaleName) { 147 MAKE_BREAK_ITERATOR_INSTANCE(BreakIterator::createCharacterInstance); 148 } 149 150 static jlong NativeBreakIterator_getLineInstanceImpl(JNIEnv* env, jclass, jstring javaLocaleName) { 151 MAKE_BREAK_ITERATOR_INSTANCE(BreakIterator::createLineInstance); 152 } 153 154 static jlong NativeBreakIterator_getSentenceInstanceImpl(JNIEnv* env, jclass, jstring javaLocaleName) { 155 MAKE_BREAK_ITERATOR_INSTANCE(BreakIterator::createSentenceInstance); 156 } 157 158 static jlong NativeBreakIterator_getWordInstanceImpl(JNIEnv* env, jclass, jstring javaLocaleName) { 159 MAKE_BREAK_ITERATOR_INSTANCE(BreakIterator::createWordInstance); 160 } 161 162 static jboolean NativeBreakIterator_isBoundaryImpl(JNIEnv* env, jclass, jlong address, jstring javaInput, jint offset) { 163 BreakIteratorAccessor it(env, address, javaInput, false); 164 return it->isBoundary(offset); 165 } 166 167 static jint NativeBreakIterator_lastImpl(JNIEnv* env, jclass, jlong address, jstring javaInput) { 168 BreakIteratorAccessor it(env, address, javaInput, false); 169 return it->last(); 170 } 171 172 static jint NativeBreakIterator_nextImpl(JNIEnv* env, jclass, jlong address, jstring javaInput, jint n) { 173 BreakIteratorAccessor it(env, address, javaInput, false); 174 if (n < 0) { 175 while (n++ < -1) { 176 it->previous(); 177 } 178 return it->previous(); 179 } else if (n == 0) { 180 return it->current(); 181 } else { 182 while (n-- > 1) { 183 it->next(); 184 } 185 return it->next(); 186 } 187 return -1; 188 } 189 190 static jint NativeBreakIterator_precedingImpl(JNIEnv* env, jclass, jlong address, jstring javaInput, jint offset) { 191 BreakIteratorAccessor it(env, address, javaInput, false); 192 return it->preceding(offset); 193 } 194 195 static jint NativeBreakIterator_previousImpl(JNIEnv* env, jclass, jlong address, jstring javaInput) { 196 BreakIteratorAccessor it(env, address, javaInput, false); 197 return it->previous(); 198 } 199 200 static void NativeBreakIterator_setTextImpl(JNIEnv* env, jclass, jlong address, jstring javaInput) { 201 BreakIteratorAccessor it(env, address, javaInput, true); 202 } 203 204 static JNINativeMethod gMethods[] = { 205 NATIVE_METHOD(NativeBreakIterator, cloneImpl, "(J)J"), 206 NATIVE_METHOD(NativeBreakIterator, closeImpl, "(J)V"), 207 NATIVE_METHOD(NativeBreakIterator, currentImpl, "(JLjava/lang/String;)I"), 208 NATIVE_METHOD(NativeBreakIterator, firstImpl, "(JLjava/lang/String;)I"), 209 NATIVE_METHOD(NativeBreakIterator, followingImpl, "(JLjava/lang/String;I)I"), 210 NATIVE_METHOD(NativeBreakIterator, getCharacterInstanceImpl, "(Ljava/lang/String;)J"), 211 NATIVE_METHOD(NativeBreakIterator, getLineInstanceImpl, "(Ljava/lang/String;)J"), 212 NATIVE_METHOD(NativeBreakIterator, getSentenceInstanceImpl, "(Ljava/lang/String;)J"), 213 NATIVE_METHOD(NativeBreakIterator, getWordInstanceImpl, "(Ljava/lang/String;)J"), 214 NATIVE_METHOD(NativeBreakIterator, isBoundaryImpl, "(JLjava/lang/String;I)Z"), 215 NATIVE_METHOD(NativeBreakIterator, lastImpl, "(JLjava/lang/String;)I"), 216 NATIVE_METHOD(NativeBreakIterator, nextImpl, "(JLjava/lang/String;I)I"), 217 NATIVE_METHOD(NativeBreakIterator, precedingImpl, "(JLjava/lang/String;I)I"), 218 NATIVE_METHOD(NativeBreakIterator, previousImpl, "(JLjava/lang/String;)I"), 219 NATIVE_METHOD(NativeBreakIterator, setTextImpl, "(JLjava/lang/String;)V"), 220 }; 221 void register_libcore_icu_NativeBreakIterator(JNIEnv* env) { 222 jniRegisterNativeMethods(env, "libcore/icu/NativeBreakIterator", gMethods, NELEM(gMethods)); 223 } 224