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
183aac4ddc4d17c07fa8b4908069d23d5401a77993Elliott Hughes#include "IcuUtilities.h"
19ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes#include "JNIHelp.h"
20a9f5c16a864ff63ba63f810410f8a27c086d5d52Elliott Hughes#include "JniConstants.h"
21bef9ec33e1368f57c731fce63b6a8c61628c64b0Elliott Hughes#include "JniException.h"
228044bf6b446c93cd29c5753544246316f269064fElliott Hughes#include "ScopedLocalRef.h"
23cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes#include "ScopedPrimitiveArray.h"
2437871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes#include "ScopedStringChars.h"
25ccb8b92211a3e87acaf6486c8d4423c2053b8b5eElliott Hughes#include "ScopedUtfChars.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
34b3aacde3d8af759ee4a7b395c636ea360547d92dIan Rogers#include <memory>
35ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes#include <vector>
36ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes
37adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project#include <stdlib.h>
38adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project#include <string.h>
39adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
40c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughes#define NativeConverter_REPORT 0
41c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughes#define NativeConverter_IGNORE 1
42c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughes#define NativeConverter_REPLACE 2
434722f161d49b5c483aa7aec0daad7bfee18578bbElliott Hughes
4437871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes#define MAX_REPLACEMENT_LENGTH 32 // equivalent to UCNV_ERROR_BUFFER_LENGTH
4537871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes
46cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughesstruct DecoderCallbackContext {
4737871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    UChar replacementChars[MAX_REPLACEMENT_LENGTH];
4837871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    size_t replacementCharCount;
49cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    UConverterToUCallback onUnmappableInput;
50cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    UConverterToUCallback onMalformedInput;
51cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes};
52cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes
53cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughesstruct EncoderCallbackContext {
5437871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    char replacementBytes[MAX_REPLACEMENT_LENGTH];
5537871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    size_t replacementByteCount;
56cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    UConverterFromUCallback onUnmappableInput;
57cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    UConverterFromUCallback onMalformedInput;
58cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes};
59adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
60ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughesstatic UConverter* toUConverter(jlong address) {
61ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    return reinterpret_cast<UConverter*>(static_cast<uintptr_t>(address));
62ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes}
63ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes
64783a57a7ece339bc246925e328bc82b50cae427aElliott Hughesstatic bool collectStandardNames(JNIEnv* env, const char* canonicalName, const char* standard,
65783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes                                 std::vector<std::string>& result) {
66783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes  UErrorCode status = U_ZERO_ERROR;
67a04b5c3d39232c7616591883ee2124520e3ab622Elliott Hughes  icu::UStringEnumeration e(ucnv_openStandardNames(canonicalName, standard, &status));
68783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes  if (maybeThrowIcuException(env, "ucnv_openStandardNames", status)) {
69783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes    return false;
70ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  }
71783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes
72783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes  int32_t count = e.count(status);
73783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes  if (maybeThrowIcuException(env, "StringEnumeration::count", status)) {
74783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes    return false;
75ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  }
76ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes
77783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes  for (int32_t i = 0; i < count; ++i) {
78a04b5c3d39232c7616591883ee2124520e3ab622Elliott Hughes    const icu::UnicodeString* string = e.snext(status);
79783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes    if (maybeThrowIcuException(env, "StringEnumeration::snext", status)) {
80783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes      return false;
81ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes    }
82783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes    std::string s;
83783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes    string->toUTF8String(s);
84783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes    if (s.find_first_of("+,") == std::string::npos) {
85783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes      result.push_back(s);
86ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes    }
87ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  }
88783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes
89783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes  return true;
90ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes}
91ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes
92ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughesstatic const char* getICUCanonicalName(const char* name) {
93ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  UErrorCode error = U_ZERO_ERROR;
94ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  const char* canonicalName = NULL;
95ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  if ((canonicalName = ucnv_getCanonicalName(name, "MIME", &error)) != NULL) {
96ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes    return canonicalName;
97ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  } else if ((canonicalName = ucnv_getCanonicalName(name, "IANA", &error)) != NULL) {
98ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes    return canonicalName;
99ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  } else if ((canonicalName = ucnv_getCanonicalName(name, "", &error)) != NULL) {
100ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes    return canonicalName;
101ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  } else if ((canonicalName = ucnv_getAlias(name, 0, &error)) != NULL) {
102ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes    // We have some aliases in the form x-blah .. match those first.
103ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes    return canonicalName;
104ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  } else if (strstr(name, "x-") == name) {
105ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes    // Check if the converter can be opened with the name given.
106ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes    error = U_ZERO_ERROR;
107a04b5c3d39232c7616591883ee2124520e3ab622Elliott Hughes    icu::LocalUConverterPointer cnv(ucnv_open(name + 2, &error));
108ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes    if (U_SUCCESS(error)) {
109ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes      return name + 2;
110ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes    }
111ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  }
112ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  return NULL;
113ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes}
114ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes
115ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes// If a charset listed in the IANA Charset Registry is supported by an implementation
116ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes// of the Java platform then its canonical name must be the name listed in the registry.
117ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes// Many charsets are given more than one name in the registry, in which case the registry
118ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes// identifies one of the names as MIME-preferred. If a charset has more than one registry
119ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes// name then its canonical name must be the MIME-preferred name and the other names in
120ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes// the registry must be valid aliases. If a supported charset is not listed in the IANA
121ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes// registry then its canonical name must begin with one of the strings "X-" or "x-".
122ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughesstatic jstring getJavaCanonicalName(JNIEnv* env, const char* icuCanonicalName) {
123ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  UErrorCode status = U_ZERO_ERROR;
124ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes
125ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  // Check to see if this is a well-known MIME or IANA name.
126ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  const char* cName = NULL;
127ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  if ((cName = ucnv_getStandardName(icuCanonicalName, "MIME", &status)) != NULL) {
128ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes    return env->NewStringUTF(cName);
129ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  } else if ((cName = ucnv_getStandardName(icuCanonicalName, "IANA", &status)) != NULL) {
130ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes    return env->NewStringUTF(cName);
131ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  }
132ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes
133ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  // Check to see if an alias already exists with "x-" prefix, if yes then
134ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  // make that the canonical name.
135ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  int32_t aliasCount = ucnv_countAliases(icuCanonicalName, &status);
136ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  for (int i = 0; i < aliasCount; ++i) {
137ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes    const char* name = ucnv_getAlias(icuCanonicalName, i, &status);
138ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes    if (name != NULL && name[0] == 'x' && name[1] == '-') {
139ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes      return env->NewStringUTF(name);
140ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes    }
141ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  }
142ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes
143ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  // As a last resort, prepend "x-" to any alias and make that the canonical name.
144ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  status = U_ZERO_ERROR;
145ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  const char* name = ucnv_getStandardName(icuCanonicalName, "UTR22", &status);
146ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  if (name == NULL && strchr(icuCanonicalName, ',') != NULL) {
147ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes    name = ucnv_getAlias(icuCanonicalName, 1, &status);
148ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  }
149ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  // If there is no UTR22 canonical name then just return the original name.
150ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  if (name == NULL) {
151ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes    name = icuCanonicalName;
152ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  }
153b3aacde3d8af759ee4a7b395c636ea360547d92dIan Rogers  std::unique_ptr<char[]> result(new char[2 + strlen(name) + 1]);
154ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  strcpy(&result[0], "x-");
155ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  strcat(&result[0], name);
156ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes  return env->NewStringUTF(&result[0]);
157ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes}
158ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes
1597d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic jlong NativeConverter_openConverter(JNIEnv* env, jclass, jstring converterName) {
160bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    ScopedUtfChars converterNameChars(env, converterName);
16105960876dff6a5b686821eed8f7ae7cef5af4f50Elliott Hughes    if (converterNameChars.c_str() == NULL) {
162adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return 0;
163adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
164bef9ec33e1368f57c731fce63b6a8c61628c64b0Elliott Hughes    UErrorCode status = U_ZERO_ERROR;
165bef9ec33e1368f57c731fce63b6a8c61628c64b0Elliott Hughes    UConverter* cnv = ucnv_open(converterNameChars.c_str(), &status);
1665ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    maybeThrowIcuException(env, "ucnv_open", status);
167ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    return reinterpret_cast<uintptr_t>(cnv);
168adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
169adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1707d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic void NativeConverter_closeConverter(JNIEnv*, jclass, jlong address) {
1719b4866d3438f42fb2c0df788989261afe72bf713Elliott Hughes    ucnv_close(toUConverter(address));
172adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
173adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1745ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughesstatic bool shouldCodecThrow(jboolean flush, UErrorCode error) {
1755ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    if (flush) {
1765ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        return (error != U_BUFFER_OVERFLOW_ERROR && error != U_TRUNCATED_CHAR_FOUND);
1775ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    } else {
1785ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        return (error != U_BUFFER_OVERFLOW_ERROR && error != U_INVALID_CHAR_FOUND && error != U_ILLEGAL_CHAR_FOUND);
1795ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    }
1805ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes}
1815ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes
1827d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic jint NativeConverter_encode(JNIEnv* env, jclass, jlong address,
1837d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        jcharArray source, jint sourceEnd, jbyteArray target, jint targetEnd,
1847d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        jintArray data, jboolean flush) {
1857d52d787302b862019da41aa753646d88d43fd61Elliott Hughes
186ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    UConverter* cnv = toUConverter(address);
18764101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    if (cnv == NULL) {
1885ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "toUConverter", U_ILLEGAL_ARGUMENT_ERROR);
18964101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes        return U_ILLEGAL_ARGUMENT_ERROR;
19064101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    }
191ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    ScopedCharArrayRO uSource(env, source);
19264101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    if (uSource.get() == NULL) {
1935ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "uSource", U_ILLEGAL_ARGUMENT_ERROR);
19464101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes        return U_ILLEGAL_ARGUMENT_ERROR;
19564101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    }
196ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    ScopedByteArrayRW uTarget(env, target);
19764101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    if (uTarget.get() == NULL) {
1985ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "uTarget", U_ILLEGAL_ARGUMENT_ERROR);
19964101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes        return U_ILLEGAL_ARGUMENT_ERROR;
20064101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    }
201ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    ScopedIntArrayRW myData(env, data);
20264101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    if (myData.get() == NULL) {
2035ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "myData", U_ILLEGAL_ARGUMENT_ERROR);
20499c59bfa432e36933a7a5033fba8b89209f737bcElliott Hughes        return U_ILLEGAL_ARGUMENT_ERROR;
20599c59bfa432e36933a7a5033fba8b89209f737bcElliott Hughes    }
206ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes
207ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    // Do the conversion.
208ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    jint* sourceOffset = &myData[0];
209ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    jint* targetOffset = &myData[1];
210ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    const jchar* mySource = uSource.get() + *sourceOffset;
211ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    const UChar* mySourceLimit= uSource.get() + sourceEnd;
212ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    char* cTarget = reinterpret_cast<char*>(uTarget.get() + *targetOffset);
213ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    const char* cTargetLimit = reinterpret_cast<const char*>(uTarget.get() + targetEnd);
21499c59bfa432e36933a7a5033fba8b89209f737bcElliott Hughes    UErrorCode errorCode = U_ZERO_ERROR;
215ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    ucnv_fromUnicode(cnv , &cTarget, cTargetLimit, &mySource, mySourceLimit, NULL, (UBool) flush, &errorCode);
216ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    *sourceOffset = (mySource - uSource.get()) - *sourceOffset;
2174e4b93ffe988b0a597835d852489573b57e0f3feNarayan Kamath    *targetOffset = (reinterpret_cast<jbyte*>(cTarget) - uTarget.get());
218ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes
219ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    // If there was an error, count the problematic characters.
220f15b280ac1c2e32f7becdb314440c2999dc8d204Narayan Kamath    if (errorCode == U_ILLEGAL_CHAR_FOUND || errorCode == U_INVALID_CHAR_FOUND ||
221f15b280ac1c2e32f7becdb314440c2999dc8d204Narayan Kamath        errorCode == U_TRUNCATED_CHAR_FOUND) {
2222981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes        int8_t invalidUCharCount = 32;
223ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes        UChar invalidUChars[32];
22433604713c5c70f9e6cad61dee6eb628db666bb22Elliott Hughes        UErrorCode minorErrorCode = U_ZERO_ERROR;
2252981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes        ucnv_getInvalidUChars(cnv, invalidUChars, &invalidUCharCount, &minorErrorCode);
226ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes        if (U_SUCCESS(minorErrorCode)) {
2272981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes            myData[2] = invalidUCharCount;
228adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
229adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
2305ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes
2315ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    // Managed code handles some cases; throw all other errors.
2325ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    if (shouldCodecThrow(flush, errorCode)) {
2335ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "ucnv_fromUnicode", errorCode);
2345ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    }
235adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    return errorCode;
236adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
237adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2387d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic jint NativeConverter_decode(JNIEnv* env, jclass, jlong address,
2397d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        jbyteArray source, jint sourceEnd, jcharArray target, jint targetEnd,
2407d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        jintArray data, jboolean flush) {
2417d52d787302b862019da41aa753646d88d43fd61Elliott Hughes
242ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    UConverter* cnv = toUConverter(address);
24364101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    if (cnv == NULL) {
2445ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "toUConverter", U_ILLEGAL_ARGUMENT_ERROR);
24564101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes        return U_ILLEGAL_ARGUMENT_ERROR;
24664101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    }
247ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    ScopedByteArrayRO uSource(env, source);
24864101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    if (uSource.get() == NULL) {
2495ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "uSource", U_ILLEGAL_ARGUMENT_ERROR);
25064101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes        return U_ILLEGAL_ARGUMENT_ERROR;
25164101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    }
252ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    ScopedCharArrayRW uTarget(env, target);
25364101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    if (uTarget.get() == NULL) {
2545ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "uTarget", U_ILLEGAL_ARGUMENT_ERROR);
25564101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes        return U_ILLEGAL_ARGUMENT_ERROR;
25664101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    }
257ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    ScopedIntArrayRW myData(env, data);
25864101124267c6a0a9a12dc854bdec39cbc506259Elliott Hughes    if (myData.get() == NULL) {
2595ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "myData", U_ILLEGAL_ARGUMENT_ERROR);
260ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes        return U_ILLEGAL_ARGUMENT_ERROR;
261adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
262adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
263ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    // Do the conversion.
264ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    jint* sourceOffset = &myData[0];
265ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    jint* targetOffset = &myData[1];
266583ce47780cae3a014ca46534e08f5c2adc8fa88Elliott Hughes    const char* mySource = reinterpret_cast<const char*>(uSource.get() + *sourceOffset);
267ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    const char* mySourceLimit = reinterpret_cast<const char*>(uSource.get() + sourceEnd);
268ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    UChar* cTarget = uTarget.get() + *targetOffset;
269ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    const UChar* cTargetLimit = uTarget.get() + targetEnd;
270ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    UErrorCode errorCode = U_ZERO_ERROR;
271583ce47780cae3a014ca46534e08f5c2adc8fa88Elliott Hughes    ucnv_toUnicode(cnv, &cTarget, cTargetLimit, &mySource, mySourceLimit, NULL, flush, &errorCode);
272583ce47780cae3a014ca46534e08f5c2adc8fa88Elliott Hughes    *sourceOffset = mySource - reinterpret_cast<const char*>(uSource.get()) - *sourceOffset;
273ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    *targetOffset = cTarget - uTarget.get() - *targetOffset;
274ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes
275ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    // If there was an error, count the problematic bytes.
276bd024cc687470a008999d96fd3af4a1261413d55Narayan Kamath    if (errorCode == U_ILLEGAL_CHAR_FOUND || errorCode == U_INVALID_CHAR_FOUND ||
277bd024cc687470a008999d96fd3af4a1261413d55Narayan Kamath        errorCode == U_TRUNCATED_CHAR_FOUND) {
2782981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes        int8_t invalidByteCount = 32;
2792981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes        char invalidBytes[32] = {'\0'};
2802981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes        UErrorCode minorErrorCode = U_ZERO_ERROR;
2812981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes        ucnv_getInvalidChars(cnv, invalidBytes, &invalidByteCount, &minorErrorCode);
282ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes        if (U_SUCCESS(minorErrorCode)) {
2832981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes            myData[2] = invalidByteCount;
284adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
285adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
286adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2875ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    // Managed code handles some cases; throw all other errors.
2885ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    if (shouldCodecThrow(flush, errorCode)) {
2895ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "ucnv_toUnicode", errorCode);
2905ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    }
291ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    return errorCode;
292adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
293adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2947d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic void NativeConverter_resetByteToChar(JNIEnv*, jclass, jlong address) {
295ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    UConverter* cnv = toUConverter(address);
296bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    if (cnv) {
297adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        ucnv_resetToUnicode(cnv);
298adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
299adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
300adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
3017d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic void NativeConverter_resetCharToByte(JNIEnv*, jclass, jlong address) {
302ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    UConverter* cnv = toUConverter(address);
303bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    if (cnv) {
304adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        ucnv_resetFromUnicode(cnv);
305adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
306adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
307adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
3087d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic jint NativeConverter_getMaxBytesPerChar(JNIEnv*, jclass, jlong address) {
309ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    UConverter* cnv = toUConverter(address);
310bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    return (cnv != NULL) ? ucnv_getMaxCharSize(cnv) : -1;
311adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
312adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
3137d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic jint NativeConverter_getMinBytesPerChar(JNIEnv*, jclass, jlong address) {
314ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    UConverter* cnv = toUConverter(address);
315bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    return (cnv != NULL) ? ucnv_getMinCharSize(cnv) : -1;
316adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
317adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
3187d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic jfloat NativeConverter_getAveBytesPerChar(JNIEnv*, jclass, jlong address) {
319ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    UConverter* cnv = toUConverter(address);
320c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughes    return (cnv != NULL) ? ((ucnv_getMaxCharSize(cnv) + ucnv_getMinCharSize(cnv)) / 2.0) : -1;
321adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
322bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes
3237d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic jobjectArray NativeConverter_getAvailableCharsetNames(JNIEnv* env, jclass) {
324bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    int32_t num = ucnv_countAvailable();
325a9f5c16a864ff63ba63f810410f8a27c086d5d52Elliott Hughes    jobjectArray result = env->NewObjectArray(num, JniConstants::stringClass, NULL);
326ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes    if (result == NULL) {
327ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes        return NULL;
328ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes    }
329bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    for (int i = 0; i < num; ++i) {
330bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes        const char* name = ucnv_getAvailableName(i);
3318044bf6b446c93cd29c5753544246316f269064fElliott Hughes        ScopedLocalRef<jstring> javaCanonicalName(env, getJavaCanonicalName(env, name));
332ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes        if (javaCanonicalName.get() == NULL) {
333ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes            return NULL;
334ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes        }
3358044bf6b446c93cd29c5753544246316f269064fElliott Hughes        env->SetObjectArrayElement(result, i, javaCanonicalName.get());
336ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes        if (env->ExceptionCheck()) {
337ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes            return NULL;
338ddafeb1d73cfe504720d10a2634b5858fc4cc413Elliott Hughes        }
339adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
340bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    return result;
341adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
342adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
343cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughesstatic void CHARSET_ENCODER_CALLBACK(const void* rawContext, UConverterFromUnicodeArgs* args,
344cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        const UChar* codeUnits, int32_t length, UChar32 codePoint, UConverterCallbackReason reason,
345cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        UErrorCode* status) {
346cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    if (!rawContext) {
347cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        return;
348cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    }
349cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    const EncoderCallbackContext* ctx = reinterpret_cast<const EncoderCallbackContext*>(rawContext);
350cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    switch(reason) {
351cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    case UCNV_UNASSIGNED:
352cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        ctx->onUnmappableInput(ctx, args, codeUnits, length, codePoint, reason, status);
353cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        return;
354cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    case UCNV_ILLEGAL:
355cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    case UCNV_IRREGULAR:
356cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        ctx->onMalformedInput(ctx, args, codeUnits, length, codePoint, reason, status);
357cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        return;
3589b4866d3438f42fb2c0df788989261afe72bf713Elliott Hughes    case UCNV_CLOSE:
3599b4866d3438f42fb2c0df788989261afe72bf713Elliott Hughes        delete ctx;
3609b4866d3438f42fb2c0df788989261afe72bf713Elliott Hughes        return;
361cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    default:
362cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        *status = U_ILLEGAL_ARGUMENT_ERROR;
363cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        return;
36499c59bfa432e36933a7a5033fba8b89209f737bcElliott Hughes    }
365adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
366adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
367c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughesstatic void encoderReplaceCallback(const void* rawContext,
368acce5ff29455054faa08a10e1486a156b9d1553eElliott Hughes        UConverterFromUnicodeArgs* fromArgs, const UChar*, int32_t, UChar32,
369cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        UConverterCallbackReason, UErrorCode * err) {
370cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    if (rawContext == NULL) {
371cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        return;
372cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    }
373cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    const EncoderCallbackContext* context = reinterpret_cast<const EncoderCallbackContext*>(rawContext);
374cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    *err = U_ZERO_ERROR;
37537871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    ucnv_cbFromUWriteBytes(fromArgs, context->replacementBytes, context->replacementByteCount, 0, err);
376adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
377adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
3781698d148fba5718d68de04df1b726940c50fd015Elliott Hughesstatic UConverterFromUCallback getFromUCallback(int32_t mode) {
379adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    switch(mode) {
3802981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes    case NativeConverter_IGNORE: return UCNV_FROM_U_CALLBACK_SKIP;
3812981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes    case NativeConverter_REPLACE: return encoderReplaceCallback;
3822981b5e8cf7c19dfd85b2088b18b7a6146825317Elliott Hughes    case NativeConverter_REPORT: return UCNV_FROM_U_CALLBACK_STOP;
383adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
384cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    abort();
385adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
386adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
3875ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughesstatic void NativeConverter_setCallbackEncode(JNIEnv* env, jclass, jlong address,
38837871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes        jint onMalformedInput, jint onUnmappableInput, jbyteArray javaReplacement) {
389ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    UConverter* cnv = toUConverter(address);
3905ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    if (cnv == NULL) {
3915ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "toUConverter", U_ILLEGAL_ARGUMENT_ERROR);
3925ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        return;
393adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
39437871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes
39537871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    UConverterFromUCallback oldCallback = NULL;
39637871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    const void* oldCallbackContext = NULL;
39737871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    ucnv_getFromUCallBack(cnv, &oldCallback, const_cast<const void**>(&oldCallbackContext));
39837871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes
39937871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    EncoderCallbackContext* callbackContext = const_cast<EncoderCallbackContext*>(
40037871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes            reinterpret_cast<const EncoderCallbackContext*>(oldCallbackContext));
40137871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    if (callbackContext == NULL) {
40237871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes        callbackContext = new EncoderCallbackContext;
40337871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    }
40437871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes
40537871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    callbackContext->onMalformedInput = getFromUCallback(onMalformedInput);
40637871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    callbackContext->onUnmappableInput = getFromUCallback(onUnmappableInput);
40737871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes
40837871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    ScopedByteArrayRO replacementBytes(env, javaReplacement);
40937871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    if (replacementBytes.get() == NULL) {
4105ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "replacementBytes", U_ILLEGAL_ARGUMENT_ERROR);
4115ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        return;
412adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
41337871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    memcpy(callbackContext->replacementBytes, replacementBytes.get(), replacementBytes.size());
41437871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    callbackContext->replacementByteCount = replacementBytes.size();
41537871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes
416cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    UErrorCode errorCode = U_ZERO_ERROR;
41737871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    ucnv_setFromUCallBack(cnv, CHARSET_ENCODER_CALLBACK, callbackContext, NULL, NULL, &errorCode);
4185ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    maybeThrowIcuException(env, "ucnv_setFromUCallBack", errorCode);
419adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
420adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
421c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughesstatic void decoderIgnoreCallback(const void*, UConverterToUnicodeArgs*, const char*, int32_t, UConverterCallbackReason, UErrorCode* err) {
422c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughes    // The icu4c UCNV_FROM_U_CALLBACK_SKIP callback requires that the context is NULL, which is
423c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughes    // never true for us.
424c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughes    *err = U_ZERO_ERROR;
425c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughes}
426c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughes
427c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughesstatic void decoderReplaceCallback(const void* rawContext,
428cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        UConverterToUnicodeArgs* toArgs, const char*, int32_t, UConverterCallbackReason,
429cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        UErrorCode* err) {
430cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    if (!rawContext) {
431cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        return;
43299c59bfa432e36933a7a5033fba8b89209f737bcElliott Hughes    }
433cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    const DecoderCallbackContext* context = reinterpret_cast<const DecoderCallbackContext*>(rawContext);
434cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    *err = U_ZERO_ERROR;
43537871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    ucnv_cbToUWriteUChars(toArgs,context->replacementChars, context->replacementCharCount, 0, err);
436adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
437adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
438cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughesstatic UConverterToUCallback getToUCallback(int32_t mode) {
439cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    switch (mode) {
440c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughes    case NativeConverter_IGNORE: return decoderIgnoreCallback;
441c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughes    case NativeConverter_REPLACE: return decoderReplaceCallback;
442c60bc1815dca549f3fb4e572f6aac749d7fa9fc6Elliott Hughes    case NativeConverter_REPORT: return UCNV_TO_U_CALLBACK_STOP;
443cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    }
444cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    abort();
445cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes}
446cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes
447cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughesstatic void CHARSET_DECODER_CALLBACK(const void* rawContext, UConverterToUnicodeArgs* args,
448cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        const char* codeUnits, int32_t length,
449cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        UConverterCallbackReason reason, UErrorCode* status) {
450cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    if (!rawContext) {
451cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        return;
452cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    }
453cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    const DecoderCallbackContext* ctx = reinterpret_cast<const DecoderCallbackContext*>(rawContext);
454cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    switch(reason) {
455cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    case UCNV_UNASSIGNED:
456cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        ctx->onUnmappableInput(ctx, args, codeUnits, length, reason, status);
457cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        return;
458cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    case UCNV_ILLEGAL:
459cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    case UCNV_IRREGULAR:
460cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        ctx->onMalformedInput(ctx, args, codeUnits, length, reason, status);
461cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        return;
4629b4866d3438f42fb2c0df788989261afe72bf713Elliott Hughes    case UCNV_CLOSE:
4639b4866d3438f42fb2c0df788989261afe72bf713Elliott Hughes        delete ctx;
4649b4866d3438f42fb2c0df788989261afe72bf713Elliott Hughes        return;
465cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    default:
466cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        *status = U_ILLEGAL_ARGUMENT_ERROR;
467cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        return;
468cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    }
469cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes}
470cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes
4715ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughesstatic void NativeConverter_setCallbackDecode(JNIEnv* env, jclass, jlong address,
47237871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes        jint onMalformedInput, jint onUnmappableInput, jstring javaReplacement) {
473ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    UConverter* cnv = toUConverter(address);
474ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    if (cnv == NULL) {
4755ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "toConverter", U_ILLEGAL_ARGUMENT_ERROR);
4765ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        return;
477adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
478adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
47937871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    UConverterToUCallback oldCallback;
48037871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    const void* oldCallbackContext;
48137871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    ucnv_getToUCallBack(cnv, &oldCallback, &oldCallbackContext);
48237871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes
48337871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    DecoderCallbackContext* callbackContext = const_cast<DecoderCallbackContext*>(
48437871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes            reinterpret_cast<const DecoderCallbackContext*>(oldCallbackContext));
48537871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    if (callbackContext == NULL) {
48637871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes        callbackContext = new DecoderCallbackContext;
48737871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    }
48837871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes
48937871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    callbackContext->onMalformedInput = getToUCallback(onMalformedInput);
49037871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    callbackContext->onUnmappableInput = getToUCallback(onUnmappableInput);
49137871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes
49237871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    ScopedStringChars replacement(env, javaReplacement);
49337871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    if (replacement.get() == NULL) {
4945ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        maybeThrowIcuException(env, "replacement", U_ILLEGAL_ARGUMENT_ERROR);
4955ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes        return;
496cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    }
49737871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    u_strncpy(callbackContext->replacementChars, replacement.get(), replacement.size());
49837871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    callbackContext->replacementCharCount = replacement.size();
49937871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes
500cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    UErrorCode errorCode = U_ZERO_ERROR;
50137871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    ucnv_setToUCallBack(cnv, CHARSET_DECODER_CALLBACK, callbackContext, NULL, NULL, &errorCode);
5025ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    maybeThrowIcuException(env, "ucnv_setToUCallBack", errorCode);
503adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
504adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
5057d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic jfloat NativeConverter_getAveCharsPerByte(JNIEnv* env, jclass, jlong handle) {
5067d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    return (1 / (jfloat) NativeConverter_getMaxBytesPerChar(env, NULL, handle));
507adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
508adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
5097d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic jbyteArray NativeConverter_getSubstitutionBytes(JNIEnv* env, jclass, jlong address) {
510ac0ccbb85b4a96ba79b4e770f5b71687bf8f25e9Elliott Hughes    UConverter* cnv = toUConverter(address);
511cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    if (cnv == NULL) {
512cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        return NULL;
513adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
514cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    UErrorCode status = U_ZERO_ERROR;
51537871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    char replacementBytes[MAX_REPLACEMENT_LENGTH];
51637871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    int8_t len = sizeof(replacementBytes);
51737871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    ucnv_getSubstChars(cnv, replacementBytes, &len, &status);
518cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    if (!U_SUCCESS(status)) {
519cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        return env->NewByteArray(0);
520cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    }
521cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    jbyteArray result = env->NewByteArray(len);
522cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    if (result == NULL) {
523cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes        return NULL;
524cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    }
52537871fb106b08055ad56d7f04d4faccdd163e1afElliott Hughes    env->SetByteArrayRegion(result, 0, len, reinterpret_cast<jbyte*>(replacementBytes));
526cf6c3a752da274cc5025191d3bcd62e6222f4a4cElliott Hughes    return result;
527adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
528adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
5297d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic jboolean NativeConverter_contains(JNIEnv* env, jclass, jstring name1, jstring name2) {
5307d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    ScopedUtfChars name1Chars(env, name1);
5317d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    if (name1Chars.c_str() == NULL) {
5327d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        return JNI_FALSE;
533adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
5347d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    ScopedUtfChars name2Chars(env, name2);
5357d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    if (name2Chars.c_str() == NULL) {
5367d52d787302b862019da41aa753646d88d43fd61Elliott Hughes        return JNI_FALSE;
5377d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    }
5387d52d787302b862019da41aa753646d88d43fd61Elliott Hughes
5397d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    UErrorCode errorCode = U_ZERO_ERROR;
540a04b5c3d39232c7616591883ee2124520e3ab622Elliott Hughes    icu::LocalUConverterPointer converter1(ucnv_open(name1Chars.c_str(), &errorCode));
541a04b5c3d39232c7616591883ee2124520e3ab622Elliott Hughes    icu::UnicodeSet set1;
542ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes    ucnv_getUnicodeSet(&*converter1, set1.toUSet(), UCNV_ROUNDTRIP_SET, &errorCode);
5437d52d787302b862019da41aa753646d88d43fd61Elliott Hughes
544a04b5c3d39232c7616591883ee2124520e3ab622Elliott Hughes    icu::LocalUConverterPointer converter2(ucnv_open(name2Chars.c_str(), &errorCode));
545a04b5c3d39232c7616591883ee2124520e3ab622Elliott Hughes    icu::UnicodeSet set2;
546ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes    ucnv_getUnicodeSet(&*converter2, set2.toUSet(), UCNV_ROUNDTRIP_SET, &errorCode);
5477d52d787302b862019da41aa753646d88d43fd61Elliott Hughes
5487d52d787302b862019da41aa753646d88d43fd61Elliott Hughes    return U_SUCCESS(errorCode) && set1.containsAll(set2);
549bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes}
550bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes
5517d52d787302b862019da41aa753646d88d43fd61Elliott Hughesstatic jobject NativeConverter_charsetForName(JNIEnv* env, jclass, jstring charsetName) {
552bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    ScopedUtfChars charsetNameChars(env, charsetName);
55305960876dff6a5b686821eed8f7ae7cef5af4f50Elliott Hughes    if (charsetNameChars.c_str() == NULL) {
554bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes        return NULL;
555bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    }
556ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes
557bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    // Get ICU's canonical name for this charset.
5580808cae1a2616ba9c708c7cc4489723b4060178eElliott Hughes    const char* icuCanonicalName = getICUCanonicalName(charsetNameChars.c_str());
559bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    if (icuCanonicalName == NULL) {
560bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes        return NULL;
561bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    }
562ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes
563bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    // Get Java's canonical name for this charset.
564bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    jstring javaCanonicalName = getJavaCanonicalName(env, icuCanonicalName);
5656c1e5f4ad36c1f51687aa2b059e998a7c2db2e36Elliott Hughes    if (env->ExceptionCheck()) {
566bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes        return NULL;
567bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    }
568bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes
569bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    // Check that this charset is supported.
570ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes    {
571ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes        // ICU doesn't offer any "isSupported", so we just open and immediately close.
572ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes        UErrorCode error = U_ZERO_ERROR;
573a04b5c3d39232c7616591883ee2124520e3ab622Elliott Hughes        icu::LocalUConverterPointer cnv(ucnv_open(icuCanonicalName, &error));
574ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes        if (!U_SUCCESS(error)) {
575ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes            return NULL;
576ea1caf4ccc5c2255f384c0774aa9e055763a6a41Elliott Hughes        }
577bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    }
578bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes
579bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    // Get the aliases for this charset.
580783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes    std::vector<std::string> aliases;
581783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes    if (!collectStandardNames(env, icuCanonicalName, "IANA", aliases)) {
582783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes        return NULL;
583783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes    }
584783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes    if (!collectStandardNames(env, icuCanonicalName, "MIME", aliases)) {
585783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes        return NULL;
586783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes    }
587783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes    if (!collectStandardNames(env, icuCanonicalName, "JAVA", aliases)) {
588783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes        return NULL;
589783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes    }
590783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes    if (!collectStandardNames(env, icuCanonicalName, "WINDOWS", aliases)) {
591783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes        return NULL;
592783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes    }
593783a57a7ece339bc246925e328bc82b50cae427aElliott Hughes    jobjectArray javaAliases = toStringArray(env, aliases);
5946c1e5f4ad36c1f51687aa2b059e998a7c2db2e36Elliott Hughes    if (env->ExceptionCheck()) {
595bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes        return NULL;
596bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    }
597bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes
598bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    // Construct the CharsetICU object.
5996c1e5f4ad36c1f51687aa2b059e998a7c2db2e36Elliott Hughes    static jmethodID charsetConstructor = env->GetMethodID(JniConstants::charsetICUClass, "<init>",
600bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes            "(Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V");
6016c1e5f4ad36c1f51687aa2b059e998a7c2db2e36Elliott Hughes    if (env->ExceptionCheck()) {
602bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes        return NULL;
603bcf7c66e617ad0c33bb320184bb2401def517342Elliott Hughes    }
6048fbe7d8a9df5f464da59a2938acaf211845b89daYi Kong
6058fbe7d8a9df5f464da59a2938acaf211845b89daYi Kong    jstring icuCanonicalNameStr = env->NewStringUTF(icuCanonicalName);
6068fbe7d8a9df5f464da59a2938acaf211845b89daYi Kong    if (env->ExceptionCheck()) {
6078fbe7d8a9df5f464da59a2938acaf211845b89daYi Kong        return NULL;
6088fbe7d8a9df5f464da59a2938acaf211845b89daYi Kong    }
6098fbe7d8a9df5f464da59a2938acaf211845b89daYi Kong
610a9f5c16a864ff63ba63f810410f8a27c086d5d52Elliott Hughes    return env->NewObject(JniConstants::charsetICUClass, charsetConstructor,
6118fbe7d8a9df5f464da59a2938acaf211845b89daYi Kong            javaCanonicalName, icuCanonicalNameStr, javaAliases);
612adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
613adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
6141bdc6bc1c72b033ed860360e69fc9f1f1121579bNarayan Kamathstatic void FreeNativeConverter(void *converter) {
6151bdc6bc1c72b033ed860360e69fc9f1f1121579bNarayan Kamath    ucnv_close(reinterpret_cast<UConverter*>(converter));
6161bdc6bc1c72b033ed860360e69fc9f1f1121579bNarayan Kamath}
6171bdc6bc1c72b033ed860360e69fc9f1f1121579bNarayan Kamath
6181bdc6bc1c72b033ed860360e69fc9f1f1121579bNarayan Kamathstatic jlong NativeConverter_getNativeFinalizer(JNIEnv*, jclass) {
6191bdc6bc1c72b033ed860360e69fc9f1f1121579bNarayan Kamath    return reinterpret_cast<jlong>(&FreeNativeConverter);
6201bdc6bc1c72b033ed860360e69fc9f1f1121579bNarayan Kamath}
6211bdc6bc1c72b033ed860360e69fc9f1f1121579bNarayan Kamath
6221bdc6bc1c72b033ed860360e69fc9f1f1121579bNarayan Kamath
6231bdc6bc1c72b033ed860360e69fc9f1f1121579bNarayan Kamathstatic jlong NativeConverter_getNativeSize(JNIEnv*, jclass, jstring) {
6241bdc6bc1c72b033ed860360e69fc9f1f1121579bNarayan Kamath    // TODO: Improve estimate.
6251bdc6bc1c72b033ed860360e69fc9f1f1121579bNarayan Kamath    return 200;
6261bdc6bc1c72b033ed860360e69fc9f1f1121579bNarayan Kamath}
6271bdc6bc1c72b033ed860360e69fc9f1f1121579bNarayan Kamath
628adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectstatic JNINativeMethod gMethods[] = {
629e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, charsetForName, "(Ljava/lang/String;)Ljava/nio/charset/Charset;"),
630e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, closeConverter, "(J)V"),
631e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, contains, "(Ljava/lang/String;Ljava/lang/String;)Z"),
632e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, decode, "(J[BI[CI[IZ)I"),
633e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, encode, "(J[CI[BI[IZ)I"),
634e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, getAvailableCharsetNames, "()[Ljava/lang/String;"),
635e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, getAveBytesPerChar, "(J)F"),
636e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, getAveCharsPerByte, "(J)F"),
637e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, getMaxBytesPerChar, "(J)I"),
638e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, getMinBytesPerChar, "(J)I"),
639e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, getSubstitutionBytes, "(J)[B"),
640e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, openConverter, "(Ljava/lang/String;)J"),
641e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, resetByteToChar, "(J)V"),
642e22935d3c7040c22b48d53bd18878844f381287cElliott Hughes    NATIVE_METHOD(NativeConverter, resetCharToByte, "(J)V"),
6435ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    NATIVE_METHOD(NativeConverter, setCallbackDecode, "(JIILjava/lang/String;)V"),
6445ec69b20ab9b3e2dcbe225d548168b09afbbbac2Elliott Hughes    NATIVE_METHOD(NativeConverter, setCallbackEncode, "(JII[B)V"),
6451bdc6bc1c72b033ed860360e69fc9f1f1121579bNarayan Kamath    NATIVE_METHOD(NativeConverter, getNativeFinalizer, "()J"),
6461bdc6bc1c72b033ed860360e69fc9f1f1121579bNarayan Kamath    NATIVE_METHOD(NativeConverter, getNativeSize, "()J")
647adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project};
6487cd6760f7045d771faae8080a8c6150bf678f679Elliott Hughesvoid register_libcore_icu_NativeConverter(JNIEnv* env) {
6497cd6760f7045d771faae8080a8c6150bf678f679Elliott Hughes    jniRegisterNativeMethods(env, "libcore/icu/NativeConverter", gMethods, NELEM(gMethods));
650adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
651