Home | History | Annotate | Download | only in native
      1 /**
      2 *******************************************************************************
      3 * Copyright (C) 1996-2006, International Business Machines Corporation and    *
      4 * others. All Rights Reserved.                                                *
      5 *******************************************************************************
      6 *
      7 *
      8 *******************************************************************************
      9 */
     10 /*
     11  * (C) Copyright IBM Corp. 2000 - All Rights Reserved
     12  *  A JNI wrapper to ICU native converter Interface
     13  * @author: Ram Viswanadha
     14  */
     15 
     16 #define LOG_TAG "NativeConverter"
     17 
     18 #include "JNIHelp.h"
     19 #include "JniConstants.h"
     20 #include "JniException.h"
     21 #include "ScopedLocalRef.h"
     22 #include "ScopedPrimitiveArray.h"
     23 #include "ScopedStringChars.h"
     24 #include "ScopedUtfChars.h"
     25 #include "UniquePtr.h"
     26 #include "cutils/log.h"
     27 #include "toStringArray.h"
     28 #include "unicode/ucnv.h"
     29 #include "unicode/ucnv_cb.h"
     30 #include "unicode/uniset.h"
     31 #include "unicode/ustring.h"
     32 #include "unicode/utypes.h"
     33 
     34 #include <vector>
     35 
     36 #include <stdlib.h>
     37 #include <string.h>
     38 
     39 #define NativeConverter_REPORT 0
     40 #define NativeConverter_IGNORE 1
     41 #define NativeConverter_REPLACE 2
     42 
     43 #define MAX_REPLACEMENT_LENGTH 32 // equivalent to UCNV_ERROR_BUFFER_LENGTH
     44 
     45 struct DecoderCallbackContext {
     46     UChar replacementChars[MAX_REPLACEMENT_LENGTH];
     47     size_t replacementCharCount;
     48     UConverterToUCallback onUnmappableInput;
     49     UConverterToUCallback onMalformedInput;
     50 };
     51 
     52 struct EncoderCallbackContext {
     53     char replacementBytes[MAX_REPLACEMENT_LENGTH];
     54     size_t replacementByteCount;
     55     UConverterFromUCallback onUnmappableInput;
     56     UConverterFromUCallback onMalformedInput;
     57 };
     58 
     59 struct UConverterDeleter {
     60     void operator()(UConverter* p) const {
     61         ucnv_close(p);
     62     }
     63 };
     64 typedef UniquePtr<UConverter, UConverterDeleter> UniqueUConverter;
     65 
     66 static UConverter* toUConverter(jlong address) {
     67     return reinterpret_cast<UConverter*>(static_cast<uintptr_t>(address));
     68 }
     69 
     70 static jlong NativeConverter_openConverter(JNIEnv* env, jclass, jstring converterName) {
     71     ScopedUtfChars converterNameChars(env, converterName);
     72     if (converterNameChars.c_str() == NULL) {
     73         return 0;
     74     }
     75     UErrorCode status = U_ZERO_ERROR;
     76     UConverter* cnv = ucnv_open(converterNameChars.c_str(), &status);
     77     maybeThrowIcuException(env, status);
     78     return reinterpret_cast<uintptr_t>(cnv);
     79 }
     80 
     81 static void NativeConverter_closeConverter(JNIEnv*, jclass, jlong address) {
     82     ucnv_close(toUConverter(address));
     83 }
     84 
     85 static jint NativeConverter_encode(JNIEnv* env, jclass, jlong address,
     86         jcharArray source, jint sourceEnd, jbyteArray target, jint targetEnd,
     87         jintArray data, jboolean flush) {
     88 
     89     UConverter* cnv = toUConverter(address);
     90     if (cnv == NULL) {
     91         return U_ILLEGAL_ARGUMENT_ERROR;
     92     }
     93     ScopedCharArrayRO uSource(env, source);
     94     if (uSource.get() == NULL) {
     95         return U_ILLEGAL_ARGUMENT_ERROR;
     96     }
     97     ScopedByteArrayRW uTarget(env, target);
     98     if (uTarget.get() == NULL) {
     99         return U_ILLEGAL_ARGUMENT_ERROR;
    100     }
    101     ScopedIntArrayRW myData(env, data);
    102     if (myData.get() == NULL) {
    103         return U_ILLEGAL_ARGUMENT_ERROR;
    104     }
    105 
    106     // Do the conversion.
    107     jint* sourceOffset = &myData[0];
    108     jint* targetOffset = &myData[1];
    109     const jchar* mySource = uSource.get() + *sourceOffset;
    110     const UChar* mySourceLimit= uSource.get() + sourceEnd;
    111     char* cTarget = reinterpret_cast<char*>(uTarget.get() + *targetOffset);
    112     const char* cTargetLimit = reinterpret_cast<const char*>(uTarget.get() + targetEnd);
    113     UErrorCode errorCode = U_ZERO_ERROR;
    114     ucnv_fromUnicode(cnv , &cTarget, cTargetLimit, &mySource, mySourceLimit, NULL, (UBool) flush, &errorCode);
    115     *sourceOffset = (mySource - uSource.get()) - *sourceOffset;
    116     *targetOffset = (reinterpret_cast<jbyte*>(cTarget) - uTarget.get()) - *targetOffset;
    117 
    118     // If there was an error, count the problematic characters.
    119     if (errorCode == U_ILLEGAL_CHAR_FOUND || errorCode == U_INVALID_CHAR_FOUND) {
    120         int8_t invalidUCharCount = 32;
    121         UChar invalidUChars[32];
    122         UErrorCode minorErrorCode = U_ZERO_ERROR;
    123         ucnv_getInvalidUChars(cnv, invalidUChars, &invalidUCharCount, &minorErrorCode);
    124         if (U_SUCCESS(minorErrorCode)) {
    125             myData[2] = invalidUCharCount;
    126         }
    127     }
    128     return errorCode;
    129 }
    130 
    131 static jint NativeConverter_decode(JNIEnv* env, jclass, jlong address,
    132         jbyteArray source, jint sourceEnd, jcharArray target, jint targetEnd,
    133         jintArray data, jboolean flush) {
    134 
    135     UConverter* cnv = toUConverter(address);
    136     if (cnv == NULL) {
    137         return U_ILLEGAL_ARGUMENT_ERROR;
    138     }
    139     ScopedByteArrayRO uSource(env, source);
    140     if (uSource.get() == NULL) {
    141         return U_ILLEGAL_ARGUMENT_ERROR;
    142     }
    143     ScopedCharArrayRW uTarget(env, target);
    144     if (uTarget.get() == NULL) {
    145         return U_ILLEGAL_ARGUMENT_ERROR;
    146     }
    147     ScopedIntArrayRW myData(env, data);
    148     if (myData.get() == NULL) {
    149         return U_ILLEGAL_ARGUMENT_ERROR;
    150     }
    151 
    152     // Do the conversion.
    153     jint* sourceOffset = &myData[0];
    154     jint* targetOffset = &myData[1];
    155     const char* mySource = reinterpret_cast<const char*>(uSource.get() + *sourceOffset);
    156     const char* mySourceLimit = reinterpret_cast<const char*>(uSource.get() + sourceEnd);
    157     UChar* cTarget = uTarget.get() + *targetOffset;
    158     const UChar* cTargetLimit = uTarget.get() + targetEnd;
    159     UErrorCode errorCode = U_ZERO_ERROR;
    160     ucnv_toUnicode(cnv, &cTarget, cTargetLimit, &mySource, mySourceLimit, NULL, flush, &errorCode);
    161     *sourceOffset = mySource - reinterpret_cast<const char*>(uSource.get()) - *sourceOffset;
    162     *targetOffset = cTarget - uTarget.get() - *targetOffset;
    163 
    164     // If there was an error, count the problematic bytes.
    165     if (errorCode == U_ILLEGAL_CHAR_FOUND || errorCode == U_INVALID_CHAR_FOUND) {
    166         int8_t invalidByteCount = 32;
    167         char invalidBytes[32] = {'\0'};
    168         UErrorCode minorErrorCode = U_ZERO_ERROR;
    169         ucnv_getInvalidChars(cnv, invalidBytes, &invalidByteCount, &minorErrorCode);
    170         if (U_SUCCESS(minorErrorCode)) {
    171             myData[2] = invalidByteCount;
    172         }
    173     }
    174 
    175     return errorCode;
    176 }
    177 
    178 static void NativeConverter_resetByteToChar(JNIEnv*, jclass, jlong address) {
    179     UConverter* cnv = toUConverter(address);
    180     if (cnv) {
    181         ucnv_resetToUnicode(cnv);
    182     }
    183 }
    184 
    185 static void NativeConverter_resetCharToByte(JNIEnv*, jclass, jlong address) {
    186     UConverter* cnv = toUConverter(address);
    187     if (cnv) {
    188         ucnv_resetFromUnicode(cnv);
    189     }
    190 }
    191 
    192 static jint NativeConverter_getMaxBytesPerChar(JNIEnv*, jclass, jlong address) {
    193     UConverter* cnv = toUConverter(address);
    194     return (cnv != NULL) ? ucnv_getMaxCharSize(cnv) : -1;
    195 }
    196 
    197 static jint NativeConverter_getMinBytesPerChar(JNIEnv*, jclass, jlong address) {
    198     UConverter* cnv = toUConverter(address);
    199     return (cnv != NULL) ? ucnv_getMinCharSize(cnv) : -1;
    200 }
    201 
    202 static jfloat NativeConverter_getAveBytesPerChar(JNIEnv*, jclass, jlong address) {
    203     UConverter* cnv = toUConverter(address);
    204     return (cnv != NULL) ? ((ucnv_getMaxCharSize(cnv) + ucnv_getMinCharSize(cnv)) / 2.0) : -1;
    205 }
    206 
    207 static jboolean NativeConverter_canEncode(JNIEnv*, jclass, jlong address, jint codeUnit) {
    208     UErrorCode errorCode = U_ZERO_ERROR;
    209     UConverter* cnv = toUConverter(address);
    210     if (cnv == NULL) {
    211         return JNI_FALSE;
    212     }
    213 
    214     UChar srcBuffer[3];
    215     const UChar* src = &srcBuffer[0];
    216     const UChar* srcLimit = (codeUnit < 0x10000) ? &src[1] : &src[2];
    217 
    218     char dstBuffer[5];
    219     char* dst = &dstBuffer[0];
    220     const char* dstLimit = &dstBuffer[4];
    221 
    222     int i = 0;
    223     UTF_APPEND_CHAR(&srcBuffer[0], i, 2, codeUnit);
    224 
    225     ucnv_fromUnicode(cnv, &dst, dstLimit, &src, srcLimit, NULL, TRUE, &errorCode);
    226     return U_SUCCESS(errorCode);
    227 }
    228 
    229 /*
    230  * If a charset listed in the IANA Charset Registry is supported by an implementation
    231  * of the Java platform then its canonical name must be the name listed in the registry.
    232  * Many charsets are given more than one name in the registry, in which case the registry
    233  * identifies one of the names as MIME-preferred. If a charset has more than one registry
    234  * name then its canonical name must be the MIME-preferred name and the other names in
    235  * the registry must be valid aliases. If a supported charset is not listed in the IANA
    236  * registry then its canonical name must begin with one of the strings "X-" or "x-".
    237  */
    238 static jstring getJavaCanonicalName(JNIEnv* env, const char* icuCanonicalName) {
    239     UErrorCode status = U_ZERO_ERROR;
    240 
    241     // Check to see if this is a well-known MIME or IANA name.
    242     const char* cName = NULL;
    243     if ((cName = ucnv_getStandardName(icuCanonicalName, "MIME", &status)) != NULL) {
    244         return env->NewStringUTF(cName);
    245     } else if ((cName = ucnv_getStandardName(icuCanonicalName, "IANA", &status)) != NULL) {
    246         return env->NewStringUTF(cName);
    247     }
    248 
    249     // Check to see if an alias already exists with "x-" prefix, if yes then
    250     // make that the canonical name.
    251     int32_t aliasCount = ucnv_countAliases(icuCanonicalName, &status);
    252     for (int i = 0; i < aliasCount; ++i) {
    253         const char* name = ucnv_getAlias(icuCanonicalName, i, &status);
    254         if (name != NULL && name[0] == 'x' && name[1] == '-') {
    255             return env->NewStringUTF(name);
    256         }
    257     }
    258 
    259     // As a last resort, prepend "x-" to any alias and make that the canonical name.
    260     status = U_ZERO_ERROR;
    261     const char* name = ucnv_getStandardName(icuCanonicalName, "UTR22", &status);
    262     if (name == NULL && strchr(icuCanonicalName, ',') != NULL) {
    263         name = ucnv_getAlias(icuCanonicalName, 1, &status);
    264     }
    265     // If there is no UTR22 canonical name then just return the original name.
    266     if (name == NULL) {
    267         name = icuCanonicalName;
    268     }
    269     UniquePtr<char[]> result(new char[2 + strlen(name) + 1]);
    270     strcpy(&result[0], "x-");
    271     strcat(&result[0], name);
    272     return env->NewStringUTF(&result[0]);
    273 }
    274 
    275 static jobjectArray NativeConverter_getAvailableCharsetNames(JNIEnv* env, jclass) {
    276     int32_t num = ucnv_countAvailable();
    277     jobjectArray result = env->NewObjectArray(num, JniConstants::stringClass, NULL);
    278     if (result == NULL) {
    279         return NULL;
    280     }
    281     for (int i = 0; i < num; ++i) {
    282         const char* name = ucnv_getAvailableName(i);
    283         ScopedLocalRef<jstring> javaCanonicalName(env, getJavaCanonicalName(env, name));
    284         if (javaCanonicalName.get() == NULL) {
    285             return NULL;
    286         }
    287         env->SetObjectArrayElement(result, i, javaCanonicalName.get());
    288         if (env->ExceptionCheck()) {
    289             return NULL;
    290         }
    291     }
    292     return result;
    293 }
    294 
    295 static jobjectArray getAliases(JNIEnv* env, const char* icuCanonicalName) {
    296     // Get an upper bound on the number of aliases...
    297     const char* myEncName = icuCanonicalName;
    298     UErrorCode error = U_ZERO_ERROR;
    299     size_t aliasCount = ucnv_countAliases(myEncName, &error);
    300     if (aliasCount == 0 && myEncName[0] == 'x' && myEncName[1] == '-') {
    301         myEncName = myEncName + 2;
    302         aliasCount = ucnv_countAliases(myEncName, &error);
    303     }
    304     if (!U_SUCCESS(error)) {
    305         return NULL;
    306     }
    307 
    308     // Collect the aliases we want...
    309     std::vector<std::string> aliases;
    310     for (size_t i = 0; i < aliasCount; ++i) {
    311         const char* name = ucnv_getAlias(myEncName, i, &error);
    312         if (!U_SUCCESS(error)) {
    313             return NULL;
    314         }
    315         // TODO: why do we ignore these ones?
    316         if (strchr(name, '+') == 0 && strchr(name, ',') == 0) {
    317             aliases.push_back(name);
    318         }
    319     }
    320     return toStringArray(env, aliases);
    321 }
    322 
    323 static const char* getICUCanonicalName(const char* name) {
    324     UErrorCode error = U_ZERO_ERROR;
    325     const char* canonicalName = NULL;
    326     if ((canonicalName = ucnv_getCanonicalName(name, "MIME", &error)) != NULL) {
    327         return canonicalName;
    328     } else if((canonicalName = ucnv_getCanonicalName(name, "IANA", &error)) != NULL) {
    329         return canonicalName;
    330     } else if((canonicalName = ucnv_getCanonicalName(name, "", &error)) != NULL) {
    331         return canonicalName;
    332     } else if((canonicalName =  ucnv_getAlias(name, 0, &error)) != NULL) {
    333         /* we have some aliases in the form x-blah .. match those first */
    334         return canonicalName;
    335     } else if (strstr(name, "x-") == name) {
    336         /* check if the converter can be opened with the name given */
    337         error = U_ZERO_ERROR;
    338         UniqueUConverter cnv(ucnv_open(name + 2, &error));
    339         if (cnv.get() != NULL) {
    340             return name + 2;
    341         }
    342     }
    343     return NULL;
    344 }
    345 
    346 static void CHARSET_ENCODER_CALLBACK(const void* rawContext, UConverterFromUnicodeArgs* args,
    347         const UChar* codeUnits, int32_t length, UChar32 codePoint, UConverterCallbackReason reason,
    348         UErrorCode* status) {
    349     if (!rawContext) {
    350         return;
    351     }
    352     const EncoderCallbackContext* ctx = reinterpret_cast<const EncoderCallbackContext*>(rawContext);
    353     switch(reason) {
    354     case UCNV_UNASSIGNED:
    355         ctx->onUnmappableInput(ctx, args, codeUnits, length, codePoint, reason, status);
    356         return;
    357     case UCNV_ILLEGAL:
    358     case UCNV_IRREGULAR:
    359         ctx->onMalformedInput(ctx, args, codeUnits, length, codePoint, reason, status);
    360         return;
    361     case UCNV_CLOSE:
    362         delete ctx;
    363         return;
    364     default:
    365         *status = U_ILLEGAL_ARGUMENT_ERROR;
    366         return;
    367     }
    368 }
    369 
    370 static void encoderReplaceCallback(const void* rawContext,
    371         UConverterFromUnicodeArgs* fromArgs, const UChar*, int32_t, UChar32,
    372         UConverterCallbackReason, UErrorCode * err) {
    373     if (rawContext == NULL) {
    374         return;
    375     }
    376     const EncoderCallbackContext* context = reinterpret_cast<const EncoderCallbackContext*>(rawContext);
    377     *err = U_ZERO_ERROR;
    378     ucnv_cbFromUWriteBytes(fromArgs, context->replacementBytes, context->replacementByteCount, 0, err);
    379 }
    380 
    381 static UConverterFromUCallback getFromUCallback(int32_t mode) {
    382     switch(mode) {
    383     case NativeConverter_IGNORE: return UCNV_FROM_U_CALLBACK_SKIP;
    384     case NativeConverter_REPLACE: return encoderReplaceCallback;
    385     case NativeConverter_REPORT: return UCNV_FROM_U_CALLBACK_STOP;
    386     }
    387     abort();
    388 }
    389 
    390 static jint NativeConverter_setCallbackEncode(JNIEnv* env, jclass, jlong address,
    391         jint onMalformedInput, jint onUnmappableInput, jbyteArray javaReplacement) {
    392     UConverter* cnv = toUConverter(address);
    393     if (!cnv) {
    394         return U_ILLEGAL_ARGUMENT_ERROR;
    395     }
    396 
    397     UConverterFromUCallback oldCallback = NULL;
    398     const void* oldCallbackContext = NULL;
    399     ucnv_getFromUCallBack(cnv, &oldCallback, const_cast<const void**>(&oldCallbackContext));
    400 
    401     EncoderCallbackContext* callbackContext = const_cast<EncoderCallbackContext*>(
    402             reinterpret_cast<const EncoderCallbackContext*>(oldCallbackContext));
    403     if (callbackContext == NULL) {
    404         callbackContext = new EncoderCallbackContext;
    405     }
    406 
    407     callbackContext->onMalformedInput = getFromUCallback(onMalformedInput);
    408     callbackContext->onUnmappableInput = getFromUCallback(onUnmappableInput);
    409 
    410     ScopedByteArrayRO replacementBytes(env, javaReplacement);
    411     if (replacementBytes.get() == NULL) {
    412         return U_ILLEGAL_ARGUMENT_ERROR;
    413     }
    414     memcpy(callbackContext->replacementBytes, replacementBytes.get(), replacementBytes.size());
    415     callbackContext->replacementByteCount = replacementBytes.size();
    416 
    417     UErrorCode errorCode = U_ZERO_ERROR;
    418     ucnv_setFromUCallBack(cnv, CHARSET_ENCODER_CALLBACK, callbackContext, NULL, NULL, &errorCode);
    419     return errorCode;
    420 }
    421 
    422 static void decoderIgnoreCallback(const void*, UConverterToUnicodeArgs*, const char*, int32_t, UConverterCallbackReason, UErrorCode* err) {
    423     // The icu4c UCNV_FROM_U_CALLBACK_SKIP callback requires that the context is NULL, which is
    424     // never true for us.
    425     *err = U_ZERO_ERROR;
    426 }
    427 
    428 static void decoderReplaceCallback(const void* rawContext,
    429         UConverterToUnicodeArgs* toArgs, const char*, int32_t, UConverterCallbackReason,
    430         UErrorCode* err) {
    431     if (!rawContext) {
    432         return;
    433     }
    434     const DecoderCallbackContext* context = reinterpret_cast<const DecoderCallbackContext*>(rawContext);
    435     *err = U_ZERO_ERROR;
    436     ucnv_cbToUWriteUChars(toArgs,context->replacementChars, context->replacementCharCount, 0, err);
    437 }
    438 
    439 static UConverterToUCallback getToUCallback(int32_t mode) {
    440     switch (mode) {
    441     case NativeConverter_IGNORE: return decoderIgnoreCallback;
    442     case NativeConverter_REPLACE: return decoderReplaceCallback;
    443     case NativeConverter_REPORT: return UCNV_TO_U_CALLBACK_STOP;
    444     }
    445     abort();
    446 }
    447 
    448 static void CHARSET_DECODER_CALLBACK(const void* rawContext, UConverterToUnicodeArgs* args,
    449         const char* codeUnits, int32_t length,
    450         UConverterCallbackReason reason, UErrorCode* status) {
    451     if (!rawContext) {
    452         return;
    453     }
    454     const DecoderCallbackContext* ctx = reinterpret_cast<const DecoderCallbackContext*>(rawContext);
    455     switch(reason) {
    456     case UCNV_UNASSIGNED:
    457         ctx->onUnmappableInput(ctx, args, codeUnits, length, reason, status);
    458         return;
    459     case UCNV_ILLEGAL:
    460     case UCNV_IRREGULAR:
    461         ctx->onMalformedInput(ctx, args, codeUnits, length, reason, status);
    462         return;
    463     case UCNV_CLOSE:
    464         delete ctx;
    465         return;
    466     default:
    467         *status = U_ILLEGAL_ARGUMENT_ERROR;
    468         return;
    469     }
    470 }
    471 
    472 static jint NativeConverter_setCallbackDecode(JNIEnv* env, jclass, jlong address,
    473         jint onMalformedInput, jint onUnmappableInput, jstring javaReplacement) {
    474     UConverter* cnv = toUConverter(address);
    475     if (cnv == NULL) {
    476         return U_ILLEGAL_ARGUMENT_ERROR;
    477     }
    478 
    479     UConverterToUCallback oldCallback;
    480     const void* oldCallbackContext;
    481     ucnv_getToUCallBack(cnv, &oldCallback, &oldCallbackContext);
    482 
    483     DecoderCallbackContext* callbackContext = const_cast<DecoderCallbackContext*>(
    484             reinterpret_cast<const DecoderCallbackContext*>(oldCallbackContext));
    485     if (callbackContext == NULL) {
    486         callbackContext = new DecoderCallbackContext;
    487     }
    488 
    489     callbackContext->onMalformedInput = getToUCallback(onMalformedInput);
    490     callbackContext->onUnmappableInput = getToUCallback(onUnmappableInput);
    491 
    492     ScopedStringChars replacement(env, javaReplacement);
    493     if (replacement.get() == NULL) {
    494         return U_ILLEGAL_ARGUMENT_ERROR;
    495     }
    496     u_strncpy(callbackContext->replacementChars, replacement.get(), replacement.size());
    497     callbackContext->replacementCharCount = replacement.size();
    498 
    499     UErrorCode errorCode = U_ZERO_ERROR;
    500     ucnv_setToUCallBack(cnv, CHARSET_DECODER_CALLBACK, callbackContext, NULL, NULL, &errorCode);
    501     return errorCode;
    502 }
    503 
    504 static jfloat NativeConverter_getAveCharsPerByte(JNIEnv* env, jclass, jlong handle) {
    505     return (1 / (jfloat) NativeConverter_getMaxBytesPerChar(env, NULL, handle));
    506 }
    507 
    508 static jbyteArray NativeConverter_getSubstitutionBytes(JNIEnv* env, jclass, jlong address) {
    509     UConverter* cnv = toUConverter(address);
    510     if (cnv == NULL) {
    511         return NULL;
    512     }
    513     UErrorCode status = U_ZERO_ERROR;
    514     char replacementBytes[MAX_REPLACEMENT_LENGTH];
    515     int8_t len = sizeof(replacementBytes);
    516     ucnv_getSubstChars(cnv, replacementBytes, &len, &status);
    517     if (!U_SUCCESS(status)) {
    518         return env->NewByteArray(0);
    519     }
    520     jbyteArray result = env->NewByteArray(len);
    521     if (result == NULL) {
    522         return NULL;
    523     }
    524     env->SetByteArrayRegion(result, 0, len, reinterpret_cast<jbyte*>(replacementBytes));
    525     return result;
    526 }
    527 
    528 static jboolean NativeConverter_contains(JNIEnv* env, jclass, jstring name1, jstring name2) {
    529     ScopedUtfChars name1Chars(env, name1);
    530     if (name1Chars.c_str() == NULL) {
    531         return JNI_FALSE;
    532     }
    533     ScopedUtfChars name2Chars(env, name2);
    534     if (name2Chars.c_str() == NULL) {
    535         return JNI_FALSE;
    536     }
    537 
    538     UErrorCode errorCode = U_ZERO_ERROR;
    539     UniqueUConverter converter1(ucnv_open(name1Chars.c_str(), &errorCode));
    540     UnicodeSet set1;
    541     ucnv_getUnicodeSet(converter1.get(), set1.toUSet(), UCNV_ROUNDTRIP_SET, &errorCode);
    542 
    543     UniqueUConverter converter2(ucnv_open(name2Chars.c_str(), &errorCode));
    544     UnicodeSet set2;
    545     ucnv_getUnicodeSet(converter2.get(), set2.toUSet(), UCNV_ROUNDTRIP_SET, &errorCode);
    546 
    547     return U_SUCCESS(errorCode) && set1.containsAll(set2);
    548 }
    549 
    550 static jobject NativeConverter_charsetForName(JNIEnv* env, jclass, jstring charsetName) {
    551     ScopedUtfChars charsetNameChars(env, charsetName);
    552     if (charsetNameChars.c_str() == NULL) {
    553         return NULL;
    554     }
    555     // Get ICU's canonical name for this charset.
    556     const char* icuCanonicalName = getICUCanonicalName(charsetNameChars.c_str());
    557     if (icuCanonicalName == NULL) {
    558         return NULL;
    559     }
    560     // Get Java's canonical name for this charset.
    561     jstring javaCanonicalName = getJavaCanonicalName(env, icuCanonicalName);
    562     if (env->ExceptionOccurred()) {
    563         return NULL;
    564     }
    565 
    566     // Check that this charset is supported.
    567     // ICU doesn't offer any "isSupported", so we just open and immediately close.
    568     // We ignore the UErrorCode because ucnv_open returning NULL is all the information we need.
    569     UErrorCode dummy = U_ZERO_ERROR;
    570     UniqueUConverter cnv(ucnv_open(icuCanonicalName, &dummy));
    571     if (cnv.get() == NULL) {
    572         return NULL;
    573     }
    574     cnv.reset();
    575 
    576     // Get the aliases for this charset.
    577     jobjectArray aliases = getAliases(env, icuCanonicalName);
    578     if (env->ExceptionOccurred()) {
    579         return NULL;
    580     }
    581 
    582     // Construct the CharsetICU object.
    583     jmethodID charsetConstructor = env->GetMethodID(JniConstants::charsetICUClass, "<init>",
    584             "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V");
    585     if (env->ExceptionOccurred()) {
    586         return NULL;
    587     }
    588     return env->NewObject(JniConstants::charsetICUClass, charsetConstructor,
    589             javaCanonicalName, env->NewStringUTF(icuCanonicalName), aliases);
    590 }
    591 
    592 static JNINativeMethod gMethods[] = {
    593     NATIVE_METHOD(NativeConverter, canEncode, "(JI)Z"),
    594     NATIVE_METHOD(NativeConverter, charsetForName, "(Ljava/lang/String;)Ljava/nio/charset/Charset;"),
    595     NATIVE_METHOD(NativeConverter, closeConverter, "(J)V"),
    596     NATIVE_METHOD(NativeConverter, contains, "(Ljava/lang/String;Ljava/lang/String;)Z"),
    597     NATIVE_METHOD(NativeConverter, decode, "(J[BI[CI[IZ)I"),
    598     NATIVE_METHOD(NativeConverter, encode, "(J[CI[BI[IZ)I"),
    599     NATIVE_METHOD(NativeConverter, getAvailableCharsetNames, "()[Ljava/lang/String;"),
    600     NATIVE_METHOD(NativeConverter, getAveBytesPerChar, "(J)F"),
    601     NATIVE_METHOD(NativeConverter, getAveCharsPerByte, "(J)F"),
    602     NATIVE_METHOD(NativeConverter, getMaxBytesPerChar, "(J)I"),
    603     NATIVE_METHOD(NativeConverter, getMinBytesPerChar, "(J)I"),
    604     NATIVE_METHOD(NativeConverter, getSubstitutionBytes, "(J)[B"),
    605     NATIVE_METHOD(NativeConverter, openConverter, "(Ljava/lang/String;)J"),
    606     NATIVE_METHOD(NativeConverter, resetByteToChar, "(J)V"),
    607     NATIVE_METHOD(NativeConverter, resetCharToByte, "(J)V"),
    608     NATIVE_METHOD(NativeConverter, setCallbackDecode, "(JIILjava/lang/String;)I"),
    609     NATIVE_METHOD(NativeConverter, setCallbackEncode, "(JII[B)I"),
    610 };
    611 void register_libcore_icu_NativeConverter(JNIEnv* env) {
    612     jniRegisterNativeMethods(env, "libcore/icu/NativeConverter", gMethods, NELEM(gMethods));
    613 }
    614