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 16-bit Unicode character sequence to byte
028: * sequence in some charset .
029: * <p>
030: * The input character sequence is wrapped by
031: * {@link java.nio.CharBuffer CharBuffer} and the output character sequence is
032: * {@link java.nio.ByteBuffer ByteBuffer}. A encoder instance should be used in
033: * following sequence, which is referred to as a encoding operation:
034: * <ol>
035: * <li>Invoking the {@link #reset() reset} method to reset the encoder if the
036: * encoder has been used;</li>
037: * <li>Invoking the {@link #encode(CharBuffer, ByteBuffer, boolean) encode}
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 #encode(CharBuffer, ByteBuffer, boolean) encode}
042: * method last time, and the the <code>endOfInput</code> parameter must be set
043: * to true</li>
044: * <li>Invoking the {@link #flush(ByteBuffer) flush} method to flush the
045: * output.</li>
046: * </ol>
047: * </p>
048: * <p>
049: * The {@link #encode(CharBuffer, ByteBuffer, boolean) encode} method will
050: * convert as many characters as possible, and the process won't stop except the
051: * input characters has been run out of, the output buffer has been filled or
052: * some 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 encoding errors. One is named as malformed and it is
059: * returned when the input content is illegal 16-bit Unicode character sequence,
060: * the other is named as unmappable character and occurs when there is a problem
061: * mapping the input to a valid byte sequence in the specific charset.
062: * </p>
063: * <p>
064: * The two errors can be handled in three ways, the default one is to report the
065: * error to the invoker by a {@link CoderResult CoderResult} instance, and the
066: * alternatives are to ignore it or to replace the erroneous input with the
067: * replacement byte array. The replacement byte array is {(byte)'?'} by default
068: * and can be changed by invoking {@link #replaceWith(byte[]) replaceWith}
069: * method. The invoker of this encoder can choose one way by specifying a
070: * {@link CodingErrorAction CodingErrorAction} instance for each error type via
071: * {@link #onMalformedInput(CodingErrorAction) onMalformedInput} method and
072: * {@link #onUnmappableCharacter(CodingErrorAction) onUnmappableCharacter}
073: * method.
074: * </p>
075: * <p>
076: * This class is abstract class and encapsulate many common operations of
077: * encoding process for all charsets. encoder for specific charset should extend
078: * this class and need only implement
079: * {@link #encodeLoop(CharBuffer, ByteBuffer) encodeLoop} method for basic
080: * encoding loop. If a subclass maintains internal state, it should override the
081: * {@link #implFlush(ByteBuffer) implFlush} method and
082: * {@link #implReset() implReset} method in addition.
083: * </p>
084: * <p>
085: * This class is not thread-safe.
086: * </p>
087: *
088: * @see java.nio.charset.Charset
089: * @see java.nio.charset.CharsetDecoder
090: */
091: public abstract class CharsetEncoder {
092: /*
093: * --------------------------------------- Consts
094: * ---------------------------------------
095: */
096: /*
097: * internal status consts
098: */
099: private static final int INIT = 0;
100:
101: private static final int ONGOING = 1;
102:
103: private static final int END = 2;
104:
105: private static final int FLUSH = 3;
106:
107: /*
108: * --------------------------------------- Instance variables
109: * ---------------------------------------
110: */
111: // the Charset which creates this encoder
112: private Charset cs;
113:
114: // average bytes per character created by this encoder
115: private float averBytes;
116:
117: // maximum bytes per character can be created by this encoder
118: private float maxBytes;
119:
120: // replacement byte array
121: private byte[] replace;
122:
123: // internal status
124: private int status;
125:
126: // action for malformed input
127: private CodingErrorAction malformAction;
128:
129: // action for unmapped char input
130: private CodingErrorAction unmapAction;
131:
132: // decoder instance for this encoder's charset, used for replacement value
133: // checking
134: private CharsetDecoder decoder;
135:
136: /*
137: * --------------------------------------- Constructors
138: * ---------------------------------------
139: */
140:
141: /**
142: * Construct a new <code>CharsetEncoder</code> using given
143: * <code>Charset</code>, average number and maximum number of bytes
144: * created by this encoder for one input character.
145: *
146: * @param cs
147: * this encoder's <code>Charset</code>, which create this
148: * encoder
149: * @param averageBytesPerChar
150: * average number of bytes created by this encoder for one input
151: * character, must be positive
152: * @param maxBytesPerChar
153: * maximum number of bytes which can be created by this encoder
154: * for one input character, must be positive
155: * @throws IllegalArgumentException
156: * if <code>maxBytesPerChar</code> or
157: * <code>averageBytePerChar</code> is negative
158: */
159: protected CharsetEncoder(Charset cs, float averageBytesPerChar,
160: float maxBytesPerChar) {
161: this (cs, averageBytesPerChar, maxBytesPerChar,
162: new byte[] { (byte) '?' });
163: }
164:
165: /**
166: * Construct a new <code>CharsetEncoder</code> using given
167: * <code>Charset</code>, replace byte array, average number and maximum
168: * number of bytes created by this encoder for one input character.
169: *
170: * @param cs
171: * the this encoder's <code>Charset</code>, which create this
172: * encoder
173: * @param averageBytesPerChar
174: * average number of bytes created by this encoder for single
175: * input character, must be positive
176: * @param maxBytesPerChar
177: * maximum number of bytes which can be created by this encoder
178: * for single input character, must be positive
179: * @param replacement
180: * the replacement byte array, cannot be null or empty, its
181: * length cannot larger than <code>maxBytesPerChar</code>, and
182: * must be legal replacement, which can be justified by
183: * {@link #isLegalReplacement(byte[]) isLegalReplacement}
184: * @throws IllegalArgumentException
185: * if any parameters are invalid
186: */
187: protected CharsetEncoder(Charset cs, float averageBytesPerChar,
188: float maxBytesPerChar, byte[] replacement) {
189: if (averageBytesPerChar <= 0 || maxBytesPerChar <= 0) {
190: // niochar.02=Bytes number for one character must be positive.
191: throw new IllegalArgumentException(Messages
192: .getString("niochar.02")); //$NON-NLS-1$
193: }
194: if (averageBytesPerChar > maxBytesPerChar) {
195: // niochar.03=averageBytesPerChar is greater than maxBytesPerChar.
196: throw new IllegalArgumentException(Messages
197: .getString("niochar.03")); //$NON-NLS-1$
198: }
199: this .cs = cs;
200: averBytes = averageBytesPerChar;
201: maxBytes = maxBytesPerChar;
202: status = INIT;
203: malformAction = CodingErrorAction.REPORT;
204: unmapAction = CodingErrorAction.REPORT;
205: replaceWith(replacement);
206: }
207:
208: /*
209: * --------------------------------------- Methods
210: * ---------------------------------------
211: */
212: /**
213: * get the average number of bytes created by this encoder for single input
214: * character
215: *
216: * @return the average number of bytes created by this encoder for single
217: * input character
218: */
219: public final float averageBytesPerChar() {
220: return averBytes;
221: }
222:
223: /**
224: * Check if given character can be encoded by this encoder.
225: *
226: * Note that this method can change the internal status of this encoder, so
227: * it should not be called when another encode process is ongoing, otherwise
228: * it will throw <code>IllegalStateException</code>.
229: *
230: * This method can be overridden for performance improvement.
231: *
232: * @param c
233: * the given encoder
234: * @return true if given character can be encoded by this encoder
235: * @throws IllegalStateException
236: * if another encode process is ongoing so that current internal
237: * status is neither RESET or FLUSH
238: */
239: public boolean canEncode(char c) {
240: return implCanEncode(CharBuffer.wrap(new char[] { c }));
241: }
242:
243: // implementation of canEncode
244: private boolean implCanEncode(CharBuffer cb) {
245: if (status == FLUSH) {
246: status = INIT;
247: }
248: if (status != INIT) {
249: // niochar.0B=Another encoding process is ongoing\!
250: throw new IllegalStateException(Messages
251: .getString("niochar.0B")); //$NON-NLS-1$
252: }
253: CodingErrorAction malformBak = malformAction;
254: CodingErrorAction unmapBak = unmapAction;
255: onMalformedInput(CodingErrorAction.REPORT);
256: onUnmappableCharacter(CodingErrorAction.REPORT);
257: boolean result = true;
258: try {
259: this .encode(cb);
260: } catch (CharacterCodingException e) {
261: result = false;
262: }
263: onMalformedInput(malformBak);
264: onUnmappableCharacter(unmapBak);
265: reset();
266: return result;
267: }
268:
269: /**
270: * Check if given <code>CharSequence</code> can be encoded by this
271: * encoder.
272: *
273: * Note that this method can change the internal status of this encoder, so
274: * it should not be called when another encode process is ongoing, otherwise
275: * it will throw <code>IllegalStateException</code>.
276: *
277: * This method can be overridden for performance improvement.
278: *
279: * @param sequence
280: * the given <code>CharSequence</code>
281: * @return true if given <code>CharSequence</code> can be encoded by this
282: * encoder
283: * @throws IllegalStateException
284: * if current internal status is neither RESET or FLUSH
285: */
286: public boolean canEncode(CharSequence sequence) {
287: CharBuffer cb;
288: if (sequence instanceof CharBuffer) {
289: cb = ((CharBuffer) sequence).duplicate();
290: } else {
291: cb = CharBuffer.wrap(sequence);
292: }
293: return implCanEncode(cb);
294: }
295:
296: /**
297: * Get the <code>Charset</code> which creates this encoder.
298: *
299: * @return the <code>Charset</code> which creates this encoder
300: */
301: public final Charset charset() {
302: return cs;
303: }
304:
305: /**
306: * This is a facade method for encoding operation.
307: * <p>
308: * This method encodes the remaining character sequence of the given
309: * character buffer into a new byte buffer. This method performs a complete
310: * encoding operation, resets at first, then encodes, and flushes at last.
311: * </p>
312: * <p>
313: * This method should not be invoked if another encode operation is ongoing.
314: * </p>
315: *
316: * @param in
317: * the input buffer
318: * @return a new <code>ByteBuffer</code> containing the the bytes produced
319: * by this encoding operation. The buffer's limit will be the
320: * position of last byte in buffer, and the position will be zero
321: * @throws IllegalStateException
322: * if another encoding operation is ongoing
323: * @throws MalformedInputException
324: * if illegal input character sequence for this charset
325: * encountered, and the action for malformed error is
326: * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT}
327: * @throws UnmappableCharacterException
328: * if legal but unmappable input character sequence for this
329: * charset encountered, and the action for unmappable character
330: * error is
331: * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT}.
332: * Unmappable means the Unicode character sequence at the input
333: * buffer's current position cannot be mapped to a equivalent
334: * byte sequence.
335: * @throws CharacterCodingException
336: * if other exception happened during the encode operation
337: */
338: public final ByteBuffer encode(CharBuffer in)
339: throws CharacterCodingException {
340: if (in.remaining() == 0) {
341: return ByteBuffer.allocate(0);
342: }
343: reset();
344: int length = (int) (in.remaining() * averBytes);
345: ByteBuffer output = ByteBuffer.allocate(length);
346: CoderResult result = null;
347: while (true) {
348: result = encode(in, output, false);
349: if (result == CoderResult.UNDERFLOW) {
350: break;
351: } else if (result == CoderResult.OVERFLOW) {
352: output = allocateMore(output);
353: continue;
354: }
355: checkCoderResult(result);
356: }
357: result = encode(in, output, true);
358: checkCoderResult(result);
359:
360: while (true) {
361: result = flush(output);
362: if (result == CoderResult.UNDERFLOW) {
363: output.flip();
364: break;
365: } else if (result == CoderResult.OVERFLOW) {
366: output = allocateMore(output);
367: continue;
368: }
369: checkCoderResult(result);
370: output.flip();
371: if (result.isMalformed()) {
372: throw new MalformedInputException(result.length());
373: } else if (result.isUnmappable()) {
374: throw new UnmappableCharacterException(result.length());
375: }
376: break;
377: }
378: status = FLUSH;
379: return output;
380: }
381:
382: /*
383: * checks the result whether it needs to throw CharacterCodingException.
384: */
385: private void checkCoderResult(CoderResult result)
386: throws CharacterCodingException {
387: if (malformAction == CodingErrorAction.REPORT
388: && result.isMalformed()) {
389: throw new MalformedInputException(result.length());
390: } else if (unmapAction == CodingErrorAction.REPORT
391: && result.isUnmappable()) {
392: throw new UnmappableCharacterException(result.length());
393: }
394: }
395:
396: // allocate more spaces to the given ByteBuffer
397: private ByteBuffer allocateMore(ByteBuffer output) {
398: if (output.capacity() == 0) {
399: return ByteBuffer.allocate(1);
400: }
401: ByteBuffer result = ByteBuffer.allocate(output.capacity() * 2);
402: output.flip();
403: result.put(output);
404: return result;
405: }
406:
407: /**
408: * Encodes characters starting at the current position of the given input
409: * buffer, and writes the equivalent byte sequence into the given output
410: * buffer from its current position.
411: * <p>
412: * The buffers' position will be changed with the reading and writing
413: * operation, but their limits and marks will be kept intact.
414: * </p>
415: * <p>
416: * A <code>CoderResult</code> instance will be returned according to
417: * following rules:
418: * <ul>
419: * <li>A {@link CoderResult#malformedForLength(int) malformed input} result
420: * indicates that some malformed input error encountered, and the erroneous
421: * characters start at the input buffer's position and their number can be
422: * got by result's {@link CoderResult#length() length}. This kind of result
423: * can be returned only if the malformed action is
424: * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT}. </li>
425: * <li>{@link CoderResult#UNDERFLOW CoderResult.UNDERFLOW} indicates that
426: * as many characters as possible in the input buffer has been encoded. If
427: * there is no further input and no characters left in the input buffer then
428: * this task is complete. If this is not the case then the client should
429: * call this method again supplying some more input characters.</li>
430: * <li>{@link CoderResult#OVERFLOW CoderResult.OVERFLOW} indicates that the
431: * output buffer has been filled, while there are still some characters
432: * remaining in the input buffer. This method should be invoked again with a
433: * non-full output buffer </li>
434: * <li>A {@link CoderResult#unmappableForLength(int) unmappable character}
435: * result indicates that some unmappable character error was encountered,
436: * and the erroneous characters start at the input buffer's position and
437: * their number can be got by result's {@link CoderResult#length() length}.
438: * This kind of result can be returned only on
439: * {@link CodingErrorAction#REPORT CodingErrorAction.REPORT}. </li>
440: * </ul>
441: * </p>
442: * <p>
443: * The <code>endOfInput</code> parameter indicates that if the invoker can
444: * provider further input. This parameter is true if and only if the
445: * characters in current input buffer are all inputs for this encoding
446: * operation. Note that it is common and won't cause error that the invoker
447: * sets false and then finds no more input available; while it may cause
448: * error that the invoker always sets true in several consecutive
449: * invocations so that any remaining input will be treated as malformed
450: * input.
451: * </p>
452: * <p>
453: * This method invokes
454: * {@link #encodeLoop(CharBuffer, ByteBuffer) encodeLoop} method to
455: * implement basic encode logic for specific charset.
456: * </p>
457: *
458: * @param in
459: * the input buffer
460: * @param out
461: * the output buffer
462: * @param endOfInput
463: * true if all the input characters have been provided
464: * @return a <code>CoderResult</code> instance indicating the result
465: * @throws IllegalStateException
466: * if the encoding operation has already started or no more
467: * input needed in this encoding progress.
468: * @throws CoderMalfunctionError
469: * If the {@link #encodeLoop(CharBuffer, ByteBuffer) encodeLoop}
470: * method threw an <code>BufferUnderflowException</code> or
471: * <code>BufferUnderflowException</code>
472: */
473: public final CoderResult encode(CharBuffer in, ByteBuffer out,
474: boolean endOfInput) {
475: if ((status == FLUSH) || (!endOfInput && status == END)) {
476: throw new IllegalStateException();
477: }
478:
479: CoderResult result;
480: while (true) {
481: try {
482: result = encodeLoop(in, out);
483: } catch (BufferOverflowException e) {
484: throw new CoderMalfunctionError(e);
485: } catch (BufferUnderflowException e) {
486: throw new CoderMalfunctionError(e);
487: }
488: if (result == CoderResult.UNDERFLOW) {
489: status = endOfInput ? END : ONGOING;
490: if (endOfInput) {
491: int remaining = in.remaining();
492: if (remaining > 0) {
493: result = CoderResult
494: .malformedForLength(remaining);
495: } else {
496: return result;
497: }
498: } else {
499: return result;
500: }
501: } else if (result == CoderResult.OVERFLOW) {
502: status = endOfInput ? END : ONGOING;
503: return result;
504: }
505: CodingErrorAction action = malformAction;
506: if (result.isUnmappable()) {
507: action = unmapAction;
508: }
509: // If the action is IGNORE or REPLACE, we should continue
510: // encoding.
511: if (action == CodingErrorAction.REPLACE) {
512: if (out.remaining() < replace.length) {
513: return CoderResult.OVERFLOW;
514: }
515: out.put(replace);
516: } else {
517: if (action != CodingErrorAction.IGNORE) {
518: return result;
519: }
520: }
521: in.position(in.position() + result.length());
522: }
523: }
524:
525: /**
526: * Encode characters into bytes. This method is called by
527: * {@link #encode(CharBuffer, ByteBuffer, boolean) encode}.
528: *
529: * This method will implement the essential encoding operation, and it won't
530: * stop encoding until either all the input characters are read, the output
531: * buffer is filled, or some exception encountered. And then it will return
532: * a <code>CoderResult</code> object indicating the result of current
533: * encoding operation. The rules to construct the <code>CoderResult</code>
534: * is same as the {@link #encode(CharBuffer, ByteBuffer, boolean) encode}.
535: * When exception encountered in the encoding operation, most implementation
536: * of this method will return a relevant result object to
537: * {@link #encode(CharBuffer, ByteBuffer, boolean) encode} method, and some
538: * performance optimized implementation may handle the exception and
539: * implement the error action itself.
540: *
541: * The buffers are scanned from their current positions, and their positions
542: * will be modified accordingly, while their marks and limits will be
543: * intact. At most {@link CharBuffer#remaining() in.remaining()} characters
544: * will be read, and {@link ByteBuffer#remaining() out.remaining()} bytes
545: * will be written.
546: *
547: * Note that some implementation may pre-scan the input buffer and return
548: * <code>CoderResult.UNDERFLOW</code> until it receives sufficient input.
549: *
550: * @param in
551: * the input buffer
552: * @param out
553: * the output buffer
554: * @return a <code>CoderResult</code> instance indicating the result
555: */
556: protected abstract CoderResult encodeLoop(CharBuffer in,
557: ByteBuffer out);
558:
559: /**
560: * Flush this encoder.
561: *
562: * This method will call {@link #implFlush(ByteBuffer) implFlush}. Some
563: * encoders may need to write some bytes to the output buffer when they have
564: * read all input characters, subclasses can overridden
565: * {@link #implFlush(ByteBuffer) implFlush} to perform writing action.
566: *
567: * The maximum number of written bytes won't larger than
568: * {@link ByteBuffer#remaining() out.remaining()}. If some encoder want to
569: * write more bytes than output buffer's remaining spaces, then
570: * <code>CoderResult.OVERFLOW</code> will be returned, and this method
571: * must be called again with a byte buffer has more spaces. Otherwise this
572: * method will return <code>CoderResult.UNDERFLOW</code>, which means one
573: * encoding process has been completed successfully.
574: *
575: * During the flush, the output buffer's position will be changed
576: * accordingly, while its mark and limit will be intact.
577: *
578: * @param out
579: * the given output buffer
580: * @return <code>CoderResult.UNDERFLOW</code> or
581: * <code>CoderResult.OVERFLOW</code>
582: * @throws IllegalStateException
583: * if this encoder hasn't read all input characters during one
584: * encoding process, which means neither after calling
585: * {@link #encode(CharBuffer) encode(CharBuffer)} nor after
586: * calling {@link #encode(CharBuffer, ByteBuffer, boolean)
587: * encode(CharBuffer, ByteBuffer, boolean)} with true value for
588: * the last boolean parameter
589: */
590: public final CoderResult flush(ByteBuffer out) {
591: if (status != END && status != INIT) {
592: throw new IllegalStateException();
593: }
594: CoderResult result = implFlush(out);
595: if (result == CoderResult.UNDERFLOW) {
596: status = FLUSH;
597: }
598: return result;
599: }
600:
601: /**
602: * Flush this encoder. Default implementation does nothing and always return
603: * <code>CoderResult.UNDERFLOW</code>, and this method can be overridden
604: * if needed.
605: *
606: * @param out
607: * the output buffer
608: * @return <code>CoderResult.UNDERFLOW</code> or
609: * <code>CoderResult.OVERFLOW</code>
610: */
611: protected CoderResult implFlush(ByteBuffer out) {
612: return CoderResult.UNDERFLOW;
613: }
614:
615: /**
616: * Notify that this encoder's <code>CodingErrorAction</code> specified for
617: * malformed input error has been changed. Default implementation does
618: * nothing, and this method can be overridden if needed.
619: *
620: * @param newAction
621: * The new action
622: */
623: protected void implOnMalformedInput(CodingErrorAction newAction) {
624: // default implementation is empty
625: }
626:
627: /**
628: * Notify that this encoder's <code>CodingErrorAction</code> specified for
629: * unmappable character error has been changed. Default implementation does
630: * nothing, and this method can be overridden if needed.
631: *
632: * @param newAction
633: * The new action
634: */
635: protected void implOnUnmappableCharacter(CodingErrorAction newAction) {
636: // default implementation is empty
637: }
638:
639: /**
640: * Notify that this encoder's replacement has been changed. Default
641: * implementation does nothing, and this method can be overridden if needed.
642: *
643: * @param newReplacement
644: * the new replacement string
645: */
646: protected void implReplaceWith(byte[] newReplacement) {
647: // default implementation is empty
648: }
649:
650: /**
651: * Reset this encoder's charset related state. Default implementation does
652: * nothing, and this method can be overridden if needed.
653: */
654: protected void implReset() {
655: // default implementation is empty
656: }
657:
658: /**
659: * Check if the given argument is legal as this encoder's replacement byte
660: * array.
661: *
662: * The given byte array is legal if and only if it can be decode into
663: * sixteen bits Unicode characters.
664: *
665: * This method can be overridden for performance improvement.
666: *
667: * @param repl
668: * the given byte array to be checked
669: * @return true if the the given argument is legal as this encoder's
670: * replacement byte array.
671: */
672: public boolean isLegalReplacement(byte[] repl) {
673: if (decoder == null) {
674: decoder = cs.newDecoder();
675: }
676:
677: CodingErrorAction malform = decoder.malformedInputAction();
678: CodingErrorAction unmap = decoder.unmappableCharacterAction();
679: decoder.onMalformedInput(CodingErrorAction.REPORT);
680: decoder.onUnmappableCharacter(CodingErrorAction.REPORT);
681: ByteBuffer in = ByteBuffer.wrap(repl);
682: CharBuffer out = CharBuffer
683: .allocate((int) (repl.length * decoder
684: .maxCharsPerByte()));
685: CoderResult result = decoder.decode(in, out, true);
686: decoder.onMalformedInput(malform);
687: decoder.onUnmappableCharacter(unmap);
688: return !result.isError();
689: }
690:
691: /**
692: * Gets this encoder's <code>CodingErrorAction</code> when malformed input
693: * occurred during encoding process.
694: *
695: * @return this encoder's <code>CodingErrorAction</code> when malformed
696: * input occurred during encoding process.
697: */
698: public CodingErrorAction malformedInputAction() {
699: return malformAction;
700: }
701:
702: /**
703: * Get the maximum number of bytes which can be created by this encoder for
704: * one input character, must be positive
705: *
706: * @return the maximum number of bytes which can be created by this encoder
707: * for one input character, must be positive
708: */
709: public final float maxBytesPerChar() {
710: return maxBytes;
711: }
712:
713: /**
714: * Set this encoder's action on malformed input error.
715: *
716: * This method will call the
717: * {@link #implOnMalformedInput(CodingErrorAction) implOnMalformedInput}
718: * method with the given new action as argument.
719: *
720: * @param newAction
721: * the new action on malformed input error
722: * @return this encoder
723: * @throws IllegalArgumentException
724: * if the given newAction is null
725: */
726: public final CharsetEncoder onMalformedInput(
727: CodingErrorAction newAction) {
728: if (null == newAction) {
729: // niochar.0C=Action on malformed input error cannot be null\!
730: throw new IllegalArgumentException(Messages
731: .getString("niochar.0C")); //$NON-NLS-1$
732: }
733: malformAction = newAction;
734: implOnMalformedInput(newAction);
735: return this ;
736: }
737:
738: /**
739: * Set this encoder's action on unmappable character error.
740: *
741: * This method will call the
742: * {@link #implOnUnmappableCharacter(CodingErrorAction) implOnUnmappableCharacter}
743: * method with the given new action as argument.
744: *
745: * @param newAction
746: * the new action on unmappable character error
747: * @return this encoder
748: * @throws IllegalArgumentException
749: * if the given newAction is null
750: */
751: public final CharsetEncoder onUnmappableCharacter(
752: CodingErrorAction newAction) {
753: if (null == newAction) {
754: // niochar.0D=Action on unmappable character error cannot be null\!
755: throw new IllegalArgumentException(Messages
756: .getString("niochar.0D")); //$NON-NLS-1$
757: }
758: unmapAction = newAction;
759: implOnUnmappableCharacter(newAction);
760: return this ;
761: }
762:
763: /**
764: * Get the replacement byte array, which is never null or empty, and it is
765: * legal
766: *
767: * @return the replacement byte array, cannot be null or empty, and it is
768: * legal
769: */
770: public final byte[] replacement() {
771: return replace;
772: }
773:
774: /**
775: * Set new replacement value.
776: *
777: * This method first checks the given replacement's validity, then changes
778: * the replacement value, and at last calls
779: * {@link #implReplaceWith(byte[]) implReplaceWith} method with the given
780: * new replacement as argument.
781: *
782: * @param replacement
783: * the replacement byte array, cannot be null or empty, its
784: * length cannot larger than <code>maxBytesPerChar</code>, and
785: * must be legal replacement, which can be justified by
786: * <code>isLegalReplacement(byte[] repl)</code>
787: * @return this encoder
788: * @throws IllegalArgumentException
789: * if the given replacement cannot satisfy the requirement
790: * mentioned above
791: */
792: public final CharsetEncoder replaceWith(byte[] replacement) {
793: if (null == replacement || 0 == replacement.length
794: || maxBytes < replacement.length
795: || !isLegalReplacement(replacement)) {
796: // niochar.0E=Replacement is illegal
797: throw new IllegalArgumentException(Messages
798: .getString("niochar.0E")); //$NON-NLS-1$
799: }
800: replace = replacement;
801: implReplaceWith(replacement);
802: return this ;
803: }
804:
805: /**
806: * Reset this encoder. This method will reset internal status, and then call
807: * <code>implReset()</code> to reset any status related to specific
808: * charset.
809: *
810: * @return this encoder
811: */
812: public final CharsetEncoder reset() {
813: status = INIT;
814: implReset();
815: return this ;
816: }
817:
818: /**
819: * Gets this encoder's <code>CodingErrorAction</code> when unmappable
820: * character occurred during encoding process.
821: *
822: * @return this encoder's <code>CodingErrorAction</code> when unmappable
823: * character occurred during encoding process.
824: */
825: public CodingErrorAction unmappableCharacterAction() {
826: return unmapAction;
827: }
828: }
|