javaastviewer.js revision 802:235d22ccfd24
1169689Skan#// Usage: jjs -fx javaastviewer.js -- <.java files>
2169689Skan
3169689Skan/*
4169689Skan * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
5169689Skan *
6169689Skan * Redistribution and use in source and binary forms, with or without
7169689Skan * modification, are permitted provided that the following conditions
8169689Skan * are met:
9169689Skan *
10169689Skan *   - Redistributions of source code must retain the above copyright
11169689Skan *     notice, this list of conditions and the following disclaimer.
12169689Skan *
13169689Skan *   - Redistributions in binary form must reproduce the above copyright
14169689Skan *     notice, this list of conditions and the following disclaimer in the
15169689Skan *     documentation and/or other materials provided with the distribution.
16169689Skan *
17169689Skan *   - Neither the name of Oracle nor the names of its
18169689Skan *     contributors may be used to endorse or promote products derived
19169689Skan *     from this software without specific prior written permission.
20169689Skan *
21169689Skan * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
22169689Skan * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
23169689Skan * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24169689Skan * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
25169689Skan * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26169689Skan * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27169689Skan * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28169689Skan * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29169689Skan * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30169689Skan * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31169689Skan * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32169689Skan */
33169689Skan
34169689Skan// This example demonstrates Java subclassing by Java.extend
35169689Skan// and javac Compiler and Tree API. This example also uses
36169689Skan// -fx and javafx TreeView to visualize Java AST as TreeView
37169689Skan
38169689Skanif (!$OPTIONS._fx || arguments.length == 0) {
39169689Skan    print("Usage: jjs -fx javaastviewer.js -- <.java files>");
40169689Skan    exit(1);
41169689Skan}
42169689Skan
43169689Skan// Java types used
44169689Skanvar Enum = Java.type("java.lang.Enum");
45169689Skanvar HashSet  = Java.type("java.util.HashSet");
46169689Skanvar Name = Java.type("javax.lang.model.element.Name");
47169689Skanvar List = Java.type("java.util.List");
48169689Skanvar Set  = Java.type("java.util.Set");
49169689Skanvar SimpleTreeVisitor = Java.type("com.sun.source.util.SimpleTreeVisitor");
50169689Skanvar StringArray = Java.type("java.lang.String[]");
51169689Skanvar ToolProvider = Java.type("javax.tools.ToolProvider");
52169689Skanvar Tree = Java.type("com.sun.source.tree.Tree");
53169689Skan
54169689Skanfunction javaASTToScriptObject(args) {
55169689Skan    // properties ignored (javac implementation class properties) in AST view.
56169689Skan    // may not be exhaustive - any getAbc would become "abc" property or
57169689Skan    // public field becomes a property of same name.
58169689Skan    var ignoredProps = new HashSet();
59169689Skan    for each (var word in
60169689Skan        ['extending', 'implementing', 'init', 'mods', 'clazz', 'defs',
61169689Skan         'expr', 'tag', 'preferredPosition', 'qualid', 'recvparam',
62169689Skan         'restype', 'params', 'startPosition', 'thrown',
63169689Skan         'tree', 'typarams', 'typetag', 'vartype']) {
64169689Skan        ignoredProps.add(word);
65169689Skan    }
66169689Skan
67169689Skan    // get the system compiler tool
68169689Skan    var compiler = ToolProvider.systemJavaCompiler;
69169689Skan
70169689Skan    // get standard file manager
71169689Skan    var fileMgr = compiler.getStandardFileManager(null, null, null);
72169689Skan
73169689Skan    // make a list of compilation unit from command line argument file names
74169689Skan    // Using Java.to convert script array (arguments) to a Java String[]
75169689Skan    var compUnits = fileMgr.getJavaFileObjects(Java.to(args, StringArray));
76169689Skan
77169689Skan    // create a new compilation task
78169689Skan    var task = compiler.getTask(null, fileMgr, null, null, null, compUnits);
79169689Skan
80169689Skan    // subclass SimpleTreeVisitor - converts Java AST node to
81169689Skan    // a simple script object by walking through it
82169689Skan    var ConverterVisitor = Java.extend(SimpleTreeVisitor);
83169689Skan
84169689Skan    var visitor = new ConverterVisitor() {
85169689Skan        // convert java AST node to a friendly script object
86169689Skan        // which can be viewed. Every node ends up in defaultAction
87169689Skan        // method of SimpleTreeVisitor method.
88169689Skan
89169689Skan        defaultAction: function (node, p) {
90169689Skan            var resultObj = {};
91169689Skan            // Nashorn does not iterate properties and methods of Java objects
92169689Skan            // But, we can bind properties of any object (including java objects)
93169689Skan            // to a script object and iterate it!
94169689Skan            var obj = {};
95169689Skan            Object.bindProperties(obj, node);
96169689Skan
97169689Skan            // we don't want every property, method of java object
98169689Skan            for (var prop in obj) {
99169689Skan                var val = obj[prop];
100169689Skan                var type = typeof val;
101169689Skan                // ignore 'method' members
102169689Skan                if (type == 'function' || type == 'undefined') {
103169689Skan                    continue;
104169689Skan                }
105169689Skan
106169689Skan                // ignore properties from Javac implementation
107169689Skan                // classes - hack by name!!
108169689Skan                if (ignoredProps.contains(prop)) {
109169689Skan                    continue;
110169689Skan                }
111169689Skan
112169689Skan                // subtree - recurse it
113169689Skan                if (val instanceof Tree) {
114169689Skan                    resultObj[prop] = visitor.visit(val, p);
115169689Skan                } else if (val instanceof List) {
116169689Skan                    // List of trees - recurse each and make an array
117169689Skan                    var len = val.size();
118169689Skan                    if (len != 0) {
119169689Skan                        var arr = [];
120169689Skan                        for (var j = 0; j < len; j++) {
121169689Skan                            var e = val[j];
122169689Skan                            if (e instanceof Tree) {
123169689Skan                                arr.push(visitor.visit(e, p));
124169689Skan                            }
125169689Skan                        }
126169689Skan                        resultObj[prop] = arr;
127169689Skan                    }
128169689Skan                } else if (val instanceof Set) {
129169689Skan                    // Set - used for modifier flags
130169689Skan                    // make array
131169689Skan                    var len = val.size();
132169689Skan                    if (len != 0) {
133169689Skan                        var arr = [];
134169689Skan                        for each (var e in val) {
135169689Skan                            if (e instanceof Enum || typeof e == 'string') {
136169689Skan                                arr.push(e.toString());
137169689Skan                            }
138169689Skan                        }
139169689Skan                        resultObj[prop] = arr;
140169689Skan                    }
141169689Skan                } else if (val instanceof Enum || val instanceof Name) {
142169689Skan                    // make string for any Enum or Name
143169689Skan                    resultObj[prop] = val.toString();
144169689Skan                } else if (type != 'object') {
145169689Skan                    // primitives 'as is'
146169689Skan                    resultObj[prop] = val;
147169689Skan                }
148169689Skan            }
149169689Skan            return resultObj;
150169689Skan        }
151169689Skan    }
152169689Skan
153169689Skan    // top level object with one property for each compilation unit
154169689Skan    var scriptObj = {};
155169689Skan    for each (var cu in task.parse()) {
156169689Skan        scriptObj[cu.sourceFile.name] = cu.accept(visitor, null);
157169689Skan    }
158169689Skan
159169689Skan    return scriptObj;
160169689Skan}
161169689Skan
162169689Skan// JavaFX classes used
163169689Skanvar StackPane = Java.type("javafx.scene.layout.StackPane");
164169689Skanvar Scene     = Java.type("javafx.scene.Scene");
165169689Skanvar TreeItem  = Java.type("javafx.scene.control.TreeItem");
166169689Skanvar TreeView  = Java.type("javafx.scene.control.TreeView");
167169689Skan
168169689Skan// Create a javafx TreeItem to view a script object
169169689Skanfunction treeItemForObject(obj, name) {
170169689Skan    var item = new TreeItem(name);
171169689Skan    for (var prop in obj) {
172169689Skan       var node = obj[prop];
173169689Skan       if (typeof node == 'object') {
174169689Skan           if (node == null) {
175169689Skan               // skip nulls
176169689Skan               continue;
177169689Skan           }
178169689Skan           var subitem = treeItemForObject(node, prop);
179169689Skan       } else {
180169689Skan           var subitem = new TreeItem(prop + ": " + node);
181169689Skan       }
182169689Skan       item.children.add(subitem);
183169689Skan    }
184169689Skan
185169689Skan    item.expanded = true;
186169689Skan    return item;
187169689Skan}
188169689Skan
189169689Skanvar commandArgs = arguments;
190169689Skan
191169689Skan// JavaFX start method
192169689Skanfunction start(stage) {
193169689Skan    var obj = javaASTToScriptObject(commandArgs);
194169689Skan    stage.title = "Java AST Viewer"
195169689Skan    var rootItem = treeItemForObject(obj, "AST");
196169689Skan    rootItem.expanded = true;
197169689Skan    var tree = new TreeView(rootItem);
198169689Skan    var root = new StackPane();
199169689Skan    root.children.add(tree);
200169689Skan    stage.scene = new Scene(root, 300, 450);
201169689Skan    stage.show();
202169689Skan}
203169689Skan