1adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/**
2adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project*******************************************************************************
3adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project* Copyright (C) 1996-2006, International Business Machines Corporation and    *
4adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project* others. All Rights Reserved.                                                *
5adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project*******************************************************************************
6adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project*
7adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project*
8adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project*******************************************************************************
9adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project*/
10adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/*
11adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * (C) Copyright IBM Corp. 2000 - All Rights Reserved
12adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *  A JNI wrapper to ICU native converter Interface
13adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * @author: Ram Viswanadha
14adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */
15adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
16757a7942eed2b0aa457f8517a0259d2ac82c5b18Elliott Hughes#define LOG_TAG "NativeConverter"
17757a7942eed2b0aa457f8517a0259d2ac82c5b18Elliott Hughes
18ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes#include "JNIHelp.h"
19a9f5c16a864ff63ba63f810410f8a27c086d5d52Elliott Hughes#include "JniConstants.h"
20bef9ec33e1368f57c731fce63b6a8c61628c64b0Elliott Hughes#include "JniException.h"
218044bf6b446c93cd29c5753544246316f269064fElliott Hughes#include "ScopedLocalRef.h"
22cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes#include "ScopedPrimitiveArray.h"
2337871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes#include "ScopedStringChars.h"
24ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes#include "ScopedUtfChars.h"
25ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes#include "UniquePtr.h"
26cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes#include "cutils/log.h"
27ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes#include "toStringArray.h"
28ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes#include "unicode/ucnv.h"
29ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes#include "unicode/ucnv_cb.h"
307d52d787302b862019da41aa753646d88d43fd61Elliott Hughes#include "unicode/uniset.h"
31ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes#include "unicode/ustring.h"
32ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes#include "unicode/utypes.h"
33ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes
34ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes#include <vector>
35ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes
36adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project#include <stdlib.h>
37adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project#include <string.h>
38adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
39c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughes#define NativeConverter_REPORT 0
40c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughes#define NativeConverter_IGNORE 1
41c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughes#define NativeConverter_REPLACE 2
424722f161d49b5c483aa7aec0daad7bfee18578bbElliott Hughes
4337871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes#define MAX_REPLACEMENT_LENGTH 32 // equivalent to UCNV_ERROR_BUFFER_LENGTH
4437871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes
45cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughesstruct DecoderCallbackContext {
4637871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    UChar replacementChars[MAX_REPLACEMENT_LENGTH];
4737871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    size_t replacementCharCount;
48cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    UConverterToUCallback onUnmappableInput;
49cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    UConverterToUCallback onMalformedInput;
50cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes};
51cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes
52cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughesstruct EncoderCallbackContext {
5337871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    char replacementBytes[MAX_REPLACEMENT_LENGTH];
5437871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    size_t replacementByteCount;
55cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    UConverterFromUCallback onUnmappableInput;
56cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    UConverterFromUCallback onMalformedInput;
57cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes};
58adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
597d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstruct UConverterDeleter {
607d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    void operator()(UConverter* p) const {
617d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        ucnv_close(p);
627d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    }
637d52d787302b862019da41aa753646d88d43fd61Elliott Hughes};
647d52d787302b862019da41aa753646d88d43fd61Elliott Hughestypedef UniquePtr<UConverter, UConverterDeleter> UniqueUConverter;
657d52d787302b862019da41aa753646d88d43fd61Elliott Hughes
66ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughesstatic UConverter* toUConverter(jlong address) {
67ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    return reinterpret_cast<UConverter*>(static_cast<uintptr_t>(address));
68ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes}
69ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes
707d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic jlong NativeConverter_openConverter(JNIEnv* env, jclass, jstring converterName) {
71bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    ScopedUtfChars converterNameChars(env, converterName);
7205960876dff6a5b686821eed8f7ae7cef5af4f50Elliott Hughes    if (converterNameChars.c_str() == NULL) {
73adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return 0;
74adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
75bef9ec33e1368f57c731fce63b6a8c61628c64b0Elliott Hughes    UErrorCode status = U_ZERO_ERROR;
76bef9ec33e1368f57c731fce63b6a8c61628c64b0Elliott Hughes    UConverter* cnv = ucnv_open(converterNameChars.c_str(), &status);
775ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    maybeThrowIcuException(env, "ucnv_open", status);
78ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    return reinterpret_cast<uintptr_t>(cnv);
79adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
80adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
817d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic void NativeConverter_closeConverter(JNIEnv*, jclass, jlong address) {
829b4866d3438f42fb2c0df788989261afe72bf713Elliott Hughes    ucnv_close(toUConverter(address));
83adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
84adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
855ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughesstatic bool shouldCodecThrow(jboolean flush, UErrorCode error) {
865ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    if (flush) {
875ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        return (error != U_BUFFER_OVERFLOW_ERROR && error != U_TRUNCATED_CHAR_FOUND);
885ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    } else {
895ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        return (error != U_BUFFER_OVERFLOW_ERROR && error != U_INVALID_CHAR_FOUND && error != U_ILLEGAL_CHAR_FOUND);
905ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    }
915ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes}
925ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes
937d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic jint NativeConverter_encode(JNIEnv* env, jclass, jlong address,
947d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        jcharArray source, jint sourceEnd, jbyteArray target, jint targetEnd,
957d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        jintArray data, jboolean flush) {
967d52d787302b862019da41aa753646d88d43fd61Elliott Hughes
97ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    UConverter* cnv = toUConverter(address);
9864101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    if (cnv == NULL) {
995ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "toUConverter", U_ILLEGAL_ARGUMENT_ERROR);
10064101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes        return U_ILLEGAL_ARGUMENT_ERROR;
10164101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    }
102ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    ScopedCharArrayRO uSource(env, source);
10364101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    if (uSource.get() == NULL) {
1045ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "uSource", U_ILLEGAL_ARGUMENT_ERROR);
10564101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes        return U_ILLEGAL_ARGUMENT_ERROR;
10664101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    }
107ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    ScopedByteArrayRW uTarget(env, target);
10864101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    if (uTarget.get() == NULL) {
1095ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "uTarget", U_ILLEGAL_ARGUMENT_ERROR);
11064101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes        return U_ILLEGAL_ARGUMENT_ERROR;
11164101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    }
112ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    ScopedIntArrayRW myData(env, data);
11364101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    if (myData.get() == NULL) {
1145ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "myData", U_ILLEGAL_ARGUMENT_ERROR);
11599c59bfa432e36933a7a5033fba8b89209f737bcElliott Hughes        return U_ILLEGAL_ARGUMENT_ERROR;
11699c59bfa432e36933a7a5033fba8b89209f737bcElliott Hughes    }
117ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes
118ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    // Do the conversion.
119ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    jint* sourceOffset = &myData[0];
120ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    jint* targetOffset = &myData[1];
121ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    const jchar* mySource = uSource.get() + *sourceOffset;
122ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    const UChar* mySourceLimit= uSource.get() + sourceEnd;
123ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    char* cTarget = reinterpret_cast<char*>(uTarget.get() + *targetOffset);
124ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    const char* cTargetLimit = reinterpret_cast<const char*>(uTarget.get() + targetEnd);
12599c59bfa432e36933a7a5033fba8b89209f737bcElliott Hughes    UErrorCode errorCode = U_ZERO_ERROR;
126ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    ucnv_fromUnicode(cnv , &cTarget, cTargetLimit, &mySource, mySourceLimit, NULL, (UBool) flush, &errorCode);
127ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    *sourceOffset = (mySource - uSource.get()) - *sourceOffset;
128ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    *targetOffset = (reinterpret_cast<jbyte*>(cTarget) - uTarget.get()) - *targetOffset;
129ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes
130ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    // If there was an error, count the problematic characters.
131ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    if (errorCode == U_ILLEGAL_CHAR_FOUND || errorCode == U_INVALID_CHAR_FOUND) {
1322981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes        int8_t invalidUCharCount = 32;
133ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes        UChar invalidUChars[32];
13433604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes        UErrorCode minorErrorCode = U_ZERO_ERROR;
1352981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes        ucnv_getInvalidUChars(cnv, invalidUChars, &invalidUCharCount, &minorErrorCode);
136ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes        if (U_SUCCESS(minorErrorCode)) {
1372981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes            myData[2] = invalidUCharCount;
138adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
139adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1405ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes
1415ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    // Managed code handles some cases; throw all other errors.
1425ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    if (shouldCodecThrow(flush, errorCode)) {
1435ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "ucnv_fromUnicode", errorCode);
1445ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    }
145adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    return errorCode;
146adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
147adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1487d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic jint NativeConverter_decode(JNIEnv* env, jclass, jlong address,
1497d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        jbyteArray source, jint sourceEnd, jcharArray target, jint targetEnd,
1507d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        jintArray data, jboolean flush) {
1517d52d787302b862019da41aa753646d88d43fd61Elliott Hughes
152ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    UConverter* cnv = toUConverter(address);
15364101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    if (cnv == NULL) {
1545ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "toUConverter", U_ILLEGAL_ARGUMENT_ERROR);
15564101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes        return U_ILLEGAL_ARGUMENT_ERROR;
15664101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    }
157ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    ScopedByteArrayRO uSource(env, source);
15864101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    if (uSource.get() == NULL) {
1595ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "uSource", U_ILLEGAL_ARGUMENT_ERROR);
16064101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes        return U_ILLEGAL_ARGUMENT_ERROR;
16164101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    }
162ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    ScopedCharArrayRW uTarget(env, target);
16364101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    if (uTarget.get() == NULL) {
1645ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "uTarget", U_ILLEGAL_ARGUMENT_ERROR);
16564101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes        return U_ILLEGAL_ARGUMENT_ERROR;
16664101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    }
167ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    ScopedIntArrayRW myData(env, data);
16864101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    if (myData.get() == NULL) {
1695ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "myData", U_ILLEGAL_ARGUMENT_ERROR);
170ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes        return U_ILLEGAL_ARGUMENT_ERROR;
171adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
172adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
173ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    // Do the conversion.
174ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    jint* sourceOffset = &myData[0];
175ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    jint* targetOffset = &myData[1];
176583ce47780cae3a014ca46534e08f5c2adc8fa88Elliott Hughes    const char* mySource = reinterpret_cast<const char*>(uSource.get() + *sourceOffset);
177ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    const char* mySourceLimit = reinterpret_cast<const char*>(uSource.get() + sourceEnd);
178ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    UChar* cTarget = uTarget.get() + *targetOffset;
179ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    const UChar* cTargetLimit = uTarget.get() + targetEnd;
180ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    UErrorCode errorCode = U_ZERO_ERROR;
181583ce47780cae3a014ca46534e08f5c2adc8fa88Elliott Hughes    ucnv_toUnicode(cnv, &cTarget, cTargetLimit, &mySource, mySourceLimit, NULL, flush, &errorCode);
182583ce47780cae3a014ca46534e08f5c2adc8fa88Elliott Hughes    *sourceOffset = mySource - reinterpret_cast<const char*>(uSource.get()) - *sourceOffset;
183ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    *targetOffset = cTarget - uTarget.get() - *targetOffset;
184ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes
185ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    // If there was an error, count the problematic bytes.
186ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    if (errorCode == U_ILLEGAL_CHAR_FOUND || errorCode == U_INVALID_CHAR_FOUND) {
1872981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes        int8_t invalidByteCount = 32;
1882981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes        char invalidBytes[32] = {'\0'};
1892981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes        UErrorCode minorErrorCode = U_ZERO_ERROR;
1902981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes        ucnv_getInvalidChars(cnv, invalidBytes, &invalidByteCount, &minorErrorCode);
191ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes        if (U_SUCCESS(minorErrorCode)) {
1922981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes            myData[2] = invalidByteCount;
193adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
194adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
195adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1965ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    // Managed code handles some cases; throw all other errors.
1975ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    if (shouldCodecThrow(flush, errorCode)) {
1985ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "ucnv_toUnicode", errorCode);
1995ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    }
200ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    return errorCode;
201adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
202adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2037d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic void NativeConverter_resetByteToChar(JNIEnv*, jclass, jlong address) {
204ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    UConverter* cnv = toUConverter(address);
205bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    if (cnv) {
206adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        ucnv_resetToUnicode(cnv);
207adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
208adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
209adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2107d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic void NativeConverter_resetCharToByte(JNIEnv*, jclass, jlong address) {
211ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    UConverter* cnv = toUConverter(address);
212bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    if (cnv) {
213adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        ucnv_resetFromUnicode(cnv);
214adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
215adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
216adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2177d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic jint NativeConverter_getMaxBytesPerChar(JNIEnv*, jclass, jlong address) {
218ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    UConverter* cnv = toUConverter(address);
219bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    return (cnv != NULL) ? ucnv_getMaxCharSize(cnv) : -1;
220adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
221adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2227d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic jint NativeConverter_getMinBytesPerChar(JNIEnv*, jclass, jlong address) {
223ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    UConverter* cnv = toUConverter(address);
224bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    return (cnv != NULL) ? ucnv_getMinCharSize(cnv) : -1;
225adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
226adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2277d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic jfloat NativeConverter_getAveBytesPerChar(JNIEnv*, jclass, jlong address) {
228ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    UConverter* cnv = toUConverter(address);
229c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughes    return (cnv != NULL) ? ((ucnv_getMaxCharSize(cnv) + ucnv_getMinCharSize(cnv)) / 2.0) : -1;
230adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
231bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes
2327d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic jboolean NativeConverter_canEncode(JNIEnv*, jclass, jlong address, jint codeUnit) {
233583ce47780cae3a014ca46534e08f5c2adc8fa88Elliott Hughes    UErrorCode errorCode = U_ZERO_ERROR;
234ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    UConverter* cnv = toUConverter(address);
235583ce47780cae3a014ca46534e08f5c2adc8fa88Elliott Hughes    if (cnv == NULL) {
236583ce47780cae3a014ca46534e08f5c2adc8fa88Elliott Hughes        return JNI_FALSE;
237adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
238583ce47780cae3a014ca46534e08f5c2adc8fa88Elliott Hughes
239583ce47780cae3a014ca46534e08f5c2adc8fa88Elliott Hughes    UChar srcBuffer[3];
240583ce47780cae3a014ca46534e08f5c2adc8fa88Elliott Hughes    const UChar* src = &srcBuffer[0];
241583ce47780cae3a014ca46534e08f5c2adc8fa88Elliott Hughes    const UChar* srcLimit = (codeUnit < 0x10000) ? &src[1] : &src[2];
242583ce47780cae3a014ca46534e08f5c2adc8fa88Elliott Hughes
243583ce47780cae3a014ca46534e08f5c2adc8fa88Elliott Hughes    char dstBuffer[5];
244583ce47780cae3a014ca46534e08f5c2adc8fa88Elliott Hughes    char* dst = &dstBuffer[0];
245583ce47780cae3a014ca46534e08f5c2adc8fa88Elliott Hughes    const char* dstLimit = &dstBuffer[4];
246583ce47780cae3a014ca46534e08f5c2adc8fa88Elliott Hughes
247583ce47780cae3a014ca46534e08f5c2adc8fa88Elliott Hughes    int i = 0;
248583ce47780cae3a014ca46534e08f5c2adc8fa88Elliott Hughes    UTF_APPEND_CHAR(&srcBuffer[0], i, 2, codeUnit);
249583ce47780cae3a014ca46534e08f5c2adc8fa88Elliott Hughes
250583ce47780cae3a014ca46534e08f5c2adc8fa88Elliott Hughes    ucnv_fromUnicode(cnv, &dst, dstLimit, &src, srcLimit, NULL, TRUE, &errorCode);
251583ce47780cae3a014ca46534e08f5c2adc8fa88Elliott Hughes    return U_SUCCESS(errorCode);
252adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
253adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
254ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes/*
255ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes * If a charset listed in the IANA Charset Registry is supported by an implementation
256ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes * of the Java platform then its canonical name must be the name listed in the registry.
257ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes * Many charsets are given more than one name in the registry, in which case the registry
258ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes * identifies one of the names as MIME-preferred. If a charset has more than one registry
259ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes * name then its canonical name must be the MIME-preferred name and the other names in
260ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes * the registry must be valid aliases. If a supported charset is not listed in the IANA
261ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes * registry then its canonical name must begin with one of the strings "X-" or "x-".
262ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes */
26344e0e560c92338110953ce806df475fedcdf926eBrian Carlstromstatic jstring getJavaCanonicalName(JNIEnv* env, const char* icuCanonicalName) {
264ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes    UErrorCode status = U_ZERO_ERROR;
265adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
266ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes    // Check to see if this is a well-known MIME or IANA name.
267adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    const char* cName = NULL;
268ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes    if ((cName = ucnv_getStandardName(icuCanonicalName, "MIME", &status)) != NULL) {
269ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes        return env->NewStringUTF(cName);
270ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes    } else if ((cName = ucnv_getStandardName(icuCanonicalName, "IANA", &status)) != NULL) {
271ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes        return env->NewStringUTF(cName);
272ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes    }
273ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes
274ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes    // Check to see if an alias already exists with "x-" prefix, if yes then
275ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes    // make that the canonical name.
276ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes    int32_t aliasCount = ucnv_countAliases(icuCanonicalName, &status);
277ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes    for (int i = 0; i < aliasCount; ++i) {
278ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes        const char* name = ucnv_getAlias(icuCanonicalName, i, &status);
279ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes        if (name != NULL && name[0] == 'x' && name[1] == '-') {
280ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes            return env->NewStringUTF(name);
281adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
282adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
283adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
284ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes    // As a last resort, prepend "x-" to any alias and make that the canonical name.
285ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes    status = U_ZERO_ERROR;
286ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes    const char* name = ucnv_getStandardName(icuCanonicalName, "UTR22", &status);
287ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes    if (name == NULL && strchr(icuCanonicalName, ',') != NULL) {
288ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes        name = ucnv_getAlias(icuCanonicalName, 1, &status);
289adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
290ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes    // If there is no UTR22 canonical name then just return the original name.
291ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes    if (name == NULL) {
292ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes        name = icuCanonicalName;
293ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes    }
294ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes    UniquePtr<char[]> result(new char[2 + strlen(name) + 1]);
295ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes    strcpy(&result[0], "x-");
296ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes    strcat(&result[0], name);
297ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes    return env->NewStringUTF(&result[0]);
298adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
299adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
3007d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic jobjectArray NativeConverter_getAvailableCharsetNames(JNIEnv* env, jclass) {
301bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    int32_t num = ucnv_countAvailable();
302a9f5c16a864ff63ba63f810410f8a27c086d5d52Elliott Hughes    jobjectArray result = env->NewObjectArray(num, JniConstants::stringClass, NULL);
303ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes    if (result == NULL) {
304ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes        return NULL;
305ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes    }
306bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    for (int i = 0; i < num; ++i) {
307bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes        const char* name = ucnv_getAvailableName(i);
3088044bf6b446c93cd29c5753544246316f269064fElliott Hughes        ScopedLocalRef<jstring> javaCanonicalName(env, getJavaCanonicalName(env, name));
309ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes        if (javaCanonicalName.get() == NULL) {
310ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes            return NULL;
311ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes        }
3128044bf6b446c93cd29c5753544246316f269064fElliott Hughes        env->SetObjectArrayElement(result, i, javaCanonicalName.get());
313ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes        if (env->ExceptionCheck()) {
314ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes            return NULL;
315ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes        }
316adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
317bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    return result;
318adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
319adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
320bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughesstatic jobjectArray getAliases(JNIEnv* env, const char* icuCanonicalName) {
321bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    // Get an upper bound on the number of aliases...
322bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    const char* myEncName = icuCanonicalName;
323adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    UErrorCode error = U_ZERO_ERROR;
324ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes    size_t aliasCount = ucnv_countAliases(myEncName, &error);
325bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    if (aliasCount == 0 && myEncName[0] == 'x' && myEncName[1] == '-') {
326bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes        myEncName = myEncName + 2;
327bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes        aliasCount = ucnv_countAliases(myEncName, &error);
328bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    }
329bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    if (!U_SUCCESS(error)) {
330bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes        return NULL;
331bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    }
332adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
333bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    // Collect the aliases we want...
334ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes    std::vector<std::string> aliases;
335ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes    for (size_t i = 0; i < aliasCount; ++i) {
336ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes        const char* name = ucnv_getAlias(myEncName, i, &error);
337bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes        if (!U_SUCCESS(error)) {
338bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes            return NULL;
339bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes        }
340bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes        // TODO: why do we ignore these ones?
341bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes        if (strchr(name, '+') == 0 && strchr(name, ',') == 0) {
342ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes            aliases.push_back(name);
343adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
344adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
345ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes    return toStringArray(env, aliases);
346adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
347adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
348bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughesstatic const char* getICUCanonicalName(const char* name) {
349adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    UErrorCode error = U_ZERO_ERROR;
350adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    const char* canonicalName = NULL;
351bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    if ((canonicalName = ucnv_getCanonicalName(name, "MIME", &error)) != NULL) {
352bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes        return canonicalName;
353bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    } else if((canonicalName = ucnv_getCanonicalName(name, "IANA", &error)) != NULL) {
354bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes        return canonicalName;
355bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    } else if((canonicalName = ucnv_getCanonicalName(name, "", &error)) != NULL) {
356bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes        return canonicalName;
357bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    } else if((canonicalName =  ucnv_getAlias(name, 0, &error)) != NULL) {
358bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes        /* we have some aliases in the form x-blah .. match those first */
359bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes        return canonicalName;
360bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    } else if (strstr(name, "x-") == name) {
361bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes        /* check if the converter can be opened with the name given */
362bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes        error = U_ZERO_ERROR;
3637d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        UniqueUConverter cnv(ucnv_open(name + 2, &error));
3647d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        if (cnv.get() != NULL) {
365bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes            return name + 2;
366adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
367adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
368bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    return NULL;
369adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
370adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
371cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughesstatic void CHARSET_ENCODER_CALLBACK(const void* rawContext, UConverterFromUnicodeArgs* args,
372cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        const UChar* codeUnits, int32_t length, UChar32 codePoint, UConverterCallbackReason reason,
373cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        UErrorCode* status) {
374cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    if (!rawContext) {
375cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        return;
376cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    }
377cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    const EncoderCallbackContext* ctx = reinterpret_cast<const EncoderCallbackContext*>(rawContext);
378cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    switch(reason) {
379cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    case UCNV_UNASSIGNED:
380cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        ctx->onUnmappableInput(ctx, args, codeUnits, length, codePoint, reason, status);
381cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        return;
382cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    case UCNV_ILLEGAL:
383cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    case UCNV_IRREGULAR:
384cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        ctx->onMalformedInput(ctx, args, codeUnits, length, codePoint, reason, status);
385cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        return;
3869b4866d3438f42fb2c0df788989261afe72bf713Elliott Hughes    case UCNV_CLOSE:
3879b4866d3438f42fb2c0df788989261afe72bf713Elliott Hughes        delete ctx;
3889b4866d3438f42fb2c0df788989261afe72bf713Elliott Hughes        return;
389cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    default:
390cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        *status = U_ILLEGAL_ARGUMENT_ERROR;
391cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        return;
39299c59bfa432e36933a7a5033fba8b89209f737bcElliott Hughes    }
393adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
394adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
395c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughesstatic void encoderReplaceCallback(const void* rawContext,
396acce5ff29455054faa08a10e1486a156b9d1553eElliott Hughes        UConverterFromUnicodeArgs* fromArgs, const UChar*, int32_t, UChar32,
397cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        UConverterCallbackReason, UErrorCode * err) {
398cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    if (rawContext == NULL) {
399cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        return;
400cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    }
401cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    const EncoderCallbackContext* context = reinterpret_cast<const EncoderCallbackContext*>(rawContext);
402cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    *err = U_ZERO_ERROR;
40337871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    ucnv_cbFromUWriteBytes(fromArgs, context->replacementBytes, context->replacementByteCount, 0, err);
404adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
405adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
4061698d148fba5718d68de04df1b726940c50fd015Elliott Hughesstatic UConverterFromUCallback getFromUCallback(int32_t mode) {
407adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    switch(mode) {
4082981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes    case NativeConverter_IGNORE: return UCNV_FROM_U_CALLBACK_SKIP;
4092981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes    case NativeConverter_REPLACE: return encoderReplaceCallback;
4102981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes    case NativeConverter_REPORT: return UCNV_FROM_U_CALLBACK_STOP;
411adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
412cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    abort();
413adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
414adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
4155ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughesstatic void NativeConverter_setCallbackEncode(JNIEnv* env, jclass, jlong address,
41637871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes        jint onMalformedInput, jint onUnmappableInput, jbyteArray javaReplacement) {
417ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    UConverter* cnv = toUConverter(address);
4185ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    if (cnv == NULL) {
4195ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "toUConverter", U_ILLEGAL_ARGUMENT_ERROR);
4205ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        return;
421adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
42237871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes
42337871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    UConverterFromUCallback oldCallback = NULL;
42437871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    const void* oldCallbackContext = NULL;
42537871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    ucnv_getFromUCallBack(cnv, &oldCallback, const_cast<const void**>(&oldCallbackContext));
42637871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes
42737871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    EncoderCallbackContext* callbackContext = const_cast<EncoderCallbackContext*>(
42837871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes            reinterpret_cast<const EncoderCallbackContext*>(oldCallbackContext));
42937871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    if (callbackContext == NULL) {
43037871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes        callbackContext = new EncoderCallbackContext;
43137871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    }
43237871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes
43337871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    callbackContext->onMalformedInput = getFromUCallback(onMalformedInput);
43437871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    callbackContext->onUnmappableInput = getFromUCallback(onUnmappableInput);
43537871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes
43637871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    ScopedByteArrayRO replacementBytes(env, javaReplacement);
43737871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    if (replacementBytes.get() == NULL) {
4385ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "replacementBytes", U_ILLEGAL_ARGUMENT_ERROR);
4395ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        return;
440adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
44137871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    memcpy(callbackContext->replacementBytes, replacementBytes.get(), replacementBytes.size());
44237871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    callbackContext->replacementByteCount = replacementBytes.size();
44337871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes
444cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    UErrorCode errorCode = U_ZERO_ERROR;
44537871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    ucnv_setFromUCallBack(cnv, CHARSET_ENCODER_CALLBACK, callbackContext, NULL, NULL, &errorCode);
4465ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    maybeThrowIcuException(env, "ucnv_setFromUCallBack", errorCode);
447adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
448adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
449c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughesstatic void decoderIgnoreCallback(const void*, UConverterToUnicodeArgs*, const char*, int32_t, UConverterCallbackReason, UErrorCode* err) {
450c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughes    // The icu4c UCNV_FROM_U_CALLBACK_SKIP callback requires that the context is NULL, which is
451c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughes    // never true for us.
452c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughes    *err = U_ZERO_ERROR;
453c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughes}
454c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughes
455c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughesstatic void decoderReplaceCallback(const void* rawContext,
456cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        UConverterToUnicodeArgs* toArgs, const char*, int32_t, UConverterCallbackReason,
457cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        UErrorCode* err) {
458cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    if (!rawContext) {
459cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        return;
46099c59bfa432e36933a7a5033fba8b89209f737bcElliott Hughes    }
461cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    const DecoderCallbackContext* context = reinterpret_cast<const DecoderCallbackContext*>(rawContext);
462cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    *err = U_ZERO_ERROR;
46337871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    ucnv_cbToUWriteUChars(toArgs,context->replacementChars, context->replacementCharCount, 0, err);
464adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
465adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
466cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughesstatic UConverterToUCallback getToUCallback(int32_t mode) {
467cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    switch (mode) {
468c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughes    case NativeConverter_IGNORE: return decoderIgnoreCallback;
469c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughes    case NativeConverter_REPLACE: return decoderReplaceCallback;
470c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughes    case NativeConverter_REPORT: return UCNV_TO_U_CALLBACK_STOP;
471cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    }
472cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    abort();
473cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes}
474cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes
475cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughesstatic void CHARSET_DECODER_CALLBACK(const void* rawContext, UConverterToUnicodeArgs* args,
476cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        const char* codeUnits, int32_t length,
477cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        UConverterCallbackReason reason, UErrorCode* status) {
478cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    if (!rawContext) {
479cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        return;
480cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    }
481cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    const DecoderCallbackContext* ctx = reinterpret_cast<const DecoderCallbackContext*>(rawContext);
482cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    switch(reason) {
483cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    case UCNV_UNASSIGNED:
484cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        ctx->onUnmappableInput(ctx, args, codeUnits, length, reason, status);
485cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        return;
486cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    case UCNV_ILLEGAL:
487cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    case UCNV_IRREGULAR:
488cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        ctx->onMalformedInput(ctx, args, codeUnits, length, reason, status);
489cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        return;
4909b4866d3438f42fb2c0df788989261afe72bf713Elliott Hughes    case UCNV_CLOSE:
4919b4866d3438f42fb2c0df788989261afe72bf713Elliott Hughes        delete ctx;
4929b4866d3438f42fb2c0df788989261afe72bf713Elliott Hughes        return;
493cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    default:
494cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        *status = U_ILLEGAL_ARGUMENT_ERROR;
495cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        return;
496cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    }
497cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes}
498cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes
4995ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughesstatic void NativeConverter_setCallbackDecode(JNIEnv* env, jclass, jlong address,
50037871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes        jint onMalformedInput, jint onUnmappableInput, jstring javaReplacement) {
501ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    UConverter* cnv = toUConverter(address);
502ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    if (cnv == NULL) {
5035ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "toConverter", U_ILLEGAL_ARGUMENT_ERROR);
5045ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        return;
505adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
506adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
50737871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    UConverterToUCallback oldCallback;
50837871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    const void* oldCallbackContext;
50937871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    ucnv_getToUCallBack(cnv, &oldCallback, &oldCallbackContext);
51037871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes
51137871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    DecoderCallbackContext* callbackContext = const_cast<DecoderCallbackContext*>(
51237871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes            reinterpret_cast<const DecoderCallbackContext*>(oldCallbackContext));
51337871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    if (callbackContext == NULL) {
51437871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes        callbackContext = new DecoderCallbackContext;
51537871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    }
51637871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes
51737871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    callbackContext->onMalformedInput = getToUCallback(onMalformedInput);
51837871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    callbackContext->onUnmappableInput = getToUCallback(onUnmappableInput);
51937871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes
52037871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    ScopedStringChars replacement(env, javaReplacement);
52137871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    if (replacement.get() == NULL) {
5225ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "replacement", U_ILLEGAL_ARGUMENT_ERROR);
5235ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        return;
524cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    }
52537871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    u_strncpy(callbackContext->replacementChars, replacement.get(), replacement.size());
52637871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    callbackContext->replacementCharCount = replacement.size();
52737871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes
528cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    UErrorCode errorCode = U_ZERO_ERROR;
52937871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    ucnv_setToUCallBack(cnv, CHARSET_DECODER_CALLBACK, callbackContext, NULL, NULL, &errorCode);
5305ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    maybeThrowIcuException(env, "ucnv_setToUCallBack", errorCode);
531adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
532adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
5337d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic jfloat NativeConverter_getAveCharsPerByte(JNIEnv* env, jclass, jlong handle) {
5347d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    return (1 / (jfloat) NativeConverter_getMaxBytesPerChar(env, NULL, handle));
535adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
536adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
5377d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic jbyteArray NativeConverter_getSubstitutionBytes(JNIEnv* env, jclass, jlong address) {
538ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    UConverter* cnv = toUConverter(address);
539cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    if (cnv == NULL) {
540cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        return NULL;
541adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
542cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    UErrorCode status = U_ZERO_ERROR;
54337871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    char replacementBytes[MAX_REPLACEMENT_LENGTH];
54437871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    int8_t len = sizeof(replacementBytes);
54537871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    ucnv_getSubstChars(cnv, replacementBytes, &len, &status);
546cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    if (!U_SUCCESS(status)) {
547cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        return env->NewByteArray(0);
548cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    }
549cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    jbyteArray result = env->NewByteArray(len);
550cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    if (result == NULL) {
551cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        return NULL;
552cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    }
55337871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    env->SetByteArrayRegion(result, 0, len, reinterpret_cast<jbyte*>(replacementBytes));
554cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    return result;
555adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
556adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
5577d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic jboolean NativeConverter_contains(JNIEnv* env, jclass, jstring name1, jstring name2) {
5587d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    ScopedUtfChars name1Chars(env, name1);
5597d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    if (name1Chars.c_str() == NULL) {
5607d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        return JNI_FALSE;
561adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
5627d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    ScopedUtfChars name2Chars(env, name2);
5637d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    if (name2Chars.c_str() == NULL) {
5647d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        return JNI_FALSE;
5657d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    }
5667d52d787302b862019da41aa753646d88d43fd61Elliott Hughes
5677d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    UErrorCode errorCode = U_ZERO_ERROR;
5687d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    UniqueUConverter converter1(ucnv_open(name1Chars.c_str(), &errorCode));
5697d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    UnicodeSet set1;
5707d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    ucnv_getUnicodeSet(converter1.get(), set1.toUSet(), UCNV_ROUNDTRIP_SET, &errorCode);
5717d52d787302b862019da41aa753646d88d43fd61Elliott Hughes
5727d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    UniqueUConverter converter2(ucnv_open(name2Chars.c_str(), &errorCode));
5737d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    UnicodeSet set2;
5747d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    ucnv_getUnicodeSet(converter2.get(), set2.toUSet(), UCNV_ROUNDTRIP_SET, &errorCode);
5757d52d787302b862019da41aa753646d88d43fd61Elliott Hughes
5767d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    return U_SUCCESS(errorCode) && set1.containsAll(set2);
577bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes}
578bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes
5797d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic jobject NativeConverter_charsetForName(JNIEnv* env, jclass, jstring charsetName) {
580bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    ScopedUtfChars charsetNameChars(env, charsetName);
58105960876dff6a5b686821eed8f7ae7cef5af4f50Elliott Hughes    if (charsetNameChars.c_str() == NULL) {
582bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes        return NULL;
583bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    }
584bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    // Get ICU's canonical name for this charset.
5850808cae1a2616ba9c708c7cc4489723b4060178eElliott Hughes    const char* icuCanonicalName = getICUCanonicalName(charsetNameChars.c_str());
586bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    if (icuCanonicalName == NULL) {
587bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes        return NULL;
588bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    }
589bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    // Get Java's canonical name for this charset.
590bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    jstring javaCanonicalName = getJavaCanonicalName(env, icuCanonicalName);
591bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    if (env->ExceptionOccurred()) {
592bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes        return NULL;
593bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    }
594bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes
595bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    // Check that this charset is supported.
596e1d0ebb5a4ccfe3c3f5faec1fbdf5e470fd63104Elliott Hughes    // ICU doesn't offer any "isSupported", so we just open and immediately close.
597e1d0ebb5a4ccfe3c3f5faec1fbdf5e470fd63104Elliott Hughes    // We ignore the UErrorCode because ucnv_open returning NULL is all the information we need.
598e1d0ebb5a4ccfe3c3f5faec1fbdf5e470fd63104Elliott Hughes    UErrorCode dummy = U_ZERO_ERROR;
5997d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    UniqueUConverter cnv(ucnv_open(icuCanonicalName, &dummy));
6007d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    if (cnv.get() == NULL) {
601bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes        return NULL;
602bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    }
6037d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    cnv.reset();
604bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes
605bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    // Get the aliases for this charset.
606bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    jobjectArray aliases = getAliases(env, icuCanonicalName);
607bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    if (env->ExceptionOccurred()) {
608bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes        return NULL;
609bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    }
610bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes
611bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    // Construct the CharsetICU object.
612a9f5c16a864ff63ba63f810410f8a27c086d5d52Elliott Hughes    jmethodID charsetConstructor = env->GetMethodID(JniConstants::charsetICUClass, "<init>",
613bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes            "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V");
614bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    if (env->ExceptionOccurred()) {
615bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes        return NULL;
616bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    }
617a9f5c16a864ff63ba63f810410f8a27c086d5d52Elliott Hughes    return env->NewObject(JniConstants::charsetICUClass, charsetConstructor,
618bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes            javaCanonicalName, env->NewStringUTF(icuCanonicalName), aliases);
619adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
620adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
621adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectstatic JNINativeMethod gMethods[] = {
622e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, canEncode, "(JI)Z"),
623e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, charsetForName, "(Ljava/lang/String;)Ljava/nio/charset/Charset;"),
624e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, closeConverter, "(J)V"),
625e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, contains, "(Ljava/lang/String;Ljava/lang/String;)Z"),
626e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, decode, "(J[BI[CI[IZ)I"),
627e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, encode, "(J[CI[BI[IZ)I"),
628e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, getAvailableCharsetNames, "()[Ljava/lang/String;"),
629e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, getAveBytesPerChar, "(J)F"),
630e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, getAveCharsPerByte, "(J)F"),
631e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, getMaxBytesPerChar, "(J)I"),
632e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, getMinBytesPerChar, "(J)I"),
633e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, getSubstitutionBytes, "(J)[B"),
634e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, openConverter, "(Ljava/lang/String;)J"),
635e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, resetByteToChar, "(J)V"),
636e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, resetCharToByte, "(J)V"),
6375ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    NATIVE_METHOD(NativeConverter, setCallbackDecode, "(JIILjava/lang/String;)V"),
6385ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    NATIVE_METHOD(NativeConverter, setCallbackEncode, "(JII[B)V"),
639adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project};
6407cd6760f7045d771faae8080a8c6150bf678f679Elliott Hughesvoid register_libcore_icu_NativeConverter(JNIEnv* env) {
6417cd6760f7045d771faae8080a8c6150bf678f679Elliott Hughes    jniRegisterNativeMethods(env, "libcore/icu/NativeConverter", gMethods, NELEM(gMethods));
642adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
643