Home | History | Annotate | Download | only in native
      1 /*
      2  * Licensed to the Apache Software Foundation (ASF) under one or more
      3  * contributor license agreements.  See the NOTICE file distributed with
      4  * this work for additional information regarding copyright ownership.
      5  * The ASF licenses this file to You under the Apache License, Version 2.0
      6  * (the "License"); you may not use this file except in compliance with
      7  * the License.  You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 #define LOG_TAG "Bidi"
     19 
     20 #include "IcuUtilities.h"
     21 #include "JNIHelp.h"
     22 #include "JniConstants.h"
     23 #include "JniException.h"
     24 #include "ScopedPrimitiveArray.h"
     25 #include "UniquePtr.h"
     26 #include "unicode/ubidi.h"
     27 
     28 #include <stdlib.h>
     29 #include <string.h>
     30 
     31 struct BiDiData {
     32     BiDiData(UBiDi* biDi) : mBiDi(biDi), mEmbeddingLevels(NULL) {
     33     }
     34 
     35     ~BiDiData() {
     36         ubidi_close(mBiDi);
     37     }
     38 
     39     UBiDiLevel* embeddingLevels() {
     40         return reinterpret_cast<UBiDiLevel*>(&mEmbeddingLevels[0]);
     41     }
     42 
     43     void setEmbeddingLevels(jbyte* newEmbeddingLevels) {
     44         mEmbeddingLevels.reset(newEmbeddingLevels);
     45     }
     46 
     47     UBiDi* uBiDi() {
     48         return mBiDi;
     49     }
     50 
     51 private:
     52     UBiDi* mBiDi;
     53     UniquePtr<jbyte[]> mEmbeddingLevels;
     54 
     55     // Disallow copy and assignment.
     56     BiDiData(const BiDiData&);
     57     void operator=(const BiDiData&);
     58 };
     59 
     60 static BiDiData* biDiData(jlong ptr) {
     61     return reinterpret_cast<BiDiData*>(static_cast<uintptr_t>(ptr));
     62 }
     63 
     64 static UBiDi* uBiDi(jlong ptr) {
     65     return reinterpret_cast<BiDiData*>(static_cast<uintptr_t>(ptr))->uBiDi();
     66 }
     67 
     68 static jlong Bidi_ubidi_open(JNIEnv*, jclass) {
     69     return reinterpret_cast<uintptr_t>(new BiDiData(ubidi_open()));
     70 }
     71 
     72 static void Bidi_ubidi_close(JNIEnv*, jclass, jlong ptr) {
     73     delete biDiData(ptr);
     74 }
     75 
     76 static void Bidi_ubidi_setPara(JNIEnv* env, jclass, jlong ptr, jcharArray text, jint length, jint paraLevel, jbyteArray newEmbeddingLevels) {
     77     BiDiData* data = biDiData(ptr);
     78     // Copy the new embedding levels from the Java heap to the native heap.
     79     if (newEmbeddingLevels != NULL) {
     80         jbyte* dst;
     81         data->setEmbeddingLevels(dst = new jbyte[length]);
     82         env->GetByteArrayRegion(newEmbeddingLevels, 0, length, dst);
     83     } else {
     84         data->setEmbeddingLevels(NULL);
     85     }
     86     ScopedCharArrayRO chars(env, text);
     87     if (chars.get() == NULL) {
     88         return;
     89     }
     90     UErrorCode err = U_ZERO_ERROR;
     91     ubidi_setPara(data->uBiDi(), chars.get(), length, paraLevel, data->embeddingLevels(), &err);
     92     maybeThrowIcuException(env, "ubidi_setPara", err);
     93 }
     94 
     95 static jlong Bidi_ubidi_setLine(JNIEnv* env, jclass, jlong ptr, jint start, jint limit) {
     96     UErrorCode status = U_ZERO_ERROR;
     97     UBiDi* sized = ubidi_openSized(limit - start, 0, &status);
     98     if (maybeThrowIcuException(env, "ubidi_openSized", status)) {
     99         return 0;
    100     }
    101     UniquePtr<BiDiData> lineData(new BiDiData(sized));
    102     ubidi_setLine(uBiDi(ptr), start, limit, lineData->uBiDi(), &status);
    103     maybeThrowIcuException(env, "ubidi_setLine", status);
    104     return reinterpret_cast<uintptr_t>(lineData.release());
    105 }
    106 
    107 static jint Bidi_ubidi_getDirection(JNIEnv*, jclass, jlong ptr) {
    108     return ubidi_getDirection(uBiDi(ptr));
    109 }
    110 
    111 static jint Bidi_ubidi_getLength(JNIEnv*, jclass, jlong ptr) {
    112     return ubidi_getLength(uBiDi(ptr));
    113 }
    114 
    115 static jbyte Bidi_ubidi_getParaLevel(JNIEnv*, jclass, jlong ptr) {
    116     return ubidi_getParaLevel(uBiDi(ptr));
    117 }
    118 
    119 static jbyteArray Bidi_ubidi_getLevels(JNIEnv* env, jclass, jlong ptr) {
    120     UErrorCode status = U_ZERO_ERROR;
    121     const UBiDiLevel* levels = ubidi_getLevels(uBiDi(ptr), &status);
    122     if (maybeThrowIcuException(env, "ubidi_getLevels", status)) {
    123         return NULL;
    124     }
    125     int len = ubidi_getLength(uBiDi(ptr));
    126     jbyteArray result = env->NewByteArray(len);
    127     env->SetByteArrayRegion(result, 0, len, reinterpret_cast<const jbyte*>(levels));
    128     return result;
    129 }
    130 
    131 static jint Bidi_ubidi_countRuns(JNIEnv* env, jclass, jlong ptr) {
    132     UErrorCode status = U_ZERO_ERROR;
    133     int count = ubidi_countRuns(uBiDi(ptr), &status);
    134     maybeThrowIcuException(env, "ubidi_countRuns", status);
    135     return count;
    136 }
    137 
    138 /**
    139  * TODO: if we care about performance, we might just want to use an int[] instead of a Run[].
    140  */
    141 static jobjectArray Bidi_ubidi_getRuns(JNIEnv* env, jclass, jlong ptr) {
    142     UBiDi* ubidi = uBiDi(ptr);
    143     UErrorCode status = U_ZERO_ERROR;
    144     int runCount = ubidi_countRuns(ubidi, &status);
    145     if (maybeThrowIcuException(env, "ubidi_countRuns", status)) {
    146         return NULL;
    147     }
    148     static jmethodID bidiRunConstructor =
    149             env->GetMethodID(JniConstants::bidiRunClass, "<init>", "(III)V");
    150     jobjectArray runs = env->NewObjectArray(runCount, JniConstants::bidiRunClass, NULL);
    151     UBiDiLevel level = 0;
    152     int start = 0;
    153     int limit = 0;
    154     for (int i = 0; i < runCount; ++i) {
    155         ubidi_getLogicalRun(ubidi, start, &limit, &level);
    156         jobject run = env->NewObject(JniConstants::bidiRunClass, bidiRunConstructor, start, limit, level);
    157         env->SetObjectArrayElement(runs, i, run);
    158         start = limit;
    159     }
    160     return runs;
    161 }
    162 
    163 static jintArray Bidi_ubidi_reorderVisual(JNIEnv* env, jclass, jbyteArray javaLevels, jint length) {
    164     ScopedByteArrayRO levelBytes(env, javaLevels);
    165     if (levelBytes.get() == NULL) {
    166         return NULL;
    167     }
    168 
    169     const UBiDiLevel* levels = reinterpret_cast<const UBiDiLevel*>(levelBytes.get());
    170 
    171     UniquePtr<int[]> indexMap(new int[length]);
    172     ubidi_reorderVisual(levels, length, &indexMap[0]);
    173 
    174     jintArray result = env->NewIntArray(length);
    175     env->SetIntArrayRegion(result, 0, length, &indexMap[0]);
    176     return result;
    177 }
    178 
    179 static JNINativeMethod gMethods[] = {
    180     NATIVE_METHOD(Bidi, ubidi_close, "(J)V"),
    181     NATIVE_METHOD(Bidi, ubidi_countRuns, "(J)I"),
    182     NATIVE_METHOD(Bidi, ubidi_getDirection, "(J)I"),
    183     NATIVE_METHOD(Bidi, ubidi_getLength, "(J)I"),
    184     NATIVE_METHOD(Bidi, ubidi_getLevels, "(J)[B"),
    185     NATIVE_METHOD(Bidi, ubidi_getParaLevel, "(J)B"),
    186     NATIVE_METHOD(Bidi, ubidi_getRuns, "(J)[Ljava/text/Bidi$Run;"),
    187     NATIVE_METHOD(Bidi, ubidi_open, "()J"),
    188     NATIVE_METHOD(Bidi, ubidi_reorderVisual, "([BI)[I"),
    189     NATIVE_METHOD(Bidi, ubidi_setLine, "(JII)J"),
    190     NATIVE_METHOD(Bidi, ubidi_setPara, "(J[CII[B)V"),
    191 };
    192 void register_java_text_Bidi(JNIEnv* env) {
    193     jniRegisterNativeMethods(env, "java/text/Bidi", gMethods, NELEM(gMethods));
    194 }
    195