• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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