Home | History | Annotate | Download | only in native
      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 "ScopedUtfChars.h"
     24 #include "unicode/brkiter.h"
     25 #include "unicode/putil.h"
     26 #include <stdlib.h>
     27 
     28 // ICU documentation: http://icu-project.org/apiref/icu4c/classBreakIterator.html
     29 
     30 static BreakIterator* toBreakIterator(jlong address) {
     31   return reinterpret_cast<BreakIterator*>(static_cast<uintptr_t>(address));
     32 }
     33 
     34 /**
     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.
     39  */
     40 class BreakIteratorAccessor {
     41  public:
     42   BreakIteratorAccessor(JNIEnv* env, jlong address, jstring javaInput, bool reset) {
     43     init(env, address);
     44     mJavaInput = javaInput;
     45 
     46     if (mJavaInput == NULL) {
     47       return;
     48     }
     49 
     50     mChars = env->GetStringChars(mJavaInput, NULL);
     51     if (mChars == NULL) {
     52       return;
     53     }
     54 
     55     mUText = utext_openUChars(NULL, mChars, env->GetStringLength(mJavaInput), &mStatus);
     56     if (mUText == NULL) {
     57       return;
     58     }
     59 
     60     if (reset) {
     61       mBreakIterator->setText(mUText, mStatus);
     62     } else {
     63       mBreakIterator->refreshInputText(mUText, mStatus);
     64     }
     65   }
     66 
     67   BreakIteratorAccessor(JNIEnv* env, jlong address) {
     68     init(env, address);
     69   }
     70 
     71   ~BreakIteratorAccessor() {
     72     utext_close(mUText);
     73     if (mJavaInput) {
     74       mEnv->ReleaseStringChars(mJavaInput, mChars);
     75     }
     76     maybeThrowIcuException(mEnv, "utext_close", mStatus);
     77   }
     78 
     79   BreakIterator* operator->() {
     80     return mBreakIterator;
     81   }
     82 
     83   UErrorCode& status() {
     84     return mStatus;
     85   }
     86 
     87  private:
     88   void init(JNIEnv* env, jlong address) {
     89     mEnv = env;
     90     mJavaInput = NULL;
     91     mBreakIterator = toBreakIterator(address);
     92     mChars = NULL;
     93     mStatus = U_ZERO_ERROR;
     94     mUText = NULL;
     95   }
     96 
     97   JNIEnv* mEnv;
     98   jstring mJavaInput;
     99   BreakIterator* mBreakIterator;
    100   const jchar* mChars;
    101   UErrorCode mStatus;
    102   UText* mUText;
    103 
    104   // Disallow copy and assignment.
    105   BreakIteratorAccessor(const BreakIteratorAccessor&);
    106   void operator=(const BreakIteratorAccessor&);
    107 };
    108 
    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)
    121 
    122 static jint 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 jint NativeBreakIterator_getCharacterInstanceImpl(JNIEnv* env, jclass, jstring javaLocale) {
    147   MAKE_BREAK_ITERATOR_INSTANCE(BreakIterator::createCharacterInstance);
    148 }
    149 
    150 static jint NativeBreakIterator_getLineInstanceImpl(JNIEnv* env, jclass, jstring javaLocale) {
    151   MAKE_BREAK_ITERATOR_INSTANCE(BreakIterator::createLineInstance);
    152 }
    153 
    154 static jint NativeBreakIterator_getSentenceInstanceImpl(JNIEnv* env, jclass, jstring javaLocale) {
    155   MAKE_BREAK_ITERATOR_INSTANCE(BreakIterator::createSentenceInstance);
    156 }
    157 
    158 static jint NativeBreakIterator_getWordInstanceImpl(JNIEnv* env, jclass, jstring javaLocale) {
    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