boa.datagen.util.Java7Visitor.java Source code

Java tutorial

Introduction

Here is the source code for boa.datagen.util.Java7Visitor.java

Source

/*
 * Copyright 2016, Hridesh Rajan, Robert Dyer, Hoan Nguyen, Farheen Sultana
 *                 Iowa State University of Science and Technology
 *                 and Bowling Green State University
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package boa.datagen.util;

import java.util.*;

import org.eclipse.jdt.core.dom.*;

import boa.types.Ast.*;

/**
 * @author rdyer
 */
public class Java7Visitor extends ASTVisitor {
    protected HashMap<String, Integer> nameIndices;

    protected CompilationUnit root = null;
    protected PositionInfo.Builder pos = null;
    protected String src = null;

    protected Namespace.Builder b = Namespace.newBuilder();
    protected List<boa.types.Ast.Comment> comments = new ArrayList<boa.types.Ast.Comment>();
    protected List<String> imports = new ArrayList<String>();
    protected Stack<List<boa.types.Ast.Declaration>> declarations = new Stack<List<boa.types.Ast.Declaration>>();
    protected Stack<boa.types.Ast.Modifier> modifiers = new Stack<boa.types.Ast.Modifier>();
    protected Stack<boa.types.Ast.Expression> expressions = new Stack<boa.types.Ast.Expression>();
    protected Stack<List<boa.types.Ast.Variable>> fields = new Stack<List<boa.types.Ast.Variable>>();
    protected Stack<List<boa.types.Ast.Method>> methods = new Stack<List<boa.types.Ast.Method>>();
    protected Stack<List<boa.types.Ast.Statement>> statements = new Stack<List<boa.types.Ast.Statement>>();

    public Java7Visitor(String src, HashMap<String, Integer> nameIndices) {
        super();
        this.src = src;
        this.nameIndices = nameIndices;
    }

    public Namespace getNamespaces(CompilationUnit node) {
        root = node;
        node.accept(this);
        return b.build();
    }

    public List<boa.types.Ast.Comment> getComments() {
        return comments;
    }

    public List<String> getImports() {
        return imports;
    }

    /*
       public void preVisit(ASTNode node) {
          buildPosition(node);
       }
    */

    protected void buildPosition(final ASTNode node) {
        pos = PositionInfo.newBuilder();
        int start = node.getStartPosition();
        int length = node.getLength();
        pos.setStartPos(start);
        pos.setLength(length);
        pos.setStartLine(root.getLineNumber(start));
        pos.setStartCol(root.getColumnNumber(start));
        pos.setEndLine(root.getLineNumber(start + length));
        pos.setEndCol(root.getColumnNumber(start + length));
    }

    @Override
    public boolean visit(CompilationUnit node) {
        //      b.setPosition(pos.build());
        PackageDeclaration pkg = node.getPackage();
        if (pkg == null) {
            b.setName("");
        } else {
            b.setName(pkg.getName().getFullyQualifiedName());
            for (Object a : pkg.annotations()) {
                ((Annotation) a).accept(this);
                b.addModifiers(modifiers.pop());
            }
        }
        for (Object i : node.imports()) {
            ImportDeclaration id = (ImportDeclaration) i;
            String imp = "";
            if (id.isStatic())
                imp += "static ";
            imp += id.getName().getFullyQualifiedName();
            if (id.isOnDemand())
                imp += ".*";
            imports.add(imp);
        }
        for (Object t : node.types()) {
            declarations.push(new ArrayList<boa.types.Ast.Declaration>());
            ((AbstractTypeDeclaration) t).accept(this);
            for (boa.types.Ast.Declaration d : declarations.pop())
                b.addDeclarations(d);
        }
        for (Object c : node.getCommentList())
            ((org.eclipse.jdt.core.dom.Comment) c).accept(this);
        return false;
    }

    @Override
    public boolean visit(BlockComment node) {
        boa.types.Ast.Comment.Builder b = boa.types.Ast.Comment.newBuilder();
        buildPosition(node);
        b.setPosition(pos.build());
        b.setKind(boa.types.Ast.Comment.CommentKind.BLOCK);
        b.setValue(src.substring(node.getStartPosition(), node.getStartPosition() + node.getLength()));
        comments.add(b.build());
        return false;
    }

    @Override
    public boolean visit(LineComment node) {
        boa.types.Ast.Comment.Builder b = boa.types.Ast.Comment.newBuilder();
        buildPosition(node);
        b.setPosition(pos.build());
        b.setKind(boa.types.Ast.Comment.CommentKind.LINE);
        b.setValue(src.substring(node.getStartPosition(), node.getStartPosition() + node.getLength()));
        comments.add(b.build());
        return false;
    }

    @Override
    public boolean visit(Javadoc node) {
        boa.types.Ast.Comment.Builder b = boa.types.Ast.Comment.newBuilder();
        buildPosition(node);
        b.setPosition(pos.build());
        b.setKind(boa.types.Ast.Comment.CommentKind.DOC);
        b.setValue(src.substring(node.getStartPosition(), node.getStartPosition() + node.getLength()));
        comments.add(b.build());
        return false;
    }

    //////////////////////////////////////////////////////////////
    // Type Declarations

    @Override
    public boolean visit(TypeDeclaration node) {
        boa.types.Ast.Declaration.Builder b = boa.types.Ast.Declaration.newBuilder();
        //      b.setPosition(pos.build());
        b.setName(node.getName().getFullyQualifiedName());
        if (node.isInterface())
            b.setKind(boa.types.Ast.TypeKind.INTERFACE);
        else
            b.setKind(boa.types.Ast.TypeKind.CLASS);
        for (Object m : node.modifiers()) {
            if (((IExtendedModifier) m).isAnnotation())
                ((Annotation) m).accept(this);
            else
                ((org.eclipse.jdt.core.dom.Modifier) m).accept(this);
            b.addModifiers(modifiers.pop());
        }
        for (Object t : node.typeParameters()) {
            boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
            String name = ((TypeParameter) t).getName().getFullyQualifiedName();
            String bounds = "";
            for (Object o : ((TypeParameter) t).typeBounds()) {
                if (bounds.length() > 0)
                    bounds += " & ";
                bounds += typeName((org.eclipse.jdt.core.dom.Type) o);
            }
            if (bounds.length() > 0)
                name = name + " extends " + bounds;
            tb.setName(getIndex(name));
            tb.setKind(boa.types.Ast.TypeKind.GENERIC);
            b.addGenericParameters(tb.build());
        }
        if (node.getSuperclassType() != null) {
            boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
            tb.setName(getIndex(typeName(node.getSuperclassType())));
            tb.setKind(boa.types.Ast.TypeKind.CLASS);
            b.addParents(tb.build());
        }
        for (Object t : node.superInterfaceTypes()) {
            boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
            tb.setName(getIndex(typeName((org.eclipse.jdt.core.dom.Type) t)));
            tb.setKind(boa.types.Ast.TypeKind.INTERFACE);
            b.addParents(tb.build());
        }
        for (Object d : node.bodyDeclarations()) {
            if (d instanceof FieldDeclaration) {
                fields.push(new ArrayList<boa.types.Ast.Variable>());
                ((FieldDeclaration) d).accept(this);
                for (boa.types.Ast.Variable v : fields.pop())
                    b.addFields(v);
            } else if (d instanceof MethodDeclaration) {
                methods.push(new ArrayList<boa.types.Ast.Method>());
                ((MethodDeclaration) d).accept(this);
                for (boa.types.Ast.Method m : methods.pop())
                    b.addMethods(m);
            } else if (d instanceof Initializer) {
                methods.push(new ArrayList<boa.types.Ast.Method>());
                ((Initializer) d).accept(this);
                for (boa.types.Ast.Method m : methods.pop())
                    b.addMethods(m);
            } else {
                declarations.push(new ArrayList<boa.types.Ast.Declaration>());
                ((BodyDeclaration) d).accept(this);
                for (boa.types.Ast.Declaration nd : declarations.pop())
                    b.addNestedDeclarations(nd);
            }
        }
        // TODO initializers
        // TODO enum constants
        // TODO annotation type members
        declarations.peek().add(b.build());
        return false;
    }

    @Override
    public boolean visit(AnonymousClassDeclaration node) {
        boa.types.Ast.Declaration.Builder b = boa.types.Ast.Declaration.newBuilder();
        //      b.setPosition(pos.build());
        b.setName("");
        b.setKind(boa.types.Ast.TypeKind.ANONYMOUS);
        for (Object d : node.bodyDeclarations()) {
            if (d instanceof FieldDeclaration) {
                fields.push(new ArrayList<boa.types.Ast.Variable>());
                ((FieldDeclaration) d).accept(this);
                for (boa.types.Ast.Variable v : fields.pop())
                    b.addFields(v);
            } else if (d instanceof MethodDeclaration) {
                methods.push(new ArrayList<boa.types.Ast.Method>());
                ((MethodDeclaration) d).accept(this);
                for (boa.types.Ast.Method m : methods.pop())
                    b.addMethods(m);
            } else if (d instanceof Initializer) {
                methods.push(new ArrayList<boa.types.Ast.Method>());
                ((Initializer) d).accept(this);
                for (boa.types.Ast.Method m : methods.pop())
                    b.addMethods(m);
            } else {
                declarations.push(new ArrayList<boa.types.Ast.Declaration>());
                ((BodyDeclaration) d).accept(this);
                for (boa.types.Ast.Declaration nd : declarations.pop())
                    b.addNestedDeclarations(nd);
            }
        }
        declarations.peek().add(b.build());
        return false;
    }

    @Override
    public boolean visit(EnumDeclaration node) {
        boa.types.Ast.Declaration.Builder b = boa.types.Ast.Declaration.newBuilder();
        //      b.setPosition(pos.build());
        b.setName(node.getName().getFullyQualifiedName());
        b.setKind(boa.types.Ast.TypeKind.ENUM);
        // TODO
        declarations.peek().add(b.build());
        return false;
    }

    @Override
    public boolean visit(AnnotationTypeDeclaration node) {
        boa.types.Ast.Declaration.Builder b = boa.types.Ast.Declaration.newBuilder();
        //      b.setPosition(pos.build());
        b.setName(node.getName().getFullyQualifiedName());
        b.setKind(boa.types.Ast.TypeKind.ANNOTATION);
        // TODO
        declarations.peek().add(b.build());
        return false;
    }

    //////////////////////////////////////////////////////////////
    // Field/Method Declarations

    @Override
    public boolean visit(MethodDeclaration node) {
        List<boa.types.Ast.Method> list = methods.peek();
        Method.Builder b = Method.newBuilder();
        //      b.setPosition(pos.build());
        if (node.isConstructor())
            b.setName("<init>");
        else
            b.setName(node.getName().getFullyQualifiedName());
        for (Object m : node.modifiers()) {
            if (((IExtendedModifier) m).isAnnotation())
                ((Annotation) m).accept(this);
            else
                ((org.eclipse.jdt.core.dom.Modifier) m).accept(this);
            b.addModifiers(modifiers.pop());
        }
        boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
        if (node.getReturnType2() != null) {
            String name = typeName(node.getReturnType2());
            for (int i = 0; i < node.getExtraDimensions(); i++)
                name += "[]";
            tb.setName(getIndex(name));
            tb.setKind(boa.types.Ast.TypeKind.OTHER);
            b.setReturnType(tb.build());
        } else {
            tb.setName(getIndex("void"));
            tb.setKind(boa.types.Ast.TypeKind.OTHER);
            b.setReturnType(tb.build());
        }
        for (Object t : node.typeParameters()) {
            boa.types.Ast.Type.Builder tp = boa.types.Ast.Type.newBuilder();
            String name = ((TypeParameter) t).getName().getFullyQualifiedName();
            String bounds = "";
            for (Object o : ((TypeParameter) t).typeBounds()) {
                if (bounds.length() > 0)
                    bounds += " & ";
                bounds += typeName((org.eclipse.jdt.core.dom.Type) o);
            }
            if (bounds.length() > 0)
                name = name + " extends " + bounds;
            tp.setName(getIndex(name));
            tp.setKind(boa.types.Ast.TypeKind.GENERIC);
            b.addGenericParameters(tp.build());
        }
        for (Object o : node.parameters()) {
            SingleVariableDeclaration ex = (SingleVariableDeclaration) o;
            Variable.Builder vb = Variable.newBuilder();
            //         vb.setPosition(pos.build()); // FIXME
            vb.setName(ex.getName().getFullyQualifiedName());
            for (Object m : ex.modifiers()) {
                if (((IExtendedModifier) m).isAnnotation())
                    ((Annotation) m).accept(this);
                else
                    ((org.eclipse.jdt.core.dom.Modifier) m).accept(this);
                vb.addModifiers(modifiers.pop());
            }
            boa.types.Ast.Type.Builder tp = boa.types.Ast.Type.newBuilder();
            String name = typeName(ex.getType());
            for (int i = 0; i < ex.getExtraDimensions(); i++)
                name += "[]";
            if (ex.isVarargs())
                name += "...";
            tp.setName(getIndex(name));
            tp.setKind(boa.types.Ast.TypeKind.OTHER);
            vb.setVariableType(tp.build());
            if (ex.getInitializer() != null) {
                ex.getInitializer().accept(this);
                vb.setInitializer(expressions.pop());
            }
            b.addArguments(vb.build());
        }
        for (Object o : node.thrownExceptions()) {
            boa.types.Ast.Type.Builder tp = boa.types.Ast.Type.newBuilder();
            tp.setName(getIndex(((Name) o).getFullyQualifiedName()));
            tp.setKind(boa.types.Ast.TypeKind.CLASS);
            b.addExceptionTypes(tp.build());
        }
        if (node.getBody() != null) {
            statements.push(new ArrayList<boa.types.Ast.Statement>());
            node.getBody().accept(this);
            for (boa.types.Ast.Statement s : statements.pop())
                b.addStatements(s);
        }
        list.add(b.build());
        return false;
    }

    @Override
    public boolean visit(AnnotationTypeMemberDeclaration node) {
        List<boa.types.Ast.Method> list = methods.peek();
        Method.Builder b = Method.newBuilder();
        //      b.setPosition(pos.build());
        b.setName(node.getName().getFullyQualifiedName());
        for (Object m : node.modifiers()) {
            if (((IExtendedModifier) m).isAnnotation())
                ((Annotation) m).accept(this);
            else
                ((org.eclipse.jdt.core.dom.Modifier) m).accept(this);
            b.addModifiers(modifiers.pop());
        }
        boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
        tb.setName(getIndex(typeName(node.getType())));
        tb.setKind(boa.types.Ast.TypeKind.OTHER);
        b.setReturnType(tb.build());
        if (node.getDefault() != null) {
            boa.types.Ast.Statement.Builder sb = boa.types.Ast.Statement.newBuilder();
            //      sb.setPosition(pos.build());
            sb.setKind(boa.types.Ast.Statement.StatementKind.EXPRESSION);
            node.getDefault().accept(this);
            sb.setExpression(expressions.pop());
            b.addStatements(sb.build());
        }
        list.add(b.build());
        return false;
    }

    @Override
    public boolean visit(FieldDeclaration node) {
        List<boa.types.Ast.Variable> list = fields.peek();
        for (Object o : node.fragments()) {
            VariableDeclarationFragment f = (VariableDeclarationFragment) o;
            Variable.Builder b = Variable.newBuilder();
            //         b.setPosition(pos.build()); // FIXME
            b.setName(f.getName().getFullyQualifiedName());
            for (Object m : node.modifiers()) {
                if (((IExtendedModifier) m).isAnnotation())
                    ((Annotation) m).accept(this);
                else
                    ((org.eclipse.jdt.core.dom.Modifier) m).accept(this);
                b.addModifiers(modifiers.pop());
            }
            boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
            String name = typeName(node.getType());
            for (int i = 0; i < f.getExtraDimensions(); i++)
                name += "[]";
            tb.setName(getIndex(name));
            tb.setKind(boa.types.Ast.TypeKind.OTHER);
            b.setVariableType(tb.build());
            if (f.getInitializer() != null) {
                f.getInitializer().accept(this);
                b.setInitializer(expressions.pop());
            }
            list.add(b.build());
        }
        return false;
    }

    //////////////////////////////////////////////////////////////
    // Modifiers and Annotations

    protected boa.types.Ast.Modifier.Builder getAnnotationBuilder(Annotation node) {
        boa.types.Ast.Modifier.Builder b = boa.types.Ast.Modifier.newBuilder();
        //      b.setPosition(pos.build());
        b.setKind(boa.types.Ast.Modifier.ModifierKind.ANNOTATION);
        b.setAnnotationName(node.getTypeName().getFullyQualifiedName());
        return b;
    }

    @Override
    public boolean visit(MarkerAnnotation node) {
        modifiers.push(getAnnotationBuilder(node).build());
        return false;
    }

    @Override
    public boolean visit(SingleMemberAnnotation node) {
        boa.types.Ast.Modifier.Builder b = getAnnotationBuilder(node);
        node.getValue().accept(this);
        if (expressions.empty()) {
            boa.types.Ast.Expression.Builder eb = boa.types.Ast.Expression.newBuilder();
            //         eb.setPosition(pos.build()); // FIXME
            eb.setKind(boa.types.Ast.Expression.ExpressionKind.ANNOTATION);
            eb.setAnnotation(modifiers.pop());
            b.addAnnotationMembers("value");
            b.addAnnotationValues(eb.build());
        } else {
            b.addAnnotationMembers("value");
            b.addAnnotationValues(expressions.pop());
        }
        modifiers.push(b.build());
        return false;
    }

    @Override
    public boolean visit(NormalAnnotation node) {
        boa.types.Ast.Modifier.Builder b = getAnnotationBuilder(node);
        for (Object v : node.values()) {
            MemberValuePair pair = (MemberValuePair) v;
            pair.getValue().accept(this);
            if (expressions.empty()) {
                boa.types.Ast.Expression.Builder eb = boa.types.Ast.Expression.newBuilder();
                //         eb.setPosition(pos.build()); // FIXME
                eb.setKind(boa.types.Ast.Expression.ExpressionKind.ANNOTATION);
                eb.setAnnotation(modifiers.pop());
                b.addAnnotationMembers(pair.getName().getFullyQualifiedName());
                b.addAnnotationValues(eb.build());
            } else {
                b.addAnnotationMembers(pair.getName().getFullyQualifiedName());
                b.addAnnotationValues(expressions.pop());
            }
        }
        modifiers.push(b.build());
        return false;
    }

    @Override
    public boolean visit(org.eclipse.jdt.core.dom.Modifier node) {
        boa.types.Ast.Modifier.Builder b = boa.types.Ast.Modifier.newBuilder();
        //      b.setPosition(pos.build());
        if (node.isFinal())
            b.setKind(boa.types.Ast.Modifier.ModifierKind.FINAL);
        else if (node.isAbstract())
            b.setKind(boa.types.Ast.Modifier.ModifierKind.ABSTRACT);
        else if (node.isStatic())
            b.setKind(boa.types.Ast.Modifier.ModifierKind.STATIC);
        else if (node.isSynchronized())
            b.setKind(boa.types.Ast.Modifier.ModifierKind.SYNCHRONIZED);
        else if (node.isPublic()) {
            b.setKind(boa.types.Ast.Modifier.ModifierKind.VISIBILITY);
            b.setVisibility(boa.types.Ast.Modifier.Visibility.PUBLIC);
        } else if (node.isPrivate()) {
            b.setKind(boa.types.Ast.Modifier.ModifierKind.VISIBILITY);
            b.setVisibility(boa.types.Ast.Modifier.Visibility.PRIVATE);
        } else if (node.isProtected()) {
            b.setKind(boa.types.Ast.Modifier.ModifierKind.VISIBILITY);
            b.setVisibility(boa.types.Ast.Modifier.Visibility.PROTECTED);
        } else {
            b.setKind(boa.types.Ast.Modifier.ModifierKind.OTHER);
            b.setOther(node.getKeyword().toString());
        }
        modifiers.push(b.build());
        return false;
    }

    //////////////////////////////////////////////////////////////
    // Statements

    @Override
    public boolean visit(AssertStatement node) {
        boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
        //      b.setPosition(pos.build());
        List<boa.types.Ast.Statement> list = statements.peek();
        b.setKind(boa.types.Ast.Statement.StatementKind.ASSERT);
        node.getExpression().accept(this);
        b.setExpression(expressions.pop());
        if (node.getMessage() != null) {
            node.getMessage().accept(this);
            b.setExpression(expressions.pop());
        }
        list.add(b.build());
        return false;
    }

    @Override
    public boolean visit(Block node) {
        boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
        //      b.setPosition(pos.build());
        List<boa.types.Ast.Statement> list = statements.peek();
        b.setKind(boa.types.Ast.Statement.StatementKind.BLOCK);
        for (Object s : node.statements()) {
            statements.push(new ArrayList<boa.types.Ast.Statement>());
            ((org.eclipse.jdt.core.dom.Statement) s).accept(this);
            for (boa.types.Ast.Statement st : statements.pop())
                b.addStatements(st);
        }
        list.add(b.build());
        return false;
    }

    @Override
    public boolean visit(BreakStatement node) {
        boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
        //      b.setPosition(pos.build());
        List<boa.types.Ast.Statement> list = statements.peek();
        b.setKind(boa.types.Ast.Statement.StatementKind.BREAK);
        if (node.getLabel() != null) {
            boa.types.Ast.Expression.Builder eb = boa.types.Ast.Expression.newBuilder();
            //         eb.setPosition(pos.build()); // FIXME
            eb.setLiteral(node.getLabel().getFullyQualifiedName());
            eb.setKind(boa.types.Ast.Expression.ExpressionKind.LITERAL);
            b.setExpression(eb.build());
        }
        list.add(b.build());
        return false;
    }

    @Override
    public boolean visit(CatchClause node) {
        boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
        //      b.setPosition(pos.build());
        List<boa.types.Ast.Statement> list = statements.peek();
        b.setKind(boa.types.Ast.Statement.StatementKind.CATCH);
        SingleVariableDeclaration ex = node.getException();
        Variable.Builder vb = Variable.newBuilder();
        //      vb.setPosition(pos.build());// FIXME
        vb.setName(ex.getName().getFullyQualifiedName());
        for (Object m : ex.modifiers()) {
            if (((IExtendedModifier) m).isAnnotation())
                ((Annotation) m).accept(this);
            else
                ((org.eclipse.jdt.core.dom.Modifier) m).accept(this);
            vb.addModifiers(modifiers.pop());
        }
        boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
        String name = typeName(ex.getType());
        for (int i = 0; i < ex.getExtraDimensions(); i++)
            name += "[]";
        tb.setName(getIndex(name));
        tb.setKind(boa.types.Ast.TypeKind.OTHER);
        vb.setVariableType(tb.build());
        if (ex.getInitializer() != null) {
            ex.getInitializer().accept(this);
            vb.setInitializer(expressions.pop());
        }
        b.setVariableDeclaration(vb.build());
        statements.push(new ArrayList<boa.types.Ast.Statement>());
        for (Object s : node.getBody().statements())
            ((org.eclipse.jdt.core.dom.Statement) s).accept(this);
        for (boa.types.Ast.Statement s : statements.pop())
            b.addStatements(s);
        list.add(b.build());
        return false;
    }

    @Override
    public boolean visit(ConstructorInvocation node) {
        boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
        //      b.setPosition(pos.build());
        List<boa.types.Ast.Statement> list = statements.peek();
        b.setKind(boa.types.Ast.Statement.StatementKind.EXPRESSION);
        boa.types.Ast.Expression.Builder eb = boa.types.Ast.Expression.newBuilder();
        //      eb.setPosition(pos.build());//FIXME
        eb.setKind(boa.types.Ast.Expression.ExpressionKind.METHODCALL);
        eb.setMethod("<init>");
        for (Object a : node.arguments()) {
            ((org.eclipse.jdt.core.dom.Expression) a).accept(this);
            eb.addMethodArgs(expressions.pop());
        }
        for (Object t : node.typeArguments()) {
            boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
            tb.setName(getIndex(typeName((org.eclipse.jdt.core.dom.Type) t)));
            tb.setKind(boa.types.Ast.TypeKind.GENERIC);
            eb.addGenericParameters(tb.build());
        }
        b.setExpression(eb.build());
        list.add(b.build());
        return false;
    }

    @Override
    public boolean visit(ContinueStatement node) {
        boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
        //      b.setPosition(pos.build());
        List<boa.types.Ast.Statement> list = statements.peek();
        b.setKind(boa.types.Ast.Statement.StatementKind.CONTINUE);
        if (node.getLabel() != null) {
            boa.types.Ast.Expression.Builder eb = boa.types.Ast.Expression.newBuilder();
            //         eb.setPosition(pos.build());//FIXME
            eb.setLiteral(node.getLabel().getFullyQualifiedName());
            eb.setKind(boa.types.Ast.Expression.ExpressionKind.LITERAL);
            b.setExpression(eb.build());
        }
        list.add(b.build());
        return false;
    }

    @Override
    public boolean visit(DoStatement node) {
        boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
        //      b.setPosition(pos.build());
        List<boa.types.Ast.Statement> list = statements.peek();
        b.setKind(boa.types.Ast.Statement.StatementKind.DO);
        node.getExpression().accept(this);
        b.setExpression(expressions.pop());
        statements.push(new ArrayList<boa.types.Ast.Statement>());
        node.getBody().accept(this);
        for (boa.types.Ast.Statement s : statements.pop())
            b.addStatements(s);
        list.add(b.build());
        return false;
    }

    @Override
    public boolean visit(EmptyStatement node) {
        boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
        //      b.setPosition(pos.build());
        List<boa.types.Ast.Statement> list = statements.peek();
        b.setKind(boa.types.Ast.Statement.StatementKind.EMPTY);
        list.add(b.build());
        return false;
    }

    @Override
    public boolean visit(EnhancedForStatement node) {
        boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
        //      b.setPosition(pos.build());
        List<boa.types.Ast.Statement> list = statements.peek();
        b.setKind(boa.types.Ast.Statement.StatementKind.FOR);
        SingleVariableDeclaration ex = node.getParameter();
        Variable.Builder vb = Variable.newBuilder();
        //      vb.setPosition(pos.build());//FIXME
        vb.setName(ex.getName().getFullyQualifiedName());
        for (Object m : ex.modifiers()) {
            if (((IExtendedModifier) m).isAnnotation())
                ((Annotation) m).accept(this);
            else
                ((org.eclipse.jdt.core.dom.Modifier) m).accept(this);
            vb.addModifiers(modifiers.pop());
        }
        boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
        String name = typeName(ex.getType());
        for (int i = 0; i < ex.getExtraDimensions(); i++)
            name += "[]";
        tb.setName(getIndex(name));
        tb.setKind(boa.types.Ast.TypeKind.OTHER);
        vb.setVariableType(tb.build());
        if (ex.getInitializer() != null) {
            ex.getInitializer().accept(this);
            vb.setInitializer(expressions.pop());
        }
        b.setVariableDeclaration(vb.build());
        node.getExpression().accept(this);
        b.setExpression(expressions.pop());
        statements.push(new ArrayList<boa.types.Ast.Statement>());
        node.getBody().accept(this);
        for (boa.types.Ast.Statement s : statements.pop())
            b.addStatements(s);
        list.add(b.build());
        return false;
    }

    @Override
    public boolean visit(ExpressionStatement node) {
        boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
        //      b.setPosition(pos.build());
        List<boa.types.Ast.Statement> list = statements.peek();
        b.setKind(boa.types.Ast.Statement.StatementKind.EXPRESSION);
        node.getExpression().accept(this);
        b.setExpression(expressions.pop());
        list.add(b.build());
        return false;
    }

    @Override
    public boolean visit(ForStatement node) {
        boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
        //      b.setPosition(pos.build());
        List<boa.types.Ast.Statement> list = statements.peek();
        b.setKind(boa.types.Ast.Statement.StatementKind.FOR);
        for (Object e : node.initializers()) {
            ((org.eclipse.jdt.core.dom.Expression) e).accept(this);
            b.addInitializations(expressions.pop());
        }
        for (Object e : node.updaters()) {
            ((org.eclipse.jdt.core.dom.Expression) e).accept(this);
            b.addUpdates(expressions.pop());
        }
        if (node.getExpression() != null) {
            node.getExpression().accept(this);
            b.setExpression(expressions.pop());
        }
        statements.push(new ArrayList<boa.types.Ast.Statement>());
        node.getBody().accept(this);
        for (boa.types.Ast.Statement s : statements.pop())
            b.addStatements(s);
        list.add(b.build());
        return false;
    }

    @Override
    public boolean visit(IfStatement node) {
        boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
        //      b.setPosition(pos.build());
        List<boa.types.Ast.Statement> list = statements.peek();
        b.setKind(boa.types.Ast.Statement.StatementKind.IF);
        node.getExpression().accept(this);
        b.setExpression(expressions.pop());
        statements.push(new ArrayList<boa.types.Ast.Statement>());
        node.getThenStatement().accept(this);
        for (boa.types.Ast.Statement s : statements.pop())
            b.addStatements(s);
        // FIXME
        if (node.getElseStatement() != null) {
            statements.push(new ArrayList<boa.types.Ast.Statement>());
            node.getElseStatement().accept(this);
            for (boa.types.Ast.Statement s : statements.pop())
                b.addStatements(s);
        }
        list.add(b.build());
        return false;
    }

    @Override
    public boolean visit(Initializer node) {
        List<boa.types.Ast.Method> list = methods.peek();
        Method.Builder b = Method.newBuilder();
        //      b.setPosition(pos.build());
        b.setName("<clinit>");
        for (Object m : node.modifiers()) {
            if (((IExtendedModifier) m).isAnnotation())
                ((Annotation) m).accept(this);
            else
                ((org.eclipse.jdt.core.dom.Modifier) m).accept(this);
            b.addModifiers(modifiers.pop());
        }
        boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
        tb.setName(getIndex("void"));
        tb.setKind(boa.types.Ast.TypeKind.OTHER);
        b.setReturnType(tb.build());
        if (node.getBody() != null) {
            statements.push(new ArrayList<boa.types.Ast.Statement>());
            node.getBody().accept(this);
            for (boa.types.Ast.Statement s : statements.pop())
                b.addStatements(s);
        }
        list.add(b.build());
        return false;
    }

    @Override
    public boolean visit(LabeledStatement node) {
        boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
        //      b.setPosition(pos.build());
        List<boa.types.Ast.Statement> list = statements.peek();
        b.setKind(boa.types.Ast.Statement.StatementKind.LABEL);
        statements.push(new ArrayList<boa.types.Ast.Statement>());
        node.getBody().accept(this);
        for (boa.types.Ast.Statement s : statements.pop())
            b.addStatements(s);
        boa.types.Ast.Expression.Builder eb = boa.types.Ast.Expression.newBuilder();
        //      eb.setPosition(pos.build());//FIXME
        eb.setKind(boa.types.Ast.Expression.ExpressionKind.LITERAL);
        eb.setLiteral(node.getLabel().getFullyQualifiedName());
        b.setExpression(eb.build());
        list.add(b.build());
        return false;
    }

    @Override
    public boolean visit(ReturnStatement node) {
        boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
        //      b.setPosition(pos.build());
        List<boa.types.Ast.Statement> list = statements.peek();
        b.setKind(boa.types.Ast.Statement.StatementKind.RETURN);
        if (node.getExpression() != null) {
            node.getExpression().accept(this);
            b.setExpression(expressions.pop());
        }
        list.add(b.build());
        return false;
    }

    @Override
    public boolean visit(SuperConstructorInvocation node) {
        boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
        //      b.setPosition(pos.build());
        List<boa.types.Ast.Statement> list = statements.peek();
        b.setKind(boa.types.Ast.Statement.StatementKind.EXPRESSION);
        boa.types.Ast.Expression.Builder eb = boa.types.Ast.Expression.newBuilder();
        //      eb.setPosition(pos.build());//FIXME
        eb.setKind(boa.types.Ast.Expression.ExpressionKind.METHODCALL);
        eb.setMethod("super");
        if (node.getExpression() != null) {
            node.getExpression().accept(this);
            eb.addExpressions(expressions.pop());
        }
        for (Object a : node.arguments()) {
            ((org.eclipse.jdt.core.dom.Expression) a).accept(this);
            eb.addMethodArgs(expressions.pop());
        }
        for (Object t : node.typeArguments()) {
            boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
            tb.setName(getIndex(typeName((org.eclipse.jdt.core.dom.Type) t)));
            tb.setKind(boa.types.Ast.TypeKind.GENERIC);
            eb.addGenericParameters(tb.build());
        }
        b.setExpression(eb.build());
        list.add(b.build());
        return false;
    }

    @Override
    public boolean visit(SwitchCase node) {
        boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
        //      b.setPosition(pos.build());
        List<boa.types.Ast.Statement> list = statements.peek();
        b.setKind(boa.types.Ast.Statement.StatementKind.CASE);
        if (node.getExpression() != null) {
            node.getExpression().accept(this);
            b.setExpression(expressions.pop());
        }
        list.add(b.build());
        return false;
    }

    @Override
    public boolean visit(SwitchStatement node) {
        boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
        //      b.setPosition(pos.build());
        List<boa.types.Ast.Statement> list = statements.peek();
        b.setKind(boa.types.Ast.Statement.StatementKind.SWITCH);
        node.getExpression().accept(this);
        b.setExpression(expressions.pop());
        statements.push(new ArrayList<boa.types.Ast.Statement>());
        for (Object s : node.statements())
            ((org.eclipse.jdt.core.dom.Statement) s).accept(this);
        for (boa.types.Ast.Statement s : statements.pop())
            b.addStatements(s);
        list.add(b.build());
        return false;
    }

    @Override
    public boolean visit(SynchronizedStatement node) {
        boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
        //      b.setPosition(pos.build());
        List<boa.types.Ast.Statement> list = statements.peek();
        b.setKind(boa.types.Ast.Statement.StatementKind.SYNCHRONIZED);
        node.getExpression().accept(this);
        b.setExpression(expressions.pop());
        statements.push(new ArrayList<boa.types.Ast.Statement>());
        for (Object s : node.getBody().statements())
            ((org.eclipse.jdt.core.dom.Statement) s).accept(this);
        for (boa.types.Ast.Statement s : statements.pop())
            b.addStatements(s);
        list.add(b.build());
        return false;
    }

    @Override
    public boolean visit(ThrowStatement node) {
        boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
        //      b.setPosition(pos.build());
        List<boa.types.Ast.Statement> list = statements.peek();
        b.setKind(boa.types.Ast.Statement.StatementKind.THROW);
        node.getExpression().accept(this);
        b.setExpression(expressions.pop());
        list.add(b.build());
        return false;
    }

    @Override
    public boolean visit(TryStatement node) {
        boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
        //      b.setPosition(pos.build());
        List<boa.types.Ast.Statement> list = statements.peek();
        b.setKind(boa.types.Ast.Statement.StatementKind.TRY);
        statements.push(new ArrayList<boa.types.Ast.Statement>());
        node.getBody().accept(this);
        for (Object c : node.catchClauses())
            ((CatchClause) c).accept(this);
        if (node.getFinally() != null)
            node.getFinally().accept(this);
        for (boa.types.Ast.Statement s : statements.pop())
            b.addStatements(s);
        if (node.resources() != null)
            for (Object v : node.resources()) {
                ((VariableDeclarationExpression) v).accept(this);
                b.addInitializations(expressions.pop());
            }
        list.add(b.build());
        return false;
    }

    @Override
    public boolean visit(TypeDeclarationStatement node) {
        boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
        //      b.setPosition(pos.build());
        List<boa.types.Ast.Statement> list = statements.peek();
        b.setKind(boa.types.Ast.Statement.StatementKind.TYPEDECL);
        declarations.push(new ArrayList<boa.types.Ast.Declaration>());
        node.getDeclaration().accept(this);
        for (boa.types.Ast.Declaration d : declarations.pop())
            b.setTypeDeclaration(d);
        list.add(b.build());
        return false;
    }

    @Override
    public boolean visit(VariableDeclarationStatement node) {
        boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
        //      b.setPosition(pos.build());
        List<boa.types.Ast.Statement> list = statements.peek();
        b.setKind(boa.types.Ast.Statement.StatementKind.EXPRESSION);
        boa.types.Ast.Expression.Builder eb = boa.types.Ast.Expression.newBuilder();
        //      eb.setPosition(pos.build());//FIXME
        eb.setKind(boa.types.Ast.Expression.ExpressionKind.VARDECL);
        for (Object o : node.fragments()) {
            VariableDeclarationFragment f = (VariableDeclarationFragment) o;
            Variable.Builder vb = Variable.newBuilder();
            //         vb.setPosition(pos.build());//FIXME
            vb.setName(f.getName().getFullyQualifiedName());
            for (Object m : node.modifiers()) {
                if (((IExtendedModifier) m).isAnnotation())
                    ((Annotation) m).accept(this);
                else
                    ((org.eclipse.jdt.core.dom.Modifier) m).accept(this);
                vb.addModifiers(modifiers.pop());
            }
            boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
            String name = typeName(node.getType());
            for (int i = 0; i < f.getExtraDimensions(); i++)
                name += "[]";
            tb.setName(getIndex(name));
            tb.setKind(boa.types.Ast.TypeKind.OTHER);
            vb.setVariableType(tb.build());
            if (f.getInitializer() != null) {
                f.getInitializer().accept(this);
                vb.setInitializer(expressions.pop());
            }
            eb.addVariableDecls(vb.build());
        }
        b.setExpression(eb.build());
        list.add(b.build());
        return false;
    }

    @Override
    public boolean visit(WhileStatement node) {
        boa.types.Ast.Statement.Builder b = boa.types.Ast.Statement.newBuilder();
        //      b.setPosition(pos.build());
        List<boa.types.Ast.Statement> list = statements.peek();
        b.setKind(boa.types.Ast.Statement.StatementKind.WHILE);
        node.getExpression().accept(this);
        b.setExpression(expressions.pop());
        statements.push(new ArrayList<boa.types.Ast.Statement>());
        node.getBody().accept(this);
        for (boa.types.Ast.Statement s : statements.pop())
            b.addStatements(s);
        list.add(b.build());
        return false;
    }

    //////////////////////////////////////////////////////////////
    // Expressions

    @Override
    public boolean visit(ArrayAccess node) {
        boa.types.Ast.Expression.Builder b = boa.types.Ast.Expression.newBuilder();
        //      b.setPosition(pos.build());
        b.setKind(boa.types.Ast.Expression.ExpressionKind.ARRAYINDEX);
        node.getArray().accept(this);
        b.addExpressions(expressions.pop());
        node.getIndex().accept(this);
        b.addExpressions(expressions.pop());
        expressions.push(b.build());
        return false;
    }

    @Override
    public boolean visit(ArrayCreation node) {
        boa.types.Ast.Expression.Builder b = boa.types.Ast.Expression.newBuilder();
        //      b.setPosition(pos.build());
        b.setKind(boa.types.Ast.Expression.ExpressionKind.NEWARRAY);
        boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
        tb.setName(getIndex(typeName(node.getType())));
        tb.setKind(boa.types.Ast.TypeKind.OTHER);
        b.setNewType(tb.build());
        for (Object e : node.dimensions()) {
            ((org.eclipse.jdt.core.dom.Expression) e).accept(this);
            b.addExpressions(expressions.pop());
        }
        if (node.getInitializer() != null) {
            node.getInitializer().accept(this);
            // FIXME
            b.addExpressions(expressions.pop());
        }
        expressions.push(b.build());
        return false;
    }

    @Override
    public boolean visit(ArrayInitializer node) {
        boa.types.Ast.Expression.Builder b = boa.types.Ast.Expression.newBuilder();
        //      b.setPosition(pos.build());
        b.setKind(boa.types.Ast.Expression.ExpressionKind.ARRAYINIT);
        for (Object e : node.expressions()) {
            ((org.eclipse.jdt.core.dom.Expression) e).accept(this);
            if (expressions.empty()) {
                boa.types.Ast.Expression.Builder eb = boa.types.Ast.Expression.newBuilder();
                //         eb.setPosition(pos.build()); // FIXME
                eb.setKind(boa.types.Ast.Expression.ExpressionKind.ANNOTATION);
                eb.setAnnotation(modifiers.pop());
                b.addExpressions(eb.build());
            } else {
                b.addExpressions(expressions.pop());
            }
        }
        expressions.push(b.build());
        return false;
    }

    @Override
    public boolean visit(Assignment node) {
        boa.types.Ast.Expression.Builder b = boa.types.Ast.Expression.newBuilder();
        //      b.setPosition(pos.build());
        node.getLeftHandSide().accept(this);
        b.addExpressions(expressions.pop());
        node.getRightHandSide().accept(this);
        b.addExpressions(expressions.pop());
        if (node.getOperator() == Assignment.Operator.ASSIGN)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.ASSIGN);
        else if (node.getOperator() == Assignment.Operator.BIT_AND_ASSIGN)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.ASSIGN_BITAND);
        else if (node.getOperator() == Assignment.Operator.BIT_OR_ASSIGN)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.ASSIGN_BITOR);
        else if (node.getOperator() == Assignment.Operator.BIT_XOR_ASSIGN)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.ASSIGN_BITXOR);
        else if (node.getOperator() == Assignment.Operator.DIVIDE_ASSIGN)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.ASSIGN_DIV);
        else if (node.getOperator() == Assignment.Operator.LEFT_SHIFT_ASSIGN)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.ASSIGN_LSHIFT);
        else if (node.getOperator() == Assignment.Operator.MINUS_ASSIGN)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.ASSIGN_SUB);
        else if (node.getOperator() == Assignment.Operator.PLUS_ASSIGN)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.ASSIGN_ADD);
        else if (node.getOperator() == Assignment.Operator.REMAINDER_ASSIGN)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.ASSIGN_MOD);
        else if (node.getOperator() == Assignment.Operator.RIGHT_SHIFT_SIGNED_ASSIGN)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.ASSIGN_RSHIFT);
        else if (node.getOperator() == Assignment.Operator.RIGHT_SHIFT_UNSIGNED_ASSIGN)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.ASSIGN_UNSIGNEDRSHIFT);
        else if (node.getOperator() == Assignment.Operator.TIMES_ASSIGN)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.ASSIGN_MULT);
        expressions.push(b.build());
        return false;
    }

    @Override
    public boolean visit(BooleanLiteral node) {
        boa.types.Ast.Expression.Builder b = boa.types.Ast.Expression.newBuilder();
        //      b.setPosition(pos.build());
        b.setKind(boa.types.Ast.Expression.ExpressionKind.LITERAL);
        if (node.booleanValue())
            b.setLiteral("true");
        else
            b.setLiteral("false");
        expressions.push(b.build());
        return false;
    }

    @Override
    public boolean visit(CastExpression node) {
        boa.types.Ast.Expression.Builder b = boa.types.Ast.Expression.newBuilder();
        //      b.setPosition(pos.build());
        b.setKind(boa.types.Ast.Expression.ExpressionKind.CAST);
        boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
        tb.setName(getIndex(typeName(node.getType())));
        tb.setKind(boa.types.Ast.TypeKind.OTHER);
        b.setNewType(tb.build());
        node.getExpression().accept(this);
        b.addExpressions(expressions.pop());
        expressions.push(b.build());
        return false;
    }

    @Override
    public boolean visit(CharacterLiteral node) {
        boa.types.Ast.Expression.Builder b = boa.types.Ast.Expression.newBuilder();
        //      b.setPosition(pos.build());
        b.setKind(boa.types.Ast.Expression.ExpressionKind.LITERAL);
        b.setLiteral(node.getEscapedValue());
        expressions.push(b.build());
        return false;
    }

    @Override
    public boolean visit(ClassInstanceCreation node) {
        boa.types.Ast.Expression.Builder b = boa.types.Ast.Expression.newBuilder();
        //      b.setPosition(pos.build());
        b.setKind(boa.types.Ast.Expression.ExpressionKind.NEW);
        boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
        tb.setName(getIndex(typeName(node.getType())));
        tb.setKind(boa.types.Ast.TypeKind.CLASS);
        b.setNewType(tb.build());
        for (Object t : node.typeArguments()) {
            boa.types.Ast.Type.Builder gtb = boa.types.Ast.Type.newBuilder();
            gtb.setName(getIndex(typeName((org.eclipse.jdt.core.dom.Type) t)));
            gtb.setKind(boa.types.Ast.TypeKind.GENERIC);
            b.addGenericParameters(gtb.build());
        }
        if (node.getExpression() != null) {
            node.getExpression().accept(this);
            b.addExpressions(expressions.pop());
        }
        for (Object a : node.arguments()) {
            ((org.eclipse.jdt.core.dom.Expression) a).accept(this);
            b.addExpressions(expressions.pop());
        }
        if (node.getAnonymousClassDeclaration() != null) {
            declarations.push(new ArrayList<boa.types.Ast.Declaration>());
            node.getAnonymousClassDeclaration().accept(this);
            for (boa.types.Ast.Declaration d : declarations.pop())
                b.setAnonDeclaration(d);
        }
        expressions.push(b.build());
        return false;
    }

    @Override
    public boolean visit(ConditionalExpression node) {
        boa.types.Ast.Expression.Builder b = boa.types.Ast.Expression.newBuilder();
        //      b.setPosition(pos.build());
        b.setKind(boa.types.Ast.Expression.ExpressionKind.CONDITIONAL);
        node.getExpression().accept(this);
        b.addExpressions(expressions.pop());
        node.getThenExpression().accept(this);
        b.addExpressions(expressions.pop());
        node.getElseExpression().accept(this);
        b.addExpressions(expressions.pop());
        expressions.push(b.build());
        return false;
    }

    @Override
    public boolean visit(FieldAccess node) {
        boa.types.Ast.Expression.Builder b = boa.types.Ast.Expression.newBuilder();
        //      b.setPosition(pos.build());
        b.setKind(boa.types.Ast.Expression.ExpressionKind.VARACCESS);
        node.getExpression().accept(this);
        b.addExpressions(expressions.pop());
        b.setVariable(node.getName().getFullyQualifiedName());
        expressions.push(b.build());
        return false;
    }

    @Override
    public boolean visit(SimpleName node) {
        boa.types.Ast.Expression.Builder b = boa.types.Ast.Expression.newBuilder();
        //      b.setPosition(pos.build());
        b.setKind(boa.types.Ast.Expression.ExpressionKind.VARACCESS);
        b.setVariable(node.getFullyQualifiedName());
        expressions.push(b.build());
        return false;
    }

    @Override
    public boolean visit(QualifiedName node) {
        boa.types.Ast.Expression.Builder b = boa.types.Ast.Expression.newBuilder();
        //      b.setPosition(pos.build());
        b.setKind(boa.types.Ast.Expression.ExpressionKind.VARACCESS);
        b.setVariable(node.getFullyQualifiedName());
        expressions.push(b.build());
        return false;
    }

    @Override
    public boolean visit(InfixExpression node) {
        boa.types.Ast.Expression.Builder b = boa.types.Ast.Expression.newBuilder();
        //      b.setPosition(pos.build());
        if (node.getOperator() == InfixExpression.Operator.AND)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.BIT_AND);
        else if (node.getOperator() == InfixExpression.Operator.CONDITIONAL_AND)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.LOGICAL_AND);
        else if (node.getOperator() == InfixExpression.Operator.CONDITIONAL_OR)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.LOGICAL_OR);
        else if (node.getOperator() == InfixExpression.Operator.DIVIDE)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.OP_DIV);
        else if (node.getOperator() == InfixExpression.Operator.EQUALS)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.EQ);
        else if (node.getOperator() == InfixExpression.Operator.GREATER)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.GT);
        else if (node.getOperator() == InfixExpression.Operator.GREATER_EQUALS)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.GTEQ);
        else if (node.getOperator() == InfixExpression.Operator.LEFT_SHIFT)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.BIT_LSHIFT);
        else if (node.getOperator() == InfixExpression.Operator.LESS)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.LT);
        else if (node.getOperator() == InfixExpression.Operator.LESS_EQUALS)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.LTEQ);
        else if (node.getOperator() == InfixExpression.Operator.MINUS)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.OP_SUB);
        else if (node.getOperator() == InfixExpression.Operator.NOT_EQUALS)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.NEQ);
        else if (node.getOperator() == InfixExpression.Operator.OR)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.BIT_OR);
        else if (node.getOperator() == InfixExpression.Operator.PLUS)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.OP_ADD);
        else if (node.getOperator() == InfixExpression.Operator.REMAINDER)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.OP_MOD);
        else if (node.getOperator() == InfixExpression.Operator.RIGHT_SHIFT_SIGNED)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.BIT_RSHIFT);
        else if (node.getOperator() == InfixExpression.Operator.RIGHT_SHIFT_UNSIGNED)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.BIT_UNSIGNEDRSHIFT);
        else if (node.getOperator() == InfixExpression.Operator.TIMES)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.OP_MULT);
        else if (node.getOperator() == InfixExpression.Operator.XOR)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.BIT_XOR);
        node.getLeftOperand().accept(this);
        b.addExpressions(expressions.pop());
        node.getRightOperand().accept(this);
        b.addExpressions(expressions.pop());
        for (Object e : node.extendedOperands()) {
            ((org.eclipse.jdt.core.dom.Expression) e).accept(this);
            b.addExpressions(expressions.pop());
        }
        expressions.push(b.build());
        return false;
    }

    @Override
    public boolean visit(InstanceofExpression node) {
        boa.types.Ast.Expression.Builder b = boa.types.Ast.Expression.newBuilder();
        //      b.setPosition(pos.build());
        b.setKind(boa.types.Ast.Expression.ExpressionKind.TYPECOMPARE);
        node.getLeftOperand().accept(this);
        b.addExpressions(expressions.pop());
        boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
        tb.setName(getIndex(typeName(node.getRightOperand())));
        tb.setKind(boa.types.Ast.TypeKind.OTHER);
        b.setNewType(tb.build());
        expressions.push(b.build());
        return false;
    }

    @Override
    public boolean visit(MethodInvocation node) {
        boa.types.Ast.Expression.Builder b = boa.types.Ast.Expression.newBuilder();
        //      b.setPosition(pos.build());
        b.setKind(boa.types.Ast.Expression.ExpressionKind.METHODCALL);
        b.setMethod(node.getName().getFullyQualifiedName());
        if (node.getExpression() != null) {
            node.getExpression().accept(this);
            b.addExpressions(expressions.pop());
        }
        for (Object a : node.arguments()) {
            ((org.eclipse.jdt.core.dom.Expression) a).accept(this);
            b.addMethodArgs(expressions.pop());
        }
        for (Object t : node.typeArguments()) {
            boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
            tb.setName(getIndex(typeName((org.eclipse.jdt.core.dom.Type) t)));
            tb.setKind(boa.types.Ast.TypeKind.GENERIC);
            b.addGenericParameters(tb.build());
        }
        expressions.push(b.build());
        return false;
    }

    @Override
    public boolean visit(NullLiteral node) {
        boa.types.Ast.Expression.Builder b = boa.types.Ast.Expression.newBuilder();
        //      b.setPosition(pos.build());
        b.setKind(boa.types.Ast.Expression.ExpressionKind.LITERAL);
        b.setLiteral("null");
        expressions.push(b.build());
        return false;
    }

    @Override
    public boolean visit(NumberLiteral node) {
        boa.types.Ast.Expression.Builder b = boa.types.Ast.Expression.newBuilder();
        //      b.setPosition(pos.build());
        b.setKind(boa.types.Ast.Expression.ExpressionKind.LITERAL);
        b.setLiteral(node.getToken());
        expressions.push(b.build());
        return false;
    }

    @Override
    public boolean visit(ParenthesizedExpression node) {
        boa.types.Ast.Expression.Builder b = boa.types.Ast.Expression.newBuilder();
        //      b.setPosition(pos.build());
        b.setKind(boa.types.Ast.Expression.ExpressionKind.PAREN);
        node.getExpression().accept(this);
        b.addExpressions(expressions.pop());
        expressions.push(b.build());
        return true;
    }

    @Override
    public boolean visit(PostfixExpression node) {
        boa.types.Ast.Expression.Builder b = boa.types.Ast.Expression.newBuilder();
        //      b.setPosition(pos.build());
        if (node.getOperator() == PostfixExpression.Operator.DECREMENT)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.OP_DEC);
        else if (node.getOperator() == PostfixExpression.Operator.INCREMENT)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.OP_INC);
        node.getOperand().accept(this);
        b.addExpressions(expressions.pop());
        b.setIsPostfix(true);
        expressions.push(b.build());
        return false;
    }

    @Override
    public boolean visit(PrefixExpression node) {
        boa.types.Ast.Expression.Builder b = boa.types.Ast.Expression.newBuilder();
        //      b.setPosition(pos.build());
        if (node.getOperator() == PrefixExpression.Operator.DECREMENT)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.OP_DEC);
        else if (node.getOperator() == PrefixExpression.Operator.INCREMENT)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.OP_INC);
        else if (node.getOperator() == PrefixExpression.Operator.PLUS)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.OP_ADD);
        else if (node.getOperator() == PrefixExpression.Operator.MINUS)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.OP_SUB);
        else if (node.getOperator() == PrefixExpression.Operator.COMPLEMENT)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.BIT_NOT);
        else if (node.getOperator() == PrefixExpression.Operator.NOT)
            b.setKind(boa.types.Ast.Expression.ExpressionKind.LOGICAL_NOT);
        node.getOperand().accept(this);
        b.addExpressions(expressions.pop());
        expressions.push(b.build());
        return false;
    }

    @Override
    public boolean visit(StringLiteral node) {
        boa.types.Ast.Expression.Builder b = boa.types.Ast.Expression.newBuilder();
        //      b.setPosition(pos.build());
        b.setKind(boa.types.Ast.Expression.ExpressionKind.LITERAL);
        b.setLiteral(node.getEscapedValue());
        expressions.push(b.build());
        return false;
    }

    @Override
    public boolean visit(SuperFieldAccess node) {
        boa.types.Ast.Expression.Builder b = boa.types.Ast.Expression.newBuilder();
        //      b.setPosition(pos.build());
        b.setKind(boa.types.Ast.Expression.ExpressionKind.VARACCESS);
        String name = "super." + node.getName().getFullyQualifiedName();
        if (node.getQualifier() != null)
            name = node.getQualifier().getFullyQualifiedName() + "." + name;
        b.setVariable(name);
        expressions.push(b.build());
        return false;
    }

    @Override
    public boolean visit(SuperMethodInvocation node) {
        boa.types.Ast.Expression.Builder b = boa.types.Ast.Expression.newBuilder();
        //      b.setPosition(pos.build());
        b.setKind(boa.types.Ast.Expression.ExpressionKind.METHODCALL);
        String name = "super." + node.getName().getFullyQualifiedName();
        if (node.getQualifier() != null)
            name = node.getQualifier().getFullyQualifiedName() + "." + name;
        b.setMethod(name);
        for (Object a : node.arguments()) {
            ((org.eclipse.jdt.core.dom.Expression) a).accept(this);
            b.addMethodArgs(expressions.pop());
        }
        for (Object t : node.typeArguments()) {
            boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
            tb.setName(getIndex(typeName((org.eclipse.jdt.core.dom.Type) t)));
            tb.setKind(boa.types.Ast.TypeKind.GENERIC);
            b.addGenericParameters(tb.build());
        }
        expressions.push(b.build());
        return false;
    }

    @Override
    public boolean visit(ThisExpression node) {
        boa.types.Ast.Expression.Builder b = boa.types.Ast.Expression.newBuilder();
        //      b.setPosition(pos.build());
        String name = "";
        if (node.getQualifier() != null)
            name += node.getQualifier().getFullyQualifiedName() + ".";
        b.setKind(boa.types.Ast.Expression.ExpressionKind.LITERAL);
        b.setLiteral(name + "this");
        expressions.push(b.build());
        return false;
    }

    @Override
    public boolean visit(TypeLiteral node) {
        boa.types.Ast.Expression.Builder b = boa.types.Ast.Expression.newBuilder();
        //      b.setPosition(pos.build());
        b.setKind(boa.types.Ast.Expression.ExpressionKind.LITERAL);
        b.setLiteral(typeName(node.getType()) + ".class");
        expressions.push(b.build());
        return false;
    }

    @Override
    public boolean visit(VariableDeclarationExpression node) {
        boa.types.Ast.Expression.Builder eb = boa.types.Ast.Expression.newBuilder();
        //      eb.setPosition(pos.build());
        eb.setKind(boa.types.Ast.Expression.ExpressionKind.VARDECL);
        for (Object o : node.fragments()) {
            VariableDeclarationFragment f = (VariableDeclarationFragment) o;
            Variable.Builder b = Variable.newBuilder();
            //         b.setPosition(pos.build());//FIXME
            b.setName(f.getName().getFullyQualifiedName());
            for (Object m : node.modifiers()) {
                if (((IExtendedModifier) m).isAnnotation())
                    ((Annotation) m).accept(this);
                else
                    ((org.eclipse.jdt.core.dom.Modifier) m).accept(this);
                b.addModifiers(modifiers.pop());
            }
            boa.types.Ast.Type.Builder tb = boa.types.Ast.Type.newBuilder();
            String name = typeName(node.getType());
            for (int i = 0; i < f.getExtraDimensions(); i++)
                name += "[]";
            tb.setName(getIndex(name));
            tb.setKind(boa.types.Ast.TypeKind.OTHER);
            b.setVariableType(tb.build());
            if (f.getInitializer() != null) {
                f.getInitializer().accept(this);
                b.setInitializer(expressions.pop());
            }
            eb.addVariableDecls(b.build());
        }
        expressions.push(eb.build());
        return false;
    }

    //////////////////////////////////////////////////////////////
    // Utility methods

    protected String typeName(final org.eclipse.jdt.core.dom.Type t) {
        if (t.isArrayType())
            return typeName((ArrayType) t);
        if (t.isParameterizedType())
            return typeName((ParameterizedType) t);
        if (t.isPrimitiveType())
            return typeName((PrimitiveType) t);
        if (t.isQualifiedType())
            return typeName((QualifiedType) t);
        if (t.isIntersectionType())
            return typeName((IntersectionType) t);
        if (t.isUnionType())
            return typeName((UnionType) t);
        if (t.isWildcardType())
            return typeName((WildcardType) t);
        return typeName((SimpleType) t);
    }

    protected String typeName(final ArrayType t) {
        return typeName(t.getComponentType()) + "[]";
    }

    protected String typeName(final ParameterizedType t) {
        String name = "";
        for (final Object o : t.typeArguments()) {
            if (name.length() > 0)
                name += ", ";
            name += typeName((org.eclipse.jdt.core.dom.Type) o);
        }
        return typeName(t.getType()) + "<" + name + ">";
    }

    protected String typeName(final PrimitiveType t) {
        return t.getPrimitiveTypeCode().toString();
    }

    protected String typeName(final QualifiedType t) {
        return typeName(t.getQualifier()) + "." + t.getName().getFullyQualifiedName();
    }

    protected String typeName(final IntersectionType t) {
        String name = "";
        for (final Object o : t.types()) {
            if (name.length() > 0)
                name += " & ";
            name += typeName((org.eclipse.jdt.core.dom.Type) o);
        }
        return name;
    }

    protected String typeName(final UnionType t) {
        String name = "";
        for (final Object o : t.types()) {
            if (name.length() > 0)
                name += " | ";
            name += typeName((org.eclipse.jdt.core.dom.Type) o);
        }
        return name;
    }

    protected String typeName(final WildcardType t) {
        String name = "?";
        if (t.getBound() != null) {
            name += " " + (t.isUpperBound() ? "extends" : "super");
            name += " " + typeName(t.getBound());
        }
        return name;
    }

    protected String typeName(final SimpleType t) {
        return t.getName().getFullyQualifiedName();
    }

    protected int getIndex(final String name) {
        Integer index = this.nameIndices.get(name);
        if (index == null) {
            index = this.nameIndices.size();
            this.nameIndices.put(name, index);
        }
        return index;
    }

    //////////////////////////////////////////////////////////////
    // Unused node types

    @Override
    public boolean visit(ArrayType node) {
        throw new RuntimeException("visited unused node " + node.getClass().getSimpleName());
    }

    @Override
    public boolean visit(ParameterizedType node) {
        throw new RuntimeException("visited unused node " + node.getClass().getSimpleName());
    }

    @Override
    public boolean visit(PrimitiveType node) {
        throw new RuntimeException("visited unused node " + node.getClass().getSimpleName());
    }

    @Override
    public boolean visit(QualifiedType node) {
        throw new RuntimeException("visited unused node " + node.getClass().getSimpleName());
    }

    @Override
    public boolean visit(SimpleType node) {
        throw new RuntimeException("visited unused node " + node.getClass().getSimpleName());
    }

    @Override
    public boolean visit(UnionType node) {
        throw new RuntimeException("visited unused node " + node.getClass().getSimpleName());
    }

    @Override
    public boolean visit(WildcardType node) {
        throw new RuntimeException("visited unused node " + node.getClass().getSimpleName());
    }

    @Override
    public boolean visit(EnumConstantDeclaration node) {
        throw new RuntimeException("visited unused node " + node.getClass().getSimpleName());
    }

    @Override
    public boolean visit(ImportDeclaration node) {
        throw new RuntimeException("visited unused node " + node.getClass().getSimpleName());
    }

    @Override
    public boolean visit(PackageDeclaration node) {
        throw new RuntimeException("visited unused node " + node.getClass().getSimpleName());
    }

    @Override
    public boolean visit(SingleVariableDeclaration node) {
        throw new RuntimeException("visited unused node " + node.getClass().getSimpleName());
    }

    @Override
    public boolean visit(MemberRef node) {
        throw new RuntimeException("visited unused node " + node.getClass().getSimpleName());
    }

    @Override
    public boolean visit(MemberValuePair node) {
        throw new RuntimeException("visited unused node " + node.getClass().getSimpleName());
    }

    @Override
    public boolean visit(TypeParameter node) {
        throw new RuntimeException("visited unused node " + node.getClass().getSimpleName());
    }

    @Override
    public boolean visit(VariableDeclarationFragment node) {
        throw new RuntimeException("visited unused node " + node.getClass().getSimpleName());
    }

    @Override
    public boolean visit(MethodRef node) {
        throw new RuntimeException("visited unused node " + node.getClass().getSimpleName());
    }

    @Override
    public boolean visit(MethodRefParameter node) {
        throw new RuntimeException("visited unused node " + node.getClass().getSimpleName());
    }

    @Override
    public boolean visit(TagElement node) {
        throw new RuntimeException("visited unused node " + node.getClass().getSimpleName());
    }

    @Override
    public boolean visit(TextElement node) {
        throw new RuntimeException("visited unused node " + node.getClass().getSimpleName());
    }
}