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