autoimports.js revision 1311:b8ea01a76cbc
1# autoimports script requires -scripting mode
2
3/*
4 * Copyright (c) 2015, 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
48(function() {
49    var ArrayList = Java.type("java.util.ArrayList");
50    var HashMap = Java.type("java.util.HashMap");
51    var Files = Java.type("java.nio.file.Files");
52    var FileSystems = Java.type("java.nio.file.FileSystems");
53    var URI = Java.type("java.net.URI");
54
55    // initialize a class to package map by iterating all
56    // classes available in the system by walking through "jrt fs"
57    var fs = FileSystems.getFileSystem(URI.create("jrt:/"));
58    var root = fs.getPath('/');
59
60    var clsToPkg = new HashMap();
61
62    function addToClsToPkg(c, p) {
63        if (clsToPkg.containsKey(c)) {
64            var val = clsToPkg.get(c);
65            if (val instanceof ArrayList) {
66                val.add(p);
67            } else {
68                var al = new ArrayList();
69                al.add(val);
70                al.add(p);
71                clsToPkg.put(c, al);
72            }
73        } else {
74            clsToPkg.put(c, p);
75        }
76    }
77
78    // handle collision and allow user to choose package
79    function getPkgOfCls(c) {
80        var val = clsToPkg.get(c);
81        if (val instanceof ArrayList) {
82            var count = 1;
83            print("Multiple matches for " + c + ", choose package:");
84            for each (var v in val) {
85                print(count + ". " + v);
86                count++;
87            }
88            var choice = parseInt(readLine());
89            if (isNaN(choice) || choice < 1 || choice > val.size()) {
90                print("invalid choice: " + choice);
91                return undefined;
92            }
93            return val.get(choice - 1);
94        } else {
95            return val;
96        }
97    }
98
99    Files.walk(root).forEach(function(p) {
100        if (Files.isRegularFile(p)) {
101            var str = p.toString();
102            if (str.endsWith(".class")) {
103                str = str.substring(1);
104                var idx = str.indexOf('/');
105                if (idx != -1) {
106                    str = str.substring(idx + 1);
107                    if (str.startsWith("java") ||
108                        str.startsWith("javax") ||
109                        str.startsWith("org")) {
110                        var lastIdx = str.lastIndexOf('/');
111                        if (lastIdx != -1) {
112                            var pkg = str.substring(0, lastIdx).replaceAll('/', '.');
113                            var cls = str.substring(lastIdx + 1, str.lastIndexOf(".class"));
114                            addToClsToPkg(cls, pkg);
115                        }
116                    }
117                }
118            }
119        }
120    });
121
122    var imports = new ArrayList();
123    var global = this;
124    var oldNoSuchProp = global.__noSuchProperty__;
125    this.__noSuchProperty__ = function(name) {
126        'use strict';
127
128        if (clsToPkg.containsKey(name)) {
129            var pkg = getPkgOfCls(name);
130            if (pkg) {
131                var clsName = pkg + "." + name;
132                imports.add("var " + name + " = Java.type('" + clsName + "');");
133                return global[name] = Java.type(clsName);
134            }
135        } else if (typeof oldNoSuchProp == 'function') {
136            return oldNoSuchProp.call(this, name);
137        }
138
139        if (typeof this == 'undefined') {
140            throw new ReferenceError(name);
141        } else {
142            return undefined;
143        }
144    }
145
146    this.autoimports = function() {
147        for each (var im in imports) {
148            print(im);
149        }
150    }
151})();
152