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