001: /* Licensed to the Apache Software Foundation (ASF) under one or more
002: * contributor license agreements. See the NOTICE file distributed with
003: * this work for additional information regarding copyright ownership.
004: * The ASF licenses this file to You under the Apache License, Version 2.0
005: * (the "License"); you may not use this file except in compliance with
006: * the License. You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package java.nio.charset;
018:
019: import java.nio.BufferOverflowException;
020: import java.nio.BufferUnderflowException;
021: import java.nio.ByteBuffer;
022: import java.nio.CharBuffer;
023:
024: import org.apache.harmony.niochar.internal.nls.Messages;
025:
026: /**
027: * An converter that can convert bytes sequence in some charset to 16-bit
028: * Unicode character sequence.
029: * <p>
030: * The input byte sequence is wrapped by {@link java.nio.ByteBuffer ByteBuffer}
031: * and the output character sequence is {@link java.nio.CharBuffer CharBuffer}.
032: * A decoder instance should be used in following sequence, which is referred to
033: * as a decoding operation:
034: * <ol>
035: * <li>Invoking the {@link #reset() reset} method to reset the decoder if the
036: * decoder has been used;</li>
037: * <li>Invoking the {@link #decode(ByteBuffer, CharBuffer, boolean) decode}
038: * method until the additional input is not needed, the <code>endOfInput</code>
039: * parameter must be set to false, the input buffer must be filled and the
040: * output buffer must be flushed between invocations;</li>
041: * <li>Invoking the {@link #decode(ByteBuffer, CharBuffer, boolean) decode}
042: * method last time, and the the <code>endOfInput</code> parameter must be set
043: * to true</li>
044: * <li>Invoking the {@link #flush(CharBuffer) flush} method to flush the
045: * output.</li>
046: * </ol>
047: * </p>
048: * <p>
049: * The {@link #decode(ByteBuffer, CharBuffer, boolean) decode} method will
050: * convert as many bytes as possible, and the process won't stop except the
051: * input bytes has been run out of, the output buffer has been filled or some
052: * error has happened. A {@link CoderResult CoderResult} instance will be
053: * returned to indicate the stop reason, and the invoker can identify the result
054: * and choose further action, which can include filling the input buffer,
055: * flushing the output buffer, recovering from error and trying again.
056: * </p>
057: * <p>
058: * There are two common decoding errors. One is named as malformed and it is
059: * returned when the input byte sequence is illegal for current specific
060: * charset, the other is named as unmappable character and it is returned when a
061: * problem occurs mapping a legal input byte sequence to its Unicode character
062: * equivalent.
063: * </p>
064: * <p>
065: * The two errors can be handled in three ways, the default one is to report the
066: * error to the invoker by a {@link CoderResult CoderResult} instance, and the
067: * alternatives are to ignore it or to replace the erroneous input with the
068: * replacement string. The replacement string is "\uFFFD" by default and can be
069: * changed by invoking {@link #replaceWith(String) replaceWith} method. The
070: * invoker of this decoder can choose one way by specifying a
071: * {@link CodingErrorAction CodingErrorAction} instance for each error type via
072: * {@link #onMalformedInput(CodingErrorAction) onMalformedInput} method and
073: * {@link #onUnmappableCharacter(CodingErrorAction) onUnmappableCharacter}
074: * method.
075: * </p>
076: * <p>
077: * This class is abstract class and encapsulate many common operations of
078: * decoding process for all charsets. Decoder for specific charset should extend
079: * this class and need only implement
080: * {@link #decodeLoop(ByteBuffer, CharBuffer) decodeLoop} method for basic
081: * decoding loop. If a subclass maintains internal state, it should override the
082: * {@link #implFlush(CharBuffer) implFlush} method and
083: * {@link #implReset() implReset} method in addition.
084: * </p>
085: * <p>
086: * This class is not thread-safe.
087: * </p>
088: *
089: * @see java.nio.charset.Charset
090: * @see java.nio.charset.CharsetEncoder
091: */
092: public abstract class CharsetDecoder {
093: /*
094: * --------------------------------------- Consts
095: * ---------------------------------------
096: */
097: /*
098: * internal status consts
099: */
100: private static final int INIT = 0;
101:
102: private static final int ONGOING = 1;
103:
104: private static final int END = 2;
105:
106: private static final int FLUSH = 3;
107:
108: /*
109: * --------------------------------------- Instance variables
110: * ---------------------------------------
111: */
112: // average number of chars for one byte
113: private float averChars;
114:
115: // maximum number of chars for one byte
116: private float maxChars;
117:
118: // charset for this decoder
119: private Charset cs;
120:
121: // specify the action if malformed input error encountered
122: private CodingErrorAction malformAction;
123:
124: // specify the action if unmappable character error encountered
125: private CodingErrorAction unmapAction;
126:
127: // the replacement string
128: private String replace;
129:
130: // the current status
131: private int status;
132:
133: /*
134: * --------------------------------------- Constructor
135: * ---------------------------------------
136: */
137: /**
138: * Construct a new <code>CharsetDecoder</code> using given
139: * <code>Charset</code>, average number and maximum number of characters
140: * created by this decoder for one input byte, and the default replacement
141: * string "\uFFFD".
142: *
143: * @param charset
144: * this decoder's <code>Charset</code>, which create this
145: * decoder
146: * @param averageCharsPerByte
147: * average number of characters created by this decoder for one
148: * input byte, must be positive
149: * @param maxCharsPerByte
150: * maximum number of characters created by this decoder for one
151: * input byte, must be positive
152: * @throws IllegalArgumentException
153: * if <code>averageCharsPerByte</code> or
154: * <code>maxCharsPerByte</code> is negative
155: */
156: protected CharsetDecoder(Charset charset,
157: float averageCharsPerByte, float maxCharsPerByte) {
158: if (averageCharsPerByte <= 0 || maxCharsPerByte <= 0) {
159: // niochar.00=Characters number for one byte must be positive.
160: throw new IllegalArgumentException(Messages
161: .getString("niochar.00")); //$NON-NLS-1$
162: }
163: if (averageCharsPerByte > maxCharsPerByte) {
164: // niochar.01=averageCharsPerByte is greater than maxCharsPerByte
165: throw new IllegalArgumentException(Messages
166: .getString("niochar.01")); //$NON-NLS-1$
167: }
168: averChars = averageCharsPerByte;
169: maxChars = maxCharsPerByte;
170: cs = charset;
171: status = INIT;
172: malformAction = CodingErrorAction.REPORT;
173: unmapAction = CodingErrorAction.REPORT;
174: replace = "\ufffd"; //$NON-NLS-1$
175: }
176:
177: /*
178: * --------------------------------------- Methods
179: * ---------------------------------------
180: */
181: /**
182: * get the average number of characters created by this decoder for single
183: * input byte
184: *
185: * @return the average number of characters created by this decoder for
186: * single input byte
187: */
188: public final float averageCharsPerByte() {
189: return averChars;
190: }
191:
192: /**
193: * Get the <code>Charset</code> which creates this decoder.
194: *
195: * @return the <code>Charset</code> which creates this decoder
196: */
197: public final Charset charset() {
198: return cs;
199: }
200:
201: /**
202: * This is a facade method for decoding operation.
203: * <p>
204: * This method decodes the remaining byte sequence of the given byte buffer
205: * into a new character buffer. This method performs a complete decoding
206: * operation, resets at first, then decodes, and flushes at last.
207: * </p>
208: * <p>
209: * This method should not be invoked if another decode operation is ongoing.
210: * </p>
211: *
212: * @param in
213: * the input buffer
214: * @return a new <code>CharBuffer</code> containing the the characters
215: * produced by this decoding operation. The buffer's limit will be
216: * the position of last character in buffer, and the position will
217: * be zero
218: * @throws IllegalStateException
219: * if another decoding operation is ongoing
220: * @throws MalformedInputException
221: * if illegal input byte sequence for this charset encountered,
222: * and the action for malformed error is
223: * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT}
224: * @throws UnmappableCharacterException
225: * if legal but unmappable input byte sequence for this charset
226: * encountered, and the action for unmappable character error is
227: * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT}.
228: * Unmappable means the byte sequence at the input buffer's
229: * current position cannot be mapped to a Unicode character
230: * sequence.
231: * @throws CharacterCodingException
232: * if other exception happened during the decode operation
233: */
234: public final CharBuffer decode(ByteBuffer in)
235: throws CharacterCodingException {
236: reset();
237: int length = (int) (in.remaining() * averChars);
238: CharBuffer output = CharBuffer.allocate(length);
239: CoderResult result = null;
240: while (true) {
241: result = decode(in, output, false);
242: checkCoderResult(result);
243: if (result.isUnderflow()) {
244: break;
245: } else if (result.isOverflow()) {
246: output = allocateMore(output);
247: }
248: }
249: result = decode(in, output, true);
250: checkCoderResult(result);
251:
252: while (true) {
253: result = flush(output);
254: checkCoderResult(result);
255: if (result.isOverflow()) {
256: output = allocateMore(output);
257: } else {
258: break;
259: }
260: }
261:
262: output.flip();
263: status = FLUSH;
264: return output;
265: }
266:
267: /*
268: * checks the result whether it needs to throw CharacterCodingException.
269: */
270: private void checkCoderResult(CoderResult result)
271: throws CharacterCodingException {
272: if (result.isMalformed()
273: && malformAction == CodingErrorAction.REPORT) {
274: throw new MalformedInputException(result.length());
275: } else if (result.isUnmappable()
276: && unmapAction == CodingErrorAction.REPORT) {
277: throw new UnmappableCharacterException(result.length());
278: }
279: }
280:
281: /*
282: * original output is full and doesn't have remaining. allocate more space
283: * to new CharBuffer and return it, the contents in the given buffer will be
284: * copied into the new buffer.
285: */
286: private CharBuffer allocateMore(CharBuffer output) {
287: if (output.capacity() == 0) {
288: return CharBuffer.allocate(1);
289: }
290: CharBuffer result = CharBuffer.allocate(output.capacity() * 2);
291: output.flip();
292: result.put(output);
293: return result;
294: }
295:
296: /**
297: * Decodes bytes starting at the current position of the given input buffer,
298: * and writes the equivalent character sequence into the given output buffer
299: * from its current position.
300: * <p>
301: * The buffers' position will be changed with the reading and writing
302: * operation, but their limits and marks will be kept intact.
303: * </p>
304: * <p>
305: * A <code>CoderResult</code> instance will be returned according to
306: * following rules:
307: * <ul>
308: * <li>{@link CoderResult#OVERFLOW CoderResult.OVERFLOW} indicates that
309: * even though not all of the input has been processed, the buffer the
310: * output is being written to has reached its capacity. In the event of this
311: * code being returned this method should be called once more with an
312: * <code>out</code> argument that has not already been filled.</li>
313: * <li>{@link CoderResult#UNDERFLOW CoderResult.UNDERFLOW} indicates that
314: * as many bytes as possible in the input buffer have been decoded. If there
315: * is no further input and no remaining bytes in the input buffer then this
316: * operation may be regarded as complete. Otherwise, this method should be
317: * called once more with additional input.</li>
318: * <li>A {@link CoderResult#malformedForLength(int) malformed input} result
319: * indicates that some malformed input error encountered, and the erroneous
320: * bytes start at the input buffer's position and their number can be got by
321: * result's {@link CoderResult#length() length}. This kind of result can be
322: * returned only if the malformed action is
323: * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT}. </li>
324: * <li>A {@link CoderResult#unmappableForLength(int) unmappable character}
325: * result indicates that some unmappable character error encountered, and
326: * the erroneous bytes start at the input buffer's position and their number
327: * can be got by result's {@link CoderResult#length() length}. This kind of
328: * result can be returned only if the unmappable character action is
329: * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT}. </li>
330: * </ul>
331: * </p>
332: * <p>
333: * The <code>endOfInput</code> parameter indicates that if the invoker can
334: * provider further input. This parameter is true if and only if the bytes
335: * in current input buffer are all inputs for this decoding operation. Note
336: * that it is common and won't cause error that the invoker sets false and
337: * then finds no more input available; while it may cause error that the
338: * invoker always sets true in several consecutive invocations so that any
339: * remaining input will be treated as malformed input.
340: * </p>
341: * <p>
342: * This method invokes
343: * {@link #decodeLoop(ByteBuffer, CharBuffer) decodeLoop} method to
344: * implement basic decode logic for specific charset.
345: * </p>
346: *
347: * @param in
348: * the input buffer
349: * @param out
350: * the output buffer
351: * @param endOfInput
352: * true if all the input characters have been provided
353: * @return a <code>CoderResult</code> instance which indicates the reason
354: * of termination
355: * @throws IllegalStateException
356: * if decoding has started or no more input is needed in this
357: * decoding progress.
358: * @throws CoderMalfunctionError
359: * if the {@link #decodeLoop(ByteBuffer, CharBuffer) decodeLoop}
360: * method threw an <code>BufferUnderflowException</code> or
361: * <code>BufferOverflowException</code>
362: */
363: public final CoderResult decode(ByteBuffer in, CharBuffer out,
364: boolean endOfInput) {
365: /*
366: * status check
367: */
368: if ((status == FLUSH) || (!endOfInput && status == END)) {
369: throw new IllegalStateException();
370: }
371:
372: CoderResult result = null;
373:
374: // begin to decode
375: while (true) {
376: CodingErrorAction action = null;
377: try {
378: result = decodeLoop(in, out);
379: } catch (BufferOverflowException ex) {
380: // unexpected exception
381: throw new CoderMalfunctionError(ex);
382: } catch (BufferUnderflowException ex) {
383: // unexpected exception
384: throw new CoderMalfunctionError(ex);
385: }
386:
387: /*
388: * result handling
389: */
390: if (result.isUnderflow()) {
391: int remaining = in.remaining();
392: status = endOfInput ? END : ONGOING;
393: if (endOfInput && remaining > 0) {
394: result = CoderResult.malformedForLength(remaining);
395: } else {
396: return result;
397: }
398: }
399: if (result.isOverflow()) {
400: return result;
401: }
402: // set coding error handle action
403: action = malformAction;
404: if (result.isUnmappable()) {
405: action = unmapAction;
406: }
407: // If the action is IGNORE or REPLACE, we should continue decoding.
408: if (action == CodingErrorAction.REPLACE) {
409: if (out.remaining() < replace.length()) {
410: return CoderResult.OVERFLOW;
411: }
412: out.put(replace);
413: } else {
414: if (action != CodingErrorAction.IGNORE)
415: return result;
416: }
417: in.position(in.position() + result.length());
418: }
419: }
420:
421: /**
422: * Decode bytes into characters. This method is called by
423: * {@link #decode(ByteBuffer, CharBuffer, boolean) decode} method.
424: *
425: * This method will implement the essential decoding operation, and it won't
426: * stop decoding until either all the input bytes are read, the output
427: * buffer is filled, or some exception encountered. And then it will return
428: * a <code>CoderResult</code> object indicating the result of current
429: * decoding operation. The rules to construct the <code>CoderResult</code>
430: * is same as the {@link #decode(ByteBuffer, CharBuffer, boolean) decode}.
431: * When exception encountered in the decoding operation, most implementation
432: * of this method will return a relevant result object to
433: * {@link #decode(ByteBuffer, CharBuffer, boolean) decode} method, and some
434: * performance optimized implementation may handle the exception and
435: * implement the error action itself.
436: *
437: * The buffers are scanned from their current positions, and their positions
438: * will be modified accordingly, while their marks and limits will be
439: * intact. At most {@link ByteBuffer#remaining() in.remaining()} characters
440: * will be read, and {@link CharBuffer#remaining() out.remaining()} bytes
441: * will be written.
442: *
443: * Note that some implementation may pre-scan the input buffer and return
444: * <code>CoderResult.UNDERFLOW</code> until it receives sufficient input.
445: *
446: * @param in
447: * the input buffer
448: * @param out
449: * the output buffer
450: * @return a <code>CoderResult</code> instance indicating the result
451: */
452: protected abstract CoderResult decodeLoop(ByteBuffer in,
453: CharBuffer out);
454:
455: /**
456: * Get the charset detected by this decoder, this method is optional.
457: * <p>
458: * If implementing an auto-detecting charset, then this decoder returns the
459: * detected charset from this method when it is available. The returned
460: * charset will be the same for the rest of the decode operation.
461: * </p>
462: * <p>
463: * If insufficient bytes have been read to determine the charset,
464: * <code>IllegalStateException</code> will be thrown.
465: * </p>
466: * <p>
467: * The default implementation always throws
468: * <code>UnsupportedOperationException</code>, so it should be overridden
469: * by subclass if needed.
470: * </p>
471: *
472: * @return the charset detected by this decoder, or null if it is not yet
473: * determined
474: * @throws UnsupportedOperationException
475: * if this decoder does not implement an auto-detecting charset
476: * @throws IllegalStateException
477: * if insufficient bytes have been read to determine the charset
478: */
479: public Charset detectedCharset() {
480: throw new UnsupportedOperationException();
481: }
482:
483: /**
484: * Flush this decoder.
485: *
486: * This method will call {@link #implFlush(CharBuffer) implFlush}. Some
487: * decoders may need to write some characters to the output buffer when they
488: * have read all input bytes, subclasses can overridden
489: * {@link #implFlush(CharBuffer) implFlush} to perform writing action.
490: *
491: * The maximum number of written bytes won't larger than
492: * {@link CharBuffer#remaining() out.remaining()}. If some decoder want to
493: * write more bytes than output buffer's remaining spaces, then
494: * <code>CoderResult.OVERFLOW</code> will be returned, and this method
495: * must be called again with a character buffer that has more spaces.
496: * Otherwise this method will return <code>CoderResult.UNDERFLOW</code>,
497: * which means one decoding process has been completed successfully.
498: *
499: * During the flush, the output buffer's position will be changed
500: * accordingly, while its mark and limit will be intact.
501: *
502: * @param out
503: * the given output buffer
504: * @return <code>CoderResult.UNDERFLOW</code> or
505: * <code>CoderResult.OVERFLOW</code>
506: * @throws IllegalStateException
507: * if this decoder hasn't read all input bytes during one
508: * decoding process, which means neither after calling
509: * {@link #decode(ByteBuffer) decode(ByteBuffer)} nor after
510: * calling {@link #decode(ByteBuffer, CharBuffer, boolean)
511: * decode(ByteBuffer, CharBuffer, boolean)} with true value for
512: * the last boolean parameter
513: */
514: public final CoderResult flush(CharBuffer out) {
515: if (status != END && status != INIT) {
516: throw new IllegalStateException();
517: }
518: CoderResult result = implFlush(out);
519: if (result == CoderResult.UNDERFLOW) {
520: status = FLUSH;
521: }
522: return result;
523: }
524:
525: /**
526: * Flush this decoder. Default implementation does nothing and always return
527: * <code>CoderResult.UNDERFLOW</code>, and this method can be overridden
528: * if needed.
529: *
530: * @param out
531: * the output buffer
532: * @return <code>CoderResult.UNDERFLOW</code> or
533: * <code>CoderResult.OVERFLOW</code>
534: */
535: protected CoderResult implFlush(CharBuffer out) {
536: return CoderResult.UNDERFLOW;
537: }
538:
539: /**
540: * Notify that this decoder's <code>CodingErrorAction</code> specified for
541: * malformed input error has been changed. Default implementation does
542: * nothing, and this method can be overridden if needed.
543: *
544: * @param newAction
545: * The new action
546: */
547: protected void implOnMalformedInput(CodingErrorAction newAction) {
548: // default implementation is empty
549: }
550:
551: /**
552: * Notify that this decoder's <code>CodingErrorAction</code> specified for
553: * unmappable character error has been changed. Default implementation does
554: * nothing, and this method can be overridden if needed.
555: *
556: * @param newAction
557: * The new action
558: */
559: protected void implOnUnmappableCharacter(CodingErrorAction newAction) {
560: // default implementation is empty
561: }
562:
563: /**
564: * Notify that this decoder's replacement has been changed. Default
565: * implementation does nothing, and this method can be overridden if needed.
566: *
567: * @param newReplacement
568: * the new replacement string
569: */
570: protected void implReplaceWith(String newReplacement) {
571: // default implementation is empty
572: }
573:
574: /**
575: * Reset this decoder's charset related state. Default implementation does
576: * nothing, and this method can be overridden if needed.
577: */
578: protected void implReset() {
579: // default implementation is empty
580: }
581:
582: /**
583: * Get if this decoder implements an auto-detecting charset.
584: *
585: * @return <code>true</code> if this decoder implements an auto-detecting
586: * charset
587: */
588: public boolean isAutoDetecting() {
589: return false;
590: }
591:
592: /**
593: * Get if this decoder has detected a charset, this method is optional.
594: * <p>
595: * If this decoder implements an auto-detecting charset, then this method
596: * may start to return true during decoding operation to indicate that a
597: * charset has been detected in the input bytes and that the charset can be
598: * retrieved by invoking {@link #detectedCharset() detectedCharset} method.
599: * </p>
600: * <p>
601: * Note that a decoder that implements an auto-detecting charset may still
602: * succeed in decoding a portion of the given input even when it is unable
603: * to detect the charset. For this reason users should be aware that a
604: * <code>false</code> return value does not indicate that no decoding took
605: * place.
606: * </p>
607: * <p>
608: * The default implementation always throws an
609: * <code>UnsupportedOperationException</code>; it should be overridden by
610: * subclass if needed.
611: * </p>
612: *
613: * @return <code>true</code> this decoder has detected a charset
614: * @throws UnsupportedOperationException
615: * if this decoder doesn't implement an auto-detecting charset
616: */
617: public boolean isCharsetDetected() {
618: throw new UnsupportedOperationException();
619: }
620:
621: /**
622: * Gets this decoder's <code>CodingErrorAction</code> when malformed input
623: * occurred during decoding process.
624: *
625: * @return this decoder's <code>CodingErrorAction</code> when malformed
626: * input occurred during decoding process.
627: */
628: public CodingErrorAction malformedInputAction() {
629: return malformAction;
630: }
631:
632: /**
633: * Get the maximum number of characters which can be created by this decoder
634: * for one input byte, must be positive
635: *
636: * @return the maximum number of characters which can be created by this
637: * decoder for one input byte, must be positive
638: */
639: public final float maxCharsPerByte() {
640: return maxChars;
641: }
642:
643: /**
644: * Set this decoder's action on malformed input error.
645: *
646: * This method will call the
647: * {@link #implOnMalformedInput(CodingErrorAction) implOnMalformedInput}
648: * method with the given new action as argument.
649: *
650: * @param newAction
651: * the new action on malformed input error
652: * @return this decoder
653: * @throws IllegalArgumentException
654: * if the given newAction is null
655: */
656: public final CharsetDecoder onMalformedInput(
657: CodingErrorAction newAction) {
658: if (null == newAction) {
659: throw new IllegalArgumentException();
660: }
661: malformAction = newAction;
662: implOnMalformedInput(newAction);
663: return this ;
664: }
665:
666: /**
667: * Set this decoder's action on unmappable character error.
668: *
669: * This method will call the
670: * {@link #implOnUnmappableCharacter(CodingErrorAction) implOnUnmappableCharacter}
671: * method with the given new action as argument.
672: *
673: * @param newAction
674: * the new action on unmappable character error
675: * @return this decoder
676: * @throws IllegalArgumentException
677: * if the given newAction is null
678: */
679: public final CharsetDecoder onUnmappableCharacter(
680: CodingErrorAction newAction) {
681: if (null == newAction) {
682: throw new IllegalArgumentException();
683: }
684: unmapAction = newAction;
685: implOnUnmappableCharacter(newAction);
686: return this ;
687: }
688:
689: /**
690: * Get the replacement string, which is never null or empty
691: *
692: * @return the replacement string, cannot be null or empty
693: */
694: public final String replacement() {
695: return replace;
696: }
697:
698: /**
699: * Set new replacement value.
700: *
701: * This method first checks the given replacement's validity, then changes
702: * the replacement value, and at last calls
703: * {@link #implReplaceWith(String) implReplaceWith} method with the given
704: * new replacement as argument.
705: *
706: * @param newReplacement
707: * the replacement string, cannot be null or empty
708: * @return this decoder
709: * @throws IllegalArgumentException
710: * if the given replacement cannot satisfy the requirement
711: * mentioned above
712: */
713: public final CharsetDecoder replaceWith(String newReplacement) {
714: if (null == newReplacement || newReplacement.length() == 0) {
715: // niochar.06=Replacement string cannot be null or empty.
716: throw new IllegalArgumentException(Messages
717: .getString("niochar.06")); //$NON-NLS-1$
718: }
719: if (newReplacement.length() > maxChars) {
720: // niochar.07=Replacement string's length cannot be larger than max
721: // characters per byte.
722: throw new IllegalArgumentException(Messages
723: .getString("niochar.07")); //$NON-NLS-1$
724: }
725: replace = newReplacement;
726: implReplaceWith(newReplacement);
727: return this ;
728: }
729:
730: /**
731: * Reset this decoder. This method will reset internal status, and then call
732: * <code>implReset()</code> to reset any status related to specific
733: * charset.
734: *
735: * @return this decoder
736: */
737: public final CharsetDecoder reset() {
738: status = INIT;
739: implReset();
740: return this ;
741: }
742:
743: /**
744: * Gets this decoder's <code>CodingErrorAction</code> when unmappable
745: * character occurred during decoding process.
746: *
747: * @return this decoder's <code>CodingErrorAction</code> when unmappable
748: * character occurred during decoding process.
749: */
750: public CodingErrorAction unmappableCharacterAction() {
751: return unmapAction;
752: }
753: }
|