1 /*
   2  * Copyright 2003-2006 Sun Microsystems, Inc.  All Rights Reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any questions.
  24  */
  25 
  26 /*
  27  */
  28 
  29 
  30 import java.nio.ByteBuffer;
  31 import java.nio.CharBuffer;
  32 import java.nio.charset.Charset;
  33 import java.nio.charset.CharsetDecoder;
  34 import java.nio.charset.CoderResult;
  35 
  36 /**
  37  * An abstract base class for subclasses which decode
  38  * IBM double byte ebcdic host encodings such as ibm code
  39  * pages 933, 935, 937,... etc
  40  *
  41  */
  42 
  43 public abstract class DBCS_IBM_EBCDIC_Decoder extends CharsetDecoder
  44 {
  45 
  46     private DBCSDecoderMapping decoderMapping;
  47     protected static final char REPLACE_CHAR='\uFFFD';
  48 
  49     protected String  singleByteToChar;
  50     protected short   index1[];
  51     protected String  index2;
  52     protected int     mask1;
  53     protected int     mask2;
  54     protected int     shift;
  55 
  56     private static final int SBCS = 0;
  57     private static final int DBCS = 1;
  58 
  59     private static final int SO = 0x0e;
  60     private static final int SI = 0x0f;
  61     private int  currentState;
  62 
  63     protected DBCS_IBM_EBCDIC_Decoder(Charset cs) {
  64         super(cs, 0.5f, 1.0f);
  65     }
  66 
  67     protected void implReset() {
  68         currentState = SBCS;
  69     }
  70 
  71     private CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) {
  72         byte[] sa = src.array();
  73         int sp = src.arrayOffset() + src.position();
  74         int sl = src.arrayOffset() + src.limit();
  75         assert (sp <= sl);
  76         sp = (sp <= sl ? sp : sl);
  77         char[] da = dst.array();
  78         int dp = dst.arrayOffset() + dst.position();
  79         int dl = dst.arrayOffset() + dst.limit();
  80         assert (dp <= dl);
  81         dp = (dp <= dl ? dp : dl);
  82 
  83         try {
  84             while (sp < sl) {
  85                 int b1, b2;
  86                 b1 = sa[sp];
  87                 int inputSize = 1;
  88                 int v = 0;
  89                 char outputChar = REPLACE_CHAR;
  90 
  91                 if (b1 < 0)
  92                     b1 += 256;
  93 
  94                 if (b1 == SO) {  // Shift out
  95                     // For SO characters - simply validate the state and if OK
  96                     //    update the state and go to the next byte
  97 
  98                     if (currentState != SBCS)
  99                         return CoderResult.malformedForLength(1);
 100                     else
 101                         currentState = DBCS;
 102                 } else if (b1 == SI) {
 103                     // For SI characters - simply validate the state and if OK
 104                     //    update the state and go to the next byte
 105 
 106                     if (currentState != DBCS) {
 107                         return CoderResult.malformedForLength(1);
 108                     } else {
 109                         currentState = SBCS;
 110                     }
 111                 } else {
 112                     if (currentState == SBCS) {
 113                         outputChar = singleByteToChar.charAt(b1);
 114                     } else {
 115                     if (sl - sp < 2)
 116                         return CoderResult.UNDERFLOW;
 117                     b2 = sa[sp + 1];
 118                     if (b2 < 0)
 119                         b2 += 256;
 120 
 121                     inputSize++;
 122 
 123                     // Check validity of dbcs ebcdic byte pair values
 124                     if ((b1 != 0x40 || b2 != 0x40) &&
 125                       (b2 < 0x41 || b2 > 0xfe)) {
 126                       return CoderResult.malformedForLength(2);
 127                     }
 128 
 129                     // Lookup in the two level index
 130                     v = b1 * 256 + b2;
 131                     outputChar = index2.charAt(index1[((v & mask1) >> shift)]
 132                                                 + (v & mask2));
 133                     }
 134                     if (outputChar == '\uFFFD')
 135                         return CoderResult.unmappableForLength(inputSize);
 136 
 137                     if (dl - dp < 1)
 138                         return CoderResult.OVERFLOW;
 139                     da[dp++] = outputChar;
 140                 }
 141                 sp += inputSize;
 142             }
 143             return CoderResult.UNDERFLOW;
 144         } finally {
 145             src.position(sp - src.arrayOffset());
 146             dst.position(dp - dst.arrayOffset());
 147         }
 148     }
 149 
 150     private CoderResult decodeBufferLoop(ByteBuffer src, CharBuffer dst) {
 151         int mark = src.position();
 152 
 153         try {
 154             while (src.hasRemaining()) {
 155                 int b1, b2;
 156                 int v = 0;
 157                 b1 = src.get();
 158                 int inputSize = 1;
 159                 char outputChar = REPLACE_CHAR;
 160 
 161                 if (b1 < 0)
 162                     b1 += 256;
 163 
 164 
 165                 if (b1 == SO) {  // Shift out
 166                     // For SO characters - simply validate the state and if OK
 167                     //    update the state and go to the next byte
 168 
 169                     if (currentState != SBCS)
 170                         return CoderResult.malformedForLength(1);
 171                     else
 172                         currentState = DBCS;
 173                 } else if (b1 == SI) {
 174                     // For SI characters - simply validate the state and if OK
 175                     //    update the state and go to the next byte
 176 
 177                     if (currentState != DBCS) {
 178                         return CoderResult.malformedForLength(1);
 179                     } else {
 180                         currentState = SBCS;
 181                     }
 182                 } else {
 183                     if (currentState == SBCS) {
 184                       outputChar = singleByteToChar.charAt(b1);
 185                     } else {
 186                         if (src.remaining() < 1)
 187                             return CoderResult.UNDERFLOW;
 188                         b2 = src.get();
 189                         if (b2 < 0)
 190                             b2 += 256;
 191                         inputSize++;
 192 
 193                         // Check validity of dbcs ebcdic byte pair values
 194                         if ((b1 != 0x40 || b2 != 0x40) &&
 195                            (b2 < 0x41 || b2 > 0xfe)) {
 196                           return CoderResult.malformedForLength(2);
 197                         }
 198 
 199                         // Lookup in the two level index
 200                         v = b1 * 256 + b2;
 201                         outputChar = index2.charAt(index1[((v & mask1) >> shift)]
 202                                                             + (v & mask2));
 203                     }
 204                     if (outputChar == REPLACE_CHAR)
 205                         return CoderResult.unmappableForLength(inputSize);
 206 
 207                     if (!dst.hasRemaining())
 208                         return CoderResult.OVERFLOW;
 209                     dst.put(outputChar);
 210                 }
 211                 mark += inputSize;
 212             }
 213             return CoderResult.UNDERFLOW;
 214         } finally {
 215             src.position(mark);
 216         }
 217     }
 218 
 219     protected CoderResult decodeLoop(ByteBuffer src, CharBuffer dst) {
 220         if (src.hasArray() && dst.hasArray())
 221             return decodeArrayLoop(src, dst);
 222         else
 223             return decodeBufferLoop(src, dst);
 224     }
 225 }