1 /*
   2  * Copyright (c) 1996, 2010, 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.io;
  27 
  28 public class ByteToCharISO2022JP extends ByteToCharJIS0208 {
  29 
  30     private static final int ASCII = 0;                 // ESC ( B
  31     private static final int JISX0201_1976 = 1;         // ESC ( J
  32     private static final int JISX0208_1978 = 2;         // ESC $ @
  33     private static final int JISX0208_1983 = 3;         // ESC $ B
  34     private static final int JISX0201_1976_KANA = 4;    // ESC ( I
  35     private static final int SHIFTOUT = 5;              // SO (0x0e)
  36 
  37     private int currentState;
  38     private int savedSize;
  39     private byte[] savedBytes;
  40 
  41     public ByteToCharISO2022JP() {
  42         super();
  43         savedBytes = new byte[2];
  44         currentState = ASCII;
  45         savedSize = 0;
  46     }
  47 
  48     public int flush(char [] output, int outStart, int outEnd)
  49         throws MalformedInputException
  50     {
  51         if (savedSize != 0) {
  52             savedSize = 0;
  53             currentState = ASCII;
  54             badInputLength = 0;
  55             throw new MalformedInputException();
  56         }
  57         byteOff = charOff = 0;
  58         return 0;
  59     }
  60 
  61 
  62     /**
  63      * Character conversion
  64      */
  65     public int convert(byte[] input, int inOff, int inEnd,
  66                        char[] output, int outOff, int outEnd)
  67         throws UnknownCharacterException, MalformedInputException,
  68                ConversionBufferFullException
  69     {
  70         int previousState = ASCII;
  71         int inputSize = 0;
  72         char outputChar = '\uFFFD';
  73         // readOff keeps the actual buffer's pointer.
  74         // byteOff keeps original buffer's pointer.
  75         int readOff = byteOff = inOff;
  76 
  77         if (savedSize != 0) {
  78             if (savedBytes[0] == 0x1b) { // ESC
  79                 if ((savedSize == 2 &&
  80                      (savedBytes[1] == 0x28 &&
  81                       input[0] != 'B' &&
  82                       input[0] != 'J' &&
  83                       input[0] != 'I') &&
  84                      (savedBytes[1] == 0x24 &&
  85                       input[0] != '@' &&
  86                       input[0] != 'B')) ||
  87                     ((savedSize == 1) &&
  88                      (input[0] != 0x28 &&
  89                       input[0] != 0x24))) {
  90                     badInputLength = 0;
  91                     throw new MalformedInputException();
  92                 }
  93                 if ((inEnd - inOff) == 1 && savedSize == 1 &&
  94                     savedBytes[0] == 0x1b) {
  95                     savedSize = 2;
  96                     savedBytes[1] = input[0];
  97                     byteOff++;
  98                     return 0;
  99                 }
 100             }
 101             byte[] newBuf = new byte[inEnd - inOff + savedSize];
 102             for (int i = 0; i < savedSize; i++) {
 103                 newBuf[i] = savedBytes[i];
 104             }
 105             System.arraycopy(input, inOff, newBuf, savedSize, inEnd - inOff);
 106             byteOff -= savedSize;
 107             input = newBuf;
 108             inOff = 0;
 109             inEnd = newBuf.length;
 110             savedSize = 0;
 111         }
 112 
 113         charOff = outOff;
 114         readOff = inOff;
 115 
 116         while(readOff < inEnd) {
 117             int byte1, byte2, byte3;
 118             boolean noOutput = false;
 119 
 120             // Is there room in the output buffer for the result?
 121             if (charOff >= outEnd) {
 122                 throw new ConversionBufferFullException();
 123             }
 124 
 125             // Get the input byte
 126             byte1 = input[readOff] & 0xFF;
 127             inputSize = 1;
 128 
 129             if ((byte1 & (byte)0x80) != 0){
 130                 badInputLength = 1;
 131                 throw new MalformedInputException();
 132             }
 133 
 134             // Is this a escape sequence?
 135             while (byte1 == 0x1b || byte1 == 0x0e || byte1 == 0x0f) {
 136                 if (byte1 == 0x1b){  // ESC
 137                     if (readOff + inputSize + 1 >= inEnd) {
 138                         if (readOff + inputSize >= inEnd) {
 139                             savedSize = 1;
 140                             savedBytes[0] = (byte)byte1;
 141                         } else {
 142                             savedSize = 2;
 143                             savedBytes[0] = (byte)byte1;
 144                             savedBytes[1] = input[readOff + inputSize];
 145                             inputSize++;
 146                         }
 147                         break;
 148                     }
 149                     byte2 = input[readOff + inputSize] & 0xFF;
 150                     inputSize++;
 151                     if ((byte2 & (byte)0x80) != 0){
 152                         badInputLength = 2;
 153                         throw new MalformedInputException();
 154                     }
 155                     if (byte2 == 0x28){
 156                         byte3 = input[readOff + inputSize] & 0xFF;
 157                         inputSize++;
 158                         if (byte3 == 'B'){
 159                             currentState = ASCII;
 160                         } else if (byte3 == 'J'){
 161                             currentState = JISX0201_1976;
 162                         } else if (byte3 == 'I'){
 163                             currentState = JISX0201_1976_KANA;
 164                         } else {
 165                             // illegal ESC sequence
 166                             badInputLength = 3;
 167                             throw new MalformedInputException();
 168                         }
 169                     } else if (byte2 == '$'){
 170                         byte3 = input[readOff + inputSize] & 0xFF;
 171                         inputSize++;
 172                         if ((byte3 & (byte)0x80) != 0){
 173                             badInputLength = 3;
 174                             throw new MalformedInputException();
 175                         }
 176                         if (byte3 == '@'){
 177                             currentState = JISX0208_1978;
 178                         } else if (byte3 == 'B'){
 179                             currentState = JISX0208_1983;
 180                         } else {
 181                             // illegal ESC sequence
 182                             badInputLength = 3;
 183                             throw new MalformedInputException();
 184                         }
 185                     } else {
 186                         // illegal ESC sequence
 187                         badInputLength = 2;
 188                         throw new MalformedInputException();
 189                     }
 190                     if (readOff + inputSize >= inEnd) {
 191                         noOutput = true;
 192                         break;
 193                     } else {
 194                         byte1 = input[readOff + inputSize];
 195                         inputSize++;
 196                     }
 197                 } else if (byte1 == 0x0e){  // shift out for one byte kana
 198                     previousState = currentState;
 199                     currentState = SHIFTOUT;
 200                     if (readOff + inputSize >= inEnd) {
 201                         noOutput = true;
 202                         break;
 203                     }
 204                     byte1 = input[readOff + inputSize];
 205                     inputSize++;
 206                     if ((byte1 & (byte)0x80) != 0){
 207                         badInputLength = 1;
 208                         throw new MalformedInputException();
 209                     }
 210                 } else if (byte1 == 0x0f){  // shift in for previous mode
 211                     currentState = previousState;
 212                     if (readOff + inputSize >= inEnd) {
 213                         noOutput = true;
 214                         break;
 215                     }
 216                     byte1 = input[readOff + inputSize];
 217                     inputSize++;
 218                     if ((byte1 & (byte)0x80) != 0){
 219                         badInputLength = 1;
 220                         throw new MalformedInputException();
 221                     }
 222                 }
 223             }
 224             if (noOutput || savedSize != 0) {
 225                 byteOff += inputSize;
 226                 break;
 227             }
 228             noOutput = false;
 229             switch (currentState){
 230               case ASCII:
 231                 outputChar = (char)(byte1 & 0xff);
 232                 break;
 233               case JISX0201_1976:
 234                 switch (byte1) {
 235                   case 0x5c:
 236                     outputChar = '\u00a5';
 237                     break;
 238                   case 0x7e:
 239                     outputChar = '\u203e';
 240                     break;
 241                   default:
 242                     outputChar = (char)byte1;
 243                     break;
 244                 }
 245                 break;
 246               case JISX0208_1978:
 247               case JISX0208_1983:
 248                 if (readOff + inputSize >= inEnd) {
 249                     savedSize = 1;
 250                     savedBytes[0] = (byte)byte1;
 251                     break;
 252                 }
 253                 byte2 = input[readOff + inputSize] & 0xff;
 254                 inputSize++;
 255                 if ((byte2 & (byte)0x80) != 0){
 256                     badInputLength = 1;
 257                     throw new MalformedInputException();
 258                 }
 259                 // jisx0208Chars table convert FULLWIDTH_REVERSE_SOLIDUS
 260                 // 0x2140 to REVERSE_SOLIDUS (BACKSLASH) 0x5c.
 261                 // This behavior causes problem because
 262                 // 0x5c is special escape character for java.
 263                 if (byte1 == 0x21 && byte2 == 0x40) {
 264                     outputChar = '\uFF3C';
 265                 } else {
 266                     try {
 267                         outputChar = getUnicode(byte1, byte2);
 268                     } catch (ArrayIndexOutOfBoundsException e) {
 269                         outputChar = '\uFFFD';
 270                     }
 271                 }
 272                 break;
 273               case JISX0201_1976_KANA:
 274               case SHIFTOUT:
 275                 if (byte1 > 0x60) {
 276                     badInputLength = 1;
 277                     throw new MalformedInputException();
 278                 }
 279                 outputChar = (char)(byte1 + 0xff40);
 280                 break;
 281             }
 282 
 283             if (savedSize != 0) {
 284                 byteOff += inputSize;
 285                 break;
 286             }
 287 
 288             if (outputChar == '\uFFFD') {
 289                 if (subMode)
 290                     outputChar = subChars[0];
 291                 else {
 292                     badInputLength = inputSize;
 293                     throw new UnknownCharacterException();
 294                 }
 295             }
 296             readOff += inputSize;
 297             byteOff += inputSize;
 298             output[charOff++] = outputChar;
 299         }
 300 
 301         return charOff - outOff;
 302     }
 303 
 304     public void reset() {
 305         byteOff = charOff = 0;
 306         currentState = ASCII;
 307         savedSize = 0;
 308     }
 309 
 310     public String getCharacterEncoding() {
 311         return "ISO2022JP";
 312     }
 313 }