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 {
BiDiDataBiDiData32 BiDiData(UBiDi* biDi) : mBiDi(biDi), mEmbeddingLevels(NULL) {
33 }
34
~BiDiDataBiDiData35 ~BiDiData() {
36 ubidi_close(mBiDi);
37 }
38
embeddingLevelsBiDiData39 UBiDiLevel* embeddingLevels() {
40 return reinterpret_cast<UBiDiLevel*>(&mEmbeddingLevels[0]);
41 }
42
setEmbeddingLevelsBiDiData43 void setEmbeddingLevels(jbyte* newEmbeddingLevels) {
44 mEmbeddingLevels.reset(newEmbeddingLevels);
45 }
46
uBiDiBiDiData47 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
biDiData(jlong ptr)60 static BiDiData* biDiData(jlong ptr) {
61 return reinterpret_cast<BiDiData*>(static_cast<uintptr_t>(ptr));
62 }
63
uBiDi(jlong ptr)64 static UBiDi* uBiDi(jlong ptr) {
65 return reinterpret_cast<BiDiData*>(static_cast<uintptr_t>(ptr))->uBiDi();
66 }
67
Bidi_ubidi_open(JNIEnv *,jclass)68 static jlong Bidi_ubidi_open(JNIEnv*, jclass) {
69 return reinterpret_cast<uintptr_t>(new BiDiData(ubidi_open()));
70 }
71
Bidi_ubidi_close(JNIEnv *,jclass,jlong ptr)72 static void Bidi_ubidi_close(JNIEnv*, jclass, jlong ptr) {
73 delete biDiData(ptr);
74 }
75
Bidi_ubidi_setPara(JNIEnv * env,jclass,jlong ptr,jcharArray text,jint length,jint paraLevel,jbyteArray newEmbeddingLevels)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
Bidi_ubidi_setLine(JNIEnv * env,jclass,jlong ptr,jint start,jint limit)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
Bidi_ubidi_getDirection(JNIEnv *,jclass,jlong ptr)107 static jint Bidi_ubidi_getDirection(JNIEnv*, jclass, jlong ptr) {
108 return ubidi_getDirection(uBiDi(ptr));
109 }
110
Bidi_ubidi_getLength(JNIEnv *,jclass,jlong ptr)111 static jint Bidi_ubidi_getLength(JNIEnv*, jclass, jlong ptr) {
112 return ubidi_getLength(uBiDi(ptr));
113 }
114
Bidi_ubidi_getParaLevel(JNIEnv *,jclass,jlong ptr)115 static jbyte Bidi_ubidi_getParaLevel(JNIEnv*, jclass, jlong ptr) {
116 return ubidi_getParaLevel(uBiDi(ptr));
117 }
118
Bidi_ubidi_getLevels(JNIEnv * env,jclass,jlong ptr)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
Bidi_ubidi_countRuns(JNIEnv * env,jclass,jlong ptr)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 */
Bidi_ubidi_getRuns(JNIEnv * env,jclass,jlong ptr)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
Bidi_ubidi_reorderVisual(JNIEnv * env,jclass,jbyteArray javaLevels,jint length)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 };
register_java_text_Bidi(JNIEnv * env)192 void register_java_text_Bidi(JNIEnv* env) {
193 jniRegisterNativeMethods(env, "java/text/Bidi", gMethods, NELEM(gMethods));
194 }
195