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 "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