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 int register_java_text_Bidi(JNIEnv* env) { 191 return jniRegisterNativeMethods(env, "java/text/Bidi", gMethods, NELEM(gMethods)); 192 } 193