Elliott Hughes | 1698d14 | 2010-01-22 18:22:53 -0800 | [diff] [blame] | 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 | |
Elliott Hughes | 757a794 | 2010-04-16 14:14:28 -0700 | [diff] [blame] | 17 | #define LOG_TAG "NativeBreakIterator" |
| 18 | |
Elliott Hughes | 3aac4dd | 2013-02-04 15:37:52 -0800 | [diff] [blame] | 19 | #include "IcuUtilities.h" |
Elliott Hughes | 1698d14 | 2010-01-22 18:22:53 -0800 | [diff] [blame] | 20 | #include "JNIHelp.h" |
Elliott Hughes | e22935d | 2010-08-12 17:27:27 -0700 | [diff] [blame] | 21 | #include "JniConstants.h" |
Elliott Hughes | bef9ec3 | 2011-04-19 10:26:58 -0700 | [diff] [blame] | 22 | #include "JniException.h" |
Elliott Hughes | bcf7c66 | 2010-03-26 11:30:00 -0700 | [diff] [blame] | 23 | #include "ScopedUtfChars.h" |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 24 | #include "unicode/brkiter.h" |
Elliott Hughes | 1698d14 | 2010-01-22 18:22:53 -0800 | [diff] [blame] | 25 | #include "unicode/putil.h" |
| 26 | #include <stdlib.h> |
| 27 | |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 28 | // ICU documentation: http://icu-project.org/apiref/icu4c/classBreakIterator.html |
| 29 | |
Elliott Hughes | 1e5d730 | 2013-04-22 17:01:52 -0700 | [diff] [blame] | 30 | static BreakIterator* toBreakIterator(jlong address) { |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 31 | return reinterpret_cast<BreakIterator*>(static_cast<uintptr_t>(address)); |
| 32 | } |
| 33 | |
Elliott Hughes | aa96cbd | 2011-04-22 16:03:49 -0700 | [diff] [blame] | 34 | /** |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 35 | * We use ICU4C's BreakIterator class, but our input is on the Java heap and potentially moving |
| 36 | * around between calls. This wrapper class ensures that our RegexMatcher is always pointing at |
| 37 | * the current location of the char[]. Earlier versions of Android simply copied the data to the |
| 38 | * native heap, but that's wasteful and hides allocations from the garbage collector. |
Elliott Hughes | aa96cbd | 2011-04-22 16:03:49 -0700 | [diff] [blame] | 39 | */ |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 40 | class BreakIteratorAccessor { |
| 41 | public: |
Elliott Hughes | 1e5d730 | 2013-04-22 17:01:52 -0700 | [diff] [blame] | 42 | BreakIteratorAccessor(JNIEnv* env, jlong address, jstring javaInput, bool reset) { |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 43 | init(env, address); |
| 44 | mJavaInput = javaInput; |
| 45 | |
| 46 | if (mJavaInput == NULL) { |
| 47 | return; |
Elliott Hughes | aa96cbd | 2011-04-22 16:03:49 -0700 | [diff] [blame] | 48 | } |
| 49 | |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 50 | mChars = env->GetStringChars(mJavaInput, NULL); |
| 51 | if (mChars == NULL) { |
| 52 | return; |
Elliott Hughes | aa96cbd | 2011-04-22 16:03:49 -0700 | [diff] [blame] | 53 | } |
| 54 | |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 55 | mUText = utext_openUChars(NULL, mChars, env->GetStringLength(mJavaInput), &mStatus); |
| 56 | if (mUText == NULL) { |
| 57 | return; |
Elliott Hughes | aa96cbd | 2011-04-22 16:03:49 -0700 | [diff] [blame] | 58 | } |
| 59 | |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 60 | if (reset) { |
| 61 | mBreakIterator->setText(mUText, mStatus); |
| 62 | } else { |
| 63 | mBreakIterator->refreshInputText(mUText, mStatus); |
Elliott Hughes | aa96cbd | 2011-04-22 16:03:49 -0700 | [diff] [blame] | 64 | } |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 65 | } |
Elliott Hughes | aa96cbd | 2011-04-22 16:03:49 -0700 | [diff] [blame] | 66 | |
Elliott Hughes | 1e5d730 | 2013-04-22 17:01:52 -0700 | [diff] [blame] | 67 | BreakIteratorAccessor(JNIEnv* env, jlong address) { |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 68 | init(env, address); |
| 69 | } |
| 70 | |
| 71 | ~BreakIteratorAccessor() { |
| 72 | utext_close(mUText); |
| 73 | if (mJavaInput) { |
| 74 | mEnv->ReleaseStringChars(mJavaInput, mChars); |
Elliott Hughes | aa96cbd | 2011-04-22 16:03:49 -0700 | [diff] [blame] | 75 | } |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 76 | maybeThrowIcuException(mEnv, "utext_close", mStatus); |
| 77 | } |
Elliott Hughes | aa96cbd | 2011-04-22 16:03:49 -0700 | [diff] [blame] | 78 | |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 79 | BreakIterator* operator->() { |
| 80 | return mBreakIterator; |
| 81 | } |
Elliott Hughes | aa96cbd | 2011-04-22 16:03:49 -0700 | [diff] [blame] | 82 | |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 83 | UErrorCode& status() { |
| 84 | return mStatus; |
| 85 | } |
Elliott Hughes | aa96cbd | 2011-04-22 16:03:49 -0700 | [diff] [blame] | 86 | |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 87 | private: |
Elliott Hughes | 1e5d730 | 2013-04-22 17:01:52 -0700 | [diff] [blame] | 88 | void init(JNIEnv* env, jlong address) { |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 89 | mEnv = env; |
| 90 | mJavaInput = NULL; |
| 91 | mBreakIterator = toBreakIterator(address); |
| 92 | mChars = NULL; |
| 93 | mStatus = U_ZERO_ERROR; |
| 94 | mUText = NULL; |
| 95 | } |
Elliott Hughes | aa96cbd | 2011-04-22 16:03:49 -0700 | [diff] [blame] | 96 | |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 97 | JNIEnv* mEnv; |
| 98 | jstring mJavaInput; |
| 99 | BreakIterator* mBreakIterator; |
| 100 | const jchar* mChars; |
| 101 | UErrorCode mStatus; |
| 102 | UText* mUText; |
Elliott Hughes | aa96cbd | 2011-04-22 16:03:49 -0700 | [diff] [blame] | 103 | |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 104 | // Disallow copy and assignment. |
| 105 | BreakIteratorAccessor(const BreakIteratorAccessor&); |
| 106 | void operator=(const BreakIteratorAccessor&); |
Elliott Hughes | aa96cbd | 2011-04-22 16:03:49 -0700 | [diff] [blame] | 107 | }; |
| 108 | |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 109 | #define MAKE_BREAK_ITERATOR_INSTANCE(F) \ |
| 110 | UErrorCode status = U_ZERO_ERROR; \ |
| 111 | const ScopedUtfChars localeChars(env, javaLocale); \ |
| 112 | if (localeChars.c_str() == NULL) { \ |
| 113 | return 0; \ |
| 114 | } \ |
| 115 | Locale locale(Locale::createFromName(localeChars.c_str())); \ |
| 116 | BreakIterator* it = F(locale, status); \ |
| 117 | if (maybeThrowIcuException(env, "ubrk_open", status)) { \ |
| 118 | return 0; \ |
| 119 | } \ |
| 120 | return reinterpret_cast<uintptr_t>(it) |
Elliott Hughes | 1698d14 | 2010-01-22 18:22:53 -0800 | [diff] [blame] | 121 | |
Elliott Hughes | b717644 | 2014-02-21 09:58:17 -0800 | [diff] [blame] | 122 | static jlong NativeBreakIterator_cloneImpl(JNIEnv* env, jclass, jlong address) { |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 123 | BreakIteratorAccessor it(env, address); |
| 124 | return reinterpret_cast<uintptr_t>(it->clone()); |
Elliott Hughes | 1698d14 | 2010-01-22 18:22:53 -0800 | [diff] [blame] | 125 | } |
| 126 | |
Elliott Hughes | 1e5d730 | 2013-04-22 17:01:52 -0700 | [diff] [blame] | 127 | static void NativeBreakIterator_closeImpl(JNIEnv*, jclass, jlong address) { |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 128 | delete toBreakIterator(address); |
Elliott Hughes | 1698d14 | 2010-01-22 18:22:53 -0800 | [diff] [blame] | 129 | } |
| 130 | |
Elliott Hughes | 1e5d730 | 2013-04-22 17:01:52 -0700 | [diff] [blame] | 131 | static jint NativeBreakIterator_currentImpl(JNIEnv* env, jclass, jlong address, jstring javaInput) { |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 132 | BreakIteratorAccessor it(env, address, javaInput, false); |
| 133 | return it->current(); |
Elliott Hughes | 1698d14 | 2010-01-22 18:22:53 -0800 | [diff] [blame] | 134 | } |
| 135 | |
Elliott Hughes | 1e5d730 | 2013-04-22 17:01:52 -0700 | [diff] [blame] | 136 | static jint NativeBreakIterator_firstImpl(JNIEnv* env, jclass, jlong address, jstring javaInput) { |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 137 | BreakIteratorAccessor it(env, address, javaInput, false); |
| 138 | return it->first(); |
| 139 | } |
| 140 | |
Elliott Hughes | 1e5d730 | 2013-04-22 17:01:52 -0700 | [diff] [blame] | 141 | static jint NativeBreakIterator_followingImpl(JNIEnv* env, jclass, jlong address, jstring javaInput, jint offset) { |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 142 | BreakIteratorAccessor it(env, address, javaInput, false); |
| 143 | return it->following(offset); |
| 144 | } |
| 145 | |
Elliott Hughes | 9adba44 | 2014-02-03 17:08:19 -0800 | [diff] [blame] | 146 | static jlong NativeBreakIterator_getCharacterInstanceImpl(JNIEnv* env, jclass, jstring javaLocale) { |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 147 | MAKE_BREAK_ITERATOR_INSTANCE(BreakIterator::createCharacterInstance); |
| 148 | } |
| 149 | |
Elliott Hughes | 9adba44 | 2014-02-03 17:08:19 -0800 | [diff] [blame] | 150 | static jlong NativeBreakIterator_getLineInstanceImpl(JNIEnv* env, jclass, jstring javaLocale) { |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 151 | MAKE_BREAK_ITERATOR_INSTANCE(BreakIterator::createLineInstance); |
| 152 | } |
| 153 | |
Elliott Hughes | 9adba44 | 2014-02-03 17:08:19 -0800 | [diff] [blame] | 154 | static jlong NativeBreakIterator_getSentenceInstanceImpl(JNIEnv* env, jclass, jstring javaLocale) { |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 155 | MAKE_BREAK_ITERATOR_INSTANCE(BreakIterator::createSentenceInstance); |
| 156 | } |
| 157 | |
Elliott Hughes | 9adba44 | 2014-02-03 17:08:19 -0800 | [diff] [blame] | 158 | static jlong NativeBreakIterator_getWordInstanceImpl(JNIEnv* env, jclass, jstring javaLocale) { |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 159 | MAKE_BREAK_ITERATOR_INSTANCE(BreakIterator::createWordInstance); |
| 160 | } |
| 161 | |
Elliott Hughes | 1e5d730 | 2013-04-22 17:01:52 -0700 | [diff] [blame] | 162 | static jboolean NativeBreakIterator_isBoundaryImpl(JNIEnv* env, jclass, jlong address, jstring javaInput, jint offset) { |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 163 | BreakIteratorAccessor it(env, address, javaInput, false); |
| 164 | return it->isBoundary(offset); |
| 165 | } |
| 166 | |
Elliott Hughes | 1e5d730 | 2013-04-22 17:01:52 -0700 | [diff] [blame] | 167 | static jint NativeBreakIterator_lastImpl(JNIEnv* env, jclass, jlong address, jstring javaInput) { |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 168 | BreakIteratorAccessor it(env, address, javaInput, false); |
| 169 | return it->last(); |
| 170 | } |
| 171 | |
Elliott Hughes | 1e5d730 | 2013-04-22 17:01:52 -0700 | [diff] [blame] | 172 | static jint NativeBreakIterator_nextImpl(JNIEnv* env, jclass, jlong address, jstring javaInput, jint n) { |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 173 | BreakIteratorAccessor it(env, address, javaInput, false); |
| 174 | if (n < 0) { |
| 175 | while (n++ < -1) { |
| 176 | it->previous(); |
Elliott Hughes | 1698d14 | 2010-01-22 18:22:53 -0800 | [diff] [blame] | 177 | } |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 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; |
Elliott Hughes | 1698d14 | 2010-01-22 18:22:53 -0800 | [diff] [blame] | 188 | } |
| 189 | |
Elliott Hughes | 1e5d730 | 2013-04-22 17:01:52 -0700 | [diff] [blame] | 190 | static jint NativeBreakIterator_precedingImpl(JNIEnv* env, jclass, jlong address, jstring javaInput, jint offset) { |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 191 | BreakIteratorAccessor it(env, address, javaInput, false); |
| 192 | return it->preceding(offset); |
Elliott Hughes | 1698d14 | 2010-01-22 18:22:53 -0800 | [diff] [blame] | 193 | } |
| 194 | |
Elliott Hughes | 1e5d730 | 2013-04-22 17:01:52 -0700 | [diff] [blame] | 195 | static jint NativeBreakIterator_previousImpl(JNIEnv* env, jclass, jlong address, jstring javaInput) { |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 196 | BreakIteratorAccessor it(env, address, javaInput, false); |
| 197 | return it->previous(); |
Elliott Hughes | 1698d14 | 2010-01-22 18:22:53 -0800 | [diff] [blame] | 198 | } |
| 199 | |
Elliott Hughes | 1e5d730 | 2013-04-22 17:01:52 -0700 | [diff] [blame] | 200 | static void NativeBreakIterator_setTextImpl(JNIEnv* env, jclass, jlong address, jstring javaInput) { |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 201 | BreakIteratorAccessor it(env, address, javaInput, true); |
Elliott Hughes | 1698d14 | 2010-01-22 18:22:53 -0800 | [diff] [blame] | 202 | } |
| 203 | |
| 204 | static JNINativeMethod gMethods[] = { |
Elliott Hughes | 1e5d730 | 2013-04-22 17:01:52 -0700 | [diff] [blame] | 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"), |
Elliott Hughes | 1698d14 | 2010-01-22 18:22:53 -0800 | [diff] [blame] | 220 | }; |
Elliott Hughes | 7cd6760 | 2012-05-03 17:21:04 -0700 | [diff] [blame] | 221 | void register_libcore_icu_NativeBreakIterator(JNIEnv* env) { |
Elliott Hughes | 0158436 | 2012-10-08 14:22:51 -0700 | [diff] [blame] | 222 | jniRegisterNativeMethods(env, "libcore/icu/NativeBreakIterator", gMethods, NELEM(gMethods)); |
Elliott Hughes | 1698d14 | 2010-01-22 18:22:53 -0800 | [diff] [blame] | 223 | } |