1 /* 2 * Copyright (c) 2001, 2005, Oracle and/or its affiliates. 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. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.nio.cs.ext; 27 import java.io.ByteArrayOutputStream; 28 import java.nio.ByteBuffer; 29 import java.nio.CharBuffer; 30 import java.nio.charset.*; 31 32 /** 33 * An algorithmic conversion from COMPOUND_TEXT to Unicode. 34 */ 35 36 public 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 }