rt/vm/src/main/java/org/apidesign/vm4brwsr/Main.java
author Jaroslav Tulach <jaroslav.tulach@apidesign.org>
Fri, 09 Jan 2015 20:46:35 +0100
changeset 1762 293838e72201
parent 1604 7665471a56c1
child 1763 647282885e6f
permissions -rw-r--r--
Always write down the JavaScript file in UTF-8 encoding
     1 /**
     2  * Back 2 Browser Bytecode Translator
     3  * Copyright (C) 2012 Jaroslav Tulach <jaroslav.tulach@apidesign.org>
     4  *
     5  * This program is free software: you can redistribute it and/or modify
     6  * it under the terms of the GNU General Public License as published by
     7  * the Free Software Foundation, version 2 of the License.
     8  *
     9  * This program is distributed in the hope that it will be useful,
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12  * GNU General Public License for more details.
    13  *
    14  * You should have received a copy of the GNU General Public License
    15  * along with this program. Look for COPYING file in the top folder.
    16  * If not, see http://opensource.org/licenses/GPL-2.0.
    17  */
    18 package org.apidesign.vm4brwsr;
    19 
    20 import java.io.BufferedWriter;
    21 import java.io.File;
    22 import java.io.FileOutputStream;
    23 import java.io.IOException;
    24 import java.io.OutputStreamWriter;
    25 import java.io.Writer;
    26 import java.net.URI;
    27 import java.net.URISyntaxException;
    28 import java.net.URL;
    29 import java.util.Enumeration;
    30 import java.util.jar.JarEntry;
    31 import java.util.jar.JarFile;
    32 
    33 /** Generator of JavaScript from bytecode of classes on classpath of the VM
    34  * with a Main method.
    35  *
    36  * @author Jaroslav Tulach <jtulach@netbeans.org>
    37  */
    38 final class Main {
    39     private Main() {}
    40     
    41     public static void main(String... args) throws IOException, URISyntaxException {
    42         final String obfuscate = "--obfuscatelevel";
    43         final String extension = "--createextension";
    44 
    45         if (args.length < 2) {
    46             System.err.println("Bck2Brwsr Translator from Java(tm) to JavaScript, (c) Jaroslav Tulach 2012");
    47             System.err.print("Usage: java -cp ... -jar ... [");
    48             System.err.print(obfuscate);
    49             System.err.print(" [");
    50             boolean first = true;
    51             for (ObfuscationLevel l : ObfuscationLevel.values()) {
    52                 if (!first) {
    53                     System.err.print('|');
    54                 }
    55                 System.err.print(l.name());
    56                 first = false;
    57             }
    58             System.err.print("]] [");
    59             System.err.print(extension);
    60             System.err.println("] <file_to_generate_js_code_to> java/lang/Class org/your/App ...");
    61             System.exit(9);
    62         }
    63 
    64         final ClassLoader mainClassLoader = Main.class.getClassLoader();
    65 
    66         ObfuscationLevel obfLevel = ObfuscationLevel.NONE;
    67         boolean createExtension = false;
    68         StringArray classes = new StringArray();
    69         String generateTo = null;
    70         for (int i = 0; i < args.length; i++) {
    71             if (obfuscate.equals(args[i])) { // NOI18N
    72                 i++;
    73                 try {
    74                     obfLevel = ObfuscationLevel.valueOf(args[i]);
    75                 } catch (Exception e) {
    76                     System.err.print(obfuscate);
    77                     System.err.print(" parameter needs to be followed by one of ");
    78                     boolean first = true;
    79                     for (ObfuscationLevel l : ObfuscationLevel.values()) {
    80                         if (!first) {
    81                             System.err.print(", ");
    82                         }
    83                         System.err.print(l.name());
    84                         first = false;
    85                     }
    86                     System.err.println();
    87                     System.exit(1);
    88                 }
    89                 continue;
    90             }
    91             if (extension.equals(args[i])) { // NOI18N
    92                 createExtension = true;
    93                 continue;
    94             }
    95             if (generateTo == null) {
    96                 generateTo = args[i];
    97             } else {
    98                 collectClasses(classes, mainClassLoader, args[i]);
    99             }
   100         }
   101         
   102         File gt = new File(generateTo);
   103         if (Boolean.getBoolean("skip.if.exists") && gt.isFile()) {
   104             System.err.println("Skipping as " + gt + " exists.");
   105             System.exit(0);
   106         }
   107         
   108         try (Writer w = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(gt), "UTF-8"))) {
   109             Bck2Brwsr c = Bck2Brwsr.newCompiler().
   110                 obfuscation(obfLevel).
   111                 addRootClasses(classes.toArray()).
   112                 resources(new LdrRsrcs(Main.class.getClassLoader(), true));
   113             
   114             if (createExtension) {
   115                 c = c.library();
   116             }
   117             
   118             c.generate(w);
   119         }
   120     }
   121 
   122     private static void collectClasses(
   123             final StringArray dest,
   124             final ClassLoader cl, final String relativePath)
   125                 throws IOException, URISyntaxException {
   126         final Enumeration<URL> urls = cl.getResources(relativePath);
   127         if (!urls.hasMoreElements()) {
   128             dest.add(relativePath);
   129             return;
   130         }
   131         do {
   132             final URL url = urls.nextElement();
   133             switch (url.getProtocol()) {
   134                 case "file":
   135                     collectClasses(dest, relativePath,
   136                                    new File(new URI(url.toString())));
   137                     continue;
   138                 case "jar":
   139                     final String fullPath = url.getPath();
   140                     final int sepIndex = fullPath.indexOf('!');
   141                     final String jarFilePath =
   142                             (sepIndex != -1) ? fullPath.substring(0, sepIndex)
   143                                              : fullPath;
   144 
   145                     final URI jarUri = new URI(jarFilePath);
   146                     if (jarUri.getScheme().equals("file")) {
   147                         try (JarFile jarFile = new JarFile(new File(jarUri))) {
   148                             collectClasses(dest, relativePath, jarFile);
   149                             continue;
   150                         }
   151                     }
   152                     break;
   153             }
   154 
   155             dest.add(relativePath);
   156         } while (urls.hasMoreElements());
   157     }
   158 
   159     private static void collectClasses(final StringArray dest,
   160                                        final String relativePath,
   161                                        final File file) {
   162         if (file.isDirectory()) {
   163             final File[] subFiles = file.listFiles();
   164             for (final File subFile: subFiles) {
   165                 collectClasses(dest,
   166                                extendPath(relativePath, subFile.getName()),
   167                                subFile);
   168             }
   169 
   170             return;
   171         }
   172 
   173         final String filePath = file.getPath();
   174         if (filePath.endsWith(".class")) {
   175             validateAndAddClass(dest, relativePath);
   176         }
   177     }
   178 
   179     private static void collectClasses(final StringArray dest,
   180                                        final String relativePath,
   181                                        final JarFile jarFile) {
   182         if (relativePath.endsWith(".class")) {
   183             if (jarFile.getJarEntry(relativePath) != null) {
   184                 validateAndAddClass(dest, relativePath);
   185             }
   186 
   187             return;
   188         }
   189 
   190         final String expectedPrefix =
   191                 relativePath.endsWith("/") ? relativePath
   192                                            : relativePath + '/';
   193         final Enumeration<JarEntry> entries = jarFile.entries();
   194         while (entries.hasMoreElements()) {
   195             final JarEntry entry = entries.nextElement();
   196             if (!entry.isDirectory()) {
   197                 final String entryName = entry.getName();
   198                 if (entryName.startsWith(expectedPrefix)
   199                         && entryName.endsWith(".class")) {
   200                     validateAndAddClass(dest, entryName);
   201                 }
   202             }
   203         }
   204     }
   205 
   206     private static String extendPath(final String relativePath,
   207                                      final String fileName) {
   208         return relativePath.endsWith("/") ? relativePath + fileName
   209                                           : relativePath + '/' + fileName;
   210     }
   211 
   212     private static void validateAndAddClass(final StringArray dest,
   213                                             final String relativePath) {
   214         final String className =
   215                 relativePath.substring(0, relativePath.length() - 6);
   216         if (!className.endsWith("package-info")) {
   217             dest.add(className);
   218         }
   219     }
   220 }