1 /*
   2  * Copyright (c) 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.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.util.Arrays;
  35 import sun.nio.cs.Surrogate;
  36 import static sun.nio.cs.CharsetMapping.*;
  37 
  38 public class HKSCS {
  39 
  40     public static class Decoder extends DoubleByte.Decoder {
  41         static int b2Min = 0x40;
  42         static int b2Max = 0xfe;
  43 
  44         private char[][] b2cBmp;
  45         private char[][] b2cSupp;
  46         private DoubleByte.Decoder big5Dec;
  47 
  48         protected Decoder(Charset cs,
  49                           DoubleByte.Decoder big5Dec,
  50                           char[][] b2cBmp, char[][] b2cSupp)
  51         {
  52             // super(cs, 0.5f, 1.0f);
  53             // need to extends DoubleByte.Decoder so the
  54             // sun.io can use it. this implementation
  55             super(cs, 0.5f, 1.0f, null, null, 0, 0);
  56             this.big5Dec = big5Dec;
  57             this.b2cBmp = b2cBmp;
  58             this.b2cSupp = b2cSupp;
  59         }
  60 
  61         public char decodeSingle(int b) {
  62             return big5Dec.decodeSingle(b);
  63         }
  64 
  65         public char decodeBig5(int b1, int b2) {
  66             return big5Dec.decodeDouble(b1, b2);
  67         }
  68 
  69         public char decodeDouble(int b1, int b2) {
  70             return b2cBmp[b1][b2 - b2Min];
  71         }
  72 
  73         public char decodeDoubleEx(int b1, int b2) {
  74             /* if the b2cSupp is null, the subclass need
  75                to override the methold
  76             if (b2cSupp == null)
  77                 return UNMAPPABLE_DECODING;
  78              */
  79             return b2cSupp[b1][b2 - b2Min];
  80         }
  81 
  82         protected CoderResult decodeArrayLoop(ByteBuffer src, CharBuffer dst) {
  83             byte[] sa = src.array();
  84             int sp = src.arrayOffset() + src.position();
  85             int sl = src.arrayOffset() + src.limit();
  86 
  87             char[] da = dst.array();
  88             int dp = dst.arrayOffset() + dst.position();
  89             int dl = dst.arrayOffset() + dst.limit();
  90 
  91             try {
  92                 while (sp < sl) {
  93                     int b1 = sa[sp] & 0xff;
  94                     char c = decodeSingle(b1);
  95                     int inSize = 1, outSize = 1;
  96                     char[] cc = null;
  97                     if (c == UNMAPPABLE_DECODING) {
  98                         if (sl - sp < 2)
  99                             return CoderResult.UNDERFLOW;
 100                         int b2 = sa[sp + 1] & 0xff;
 101                         inSize++;
 102                         if (b2 < b2Min || b2 > b2Max)
 103                             return CoderResult.unmappableForLength(2);
 104                         c = decodeDouble(b1, b2);           //bmp
 105                         if (c == UNMAPPABLE_DECODING) {
 106                             c = decodeDoubleEx(b1, b2);     //supp
 107                             if (c == UNMAPPABLE_DECODING) {
 108                                 c = decodeBig5(b1, b2);     //big5
 109                                 if (c == UNMAPPABLE_DECODING)
 110                                     return CoderResult.unmappableForLength(2);
 111                             } else {
 112                                 // supplementary character in u+2xxxx area
 113                                 outSize = 2;
 114                             }
 115                         }
 116                     }
 117                     if (dl - dp < outSize)
 118                         return CoderResult.OVERFLOW;
 119                     if (outSize == 2) {
 120                         // supplementary characters
 121                         da[dp++] = Surrogate.high(0x20000 + c);
 122                         da[dp++] = Surrogate.low(0x20000 + c);
 123                     } else {
 124                         da[dp++] = c;
 125                     }
 126                     sp += inSize;
 127                 }
 128                 return CoderResult.UNDERFLOW;
 129             } finally {
 130                 src.position(sp - src.arrayOffset());
 131                 dst.position(dp - dst.arrayOffset());
 132             }
 133         }
 134 
 135         protected CoderResult decodeBufferLoop(ByteBuffer src, CharBuffer dst) {
 136             int mark = src.position();
 137             try {
 138                 while (src.hasRemaining()) {
 139                     char[] cc = null;
 140                     int b1 = src.get() & 0xff;
 141                     int inSize = 1, outSize = 1;
 142                     char c = decodeSingle(b1);
 143                     if (c == UNMAPPABLE_DECODING) {
 144                         if (src.remaining() < 1)
 145                             return CoderResult.UNDERFLOW;
 146                         int b2 = src.get() & 0xff;
 147                         inSize++;
 148                         if (b2 < b2Min || b2 > b2Max)
 149                             return CoderResult.unmappableForLength(2);
 150                         c = decodeDouble(b1, b2);           //bmp
 151                         if (c == UNMAPPABLE_DECODING) {
 152                             c = decodeDoubleEx(b1, b2);     //supp
 153                             if (c == UNMAPPABLE_DECODING) {
 154                                 c = decodeBig5(b1, b2);     //big5
 155                                 if (c == UNMAPPABLE_DECODING)
 156                                     return CoderResult.unmappableForLength(2);
 157                             } else {
 158                                 outSize = 2;
 159                             }
 160                         }
 161                     }
 162                     if (dst.remaining() < outSize)
 163                         return CoderResult.OVERFLOW;
 164                     if (outSize == 2) {
 165                         dst.put(Surrogate.high(0x20000 + c));
 166                         dst.put(Surrogate.low(0x20000 + c));
 167                     } else {
 168                         dst.put(c);
 169                     }
 170                     mark += inSize;
 171                 }
 172                 return CoderResult.UNDERFLOW;
 173             } finally {
 174                 src.position(mark);
 175             }
 176         }
 177 
 178         public CoderResult decodeLoop(ByteBuffer src, CharBuffer dst) {
 179             if (src.hasArray() && dst.hasArray())
 180                 return decodeArrayLoop(src, dst);
 181             else
 182                 return decodeBufferLoop(src, dst);
 183         }
 184 
 185         static void initb2c(char[][]b2c, String[] b2cStr)
 186         {
 187             for (int i = 0; i < b2cStr.length; i++) {
 188                 if (b2cStr[i] == null)
 189                     b2c[i] = DoubleByte.B2C_UNMAPPABLE;
 190                 else
 191                     b2c[i] = b2cStr[i].toCharArray();
 192             }
 193         }
 194 
 195     }
 196 
 197     public static class Encoder extends DoubleByte.Encoder {
 198         private DoubleByte.Encoder big5Enc;
 199         private char[][] c2bBmp;
 200         private char[][] c2bSupp;
 201 
 202         protected Encoder(Charset cs,
 203                           DoubleByte.Encoder big5Enc,
 204                           char[][] c2bBmp,
 205                           char[][] c2bSupp)
 206         {
 207             super(cs, null, null);
 208             this.big5Enc = big5Enc;
 209             this.c2bBmp = c2bBmp;
 210             this.c2bSupp = c2bSupp;
 211         }
 212 
 213         public int encodeBig5(char ch) {
 214             return big5Enc.encodeChar(ch);
 215         }
 216 
 217         public int encodeChar(char ch) {
 218             int bb = c2bBmp[ch >> 8][ch & 0xff];
 219             if (bb == UNMAPPABLE_ENCODING)
 220                 return encodeBig5(ch);
 221             return bb;
 222         }
 223 
 224         public int encodeSupp(int cp) {
 225             if ((cp & 0xf0000) != 0x20000)
 226                 return UNMAPPABLE_ENCODING;
 227             return c2bSupp[(cp >> 8) & 0xff][cp & 0xff];
 228         }
 229 
 230         public boolean canEncode(char c) {
 231             return encodeChar(c) != UNMAPPABLE_ENCODING;
 232         }
 233 
 234         protected CoderResult encodeArrayLoop(CharBuffer src, ByteBuffer dst) {
 235             char[] sa = src.array();
 236             int sp = src.arrayOffset() + src.position();
 237             int sl = src.arrayOffset() + src.limit();
 238 
 239             byte[] da = dst.array();
 240             int dp = dst.arrayOffset() + dst.position();
 241             int dl = dst.arrayOffset() + dst.limit();
 242 
 243             try {
 244                 while (sp < sl) {
 245                     char c = sa[sp];
 246                     int inSize = 1;
 247                     int bb = encodeChar(c);
 248                     if (bb == UNMAPPABLE_ENCODING) {
 249                         if (Character.isSurrogate(c)) {
 250                             int cp;
 251                             if ((cp = sgp().parse(c, sa, sp, sl)) < 0)
 252                                 return sgp.error();
 253                             bb = encodeSupp(cp);
 254                             if (bb == UNMAPPABLE_ENCODING)
 255                                 return CoderResult.unmappableForLength(2);
 256                             inSize = 2;
 257                         } else {
 258                             return CoderResult.unmappableForLength(1);
 259                         }
 260                     }
 261                     if (bb > MAX_SINGLEBYTE) {    // DoubleByte
 262                         if (dl - dp < 2)
 263                             return CoderResult.OVERFLOW;
 264                         da[dp++] = (byte)(bb >> 8);
 265                         da[dp++] = (byte)bb;
 266                     } else {                      // SingleByte
 267                         if (dl - dp < 1)
 268                             return CoderResult.OVERFLOW;
 269                         da[dp++] = (byte)bb;
 270                     }
 271                     sp += inSize;
 272                 }
 273                 return CoderResult.UNDERFLOW;
 274             } finally {
 275                 src.position(sp - src.arrayOffset());
 276                 dst.position(dp - dst.arrayOffset());
 277             }
 278         }
 279 
 280         protected CoderResult encodeBufferLoop(CharBuffer src, ByteBuffer dst) {
 281             int mark = src.position();
 282             try {
 283                 while (src.hasRemaining()) {
 284                     int inSize = 1;
 285                     char c = src.get();
 286                     int bb = encodeChar(c);
 287                     if (bb == UNMAPPABLE_ENCODING) {
 288                         if (Character.isSurrogate(c)) {
 289                             int cp;
 290                             if ((cp = sgp().parse(c, src)) < 0)
 291                                 return sgp.error();
 292                             bb = encodeSupp(cp);
 293                             if (bb == UNMAPPABLE_ENCODING)
 294                                 return CoderResult.unmappableForLength(2);
 295                             inSize = 2;
 296                         } else {
 297                             return CoderResult.unmappableForLength(1);
 298                         }
 299                     }
 300                     if (bb > MAX_SINGLEBYTE) {  // DoubleByte
 301                         if (dst.remaining() < 2)
 302                             return CoderResult.OVERFLOW;
 303                         dst.put((byte)(bb >> 8));
 304                         dst.put((byte)(bb));
 305                     } else {
 306                         if (dst.remaining() < 1)
 307                         return CoderResult.OVERFLOW;
 308                         dst.put((byte)bb);
 309                     }
 310                     mark += inSize;
 311                 }
 312                 return CoderResult.UNDERFLOW;
 313             } finally {
 314                 src.position(mark);
 315             }
 316         }
 317 
 318         protected CoderResult encodeLoop(CharBuffer src, ByteBuffer dst) {
 319             if (src.hasArray() && dst.hasArray())
 320                 return encodeArrayLoop(src, dst);
 321             else
 322                 return encodeBufferLoop(src, dst);
 323         }
 324 
 325         static char[] C2B_UNMAPPABLE = new char[0x100];
 326         static {
 327             Arrays.fill(C2B_UNMAPPABLE, (char)UNMAPPABLE_ENCODING);
 328         }
 329 
 330        static void initc2b(char[][] c2b, String[] b2cStr, String pua) {
 331             // init c2b/c2bSupp from b2cStr and supp
 332             int b2Min = 0x40;
 333             Arrays.fill(c2b, C2B_UNMAPPABLE);
 334             for (int b1 = 0; b1 < 0x100; b1++) {
 335                 String s = b2cStr[b1];
 336                 if (s == null)
 337                     continue;
 338                 for (int i = 0; i < s.length(); i++) {
 339                     char c = s.charAt(i);
 340                     int hi = c >> 8;
 341                     if (c2b[hi] == C2B_UNMAPPABLE) {
 342                         c2b[hi] = new char[0x100];
 343                         Arrays.fill(c2b[hi], (char)UNMAPPABLE_ENCODING);
 344                     }
 345                     c2b[hi][c & 0xff] = (char)((b1 << 8) | (i + b2Min));
 346                 }
 347             }
 348             if (pua != null) {        // add the compatibility pua entries
 349                 char c = '\ue000';    //first pua character
 350                 for (int i = 0; i < pua.length(); i++) {
 351                     char bb = pua.charAt(i);
 352                     if (bb != UNMAPPABLE_DECODING) {
 353                         int hi = c >> 8;
 354                         if (c2b[hi] == C2B_UNMAPPABLE) {
 355                             c2b[hi] = new char[0x100];
 356                             Arrays.fill(c2b[hi], (char)UNMAPPABLE_ENCODING);
 357                         }
 358                         c2b[hi][c & 0xff] = bb;
 359                     }
 360                     c++;
 361                 }
 362             }
 363         }
 364     }
 365 }