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 "JNIHelp.h"
     21 #include "JniConstants.h"
     22 #include "JniException.h"
     23 #include "ScopedPrimitiveArray.h"
     24 #include "UniquePtr.h"
     25 #include "unicode/ubidi.h"
     26 
     27 #include <stdlib.h>
     28 #include <string.h>
     29 
     30 struct BiDiData {
     31     BiDiData(UBiDi* biDi) : mBiDi(biDi), mEmbeddingLevels(NULL) {
     32     }
     33 
     34     ~BiDiData() {
     35         ubidi_close(mBiDi);
     36     }
     37 
     38     UBiDiLevel* embeddingLevels() {
     39         return reinterpret_cast<UBiDiLevel*>(&mEmbeddingLevels[0]);
     40     }
     41 
     42     void setEmbeddingLevels(jbyte* newEmbeddingLevels) {
     43         mEmbeddingLevels.reset(newEmbeddingLevels);
     44     }
     45 
     46     UBiDi* uBiDi() {
     47         return mBiDi;
     48     }
     49 
     50 private:
     51     UBiDi* mBiDi;
     52     UniquePtr<jbyte[]> mEmbeddingLevels;
     53 
     54     // Disallow copy and assignment.
     55     BiDiData(const BiDiData&);
     56     void operator=(const BiDiData&);
     57 };
     58 
     59 static BiDiData* biDiData(jlong ptr) {
     60     return reinterpret_cast<BiDiData*>(static_cast<uintptr_t>(ptr));
     61 }
     62 
     63 static UBiDi* uBiDi(jlong ptr) {
     64     return reinterpret_cast<BiDiData*>(static_cast<uintptr_t>(ptr))->uBiDi();
     65 }
     66 
     67 static jlong Bidi_ubidi_open(JNIEnv*, jclass) {
     68     return reinterpret_cast<uintptr_t>(new BiDiData(ubidi_open()));
     69 }
     70 
     71 static void Bidi_ubidi_close(JNIEnv*, jclass, jlong ptr) {
     72     delete biDiData(ptr);
     73 }
     74 
     75 static void Bidi_ubidi_setPara(JNIEnv* env, jclass, jlong ptr, jcharArray text, jint length, jint paraLevel, jbyteArray newEmbeddingLevels) {
     76     BiDiData* data = biDiData(ptr);
     77     // Copy the new embedding levels from the Java heap to the native heap.
     78     if (newEmbeddingLevels != NULL) {
     79         jbyte* dst;
     80         data->setEmbeddingLevels(dst = new jbyte[length]);
     81         env->GetByteArrayRegion(newEmbeddingLevels, 0, length, dst);
     82     } else {
     83         data->setEmbeddingLevels(NULL);
     84     }
     85     ScopedCharArrayRO chars(env, text);
     86     if (chars.get() == NULL) {
     87         return;
     88     }
     89     UErrorCode err = U_ZERO_ERROR;
     90     ubidi_setPara(data->uBiDi(), chars.get(), length, paraLevel, data->embeddingLevels(), &err);
     91     maybeThrowIcuException(env, err);
     92 }
     93 
     94 static jlong Bidi_ubidi_setLine(JNIEnv* env, jclass, jlong ptr, jint start, jint limit) {
     95     UErrorCode status = U_ZERO_ERROR;
     96     UBiDi* sized = ubidi_openSized(limit - start, 0, &status);
     97     if (maybeThrowIcuException(env, status)) {
     98         return 0;
     99     }
    100     UniquePtr<BiDiData> lineData(new BiDiData(sized));
    101     ubidi_setLine(uBiDi(ptr), start, limit, lineData->uBiDi(), &status);
    102     maybeThrowIcuException(env, status);
    103     return reinterpret_cast<uintptr_t>(lineData.release());
    104 }
    105 
    106 static jint Bidi_ubidi_getDirection(JNIEnv*, jclass, jlong ptr) {
    107     return ubidi_getDirection(uBiDi(ptr));
    108 }
    109 
    110 static jint Bidi_ubidi_getLength(JNIEnv*, jclass, jlong ptr) {
    111     return ubidi_getLength(uBiDi(ptr));
    112 }
    113 
    114 static jbyte Bidi_ubidi_getParaLevel(JNIEnv*, jclass, jlong ptr) {
    115     return ubidi_getParaLevel(uBiDi(ptr));
    116 }
    117 
    118 static jbyteArray Bidi_ubidi_getLevels(JNIEnv* env, jclass, jlong ptr) {
    119     UErrorCode status = U_ZERO_ERROR;
    120     const UBiDiLevel* levels = ubidi_getLevels(uBiDi(ptr), &status);
    121     if (maybeThrowIcuException(env, status)) {
    122         return NULL;
    123     }
    124     int len = ubidi_getLength(uBiDi(ptr));
    125     jbyteArray result = env->NewByteArray(len);
    126     env->SetByteArrayRegion(result, 0, len, reinterpret_cast<const jbyte*>(levels));
    127     return result;
    128 }
    129 
    130 static jint Bidi_ubidi_countRuns(JNIEnv* env, jclass, jlong ptr) {
    131     UErrorCode status = U_ZERO_ERROR;
    132     int count = ubidi_countRuns(uBiDi(ptr), &status);
    133     maybeThrowIcuException(env, status);
    134     return count;
    135 }
    136 
    137 /**
    138  * TODO: if we care about performance, we might just want to use an int[] instead of a Run[].
    139  */
    140 static jobjectArray Bidi_ubidi_getRuns(JNIEnv* env, jclass, jlong ptr) {
    141     UBiDi* ubidi = uBiDi(ptr);
    142     UErrorCode status = U_ZERO_ERROR;
    143     int runCount = ubidi_countRuns(ubidi, &status);
    144     if (maybeThrowIcuException(env, status)) {
    145         return NULL;
    146     }
    147     jmethodID bidiRunConstructor = env->GetMethodID(JniConstants::bidiRunClass, "<init>", "(III)V");
    148     jobjectArray runs = env->NewObjectArray(runCount, JniConstants::bidiRunClass, NULL);
    149     UBiDiLevel level = 0;
    150     int start = 0;
    151     int limit = 0;
    152     for (int i = 0; i < runCount; ++i) {
    153         ubidi_getLogicalRun(ubidi, start, &limit, &level);
    154         jobject run = env->NewObject(JniConstants::bidiRunClass, bidiRunConstructor, start, limit, level);
    155         env->SetObjectArrayElement(runs, i, run);
    156         start = limit;
    157     }
    158     return runs;
    159 }
    160 
    161 static jintArray Bidi_ubidi_reorderVisual(JNIEnv* env, jclass, jbyteArray javaLevels, jint length) {
    162     ScopedByteArrayRO levelBytes(env, javaLevels);
    163     if (levelBytes.get() == NULL) {
    164         return NULL;
    165     }
    166 
    167     const UBiDiLevel* levels = reinterpret_cast<const UBiDiLevel*>(levelBytes.get());
    168 
    169     UniquePtr<int[]> indexMap(new int[length]);
    170     ubidi_reorderVisual(levels, length, &indexMap[0]);
    171 
    172     jintArray result = env->NewIntArray(length);
    173     env->SetIntArrayRegion(result, 0, length, &indexMap[0]);
    174     return result;
    175 }
    176 
    177 static JNINativeMethod gMethods[] = {
    178     NATIVE_METHOD(Bidi, ubidi_close, "(J)V"),
    179     NATIVE_METHOD(Bidi, ubidi_countRuns, "(J)I"),
    180     NATIVE_METHOD(Bidi, ubidi_getDirection, "(J)I"),
    181     NATIVE_METHOD(Bidi, ubidi_getLength, "(J)I"),
    182     NATIVE_METHOD(Bidi, ubidi_getLevels, "(J)[B"),
    183     NATIVE_METHOD(Bidi, ubidi_getParaLevel, "(J)B"),
    184     NATIVE_METHOD(Bidi, ubidi_getRuns, "(J)[Ljava/text/Bidi$Run;"),
    185     NATIVE_METHOD(Bidi, ubidi_open, "()J"),
    186     NATIVE_METHOD(Bidi, ubidi_reorderVisual, "([BI)[I"),
    187     NATIVE_METHOD(Bidi, ubidi_setLine, "(JII)J"),
    188     NATIVE_METHOD(Bidi, ubidi_setPara, "(J[CII[B)V"),
    189 };
    190 void register_java_text_Bidi(JNIEnv* env) {
    191     jniRegisterNativeMethods(env, "java/text/Bidi", gMethods, NELEM(gMethods));
    192 }
    193