1 /*
   2  * Copyright (c) 2002, 2012, 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 
  28 import java.nio.ByteBuffer;
  29 import java.nio.CharBuffer;
  30 import java.nio.charset.Charset;
  31 import java.nio.charset.CharsetDecoder;
  32 import java.nio.charset.CharsetEncoder;
  33 import java.nio.charset.CoderResult;
  34 import java.nio.charset.CodingErrorAction;
  35 import sun.nio.cs.DelegatableDecoder;
  36 import sun.nio.cs.DoubleByte;
  37 import sun.nio.cs.HistoricallyNamedCharset;
  38 import sun.nio.cs.Surrogate;
  39 import sun.nio.cs.US_ASCII;
  40 import sun.nio.cs.*;
  41 import static sun.nio.cs.CharsetMapping.*;
  42 
  43 /*
  44  * Implementation notes:
  45  *
  46  * (1)"Standard based" (ASCII, JIS_X_0201 and JIS_X_0208) ISO2022-JP charset
  47  * is provided by the base implementation of this class.
  48  *
  49  * Three Microsoft ISO2022-JP variants, MS50220, MS50221 and MSISO2022JP
  50  * are provided via subclasses.
  51  *
  52  * (2)MS50220 and MS50221 are assumed to work the same way as Microsoft
  53  * CP50220 and CP50221's 7-bit implementation works by using CP5022X
  54  * specific JIS0208 and JIS0212 mapping tables (generated via Microsoft's
  55  * MultiByteToWideChar/WideCharToMultiByte APIs). The only difference
  56  * between these 2 classes is that MS50220 does not support singlebyte
  57  * halfwidth kana (Uff61-Uff9f) shiftin mechanism when "encoding", instead
  58  * these halfwidth kana characters are converted to their fullwidth JIS0208
  59  * counterparts.
  60  *
  61  * The difference between the standard JIS_X_0208 and JIS_X_0212 mappings
  62  * and the CP50220/50221 specific are
  63  *
  64  * 0208 mapping:
  65  *              1)0x213d <-> U2015 (compared to U2014)
  66  *              2)One way mappings for 5 characters below
  67  *                u2225 (ms) -> 0x2142 <-> u2016 (jis)
  68  *                uff0d (ms) -> 0x215d <-> u2212 (jis)
  69  *                uffe0 (ms) -> 0x2171 <-> u00a2 (jis)
  70  *                uffe1 (ms) -> 0x2172 <-> u00a3 (jis)
  71  *                uffe2 (ms) -> 0x224c <-> u00ac (jis)
  72  *                //should consider 0xff5e -> 0x2141 <-> U301c?
  73  *              3)NEC Row13 0x2d21-0x2d79
  74  *              4)85-94 ku <-> UE000,UE3AB (includes NEC selected
  75  *                IBM kanji in 89-92ku)
  76  *              5)UFF61-UFF9f -> Fullwidth 0208 KANA
  77  *
  78  * 0212 mapping:
  79  *              1)0x2237 <-> UFF5E (Fullwidth Tilde)
  80  *              2)0x2271 <-> U2116 (Numero Sign)
  81  *              3)85-94 ku <-> UE3AC - UE757
  82  *
  83  * (3)MSISO2022JP uses a JIS0208 mapping generated from MS932DB.b2c
  84  * and MS932DB.c2b by converting the SJIS codepoints back to their
  85  * JIS0208 counterparts. With the exception of
  86  *
  87  * (a)Codepoints with a resulting JIS0208 codepoints beyond 0x7e00 are
  88  *    dropped (this includs the IBM Extended Kanji/Non-kanji from 0x9321
  89  *    to 0x972c)
  90  * (b)The Unicode codepoints that the IBM Extended Kanji/Non-kanji are
  91  *    mapped to (in MS932) are mapped back to NEC selected IBM Kanji/
  92  *    Non-kanji area at 0x7921-0x7c7e.
  93  *
  94  * Compared to JIS_X_0208 mapping, this MS932 based mapping has
  95 
  96  * (a)different mappings for 7 JIS codepoints
  97  *        0x213d <-> U2015
  98  *        0x2141 <-> UFF5E
  99  *        0x2142 <-> U2225
 100  *        0x215d <-> Uff0d
 101  *        0x2171 <-> Uffe0
 102  *        0x2172 <-> Uffe1
 103  *        0x224c <-> Uffe2
 104  * (b)added one-way c2b mappings for
 105  *        U00b8 -> 0x2124
 106  *        U00b7 -> 0x2126
 107  *        U00af -> 0x2131
 108  *        U00ab -> 0x2263
 109  *        U00bb -> 0x2264
 110  *        U3094 -> 0x2574
 111  *        U00b5 -> 0x264c
 112  * (c)NEC Row 13
 113  * (d)NEC selected IBM extended Kanji/Non-kanji
 114  *    These codepoints are mapped to the same Unicode codepoints as
 115  *    the MS932 does, while MS50220/50221 maps them to the Unicode
 116  *    private area.
 117  *
 118  * # There is also an interesting difference when compared to MS5022X
 119  *   0208 mapping for JIS codepoint "0x2D60", MS932 maps it to U301d
 120  *   but MS5022X maps it to U301e, obvious MS5022X is wrong, but...
 121  */
 122 
 123 public class ISO2022_JP
 124     extends Charset
 125     implements HistoricallyNamedCharset
 126 {
 127     private static final int ASCII = 0;                 // ESC ( B
 128     private static final int JISX0201_1976 = 1;         // ESC ( J
 129     private static final int JISX0208_1978 = 2;         // ESC $ @
 130     private static final int JISX0208_1983 = 3;         // ESC $ B
 131     private static final int JISX0212_1990 = 4;         // ESC $ ( D
 132     private static final int JISX0201_1976_KANA = 5;    // ESC ( I
 133     private static final int SHIFTOUT = 6;
 134 
 135     private static final int ESC = 0x1b;
 136     private static final int SO = 0x0e;
 137     private static final int SI = 0x0f;
 138 
 139     public ISO2022_JP() {
 140         super("ISO-2022-JP",
 141               ExtendedCharsets.aliasesFor("ISO-2022-JP"));
 142     }
 143 
 144     protected ISO2022_JP(String canonicalName,
 145                          String[] aliases) {
 146         super(canonicalName, aliases);
 147     }
 148 
 149     public String historicalName() {
 150         return "ISO2022JP";
 151     }
 152 
 153     public boolean contains(Charset cs) {
 154         return ((cs instanceof JIS_X_0201)
 155                 || (cs instanceof US_ASCII)
 156                 || (cs instanceof JIS_X_0208)
 157                 || (cs instanceof ISO2022_JP));
 158     }
 159 
 160     public CharsetDecoder newDecoder() {
 161         return new Decoder(this);
 162     }
 163 
 164     public CharsetEncoder newEncoder() {
 165         return new Encoder(this);
 166     }
 167 
 168     protected boolean doSBKANA() {
 169         return true;
 170     }
 171 
 172     static class Decoder extends CharsetDecoder
 173         implements DelegatableDecoder {
 174 
 175         final static DoubleByte.Decoder DEC0208 =
 176             (DoubleByte.Decoder)new JIS_X_0208().newDecoder();
 177 
 178         private int currentState;
 179         private int previousState;
 180 
 181         private DoubleByte.Decoder dec0208;
 182         private DoubleByte.Decoder dec0212;
 183 
 184         private Decoder(Charset cs) {
 185             this(cs, DEC0208, null);
 186         }
 187 
 188         protected Decoder(Charset cs,
 189                           DoubleByte.Decoder dec0208,
 190                           DoubleByte.Decoder dec0212) {
 191             super(cs, 0.5f, 1.0f);
 192             this.dec0208 = dec0208;
 193             this.dec0212 = dec0212;
 194             currentState = ASCII;
 195             previousState = ASCII;
 196         }
 197 
 198         public void implReset() {
 199             currentState = ASCII;
 200             previousState = ASCII;
 201         }
 202 
 203         private CoderResult decodeArrayLoop(ByteBuffer src,
 204                                             CharBuffer dst)
 205         {
 206             int inputSize = 0;
 207             int b1 = 0, b2 = 0, b3 = 0, b4 = 0;
 208             char c = UNMAPPABLE_DECODING;
 209             byte[] sa = src.array();
 210             int sp = src.arrayOffset() + src.position();
 211             int sl = src.arrayOffset() + src.limit();
 212             assert (sp <= sl);
 213             sp = (sp <= sl ? sp : sl);
 214 
 215             char[] da = dst.array();
 216             int dp = dst.arrayOffset() + dst.position();
 217             int dl = dst.arrayOffset() + dst.limit();
 218             assert (dp <= dl);
 219             dp = (dp <= dl ? dp : dl);
 220 
 221             try {
 222                 while (sp < sl) {
 223                     b1 = sa[sp] & 0xff;
 224                     inputSize = 1;
 225                     if ((b1 & 0x80) != 0) {
 226                         return CoderResult.malformedForLength(inputSize);
 227                     }
 228                     if (b1 == ESC || b1 == SO || b1 == SI) {
 229                         if (b1 == ESC) {
 230                             if (sp + inputSize + 2 > sl)
 231                                 return CoderResult.UNDERFLOW;
 232                             b2 = sa[sp + inputSize++] & 0xff;
 233                             if (b2 == '(') {
 234                                 b3 = sa[sp + inputSize++] & 0xff;
 235                                 if (b3 == 'B'){
 236                                     currentState = ASCII;
 237                                 } else if (b3 == 'J'){
 238                                     currentState = JISX0201_1976;
 239                                 } else if (b3 == 'I'){
 240                                     currentState = JISX0201_1976_KANA;
 241                                 } else {
 242                                     return CoderResult.malformedForLength(inputSize);
 243                                 }
 244                             } else if (b2 == '$'){
 245                                 b3 = sa[sp + inputSize++] & 0xff;
 246                                 if (b3 == '@'){
 247                                     currentState = JISX0208_1978;
 248                                 } else if (b3 == 'B'){
 249                                     currentState = JISX0208_1983;
 250                                 } else if (b3 == '(' && dec0212 != null) {
 251                                     if (sp + inputSize + 1 > sl)
 252                                         return CoderResult.UNDERFLOW;
 253                                     b4 = sa[sp + inputSize++] & 0xff;
 254                                     if (b4 == 'D') {
 255                                         currentState = JISX0212_1990;
 256                                     } else {
 257                                         return CoderResult.malformedForLength(inputSize);
 258                                     }
 259                                 } else {
 260                                     return CoderResult.malformedForLength(inputSize);
 261                                 }
 262                             } else {
 263                                 return CoderResult.malformedForLength(inputSize);
 264                             }
 265                         } else if (b1 == SO) {
 266                             previousState = currentState;
 267                             currentState = SHIFTOUT;
 268                         } else if (b1 == SI) {
 269                             currentState = previousState;
 270                         }
 271                         sp += inputSize;
 272                         continue;
 273                     }
 274                     if (dp + 1 > dl)
 275                         return CoderResult.OVERFLOW;
 276 
 277                     switch (currentState){
 278                         case ASCII:
 279                             da[dp++] = (char)(b1 & 0xff);
 280                             break;
 281                         case JISX0201_1976:
 282                           switch (b1) {
 283                               case 0x5c:  // Yen/tilde substitution
 284                                 da[dp++] = '\u00a5';
 285                                 break;
 286                               case 0x7e:
 287                                 da[dp++] = '\u203e';
 288                                 break;
 289                               default:
 290                                 da[dp++] = (char)b1;
 291                                 break;
 292                             }
 293                             break;
 294                         case JISX0208_1978:
 295                         case JISX0208_1983:
 296                             if (sp + inputSize + 1 > sl)
 297                                 return CoderResult.UNDERFLOW;
 298                             b2 = sa[sp + inputSize++] & 0xff;
 299                             c = dec0208.decodeDouble(b1,b2);
 300                             if (c == UNMAPPABLE_DECODING)
 301                                 return CoderResult.unmappableForLength(inputSize);
 302                             da[dp++] = c;
 303                             break;
 304                         case JISX0212_1990:
 305                             if (sp + inputSize + 1 > sl)
 306                                 return CoderResult.UNDERFLOW;
 307                             b2 = sa[sp + inputSize++] & 0xff;
 308                             c = dec0212.decodeDouble(b1,b2);
 309                             if (c == UNMAPPABLE_DECODING)
 310                                 return CoderResult.unmappableForLength(inputSize);
 311                             da[dp++] = c;
 312                             break;
 313                         case JISX0201_1976_KANA:
 314                         case SHIFTOUT:
 315                             if (b1 > 0x60) {
 316                                 return CoderResult.malformedForLength(inputSize);
 317                             }
 318                             da[dp++] = (char)(b1 + 0xff40);
 319                             break;
 320                     }
 321                     sp += inputSize;
 322                 }
 323                 return CoderResult.UNDERFLOW;
 324             } finally {
 325                 src.position(sp - src.arrayOffset());
 326                 dst.position(dp - dst.arrayOffset());
 327             }
 328         }
 329 
 330         private CoderResult decodeBufferLoop(ByteBuffer src,
 331                                              CharBuffer dst)
 332         {
 333             int mark = src.position();
 334             int b1 = 0, b2 = 0, b3 = 0, b4=0;
 335             char c = UNMAPPABLE_DECODING;
 336             int inputSize = 0;
 337             try {
 338                 while (src.hasRemaining()) {
 339                     b1 = src.get() & 0xff;
 340                     inputSize = 1;
 341                     if ((b1 & 0x80) != 0)
 342                         return CoderResult.malformedForLength(inputSize);
 343                     if (b1 == ESC || b1 == SO || b1 == SI) {
 344                         if (b1 == ESC) {  // ESC
 345                             if (src.remaining() < 2)
 346                                 return CoderResult.UNDERFLOW;
 347                             b2 = src.get() & 0xff;
 348                             inputSize++;
 349                             if (b2 == '(') {
 350                                 b3 = src.get() & 0xff;
 351                                 inputSize++;
 352                                 if (b3 == 'B'){
 353                                     currentState = ASCII;
 354                                 } else if (b3 == 'J'){
 355                                     currentState = JISX0201_1976;
 356                                 } else if (b3 == 'I'){
 357                                     currentState = JISX0201_1976_KANA;
 358                                 } else {
 359                                    return CoderResult.malformedForLength(inputSize);
 360                                 }
 361                             } else if (b2 == '$'){
 362                                 b3 = src.get() & 0xff;
 363                                 inputSize++;
 364                                 if (b3 == '@'){
 365                                     currentState = JISX0208_1978;
 366                                 } else if (b3 == 'B'){
 367                                     currentState = JISX0208_1983;
 368                                 } else if (b3 == '(' && dec0212 != null) {
 369                                     if (!src.hasRemaining())
 370                                         return CoderResult.UNDERFLOW;
 371                                     b4 = src.get() & 0xff;
 372                                     inputSize++;
 373                                     if (b4 == 'D') {
 374                                         currentState = JISX0212_1990;
 375                                     } else {
 376                                         return CoderResult.malformedForLength(inputSize);
 377                                     }
 378                                 } else {
 379                                     return CoderResult.malformedForLength(inputSize);
 380                                 }
 381                             } else {
 382                                 return CoderResult.malformedForLength(inputSize);
 383                             }
 384                         } else if (b1 == SO) {
 385                             previousState = currentState;
 386                             currentState = SHIFTOUT;
 387                         } else if (b1 == SI) { // shift back in
 388                             currentState = previousState;
 389                         }
 390                         mark += inputSize;
 391                         continue;
 392                     }
 393                     if (!dst.hasRemaining())
 394                         return CoderResult.OVERFLOW;
 395 
 396                     switch (currentState){
 397                         case ASCII:
 398                             dst.put((char)(b1 & 0xff));
 399                             break;
 400                         case JISX0201_1976:
 401                             switch (b1) {
 402                               case 0x5c:  // Yen/tilde substitution
 403                                 dst.put('\u00a5');
 404                                 break;
 405                               case 0x7e:
 406                                 dst.put('\u203e');
 407                                 break;
 408                               default:
 409                                 dst.put((char)b1);
 410                                 break;
 411                             }
 412                             break;
 413                         case JISX0208_1978:
 414                         case JISX0208_1983:
 415                             if (!src.hasRemaining())
 416                                 return CoderResult.UNDERFLOW;
 417                             b2 = src.get() & 0xff;
 418                             inputSize++;
 419                             c = dec0208.decodeDouble(b1,b2);
 420                             if (c == UNMAPPABLE_DECODING)
 421                                 return CoderResult.unmappableForLength(inputSize);
 422                             dst.put(c);
 423                             break;
 424                         case JISX0212_1990:
 425                             if (!src.hasRemaining())
 426                                 return CoderResult.UNDERFLOW;
 427                             b2 = src.get() & 0xff;
 428                             inputSize++;
 429                             c = dec0212.decodeDouble(b1,b2);
 430                             if (c == UNMAPPABLE_DECODING)
 431                                 return CoderResult.unmappableForLength(inputSize);
 432                             dst.put(c);
 433                             break;
 434                         case JISX0201_1976_KANA:
 435                         case SHIFTOUT:
 436                             if (b1 > 0x60) {
 437                                 return CoderResult.malformedForLength(inputSize);
 438                             }
 439                             dst.put((char)(b1 + 0xff40));
 440                             break;
 441                     }
 442                     mark += inputSize;
 443                 }
 444                 return CoderResult.UNDERFLOW;
 445             } finally {
 446                 src.position(mark);
 447             }
 448         }
 449 
 450         // Make some protected methods public for use by JISAutoDetect
 451         public CoderResult decodeLoop(ByteBuffer src, CharBuffer dst) {
 452             if (src.hasArray() && dst.hasArray())
 453                 return decodeArrayLoop(src, dst);
 454             else
 455                 return decodeBufferLoop(src, dst);
 456         }
 457 
 458         public CoderResult implFlush(CharBuffer out) {
 459             return super.implFlush(out);
 460         }
 461     }
 462 
 463     static class Encoder extends CharsetEncoder {
 464 
 465         final static DoubleByte.Encoder ENC0208 =
 466             (DoubleByte.Encoder)new JIS_X_0208().newEncoder();
 467 
 468         private static byte[] repl = { (byte)0x21, (byte)0x29 };
 469         private int currentMode = ASCII;
 470         private int replaceMode = JISX0208_1983;
 471         private DoubleByte.Encoder enc0208;
 472         private DoubleByte.Encoder enc0212;
 473         private boolean doSBKANA;
 474 
 475         private Encoder(Charset cs) {
 476             this(cs, ENC0208, null, true);
 477         }
 478 
 479         Encoder(Charset cs,
 480                 DoubleByte.Encoder enc0208,
 481                 DoubleByte.Encoder enc0212,
 482                 boolean doSBKANA) {
 483             super(cs, 4.0f, (enc0212 != null)? 9.0f : 8.0f, repl);
 484             this.enc0208 = enc0208;
 485             this.enc0212 = enc0212;
 486             this.doSBKANA = doSBKANA;
 487         }
 488 
 489         protected int encodeSingle(char inputChar) {
 490             return -1;
 491         }
 492 
 493         protected void implReset() {
 494             currentMode = ASCII;
 495         }
 496 
 497         protected void implReplaceWith(byte[] newReplacement) {
 498             /* It's almost impossible to decide which charset they belong
 499                to. The best thing we can do here is to "guess" based on
 500                the length of newReplacement.
 501              */
 502             if (newReplacement.length == 1) {
 503                 replaceMode = ASCII;
 504             } else if (newReplacement.length == 2) {
 505                 replaceMode = JISX0208_1983;
 506             }
 507         }
 508 
 509         protected CoderResult implFlush(ByteBuffer out) {
 510             if (currentMode != ASCII) {
 511                 if (out.remaining() < 3)
 512                     return CoderResult.OVERFLOW;
 513                 out.put((byte)0x1b);
 514                 out.put((byte)0x28);
 515                 out.put((byte)0x42);
 516                 currentMode = ASCII;
 517             }
 518             return CoderResult.UNDERFLOW;
 519         }
 520 
 521         public boolean canEncode(char c) {
 522             return ((c <= '\u007F') ||
 523                     (c >= 0xFF61 && c <= 0xFF9F) ||
 524                     (c == '\u00A5') ||
 525                     (c == '\u203E') ||
 526                     enc0208.canEncode(c) ||
 527                     (enc0212!=null && enc0212.canEncode(c)));
 528         }
 529 
 530         private final Surrogate.Parser sgp = new Surrogate.Parser();
 531 
 532         private CoderResult encodeArrayLoop(CharBuffer src,
 533                                             ByteBuffer dst)
 534         {
 535             char[] sa = src.array();
 536             int sp = src.arrayOffset() + src.position();
 537             int sl = src.arrayOffset() + src.limit();
 538             assert (sp <= sl);
 539             sp = (sp <= sl ? sp : sl);
 540             byte[] da = dst.array();
 541             int dp = dst.arrayOffset() + dst.position();
 542             int dl = dst.arrayOffset() + dst.limit();
 543             assert (dp <= dl);
 544             dp = (dp <= dl ? dp : dl);
 545 
 546             try {
 547                 while (sp < sl) {
 548                     char c = sa[sp];
 549                     if (c <= '\u007F') {
 550                         if (currentMode != ASCII) {
 551                             if (dl - dp < 3)
 552                                 return CoderResult.OVERFLOW;
 553                             da[dp++] = (byte)0x1b;
 554                             da[dp++] = (byte)0x28;
 555                             da[dp++] = (byte)0x42;
 556                             currentMode = ASCII;
 557                         }
 558                         if (dl - dp < 1)
 559                             return CoderResult.OVERFLOW;
 560                         da[dp++] = (byte)c;
 561                     } else if (c >= 0xff61 && c <= 0xff9f && doSBKANA) {
 562                         //a single byte kana
 563                         if (currentMode != JISX0201_1976_KANA) {
 564                             if (dl - dp < 3)
 565                                 return CoderResult.OVERFLOW;
 566                             da[dp++] = (byte)0x1b;
 567                             da[dp++] = (byte)0x28;
 568                             da[dp++] = (byte)0x49;
 569                             currentMode = JISX0201_1976_KANA;
 570                         }
 571                         if (dl - dp < 1)
 572                             return CoderResult.OVERFLOW;
 573                         da[dp++] = (byte)(c - 0xff40);
 574                     } else if (c == '\u00A5' || c == '\u203E') {
 575                         //backslash or tilde
 576                         if (currentMode != JISX0201_1976) {
 577                             if (dl - dp < 3)
 578                                 return CoderResult.OVERFLOW;
 579                             da[dp++] = (byte)0x1b;
 580                             da[dp++] = (byte)0x28;
 581                             da[dp++] = (byte)0x4a;
 582                             currentMode = JISX0201_1976;
 583                         }
 584                         if (dl - dp < 1)
 585                             return CoderResult.OVERFLOW;
 586                         da[dp++] = (c == '\u00A5')?(byte)0x5C:(byte)0x7e;
 587                     } else {
 588                         int index = enc0208.encodeChar(c);
 589                         if (index != UNMAPPABLE_ENCODING) {
 590                             if (currentMode != JISX0208_1983) {
 591                                 if (dl - dp < 3)
 592                                     return CoderResult.OVERFLOW;
 593                                 da[dp++] = (byte)0x1b;
 594                                 da[dp++] = (byte)0x24;
 595                                 da[dp++] = (byte)0x42;
 596                                 currentMode = JISX0208_1983;
 597                             }
 598                             if (dl - dp < 2)
 599                                 return CoderResult.OVERFLOW;
 600                             da[dp++] = (byte)(index >> 8);
 601                             da[dp++] = (byte)(index & 0xff);
 602                         } else if (enc0212 != null &&
 603                                    (index = enc0212.encodeChar(c)) != UNMAPPABLE_ENCODING) {
 604                             if (currentMode != JISX0212_1990) {
 605                                 if (dl - dp < 4)
 606                                     return CoderResult.OVERFLOW;
 607                                 da[dp++] = (byte)0x1b;
 608                                 da[dp++] = (byte)0x24;
 609                                 da[dp++] = (byte)0x28;
 610                                 da[dp++] = (byte)0x44;
 611                                 currentMode = JISX0212_1990;
 612                             }
 613                             if (dl - dp < 2)
 614                                 return CoderResult.OVERFLOW;
 615                             da[dp++] = (byte)(index >> 8);
 616                             da[dp++] = (byte)(index & 0xff);
 617                         } else {
 618                             if (Character.isSurrogate(c) && sgp.parse(c, sa, sp, sl) < 0)
 619                                 return sgp.error();
 620                             if (unmappableCharacterAction()
 621                                 == CodingErrorAction.REPLACE
 622                                 && currentMode != replaceMode) {
 623                                 if (dl - dp < 3)
 624                                     return CoderResult.OVERFLOW;
 625                                 if (replaceMode == ASCII) {
 626                                     da[dp++] = (byte)0x1b;
 627                                     da[dp++] = (byte)0x28;
 628                                     da[dp++] = (byte)0x42;
 629                                 } else {
 630                                     da[dp++] = (byte)0x1b;
 631                                     da[dp++] = (byte)0x24;
 632                                     da[dp++] = (byte)0x42;
 633                                 }
 634                                 currentMode = replaceMode;
 635                             }
 636                             if (Character.isSurrogate(c))
 637                                 return sgp.unmappableResult();
 638                             return CoderResult.unmappableForLength(1);
 639                         }
 640                     }
 641                     sp++;
 642                 }
 643                 return CoderResult.UNDERFLOW;
 644             } finally {
 645                 src.position(sp - src.arrayOffset());
 646                 dst.position(dp - dst.arrayOffset());
 647             }
 648         }
 649 
 650         private CoderResult encodeBufferLoop(CharBuffer src,
 651                                              ByteBuffer dst)
 652         {
 653             int mark = src.position();
 654             try {
 655                 while (src.hasRemaining()) {
 656                     char c = src.get();
 657 
 658                     if (c <= '\u007F') {
 659                         if (currentMode != ASCII) {
 660                             if (dst.remaining() < 3)
 661                                 return CoderResult.OVERFLOW;
 662                             dst.put((byte)0x1b);
 663                             dst.put((byte)0x28);
 664                             dst.put((byte)0x42);
 665                             currentMode = ASCII;
 666                         }
 667                         if (dst.remaining() < 1)
 668                             return CoderResult.OVERFLOW;
 669                         dst.put((byte)c);
 670                     } else if (c >= 0xff61 && c <= 0xff9f && doSBKANA) {
 671                         //Is it a single byte kana?
 672                         if (currentMode != JISX0201_1976_KANA) {
 673                             if (dst.remaining() < 3)
 674                                 return CoderResult.OVERFLOW;
 675                             dst.put((byte)0x1b);
 676                             dst.put((byte)0x28);
 677                             dst.put((byte)0x49);
 678                             currentMode = JISX0201_1976_KANA;
 679                         }
 680                         if (dst.remaining() < 1)
 681                             return CoderResult.OVERFLOW;
 682                         dst.put((byte)(c - 0xff40));
 683                     } else if (c == '\u00a5' || c == '\u203E') {
 684                         if (currentMode != JISX0201_1976) {
 685                             if (dst.remaining() < 3)
 686                                 return CoderResult.OVERFLOW;
 687                             dst.put((byte)0x1b);
 688                             dst.put((byte)0x28);
 689                             dst.put((byte)0x4a);
 690                             currentMode = JISX0201_1976;
 691                         }
 692                         if (dst.remaining() < 1)
 693                             return CoderResult.OVERFLOW;
 694                         dst.put((c == '\u00A5')?(byte)0x5C:(byte)0x7e);
 695                     } else {
 696                         int index = enc0208.encodeChar(c);
 697                         if (index != UNMAPPABLE_ENCODING) {
 698                             if (currentMode != JISX0208_1983) {
 699                                 if (dst.remaining() < 3)
 700                                     return CoderResult.OVERFLOW;
 701                                 dst.put((byte)0x1b);
 702                                 dst.put((byte)0x24);
 703                                 dst.put((byte)0x42);
 704                                 currentMode = JISX0208_1983;
 705                             }
 706                             if (dst.remaining() < 2)
 707                                 return CoderResult.OVERFLOW;
 708                             dst.put((byte)(index >> 8));
 709                             dst.put((byte)(index & 0xff));
 710                         } else if (enc0212 != null &&
 711                                    (index = enc0212.encodeChar(c)) != UNMAPPABLE_ENCODING) {
 712                             if (currentMode != JISX0212_1990) {
 713                                 if (dst.remaining() < 4)
 714                                     return CoderResult.OVERFLOW;
 715                                 dst.put((byte)0x1b);
 716                                 dst.put((byte)0x24);
 717                                 dst.put((byte)0x28);
 718                                 dst.put((byte)0x44);
 719                                 currentMode = JISX0212_1990;
 720                             }
 721                             if (dst.remaining() < 2)
 722                                 return CoderResult.OVERFLOW;
 723                             dst.put((byte)(index >> 8));
 724                             dst.put((byte)(index & 0xff));
 725                         } else {
 726                             if (Character.isSurrogate(c) && sgp.parse(c, src) < 0)
 727                                 return sgp.error();
 728                             if (unmappableCharacterAction() == CodingErrorAction.REPLACE
 729                                 && currentMode != replaceMode) {
 730                                 if (dst.remaining() < 3)
 731                                     return CoderResult.OVERFLOW;
 732                                 if (replaceMode == ASCII) {
 733                                     dst.put((byte)0x1b);
 734                                     dst.put((byte)0x28);
 735                                     dst.put((byte)0x42);
 736                                 } else {
 737                                     dst.put((byte)0x1b);
 738                                     dst.put((byte)0x24);
 739                                     dst.put((byte)0x42);
 740                                 }
 741                                 currentMode = replaceMode;
 742                             }
 743                             if (Character.isSurrogate(c))
 744                                 return sgp.unmappableResult();
 745                             return CoderResult.unmappableForLength(1);
 746                         }
 747                     }
 748                     mark++;
 749                 }
 750                 return CoderResult.UNDERFLOW;
 751               } finally {
 752                 src.position(mark);
 753             }
 754         }
 755 
 756         protected CoderResult encodeLoop(CharBuffer src,
 757                                          ByteBuffer dst)
 758         {
 759             if (src.hasArray() && dst.hasArray())
 760                 return encodeArrayLoop(src, dst);
 761             else
 762                 return encodeBufferLoop(src, dst);
 763         }
 764     }
 765 }