diff options
-rw-r--r-- | include/ScopedStringChars.h | 51 | ||||
-rw-r--r-- | luni/src/main/java/java/nio/charset/CharsetDecoder.java | 124 | ||||
-rw-r--r-- | luni/src/main/java/java/nio/charset/CharsetEncoder.java | 125 | ||||
-rw-r--r-- | luni/src/main/java/libcore/icu/CharsetDecoderICU.java | 9 | ||||
-rw-r--r-- | luni/src/main/java/libcore/icu/CharsetEncoderICU.java | 15 | ||||
-rw-r--r-- | luni/src/main/java/libcore/icu/NativeConverter.java | 4 | ||||
-rw-r--r-- | luni/src/main/native/NativeConverter.cpp | 124 |
7 files changed, 210 insertions, 242 deletions
diff --git a/include/ScopedStringChars.h b/include/ScopedStringChars.h new file mode 100644 index 0000000..9f543b7 --- /dev/null +++ b/include/ScopedStringChars.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SCOPED_STRING_CHARS_H_included +#define SCOPED_STRING_CHARS_H_included + +#include "JNIHelp.h" + +// A smart pointer that provides access to a jchar* given a JNI jstring. +class ScopedStringChars { +public: + ScopedStringChars(JNIEnv* env, jstring s) : mEnv(env), mString(s), mSize(0) { + mChars = env->GetStringChars(mString, NULL); + if (mChars != NULL) { + mSize = env->GetStringLength(mString); + } + } + + ~ScopedStringChars() { + mEnv->ReleaseStringChars(mString, mChars); + } + + const jchar* get() const { return mChars; } + const jchar& operator[](size_t n) const { return mChars[n]; } + size_t size() const { return mSize; } + +private: + JNIEnv* mEnv; + jstring mString; + const UChar* mChars; + size_t mSize; + + // Disallow copy and assignment. + ScopedStringChars(const ScopedStringChars&); + void operator=(const ScopedStringChars&); +}; + +#endif // SCOPED_STRING_CHARS_H_included diff --git a/luni/src/main/java/java/nio/charset/CharsetDecoder.java b/luni/src/main/java/java/nio/charset/CharsetDecoder.java index df2bb6a..f67dbbc 100644 --- a/luni/src/main/java/java/nio/charset/CharsetDecoder.java +++ b/luni/src/main/java/java/nio/charset/CharsetDecoder.java @@ -82,37 +82,21 @@ import java.nio.CharBuffer; * @see java.nio.charset.CharsetEncoder */ public abstract class CharsetDecoder { - - /* - * internal status consts - */ private static final int INIT = 0; - private static final int ONGOING = 1; - private static final int END = 2; - private static final int FLUSH = 3; - // average number of chars for one byte - private float averChars; - - // maximum number of chars for one byte - private float maxChars; - - // charset for this decoder - private Charset cs; + private final float averageCharsPerByte; + private final float maxCharsPerByte; - // specify the action if malformed input error encountered - private CodingErrorAction malformAction; + private final Charset cs; - // specify the action if unmappable character error encountered - private CodingErrorAction unmapAction; + private CodingErrorAction malformedInputAction; + private CodingErrorAction unmappableCharacterAction; - // the replacement string - private String replace; + private String replacementChars; - // the current status private int status; /** @@ -133,38 +117,32 @@ public abstract class CharsetDecoder { * if <code>averageCharsPerByte</code> or * <code>maxCharsPerByte</code> is negative. */ - protected CharsetDecoder(Charset charset, float averageCharsPerByte, - float maxCharsPerByte) { + protected CharsetDecoder(Charset charset, float averageCharsPerByte, float maxCharsPerByte) { if (averageCharsPerByte <= 0 || maxCharsPerByte <= 0) { throw new IllegalArgumentException("averageCharsPerByte and maxCharsPerByte must be positive"); } if (averageCharsPerByte > maxCharsPerByte) { throw new IllegalArgumentException("averageCharsPerByte is greater than maxCharsPerByte"); } - averChars = averageCharsPerByte; - maxChars = maxCharsPerByte; + this.averageCharsPerByte = averageCharsPerByte; + this.maxCharsPerByte = maxCharsPerByte; cs = charset; status = INIT; - malformAction = CodingErrorAction.REPORT; - unmapAction = CodingErrorAction.REPORT; - replace = "\ufffd"; + malformedInputAction = CodingErrorAction.REPORT; + unmappableCharacterAction = CodingErrorAction.REPORT; + replacementChars = "\ufffd"; } /** - * Gets the average number of characters created by this decoder for a + * Returns the average number of characters created by this decoder for a * single input byte. - * - * @return the average number of characters created by this decoder for a - * single input byte. */ public final float averageCharsPerByte() { - return averChars; + return averageCharsPerByte; } /** - * Gets the <code>Charset</code> which this decoder uses. - * - * @return the <code>Charset</code> which this decoder uses. + * Returns the {@link Charset} which this decoder uses. */ public final Charset charset() { return cs; @@ -203,10 +181,9 @@ public abstract class CharsetDecoder { * @throws CharacterCodingException * if another exception happened during the decode operation. */ - public final CharBuffer decode(ByteBuffer in) - throws CharacterCodingException { + public final CharBuffer decode(ByteBuffer in) throws CharacterCodingException { reset(); - int length = (int) (in.remaining() * averChars); + int length = (int) (in.remaining() * averageCharsPerByte); CharBuffer output = CharBuffer.allocate(length); CoderResult result = null; while (true) { @@ -239,12 +216,10 @@ public abstract class CharsetDecoder { /* * checks the result whether it needs to throw CharacterCodingException. */ - private void checkCoderResult(CoderResult result) - throws CharacterCodingException { - if (result.isMalformed() && malformAction == CodingErrorAction.REPORT) { + private void checkCoderResult(CoderResult result) throws CharacterCodingException { + if (result.isMalformed() && malformedInputAction == CodingErrorAction.REPORT) { throw new MalformedInputException(result.length()); - } else if (result.isUnmappable() - && unmapAction == CodingErrorAction.REPORT) { + } else if (result.isUnmappable() && unmappableCharacterAction == CodingErrorAction.REPORT) { throw new UnmappableCharacterException(result.length()); } } @@ -368,16 +343,16 @@ public abstract class CharsetDecoder { return result; } // set coding error handle action - action = malformAction; + action = malformedInputAction; if (result.isUnmappable()) { - action = unmapAction; + action = unmappableCharacterAction; } // If the action is IGNORE or REPLACE, we should continue decoding. if (action == CodingErrorAction.REPLACE) { - if (out.remaining() < replace.length()) { + if (out.remaining() < replacementChars.length()) { return CoderResult.OVERFLOW; } - out.put(replace); + out.put(replacementChars); } else { if (action != CodingErrorAction.IGNORE) return result; @@ -585,25 +560,19 @@ public abstract class CharsetDecoder { } /** - * Gets this decoder's <code>CodingErrorAction</code> when malformed input + * Returns this decoder's <code>CodingErrorAction</code> when malformed input * occurred during the decoding process. - * - * @return this decoder's <code>CodingErrorAction</code> when malformed - * input occurred during the decoding process. */ public CodingErrorAction malformedInputAction() { - return malformAction; + return malformedInputAction; } /** - * Gets the maximum number of characters which can be created by this + * Returns the maximum number of characters which can be created by this * decoder for one input byte, must be positive. - * - * @return the maximum number of characters which can be created by this - * decoder for one input byte, must be positive. */ public final float maxCharsPerByte() { - return maxChars; + return maxCharsPerByte; } /** @@ -623,7 +592,7 @@ public abstract class CharsetDecoder { if (newAction == null) { throw new IllegalArgumentException(); } - malformAction = newAction; + malformedInputAction = newAction; implOnMalformedInput(newAction); return this; } @@ -645,18 +614,16 @@ public abstract class CharsetDecoder { if (newAction == null) { throw new IllegalArgumentException(); } - unmapAction = newAction; + unmappableCharacterAction = newAction; implOnUnmappableCharacter(newAction); return this; } /** - * Gets the replacement string, which is never null or empty. - * - * @return the replacement string, cannot be null or empty. + * Returns the replacement string, which is never null or empty. */ public final String replacement() { - return replace; + return replacementChars; } /** @@ -667,7 +634,7 @@ public abstract class CharsetDecoder { * {@link #implReplaceWith(String) implReplaceWith} method with the given * new replacement as argument. * - * @param newReplacement + * @param replacement * the replacement string, cannot be null or empty. Its length * cannot be larger than {@link #maxCharsPerByte()}. * @return this decoder. @@ -675,15 +642,19 @@ public abstract class CharsetDecoder { * if the given replacement cannot satisfy the requirement * mentioned above. */ - public final CharsetDecoder replaceWith(String newReplacement) { - if (newReplacement == null || newReplacement.isEmpty()) { - throw new IllegalArgumentException("Replacement string cannot be null or empty"); + public final CharsetDecoder replaceWith(String replacement) { + if (replacement == null) { + throw new IllegalArgumentException("replacement == null"); } - if (newReplacement.length() > maxChars) { - throw new IllegalArgumentException("Replacement string cannot be longer than max characters per byte"); + if (replacement.isEmpty()) { + throw new IllegalArgumentException("replacement.isEmpty()"); } - replace = newReplacement; - implReplaceWith(newReplacement); + if (replacement.length() > maxCharsPerByte()) { + throw new IllegalArgumentException("replacement length > maxCharsPerByte: " + + replacement.length() + " > " + maxCharsPerByte()); + } + replacementChars = replacement; + implReplaceWith(replacement); return this; } @@ -701,13 +672,10 @@ public abstract class CharsetDecoder { } /** - * Gets this decoder's <code>CodingErrorAction</code> when an unmappable + * Returns this decoder's <code>CodingErrorAction</code> when an unmappable * character error occurred during the decoding process. - * - * @return this decoder's <code>CodingErrorAction</code> when an - * unmappable character error occurred during the decoding process. */ public CodingErrorAction unmappableCharacterAction() { - return unmapAction; + return unmappableCharacterAction; } } diff --git a/luni/src/main/java/java/nio/charset/CharsetEncoder.java b/luni/src/main/java/java/nio/charset/CharsetEncoder.java index 8208a56..d2d7efb 100644 --- a/luni/src/main/java/java/nio/charset/CharsetEncoder.java +++ b/luni/src/main/java/java/nio/charset/CharsetEncoder.java @@ -77,42 +77,27 @@ import libcore.icu.CharsetEncoderICU; * @see java.nio.charset.CharsetDecoder */ public abstract class CharsetEncoder { - - /* - * internal status consts - */ private static final int READY = 0; private static final int ONGOING = 1; private static final int END = 2; private static final int FLUSH = 3; private static final int INIT = 4; - // the Charset which creates this encoder - private Charset cs; + private final Charset cs; - // average bytes per character created by this encoder - private float averBytes; + private final float averageBytesPerChar; + private final float maxBytesPerChar; - // maximum bytes per character can be created by this encoder - private float maxBytes; + private byte[] replacementBytes; - // replacement byte array - private byte[] replace; - - // internal status private int status; - // internal status indicates encode(CharBuffer) operation is finished private boolean finished; - // action for malformed input - private CodingErrorAction malformAction; + private CodingErrorAction malformedInputAction; + private CodingErrorAction unmappableCharacterAction; - // action for unmapped char input - private CodingErrorAction unmapAction; - - // decoder instance for this encoder's charset, used for replacement value - // checking + // decoder instance for this encoder's charset, used for replacement value checking private CharsetDecoder decoder; /** @@ -153,29 +138,20 @@ public abstract class CharsetEncoder { throw new IllegalArgumentException("averageBytesPerChar is greater than maxBytesPerChar"); } this.cs = cs; - averBytes = averageBytesPerChar; - maxBytes = maxBytesPerChar; + this.averageBytesPerChar = averageBytesPerChar; + this.maxBytesPerChar = maxBytesPerChar; status = INIT; - malformAction = CodingErrorAction.REPORT; - unmapAction = CodingErrorAction.REPORT; - if (this instanceof CharsetEncoderICU) { - // The RI enforces unnecessary restrictions on the replacement bytes. We trust ICU to - // know what it's doing. This lets us support EUC-JP, SCSU, and Shift_JIS. - uncheckedReplaceWith(replacement); - } else { - replaceWith(replacement); - } + malformedInputAction = CodingErrorAction.REPORT; + unmappableCharacterAction = CodingErrorAction.REPORT; + replaceWith(replacement); } /** - * Gets the average number of bytes created by this encoder for a single + * Returns the average number of bytes created by this encoder for a single * input character. - * - * @return the average number of bytes created by this encoder for a single - * input character. */ public final float averageBytesPerChar() { - return averBytes; + return averageBytesPerChar; } /** @@ -206,8 +182,8 @@ public abstract class CharsetEncoder { if (status != READY) { throw new IllegalStateException("encoding already in progress"); } - CodingErrorAction malformBak = malformAction; - CodingErrorAction unmapBak = unmapAction; + CodingErrorAction malformBak = malformedInputAction; + CodingErrorAction unmapBak = unmappableCharacterAction; onMalformedInput(CodingErrorAction.REPORT); onUnmappableCharacter(CodingErrorAction.REPORT); boolean result = true; @@ -250,9 +226,7 @@ public abstract class CharsetEncoder { } /** - * Gets the <code>Charset</code> which this encoder uses. - * - * @return the <code>Charset</code> which this encoder uses. + * Returns the {@link Charset} which this encoder uses. */ public final Charset charset() { return cs; @@ -294,7 +268,7 @@ public abstract class CharsetEncoder { return ByteBuffer.allocate(0); } reset(); - int length = (int) (in.remaining() * averBytes); + int length = (int) (in.remaining() * averageBytesPerChar); ByteBuffer output = ByteBuffer.allocate(length); CoderResult result = null; while (true) { @@ -337,9 +311,9 @@ public abstract class CharsetEncoder { * checks the result whether it needs to throw CharacterCodingException. */ private void checkCoderResult(CoderResult result) throws CharacterCodingException { - if (malformAction == CodingErrorAction.REPORT && result.isMalformed() ) { + if (malformedInputAction == CodingErrorAction.REPORT && result.isMalformed() ) { throw new MalformedInputException(result.length()); - } else if (unmapAction == CodingErrorAction.REPORT && result.isUnmappable()) { + } else if (unmappableCharacterAction == CodingErrorAction.REPORT && result.isUnmappable()) { throw new UnmappableCharacterException(result.length()); } } @@ -452,17 +426,17 @@ public abstract class CharsetEncoder { status = endOfInput ? END : ONGOING; return result; } - CodingErrorAction action = malformAction; + CodingErrorAction action = malformedInputAction; if (result.isUnmappable()) { - action = unmapAction; + action = unmappableCharacterAction; } // If the action is IGNORE or REPLACE, we should continue // encoding. if (action == CodingErrorAction.REPLACE) { - if (out.remaining() < replace.length) { + if (out.remaining() < replacementBytes.length) { return CoderResult.OVERFLOW; } - out.put(replace); + out.put(replacementBytes); } else { if (action != CodingErrorAction.IGNORE) { return result; @@ -632,25 +606,19 @@ public abstract class CharsetEncoder { } /** - * Gets this encoder's <code>CodingErrorAction</code> when a malformed + * Returns this encoder's <code>CodingErrorAction</code> when a malformed * input error occurred during the encoding process. - * - * @return this encoder's <code>CodingErrorAction</code> when a malformed - * input error occurred during the encoding process. */ public CodingErrorAction malformedInputAction() { - return malformAction; + return malformedInputAction; } /** - * Gets the maximum number of bytes which can be created by this encoder for + * Returns the maximum number of bytes which can be created by this encoder for * one input character, must be positive. - * - * @return the maximum number of bytes which can be created by this encoder - * for one input character, must be positive. */ public final float maxBytesPerChar() { - return maxBytes; + return maxBytesPerChar; } /** @@ -670,7 +638,7 @@ public abstract class CharsetEncoder { if (newAction == null) { throw new IllegalArgumentException("newAction == null"); } - malformAction = newAction; + malformedInputAction = newAction; implOnMalformedInput(newAction); return this; } @@ -692,18 +660,16 @@ public abstract class CharsetEncoder { if (newAction == null) { throw new IllegalArgumentException("newAction == null"); } - unmapAction = newAction; + unmappableCharacterAction = newAction; implOnUnmappableCharacter(newAction); return this; } /** - * Gets the replacement byte array, which is never null or empty. - * - * @return the replacement byte array, cannot be null or empty. + * Returns the replacement byte array, which is never null or empty. */ public final byte[] replacement() { - return replace; + return replacementBytes; } /** @@ -728,25 +694,29 @@ public abstract class CharsetEncoder { if (replacement == null) { throw new IllegalArgumentException("replacement == null"); } - if (replacement.length == 0 || maxBytes < replacement.length) { - throw new IllegalArgumentException("bad replacement length: " + replacement.length); + if (replacement.length == 0) { + throw new IllegalArgumentException("replacement.length == 0"); + } + if (replacement.length > maxBytesPerChar()) { + throw new IllegalArgumentException("replacement length > maxBytesPerChar: " + + replacement.length + " > " + maxBytesPerChar()); } if (!isLegalReplacement(replacement)) { throw new IllegalArgumentException("bad replacement: " + Arrays.toString(replacement)); } - uncheckedReplaceWith(replacement); - return this; + return uncheckedReplaceWith(replacement); } - private final void uncheckedReplaceWith(byte[] replacement) { + public final CharsetEncoder uncheckedReplaceWith(byte[] replacement) { // It seems like a bug, but the RI doesn't clone, and we have tests that check we don't. - replace = replacement; - implReplaceWith(replace); + this.replacementBytes = replacement; + implReplaceWith(replacementBytes); + return this; } /** * Resets this encoder. This method will reset the internal status and then - * calla <code>implReset()</code> to reset any status related to the + * calls <code>implReset()</code> to reset any status related to the * specific charset. * * @return this encoder. @@ -758,13 +728,10 @@ public abstract class CharsetEncoder { } /** - * Gets this encoder's <code>CodingErrorAction</code> when unmappable + * Returns this encoder's <code>CodingErrorAction</code> when unmappable * character occurred during encoding process. - * - * @return this encoder's <code>CodingErrorAction</code> when unmappable - * character occurred during encoding process. */ public CodingErrorAction unmappableCharacterAction() { - return unmapAction; + return unmappableCharacterAction; } } diff --git a/luni/src/main/java/libcore/icu/CharsetDecoderICU.java b/luni/src/main/java/libcore/icu/CharsetDecoderICU.java index d42368e..db7371b 100644 --- a/luni/src/main/java/libcore/icu/CharsetDecoderICU.java +++ b/luni/src/main/java/libcore/icu/CharsetDecoderICU.java @@ -74,14 +74,7 @@ public final class CharsetDecoderICU extends CharsetDecoder { } @Override protected void implReplaceWith(String newReplacement) { - if (converterHandle > 0) { - // TODO: is this the right test? we're providing characters. - int max = NativeConverter.getMaxBytesPerChar(converterHandle); - if (newReplacement.length() > max) { - throw new IllegalArgumentException("replacement length (" + newReplacement.length() + ") > maximum (" + max + ")"); - } - updateCallback(); - } + updateCallback(); } @Override protected final void implOnMalformedInput(CodingErrorAction newAction) { diff --git a/luni/src/main/java/libcore/icu/CharsetEncoderICU.java b/luni/src/main/java/libcore/icu/CharsetEncoderICU.java index 15b2788..1ea261c 100644 --- a/luni/src/main/java/libcore/icu/CharsetEncoderICU.java +++ b/luni/src/main/java/libcore/icu/CharsetEncoderICU.java @@ -76,7 +76,6 @@ public final class CharsetEncoderICU extends CharsetEncoder { byte[] replacement = makeReplacement(icuCanonicalName, address); CharsetEncoderICU result = new CharsetEncoderICU(cs, averageBytesPerChar, maxBytesPerChar, replacement, address); address = 0; // CharsetEncoderICU has taken ownership; its finalizer will do the free. - result.updateCallback(); return result; } finally { if (address != 0) { @@ -96,16 +95,20 @@ public final class CharsetEncoderICU extends CharsetEncoder { } private CharsetEncoderICU(Charset cs, float averageBytesPerChar, float maxBytesPerChar, byte[] replacement, long address) { - super(cs, averageBytesPerChar, maxBytesPerChar, replacement); + // We lie to our superclass and let it think we're using the default replacement... + super(cs, averageBytesPerChar, maxBytesPerChar); this.converterHandle = address; + // ...and then when our native peer is accessible, we tell it the truth. + // We still can't just call replaceWith because the RI enforces unnecessary restrictions + // on the replacement bytes. We trust ICU to know what it's doing. Doing so lets us support + // EUC-JP, SCSU, and Shift_JIS with ICU's suggested replacement bytes. + uncheckedReplaceWith(replacement); } @Override protected void implReplaceWith(byte[] newReplacement) { + // This hack is necessary because CharsetEncoder calls this abstract method from its + // constructor, and subclasses like CharsetEncoderICU aren't initialized at that point. if (converterHandle != 0) { - int max = NativeConverter.getMaxBytesPerChar(converterHandle); - if (newReplacement.length > max) { - throw new IllegalArgumentException("replacement length (" + newReplacement.length + ") > maximum (" + max + ")"); - } updateCallback(); } } diff --git a/luni/src/main/java/libcore/icu/NativeConverter.java b/luni/src/main/java/libcore/icu/NativeConverter.java index 8769032..2d8630c 100644 --- a/luni/src/main/java/libcore/icu/NativeConverter.java +++ b/luni/src/main/java/libcore/icu/NativeConverter.java @@ -58,9 +58,9 @@ public final class NativeConverter { return setCallbackDecode(converterHandle, translateCodingErrorAction(decoder.malformedInputAction()), translateCodingErrorAction(decoder.unmappableCharacterAction()), - decoder.replacement().toCharArray()); + decoder.replacement()); } - private static native int setCallbackDecode(long converterHandle, int onMalformedInput, int onUnmappableInput, char[] subChars); + private static native int setCallbackDecode(long converterHandle, int onMalformedInput, int onUnmappableInput, String subChars); public static int setCallbackEncode(long converterHandle, CharsetEncoder encoder) { return setCallbackEncode(converterHandle, diff --git a/luni/src/main/native/NativeConverter.cpp b/luni/src/main/native/NativeConverter.cpp index ff7b0d0..544bd99 100644 --- a/luni/src/main/native/NativeConverter.cpp +++ b/luni/src/main/native/NativeConverter.cpp @@ -20,6 +20,7 @@ #include "JniConstants.h" #include "ScopedLocalRef.h" #include "ScopedPrimitiveArray.h" +#include "ScopedStringChars.h" #include "ScopedUtfChars.h" #include "UniquePtr.h" #include "cutils/log.h" @@ -35,16 +36,18 @@ #define NativeConverter_IGNORE 1 #define NativeConverter_REPLACE 2 +#define MAX_REPLACEMENT_LENGTH 32 // equivalent to UCNV_ERROR_BUFFER_LENGTH + struct DecoderCallbackContext { - int length; - UChar subUChars[256]; + UChar replacementChars[MAX_REPLACEMENT_LENGTH]; + size_t replacementCharCount; UConverterToUCallback onUnmappableInput; UConverterToUCallback onMalformedInput; }; struct EncoderCallbackContext { - int length; - char subBytes[256]; + char replacementBytes[MAX_REPLACEMENT_LENGTH]; + size_t replacementByteCount; UConverterFromUCallback onUnmappableInput; UConverterFromUCallback onMalformedInput; }; @@ -367,7 +370,7 @@ static void encoderReplaceCallback(const void* rawContext, } const EncoderCallbackContext* context = reinterpret_cast<const EncoderCallbackContext*>(rawContext); *err = U_ZERO_ERROR; - ucnv_cbFromUWriteBytes(fromArgs, context->subBytes, context->length, 0, err); + ucnv_cbFromUWriteBytes(fromArgs, context->replacementBytes, context->replacementByteCount, 0, err); } static UConverterFromUCallback getFromUCallback(int32_t mode) { @@ -380,42 +383,34 @@ static UConverterFromUCallback getFromUCallback(int32_t mode) { } static jint NativeConverter_setCallbackEncode(JNIEnv* env, jclass, jlong address, - jint onMalformedInput, jint onUnmappableInput, jbyteArray subBytes) { + jint onMalformedInput, jint onUnmappableInput, jbyteArray javaReplacement) { UConverter* cnv = toUConverter(address); if (!cnv) { return U_ILLEGAL_ARGUMENT_ERROR; } - UConverterFromUCallback fromUOldAction = NULL; - const void* fromUOldContext = NULL; - ucnv_getFromUCallBack(cnv, &fromUOldAction, const_cast<const void**>(&fromUOldContext)); - - /* fromUOldContext can only be EncoderCallbackContext since - * the converter created is private data for the decoder - * and callbacks can only be set via this method! - */ - EncoderCallbackContext* fromUNewContext = NULL; - UConverterFromUCallback fromUNewAction = NULL; - if (fromUOldContext == NULL) { - fromUNewContext = new EncoderCallbackContext; - fromUNewAction = CHARSET_ENCODER_CALLBACK; - } else { - fromUNewContext = const_cast<EncoderCallbackContext*>( - reinterpret_cast<const EncoderCallbackContext*>(fromUOldContext)); - fromUNewAction = fromUOldAction; - fromUOldAction = NULL; - fromUOldContext = NULL; - } - fromUNewContext->onMalformedInput = getFromUCallback(onMalformedInput); - fromUNewContext->onUnmappableInput = getFromUCallback(onUnmappableInput); - ScopedByteArrayRO sub(env, subBytes); - if (sub.get() == NULL) { + + UConverterFromUCallback oldCallback = NULL; + const void* oldCallbackContext = NULL; + ucnv_getFromUCallBack(cnv, &oldCallback, const_cast<const void**>(&oldCallbackContext)); + + EncoderCallbackContext* callbackContext = const_cast<EncoderCallbackContext*>( + reinterpret_cast<const EncoderCallbackContext*>(oldCallbackContext)); + if (callbackContext == NULL) { + callbackContext = new EncoderCallbackContext; + } + + callbackContext->onMalformedInput = getFromUCallback(onMalformedInput); + callbackContext->onUnmappableInput = getFromUCallback(onUnmappableInput); + + ScopedByteArrayRO replacementBytes(env, javaReplacement); + if (replacementBytes.get() == NULL) { return U_ILLEGAL_ARGUMENT_ERROR; } - fromUNewContext->length = sub.size(); - memcpy(fromUNewContext->subBytes, sub.get(), sub.size()); + memcpy(callbackContext->replacementBytes, replacementBytes.get(), replacementBytes.size()); + callbackContext->replacementByteCount = replacementBytes.size(); + UErrorCode errorCode = U_ZERO_ERROR; - ucnv_setFromUCallBack(cnv, fromUNewAction, fromUNewContext, &fromUOldAction, &fromUOldContext, - &errorCode); + ucnv_setFromUCallBack(cnv, CHARSET_ENCODER_CALLBACK, callbackContext, NULL, NULL, &errorCode); return errorCode; } @@ -433,7 +428,7 @@ static void decoderReplaceCallback(const void* rawContext, } const DecoderCallbackContext* context = reinterpret_cast<const DecoderCallbackContext*>(rawContext); *err = U_ZERO_ERROR; - ucnv_cbToUWriteUChars(toArgs,context->subUChars, context->length, 0, err); + ucnv_cbToUWriteUChars(toArgs,context->replacementChars, context->replacementCharCount, 0, err); } static UConverterToUCallback getToUCallback(int32_t mode) { @@ -470,43 +465,34 @@ static void CHARSET_DECODER_CALLBACK(const void* rawContext, UConverterToUnicode } static jint NativeConverter_setCallbackDecode(JNIEnv* env, jclass, jlong address, - jint onMalformedInput, jint onUnmappableInput, jcharArray subChars) { + jint onMalformedInput, jint onUnmappableInput, jstring javaReplacement) { UConverter* cnv = toUConverter(address); if (cnv == NULL) { return U_ILLEGAL_ARGUMENT_ERROR; } - UConverterToUCallback toUOldAction; - const void* toUOldContext; - ucnv_getToUCallBack(cnv, &toUOldAction, &toUOldContext); - - /* toUOldContext can only be DecoderCallbackContext since - * the converter created is private data for the decoder - * and callbacks can only be set via this method! - */ - DecoderCallbackContext* toUNewContext = NULL; - UConverterToUCallback toUNewAction = NULL; - if (toUOldContext == NULL) { - toUNewContext = new DecoderCallbackContext; - toUNewAction = CHARSET_DECODER_CALLBACK; - } else { - toUNewContext = const_cast<DecoderCallbackContext*>( - reinterpret_cast<const DecoderCallbackContext*>(toUOldContext)); - toUNewAction = toUOldAction; - toUOldAction = NULL; - toUOldContext = NULL; - } - toUNewContext->onMalformedInput = getToUCallback(onMalformedInput); - toUNewContext->onUnmappableInput = getToUCallback(onUnmappableInput); - ScopedCharArrayRO sub(env, subChars); - if (sub.get() == NULL) { + UConverterToUCallback oldCallback; + const void* oldCallbackContext; + ucnv_getToUCallBack(cnv, &oldCallback, &oldCallbackContext); + + DecoderCallbackContext* callbackContext = const_cast<DecoderCallbackContext*>( + reinterpret_cast<const DecoderCallbackContext*>(oldCallbackContext)); + if (callbackContext == NULL) { + callbackContext = new DecoderCallbackContext; + } + + callbackContext->onMalformedInput = getToUCallback(onMalformedInput); + callbackContext->onUnmappableInput = getToUCallback(onUnmappableInput); + + ScopedStringChars replacement(env, javaReplacement); + if (replacement.get() == NULL) { return U_ILLEGAL_ARGUMENT_ERROR; } - toUNewContext->length = sub.size(); - u_strncpy(toUNewContext->subUChars, sub.get(), sub.size()); + u_strncpy(callbackContext->replacementChars, replacement.get(), replacement.size()); + callbackContext->replacementCharCount = replacement.size(); + UErrorCode errorCode = U_ZERO_ERROR; - ucnv_setToUCallBack(cnv, toUNewAction, toUNewContext, &toUOldAction, &toUOldContext, - &errorCode); + ucnv_setToUCallBack(cnv, CHARSET_DECODER_CALLBACK, callbackContext, NULL, NULL, &errorCode); return errorCode; } @@ -520,9 +506,9 @@ static jbyteArray NativeConverter_getSubstitutionBytes(JNIEnv* env, jclass, jlon return NULL; } UErrorCode status = U_ZERO_ERROR; - char subBytes[10]; - int8_t len = sizeof(subBytes); - ucnv_getSubstChars(cnv, subBytes, &len, &status); + char replacementBytes[MAX_REPLACEMENT_LENGTH]; + int8_t len = sizeof(replacementBytes); + ucnv_getSubstChars(cnv, replacementBytes, &len, &status); if (!U_SUCCESS(status)) { return env->NewByteArray(0); } @@ -530,7 +516,7 @@ static jbyteArray NativeConverter_getSubstitutionBytes(JNIEnv* env, jclass, jlon if (result == NULL) { return NULL; } - env->SetByteArrayRegion(result, 0, len, reinterpret_cast<jbyte*>(subBytes)); + env->SetByteArrayRegion(result, 0, len, reinterpret_cast<jbyte*>(replacementBytes)); return result; } @@ -614,7 +600,7 @@ static JNINativeMethod gMethods[] = { NATIVE_METHOD(NativeConverter, openConverter, "(Ljava/lang/String;)J"), NATIVE_METHOD(NativeConverter, resetByteToChar, "(J)V"), NATIVE_METHOD(NativeConverter, resetCharToByte, "(J)V"), - NATIVE_METHOD(NativeConverter, setCallbackDecode, "(JII[C)I"), + NATIVE_METHOD(NativeConverter, setCallbackDecode, "(JIILjava/lang/String;)I"), NATIVE_METHOD(NativeConverter, setCallbackEncode, "(JII[B)I"), }; int register_libcore_icu_NativeConverter(JNIEnv* env) { |