edu.cmu.cs.crystal.cfg.eclipse.EclipseCFG.java Source code

Java tutorial

Introduction

Here is the source code for edu.cmu.cs.crystal.cfg.eclipse.EclipseCFG.java

Source

/**
 * Copyright (c) 2006, 2007, 2008 Marwan Abi-Antoun, Jonathan Aldrich, Nels E. Beckman, Kevin
 * Bierhoff, David Dickey, Ciera Jaspan, Thomas LaToza, Gabriel Zenarosa, and others.
 * 
 * This file is part of Crystal.
 * 
 * Crystal is free software: you can redistribute it and/or modify it under the terms of the GNU
 * Lesser General Public License as published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 * 
 * Crystal is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
 * General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with Crystal. If
 * not, see <http://www.gnu.org/licenses/>.
 */
package edu.cmu.cs.crystal.cfg.eclipse;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;

import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
import org.eclipse.jdt.core.dom.ArrayAccess;
import org.eclipse.jdt.core.dom.ArrayCreation;
import org.eclipse.jdt.core.dom.ArrayInitializer;
import org.eclipse.jdt.core.dom.ArrayType;
import org.eclipse.jdt.core.dom.AssertStatement;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.BooleanLiteral;
import org.eclipse.jdt.core.dom.BreakStatement;
import org.eclipse.jdt.core.dom.CastExpression;
import org.eclipse.jdt.core.dom.CatchClause;
import org.eclipse.jdt.core.dom.CharacterLiteral;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.ConditionalExpression;
import org.eclipse.jdt.core.dom.ConstructorInvocation;
import org.eclipse.jdt.core.dom.ContinueStatement;
import org.eclipse.jdt.core.dom.DoStatement;
import org.eclipse.jdt.core.dom.EnhancedForStatement;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.ExpressionStatement;
import org.eclipse.jdt.core.dom.FieldAccess;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.ForStatement;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IfStatement;
import org.eclipse.jdt.core.dom.InfixExpression;
import org.eclipse.jdt.core.dom.InfixExpression.Operator;
import org.eclipse.jdt.core.dom.InstanceofExpression;
import org.eclipse.jdt.core.dom.LabeledStatement;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.NullLiteral;
import org.eclipse.jdt.core.dom.NumberLiteral;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.ParenthesizedExpression;
import org.eclipse.jdt.core.dom.PostfixExpression;
import org.eclipse.jdt.core.dom.PrefixExpression;
import org.eclipse.jdt.core.dom.PrimitiveType;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.QualifiedType;
import org.eclipse.jdt.core.dom.ReturnStatement;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.StringLiteral;
import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
import org.eclipse.jdt.core.dom.SuperFieldAccess;
import org.eclipse.jdt.core.dom.SuperMethodInvocation;
import org.eclipse.jdt.core.dom.SwitchCase;
import org.eclipse.jdt.core.dom.SwitchStatement;
import org.eclipse.jdt.core.dom.SynchronizedStatement;
import org.eclipse.jdt.core.dom.ThisExpression;
import org.eclipse.jdt.core.dom.ThrowStatement;
import org.eclipse.jdt.core.dom.TryStatement;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.TypeDeclarationStatement;
import org.eclipse.jdt.core.dom.TypeLiteral;
import org.eclipse.jdt.core.dom.VariableDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
import org.eclipse.jdt.core.dom.WhileStatement;
import org.eclipse.jdt.core.dom.WildcardType;

import att.grappa.Graph;
import edu.cmu.cs.crystal.cfg.BlockStack;
import edu.cmu.cs.crystal.cfg.ExceptionMap;
import edu.cmu.cs.crystal.cfg.ICFGNode;
import edu.cmu.cs.crystal.cfg.IControlFlowGraph;
import edu.cmu.cs.crystal.flow.BooleanLabel;
import edu.cmu.cs.crystal.flow.ExceptionalLabel;
import edu.cmu.cs.crystal.flow.ILabel;
import edu.cmu.cs.crystal.flow.IteratorLabel;
import edu.cmu.cs.crystal.flow.NormalLabel;
import edu.cmu.cs.crystal.flow.SwitchLabel;

/**
 * Builds a CFG on the Eclipse AST. This class has been tested at the method
 * level only, it has not been tested at higher or lower levels, though it would
 * theoretically work.
 * 
 * There is an interesting problem regarding when the transfer function for a
 * node appears if it is a control flow statement. That is, when does the
 * transfer statement for constructs like if and while occur? We have produced
 * three possibilities:
 * 
 * Kevin: Should appear at the point when the branching of control flow occurs.
 * For example, the the conditionals of if and while.
 * 
 * Nels: Should appear at the merge point of the control flow. This is the
 * conditional for while, and the node that merges the two if branches.
 * 
 * Ciera: Should appear after all the children have been visited. This is the
 * merge point on if, and it is the node after the conditional on while.
 * 
 * Then we decided that no one in their right mind should be using transfer
 * functions on control flow anyway, so we can do whatever we want. Since I'm
 * writing this and I have a nice design for the third option anyway, we're
 * going with that.
 * 
 * Note: Since this was written someone (Thomas? Nels?) did need to transfer on
 * the control flow. He needed it at the merge point though. The subtype
 * 
 * @link{EclipseNodeFirstCFG handles that, and is currently the default control
 *                           flow graph used in Crystal.
 * 
 *                           Algorithm description: This class visits each node
 *                           of the AST. When it visits a node, it maps the
 *                           ASTNode to the CFGNode that it is creating. This
 *                           allows a parent node to pull it out later and
 *                           insert it into the graph. There are several other
 *                           data structures to help us with control flow. These
 *                           are described individually later. - In the previsit
 *                           method, we create the node and put it in the map. -
 *                           In the visit method, we prepare any data structures
 *                           that will be needed by the children. - In the
 *                           endvisit method, we put together the CFG for this
 *                           node and its children (with the exception of any
 *                           edges that were added by children through the data
 *                           structures.
 * 
 * @author ciera
 */
public class EclipseCFG extends ASTVisitor implements IControlFlowGraph<ASTNode>, Cloneable {

    // build simple cfg first, many edges into and out of finally
    // then traverse using a DFS. When we encounter an exception tag, track it
    // to a finally.
    // mark the entrance/exit of the finally duplicate if necessary and store
    // remember to stop within a finally and use the "original" if we hit a
    // control flow statement that overrides normal flow
    // continue until no more tags have been traversed.

    // protected EclipseCFGNode normalExit;

    protected BlockStack<EclipseCFGNode> blockStack;

    protected ExceptionMap<ASTNode, EclipseCFGNode> exceptionMap;

    protected HashMap<ASTNode, EclipseCFGNode> nodeMap;

    /**
     * Each call to endvisit will reset what the startNode and endNode is.
     */
    protected EclipseCFGNode startNode;
    protected EclipseCFGNode uberReturn;
    protected Map<ITypeBinding, EclipseCFGNode> excpReturns;
    protected EclipseCFGNode endNode;
    protected String name;

    private EclipseCFGNode undeclExit;

    public EclipseCFG(MethodDeclaration method) {
        nodeMap = new HashMap<ASTNode, EclipseCFGNode>();
        blockStack = new BlockStack<EclipseCFGNode>();
        exceptionMap = new ExceptionMap<ASTNode, EclipseCFGNode>();
        EclipseCFGNode.NEXT_ID = 0;
        createGraph(method);
    }

    public EclipseCFG() {
        nodeMap = new HashMap<ASTNode, EclipseCFGNode>();
        blockStack = new BlockStack<EclipseCFGNode>();
        exceptionMap = new ExceptionMap<ASTNode, EclipseCFGNode>();
        EclipseCFGNode.NEXT_ID = 0;
    }

    public void createGraph(MethodDeclaration method) {
        name = method.getName().getFullyQualifiedName();
        method.accept(this);
    }

    public ICFGNode<ASTNode> getStartNode() {
        return startNode;
    }

    public ICFGNode<ASTNode> getEndNode() {
        return endNode;
    }

    public ICFGNode<ASTNode> getUberReturn() {
        return uberReturn;
    }

    public ICFGNode<ASTNode> getUndeclaredExit() {
        return undeclExit;
    }

    public Map<ITypeBinding, EclipseCFGNode> getExceptionalExits() {
        return excpReturns;
    }

    public Graph getDotGraph() {
        Graph graph = new Graph(name);
        startNode.addToGraph(graph);
        return graph;
    }

    public Map<ASTNode, EclipseCFGNode> getNodeMap() {
        return this.nodeMap;
    }

    /**
     * Why might source be null? We get source from an endNode of something
     * else. endNodes are allowed to be null if a node decides to handle their
     * own outgoing control flow. This happens in breaks, returns, continues,
     * throws, etc.
     */
    private void createEdge(EclipseCFGNode source, EclipseCFGNode sink, ILabel label) {
        if (source != null) {
            EclipseCFGEdge edge = new EclipseCFGEdge(source, sink, label);
            source.addOutputEdge(edge);
            sink.addInputEdge(edge);
        }
    }

    protected void createEdge(EclipseCFGNode source, EclipseCFGNode sink) {
        NormalLabel label = NormalLabel.getNormalLabel();
        createEdge(source, sink, label);
    }

    protected void createItrEdge(EclipseCFGNode source, EclipseCFGNode sink, boolean isEmpty) {
        IteratorLabel label = IteratorLabel.getItrLabel(isEmpty);
        createEdge(source, sink, label);
    }

    protected void createBooleanEdge(EclipseCFGNode source, EclipseCFGNode sink, boolean boolValue) {
        BooleanLabel label = BooleanLabel.getBooleanLabel(boolValue);
        createEdge(source, sink, label);
    }

    protected void createEdge(EclipseCFGNode source, EclipseCFGNode sink, ITypeBinding exception) {
        ExceptionalLabel label = new ExceptionalLabel(exception);
        createEdge(source, sink, label);
    }

    protected void createEdge(EclipseCFGNode source, EclipseCFGNode sink, Expression switchCase) {
        SwitchLabel label = new SwitchLabel(switchCase);
        createEdge(source, sink, label);
    }

    private void makeListEdges(EclipseCFGNode init, List<ASTNode> list, EclipseCFGNode parent) {
        EclipseCFGNode current, last = init;

        if (last != null) {
            parent.setStart(last.getStart());
        }

        for (ASTNode node : list) {
            current = nodeMap.get(node);
            if (last != null) {
                createEdge(last.getEnd(), current.getStart());
            } else
                parent.setStart(current.getStart());
            last = current;
        }

        if (last != null) {
            createEdge(last.getEnd(), parent);
        }
    }

    @Override
    public void preVisit(ASTNode node) {
        EclipseCFGNode cfgNode = new EclipseCFGNode(node);
        nodeMap.put(node, cfgNode);
    }

    /**
     * Creates an edge DIRECTLY from source to the proper finally blocks, and
     * then from the finally blocks DIRECTLY to the destination. By directly,
     * this means that it won't call getEnd or getStart. It uses the
     * exceptionToStopAt to determine which finally blocks to grab. It will also
     * make the edge from the source to the finally block have this exception. A
     * normal edge will be used when exceptionToStopAt is null
     */
    protected void hookFinally(EclipseCFGNode source, ITypeBinding exceptionToStopAt, EclipseCFGNode dest) {
        Stack<EclipseCFGNode> finallyStack = exceptionMap.getFinallyToException(exceptionToStopAt);
        EclipseCFGNode last = null, current = null, cloneCurrent = null, first = null;

        while (!finallyStack.isEmpty()) {
            current = finallyStack.pop();
            cloneCurrent = copySubgraph(current);

            if (first == null)
                first = cloneCurrent;

            if (last != null)
                createEdge(last.getEnd(), cloneCurrent.getStart());
            last = cloneCurrent;
        }

        if (first != null) { // there is a finally
            if (exceptionToStopAt == null)
                createEdge(source, first.getStart());
            else
                createEdge(source, first.getStart(), exceptionToStopAt);
            createEdge(last.getEnd(), dest);
        } else {
            // no finally
            if (exceptionToStopAt == null)
                createEdge(source, dest);
            else
                createEdge(source, dest, exceptionToStopAt);
        }
    }

    private EclipseCFGNode copySubgraph(EclipseCFGNode current) {
        HashMap<EclipseCFGNode, EclipseCFGNode> cloneMap = new HashMap<EclipseCFGNode, EclipseCFGNode>();
        EclipseCFGNode cloneNode;

        copySubgraphRecur(current.getStart(), current.getEnd(), cloneMap);

        cloneNode = cloneMap.get(current);
        return cloneNode;
    }

    /**
     * Modified by Na Meng to prevent infinite loop when enumerating edges
     * 
     * @param current
     * @return
     */
    private EclipseCFGNode copySubgraphRecur(EclipseCFGNode current, EclipseCFGNode stopNode,
            HashMap<EclipseCFGNode, EclipseCFGNode> cloneMap) {
        ASTNode node = current.getASTNode();
        EclipseCFGNode clone = cloneMap.get(current);
        if (clone != null)
            return clone;

        clone = new EclipseCFGNode(node);
        EclipseCFGNode start = current.getStart();
        EclipseCFGNode end = current.getEnd();

        clone.setName(current.getName());
        if (cloneMap.containsKey(start))
            start = cloneMap.get(start);
        if (cloneMap.containsKey(end))
            end = cloneMap.get(end);

        clone.setStart(start);
        clone.setEnd(end);
        cloneMap.put(current, clone);

        if (current == stopNode || node instanceof ReturnStatement || node instanceof ThrowStatement
                || node instanceof BreakStatement || node instanceof ContinueStatement) {
            for (EclipseCFGEdge edge : current.getOutputs()) {
                EclipseCFGEdge cloneEdge = new EclipseCFGEdge(clone, edge.getSink(), edge.getLabel());
                edge.getSink().addInputEdge(cloneEdge);
                clone.addOutputEdge(cloneEdge);
            }
        } else {
            for (EclipseCFGEdge edge : current.getOutputs()) {
                EclipseCFGNode cloneSink = copySubgraphRecur(edge.getSink(), stopNode, cloneMap);
                EclipseCFGEdge cloneEdge = new EclipseCFGEdge(clone, cloneSink, edge.getLabel());

                cloneSink.addInputEdge(cloneEdge);
                clone.addOutputEdge(cloneEdge);
            }
        }

        return clone;
    }

    /* CONTROL FLOW */
    @Override
    public void endVisit(AssertStatement node) {
        EclipseCFGNode assertNode = nodeMap.get(node);
        EclipseCFGNode expNode = nodeMap.get(node.getExpression());
        EclipseCFGNode messageNode = nodeMap.get(node.getMessage());
        EclipseCFGNode falsePath = new EclipseCFGNode(null);
        ITypeBinding binding = node.getAST().resolveWellKnownType("java.lang.Throwable");
        EclipseCFGNode catchNode = exceptionMap.getCatchNode(binding);

        createEdge(assertNode, expNode.getStart());
        assertNode.setStart(expNode.getStart());
        createBooleanEdge(expNode.getEnd(), assertNode, true);

        falsePath.setName("POP!");

        if (messageNode != null) {
            createBooleanEdge(expNode.getEnd(), messageNode.getStart(), false);
            createEdge(messageNode.getEnd(), falsePath);
        } else {
            createBooleanEdge(expNode.getEnd(), falsePath, false);
        }

        hookFinally(falsePath, binding, catchNode);

    }

    @Override
    public boolean visit(BreakStatement node) {
        EclipseCFGNode breakStmnt = nodeMap.get(node);
        blockStack.overrideIfExists(node, breakStmnt, null);
        return true;
    }

    @Override
    public void endVisit(BreakStatement node) {
        EclipseCFGNode breakStmnt = nodeMap.get(node);
        String label = (node.getLabel() != null) ? node.getLabel().getIdentifier() : null;

        EclipseCFGNode breakPoint = blockStack.getBreakPoint(label);
        breakStmnt.setName("break");
        hookFinally(breakStmnt, null, breakPoint);
        breakStmnt.setEnd(null);
    }

    @Override
    public void endVisit(ContinueStatement node) {
        EclipseCFGNode continueStmnt = nodeMap.get(node);
        String label = (node.getLabel() != null) ? node.getLabel().getIdentifier() : null;

        EclipseCFGNode continuePoint = blockStack.getContinuePoint(label);
        continueStmnt.setName("continue");
        hookFinally(continueStmnt, null, continuePoint);
        continueStmnt.setEnd(null);
    }

    @Override
    public void endVisit(ConditionalExpression node) {
        EclipseCFGNode ifExp = nodeMap.get(node);
        EclipseCFGNode cond = nodeMap.get(node.getExpression());
        EclipseCFGNode thenClause = nodeMap.get(node.getThenExpression());
        EclipseCFGNode elseClause = nodeMap.get(node.getElseExpression());

        createBooleanEdge(cond.getEnd(), thenClause.getStart(), true);
        createEdge(thenClause.getEnd(), ifExp);
        createBooleanEdge(cond.getEnd(), elseClause.getStart(), false);
        createEdge(elseClause.getEnd(), ifExp);

        ifExp.setStart(cond.getStart());
        ifExp.setName("? :");
    }

    @Override
    public boolean visit(DoStatement node) {
        EclipseCFGNode doEnd = nodeMap.get(node);
        EclipseCFGNode doBegin = new EclipseCFGNode(null);

        blockStack.pushUnlabeled(node, doEnd, doBegin);
        doEnd.setStart(doBegin);

        return true;
    }

    @Override
    public void endVisit(DoStatement node) {
        EclipseCFGNode doEnd = nodeMap.get(node);
        EclipseCFGNode cond = nodeMap.get(node.getExpression());
        EclipseCFGNode body = nodeMap.get(node.getBody());
        EclipseCFGNode doBegin = doEnd.getStart();

        createEdge(doBegin, body.getStart());
        createEdge(body.getEnd(), cond.getStart());
        createBooleanEdge(cond.getEnd(), body.getStart(), true);
        createBooleanEdge(cond.getEnd(), doEnd, false);

        blockStack.popUnlabeled();

        doBegin.setName("do");
        doEnd.setName("od");
    }

    @Override
    public boolean visit(EnhancedForStatement node) {
        EclipseCFGNode eforEnd = nodeMap.get(node);
        EclipseCFGNode eforBegin = new EclipseCFGNode(null);

        blockStack.pushUnlabeled(node, eforEnd, eforBegin);
        eforEnd.setStart(eforBegin);

        return true;
    }

    @Override
    public void endVisit(EnhancedForStatement node) {
        EclipseCFGNode eforEnd = nodeMap.get(node);
        EclipseCFGNode list = nodeMap.get(node.getExpression());
        EclipseCFGNode body = nodeMap.get(node.getBody());
        EclipseCFGNode param = nodeMap.get(node.getParameter());
        EclipseCFGNode eforBegin = eforEnd.getStart();

        eforEnd.setStart(list.getStart());

        createEdge(list.getEnd(), eforBegin);
        createItrEdge(eforBegin, param.getStart(), false);
        createItrEdge(eforBegin, eforEnd, true);
        createEdge(param.getEnd(), body.getStart());
        createEdge(body.getEnd(), eforBegin);

        blockStack.popUnlabeled();

        eforBegin.setName("efor");
        eforEnd.setName("rofe");
    }

    @Override
    public boolean visit(ForStatement node) {
        EclipseCFGNode forEnd = nodeMap.get(node);
        EclipseCFGNode forBegin = new EclipseCFGNode(null);

        blockStack.pushUnlabeled(node, forEnd, forBegin);
        forEnd.setStart(forBegin);

        return true;
    }

    @Override
    public void endVisit(ForStatement node) {
        EclipseCFGNode forEnd = nodeMap.get(node);
        EclipseCFGNode cond = nodeMap.get(node.getExpression());
        EclipseCFGNode body = nodeMap.get(node.getBody());
        EclipseCFGNode forBegin = forEnd.getStart();
        EclipseCFGNode current, last = null;

        // first, run the initializers
        for (ASTNode init : (List<ASTNode>) node.initializers()) {
            current = nodeMap.get(init);
            if (last != null)
                createEdge(last.getEnd(), current.getStart());
            else
                forEnd.setStart(current.getStart());
            last = current;
        }

        if (cond != null) {
            if (last != null)
                createEdge(last.getEnd(), cond.getStart());
            else
                forEnd.setStart(cond.getStart());

            createBooleanEdge(cond.getEnd(), forEnd, false);
            createBooleanEdge(cond.getEnd(), body.getStart(), true);
        } else {
            if (last != null)
                createEdge(last.getEnd(), body.getStart());
            else
                forEnd.setStart(body.getStart());
        }

        // notice that we are inserting forbegin after the initializers,
        // but just before the updaters. This is so that
        // when we hit a continue, we jump to the updaters and NOT the
        // inializers.
        // it is a little weird since the begin point is after the body, but
        // since it's
        // a loop, it's also before the body. :)
        // we also changed forEnd.getStart so that it no longer returns forBegin
        // (see above)
        createEdge(body.getEnd(), forBegin);

        last = forBegin;
        for (ASTNode update : (List<ASTNode>) node.updaters()) {
            current = nodeMap.get(update);
            createEdge(last.getEnd(), current.getStart());
            last = current;
        }

        if (cond != null)
            createEdge(last.getEnd(), cond.getStart());
        else
            createEdge(last.getEnd(), body.getStart());

        blockStack.popUnlabeled();

        forBegin.setName("for");
        forEnd.setName("rof");
    }

    @Override
    public boolean visit(LabeledStatement node) {
        blockStack.pushLabeled(node.getLabel().getIdentifier(), node.getBody());
        return true;
    }

    @Override
    public void endVisit(LabeledStatement node) {
        EclipseCFGNode label = nodeMap.get(node);
        EclipseCFGNode body = nodeMap.get(node.getBody());

        createEdge(body.getEnd(), label);

        blockStack.popLabeled();

        label.setName("label " + node.getLabel().toString());
        label.setStart(body.getStart());
    }

    @Override
    public boolean visit(IfStatement node) {
        EclipseCFGNode ifStmnt = nodeMap.get(node);
        blockStack.overrideIfExists(node, ifStmnt, null);
        return true;
    }

    @Override
    public void endVisit(IfStatement node) {
        EclipseCFGNode ifStmnt = nodeMap.get(node);
        EclipseCFGNode cond = nodeMap.get(node.getExpression());
        EclipseCFGNode thenClause = nodeMap.get(node.getThenStatement());

        createBooleanEdge(cond.getEnd(), thenClause.getStart(), true);
        createEdge(thenClause.getEnd(), ifStmnt);

        if (node.getElseStatement() != null) {
            EclipseCFGNode elseClause = nodeMap.get(node.getElseStatement());
            createBooleanEdge(cond.getEnd(), elseClause.getStart(), false);
            createEdge(elseClause.getEnd(), ifStmnt);
        } else {
            createBooleanEdge(cond.getEnd(), ifStmnt, false);
        }

        ifStmnt.setStart(cond.getStart());
        ifStmnt.setName("if");
    }

    @Override
    public boolean visit(MethodDeclaration node) {
        EclipseCFGNode method = nodeMap.get(node);
        EclipseCFGNode implicitCatch;

        excpReturns = new HashMap<ITypeBinding, EclipseCFGNode>();

        undeclExit = new EclipseCFGNode(null);
        createEdge(undeclExit, method);
        undeclExit.setName("(error)");
        exceptionMap.pushCatch(undeclExit, node.getAST().resolveWellKnownType("java.lang.Throwable"));

        for (Name name : (List<Name>) node.thrownExceptions()) {
            implicitCatch = new EclipseCFGNode(null);
            createEdge(implicitCatch, method);
            implicitCatch.setName("(throws)");
            exceptionMap.pushCatch(implicitCatch, (ITypeBinding) name.resolveBinding());
            excpReturns.put(name.resolveTypeBinding(), implicitCatch);
        }

        uberReturn = new EclipseCFGNode(null);
        uberReturn.setName("(uber-return)");
        createEdge(uberReturn, method);

        if (node.isConstructor()) {
            TypeDeclaration type = (TypeDeclaration) node.getParent();
            for (FieldDeclaration field : type.getFields()) {
                if (!Modifier.isStatic(field.getModifiers()))
                    field.accept(this);
            }
        }

        // visit the statements individually.
        // we'll need to put them together by hand later so we can insert the
        // field decls
        // into constructors.
        for (ASTNode param : (List<ASTNode>) node.parameters())
            param.accept(this);
        if (node.getBody() != null)
            for (ASTNode stmt : (List<ASTNode>) node.getBody().statements())
                stmt.accept(this);

        return false;
    }

    @Override
    public void endVisit(MethodDeclaration node) {
        EclipseCFGNode method = nodeMap.get(node);
        EclipseCFGNode body = null;
        EclipseCFGNode current, last = null;

        // connect params together
        for (ASTNode arg : (List<ASTNode>) node.parameters()) {
            current = nodeMap.get(arg);
            if (last != null)
                createEdge(last.getEnd(), current.getStart());
            last = current;
        }

        if (node.isConstructor())
            body = setUpConstructorBody(node);
        else
            body = setUpMethodBody(node);

        // connect the end of the body to the fall-off return
        createEdge(body.getEnd(), uberReturn);

        if (last != null) {
            createEdge(last.getEnd(), body.getStart());
            method.setStart(nodeMap.get(node.parameters().get(0)).getStart());
        } else
            method.setStart(body.getStart());

        // finish off exceptions
        for (int ndx = 0; ndx < node.thrownExceptions().size(); ndx++)
            exceptionMap.popCatch();
        // exceptionMap.popFinally();

        startNode = method.getStart();
        endNode = method;
        method.setName("Declare " + node.getName().getIdentifier());
    }

    /**
     * set up the body part of a constructor declaration. Return the node that
     * represents the body.
     * 
     * @param node
     * @return
     */
    private EclipseCFGNode setUpConstructorBody(MethodDeclaration node) {
        EclipseCFGNode last = null, current = null;
        List<ASTNode> statements = new ArrayList<ASTNode>(node.getBody().statements());
        ASTNode firstStmt = null;
        EclipseCFGNode body = new EclipseCFGNode(node.getBody());

        // connect field declarations with initializers together
        for (FieldDeclaration field : ((TypeDeclaration) node.getParent()).getFields()) {
            if (Modifier.isStatic(field.getModifiers()))
                continue;
            for (VariableDeclarationFragment frag : (List<VariableDeclarationFragment>) field.fragments()) {
                if (frag.getInitializer() != null) {
                    current = nodeMap.get(frag);
                    if (last != null) {
                        createEdge(last.getEnd(), current.getStart());
                        current.setStart(last.getStart());
                    }
                    last = current;
                }
            }
        }

        // now figure out where to insert the initializers
        if (statements.size() > 0) {
            firstStmt = statements.get(0);

            if (firstStmt instanceof SuperConstructorInvocation) {
                current = nodeMap.get(firstStmt);
                if (last != null) {
                    createEdge(current.getEnd(), last.getStart());
                    current.setEnd(last.getEnd());
                }
                last = current;
                statements.remove(firstStmt);
            } else if (firstStmt instanceof ConstructorInvocation) {
                last = null;
            }
        }

        makeListEdges(last, statements, body);

        return body;
    }

    /**
     * set up the body part of a method declaration. Return the node that
     * represents the body.
     * 
     * @param node
     * @return
     */
    private EclipseCFGNode setUpMethodBody(MethodDeclaration node) {
        EclipseCFGNode body = new EclipseCFGNode(node.getBody());

        if (node.getBody() != null)
            makeListEdges(null, node.getBody().statements(), body);

        return body;
    }

    @Override
    public void endVisit(ReturnStatement node) {
        EclipseCFGNode ret = nodeMap.get(node);
        EclipseCFGNode exp = nodeMap.get(node.getExpression());

        if (exp != null) {
            createEdge(exp.getEnd(), ret);
            ret.setStart(exp.getStart());
        }

        hookFinally(ret, (ITypeBinding) null, uberReturn);

        ret.setEnd(null);
        ret.setName("return");
    }

    @Override
    public boolean visit(SwitchStatement node) {
        EclipseCFGNode switchEnd = nodeMap.get(node);
        EclipseCFGNode switchBegin = new EclipseCFGNode(null);

        blockStack.pushUnlabeled(node, switchEnd, null);
        switchEnd.setStart(switchBegin);

        return true;
    }

    @Override
    public void endVisit(SwitchStatement node) {
        EclipseCFGNode switchEnd = nodeMap.get(node);
        EclipseCFGNode switchBegin = switchEnd.getStart();
        EclipseCFGNode exp = nodeMap.get(node.getExpression());
        EclipseCFGNode last = null;
        boolean hasDefault = false;
        List<ASTNode> stmnts = (List<ASTNode>) node.statements();

        createEdge(switchBegin, exp.getStart());

        last = switchEnd;
        for (int ndx = stmnts.size() - 1; ndx >= 0; ndx--) {
            ASTNode currentAST = stmnts.get(ndx);
            EclipseCFGNode current = nodeMap.get(currentAST);

            if (currentAST instanceof SwitchCase) {
                createEdge(exp.getEnd(), current.getStart(), ((SwitchCase) currentAST).getExpression());
                createEdge(current.getEnd(), last);
                hasDefault = hasDefault || ((SwitchCase) currentAST).getExpression() == null;
                continue;
            }
            createEdge(current.getEnd(), last);
            last = current.getStart();
        }

        if (!hasDefault)
            createEdge(exp.getEnd(), switchEnd, (Expression) null);

        blockStack.popUnlabeled();

        switchBegin.setName("switch");
        switchEnd.setName("hctiws");

    }

    @Override
    public boolean visit(CatchClause node) {
        EclipseCFGNode catchNode = nodeMap.get(node);
        blockStack.overrideIfExists(node, catchNode, null);
        return true;
    }

    @Override
    public void endVisit(CatchClause node) {
        EclipseCFGNode catchNode = nodeMap.get(node);
        EclipseCFGNode declNode = nodeMap.get(node.getException());
        EclipseCFGNode bodyNode = nodeMap.get(node.getBody());

        createEdge(declNode.getEnd(), bodyNode.getStart());
        createEdge(bodyNode.getEnd(), catchNode);

        catchNode.setStart(declNode.getStart());
        catchNode.setName("catch");
    }

    @Override
    public void endVisit(ThrowStatement node) {
        EclipseCFGNode throwNode = nodeMap.get(node);
        EclipseCFGNode expNode = nodeMap.get(node.getExpression());
        ITypeBinding binding = node.getExpression().resolveTypeBinding();
        EclipseCFGNode catchNode = exceptionMap.getCatchNode(binding);
        EclipseCFGNode current = throwNode;

        createEdge(expNode.getEnd(), throwNode);

        if (catchNode != null)
            hookFinally(throwNode, binding, catchNode.getStart());

        throwNode.setStart(expNode.getStart());
        throwNode.setName("throw");
        throwNode.setEnd(null);
    }

    @Override
    public boolean visit(TryStatement node) {
        EclipseCFGNode tryNode = nodeMap.get(node);
        EclipseCFGNode finallyNode = null, catchNode, bodyNode;

        blockStack.overrideIfExists(node, tryNode, null);

        // first analyze the finally
        if (node.getFinally() != null) {
            node.getFinally().accept(this);
            finallyNode = nodeMap.get(node.getFinally());
            exceptionMap.pushFinally(finallyNode);

        }

        // then analyze the catches
        for (CatchClause catchClause : (List<CatchClause>) node.catchClauses()) {
            catchClause.accept(this);
            catchNode = nodeMap.get(catchClause);
            exceptionMap.pushCatch(catchNode, catchClause.getException().getType().resolveBinding());
        }

        // analyze body now that the exceptionMap is set up
        node.getBody().accept(this);
        bodyNode = nodeMap.get(node.getBody());

        // remove the catches
        for (int ndx = 0; ndx < node.catchClauses().size(); ndx++)
            exceptionMap.popCatch();

        // set up normal flow
        if (finallyNode != null) {
            exceptionMap.popFinally();
            if (bodyNode.getEnd() != null) {
                // if the try has no normal ending, don't attach it to the
                // finally
                createEdge(bodyNode.getEnd(), finallyNode.getStart());
                createEdge(finallyNode.getEnd(), tryNode);
            }
        } else
            createEdge(bodyNode.getEnd(), tryNode);

        // set up exceptional flow
        for (CatchClause catchClause : (List<CatchClause>) node.catchClauses()) {
            catchNode = nodeMap.get(catchClause);

            if (finallyNode != null)
                createEdge(catchNode.getEnd(), finallyNode.getStart());
            else
                createEdge(catchNode.getEnd(), tryNode);
        }

        tryNode.setStart(bodyNode.getStart());
        tryNode.setName("try");
        return false;
    }

    @Override
    public boolean visit(WhileStatement node) {
        EclipseCFGNode whileEnd = nodeMap.get(node);
        EclipseCFGNode whileBegin = new EclipseCFGNode(null);

        blockStack.pushUnlabeled(node, whileEnd, whileBegin);
        whileEnd.setStart(whileBegin);

        return true;
    }

    @Override
    public void endVisit(WhileStatement node) {
        EclipseCFGNode whileEnd = nodeMap.get(node);
        EclipseCFGNode cond = nodeMap.get(node.getExpression());
        EclipseCFGNode body = nodeMap.get(node.getBody());
        EclipseCFGNode whileBegin = whileEnd.getStart();

        createEdge(whileBegin, cond.getStart());
        createBooleanEdge(cond.getEnd(), whileEnd, false);
        createBooleanEdge(cond.getEnd(), body.getStart(), true);
        createEdge(body.getEnd(), cond.getStart());

        blockStack.popUnlabeled();

        whileBegin.setName("while");
        whileEnd.setName("elihw");
    }

    /* BLOCKS */

    @Override
    public boolean visit(Block node) {
        EclipseCFGNode block = nodeMap.get(node);
        blockStack.overrideIfExists(node, block, null);
        return true;
    }

    @Override
    public void endVisit(Block node) {
        EclipseCFGNode bNode = nodeMap.get(node);
        makeListEdges(null, node.statements(), bNode);

        bNode.setName("{}");
    }

    @Override
    public void endVisit(ExpressionStatement node) {
        EclipseCFGNode exp = nodeMap.get(node.getExpression());
        EclipseCFGNode stmnt = nodeMap.get(node);

        createEdge(exp.getEnd(), stmnt);
        stmnt.setStart(exp.getStart());
    }

    @Override
    public void endVisit(ParenthesizedExpression node) {
        EclipseCFGNode exp = nodeMap.get(node.getExpression());
        nodeMap.put(node, exp);
    }

    @Override
    public void endVisit(SwitchCase node) {
        if (node.getExpression() != null) {
            EclipseCFGNode exp = nodeMap.get(node.getExpression());
            nodeMap.put(node, exp);
        } else {
            EclipseCFGNode defCase = nodeMap.get(node);
            defCase.setName("default");
        }
    }

    @Override
    public void endVisit(SynchronizedStatement node) {
        // TODO duplicate for all possible ways to get out of the sync!
        EclipseCFGNode sync = nodeMap.get(node);
        EclipseCFGNode exp = nodeMap.get(node.getExpression());
        EclipseCFGNode body = nodeMap.get(node.getBody());

        createEdge(exp.getEnd(), body.getStart());
        createEdge(body.getEnd(), sync);
        sync.setStart(exp.getStart());
    }

    /* VARIABLE DECLARATIONS */

    @Override
    public void endVisit(VariableDeclarationExpression node) {
        EclipseCFGNode decls = nodeMap.get(node);
        EclipseCFGNode typeNode = nodeMap.get(node.getType());

        EclipseCFGNode current, last = null;

        createEdge(decls, typeNode.getStart());
        last = typeNode;

        for (ASTNode frag : (List<ASTNode>) node.fragments()) {
            current = nodeMap.get(frag);
            createEdge(last.getEnd(), current.getStart());
            last = current;
        }

        decls.setStart(decls);
        decls.setEnd(last.getEnd());
        decls.setName(node.getType().toString());
    }

    @Override
    public void endVisit(VariableDeclarationFragment node) {
        EclipseCFGNode decl = handleVariableDecl(node, null);
        decl.setName(node.getName().getIdentifier());
    }

    @Override
    public void endVisit(VariableDeclarationStatement node) {
        EclipseCFGNode decls = nodeMap.get(node);
        EclipseCFGNode typeNode = nodeMap.get(node.getType());

        EclipseCFGNode current, last = null;

        createEdge(decls, typeNode.getStart());
        last = typeNode;

        for (ASTNode frag : (List<ASTNode>) node.fragments()) {
            current = nodeMap.get(frag);
            createEdge(last.getEnd(), current.getStart());
            last = current;
        }

        decls.setStart(decls);
        decls.setEnd(last.getEnd());
        decls.setName(node.getType().toString());
    }

    @Override
    public void endVisit(SingleVariableDeclaration node) {
        EclipseCFGNode type = nodeMap.get(node.getType());
        EclipseCFGNode decl = handleVariableDecl(node, type);

        decl.setName(node.getType().toString() + " " + node.getName().getIdentifier());
    }

    private EclipseCFGNode handleVariableDecl(VariableDeclaration node, EclipseCFGNode startPoint) {
        EclipseCFGNode decl = nodeMap.get(node);
        EclipseCFGNode name = nodeMap.get(node.getName());
        EclipseCFGNode current = null;

        if (startPoint != null) {
            current = startPoint.getEnd();
        }

        if (node.getInitializer() != null) {
            EclipseCFGNode init = nodeMap.get(node.getInitializer());
            if (current != null)
                createEdge(current, init.getStart());
            else
                startPoint = init;
            current = init.getEnd();
        }

        if (current != null)
            createEdge(current, name.getStart());
        else
            startPoint = name;

        createEdge(name.getEnd(), decl);

        decl.setStart(startPoint.getStart());

        return decl;
    }

    /* EXPRESSIONS */

    @Override
    public void endVisit(ArrayAccess node) {
        EclipseCFGNode arrayAccess = nodeMap.get(node);
        EclipseCFGNode array = nodeMap.get(node.getArray());
        EclipseCFGNode index = nodeMap.get(node.getIndex());

        createEdge(array.getEnd(), index.getStart());
        createEdge(index.getEnd(), arrayAccess);
        arrayAccess.setStart(array.getStart());
    }

    @Override
    public void endVisit(ArrayCreation node) {
        EclipseCFGNode arrayCreation = nodeMap.get(node);
        if (node.getInitializer() == null)
            makeListEdges(null, node.dimensions(), arrayCreation);
        else {
            EclipseCFGNode arrayInit = nodeMap.get(node.getInitializer());
            createEdge(arrayInit.getEnd(), arrayCreation);
            arrayCreation.setStart(arrayInit.getStart());
        }
    }

    @Override
    public void endVisit(ArrayInitializer node) {
        EclipseCFGNode arrayInit = nodeMap.get(node);
        makeListEdges(null, node.expressions(), arrayInit);
    }

    @Override
    public void endVisit(Assignment node) {
        EclipseCFGNode rhs = nodeMap.get(node.getRightHandSide());
        EclipseCFGNode lhs = nodeMap.get(node.getLeftHandSide());
        EclipseCFGNode assign = nodeMap.get(node);

        createEdge(rhs.getEnd(), lhs.getStart());
        createEdge(lhs.getEnd(), assign);
        assign.setStart(rhs.getStart());
        assign.setEnd(assign);

        assign.setName(node.getOperator().toString());
    }

    @Override
    public void endVisit(CastExpression node) {
        EclipseCFGNode cast = nodeMap.get(node);
        EclipseCFGNode expression = nodeMap.get(node.getExpression());

        createEdge(expression.getEnd(), cast);
        cast.setStart(expression.getStart());
    }

    @Override
    public void endVisit(ConstructorInvocation node) {
        EclipseCFGNode constructor = nodeMap.get(node);
        makeListEdges(null, (List<ASTNode>) node.arguments(), constructor);
    }

    @Override
    public void endVisit(FieldAccess node) {
        EclipseCFGNode field = nodeMap.get(node);
        EclipseCFGNode expression = nodeMap.get(node.getExpression());
        EclipseCFGNode name = nodeMap.get(node.getName());

        createEdge(expression.getEnd(), name.getStart());
        createEdge(name.getEnd(), field);
        field.setStart(expression.getStart());
        field.setName(".");
    }

    @Override
    public void endVisit(InfixExpression node) {
        EclipseCFGNode infix = nodeMap.get(node);
        EclipseCFGNode lhs = nodeMap.get(node.getLeftOperand());
        EclipseCFGNode rhs = nodeMap.get(node.getRightOperand());
        Operator op = node.getOperator();

        // short circuiting
        if (op.equals(Operator.CONDITIONAL_AND) || op.equals(Operator.CONDITIONAL_OR)) {
            boolean isAnd = node.getOperator().equals(InfixExpression.Operator.CONDITIONAL_AND);
            List<Expression> operands = new ArrayList<Expression>();
            EclipseCFGNode last = null;

            operands.add(node.getLeftOperand());
            operands.add(node.getRightOperand());
            operands.addAll(node.extendedOperands());

            int ndx = 0;

            for (Expression opNode : operands) {
                EclipseCFGNode operand = nodeMap.get(opNode);

                if (last != null)
                    createBooleanEdge(last.getEnd(), operand.getStart(), isAnd);
                createBooleanEdge(operand.getEnd(), infix, !isAnd);
                if (ndx == operands.size() - 1)
                    createBooleanEdge(operand.getEnd(), infix, isAnd);
                last = operand;
                ndx++;
            }
        } else {
            createEdge(lhs.getEnd(), rhs.getStart());

            makeListEdges(rhs, (List<ASTNode>) node.extendedOperands(), infix);
        }

        infix.setName(node.getOperator().toString());
        infix.setStart(lhs.getStart());
    }

    @Override
    public void endVisit(InstanceofExpression node) {
        EclipseCFGNode instanceTest = nodeMap.get(node);
        EclipseCFGNode lhs = nodeMap.get(node.getLeftOperand());
        EclipseCFGNode rhs = nodeMap.get(node.getRightOperand());

        createEdge(lhs.getEnd(), rhs.getStart());
        createEdge(rhs.getEnd(), instanceTest);
        instanceTest.setStart(lhs.getStart());

        instanceTest.setName("instanceof");
    }

    @Override
    public void endVisit(ClassInstanceCreation node) {
        EclipseCFGNode constructor = nodeMap.get(node);

        // Normal edges
        makeListEdges(null, (List<ASTNode>) node.arguments(), constructor);

        // handle exception edges
        if (node.resolveConstructorBinding() != null) {
            for (ITypeBinding exception : node.resolveConstructorBinding().getExceptionTypes()) {
                EclipseCFGNode catchNode = exceptionMap.getCatchNode(exception);

                if (catchNode != null)
                    createEdge(constructor, catchNode.getStart(), exception);
                // else
                // createEdge(invocation, normalExit, exception);
            }
        }

        if (node.getType().resolveBinding() != null) {
            constructor.setName("new " + node.getType().resolveBinding().getName());
        } else {
            constructor.setName("new " + node.getType().toString());
        }
    }

    @Override
    public void endVisit(MethodInvocation node) {
        EclipseCFGNode invocation = nodeMap.get(node);

        // normal edges
        makeListEdges(nodeMap.get(node.getExpression()), (List<ASTNode>) node.arguments(), invocation);

        // handle exception edges
        if (node.resolveMethodBinding() != null) {
            for (ITypeBinding exception : node.resolveMethodBinding().getExceptionTypes()) {
                EclipseCFGNode catchNode = exceptionMap.getCatchNode(exception);

                if (catchNode != null)
                    createEdge(invocation, catchNode.getStart(), exception);
                // else
                // createEdge(invocation, normalExit, exception);
            }
        } else {
            // ignore -- added by Na Meng
        }
        invocation.setName("Call " + node.getName().getIdentifier());
    }

    @Override
    public void endVisit(PostfixExpression node) {
        EclipseCFGNode exp = nodeMap.get(node.getOperand());
        EclipseCFGNode postfix = nodeMap.get(node);

        createEdge(exp.getEnd(), postfix);
        postfix.setStart(exp.getStart());

        postfix.setName(node.getOperator().toString());
    }

    @Override
    public void endVisit(PrefixExpression node) {
        EclipseCFGNode exp = nodeMap.get(node.getOperand());
        EclipseCFGNode prefix = nodeMap.get(node);

        createEdge(exp.getEnd(), prefix);
        prefix.setStart(exp.getStart());

        prefix.setName(node.getOperator().toString());
    }

    @Override
    public void endVisit(QualifiedName node) {
        EclipseCFGNode fullName = nodeMap.get(node);
        EclipseCFGNode qual = nodeMap.get(node.getQualifier());
        EclipseCFGNode name = nodeMap.get(node.getName());

        createEdge(qual.getEnd(), name.getStart());
        createEdge(name.getEnd(), fullName);
        fullName.setStart(qual.getStart());
        fullName.setName(".");
    }

    @Override
    public void endVisit(SuperConstructorInvocation node) {
        EclipseCFGNode constructor = nodeMap.get(node);
        EclipseCFGNode exp = nodeMap.get(node.getExpression());

        makeListEdges(exp, (List<ASTNode>) node.arguments(), constructor);
    }

    @Override
    public void endVisit(SuperFieldAccess node) {
        EclipseCFGNode field = nodeMap.get(node);
        EclipseCFGNode name = nodeMap.get(node.getName());

        createEdge(name.getEnd(), field);

        if (node.getQualifier() != null) {
            EclipseCFGNode qual = nodeMap.get(node.getQualifier());
            createEdge(qual.getEnd(), name.getStart());
            field.setStart(qual.getStart());
        } else
            field.setStart(name.getStart());
    }

    @Override
    public void endVisit(SuperMethodInvocation node) {
        EclipseCFGNode invocation = nodeMap.get(node);
        EclipseCFGNode qual = nodeMap.get(node.getQualifier());

        makeListEdges(qual, (List<ASTNode>) node.arguments(), invocation);
        invocation.setName("Call " + node.getName().getIdentifier());
    }

    /*
     * These methods are only here for debugging purposes and can be removed
     * later.
     */

    @Override
    public void endVisit(StringLiteral node) {
        EclipseCFGNode name = nodeMap.get(node);
        name.setName(node.getLiteralValue());
    }

    @Override
    public void endVisit(ThisExpression node) {
        EclipseCFGNode name = nodeMap.get(node);
        name.setName("this");
    }

    @Override
    public void endVisit(SimpleName node) {
        EclipseCFGNode name = nodeMap.get(node);
        name.setName(node.getIdentifier());
    }

    @Override
    public void endVisit(NullLiteral node) {
        EclipseCFGNode nullNode = nodeMap.get(node);
        nullNode.setName("null");
    }

    @Override
    public void endVisit(NumberLiteral node) {
        EclipseCFGNode num = nodeMap.get(node);
        num.setName(node.getToken());
    }

    @Override
    public void endVisit(TypeLiteral node) {
        EclipseCFGNode num = nodeMap.get(node);
        num.setName(node.getType().toString());
    }

    @Override
    public void endVisit(CharacterLiteral node) {
        EclipseCFGNode num = nodeMap.get(node);
        num.setName(node.getEscapedValue());
    }

    @Override
    public void endVisit(BooleanLiteral node) {
        EclipseCFGNode num = nodeMap.get(node);
        num.setName(node.toString());
    }

    /** TYPES * */

    @Override
    public void endVisit(ArrayType node) {
        EclipseCFGNode type = nodeMap.get(node);
        EclipseCFGNode compType = nodeMap.get(node.getComponentType());

        createEdge(compType.getEnd(), type);
        type.setStart(compType.getStart());
        type.setName(node.toString());
    }

    @Override
    public void endVisit(ParameterizedType node) {
        EclipseCFGNode type = nodeMap.get(node);
        EclipseCFGNode root = nodeMap.get(node.getType());

        makeListEdges(root, node.typeArguments(), type);
        type.setName(node.toString());
    }

    @Override
    public void endVisit(PrimitiveType node) {
        EclipseCFGNode type = nodeMap.get(node);

        type.setName(node.toString());
    }

    @Override
    public void endVisit(QualifiedType node) {
        EclipseCFGNode type = nodeMap.get(node);
        EclipseCFGNode name = nodeMap.get(node.getName());
        EclipseCFGNode qual = nodeMap.get(node.getQualifier());

        createEdge(name.getEnd(), qual.getStart());
        createEdge(qual.getEnd(), type);
        type.setStart(name.getStart());
        type.setName(node.toString());
    }

    @Override
    public void endVisit(SimpleType node) {
        EclipseCFGNode type = nodeMap.get(node);
        EclipseCFGNode name = nodeMap.get(node.getName());

        createEdge(name.getEnd(), type);
        type.setStart(name.getStart());
        type.setName(node.toString());
    }

    @Override
    public void endVisit(WildcardType node) {
        EclipseCFGNode type = nodeMap.get(node);

        if (node.getBound() != null) {
            EclipseCFGNode bound = nodeMap.get(node.getBound());
            createEdge(bound.getEnd(), type);
            type.setStart(bound.getStart());
        }
        type.setName(node.toString());
    }

    @Override
    public boolean visit(TypeDeclarationStatement node) {
        return false;
    }

    @Override
    public boolean visit(AnonymousClassDeclaration node) {
        return false;
    }

}