1 /*
   2  * Copyright (c) 2002, 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 sun.nio.cs.HistoricallyNamedCharset;
  35 import sun.nio.cs.Surrogate;
  36 import sun.nio.cs.SingleByte;
  37 import static sun.nio.cs.CharsetMapping.*;
  38 
  39 public class EUC_JP
  40     extends Charset
  41     implements HistoricallyNamedCharset
  42 {
  43     public EUC_JP() {
  44         super("EUC-JP", ExtendedCharsets.aliasesFor("EUC-JP"));
  45     }
  46 
  47     public String historicalName() {
  48         return "EUC_JP";
  49     }
  50 
  51     public boolean contains(Charset cs) {
  52         return ((cs.name().equals("US-ASCII"))
  53                 || (cs instanceof JIS_X_0201)
  54                 || (cs instanceof JIS_X_0208)
  55                 || (cs instanceof JIS_X_0212)
  56                 || (cs instanceof EUC_JP));
  57     }
  58 
  59     public CharsetDecoder newDecoder() {
  60         return new Decoder(this);
  61     }
  62 
  63     public CharsetEncoder newEncoder() {
  64         return new Encoder(this);
  65     }
  66 
  67     static class Decoder extends CharsetDecoder
  68         implements DelegatableDecoder {
  69 
  70         final static SingleByte.Decoder DEC0201 =
  71             (SingleByte.Decoder)new JIS_X_0201().newDecoder();
  72 
  73         final static DoubleByte.Decoder DEC0208 =
  74             (DoubleByte.Decoder)new JIS_X_0208().newDecoder();
  75 
  76         final static DoubleByte.Decoder DEC0212 =
  77             (DoubleByte.Decoder)new JIS_X_0212().newDecoder();
  78 
  79         private final SingleByte.Decoder dec0201;
  80         private final DoubleByte.Decoder dec0208;
  81         private final DoubleByte.Decoder dec0212;
  82 
  83         protected Decoder(Charset cs) {
  84             this(cs, 0.5f, 1.0f, DEC0201, DEC0208, DEC0212);
  85         }
  86 
  87         protected Decoder(Charset cs, float avgCpb, float maxCpb,
  88                           SingleByte.Decoder dec0201,
  89                           DoubleByte.Decoder dec0208,
  90                           DoubleByte.Decoder dec0212) {
  91             super(cs, avgCpb, maxCpb);
  92             this.dec0201 = dec0201;
  93             this.dec0208 = dec0208;
  94             this.dec0212 = dec0212;
  95         }
  96 
  97 
  98         protected char decodeDouble(int byte1, int byte2) {
  99             if (byte1 == 0x8e) {
 100                 if (byte2 < 0x80)
 101                     return UNMAPPABLE_DECODING;
 102                 return dec0201.decode((byte)byte2);
 103             }
 104             return dec0208.decodeDouble(byte1 - 0x80, byte2 - 0x80);
 105         }
 106 
 107         private CoderResult decodeArrayLoop(ByteBuffer src,
 108                                             CharBuffer dst)
 109         {
 110             byte[] sa = src.array();
 111             int sp = src.arrayOffset() + src.position();
 112             int sl = src.arrayOffset() + src.limit();
 113             assert (sp <= sl);
 114             sp = (sp <= sl ? sp : sl);
 115 
 116             char[] da = dst.array();
 117             int dp = dst.arrayOffset() + dst.position();
 118             int dl = dst.arrayOffset() + dst.limit();
 119             assert (dp <= dl);
 120             dp = (dp <= dl ? dp : dl);
 121 
 122             int b1 = 0, b2 = 0;
 123             int inputSize = 0;
 124             char outputChar = UNMAPPABLE_DECODING;
 125             try {
 126                 while (sp < sl) {
 127                     b1 = sa[sp] & 0xff;
 128                     inputSize = 1;
 129 
 130                     if ((b1 & 0x80) == 0) {
 131                         outputChar = (char)b1;
 132                     } else {                        // Multibyte char
 133                         if (b1 == 0x8f) {           // JIS0212
 134                             if (sp + 3 > sl)
 135                                return CoderResult.UNDERFLOW;
 136                             b1 = sa[sp + 1] & 0xff;
 137                             b2 = sa[sp + 2] & 0xff;
 138                             inputSize += 2;
 139                             if (dec0212 == null)    // JIS02012 not supported
 140                                 return CoderResult.unmappableForLength(inputSize);
 141                             outputChar = dec0212.decodeDouble(b1-0x80, b2-0x80);
 142                         } else {                     // JIS0201, JIS0208
 143                             if (sp + 2 > sl)
 144                                return CoderResult.UNDERFLOW;
 145                             b2 = sa[sp + 1] & 0xff;
 146                             inputSize++;
 147                             outputChar = decodeDouble(b1, b2);
 148                         }
 149                     }
 150                     if (outputChar == UNMAPPABLE_DECODING) { // can't be decoded
 151                         return CoderResult.unmappableForLength(inputSize);
 152                     }
 153                     if (dp + 1 > dl)
 154                         return CoderResult.OVERFLOW;
 155                     da[dp++] = outputChar;
 156                     sp += inputSize;
 157                 }
 158                 return CoderResult.UNDERFLOW;
 159             } finally {
 160                 src.position(sp - src.arrayOffset());
 161                 dst.position(dp - dst.arrayOffset());
 162             }
 163         }
 164 
 165         private CoderResult decodeBufferLoop(ByteBuffer src,
 166                                              CharBuffer dst)
 167         {
 168             int mark = src.position();
 169             int b1 = 0, b2 = 0;
 170             int inputSize = 0;
 171             char outputChar = UNMAPPABLE_DECODING;
 172 
 173             try {
 174                 while (src.hasRemaining()) {
 175                     b1 = src.get() & 0xff;
 176                     inputSize = 1;
 177                     if ((b1 & 0x80) == 0) {
 178                         outputChar = (char)b1;
 179                     } else {                         // Multibyte char
 180                         if (b1 == 0x8f) {   // JIS0212
 181                             if (src.remaining() < 2)
 182                                return CoderResult.UNDERFLOW;
 183                             b1 = src.get() & 0xff;
 184                             b2 = src.get() & 0xff;
 185                             inputSize += 2;
 186                             if (dec0212 == null)    // JIS02012 not supported
 187                                 return CoderResult.unmappableForLength(inputSize);
 188                             outputChar = dec0212.decodeDouble(b1-0x80, b2-0x80);
 189                         } else {                     // JIS0201 JIS0208
 190                             if (src.remaining() < 1)
 191                                return CoderResult.UNDERFLOW;
 192                             b2 = src.get() & 0xff;
 193                             inputSize++;
 194                             outputChar = decodeDouble(b1, b2);
 195                         }
 196                     }
 197                     if (outputChar == UNMAPPABLE_DECODING) {
 198                         return CoderResult.unmappableForLength(inputSize);
 199                     }
 200                 if (dst.remaining() < 1)
 201                     return CoderResult.OVERFLOW;
 202                 dst.put(outputChar);
 203                 mark += inputSize;
 204                 }
 205                 return CoderResult.UNDERFLOW;
 206             } finally {
 207                 src.position(mark);
 208             }
 209         }
 210 
 211         // Make some protected methods public for use by JISAutoDetect
 212         public CoderResult decodeLoop(ByteBuffer src, CharBuffer dst) {
 213             if (src.hasArray() && dst.hasArray())
 214                 return decodeArrayLoop(src, dst);
 215             else
 216                 return decodeBufferLoop(src, dst);
 217         }
 218         public void implReset() {
 219             super.implReset();
 220         }
 221         public CoderResult implFlush(CharBuffer out) {
 222             return super.implFlush(out);
 223         }
 224     }
 225 
 226 
 227     static class Encoder extends CharsetEncoder {
 228 
 229         final static SingleByte.Encoder ENC0201 =
 230             (SingleByte.Encoder)new JIS_X_0201().newEncoder();
 231 
 232         final static DoubleByte.Encoder ENC0208 =
 233             (DoubleByte.Encoder)new JIS_X_0208().newEncoder();
 234 
 235         final static DoubleByte.Encoder ENC0212 =
 236             (DoubleByte.Encoder)new JIS_X_0212().newEncoder();
 237 
 238         private final Surrogate.Parser sgp = new Surrogate.Parser();
 239 
 240 
 241         private final SingleByte.Encoder enc0201;
 242         private final DoubleByte.Encoder enc0208;
 243         private final DoubleByte.Encoder enc0212;
 244 
 245         protected Encoder(Charset cs) {
 246             this(cs, 3.0f, 3.0f, ENC0201, ENC0208, ENC0212);
 247         }
 248 
 249         protected Encoder(Charset cs, float avgBpc, float maxBpc,
 250                           SingleByte.Encoder enc0201,
 251                           DoubleByte.Encoder enc0208,
 252                           DoubleByte.Encoder enc0212) {
 253             super(cs, avgBpc, maxBpc);
 254             this.enc0201 = enc0201;
 255             this.enc0208 = enc0208;
 256             this.enc0212 = enc0212;
 257         }
 258 
 259         public boolean canEncode(char c) {
 260             byte[]  encodedBytes = new byte[3];
 261             return encodeSingle(c, encodedBytes) != 0 ||
 262                    encodeDouble(c) != UNMAPPABLE_ENCODING;
 263         }
 264 
 265         protected int encodeSingle(char inputChar, byte[] outputByte) {
 266             int b = enc0201.encode(inputChar);
 267             if (b == UNMAPPABLE_ENCODING)
 268                 return 0;
 269             if (b >= 0 && b < 128) {
 270                 outputByte[0] = (byte)b;
 271                 return 1;
 272             }
 273             outputByte[0] = (byte)0x8e;
 274             outputByte[1] = (byte)b;
 275             return 2;
 276         }
 277 
 278         protected int encodeDouble(char ch) {
 279             int b = enc0208.encodeChar(ch);
 280             if (b != UNMAPPABLE_ENCODING)
 281                 return b + 0x8080;
 282             if (enc0212 != null) {
 283                 b = enc0212.encodeChar(ch);
 284                 if (b != UNMAPPABLE_ENCODING)
 285                     b += 0x8F8080;
 286             }
 287             return b;
 288         }
 289 
 290         private CoderResult encodeArrayLoop(CharBuffer src,
 291                                             ByteBuffer dst)
 292         {
 293             char[] sa = src.array();
 294             int sp = src.arrayOffset() + src.position();
 295             int sl = src.arrayOffset() + src.limit();
 296             assert (sp <= sl);
 297             sp = (sp <= sl ? sp : sl);
 298             byte[] da = dst.array();
 299             int dp = dst.arrayOffset() + dst.position();
 300             int dl = dst.arrayOffset() + dst.limit();
 301             assert (dp <= dl);
 302             dp = (dp <= dl ? dp : dl);
 303 
 304             int outputSize = 0;
 305             byte[]  outputByte;
 306             int     inputSize = 0;                 // Size of input
 307             byte[]  tmpBuf = new byte[3];
 308 
 309             try {
 310                 while (sp < sl) {
 311                     outputByte = tmpBuf;
 312                     char c = sa[sp];
 313                     if (Character.isSurrogate(c)) {
 314                         if (sgp.parse(c, sa, sp, sl) < 0)
 315                             return sgp.error();
 316                         return sgp.unmappableResult();
 317                     }
 318                     outputSize = encodeSingle(c, outputByte);
 319                     if (outputSize == 0) { // DoubleByte
 320                         int ncode = encodeDouble(c);
 321                         if (ncode != UNMAPPABLE_ENCODING) {
 322                             if ((ncode & 0xFF0000) == 0) {
 323                                 outputByte[0] = (byte) ((ncode & 0xff00) >> 8);
 324                                 outputByte[1] = (byte) (ncode & 0xff);
 325                                 outputSize = 2;
 326                             } else {
 327                                 outputByte[0] = (byte) 0x8f;
 328                                 outputByte[1] = (byte) ((ncode & 0xff00) >> 8);
 329                                 outputByte[2] = (byte) (ncode & 0xff);
 330                                 outputSize = 3;
 331                             }
 332                         } else {
 333                             return CoderResult.unmappableForLength(1);
 334                         }
 335                     }
 336                     if (dl - dp < outputSize)
 337                         return CoderResult.OVERFLOW;
 338                     // Put the byte in the output buffer
 339                     for (int i = 0; i < outputSize; i++) {
 340                         da[dp++] = outputByte[i];
 341                     }
 342                     sp++;
 343                 }
 344                 return CoderResult.UNDERFLOW;
 345             } finally {
 346                 src.position(sp - src.arrayOffset());
 347                 dst.position(dp - dst.arrayOffset());
 348             }
 349         }
 350 
 351         private CoderResult encodeBufferLoop(CharBuffer src,
 352                                              ByteBuffer dst)
 353         {
 354             int outputSize = 0;
 355             byte[]  outputByte;
 356             int     inputSize = 0;                 // Size of input
 357             byte[]  tmpBuf = new byte[3];
 358 
 359             int mark = src.position();
 360 
 361             try {
 362                 while (src.hasRemaining()) {
 363                     outputByte = tmpBuf;
 364                     char c = src.get();
 365                     if (Character.isSurrogate(c)) {
 366                         if (sgp.parse(c, src) < 0)
 367                             return sgp.error();
 368                         return sgp.unmappableResult();
 369                     }
 370                     outputSize = encodeSingle(c, outputByte);
 371                     if (outputSize == 0) { // DoubleByte
 372                         int ncode = encodeDouble(c);
 373                         if (ncode != UNMAPPABLE_ENCODING) {
 374                             if ((ncode & 0xFF0000) == 0) {
 375                                 outputByte[0] = (byte) ((ncode & 0xff00) >> 8);
 376                                 outputByte[1] = (byte) (ncode & 0xff);
 377                                 outputSize = 2;
 378                             } else {
 379                                 outputByte[0] = (byte) 0x8f;
 380                                 outputByte[1] = (byte) ((ncode & 0xff00) >> 8);
 381                                 outputByte[2] = (byte) (ncode & 0xff);
 382                                 outputSize = 3;
 383                             }
 384                         } else {
 385                             return CoderResult.unmappableForLength(1);
 386                         }
 387                     }
 388                     if (dst.remaining() < outputSize)
 389                         return CoderResult.OVERFLOW;
 390                     // Put the byte in the output buffer
 391                     for (int i = 0; i < outputSize; i++) {
 392                         dst.put(outputByte[i]);
 393                     }
 394                     mark++;
 395                 }
 396                 return CoderResult.UNDERFLOW;
 397             } finally {
 398                 src.position(mark);
 399             }
 400         }
 401 
 402         protected CoderResult encodeLoop(CharBuffer src,
 403                                          ByteBuffer dst)
 404         {
 405             if (src.hasArray() && dst.hasArray())
 406                 return encodeArrayLoop(src, dst);
 407             else
 408                 return encodeBufferLoop(src, dst);
 409         }
 410     }
 411 }