/* * Copyright (C) 2009-2011 The Project Lombok Authors. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package lombok.eclipse; import static lombok.eclipse.handlers.EclipseHandlerUtil.isGenerated; import java.io.PrintStream; import java.lang.reflect.Modifier; import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration; import org.eclipse.jdt.internal.compiler.ast.Annotation; import org.eclipse.jdt.internal.compiler.ast.Argument; import org.eclipse.jdt.internal.compiler.ast.Block; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration; import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration; import org.eclipse.jdt.internal.compiler.ast.Initializer; import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration; import org.eclipse.jdt.internal.compiler.ast.Statement; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeReference; /** * Implement so you can ask any JavacAST.Node to traverse depth-first through all children, * calling the appropriate visit and endVisit methods. */ public interface EclipseASTVisitor { /** * Return true if this handler should not be run in the diet parse phase. * 'diet parse' is where method bodies aren't filled in yet. If you have a method-level annotation that modifies the contents of that method, * return {@code true} here. Otherwise, return {@code false} here. */ boolean deferUntilPostDiet(); /** * Called at the very beginning and end. */ void visitCompilationUnit(EclipseNode top, CompilationUnitDeclaration unit); void endVisitCompilationUnit(EclipseNode top, CompilationUnitDeclaration unit); /** * Called when visiting a type (a class, interface, annotation, enum, etcetera). */ void visitType(EclipseNode typeNode, TypeDeclaration type); void visitAnnotationOnType(TypeDeclaration type, EclipseNode annotationNode, Annotation annotation); void endVisitType(EclipseNode typeNode, TypeDeclaration type); /** * Called when visiting a field of a class. * Even though in Eclipse initializers (both instance and static) are represented as Initializer objects, * which are a subclass of FieldDeclaration, those do NOT result in a call to this method. They result * in a call to the visitInitializer method. */ void visitField(EclipseNode fieldNode, FieldDeclaration field); void visitAnnotationOnField(FieldDeclaration field, EclipseNode annotationNode, Annotation annotation); void endVisitField(EclipseNode fieldNode, FieldDeclaration field); /** * Called for static and instance initializers. You can tell the difference via the modifier flag on the * ASTNode (8 for static, 0 for not static). The content is in the 'block', not in the 'initialization', * which would always be null for an initializer instance. */ void visitInitializer(EclipseNode initializerNode, Initializer initializer); void endVisitInitializer(EclipseNode initializerNode, Initializer initializer); /** * Called for both methods (MethodDeclaration) and constructors (ConstructorDeclaration), but not for * Clinit objects, which are a vestigial Eclipse thing that never contain anything. Static initializers * show up as 'Initializer', in the visitInitializer method, with modifier bit STATIC set. */ void visitMethod(EclipseNode methodNode, AbstractMethodDeclaration method); void visitAnnotationOnMethod(AbstractMethodDeclaration method, EclipseNode annotationNode, Annotation annotation); void endVisitMethod(EclipseNode methodNode, AbstractMethodDeclaration method); /** * Visits a method argument */ void visitMethodArgument(EclipseNode argNode, Argument arg, AbstractMethodDeclaration method); void visitAnnotationOnMethodArgument(Argument arg, AbstractMethodDeclaration method, EclipseNode annotationNode, Annotation annotation); void endVisitMethodArgument(EclipseNode argNode, Argument arg, AbstractMethodDeclaration method); /** * Visits a local declaration - that is, something like 'int x = 10;' on the method level. */ void visitLocal(EclipseNode localNode, LocalDeclaration local); void visitAnnotationOnLocal(LocalDeclaration local, EclipseNode annotationNode, Annotation annotation); void endVisitLocal(EclipseNode localNode, LocalDeclaration local); /** * Visits a statement that isn't any of the other visit methods (e.g. TypeDeclaration). */ void visitStatement(EclipseNode statementNode, Statement statement); void endVisitStatement(EclipseNode statementNode, Statement statement); /** * Prints the structure of an AST. */ public static class Printer implements EclipseASTVisitor { private final PrintStream out; private final boolean printContent; private int disablePrinting = 0; private int indent = 0; private boolean printClassNames = false; public boolean deferUntilPostDiet() { return false; } /** * @param printContent if true, bodies are printed directly, as java code, * instead of a tree listing of every AST node inside it. */ public Printer(boolean printContent) { this(printContent, System.out); } /** * @param printContent if true, bodies are printed directly, as java code, * instead of a tree listing of every AST node inside it. * @param out write output to this stream. You must close it yourself. flush() is called after every line. * * @see java.io.PrintStream#flush() */ public Printer(boolean printContent, PrintStream out) { this.printContent = printContent; this.out = out; } private void forcePrint(String text, Object... params) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < indent; i++) sb.append(" "); sb.append(text); Object[] t; if (printClassNames && params.length > 0) { sb.append(" ["); for (int i = 0; i < params.length; i++) { if (i > 0) sb.append(", "); sb.append("%s"); } sb.append("]"); t = new Object[params.length + params.length]; for (int i = 0; i < params.length; i++) { t[i] = params[i]; t[i + params.length] = (params[i] == null) ? "NULL " : params[i].getClass(); } } else { t = params; } sb.append("\n"); out.printf(sb.toString(), t); out.flush(); } private void print(String text, Object... params) { if (disablePrinting == 0) forcePrint(text, params); } private String str(char[] c) { if (c == null) return "(NULL)"; return new String(c); } private String str(TypeReference type) { if (type == null) return "(NULL)"; char[][] c = type.getTypeName(); StringBuilder sb = new StringBuilder(); boolean first = true; for (char[] d : c) { sb.append(first ? "" : ".").append(new String(d)); first = false; } return sb.toString(); } public void visitCompilationUnit(EclipseNode node, CompilationUnitDeclaration unit) { out.println("---------------------------------------------------------"); out.println(node.isCompleteParse() ? "COMPLETE" : "incomplete"); print("", node.getFileName(), isGenerated(unit) ? " (GENERATED)" : ""); indent++; } public void endVisitCompilationUnit(EclipseNode node, CompilationUnitDeclaration unit) { indent--; print(""); } public void visitType(EclipseNode node, TypeDeclaration type) { print("", str(type.name), isGenerated(type) ? " (GENERATED)" : ""); indent++; if (printContent) { print("%s", type); disablePrinting++; } } public void visitAnnotationOnType(TypeDeclaration type, EclipseNode node, Annotation annotation) { forcePrint("", isGenerated(annotation) ? " (GENERATED)" : "", annotation); } public void endVisitType(EclipseNode node, TypeDeclaration type) { if (printContent) disablePrinting--; indent--; print("", str(type.name)); } public void visitInitializer(EclipseNode node, Initializer initializer) { Block block = initializer.block; boolean s = (block != null && block.statements != null); print("<%s INITIALIZER: %s%s>", (initializer.modifiers & Modifier.STATIC) != 0 ? "static" : "instance", s ? "filled" : "blank", isGenerated(initializer) ? " (GENERATED)" : ""); indent++; if (printContent) { if (initializer.block != null) print("%s", initializer.block); disablePrinting++; } } public void endVisitInitializer(EclipseNode node, Initializer initializer) { if (printContent) disablePrinting--; indent--; print("", (initializer.modifiers & Modifier.STATIC) != 0 ? "static" : "instance"); } public void visitField(EclipseNode node, FieldDeclaration field) { print("", isGenerated(field) ? " (GENERATED)" : "", str(field.type), str(field.name), field.initialization); indent++; if (printContent) { if (field.initialization != null) print("%s", field.initialization); disablePrinting++; } } public void visitAnnotationOnField(FieldDeclaration field, EclipseNode node, Annotation annotation) { forcePrint("", isGenerated(annotation) ? " (GENERATED)" : "", annotation); } public void endVisitField(EclipseNode node, FieldDeclaration field) { if (printContent) disablePrinting--; indent--; print("", str(field.type), str(field.name)); } public void visitMethod(EclipseNode node, AbstractMethodDeclaration method) { String type = method instanceof ConstructorDeclaration ? "CONSTRUCTOR" : "METHOD"; print("<%s %s: %s%s>", type, str(method.selector), method.statements != null ? "filled" : "blank", isGenerated(method) ? " (GENERATED)" : ""); indent++; if (printContent) { if (method.statements != null) print("%s", method); disablePrinting++; } } public void visitAnnotationOnMethod(AbstractMethodDeclaration method, EclipseNode node, Annotation annotation) { forcePrint("", isGenerated(method) ? " (GENERATED)" : "", annotation); } public void endVisitMethod(EclipseNode node, AbstractMethodDeclaration method) { if (printContent) disablePrinting--; String type = method instanceof ConstructorDeclaration ? "CONSTRUCTOR" : "METHOD"; indent--; print("", type, str(method.selector)); } public void visitMethodArgument(EclipseNode node, Argument arg, AbstractMethodDeclaration method) { print("", isGenerated(arg) ? " (GENERATED)" : "", str(arg.type), str(arg.name), arg.initialization); indent++; } public void visitAnnotationOnMethodArgument(Argument arg, AbstractMethodDeclaration method, EclipseNode node, Annotation annotation) { print("", isGenerated(annotation) ? " (GENERATED)" : "", annotation); } public void endVisitMethodArgument(EclipseNode node, Argument arg, AbstractMethodDeclaration method) { indent--; print("", str(arg.type), str(arg.name)); } public void visitLocal(EclipseNode node, LocalDeclaration local) { print("", isGenerated(local) ? " (GENERATED)" : "", str(local.type), str(local.name), local.initialization); indent++; } public void visitAnnotationOnLocal(LocalDeclaration local, EclipseNode node, Annotation annotation) { print("", isGenerated(annotation) ? " (GENERATED)" : "", annotation); } public void endVisitLocal(EclipseNode node, LocalDeclaration local) { indent--; print("", str(local.type), str(local.name)); } public void visitStatement(EclipseNode node, Statement statement) { print("<%s%s>", statement.getClass(), isGenerated(statement) ? " (GENERATED)" : ""); indent++; print("%s", statement); } public void endVisitStatement(EclipseNode node, Statement statement) { indent--; print("", statement.getClass()); } } }