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 }