You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@harmony.apache.org by te...@apache.org on 2006/03/07 13:40:58 UTC
svn commit: r383865 -
/incubator/harmony/enhanced/classlib/trunk/modules/nio_char/src/main/java/java/nio/charset/CharsetDecoder.java
Author: tellison
Date: Tue Mar 7 04:40:57 2006
New Revision: 383865
URL: http://svn.apache.org/viewcvs?rev=383865&view=rev
Log:
Replaced with HARMONY-148 fix.
Added:
incubator/harmony/enhanced/classlib/trunk/modules/nio_char/src/main/java/java/nio/charset/CharsetDecoder.java
Added: incubator/harmony/enhanced/classlib/trunk/modules/nio_char/src/main/java/java/nio/charset/CharsetDecoder.java
URL: http://svn.apache.org/viewcvs/incubator/harmony/enhanced/classlib/trunk/modules/nio_char/src/main/java/java/nio/charset/CharsetDecoder.java?rev=383865&view=auto
==============================================================================
--- incubator/harmony/enhanced/classlib/trunk/modules/nio_char/src/main/java/java/nio/charset/CharsetDecoder.java (added)
+++ incubator/harmony/enhanced/classlib/trunk/modules/nio_char/src/main/java/java/nio/charset/CharsetDecoder.java Tue Mar 7 04:40:57 2006
@@ -0,0 +1,803 @@
+/* Copyright 2004 The Apache Software Foundation or its licensors, as applicable
+ *
+ * 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.
+ */
+
+package java.nio.charset;
+
+import java.nio.BufferOverflowException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+
+/**
+ * An converter that can convert bytes sequence in some charset to 16-bit
+ * Unicode character sequence.
+ * <p>
+ * The input byte sequence is wrapped by {@link java.nio.ByteBuffer ByteBuffer}
+ * and the output character sequence is {@link java.nio.CharBuffer CharBuffer}.
+ * A decoder instance should be used in following sequence, which is referred to
+ * as a decoding operation:
+ * <ol>
+ * <li>Invoking the {@link #reset() reset} method to reset the decoder if the
+ * decoder has been used;</li>
+ * <li>Invoking the {@link #decode(ByteBuffer, CharBuffer, boolean) decode}
+ * method until the addtional input is not needed, the <code>endOfInput</code>
+ * parameter must be set to false, the input buffer must be filled and the
+ * output buffer must be flushed between invocations;</li>
+ * <li>Invoking the {@link #decode(ByteBuffer, CharBuffer, boolean) decode}
+ * method last time, and the the <code>endOfInput</code> parameter must be set
+ * to true</li>
+ * <li>Invoking the {@link #flush(CharBuffer) flush} method to flush the
+ * output.</li>
+ * </ol>
+ * </p>
+ * <p>
+ * The {@link #decode(ByteBuffer, CharBuffer, boolean) decode} method will
+ * convert as many bytes as possible, and the process won't stop except the
+ * input bytes has been run out of, 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 can include filling the input buffer,
+ * flushing the output buffer, recovering from error and trying again.
+ * </p>
+ * <p>
+ * There are two common decoding errors. One is named as malformed and it is
+ * returned when the input byte sequence is illegal for current specific
+ * charset, the other is named as unmappable character and it is returned when a
+ * problem occurs mapping a legal input byte sequence to its Unicode character
+ * equivalent.
+ * </p>
+ * <p>
+ * The two 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 string. The replacement string is "\uFFFD" by default and can be
+ * changed by invoking {@link #replaceWith(String) replaceWith} method. The
+ * invoker of this decoder can choose one way by specifing a
+ * {@link CodingErrorAction CodingErrorAction} instance for each error type via
+ * {@link #onMalformedInput(CodingErrorAction) onMalformedInput} method and
+ * {@link #onUnmappableCharacter(CodingErrorAction) onUnmappableCharacter}
+ * method.
+ * </p>
+ * <p>
+ * This class is abstract class and encapsulate many common operations of
+ * decoding process for all charsets. Decoder for specific charset should extend
+ * this class and need only implement
+ * {@link #decodeLoop(ByteBuffer, CharBuffer) decodeLoop} method for basic
+ * decoding loop. If a subclass mantains internl state, it should override the
+ * {@link #implFlush(CharBuffer) implFlush} method and
+ * {@link #implReset() implReset} method in addition.
+ * </p>
+ * <p>
+ * This class is not thread-safe.
+ * </p>
+ *
+ * @see java.nio.charset.Charset
+ * @see java.nio.charset.CharsetEncoder
+ */
+public abstract class CharsetDecoder {
+ /*
+ * --------------------------------------- Consts
+ * ---------------------------------------
+ */
+ /*
+ * 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;
+
+ /*
+ * --------------------------------------- Instance variables
+ * ---------------------------------------
+ */
+ // 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;
+
+ private byte[] remains = null;
+
+ // replaceString should be put into output by next decode invocation
+ // if OVERFLOW occured in current decode invocation.
+ private boolean needReplace = false;
+
+ /*
+ * --------------------------------------- Constructor
+ * ---------------------------------------
+ */
+ /**
+ * Construct a new <code>CharsetDecoder</code> using given
+ * <code>Charset</code>, average number and maximum number of characters
+ * created by this decoder for one input byte, and the default replacement
+ * string "\uFFFD".
+ *
+ * @param charset
+ * this decoder's <code>Charset</code>, which create this
+ * decoder
+ * @param averageCharsPerByte
+ * average number of characters created by this decoder for one
+ * input byte, must be positive
+ * @param maxCharsPerByte
+ * maximum number of characters created by this decoder for one
+ * input byte, must be positive
+ * @throws IllegalArgumentException
+ * if <code>averageCharsPerByte</code> or
+ * <code>maxCharsPerByte</code> is negative
+ */
+ protected CharsetDecoder(Charset charset, float averageCharsPerByte,
+ float maxCharsPerByte) {
+ if (averageCharsPerByte <= 0 || maxCharsPerByte <= 0) {
+ throw new IllegalArgumentException(
+ "Characters number for one byte must be positive.");
+ }
+ if(averageCharsPerByte > maxCharsPerByte){
+ throw new IllegalArgumentException(
+ "averageCharsPerByte is greater than maxCharsPerByte");
+ }
+ averChars = averageCharsPerByte;
+ maxChars = maxCharsPerByte;
+ cs = charset;
+ status = INIT;
+ malformAction = CodingErrorAction.REPORT;
+ unmapAction = CodingErrorAction.REPORT;
+ replace = "\ufffd"; //$NON-NLS-1$
+ }
+
+ /*
+ * --------------------------------------- Methods
+ * ---------------------------------------
+ */
+ /**
+ * get the average number of characters created by this decoder for single
+ * input byte
+ *
+ * @return the average number of characters created by this decoder for
+ * single input byte
+ */
+ public final float averageCharsPerByte() {
+ return averChars;
+ }
+
+ /**
+ * Get the <code>Charset</code> which creates this decoder.
+ *
+ * @return the <code>Charset</code> which creates this decoder
+ */
+ public final Charset charset() {
+ return cs;
+ }
+
+ /**
+ * This is a facade method for decoding operation.
+ * <p>
+ * This method decodes the remaning byte sequence of the given byte buffer
+ * into a new character buffer. This method performs a complete decoding
+ * operation, resets at first, then decodes, and flushes at last.
+ * </p>
+ * <p>
+ * This method should not be invoked if another decode operation is ongoing.
+ * </p>
+ *
+ * @param in
+ * the input buffer
+ * @return a new <code>CharBuffer</code> containing the the characters
+ * produced by this decoding operation. The buffer's limit will be
+ * the position of last character in buffer, and the position will
+ * be zero
+ * @throws IllegalStateException
+ * if another decoding operation is ongoing
+ * @throws MalformedInputException
+ * if illegal input byte sequence for this charset encountered,
+ * and the action for malformed error is
+ * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT}
+ * @throws UnmappableCharacterException
+ * if legal but unmappable input byte sequence for this charset
+ * encountered, and the action for unmappable character error is
+ * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT}.
+ * Unmappable means the byte sequence at the input buffer's
+ * current postion cannot be mapped to a Unicode character
+ * sequence.
+ * @throws CharacterCodingException
+ * if other exception happened during the decode operation
+ */
+ 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();
+ CharBuffer truncatedBuffer = null;
+ // truncate elements after limit in the output.
+ // clippedBuffer has the same value of capcity and limit.
+ if (output.limit() == output.capacity()) {
+ truncatedBuffer = output;
+ } else {
+ truncatedBuffer = CharBuffer.allocate(output.remaining());
+ truncatedBuffer.put(output);
+ truncatedBuffer.flip();
+ }
+ status = FLUSH;
+ return truncatedBuffer;
+ }
+
+ /*
+ * 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,
+ * and writes the equivalent character sequence into the given output buffer
+ * from its current postion.
+ * <p>
+ * The buffers' position will be changed with the reading and writing
+ * operation, but their limis and marks will be kept intact.
+ * </p>
+ * <p>
+ * A <code>CoderResult</code> instance will be returned according to
+ * following rules:
+ * <ul>
+ * <li>{@link CoderResult#OVERFLOW CoderResult.OVERFLOW} indicates that
+ * even though not all of the input has ben processed, the buffer the output
+ * is being written to has reached its capacity. In the event of this code
+ * being returned this method should be called once more with an
+ * <code>out</code> argument that has not already been filled.</li>
+ * <li>{@link CoderResult#UNDERFLOW CoderResult.UNDERFLOW} indicates that
+ * as many bytes as possible in the input buffer have been decoded. If there
+ * is no further input and no remaining bytes in the input buffer then this
+ * operation may be regarded as complete. Otherwise, this method should be
+ * called once more with additional input.</li>
+ * <li>A {@link CoderResult#malformedForLength(int) malformed input} result
+ * indicates that some malformed input error encountered, and the erroneous
+ * bytes start at the input buffer's postion 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}. </li>
+ * <li>A {@link CoderResult#unmappableForLength(int) unmappable character}
+ * result indicates that some unmappable character error encountered, and
+ * the erroneous bytes start at the input buffer's postion and their number
+ * can be got by result's {@link CoderResult#length() length}. This kind of
+ * result can be returned only if the unmappable character action is
+ * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT}. </li>
+ * </ul>
+ * </p>
+ * <p>
+ * The <code>endOfInput</code> parameter indicates that if the invoker can
+ * provider further input. This parameter is true if and only if the bytes
+ * in current input buffer are all inputs for this decoding operation. Note
+ * that it is common and won't cause error that the invoker sets false and
+ * then finds no more input available; while it may cause error that the
+ * invoker always sets true in several consecutive invocations so that any
+ * remaining input will be treated as malformed input.
+ * </p>
+ * <p>
+ * This method invokes
+ * {@link #decodeLoop(ByteBuffer, CharBuffer) decodeLoop} method to
+ * implement basic decode logic for specific charset.
+ * </p>
+ *
+ * @param in
+ * the input buffer
+ * @param out
+ * the output buffer
+ * @param endOfInput
+ * true if all the input characters have been provided
+ * @return a <code>CoderResult</code> instance which indicates the reason
+ * of termination
+ * @throws IllegalStateException
+ * if decoding has started or no more input is needed in this
+ * decoding progress.
+ * @throws CoderMalfunctionError
+ * if the {@link #decodeLoop(ByteBuffer, CharBuffer) decodeLoop}
+ * method threw an <code>BufferUnderflowException</code> or
+ * <code>BufferOverflowException</code>
+ */
+ public final CoderResult decode(ByteBuffer in, CharBuffer out,
+ boolean endOfInput) {
+ /*
+ * status check
+ */
+ if ((status == FLUSH) || (!endOfInput && status == END)) {
+ throw new IllegalStateException();
+ }
+
+ // the last decode invocation hasn't put replace string into out
+ // because of OVERFLOW
+ if(needReplace){
+ if(out.remaining() >= replace.length()){
+ out.put(replace);
+ needReplace = false;
+ }else{
+ return CoderResult.OVERFLOW;
+ }
+ }
+
+ CoderResult result = null;
+
+ // save old values of remains.length and the position of in.
+ int remainsLength = 0;
+ int inOldPosition = in.position();
+
+ // construct decodingBuffer for decode.
+ // put "remains" and "in" into decodingBuffer.
+ ByteBuffer decodingBuffer = null;
+ if (remains != null) {
+ remainsLength = remains.length;
+ decodingBuffer = ByteBuffer.allocate(remains.length
+ + in.remaining());
+ decodingBuffer.put(remains);
+ remains = null;
+ } else {
+ decodingBuffer = ByteBuffer.allocate(in.remaining());
+ }
+ decodingBuffer.put(in);
+ decodingBuffer.flip();
+
+ // begin to decode
+ while (true) {
+ CodingErrorAction action = null;
+ try {
+ result = decodeLoop(decodingBuffer, out);
+ } catch (BufferOverflowException ex) {
+ // unexpected exception
+ throw new CoderMalfunctionError(ex);
+ } catch (BufferUnderflowException ex) {
+ // unexpected exception
+ throw new CoderMalfunctionError(ex);
+ }
+
+ /*
+ * result handling
+ */
+ if (result.isUnderflow()) {
+ if (endOfInput) {
+ if (decodingBuffer.hasRemaining()) {
+ result = CoderResult.malformedForLength(decodingBuffer
+ .remaining());
+ decodingBuffer.position(decodingBuffer.limit());
+ }
+ } else {
+ if (decodingBuffer.hasRemaining()) {
+ remains = new byte[decodingBuffer.remaining()];
+ decodingBuffer.get(remains);
+ }
+ }
+ }
+ // set coding error handle action
+ if (result.isMalformed()) {
+ action = malformAction;
+ } else if (result.isUnmappable()) {
+ action = unmapAction;
+ }
+ // If the action is IGNORE or REPLACE, we should continue decoding.
+ if (action == CodingErrorAction.IGNORE) {
+ continue;
+ } else if (action == CodingErrorAction.REPLACE) {
+ if (out.remaining() < replace.length()) {
+ result = CoderResult.OVERFLOW;
+ needReplace = true;
+ } else {
+ out.put(replace);
+ continue;
+ }
+ }
+ // otherwise, the decode process ends.
+ break;
+ }
+ // set in new position
+ int offset = decodingBuffer.position() > remainsLength ? (decodingBuffer
+ .position() - remainsLength)
+ : 0;
+ in.position(inOldPosition + offset);
+
+ status = endOfInput ? END : ONGOING;
+ return result;
+ }
+
+ /**
+ * Decode bytes into characters. This method is called by
+ * {@link #decode(ByteBuffer, CharBuffer, boolean) decode} method.
+ *
+ * This method will implement the essential decoding operation, and it won't
+ * stop decoding until either all the input bytes are read, the output
+ * buffer is filled, or some exception encountered. And then it will return
+ * a <code>CoderResult</code> object indicating the result of current
+ * decoding operation. The rules to construct the <code>CoderResult</code>
+ * is same as the {@link #decode(ByteBuffer, CharBuffer, boolean) decode}.
+ * When exception encountered in the decoding operation, most implementation
+ * of this mehtod will return a relevant result object to
+ * {@link #decode(ByteBuffer, CharBuffer, boolean) decode} 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 postions
+ * will be modified accordingly, while their marks and limits will be
+ * intact. At most {@link ByteBuffer#remaining() in.remaining()} characters
+ * will be read, and {@link CharBuffer#remaining() out.remaining()} bytes
+ * will be written.
+ *
+ * Note that some implementation may pre-scan the input buffer and return
+ * <code>CoderResult.UNDERFLOW</code> until it receives sufficient input.
+ *
+ * @param in
+ * the input buffer
+ * @param out
+ * the output buffer
+ * @return a <code>CoderResult</code> instance indicating the result
+ */
+ protected abstract CoderResult decodeLoop(ByteBuffer in, CharBuffer out);
+
+ /**
+ * Get the charset detected by this decoder, this method is optional.
+ * <p>
+ * If implementing an auto-detecting charset, then this decoder returns the
+ * detected charset from this method when it is available. The returned
+ * charset will be the same for the rest of the decode operation.
+ * </p>
+ * <p>
+ * If insuffient bytes have been read to determine the charset,
+ * <code>IllegalStateException</code> will be throwed.
+ * </p>
+ * <p>
+ * The default implementation alwyas throws
+ * <code>UnsupportedOperationException</code>, so it should be overridden
+ * by subclass if needed.
+ * </p>
+ *
+ * @return the charset detected by this decoder, or null if it is not yet
+ * determined
+ * @throws UnsupportedOperationException
+ * if this decoder does not implmenet an auto-detecting charset
+ * @throws IllegalStateException
+ * if insuffient bytes have been read to determine the charset
+ */
+ public Charset detectedCharset() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Flush this decoder.
+ *
+ * This method will call {@link #implFlush(CharBuffer) implFlush}. Some
+ * decoders may need to write some charaters to the output buffer when they
+ * have read all input bytes, subclasses can overridden
+ * {@link #implFlush(CharBuffer) implFlush} to perform writing action.
+ *
+ * The maximum number of written bytes won't larger than
+ * {@link CharBuffer#remaining() out.remaining()}. If some decoder want to
+ * write more bytes than output buffer's remaining spaces, then
+ * <code>CoderResult.OVERFLOW</code> will be returned, and this method
+ * must be called again with a character buffer that has more spaces.
+ * Otherwise this method will return <code>CoderResult.UNDERFLOW</code>,
+ * which means one decoding process has been completed sucessfully.
+ *
+ * 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 <code>CoderResult.UNDERFLOW</code> or
+ * <code>CoderResult.OVERFLOW</code>
+ * @throws IllegalStateException
+ * if this decoder hasn't read all input bytes during one
+ * decoding process, which means neither after calling
+ * {@link #decode(ByteBuffer) decode(ByteBuffer)} nor after
+ * calling {@link #decode(ByteBuffer, CharBuffer, boolean)
+ * decode(ByteBuffer, CharBuffer, boolean)} with true value 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;
+ }
+
+ /**
+ * Flush this decoder. Default implementaion does nothing and always return
+ * <code>CoderResult.UNDERFLOW</code>, and this method can be overridden
+ * if needed.
+ *
+ * @param out
+ * the output buffer
+ * @return <code>CoderResult.UNDERFLOW</code> or
+ * <code>CoderResult.OVERFLOW</code>
+ */
+ protected CoderResult implFlush(CharBuffer out) {
+ return CoderResult.UNDERFLOW;
+ }
+
+ /**
+ * Notify that this decoder's <code>CodingErrorAction</code> specified for
+ * malformed input error has been changed. Default implementaion does
+ * nothing, and this method can be overridden if needed.
+ *
+ * @param newAction
+ * The new action
+ */
+ protected void implOnMalformedInput(CodingErrorAction newAction) {
+ // default implementation is empty
+ }
+
+ /**
+ * Notify that this decoder's <code>CodingErrorAction</code> specified for
+ * unmappable character error has been changed. Default implementaion does
+ * nothing, and this method can be overridden if needed.
+ *
+ * @param newAction
+ * The new action
+ */
+ protected void implOnUnmappableCharacter(CodingErrorAction newAction) {
+ // default implementation is empty
+ }
+
+ /**
+ * Notify that this decoder's replacement has been changed. Default
+ * implementaion does nothing, and 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. Default implementaion does
+ * nothing, and this method can be overridden if needed.
+ */
+ protected void implReset() {
+ // default implementation is empty
+ }
+
+ /**
+ * Get if this decoder implements an auto-detecting charset.
+ *
+ * @return <code>true</code> if this decoder implements an auto-detecting
+ * charset
+ */
+ public boolean isAutoDetecting() {
+ return false;
+ }
+
+ /**
+ * Get if this decoder has detected a charset, this method is optional.
+ * <p>
+ * If this decoder implements an auto-detecting charset, then this method
+ * may start to return true during decoding operation to indicate that a
+ * charset has been detected in the input bytes and that the charset can be
+ * retrieved by invoking {@link #detectedCharset() detectedCharset} method.
+ * </p>
+ * <p>
+ * Note that a decoder that implements an auto-detecting charset may still
+ * succeed in decoding a portion of the given input even when it is unable
+ * to detect the charset. For this reason users should be aware that a
+ * <code>false</code> return value does not indicate that no decoding took
+ * place.
+ * </p>
+ * <p>
+ * The default implementation always throws an
+ * <code>UnsupportedOperationException</code>; it should be overridden by
+ * subclass if needed.
+ * </p>
+ *
+ * @return <code>true</code> this decoder has detected a charset
+ * @throws UnsupportedOperationException
+ * if this decoder doesn't implement an auto-detecting charset
+ */
+ public boolean isCharsetDetected() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * Gets this decoder's <code>CodingErrorAction</code> when malformed input
+ * occured during decoding process.
+ *
+ * @return this decoder's <code>CodingErrorAction</code> when malformed
+ * input occured during decoding process.
+ */
+ public CodingErrorAction malformedInputAction() {
+ return malformAction;
+ }
+
+ /**
+ * Get 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;
+ }
+
+ /**
+ * Set this decoder'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 decoder
+ * @throws IllegalArgumentException
+ * if the given newAction is null
+ */
+ public final CharsetDecoder onMalformedInput(CodingErrorAction newAction) {
+ if (null == newAction) {
+ throw new IllegalArgumentException();
+ }
+ malformAction = newAction;
+ implOnMalformedInput(newAction);
+ return this;
+ }
+
+ /**
+ * Set this decoder'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 decoder
+ * @throws IllegalArgumentException
+ * if the given newAction is null
+ */
+ public final CharsetDecoder onUnmappableCharacter(
+ CodingErrorAction newAction) {
+ if (null == newAction) {
+ throw new IllegalArgumentException();
+ }
+ unmapAction = newAction;
+ implOnUnmappableCharacter(newAction);
+ return this;
+ }
+
+ /**
+ * Get the replacement string, which is never null or empty
+ *
+ * @return the replacement string, cannot be null or empty
+ */
+ public final String replacement() {
+ return replace;
+ }
+
+ /**
+ * Set new replacement value.
+ *
+ * This method first checks the given replacement's validity, then changes
+ * the replacement value, and at last calls
+ * {@link #implReplaceWith(String) implReplaceWith} method with the given
+ * new replacement as argument.
+ *
+ * @param newReplacement
+ * the replacement string, cannot be null or empty
+ * @return this decoder
+ * @throws IllegalArgumentException
+ * if the given replacement cannot satisfy the requirement
+ * metioned above
+ */
+ public final CharsetDecoder replaceWith(String newReplacement) {
+ if (null == newReplacement || newReplacement.length() == 0) {
+ throw new IllegalArgumentException(
+ "Replacement string cannot be null or empty."); //$NON-NLS-1$
+ }
+ if (newReplacement.length() > maxChars) {
+ throw new IllegalArgumentException(
+ "Replacement string's length cannot be larger than max characters per byte."); //$NON-NLS-1$
+ }
+ replace = newReplacement;
+ implReplaceWith(newReplacement);
+ return this;
+ }
+
+ /**
+ * Reset this decoder. This method will reset internal status, and then call
+ * <code>implReset()</code> to reset any status related to specific
+ * charset.
+ *
+ * @return this decoder
+ */
+ public final CharsetDecoder reset() {
+ status = INIT;
+ remains = null;
+ implReset();
+ return this;
+ }
+
+ /**
+ * Gets this decoder's <code>CodingErrorAction</code> when unmappable
+ * character occured during decoding process.
+ *
+ * @return this decoder's <code>CodingErrorAction</code> when unmappable
+ * character occured during decoding process.
+ */
+ public CodingErrorAction unmappableCharacterAction() {
+ return unmapAction;
+ }
+}