/* * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.awt.motif; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.nio.charset.CoderResult; import java.util.Arrays; public class DoubleByte { public final static char UNMAPPABLE_DECODING = '\uFFFD'; public final static int UNMAPPABLE_ENCODING = 0xFFFD; public final static char[] B2C_UNMAPPABLE; static { B2C_UNMAPPABLE = new char[0x100]; Arrays.fill(B2C_UNMAPPABLE, UNMAPPABLE_DECODING); } public static class Decoder extends CharsetDecoder { final char[][] b2c; final char[] b2cSB; final int b2Min; final int b2Max; // for SimpleEUC override protected CoderResult crMalformedOrUnderFlow(int b) { return CoderResult.UNDERFLOW; } protected CoderResult crMalformedOrUnmappable(int b1, int b2) { if (b2c[b1] == B2C_UNMAPPABLE || // isNotLeadingByte(b1) b2c[b2] != B2C_UNMAPPABLE || // isLeadingByte(b2) decodeSingle(b2) != UNMAPPABLE_DECODING) { // isSingle(b2) return CoderResult.malformedForLength(1); } return CoderResult.unmappableForLength(2); } Decoder(Charset cs, float avgcpb, float maxcpb, char[][] b2c, char[] b2cSB, int b2Min, int b2Max) { super(cs, avgcpb, maxcpb); this.b2c = b2c; this.b2cSB = b2cSB; this.b2Min = b2Min; this.b2Max = b2Max; } Decoder(Charset cs, char[][] b2c, char[] b2cSB, int b2Min, int b2Max) { this(cs, 0.5f, 1.0f, b2c, b2cSB, b2Min, b2Max); } protected CoderResult decodeLoop(ByteBuffer src, CharBuffer dst) { int mark = src.position(); try { while (src.hasRemaining() && dst.hasRemaining()) { int b1 = src.get() & 0xff; char c = b2cSB[b1]; int inSize = 1; if (c == UNMAPPABLE_DECODING) { if (src.remaining() < 1) return crMalformedOrUnderFlow(b1); int b2 = src.get() & 0xff; if (b2 < b2Min || b2 > b2Max || (c = b2c[b1][b2 - b2Min]) == UNMAPPABLE_DECODING) return crMalformedOrUnmappable(b1, b2); inSize++; } dst.put(c); mark += inSize; } return src.hasRemaining()? CoderResult.OVERFLOW : CoderResult.UNDERFLOW; } finally { src.position(mark); } } // decode loops are not using decodeSingle/Double() for performance // reason. public char decodeSingle(int b) { return b2cSB[b]; } public char decodeDouble(int b1, int b2) { if (b1 < 0 || b1 > b2c.length || b2 < b2Min || b2 > b2Max) return UNMAPPABLE_DECODING; return b2c[b1][b2 - b2Min]; } } public static class Encoder extends CharsetEncoder { final int MAX_SINGLEBYTE = 0xff; private final char[] c2b; private final char[] c2bIndex; SurrogateParser sgp; protected Encoder(Charset cs, char[] c2b, char[] c2bIndex) { super(cs, 2.0f, 2.0f); this.c2b = c2b; this.c2bIndex = c2bIndex; } Encoder(Charset cs, float avg, float max, byte[] repl, char[] c2b, char[] c2bIndex) { super(cs, avg, max, repl); this.c2b = c2b; this.c2bIndex = c2bIndex; } public boolean canEncode(char c) { return encodeChar(c) != UNMAPPABLE_ENCODING; } SurrogateParser sgp() { if (sgp == null) sgp = new SurrogateParser(); return sgp; } protected CoderResult encodeLoop(CharBuffer src, ByteBuffer dst) { int mark = src.position(); try { while (src.hasRemaining()) { char c = src.get(); int bb = encodeChar(c); if (bb == UNMAPPABLE_ENCODING) { if (Character.isSurrogate(c)) { if (sgp().parse(c, src) < 0) return sgp.error(); return sgp.unmappableResult(); } return CoderResult.unmappableForLength(1); } if (bb > MAX_SINGLEBYTE) { // DoubleByte if (dst.remaining() < 2) return CoderResult.OVERFLOW; dst.put((byte)(bb >> 8)); dst.put((byte)(bb)); } else { if (dst.remaining() < 1) return CoderResult.OVERFLOW; dst.put((byte)bb); } mark++; } return CoderResult.UNDERFLOW; } finally { src.position(mark); } } protected byte[] repl = replacement(); protected void implReplaceWith(byte[] newReplacement) { repl = newReplacement; } public int encodeChar(char ch) { return c2b[c2bIndex[ch >> 8] + (ch & 0xff)]; } // init the c2b and c2bIndex tables from b2c. static void initC2B(String[] b2c, String b2cSB, String b2cNR, String c2bNR, int b2Min, int b2Max, char[] c2b, char[] c2bIndex) { Arrays.fill(c2b, (char)UNMAPPABLE_ENCODING); int off = 0x100; char[][] b2c_ca = new char[b2c.length][]; char[] b2cSB_ca = null; if (b2cSB != null) b2cSB_ca = b2cSB.toCharArray(); for (int i = 0; i < b2c.length; i++) { if (b2c[i] == null) continue; b2c_ca[i] = b2c[i].toCharArray(); } if (b2cNR != null) { int j = 0; while (j < b2cNR.length()) { char b = b2cNR.charAt(j++); char c = b2cNR.charAt(j++); if (b < 0x100 && b2cSB_ca != null) { if (b2cSB_ca[b] == c) b2cSB_ca[b] = UNMAPPABLE_DECODING; } else { if (b2c_ca[b >> 8][(b & 0xff) - b2Min] == c) b2c_ca[b >> 8][(b & 0xff) - b2Min] = UNMAPPABLE_DECODING; } } } if (b2cSB_ca != null) { // SingleByte for (int b = 0; b < b2cSB_ca.length; b++) { char c = b2cSB_ca[b]; if (c == UNMAPPABLE_DECODING) continue; int index = c2bIndex[c >> 8]; if (index == 0) { index = off; off += 0x100; c2bIndex[c >> 8] = (char)index; } c2b[index + (c & 0xff)] = (char)b; } } for (int b1 = 0; b1 < b2c.length; b1++) { // DoubleByte char[] db = b2c_ca[b1]; if (db == null) continue; for (int b2 = b2Min; b2 <= b2Max; b2++) { char c = db[b2 - b2Min]; if (c == UNMAPPABLE_DECODING) continue; int index = c2bIndex[c >> 8]; if (index == 0) { index = off; off += 0x100; c2bIndex[c >> 8] = (char)index; } c2b[index + (c & 0xff)] = (char)((b1 << 8) | b2); } } if (c2bNR != null) { // add c->b only nr entries for (int i = 0; i < c2bNR.length(); i += 2) { char b = c2bNR.charAt(i); char c = c2bNR.charAt(i + 1); int index = (c >> 8); if (c2bIndex[index] == 0) { c2bIndex[index] = (char)off; off += 0x100; } index = c2bIndex[index] + (c & 0xff); c2b[index] = b; } } } } }