blob: d9ef35df71a8307a11264e66e3635597ed641c2c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define LOG_TAG "Bidi"
#include "IcuUtilities.h"
#include "JNIHelp.h"
#include "JniConstants.h"
#include "JniException.h"
#include "ScopedPrimitiveArray.h"
#include "UniquePtr.h"
#include "unicode/ubidi.h"
#include <stdlib.h>
#include <string.h>
struct BiDiData {
BiDiData(UBiDi* biDi) : mBiDi(biDi), mEmbeddingLevels(NULL) {
}
~BiDiData() {
ubidi_close(mBiDi);
}
UBiDiLevel* embeddingLevels() {
return reinterpret_cast<UBiDiLevel*>(&mEmbeddingLevels[0]);
}
void setEmbeddingLevels(jbyte* newEmbeddingLevels) {
mEmbeddingLevels.reset(newEmbeddingLevels);
}
UBiDi* uBiDi() {
return mBiDi;
}
private:
UBiDi* mBiDi;
UniquePtr<jbyte[]> mEmbeddingLevels;
// Disallow copy and assignment.
BiDiData(const BiDiData&);
void operator=(const BiDiData&);
};
static BiDiData* biDiData(jlong ptr) {
return reinterpret_cast<BiDiData*>(static_cast<uintptr_t>(ptr));
}
static UBiDi* uBiDi(jlong ptr) {
return reinterpret_cast<BiDiData*>(static_cast<uintptr_t>(ptr))->uBiDi();
}
static jlong Bidi_ubidi_open(JNIEnv*, jclass) {
return reinterpret_cast<uintptr_t>(new BiDiData(ubidi_open()));
}
static void Bidi_ubidi_close(JNIEnv*, jclass, jlong ptr) {
delete biDiData(ptr);
}
static void Bidi_ubidi_setPara(JNIEnv* env, jclass, jlong ptr, jcharArray text, jint length, jint paraLevel, jbyteArray newEmbeddingLevels) {
BiDiData* data = biDiData(ptr);
// Copy the new embedding levels from the Java heap to the native heap.
if (newEmbeddingLevels != NULL) {
jbyte* dst;
data->setEmbeddingLevels(dst = new jbyte[length]);
env->GetByteArrayRegion(newEmbeddingLevels, 0, length, dst);
} else {
data->setEmbeddingLevels(NULL);
}
ScopedCharArrayRO chars(env, text);
if (chars.get() == NULL) {
return;
}
UErrorCode err = U_ZERO_ERROR;
ubidi_setPara(data->uBiDi(), chars.get(), length, paraLevel, data->embeddingLevels(), &err);
maybeThrowIcuException(env, "ubidi_setPara", err);
}
static jlong Bidi_ubidi_setLine(JNIEnv* env, jclass, jlong ptr, jint start, jint limit) {
UErrorCode status = U_ZERO_ERROR;
UBiDi* sized = ubidi_openSized(limit - start, 0, &status);
if (maybeThrowIcuException(env, "ubidi_openSized", status)) {
return 0;
}
UniquePtr<BiDiData> lineData(new BiDiData(sized));
ubidi_setLine(uBiDi(ptr), start, limit, lineData->uBiDi(), &status);
maybeThrowIcuException(env, "ubidi_setLine", status);
return reinterpret_cast<uintptr_t>(lineData.release());
}
static jint Bidi_ubidi_getDirection(JNIEnv*, jclass, jlong ptr) {
return ubidi_getDirection(uBiDi(ptr));
}
static jint Bidi_ubidi_getLength(JNIEnv*, jclass, jlong ptr) {
return ubidi_getLength(uBiDi(ptr));
}
static jbyte Bidi_ubidi_getParaLevel(JNIEnv*, jclass, jlong ptr) {
return ubidi_getParaLevel(uBiDi(ptr));
}
static jbyteArray Bidi_ubidi_getLevels(JNIEnv* env, jclass, jlong ptr) {
UErrorCode status = U_ZERO_ERROR;
const UBiDiLevel* levels = ubidi_getLevels(uBiDi(ptr), &status);
if (maybeThrowIcuException(env, "ubidi_getLevels", status)) {
return NULL;
}
int len = ubidi_getLength(uBiDi(ptr));
jbyteArray result = env->NewByteArray(len);
env->SetByteArrayRegion(result, 0, len, reinterpret_cast<const jbyte*>(levels));
return result;
}
static jint Bidi_ubidi_countRuns(JNIEnv* env, jclass, jlong ptr) {
UErrorCode status = U_ZERO_ERROR;
int count = ubidi_countRuns(uBiDi(ptr), &status);
maybeThrowIcuException(env, "ubidi_countRuns", status);
return count;
}
/**
* TODO: if we care about performance, we might just want to use an int[] instead of a Run[].
*/
static jobjectArray Bidi_ubidi_getRuns(JNIEnv* env, jclass, jlong ptr) {
UBiDi* ubidi = uBiDi(ptr);
UErrorCode status = U_ZERO_ERROR;
int runCount = ubidi_countRuns(ubidi, &status);
if (maybeThrowIcuException(env, "ubidi_countRuns", status)) {
return NULL;
}
static jmethodID bidiRunConstructor =
env->GetMethodID(JniConstants::bidiRunClass, "<init>", "(III)V");
jobjectArray runs = env->NewObjectArray(runCount, JniConstants::bidiRunClass, NULL);
UBiDiLevel level = 0;
int start = 0;
int limit = 0;
for (int i = 0; i < runCount; ++i) {
ubidi_getLogicalRun(ubidi, start, &limit, &level);
jobject run = env->NewObject(JniConstants::bidiRunClass, bidiRunConstructor, start, limit, level);
env->SetObjectArrayElement(runs, i, run);
start = limit;
}
return runs;
}
static jintArray Bidi_ubidi_reorderVisual(JNIEnv* env, jclass, jbyteArray javaLevels, jint length) {
ScopedByteArrayRO levelBytes(env, javaLevels);
if (levelBytes.get() == NULL) {
return NULL;
}
const UBiDiLevel* levels = reinterpret_cast<const UBiDiLevel*>(levelBytes.get());
UniquePtr<int[]> indexMap(new int[length]);
ubidi_reorderVisual(levels, length, &indexMap[0]);
jintArray result = env->NewIntArray(length);
env->SetIntArrayRegion(result, 0, length, &indexMap[0]);
return result;
}
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(Bidi, ubidi_close, "(J)V"),
NATIVE_METHOD(Bidi, ubidi_countRuns, "(J)I"),
NATIVE_METHOD(Bidi, ubidi_getDirection, "(J)I"),
NATIVE_METHOD(Bidi, ubidi_getLength, "(J)I"),
NATIVE_METHOD(Bidi, ubidi_getLevels, "(J)[B"),
NATIVE_METHOD(Bidi, ubidi_getParaLevel, "(J)B"),
NATIVE_METHOD(Bidi, ubidi_getRuns, "(J)[Ljava/text/Bidi$Run;"),
NATIVE_METHOD(Bidi, ubidi_open, "()J"),
NATIVE_METHOD(Bidi, ubidi_reorderVisual, "([BI)[I"),
NATIVE_METHOD(Bidi, ubidi_setLine, "(JII)J"),
NATIVE_METHOD(Bidi, ubidi_setPara, "(J[CII[B)V"),
};
void register_java_text_Bidi(JNIEnv* env) {
jniRegisterNativeMethods(env, "java/text/Bidi", gMethods, NELEM(gMethods));
}