Java tutorial
/* * Copyright (C) 2010-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.ast.javac; import static lombok.ast.ConversionPositionInfo.setConversionPositionInfo; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collections; import java.util.Map; import java.util.NoSuchElementException; import java.util.TreeMap; import lombok.val; import lombok.ast.AlternateConstructorInvocation; import lombok.ast.Annotation; import lombok.ast.AnnotationDeclaration; import lombok.ast.AnnotationElement; import lombok.ast.AnnotationMethodDeclaration; import lombok.ast.ArrayAccess; import lombok.ast.ArrayCreation; import lombok.ast.ArrayDimension; import lombok.ast.ArrayInitializer; import lombok.ast.Assert; import lombok.ast.BinaryExpression; import lombok.ast.BinaryOperator; import lombok.ast.Block; import lombok.ast.BooleanLiteral; import lombok.ast.Break; import lombok.ast.Case; import lombok.ast.Cast; import lombok.ast.Catch; import lombok.ast.CharLiteral; import lombok.ast.ClassDeclaration; import lombok.ast.ClassLiteral; import lombok.ast.Comment; import lombok.ast.CompilationUnit; import lombok.ast.ConstructorDeclaration; import lombok.ast.ConstructorInvocation; import lombok.ast.Continue; import lombok.ast.ConversionPositionInfo; import lombok.ast.Default; import lombok.ast.DoWhile; import lombok.ast.EmptyDeclaration; import lombok.ast.EmptyStatement; import lombok.ast.EnumConstant; import lombok.ast.EnumDeclaration; import lombok.ast.EnumTypeBody; import lombok.ast.Expression; import lombok.ast.ExpressionStatement; import lombok.ast.FloatingPointLiteral; import lombok.ast.For; import lombok.ast.ForEach; import lombok.ast.ForwardingAstVisitor; import lombok.ast.Identifier; import lombok.ast.If; import lombok.ast.ImportDeclaration; import lombok.ast.InlineIfExpression; import lombok.ast.InstanceInitializer; import lombok.ast.InstanceOf; import lombok.ast.IntegralLiteral; import lombok.ast.InterfaceDeclaration; import lombok.ast.JavadocContainer; import lombok.ast.KeywordModifier; import lombok.ast.LabelledStatement; import lombok.ast.MethodDeclaration; import lombok.ast.MethodInvocation; import lombok.ast.Modifiers; import lombok.ast.Node; import lombok.ast.NormalTypeBody; import lombok.ast.NullLiteral; import lombok.ast.PackageDeclaration; import lombok.ast.Position; import lombok.ast.RawListAccessor; import lombok.ast.Return; import lombok.ast.Select; import lombok.ast.StaticInitializer; import lombok.ast.StrictListAccessor; import lombok.ast.StringLiteral; import lombok.ast.Super; import lombok.ast.SuperConstructorInvocation; import lombok.ast.Switch; import lombok.ast.Synchronized; import lombok.ast.This; import lombok.ast.Throw; import lombok.ast.Try; import lombok.ast.TypeDeclaration; import lombok.ast.TypeReference; import lombok.ast.TypeReferencePart; import lombok.ast.TypeVariable; import lombok.ast.UnaryExpression; import lombok.ast.UnaryOperator; import lombok.ast.VariableDeclaration; import lombok.ast.VariableDefinition; import lombok.ast.VariableDefinitionEntry; import lombok.ast.VariableReference; import lombok.ast.While; import lombok.ast.WildcardKind; import lombok.javac.CommentInfo; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.sun.tools.javac.code.Flags; import com.sun.tools.javac.tree.JCTree; import com.sun.tools.javac.tree.JCTree.JCAnnotation; import com.sun.tools.javac.tree.JCTree.JCArrayAccess; import com.sun.tools.javac.tree.JCTree.JCArrayTypeTree; import com.sun.tools.javac.tree.JCTree.JCAssert; import com.sun.tools.javac.tree.JCTree.JCAssign; import com.sun.tools.javac.tree.JCTree.JCAssignOp; import com.sun.tools.javac.tree.JCTree.JCBinary; import com.sun.tools.javac.tree.JCTree.JCBlock; import com.sun.tools.javac.tree.JCTree.JCBreak; import com.sun.tools.javac.tree.JCTree.JCCase; import com.sun.tools.javac.tree.JCTree.JCCatch; import com.sun.tools.javac.tree.JCTree.JCClassDecl; import com.sun.tools.javac.tree.JCTree.JCCompilationUnit; import com.sun.tools.javac.tree.JCTree.JCConditional; import com.sun.tools.javac.tree.JCTree.JCContinue; import com.sun.tools.javac.tree.JCTree.JCDoWhileLoop; import com.sun.tools.javac.tree.JCTree.JCEnhancedForLoop; import com.sun.tools.javac.tree.JCTree.JCExpression; import com.sun.tools.javac.tree.JCTree.JCExpressionStatement; import com.sun.tools.javac.tree.JCTree.JCFieldAccess; import com.sun.tools.javac.tree.JCTree.JCForLoop; import com.sun.tools.javac.tree.JCTree.JCIdent; import com.sun.tools.javac.tree.JCTree.JCIf; import com.sun.tools.javac.tree.JCTree.JCImport; import com.sun.tools.javac.tree.JCTree.JCInstanceOf; import com.sun.tools.javac.tree.JCTree.JCLabeledStatement; import com.sun.tools.javac.tree.JCTree.JCLiteral; import com.sun.tools.javac.tree.JCTree.JCMethodDecl; import com.sun.tools.javac.tree.JCTree.JCMethodInvocation; import com.sun.tools.javac.tree.JCTree.JCModifiers; import com.sun.tools.javac.tree.JCTree.JCNewArray; import com.sun.tools.javac.tree.JCTree.JCNewClass; import com.sun.tools.javac.tree.JCTree.JCParens; import com.sun.tools.javac.tree.JCTree.JCPrimitiveTypeTree; import com.sun.tools.javac.tree.JCTree.JCReturn; import com.sun.tools.javac.tree.JCTree.JCSkip; import com.sun.tools.javac.tree.JCTree.JCStatement; import com.sun.tools.javac.tree.JCTree.JCSwitch; import com.sun.tools.javac.tree.JCTree.JCSynchronized; import com.sun.tools.javac.tree.JCTree.JCThrow; import com.sun.tools.javac.tree.JCTree.JCTry; import com.sun.tools.javac.tree.JCTree.JCTypeApply; import com.sun.tools.javac.tree.JCTree.JCTypeCast; import com.sun.tools.javac.tree.JCTree.JCTypeParameter; import com.sun.tools.javac.tree.JCTree.JCUnary; import com.sun.tools.javac.tree.JCTree.JCVariableDecl; import com.sun.tools.javac.tree.JCTree.JCWhileLoop; import com.sun.tools.javac.tree.JCTree.JCWildcard; import com.sun.tools.javac.tree.JCTree.TypeBoundKind; import com.sun.tools.javac.util.List; import com.sun.tools.javac.util.ListBuffer; public class JcTreeConverter { private enum FlagKey { BLOCKS_ARE_INITIALIZERS, SKIP_IS_DECL, VARDEF_IS_DEFINITION, NO_VARDECL_FOLDING, CONTAINING_TYPE_NAME, TYPE_REFERENCE, METHODS_ARE_ANNMETHODS; } private java.util.List<? extends Node> result; private Map<JCTree, Integer> endPosTable; private ConvertingVisitor visitor = new ConvertingVisitor(); private Map<FlagKey, Object> params; private static final Field JCWILDCARD_KIND, JCTREE_TAG; private static final Method JCTREE_GETTAG; static { Field f; Method m; f = null; try { f = JCWildcard.class.getDeclaredField("kind"); } catch (NoSuchFieldException e) { } JCWILDCARD_KIND = f; f = null; try { f = JCTree.class.getDeclaredField("tag"); } catch (NoSuchFieldException e) { } JCTREE_TAG = f; m = null; try { m = JCTree.class.getDeclaredMethod("getTag"); } catch (NoSuchMethodException e) { } JCTREE_GETTAG = m; } public JcTreeConverter() { this(null, null); } public JcTreeConverter(Map<JCTree, Integer> endPosTable, Map<FlagKey, Object> params) { this.endPosTable = endPosTable; this.params = params == null ? ImmutableMap.<FlagKey, Object>of() : params; } private boolean hasFlag(FlagKey key) { return params.containsKey(key); } private Object getFlag(FlagKey key) { return params.get(key); } java.util.List<? extends Node> getAll() { return result; } Node get() { if (result.isEmpty()) { return null; } if (result.size() == 1) { return result.get(0); } throw new RuntimeException("Expected only one result but got " + result.size()); } private void set(JCTree node, Node value) { if (result != null) throw new IllegalStateException("result is already set"); if (value != null && value.getPosition().isUnplaced()) setPos(node, value); java.util.List<Node> result = Lists.newArrayList(); if (value != null) result.add(value); this.result = result; } private Node toTree(JCTree node, FlagKey... keys) { Map<FlagKey, Object> map = Maps.newEnumMap(FlagKey.class); for (FlagKey key : keys) map.put(key, key); return toTree(node, map); } private Node toTree(JCTree node, Map<FlagKey, Object> params) { if (node == null) return null; JcTreeConverter newConverter = new JcTreeConverter(endPosTable, params); node.accept(newConverter.visitor); try { return newConverter.get(); } catch (RuntimeException e) { System.err.printf("Node '%s' (%s) did not produce any results\n", node, node.getClass().getSimpleName()); throw e; } } private void addJavadoc(JavadocContainer container, JCModifiers mods) { if ((mods.flags & Flags.DEPRECATED) != 0) { container.astJavadoc(new Comment().astBlockComment(true).astContent("*\n * @deprecated\n ")); } } private Node toVariableDefinition(java.util.List<JCVariableDecl> decls, FlagKey... keys) { Map<FlagKey, Object> map = Maps.newEnumMap(FlagKey.class); for (FlagKey key : keys) map.put(key, key); return toVariableDefinition(decls, map); } private Position getPosition(JCTree node) { if (node == null) return Position.UNPLACED; int start = node.pos; Integer end_ = null; if (endPosTable != null) end_ = endPosTable.get(node); int end = end_ == null ? node.getEndPosition(endPosTable) : end_; return new Position(start, end); } private Node toVariableDefinition(java.util.List<JCVariableDecl> decls, Map<FlagKey, Object> keys) { boolean createDeclaration = !keys.containsKey(FlagKey.VARDEF_IS_DEFINITION); if (decls == null || decls.isEmpty()) { VariableDefinition def = new VariableDefinition(); return createDeclaration ? new VariableDeclaration().astDefinition(def) : def; } JCVariableDecl first = decls.get(0); int startPosFirst = first.pos; JCExpression baseType = first.vartype; while (baseType instanceof JCArrayTypeTree) { // if written as int[] a[], b; then the base Type is *NOT* a's type, but a's type with any number of JCATT's dewrapped. // The only way to tell the difference is by checking positions, unfortunately: If end pos of type is after start pos of decl, it's a split. int endPosType = baseType.getEndPosition(endPosTable); if (endPosType != -1 && startPosFirst != -1 && endPosType > startPosFirst) { baseType = ((JCArrayTypeTree) baseType).elemtype; } else { break; } } VariableDefinition def = new VariableDefinition(); def.astModifiers((Modifiers) toTree(first.mods)); setPos(decls.get(decls.size() - 1), def); if (decls.size() > 1) def.setPosition(new Position(startPosFirst, def.getPosition().getEnd())); int baseDims = countDims(baseType); if ((first.mods.flags & Flags.VARARGS) != 0) { def.astVarargs(true); setConversionPositionInfo(def, "...", getPosition(baseType)); if (baseType instanceof JCArrayTypeTree) baseType = ((JCArrayTypeTree) baseType).elemtype; } def.rawTypeReference(toTree(baseType, FlagKey.TYPE_REFERENCE)); def.astVarargs((first.mods.flags & Flags.VARARGS) != 0); for (JCVariableDecl varDecl : decls) { int extraDims = countDims(varDecl.vartype) - baseDims; VariableDefinitionEntry entry = new VariableDefinitionEntry(); entry.astArrayDimensions(extraDims); entry.astName(setPos(varDecl, new Identifier().astValue(varDecl.name.toString()))); entry.rawInitializer(toTree(varDecl.init)); setPos(varDecl, entry); if (extraDims > 0) { JCArrayTypeTree arrayType = (JCArrayTypeTree) varDecl.vartype; for (int i = 0; i < extraDims; i++) { if (arrayType != null) setConversionPositionInfo(entry, "[]" + (extraDims - i - 1), getPosition(arrayType)); arrayType = arrayType.elemtype instanceof JCArrayTypeTree ? (JCArrayTypeTree) arrayType.elemtype : null; } } def.astVariables().addToEnd(entry); } if (createDeclaration) { VariableDeclaration decl = new VariableDeclaration().astDefinition(def); decl.setPosition(def.getPosition()); addJavadoc(decl, first.mods); return decl; } return def; } private static int countDims(JCExpression type) { int dims = 0; while (type instanceof JCArrayTypeTree) { type = ((JCArrayTypeTree) type).elemtype; dims++; } return dims; } private void fillList(java.util.List<? extends JCTree> nodes, RawListAccessor<?, ?> list, FlagKey... keys) { Map<FlagKey, Object> map = Maps.newEnumMap(FlagKey.class); for (FlagKey key : keys) map.put(key, key); fillList(nodes, list, map); } private void fillList(java.util.List<? extends JCTree> nodes, RawListAccessor<?, ?> list, Map<FlagKey, Object> keys) { if (nodes == null) return; // int i, j; is represented with multiple JCVariableDeclarations, but in lombok.ast, it's 1 node. We need to // gather up sequential JCVD nodes, check if their modifier objects are == equal, and call a special method // to convert them. java.util.List<JCVariableDecl> varDeclQueue = new ArrayList<JCVariableDecl>(); boolean fold = !keys.containsKey(FlagKey.NO_VARDECL_FOLDING); for (JCTree node : nodes) { if (node instanceof JCVariableDecl) { if (fold && (varDeclQueue.isEmpty() || varDeclQueue.get(0).mods == ((JCVariableDecl) node).mods)) { varDeclQueue.add((JCVariableDecl) node); continue; } else { if (!varDeclQueue.isEmpty()) list.addToEnd(toVariableDefinition(varDeclQueue, keys)); varDeclQueue.clear(); varDeclQueue.add((JCVariableDecl) node); continue; } } if (!varDeclQueue.isEmpty()) list.addToEnd(toVariableDefinition(varDeclQueue, keys)); varDeclQueue.clear(); list.addToEnd(toTree(node, keys)); } if (!varDeclQueue.isEmpty()) list.addToEnd(toVariableDefinition(varDeclQueue, keys)); } private static JCTree removeParens(JCTree node) { if (!(node instanceof JCParens)) return node; return ((JCParens) node).getExpression(); } public void visit(JCCompilationUnit cu) { visit(cu, cu.endPositions); } public void visit(JCTree node, Map<JCTree, Integer> endPosTable) { this.endPosTable = endPosTable; node.accept(visitor); } public Node getResult() { return get(); } public Node getResultWithJavadoc(java.util.List<CommentInfo> comments) { ListBuffer<CommentInfo> javadocs = ListBuffer.lb(); for (CommentInfo commentInfo : comments) { if (commentInfo.isJavadoc()) javadocs.append(commentInfo); } Node result = getResult(); if (javadocs.isEmpty()) return result; val nodePositions = new TreeMap<Integer, Node>(); result.accept(new ForwardingAstVisitor() { private void addToMap(Node positionNode, Node linked) { if (positionNode == null) return; int start = positionNode.getPosition().getStart(); if (start == -1) return; if (linked instanceof JavadocContainer || !nodePositions.containsKey(start)) { nodePositions.put(start, linked); } } private void addToMap(StrictListAccessor<?, ?> nodes, Node linked) { if (nodes == null) return; for (Node node : nodes) { addToMap(node, linked); } } @Override public boolean visitNode(Node node) { int start = node.getPosition().getStart(); addToMap(node, node); try { if (node instanceof VariableDeclaration) { addToMap(((VariableDeclaration) node).astDefinition().astModifiers(), node); addToMap(((VariableDeclaration) node).astDefinition().astModifiers().astAnnotations(), node); addToMap(((VariableDeclaration) node).astDefinition().astTypeReference(), node); addToMap(((VariableDeclaration) node).astDefinition().astVariables().first().astName(), node); } else if (node instanceof TypeDeclaration) { addToMap(((TypeDeclaration) node).astModifiers(), node); addToMap(((TypeDeclaration) node).astModifiers().astAnnotations(), node); } else if (node instanceof MethodDeclaration) { addToMap(((MethodDeclaration) node).astModifiers(), node); addToMap(((MethodDeclaration) node).astModifiers().astAnnotations(), node); addToMap(((MethodDeclaration) node).astTypeVariables().first(), node); addToMap(((MethodDeclaration) node).astReturnTypeReference(), node); addToMap(((MethodDeclaration) node).astMethodName(), node); } else if (node instanceof ConstructorDeclaration) { addToMap(((ConstructorDeclaration) node).astModifiers(), node); addToMap(((ConstructorDeclaration) node).astModifiers().astAnnotations(), node); addToMap(((ConstructorDeclaration) node).astTypeVariables().first(), node); addToMap(((ConstructorDeclaration) node).astTypeName(), node); } else if (node instanceof EnumConstant) { addToMap(((EnumConstant) node).astName(), node); addToMap(((EnumConstant) node).astAnnotations(), node); } else if (node instanceof AnnotationMethodDeclaration) { addToMap(((AnnotationMethodDeclaration) node).astModifiers(), node); addToMap(((AnnotationMethodDeclaration) node).astModifiers().astAnnotations(), node); addToMap(((AnnotationMethodDeclaration) node).astReturnTypeReference(), node); addToMap(((AnnotationMethodDeclaration) node).astMethodName(), node); } else if (node instanceof PackageDeclaration) { addToMap(((PackageDeclaration) node).astAnnotations(), node); } } catch (NullPointerException e) { // Syntax errors in file - javadoc won't get linked up correctly. } if (node instanceof JavadocContainer || !nodePositions.containsKey(start)) { nodePositions.put(start, node); } return false; } }); for (CommentInfo javadoc : javadocs) { try { Integer key = nodePositions.tailMap(javadoc.endPos).firstKey(); Node node = nodePositions.get(key); if (node instanceof JavadocContainer) { attachJavadocToNode(javadoc, (JavadocContainer) node); } } catch (NoSuchElementException e) { // Then the javadoc obviously is 'floating', as its the last thing in the file. } } return result; } private void attachJavadocToNode(CommentInfo javadoc, JavadocContainer node) { String content = javadoc.content; if (content.startsWith("/*") && content.endsWith("*/")) content = content.substring(2, content.length() - 2); Comment comment = new Comment().astBlockComment(true).astContent(content); comment.setPosition(new Position(javadoc.pos, javadoc.endPos)); node.astJavadoc(comment); if (!node.getPosition().isUnplaced()) { int oldStart = node.getPosition().getStart(); if (oldStart == -1 || comment.getPosition().getStart() < oldStart) { node.setPosition(new Position(comment.getPosition().getStart(), node.getPosition().getEnd())); } } } private <N extends Node> N setPos(JCTree node, N astNode) { if (astNode != null && node != null) { int start = node.pos; Integer end_ = endPosTable.get(node); if (node instanceof JCUnary) end_ = node.getEndPosition(endPosTable); int end = end_ == null ? node.getEndPosition(endPosTable) : end_; if (start != com.sun.tools.javac.util.Position.NOPOS && end != com.sun.tools.javac.util.Position.NOPOS) { astNode.setPosition(new Position(start, end)); } } return astNode; } private void fillWithIdentifiers(JCTree node, StrictListAccessor<Identifier, ?> list) { if (node instanceof JCIdent) { JCIdent id = (JCIdent) node; list.addToEnd(setPos(node, new Identifier().astValue(id.name.toString()))); } else if (node instanceof JCFieldAccess) { JCFieldAccess sel = (JCFieldAccess) node; fillWithIdentifiers(sel.selected, list); list.addToEnd(setPos(node, new Identifier().astValue(sel.name.toString()))); } } private static void setConversionStructureInfo(Node node, String key) { ConversionPositionInfo.setConversionPositionInfo(node, key, Position.UNPLACED); } private class ConvertingVisitor extends JCTree.Visitor { @Override public void visitTree(JCTree node) { throw new UnsupportedOperationException("visit" + node.getClass().getSimpleName() + " not implemented"); } @Override public void visitTopLevel(JCCompilationUnit node) { CompilationUnit unit = new CompilationUnit(); if (node.pid != null) { PackageDeclaration pkg = new PackageDeclaration(); fillWithIdentifiers(node.pid, pkg.astParts()); unit.astPackageDeclaration(setPos(node.pid, pkg)); fillList(node.packageAnnotations, pkg.rawAnnotations()); } for (JCTree def : node.defs) { if (def instanceof JCImport) { unit.rawImportDeclarations().addToEnd(toTree(def)); } else { unit.rawTypeDeclarations().addToEnd(toTree(def, FlagKey.SKIP_IS_DECL)); } } setConversionStructureInfo(unit, "converted"); set(node, unit); } @Override public void visitImport(JCImport node) { ImportDeclaration imp = new ImportDeclaration(); fillWithIdentifiers(node.getQualifiedIdentifier(), imp.astParts()); Identifier last = imp.astParts().last(); if (last != null && "*".equals(last.astValue())) { imp.astParts().remove(last); imp.astStarImport(true); setConversionPositionInfo(imp, ".*", last.getPosition()); } imp.astStaticImport(node.isStatic()); set(node, imp); } private static final long ENUM_CONSTANT_FLAGS = Flags.PUBLIC | Flags.STATIC | Flags.FINAL | Flags.ENUM; @Override public void visitClassDef(JCClassDecl node) { long flags = node.mods.flags; String name = node.getSimpleName().toString(); TypeDeclaration typeDecl; Map<FlagKey, Object> flagKeyMap = Maps.newHashMap(); flagKeyMap.put(FlagKey.CONTAINING_TYPE_NAME, name); flagKeyMap.put(FlagKey.BLOCKS_ARE_INITIALIZERS, FlagKey.BLOCKS_ARE_INITIALIZERS); flagKeyMap.put(FlagKey.SKIP_IS_DECL, FlagKey.SKIP_IS_DECL); if ((flags & (Flags.ENUM | Flags.INTERFACE)) == 0) { ClassDeclaration classDecl = new ClassDeclaration(); typeDecl = classDecl; fillList(node.implementing, classDecl.rawImplementing(), FlagKey.TYPE_REFERENCE); classDecl.rawExtending(toTree(node.extending, FlagKey.TYPE_REFERENCE)); fillList(node.typarams, classDecl.rawTypeVariables()); NormalTypeBody body = new NormalTypeBody(); fillList(node.defs, body.rawMembers(), flagKeyMap); classDecl.astBody(body); } else if ((flags & Flags.ANNOTATION) != 0) { AnnotationDeclaration annDecl = new AnnotationDeclaration(); typeDecl = annDecl; NormalTypeBody body = new NormalTypeBody(); flagKeyMap.put(FlagKey.METHODS_ARE_ANNMETHODS, FlagKey.METHODS_ARE_ANNMETHODS); fillList(node.defs, body.rawMembers(), flagKeyMap); annDecl.astBody(body); } else if ((flags & Flags.INTERFACE) != 0) { InterfaceDeclaration itfDecl = new InterfaceDeclaration(); typeDecl = itfDecl; fillList(node.typarams, itfDecl.rawTypeVariables()); fillList(node.implementing, itfDecl.rawExtending(), FlagKey.TYPE_REFERENCE); NormalTypeBody body = new NormalTypeBody(); fillList(node.defs, body.rawMembers(), flagKeyMap); itfDecl.astBody(body); } else if ((flags & Flags.ENUM) != 0) { EnumDeclaration enumDecl = new EnumDeclaration(); typeDecl = enumDecl; EnumTypeBody body = new EnumTypeBody(); fillList(node.implementing, enumDecl.rawImplementing(), FlagKey.TYPE_REFERENCE); java.util.List<JCTree> defs = new ArrayList<JCTree>(); for (JCTree def : node.defs) { if (def instanceof JCVariableDecl) { JCVariableDecl vd = (JCVariableDecl) def; if (vd.mods != null && (vd.mods.flags & ENUM_CONSTANT_FLAGS) == ENUM_CONSTANT_FLAGS) { // This is an enum constant, not a field of the enum class. EnumConstant ec = new EnumConstant(); setPos(def, ec); ec.astName(new Identifier().astValue(vd.getName().toString())); fillList(vd.mods.annotations, ec.rawAnnotations()); if (vd.init instanceof JCNewClass) { JCNewClass init = (JCNewClass) vd.init; fillList(init.getArguments(), ec.rawArguments()); if (init.getClassBody() != null) { NormalTypeBody constantBody = setPos(init, new NormalTypeBody()); fillList(init.getClassBody().getMembers(), constantBody.rawMembers()); ec.astBody(constantBody); } setConversionPositionInfo(ec, "newClass", getPosition(init)); } body.astConstants().addToEnd(ec); continue; } } defs.add(def); } fillList(defs, body.rawMembers(), flagKeyMap); enumDecl.astBody(body); } else { throw new IllegalStateException("Unknown type declaration: " + node); } typeDecl.astName(new Identifier().astValue(name)); typeDecl.astModifiers((Modifiers) toTree(node.mods)); addJavadoc(typeDecl, node.mods); set(node, typeDecl); } @Override public void visitModifiers(JCModifiers node) { Modifiers m = new Modifiers(); fillList(node.annotations, m.rawAnnotations()); for (KeywordModifier mod : KeywordModifier.fromReflectModifiers((int) node.flags)) m.astKeywords().addToEnd(mod); setConversionStructureInfo(m, "converted"); set(node, m); } @Override public void visitBlock(JCBlock node) { Node n; Block b = new Block(); fillList(node.stats, b.rawContents()); setPos(node, b); if (hasFlag(FlagKey.BLOCKS_ARE_INITIALIZERS)) { if ((node.flags & Flags.STATIC) != 0) { n = setPos(node, new StaticInitializer().astBody(b)); } else { // For some strange reason, solitary ; in a type body are represented not as JCSkips, but as JCBlocks with no endpos. Don't ask me why! if (b.rawContents().isEmpty() && node.endpos == -1) { n = setPos(node, new EmptyDeclaration()); } else { n = setPos(node, new InstanceInitializer().astBody(b)); } } } else { n = b; } set(node, n); } @Override public void visitSkip(JCSkip node) { if (hasFlag(FlagKey.SKIP_IS_DECL)) { set(node, new EmptyDeclaration()); } else { set(node, new EmptyStatement()); } } @Override public void visitVarDef(JCVariableDecl node) { if (hasFlag(FlagKey.VARDEF_IS_DEFINITION)) { set(node, toVariableDefinition(Collections.singletonList(node), FlagKey.VARDEF_IS_DEFINITION)); } else { set(node, toVariableDefinition(Collections.singletonList(node))); } } @Override public void visitTypeIdent(JCPrimitiveTypeTree node) { String primitiveType = JcTreeBuilder.PRIMITIVES.inverse().get(node.typetag); if (primitiveType == null) throw new IllegalArgumentException("Uknown primitive type tag: " + node.typetag); TypeReferencePart part = setPos(node, new TypeReferencePart().astIdentifier(setPos(node, new Identifier().astValue(primitiveType)))); set(node, new TypeReference().astParts().addToEnd(part)); } @Override public void visitIdent(JCIdent node) { String name = node.getName().toString(); if ("this".equals(name)) { This t = new This(); set(node, t); setConversionPositionInfo(t, "this", getPosition(node)); return; } if ("super".equals(name)) { Super s = new Super(); set(node, s); setConversionPositionInfo(s, "super", getPosition(node)); return; } Identifier id = setPos(node, new Identifier().astValue(name)); if (hasFlag(FlagKey.TYPE_REFERENCE)) { TypeReferencePart part = setPos(node, new TypeReferencePart().astIdentifier(id)); set(node, new TypeReference().astParts().addToEnd(part)); return; } set(node, new VariableReference().astIdentifier(id)); } @Override public void visitSelect(JCFieldAccess node) { String name = node.getIdentifier().toString(); Identifier id = setPos(node, new Identifier().astValue(name)); Node selected = toTree(node.selected, params); if (hasFlag(FlagKey.TYPE_REFERENCE)) { TypeReference parent = (TypeReference) selected; parent.astParts().addToEnd(setPos(node, new TypeReferencePart().astIdentifier(id))); set(node, parent); return; } if ("this".equals(name)) { This t = new This(); setConversionPositionInfo(t, "this", getPosition(node)); set(node, t.rawQualifier(toTree(node.getExpression(), FlagKey.TYPE_REFERENCE))); return; } if ("super".equals(name)) { Super s = new Super(); setConversionPositionInfo(s, "super", getPosition(node)); set(node, s.rawQualifier(toTree(node.getExpression(), FlagKey.TYPE_REFERENCE))); return; } if ("class".equals(name)) { ClassLiteral c = new ClassLiteral(); setConversionPositionInfo(c, "class", getPosition(node)); set(node, c.rawTypeReference(toTree(node.getExpression(), FlagKey.TYPE_REFERENCE))); return; } set(node, new Select().astIdentifier(id).rawOperand(toTree(node.getExpression()))); } @Override public void visitTypeApply(JCTypeApply node) { TypeReference ref = (TypeReference) toTree(node.clazz, FlagKey.TYPE_REFERENCE); TypeReferencePart last = ref.astParts().last(); fillList(node.arguments, last.rawTypeArguments(), FlagKey.TYPE_REFERENCE); setPos(node, ref); setConversionPositionInfo(last, "<", getPosition(node)); set(node, ref); } @Override public void visitWildcard(JCWildcard node) { TypeReference ref = (TypeReference) toTree(node.getBound(), FlagKey.TYPE_REFERENCE); if (ref == null) ref = new TypeReference(); switch (node.getKind()) { case UNBOUNDED_WILDCARD: ref.astWildcard(WildcardKind.UNBOUND); break; case EXTENDS_WILDCARD: ref.astWildcard(WildcardKind.EXTENDS); setConversionPositionInfo(ref, "extends", getTypeBoundKindPosition(node)); break; case SUPER_WILDCARD: ref.astWildcard(WildcardKind.SUPER); setConversionPositionInfo(ref, "super", getTypeBoundKindPosition(node)); break; } set(node, ref); } private Position getTypeBoundKindPosition(JCWildcard node) { try { Object o = JCWILDCARD_KIND.get(node); if (o instanceof TypeBoundKind) { return getPosition((TypeBoundKind) o); } } catch (Exception e) { } return Position.UNPLACED; } private int getTag(JCTree node) { if (JCTREE_GETTAG != null) { try { return (Integer) JCTREE_GETTAG.invoke(node); } catch (Exception e) { } } try { return (Integer) JCTREE_TAG.get(node); } catch (Exception e) { throw new IllegalStateException("Can't get node tag"); } } @Override public void visitTypeParameter(JCTypeParameter node) { TypeVariable var = new TypeVariable(); var.astName(setPos(node, new Identifier().astValue(node.name.toString()))); fillList(node.bounds, var.rawExtending(), FlagKey.TYPE_REFERENCE); set(node, var); } @Override public void visitTypeArray(JCArrayTypeTree node) { TypeReference ref = (TypeReference) toTree(node.getType(), FlagKey.TYPE_REFERENCE); int currentDim = ref.astArrayDimensions(); ref.astArrayDimensions(currentDim + 1); setConversionPositionInfo(ref, "[]" + currentDim, getPosition(node)); set(node, ref); } @Override public void visitLiteral(JCLiteral node) { Object val = node.getValue(); boolean negative = false; Expression literal = null; switch (node.getKind()) { case INT_LITERAL: int intValue = ((Number) val).intValue(); negative = intValue < 0; if (intValue == Integer.MIN_VALUE) literal = new IntegralLiteral().astIntValue(Integer.MIN_VALUE); else if (negative) literal = new IntegralLiteral().astIntValue(-intValue); else literal = new IntegralLiteral().astIntValue(intValue); break; case LONG_LITERAL: long longValue = ((Number) val).longValue(); negative = longValue < 0; if (longValue == Long.MIN_VALUE) literal = new IntegralLiteral().astLongValue(Long.MIN_VALUE); else if (negative) literal = new IntegralLiteral().astLongValue(-longValue); else literal = new IntegralLiteral().astLongValue(longValue); break; case FLOAT_LITERAL: set(node, new FloatingPointLiteral().astFloatValue(((Number) val).floatValue())); return; case DOUBLE_LITERAL: set(node, new FloatingPointLiteral().astDoubleValue(((Number) val).doubleValue())); return; case BOOLEAN_LITERAL: set(node, new BooleanLiteral().astValue((Boolean) val)); return; case CHAR_LITERAL: set(node, new CharLiteral().astValue((Character) val)); return; case STRING_LITERAL: set(node, new StringLiteral().astValue(val == null ? "" : val.toString())); return; case NULL_LITERAL: set(node, new NullLiteral()); return; } if (literal != null) { if (negative) set(node, new UnaryExpression().astOperand(setPos(node, literal)) .astOperator(UnaryOperator.UNARY_MINUS)); else set(node, literal); } else { throw new IllegalArgumentException("Unknown JCLiteral type tag:" + node.typetag); } } @Override public void visitParens(JCParens node) { Expression expr = (Expression) toTree(node.getExpression()); expr.astParensPositions().add(getPosition(node)); set(node, expr); } @Override public void visitTypeCast(JCTypeCast node) { Cast cast = new Cast(); cast.rawOperand(toTree(node.getExpression())); cast.rawTypeReference(toTree(node.getType(), FlagKey.TYPE_REFERENCE)); set(node, cast); } @Override public void visitUnary(JCUnary node) { UnaryExpression expr = new UnaryExpression(); expr.rawOperand(toTree(node.getExpression())); expr.astOperator(JcTreeBuilder.UNARY_OPERATORS.inverse().get(getTag(node))); set(node, expr); } @Override public void visitBinary(JCBinary node) { BinaryExpression expr = new BinaryExpression(); expr.rawLeft(toTree(node.getLeftOperand())); expr.rawRight(toTree(node.getRightOperand())); expr.astOperator(JcTreeBuilder.BINARY_OPERATORS.inverse().get(getTag(node))); set(node, expr); } @Override public void visitNewClass(JCNewClass node) { ConstructorInvocation inv = new ConstructorInvocation(); fillList(node.getArguments(), inv.rawArguments()); fillList(node.getTypeArguments(), inv.rawConstructorTypeArguments(), FlagKey.TYPE_REFERENCE); inv.rawTypeReference(toTree(node.getIdentifier(), FlagKey.TYPE_REFERENCE)); inv.rawQualifier(toTree(node.getEnclosingExpression())); Node n = toTree(node.getClassBody()); if (n instanceof TypeDeclaration) { NormalTypeBody body = ((ClassDeclaration) n).astBody(); if (body != null) body.unparent(); inv.rawAnonymousClassBody(setPos(node.getClassBody(), body)); } set(node, inv); } @Override public void visitTypeTest(JCInstanceOf node) { InstanceOf io = new InstanceOf(); io.rawTypeReference(toTree(node.getType(), FlagKey.TYPE_REFERENCE)); io.rawObjectReference(toTree(node.getExpression())); set(node, io); } @Override public void visitConditional(JCConditional node) { InlineIfExpression iie = new InlineIfExpression(); iie.rawCondition(toTree(node.getCondition())); iie.rawIfTrue(toTree(node.getTrueExpression())); iie.rawIfFalse(toTree(node.getFalseExpression())); set(node, iie); } @Override public void visitAssign(JCAssign node) { BinaryExpression expr = new BinaryExpression(); expr.rawRight(toTree(node.getExpression())); expr.rawLeft(toTree(node.getVariable())); expr.astOperator(BinaryOperator.ASSIGN); set(node, expr); } @Override public void visitAssignop(JCAssignOp node) { BinaryExpression expr = new BinaryExpression(); expr.rawRight(toTree(node.getExpression())); expr.rawLeft(toTree(node.getVariable())); expr.astOperator(JcTreeBuilder.BINARY_OPERATORS.inverse().get(getTag(node))); set(node, expr); } @Override public void visitExec(JCExpressionStatement node) { Node expr = toTree(node.getExpression()); if (expr instanceof SuperConstructorInvocation || expr instanceof AlternateConstructorInvocation) { setConversionPositionInfo(expr, "exec", getPosition(node)); set(node, expr); return; } ExpressionStatement exec = new ExpressionStatement(); exec.rawExpression(expr); set(node, exec); } @Override public void visitApply(JCMethodInvocation node) { MethodInvocation inv = new MethodInvocation(); JCTree sel = node.getMethodSelect(); Identifier id = new Identifier(); if (sel instanceof JCIdent) { String name = ((JCIdent) sel).getName().toString(); if ("this".equals(name)) { AlternateConstructorInvocation aci = new AlternateConstructorInvocation(); fillList(node.getTypeArguments(), aci.rawConstructorTypeArguments(), FlagKey.TYPE_REFERENCE); fillList(node.getArguments(), aci.rawArguments()); set(node, aci); setConversionPositionInfo(aci, "this", getPosition(sel)); return; } if ("super".equals(name)) { SuperConstructorInvocation sci = new SuperConstructorInvocation(); fillList(node.getTypeArguments(), sci.rawConstructorTypeArguments(), FlagKey.TYPE_REFERENCE); fillList(node.getArguments(), sci.rawArguments()); set(node, sci); setConversionPositionInfo(sci, "super", getPosition(sel)); return; } setPos(sel, id.astValue(name)); sel = null; } else if (sel instanceof JCFieldAccess) { String name = ((JCFieldAccess) sel).getIdentifier().toString(); if ("super".equals(name)) { SuperConstructorInvocation sci = new SuperConstructorInvocation(); fillList(node.getTypeArguments(), sci.rawConstructorTypeArguments(), FlagKey.TYPE_REFERENCE); fillList(node.getArguments(), sci.rawArguments()); sci.rawQualifier(toTree(((JCFieldAccess) sel).getExpression())); set(node, sci); setConversionPositionInfo(sci, "super", getPosition(sel)); return; } setPos(sel, id.astValue(name)); sel = ((JCFieldAccess) sel).getExpression(); } inv.astName(id).rawOperand(toTree(sel)); fillList(node.getTypeArguments(), inv.rawMethodTypeArguments(), FlagKey.TYPE_REFERENCE); fillList(node.getArguments(), inv.rawArguments()); set(node, inv); } @Override public void visitNewArray(JCNewArray node) { ArrayInitializer init = null; if (node.getInitializers() != null) { init = setPos(node, new ArrayInitializer()); fillList(node.getInitializers(), init.rawExpressions()); } if (node.getType() == null) { set(node, init == null ? new ArrayInitializer() : init); return; } ArrayCreation crea = new ArrayCreation(); JCTree type = node.getType(); java.util.List<Position> inits = Lists.newArrayList(); while (type instanceof JCArrayTypeTree) { inits.add(getPosition(type)); type = ((JCArrayTypeTree) type).getType(); } crea.rawComponentTypeReference(toTree(type, FlagKey.TYPE_REFERENCE)); if (node.getDimensions() != null) for (JCExpression dim : node.getDimensions()) { crea.astDimensions().addToEnd(setPos(dim, new ArrayDimension().rawDimension(toTree(dim)))); } if (init != null) crea.astDimensions().addToEnd(new ArrayDimension()); // new boolean [][][] {} in javac has one less dimension for some reason. for (Position i : inits) { ArrayDimension dim = new ArrayDimension(); dim.setPosition(i); crea.astDimensions().addToEnd(dim); } crea.astInitializer(init); set(node, crea); } @Override public void visitIndexed(JCArrayAccess node) { ArrayAccess aa = new ArrayAccess(); aa.rawIndexExpression(toTree(node.getIndex())); aa.rawOperand(toTree(node.getExpression())); set(node, aa); } @Override public void visitAssert(JCAssert node) { set(node, new Assert().rawAssertion(toTree(node.getCondition())).rawMessage(toTree(node.getDetail()))); } @Override public void visitDoLoop(JCDoWhileLoop node) { DoWhile dw = new DoWhile(); JCExpression cond = node.getCondition(); setConversionPositionInfo(dw, "()", getPosition(cond)); set(node, dw.rawCondition(toTree(removeParens(cond))).rawStatement(toTree(node.getStatement()))); } @Override public void visitContinue(JCContinue node) { Continue c = new Continue(); if (node.getLabel() != null) { c.astLabel(new Identifier().astValue(node.getLabel().toString())); } set(node, c); } @Override public void visitBreak(JCBreak node) { Break b = new Break(); if (node.getLabel() != null) { b.astLabel(new Identifier().astValue(node.getLabel().toString())); } set(node, b); } @Override public void visitForeachLoop(JCEnhancedForLoop node) { ForEach fe = new ForEach(); fe.rawIterable(toTree(node.getExpression())); fe.rawStatement(toTree(node.getStatement())); fe.rawVariable(toTree(node.getVariable(), FlagKey.VARDEF_IS_DEFINITION)); set(node, fe); } @Override public void visitIf(JCIf node) { If i = new If(); JCExpression cond = node.getCondition(); setConversionPositionInfo(i, "()", getPosition(cond)); i.rawCondition(toTree(removeParens(cond))); i.rawStatement(toTree(node.getThenStatement())); i.rawElseStatement(toTree(node.getElseStatement())); set(node, i); } @Override public void visitLabelled(JCLabeledStatement node) { Identifier lbl = new Identifier().astValue(node.getLabel().toString()); set(node, new LabelledStatement().rawStatement(toTree(node.getStatement())).astLabel(lbl)); } @Override public void visitForLoop(JCForLoop node) { For f = new For(); f.rawCondition(toTree(node.getCondition())); f.rawStatement(toTree(node.getStatement())); for (JCExpressionStatement upd : node.getUpdate()) { Node updateNode = toTree(upd.getExpression()); setConversionPositionInfo(updateNode, "exec", getPosition(upd)); f.rawUpdates().addToEnd(updateNode); } List<JCStatement> initializers = node.getInitializer(); // Multiple vardefs in a row need to trigger the JCVD version AND be washed through fillList to be turned into 1 VD. if (!initializers.isEmpty() && initializers.get(0) instanceof JCVariableDecl) { Block tmp = new Block(); fillList(initializers, tmp.rawContents(), FlagKey.VARDEF_IS_DEFINITION); Node varDecl = tmp.rawContents().first(); if (varDecl != null) varDecl.unparent(); f.rawVariableDeclaration(varDecl); } else { for (JCStatement init : initializers) { if (init instanceof JCExpressionStatement) { Node initNode = toTree(((JCExpressionStatement) init).getExpression()); setConversionPositionInfo(initNode, "exec", getPosition(init)); f.rawExpressionInits().addToEnd(initNode); } else { f.rawExpressionInits().addToEnd(toTree(init)); } } } set(node, f); } @Override public void visitSwitch(JCSwitch node) { Switch s = new Switch(); JCExpression cond = node.getExpression(); setConversionPositionInfo(s, "()", getPosition(cond)); s.rawCondition(toTree(removeParens(cond))); Block b = new Block(); s.astBody(b); for (JCCase c : node.getCases()) { JCExpression rawExpr = c.getExpression(); if (rawExpr == null) b.rawContents().addToEnd(setPos(c, new Default())); else b.rawContents().addToEnd(setPos(c, new Case().rawCondition(toTree(rawExpr)))); fillList(c.getStatements(), b.rawContents()); } set(node, s); } @Override public void visitSynchronized(JCSynchronized node) { Synchronized s = new Synchronized(); JCExpression cond = node.getExpression(); setConversionPositionInfo(s, "()", getPosition(cond)); set(node, s.rawLock(toTree(removeParens(cond))).rawBody(toTree(node.getBlock()))); } @Override public void visitTry(JCTry node) { Try t = new Try(); t.rawBody(toTree(node.getBlock())); t.rawFinally(toTree(node.getFinallyBlock())); fillList(node.getCatches(), t.rawCatches()); set(node, t); } @Override public void visitCatch(JCCatch node) { set(node, new Catch().rawExceptionDeclaration(toTree(node.getParameter(), FlagKey.VARDEF_IS_DEFINITION)) .rawBody(toTree(node.getBlock()))); } @Override public void visitThrow(JCThrow node) { set(node, new Throw().rawThrowable(toTree(node.getExpression()))); } @Override public void visitWhileLoop(JCWhileLoop node) { While w = new While(); JCExpression cond = node.getCondition(); setConversionPositionInfo(w, "()", getPosition(cond)); set(node, w.rawCondition(toTree(removeParens(cond))).rawStatement(toTree(node.getStatement()))); } @Override public void visitReturn(JCReturn node) { set(node, new Return().rawValue(toTree(node.getExpression()))); } @Override public void visitMethodDef(JCMethodDecl node) { String name = node.getName() == null ? null : node.getName().toString(); if ("<init>".equals(name)) { ConstructorDeclaration cd = new ConstructorDeclaration(); cd.astModifiers((Modifiers) toTree(node.getModifiers())); cd.rawBody(toTree(node.getBody())); fillList(node.getThrows(), cd.rawThrownTypeReferences(), FlagKey.TYPE_REFERENCE); fillList(node.getTypeParameters(), cd.rawTypeVariables()); fillList(node.getParameters(), cd.rawParameters(), FlagKey.NO_VARDECL_FOLDING, FlagKey.VARDEF_IS_DEFINITION); String typeName = (String) getFlag(FlagKey.CONTAINING_TYPE_NAME); cd.astTypeName(setPos(node, new Identifier().astValue(typeName))); addJavadoc(cd, node.mods); set(node, cd); return; } if (hasFlag(FlagKey.METHODS_ARE_ANNMETHODS)) { AnnotationMethodDeclaration md = new AnnotationMethodDeclaration(); md.astModifiers((Modifiers) toTree(node.getModifiers())); md.astMethodName(setPos(node, new Identifier().astValue(name))); md.rawReturnTypeReference(toTree(node.getReturnType(), FlagKey.TYPE_REFERENCE)); md.rawDefaultValue(toTree(node.getDefaultValue())); addJavadoc(md, node.mods); set(node, md); return; } MethodDeclaration md = new MethodDeclaration(); md.rawBody(toTree(node.getBody())); md.astModifiers((Modifiers) toTree(node.getModifiers())); md.astMethodName(setPos(node, new Identifier().astValue(name))); fillList(node.getThrows(), md.rawThrownTypeReferences(), FlagKey.TYPE_REFERENCE); fillList(node.getTypeParameters(), md.rawTypeVariables()); fillList(node.getParameters(), md.rawParameters(), FlagKey.NO_VARDECL_FOLDING, FlagKey.VARDEF_IS_DEFINITION); md.rawReturnTypeReference(toTree(node.getReturnType(), FlagKey.TYPE_REFERENCE)); addJavadoc(md, node.mods); set(node, md); } @Override public void visitAnnotation(JCAnnotation node) { Annotation a = new Annotation(); a.rawAnnotationTypeReference(toTree(node.getAnnotationType(), FlagKey.TYPE_REFERENCE)); for (JCExpression elem : node.getArguments()) { AnnotationElement e = new AnnotationElement(); if (elem instanceof JCAssign) { JCExpression rawName = ((JCAssign) elem).getVariable(); if (rawName instanceof JCIdent) e.astName(setPos(rawName, new Identifier().astValue(((JCIdent) rawName).getName().toString()))); elem = ((JCAssign) elem).getExpression(); } e.rawValue(toTree(elem)); a.astElements().addToEnd(e); } set(node, a); } } }