177N/A/*
2362N/A * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved.
177N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
177N/A *
177N/A * This code is free software; you can redistribute it and/or modify it
177N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
177N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
177N/A *
177N/A * This code is distributed in the hope that it will be useful, but WITHOUT
177N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
177N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
177N/A * version 2 for more details (a copy is included in the LICENSE file that
177N/A * accompanied this code).
177N/A *
177N/A * You should have received a copy of the GNU General Public License version
177N/A * 2 along with this work; if not, write to the Free Software Foundation,
177N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
177N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
177N/A */
177N/A
177N/Apackage sun.nio.cs;
177N/A
177N/Aimport java.io.InputStream;
177N/Aimport java.io.InputStreamReader;
177N/Aimport java.io.OutputStream;
177N/Aimport java.io.BufferedReader;
177N/Aimport java.io.IOException;
177N/Aimport java.util.regex.Matcher;
177N/Aimport java.util.regex.Pattern;
177N/Aimport java.util.*;
177N/Aimport java.security.*;
177N/A
177N/Apublic class CharsetMapping {
177N/A public final static char UNMAPPABLE_DECODING = '\uFFFD';
354N/A public final static int UNMAPPABLE_ENCODING = 0xFFFD;
177N/A
177N/A char[] b2cSB; //singlebyte b->c
177N/A char[] b2cDB1; //dobulebyte b->c /db1
177N/A char[] b2cDB2; //dobulebyte b->c /db2
177N/A
177N/A int b2Min, b2Max; //min/max(start/end) value of 2nd byte
177N/A int b1MinDB1, b1MaxDB1; //min/Max(start/end) value of 1st byte/db1
177N/A int b1MinDB2, b1MaxDB2; //min/Max(start/end) value of 1st byte/db2
177N/A int dbSegSize;
177N/A
177N/A char[] c2b;
177N/A char[] c2bIndex;
177N/A
177N/A // Supplementary
177N/A char[] b2cSupp;
177N/A char[] c2bSupp;
177N/A
177N/A // Composite
177N/A Entry[] b2cComp;
177N/A Entry[] c2bComp;
177N/A
177N/A public char decodeSingle(int b) {
177N/A return b2cSB[b];
177N/A }
177N/A
177N/A public char decodeDouble(int b1, int b2) {
177N/A if (b2 >= b2Min && b2 < b2Max) {
177N/A b2 -= b2Min;
177N/A if (b1 >= b1MinDB1 && b1 <= b1MaxDB1) {
177N/A b1 -= b1MinDB1;
177N/A return b2cDB1[b1 * dbSegSize + b2];
177N/A }
177N/A if (b1 >= b1MinDB2 && b1 <= b1MaxDB2) {
177N/A b1 -= b1MinDB2;
177N/A return b2cDB2[b1 * dbSegSize + b2];
177N/A }
177N/A }
177N/A return UNMAPPABLE_DECODING;
177N/A }
177N/A
177N/A // for jis0213 all supplementary characters are in 0x2xxxx range,
177N/A // so only the xxxx part is now stored, should actually store the
177N/A // codepoint value instead.
177N/A public char[] decodeSurrogate(int db, char[] cc) {
177N/A int end = b2cSupp.length / 2;
177N/A int i = Arrays.binarySearch(b2cSupp, 0, end, (char)db);
177N/A if (i >= 0) {
177N/A Character.toChars(b2cSupp[end + i] + 0x20000, cc, 0);
177N/A return cc;
177N/A }
177N/A return null;
177N/A }
177N/A
177N/A public char[] decodeComposite(Entry comp, char[] cc) {
177N/A int i = findBytes(b2cComp, comp);
177N/A if (i >= 0) {
177N/A cc[0] = (char)b2cComp[i].cp;
177N/A cc[1] = (char)b2cComp[i].cp2;
177N/A return cc;
177N/A }
177N/A return null;
177N/A }
177N/A
177N/A public int encodeChar(char ch) {
177N/A int index = c2bIndex[ch >> 8];
177N/A if (index == 0xffff)
177N/A return UNMAPPABLE_ENCODING;
177N/A return c2b[index + (ch & 0xff)];
177N/A }
177N/A
177N/A public int encodeSurrogate(char hi, char lo) {
354N/A int cp = Character.toCodePoint(hi, lo);
354N/A if (cp < 0x20000 || cp >= 0x30000)
354N/A return UNMAPPABLE_ENCODING;
177N/A int end = c2bSupp.length / 2;
354N/A int i = Arrays.binarySearch(c2bSupp, 0, end, (char)cp);
177N/A if (i >= 0)
177N/A return c2bSupp[end + i];
177N/A return UNMAPPABLE_ENCODING;
177N/A }
177N/A
177N/A public boolean isCompositeBase(Entry comp) {
177N/A if (comp.cp <= 0x31f7 && comp.cp >= 0xe6) {
177N/A return (findCP(c2bComp, comp) >= 0);
177N/A }
177N/A return false;
177N/A }
177N/A
177N/A public int encodeComposite(Entry comp) {
177N/A int i = findComp(c2bComp, comp);
177N/A if (i >= 0)
177N/A return c2bComp[i].bs;
177N/A return UNMAPPABLE_ENCODING;
177N/A }
177N/A
177N/A // init the CharsetMapping object from the .dat binary file
177N/A public static CharsetMapping get(final InputStream is) {
177N/A return AccessController.doPrivileged(new PrivilegedAction<CharsetMapping>() {
177N/A public CharsetMapping run() {
177N/A return new CharsetMapping().load(is);
177N/A }
177N/A });
177N/A }
177N/A
177N/A public static class Entry {
177N/A public int bs; //byte sequence reps
177N/A public int cp; //Unicode codepoint
177N/A public int cp2; //CC of composite
177N/A }
177N/A
177N/A static Comparator<Entry> comparatorBytes =
177N/A new Comparator<Entry>() {
177N/A public int compare(Entry m1, Entry m2) {
177N/A return m1.bs - m2.bs;
177N/A }
177N/A public boolean equals(Object obj) {
177N/A return this == obj;
177N/A }
177N/A };
177N/A
177N/A static Comparator<Entry> comparatorCP =
177N/A new Comparator<Entry>() {
177N/A public int compare(Entry m1, Entry m2) {
177N/A return m1.cp - m2.cp;
177N/A }
177N/A public boolean equals(Object obj) {
177N/A return this == obj;
177N/A }
177N/A };
177N/A
177N/A static Comparator<Entry> comparatorComp =
177N/A new Comparator<Entry>() {
177N/A public int compare(Entry m1, Entry m2) {
177N/A int v = m1.cp - m2.cp;
177N/A if (v == 0)
177N/A v = m1.cp2 - m2.cp2;
177N/A return v;
177N/A }
177N/A public boolean equals(Object obj) {
177N/A return this == obj;
177N/A }
177N/A };
177N/A
177N/A static int findBytes(Entry[] a, Entry k) {
177N/A return Arrays.binarySearch(a, 0, a.length, k, comparatorBytes);
177N/A }
177N/A
177N/A static int findCP(Entry[] a, Entry k) {
177N/A return Arrays.binarySearch(a, 0, a.length, k, comparatorCP);
177N/A }
177N/A
177N/A static int findComp(Entry[] a, Entry k) {
177N/A return Arrays.binarySearch(a, 0, a.length, k, comparatorComp);
177N/A }
177N/A
177N/A /*****************************************************************************/
177N/A // tags of different charset mapping tables
177N/A private final static int MAP_SINGLEBYTE = 0x1; // 0..256 : c
177N/A private final static int MAP_DOUBLEBYTE1 = 0x2; // min..max: c
177N/A private final static int MAP_DOUBLEBYTE2 = 0x3; // min..max: c [DB2]
177N/A private final static int MAP_SUPPLEMENT = 0x5; // db,c
177N/A private final static int MAP_SUPPLEMENT_C2B = 0x6; // c,db
177N/A private final static int MAP_COMPOSITE = 0x7; // db,base,cc
177N/A private final static int MAP_INDEXC2B = 0x8; // index table of c->bb
177N/A
177N/A private static final boolean readNBytes(InputStream in, byte[] bb, int N)
177N/A throws IOException
177N/A {
177N/A int off = 0;
177N/A while (N > 0) {
177N/A int n = in.read(bb, off, N);
177N/A if (n == -1)
177N/A return false;
177N/A N = N - n;
177N/A off += n;
177N/A }
177N/A return true;
177N/A }
177N/A
177N/A int off = 0;
177N/A byte[] bb;
177N/A private char[] readCharArray() {
177N/A // first 2 bytes are the number of "chars" stored in this table
177N/A int size = ((bb[off++]&0xff)<<8) | (bb[off++]&0xff);
177N/A char [] cc = new char[size];
177N/A for (int i = 0; i < size; i++) {
177N/A cc[i] = (char)(((bb[off++]&0xff)<<8) | (bb[off++]&0xff));
177N/A }
177N/A return cc;
177N/A }
177N/A
177N/A void readSINGLEBYTE() {
177N/A char[] map = readCharArray();
177N/A for (int i = 0; i < map.length; i++) {
177N/A char c = map[i];
177N/A if (c != UNMAPPABLE_DECODING) {
177N/A c2b[c2bIndex[c >> 8] + (c&0xff)] = (char)i;
177N/A }
177N/A }
177N/A b2cSB = map;
177N/A }
177N/A
177N/A void readINDEXC2B() {
177N/A char[] map = readCharArray();
177N/A for (int i = map.length - 1; i >= 0; i--) {
177N/A if (c2b == null && map[i] != -1) {
177N/A c2b = new char[map[i] + 256];
177N/A Arrays.fill(c2b, (char)UNMAPPABLE_ENCODING);
177N/A break;
177N/A }
177N/A }
177N/A c2bIndex = map;
177N/A }
177N/A
177N/A char[] readDB(int b1Min, int b2Min, int segSize) {
177N/A char[] map = readCharArray();
177N/A for (int i = 0; i < map.length; i++) {
177N/A char c = map[i];
177N/A if (c != UNMAPPABLE_DECODING) {
177N/A int b1 = i / segSize;
177N/A int b2 = i % segSize;
177N/A int b = (b1 + b1Min)* 256 + (b2 + b2Min);
177N/A //System.out.printf(" DB %x\t%x%n", b, c & 0xffff);
177N/A c2b[c2bIndex[c >> 8] + (c&0xff)] = (char)(b);
177N/A }
177N/A }
177N/A return map;
177N/A }
177N/A
177N/A void readDOUBLEBYTE1() {
177N/A b1MinDB1 = ((bb[off++]&0xff)<<8) | (bb[off++]&0xff);
177N/A b1MaxDB1 = ((bb[off++]&0xff)<<8) | (bb[off++]&0xff);
177N/A b2Min = ((bb[off++]&0xff)<<8) | (bb[off++]&0xff);
177N/A b2Max = ((bb[off++]&0xff)<<8) | (bb[off++]&0xff);
177N/A dbSegSize = b2Max - b2Min + 1;
177N/A b2cDB1 = readDB(b1MinDB1, b2Min, dbSegSize);
177N/A }
177N/A
177N/A void readDOUBLEBYTE2() {
177N/A b1MinDB2 = ((bb[off++]&0xff)<<8) | (bb[off++]&0xff);
177N/A b1MaxDB2 = ((bb[off++]&0xff)<<8) | (bb[off++]&0xff);
177N/A b2Min = ((bb[off++]&0xff)<<8) | (bb[off++]&0xff);
177N/A b2Max = ((bb[off++]&0xff)<<8) | (bb[off++]&0xff);
177N/A dbSegSize = b2Max - b2Min + 1;
177N/A b2cDB2 = readDB(b1MinDB2, b2Min, dbSegSize);
177N/A }
177N/A
177N/A void readCOMPOSITE() {
177N/A char[] map = readCharArray();
177N/A int mLen = map.length/3;
177N/A b2cComp = new Entry[mLen];
177N/A c2bComp = new Entry[mLen];
177N/A for (int i = 0, j= 0; i < mLen; i++) {
177N/A Entry m = new Entry();
177N/A m.bs = map[j++];
177N/A m.cp = map[j++];
177N/A m.cp2 = map[j++];
177N/A b2cComp[i] = m;
177N/A c2bComp[i] = m;
177N/A }
177N/A Arrays.sort(c2bComp, 0, c2bComp.length, comparatorComp);
177N/A }
177N/A
177N/A CharsetMapping load(InputStream in) {
177N/A try {
177N/A // The first 4 bytes are the size of the total data followed in
177N/A // this .dat file.
177N/A int len = ((in.read()&0xff) << 24) | ((in.read()&0xff) << 16) |
177N/A ((in.read()&0xff) << 8) | (in.read()&0xff);
177N/A bb = new byte[len];
177N/A off = 0;
177N/A //System.out.printf("In : Total=%d%n", len);
177N/A // Read in all bytes
177N/A if (!readNBytes(in, bb, len))
177N/A throw new RuntimeException("Corrupted data file");
177N/A in.close();
177N/A
177N/A while (off < len) {
177N/A int type = ((bb[off++]&0xff)<<8) | (bb[off++]&0xff);
177N/A switch(type) {
177N/A case MAP_INDEXC2B:
177N/A readINDEXC2B();
177N/A break;
177N/A case MAP_SINGLEBYTE:
177N/A readSINGLEBYTE();
177N/A break;
177N/A case MAP_DOUBLEBYTE1:
177N/A readDOUBLEBYTE1();
177N/A break;
177N/A case MAP_DOUBLEBYTE2:
177N/A readDOUBLEBYTE2();
177N/A break;
177N/A case MAP_SUPPLEMENT:
177N/A b2cSupp = readCharArray();
177N/A break;
177N/A case MAP_SUPPLEMENT_C2B:
177N/A c2bSupp = readCharArray();
177N/A break;
177N/A case MAP_COMPOSITE:
177N/A readCOMPOSITE();
177N/A break;
177N/A default:
177N/A throw new RuntimeException("Corrupted data file");
177N/A }
177N/A }
177N/A bb = null;
177N/A return this;
177N/A } catch (IOException x) {
177N/A x.printStackTrace();
177N/A return null;
177N/A }
177N/A }
177N/A}