From f20adf218831eb8473fe419d0ab35d1521a59876 Mon Sep 17 00:00:00 2001 From: tball Date: Fri, 26 Aug 2016 10:14:14 -0700 Subject: [PATCH] Restored java.nio.charset encoder API, added CharsetProvider, moved iOS support classes. Change on 2016/08/26 by tball ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=131413312 --- jre_emul/.classpath | 1 + jre_emul/Classes/NSString+JavaString.m | 15 +- .../j2objc}/nio/charset/IOSCharset.java | 35 +- .../nio/charset/IOSCharsetDecoder.java | 12 +- .../nio/charset/IOSCharsetEncoder.java | 11 +- .../main/java/java/nio/charset/Charset.java | 1 + .../java/java/nio/charset/CharsetEncoder.java | 502 ----------- .../java/java/nio/charset/CharsetDecoder.java | 341 +++++++- .../java/java/nio/charset/CharsetEncoder.java | 791 ++++++++++++++++++ .../java/nio/charset/spi/CharsetProvider.java | 61 ++ jre_emul/environment.mk | 3 +- jre_emul/java_sources.mk | 7 +- 12 files changed, 1200 insertions(+), 580 deletions(-) rename jre_emul/{android/libcore/luni/src/main/java/java => Classes/com/google/j2objc}/nio/charset/IOSCharset.java (88%) rename jre_emul/{android/libcore/luni/src/main/java/java => Classes/com/google/j2objc}/nio/charset/IOSCharsetDecoder.java (94%) rename jre_emul/{android/libcore/luni/src/main/java/java => Classes/com/google/j2objc}/nio/charset/IOSCharsetEncoder.java (94%) delete mode 100644 jre_emul/android/libcore/luni/src/main/java/java/nio/charset/CharsetEncoder.java rename jre_emul/{android/libcore/luni => apache_harmony/classlib/modules/nio_char}/src/main/java/java/nio/charset/CharsetDecoder.java (62%) create mode 100644 jre_emul/apache_harmony/classlib/modules/nio_char/src/main/java/java/nio/charset/CharsetEncoder.java create mode 100644 jre_emul/apache_harmony/classlib/modules/nio_char/src/main/java/java/nio/charset/spi/CharsetProvider.java diff --git a/jre_emul/.classpath b/jre_emul/.classpath index 046fa6e62b..b2f0588fef 100644 --- a/jre_emul/.classpath +++ b/jre_emul/.classpath @@ -20,6 +20,7 @@ + diff --git a/jre_emul/Classes/NSString+JavaString.m b/jre_emul/Classes/NSString+JavaString.m index fdaec95b33..9facaf83d9 100644 --- a/jre_emul/Classes/NSString+JavaString.m +++ b/jre_emul/Classes/NSString+JavaString.m @@ -23,6 +23,7 @@ #import "IOSClass.h" #import "J2ObjC_source.h" +#import "com/google/j2objc/nio/charset/IOSCharset.h" #import "java/io/ObjectStreamField.h" #import "java/io/Serializable.h" #import "java/io/UnsupportedEncodingException.h" @@ -35,7 +36,6 @@ #import "java/lang/StringBuilder.h" #import "java/lang/StringIndexOutOfBoundsException.h" #import "java/nio/charset/Charset.h" -#import "java/nio/charset/IOSCharset.h" #import "java/nio/charset/UnsupportedCharsetException.h" #import "java/util/Comparator.h" #import "java/util/Formatter.h" @@ -498,7 +498,7 @@ + (NSString *)stringWithBytes:(IOSByteArray *)value NSStringEncoding parseCharsetName(NSString *charset) { JavaNioCharsetCharset *cs = JavaNioCharsetCharset_forNameUEEWithNSString_(charset); - return (NSStringEncoding)[(JavaNioCharsetIOSCharset *)cs nsEncoding]; + return (NSStringEncoding)[(ComGoogleJ2objcNioCharsetIOSCharset *)cs nsEncoding]; } + (NSString *)stringWithBytes:(IOSByteArray *)value @@ -534,11 +534,11 @@ + (NSString *)stringWithBytes:(IOSByteArray *)value offset:(int)offset length:(int)count charset:(JavaNioCharsetCharset *)charset { - if (![charset isKindOfClass:[JavaNioCharsetIOSCharset class]]) { + if (![charset isKindOfClass:[ComGoogleJ2objcNioCharsetIOSCharset class]]) { @throw AUTORELEASE([[JavaNioCharsetUnsupportedCharsetException alloc] initWithNSString:[charset description]]); } - JavaNioCharsetIOSCharset *iosCharset = (JavaNioCharsetIOSCharset *) charset; + ComGoogleJ2objcNioCharsetIOSCharset *iosCharset = (ComGoogleJ2objcNioCharsetIOSCharset *) charset; NSStringEncoding encoding = (NSStringEncoding) [iosCharset nsEncoding]; return [NSString stringWithBytes:value offset:offset @@ -611,7 +611,8 @@ + (NSString *)stringWithInts:(IOSIntArray *)codePoints - (IOSByteArray *)getBytes { JavaNioCharsetCharset *charset = JavaNioCharsetCharset_defaultCharset(); - NSStringEncoding encoding = (NSStringEncoding)[(JavaNioCharsetIOSCharset *)charset nsEncoding]; + NSStringEncoding encoding = + (NSStringEncoding)[(ComGoogleJ2objcNioCharsetIOSCharset *)charset nsEncoding]; return [self getBytesWithEncoding:encoding]; } @@ -625,11 +626,11 @@ - (IOSByteArray *)getBytesWithCharsetName:(NSString *)charsetName { - (IOSByteArray *)getBytesWithCharset:(JavaNioCharsetCharset *)charset { nil_chk(charset); - if (![charset isKindOfClass:[JavaNioCharsetIOSCharset class]]) { + if (![charset isKindOfClass:[ComGoogleJ2objcNioCharsetIOSCharset class]]) { @throw AUTORELEASE([[JavaNioCharsetUnsupportedCharsetException alloc] initWithNSString:[charset description]]); } - JavaNioCharsetIOSCharset *iosCharset = (JavaNioCharsetIOSCharset *) charset; + ComGoogleJ2objcNioCharsetIOSCharset *iosCharset = (ComGoogleJ2objcNioCharsetIOSCharset *) charset; NSStringEncoding encoding = (NSStringEncoding) [iosCharset nsEncoding]; return [self getBytesWithEncoding:encoding]; } diff --git a/jre_emul/android/libcore/luni/src/main/java/java/nio/charset/IOSCharset.java b/jre_emul/Classes/com/google/j2objc/nio/charset/IOSCharset.java similarity index 88% rename from jre_emul/android/libcore/luni/src/main/java/java/nio/charset/IOSCharset.java rename to jre_emul/Classes/com/google/j2objc/nio/charset/IOSCharset.java index db10b4aef4..404596b810 100644 --- a/jre_emul/android/libcore/luni/src/main/java/java/nio/charset/IOSCharset.java +++ b/jre_emul/Classes/com/google/j2objc/nio/charset/IOSCharset.java @@ -15,13 +15,14 @@ * limitations under the License. */ -package java.nio.charset; +package com.google.j2objc.nio.charset; -import java.io.UnsupportedEncodingException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; import java.util.HashMap; import java.util.Map; import java.util.Set; -import java.util.logging.Logger; /*-[ #import "java/io/UnsupportedEncodingException.h" @@ -34,7 +35,7 @@ * * @author Tom Ball */ -class IOSCharset extends Charset { +public class IOSCharset extends Charset { // The NSStringEncoding enum value for this charset. private long nsEncoding; @@ -44,6 +45,8 @@ class IOSCharset extends Charset { private static Map encodings = new HashMap(); + public static final IOSCharset DEFAULT_CHARSET = getDefaultCharset(); + private IOSCharset(long nsEncoding, String canonicalName, String[] aliases, float charBytes) { super(canonicalName, aliases); this.nsEncoding = nsEncoding; @@ -69,11 +72,11 @@ public CharsetDecoder newDecoder() { return new IOSCharsetDecoder(this); } - static Set getAvailableCharsetNames() { + public static Set getAvailableCharsetNames() { return getEncodings().keySet(); } - static Charset charsetForName(String charsetName) { + public static Charset charsetForName(String charsetName) { // See if an encoding was requested by name. Map encodings = getEncodings(); IOSCharset result = encodings.get(charsetName.toUpperCase()); @@ -165,16 +168,16 @@ static Charset charsetForName(String charsetName) { }; static const int numIosCharsets = sizeof(iosCharsets) / sizeof(CharsetInfo); - static JavaNioCharsetIOSCharset *addEncoding(CharsetInfo info) { + static ComGoogleJ2objcNioCharsetIOSCharset *addEncoding(CharsetInfo info) { IOSObjectArray *aliases = [IOSObjectArray arrayWithObjects:info.aliases count:info.aliasCount type:NSString_class_()]; - JavaNioCharsetIOSCharset *cs = [[[JavaNioCharsetIOSCharset alloc] - initWithLong:info.encoding - withNSString:info.name - withNSStringArray:aliases - withFloat:info.charBytes] autorelease]; - [JavaNioCharsetIOSCharset_encodings putWithId:info.name withId:cs]; + ComGoogleJ2objcNioCharsetIOSCharset *cs = [[[ComGoogleJ2objcNioCharsetIOSCharset alloc] + initWithLong:info.encoding + withNSString:info.name + withNSStringArray:aliases + withFloat:info.charBytes] autorelease]; + [ComGoogleJ2objcNioCharsetIOSCharset_encodings putWithId:info.name withId:cs]; return cs; } ]-*/ @@ -183,7 +186,7 @@ private static native IOSCharset getDefaultCharset() /*-[ NSString *fileEncoding = JavaLangSystem_getPropertyWithNSString_(@"file.encoding"); if (fileEncoding) { @try { - return (JavaNioCharsetIOSCharset *) + return (ComGoogleJ2objcNioCharsetIOSCharset *) JavaNioCharsetCharset_forNameUEEWithNSString_(fileEncoding); } @catch (JavaIoUnsupportedEncodingException *e) { @@ -194,8 +197,6 @@ private static native IOSCharset getDefaultCharset() /*-[ return addEncoding(iosCharsets[0]); ]-*/; - static final IOSCharset DEFAULT_CHARSET = getDefaultCharset(); - private static native Map getEncodings() /*-[ static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ @@ -203,6 +204,6 @@ private static native Map getEncodings() /*-[ addEncoding(iosCharsets[i]); } }); - return JavaNioCharsetIOSCharset_encodings; + return ComGoogleJ2objcNioCharsetIOSCharset_encodings; ]-*/; } diff --git a/jre_emul/android/libcore/luni/src/main/java/java/nio/charset/IOSCharsetDecoder.java b/jre_emul/Classes/com/google/j2objc/nio/charset/IOSCharsetDecoder.java similarity index 94% rename from jre_emul/android/libcore/luni/src/main/java/java/nio/charset/IOSCharsetDecoder.java rename to jre_emul/Classes/com/google/j2objc/nio/charset/IOSCharsetDecoder.java index 500fe72fd6..83cff0edf6 100644 --- a/jre_emul/android/libcore/luni/src/main/java/java/nio/charset/IOSCharsetDecoder.java +++ b/jre_emul/Classes/com/google/j2objc/nio/charset/IOSCharsetDecoder.java @@ -15,10 +15,14 @@ * limitations under the License. */ -package java.nio.charset; +package com.google.j2objc.nio.charset; import java.nio.ByteBuffer; import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CoderResult; +import java.nio.charset.UnsupportedCharsetException; /** * iOS native charset decoder. @@ -59,12 +63,6 @@ protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) { return CoderResult.UNDERFLOW; // All input data was decoded. } - @Override - public CharBuffer decode(ByteBuffer in) throws CharacterCodingException { - String s = decodeImpl(in); - return CharBuffer.wrap(s); - } - private String decodeImpl(ByteBuffer in) { Charset cs = charset(); if (!(cs instanceof IOSCharset)) { diff --git a/jre_emul/android/libcore/luni/src/main/java/java/nio/charset/IOSCharsetEncoder.java b/jre_emul/Classes/com/google/j2objc/nio/charset/IOSCharsetEncoder.java similarity index 94% rename from jre_emul/android/libcore/luni/src/main/java/java/nio/charset/IOSCharsetEncoder.java rename to jre_emul/Classes/com/google/j2objc/nio/charset/IOSCharsetEncoder.java index e93658f719..d6ee39159a 100644 --- a/jre_emul/android/libcore/luni/src/main/java/java/nio/charset/IOSCharsetEncoder.java +++ b/jre_emul/Classes/com/google/j2objc/nio/charset/IOSCharsetEncoder.java @@ -15,10 +15,14 @@ * limitations under the License. */ -package java.nio.charset; +package com.google.j2objc.nio.charset; import java.nio.ByteBuffer; import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.nio.charset.UnsupportedCharsetException; /** * iOS native charset encoder. @@ -34,11 +38,6 @@ protected IOSCharsetEncoder(Charset charset, float maxBytesPerChar) { super(charset, maxBytesPerChar, maxBytesPerChar, new byte[] { (byte) '?' }); } - @Override - public ByteBuffer encode(CharBuffer in) throws CharacterCodingException { - return ByteBuffer.wrap(encodeImpl(in)); - } - @Override protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) { if (byteBuffer != null) { diff --git a/jre_emul/android/libcore/luni/src/main/java/java/nio/charset/Charset.java b/jre_emul/android/libcore/luni/src/main/java/java/nio/charset/Charset.java index 0cf85df354..79f0249fc6 100644 --- a/jre_emul/android/libcore/luni/src/main/java/java/nio/charset/Charset.java +++ b/jre_emul/android/libcore/luni/src/main/java/java/nio/charset/Charset.java @@ -17,6 +17,7 @@ package java.nio.charset; +import com.google.j2objc.nio.charset.IOSCharset; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.nio.CharBuffer; diff --git a/jre_emul/android/libcore/luni/src/main/java/java/nio/charset/CharsetEncoder.java b/jre_emul/android/libcore/luni/src/main/java/java/nio/charset/CharsetEncoder.java deleted file mode 100644 index 43af79bf9d..0000000000 --- a/jre_emul/android/libcore/luni/src/main/java/java/nio/charset/CharsetEncoder.java +++ /dev/null @@ -1,502 +0,0 @@ -/* Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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. - */ - -package java.nio.charset; - -import java.nio.BufferOverflowException; -import java.nio.BufferUnderflowException; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; - -/** - * Transforms a sequence of 16-bit Java characters to a byte sequence in some encoding. - * - *

The input character sequence is a {@link java.nio.CharBuffer CharBuffer} and the - * output byte sequence is a {@link java.nio.ByteBuffer ByteBuffer}. - * - *

Use {@link #encode(CharBuffer)} to encode an entire {@code CharBuffer} to a - * new {@code ByteBuffer}, or {@link #encode(CharBuffer, ByteBuffer, boolean)} for more - * control. When using the latter method, the entire operation proceeds as follows: - *

    - *
  1. Invoke {@link #reset()} to reset the encoder if this instance has been used before.
  2. - *
  3. Invoke {@link #encode(CharBuffer, ByteBuffer, boolean) encode} with the {@code endOfInput} - * parameter set to false until additional input is not needed (as signaled by the return value). - * The input buffer must be filled and the output buffer must be flushed between invocations. - *

    The {@link #encode(CharBuffer, ByteBuffer, boolean) encode} method will - * convert as many characters as possible, and the process won't stop until the - * input buffer has been exhausted, the output buffer has been filled, or an - * error has occurred. A {@link CoderResult CoderResult} instance will be - * returned to indicate the current state. The caller should fill the input buffer, flush - * the output buffer, or recovering from an error and try again, accordingly. - *

  4. - *
  5. Invoke {@link #encode(CharBuffer, ByteBuffer, boolean) encode} for the last time with - * {@code endOfInput} set to true.
  6. - *
  7. Invoke {@link #flush(ByteBuffer)} to flush remaining output.
  8. - *
- * - *

There are two classes of encoding error: malformed input - * signifies that the input character sequence is not legal, while unmappable character - * signifies that the input is legal but cannot be mapped to a byte sequence (because the charset - * cannot represent the character, for example). - * - *

Errors can be handled in three ways. The default is to - * {@link CodingErrorAction#REPORT report} the error to the caller. The alternatives are to - * {@link CodingErrorAction#IGNORE ignore} the error or {@link CodingErrorAction#REPLACE replace} - * the problematic input with the byte sequence returned by {@link #replacement}. The disposition - * for each of the two kinds of error can be set independently using the {@link #onMalformedInput} - * and {@link #onUnmappableCharacter} methods. - * - *

The default replacement bytes depend on the charset but can be overridden using the - * {@link #replaceWith} method. - * - *

This class is abstract and encapsulates many common operations of the - * encoding process for all charsets. Encoders for a specific charset should - * extend this class and need only to implement the - * {@link #encodeLoop(CharBuffer, ByteBuffer) encodeLoop} method for basic - * encoding. - * - *

This class is not thread-safe. - * - * @see java.nio.charset.Charset - * @see java.nio.charset.CharsetDecoder - */ -public abstract class CharsetEncoder { - 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; - - private final Charset cs; - - private final float averageBytesPerChar; - private final float maxBytesPerChar; - - private byte[] replacementBytes; - - private int status; - // internal status indicates encode(CharBuffer) operation is finished - private boolean finished; - - private CodingErrorAction malformedInputAction; - private CodingErrorAction unmappableCharacterAction; - - // decoder instance for this encoder's charset, used for replacement value checking - private CharsetDecoder decoder; - - /** - * Constructs a new {@code CharsetEncoder} using the given parameters and - * the replacement byte array {@code { (byte) '?' }}. - */ - protected CharsetEncoder(Charset cs, float averageBytesPerChar, float maxBytesPerChar) { - this(cs, averageBytesPerChar, maxBytesPerChar, new byte[] { (byte) '?' }); - } - - /** - * Constructs a new CharsetEncoder using the given - * Charset, replacement byte array, average number and - * maximum number of bytes created by this encoder for one input character. - * - * @param cs - * the Charset to be used by this encoder. - * @param averageBytesPerChar - * average number of bytes created by this encoder for one single - * input character, must be positive. - * @param maxBytesPerChar - * maximum number of bytes which can be created by this encoder - * for one single input character, must be positive. - * @param replacement - * the replacement byte array, cannot be null or empty, its - * length cannot be larger than maxBytesPerChar, - * and must be a legal replacement. - * @throws IllegalArgumentException - * if any parameters are invalid. - */ - protected CharsetEncoder(Charset cs, float averageBytesPerChar, float maxBytesPerChar, byte[] replacement) { - if (averageBytesPerChar <= 0 || maxBytesPerChar <= 0) { - throw new IllegalArgumentException("averageBytesPerChar and maxBytesPerChar must both be positive"); - } - if (averageBytesPerChar > maxBytesPerChar) { - throw new IllegalArgumentException("averageBytesPerChar is greater than maxBytesPerChar"); - } - this.cs = cs; - this.averageBytesPerChar = averageBytesPerChar; - this.maxBytesPerChar = maxBytesPerChar; - status = INIT; - malformedInputAction = CodingErrorAction.REPORT; - unmappableCharacterAction = CodingErrorAction.REPORT; - this.replacementBytes = replacement; - } - - /** - * Returns the average number of bytes created by this encoder for a single - * input character. - */ - public final float averageBytesPerChar() { - return averageBytesPerChar; - } - - /** - * Checks if the given character can be encoded by this encoder. - *

- * Note that this method can change the internal status of this encoder, so - * it should not be called when another encoding process is ongoing, - * otherwise it will throw an IllegalStateException. - *

- * This method can be overridden for performance improvement. - * - * @param c - * the given encoder. - * @return true if given character can be encoded by this encoder. - * @throws IllegalStateException - * if another encode process is ongoing so that the current - * internal status is neither RESET or FLUSH. - */ - public boolean canEncode(char c) { - return implCanEncode(CharBuffer.wrap(new char[] { c })); - } - - // implementation of canEncode - private boolean implCanEncode(CharBuffer cb) { - if (status == FLUSH || status == INIT) { - status = READY; - } - if (status != READY) { - throw new IllegalStateException("encoding already in progress"); - } - CodingErrorAction malformBak = malformedInputAction; - CodingErrorAction unmapBak = unmappableCharacterAction; - onMalformedInput(CodingErrorAction.REPORT); - onUnmappableCharacter(CodingErrorAction.REPORT); - boolean result = true; - try { - this.encode(cb); - } catch (CharacterCodingException e) { - result = false; - } - onMalformedInput(malformBak); - onUnmappableCharacter(unmapBak); - reset(); - return result; - } - - /** - * Checks if a given CharSequence can be encoded by this - * encoder. - * - * Note that this method can change the internal status of this encoder, so - * it should not be called when another encode process is ongoing, otherwise - * it will throw an IllegalStateException. - * - * This method can be overridden for performance improvement. - * - * @param sequence - * the given CharSequence. - * @return true if the given CharSequence can be encoded by - * this encoder. - * @throws IllegalStateException - * if current internal status is neither RESET or FLUSH. - */ - public boolean canEncode(CharSequence sequence) { - CharBuffer cb; - if (sequence instanceof CharBuffer) { - cb = ((CharBuffer) sequence).duplicate(); - } else { - cb = CharBuffer.wrap(sequence); - } - return implCanEncode(cb); - } - - /** - * Returns the {@link Charset} which this encoder uses. - */ - public final Charset charset() { - return cs; - } - - /** - * This is a facade method for the encoding operation. - *

- * This method encodes the remaining character sequence of the given - * character buffer into a new byte buffer. This method performs a complete - * encoding operation, resets at first, then encodes, and flushes at last. - *

- * This method should not be invoked if another encode operation is ongoing. - * - * @param in - * the input buffer. - * @return a new ByteBuffer containing the bytes produced by - * this encoding operation. The buffer's limit will be the position - * of the last byte in the buffer, and the position will be zero. - * @throws IllegalStateException - * if another encoding operation is ongoing. - * @throws MalformedInputException - * if an illegal input character sequence for this charset is - * encountered, and the action for malformed error is - * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT} - * @throws UnmappableCharacterException - * if a legal but unmappable input character sequence for this - * charset is encountered, and the action for unmappable - * character error is - * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT}. - * Unmappable means the Unicode character sequence at the input - * buffer's current position cannot be mapped to a equivalent - * byte sequence. - * @throws CharacterCodingException - * if other exception happened during the encode operation. - */ - public abstract ByteBuffer encode(CharBuffer in) throws CharacterCodingException; - - /** - * Encodes characters starting at the current position of the given input - * buffer, and writes the equivalent byte sequence into the given output - * buffer from its current position. - *

- * The buffers' position will be changed with the reading and writing - * operation, but their limits and marks will be kept intact. - *

- * A CoderResult instance will be returned according to - * following rules: - *

    - *
  • A {@link CoderResult#malformedForLength(int) malformed input} result - * indicates that some malformed input error was encountered, and the - * erroneous characters start at the input buffer's position and their - * number can be got by result's {@link CoderResult#length() length}. This - * kind of result can be returned only if the malformed action is - * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT}.
  • - *
  • {@link CoderResult#UNDERFLOW CoderResult.UNDERFLOW} indicates that - * as many characters as possible in the input buffer have been encoded. If - * there is no further input and no characters left in the input buffer then - * this task is complete. If this is not the case then the client should - * call this method again supplying some more input characters.
  • - *
  • {@link CoderResult#OVERFLOW CoderResult.OVERFLOW} indicates that the - * output buffer has been filled, while there are still some characters - * remaining in the input buffer. This method should be invoked again with a - * non-full output buffer.
  • - *
  • A {@link CoderResult#unmappableForLength(int) unmappable character} - * result indicates that some unmappable character error was encountered, - * and the erroneous characters start at the input buffer's position and - * their number can be got by result's {@link CoderResult#length() length}. - * This kind of result can be returned only on - * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT}.
  • - *
- *

- * The endOfInput parameter indicates if the invoker can - * provider further input. This parameter is true if and only if the - * characters in the current input buffer are all inputs for this encoding - * operation. Note that it is common and won't cause an error if the invoker - * sets false and then has no more input available, while it may cause an - * error if the invoker always sets true in several consecutive invocations. - * This would make the remaining input to be treated as malformed input. - * input. - *

- * This method invokes the - * {@link #encodeLoop(CharBuffer, ByteBuffer) encodeLoop} method to - * implement the basic encode logic for a specific charset. - * - * @param in - * the input buffer. - * @param out - * the output buffer. - * @param endOfInput - * true if all the input characters have been provided. - * @return a CoderResult instance indicating the result. - * @throws IllegalStateException - * if the encoding operation has already started or no more - * input is needed in this encoding process. - * @throws CoderMalfunctionError - * If the {@link #encodeLoop(CharBuffer, ByteBuffer) encodeLoop} - * method threw an BufferUnderflowException or - * BufferUnderflowException. - */ - public final CoderResult encode(CharBuffer in, ByteBuffer out, boolean endOfInput) { - try { - return encodeLoop(in, out); - } catch (BufferOverflowException e) { - throw new CoderMalfunctionError(e); - } catch (BufferUnderflowException e) { - throw new CoderMalfunctionError(e); - } - } - - /** - * Encodes characters into bytes. This method is called by - * {@link #encode(CharBuffer, ByteBuffer, boolean) encode}. - *

- * This method will implement the essential encoding operation, and it won't - * stop encoding until either all the input characters are read, the output - * buffer is filled, or some exception is encountered. Then it will - * return a CoderResult object indicating the result of the - * current encoding operation. The rule to construct the - * CoderResult is the same as for - * {@link #encode(CharBuffer, ByteBuffer, boolean) encode}. When an - * exception is encountered in the encoding operation, most implementations - * of this method will return a relevant result object to the - * {@link #encode(CharBuffer, ByteBuffer, boolean) encode} method, and some - * performance optimized implementation may handle the exception and - * implement the error action itself. - *

- * The buffers are scanned from their current positions, and their positions - * will be modified accordingly, while their marks and limits will be - * intact. At most {@link CharBuffer#remaining() in.remaining()} characters - * will be read, and {@link ByteBuffer#remaining() out.remaining()} bytes - * will be written. - *

- * Note that some implementations may pre-scan the input buffer and return - * CoderResult.UNDERFLOW until it receives sufficient input. - *

- * @param in - * the input buffer. - * @param out - * the output buffer. - * @return a CoderResult instance indicating the result. - */ - protected abstract CoderResult encodeLoop(CharBuffer in, ByteBuffer out); - - /** - * Flushes this encoder. - *

- * The maximum number of written bytes won't larger than - * {@link ByteBuffer#remaining() out.remaining()}. If some encoder wants to - * write more bytes than the output buffer's available remaining space, then - * CoderResult.OVERFLOW will be returned, and this method - * must be called again with a byte buffer that has free space. Otherwise - * this method will return CoderResult.UNDERFLOW, which - * means one encoding process has been completed successfully. - *

- * During the flush, the output buffer's position will be changed - * accordingly, while its mark and limit will be intact. - * - * @param out - * the given output buffer. - * @return CoderResult.UNDERFLOW or - * CoderResult.OVERFLOW. - * @throws IllegalStateException - * if this encoder hasn't read all input characters during one - * encoding process, which means neither after calling - * {@link #encode(CharBuffer) encode(CharBuffer)} nor after - * calling {@link #encode(CharBuffer, ByteBuffer, boolean) - * encode(CharBuffer, ByteBuffer, boolean)} with {@code true} - * for the last boolean parameter. - */ - public final CoderResult flush(ByteBuffer out) { - return CoderResult.UNDERFLOW; - } - - /** - * Returns this encoder's CodingErrorAction when a malformed - * input error occurred during the encoding process. - */ - public CodingErrorAction malformedInputAction() { - return malformedInputAction; - } - - /** - * Returns the maximum number of bytes which can be created by this encoder for - * one input character, must be positive. - */ - public final float maxBytesPerChar() { - return maxBytesPerChar; - } - - /** - * Sets this encoder's action on malformed input error. - * - * @param newAction - * the new action on malformed input error. - * @return this encoder. - * @throws IllegalArgumentException - * if the given newAction is null. - */ - public final CharsetEncoder onMalformedInput(CodingErrorAction newAction) { - if (newAction == null) { - throw new IllegalArgumentException("newAction == null"); - } - malformedInputAction = newAction; - return this; - } - - /** - * Sets this encoder's action on unmappable character error. - * - * @param newAction - * the new action on unmappable character error. - * @return this encoder. - * @throws IllegalArgumentException - * if the given newAction is null. - */ - public final CharsetEncoder onUnmappableCharacter(CodingErrorAction newAction) { - if (newAction == null) { - throw new IllegalArgumentException("newAction == null"); - } - unmappableCharacterAction = newAction; - return this; - } - - /** - * Returns the replacement byte array, which is never null or empty. - */ - public final byte[] replacement() { - return replacementBytes; - } - - /** - * Sets the new replacement value. - * - * This method first checks the given replacement's validity, then changes - * the replacement value. - * - * @param replacement - * the replacement byte array, cannot be null or empty, its - * length cannot be larger than maxBytesPerChar, - * and it must be legal replacement. - * @return this encoder. - * @throws IllegalArgumentException - * if the given replacement cannot satisfy the requirement - * mentioned above. - */ - public final CharsetEncoder replaceWith(byte[] replacement) { - if (replacement == null) { - throw new IllegalArgumentException("replacement == null"); - } - if (replacement.length == 0) { - throw new IllegalArgumentException("replacement.length == 0"); - } - if (replacement.length > maxBytesPerChar()) { - throw new IllegalArgumentException("replacement.length > maxBytesPerChar: " + - replacement.length + " > " + maxBytesPerChar()); - } - this.replacementBytes = replacement; - return this; - } - - /** - * Resets this encoder. - * - * @return this encoder. - */ - public final CharsetEncoder reset() { - return this; - } - - /** - * Returns this encoder's CodingErrorAction when unmappable - * character occurred during encoding process. - */ - public CodingErrorAction unmappableCharacterAction() { - return unmappableCharacterAction; - } -} diff --git a/jre_emul/android/libcore/luni/src/main/java/java/nio/charset/CharsetDecoder.java b/jre_emul/apache_harmony/classlib/modules/nio_char/src/main/java/java/nio/charset/CharsetDecoder.java similarity index 62% rename from jre_emul/android/libcore/luni/src/main/java/java/nio/charset/CharsetDecoder.java rename to jre_emul/apache_harmony/classlib/modules/nio_char/src/main/java/java/nio/charset/CharsetDecoder.java index fec6a1973f..7af4004def 100644 --- a/jre_emul/android/libcore/luni/src/main/java/java/nio/charset/CharsetDecoder.java +++ b/jre_emul/apache_harmony/classlib/modules/nio_char/src/main/java/java/nio/charset/CharsetDecoder.java @@ -72,7 +72,9 @@ * decoding process for all charsets. Decoders for a specific charset should * extend this class and need only to implement the * {@link #decodeLoop(ByteBuffer, CharBuffer) decodeLoop} method for the basic - * decoding. + * decoding. If a subclass maintains an internal state, it should override the + * {@link #implFlush(CharBuffer) implFlush} method and the + * {@link #implReset() implReset} method in addition. *

* This class is not thread-safe. * @@ -80,12 +82,37 @@ * @see java.nio.charset.CharsetEncoder */ public abstract class CharsetDecoder { - private final float averageCharsPerByte; - private final float maxCharsPerByte; - private final Charset cs; - - private String replacementChars; + /* + * 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; + + // specify the action if malformed input error encountered + private CodingErrorAction malformAction; + + // specify the action if unmappable character error encountered + private CodingErrorAction unmapAction; + + // the replacement string + private String replace; + + // the current status private int status; /** @@ -106,29 +133,39 @@ public abstract class CharsetDecoder { * if averageCharsPerByte or * maxCharsPerByte 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"); + throw new IllegalArgumentException("Characters number for one byte must be positive."); } if (averageCharsPerByte > maxCharsPerByte) { - throw new IllegalArgumentException("averageCharsPerByte is greater than maxCharsPerByte"); + throw new IllegalArgumentException( + "averageCharsPerByte is greater than maxCharsPerByte"); } - this.averageCharsPerByte = averageCharsPerByte; - this.maxCharsPerByte = maxCharsPerByte; + averChars = averageCharsPerByte; + maxChars = maxCharsPerByte; cs = charset; - replacementChars = "\ufffd"; + status = INIT; + malformAction = CodingErrorAction.REPORT; + unmapAction = CodingErrorAction.REPORT; + replace = "\ufffd"; //$NON-NLS-1$ } /** - * Returns the average number of characters created by this decoder for a + * Gets 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 averageCharsPerByte; + return averChars; } /** - * Returns the {@link Charset} which this decoder uses. + * Gets the Charset which this decoder uses. + * + * @return the Charset which this decoder uses. */ public final Charset charset() { return cs; @@ -167,7 +204,66 @@ public final Charset charset() { * @throws CharacterCodingException * if another exception happened during the decode operation. */ - public abstract CharBuffer decode(ByteBuffer in) throws CharacterCodingException; + public final CharBuffer decode(ByteBuffer in) + throws CharacterCodingException { + reset(); + int length = (int) (in.remaining() * averChars); + CharBuffer output = CharBuffer.allocate(length); + CoderResult result = null; + while (true) { + result = decode(in, output, false); + checkCoderResult(result); + if (result.isUnderflow()) { + break; + } else if (result.isOverflow()) { + output = allocateMore(output); + } + } + result = decode(in, output, true); + checkCoderResult(result); + + while (true) { + result = flush(output); + checkCoderResult(result); + if (result.isOverflow()) { + output = allocateMore(output); + } else { + break; + } + } + + output.flip(); + status = FLUSH; + return output; + } + + /* + * checks the result whether it needs to throw CharacterCodingException. + */ + private void checkCoderResult(CoderResult result) + throws CharacterCodingException { + if (result.isMalformed() && malformAction == CodingErrorAction.REPORT) { + throw new MalformedInputException(result.length()); + } else if (result.isUnmappable() + && unmapAction == CodingErrorAction.REPORT) { + throw new UnmappableCharacterException(result.length()); + } + } + + /* + * original output is full and doesn't have remaining. allocate more space + * to new CharBuffer and return it, the contents in the given buffer will be + * copied into the new buffer. + */ + private CharBuffer allocateMore(CharBuffer output) { + if (output.capacity() == 0) { + return CharBuffer.allocate(1); + } + CharBuffer result = CharBuffer.allocate(output.capacity() * 2); + output.flip(); + result.put(output); + return result; + } /** * Decodes bytes starting at the current position of the given input buffer, @@ -235,14 +331,59 @@ public final Charset charset() { */ public final CoderResult decode(ByteBuffer in, CharBuffer out, boolean endOfInput) { - try { - return decodeLoop(in, out); - } catch (BufferOverflowException ex) { - // unexpected exception - throw new CoderMalfunctionError(ex); - } catch (BufferUnderflowException ex) { - // unexpected exception - throw new CoderMalfunctionError(ex); + /* + * status check + */ + if ((status == FLUSH) || (!endOfInput && status == END)) { + throw new IllegalStateException(); + } + + CoderResult result = null; + + // begin to decode + while (true) { + CodingErrorAction action = null; + try { + result = decodeLoop(in, out); + } catch (BufferOverflowException ex) { + // unexpected exception + throw new CoderMalfunctionError(ex); + } catch (BufferUnderflowException ex) { + // unexpected exception + throw new CoderMalfunctionError(ex); + } + + /* + * result handling + */ + if (result.isUnderflow()) { + int remaining = in.remaining(); + status = endOfInput ? END : ONGOING; + if (endOfInput && remaining > 0) { + result = CoderResult.malformedForLength(remaining); + } else { + return result; + } + } + if (result.isOverflow()) { + return result; + } + // set coding error handle action + action = malformAction; + if (result.isUnmappable()) { + action = unmapAction; + } + // If the action is IGNORE or REPLACE, we should continue decoding. + if (action == CodingErrorAction.REPLACE) { + if (out.remaining() < replace.length()) { + return CoderResult.OVERFLOW; + } + out.put(replace); + } else { + if (action != CodingErrorAction.IGNORE) + return result; + } + in.position(in.position() + result.length()); } } @@ -309,6 +450,11 @@ public Charset detectedCharset() { /** * Flushes this decoder. * + * This method will call {@link #implFlush(CharBuffer) implFlush}. Some + * decoders may need to write some characters to the output buffer when they + * have read all input bytes; subclasses can override + * {@link #implFlush(CharBuffer) implFlush} to perform the writing operation. + *

* The maximum number of written bytes won't be larger than * {@link CharBuffer#remaining() out.remaining()}. If some decoder wants to * write more bytes than an output buffer's remaining space allows, then a @@ -334,9 +480,73 @@ public Charset detectedCharset() { * for the last boolean parameter. */ public final CoderResult flush(CharBuffer out) { + if (status != END && status != INIT) { + throw new IllegalStateException(); + } + CoderResult result = implFlush(out); + if (result == CoderResult.UNDERFLOW) { + status = FLUSH; + } + return result; + } + + /** + * Flushes this decoder. The default implementation does nothing and always + * returns CoderResult.UNDERFLOW; this method can be + * overridden if needed. + * + * @param out + * the output buffer. + * @return CoderResult.UNDERFLOW or + * CoderResult.OVERFLOW. + */ + protected CoderResult implFlush(CharBuffer out) { return CoderResult.UNDERFLOW; } + /** + * Notifies that this decoder's CodingErrorAction specified + * for malformed input error has been changed. The default implementation + * does nothing; this method can be overridden if needed. + * + * @param newAction + * the new action. + */ + protected void implOnMalformedInput(CodingErrorAction newAction) { + // default implementation is empty + } + + /** + * Notifies that this decoder's CodingErrorAction specified + * for unmappable character error has been changed. The default + * implementation does nothing; this method can be overridden if needed. + * + * @param newAction + * the new action. + */ + protected void implOnUnmappableCharacter(CodingErrorAction newAction) { + // default implementation is empty + } + + /** + * Notifies that this decoder's replacement has been changed. The default + * implementation does nothing; this method can be overridden if needed. + * + * @param newReplacement + * the new replacement string. + */ + protected void implReplaceWith(String newReplacement) { + // default implementation is empty + } + + /** + * Reset this decoder's charset related state. The default implementation + * does nothing; this method can be overridden if needed. + */ + protected void implReset() { + // default implementation is empty + } + /** * Indicates whether this decoder implements an auto-detecting charset. * @@ -376,16 +586,34 @@ public boolean isCharsetDetected() { } /** - * Returns the maximum number of characters which can be created by this + * Gets this decoder's CodingErrorAction when malformed input + * occurred during the decoding process. + * + * @return this decoder's CodingErrorAction when malformed + * input occurred during the decoding process. + */ + public CodingErrorAction malformedInputAction() { + return malformAction; + } + + /** + * Gets 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 maxCharsPerByte; + return maxChars; } /** * Sets this decoder's action on malformed input errors. * + * This method will call the + * {@link #implOnMalformedInput(CodingErrorAction) implOnMalformedInput} + * method with the given new action as argument. + * * @param newAction * the new action on malformed input error. * @return this decoder. @@ -393,33 +621,55 @@ public final float maxCharsPerByte() { * if {@code newAction} is {@code null}. */ public final CharsetDecoder onMalformedInput(CodingErrorAction newAction) { + if (null == newAction) { + throw new IllegalArgumentException(); + } + malformAction = newAction; + implOnMalformedInput(newAction); return this; } /** * Sets this decoder's action on unmappable character errors. * + * This method will call the + * {@link #implOnUnmappableCharacter(CodingErrorAction) implOnUnmappableCharacter} + * method with the given new action as argument. + * * @param newAction * the new action on unmappable character error. * @return this decoder. * @throws IllegalArgumentException * if {@code newAction} is {@code null}. */ - public final CharsetDecoder onUnmappableCharacter(CodingErrorAction newAction) { + public final CharsetDecoder onUnmappableCharacter( + CodingErrorAction newAction) { + if (null == newAction) { + throw new IllegalArgumentException(); + } + unmapAction = newAction; + implOnUnmappableCharacter(newAction); return this; } /** - * Returns the replacement string, which is never null or empty. + * Gets the replacement string, which is never null or empty. + * + * @return the replacement string, cannot be null or empty. */ public final String replacement() { - return replacementChars; + return replace; } /** * Sets the new replacement string. * - * @param replacement + * This method first checks the given replacement's validity, then changes + * the replacement value, and at last calls the + * {@link #implReplaceWith(String) implReplaceWith} method with the given + * new replacement as argument. + * + * @param newReplacement * the replacement string, cannot be null or empty. Its length * cannot be larger than {@link #maxCharsPerByte()}. * @return this decoder. @@ -427,23 +677,40 @@ public final String replacement() { * if the given replacement cannot satisfy the requirement * mentioned above. */ - public final CharsetDecoder replaceWith(String replacement) { - if (replacement == null) { - throw new IllegalArgumentException("replacement == null"); + public final CharsetDecoder replaceWith(String newReplacement) { + if (null == newReplacement || newReplacement.length() == 0) { + throw new IllegalArgumentException("Replacement string cannot be null or empty."); } - if (replacement.isEmpty()) { - throw new IllegalArgumentException("replacement.isEmpty()"); + if (newReplacement.length() > maxChars) { + throw new IllegalArgumentException( + "Replacement string's length cannot be larger than max characters per byte."); } - replacementChars = replacement; + replace = newReplacement; + implReplaceWith(newReplacement); return this; } /** - * Resets this decoder. + * Resets this decoder. This method will reset the internal status, and then + * calls implReset() to reset any status related to the + * specific charset. * * @return this decoder. */ public final CharsetDecoder reset() { + status = INIT; + implReset(); return this; } + + /** + * Gets this decoder's CodingErrorAction when an unmappable + * character error occurred during the decoding process. + * + * @return this decoder's CodingErrorAction when an + * unmappable character error occurred during the decoding process. + */ + public CodingErrorAction unmappableCharacterAction() { + return unmapAction; + } } diff --git a/jre_emul/apache_harmony/classlib/modules/nio_char/src/main/java/java/nio/charset/CharsetEncoder.java b/jre_emul/apache_harmony/classlib/modules/nio_char/src/main/java/java/nio/charset/CharsetEncoder.java new file mode 100644 index 0000000000..36437e8f7e --- /dev/null +++ b/jre_emul/apache_harmony/classlib/modules/nio_char/src/main/java/java/nio/charset/CharsetEncoder.java @@ -0,0 +1,791 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +package java.nio.charset; + +import java.nio.BufferOverflowException; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; + +/** + * A converter that can converts a 16-bit Unicode character sequence to a byte + * sequence in some charset. + *

+ * The input character sequence is wrapped by a + * {@link java.nio.CharBuffer CharBuffer} and the output character sequence is a + * {@link java.nio.ByteBuffer ByteBuffer}. An encoder instance should be used + * in the following sequence, which is referred to as a encoding operation: + *

    + *
  1. invoking the {@link #reset() reset} method to reset the encoder if the + * encoder has been used;
  2. + *
  3. invoking the {@link #encode(CharBuffer, ByteBuffer, boolean) encode} + * method until the additional input is not needed, the endOfInput + * parameter must be set to false, the input buffer must be filled and the + * output buffer must be flushed between invocations;
  4. + *
  5. invoking the {@link #encode(CharBuffer, ByteBuffer, boolean) encode} + * method for the last time and the endOfInput parameter must be + * set to {@code true}
  6. + *
  7. invoking the {@link #flush(ByteBuffer) flush} method to flush the + * output.
  8. + *
+ *

+ * The {@link #encode(CharBuffer, ByteBuffer, boolean) encode} method will + * convert as many characters as possible, and the process won't stop until the + * input characters have run out, the output buffer has been filled or some + * error has happened. A {@link CoderResult CoderResult} instance will be + * returned to indicate the stop reason, and the invoker can identify the result + * and choose further action, which includes filling the input buffer, flushing + * the output buffer or recovering from an error and trying again. + *

+ * There are two common encoding errors. One is named malformed and it is + * returned when the input content is an illegal 16-bit Unicode character + * sequence, the other is named unmappable character and occurs when there is a + * problem mapping the input to a valid byte sequence in the specified charset. + *

+ * Both errors can be handled in three ways, the default one is to report the + * error to the invoker by a {@link CoderResult CoderResult} instance, and the + * alternatives are to ignore it or to replace the erroneous input with the + * replacement byte array. The replacement byte array is '{@code ?}' by + * default and can be changed by invoking the + * {@link #replaceWith(byte[]) replaceWith} method. The invoker of this encoder + * can choose one way by specifying a + * {@link CodingErrorAction CodingErrorAction} instance for each error type via + * the {@link #onMalformedInput(CodingErrorAction) onMalformedInput} method and + * the {@link #onUnmappableCharacter(CodingErrorAction) onUnmappableCharacter} + * method. + *

+ * This class is abstract and encapsulates many common operations of the + * encoding process for all charsets. Encoders for a specific charset should + * extend this class and need only to implement the + * {@link #encodeLoop(CharBuffer, ByteBuffer) encodeLoop} method for basic + * encoding. If a subclass maintains an internal state, it should override the + * {@link #implFlush(ByteBuffer) implFlush} method and the + * {@link #implReset() implReset} method in addition. + *

+ * This class is not thread-safe. + * + * @see java.nio.charset.Charset + * @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; + + // average bytes per character created by this encoder + private float averBytes; + + // maximum bytes per character can be created by this encoder + private float maxBytes; + + // 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; + + // action for unmapped char input + private CodingErrorAction unmapAction; + + // decoder instance for this encoder's charset, used for replacement value + // checking + private CharsetDecoder decoder; + + /** + * Constructs a new CharsetEncoder using the given + * Charset, average number and maximum number of bytes + * created by this encoder for one input character. + * + * @param cs + * the Charset to be used by this encoder. + * @param averageBytesPerChar + * average number of bytes created by this encoder for one input + * character, must be positive. + * @param maxBytesPerChar + * maximum number of bytes which can be created by this encoder + * for one input character, must be positive. + * @throws IllegalArgumentException + * if maxBytesPerChar or + * averageBytesPerChar is negative. + */ + protected CharsetEncoder(Charset cs, float averageBytesPerChar, + float maxBytesPerChar) { + this(cs, averageBytesPerChar, maxBytesPerChar, + new byte[] { (byte) '?' }); + } + + /** + * Constructs a new CharsetEncoder using the given + * Charset, replacement byte array, average number and + * maximum number of bytes created by this encoder for one input character. + * + * @param cs + * the Charset to be used by this encoder. + * @param averageBytesPerChar + * average number of bytes created by this encoder for one single + * input character, must be positive. + * @param maxBytesPerChar + * maximum number of bytes which can be created by this encoder + * for one single input character, must be positive. + * @param replacement + * the replacement byte array, cannot be null or empty, its + * length cannot be larger than maxBytesPerChar, + * and must be a legal replacement, which can be justified by + * {@link #isLegalReplacement(byte[]) isLegalReplacement}. + * @throws IllegalArgumentException + * if any parameters are invalid. + */ + protected CharsetEncoder(Charset cs, float averageBytesPerChar, + float maxBytesPerChar, byte[] replacement) { + if (averageBytesPerChar <= 0 || maxBytesPerChar <= 0) { + throw new IllegalArgumentException("Bytes number for one character must be positive."); + } + if (averageBytesPerChar > maxBytesPerChar) { + throw new IllegalArgumentException( + "averageBytesPerChar is greater than maxBytesPerChar."); + } + this.cs = cs; + averBytes = averageBytesPerChar; + maxBytes = maxBytesPerChar; + status = INIT; + malformAction = CodingErrorAction.REPORT; + unmapAction = CodingErrorAction.REPORT; + replaceWith(replacement); + } + + /** + * Gets 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; + } + + /** + * Checks if the given character can be encoded by this encoder. + *

+ * Note that this method can change the internal status of this encoder, so + * it should not be called when another encoding process is ongoing, + * otherwise it will throw an IllegalStateException. + *

+ * This method can be overridden for performance improvement. + * + * @param c + * the given encoder. + * @return true if given character can be encoded by this encoder. + * @throws IllegalStateException + * if another encode process is ongoing so that the current + * internal status is neither RESET or FLUSH. + */ + public boolean canEncode(char c) { + return implCanEncode(CharBuffer.wrap(new char[] { c })); + } + + // implementation of canEncode + private boolean implCanEncode(CharBuffer cb) { + if (status == FLUSH || status == INIT) { + status = READY; + } + if (status != READY) { + throw new IllegalStateException("Another encoding process is ongoing"); + } + CodingErrorAction malformBak = malformAction; + CodingErrorAction unmapBak = unmapAction; + onMalformedInput(CodingErrorAction.REPORT); + onUnmappableCharacter(CodingErrorAction.REPORT); + boolean result = true; + try { + this.encode(cb); + } catch (CharacterCodingException e) { + result = false; + } + onMalformedInput(malformBak); + onUnmappableCharacter(unmapBak); + reset(); + return result; + } + + /** + * Checks if a given CharSequence can be encoded by this + * encoder. + * + * Note that this method can change the internal status of this encoder, so + * it should not be called when another encode process is ongoing, otherwise + * it will throw an IllegalStateException. + * + * This method can be overridden for performance improvement. + * + * @param sequence + * the given CharSequence. + * @return true if the given CharSequence can be encoded by + * this encoder. + * @throws IllegalStateException + * if current internal status is neither RESET or FLUSH. + */ + public boolean canEncode(CharSequence sequence) { + CharBuffer cb; + if (sequence instanceof CharBuffer) { + cb = ((CharBuffer) sequence).duplicate(); + } else { + cb = CharBuffer.wrap(sequence); + } + return implCanEncode(cb); + } + + /** + * Gets the Charset which this encoder uses. + * + * @return the Charset which this encoder uses. + */ + public final Charset charset() { + return cs; + } + + /** + * This is a facade method for the encoding operation. + *

+ * This method encodes the remaining character sequence of the given + * character buffer into a new byte buffer. This method performs a complete + * encoding operation, resets at first, then encodes, and flushes at last. + *

+ * This method should not be invoked if another encode operation is ongoing. + * + * @param in + * the input buffer. + * @return a new ByteBuffer containing the bytes produced by + * this encoding operation. The buffer's limit will be the position + * of the last byte in the buffer, and the position will be zero. + * @throws IllegalStateException + * if another encoding operation is ongoing. + * @throws MalformedInputException + * if an illegal input character sequence for this charset is + * encountered, and the action for malformed error is + * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT} + * @throws UnmappableCharacterException + * if a legal but unmappable input character sequence for this + * charset is encountered, and the action for unmappable + * character error is + * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT}. + * Unmappable means the Unicode character sequence at the input + * buffer's current position cannot be mapped to a equivalent + * byte sequence. + * @throws CharacterCodingException + * if other exception happened during the encode operation. + */ + public final ByteBuffer encode(CharBuffer in) + throws CharacterCodingException { + if (in.remaining() == 0) { + return ByteBuffer.allocate(0); + } + reset(); + int length = (int) (in.remaining() * averBytes); + ByteBuffer output = ByteBuffer.allocate(length); + CoderResult result = null; + while (true) { + result = encode(in, output, false); + if (result==CoderResult.UNDERFLOW) { + break; + } else if (result==CoderResult.OVERFLOW) { + output = allocateMore(output); + continue; + } + checkCoderResult(result); + } + result = encode(in, output, true); + checkCoderResult(result); + + while (true) { + result = flush(output); + if (result==CoderResult.UNDERFLOW) { + output.flip(); + break; + } else if (result==CoderResult.OVERFLOW) { + output = allocateMore(output); + continue; + } + checkCoderResult(result); + output.flip(); + if (result.isMalformed()) { + throw new MalformedInputException(result.length()); + } else if (result.isUnmappable()) { + throw new UnmappableCharacterException(result.length()); + } + break; + } + status = READY; + finished = true; + return output; + } + + /* + * checks the result whether it needs to throw CharacterCodingException. + */ + private void checkCoderResult(CoderResult result) + throws CharacterCodingException { + if (malformAction == CodingErrorAction.REPORT && result.isMalformed() ) { + throw new MalformedInputException(result.length()); + } else if (unmapAction == CodingErrorAction.REPORT && result.isUnmappable()) { + throw new UnmappableCharacterException(result.length()); + } + } + + // allocate more spaces to the given ByteBuffer + private ByteBuffer allocateMore(ByteBuffer output) { + if (output.capacity() == 0) { + return ByteBuffer.allocate(1); + } + ByteBuffer result = ByteBuffer.allocate(output.capacity() * 2); + output.flip(); + result.put(output); + return result; + } + + /** + * Encodes characters starting at the current position of the given input + * buffer, and writes the equivalent byte sequence into the given output + * buffer from its current position. + *

+ * The buffers' position will be changed with the reading and writing + * operation, but their limits and marks will be kept intact. + *

+ * A CoderResult instance will be returned according to + * following rules: + *

    + *
  • A {@link CoderResult#malformedForLength(int) malformed input} result + * indicates that some malformed input error was encountered, and the + * erroneous characters start at the input buffer's position and their + * number can be got by result's {@link CoderResult#length() length}. This + * kind of result can be returned only if the malformed action is + * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT}.
  • + *
  • {@link CoderResult#UNDERFLOW CoderResult.UNDERFLOW} indicates that + * as many characters as possible in the input buffer have been encoded. If + * there is no further input and no characters left in the input buffer then + * this task is complete. If this is not the case then the client should + * call this method again supplying some more input characters.
  • + *
  • {@link CoderResult#OVERFLOW CoderResult.OVERFLOW} indicates that the + * output buffer has been filled, while there are still some characters + * remaining in the input buffer. This method should be invoked again with a + * non-full output buffer.
  • + *
  • A {@link CoderResult#unmappableForLength(int) unmappable character} + * result indicates that some unmappable character error was encountered, + * and the erroneous characters start at the input buffer's position and + * their number can be got by result's {@link CoderResult#length() length}. + * This kind of result can be returned only on + * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT}.
  • + *
+ *

+ * The endOfInput parameter indicates if the invoker can + * provider further input. This parameter is true if and only if the + * characters in the current input buffer are all inputs for this encoding + * operation. Note that it is common and won't cause an error if the invoker + * sets false and then has no more input available, while it may cause an + * error if the invoker always sets true in several consecutive invocations. + * This would make the remaining input to be treated as malformed input. + * input. + *

+ * This method invokes the + * {@link #encodeLoop(CharBuffer, ByteBuffer) encodeLoop} method to + * implement the basic encode logic for a specific charset. + * + * @param in + * the input buffer. + * @param out + * the output buffer. + * @param endOfInput + * true if all the input characters have been provided. + * @return a CoderResult instance indicating the result. + * @throws IllegalStateException + * if the encoding operation has already started or no more + * input is needed in this encoding process. + * @throws CoderMalfunctionError + * If the {@link #encodeLoop(CharBuffer, ByteBuffer) encodeLoop} + * method threw an BufferUnderflowException or + * BufferUnderflowException. + */ + public final CoderResult encode(CharBuffer in, ByteBuffer out, + boolean endOfInput) { + //If the previous step is encode(CharBuffer), then no more input is needed + // thus endOfInput should not be false + if (status == READY && finished && !endOfInput) { + throw new IllegalStateException(); + } + + if ((status == FLUSH) || (!endOfInput && status == END)) { + throw new IllegalStateException(); + } + + CoderResult result; + while (true) { + try { + result = encodeLoop(in, out); + } catch (BufferOverflowException e) { + throw new CoderMalfunctionError(e); + } catch (BufferUnderflowException e) { + throw new CoderMalfunctionError(e); + } + if (result == CoderResult.UNDERFLOW) { + status = endOfInput ? END : ONGOING; + if (endOfInput) { + int remaining = in.remaining(); + if (remaining > 0) { + result = CoderResult.malformedForLength(remaining); + } else { + return result; + } + } else { + return result; + } + } else if (result==CoderResult.OVERFLOW) { + status = endOfInput ? END : ONGOING; + return result; + } + CodingErrorAction action = malformAction; + if (result.isUnmappable()) { + action = unmapAction; + } + // If the action is IGNORE or REPLACE, we should continue + // encoding. + if (action == CodingErrorAction.REPLACE) { + if (out.remaining() < replace.length) { + return CoderResult.OVERFLOW; + } + out.put(replace); + } else { + if (action != CodingErrorAction.IGNORE) { + return result; + } + } + in.position(in.position() + result.length()); + } + } + + /** + * Encodes characters into bytes. This method is called by + * {@link #encode(CharBuffer, ByteBuffer, boolean) encode}. + *

+ * This method will implement the essential encoding operation, and it won't + * stop encoding until either all the input characters are read, the output + * buffer is filled, or some exception is encountered. Then it will + * return a CoderResult object indicating the result of the + * current encoding operation. The rule to construct the + * CoderResult is the same as for + * {@link #encode(CharBuffer, ByteBuffer, boolean) encode}. When an + * exception is encountered in the encoding operation, most implementations + * of this method will return a relevant result object to the + * {@link #encode(CharBuffer, ByteBuffer, boolean) encode} method, and some + * performance optimized implementation may handle the exception and + * implement the error action itself. + *

+ * The buffers are scanned from their current positions, and their positions + * will be modified accordingly, while their marks and limits will be + * intact. At most {@link CharBuffer#remaining() in.remaining()} characters + * will be read, and {@link ByteBuffer#remaining() out.remaining()} bytes + * will be written. + *

+ * Note that some implementations may pre-scan the input buffer and return + * CoderResult.UNDERFLOW until it receives sufficient input. + *

+ * @param in + * the input buffer. + * @param out + * the output buffer. + * @return a CoderResult instance indicating the result. + */ + protected abstract CoderResult encodeLoop(CharBuffer in, ByteBuffer out); + + /** + * Flushes this encoder. + *

+ * This method will call {@link #implFlush(ByteBuffer) implFlush}. Some + * encoders may need to write some bytes to the output buffer when they have + * read all input characters, subclasses can overridden + * {@link #implFlush(ByteBuffer) implFlush} to perform writing action. + *

+ * The maximum number of written bytes won't larger than + * {@link ByteBuffer#remaining() out.remaining()}. If some encoder wants to + * write more bytes than the output buffer's available remaining space, then + * CoderResult.OVERFLOW will be returned, and this method + * must be called again with a byte buffer that has free space. Otherwise + * this method will return CoderResult.UNDERFLOW, which + * means one encoding process has been completed successfully. + *

+ * During the flush, the output buffer's position will be changed + * accordingly, while its mark and limit will be intact. + * + * @param out + * the given output buffer. + * @return CoderResult.UNDERFLOW or + * CoderResult.OVERFLOW. + * @throws IllegalStateException + * if this encoder hasn't read all input characters during one + * encoding process, which means neither after calling + * {@link #encode(CharBuffer) encode(CharBuffer)} nor after + * calling {@link #encode(CharBuffer, ByteBuffer, boolean) + * encode(CharBuffer, ByteBuffer, boolean)} with {@code true} + * for the last boolean parameter. + */ + public final CoderResult flush(ByteBuffer out) { + if (status != END && status != READY) { + throw new IllegalStateException(); + } + CoderResult result = implFlush(out); + if (result == CoderResult.UNDERFLOW) { + status = FLUSH; + } + return result; + } + + /** + * Flushes this encoder. The default implementation does nothing and always + * returns CoderResult.UNDERFLOW; this method can be + * overridden if needed. + * + * @param out + * the output buffer. + * @return CoderResult.UNDERFLOW or + * CoderResult.OVERFLOW. + */ + protected CoderResult implFlush(ByteBuffer out) { + return CoderResult.UNDERFLOW; + } + + /** + * Notifies that this encoder's CodingErrorAction specified + * for malformed input error has been changed. The default implementation + * does nothing; this method can be overridden if needed. + * + * @param newAction + * the new action. + */ + protected void implOnMalformedInput(CodingErrorAction newAction) { + // default implementation is empty + } + + /** + * Notifies that this encoder's CodingErrorAction specified + * for unmappable character error has been changed. The default + * implementation does nothing; this method can be overridden if needed. + * + * @param newAction + * the new action. + */ + protected void implOnUnmappableCharacter(CodingErrorAction newAction) { + // default implementation is empty + } + + /** + * Notifies that this encoder's replacement has been changed. The default + * implementation does nothing; this method can be overridden if needed. + * + * @param newReplacement + * the new replacement string. + */ + protected void implReplaceWith(byte[] newReplacement) { + // default implementation is empty + } + + /** + * Resets this encoder's charset related state. The default implementation + * does nothing; this method can be overridden if needed. + */ + protected void implReset() { + // default implementation is empty + } + + /** + * Checks if the given argument is legal as this encoder's replacement byte + * array. + * + * The given byte array is legal if and only if it can be decode into + * sixteen bits Unicode characters. + * + * This method can be overridden for performance improvement. + * + * @param repl + * the given byte array to be checked. + * @return true if the the given argument is legal as this encoder's + * replacement byte array. + */ + public boolean isLegalReplacement(byte[] repl) { + if (decoder == null) { + decoder = cs.newDecoder(); + } + + CodingErrorAction malform = decoder.malformedInputAction(); + CodingErrorAction unmap = decoder.unmappableCharacterAction(); + decoder.onMalformedInput(CodingErrorAction.REPORT); + decoder.onUnmappableCharacter(CodingErrorAction.REPORT); + ByteBuffer in = ByteBuffer.wrap(repl); + CharBuffer out = CharBuffer.allocate((int) (repl.length * decoder + .maxCharsPerByte())); + CoderResult result = decoder.decode(in, out, true); + decoder.onMalformedInput(malform); + decoder.onUnmappableCharacter(unmap); + return !result.isError(); + } + + /** + * Gets this encoder's CodingErrorAction when a malformed + * input error occurred during the encoding process. + * + * @return this encoder's CodingErrorAction when a malformed + * input error occurred during the encoding process. + */ + public CodingErrorAction malformedInputAction() { + return malformAction; + } + + /** + * Gets 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; + } + + /** + * Sets this encoder's action on malformed input error. + * + * This method will call the + * {@link #implOnMalformedInput(CodingErrorAction) implOnMalformedInput} + * method with the given new action as argument. + * + * @param newAction + * the new action on malformed input error. + * @return this encoder. + * @throws IllegalArgumentException + * if the given newAction is null. + */ + public final CharsetEncoder onMalformedInput(CodingErrorAction newAction) { + if (null == newAction) { + throw new IllegalArgumentException("Action on malformed input error cannot be null!"); + } + malformAction = newAction; + implOnMalformedInput(newAction); + return this; + } + + /** + * Sets this encoder's action on unmappable character error. + * + * This method will call the + * {@link #implOnUnmappableCharacter(CodingErrorAction) implOnUnmappableCharacter} + * method with the given new action as argument. + * + * @param newAction + * the new action on unmappable character error. + * @return this encoder. + * @throws IllegalArgumentException + * if the given newAction is null. + */ + public final CharsetEncoder onUnmappableCharacter( + CodingErrorAction newAction) { + if (null == newAction) { + throw new IllegalArgumentException("Action on unmappable character error cannot be null!"); + } + unmapAction = 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. + */ + public final byte[] replacement() { + return replace; + } + + /** + * Sets the new replacement value. + * + * This method first checks the given replacement's validity, then changes + * the replacement value and finally calls the + * {@link #implReplaceWith(byte[]) implReplaceWith} method with the given + * new replacement as argument. + * + * @param replacement + * the replacement byte array, cannot be null or empty, its + * length cannot be larger than maxBytesPerChar, + * and it must be legal replacement, which can be justified by + * calling isLegalReplacement(byte[] repl). + * @return this encoder. + * @throws IllegalArgumentException + * if the given replacement cannot satisfy the requirement + * mentioned above. + */ + public final CharsetEncoder replaceWith(byte[] replacement) { + if (null == replacement || 0 == replacement.length + || maxBytes < replacement.length + || !isLegalReplacement(replacement)) { + throw new IllegalArgumentException("Replacement is illegal"); + } + replace = replacement; + implReplaceWith(replacement); + return this; + } + + /** + * Resets this encoder. This method will reset the internal status and then + * calla implReset() to reset any status related to the + * specific charset. + * + * @return this encoder. + */ + public final CharsetEncoder reset() { + status = INIT; + implReset(); + return this; + } + + /** + * Gets this encoder's CodingErrorAction when unmappable + * character occurred during encoding process. + * + * @return this encoder's CodingErrorAction when unmappable + * character occurred during encoding process. + */ + public CodingErrorAction unmappableCharacterAction() { + return unmapAction; + } +} diff --git a/jre_emul/apache_harmony/classlib/modules/nio_char/src/main/java/java/nio/charset/spi/CharsetProvider.java b/jre_emul/apache_harmony/classlib/modules/nio_char/src/main/java/java/nio/charset/spi/CharsetProvider.java new file mode 100644 index 0000000000..8a470d7774 --- /dev/null +++ b/jre_emul/apache_harmony/classlib/modules/nio_char/src/main/java/java/nio/charset/spi/CharsetProvider.java @@ -0,0 +1,61 @@ +/* Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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. + */ + +package java.nio.charset.spi; + +import java.nio.charset.Charset; +import java.util.Iterator; + +/** + * The service provider class for character sets. + */ +public abstract class CharsetProvider { + + // The permission required to construct a new provider. + private static final RuntimePermission CONSTRUCT_PERM = new RuntimePermission( + "charsetProvider"); //$NON-NLS-1$ + + /** + * Constructor for subclassing with concrete types. + * + * @throws SecurityException + * if there is a security manager installed that does not permit + * the runtime permission labeled "charsetProvider". + */ + protected CharsetProvider() { + SecurityManager securityManager = System.getSecurityManager(); + if (securityManager != null) + securityManager.checkPermission(CONSTRUCT_PERM); + } + + /** + * Returns an iterator over all the available charsets. + * + * @return the iterator. + */ + public abstract Iterator charsets(); + + /** + * Returns the named charset. + *

+ * If the charset is unavailable the method returns null. + * + * @param charsetName + * the canonical or alias name of a character set. + * @return the charset, or null if unavailable. + */ + public abstract Charset charsetForName(String charsetName); +} diff --git a/jre_emul/environment.mk b/jre_emul/environment.mk index 45a671b6a5..e2d86eb744 100644 --- a/jre_emul/environment.mk +++ b/jre_emul/environment.mk @@ -39,6 +39,7 @@ JRE_BEANS_ROOT = $(APACHE_HARMONY_BASE)/beans/src/main/java JRE_CONCURRENT_ROOT = $(APACHE_HARMONY_BASE)/concurrent/src/main/java JRE_KERNEL_ROOT = $(APACHE_HARMONY_BASE)/luni-kernel/src/main/java JRE_MATH_ROOT = $(APACHE_HARMONY_BASE)/math/src/main/java +JRE_NIO_CHAR_ROOT = $(APACHE_HARMONY_BASE)/nio_char/src/main/java JRE_TEST_ROOT = $(APACHE_HARMONY_BASE)/luni/src/test/api/common JRE_MATH_TEST_ROOT = $(APACHE_HARMONY_BASE)/math/src/test/java JRE_TEXT_TEST_ROOT = $(APACHE_HARMONY_BASE)/text/src/test/java @@ -125,7 +126,7 @@ JRE_SRC_ROOTS = $(JRE_ROOT) $(JRE_CONCURRENT_ROOT) $(JRE_KERNEL_ROOT) \ $(ANDROID_XML_ROOT) $(EMULATION_CLASS_DIR) $(JRE_ARCHIVE_ROOT) \ $(ANDROID_CORE_ROOT) $(ANDROID_JSON_ROOT) $(J2OBJC_ANNOTATIONS_ROOT) \ $(JRE_BEANS_ROOT) $(NEW_ANDROID_LUNI_ROOT) $(ANDROID_OPENJDK_ROOT) \ - $(OKIO_ROOT) $(J2OBJC_OPENJDK_ROOT) + $(OKIO_ROOT) $(J2OBJC_OPENJDK_ROOT) $(JRE_NIO_CHAR_ROOT) JRE_SRC = $(subst $(eval) ,:,$(JRE_SRC_ROOTS)) TEST_SRC_ROOTS = $(JRE_TEST_ROOT) $(JRE_MATH_TEST_ROOT) \ $(TEST_SUPPORT_ROOT) $(MATH_TEST_SUPPORT_ROOT) $(REGEX_TEST_ROOT) \ diff --git a/jre_emul/java_sources.mk b/jre_emul/java_sources.mk index 79aa4a0b25..e4cbe39625 100644 --- a/jre_emul/java_sources.mk +++ b/jre_emul/java_sources.mk @@ -218,6 +218,7 @@ JAVA_PUBLIC_SOURCES_CORE = \ java/nio/charset/StandardCharsets.java \ java/nio/charset/UnmappableCharacterException.java \ java/nio/charset/UnsupportedCharsetException.java \ + java/nio/charset/spi/CharsetProvider.java \ java/security/AllPermission.java \ java/security/AllPermissionCollection.java \ java/security/BasicPermission.java \ @@ -354,6 +355,9 @@ JAVA_PUBLIC_SOURCES_CORE = \ JAVA_PRIVATE_SOURCES_CORE = \ com/google/j2objc/LibraryNotLinkedError.java \ com/google/j2objc/ReflectionStrippedError.java \ + com/google/j2objc/nio/charset/IOSCharset.java \ + com/google/j2objc/nio/charset/IOSCharsetDecoder.java \ + com/google/j2objc/nio/charset/IOSCharsetEncoder.java \ com/google/j2objc/util/NativeTimeZone.java \ dalvik/system/BlockGuard.java \ dalvik/system/CloseGuard.java \ @@ -390,9 +394,6 @@ JAVA_PRIVATE_SOURCES_CORE = \ java/nio/LongArrayBuffer.java \ java/nio/MemoryBlock.java \ java/nio/ShortArrayBuffer.java \ - java/nio/charset/IOSCharset.java \ - java/nio/charset/IOSCharsetDecoder.java \ - java/nio/charset/IOSCharsetEncoder.java \ java/nio/charset/Charsets.java \ java/nio/charset/ModifiedUtf8.java \ java/util/DualPivotQuicksort.java \