1 /* 2 * Copyright 2009 Sun Microsystems, Inc. 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. Sun designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 22 * CA 95054 USA or visit www.sun.com if you need additional information or 23 * have any questions. 24 */ 25 26 package build.tools.charsetmapping; 27 import java.io.*; 28 import java.util.Arrays; 29 import java.util.ArrayList; 30 import java.util.Scanner; 31 import java.util.Formatter; 32 import java.util.regex.*; 33 import java.nio.charset.*; 34 import static build.tools.charsetmapping.CharsetMapping.*; 35 36 public class GenerateDBCS { 37 // pattern used by this class to read in mapping table 38 static Pattern mPattern = Pattern.compile("(?:0x)?(\\p{XDigit}++)\\s++(?:0x)?(\\p{XDigit}++)(?:\\s++#.*)?"); 39 public static void genDBCS(String args[]) throws Exception { 40 41 Scanner s = new Scanner(new File(args[0], args[2])); 42 while (s.hasNextLine()) { 43 String line = s.nextLine(); 44 if (line.startsWith("#") || line.length() == 0) 45 continue; 46 String[] fields = line.split("\\s+"); 47 if (fields.length < 10) { 48 System.err.println("Misconfiged sbcs line <" + line + ">?"); 49 continue; 50 } 51 String clzName = fields[0]; 52 String csName = fields[1]; 53 String hisName = ("null".equals(fields[2]))?null:fields[2]; 54 String type = fields[3].toUpperCase(); 55 if ("BASIC".equals(type)) 56 type = ""; 57 else 58 type = "_" + type; 59 String pkgName = fields[4]; 60 boolean isASCII = Boolean.valueOf(fields[5]); 61 int b1Min = toInteger(fields[6]); 62 int b1Max = toInteger(fields[7]); 63 int b2Min = toInteger(fields[8]); 64 int b2Max = toInteger(fields[9]); 65 System.out.printf("%s,%s,%s,%b,%s%n", clzName, csName, hisName, isASCII, pkgName); 66 genClass(args[0], args[1], "DoubleByte-X.java.template", 67 clzName, csName, hisName, pkgName, 68 isASCII, type, 69 b1Min, b1Max, b2Min, b2Max); 70 } 71 } 72 73 private static int toInteger(String s) { 74 if (s.startsWith("0x") || s.startsWith("0X")) 75 return Integer.valueOf(s.substring(2), 16); 76 else 77 return Integer.valueOf(s); 78 } 79 80 private static void outString(Formatter out, 81 char[] cc, int off, int end, 82 String closure) 83 { 84 while (off < end) { 85 out.format(" \""); 86 for (int j = 0; j < 8; j++) { 87 if (off == end) 88 break; 89 char c = cc[off++]; 90 switch (c) { 91 case '\b': 92 out.format("\\b"); break; 93 case '\t': 94 out.format("\\t"); break; 95 case '\n': 96 out.format("\\n"); break; 97 case '\f': 98 out.format("\\f"); break; 99 case '\r': 100 out.format("\\r"); break; 101 case '\"': 102 out.format("\\\""); break; 103 case '\'': 104 out.format("\\'"); break; 105 case '\\': 106 out.format("\\\\"); break; 107 default: 108 out.format("\\u%04X", c & 0xffff); 109 } 110 } 111 if (off == end) 112 out.format("\" %s%n", closure); 113 else 114 out.format("\" + %n"); 115 } 116 } 117 118 private static void outString(Formatter out, 119 char[] db, 120 int b1, 121 int b2Min, int b2Max, 122 String closure) 123 { 124 char[] cc = new char[b2Max - b2Min + 1]; 125 int off = 0; 126 for (int b2 = b2Min; b2 <= b2Max; b2++) { 127 cc[off++] = db[(b1 << 8) | b2]; 128 } 129 outString(out, cc, 0, cc.length, closure); 130 } 131 132 private static void genClass(String srcDir, String dstDir, String template, 133 String clzName, 134 String csName, 135 String hisName, 136 String pkgName, 137 boolean isASCII, 138 String type, 139 int b1Min, int b1Max, 140 int b2Min, int b2Max) 141 throws Exception 142 { 143 144 StringBuilder b2cSB = new StringBuilder(); 145 StringBuilder b2cNRSB = new StringBuilder(); 146 StringBuilder c2bNRSB = new StringBuilder(); 147 148 char[] db = new char[0x10000]; 149 char[] c2bIndex = new char[0x100]; 150 int c2bOff = 0x100; // first 0x100 for unmappable segs 151 152 Arrays.fill(db, UNMAPPABLE_DECODING); 153 Arrays.fill(c2bIndex, UNMAPPABLE_DECODING); 154 155 char[] b2cIndex = new char[0x100]; 156 Arrays.fill(b2cIndex, UNMAPPABLE_DECODING); 157 158 // (1)read in .map to parse all b->c entries 159 FileInputStream in = new FileInputStream(new File(srcDir, clzName + ".map")); 160 Parser p = new Parser(in, mPattern); 161 Entry e = null; 162 while ((e = p.next()) != null) { 163 db[e.bs] = (char)e.cp; 164 165 if (e.bs > 0x100 && // db 166 b2cIndex[e.bs>>8] == UNMAPPABLE_DECODING) { 167 b2cIndex[e.bs>>8] = 1; 168 } 169 170 if (c2bIndex[e.cp>>8] == UNMAPPABLE_DECODING) { 171 c2bOff += 0x100; 172 c2bIndex[e.cp>>8] = 1; 173 } 174 } 175 Formatter fm = new Formatter(b2cSB); 176 fm.format("%n static final String b2cSBStr =%n"); 177 outString(fm, db, 0x00, 0x100, ";"); 178 179 fm.format("%n static final String[] b2cStr = {%n"); 180 for (int i = 0; i < 0x100; i++) { 181 if (b2cIndex[i] == UNMAPPABLE_DECODING) { 182 fm.format(" null,%n"); //unmappable segments 183 } else { 184 outString(fm, db, i, b2Min, b2Max, ","); 185 } 186 } 187 188 fm.format(" };%n"); 189 fm.close(); 190 191 // (2)now parse the .nr file which includes "b->c" non-roundtrip entries 192 File f = new File(srcDir, clzName + ".nr"); 193 if (f.exists()) { 194 StringBuilder sb = new StringBuilder(); 195 in = new FileInputStream(f); 196 p = new Parser(in, mPattern); 197 e = null; 198 while ((e = p.next()) != null) { 199 // A <b,c> pair 200 sb.append((char)e.bs); 201 sb.append((char)e.cp); 202 } 203 char[] nr = sb.toString().toCharArray(); 204 fm = new Formatter(b2cNRSB); 205 fm.format("String b2cNR =%n"); 206 outString(fm, nr, 0, nr.length, ";"); 207 fm.close(); 208 } else { 209 b2cNRSB.append("String b2cNR = null;"); 210 } 211 212 // (3)finally the .c2b file which includes c->b non-roundtrip entries 213 f = new File(srcDir, clzName + ".c2b"); 214 if (f.exists()) { 215 StringBuilder sb = new StringBuilder(); 216 in = new FileInputStream(f); 217 p = new Parser(in, mPattern); 218 e = null; 219 while ((e = p.next()) != null) { 220 // A <b,c> pair 221 if (c2bIndex[e.cp>>8] == UNMAPPABLE_DECODING) { 222 c2bOff += 0x100; 223 c2bIndex[e.cp>>8] = 1; 224 } 225 sb.append((char)e.bs); 226 sb.append((char)e.cp); 227 } 228 char[] nr = sb.toString().toCharArray(); 229 fm = new Formatter(c2bNRSB); 230 fm.format("String c2bNR =%n"); 231 outString(fm, nr, 0, nr.length, ";"); 232 fm.close(); 233 } else { 234 c2bNRSB.append("String c2bNR = null;"); 235 } 236 237 // (4)it's time to generate the source file 238 String b2c = b2cSB.toString(); 239 String b2cNR = b2cNRSB.toString(); 240 String c2bNR = c2bNRSB.toString(); 241 242 Scanner s = new Scanner(new File(srcDir, template)); 243 PrintStream out = new PrintStream(new FileOutputStream( 244 new File(dstDir, clzName + ".java"))); 245 if (hisName == null) 246 hisName = ""; 247 248 while (s.hasNextLine()) { 249 String line = s.nextLine(); 250 if (line.indexOf("$") == -1) { 251 out.println(line); 252 continue; 253 } 254 line = line.replace("$PACKAGE$" , pkgName) 255 .replace("$IMPLEMENTS$", (hisName == null)? 256 "" : "implements HistoricallyNamedCharset") 257 .replace("$NAME_CLZ$", clzName) 258 .replace("$NAME_ALIASES$", 259 "sun.nio.cs".equals(pkgName) ? 260 "StandardCharsets.aliases_" + clzName : 261 "ExtendedCharsets.aliasesFor(\"" + csName + "\")") 262 .replace("$NAME_CS$" , csName) 263 .replace("$CONTAINS$", 264 "MS932".equals(clzName)? 265 "return ((cs.name().equals(\"US-ASCII\")) || (cs instanceof JIS_X_0201) || (cs instanceof " + clzName + "));": 266 (isASCII ? 267 "return ((cs.name().equals(\"US-ASCII\")) || (cs instanceof " + clzName + "));": 268 "return (cs instanceof " + clzName + ");")) 269 .replace("$HISTORICALNAME$", 270 (hisName == null)? "" : 271 " public String historicalName() { return \"" + hisName + "\"; }") 272 .replace("$DECTYPE$", type) 273 .replace("$ENCTYPE$", type) 274 .replace("$B1MIN$" , "0x" + Integer.toString(b1Min, 16)) 275 .replace("$B1MAX$" , "0x" + Integer.toString(b1Max, 16)) 276 .replace("$B2MIN$" , "0x" + Integer.toString(b2Min, 16)) 277 .replace("$B2MAX$" , "0x" + Integer.toString(b2Max, 16)) 278 .replace("$B2C$", b2c) 279 .replace("$C2BLENGTH$", "0x" + Integer.toString(c2bOff, 16)) 280 .replace("$NONROUNDTRIP_B2C$", b2cNR) 281 .replace("$NONROUNDTRIP_C2B$", c2bNR); 282 283 out.println(line); 284 } 285 out.close(); 286 } 287 }