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 })();