1 /*
   2  * Copyright (c) 2008, 2015, 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 build.tools.charsetmapping;
  27 
  28 import java.io.*;
  29 import java.util.Arrays;
  30 import java.util.ArrayList;
  31 import java.util.Scanner;
  32 import java.util.Formatter;
  33 import java.util.regex.Pattern;
  34 import static build.tools.charsetmapping.Utils.*;
  35 
  36 public class SBCS {
  37 
  38     static Pattern sbmap = Pattern.compile("0x(\\p{XDigit}++)\\s++(?:U\\+|0x)?(\\p{XDigit}++)(?:\\s++#.*)?");
  39 
  40     public static void genClass(Charset cs,
  41                                 String srcDir, String dstDir, String template)
  42         throws Exception
  43     {
  44         String clzName = cs.clzName;
  45         String csName  = cs.csName;
  46         String hisName = cs.hisName;
  47         String pkgName = cs.pkgName;
  48         boolean isASCII = cs.isASCII;
  49 
  50         StringBuilder b2cSB = new StringBuilder();
  51         StringBuilder b2cNRSB = new StringBuilder();
  52         StringBuilder c2bNRSB = new StringBuilder();
  53 
  54         char[] sb = new char[0x100];
  55         char[] c2bIndex = new char[0x100];
  56         int    c2bOff = 0;
  57         Arrays.fill(sb, UNMAPPABLE_DECODING);
  58         Arrays.fill(c2bIndex, UNMAPPABLE_DECODING);
  59 
  60         // (1)read in .map to parse all b->c entries
  61         FileInputStream in = new FileInputStream(
  62                                  new File(srcDir, clzName + ".map"));
  63         Parser p = new Parser(in, sbmap);
  64         Entry  e = null;
  65 
  66         while ((e = p.next()) != null) {
  67             sb[e.bs] = (char)e.cp;
  68             if (c2bIndex[e.cp>>8] == UNMAPPABLE_DECODING) {
  69                 c2bOff += 0x100;
  70                 c2bIndex[e.cp>>8] = 1;
  71             }
  72         }
  73 
  74         Formatter fm = new Formatter(b2cSB);
  75         fm.format("%n");
  76 
  77         // vm -server shows cc[byte + 128] access is much faster than
  78         // cc[byte&0xff] so we output the upper segment first
  79         toString(sb, 0x80, 0x100, fm, "+", true);
  80         toString(sb, 0x00, 0x80,  fm, ";", true);
  81         fm.close();
  82 
  83         // (2)now the .nr file which includes "b->c" non-roundtrip entries
  84         File f = new File(srcDir, clzName + ".nr");
  85         if (f.exists()) {
  86             in = new FileInputStream(f);
  87             fm = new Formatter(b2cNRSB);
  88             p = new Parser(in, sbmap);
  89             e = null;
  90 
  91             fm.format("// remove non-roundtrip entries%n");
  92             fm.format("        b2cMap = b2cTable.toCharArray();%n");
  93             while ((e = p.next()) != null) {
  94                 fm.format("        b2cMap[%d] = UNMAPPABLE_DECODING;%n",
  95                           (e.bs>=0x80)?(e.bs-0x80):(e.bs+0x80));
  96             }
  97             fm.close();
  98         }
  99 
 100         // (3)finally the .c2b file which includes c->b non-roundtrip entries
 101         f = new File(srcDir, clzName + ".c2b");
 102         if (f.exists()) {
 103             in = new FileInputStream(f);
 104             fm = new Formatter(c2bNRSB);
 105             p = new Parser(in, sbmap);
 106             e = null;
 107             ArrayList<Entry> es = new ArrayList<Entry>();
 108             while ((e = p.next()) != null) {
 109                 if (c2bIndex[e.cp>>8] == UNMAPPABLE_DECODING) {
 110                     c2bOff += 0x100;
 111                     c2bIndex[e.cp>>8] = 1;
 112                 }
 113                 es.add(e);
 114             }
 115             fm.format("// non-roundtrip c2b only entries%n");
 116             if (es.size() < 100) {
 117                 fm.format("        c2bNR = new char[%d];%n", es.size() * 2);
 118                 int i = 0;
 119                 for (Entry entry: es) {
 120                     fm.format("        c2bNR[%d] = 0x%x; c2bNR[%d] = 0x%x;%n",
 121                               i++, entry.bs, i++, entry.cp);
 122                 }
 123             } else {
 124                 char[] cc = new char[es.size() * 2];
 125                 int i = 0;
 126                 for (Entry entry: es) {
 127                     cc[i++] = (char)entry.bs;
 128                     cc[i++] = (char)entry.cp;
 129                 }
 130                 fm.format("        c2bNR = (%n");
 131                 toString(cc, 0, i,  fm, ").toCharArray();", false);
 132             }
 133             fm.close();
 134         }
 135 
 136         // (4)it's time to generate the source file
 137         String b2c = b2cSB.toString();
 138         String b2cNR = b2cNRSB.toString();
 139         String c2bNR = c2bNRSB.toString();
 140 
 141         Scanner s = new Scanner(new File(srcDir, template));
 142         PrintStream out = new PrintStream(new FileOutputStream(
 143                               new File(dstDir, clzName + ".java")));
 144 
 145         while (s.hasNextLine()) {
 146             String line = s.nextLine();
 147             int i = line.indexOf("$");
 148             if (i == -1) {
 149                 out.println(line);
 150                 continue;
 151             }
 152             if (line.indexOf("$PACKAGE$", i) != -1) {
 153                 line = line.replace("$PACKAGE$", pkgName);
 154             }
 155             if (line.indexOf("$NAME_CLZ$", i) != -1) {
 156                 line = line.replace("$NAME_CLZ$", clzName);
 157             }
 158             if (line.indexOf("$NAME_CS$", i) != -1) {
 159                 line = line.replace("$NAME_CS$", csName);
 160             }
 161             if (line.indexOf("$NAME_ALIASES$", i) != -1) {
 162                 if ("sun.nio.cs".equals(pkgName))
 163                     line = line.replace("$NAME_ALIASES$",
 164                                         "StandardCharsets.aliases_" + clzName);
 165                 else
 166                     line = line.replace("$NAME_ALIASES$",
 167                                         "ExtendedCharsets.aliasesFor(\"" + csName + "\")");
 168             }
 169             if (line.indexOf("$NAME_HIS$", i) != -1) {
 170                 line = line.replace("$NAME_HIS$", hisName);
 171             }
 172             if (line.indexOf("$CONTAINS$", i) != -1) {
 173                 if (isASCII)
 174                     line = "        return ((cs.name().equals(\"US-ASCII\")) || (cs instanceof " + clzName + "));";
 175                 else
 176                     line = "        return (cs instanceof " + clzName + ");";
 177             }
 178             if (line.indexOf("$B2CTABLE$") != -1) {
 179                 line = line.replace("$B2CTABLE$", b2c);
 180             }
 181             if (line.indexOf("$C2BLENGTH$") != -1) {
 182                 line = line.replace("$C2BLENGTH$", "0x" + Integer.toString(c2bOff, 16));
 183             }
 184             if (line.indexOf("$NONROUNDTRIP_B2C$") != -1) {
 185                 if (b2cNR.length() == 0)
 186                     continue;
 187                 line = line.replace("$NONROUNDTRIP_B2C$", b2cNR);
 188             }
 189 
 190             if (line.indexOf("$NONROUNDTRIP_C2B$") != -1) {
 191                 if (c2bNR.length() == 0)
 192                     continue;
 193                 line = line.replace("$NONROUNDTRIP_C2B$", c2bNR);
 194             }
 195             out.println(line);
 196         }
 197         out.close();
 198     }
 199 
 200     private static void toString(char[] sb, int off, int end,
 201                                  Formatter out, String closure, boolean comment)
 202     {
 203         while (off < end) {
 204             out.format("        \"");
 205             for (int j = 0; j < 8; j++) {
 206                 if (off == end)
 207                     break;
 208                 char c = sb[off++];
 209                 switch (c) {
 210                 case '\b':
 211                     out.format("\\b"); break;
 212                 case '\t':
 213                     out.format("\\t"); break;
 214                 case '\n':
 215                     out.format("\\n"); break;
 216                 case '\f':
 217                     out.format("\\f"); break;
 218                 case '\r':
 219                     out.format("\\r"); break;
 220                 case '\"':
 221                     out.format("\\\""); break;
 222                 case '\'':
 223                     out.format("\\'"); break;
 224                 case '\\':
 225                     out.format("\\\\"); break;
 226                 default:
 227                     out.format("\\u%04X", c & 0xffff);
 228                 }
 229             }
 230             if (comment) {
 231                 if (off == end)
 232                     out.format("\" %s      // 0x%02x - 0x%02x%n",
 233                                closure, off-8, off-1);
 234                 else
 235                     out.format("\" +      // 0x%02x - 0x%02x%n",
 236                                off-8, off-1);
 237             } else {
 238                 if (off == end)
 239                     out.format("\"%s%n", closure);
 240                 else
 241                     out.format("\" +%n");
 242             }
 243         }
 244     }
 245 }