1 # autoimports script requires -scripting mode
   2 
   3 /*
   4  * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
   5  *
   6  * Redistribution and use in source and binary forms, with or without
   7  * modification, are permitted provided that the following conditions
   8  * are met:
   9  *
  10  *   - Redistributions of source code must retain the above copyright
  11  *     notice, this list of conditions and the following disclaimer.
  12  *
  13  *   - Redistributions in binary form must reproduce the above copyright
  14  *     notice, this list of conditions and the following disclaimer in the
  15  *     documentation and/or other materials provided with the distribution.
  16  *
  17  *   - Neither the name of Oracle nor the names of its
  18  *     contributors may be used to endorse or promote products derived
  19  *     from this software without specific prior written permission.
  20  *
  21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
  22  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
  23  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  24  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  25  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
  26  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
  27  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
  28  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
  29  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
  30  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  31  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  32  */
  33 
  34 /*
  35  * It is tedious to import Java classes used in a script. Sometimes it is easier
  36  * use simple names of java classes and have a script auto import Java classes. 
  37  * You can load this script at the start of an interactive jjs session or at the
  38  * start of your script. This script defines a __noSuchProperty__ hook to auto 
  39  * import Java classes as needed and when they are referred to for the first time
  40  * in your script. You can also call the "autoimports" function to print script 
  41  * statements that you need to use in your script, i.e., have the function generate
  42  * a script to import Java classes used by your script so far. After running your
  43  * script, you can call autoimports to get the exact Java imports you need and replace
  44  * the autoimports load with the generated import statements (to avoid costly init of
  45  * the autoimports script).
  46  *
  47  * Example usage of autoimports.js in interactive mode:
  48  *
  49  *     jjs -scripting autoimports.js -
  50  *     jjs> Vector
  51  *     jjs> [JavaClass java.util.Vector]
  52  */
  53 
  54 (function() {
  55     var ArrayList = Java.type("java.util.ArrayList");
  56     var HashMap = Java.type("java.util.HashMap");
  57     var Files = Java.type("java.nio.file.Files");
  58     var FileSystems = Java.type("java.nio.file.FileSystems");
  59     var URI = Java.type("java.net.URI");
  60 
  61     // initialize a class to package map by iterating all
  62     // classes available in the system by walking through "jrt fs"
  63     var fs = FileSystems.getFileSystem(URI.create("jrt:/"));
  64     var root = fs.getPath('/');
  65 
  66     var clsToPkg = new HashMap();
  67 
  68     function addToClsToPkg(c, p) {
  69         if (clsToPkg.containsKey(c)) {
  70             var val = clsToPkg.get(c);
  71             if (val instanceof ArrayList) {
  72                 val.add(p);
  73             } else {
  74                 var al = new ArrayList();
  75                 al.add(val);
  76                 al.add(p);
  77                 clsToPkg.put(c, al);
  78             }
  79         } else {
  80             clsToPkg.put(c, p);
  81         }
  82     }
  83 
  84     // handle collision and allow user to choose package
  85     function getPkgOfCls(c) {
  86         var val = clsToPkg.get(c);
  87         if (val instanceof ArrayList) {
  88             var count = 1;
  89             print("Multiple matches for " + c + ", choose package:");
  90             for each (var v in val) {
  91                 print(count + ". " + v);
  92                 count++;
  93             }
  94             var choice = parseInt(readLine());
  95             if (isNaN(choice) || choice < 1 || choice > val.size()) {
  96                 print("invalid choice: " + choice);
  97                 return undefined;
  98             }
  99             return val.get(choice - 1);
 100         } else {
 101             return val;
 102         }
 103     }
 104 
 105     Files.walk(root).forEach(function(p) {
 106         if (Files.isRegularFile(p)) {
 107             var str = p.toString();
 108             if (str.endsWith(".class") && !str.endsWith("module-info.class")) {
 109                 str = str.substring("/modules/".length);
 110                 var idx = str.indexOf('/');
 111                 if (idx != -1) {
 112                     str = str.substring(idx + 1);
 113                     if (str.startsWith("java") ||
 114                         str.startsWith("javax") ||
 115                         str.startsWith("org")) {
 116                         var lastIdx = str.lastIndexOf('/');
 117                         if (lastIdx != -1) {
 118                             var pkg = str.substring(0, lastIdx).replaceAll('/', '.');
 119                             var cls = str.substring(lastIdx + 1, str.lastIndexOf(".class"));
 120                             addToClsToPkg(cls, pkg);
 121                         }
 122                     }
 123                 }
 124             }
 125         } 
 126     });
 127 
 128     var imports = new ArrayList();
 129     var global = this;
 130     var oldNoSuchProp = global.__noSuchProperty__;
 131     this.__noSuchProperty__ = function(name) {
 132         'use strict';
 133 
 134         if (clsToPkg.containsKey(name)) {
 135             var pkg = getPkgOfCls(name);
 136             if (pkg) {
 137                 var clsName = pkg + "." + name;
 138                 imports.add("var " + name + " = Java.type('" + clsName + "');");
 139                 return global[name] = Java.type(clsName);
 140             }
 141         } else if (typeof oldNoSuchProp == 'function') {
 142             return oldNoSuchProp.call(this, name);
 143         }
 144 
 145         if (typeof this == 'undefined') {
 146             throw new ReferenceError(name);
 147         } else {
 148             return undefined;
 149         }
 150     }
 151 
 152     this.autoimports = function() {
 153         for each (var im in imports) {
 154             print(im);
 155         }
 156     }
 157 })();