1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package java.nio.charset;
18
19import java.nio.BufferOverflowException;
20import java.nio.BufferUnderflowException;
21import java.nio.ByteBuffer;
22import java.nio.CharBuffer;
23
24/**
25 * A converter that can convert a byte sequence from a charset into a 16-bit
26 * Unicode character sequence.
27 * <p>
28 * The input byte sequence is wrapped by a
29 * {@link java.nio.ByteBuffer ByteBuffer} and the output character sequence is a
30 * {@link java.nio.CharBuffer CharBuffer}. A decoder instance should be used in
31 * the following sequence, which is referred to as a decoding operation:
32 * <ol>
33 * <li>invoking the {@link #reset() reset} method to reset the decoder if the
34 * decoder has been used;</li>
35 * <li>invoking the {@link #decode(ByteBuffer, CharBuffer, boolean) decode}
36 * method until the additional input is not needed, the <code>endOfInput</code>
37 * parameter must be set to false, the input buffer must be filled and the
38 * output buffer must be flushed between invocations;</li>
39 * <li>invoking the {@link #decode(ByteBuffer, CharBuffer, boolean) decode}
40 * method for the last time, and then the <code>endOfInput</code> parameter
41 * must be set to true;</li>
42 * <li>invoking the {@link #flush(CharBuffer) flush} method to flush the
43 * output.</li>
44 * </ol>
45 * <p>
46 * The {@link #decode(ByteBuffer, CharBuffer, boolean) decode} method will
47 * convert as many bytes as possible, and the process won't stop until the input
48 * bytes have run out, the output buffer has been filled or some error has
49 * happened. A {@link CoderResult CoderResult} instance will be returned to
50 * indicate the stop reason, and the invoker can identify the result and choose
51 * further action, which includes filling the input buffer, flushing the output
52 * buffer or recovering from an error and trying again.
53 * <p>
54 * There are two common decoding errors. One is named malformed and it is
55 * returned when the input byte sequence is illegal for the current specific
56 * charset, the other is named unmappable character and it is returned when a
57 * problem occurs mapping a legal input byte sequence to its Unicode character
58 * equivalent.
59 * <p>
60 * Both errors can be handled in three ways, the default one is to report the
61 * error to the invoker by a {@link CoderResult CoderResult} instance, and the
62 * alternatives are to ignore it or to replace the erroneous input with the
63 * replacement string. The replacement string is "\uFFFD" by default and can be
64 * changed by invoking {@link #replaceWith(String) replaceWith} method. The
65 * invoker of this decoder can choose one way by specifying a
66 * {@link CodingErrorAction CodingErrorAction} instance for each error type via
67 * {@link #onMalformedInput(CodingErrorAction) onMalformedInput} method and
68 * {@link #onUnmappableCharacter(CodingErrorAction) onUnmappableCharacter}
69 * method.
70 * <p>
71 * This is an abstract class and encapsulates many common operations of the
72 * decoding process for all charsets. Decoders for a specific charset should
73 * extend this class and need only to implement the
74 * {@link #decodeLoop(ByteBuffer, CharBuffer) decodeLoop} method for the basic
75 * decoding. If a subclass maintains an internal state, it should override the
76 * {@link #implFlush(CharBuffer) implFlush} method and the
77 * {@link #implReset() implReset} method in addition.
78 * <p>
79 * This class is not thread-safe.
80 *
81 * @see java.nio.charset.Charset
82 * @see java.nio.charset.CharsetEncoder
83 */
84public abstract class CharsetDecoder {
85    private static final int INIT = 0;
86    private static final int ONGOING = 1;
87    private static final int END = 2;
88    private static final int FLUSH = 3;
89
90    private final float averageCharsPerByte;
91    private final float maxCharsPerByte;
92
93    private final Charset cs;
94
95    private CodingErrorAction malformedInputAction;
96    private CodingErrorAction unmappableCharacterAction;
97
98    private String replacementChars;
99
100    private int status;
101
102    /**
103     * Constructs a new <code>CharsetDecoder</code> using the given
104     * <code>Charset</code>, average number and maximum number of characters
105     * created by this decoder for one input byte, and the default replacement
106     * string "\uFFFD".
107     *
108     * @param charset
109     *            the <code>Charset</code> to be used by this decoder.
110     * @param averageCharsPerByte
111     *            the average number of characters created by this decoder for
112     *            one input byte, must be positive.
113     * @param maxCharsPerByte
114     *            the maximum number of characters created by this decoder for
115     *            one input byte, must be positive.
116     * @throws IllegalArgumentException
117     *             if <code>averageCharsPerByte</code> or
118     *             <code>maxCharsPerByte</code> is negative.
119     */
120    protected CharsetDecoder(Charset charset, float averageCharsPerByte, float maxCharsPerByte) {
121        if (averageCharsPerByte <= 0 || maxCharsPerByte <= 0) {
122            throw new IllegalArgumentException("averageCharsPerByte and maxCharsPerByte must be positive");
123        }
124        if (averageCharsPerByte > maxCharsPerByte) {
125            throw new IllegalArgumentException("averageCharsPerByte is greater than maxCharsPerByte");
126        }
127        this.averageCharsPerByte = averageCharsPerByte;
128        this.maxCharsPerByte = maxCharsPerByte;
129        cs = charset;
130        status = INIT;
131        malformedInputAction = CodingErrorAction.REPORT;
132        unmappableCharacterAction = CodingErrorAction.REPORT;
133        replacementChars = "\ufffd";
134    }
135
136    /**
137     * Returns the average number of characters created by this decoder for a
138     * single input byte.
139     */
140    public final float averageCharsPerByte() {
141        return averageCharsPerByte;
142    }
143
144    /**
145     * Returns the {@link Charset} which this decoder uses.
146     */
147    public final Charset charset() {
148        return cs;
149    }
150
151    /**
152     * This is a facade method for the decoding operation.
153     * <p>
154     * This method decodes the remaining byte sequence of the given byte buffer
155     * into a new character buffer. This method performs a complete decoding
156     * operation, resets at first, then decodes, and flushes at last.
157     * <p>
158     * This method should not be invoked while another {@code decode} operation
159     * is ongoing.
160     *
161     * @param in
162     *            the input buffer.
163     * @return a new <code>CharBuffer</code> containing the the characters
164     *         produced by this decoding operation. The buffer's limit will be
165     *         the position of the last character in the buffer, and the
166     *         position will be zero.
167     * @throws IllegalStateException
168     *             if another decoding operation is ongoing.
169     * @throws MalformedInputException
170     *             if an illegal input byte sequence for this charset was
171     *             encountered, and the action for malformed error is
172     *             {@link CodingErrorAction#REPORT CodingErrorAction.REPORT}
173     * @throws UnmappableCharacterException
174     *             if a legal but unmappable input byte sequence for this
175     *             charset was encountered, and the action for unmappable
176     *             character error is
177     *             {@link CodingErrorAction#REPORT CodingErrorAction.REPORT}.
178     *             Unmappable means the byte sequence at the input buffer's
179     *             current position cannot be mapped to a Unicode character
180     *             sequence.
181     * @throws CharacterCodingException
182     *             if another exception happened during the decode operation.
183     */
184    public final CharBuffer decode(ByteBuffer in) throws CharacterCodingException {
185        reset();
186        int length = (int) (in.remaining() * averageCharsPerByte);
187        CharBuffer output = CharBuffer.allocate(length);
188        CoderResult result = null;
189        while (true) {
190            result = decode(in, output, false);
191            checkCoderResult(result);
192            if (result.isUnderflow()) {
193                break;
194            } else if (result.isOverflow()) {
195                output = allocateMore(output);
196            }
197        }
198        result = decode(in, output, true);
199        checkCoderResult(result);
200
201        while (true) {
202            result = flush(output);
203            checkCoderResult(result);
204            if (result.isOverflow()) {
205                output = allocateMore(output);
206            } else {
207                break;
208            }
209        }
210
211        output.flip();
212        status = FLUSH;
213        return output;
214    }
215
216    /*
217     * checks the result whether it needs to throw CharacterCodingException.
218     */
219    private void checkCoderResult(CoderResult result) throws CharacterCodingException {
220        if (result.isMalformed() && malformedInputAction == CodingErrorAction.REPORT) {
221            throw new MalformedInputException(result.length());
222        } else if (result.isUnmappable() && unmappableCharacterAction == CodingErrorAction.REPORT) {
223            throw new UnmappableCharacterException(result.length());
224        }
225    }
226
227    /*
228     * original output is full and doesn't have remaining. allocate more space
229     * to new CharBuffer and return it, the contents in the given buffer will be
230     * copied into the new buffer.
231     */
232    private CharBuffer allocateMore(CharBuffer output) {
233        if (output.capacity() == 0) {
234            return CharBuffer.allocate(1);
235        }
236        CharBuffer result = CharBuffer.allocate(output.capacity() * 2);
237        output.flip();
238        result.put(output);
239        return result;
240    }
241
242    /**
243     * Decodes bytes starting at the current position of the given input buffer,
244     * and writes the equivalent character sequence into the given output buffer
245     * from its current position.
246     * <p>
247     * The buffers' position will be changed with the reading and writing
248     * operation, but their limits and marks will be kept intact.
249     * <p>
250     * A <code>CoderResult</code> instance will be returned according to
251     * following rules:
252     * <ul>
253     * <li>{@link CoderResult#OVERFLOW CoderResult.OVERFLOW} indicates that
254     * even though not all of the input has been processed, the buffer the
255     * output is being written to has reached its capacity. In the event of this
256     * code being returned this method should be called once more with an
257     * <code>out</code> argument that has not already been filled.</li>
258     * <li>{@link CoderResult#UNDERFLOW CoderResult.UNDERFLOW} indicates that
259     * as many bytes as possible in the input buffer have been decoded. If there
260     * is no further input and no remaining bytes in the input buffer then this
261     * operation may be regarded as complete. Otherwise, this method should be
262     * called once more with additional input.</li>
263     * <li>A {@link CoderResult#malformedForLength(int) malformed input} result
264     * indicates that some malformed input error has been encountered, and the
265     * erroneous bytes start at the input buffer's position and their number can
266     * be got by result's {@link CoderResult#length() length}. This kind of
267     * result can be returned only if the malformed action is
268     * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT}. </li>
269     * <li>A {@link CoderResult#unmappableForLength(int) unmappable character}
270     * result indicates that some unmappable character error has been
271     * encountered, and the erroneous bytes start at the input buffer's position
272     * and their number can be got by result's
273     * {@link CoderResult#length() length}. This kind of result can be returned
274     * only if the unmappable character action is
275     * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT}. </li>
276     * </ul>
277     * <p>
278     * The <code>endOfInput</code> parameter indicates that the invoker cannot
279     * provide further input. This parameter is true if and only if the bytes in
280     * current input buffer are all inputs for this decoding operation. Note
281     * that it is common and won't cause an error if the invoker sets false and
282     * then can't provide more input, while it may cause an error if the invoker
283     * always sets true in several consecutive invocations. This would make the
284     * remaining input to be treated as malformed input.
285     * <p>
286     * This method invokes the
287     * {@link #decodeLoop(ByteBuffer, CharBuffer) decodeLoop} method to
288     * implement the basic decode logic for a specific charset.
289     *
290     * @param in
291     *            the input buffer.
292     * @param out
293     *            the output buffer.
294     * @param endOfInput
295     *            true if all the input characters have been provided.
296     * @return a <code>CoderResult</code> instance which indicates the reason
297     *         of termination.
298     * @throws IllegalStateException
299     *             if decoding has started or no more input is needed in this
300     *             decoding progress.
301     * @throws CoderMalfunctionError
302     *             if the {@link #decodeLoop(ByteBuffer, CharBuffer) decodeLoop}
303     *             method threw an <code>BufferUnderflowException</code> or
304     *             <code>BufferOverflowException</code>.
305     */
306    public final CoderResult decode(ByteBuffer in, CharBuffer out,
307            boolean endOfInput) {
308        /*
309         * status check
310         */
311        if ((status == FLUSH) || (!endOfInput && status == END)) {
312            throw new IllegalStateException();
313        }
314
315        CoderResult result = null;
316
317        // begin to decode
318        while (true) {
319            CodingErrorAction action = null;
320            try {
321                result = decodeLoop(in, out);
322            } catch (BufferOverflowException ex) {
323                // unexpected exception
324                throw new CoderMalfunctionError(ex);
325            } catch (BufferUnderflowException ex) {
326                // unexpected exception
327                throw new CoderMalfunctionError(ex);
328            }
329
330            /*
331             * result handling
332             */
333            if (result.isUnderflow()) {
334                int remaining = in.remaining();
335                status = endOfInput ? END : ONGOING;
336                if (endOfInput && remaining > 0) {
337                    result = CoderResult.malformedForLength(remaining);
338                } else {
339                    return result;
340                }
341            }
342            if (result.isOverflow()) {
343                return result;
344            }
345            // set coding error handle action
346            action = malformedInputAction;
347            if (result.isUnmappable()) {
348                action = unmappableCharacterAction;
349            }
350            // If the action is IGNORE or REPLACE, we should continue decoding.
351            if (action == CodingErrorAction.REPLACE) {
352                if (out.remaining() < replacementChars.length()) {
353                    return CoderResult.OVERFLOW;
354                }
355                out.put(replacementChars);
356            } else {
357                if (action != CodingErrorAction.IGNORE)
358                    return result;
359            }
360            in.position(in.position() + result.length());
361        }
362    }
363
364    /**
365     * Decodes bytes into characters. This method is called by the
366     * {@link #decode(ByteBuffer, CharBuffer, boolean) decode} method.
367     * <p>
368     * This method will implement the essential decoding operation, and it won't
369     * stop decoding until either all the input bytes are read, the output
370     * buffer is filled, or some exception is encountered. Then it will return a
371     * <code>CoderResult</code> object indicating the result of current
372     * decoding operation. The rules to construct the <code>CoderResult</code>
373     * are the same as for
374     * {@link #decode(ByteBuffer, CharBuffer, boolean) decode}. When an
375     * exception is encountered in the decoding operation, most implementations
376     * of this method will return a relevant result object to the
377     * {@link #decode(ByteBuffer, CharBuffer, boolean) decode} method, and some
378     * performance optimized implementation may handle the exception and
379     * implement the error action itself.
380     * <p>
381     * The buffers are scanned from their current positions, and their positions
382     * will be modified accordingly, while their marks and limits will be
383     * intact. At most {@link ByteBuffer#remaining() in.remaining()} characters
384     * will be read, and {@link CharBuffer#remaining() out.remaining()} bytes
385     * will be written.
386     * <p>
387     * Note that some implementations may pre-scan the input buffer and return a
388     * <code>CoderResult.UNDERFLOW</code> until it receives sufficient input.
389     *
390     * @param in
391     *            the input buffer.
392     * @param out
393     *            the output buffer.
394     * @return a <code>CoderResult</code> instance indicating the result.
395     */
396    protected abstract CoderResult decodeLoop(ByteBuffer in, CharBuffer out);
397
398    /**
399     * Gets the charset detected by this decoder; this method is optional.
400     * <p>
401     * If implementing an auto-detecting charset, then this decoder returns the
402     * detected charset from this method when it is available. The returned
403     * charset will be the same for the rest of the decode operation.
404     * <p>
405     * If insufficient bytes have been read to determine the charset, an
406     * <code>IllegalStateException</code> will be thrown.
407     * <p>
408     * The default implementation always throws
409     * <code>UnsupportedOperationException</code>, so it should be overridden
410     * by a subclass if needed.
411     *
412     * @return the charset detected by this decoder, or null if it is not yet
413     *         determined.
414     * @throws UnsupportedOperationException
415     *             if this decoder does not implement an auto-detecting charset.
416     * @throws IllegalStateException
417     *             if insufficient bytes have been read to determine the
418     *             charset.
419     */
420    public Charset detectedCharset() {
421        throw new UnsupportedOperationException();
422    }
423
424    /**
425     * Flushes this decoder.
426     *
427     * This method will call {@link #implFlush(CharBuffer) implFlush}. Some
428     * decoders may need to write some characters to the output buffer when they
429     * have read all input bytes; subclasses can override
430     * {@link #implFlush(CharBuffer) implFlush} to perform the writing operation.
431     * <p>
432     * The maximum number of written bytes won't be larger than
433     * {@link CharBuffer#remaining() out.remaining()}. If some decoder wants to
434     * write more bytes than an output buffer's remaining space allows, then a
435     * <code>CoderResult.OVERFLOW</code> will be returned, and this method
436     * must be called again with a character buffer that has more remaining
437     * space. Otherwise this method will return
438     * <code>CoderResult.UNDERFLOW</code>, which means one decoding process
439     * has been completed successfully.
440     * <p>
441     * During the flush, the output buffer's position will be changed
442     * accordingly, while its mark and limit will be intact.
443     *
444     * @param out
445     *            the given output buffer.
446     * @return <code>CoderResult.UNDERFLOW</code> or
447     *         <code>CoderResult.OVERFLOW</code>.
448     * @throws IllegalStateException
449     *             if this decoder hasn't read all input bytes during one
450     *             decoding process, which means neither after calling
451     *             {@link #decode(ByteBuffer) decode(ByteBuffer)} nor after
452     *             calling {@link #decode(ByteBuffer, CharBuffer, boolean)
453     *             decode(ByteBuffer, CharBuffer, boolean)} with true as value
454     *             for the last boolean parameter.
455     */
456    public final CoderResult flush(CharBuffer out) {
457        if (status != END && status != INIT) {
458            throw new IllegalStateException();
459        }
460        CoderResult result = implFlush(out);
461        if (result == CoderResult.UNDERFLOW) {
462            status = FLUSH;
463        }
464        return result;
465    }
466
467    /**
468     * Flushes this decoder. The default implementation does nothing and always
469     * returns <code>CoderResult.UNDERFLOW</code>; this method can be
470     * overridden if needed.
471     *
472     * @param out
473     *            the output buffer.
474     * @return <code>CoderResult.UNDERFLOW</code> or
475     *         <code>CoderResult.OVERFLOW</code>.
476     */
477    protected CoderResult implFlush(CharBuffer out) {
478        return CoderResult.UNDERFLOW;
479    }
480
481    /**
482     * Notifies that this decoder's <code>CodingErrorAction</code> specified
483     * for malformed input error has been changed. The default implementation
484     * does nothing; this method can be overridden if needed.
485     *
486     * @param newAction
487     *            the new action.
488     */
489    protected void implOnMalformedInput(CodingErrorAction newAction) {
490        // default implementation is empty
491    }
492
493    /**
494     * Notifies that this decoder's <code>CodingErrorAction</code> specified
495     * for unmappable character error has been changed. The default
496     * implementation does nothing; this method can be overridden if needed.
497     *
498     * @param newAction
499     *            the new action.
500     */
501    protected void implOnUnmappableCharacter(CodingErrorAction newAction) {
502        // default implementation is empty
503    }
504
505    /**
506     * Notifies that this decoder's replacement has been changed. The default
507     * implementation does nothing; this method can be overridden if needed.
508     *
509     * @param newReplacement
510     *            the new replacement string.
511     */
512    protected void implReplaceWith(String newReplacement) {
513        // default implementation is empty
514    }
515
516    /**
517     * Reset this decoder's charset related state. The default implementation
518     * does nothing; this method can be overridden if needed.
519     */
520    protected void implReset() {
521        // default implementation is empty
522    }
523
524    /**
525     * Indicates whether this decoder implements an auto-detecting charset.
526     *
527     * @return <code>true</code> if this decoder implements an auto-detecting
528     *         charset.
529     */
530    public boolean isAutoDetecting() {
531        return false;
532    }
533
534    /**
535     * Indicates whether this decoder has detected a charset; this method is
536     * optional.
537     * <p>
538     * If this decoder implements an auto-detecting charset, then this method
539     * may start to return true during decoding operation to indicate that a
540     * charset has been detected in the input bytes and that the charset can be
541     * retrieved by invoking the {@link #detectedCharset() detectedCharset}
542     * method.
543     * <p>
544     * Note that a decoder that implements an auto-detecting charset may still
545     * succeed in decoding a portion of the given input even when it is unable
546     * to detect the charset. For this reason users should be aware that a
547     * <code>false</code> return value does not indicate that no decoding took
548     * place.
549     * <p>
550     * The default implementation always throws an
551     * <code>UnsupportedOperationException</code>; it should be overridden by
552     * a subclass if needed.
553     *
554     * @return <code>true</code> if this decoder has detected a charset.
555     * @throws UnsupportedOperationException
556     *             if this decoder doesn't implement an auto-detecting charset.
557     */
558    public boolean isCharsetDetected() {
559        throw new UnsupportedOperationException();
560    }
561
562    /**
563     * Returns this decoder's <code>CodingErrorAction</code> when malformed input
564     * occurred during the decoding process.
565     */
566    public CodingErrorAction malformedInputAction() {
567        return malformedInputAction;
568    }
569
570    /**
571     * Returns the maximum number of characters which can be created by this
572     * decoder for one input byte, must be positive.
573     */
574    public final float maxCharsPerByte() {
575        return maxCharsPerByte;
576    }
577
578    /**
579     * Sets this decoder's action on malformed input errors.
580     *
581     * This method will call the
582     * {@link #implOnMalformedInput(CodingErrorAction) implOnMalformedInput}
583     * method with the given new action as argument.
584     *
585     * @param newAction
586     *            the new action on malformed input error.
587     * @return this decoder.
588     * @throws IllegalArgumentException
589     *             if {@code newAction} is {@code null}.
590     */
591    public final CharsetDecoder onMalformedInput(CodingErrorAction newAction) {
592        if (newAction == null) {
593            throw new IllegalArgumentException("newAction == null");
594        }
595        malformedInputAction = newAction;
596        implOnMalformedInput(newAction);
597        return this;
598    }
599
600    /**
601     * Sets this decoder's action on unmappable character errors.
602     *
603     * This method will call the
604     * {@link #implOnUnmappableCharacter(CodingErrorAction) implOnUnmappableCharacter}
605     * method with the given new action as argument.
606     *
607     * @param newAction
608     *            the new action on unmappable character error.
609     * @return this decoder.
610     * @throws IllegalArgumentException
611     *             if {@code newAction} is {@code null}.
612     */
613    public final CharsetDecoder onUnmappableCharacter(CodingErrorAction newAction) {
614        if (newAction == null) {
615            throw new IllegalArgumentException("newAction == null");
616        }
617        unmappableCharacterAction = newAction;
618        implOnUnmappableCharacter(newAction);
619        return this;
620    }
621
622    /**
623     * Returns the replacement string, which is never null or empty.
624     */
625    public final String replacement() {
626        return replacementChars;
627    }
628
629    /**
630     * Sets the new replacement string.
631     *
632     * This method first checks the given replacement's validity, then changes
633     * the replacement value, and at last calls the
634     * {@link #implReplaceWith(String) implReplaceWith} method with the given
635     * new replacement as argument.
636     *
637     * @param replacement
638     *            the replacement string, cannot be null or empty. Its length
639     *            cannot be larger than {@link #maxCharsPerByte()}.
640     * @return this decoder.
641     * @throws IllegalArgumentException
642     *             if the given replacement cannot satisfy the requirement
643     *             mentioned above.
644     */
645    public final CharsetDecoder replaceWith(String replacement) {
646        if (replacement == null) {
647            throw new IllegalArgumentException("replacement == null");
648        }
649        if (replacement.isEmpty()) {
650            throw new IllegalArgumentException("replacement.isEmpty()");
651        }
652        if (replacement.length() > maxCharsPerByte()) {
653            throw new IllegalArgumentException("replacement length > maxCharsPerByte: " +
654                    replacement.length() + " > " + maxCharsPerByte());
655        }
656        replacementChars = replacement;
657        implReplaceWith(replacement);
658        return this;
659    }
660
661    /**
662     * Resets this decoder. This method will reset the internal status, and then
663     * calls <code>implReset()</code> to reset any status related to the
664     * specific charset.
665     *
666     * @return this decoder.
667     */
668    public final CharsetDecoder reset() {
669        status = INIT;
670        implReset();
671        return this;
672    }
673
674    /**
675     * Returns this decoder's <code>CodingErrorAction</code> when an unmappable
676     * character error occurred during the decoding process.
677     */
678    public CodingErrorAction unmappableCharacterAction() {
679        return unmappableCharacterAction;
680    }
681}
682