projectName(@Nullable Project project) {
160 | if (project == null) {
161 | return Optional.empty();
162 | }
163 | return Optional.of(project.getName());
164 | }
165 |
166 |
167 | public void trace(@NotNull String messageContent) {
168 | this.trace(null, messageContent);
169 | }
170 |
171 |
172 | public void trace(@Nullable Project project) {
173 | StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[2];
174 |
175 | String location = stackTraceElement.getFileName() + ":" + stackTraceElement.getLineNumber();
176 | String methodName = stackTraceElement.getMethodName();
177 | String message = location + " (#" + methodName + ")";
178 |
179 | this.trace(project, message);
180 | }
181 |
182 |
183 | public void trace(@Nullable Project project, @NotNull String messageContent) {
184 | this.logger.trace(messageContent);
185 | this.notificationGroup_trace
186 | .createNotification("TRACE", this.getSubtitle(project), messageContent, NotificationType.INFORMATION)
187 | .notify(project);
188 | }
189 |
190 |
191 | public void traceEnter() {
192 | this.traceEnter(null);
193 | }
194 |
195 |
196 | public void traceEnter(@Nullable Project project) {
197 | StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[2];
198 |
199 | String location = stackTraceElement.getFileName() + ":" + stackTraceElement.getLineNumber();
200 | String methodName = stackTraceElement.getMethodName();
201 |
202 | this.traceEnter(project, methodName, location);
203 | }
204 |
205 |
206 | public void traceEnter(@Nullable Project project, @NotNull String methodName, @NotNull String location) {
207 | // String messageContent = location + " (#" + methodName + ")";
208 | String locationString = "#" + methodName + " @ " + location + "";
209 |
210 | this.logger.trace(locationString);
211 | this.notificationGroup_trace
212 | .createNotification("TRACE ENTER", locationString, this.getSubtitle(project), NotificationType.INFORMATION)
213 | .notify(project);
214 | }
215 |
216 |
217 | public void warn(@NotNull String messageContent) {
218 | this.warn(null, messageContent);
219 | }
220 |
221 |
222 | public void warn(@Nullable Project project, @NotNull String messageContent) {
223 | this.logger.warn(messageContent);
224 | this.notificationGroup_warn
225 | .createNotification("WARNING", this.getSubtitle(project), messageContent, NotificationType.WARNING)
226 | .notify(project);
227 | }
228 |
229 |
230 | public void warn(@NotNull String messageContent, Throwable e) {
231 | this.warn(null, messageContent, e);
232 | }
233 |
234 |
235 | public void warn(@Nullable Project project, @NotNull String messageContent, Throwable e) {
236 | this.logger.warn(messageContent, e);
237 | this.notificationGroup_warn
238 | .createNotification("WARNING", this.getSubtitle(project), messageContent, NotificationType.WARNING)
239 | .notify(project);
240 | }
241 |
242 | }
243 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/printers/ASCIITreePrinter.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.printers;
2 |
3 | import com.github.javaparser.Position;
4 | import com.github.javaparser.ast.Node;
5 | import com.github.javaparser.printer.configuration.DefaultConfigurationOption;
6 | import com.github.javaparser.printer.configuration.DefaultPrinterConfiguration;
7 | import com.github.javaparser.printer.configuration.PrinterConfiguration;
8 |
9 | import java.io.BufferedReader;
10 | import java.io.IOException;
11 | import java.io.StringReader;
12 | import java.util.ArrayList;
13 | import java.util.Collections;
14 | import java.util.Iterator;
15 | import java.util.List;
16 | import java.util.function.Function;
17 |
18 | /**
19 | * ASCII printable text tree (of the Nodes within an AST) i.e.
20 | *
21 | * ASCIITreePrinter.print(StaticJavaParser.parseExpression( "new Object(){\n int i;\n}") );
22 | *
23 | * prints:
24 | *
25 | * "new Object() {...}" ObjectCreationExpr : (1,1)-(3,1)
26 | * ├─"Object" ClassOrInterfaceType : (1,5)-(1,10)
27 | * │ └─"Object" SimpleName : (1,5)-(1,10)
28 | * └─"int i;" FieldDeclaration : (2,5)-(2,10)
29 | * └─"i" VariableDeclarator : (2,9)-(2,9)
30 | * ├─"int" PrimitiveType : (2,5)-(2,7)
31 | * └─"i" SimpleName : (2,9)-(2,9)
32 | *
33 | *
34 | * ...for
35 | *
36 | * new Object(){
37 | * int i;
38 | * }
39 | *
40 | *
41 | * NOTE: this is handy for printing a summary of the {@link Node}s in the AST and how they are connected in the tree
42 | * for debugging what the structure of the AST is (directly in the console).
43 | *
44 | * The POINT of this tool is to have a "quick and dirty" way of "sanity checking" that the structure of the AST is.
45 | *
46 | * modified from:
47 | * https://stackoverflow.com/questions/4965335/how-to-print-binary-tree-diagram
48 | *
49 | * @author Eric DeFazio
50 | */
51 | public class ASCIITreePrinter implements NodePrinter {
52 |
53 |
54 | private static final int DEFAULT_STRINGBUILDER_CAPACITY = 5000;
55 |
56 | private static final String NEWLINE = String.format("%n");
57 |
58 | /**
59 | * The ASCIITreePrinter doesn't do comments by design
60 | */
61 | private static final PrinterConfiguration PRINT_NO_COMMENTS = new DefaultPrinterConfiguration()
62 | .removeOption(new DefaultConfigurationOption(DefaultPrinterConfiguration.ConfigOption.PRINT_COMMENTS))
63 | .removeOption(new DefaultConfigurationOption(DefaultPrinterConfiguration.ConfigOption.PRINT_JAVADOC));
64 |
65 | /**
66 | * Print format each {@link Node} in the tree (prints to a single line) for example:
67 | *
68 | * CompilationUnit (1,1)-(15,3) : "@Deprecated...}"
69 | * \____________/ \__________/ : \_______________/
70 | * node class node range : node summary
71 | *
72 | *
73 | * @see ASCIITreePrinter#printNodeSummary(Node)
74 | * @see ASCIITreePrinter#printRange(Node)
75 | * @see ASCIITreePrinter#printRange(Node)
76 | */
77 |
78 | public static final Function CLASS_RANGE_SUMMARY_FORMAT = n -> n.getClass().getSimpleName() + " " + printRangeCoordinates(n) + " : \"" + printNodeSummary(n) + "\"";
79 |
80 | /**
81 | * Print format each {@link Node} in the tree (prints to a single line) for example:
82 | *
83 | * "@Deprecated...}" CompilationUnit : (1,1)-(15,3)
84 | * \_______________/ \____________/ \__________/
85 | * node summary node class node range
86 | *
87 | *
88 | * @see #printNodeSummary(Node)
89 | * @see #printRange(Node)
90 | */
91 | public static final Function SUMMARY_CLASS_RANGE_FORMAT = n -> "\"" + printNodeSummary(n) + "\" " + n.getClass().getSimpleName() + " : " + printRangeCoordinates(n);
92 |
93 | /**
94 | * Print format each {@link Node} in the tree (prints to a single line) for example:
95 | *
96 | * "@Deprecated...}" [CompilationUnit]
97 | * \_______________/ \_______________/
98 | * node summary node class
99 | *
100 | *
101 | * @see #printNodeSummary(Node)
102 | */
103 | public static final Function SUMMARY_CLASS_FORMAT = n -> "\"" + printNodeSummary(n) + "\" [" + n.getClass().getSimpleName() + "]";
104 |
105 | /**
106 | * DEFAULT format for printing each node (on a single line in the tree)
107 | */
108 | public Function nodeFormat = SUMMARY_CLASS_RANGE_FORMAT;
109 |
110 |
111 | /**
112 | * Create an ASCIITreePrinter with the default format
113 | */
114 | public ASCIITreePrinter() {
115 | this(SUMMARY_CLASS_RANGE_FORMAT);
116 | }
117 |
118 |
119 | /**
120 | * An ASCIITreePrinter with a specified nodeFormat for formatting each node
121 | *
122 | * @param nodeFormat
123 | */
124 | public ASCIITreePrinter(Function nodeFormat) {
125 | this.nodeFormat = nodeFormat;
126 | }
127 |
128 |
129 | /**
130 | * Breaks the single String into an array of String Lines
131 | *
132 | * @param inputString a single String
133 | * @return a collection of strings, which is inputString split by newlines
134 | */
135 | public static List lines(String inputString) {
136 | if (inputString == null) {
137 | return Collections.emptyList();
138 | }
139 |
140 | List strLine = new ArrayList<>();
141 | try (BufferedReader br = new BufferedReader(new StringReader(inputString))) {
142 | String line = br.readLine();
143 | while (line != null) {
144 | strLine.add(line);
145 | line = br.readLine();
146 | }
147 | } catch (IOException e) {
148 | //this shouldnt happen
149 | throw new RuntimeException("Error formatting Lines", e);
150 | }
151 |
152 | return strLine;
153 | }
154 |
155 |
156 | /**
157 | * Print a Tree to System.out defining the contents with the AST node
158 | *
159 | * @param rootNode the root
160 | * @param nodeFormat
161 | */
162 | public static void print(Node rootNode, Function nodeFormat) {
163 | System.out.println(TNode.of(new TNode(rootNode)).output(nodeFormat));
164 | }
165 |
166 |
167 | /**
168 | * Print a Tree to System.out defining the contents with the AST node
169 | *
170 | * @param rootNode any AST node to describe the contents of in tree form
171 | */
172 | public static void print(Node rootNode) {
173 | System.out.println(TNode.of(new TNode(rootNode)).output(SUMMARY_CLASS_RANGE_FORMAT));
174 | }
175 |
176 |
177 | /**
178 | * Prints an abbreviated view of a AST node (as to keep the content all on one line)
179 | * in the event the text is truncated, appends "..." and the last non-empty character
180 | * on the last non-empty line of the node. i.e. for this ObjectCreationExpr AST which spans multiple lines:
181 | *
182 | * new Object(){
183 | * int i=0;
184 | * }
185 | *
186 | * prints:
187 | * "new Object() {...}"
188 | *
189 | * @param n
190 | * @return
191 | */
192 | public static String printNodeSummary(Node n) {
193 | String s = n.toString(PRINT_NO_COMMENTS).trim();
194 | if (s.isEmpty()) {
195 | return ""; //this happens, sometimes we have UnknownType (for Lambda) with NO text
196 | }
197 | List lines = lines(s);
198 | if (lines.get(lines.size() - 1).isEmpty()) {
199 | lines.remove(lines.size() - 1);
200 | }
201 | if (lines.size() == 1) {
202 | return lines.get(0); //its all on one line
203 | }
204 | String lastLine = lines.get(Math.max(lines.size() - 1, 0));
205 | //returns the first line, then "..." then the last character on the last line; usually ( '}', ';' or ')' )
206 | return lines.get(0) + "..." + lastLine.charAt(lastLine.length() - 1);
207 | }
208 |
209 |
210 | public static String printPosition(Position p) {
211 | return "(" + p.line + "," + p.column + ")";
212 | }
213 |
214 |
215 | /**
216 | * Tries to print the Range of the Node n, if the Range is not present
217 | * (which happens in UnknownType of Lambda for instance) prints (-)
218 | */
219 | public static String printRange(Node n) {
220 | if (n.getRange().isPresent()) {
221 | return n.getRange().get().toString();
222 | }
223 | //this sometimes happens (i.e. a Unknown type AST node has no text and no range)
224 | return "(-)";
225 | }
226 |
227 |
228 | /**
229 | * Prints range coordinates with (line,column)-(line,column) i.e.
230 | * (1,1)-(5,1) = line 1, column 1, to line 5 column 1
231 | *
232 | * @param n the node to print
233 | * @return String representing the line,column range coordinates
234 | */
235 | public static String printRangeCoordinates(Node n) {
236 | if (n.getRange().isPresent()) {
237 | return printPosition(n.getRange().get().begin) + "-" + printPosition(n.getRange().get().end);
238 | }
239 | //this sometimes happens (i.e. a Unknown type AST node has no text and no range)
240 | return "(-)";
241 | }
242 |
243 |
244 | /**
245 | * Build the output as a String and return it
246 | *
247 | * @param rootNode the top AST {@link Node} to print the contents of
248 | * @return a String representing an ASCII tree
249 | */
250 | @Override
251 | public String output(Node rootNode) {
252 | return TNode.of(new TNode(rootNode)).output(this.nodeFormat);
253 | }
254 |
255 |
256 | /**
257 | * @param rootNode
258 | * @param nodeFormat how to print out each node
259 | * @return a String representing an ASCII tree
260 | */
261 | public String output(Node rootNode, Function nodeFormat) {
262 | return TNode.of(new TNode(rootNode)).output(nodeFormat);
263 | }
264 |
265 |
266 | /**
267 | * Change the Node Format for printing the contents of each node to a line
268 | *
269 | * @param nodeFormat
270 | * @return
271 | */
272 | public ASCIITreePrinter setNodeFormat(Function nodeFormat) {
273 | this.nodeFormat = nodeFormat;
274 | return this;
275 | }
276 |
277 |
278 | @Override
279 | public String toString() {
280 | return "ASCIITreePrinter{" +
281 | "nodeFormat=" + this.nodeFormat +
282 | '}';
283 | }
284 |
285 |
286 | /**
287 | * Underlying Nodes that will print out
288 | */
289 | private static class TNode {
290 | final Node node;
291 | public List children = new ArrayList<>();
292 |
293 |
294 | /**
295 | * Build a ROOT TNode that can contain children
296 | *
297 | * @param rootNode
298 | */
299 | public TNode(Node rootNode) {
300 | this.node = rootNode;
301 | }
302 |
303 |
304 | /**
305 | * builds and returns a TNode and resolves
306 | *
307 | * @param tn
308 | * @return
309 | */
310 | public static TNode of(TNode tn) {
311 | tn.node.stream(Node.TreeTraversal.DIRECT_CHILDREN).forEach(c -> {
312 | TNode child = new TNode(c);
313 | tn.children.add(child);
314 | of(child);
315 | });
316 | return tn;
317 | }
318 |
319 |
320 | /**
321 | * Builds the Ascii tree into the buffer
322 | *
323 | * @param nodeStringFunction
324 | * @param buffer
325 | * @param prefix
326 | * @param childrenPrefix
327 | */
328 | private void build(Function nodeStringFunction, StringBuilder buffer, String prefix, String childrenPrefix) {
329 | buffer.append(prefix);
330 | buffer.append(nodeStringFunction.apply(this.node));
331 | buffer.append(NEWLINE);
332 | for (Iterator it = this.children.iterator(); it.hasNext(); ) {
333 | TNode next = it.next();
334 | /* this is the more "open" format
335 | if (it.hasNext()) {
336 | next.build(nodeStringFunction, buffer, childrenPrefix + "├── ", childrenPrefix + "│ ");
337 | } else {
338 | next.build(nodeStringFunction, buffer, childrenPrefix + "└── ", childrenPrefix + " ");
339 | }
340 | */
341 | // this is the "dense"/compact format
342 | if (it.hasNext()) {
343 | next.build(nodeStringFunction, buffer, childrenPrefix + "├─", childrenPrefix + "│ ");
344 | } else {
345 | next.build(nodeStringFunction, buffer, childrenPrefix + "└─", childrenPrefix + " ");
346 | }
347 | }
348 | }
349 |
350 |
351 | public String output(Function nodeToStringFunction) {
352 | StringBuilder buffer = new StringBuilder(DEFAULT_STRINGBUILDER_CAPACITY);
353 | this.build(nodeToStringFunction, buffer, "", "");
354 | return buffer.toString();
355 | }
356 | }
357 | }
358 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/printers/CustomDotPrinter.java:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | * Copyright (C) 2007-2010 Júlio Vilmar Gesser.
4 | * Copyright (C) 2011, 2013-2016 The JavaParser Team.
5 | *
6 | * This file is part of JavaParser.
7 | *
8 | * JavaParser can be used either under the terms of
9 | * a) the GNU Lesser General Public License as published by
10 | * the Free Software Foundation, either version 3 of the License, or
11 | * (at your option) any later version.
12 | * b) the terms of the Apache License
13 | *
14 | * You should have received a copy of both licenses in LICENCE.LGPL and
15 | * LICENCE.APACHE. Please refer to those files for details.
16 | *
17 | * JavaParser is distributed in the hope that it will be useful,
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 | * GNU Lesser General Public License for more details.
21 | */
22 |
23 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.printers;
24 |
25 |
26 | import com.github.javaparser.Range;
27 | import com.github.javaparser.ast.Node;
28 | import com.github.javaparser.ast.NodeList;
29 | import com.github.javaparser.ast.expr.Expression;
30 | import com.github.javaparser.metamodel.NodeMetaModel;
31 | import com.github.javaparser.metamodel.PropertyMetaModel;
32 | import com.github.javaparser.resolution.UnsolvedSymbolException;
33 | import com.github.javaparser.resolution.types.ResolvedType;
34 | import org.apache.commons.text.StringEscapeUtils;
35 |
36 | import java.util.List;
37 |
38 | import static com.github.javaparser.utils.Utils.assertNotNull;
39 | import static java.util.stream.Collectors.toList;
40 |
41 | /**
42 | * Outputs a Graphviz diagram of the AST.
43 | */
44 | public class CustomDotPrinter implements NodePrinter {
45 |
46 | private static final int DEFAULT_STRINGBUILDER_CAPACITY = 5000;
47 |
48 | private static final boolean DEFAULT_RESOLVE_TYPES = false;
49 |
50 | private final boolean outputNodeType;
51 | private int nodeCount;
52 |
53 |
54 | public CustomDotPrinter(final boolean outputNodeType) {
55 | this.outputNodeType = outputNodeType;
56 | this.nodeCount = 0;
57 | }
58 |
59 |
60 | private static String escape(String value) {
61 | return value.replace("\"", "\\\"");
62 | }
63 |
64 |
65 | private String nextNodeName() {
66 | return "n" + (this.nodeCount++);
67 | }
68 |
69 |
70 | /**
71 | * @param node The node to be printed - typically a CompilationUnit.
72 | * @param resolveTypes Should node types be resolved?
73 | * @return The DOT-formatted equivalent of node.
74 | */
75 | public String output(final Node node, final boolean resolveTypes) {
76 | this.nodeCount = 0;
77 | final StringBuilder output = new StringBuilder(DEFAULT_STRINGBUILDER_CAPACITY);
78 | output.append("digraph {");
79 | this.output(node, null, "root", output, resolveTypes);
80 | output.append(System.lineSeparator()).append("}");
81 | return output.toString();
82 | }
83 |
84 |
85 | public void output(final Node node, final String parentNodeName, final String name, final StringBuilder builder) {
86 | this.output(node, parentNodeName, name, builder, DEFAULT_RESOLVE_TYPES);
87 | }
88 |
89 |
90 | public void output(final Node node, final String parentNodeName, final String name, final StringBuilder builder, final boolean resolveTypes) {
91 | assertNotNull(node);
92 | final NodeMetaModel metaModel = node.getMetaModel();
93 | final List allPropertyMetaModels = metaModel.getAllPropertyMetaModels();
94 | final List attributes = allPropertyMetaModels.stream().filter(PropertyMetaModel::isAttribute).filter(PropertyMetaModel::isSingular).collect(toList());
95 | final List subNodes = allPropertyMetaModels.stream().filter(PropertyMetaModel::isNode).filter(PropertyMetaModel::isSingular).collect(toList());
96 | final List subLists = allPropertyMetaModels.stream().filter(PropertyMetaModel::isNodeList).collect(toList());
97 |
98 | final String typeName = metaModel.getTypeName();
99 | String range = "";
100 |
101 | // Custom: If range is present, add it.
102 | if (node.getRange().isPresent()) {
103 | range += "";
104 | range += this.rangeAsString(node.getRange().get());
105 | range += "";
106 | }
107 |
108 |
109 | final String lineColor;
110 | final String lineLabel;
111 | if ("comment".equals(name)) {
112 | // lineColor = "gray";
113 | lineColor = "LightGray";
114 | // lineLabel = "comment";
115 | lineLabel = "";
116 | } else if ("name".equals(name)) {
117 | // lineColor="darkgreen";
118 | // lineColor="blue";
119 | // lineColor="SlateBlue";
120 | lineColor = "SteelBlue";
121 | // lineLabel = "name";
122 | lineLabel = "";
123 | } else if ("StringLiteralExpr".equals(typeName)) {
124 | // } else if (typeName.endsWith("LiteralExpr")) {
125 | // lineColor="SlateBlue";
126 | lineColor = "SeaGreen";
127 | lineLabel = "Literal Expression";
128 | // lineLabel = "LiteralExpr";
129 | } else {
130 | lineColor = "black";
131 | lineLabel = "";
132 | }
133 |
134 | final String ndName = this.nextNodeName();
135 | StringBuilder nodeDot = new StringBuilder(DEFAULT_STRINGBUILDER_CAPACITY);
136 | nodeDot.append(System.lineSeparator());
137 | nodeDot.append(ndName);
138 | nodeDot.append(" [");
139 | nodeDot.append("shape=none");
140 | nodeDot.append(",");
141 | nodeDot.append("label=<");
142 |
143 | nodeDot.append("");
144 |
145 | nodeDot.append("");
146 | nodeDot.append("");
147 | nodeDot.append("");
148 | nodeDot.append("");
149 | nodeDot.append(escape(name));
150 | if (this.outputNodeType) {
151 | nodeDot.append(" (").append(typeName).append(")");
152 | }
153 | nodeDot.append("");
154 | nodeDot.append(" ");
155 | nodeDot.append("");
156 | nodeDot.append(range);
157 | nodeDot.append("");
158 |
159 |
160 | if (resolveTypes && node instanceof Expression) {
161 | final Expression bar = (Expression) node;
162 |
163 | String returnTypeString = null;
164 |
165 | try {
166 | // if (!bar.toString().equals("System") && !bar.toString().equals("String")) {
167 | ResolvedType returnType = bar.calculateResolvedType();
168 | returnTypeString = StringEscapeUtils.escapeHtml4(returnType.describe());
169 | // }
170 | } catch (final UnsolvedSymbolException e) {
171 | // returnTypeString = "Unable to resolve type of " + bar + " (UnsolvedSymbolException)";
172 | System.err.println("Unable to resolve type of " + bar + " (UnsolvedSymbolException)");
173 | e.printStackTrace();
174 | } catch (final Exception e) {
175 | // returnTypeString = "Unable to resolve type of " + bar + " (Exception - " + e.getClass().getName() + ")";
176 | System.err.println("Unable to resolve type of " + bar + " (Exception - " + e.getClass().getName() + ")");
177 | e.printStackTrace();
178 | }
179 |
180 | if (returnTypeString != null) {
181 | nodeDot.append(" ");
182 | nodeDot.append("");
183 | nodeDot.append("Resolved Type: ");
184 | nodeDot.append(returnTypeString);
185 | nodeDot.append("");
186 | }
187 | }
188 |
189 | nodeDot.append(" | ");
190 | nodeDot.append("
");
191 |
192 | for (final PropertyMetaModel a : attributes) {
193 | nodeDot.append("");
194 | nodeDot.append("").append(a.getName()).append(" | ");
195 | nodeDot.append("");
196 |
197 | String value = a.getValue(node).toString();
198 | String[] lines = value.trim().split("\\r?\\n");
199 |
200 | String cellAlignment = lines.length > 1 ? "left" : "center";
201 | nodeDot.append("");
202 | for (final String line : lines) {
203 | nodeDot.append("").append(StringEscapeUtils.escapeHtml4(line)).append(" | ");
204 | }
205 | nodeDot.append(" ");
206 |
207 | nodeDot.append(" | ");
208 | nodeDot.append("
");
209 | }
210 |
211 | nodeDot.append("
");
212 |
213 | nodeDot.append("");
214 | nodeDot.append(">];");
215 |
216 | builder.append(nodeDot.toString());
217 |
218 |
219 | if (parentNodeName != null) {
220 | builder.append(System.lineSeparator())
221 | .append(parentNodeName).append(" -> ").append(ndName)
222 | .append(" [").append("color=").append(lineColor).append(", fontcolor=").append(lineColor).append(", label=\"").append(lineLabel).append("\"").append("]")
223 | .append(";");
224 | }
225 |
226 | for (final PropertyMetaModel sn : subNodes) {
227 | final Node nd = (Node) sn.getValue(node);
228 | if (nd != null) {
229 | this.output(nd, ndName, sn.getName(), builder, resolveTypes);
230 | }
231 | }
232 |
233 | String color;
234 | String label;
235 |
236 | for (final PropertyMetaModel sl : subLists) {
237 | final NodeList extends Node> nl = (NodeList extends Node>) sl.getValue(node);
238 | if (nl != null && nl.isNonEmpty()) {
239 | // color = "FireBrick";
240 | // color = "red";
241 | color = "OrangeRed";
242 | label = "property list";
243 |
244 | final String ndLstName = this.nextNodeName();
245 | builder.append(System.lineSeparator()).append(ndLstName).append(" [shape=ellipse,color=").append(color).append(",label=\"").append(escape(sl.getName())).append("\"];");
246 | builder.append(System.lineSeparator()).append(ndName).append(" -> ")
247 | .append(ndLstName)
248 | .append(" [").append("color=").append(color).append(", fontcolor=").append(color).append(", label=\"").append(label).append("\"").append("]");
249 | // .append(" [color = ").append(color).append("];");
250 | final String slName = sl.getName().substring(0, sl.getName().length() - 1);
251 | for (final Node nd : nl) {
252 | this.output(nd, ndLstName, slName, builder, resolveTypes);
253 | }
254 | }
255 | }
256 | }
257 |
258 |
259 | @Override
260 | public String output(final Node node) {
261 | return this.output(node, DEFAULT_RESOLVE_TYPES);
262 | }
263 |
264 |
265 | private String rangeAsString(final Range range) {
266 | final int startLine = range.begin.line;
267 | final int startColumn = range.begin.column;
268 | final int endLine = range.end.line;
269 | final int endColumn = range.end.column;
270 |
271 | return "[" + startLine + ":" + startColumn + "-" + endLine + ":" + endColumn + "]";
272 | }
273 |
274 |
275 | @Override
276 | public String toString() {
277 | return "CustomDotPrinter{" +
278 | "outputNodeType=" + this.outputNodeType +
279 | ", nodeCount =" + this.nodeCount +
280 | '}';
281 | }
282 | }
283 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/printers/CustomJsonPrinter.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.printers;
2 |
3 | import com.github.javaparser.ast.Node;
4 | import com.github.javaparser.ast.NodeList;
5 | import com.github.javaparser.ast.expr.ObjectCreationExpr;
6 | import com.github.javaparser.metamodel.NodeMetaModel;
7 | import com.github.javaparser.metamodel.PropertyMetaModel;
8 |
9 | import java.util.ArrayList;
10 | import java.util.List;
11 | import java.util.stream.Collectors;
12 |
13 | import static com.github.javaparser.utils.Utils.assertNotNull;
14 | import static java.util.stream.Collectors.toList;
15 |
16 | /**
17 | * Outputs a JSON file containing the AST meant for inspecting it.
18 | */
19 | public class CustomJsonPrinter implements NodePrinter {
20 | private final boolean outputNodeType;
21 |
22 |
23 | public CustomJsonPrinter(final boolean outputNodeType) {
24 | this.outputNodeType = outputNodeType;
25 | }
26 |
27 |
28 | private static String q(final String value) {
29 | return "\"" + value.replace("\"", "\\\"").replace("\n", "\\n").replace("\r", "\\r") + "\"";
30 | }
31 |
32 |
33 | @Override
34 | public String output(final Node node) {
35 | return this.output(node, null, 0);
36 | }
37 |
38 |
39 | public String output(final Node node, final String name, final int level) {
40 | assertNotNull(node);
41 | final NodeMetaModel metaModel = node.getMetaModel();
42 | final List allPropertyMetaModels = metaModel.getAllPropertyMetaModels();
43 | final List attributes = allPropertyMetaModels.stream().filter(PropertyMetaModel::isAttribute).filter(PropertyMetaModel::isSingular).collect(toList());
44 | final List subNodes = allPropertyMetaModels.stream().filter(PropertyMetaModel::isNode).filter(PropertyMetaModel::isSingular).collect(toList());
45 | final List subLists = allPropertyMetaModels.stream().filter(PropertyMetaModel::isNodeList).collect(toList());
46 |
47 | final List content = new ArrayList<>();
48 |
49 | if (this.outputNodeType) {
50 | content.add(CustomJsonPrinter.q("_type") + ":" + CustomJsonPrinter.q(metaModel.getTypeName()));
51 | }
52 |
53 | for (final PropertyMetaModel attributeMetaModel : attributes) {
54 | content.add(CustomJsonPrinter.q(attributeMetaModel.getName()) + ":" + CustomJsonPrinter.q(attributeMetaModel.getValue(node).toString()));
55 | }
56 |
57 |
58 | // Custom: If range is present, add it.
59 | if (node.getRange().isPresent()) {
60 | content.add(CustomJsonPrinter.q("_start_line") + ":" + node.getRange().get().begin.line);
61 | content.add(CustomJsonPrinter.q("_start_column") + ":" + node.getRange().get().begin.column);
62 | content.add(CustomJsonPrinter.q("_end_line") + ":" + node.getRange().get().end.line);
63 | content.add(CustomJsonPrinter.q("_end_column") + ":" + node.getRange().get().end.column);
64 | }
65 |
66 | // Object creation
67 | if (node.getClass().getSimpleName().equals("ObjectCreationExpr")) {
68 | final ObjectCreationExpr objectCreationExpr = (ObjectCreationExpr) node;
69 | final String foo = objectCreationExpr.getType().getName().asString();
70 | content.add(CustomJsonPrinter.q("_typeNameString") + ":" + CustomJsonPrinter.q(foo));
71 | }
72 |
73 |
74 | for (final PropertyMetaModel subNodeMetaModel : subNodes) {
75 | final Node value = (Node) subNodeMetaModel.getValue(node);
76 | if (value != null) {
77 | content.add(this.output(value, subNodeMetaModel.getName(), level + 1));
78 | }
79 | }
80 |
81 | for (final PropertyMetaModel subListMetaModel : subLists) {
82 | final NodeList extends Node> subList = (NodeList extends Node>) subListMetaModel.getValue(node);
83 | if (subList != null && !subList.isEmpty()) {
84 | final List listContent = new ArrayList<>();
85 | for (final Node subListNode : subList) {
86 | listContent.add(this.output(subListNode, null, level + 1));
87 | }
88 | content.add(listContent.stream().collect(Collectors.joining(",", CustomJsonPrinter.q(subListMetaModel.getName()) + ":[", "]")));
89 | }
90 | }
91 |
92 | if (name == null) {
93 | return content.stream().collect(Collectors.joining(",", "{", "}"));
94 | }
95 | return content.stream().collect(Collectors.joining(",", CustomJsonPrinter.q(name) + ":{", "}"));
96 | }
97 |
98 |
99 | @Override
100 | public String toString() {
101 | return "CustomJsonPrinter{" +
102 | "outputNodeType=" + this.outputNodeType +
103 | '}';
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/printers/CypherPrinter.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.printers;
2 |
3 | import com.github.javaparser.ast.Node;
4 | import com.github.javaparser.ast.NodeList;
5 | import com.github.javaparser.metamodel.NodeMetaModel;
6 | import com.github.javaparser.metamodel.PropertyMetaModel;
7 | import com.github.javaparser.utils.LineSeparator;
8 |
9 | import java.util.HashSet;
10 | import java.util.List;
11 | import java.util.Set;
12 |
13 | import static com.github.javaparser.utils.Utils.assertNotNull;
14 | import static java.util.stream.Collectors.toList;
15 |
16 | public class CypherPrinter implements NodePrinter {
17 |
18 |
19 | private static final int DEFAULT_STRINGBUILDER_CAPACITY = 5000;
20 |
21 | private static final String EOL = LineSeparator.SYSTEM.asRawString();
22 |
23 | private final Set currentIds;
24 | private final boolean outputNodeType;
25 | private int nodeCount;
26 |
27 |
28 | public CypherPrinter(boolean outputNodeType) {
29 | this.outputNodeType = outputNodeType;
30 | this.currentIds = new HashSet<>();
31 | }
32 |
33 |
34 | private static String escapeQuotes(String value) {
35 | return value.replace("'", "\\'");
36 | }
37 |
38 |
39 | private String nextNodeName() {
40 | return "n" + (this.nodeCount++);
41 | }
42 |
43 |
44 | public void output(Node node, String parentNodeName, String name, StringBuilder builder) {
45 | assertNotNull(node);
46 |
47 |
48 | NodeMetaModel metaModel = node.getMetaModel();
49 | List allPropertyMetaModels = metaModel.getAllPropertyMetaModels();
50 |
51 | List attributes = allPropertyMetaModels
52 | .stream()
53 | .filter(PropertyMetaModel::isAttribute)
54 | .filter(PropertyMetaModel::isSingular)
55 | .collect(toList());
56 | List subNodes = allPropertyMetaModels
57 | .stream()
58 | .filter(PropertyMetaModel::isNode)
59 | .filter(PropertyMetaModel::isSingular)
60 | .collect(toList());
61 | List subLists = allPropertyMetaModels
62 | .stream()
63 | .filter(PropertyMetaModel::isNodeList)
64 | .collect(toList());
65 |
66 | String ndName = this.nextNodeName();
67 | this.currentIds.add(ndName);
68 |
69 | builder.append(EOL)
70 | .append("WITH ").append(String.join(", ", this.currentIds)).append(EOL);
71 |
72 |
73 | builder.append("MERGE(").append(ndName).append(":Node:").append(metaModel.getTypeName()).append(" {");
74 |
75 | if (this.outputNodeType) {
76 | builder.append(EOL)
77 | .append(" type: '").append(metaModel.getTypeName()).append("'");
78 | }
79 |
80 | // builder.append(
81 | // EOL + " nodeName: '" + ndName + "'," +
82 | // EOL + " parentName: '" + parentNodeName + "'" +
83 | // "");
84 |
85 | builder.append(",").append(EOL)
86 | .append(" ").append("name").append(": '").append(escapeQuotes(name)).append("'");
87 |
88 | for (PropertyMetaModel a : attributes) {
89 | String x = "," + EOL + " " + escapeQuotes(a.getName()) + ": '" + escapeQuotes(a.getValue(node).toString()) + "'";
90 | builder.append(x);
91 | }
92 |
93 | builder.append(EOL)
94 | .append("})");
95 |
96 | // Do relationships
97 | if (parentNodeName != null) {
98 | builder.append(EOL)
99 | .append("MERGE (").append(parentNodeName).append(")<-[:PARENT]-(").append(ndName).append(")");
100 | }
101 | builder.append(EOL);
102 | builder.append(EOL);
103 |
104 |
105 | for (PropertyMetaModel sn : subNodes) {
106 | Node nd = (Node) sn.getValue(node);
107 | if (nd != null) {
108 | this.output(nd, ndName, sn.getName(), builder);
109 | }
110 | }
111 |
112 | for (PropertyMetaModel sl : subLists) {
113 | NodeList extends Node> nl = (NodeList extends Node>) sl.getValue(node);
114 | if (nl != null && nl.isNonEmpty()) {
115 | String slName = sl.getName().substring(0, sl.getName().length() - 1);
116 | for (Node nd : nl) {
117 | this.output(nd, ndName, slName, builder);
118 | }
119 | }
120 | }
121 |
122 | this.currentIds.remove(name);
123 | }
124 |
125 |
126 | @Override
127 | public String output(Node node) {
128 | this.nodeCount = 0;
129 | StringBuilder output = new StringBuilder(DEFAULT_STRINGBUILDER_CAPACITY);
130 | this.output(node, null, "root", output);
131 | return output.toString();
132 | }
133 |
134 |
135 | public void output2(Node node, String parentNodeName, String name, StringBuilder builder) {
136 | assertNotNull(node);
137 |
138 | NodeMetaModel metaModel = node.getMetaModel();
139 | List allPropertyMetaModels = metaModel.getAllPropertyMetaModels();
140 |
141 | List attributes = allPropertyMetaModels
142 | .stream()
143 | .filter(PropertyMetaModel::isAttribute)
144 | .filter(PropertyMetaModel::isSingular)
145 | .collect(toList());
146 | List subNodes = allPropertyMetaModels
147 | .stream()
148 | .filter(PropertyMetaModel::isNode)
149 | .filter(PropertyMetaModel::isSingular)
150 | .collect(toList());
151 | List subLists = allPropertyMetaModels
152 | .stream()
153 | .filter(PropertyMetaModel::isNodeList)
154 | .collect(toList());
155 |
156 | String ndName = this.nextNodeName();
157 | if (this.outputNodeType) {
158 | builder.append(EOL)
159 | .append(ndName).append(" [label=\"").append(escapeQuotes(name)).append(" (").append(metaModel.getTypeName()).append(")\"];");
160 | } else {
161 | builder.append(EOL)
162 | .append(ndName).append(" [label=\"").append(escapeQuotes(name)).append("\"];");
163 | }
164 |
165 | if (parentNodeName != null) {
166 | builder.append(EOL)
167 | .append(parentNodeName).append(" -> ").append(ndName).append(";");
168 | }
169 |
170 | for (PropertyMetaModel a : attributes) {
171 | String attrName = this.nextNodeName();
172 | builder.append(EOL)
173 | .append(attrName).append(" [label=\"").append(escapeQuotes(a.getName())).append("='").append(escapeQuotes(a.getValue(node).toString())).append("'\"];");
174 | builder.append(EOL)
175 | .append(ndName).append(" -> ").append(attrName).append(";");
176 |
177 | }
178 |
179 | for (PropertyMetaModel sn : subNodes) {
180 | Node nd = (Node) sn.getValue(node);
181 | if (nd != null) {
182 | this.output(nd, ndName, sn.getName(), builder);
183 | }
184 | }
185 |
186 | for (PropertyMetaModel sl : subLists) {
187 | NodeList extends Node> nl = (NodeList extends Node>) sl.getValue(node);
188 | if (nl != null && nl.isNonEmpty()) {
189 | String ndLstName = this.nextNodeName();
190 | builder.append(EOL)
191 | .append(ndLstName).append(" [label=\"").append(escapeQuotes(sl.getName())).append("\"];");
192 | builder.append(EOL)
193 | .append(ndName).append(" -> ").append(ndLstName).append(";");
194 | String slName = sl.getName().substring(0, sl.getName().length() - 1);
195 | for (Node nd : nl) {
196 | this.output(nd, ndLstName, slName, builder);
197 | }
198 | }
199 | }
200 | }
201 |
202 |
203 | @Override
204 | public String toString() {
205 | return "CypherPrinter{" +
206 | "currentIds=" + this.currentIds +
207 | ", outputNodeType=" + this.outputNodeType +
208 | ", nodeCount=" + this.nodeCount +
209 | '}';
210 | }
211 | }
212 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/printers/GraphMLPrinter.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.printers;
2 |
3 | import com.github.javaparser.ast.Node;
4 | import com.github.javaparser.ast.NodeList;
5 | import com.github.javaparser.metamodel.NodeMetaModel;
6 | import com.github.javaparser.metamodel.PropertyMetaModel;
7 |
8 | import java.util.ArrayList;
9 | import java.util.List;
10 | import java.util.Set;
11 | import java.util.TreeSet;
12 |
13 | import static com.github.javaparser.utils.Utils.assertNotNull;
14 | import static java.util.stream.Collectors.toList;
15 |
16 | /**
17 | * Outputs an GraphML file containing the AST for import into a graph database.
18 | */
19 | public class GraphMLPrinter implements NodePrinter {
20 |
21 | private static final int DEFAULT_STRINGBUILDER_CAPACITY = 5000;
22 |
23 | private static final String NEWLINE = String.format("%n");
24 |
25 | private static final String DATA_INDENT = " ";
26 | private static final String EDGE_INDENT = " ";
27 | private static final String GRAPH_INDENT = " ";
28 | private static final String KEY_INDENT = " ";
29 | private static final String NODE_INDENT = " ";
30 |
31 | private final Set edgeKeys;
32 | private final List edges;
33 | private final Set nodeKeys;
34 | private final List nodes;
35 | private final boolean outputNodeType;
36 |
37 | private int edgeCount;
38 | private int nodeCount;
39 |
40 |
41 | public GraphMLPrinter(boolean outputNodeType) {
42 | this.edgeCount = 0;
43 | this.nodeCount = 0;
44 | this.outputNodeType = outputNodeType;
45 | this.nodeKeys = new TreeSet<>();
46 | this.edgeKeys = new TreeSet<>();
47 | this.nodes = new ArrayList<>();
48 | this.edges = new ArrayList<>();
49 | }
50 |
51 |
52 | private String attribute(String name, String value) {
53 | return " " + name + "=\"" + value + "\"";
54 | }
55 |
56 |
57 | private String dataEntry(String key, String value) {
58 | String escapedValue = value
59 | .replaceAll("<", "<")
60 | .replaceAll(">", ">");
61 |
62 | return "" +
65 | escapedValue +
66 | "";
67 | }
68 |
69 |
70 | private String keyEntry(String name, String elemType, String type) {
71 | return "";
77 | }
78 |
79 |
80 | private String nextEdgeName() {
81 | return "e" + (this.edgeCount++);
82 | }
83 |
84 |
85 | private String nextNodeName() {
86 | return "n" + (this.nodeCount++);
87 | }
88 |
89 |
90 | private void output(Node node, String name, int level, String parentNdName) {
91 | assertNotNull(node);
92 | NodeMetaModel metaModel = node.getMetaModel();
93 | List allPropertyMetaModels = metaModel.getAllPropertyMetaModels();
94 | List attributes = allPropertyMetaModels.stream().filter(PropertyMetaModel::isAttribute).filter(PropertyMetaModel::isSingular).collect(toList());
95 | List subNodes = allPropertyMetaModels.stream().filter(PropertyMetaModel::isNode).filter(PropertyMetaModel::isSingular).collect(toList());
96 | List subLists = allPropertyMetaModels.stream().filter(PropertyMetaModel::isNodeList).collect(toList());
97 |
98 | String ndName = this.nextNodeName();
99 | StringBuilder nodeBuilder = new StringBuilder(DEFAULT_STRINGBUILDER_CAPACITY);
100 | String typeName = metaModel.getTypeName();
101 |
102 | this.nodeKeys.add("id");
103 | this.nodeKeys.add("labels");
104 | nodeBuilder
105 | .append(NODE_INDENT).append("");
109 |
110 | if (this.outputNodeType) {
111 | this.nodeKeys.add("type");
112 | nodeBuilder.append(NEWLINE).append(DATA_INDENT).append(this.dataEntry("type", typeName));
113 | }
114 |
115 | for (PropertyMetaModel attributeMetaModel : attributes) {
116 | String attributeName = attributeMetaModel.getName();
117 | String value = attributeMetaModel.getValue(node).toString();
118 | this.nodeKeys.add(attributeName);
119 | nodeBuilder.append(NEWLINE).append(DATA_INDENT).append(this.dataEntry(attributeName, value));
120 | }
121 |
122 | nodeBuilder.append(NEWLINE).append(NODE_INDENT).append("");
123 | this.nodes.add(nodeBuilder.toString());
124 |
125 | if (parentNdName != null) {
126 | String edgeName = this.nextEdgeName();
127 | String edgeLabel = "PARENT";
128 |
129 | this.edgeKeys.add("id");
130 | this.edgeKeys.add("source");
131 | this.edgeKeys.add("target");
132 | this.edgeKeys.add("label");
133 |
134 | String edge = "";
135 | edge += EDGE_INDENT;
136 | edge += "";
142 | edge += NEWLINE + DATA_INDENT + this.dataEntry(edgeLabel, edgeLabel);
143 | edge += NEWLINE + EDGE_INDENT + "";
144 |
145 | this.edges.add(edge);
146 | }
147 |
148 | for (PropertyMetaModel subNodeMetaModel : subNodes) {
149 | Node value = (Node) subNodeMetaModel.getValue(node);
150 | if (value != null) {
151 | this.output(value, subNodeMetaModel.getName(), level + 1, ndName);
152 | }
153 | }
154 | //
155 | for (PropertyMetaModel subListMetaModel : subLists) {
156 | NodeList extends Node> subList = (NodeList extends Node>) subListMetaModel.getValue(node);
157 | if (subList != null && !subList.isEmpty()) {
158 | String listName = subListMetaModel.getName();
159 | String singular = listName.substring(0, listName.length() - 1);
160 | for (Node subListNode : subList) {
161 | this.output(subListNode, singular, level + 1, ndName);
162 | }
163 | }
164 | }
165 | }
166 |
167 |
168 | @Override
169 | public String output(Node node) {
170 | StringBuilder output = new StringBuilder(DEFAULT_STRINGBUILDER_CAPACITY);
171 | output.append("").append(NEWLINE)
172 | .append("").append(NEWLINE);
175 |
176 | this.output(node, "root", 0, null);
177 |
178 | this.nodeKeys.forEach(s -> {
179 | output.append(NEWLINE).append(KEY_INDENT).append(this.keyEntry(s, "node", "string"));
180 | });
181 | this.edgeKeys.forEach(s -> {
182 | output.append(NEWLINE).append(KEY_INDENT).append(this.keyEntry(s, "edge", "string"));
183 | });
184 |
185 | output.append(NEWLINE).append(GRAPH_INDENT).append("");
186 | this.nodes.forEach(s -> {
187 | output.append(NEWLINE).append(s);
188 | });
189 |
190 | this.edges.forEach(s -> {
191 | output.append(NEWLINE).append(s);
192 | });
193 |
194 | output.append(NEWLINE).append(GRAPH_INDENT).append("");
195 | output.append(NEWLINE).append("");
196 |
197 | return output.toString();
198 | }
199 |
200 |
201 | @Override
202 | public String toString() {
203 | return "GraphMLPrinter{" +
204 | "nodes=" + this.nodes +
205 | ", outputNodeType=" + this.outputNodeType +
206 | ", edgeCount=" + this.edgeCount +
207 | ", nodeCount=" + this.nodeCount +
208 | '}';
209 | }
210 | }
211 |
212 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/printers/NodePrinter.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.printers;
2 |
3 | import com.github.javaparser.ast.Node;
4 |
5 | public interface NodePrinter {
6 |
7 |
8 | /**
9 | * @param node The node to be printed - typically a CompilationUnit.
10 | * @return The formatted equivalent of node.
11 | */
12 | String output(Node node);
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/services/HighlightingService.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.services;
2 |
3 | import com.github.javaparser.ast.Node;
4 | import com.intellij.openapi.components.ServiceManager;
5 | import com.intellij.openapi.editor.Editor;
6 | import com.intellij.psi.PsiFile;
7 |
8 | import java.util.Optional;
9 |
10 | public interface HighlightingService {
11 | static HighlightingService getInstance() {
12 | return ServiceManager.getService(HighlightingService.class);
13 | }
14 |
15 | Optional getSelectedNode();
16 |
17 | void setSelectedNode(Node node);
18 |
19 | void updateHighlight(PsiFile psiFile, Editor editor);
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/services/JavaParserService.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.services;
2 |
3 | import com.github.javaparser.JavaParser;
4 | import com.github.javaparser.ParseResult;
5 | import com.github.javaparser.ParserConfiguration;
6 | import com.github.javaparser.Provider;
7 | import com.github.javaparser.ast.CompilationUnit;
8 | import com.github.javaparser.utils.SourceRoot;
9 | import com.intellij.openapi.components.ServiceManager;
10 | import com.intellij.openapi.project.Project;
11 | import com.intellij.openapi.vfs.VirtualFile;
12 | import org.jetbrains.annotations.NotNull;
13 |
14 | import java.util.List;
15 |
16 | public interface JavaParserService {
17 |
18 | static JavaParserService getInstance(@NotNull Project project) {
19 | return ServiceManager.getService(project, JavaParserService.class);
20 | }
21 |
22 |
23 | ParserConfiguration getConfiguration();
24 |
25 | ParserConfiguration getDefaultConfiguration();
26 |
27 | JavaParser getJavaParserInstance();
28 |
29 | ParseResult parseCu(Provider provider);
30 |
31 | List vFilesToSourceRoots(VirtualFile[] vFiles);
32 |
33 | String vFilesToSourceRoots(VirtualFile[] vFiles, String delimiter);
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/services/PrinterService.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.services;
2 |
3 | import com.github.javaparser.ast.CompilationUnit;
4 | import com.github.javaparser.ast.Node;
5 | import com.intellij.openapi.components.ServiceManager;
6 | import com.intellij.openapi.project.Project;
7 | import org.jetbrains.annotations.NotNull;
8 |
9 | public interface PrinterService {
10 |
11 | static PrinterService getInstance(@NotNull Project project) {
12 | return ServiceManager.getService(project, PrinterService.class);
13 | }
14 |
15 |
16 | String asAsciiTreeText(Node node);
17 |
18 | String asAsciiTreeText(Node node, boolean outputNodeType);
19 |
20 | String asCypher(Node node);
21 |
22 | String asCypher(Node node, boolean outputNodeType);
23 |
24 |
25 | String asDot(Node node);
26 |
27 | String asDot(Node node, boolean outputNodeType);
28 |
29 |
30 | String asDotCustom(Node node);
31 |
32 | String asDotCustom(Node node, boolean outputNodeType);
33 |
34 |
35 | String asGraphMl(Node node);
36 |
37 | String asGraphMl(Node node, boolean outputNodeType);
38 |
39 |
40 | String asJavaPrettyPrint(Node node);
41 |
42 |
43 | String asJsonCustom(Node node);
44 |
45 | String asJsonCustom(Node node, boolean outputNodeType);
46 |
47 |
48 | String asXml(Node node, boolean outputNodeType);
49 |
50 | String asXml(Node node);
51 |
52 |
53 | String asYaml(Node node, boolean outputNodeType);
54 |
55 | String asYaml(Node node);
56 |
57 |
58 | default String outputAs(String outputFormat, CompilationUnit compilationUnit) {
59 | return this.outputAs(outputFormat, compilationUnit, false);
60 | }
61 |
62 | String outputAs(String outputFormat, CompilationUnit compilationUnit, boolean includeNodeType);
63 |
64 | }
65 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/services/impl/HighlightingServiceImpl.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.services.impl;
2 |
3 | import com.github.javaparser.Range;
4 | import com.github.javaparser.ast.Node;
5 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.logging.NotificationLogger;
6 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.services.HighlightingService;
7 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.util.EditorUtil;
8 | import com.intellij.openapi.editor.Editor;
9 | import com.intellij.openapi.editor.markup.EffectType;
10 | import com.intellij.openapi.editor.markup.HighlighterLayer;
11 | import com.intellij.openapi.editor.markup.HighlighterTargetArea;
12 | import com.intellij.openapi.editor.markup.MarkupModel;
13 | import com.intellij.openapi.editor.markup.RangeHighlighter;
14 | import com.intellij.openapi.editor.markup.TextAttributes;
15 | import com.intellij.openapi.util.TextRange;
16 | import com.intellij.psi.PsiFile;
17 | import com.intellij.ui.JBColor;
18 |
19 | import java.util.Arrays;
20 | import java.util.HashMap;
21 | import java.util.List;
22 | import java.util.Map;
23 | import java.util.Optional;
24 |
25 | public class HighlightingServiceImpl implements HighlightingService {
26 |
27 | private static final int HIGHLIGHT_LAYER = HighlighterLayer.ERROR + 200;
28 |
29 | private static final NotificationLogger notificationLogger = new NotificationLogger(HighlightingServiceImpl.class);
30 |
31 | private final Map highlighters;
32 |
33 | private final TextAttributes taGreen;
34 | private final TextAttributes taOrange;
35 | private final TextAttributes taSelectedNodeInEditor;
36 | private final TextAttributes taYellow;
37 |
38 | private Node selectedNode = null;
39 |
40 |
41 | public HighlightingServiceImpl() {
42 |
43 | this.highlighters = new HashMap<>();
44 |
45 | // Setup colours
46 | this.taSelectedNodeInEditor = new TextAttributes();
47 | this.taSelectedNodeInEditor.setBackgroundColor(JBColor.YELLOW);
48 | this.taSelectedNodeInEditor.withAdditionalEffect(EffectType.BOXED, JBColor.RED);
49 |
50 | this.taYellow = new TextAttributes();
51 | this.taYellow.setBackgroundColor(JBColor.YELLOW);
52 | this.taYellow.withAdditionalEffect(EffectType.BOXED, JBColor.RED);
53 |
54 | this.taOrange = new TextAttributes();
55 | this.taOrange.setBackgroundColor(JBColor.ORANGE);
56 | this.taOrange.withAdditionalEffect(EffectType.BOXED, JBColor.RED);
57 |
58 | this.taGreen = new TextAttributes();
59 | this.taGreen.setBackgroundColor(JBColor.GREEN);
60 | this.taGreen.withAdditionalEffect(EffectType.BOXED, JBColor.RED);
61 |
62 | }
63 |
64 |
65 | /**
66 | * Which character indicates the the last character of the line, at which point the line number increments?
67 | *
68 | * Examples:
69 | *
70 | * - {@code \r} (CR) - Characters after the {@code \r} are considered to be on the next line.
71 | * - {@code \n} (LF) - Characters after the {@code \n} are considered to be on the next line.
72 | * - {@code \r\n} (CRLF) - Characters after the {@code \n} are considered to be on the next line.
73 | *
74 | */
75 | public static char lastLineSeparator(String lineSeparatorString) {
76 | char defaultSeparator = '\n';
77 | char lineSeparatorChar;
78 |
79 | if (lineSeparatorString == null) {
80 | lineSeparatorChar = defaultSeparator;
81 | } else if (lineSeparatorString.length() == 1) {
82 | lineSeparatorChar = lineSeparatorString.toCharArray()[0];
83 | } else if (lineSeparatorString.length() == 2) {
84 | lineSeparatorChar = lineSeparatorString.toCharArray()[1];
85 | } else {
86 | lineSeparatorChar = defaultSeparator;
87 | }
88 |
89 | return lineSeparatorChar;
90 | }
91 |
92 |
93 | @Override
94 | public Optional getSelectedNode() {
95 | return Optional.ofNullable(this.selectedNode);
96 | }
97 |
98 |
99 | @Override
100 | public void setSelectedNode(Node node) {
101 | this.selectedNode = node;
102 | }
103 |
104 |
105 | @Override
106 | public void updateHighlight(PsiFile psiFile, Editor editor) {
107 | notificationLogger.traceEnter();
108 |
109 | if (this.selectedNode != null) {
110 | if (this.selectedNode.getRange().isPresent()) {
111 | final Range range = this.selectedNode.getRange().get();
112 |
113 | final MarkupModel markupModel = editor.getMarkupModel();
114 |
115 | // Remove all current highlighters for this editor
116 | if (this.highlighters.containsKey(editor)) {
117 | RangeHighlighter highlighter = this.highlighters.get(editor);
118 |
119 | List allHighlighters = Arrays.asList(markupModel.getAllHighlighters());
120 | if (allHighlighters.contains(highlighter)) {
121 | markupModel.removeHighlighter(highlighter);
122 | }
123 | }
124 |
125 | // Create a new highlighter.
126 | TextRange textRange = EditorUtil.javaParserRangeToIntellijOffsetRange(editor, range);
127 | final RangeHighlighter newHighlighter = markupModel.addRangeHighlighter(
128 | textRange.getStartOffset(),
129 | textRange.getEndOffset(),
130 | HIGHLIGHT_LAYER,
131 | this.taSelectedNodeInEditor,
132 | HighlighterTargetArea.EXACT_RANGE
133 | );
134 |
135 | // Add to per-editor cache of highlighters.
136 | this.highlighters.put(editor, newHighlighter);
137 |
138 | // Scroll to the start of the highlighted range.
139 | EditorUtil.scrollToPosition(editor, newHighlighter.getStartOffset());
140 |
141 | } else {
142 | notificationLogger.warn("Selected node does not have a range, thus unable to update highlighting.");
143 | }
144 | }
145 | }
146 |
147 | }
148 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/services/impl/JavaParserServiceImpl.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.services.impl;
2 |
3 | import com.github.javaparser.JavaParser;
4 | import com.github.javaparser.ParseResult;
5 | import com.github.javaparser.ParseStart;
6 | import com.github.javaparser.ParserConfiguration;
7 | import com.github.javaparser.Provider;
8 | import com.github.javaparser.ast.CompilationUnit;
9 | import com.github.javaparser.utils.SourceRoot;
10 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.services.JavaParserService;
11 | import com.intellij.openapi.project.Project;
12 | import com.intellij.openapi.vfs.VirtualFile;
13 |
14 | import java.nio.file.Paths;
15 | import java.util.Arrays;
16 | import java.util.List;
17 | import java.util.stream.Collectors;
18 |
19 | public class JavaParserServiceImpl implements JavaParserService {
20 |
21 | private final ParserConfiguration configuration;
22 | private final JavaParser javaParser;
23 | private final Project project;
24 |
25 |
26 | public JavaParserServiceImpl(Project project) {
27 | this.project = project;
28 | this.configuration = this.getDefaultConfiguration();
29 | this.javaParser = new JavaParser(this.configuration);
30 | }
31 |
32 |
33 | @Override
34 | public ParserConfiguration getConfiguration() {
35 | return this.configuration;
36 | }
37 |
38 |
39 | @Override
40 | public ParserConfiguration getDefaultConfiguration() {
41 | return new ParserConfiguration();
42 | }
43 |
44 |
45 | @Override
46 | public JavaParser getJavaParserInstance() {
47 | return this.javaParser;
48 | }
49 |
50 |
51 | @Override
52 | public ParseResult parseCu(Provider provider) {
53 | return this.javaParser.parse(ParseStart.COMPILATION_UNIT, provider);
54 | }
55 |
56 |
57 | @Override
58 | public List vFilesToSourceRoots(VirtualFile[] vFiles) {
59 | return Arrays.stream(vFiles)
60 | .map(VirtualFile::getPath)
61 | .map(Paths::get)
62 | .map(SourceRoot::new)
63 | .collect(Collectors.toList());
64 | }
65 |
66 |
67 | @Override
68 | public String vFilesToSourceRoots(VirtualFile[] vFiles, String delimiter) {
69 | return this.vFilesToSourceRoots(vFiles).stream()
70 | .map(sourceRoot -> sourceRoot.getRoot().toString())
71 | .collect(Collectors.joining(delimiter));
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/services/impl/PrinterServiceImpl.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.services.impl;
2 |
3 | import com.github.javaparser.ast.CompilationUnit;
4 | import com.github.javaparser.ast.Node;
5 | import com.github.javaparser.printer.DotPrinter;
6 | import com.github.javaparser.printer.XmlPrinter;
7 | import com.github.javaparser.printer.YamlPrinter;
8 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.logging.NotificationLogger;
9 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.printers.ASCIITreePrinter;
10 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.printers.CustomDotPrinter;
11 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.printers.CustomJsonPrinter;
12 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.printers.CypherPrinter;
13 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.printers.GraphMLPrinter;
14 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.services.PrinterService;
15 | import com.intellij.openapi.project.Project;
16 |
17 | public class PrinterServiceImpl implements PrinterService {
18 |
19 | private static final boolean defaultOutputNodeType = true;
20 |
21 | private static final NotificationLogger notificationLogger = new NotificationLogger(PrinterServiceImpl.class);
22 | private final Project project;
23 |
24 |
25 | public PrinterServiceImpl(Project project) {
26 | this.project = project;
27 | }
28 |
29 |
30 | @Override
31 | public String asAsciiTreeText(Node node) {
32 | return this.asAsciiTreeText(node, defaultOutputNodeType);
33 | }
34 |
35 |
36 | @Override
37 | public String asAsciiTreeText(Node node, boolean outputNodeType) {
38 | ASCIITreePrinter printer = new ASCIITreePrinter();
39 | return printer.output(node);
40 | }
41 |
42 |
43 | @Override
44 | public String asCypher(Node node) {
45 | return this.asCypher(node, defaultOutputNodeType);
46 | }
47 |
48 |
49 | @Override
50 | public String asCypher(Node node, boolean outputNodeType) {
51 | CypherPrinter printer = new CypherPrinter(outputNodeType);
52 | return printer.output(node);
53 | }
54 |
55 |
56 | @Override
57 | public String asDot(Node node) {
58 | return this.asDot(node, defaultOutputNodeType);
59 | }
60 |
61 |
62 | @Override
63 | public String asDot(Node node, boolean outputNodeType) {
64 | DotPrinter printer = new DotPrinter(outputNodeType);
65 | return printer.output(node);
66 | }
67 |
68 |
69 | @Override
70 | public String asDotCustom(Node node) {
71 | return this.asDotCustom(node, defaultOutputNodeType);
72 | }
73 |
74 |
75 | @Override
76 | public String asDotCustom(Node node, boolean outputNodeType) {
77 | CustomDotPrinter printer = new CustomDotPrinter(outputNodeType);
78 | return printer.output(node);
79 | }
80 |
81 |
82 | @Override
83 | public String asGraphMl(Node node) {
84 | return this.asGraphMl(node, defaultOutputNodeType);
85 | }
86 |
87 |
88 | @Override
89 | public String asGraphMl(Node node, boolean outputNodeType) {
90 | GraphMLPrinter printer = new GraphMLPrinter(outputNodeType);
91 | return printer.output(node);
92 | }
93 |
94 |
95 | @Override
96 | public String asJavaPrettyPrint(Node node) {
97 | return node.toString();
98 | }
99 |
100 |
101 | @Override
102 | public String asJsonCustom(Node node) {
103 | return this.asJsonCustom(node, defaultOutputNodeType);
104 | }
105 |
106 |
107 | @Override
108 | public String asJsonCustom(Node node, boolean outputNodeType) {
109 | CustomJsonPrinter printer = new CustomJsonPrinter(outputNodeType);
110 | return printer.output(node);
111 | }
112 |
113 |
114 | @Override
115 | public String asXml(Node node, boolean outputNodeType) {
116 | XmlPrinter printer = new XmlPrinter(outputNodeType);
117 | return printer.output(node);
118 | }
119 |
120 |
121 | @Override
122 | public String asXml(Node node) {
123 | return this.asXml(node, defaultOutputNodeType);
124 | }
125 |
126 |
127 | @Override
128 | public String asYaml(Node node, boolean outputNodeType) {
129 | YamlPrinter printer = new YamlPrinter(outputNodeType);
130 | return printer.output(node);
131 | }
132 |
133 |
134 | @Override
135 | public String asYaml(Node node) {
136 | return this.asYaml(node, defaultOutputNodeType);
137 | }
138 |
139 |
140 | @Override
141 | public String outputAs(final String outputFormat, final CompilationUnit compilationUnit, boolean includeNodeType) {
142 |
143 | String output = null;
144 |
145 | if ("YAML".equals(outputFormat)) {
146 | output = this.asYaml(compilationUnit, includeNodeType);
147 | } else if ("XML".equals(outputFormat)) {
148 | output = this.asXml(compilationUnit, includeNodeType);
149 | } else if ("DOT".equals(outputFormat)) {
150 | output = this.asDot(compilationUnit, includeNodeType);
151 | // } else if ("Java (lexically preserving)".equals(outputFormat)) {
152 | // notificationLogger.info("Note that the lexically preserving printer does not use the setting 'include node type'. ");
153 | // output = this.asJavaPrettyPrint(compilationUnit);
154 | } else if ("Java (pretty print)".equals(outputFormat)) {
155 | notificationLogger.info("Note that the pretty printer does not use the setting 'include node type'. ");
156 | output = this.asJavaPrettyPrint(compilationUnit);
157 | } else if ("ASCII Tree".equals(outputFormat)) {
158 | output = this.asAsciiTreeText(compilationUnit, includeNodeType);
159 | } else if ("Custom DOT".equals(outputFormat)) {
160 | output = this.asDotCustom(compilationUnit, includeNodeType);
161 | } else if ("Custom DOT Image".equals(outputFormat)) {
162 | output = this.asDotCustom(compilationUnit, includeNodeType);
163 | } else if ("Custom JSON".equals(outputFormat)) {
164 | output = this.asJsonCustom(compilationUnit, includeNodeType);
165 | } else if ("Cypher".equals(outputFormat)) {
166 | output = this.asCypher(compilationUnit, includeNodeType);
167 | } else if ("GraphML".equals(outputFormat)) {
168 | output = this.asGraphMl(compilationUnit, includeNodeType);
169 | } else {
170 | notificationLogger.error("Unrecognised output format: " + outputFormat);
171 | }
172 |
173 | return output;
174 | }
175 |
176 | }
177 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/ui/notifications/NotificationsNotifier.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.notifications;
2 |
3 | import com.intellij.notification.Notification;
4 | import com.intellij.openapi.project.Project;
5 | import org.jetbrains.annotations.NotNull;
6 | import org.jetbrains.annotations.Nullable;
7 |
8 | public interface NotificationsNotifier {
9 |
10 | @NotNull
11 | default Notification notify(@NotNull final String content) {
12 | return this.notify(null, content);
13 | }
14 |
15 | @NotNull
16 | Notification notify(@Nullable Project project, @NotNull String content);
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/ui/swing_components/NodeDetailsTextPane.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.swing_components;
2 |
3 | import com.github.javaparser.ast.Node;
4 | import com.github.javaparser.ast.NodeList;
5 | import com.github.javaparser.ast.expr.ObjectCreationExpr;
6 | import com.github.javaparser.metamodel.NodeMetaModel;
7 | import com.github.javaparser.metamodel.PropertyMetaModel;
8 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.printers.ASCIITreePrinter;
9 | import com.intellij.ui.JBColor;
10 |
11 | import javax.swing.*;
12 | import javax.swing.text.BadLocationException;
13 | import javax.swing.text.SimpleAttributeSet;
14 | import javax.swing.text.StyleConstants;
15 | import javax.swing.text.StyledDocument;
16 | import java.util.List;
17 |
18 | import static java.util.stream.Collectors.toList;
19 |
20 | public class NodeDetailsTextPane extends JTextPane {
21 |
22 | private static final String EOL = System.lineSeparator();
23 | private static final String H_LINE = "----------------------------------------";
24 |
25 | private SimpleAttributeSet styleBoldBlue;
26 | private SimpleAttributeSet styleHighAlert;
27 | private SimpleAttributeSet styleNormal;
28 |
29 |
30 | public NodeDetailsTextPane() {
31 | super();
32 | this.setupStyles();
33 | }
34 |
35 |
36 | public NodeDetailsTextPane(StyledDocument doc) {
37 | super(doc);
38 | this.setupStyles();
39 | }
40 |
41 |
42 | public void addLineSeparator() {
43 | this.appendLine(H_LINE, this.styleBoldBlue);
44 | }
45 |
46 |
47 | public void appendHeading(String s) {
48 | this.appendString(s + EOL, this.styleBoldBlue);
49 | }
50 |
51 |
52 | public void appendLine(String s) {
53 | this.appendString(s + EOL, this.styleNormal);
54 | }
55 |
56 |
57 | public void appendLine(String s, SimpleAttributeSet style) {
58 | this.appendString(s + EOL, style);
59 | }
60 |
61 |
62 | public void appendString(String s) {
63 | this.appendString(s, this.styleNormal);
64 | }
65 |
66 |
67 | public void appendString(String s, SimpleAttributeSet style) {
68 | try {
69 | final StyledDocument doc = this.getStyledDocument();
70 | doc.insertString(doc.getLength(), s, style);
71 | } catch (BadLocationException e) {
72 | e.printStackTrace();
73 | }
74 | }
75 |
76 |
77 | public void clear() {
78 | this.setText("");
79 | }
80 |
81 |
82 | @Override
83 | public StyledDocument getStyledDocument() {
84 | return (StyledDocument) this.getDocument();
85 | }
86 |
87 |
88 | public void logNodeToTextPane(Node selectedNode) {
89 |
90 | // Update the side panel
91 | final NodeMetaModel metaModel = selectedNode.getMetaModel();
92 | final List allPropertyMetaModels = metaModel.getAllPropertyMetaModels();
93 | final List attributes = allPropertyMetaModels.stream().filter(PropertyMetaModel::isAttribute).filter(PropertyMetaModel::isSingular).collect(toList());
94 | final List subNodes = allPropertyMetaModels.stream().filter(PropertyMetaModel::isNode).filter(PropertyMetaModel::isSingular).collect(toList());
95 | final List subLists = allPropertyMetaModels.stream().filter(PropertyMetaModel::isNodeList).collect(toList());
96 |
97 |
98 | this.appendHeading("DETAILS ");
99 |
100 | this.addLineSeparator();
101 | this.appendLine(" - TYPE: " + metaModel.getTypeName());
102 | this.appendString(" - RANGE: ");
103 | if (selectedNode.getRange().isPresent()) {
104 | this.appendLine(selectedNode.getRange().get().toString());
105 | } else {
106 | this.appendLine("[NOT PRESENT]");
107 | }
108 | this.appendLine(" - NODE SUMMARY: " + ASCIITreePrinter.printNodeSummary(selectedNode));
109 |
110 |
111 | // Object creation
112 | if ("ObjectCreationExpr".equals(selectedNode.getClass().getSimpleName())) {
113 | this.appendHeading("");
114 | this.appendHeading("");
115 | this.appendHeading("ObjectCreationExpr");
116 | this.addLineSeparator();
117 |
118 | final ObjectCreationExpr objectCreationExpr = (ObjectCreationExpr) selectedNode;
119 | this.appendLine(" - _typeNameString:" + objectCreationExpr.getType().getName().asString());
120 | }
121 |
122 |
123 | this.appendLine("");
124 | this.appendLine("");
125 | this.appendHeading("ATTRIBUTES ");
126 | this.addLineSeparator();
127 | for (final PropertyMetaModel attributeMetaModel : attributes) {
128 | this.appendLine(" - " + attributeMetaModel.getName() + ":" + attributeMetaModel.getValue(selectedNode).toString());
129 | }
130 |
131 |
132 | this.appendLine("");
133 | this.appendLine("");
134 | this.appendHeading("SubNode Meta Model" + " (count: " + subNodes.size() + ")");
135 | this.addLineSeparator();
136 | for (final PropertyMetaModel subNodeMetaModel : subNodes) {
137 | final Node value = (Node) subNodeMetaModel.getValue(selectedNode);
138 | if (value != null) {
139 | this.appendLine(" - " + subNodeMetaModel.getName() + ": " + value);
140 | }
141 | }
142 |
143 | this.appendLine("");
144 | this.appendLine("");
145 | this.appendHeading("SubList Meta Model" + " (count: " + subLists.size() + ")");
146 | this.addLineSeparator();
147 | for (int index_allSublists = 0; index_allSublists < subLists.size(); index_allSublists++) {
148 | final PropertyMetaModel subListMetaModel = subLists.get(index_allSublists);
149 | final NodeList extends Node> subList = (NodeList extends Node>) subListMetaModel.getValue(selectedNode);
150 | if (subList != null && !subList.isEmpty()) {
151 | this.appendLine(subListMetaModel.getName() + " (count: " + subList.size() + ")");
152 | for (int index_sublist = 0; index_sublist < subList.size(); index_sublist++) {
153 | Node subListNode = subList.get(index_sublist);
154 | this.appendLine(index_sublist + ": " + ASCIITreePrinter.CLASS_RANGE_SUMMARY_FORMAT.apply(subListNode));
155 | }
156 | }
157 | if (index_allSublists < (subLists.size() - 1)) {
158 | this.appendLine("");
159 | }
160 | }
161 | }
162 |
163 |
164 | private void setupStyles() {
165 | // Setup styles
166 | this.styleNormal = new SimpleAttributeSet();
167 | StyleConstants.setFontFamily(this.styleNormal, "Monospaced");
168 |
169 | this.styleBoldBlue = new SimpleAttributeSet(this.styleNormal);
170 | StyleConstants.setBold(this.styleBoldBlue, true);
171 | StyleConstants.setForeground(this.styleBoldBlue, JBColor.BLUE);
172 |
173 | this.styleHighAlert = new SimpleAttributeSet(this.styleBoldBlue);
174 | StyleConstants.setItalic(this.styleHighAlert, true);
175 | StyleConstants.setForeground(this.styleHighAlert, JBColor.RED);
176 | }
177 |
178 |
179 | }
180 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/ui/swing_components/config_panel/CharacterEncodingComboBox.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.swing_components.config_panel;
2 |
3 | import com.github.javaparser.Providers;
4 | import org.jetbrains.annotations.NotNull;
5 |
6 | import java.nio.charset.Charset;
7 |
8 | public class CharacterEncodingComboBox extends CustomComboBox {
9 |
10 | public CharacterEncodingComboBox() {
11 | super();
12 | this.setToolTipText("Which language features should be considered valid or invalid when validating the AST?");
13 | this.setupOptions();
14 | }
15 |
16 |
17 | @Override
18 | protected void setupOptions() {
19 | // Populate
20 | this.addItem(new CharacterEncodingComboItem("UTF-8", Providers.UTF8));
21 | }
22 |
23 |
24 | private static class CharacterEncodingComboItem extends CustomComboItem {
25 |
26 | public CharacterEncodingComboItem(@NotNull String key, @NotNull Charset value) {
27 | super(key, value);
28 | }
29 |
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/ui/swing_components/config_panel/CustomComboBox.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.swing_components.config_panel;
2 |
3 | import com.intellij.openapi.ui.ComboBox;
4 | import org.jetbrains.annotations.NotNull;
5 | import org.jetbrains.annotations.Nullable;
6 |
7 | import javax.swing.*;
8 | import java.util.Objects;
9 |
10 | public abstract class CustomComboBox extends ComboBox> {
11 |
12 | public static > void setSelectedValue(@NotNull JComboBox comboBox, @Nullable E value) {
13 | I item;
14 | for (int i = 0; i < comboBox.getItemCount(); i++) {
15 | item = comboBox.getItemAt(i);
16 | if (Objects.equals(item.getValue(), value)) {
17 | comboBox.setSelectedIndex(i);
18 | break;
19 | }
20 | }
21 | }
22 |
23 |
24 | @Nullable
25 | public T getSelected() {
26 | Object itemObject = this.getSelectedItem();
27 | if (itemObject == null) {
28 | return null;
29 | }
30 |
31 | final CustomComboItem item = (CustomComboItem) itemObject;
32 | return item.getValue();
33 | }
34 |
35 |
36 | public void setSelectedByValue(@NotNull T value) {
37 | setSelectedValue(this, value);
38 | }
39 |
40 |
41 | protected abstract void setupOptions();
42 |
43 |
44 | protected static class CustomComboItem {
45 |
46 | @NotNull
47 | protected final String key;
48 |
49 | protected final E value;
50 |
51 |
52 | public CustomComboItem(@NotNull String key, E value) {
53 | this.key = key;
54 | this.value = value;
55 | }
56 |
57 |
58 | @NotNull
59 | public String getKey() {
60 | return this.key;
61 | }
62 |
63 |
64 | public E getValue() {
65 | return this.value;
66 | }
67 |
68 |
69 | @Override
70 | public int hashCode() {
71 | return Objects.hash(this.value);
72 | }
73 |
74 |
75 | @Override
76 | public boolean equals(final Object obj) {
77 | if (obj == this) {
78 | return true;
79 | }
80 | if (!(obj instanceof CustomComboBox.CustomComboItem)) {
81 | return false;
82 | }
83 |
84 | final CustomComboItem> other = (CustomComboItem>) obj;
85 | return Objects.equals(this.value, other.value);
86 | }
87 |
88 |
89 | // @Override
90 | // public String toString() {
91 | // return "CustomComboItem{" +
92 | // "key='" + key + '\'' +
93 | // ", value='" + String.valueOf(value) + '\'' +
94 | // '}';
95 | // }
96 |
97 |
98 | /**
99 | * Note that this is used as the text on the combo item.
100 | */
101 | @Override
102 | public String toString() {
103 | return this.key;
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/ui/swing_components/config_panel/ExportAsComboBox.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.swing_components.config_panel;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 |
5 | public class ExportAsComboBox extends CustomComboBox {
6 |
7 | public ExportAsComboBox() {
8 | super();
9 | this.setToolTipText("Output format.");
10 | this.setupOptions();
11 | }
12 |
13 |
14 | @Override
15 | protected void setupOptions() {
16 | // Populate
17 | this.addItem(new ExportAsComboItem("DOT", "DOT"));
18 | this.addItem(new ExportAsComboItem("XML", "XML"));
19 | // this.addItem(new ExportAsComboItem("Java (lexically preserving)", "Java (lexically preserving)"));
20 | this.addItem(new ExportAsComboItem("Java (pretty print)", "Java (pretty print)"));
21 | this.addItem(new ExportAsComboItem("ASCII Tree", "ASCII Tree"));
22 | this.addItem(new ExportAsComboItem("YAML", "YAML"));
23 | this.addItem(new ExportAsComboItem("Custom DOT", "Custom DOT"));
24 | this.addItem(new ExportAsComboItem("Custom DOT Image", "Custom DOT Image"));
25 | this.addItem(new ExportAsComboItem("Custom JSON", "Custom JSON"));
26 | this.addItem(new ExportAsComboItem("Cypher", "Cypher"));
27 | this.addItem(new ExportAsComboItem("GraphML", "GraphML"));
28 | }
29 |
30 |
31 | private static class ExportAsComboItem extends CustomComboItem {
32 |
33 | public ExportAsComboItem(@NotNull String key, @NotNull String value) {
34 | super(key, value);
35 | }
36 |
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/ui/swing_components/config_panel/LanguageLevelComboBox.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.swing_components.config_panel;
2 |
3 | import com.github.javaparser.ParserConfiguration;
4 | import org.jetbrains.annotations.NotNull;
5 |
6 | public class LanguageLevelComboBox extends CustomComboBox {
7 |
8 | public LanguageLevelComboBox() {
9 | super();
10 | this.setToolTipText("Which language features should be considered valid or invalid when validating the AST?");
11 | this.setupOptions();
12 | }
13 |
14 |
15 | @Override
16 | protected void setupOptions() {
17 | // this.addItem(new LanguageLevelComboItem("CURRENT (" + ParserConfiguration.LanguageLevel.CURRENT.name() + ")", ParserConfiguration.LanguageLevel.CURRENT));
18 | // this.addItem(new LanguageLevelComboItem("BLEEDING EDGE (" + ParserConfiguration.LanguageLevel.BLEEDING_EDGE.name() + ")", ParserConfiguration.LanguageLevel.BLEEDING_EDGE));
19 | // this.addItem(new LanguageLevelComboItem("POPULAR (" + ParserConfiguration.LanguageLevel.POPULAR.name() + ")", ParserConfiguration.LanguageLevel.POPULAR));
20 |
21 | // The "RAW" language level doesn't perform any validations (e.g. checking if 'yield' is permitted as an identifier).
22 | this.addItem(new LanguageLevelComboItem("RAW", ParserConfiguration.LanguageLevel.RAW));
23 |
24 | // List all available language levels (in descending order - recent to older).
25 | // Note that ordering of the options depends on the order they're declared within JavaParser.
26 | ParserConfiguration.LanguageLevel[] languageLevels = ParserConfiguration.LanguageLevel.values();
27 | for (int i = languageLevels.length - 1; i >= 0; i--) {
28 | this.addItem(new LanguageLevelComboItem(languageLevels[i].name(), languageLevels[i]));
29 | }
30 |
31 | }
32 |
33 |
34 | private static class LanguageLevelComboItem extends CustomComboItem {
35 |
36 | public LanguageLevelComboItem(@NotNull String key, ParserConfiguration.LanguageLevel value) {
37 | super(key, value);
38 | }
39 |
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/ui/swing_components/forms/AstInspectorToolWindow.form:
--------------------------------------------------------------------------------
1 |
2 |
120 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/ui/swing_components/forms/AstInspectorToolWindow.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.swing_components.forms;
2 |
3 | import com.github.javaparser.JavaParser;
4 | import com.github.javaparser.ParseResult;
5 | import com.github.javaparser.ParserConfiguration;
6 | import com.github.javaparser.ast.CompilationUnit;
7 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.logging.NotificationLogger;
8 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.swing_components.config_panel.ConfigPanel;
9 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.swing_components.output_results_tabs.ParseResultsTabPane;
10 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.swing_components.output_results_tabs.ParseResultsTabPanesContainer;
11 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.util.Constants;
12 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.util.PsiUtil;
13 | import com.intellij.openapi.project.Project;
14 | import com.intellij.openapi.util.IconLoader;
15 | import com.intellij.openapi.wm.ToolWindow;
16 | import com.intellij.psi.PsiFile;
17 | import org.jetbrains.annotations.NotNull;
18 |
19 | import javax.swing.*;
20 | import java.awt.*;
21 | import java.io.IOException;
22 | import java.net.URI;
23 | import java.nio.file.Path;
24 | import java.util.Optional;
25 |
26 | public class AstInspectorToolWindow implements Form {
27 |
28 | private static final NotificationLogger notificationLogger = new NotificationLogger(AstInspectorToolWindow.class);
29 |
30 | @NotNull
31 | private final Project project;
32 |
33 | @NotNull
34 | private final ToolWindow toolWindow;
35 |
36 | private final ParserConfiguration parserConfiguration;
37 |
38 | private ConfigPanel configPanel;
39 | private JButton gitHubButton;
40 | private JButton javaParserButton;
41 | private JPanel mainPanel;
42 | private JButton parseButton;
43 | private ParseResultsTabPanesContainer parseResultsTabPanesContainer1;
44 | private JButton resetButton;
45 |
46 |
47 | public AstInspectorToolWindow(@NotNull final Project project, @NotNull final ToolWindow toolWindow, @NotNull ParserConfiguration parserConfiguration) {
48 | this.project = project;
49 | this.toolWindow = toolWindow;
50 | this.parserConfiguration = parserConfiguration;
51 | }
52 |
53 |
54 | private static void browseToUrl(@NotNull final String url) {
55 | notificationLogger.info("BUTTON CLICK: URL=" + url);
56 | try {
57 | Desktop.getDesktop().browse(URI.create(url));
58 | } catch (IOException ioException) {
59 | ioException.printStackTrace();
60 | notificationLogger.warn(ioException.getMessage(), ioException);
61 | }
62 | }
63 |
64 |
65 | private JButton buttonWithIcon(@NotNull final String resourcePath) {
66 | final JButton jButton = new JButton();
67 |
68 | final Icon icon = IconLoader.getIcon(resourcePath);
69 | jButton.setIcon(icon);
70 |
71 | return jButton;
72 | }
73 |
74 |
75 | private void createUIComponents() {
76 | notificationLogger.traceEnter(this.project);
77 |
78 | //
79 | this.initButtons();
80 |
81 | //
82 | this.configPanel = new ConfigPanel(this.parserConfiguration);
83 | this.parseResultsTabPanesContainer1 = new ParseResultsTabPanesContainer();
84 | this.parseResultsTabPanesContainer1.doReset(this.project);
85 | }
86 |
87 |
88 | @Override
89 | public Optional getMainPanel() {
90 | return Optional.ofNullable(this.mainPanel);
91 | }
92 |
93 |
94 | private void initButtons() {
95 | notificationLogger.traceEnter(this.project);
96 |
97 | // Create buttons
98 | this.parseButton = new JButton();
99 | this.resetButton = new JButton();
100 | this.gitHubButton = new JButton();
101 | this.javaParserButton = this.buttonWithIcon("/logos/jp-logo_13x13.png");
102 |
103 | // Add button click handlers
104 | this.parseButton.addActionListener(e -> this.parseButtonClickHandler());
105 | this.resetButton.addActionListener(e -> this.resetButtonClickHandler());
106 | this.gitHubButton.addActionListener(e -> browseToUrl(Constants.URL_GITHUB_PLUGIN));
107 | this.javaParserButton.addActionListener(e -> browseToUrl(Constants.URL_WEBSITE_JP));
108 |
109 | }
110 |
111 |
112 | private void parseButtonClickHandler() {
113 | notificationLogger.traceEnter(this.project);
114 |
115 | // this.parseResultsTabPanesContainer1.
116 |
117 | final Optional currentFileInEditor = PsiUtil.getCurrentFileInEditor(this.project);
118 | if (currentFileInEditor.isPresent()) {
119 | final PsiFile psiFile = currentFileInEditor.get();
120 | final JavaParser javaParser = new JavaParser(this.configPanel.getConfigFromForm());
121 |
122 |
123 | // parse result
124 | // final Optional> optionalParseResult = parsePsiFile_editorContents(psiFile);
125 | final Optional> optionalParseResult = this.parsePsiFile_diskContents(javaParser, psiFile);
126 |
127 | if (optionalParseResult.isPresent()) {
128 | final ParseResult parseResult = optionalParseResult.get();
129 |
130 | // FIXME
131 | final ParseResultsTabPane pane = this.parseResultsTabPanesContainer1.addParseResultPane(this.project, psiFile, parseResult);
132 | pane.handleParseResult(this.configPanel, psiFile, parseResult);
133 |
134 | this.parseResultsTabPanesContainer1.setSelectedComponent(pane);
135 |
136 | } else {
137 | notificationLogger.warn(this.project, "No parse result available for file: " + psiFile);
138 | }
139 | } else {
140 | notificationLogger.warn(this.project, "No file selected in editor.");
141 | }
142 |
143 | }
144 |
145 |
146 | private Optional> parsePsiFile_diskContents(JavaParser javaParser, PsiFile psiFile) {
147 | notificationLogger.traceEnter(this.project);
148 | try {
149 | final Path path = PsiUtil.pathForPsi(psiFile);
150 | final ParseResult result = javaParser.parse(path);
151 | return Optional.of(result);
152 | } catch (IOException e) {
153 | notificationLogger.warn(this.project, "Error trying to parse file.", e);
154 | e.printStackTrace();
155 | return Optional.empty();
156 | }
157 | }
158 |
159 |
160 | private void resetButtonClickHandler() {
161 | notificationLogger.traceEnter(this.project);
162 | this.parseResultsTabPanesContainer1.doReset(this.project);
163 | }
164 |
165 |
166 | }
167 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/ui/swing_components/forms/Form.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.swing_components.forms;
2 |
3 | import javax.swing.*;
4 | import java.util.Optional;
5 |
6 | public interface Form {
7 | Optional getMainPanel();
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/ui/swing_components/output_results_tabs/ParseResultsTabPanesContainer.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.swing_components.output_results_tabs;
2 |
3 | import com.github.javaparser.ParseResult;
4 | import com.github.javaparser.ast.CompilationUnit;
5 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.logging.NotificationLogger;
6 | import com.intellij.openapi.project.Project;
7 | import com.intellij.psi.PsiFile;
8 | import com.intellij.ui.components.JBTabbedPane;
9 | import org.jetbrains.annotations.NotNull;
10 | import org.jetbrains.annotations.Nullable;
11 |
12 | import javax.swing.*;
13 | import java.util.ArrayList;
14 | import java.util.List;
15 |
16 | public class ParseResultsTabPanesContainer extends JBTabbedPane {
17 |
18 | private static final NotificationLogger notificationLogger = new NotificationLogger(ParseResultsTabPanesContainer.class);
19 |
20 | @NotNull
21 | private final List panes;
22 |
23 |
24 | public ParseResultsTabPanesContainer() {
25 | super();
26 | notificationLogger.traceEnter();
27 |
28 | this.panes = new ArrayList<>();
29 |
30 | this.doReset(null);
31 | }
32 |
33 |
34 | public ParseResultsTabPane addParseResultPane(@NotNull Project project, @NotNull PsiFile psiFile, @NotNull ParseResult parseResult) {
35 | notificationLogger.traceEnter(project);
36 |
37 | // Remove previous
38 | this.removeAll();
39 |
40 | // Add new
41 | final ParseResultsTabPane parseResultsTabPane = new ParseResultsTabPane(project, psiFile, parseResult);
42 | this.add(parseResultsTabPane.getPaneTitle(), parseResultsTabPane);
43 | this.setSelectedComponent(parseResultsTabPane);
44 |
45 | return parseResultsTabPane;
46 | }
47 |
48 |
49 | public void doReset(@Nullable Project project) {
50 | notificationLogger.traceEnter(project);
51 |
52 | // Remove previous
53 | this.removeAll();
54 |
55 | // Add blank
56 | final EmptyPane newPanel = new EmptyPane();
57 | this.add(newPanel);
58 | this.setSelectedComponent(newPanel);
59 | }
60 |
61 |
62 | @NotNull
63 | public List getPanes() {
64 | notificationLogger.traceEnter();
65 | return this.panes;
66 | }
67 |
68 |
69 | private static class EmptyPane extends JPanel {
70 |
71 | EmptyPane() {
72 | super();
73 | this.add(new JLabel("No files parsed."), SwingConstants.CENTER);
74 | }
75 |
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/ui/tool_window/AstBrowserToolWindowFactory.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.tool_window;
2 |
3 | import com.github.javaparser.ParserConfiguration;
4 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.logging.NotificationLogger;
5 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.swing_components.forms.AstInspectorToolWindow;
6 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.ui.swing_components.forms.Form;
7 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.util.LanguageLevelUtil;
8 | import com.intellij.openapi.project.DumbAware;
9 | import com.intellij.openapi.project.Project;
10 | import com.intellij.openapi.roots.ProjectRootManager;
11 | import com.intellij.openapi.vfs.VirtualFile;
12 | import com.intellij.openapi.wm.ToolWindow;
13 | import com.intellij.openapi.wm.ToolWindowFactory;
14 | import com.intellij.ui.content.Content;
15 | import com.intellij.ui.content.ContentFactory;
16 | import com.intellij.ui.content.ContentManager;
17 | import org.jetbrains.annotations.NotNull;
18 |
19 | import javax.swing.*;
20 | import java.util.Arrays;
21 | import java.util.Optional;
22 | import java.util.stream.Collectors;
23 |
24 | public class AstBrowserToolWindowFactory implements ToolWindowFactory, DumbAware {
25 |
26 | private static final NotificationLogger notificationLogger = new NotificationLogger(AstBrowserToolWindowFactory.class);
27 |
28 |
29 | /**
30 | * Helper method for adding content (tabs) to the tool window.
31 | */
32 | public void addContent(final ToolWindow toolWindow, String panelTitle, Form form) {
33 |
34 | /*
35 | * https://www.jetbrains.org/intellij/sdk/docs/user_interface_components/tool_windows.html
36 | * """As mentioned previously, tool windows can contain multiple tabs, or contents.
37 | * To manage the contents of a tool window, you can call ToolWindow.getContentManager().
38 | * To add a tab (content), you first need to create it by calling ContentManager.getFactory().createContent(),
39 | * and then to add it to the tool window using ContentManager.addContent()."""
40 | */
41 | final ContentManager contentManager = toolWindow.getContentManager();
42 | final ContentFactory contentManagerFactory = contentManager.getFactory();
43 |
44 | final Optional mainPanel = form.getMainPanel();
45 | if (mainPanel.isPresent()) {
46 | final Content panelContent = contentManagerFactory.createContent(mainPanel.get(), panelTitle, false);
47 | contentManager.addContent(panelContent);
48 | } else {
49 | notificationLogger.warn("The panel is unexpectedly null -- unable to produce the tool window.");
50 | }
51 |
52 | }
53 |
54 |
55 | /**
56 | * https://www.jetbrains.org/intellij/sdk/docs/user_interface_components/tool_windows.html
57 | * In addition to that, you specify the factory class - the name of a class implementing the ToolWindowFactory interface.
58 | *
59 | * When the user clicks on the tool window button, the createToolWindowContent() method of the factory class is called,
60 | * and initializes the UI of the tool window.
61 | *
62 | * This procedure ensures that unused tool windows don’t cause any overhead in startup time or memory usage: if a user
63 | * does not interact with the tool window of your plugin, no plugin code will be loaded or executed.
64 | */
65 | @Override
66 | public void createToolWindowContent(@NotNull final Project project, @NotNull final ToolWindow toolWindow) {
67 | notificationLogger.traceEnter(project);
68 |
69 |
70 | // Parse Only Panel
71 | ParserConfiguration parserConfiguration = new ParserConfiguration();
72 |
73 | ParserConfiguration.LanguageLevel defaultJpLanguageLevel = ParserConfiguration.LanguageLevel.CURRENT;
74 | ParserConfiguration.LanguageLevel languageLevelFromProject = LanguageLevelUtil
75 | .getLanguageLevelFromProject(project, defaultJpLanguageLevel);
76 | parserConfiguration.setLanguageLevel(languageLevelFromProject);
77 |
78 | String projectName = project.getName();
79 | ProjectRootManager projectRootManager = ProjectRootManager.getInstance(project);
80 | VirtualFile[] vFiles = projectRootManager.getContentSourceRoots();
81 | String sourceRootsList = Arrays.stream(vFiles)
82 | .map(VirtualFile::getUrl)
83 | .map(s -> " - " + s)
84 | .collect(Collectors.joining(String.format("%n")));
85 |
86 | notificationLogger.info(project, String.format("Detected JavaParser language level for %s:%n%s", projectName, parserConfiguration.getLanguageLevel().toString()));
87 | notificationLogger.info(project, String.format("Source roots for the %s plugin:%n%s", projectName, sourceRootsList));
88 |
89 | final AstInspectorToolWindow parseOnlyPanel = new AstInspectorToolWindow(project, toolWindow, parserConfiguration);
90 | this.addContent(toolWindow, "Parse Only", parseOnlyPanel);
91 |
92 |
93 | // Parse and Resolve Panel
94 | // ...
95 |
96 |
97 | // Parse and Export Panel
98 | // ...
99 |
100 | }
101 |
102 |
103 | }
104 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/util/Constants.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.util;
2 |
3 | import java.io.File;
4 |
5 | public final class Constants {
6 |
7 | public static final String PLUGIN_NAME = "JavaParser AST Inspector";
8 | public static final String TOOL_WINDOW_ID = "JavaParser AST Inspector"; // see plugin.xml
9 | public static final String DEFAULT_EXPORT_DIR = System.getProperty("user.home") + File.separatorChar + TOOL_WINDOW_ID;
10 |
11 | public static final String URL_GITHUB_JAVAPARSER = "https://github.com/JavaParser/JavaParser";
12 | public static final String URL_GITHUB_PLUGIN = "https://github.com/MysterAitch/JavaParser-AST-Inspector";
13 | public static final String URL_WEBSITE_JP = "http://javaparser.org/";
14 |
15 |
16 | private Constants() {
17 | // Empty private constructor, to prevent instantiation.
18 | }
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/util/EditorUtil.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.util;
2 |
3 | import com.github.javaparser.Range;
4 | import com.intellij.openapi.editor.Editor;
5 | import com.intellij.openapi.editor.LogicalPosition;
6 | import com.intellij.openapi.editor.ScrollType;
7 | import com.intellij.openapi.util.TextRange;
8 |
9 | public final class EditorUtil {
10 |
11 | private EditorUtil() {
12 | // Empty private constructor, to prevent instantiation.
13 | }
14 |
15 |
16 | public static TextRange javaParserRangeToIntellijOffsetRange(final Editor editor, final Range range) {
17 | // Note that the JavaParser Range has 1-indexed lines, while the IntelliJ LogicalPosition is 0-indexed.
18 | final LogicalPosition startPosition = new LogicalPosition(range.begin.line - 1, range.begin.column - 1); // start highlighting just before the given character/column
19 | final LogicalPosition endPosition = new LogicalPosition(range.end.line - 1, range.end.column); // end highlighting at the end of the given character/column
20 |
21 | final int startOffset = editor.logicalPositionToOffset(startPosition);
22 | final int endOffset = editor.logicalPositionToOffset(endPosition);
23 |
24 | return new TextRange(startOffset, endOffset);
25 | }
26 |
27 |
28 | public static void scrollToPosition(final Editor editor, final int offset) {
29 | // Scroll to position
30 | editor.getCaretModel().moveToOffset(offset);
31 | editor.getScrollingModel().scrollToCaret(ScrollType.CENTER);
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/util/FontUtil.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.util;
2 |
3 | import java.awt.*;
4 | import java.awt.font.TextAttribute;
5 | import java.util.HashMap;
6 | import java.util.Map;
7 |
8 | public final class FontUtil {
9 |
10 | /**
11 | * A style that represents the title font.
12 | */
13 | public static final Font TITLE_FONT;
14 |
15 | static {
16 | Map attributes = new HashMap<>();
17 |
18 | attributes.put(TextAttribute.FAMILY, Font.DIALOG);
19 | // attributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_SEMIBOLD);
20 | attributes.put(TextAttribute.WEIGHT, TextAttribute.WEIGHT_EXTRABOLD);
21 | attributes.put(TextAttribute.SIZE, 12);
22 |
23 | TITLE_FONT = Font.getFont(attributes);
24 | }
25 |
26 | private FontUtil() {
27 | // Empty private constructor, to prevent instantiation.
28 | }
29 |
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/util/LanguageLevelUtil.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.util;
2 |
3 | import com.github.javaparser.ParserConfiguration;
4 | import com.github.rogerhowell.javaparser_ast_inspector.plugin.logging.NotificationLogger;
5 | import com.intellij.openapi.project.Project;
6 | import com.intellij.openapi.roots.LanguageLevelProjectExtension;
7 | import com.intellij.pom.java.LanguageLevel;
8 | import org.jetbrains.annotations.NotNull;
9 |
10 | import java.util.ArrayList;
11 | import java.util.HashMap;
12 | import java.util.List;
13 | import java.util.Optional;
14 |
15 | public final class LanguageLevelUtil {
16 |
17 | private static final NotificationLogger notificationLogger = new NotificationLogger(LanguageLevelUtil.class);
18 |
19 | private static final HashMap languageLevelMap = new HashMap<>();
20 |
21 | static {
22 | initMappings();
23 | }
24 |
25 |
26 | private LanguageLevelUtil() {
27 | // Prevent initialisation
28 | }
29 |
30 |
31 | public static ParserConfiguration.LanguageLevel getLanguageLevelFromProject(@NotNull Project project, @NotNull ParserConfiguration.LanguageLevel defaultJpLanguageLevel) {
32 | final LanguageLevelProjectExtension languageLevelProjectExtension = LanguageLevelProjectExtension.getInstance(project);
33 | final LanguageLevel projectLanguageLevel = languageLevelProjectExtension.getLanguageLevel();
34 |
35 | return mapIntellijLanguageLevelToJavaParserLanguageLevel(projectLanguageLevel, defaultJpLanguageLevel);
36 | }
37 |
38 |
39 | public static Optional getLanguageLevelFromProject(@NotNull Project project) {
40 | final LanguageLevelProjectExtension languageLevelProjectExtension = LanguageLevelProjectExtension.getInstance(project);
41 | final LanguageLevel projectLanguageLevel = languageLevelProjectExtension.getLanguageLevel();
42 |
43 | return mapIntellijLanguageLevelToJavaParserLanguageLevel(projectLanguageLevel);
44 | }
45 |
46 |
47 | private static List getLanguageLevelNameVariants(String plainLanguageLevelName) {
48 | List variants = new ArrayList<>(10);
49 |
50 | // No prefix
51 | variants.add(plainLanguageLevelName);
52 |
53 | // JavaParser uses `JAVA_{}`
54 | variants.add("JAVA_" + plainLanguageLevelName);
55 |
56 | // IntelliJ uses `JDK_{}`.
57 | variants.add("JDK_" + plainLanguageLevelName);
58 |
59 | // Some versions have a `1.` prefix (e.g. JDK 6 is also known as 1.6, thus is listed as JDK_1_6 as opposed to JDK_6).
60 | // This is a bit of a hacky catch-all, given that not all places are consistent (e.g. JP has JAVA_6, but IntelliJ has JDK_1_6
61 | variants.add("JAVA_1_" + plainLanguageLevelName);
62 | variants.add("JDK_1_" + plainLanguageLevelName);
63 |
64 | return variants;
65 | }
66 |
67 |
68 | private static void initMappings() {
69 | /*
70 | * Iterate over all known JavaParser language levels.
71 | * Note that this will include preview versions too.
72 | */
73 | for (ParserConfiguration.LanguageLevel javaParserLanguageLevel : ParserConfiguration.LanguageLevel.values()) {
74 | String plainLanguageLevelName = javaParserLanguageLevel
75 | .name()
76 | .replace("JAVA_1_", "JAVA_")
77 | .replace("JAVA_", "");
78 |
79 | getLanguageLevelNameVariants(plainLanguageLevelName)
80 | .forEach(s -> languageLevelMap.put(s, javaParserLanguageLevel));
81 | }
82 |
83 | // IntelliJ also contains "JDK X" which includes "experimental features" - i.e., latest preview
84 | getLanguageLevelNameVariants("X")
85 | .forEach(s -> languageLevelMap.put(s, ParserConfiguration.LanguageLevel.BLEEDING_EDGE));
86 | }
87 |
88 |
89 | @NotNull
90 | public static ParserConfiguration.LanguageLevel mapIntellijLanguageLevelToJavaParserLanguageLevel(@NotNull LanguageLevel intellijLanguageLevel, @NotNull ParserConfiguration.LanguageLevel defaultJpLanguageLevel) {
91 | final String intellijLanguageLevelName = intellijLanguageLevel.name();
92 | return mapStringLanguageLevelToJavaParserLanguageLevel(intellijLanguageLevelName, defaultJpLanguageLevel);
93 | }
94 |
95 |
96 | @NotNull
97 | public static Optional mapIntellijLanguageLevelToJavaParserLanguageLevel(@NotNull LanguageLevel intellijLanguageLevel) {
98 | final String intellijLanguageLevelName = intellijLanguageLevel.name();
99 | return mapStringLanguageLevelToJavaParserLanguageLevel(intellijLanguageLevelName);
100 | }
101 |
102 |
103 | @NotNull
104 | public static ParserConfiguration.LanguageLevel mapStringLanguageLevelToJavaParserLanguageLevel(@NotNull final String languageLevelName, @NotNull ParserConfiguration.LanguageLevel defaultJpLanguageLevel) {
105 | return mapStringLanguageLevelToJavaParserLanguageLevel(languageLevelName).orElseGet(() -> {
106 | notificationLogger.warn("Mapping for IntelliJ Language Level (`" + languageLevelName + "`) not found, defaulting to JavaParser's `" + defaultJpLanguageLevel + "`.");
107 | return defaultJpLanguageLevel;
108 | });
109 | }
110 |
111 |
112 | @NotNull
113 | public static Optional mapStringLanguageLevelToJavaParserLanguageLevel(@NotNull final String languageLevelName) {
114 | ParserConfiguration.LanguageLevel mappedLanguageLevel = languageLevelMap.get(languageLevelName);
115 | return Optional.ofNullable(mappedLanguageLevel);
116 | }
117 |
118 | }
119 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/util/PsiUtil.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.util;
2 |
3 | import com.intellij.openapi.fileEditor.FileEditorManager;
4 | import com.intellij.openapi.project.Project;
5 | import com.intellij.openapi.vfs.VirtualFile;
6 | import com.intellij.psi.PsiElement;
7 | import com.intellij.psi.PsiFile;
8 | import com.intellij.psi.PsiManager;
9 | import org.jetbrains.annotations.NotNull;
10 |
11 | import java.nio.file.Path;
12 | import java.nio.file.Paths;
13 | import java.util.Objects;
14 | import java.util.Optional;
15 |
16 | public final class PsiUtil {
17 |
18 | private PsiUtil() {
19 | // Empty private constructor, to prevent instantiation.
20 | }
21 |
22 |
23 | public static Optional getCurrentFileInEditor(@NotNull Project project) {
24 | FileEditorManager manager = FileEditorManager.getInstance(project);
25 | VirtualFile[] files = manager.getSelectedFiles();
26 | if (files.length == 0) {
27 | return Optional.empty();
28 | }
29 |
30 | final VirtualFile currentFile = files[0];
31 | final PsiFile psiFile = PsiManager.getInstance(project).findFile(currentFile);
32 |
33 | return Optional.ofNullable(psiFile);
34 | }
35 |
36 |
37 | public static Optional getInputText(@NotNull Project project) {
38 | final Optional psiFile = getCurrentFileInEditor(project);
39 | return psiFile.map(PsiElement::getText);
40 | }
41 |
42 |
43 | public static Path pathForPsi(@NotNull PsiFile psiFile) {
44 | Objects.requireNonNull(psiFile);
45 |
46 | final VirtualFile virtualFile = Objects.requireNonNull(psiFile.getVirtualFile());
47 | final String canonicalPathString = virtualFile.getCanonicalPath();
48 | if (canonicalPathString == null) {
49 | throw new IllegalStateException("Unable to get the path because the canonical path to the PSI virtual file is null.");
50 | }
51 |
52 | return Paths.get(canonicalPathString);
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/src/main/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/util/StringUtil.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.util;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 |
5 | public final class StringUtil {
6 |
7 |
8 | private StringUtil() {
9 | // Empty private constructor, to prevent instantiation.
10 | }
11 |
12 | /**
13 | * @param input The text to have padding applied.
14 | * @param desiredMinimumLength The minimum length of the output.
15 | * @return When the input length is > desired minimum length, the input.
16 | * Otherwise the input padded with spaces at the END, up to the desiredMinimumLength
17 | */
18 | public static String padEnd(String input, int desiredMinimumLength) {
19 | if (input.length() > desiredMinimumLength) {
20 | return input;
21 | }
22 |
23 | StringBuilder inputBuilder = new StringBuilder(input);
24 | while (inputBuilder.length() < desiredMinimumLength) {
25 | inputBuilder.append(" ");
26 | }
27 |
28 | return inputBuilder.toString();
29 | }
30 |
31 | /**
32 | * @param input The text to have padding applied.
33 | * @param desiredMinimumLength The minimum length of the output.
34 | * @return When the input length is > desired minimum length, the input.
35 | * Otherwise the input padded with spaces at the START, up to the desiredMinimumLength
36 | */
37 | public static String padStart(String input, int desiredMinimumLength) {
38 | if (input.length() > desiredMinimumLength) {
39 | return input;
40 | }
41 |
42 | StringBuilder inputBuilder = new StringBuilder(input);
43 | while (inputBuilder.length() < desiredMinimumLength) {
44 | inputBuilder.insert(0, " ");
45 | }
46 |
47 | return inputBuilder.toString();
48 | }
49 |
50 | @SuppressWarnings({"HardcodedFileSeparator"})
51 | @NotNull
52 | public static String escapeTab(String string) {
53 | return string.replace("\t", "\\t");
54 | }
55 |
56 | @SuppressWarnings({"HardcodedLineSeparator", "HardcodedFileSeparator"})
57 | @NotNull
58 | public static String escapeNewlines(String string) {
59 | return string
60 | .replace("\n", "\\n")
61 | .replace("\r", "\\r")
62 | .replace("\r\n", "\\r\\n");
63 | }
64 |
65 | @NotNull
66 | public static String escapeWhitespace(String string) {
67 | return escapeTab(escapeNewlines(string));
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/resources/JavaCodeBrowser/graph icon -- 13x13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MysterAitch/JavaParser-AST-Inspector/092824ea47a6799161f46f267fc4774db516ce8c/src/main/resources/JavaCodeBrowser/graph icon -- 13x13.png
--------------------------------------------------------------------------------
/src/main/resources/JavaCodeBrowser/graph icon -- error -- 13x13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MysterAitch/JavaParser-AST-Inspector/092824ea47a6799161f46f267fc4774db516ce8c/src/main/resources/JavaCodeBrowser/graph icon -- error -- 13x13.png
--------------------------------------------------------------------------------
/src/main/resources/JavaCodeBrowser/graph icon.xcf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MysterAitch/JavaParser-AST-Inspector/092824ea47a6799161f46f267fc4774db516ce8c/src/main/resources/JavaCodeBrowser/graph icon.xcf
--------------------------------------------------------------------------------
/src/main/resources/META-INF/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 | com.github.rogerhowell.JavaCodeBrowser
3 | JavaParser AST Inspector
4 | Roger Howell
5 |
6 |
7 |
8 |
9 |
10 |
11 | com.intellij.modules.platform
12 |
13 |
14 |
15 |
16 | com.intellij.java
17 |
18 |
19 |
20 |
21 |
27 |
28 |
29 |
30 |
32 |
34 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/main/resources/logos/jp-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MysterAitch/JavaParser-AST-Inspector/092824ea47a6799161f46f267fc4774db516ce8c/src/main/resources/logos/jp-logo.png
--------------------------------------------------------------------------------
/src/main/resources/logos/jp-logo_13x13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MysterAitch/JavaParser-AST-Inspector/092824ea47a6799161f46f267fc4774db516ce8c/src/main/resources/logos/jp-logo_13x13.png
--------------------------------------------------------------------------------
/src/test/java/com/github/rogerhowell/javaparser_ast_inspector/plugin/util/StringUtilTest.java:
--------------------------------------------------------------------------------
1 | package com.github.rogerhowell.javaparser_ast_inspector.plugin.util;
2 |
3 | import org.junit.jupiter.api.Assertions;
4 | import org.junit.jupiter.api.Test;
5 |
6 | public class StringUtilTest {
7 |
8 | @Test
9 | public void padEndTest0() {
10 | String input = "ABC";
11 | String output = StringUtil.padEnd(input, 0);
12 | Assertions.assertEquals("ABC", output);
13 | }
14 | @Test
15 | public void padEndTest1() {
16 | String input = "ABC";
17 | String output = StringUtil.padEnd(input, 1);
18 | Assertions.assertEquals("ABC", output);
19 | }
20 | @Test
21 | public void padEndTest2() {
22 | String input = "ABC";
23 | String output = StringUtil.padEnd(input, 2);
24 | Assertions.assertEquals("ABC", output);
25 | }
26 | @Test
27 | public void padEndTest3() {
28 | String input = "ABC";
29 | String output = StringUtil.padEnd(input, 3);
30 | Assertions.assertEquals("ABC", output);
31 | }
32 | @Test
33 | public void padEndTest4() {
34 | String input = "ABC";
35 | String output = StringUtil.padEnd(input, 4);
36 | Assertions.assertEquals("ABC ", output);
37 | }
38 | @Test
39 | public void padEndTest5() {
40 | String input = "ABC";
41 | String output = StringUtil.padEnd(input, 5);
42 | Assertions.assertEquals("ABC ", output);
43 | }
44 | @Test
45 | public void padEndTest10() {
46 | String input = "ABC";
47 | String output = StringUtil.padEnd(input, 10);
48 | Assertions.assertEquals("ABC ", output);
49 | }
50 | @Test
51 | public void padStartTest0() {
52 | String input = "ABC";
53 | String output = StringUtil.padStart(input, 0);
54 | Assertions.assertEquals("ABC", output);
55 | }
56 | @Test
57 | public void padStartTest1() {
58 | String input = "ABC";
59 | String output = StringUtil.padStart(input, 1);
60 | Assertions.assertEquals("ABC", output);
61 | }
62 | @Test
63 | public void padStartTest2() {
64 | String input = "ABC";
65 | String output = StringUtil.padStart(input, 2);
66 | Assertions.assertEquals("ABC", output);
67 | }
68 | @Test
69 | public void padStartTest3() {
70 | String input = "ABC";
71 | String output = StringUtil.padStart(input, 3);
72 | Assertions.assertEquals("ABC", output);
73 | }
74 | @Test
75 | public void padStartTest4() {
76 | String input = "ABC";
77 | String output = StringUtil.padStart(input, 4);
78 | Assertions.assertEquals(" ABC", output);
79 | }
80 | @Test
81 | public void padStartTest5() {
82 | String input = "ABC";
83 | String output = StringUtil.padStart(input, 5);
84 | Assertions.assertEquals(" ABC", output);
85 | }
86 | @Test
87 | public void padStartTest10() {
88 | String input = "ABC";
89 | String output = StringUtil.padStart(input, 10);
90 | Assertions.assertEquals(" ABC", output);
91 | }
92 | }
93 |
--------------------------------------------------------------------------------