blob: 24dfa6fdb7daddad11ce3321058de298deb6134e [file] [log] [blame]
/**
*******************************************************************************
* Copyright (C) 1996-2006, International Business Machines Corporation and *
* others. All Rights Reserved. *
*******************************************************************************
*
*
*******************************************************************************
*/
/*
* (C) Copyright IBM Corp. 2000 - All Rights Reserved
* A JNI wrapper to ICU native converter Interface
* @author: Ram Viswanadha
*/
#define LOG_TAG "NativeConverter"
#include "IcuUtilities.h"
#include "JNIHelp.h"
#include "JniConstants.h"
#include "JniException.h"
#include "ScopedLocalRef.h"
#include "ScopedPrimitiveArray.h"
#include "ScopedStringChars.h"
#include "ScopedUtfChars.h"
#include "UniquePtr.h"
#include "cutils/log.h"
#include "toStringArray.h"
#include "unicode/ucnv.h"
#include "unicode/ucnv_cb.h"
#include "unicode/uniset.h"
#include "unicode/ustring.h"
#include "unicode/utypes.h"
#include <vector>
#include <stdlib.h>
#include <string.h>
#define NativeConverter_REPORT 0
#define NativeConverter_IGNORE 1
#define NativeConverter_REPLACE 2
#define MAX_REPLACEMENT_LENGTH 32 // equivalent to UCNV_ERROR_BUFFER_LENGTH
struct DecoderCallbackContext {
UChar replacementChars[MAX_REPLACEMENT_LENGTH];
size_t replacementCharCount;
UConverterToUCallback onUnmappableInput;
UConverterToUCallback onMalformedInput;
};
struct EncoderCallbackContext {
char replacementBytes[MAX_REPLACEMENT_LENGTH];
size_t replacementByteCount;
UConverterFromUCallback onUnmappableInput;
UConverterFromUCallback onMalformedInput;
};
struct UConverterDeleter {
void operator()(UConverter* p) const {
ucnv_close(p);
}
};
typedef UniquePtr<UConverter, UConverterDeleter> UniqueUConverter;
static UConverter* toUConverter(jlong address) {
return reinterpret_cast<UConverter*>(static_cast<uintptr_t>(address));
}
static jlong NativeConverter_openConverter(JNIEnv* env, jclass, jstring converterName) {
ScopedUtfChars converterNameChars(env, converterName);
if (converterNameChars.c_str() == NULL) {
return 0;
}
UErrorCode status = U_ZERO_ERROR;
UConverter* cnv = ucnv_open(converterNameChars.c_str(), &status);
maybeThrowIcuException(env, "ucnv_open", status);
return reinterpret_cast<uintptr_t>(cnv);
}
static void NativeConverter_closeConverter(JNIEnv*, jclass, jlong address) {
ucnv_close(toUConverter(address));
}
static bool shouldCodecThrow(jboolean flush, UErrorCode error) {
if (flush) {
return (error != U_BUFFER_OVERFLOW_ERROR && error != U_TRUNCATED_CHAR_FOUND);
} else {
return (error != U_BUFFER_OVERFLOW_ERROR && error != U_INVALID_CHAR_FOUND && error != U_ILLEGAL_CHAR_FOUND);
}
}
static jint NativeConverter_encode(JNIEnv* env, jclass, jlong address,
jcharArray source, jint sourceEnd, jbyteArray target, jint targetEnd,
jintArray data, jboolean flush) {
UConverter* cnv = toUConverter(address);
if (cnv == NULL) {
maybeThrowIcuException(env, "toUConverter", U_ILLEGAL_ARGUMENT_ERROR);
return U_ILLEGAL_ARGUMENT_ERROR;
}
ScopedCharArrayRO uSource(env, source);
if (uSource.get() == NULL) {
maybeThrowIcuException(env, "uSource", U_ILLEGAL_ARGUMENT_ERROR);
return U_ILLEGAL_ARGUMENT_ERROR;
}
ScopedByteArrayRW uTarget(env, target);
if (uTarget.get() == NULL) {
maybeThrowIcuException(env, "uTarget", U_ILLEGAL_ARGUMENT_ERROR);
return U_ILLEGAL_ARGUMENT_ERROR;
}
ScopedIntArrayRW myData(env, data);
if (myData.get() == NULL) {
maybeThrowIcuException(env, "myData", U_ILLEGAL_ARGUMENT_ERROR);
return U_ILLEGAL_ARGUMENT_ERROR;
}
// Do the conversion.
jint* sourceOffset = &myData[0];
jint* targetOffset = &myData[1];
const jchar* mySource = uSource.get() + *sourceOffset;
const UChar* mySourceLimit= uSource.get() + sourceEnd;
char* cTarget = reinterpret_cast<char*>(uTarget.get() + *targetOffset);
const char* cTargetLimit = reinterpret_cast<const char*>(uTarget.get() + targetEnd);
UErrorCode errorCode = U_ZERO_ERROR;
ucnv_fromUnicode(cnv , &cTarget, cTargetLimit, &mySource, mySourceLimit, NULL, (UBool) flush, &errorCode);
*sourceOffset = (mySource - uSource.get()) - *sourceOffset;
*targetOffset = (reinterpret_cast<jbyte*>(cTarget) - uTarget.get()) - *targetOffset;
// If there was an error, count the problematic characters.
if (errorCode == U_ILLEGAL_CHAR_FOUND || errorCode == U_INVALID_CHAR_FOUND) {
int8_t invalidUCharCount = 32;
UChar invalidUChars[32];
UErrorCode minorErrorCode = U_ZERO_ERROR;
ucnv_getInvalidUChars(cnv, invalidUChars, &invalidUCharCount, &minorErrorCode);
if (U_SUCCESS(minorErrorCode)) {
myData[2] = invalidUCharCount;
}
}
// Managed code handles some cases; throw all other errors.
if (shouldCodecThrow(flush, errorCode)) {
maybeThrowIcuException(env, "ucnv_fromUnicode", errorCode);
}
return errorCode;
}
static jint NativeConverter_decode(JNIEnv* env, jclass, jlong address,
jbyteArray source, jint sourceEnd, jcharArray target, jint targetEnd,
jintArray data, jboolean flush) {
UConverter* cnv = toUConverter(address);
if (cnv == NULL) {
maybeThrowIcuException(env, "toUConverter", U_ILLEGAL_ARGUMENT_ERROR);
return U_ILLEGAL_ARGUMENT_ERROR;
}
ScopedByteArrayRO uSource(env, source);
if (uSource.get() == NULL) {
maybeThrowIcuException(env, "uSource", U_ILLEGAL_ARGUMENT_ERROR);
return U_ILLEGAL_ARGUMENT_ERROR;
}
ScopedCharArrayRW uTarget(env, target);
if (uTarget.get() == NULL) {
maybeThrowIcuException(env, "uTarget", U_ILLEGAL_ARGUMENT_ERROR);
return U_ILLEGAL_ARGUMENT_ERROR;
}
ScopedIntArrayRW myData(env, data);
if (myData.get() == NULL) {
maybeThrowIcuException(env, "myData", U_ILLEGAL_ARGUMENT_ERROR);
return U_ILLEGAL_ARGUMENT_ERROR;
}
// Do the conversion.
jint* sourceOffset = &myData[0];
jint* targetOffset = &myData[1];
const char* mySource = reinterpret_cast<const char*>(uSource.get() + *sourceOffset);
const char* mySourceLimit = reinterpret_cast<const char*>(uSource.get() + sourceEnd);
UChar* cTarget = uTarget.get() + *targetOffset;
const UChar* cTargetLimit = uTarget.get() + targetEnd;
UErrorCode errorCode = U_ZERO_ERROR;
ucnv_toUnicode(cnv, &cTarget, cTargetLimit, &mySource, mySourceLimit, NULL, flush, &errorCode);
*sourceOffset = mySource - reinterpret_cast<const char*>(uSource.get()) - *sourceOffset;
*targetOffset = cTarget - uTarget.get() - *targetOffset;
// If there was an error, count the problematic bytes.
if (errorCode == U_ILLEGAL_CHAR_FOUND || errorCode == U_INVALID_CHAR_FOUND) {
int8_t invalidByteCount = 32;
char invalidBytes[32] = {'\0'};
UErrorCode minorErrorCode = U_ZERO_ERROR;
ucnv_getInvalidChars(cnv, invalidBytes, &invalidByteCount, &minorErrorCode);
if (U_SUCCESS(minorErrorCode)) {
myData[2] = invalidByteCount;
}
}
// Managed code handles some cases; throw all other errors.
if (shouldCodecThrow(flush, errorCode)) {
maybeThrowIcuException(env, "ucnv_toUnicode", errorCode);
}
return errorCode;
}
static void NativeConverter_resetByteToChar(JNIEnv*, jclass, jlong address) {
UConverter* cnv = toUConverter(address);
if (cnv) {
ucnv_resetToUnicode(cnv);
}
}
static void NativeConverter_resetCharToByte(JNIEnv*, jclass, jlong address) {
UConverter* cnv = toUConverter(address);
if (cnv) {
ucnv_resetFromUnicode(cnv);
}
}
static jint NativeConverter_getMaxBytesPerChar(JNIEnv*, jclass, jlong address) {
UConverter* cnv = toUConverter(address);
return (cnv != NULL) ? ucnv_getMaxCharSize(cnv) : -1;
}
static jint NativeConverter_getMinBytesPerChar(JNIEnv*, jclass, jlong address) {
UConverter* cnv = toUConverter(address);
return (cnv != NULL) ? ucnv_getMinCharSize(cnv) : -1;
}
static jfloat NativeConverter_getAveBytesPerChar(JNIEnv*, jclass, jlong address) {
UConverter* cnv = toUConverter(address);
return (cnv != NULL) ? ((ucnv_getMaxCharSize(cnv) + ucnv_getMinCharSize(cnv)) / 2.0) : -1;
}
static jboolean NativeConverter_canEncode(JNIEnv*, jclass, jlong address, jint codeUnit) {
UErrorCode errorCode = U_ZERO_ERROR;
UConverter* cnv = toUConverter(address);
if (cnv == NULL) {
return JNI_FALSE;
}
UChar srcBuffer[3];
const UChar* src = &srcBuffer[0];
const UChar* srcLimit = (codeUnit < 0x10000) ? &src[1] : &src[2];
char dstBuffer[5];
char* dst = &dstBuffer[0];
const char* dstLimit = &dstBuffer[4];
int i = 0;
UTF_APPEND_CHAR(&srcBuffer[0], i, 2, codeUnit);
ucnv_fromUnicode(cnv, &dst, dstLimit, &src, srcLimit, NULL, TRUE, &errorCode);
return U_SUCCESS(errorCode);
}
/*
* If a charset listed in the IANA Charset Registry is supported by an implementation
* of the Java platform then its canonical name must be the name listed in the registry.
* Many charsets are given more than one name in the registry, in which case the registry
* identifies one of the names as MIME-preferred. If a charset has more than one registry
* name then its canonical name must be the MIME-preferred name and the other names in
* the registry must be valid aliases. If a supported charset is not listed in the IANA
* registry then its canonical name must begin with one of the strings "X-" or "x-".
*/
static jstring getJavaCanonicalName(JNIEnv* env, const char* icuCanonicalName) {
UErrorCode status = U_ZERO_ERROR;
// Check to see if this is a well-known MIME or IANA name.
const char* cName = NULL;
if ((cName = ucnv_getStandardName(icuCanonicalName, "MIME", &status)) != NULL) {
return env->NewStringUTF(cName);
} else if ((cName = ucnv_getStandardName(icuCanonicalName, "IANA", &status)) != NULL) {
return env->NewStringUTF(cName);
}
// Check to see if an alias already exists with "x-" prefix, if yes then
// make that the canonical name.
int32_t aliasCount = ucnv_countAliases(icuCanonicalName, &status);
for (int i = 0; i < aliasCount; ++i) {
const char* name = ucnv_getAlias(icuCanonicalName, i, &status);
if (name != NULL && name[0] == 'x' && name[1] == '-') {
return env->NewStringUTF(name);
}
}
// As a last resort, prepend "x-" to any alias and make that the canonical name.
status = U_ZERO_ERROR;
const char* name = ucnv_getStandardName(icuCanonicalName, "UTR22", &status);
if (name == NULL && strchr(icuCanonicalName, ',') != NULL) {
name = ucnv_getAlias(icuCanonicalName, 1, &status);
}
// If there is no UTR22 canonical name then just return the original name.
if (name == NULL) {
name = icuCanonicalName;
}
UniquePtr<char[]> result(new char[2 + strlen(name) + 1]);
strcpy(&result[0], "x-");
strcat(&result[0], name);
return env->NewStringUTF(&result[0]);
}
static jobjectArray NativeConverter_getAvailableCharsetNames(JNIEnv* env, jclass) {
int32_t num = ucnv_countAvailable();
jobjectArray result = env->NewObjectArray(num, JniConstants::stringClass, NULL);
if (result == NULL) {
return NULL;
}
for (int i = 0; i < num; ++i) {
const char* name = ucnv_getAvailableName(i);
ScopedLocalRef<jstring> javaCanonicalName(env, getJavaCanonicalName(env, name));
if (javaCanonicalName.get() == NULL) {
return NULL;
}
env->SetObjectArrayElement(result, i, javaCanonicalName.get());
if (env->ExceptionCheck()) {
return NULL;
}
}
return result;
}
static jobjectArray getAliases(JNIEnv* env, const char* icuCanonicalName) {
// Get an upper bound on the number of aliases...
const char* myEncName = icuCanonicalName;
UErrorCode error = U_ZERO_ERROR;
size_t aliasCount = ucnv_countAliases(myEncName, &error);
if (aliasCount == 0 && myEncName[0] == 'x' && myEncName[1] == '-') {
myEncName = myEncName + 2;
aliasCount = ucnv_countAliases(myEncName, &error);
}
if (!U_SUCCESS(error)) {
return NULL;
}
// Collect the aliases we want...
std::vector<std::string> aliases;
for (size_t i = 0; i < aliasCount; ++i) {
const char* name = ucnv_getAlias(myEncName, i, &error);
if (!U_SUCCESS(error)) {
return NULL;
}
// TODO: why do we ignore these ones?
if (strchr(name, '+') == 0 && strchr(name, ',') == 0) {
aliases.push_back(name);
}
}
return toStringArray(env, aliases);
}
static const char* getICUCanonicalName(const char* name) {
UErrorCode error = U_ZERO_ERROR;
const char* canonicalName = NULL;
if ((canonicalName = ucnv_getCanonicalName(name, "MIME", &error)) != NULL) {
return canonicalName;
} else if((canonicalName = ucnv_getCanonicalName(name, "IANA", &error)) != NULL) {
return canonicalName;
} else if((canonicalName = ucnv_getCanonicalName(name, "", &error)) != NULL) {
return canonicalName;
} else if((canonicalName = ucnv_getAlias(name, 0, &error)) != NULL) {
/* we have some aliases in the form x-blah .. match those first */
return canonicalName;
} else if (strstr(name, "x-") == name) {
/* check if the converter can be opened with the name given */
error = U_ZERO_ERROR;
UniqueUConverter cnv(ucnv_open(name + 2, &error));
if (cnv.get() != NULL) {
return name + 2;
}
}
return NULL;
}
static void CHARSET_ENCODER_CALLBACK(const void* rawContext, UConverterFromUnicodeArgs* args,
const UChar* codeUnits, int32_t length, UChar32 codePoint, UConverterCallbackReason reason,
UErrorCode* status) {
if (!rawContext) {
return;
}
const EncoderCallbackContext* ctx = reinterpret_cast<const EncoderCallbackContext*>(rawContext);
switch(reason) {
case UCNV_UNASSIGNED:
ctx->onUnmappableInput(ctx, args, codeUnits, length, codePoint, reason, status);
return;
case UCNV_ILLEGAL:
case UCNV_IRREGULAR:
ctx->onMalformedInput(ctx, args, codeUnits, length, codePoint, reason, status);
return;
case UCNV_CLOSE:
delete ctx;
return;
default:
*status = U_ILLEGAL_ARGUMENT_ERROR;
return;
}
}
static void encoderReplaceCallback(const void* rawContext,
UConverterFromUnicodeArgs* fromArgs, const UChar*, int32_t, UChar32,
UConverterCallbackReason, UErrorCode * err) {
if (rawContext == NULL) {
return;
}
const EncoderCallbackContext* context = reinterpret_cast<const EncoderCallbackContext*>(rawContext);
*err = U_ZERO_ERROR;
ucnv_cbFromUWriteBytes(fromArgs, context->replacementBytes, context->replacementByteCount, 0, err);
}
static UConverterFromUCallback getFromUCallback(int32_t mode) {
switch(mode) {
case NativeConverter_IGNORE: return UCNV_FROM_U_CALLBACK_SKIP;
case NativeConverter_REPLACE: return encoderReplaceCallback;
case NativeConverter_REPORT: return UCNV_FROM_U_CALLBACK_STOP;
}
abort();
}
static void NativeConverter_setCallbackEncode(JNIEnv* env, jclass, jlong address,
jint onMalformedInput, jint onUnmappableInput, jbyteArray javaReplacement) {
UConverter* cnv = toUConverter(address);
if (cnv == NULL) {
maybeThrowIcuException(env, "toUConverter", U_ILLEGAL_ARGUMENT_ERROR);
return;
}
UConverterFromUCallback oldCallback = NULL;
const void* oldCallbackContext = NULL;
ucnv_getFromUCallBack(cnv, &oldCallback, const_cast<const void**>(&oldCallbackContext));
EncoderCallbackContext* callbackContext = const_cast<EncoderCallbackContext*>(
reinterpret_cast<const EncoderCallbackContext*>(oldCallbackContext));
if (callbackContext == NULL) {
callbackContext = new EncoderCallbackContext;
}
callbackContext->onMalformedInput = getFromUCallback(onMalformedInput);
callbackContext->onUnmappableInput = getFromUCallback(onUnmappableInput);
ScopedByteArrayRO replacementBytes(env, javaReplacement);
if (replacementBytes.get() == NULL) {
maybeThrowIcuException(env, "replacementBytes", U_ILLEGAL_ARGUMENT_ERROR);
return;
}
memcpy(callbackContext->replacementBytes, replacementBytes.get(), replacementBytes.size());
callbackContext->replacementByteCount = replacementBytes.size();
UErrorCode errorCode = U_ZERO_ERROR;
ucnv_setFromUCallBack(cnv, CHARSET_ENCODER_CALLBACK, callbackContext, NULL, NULL, &errorCode);
maybeThrowIcuException(env, "ucnv_setFromUCallBack", errorCode);
}
static void decoderIgnoreCallback(const void*, UConverterToUnicodeArgs*, const char*, int32_t, UConverterCallbackReason, UErrorCode* err) {
// The icu4c UCNV_FROM_U_CALLBACK_SKIP callback requires that the context is NULL, which is
// never true for us.
*err = U_ZERO_ERROR;
}
static void decoderReplaceCallback(const void* rawContext,
UConverterToUnicodeArgs* toArgs, const char*, int32_t, UConverterCallbackReason,
UErrorCode* err) {
if (!rawContext) {
return;
}
const DecoderCallbackContext* context = reinterpret_cast<const DecoderCallbackContext*>(rawContext);
*err = U_ZERO_ERROR;
ucnv_cbToUWriteUChars(toArgs,context->replacementChars, context->replacementCharCount, 0, err);
}
static UConverterToUCallback getToUCallback(int32_t mode) {
switch (mode) {
case NativeConverter_IGNORE: return decoderIgnoreCallback;
case NativeConverter_REPLACE: return decoderReplaceCallback;
case NativeConverter_REPORT: return UCNV_TO_U_CALLBACK_STOP;
}
abort();
}
static void CHARSET_DECODER_CALLBACK(const void* rawContext, UConverterToUnicodeArgs* args,
const char* codeUnits, int32_t length,
UConverterCallbackReason reason, UErrorCode* status) {
if (!rawContext) {
return;
}
const DecoderCallbackContext* ctx = reinterpret_cast<const DecoderCallbackContext*>(rawContext);
switch(reason) {
case UCNV_UNASSIGNED:
ctx->onUnmappableInput(ctx, args, codeUnits, length, reason, status);
return;
case UCNV_ILLEGAL:
case UCNV_IRREGULAR:
ctx->onMalformedInput(ctx, args, codeUnits, length, reason, status);
return;
case UCNV_CLOSE:
delete ctx;
return;
default:
*status = U_ILLEGAL_ARGUMENT_ERROR;
return;
}
}
static void NativeConverter_setCallbackDecode(JNIEnv* env, jclass, jlong address,
jint onMalformedInput, jint onUnmappableInput, jstring javaReplacement) {
UConverter* cnv = toUConverter(address);
if (cnv == NULL) {
maybeThrowIcuException(env, "toConverter", U_ILLEGAL_ARGUMENT_ERROR);
return;
}
UConverterToUCallback oldCallback;
const void* oldCallbackContext;
ucnv_getToUCallBack(cnv, &oldCallback, &oldCallbackContext);
DecoderCallbackContext* callbackContext = const_cast<DecoderCallbackContext*>(
reinterpret_cast<const DecoderCallbackContext*>(oldCallbackContext));
if (callbackContext == NULL) {
callbackContext = new DecoderCallbackContext;
}
callbackContext->onMalformedInput = getToUCallback(onMalformedInput);
callbackContext->onUnmappableInput = getToUCallback(onUnmappableInput);
ScopedStringChars replacement(env, javaReplacement);
if (replacement.get() == NULL) {
maybeThrowIcuException(env, "replacement", U_ILLEGAL_ARGUMENT_ERROR);
return;
}
u_strncpy(callbackContext->replacementChars, replacement.get(), replacement.size());
callbackContext->replacementCharCount = replacement.size();
UErrorCode errorCode = U_ZERO_ERROR;
ucnv_setToUCallBack(cnv, CHARSET_DECODER_CALLBACK, callbackContext, NULL, NULL, &errorCode);
maybeThrowIcuException(env, "ucnv_setToUCallBack", errorCode);
}
static jfloat NativeConverter_getAveCharsPerByte(JNIEnv* env, jclass, jlong handle) {
return (1 / (jfloat) NativeConverter_getMaxBytesPerChar(env, NULL, handle));
}
static jbyteArray NativeConverter_getSubstitutionBytes(JNIEnv* env, jclass, jlong address) {
UConverter* cnv = toUConverter(address);
if (cnv == NULL) {
return NULL;
}
UErrorCode status = U_ZERO_ERROR;
char replacementBytes[MAX_REPLACEMENT_LENGTH];
int8_t len = sizeof(replacementBytes);
ucnv_getSubstChars(cnv, replacementBytes, &len, &status);
if (!U_SUCCESS(status)) {
return env->NewByteArray(0);
}
jbyteArray result = env->NewByteArray(len);
if (result == NULL) {
return NULL;
}
env->SetByteArrayRegion(result, 0, len, reinterpret_cast<jbyte*>(replacementBytes));
return result;
}
static jboolean NativeConverter_contains(JNIEnv* env, jclass, jstring name1, jstring name2) {
ScopedUtfChars name1Chars(env, name1);
if (name1Chars.c_str() == NULL) {
return JNI_FALSE;
}
ScopedUtfChars name2Chars(env, name2);
if (name2Chars.c_str() == NULL) {
return JNI_FALSE;
}
UErrorCode errorCode = U_ZERO_ERROR;
UniqueUConverter converter1(ucnv_open(name1Chars.c_str(), &errorCode));
UnicodeSet set1;
ucnv_getUnicodeSet(converter1.get(), set1.toUSet(), UCNV_ROUNDTRIP_SET, &errorCode);
UniqueUConverter converter2(ucnv_open(name2Chars.c_str(), &errorCode));
UnicodeSet set2;
ucnv_getUnicodeSet(converter2.get(), set2.toUSet(), UCNV_ROUNDTRIP_SET, &errorCode);
return U_SUCCESS(errorCode) && set1.containsAll(set2);
}
static jobject NativeConverter_charsetForName(JNIEnv* env, jclass, jstring charsetName) {
ScopedUtfChars charsetNameChars(env, charsetName);
if (charsetNameChars.c_str() == NULL) {
return NULL;
}
// Get ICU's canonical name for this charset.
const char* icuCanonicalName = getICUCanonicalName(charsetNameChars.c_str());
if (icuCanonicalName == NULL) {
return NULL;
}
// Get Java's canonical name for this charset.
jstring javaCanonicalName = getJavaCanonicalName(env, icuCanonicalName);
if (env->ExceptionCheck()) {
return NULL;
}
// Check that this charset is supported.
// ICU doesn't offer any "isSupported", so we just open and immediately close.
// We ignore the UErrorCode because ucnv_open returning NULL is all the information we need.
UErrorCode dummy = U_ZERO_ERROR;
UniqueUConverter cnv(ucnv_open(icuCanonicalName, &dummy));
if (cnv.get() == NULL) {
return NULL;
}
cnv.reset();
// Get the aliases for this charset.
jobjectArray aliases = getAliases(env, icuCanonicalName);
if (env->ExceptionCheck()) {
return NULL;
}
// Construct the CharsetICU object.
static jmethodID charsetConstructor = env->GetMethodID(JniConstants::charsetICUClass, "<init>",
"(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V");
if (env->ExceptionCheck()) {
return NULL;
}
return env->NewObject(JniConstants::charsetICUClass, charsetConstructor,
javaCanonicalName, env->NewStringUTF(icuCanonicalName), aliases);
}
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(NativeConverter, canEncode, "(JI)Z"),
NATIVE_METHOD(NativeConverter, charsetForName, "(Ljava/lang/String;)Ljava/nio/charset/Charset;"),
NATIVE_METHOD(NativeConverter, closeConverter, "(J)V"),
NATIVE_METHOD(NativeConverter, contains, "(Ljava/lang/String;Ljava/lang/String;)Z"),
NATIVE_METHOD(NativeConverter, decode, "(J[BI[CI[IZ)I"),
NATIVE_METHOD(NativeConverter, encode, "(J[CI[BI[IZ)I"),
NATIVE_METHOD(NativeConverter, getAvailableCharsetNames, "()[Ljava/lang/String;"),
NATIVE_METHOD(NativeConverter, getAveBytesPerChar, "(J)F"),
NATIVE_METHOD(NativeConverter, getAveCharsPerByte, "(J)F"),
NATIVE_METHOD(NativeConverter, getMaxBytesPerChar, "(J)I"),
NATIVE_METHOD(NativeConverter, getMinBytesPerChar, "(J)I"),
NATIVE_METHOD(NativeConverter, getSubstitutionBytes, "(J)[B"),
NATIVE_METHOD(NativeConverter, openConverter, "(Ljava/lang/String;)J"),
NATIVE_METHOD(NativeConverter, resetByteToChar, "(J)V"),
NATIVE_METHOD(NativeConverter, resetCharToByte, "(J)V"),
NATIVE_METHOD(NativeConverter, setCallbackDecode, "(JIILjava/lang/String;)V"),
NATIVE_METHOD(NativeConverter, setCallbackEncode, "(JII[B)V"),
};
void register_libcore_icu_NativeConverter(JNIEnv* env) {
jniRegisterNativeMethods(env, "libcore/icu/NativeConverter", gMethods, NELEM(gMethods));
}