lombok.ast.javac.JcTreeConverter.java Source code

Java tutorial

Introduction

Here is the source code for lombok.ast.javac.JcTreeConverter.java

Source

/*
 * 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);
        }
    }
}