blob: 5f58db98e6d04c740e2691f61a5471bc2f5b710f [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.nio.cs.ext;
27import java.io.ByteArrayOutputStream;
28import java.nio.ByteBuffer;
29import java.nio.CharBuffer;
30import java.nio.charset.*;
31
32/**
33 * An algorithmic conversion from COMPOUND_TEXT to Unicode.
34 */
35
36public class COMPOUND_TEXT_Decoder extends CharsetDecoder {
37
38 private static final int NORMAL_BYTES = 0;
39 private static final int NONSTANDARD_BYTES = 1;
40 private static final int VERSION_SEQUENCE_V = 2;
41 private static final int VERSION_SEQUENCE_TERM = 3;
42 private static final int ESCAPE_SEQUENCE = 4;
43 private static final int CHARSET_NGIIF = 5;
44 private static final int CHARSET_NLIIF = 6;
45 private static final int CHARSET_NLIF = 7;
46 private static final int CHARSET_NRIIF = 8;
47 private static final int CHARSET_NRIF = 9;
48 private static final int CHARSET_NONSTANDARD_FOML = 10;
49 private static final int CHARSET_NONSTANDARD_OML = 11;
50 private static final int CHARSET_NONSTANDARD_ML = 12;
51 private static final int CHARSET_NONSTANDARD_L = 13;
52 private static final int CHARSET_NONSTANDARD = 14;
53 private static final int CHARSET_LIIF = 15;
54 private static final int CHARSET_LIF = 16;
55 private static final int CHARSET_RIIF = 17;
56 private static final int CHARSET_RIF = 18;
57 private static final int CONTROL_SEQUENCE_PIF = 19;
58 private static final int CONTROL_SEQUENCE_IF = 20;
59 private static final int EXTENSION_ML = 21;
60 private static final int EXTENSION_L = 22;
61 private static final int EXTENSION = 23;
62 private static final int ESCAPE_SEQUENCE_OTHER = 24;
63
64 private static final String ERR_LATIN1 = "ISO8859_1 unsupported";
65 private static final String ERR_ILLSTATE = "Illegal state";
66 private static final String ERR_ESCBYTE =
67 "Illegal byte in 0x1B escape sequence";
68 private static final String ERR_ENCODINGBYTE =
69 "Illegal byte in non-standard character set name";
70 private static final String ERR_CTRLBYTE =
71 "Illegal byte in 0x9B control sequence";
72 private static final String ERR_CTRLPI =
73 "P following I in 0x9B control sequence";
74 private static final String ERR_VERSTART =
75 "Versioning escape sequence can only appear at start of byte stream";
76 private static final String ERR_VERMANDATORY =
77 "Cannot parse mandatory extensions";
78 private static final String ERR_ENCODING = "Unknown encoding: ";
79 private static final String ERR_FLUSH =
80 "Escape sequence, control sequence, or ML extension not terminated";
81
82 private int state = NORMAL_BYTES ;
83 private int ext_count, ext_offset;
84 private boolean versionSequenceAllowed = true;
85 private byte[] byteBuf = new byte[1];
86 private ByteBuffer inBB = ByteBuffer.allocate(16);
87 private ByteArrayOutputStream queue = new ByteArrayOutputStream(),
88 encodingQueue = new ByteArrayOutputStream();
89
90 private CharsetDecoder glDecoder, grDecoder, nonStandardDecoder,
91 lastDecoder;
92 private boolean glHigh = false, grHigh = true;
93
94
95 public COMPOUND_TEXT_Decoder(Charset cs) {
96 super(cs, 1.0f, 1.0f);
97 try {
98 // Initial state in ISO 2022 designates Latin-1 charset.
99 glDecoder = Charset.forName("ASCII").newDecoder();
100 grDecoder = Charset.forName("ISO8859_1").newDecoder();
101 } catch (IllegalArgumentException e) {
102 error(ERR_LATIN1);
103 }
104 initDecoder(glDecoder);
105 initDecoder(grDecoder);
106 }
107
108 protected CoderResult decodeLoop(ByteBuffer src, CharBuffer des) {
109 CoderResult cr = CoderResult.UNDERFLOW;
110 byte[] input = src.array();
111 int inOff = src.arrayOffset() + src.position();
112 int inEnd = src.arrayOffset() + src.limit();
113
114 try {
115 while (inOff < inEnd && cr.isUnderflow()) {
116 // Byte parsing is done with shorts instead of bytes because
117 // Java bytes are signed, while COMPOUND_TEXT bytes are not. If
118 // we used the Java byte type, the > and < tests during parsing
119 // would not work correctly.
120 cr = handleByte((short)(input[inOff] & 0xFF), des);
121 inOff++;
122 }
123 return cr;
124 } finally {
125 src.position(inOff - src.arrayOffset());
126 }
127 }
128
129 private CoderResult handleByte(short newByte, CharBuffer cb) {
130 CoderResult cr = CoderResult.UNDERFLOW;
131 switch (state) {
132 case NORMAL_BYTES:
133 cr= normalBytes(newByte, cb);
134 break;
135 case NONSTANDARD_BYTES:
136 cr = nonStandardBytes(newByte, cb);
137 break;
138 case VERSION_SEQUENCE_V:
139 case VERSION_SEQUENCE_TERM:
140 cr = versionSequence(newByte);
141 break;
142 case ESCAPE_SEQUENCE:
143 cr = escapeSequence(newByte);
144 break;
145 case CHARSET_NGIIF:
146 cr = charset94N(newByte);
147 break;
148 case CHARSET_NLIIF:
149 case CHARSET_NLIF:
150 cr = charset94NL(newByte, cb);
151 break;
152 case CHARSET_NRIIF:
153 case CHARSET_NRIF:
154 cr = charset94NR(newByte, cb);
155 break;
156 case CHARSET_NONSTANDARD_FOML:
157 case CHARSET_NONSTANDARD_OML:
158 case CHARSET_NONSTANDARD_ML:
159 case CHARSET_NONSTANDARD_L:
160 case CHARSET_NONSTANDARD:
161 cr = charsetNonStandard(newByte, cb);
162 break;
163 case CHARSET_LIIF:
164 case CHARSET_LIF:
165 cr = charset9496L(newByte, cb);
166 break;
167 case CHARSET_RIIF:
168 case CHARSET_RIF:
169 cr = charset9496R(newByte, cb);
170 break;
171 case CONTROL_SEQUENCE_PIF:
172 case CONTROL_SEQUENCE_IF:
173 cr = controlSequence(newByte);
174 break;
175 case EXTENSION_ML:
176 case EXTENSION_L:
177 case EXTENSION:
178 cr = extension(newByte);
179 break;
180 case ESCAPE_SEQUENCE_OTHER:
181 cr = escapeSequenceOther(newByte);
182 break;
183 default:
184 error(ERR_ILLSTATE);
185 }
186 return cr;
187 }
188
189 private CoderResult normalBytes(short newByte, CharBuffer cb) {
190 CoderResult cr = CoderResult.UNDERFLOW;
191 if ((newByte >= 0x00 && newByte <= 0x1F) || // C0
192 (newByte >= 0x80 && newByte <= 0x9F)) { // C1
193 char newChar;
194
195 switch (newByte) {
196 case 0x1B:
197 state = ESCAPE_SEQUENCE;
198 queue.write(newByte);
199 return cr;
200 case 0x9B:
201 state = CONTROL_SEQUENCE_PIF;
202 versionSequenceAllowed = false;
203 queue.write(newByte);
204 return cr;
205 case 0x09:
206 versionSequenceAllowed = false;
207 newChar = '\t';
208 break;
209 case 0x0A:
210 versionSequenceAllowed = false;
211 newChar = '\n';
212 break;
213 default:
214 versionSequenceAllowed = false;
215 return cr;
216 }
217 if (!cb.hasRemaining())
218 return CoderResult.OVERFLOW;
219 else
220 cb.put(newChar);
221 } else {
222 CharsetDecoder decoder;
223 boolean high;
224 versionSequenceAllowed = false;
225
226 if (newByte >= 0x20 && newByte <= 0x7F) {
227 decoder = glDecoder;
228 high = glHigh;
229 } else /* if (newByte >= 0xA0 && newByte <= 0xFF) */ {
230 decoder = grDecoder;
231 high = grHigh;
232 }
233 if (lastDecoder != null && decoder != lastDecoder) {
234 cr = flushDecoder(lastDecoder, cb);
235 }
236 lastDecoder = decoder;
237
238 if (decoder != null) {
239 byte b = (byte)newByte;
240 if (high) {
241 b |= 0x80;
242 } else {
243 b &= 0x7F;
244 }
245 inBB.put(b);
246 inBB.flip();
247 cr = decoder.decode(inBB, cb, false);
248 if (!inBB.hasRemaining() || cr.isMalformed()) {
249 inBB.clear();
250 } else {
251 int pos = inBB.limit();
252 inBB.clear();
253 inBB.position(pos);
254 }
255 } else if (cb.remaining() < replacement().length()) {
256 cb.put(replacement());
257 } else {
258 return CoderResult.OVERFLOW;
259 }
260 }
261 return cr;
262 }
263
264 private CoderResult nonStandardBytes(short newByte, CharBuffer cb)
265 {
266 CoderResult cr = CoderResult.UNDERFLOW;
267 if (nonStandardDecoder != null) {
268 //byteBuf[0] = (byte)newByte;
269 inBB.put((byte)newByte);
270 inBB.flip();
271 cr = nonStandardDecoder.decode(inBB, cb, false);
272 if (!inBB.hasRemaining()) {
273 inBB.clear();
274 } else {
275 int pos = inBB.limit();
276 inBB.clear();
277 inBB.position(pos);
278 }
279 } else if (cb.remaining() < replacement().length()) {
280 cb.put(replacement());
281 } else {
282 return CoderResult.OVERFLOW;
283 }
284
285 ext_offset++;
286 if (ext_offset >= ext_count) {
287 ext_offset = ext_count = 0;
288 state = NORMAL_BYTES;
289 cr = flushDecoder(nonStandardDecoder, cb);
290 nonStandardDecoder = null;
291 }
292 return cr;
293 }
294
295 private CoderResult escapeSequence(short newByte) {
296 switch (newByte) {
297 case 0x23:
298 state = VERSION_SEQUENCE_V;
299 break;
300 case 0x24:
301 state = CHARSET_NGIIF;
302 versionSequenceAllowed = false;
303 break;
304 case 0x25:
305 state = CHARSET_NONSTANDARD_FOML;
306 versionSequenceAllowed = false;
307 break;
308 case 0x28:
309 state = CHARSET_LIIF;
310 versionSequenceAllowed = false;
311 break;
312 case 0x29:
313 case 0x2D:
314 state = CHARSET_RIIF;
315 versionSequenceAllowed = false;
316 break;
317 default:
318 // escapeSequenceOther will write to queue if appropriate
319 return escapeSequenceOther(newByte);
320 }
321
322 queue.write(newByte);
323 return CoderResult.UNDERFLOW;
324 }
325
326 /**
327 * Test for unknown, but valid, escape sequences.
328 */
329 private CoderResult escapeSequenceOther(short newByte) {
330 if (newByte >= 0x20 && newByte <= 0x2F) {
331 // {I}
332 state = ESCAPE_SEQUENCE_OTHER;
333 versionSequenceAllowed = false;
334 queue.write(newByte);
335 } else if (newByte >= 0x30 && newByte <= 0x7E) {
336 // F -- end of sequence
337 state = NORMAL_BYTES;
338 versionSequenceAllowed = false;
339 queue.reset();
340 } else {
341 return malformedInput(ERR_ESCBYTE);
342 }
343 return CoderResult.UNDERFLOW;
344 }
345
346 /**
347 * Parses directionality, as well as unknown, but valid, control sequences.
348 */
349 private CoderResult controlSequence(short newByte) {
350 if (newByte >= 0x30 && newByte <= 0x3F) {
351 // {P}
352 if (state == CONTROL_SEQUENCE_IF) {
353 // P no longer allowed
354 return malformedInput(ERR_CTRLPI);
355 }
356 queue.write(newByte);
357 } else if (newByte >= 0x20 && newByte <= 0x2F) {
358 // {I}
359 state = CONTROL_SEQUENCE_IF;
360 queue.write(newByte);
361 } else if (newByte >= 0x40 && newByte <= 0x7E) {
362 // F -- end of sequence
363 state = NORMAL_BYTES;
364 queue.reset();
365 } else {
366 return malformedInput(ERR_CTRLBYTE);
367 }
368 return CoderResult.UNDERFLOW;
369 }
370
371 private CoderResult versionSequence(short newByte) {
372 if (state == VERSION_SEQUENCE_V) {
373 if (newByte >= 0x20 && newByte <= 0x2F) {
374 state = VERSION_SEQUENCE_TERM;
375 queue.write(newByte);
376 } else {
377 return escapeSequenceOther(newByte);
378 }
379 } else /* if (state == VERSION_SEQUENCE_TERM) */ {
380 switch (newByte) {
381 case 0x30:
382 if (!versionSequenceAllowed) {
383 return malformedInput(ERR_VERSTART);
384 }
385
386 // OK to ignore extensions
387 versionSequenceAllowed = false;
388 state = NORMAL_BYTES;
389 queue.reset();
390 break;
391 case 0x31:
392 return malformedInput((versionSequenceAllowed)
393 ? ERR_VERMANDATORY : ERR_VERSTART);
394 default:
395 return escapeSequenceOther(newByte);
396 }
397 }
398 return CoderResult.UNDERFLOW;
399 }
400
401 private CoderResult charset94N(short newByte) {
402 switch (newByte) {
403 case 0x28:
404 state = CHARSET_NLIIF;
405 break;
406 case 0x29:
407 state = CHARSET_NRIIF;
408 break;
409 default:
410 // escapeSequenceOther will write byte if appropriate
411 return escapeSequenceOther(newByte);
412 }
413
414 queue.write(newByte);
415 return CoderResult.UNDERFLOW;
416 }
417
418 private CoderResult charset94NL(short newByte, CharBuffer cb) {
419 if (newByte >= 0x21 &&
420 newByte <= (state == CHARSET_NLIIF ? 0x23 : 0x2F)) {
421 // {I}
422 state = CHARSET_NLIF;
423 queue.write(newByte);
424 } else if (newByte >= 0x40 && newByte <= 0x7E) {
425 // F
426 return switchDecoder(newByte, cb);
427 } else {
428 return escapeSequenceOther(newByte);
429 }
430 return CoderResult.UNDERFLOW;
431 }
432
433 private CoderResult charset94NR(short newByte, CharBuffer cb)
434 {
435 if (newByte >= 0x21 &&
436 newByte <= (state == CHARSET_NRIIF ? 0x23 : 0x2F)) {
437 // {I}
438 state = CHARSET_NRIF;
439 queue.write(newByte);
440 } else if (newByte >= 0x40 && newByte <= 0x7E) {
441 // F
442 return switchDecoder(newByte, cb);
443 } else {
444 return escapeSequenceOther(newByte);
445 }
446 return CoderResult.UNDERFLOW;
447 }
448
449 private CoderResult charset9496L(short newByte, CharBuffer cb) {
450 if (newByte >= 0x21 &&
451 newByte <= (state == CHARSET_LIIF ? 0x23 : 0x2F)) {
452 // {I}
453 state = CHARSET_LIF;
454 queue.write(newByte);
455 return CoderResult.UNDERFLOW;
456 } else if (newByte >= 0x40 && newByte <= 0x7E) {
457 // F
458 return switchDecoder(newByte, cb);
459 } else {
460 return escapeSequenceOther(newByte);
461 }
462 }
463
464 private CoderResult charset9496R(short newByte, CharBuffer cb) {
465 if (newByte >= 0x21 &&
466 newByte <= (state == CHARSET_RIIF ? 0x23 : 0x2F)) {
467 // {I}
468 state = CHARSET_RIF;
469 queue.write(newByte);
470 return CoderResult.UNDERFLOW;
471 } else if (newByte >= 0x40 && newByte <= 0x7E) {
472 // F
473 return switchDecoder(newByte, cb);
474 } else {
475 return escapeSequenceOther(newByte);
476 }
477 }
478
479 private CoderResult charsetNonStandard(short newByte, CharBuffer cb) {
480 switch (state) {
481 case CHARSET_NONSTANDARD_FOML:
482 if (newByte == 0x2F) {
483 state = CHARSET_NONSTANDARD_OML;
484 queue.write(newByte);
485 } else {
486 return escapeSequenceOther(newByte);
487 }
488 break;
489 case CHARSET_NONSTANDARD_OML:
490 if (newByte >= 0x30 && newByte <= 0x34) {
491 state = CHARSET_NONSTANDARD_ML;
492 queue.write(newByte);
493 } else if (newByte >= 0x35 && newByte <= 0x3F) {
494 state = EXTENSION_ML;
495 queue.write(newByte);
496 } else {
497 return escapeSequenceOther(newByte);
498 }
499 break;
500 case CHARSET_NONSTANDARD_ML:
501 ext_count = (newByte & 0x7F) * 0x80;
502 state = CHARSET_NONSTANDARD_L;
503 break;
504 case CHARSET_NONSTANDARD_L:
505 ext_count = ext_count + (newByte & 0x7F);
506 state = (ext_count > 0) ? CHARSET_NONSTANDARD : NORMAL_BYTES;
507 break;
508 case CHARSET_NONSTANDARD:
509 if (newByte == 0x3F || newByte == 0x2A) {
510 queue.reset(); // In this case, only current byte is bad.
511 return malformedInput(ERR_ENCODINGBYTE);
512 }
513 ext_offset++;
514 if (ext_offset >= ext_count) {
515 ext_offset = ext_count = 0;
516 state = NORMAL_BYTES;
517 queue.reset();
518 encodingQueue.reset();
519 } else if (newByte == 0x02) {
520 // encoding name terminator
521 return switchDecoder((short)0, cb);
522 } else {
523 encodingQueue.write(newByte);
524 }
525 break;
526 default:
527 error(ERR_ILLSTATE);
528 }
529 return CoderResult.UNDERFLOW;
530 }
531
532 private CoderResult extension(short newByte) {
533 switch (state) {
534 case EXTENSION_ML:
535 ext_count = (newByte & 0x7F) * 0x80;
536 state = EXTENSION_L;
537 break;
538 case EXTENSION_L:
539 ext_count = ext_count + (newByte & 0x7F);
540 state = (ext_count > 0) ? EXTENSION : NORMAL_BYTES;
541 break;
542 case EXTENSION:
543 // Consume 'count' bytes. Don't bother putting them on the queue.
544 // There may be too many and we can't do anything with them anyway.
545 ext_offset++;
546 if (ext_offset >= ext_count) {
547 ext_offset = ext_count = 0;
548 state = NORMAL_BYTES;
549 queue.reset();
550 }
551 break;
552 default:
553 error(ERR_ILLSTATE);
554 }
555 return CoderResult.UNDERFLOW;
556 }
557
558 /**
559 * Preconditions:
560 * 1. 'queue' contains ControlSequence.escSequence
561 * 2. 'encodingQueue' contains ControlSequence.encoding
562 */
563 private CoderResult switchDecoder(short lastByte, CharBuffer cb) {
564 CoderResult cr = CoderResult.UNDERFLOW;
565 CharsetDecoder decoder = null;
566 boolean high = false;
567 byte[] escSequence;
568 byte[] encoding = null;
569
570 if (lastByte != 0) {
571 queue.write(lastByte);
572 }
573
574 escSequence = queue.toByteArray();
575 queue.reset();
576
577 if (state == CHARSET_NONSTANDARD) {
578 encoding = encodingQueue.toByteArray();
579 encodingQueue.reset();
580 decoder = CompoundTextSupport.
581 getNonStandardDecoder(escSequence, encoding);
582 } else {
583 decoder = CompoundTextSupport.getStandardDecoder(escSequence);
584 high = CompoundTextSupport.getHighBit(escSequence);
585 }
586 if (decoder != null) {
587 initDecoder(decoder);
588 } else if (unmappableCharacterAction() == CodingErrorAction.REPORT) {
589 int badInputLength = 1;
590 if (encoding != null) {
591 badInputLength = encoding.length;
592 } else if (escSequence.length > 0) {
593 badInputLength = escSequence.length;
594 }
595 return CoderResult.unmappableForLength(badInputLength);
596 }
597
598 if (state == CHARSET_NLIIF || state == CHARSET_NLIF ||
599 state == CHARSET_LIIF || state == CHARSET_LIF)
600 {
601 if (lastDecoder == glDecoder) {
602 cr = flushDecoder(glDecoder, cb);
603 }
604 glDecoder = lastDecoder = decoder;
605 glHigh = high;
606 state = NORMAL_BYTES;
607 } else if (state == CHARSET_NRIIF || state == CHARSET_NRIF ||
608 state == CHARSET_RIIF || state == CHARSET_RIF) {
609 if (lastDecoder == grDecoder) {
610 cr = flushDecoder(grDecoder, cb);
611 }
612 grDecoder = lastDecoder = decoder;
613 grHigh = high;
614 state = NORMAL_BYTES;
615 } else if (state == CHARSET_NONSTANDARD) {
616 if (lastDecoder != null) {
617 cr = flushDecoder(lastDecoder, cb);
618 lastDecoder = null;
619 }
620 nonStandardDecoder = decoder;
621 state = NONSTANDARD_BYTES;
622 } else {
623 error(ERR_ILLSTATE);
624 }
625 return cr;
626 }
627
628 private ByteBuffer fbb= ByteBuffer.allocate(0);
629 private CoderResult flushDecoder(CharsetDecoder dec, CharBuffer cb) {
630 dec.decode(fbb, cb, true);
631 CoderResult cr = dec.flush(cb);
632 dec.reset(); //reuse
633 return cr;
634 }
635
636 private CoderResult malformedInput(String msg) {
637 int badInputLength = queue.size() + 1 /* current byte */ ;
638 queue.reset();
639 //TBD: nowhere to put the msg in CoderResult
640 return CoderResult.malformedForLength(badInputLength);
641 }
642
643 private void error(String msg) {
644 // For now, throw InternalError. Convert to 'assert' keyword later.
645 throw new InternalError(msg);
646 }
647
648 protected CoderResult implFlush(CharBuffer out) {
649 CoderResult cr = CoderResult.UNDERFLOW;
650 if (lastDecoder != null)
651 cr = flushDecoder(lastDecoder, out);
652 if (state != NORMAL_BYTES)
653 //TBD message ERR_FLUSH;
654 cr = CoderResult.malformedForLength(0);
655 reset();
656 return cr;
657 }
658
659 /**
660 * Resets the decoder.
661 * Call this method to reset the decoder to its initial state
662 */
663 protected void implReset() {
664 state = NORMAL_BYTES;
665 ext_count = ext_offset = 0;
666 versionSequenceAllowed = true;
667 queue.reset();
668 encodingQueue.reset();
669 nonStandardDecoder = lastDecoder = null;
670 glHigh = false;
671 grHigh = true;
672 try {
673 // Initial state in ISO 2022 designates Latin-1 charset.
674 glDecoder = Charset.forName("ASCII").newDecoder();
675 grDecoder = Charset.forName("ISO8859_1").newDecoder();
676 } catch (IllegalArgumentException e) {
677 error(ERR_LATIN1);
678 }
679 initDecoder(glDecoder);
680 initDecoder(grDecoder);
681 }
682
683 protected void implOnMalformedInput(CodingErrorAction newAction) {
684 if (glDecoder != null)
685 glDecoder.onMalformedInput(newAction);
686 if (grDecoder != null)
687 grDecoder.onMalformedInput(newAction);
688 if (nonStandardDecoder != null)
689 nonStandardDecoder.onMalformedInput(newAction);
690 }
691
692 protected void implOnUnmappableCharacter(CodingErrorAction newAction) {
693 if (glDecoder != null)
694 glDecoder.onUnmappableCharacter(newAction);
695 if (grDecoder != null)
696 grDecoder.onUnmappableCharacter(newAction);
697 if (nonStandardDecoder != null)
698 nonStandardDecoder.onUnmappableCharacter(newAction);
699 }
700
701 protected void implReplaceWith(String newReplacement) {
702 if (glDecoder != null)
703 glDecoder.replaceWith(newReplacement);
704 if (grDecoder != null)
705 grDecoder.replaceWith(newReplacement);
706 if (nonStandardDecoder != null)
707 nonStandardDecoder.replaceWith(newReplacement);
708 }
709
710 private void initDecoder(CharsetDecoder dec) {
711 dec.onUnmappableCharacter(CodingErrorAction.REPLACE)
712 .replaceWith(replacement());
713 }
714}