edu.cmu.cs.crystal.internal.ControlFlowVisitor.java Source code

Java tutorial

Introduction

Here is the source code for edu.cmu.cs.crystal.internal.ControlFlowVisitor.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.internal;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration;
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.BlockComment;
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.CompilationUnit;
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.EmptyStatement;
import org.eclipse.jdt.core.dom.EnhancedForStatement;
import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
import org.eclipse.jdt.core.dom.EnumDeclaration;
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.IBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.IfStatement;
import org.eclipse.jdt.core.dom.ImportDeclaration;
import org.eclipse.jdt.core.dom.InfixExpression;
import org.eclipse.jdt.core.dom.Initializer;
import org.eclipse.jdt.core.dom.InstanceofExpression;
import org.eclipse.jdt.core.dom.Javadoc;
import org.eclipse.jdt.core.dom.LabeledStatement;
import org.eclipse.jdt.core.dom.LineComment;
import org.eclipse.jdt.core.dom.MarkerAnnotation;
import org.eclipse.jdt.core.dom.MemberRef;
import org.eclipse.jdt.core.dom.MemberValuePair;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.MethodRef;
import org.eclipse.jdt.core.dom.MethodRefParameter;
import org.eclipse.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.NormalAnnotation;
import org.eclipse.jdt.core.dom.NullLiteral;
import org.eclipse.jdt.core.dom.NumberLiteral;
import org.eclipse.jdt.core.dom.PackageDeclaration;
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.SingleMemberAnnotation;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.Statement;
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.TagElement;
import org.eclipse.jdt.core.dom.TextElement;
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.TypeParameter;
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 edu.cmu.cs.crystal.internal.ControlFlowNode.Direction;

/**
 * An ASTVisitor that traverses the ASTNode structure and connects ControlFlowNodes
 * to form a control flow graph of the ASTNode.
 *   
 * @author David Dickey
 *
 */
public class ControlFlowVisitor extends ASTVisitor {
    protected ControlFlowNode controlFlowNode;
    protected ControlFlowGraph controlFlowGraph;

    public ControlFlowVisitor(ControlFlowNode cfn) {
        if (cfn == null)
            throw new CrystalRuntimeException("Cannot create a ControlFlowVisitor for a null ControlFlowNode");
        controlFlowNode = cfn;
        controlFlowGraph = cfn.getControlFlowGraph();
        if (controlFlowGraph == null)
            throw new CrystalRuntimeException("ControlFlowNode was not part of a ControlFlowGraph");
    }

    /**
     * Carries out the visit to the stored node.
     */
    public void performVisit() {
        if (controlFlowNode.getASTNode() != null)
            controlFlowNode.getASTNode().accept(this);
        else
            throw new CrystalRuntimeException("Cannot visit a null ASTNode");
    }

    /**
     * Called before performing a visit
     */
    public void preVisit(ASTNode node) {
        // TEMP: Print out the visit
        // System.out.println(Utilities.ASTNodeToString(node));
    }

    /*
     * The visit methods follow the following steps
     * 1) Create Children Visitors
     * 2) Reroute Control Flow to first Child from Parent
     * 3) Add Edges between Children
     * 4) Evaluate Children 
     */

    /**
     * AnnotationTypeDeclaration 
     * 
     * This node does not influence control flow.
     */
    public boolean visit(AnnotationTypeDeclaration node) {
        return false;
    }

    /**
     * AnnotationTypeMemberDeclaration 
     * 
     * This node does not influence control flow.
     */
    public boolean visit(AnnotationTypeMemberDeclaration node) {
        return false;
    }

    /**
     * AnonymousClassDeclaration 
     * 
     * This node does not influence control flow.
     */
    public boolean visit(AnonymousClassDeclaration node) {
        return false;
    }

    /**
     * Example: myArray[0]
     */
    public boolean visit(ArrayAccess node) {
        if (node.getArray() == null)
            throw new CrystalRuntimeException("null array");
        if (node.getIndex() == null)
            throw new CrystalRuntimeException("null array index");
        ControlFlowNode array = controlFlowNode.newControlFlowNode(node.getArray());
        ControlFlowNode index = controlFlowNode.newControlFlowNode(node.getIndex());

        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, array);

        array.addEdge(ControlFlowNode.Direction.FORWARDS, index);
        index.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);

        array.evaluate();
        index.evaluate();

        return false;
    }

    /**
     * Example: new int[10]{1,2,3,4,5,6,7,8,9,10};
     */
    public boolean visit(ArrayCreation node) {
        List dimensions = node.dimensions();
        ArrayInitializer arrayInitializer = node.getInitializer();
        List<ControlFlowNode> cfns = null;

        if (dimensions != null && dimensions.size() > 0) {
            // Handle dimension expressions
            cfns = createCFNListFromASTNodeList(dimensions);
            ControlFlowNode cfn = cfns.get(0);
            controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, cfn);
            cfn = cfns.get(cfns.size() - 1);
            cfn.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
            evaluate(cfns);
        } else if (arrayInitializer != null) {
            // Handle initializer if there is one
            ControlFlowNode initializer = controlFlowNode.newControlFlowNode(arrayInitializer);
            controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, initializer);
            initializer.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
            initializer.evaluate();
        }
        return false;
    }

    /**
     * Example: {1.2f, 2.3f, 3.4f}
     */
    public boolean visit(ArrayInitializer node) {
        List expressions = node.expressions();
        if (expressions == null || expressions.size() == 0)
            return false;
        // Take the expression list and make more visitors from them.
        List<ControlFlowNode> cfns = createCFNListFromASTNodeList(expressions);
        ControlFlowNode cfn = cfns.get(0);
        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, cfn);
        cfn = cfns.get(cfns.size() - 1);
        cfn.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);

        evaluate(cfns);

        return false;
    }

    /**
     * ArrayType 
     * 
     * This node does not influence control flow.
     */
    public boolean visit(ArrayType node) {
        return false;
    }

    /**
     * Example: assert (x != 0) : "x is " + x + " which is not 0";
     */
    public boolean visit(AssertStatement node) {
        ControlFlowNode expression = controlFlowNode.newControlFlowNode(node.getExpression());
        Expression message = node.getMessage();

        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, expression);

        if (message != null) {
            ControlFlowNode cfnMessage = controlFlowNode.newControlFlowNode(message);
            expression.addEdge(ControlFlowNode.Direction.FORWARDS, cfnMessage);
            cfnMessage.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);

            expression.evaluate();
            cfnMessage.evaluate();
        } else {
            expression.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
            expression.evaluate();
        }

        return false;
    }

    /**
     * Example: x = y
     */
    public boolean visit(Assignment node) {
        ControlFlowNode leftCFN = controlFlowNode.newControlFlowNode(node.getLeftHandSide());
        ControlFlowNode rightCFN = controlFlowNode.newControlFlowNode(node.getRightHandSide());

        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, leftCFN);

        leftCFN.addEdge(ControlFlowNode.Direction.FORWARDS, rightCFN);
        rightCFN.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);

        leftCFN.evaluate();
        rightCFN.evaluate();

        return false;
    }

    /**
     * Example: { int x = 1; x++; }   or  {}
     */
    public boolean visit(Block node) {
        List statements = node.statements();

        if (statements == null || statements.size() == 0) {
            // controlFlowNode.remove();
            return false;
        }

        List<ControlFlowNode> cfns = createCFNListFromASTNodeList(statements);
        ControlFlowNode cfn = cfns.get(0);
        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, cfn);
        cfn = cfns.get(cfns.size() - 1);
        // Remove the block node from the graph
        cfn.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
        //controlFlowNode.moveEdges(ControlFlowNode.Direction.FORWARDS, cfn);
        //controlFlowNode.remove();

        evaluate(cfns);

        return false;
    }

    /**
     * BlockComment 
     * 
     * This node does not influence control flow.
     */
    public boolean visit(BlockComment node) {
        return false;
    }

    /**
     * BooleanLiteral 
     * 
     * This node does not influence control flow.
     */
    public boolean visit(BooleanLiteral node) {
        // Leaf, no action necessary
        return false;
    }

    /**
     * Example: break;   or   break ToHere;
     */
    public boolean visit(BreakStatement node) {
        SimpleName simpleName = node.getLabel();
        String label;
        if (simpleName == null)
            label = null;
        else
            label = simpleName.getIdentifier();

        controlFlowNode.breaking(label, true);

        return false;
    }

    /**
     * Example: (Square) myShape
     */
    public boolean visit(CastExpression node) {
        ControlFlowNode expression = controlFlowNode.newControlFlowNode(node.getExpression());

        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, expression);

        expression.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);

        expression.evaluate();
        return false;
    }

    /**
     * Example: catch (RuntimeException re) { System.out.println("RE: " + re); }
     */
    public boolean visit(CatchClause node) {
        ControlFlowNode exception = controlFlowNode.newControlFlowNode(node.getException());
        ControlFlowNode body = controlFlowNode.newControlFlowNode(node.getBody());

        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, exception);
        exception.addEdge(ControlFlowNode.Direction.FORWARDS, body);
        body.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);

        exception.evaluate();
        body.evaluate();
        return false;
    }

    /**
     * CharacterLiteral 
     * 
     * This node does not influence control flow.
     */
    public boolean visit(CharacterLiteral node) {
        // Leaf, no action necessary
        return false;
    }

    /**
     * Example: new MyClass(5, "hello");
     */
    public boolean visit(ClassInstanceCreation node) {
        Expression expression = node.getExpression();
        List arguments = node.arguments();
        ControlFlowNode expressioncfn = null, last = null;
        List<ControlFlowNode> cfns = null;

        if (expression != null) {
            expressioncfn = controlFlowNode.newControlFlowNode(expression);
            controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, expressioncfn);
            last = expressioncfn;
        }

        if (arguments != null && arguments.size() > 0) {
            // Take the argument list and make more CFNs from them.
            cfns = createCFNListFromASTNodeList(arguments);
            if (expression == null)
                controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, cfns.get(0));
            else
                expressioncfn.addEdge(ControlFlowNode.Direction.FORWARDS, cfns.get(0));
            last = cfns.get(cfns.size() - 1);
        }

        if (last == null)
            return false;
        last.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);

        if (expressioncfn != null)
            expressioncfn.evaluate();
        if (cfns != null)
            evaluate(cfns);

        return false;
    }

    /**
     * CompilationUnit 
     * 
     * This node does not influence control flow.
     */
    public boolean visit(CompilationUnit node) {
        return false;
    }

    /**
     * Example: (isTrue) ? "yep" : "nope";
     */
    public boolean visit(ConditionalExpression node) {
        ControlFlowNode conditionCFN = controlFlowNode.newControlFlowNode(node.getExpression());
        ControlFlowNode thenCFN = controlFlowNode.newControlFlowNode(node.getThenExpression());
        ControlFlowNode elseCFN = controlFlowNode.newControlFlowNode(node.getElseExpression());

        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, conditionCFN);

        conditionCFN.addEdge(ControlFlowNode.Direction.FORWARDS, thenCFN);
        conditionCFN.addEdge(ControlFlowNode.Direction.FORWARDS, elseCFN);
        thenCFN.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
        elseCFN.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);

        conditionCFN.evaluate();
        thenCFN.evaluate();
        elseCFN.evaluate();

        return false;
    }

    /**
     * Example: this(1, 23, 2005);
     */
    public boolean visit(ConstructorInvocation node) {
        List arguments = node.arguments();

        if (arguments == null || arguments.size() == 0) {
            return false;
        }

        // Handle dimension expressions
        List<ControlFlowNode> cfns = createCFNListFromASTNodeList(arguments);
        ControlFlowNode cfn = cfns.get(0);
        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, cfn);
        cfn = cfns.get(cfns.size() - 1);
        cfn.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);

        evaluate(cfns);

        return false;
    }

    /**
     * Example: continue;   or   continue ToHere;
     */
    public boolean visit(ContinueStatement node) {
        SimpleName simpleName = node.getLabel();
        String label;
        if (simpleName == null)
            label = null;
        else
            label = simpleName.getIdentifier();

        controlFlowNode.continuing(label, true);

        return false;
    }

    /**
     * Example: do { int x = 5; method(x++); } while(x < 10); 
     */
    public boolean visit(DoStatement node) {
        //       do Statement while ( Expression ) ;
        //          --- 1 ---         --- 2 ----
        //       ------------- 3 -> 1 --------------
        //       ------------- 3 -> exit -----------

        ControlFlowNode exit = controlFlowNode.getNode(ControlFlowNode.Direction.FORWARDS);
        ControlFlowNode expressionCFN = controlFlowNode.newControlFlowNode(node.getExpression());
        ControlFlowNode bodyCFN = controlFlowNode.newControlFlowNode(node.getBody());
        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, bodyCFN);

        bodyCFN.addEdge(ControlFlowNode.Direction.FORWARDS, expressionCFN);
        expressionCFN.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
        controlFlowNode.addEdge(ControlFlowNode.Direction.FORWARDS, bodyCFN);
        controlFlowNode.setLoopPaths(expressionCFN, exit);

        bodyCFN.evaluate();
        expressionCFN.evaluate();

        return false;
    }

    /**
     * Example:  ;
     */
    public boolean visit(EmptyStatement node) {
        return false;
    }

    public boolean visit(EnhancedForStatement node) {
        //       for ( FormalParameter : Expression ) Statement
        //                              ---- 1 ---   --- 3 ---
        //      ------------------- 2 -> 3 -------------------
        //      ------------------- 2 -> exit ----------------

        // TODO: Add formal parameter, so a continue; makes more sense
        Expression expression = node.getExpression();
        Statement body = node.getBody();
        ControlFlowNode expressionCFN = null;
        ControlFlowNode bodyCFN = null;
        ControlFlowNode exit = controlFlowNode.getNode(ControlFlowNode.Direction.FORWARDS);

        if (expression == null)
            throw new CrystalRuntimeException("enchancedForStatement did not have an expression");
        if (body == null)
            throw new CrystalRuntimeException("No body defined for an EnhancedForStatment");

        expressionCFN = controlFlowNode.newControlFlowNode(expression);
        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, expressionCFN);
        expressionCFN.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);

        bodyCFN = controlFlowNode.newControlFlowNode(body);
        controlFlowNode.addEdge(ControlFlowNode.Direction.FORWARDS, bodyCFN);
        bodyCFN.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
        controlFlowNode.setLoopPaths(bodyCFN, exit);

        expressionCFN.evaluate();
        bodyCFN.evaluate();

        return false;
    }

    /**
     * Example: enum MyEnum { CONST_1, CONST_2, CONST_2 }
     */
    public boolean visit(EnumConstantDeclaration node) {
        List arguments = node.arguments();

        if (arguments == null || arguments.size() == 0) {
            return false;
        }

        // Handle dimension expressions
        List<ControlFlowNode> cfns = createCFNListFromASTNodeList(arguments);
        ControlFlowNode cfn = cfns.get(0);
        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, cfn);
        cfn = cfns.get(cfns.size() - 1);
        cfn.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);

        evaluate(cfns);

        return false;
    }

    /**
     * Example: enum MyEnum { CONST_1, CONST_2 }
     */
    public boolean visit(EnumDeclaration node) {
        return false;
    }

    /**
     * Example: x = 5;
     */
    public boolean visit(ExpressionStatement node) {
        // Candidate for Removal
        ControlFlowNode expressionCFN = controlFlowNode.newControlFlowNode(node.getExpression());
        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, expressionCFN);
        expressionCFN.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
        expressionCFN.evaluate();
        return false;
    }

    /**
     * Example: x = this.myField;
     */
    public boolean visit(FieldAccess node) {
        Expression expression = node.getExpression();

        if (expression == null)
            return false;

        ControlFlowNode expressioncfn = controlFlowNode.newControlFlowNode(expression);
        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, expressioncfn);

        expressioncfn.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);

        expressioncfn.evaluate();
        return false;
    }

    /**
     * Example: protected int myField = 18;
     */
    public boolean visit(FieldDeclaration node) {
        List fragments = node.fragments();
        if (fragments == null || fragments.size() == 0)
            return false;
        List<ControlFlowNode> cfns = createCFNListFromASTNodeList(fragments);
        ControlFlowNode cfn = cfns.get(0);
        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, cfn);
        cfn = cfns.get(cfns.size() - 1);
        cfn.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
        evaluate(cfns);

        return false;
    }

    public boolean visit(ForStatement node) {
        //       for ( [ ForInit ]; [ Expression ] ; [ ForUpdate ] ) Statement
        //             ---- 1 ----   ----- 2 ---    --- 5 -> 2 --   --- 4 ---
        //      -------------------------- 3 -> 4 --------------------------
        //      -------------------------- 3 -> exit -----------------------

        List initializers = node.initializers();
        Expression expression = node.getExpression();
        List updaters = node.updaters();
        Statement body = node.getBody();
        List<ControlFlowNode> initializercfns = null, updatercfns = null;
        ControlFlowNode expressioncfn = null, bodycfn = null, tempcfn;
        ControlFlowNode exit = controlFlowNode.getNode(ControlFlowNode.Direction.FORWARDS);
        ControlFlowNode first = controlFlowNode;

        // Initialize all CFNs AND setup CFG edges
        if (initializers != null && initializers.size() > 0) {
            initializercfns = createCFNListFromASTNodeList(initializers);
            first = initializercfns.get(0);
            controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, first);
        }
        if (expression != null) {
            expressioncfn = controlFlowNode.newControlFlowNode(expression);
            if (initializercfns == null) {
                controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, expressioncfn);
                first = expressioncfn;
            } else {
                tempcfn = initializercfns.get(initializercfns.size() - 1);
                tempcfn.addEdge(ControlFlowNode.Direction.FORWARDS, expressioncfn);
            }
            expressioncfn.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
        } else if (initializercfns != null) {
            tempcfn = initializercfns.get(initializercfns.size() - 1);
            tempcfn.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
        }
        if (updaters != null && updaters.size() > 0) {
            updatercfns = createCFNListFromASTNodeList(updaters);
            tempcfn = updatercfns.get(updatercfns.size() - 1);
            if (expressioncfn == null)
                tempcfn.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
            else
                tempcfn.addEdge(ControlFlowNode.Direction.FORWARDS, expressioncfn);
        }
        if (body == null)
            throw new CrystalRuntimeException("for statement with no body");

        bodycfn = controlFlowNode.newControlFlowNode(body);
        controlFlowNode.addEdge(ControlFlowNode.Direction.FORWARDS, bodycfn);
        if (updatercfns != null)
            bodycfn.addEdge(ControlFlowNode.Direction.FORWARDS, updatercfns.get(0));
        else if (expressioncfn != null)
            bodycfn.addEdge(ControlFlowNode.Direction.FORWARDS, expressioncfn);
        else
            bodycfn.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);

        if (updatercfns != null)
            controlFlowNode.setLoopPaths(updatercfns.get(0), exit);
        else if (expressioncfn != null)
            controlFlowNode.setLoopPaths(expressioncfn, exit);
        else
            controlFlowNode.setLoopPaths(bodycfn, exit);

        // Do visits

        if (initializercfns != null)
            evaluate(initializercfns);
        if (expressioncfn != null)
            expressioncfn.evaluate();
        if (updatercfns != null)
            evaluate(updatercfns);
        if (bodycfn != null)
            bodycfn.evaluate();

        return false;
    }

    /**
     * Example: if(bool) x = 5; else x = 7;
     */
    public boolean visit(IfStatement node) {
        Expression expression = node.getExpression();
        Statement thenStatement = node.getThenStatement();
        Statement elseStatement = node.getElseStatement();

        ControlFlowNode conditionCFN = controlFlowNode.newControlFlowNode(expression);
        // conditionCFN.copyLabelsFrom(controlFlowNode);
        ControlFlowNode thenCFN = controlFlowNode.newControlFlowNode(thenStatement);
        ControlFlowNode elseCFN = null;
        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, conditionCFN);
        // THEN
        conditionCFN.addEdge(ControlFlowNode.Direction.FORWARDS, thenCFN);
        thenCFN.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);

        // ELSE
        if (elseStatement != null) {
            elseCFN = controlFlowNode.newControlFlowNode(elseStatement);
            conditionCFN.addEdge(ControlFlowNode.Direction.FORWARDS, elseCFN);
            elseCFN.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
        } else {
            conditionCFN.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
        }

        // Perform visits
        conditionCFN.evaluate();

        thenCFN.evaluate();
        if (elseCFN != null)
            elseCFN.evaluate();
        return false;
    }

    /**
     * ImportDeclaration
     * 
     * This node does not influence control flow.
     */
    public boolean visit(ImportDeclaration node) {
        return false;
    }

    /**
     * Example: "Hello " + "World " + "How " + "Are " + "You?"
     *          left + right + extOp1 + extOp2 + extOp3
     */
    public boolean visit(InfixExpression node) {
        List<ControlFlowNode> cfns = null;
        ControlFlowNode leftCFN = controlFlowNode.newControlFlowNode(node.getLeftOperand());
        ControlFlowNode rightCFN = controlFlowNode.newControlFlowNode(node.getRightOperand());
        // Connect the back edges to the first operand (ie. left)
        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, leftCFN);
        leftCFN.addEdge(ControlFlowNode.Direction.FORWARDS, rightCFN);

        // Add all extended operands
        if (node.hasExtendedOperands()) {
            cfns = createCFNListFromASTNodeList(node.extendedOperands());
            ControlFlowNode cfn = cfns.get(0);
            rightCFN.addEdge(ControlFlowNode.Direction.FORWARDS, cfn);
            cfn = cfns.get(cfns.size() - 1);
            cfn.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
        } else
            rightCFN.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);

        leftCFN.evaluate();
        rightCFN.evaluate();
        if (cfns != null)
            evaluate(cfns);

        return false;
    }

    public boolean visit(Initializer node) {
        return false;
    }

    /**
     * Example: myVar instanceof MyClass
     */
    public boolean visit(InstanceofExpression node) {
        Expression expression = node.getLeftOperand();
        if (expression == null)
            return false;

        ControlFlowNode expressioncfn = controlFlowNode.newControlFlowNode(expression);
        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, expressioncfn);
        expressioncfn.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
        expressioncfn.evaluate();
        return false;
    }

    /**
     * Javadoc
     * 
     * This node does not influence control flow.
     */
    public boolean visit(Javadoc node) {
        return false;
    }

    /**
     * Example: MyLabel: { int x = 5; }
     */
    public boolean visit(LabeledStatement node) {
        SimpleName label = node.getLabel();
        if (label == null)
            throw new CrystalRuntimeException("labeled statement had no label");
        ControlFlowNode body = controlFlowNode.newControlFlowNode(node.getBody());
        if (body == null)
            throw new CrystalRuntimeException("labeled statement had no body");

        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, body);
        body.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
        // body.copyLabelsFrom(controlFlowNode);
        // body.addLabel(label.getIdentifier());
        body.evaluate();

        return false;
    }

    /**
     * This node does not influence control flow.
     */
    public boolean visit(LineComment node) {
        return false;
    }

    /**
     * This node does not influence control flow.
     */
    public boolean visit(MarkerAnnotation node) {
        return false;
    }

    /**
     * This node does not influence control flow.
     */
    public boolean visit(MemberRef node) {
        return false;
    }

    /**
     * This node does not influence control flow.
     */
    public boolean visit(MemberValuePair node) {
        return false;
    }

    /**
     * Example: public void myMethod(int x) { return x * x; }
     */
    public boolean visit(MethodDeclaration node) {
        // If method has no body, then nothing to traverse.
        if (node.getBody() == null)
            return false;

        ControlFlowNode blockCFN = controlFlowNode.newControlFlowNode(node.getBody());
        controlFlowNode.insertNode(ControlFlowNode.Direction.BACKWARDS, blockCFN);
        blockCFN.evaluate();
        return false;
    }

    /**
     * Example: var.getRef.myMethod(1, "two", true);
     */
    public boolean visit(MethodInvocation node) {
        Expression expression = node.getExpression();
        List arguments = node.arguments();
        ControlFlowNode expressioncfn = null, last = null;
        List<ControlFlowNode> cfns = null;

        if (expression != null) {
            expressioncfn = controlFlowNode.newControlFlowNode(expression);
            controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, expressioncfn);
            last = expressioncfn;
        }

        if (arguments != null && arguments.size() > 0) {
            // Take the argument list and make more CFNs from them.
            cfns = createCFNListFromASTNodeList(arguments);
            if (expression == null)
                controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, cfns.get(0));
            else
                expressioncfn.addEdge(ControlFlowNode.Direction.FORWARDS, cfns.get(0));
            last = cfns.get(cfns.size() - 1);
        }

        if (last == null)
            return false;
        last.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);

        if (expressioncfn != null)
            expressioncfn.evaluate();
        if (cfns != null)
            evaluate(cfns);

        return false;
    }

    /**
     * This node does not influence control flow.
     */
    public boolean visit(MethodRef node) {
        return false;
    }

    /**
     * This node does not influence control flow.
     */
    public boolean visit(MethodRefParameter node) {
        return false;
    }

    /**
     * This node does not influence control flow.
     */
    public boolean visit(Modifier node) {
        // Leaf, no action necessary
        return false;
    }

    /**
     * This node does not influence control flow.
     */
    public boolean visit(NormalAnnotation node) {
        return false;
    }

    /**
     * This node does not influence control flow.
     */
    public boolean visit(NullLiteral node) {
        // Leaf, no action necessary
        return false;
    }

    /**
     * This node does not influence control flow.
     */
    public boolean visit(NumberLiteral node) {
        // Leaf, no action necessary
        return false;
    }

    /**
     * This node does not influence control flow.
     */
    public boolean visit(PackageDeclaration node) {
        return false;
    }

    /**
     * This node does not influence control flow.
     */
    public boolean visit(ParameterizedType node) {
        return false;
    }

    /**
     * Example: (5 + 3)
     */
    public boolean visit(ParenthesizedExpression node) {
        // Candidate for Removal
        ControlFlowNode expressionCFN = controlFlowNode.newControlFlowNode(node.getExpression());
        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, expressionCFN);
        expressionCFN.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
        expressionCFN.evaluate();

        return false;
    }

    /**
     * Example: x++;
     */
    public boolean visit(PostfixExpression node) {
        ControlFlowNode operandCFN = controlFlowNode.newControlFlowNode(node.getOperand());
        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, operandCFN);
        operandCFN.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
        operandCFN.evaluate();
        return false;
    }

    /**
     * Example: ++x;
     */
    public boolean visit(PrefixExpression node) {
        ControlFlowNode operandCFN = controlFlowNode.newControlFlowNode(node.getOperand());
        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, operandCFN);
        operandCFN.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
        operandCFN.evaluate();
        return false;
    }

    /**
     * This node does not influence control flow.
     */
    public boolean visit(PrimitiveType node) {
        // Leaf, no action necessary
        return false;
    }

    /**
     * Example: (2 in 1)  java.lang.System.out.println("Hello");
     */
    public boolean visit(QualifiedName node) {
        SimpleName name = node.getName();
        Name qualifier = node.getQualifier();
        IBinding nameBinding = name.resolveBinding();
        // If this is a Field access, then add children to CFG
        if (nameBinding.getKind() == IBinding.VARIABLE) {
            IVariableBinding variableBinding = (IVariableBinding) nameBinding;
            if (variableBinding.isField()) {
                ControlFlowNode nameCFN = controlFlowNode.newControlFlowNode(name);
                ControlFlowNode qualifierCFN = controlFlowNode.newControlFlowNode(qualifier);
                controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, qualifierCFN);
                qualifierCFN.addEdge(ControlFlowNode.Direction.FORWARDS, nameCFN);
                nameCFN.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
                qualifierCFN.evaluate();
                nameCFN.evaluate();
            }
        }
        // If it is NOT a field access, then do not add children to CFG
        return false;
    }

    /**
     * This node does not influence control flow.
     */
    public boolean visit(QualifiedType node) {
        // Leaf in CFG, has children in AST
        return false;
    }

    /**
     * Example: return "goodbye";
     */
    public boolean visit(ReturnStatement node) {

        controlFlowNode.returning();

        Expression expression = node.getExpression();
        if (expression != null) {
            ControlFlowNode expressionCFN = controlFlowNode.newControlFlowNode(expression);
            controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, expressionCFN);
            expressionCFN.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
            expressionCFN.evaluate();
        }

        return false;
    }

    /**
     * This node does not influence control flow.
     */
    public boolean visit(SimpleName node) {
        // Leaf, no action necessary
        return false;
    }

    /**
     * This node does not influence control flow.
     */
    public boolean visit(SimpleType node) {
        return false;
    }

    /**
     * This node does not influence control flow.
     */
    public boolean visit(SingleMemberAnnotation node) {
        return false;
    }

    public boolean visit(SingleVariableDeclaration node) {
        Expression initializer = node.getInitializer();
        if (initializer == null)
            return false;
        ControlFlowNode initializercfn = controlFlowNode.newControlFlowNode(initializer);
        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, initializercfn);
        initializercfn.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
        initializercfn.evaluate();
        return false;
    }

    /**
     * This node does not influence control flow.
     */
    public boolean visit(StringLiteral node) {
        // Leaf, no action necessary
        return false;
    }

    /**
     * Example: super(arg1);
     */
    public boolean visit(SuperConstructorInvocation node) {
        Expression expression = node.getExpression();
        List arguments = node.arguments();
        ControlFlowNode expressioncfn = null, last = null;
        List<ControlFlowNode> cfns = null;

        if (expression != null) {
            expressioncfn = controlFlowNode.newControlFlowNode(expression);
            controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, expressioncfn);
            last = expressioncfn;
        }

        if (arguments != null && arguments.size() > 0) {
            // Take the argument list and make more CFNs from them.
            cfns = createCFNListFromASTNodeList(arguments);
            if (expression == null)
                controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, cfns.get(0));
            else
                expressioncfn.addEdge(ControlFlowNode.Direction.FORWARDS, cfns.get(0));
            last = cfns.get(cfns.size() - 1);
        }

        if (last == null)
            return false;
        last.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);

        if (expressioncfn != null)
            expressioncfn.evaluate();
        if (cfns != null)
            evaluate(cfns);

        return false;
    }

    public boolean visit(SuperFieldAccess node) {
        return false;
    }

    public boolean visit(SuperMethodInvocation node) {
        List arguments = node.arguments();
        if (arguments == null || arguments.size() == 0)
            return false;
        List<ControlFlowNode> cfns = createCFNListFromASTNodeList(arguments);
        ControlFlowNode cfn = cfns.get(0);
        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, cfn);
        cfn = cfns.get(cfns.size() - 1);
        cfn.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
        evaluate(cfns);
        return false;
    }

    public boolean visit(SwitchCase node) {
        Expression expression = node.getExpression();
        if (expression == null)
            return false;

        ControlFlowNode expressioncfn = controlFlowNode.newControlFlowNode(expression);
        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, expressioncfn);
        expressioncfn.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
        expressioncfn.evaluate();
        return false;
    }

    public boolean visit(SwitchStatement node) {
        Expression expression = node.getExpression();
        List statements = node.statements();
        if (expression == null)
            throw new CrystalRuntimeException("Switch statement without an expression?");
        if (statements == null || statements.size() == 0)
            throw new CrystalRuntimeException("Switch statements without any statements?");

        ControlFlowNode expressionCFN = controlFlowNode.newControlFlowNode(expression);

        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, expressionCFN);

        Iterator i = statements.iterator();
        List<ControlFlowNode> statementCFNs = new LinkedList<ControlFlowNode>();
        ControlFlowNode cfn = null, previous = null;
        Statement astNode;
        boolean haveSeenDefault = false;
        while (i.hasNext()) {
            astNode = (Statement) i.next();
            cfn = controlFlowNode.newControlFlowNode(astNode);
            statementCFNs.add(cfn);
            if (previous != null)
                previous.addEdge(Direction.FORWARDS, cfn);
            if (astNode.getNodeType() == ASTNode.SWITCH_CASE) {
                expressionCFN.addEdge(Direction.FORWARDS, cfn);
                SwitchCase sc = (SwitchCase) astNode;
                if (sc.isDefault()) {
                    if (haveSeenDefault)
                        throw new CrystalRuntimeException("cannot have more than one default in a switch");
                    haveSeenDefault = true;
                }
            }
            previous = cfn;
        }
        if (cfn == null)
            throw new CrystalRuntimeException("no statements in switch");
        // if we never saw a default, then add an edge to the switch statement
        if (!haveSeenDefault)
            expressionCFN.addEdge(Direction.FORWARDS, controlFlowNode);
        cfn.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);

        expressionCFN.evaluate();
        if (statementCFNs != null)
            evaluate(statementCFNs);

        return false;
    }

    /**
     * Example: synchronized (myVar) { myMethod(); }
     */
    public boolean visit(SynchronizedStatement node) {
        ControlFlowNode expression = controlFlowNode.newControlFlowNode(node.getExpression());
        ControlFlowNode body = controlFlowNode.newControlFlowNode(node.getBody());

        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, expression);

        expression.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
        controlFlowNode.addEdge(ControlFlowNode.Direction.FORWARDS, body);

        expression.evaluate();
        body.evaluate();

        return false;
    }

    /**
     * This node does not influence control flow.
     */
    public boolean visit(TagElement node) {
        return false;
    }

    /**
     * This node does not influence control flow.
     */
    public boolean visit(TextElement node) {
        return false;
    }

    /**
     * This node does not influence control flow.
     */
    public boolean visit(ThisExpression node) {
        return false;
    }

    /**
     * 
     * The throw statement will either be caught locally or
     * be thrown up the stack (ie leave the method).
     */
    public boolean visit(ThrowStatement node) {
        return false;
    }

    /**
     * Example: try { int x; }
     */
    public boolean visit(TryStatement node) {
        Block body = node.getBody();
        Block finalBody = node.getFinally();
        List catches = node.catchClauses();

        ControlFlowNode cfnBody = controlFlowNode.newControlFlowNode(body);
        ControlFlowNode cfnFinalBody = null;

        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, cfnBody);

        List<ControlFlowNode> cfns = new ArrayList<ControlFlowNode>();
        ControlFlowNode cfn = cfnBody, prev = cfnBody;
        Iterator i = catches.iterator();
        CatchClause cc;
        for (; i.hasNext();) {
            cc = (CatchClause) i.next();
            cfn = controlFlowNode.newControlFlowNode(cc);
            prev.addEdge(ControlFlowNode.Direction.FORWARDS, cfn);
            prev = cfn;
            cfns.add(cfn);
        }

        if (finalBody != null) {
            cfnFinalBody = controlFlowNode.newControlFlowNode(finalBody);
            cfn.addEdge(ControlFlowNode.Direction.FORWARDS, cfnFinalBody);
            cfnFinalBody.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
        } else {
            cfn.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
        }

        // Peform Visits
        cfnBody.evaluate();
        Iterator<ControlFlowNode> j = cfns.iterator();
        for (; j.hasNext();)
            j.next().evaluate();
        if (finalBody != null)
            cfnFinalBody.evaluate();

        return false;
    }

    /**
     * This node does not influence control flow.
     */
    public boolean visit(TypeDeclaration node) {
        return false;
    }

    /**
     * This node does not influence control flow.
     */
    public boolean visit(TypeDeclarationStatement node) {
        return false;
    }

    /**
     * This node does not influence control flow.
     */
    public boolean visit(TypeLiteral node) {
        // Leaf, no action necessary
        return false;
    }

    /**
     * This node does not influence control flow.
     */
    public boolean visit(TypeParameter node) {
        return false;
    }

    public boolean visit(VariableDeclarationExpression node) {
        List fragments = node.fragments();
        if (fragments == null || fragments.size() == 0)
            return false;
        List<ControlFlowNode> cfns = createCFNListFromASTNodeList(fragments);
        ControlFlowNode cfn = cfns.get(0);
        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, cfn);
        cfn = cfns.get(cfns.size() - 1);
        cfn.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
        evaluate(cfns);

        return false;
    }

    /**
     * Example: int a[] = {5, 4}, x = 5, y;
     * VariableDeclarationFragments: (a[] = {5, 4}), (x = 5), & (y)  
     */
    public boolean visit(VariableDeclarationFragment node) {
        // Similar to Assignment
        ControlFlowNode nameCFN = controlFlowNode.newControlFlowNode(node.getName());

        // Empty Initializer
        if (node.getInitializer() == null) {
            controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, nameCFN);
            nameCFN.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
            nameCFN.evaluate();
        } else {
            ControlFlowNode initializerCFN = controlFlowNode.newControlFlowNode(node.getInitializer());

            controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, initializerCFN);

            initializerCFN.addEdge(ControlFlowNode.Direction.FORWARDS, nameCFN);
            nameCFN.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);

            initializerCFN.evaluate();
            nameCFN.evaluate();
        }

        return false;
    }

    public boolean visit(VariableDeclarationStatement node) {
        List fragments = node.fragments();
        if (fragments == null || fragments.size() == 0)
            return false;

        List<ControlFlowNode> cfns = createCFNListFromASTNodeList(fragments);
        ControlFlowNode cfn = cfns.get(0);
        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, cfn);
        cfn = cfns.get(cfns.size() - 1);
        cfn.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);

        evaluate(cfns);

        return false;
    }

    /*
     * Example: while (x < 10) { x++; run(x); };
     */
    public boolean visit(WhileStatement node) {
        //       while ( Expression ) Statement
        //             ----- 1 ---  -- 3 -> 1 --
        //      -------------- 2 -> 3 ----------
        //       -------------- 2 -> exit -------

        ControlFlowNode exit = controlFlowNode.getNode(ControlFlowNode.Direction.FORWARDS);
        ControlFlowNode expressionCFN = controlFlowNode.newControlFlowNode(node.getExpression());
        // expressionCFN.copyLabelsFrom(controlFlowNode);
        ControlFlowNode bodyCFN = controlFlowNode.newControlFlowNode(node.getBody());

        controlFlowNode.moveEdges(ControlFlowNode.Direction.BACKWARDS, expressionCFN);

        expressionCFN.addEdge(ControlFlowNode.Direction.FORWARDS, controlFlowNode);
        controlFlowNode.addEdge(ControlFlowNode.Direction.FORWARDS, bodyCFN);
        bodyCFN.addEdge(ControlFlowNode.Direction.FORWARDS, expressionCFN);

        controlFlowNode.setLoopPaths(expressionCFN, exit);

        expressionCFN.evaluate();
        bodyCFN.evaluate();

        return false;
    }

    /**
     * This node does not influence control flow.
     */
    public boolean visit(WildcardType node) {
        return false;
    }

    /*
     * Helper Methods
     * 
     */

    /**
     * Takes a list of ASTNodes and creates ControlFlowNodes for each and connects them.
     * The first element will not have any back edges and the last element will not
     * have any forward edges.
     */
    protected List<ControlFlowNode> createCFNListFromASTNodeList(List nodes) {
        if (nodes == null)
            return null;
        List<ControlFlowNode> cfns = new ArrayList<ControlFlowNode>();
        ControlFlowNode current, previous = null;
        Iterator i = nodes.iterator();
        ASTNode node;
        for (; i.hasNext();) {
            node = (ASTNode) i.next();
            current = controlFlowNode.newControlFlowNode(node);
            cfns.add(current);
            if (previous != null)
                previous.addEdge(ControlFlowNode.Direction.FORWARDS, current);
            previous = current;
        }
        return cfns;
    }

    protected void evaluate(List<ControlFlowNode> list) {
        if (list == null)
            return;
        Iterator<ControlFlowNode> i = list.iterator();
        ControlFlowNode cfn;
        for (; i.hasNext();) {
            cfn = i.next();
            cfn.evaluate();
        }
    }
}