content: /*******************************************************************************
   * Copyright (c) 2000, 2009 IBM Corporation and others.
   * All rights reserved. This program and the accompanying materials
   * are made available under the terms of the Eclipse Public License v1.0
   * which accompanies this distribution, and is available at
   * http://www.eclipse.org/legal/epl-v10.html
   *
   * Contributors:
   *     IBM Corporation - initial API and implementation
   *******************************************************************************/
  package org.eclipse.wst.jsdt.internal.corext.refactoring.code.flow;
  
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.List;
  
  import org.eclipse.jface.text.IRegion;
  import org.eclipse.jface.text.Region;
  import org.eclipse.wst.jsdt.core.dom.ASTNode;
  import org.eclipse.wst.jsdt.core.dom.AnonymousClassDeclaration;
  import org.eclipse.wst.jsdt.core.dom.ArrayAccess;
  import org.eclipse.wst.jsdt.core.dom.ArrayCreation;
  import org.eclipse.wst.jsdt.core.dom.ArrayInitializer;
  import org.eclipse.wst.jsdt.core.dom.ArrayType;
  import org.eclipse.wst.jsdt.core.dom.Assignment;
  import org.eclipse.wst.jsdt.core.dom.Block;
  import org.eclipse.wst.jsdt.core.dom.BooleanLiteral;
  import org.eclipse.wst.jsdt.core.dom.BreakStatement;
  import org.eclipse.wst.jsdt.core.dom.CatchClause;
  import org.eclipse.wst.jsdt.core.dom.CharacterLiteral;
  import org.eclipse.wst.jsdt.core.dom.ClassInstanceCreation;
  import org.eclipse.wst.jsdt.core.dom.ConditionalExpression;
  import org.eclipse.wst.jsdt.core.dom.ConstructorInvocation;
  import org.eclipse.wst.jsdt.core.dom.ContinueStatement;
  import org.eclipse.wst.jsdt.core.dom.DoStatement;
  import org.eclipse.wst.jsdt.core.dom.EmptyStatement;
  import org.eclipse.wst.jsdt.core.dom.EnhancedForStatement;
  import org.eclipse.wst.jsdt.core.dom.Expression;
  import org.eclipse.wst.jsdt.core.dom.ExpressionStatement;
  import org.eclipse.wst.jsdt.core.dom.FieldAccess;
  import org.eclipse.wst.jsdt.core.dom.FieldDeclaration;
  import org.eclipse.wst.jsdt.core.dom.ForInStatement;
  import org.eclipse.wst.jsdt.core.dom.ForStatement;
  import org.eclipse.wst.jsdt.core.dom.FunctionDeclaration;
  import org.eclipse.wst.jsdt.core.dom.FunctionInvocation;
  import org.eclipse.wst.jsdt.core.dom.IBinding;
  import org.eclipse.wst.jsdt.core.dom.IFunctionBinding;
  import org.eclipse.wst.jsdt.core.dom.IVariableBinding;
  import org.eclipse.wst.jsdt.core.dom.IfStatement;
  import org.eclipse.wst.jsdt.core.dom.ImportDeclaration;
  import org.eclipse.wst.jsdt.core.dom.InfixExpression;
  import org.eclipse.wst.jsdt.core.dom.Initializer;
  import org.eclipse.wst.jsdt.core.dom.InstanceofExpression;
  import org.eclipse.wst.jsdt.core.dom.JSdoc;
  import org.eclipse.wst.jsdt.core.dom.JavaScriptUnit;
  import org.eclipse.wst.jsdt.core.dom.LabeledStatement;
  import org.eclipse.wst.jsdt.core.dom.ListExpression;
  import org.eclipse.wst.jsdt.core.dom.Name;
  import org.eclipse.wst.jsdt.core.dom.NullLiteral;
  import org.eclipse.wst.jsdt.core.dom.NumberLiteral;
  import org.eclipse.wst.jsdt.core.dom.PackageDeclaration;
  import org.eclipse.wst.jsdt.core.dom.ParenthesizedExpression;
  import org.eclipse.wst.jsdt.core.dom.PostfixExpression;
  import org.eclipse.wst.jsdt.core.dom.PrefixExpression;
  import org.eclipse.wst.jsdt.core.dom.PrimitiveType;
  import org.eclipse.wst.jsdt.core.dom.QualifiedName;
  import org.eclipse.wst.jsdt.core.dom.QualifiedType;
  import org.eclipse.wst.jsdt.core.dom.ReturnStatement;
  import org.eclipse.wst.jsdt.core.dom.SimpleName;
  import org.eclipse.wst.jsdt.core.dom.SimpleType;
  import org.eclipse.wst.jsdt.core.dom.SingleVariableDeclaration;
  import org.eclipse.wst.jsdt.core.dom.Statement;
  import org.eclipse.wst.jsdt.core.dom.StringLiteral;
  import org.eclipse.wst.jsdt.core.dom.SuperConstructorInvocation;
  import org.eclipse.wst.jsdt.core.dom.SuperFieldAccess;
  import org.eclipse.wst.jsdt.core.dom.SuperMethodInvocation;
  import org.eclipse.wst.jsdt.core.dom.SwitchCase;
  import org.eclipse.wst.jsdt.core.dom.SwitchStatement;
  import org.eclipse.wst.jsdt.core.dom.ThisExpression;
  import org.eclipse.wst.jsdt.core.dom.ThrowStatement;
  import org.eclipse.wst.jsdt.core.dom.TryStatement;
  import org.eclipse.wst.jsdt.core.dom.TypeDeclaration;
  import org.eclipse.wst.jsdt.core.dom.TypeDeclarationStatement;
  import org.eclipse.wst.jsdt.core.dom.TypeLiteral;
  import org.eclipse.wst.jsdt.core.dom.VariableDeclarationExpression;
  import org.eclipse.wst.jsdt.core.dom.VariableDeclarationFragment;
  import org.eclipse.wst.jsdt.core.dom.VariableDeclarationStatement;
  import org.eclipse.wst.jsdt.core.dom.WhileStatement;
  import org.eclipse.wst.jsdt.core.dom.WithStatement;
  import org.eclipse.wst.jsdt.internal.corext.dom.GenericVisitor;
  
  /**
   * Special flow analyzer to determine the return value of the extracted method
   * and the variables which have to be passed to the method.
   * 
   * Note: This analyzer doesn't do a full flow analysis. For example it doesn't
   * do dead code analysis or variable initialization analysis. It analyses the
   * the first access to a variable (read or write) and if all execution paths
   * return a value.
   */
  abstract class FlowAnalyzer extends GenericVisitor {
  
  	static protected class SwitchData {
  		private boolean fHasDefaultCase;
  		private List fRanges= new ArrayList(4);
  		private List fInfos= new ArrayList(4);
  		public void setHasDefaultCase() {
  			fHasDefaultCase= true;
  		}
  		public boolean hasDefaultCase() {
  			return fHasDefaultCase;
  		}
  		public void add(IRegion range, FlowInfo info) {
  			fRanges.add(range);
  			fInfos.add(info);
  		}
  		public IRegion[] getRanges() {
  			return (IRegion[]) fRanges.toArray(new IRegion[fRanges.size()]);	
  		}
  		public FlowInfo[] getInfos() {
  			return (FlowInfo[]) fInfos.toArray(new FlowInfo[fInfos.size()]);
  		}
  		public FlowInfo getInfo(int index) {
  			return (FlowInfo)fInfos.get(index);
  		}
  	}
  
  	private HashMap fData = new HashMap(100);
  	/* package */ FlowContext fFlowContext= null;
  
  	public FlowAnalyzer(FlowContext context) {
  		fFlowContext= context;
  	}
  
  	protected abstract boolean createReturnFlowInfo(ReturnStatement node);
  
  	protected abstract boolean traverseNode(ASTNode node);
  	
  	protected boolean skipNode(ASTNode node) {
  		return !traverseNode(node);
  	}
  	
  	protected final boolean visitNode(ASTNode node) {
  		return traverseNode(node);
  	}
  	
  	//---- Hooks to create Flow info objects. User may introduce their own infos.
  	
  	protected ReturnFlowInfo createReturn(ReturnStatement statement) {
  		return new ReturnFlowInfo(statement);
  	}
  	
  	protected ThrowFlowInfo createThrow() {
  		return new ThrowFlowInfo();
  	}
  	
  	protected BranchFlowInfo createBranch(SimpleName label) {
  		return new BranchFlowInfo(label, fFlowContext);
  	}
  	
  	protected GenericSequentialFlowInfo createSequential() {
  		return new GenericSequentialFlowInfo();
  	}
  	
  	protected ConditionalFlowInfo createConditional() {
  		return new ConditionalFlowInfo();
  	}
  	
  	protected EnhancedForFlowInfo createEnhancedFor() {
  		return new EnhancedForFlowInfo();
  	}
  	
  	protected ForFlowInfo createFor() {
  		return new ForFlowInfo();
  	}
  	
  	protected TryFlowInfo createTry() {
  		return new TryFlowInfo();
  	}
  	
  	protected WhileFlowInfo createWhile() {
  		return new WhileFlowInfo();
  	}
  	protected WithFlowInfo createWith() {
  		return new WithFlowInfo();
  	}
  	
  	protected IfFlowInfo createIf() {
  		return new IfFlowInfo();
  	}
  	
  	protected DoWhileFlowInfo createDoWhile() {
  		return new DoWhileFlowInfo();
  	}
  	
  	protected SwitchFlowInfo createSwitch() {
  		return new SwitchFlowInfo();
  	}
  
  	protected BlockFlowInfo createBlock() {
  		return new BlockFlowInfo();
  	}
  	
  	protected MessageSendFlowInfo createMessageSendFlowInfo() {
  		return new MessageSendFlowInfo();
  	}
  	
  	protected FlowContext getFlowContext() {
  		return fFlowContext;
  	}
  	
  	//---- Helpers to access flow analysis objects ----------------------------------------
  	
  	protected FlowInfo getFlowInfo(ASTNode node) {
  		return (FlowInfo)fData.remove(node);
  	}
  	
  	protected void setFlowInfo(ASTNode node, FlowInfo info) {
  		fData.put(node, info);	
  	}
  	
  	protected FlowInfo assignFlowInfo(ASTNode target, ASTNode source) {
  		FlowInfo result= getFlowInfo(source);
  		setFlowInfo(target, result);
  		return result;
  	}
  	
  	protected FlowInfo accessFlowInfo(ASTNode node) {
  		return (FlowInfo)fData.get(node);
  	}
  	
  	//---- Helpers to process sequential flow infos -------------------------------------
  	
  	protected GenericSequentialFlowInfo processSequential(ASTNode parent, List nodes) {
  		GenericSequentialFlowInfo result= createSequential(parent);
  		process(result, nodes);
  		return result;
  	}
  	
  	protected GenericSequentialFlowInfo processSequential(ASTNode parent, ASTNode node1) {
  		GenericSequentialFlowInfo result= createSequential(parent);
  		if (node1 != null)
  			result.merge(getFlowInfo(node1), fFlowContext);
  		return result;
  	}
  	
  	protected GenericSequentialFlowInfo processSequential(ASTNode parent, ASTNode node1, ASTNode node2) {
  		GenericSequentialFlowInfo result= createSequential(parent);
  		if (node1 != null)
  			result.merge(getFlowInfo(node1), fFlowContext);
  		if (node2 != null)
  			result.merge(getFlowInfo(node2), fFlowContext);
  		return result;
  	}
  	
  	protected GenericSequentialFlowInfo createSequential(ASTNode parent) {
  		GenericSequentialFlowInfo result= createSequential();
  		setFlowInfo(parent, result);
  		return result;
  	}
  	
  	protected GenericSequentialFlowInfo createSequential(List nodes) {
  		GenericSequentialFlowInfo result= createSequential();
  		process(result, nodes);
  		return result;		
  	}
  	
  	//---- Generic merge methods --------------------------------------------------------
  	
  	protected void process(GenericSequentialFlowInfo info, List nodes) {
  		if (nodes == null)
  			return;
  		for (Iterator iter= nodes.iterator(); iter.hasNext();) {
  			info.merge(getFlowInfo((ASTNode)iter.next()), fFlowContext);
  		}
  	}
  	
  	protected void process(GenericSequentialFlowInfo info, ASTNode node) {
  		if (node != null)
  			info.merge(getFlowInfo(node), fFlowContext);
  	}
  	
  	protected void process(GenericSequentialFlowInfo info, ASTNode node1, ASTNode node2) {
  		if (node1 != null)
  			info.merge(getFlowInfo(node1), fFlowContext);
  		if (node2 != null)
  			info.merge(getFlowInfo(node2), fFlowContext);
  	}
  	
  	//---- special visit methods -------------------------------------------------------
  	
  	public boolean visit(EmptyStatement node) {
  		// Empty statements aren't of any interest.
  		return false;
  	}
  	
  	public boolean visit(TryStatement node) {
  		if (traverseNode(node)) {
  			fFlowContext.pushExcptions(node);
  			node.getBody().accept(this);
  			fFlowContext.popExceptions();
  			List catchClauses= node.catchClauses();
  			for (Iterator iter= catchClauses.iterator(); iter.hasNext();) {
  				((CatchClause)iter.next()).accept(this);
  			}
  			Block finallyBlock= node.getFinally();
  			if (finallyBlock != null) {
  				finallyBlock.accept(this);
  			}
  		}
  		return false;
  	}
  	
  	//---- Helper to process switch statement ----------------------------------------
  	
  	protected SwitchData createSwitchData(SwitchStatement node) {
  		SwitchData result= new SwitchData();
  		List statements= node.statements();
  		if (statements.isEmpty())
  			return result;
  			
  		int start= -1, end= -1;
  		GenericSequentialFlowInfo info= null;
  		
  		for (Iterator iter= statements.iterator(); iter.hasNext(); ) {
  			Statement statement= (Statement)iter.next();
  			if (statement instanceof SwitchCase) {
  				SwitchCase switchCase= (SwitchCase)statement;
  				if (switchCase.isDefault()) {
  					result.setHasDefaultCase();
  				}
  				if (info == null) {
  					info= createSequential();
  					start= statement.getStartPosition();
  				} else {
  					if (info.isReturn() || info.isPartialReturn() || info.branches()) {
  						result.add(new Region(start, end - start + 1), info);
  						info= createSequential();
  						start= statement.getStartPosition();
  					}
  				}
  			} else {
  				info.merge(getFlowInfo(statement), fFlowContext);
  			}
  			end= statement.getStartPosition() + statement.getLength() - 1;
  		}
  		result.add(new Region(start, end - start + 1), info);
  		return result;
  	}
  	
  	protected void endVisit(SwitchStatement node, SwitchData data) {
  		SwitchFlowInfo switchFlowInfo= createSwitch();
  		setFlowInfo(node, switchFlowInfo);
  		switchFlowInfo.mergeTest(getFlowInfo(node.getExpression()), fFlowContext);
  		FlowInfo[] cases= data.getInfos();
  		for (int i= 0; i < cases.length; i++)
  			switchFlowInfo.mergeCase(cases[i], fFlowContext);
  		switchFlowInfo.mergeDefault(data.hasDefaultCase(), fFlowContext);	
  		switchFlowInfo.removeLabel(null);
  	}
  
  	//---- concret endVisit methods ---------------------------------------------------
  	
  	public void endVisit(AnonymousClassDeclaration node) {
  		if (skipNode(node))
  			return;
  		FlowInfo info= processSequential(node, node.bodyDeclarations());
  		info.setNoReturn();
  	}
  	
  	public void endVisit(ArrayAccess node) {
  		if (skipNode(node))
  			return;
  		processSequential(node, node.getArray(), node.getIndex());
  	}
  	
  	public void endVisit(ArrayCreation node) {
  		if (skipNode(node))
  			return;
  		GenericSequentialFlowInfo info= processSequential(node, node.getType());
  		process(info, node.dimensions());
  		process(info, node.getInitializer());
  	}
  	
  	public void endVisit(ArrayInitializer node) {
  		if (skipNode(node))
  			return;
  		processSequential(node, node.expressions());
  	}
  	
  	public void endVisit(ArrayType node) {
  		if (skipNode(node))
  			return;
  		processSequential(node, node.getElementType());
  	}
  	
  	public void endVisit(Assignment node) {
  		if (skipNode(node))
  			return;
  		FlowInfo lhs= getFlowInfo(node.getLeftHandSide());
  		FlowInfo rhs= getFlowInfo(node.getRightHandSide());
  		if (lhs instanceof LocalFlowInfo) {
  			LocalFlowInfo llhs= (LocalFlowInfo)lhs;
  			llhs.setWriteAccess(fFlowContext);
  			if (node.getOperator() != Assignment.Operator.ASSIGN) {
  				GenericSequentialFlowInfo tmp= createSequential();
  				tmp.merge(new LocalFlowInfo(llhs, FlowInfo.READ, fFlowContext), fFlowContext);
  				tmp.merge(rhs, fFlowContext);
  				rhs= tmp;
  			}
  		}
  		GenericSequentialFlowInfo info= createSequential(node);
  		// first process right and side and then left hand side.
  		info.merge(rhs, fFlowContext);
  		info.merge(lhs, fFlowContext);
  	}
  	
  	public void endVisit(Block node) {
  		if (skipNode(node))
  			return;
  		BlockFlowInfo info= createBlock();
  		setFlowInfo(node, info);
  		process(info, node.statements());
  	}
  	
  	public void endVisit(BooleanLiteral node) {
  		// Leaf node.
  	}
  	
  	public void endVisit(BreakStatement node) {
  		if (skipNode(node))
  			return;
  		setFlowInfo(node, createBranch(node.getLabel()));
  	}
  	
  	public void endVisit(CatchClause node) {
  		if (skipNode(node))
  			return;
  		processSequential(node, node.getException(), node.getBody());
  	}
  	
  	public void endVisit(CharacterLiteral node) {
  		// Leaf node.
  	}
  	
  	public void endVisit(ClassInstanceCreation node) {
  		if (skipNode(node))
  			return;
  		GenericSequentialFlowInfo info= processSequential(node, node.getExpression());
  		process(info, node.getType());
  		process(info, node.arguments());
  		process(info, node.getMember());
  		process(info, node.getAnonymousClassDeclaration());
  	}
  	
  	public void endVisit(JavaScriptUnit node) {
  		if (skipNode(node))
  			return;
  		GenericSequentialFlowInfo info= processSequential(node, node.imports());
  		process(info, node.types());
  	}
  	
  	public void endVisit(ConditionalExpression node) {
  		if (skipNode(node))
  			return;
  		ConditionalFlowInfo info= createConditional();
  		setFlowInfo(node, info);
  		info.mergeCondition(getFlowInfo(node.getExpression()), fFlowContext);
  		info.merge(
  			getFlowInfo(node.getThenExpression()), 
  			getFlowInfo(node.getElseExpression()), 
  			fFlowContext);
  	}
  	
  	public void endVisit(ConstructorInvocation node) {
  		if (skipNode(node))
  			return;
  		processSequential(node, node.arguments());
  	}
  	
  	public void endVisit(ContinueStatement node) {
  		if (skipNode(node))
  			return;
  		setFlowInfo(node, createBranch(node.getLabel()));
  	}
  	
  	public void endVisit(DoStatement node) {
  		if (skipNode(node))
  			return;
  		DoWhileFlowInfo info= createDoWhile();
  		setFlowInfo(node, info);
  		info.mergeAction(getFlowInfo(node.getBody()), fFlowContext);
  		info.mergeCondition(getFlowInfo(node.getExpression()), fFlowContext);
  		info.removeLabel(null);
  	}
  	
  	public void endVisit(EmptyStatement node) {
  		// Leaf node.
  	}
  	
  	public void endVisit(EnhancedForStatement node) {
  		if (skipNode(node))
  			return;
  		EnhancedForFlowInfo forInfo= createEnhancedFor();
  		setFlowInfo(node, forInfo);
  		forInfo.mergeParameter(getFlowInfo(node.getParameter()), fFlowContext);
  		forInfo.mergeExpression(getFlowInfo(node.getExpression()), fFlowContext);
  		forInfo.mergeAction(getFlowInfo(node.getBody()), fFlowContext);
  		forInfo.removeLabel(null);
  	}
  	
  	
  	public void endVisit(ExpressionStatement node) {
  		if (skipNode(node))
  			return;
  		assignFlowInfo(node, node.getExpression());
  	}
  	
  	public void endVisit(FieldAccess node) {
  		if (skipNode(node))
  			return;
  		processSequential(node, node.getExpression(), node.getName());
  	}
  	
  	public void endVisit(FieldDeclaration node) {
  		if (skipNode(node))
  			return;
  		GenericSequentialFlowInfo info= processSequential(node, node.getType());
  		process(info, node.fragments());
  	}
  	
  	public void endVisit(ForStatement node) {
  		if (skipNode(node))
  			return;
  		ForFlowInfo forInfo= createFor();
  		setFlowInfo(node, forInfo);
  		forInfo.mergeInitializer(createSequential(node.initializers()), fFlowContext);
  		forInfo.mergeCondition(getFlowInfo(node.getExpression()), fFlowContext);
  		forInfo.mergeAction(getFlowInfo(node.getBody()), fFlowContext);
  		// Increments are executed after the action.
  		forInfo.mergeIncrement(createSequential(node.updaters()), fFlowContext);
  		forInfo.removeLabel(null);
  	}
  	public void endVisit(ForInStatement node) {
  		if (skipNode(node))
  			return;
  		EnhancedForFlowInfo forInfo= createEnhancedFor();
  		setFlowInfo(node, forInfo);
  		forInfo.mergeParameter(getFlowInfo(node.getIterationVariable()), fFlowContext);
  		forInfo.mergeExpression(getFlowInfo(node.getCollection()), fFlowContext);
  		forInfo.mergeAction(getFlowInfo(node.getBody()), fFlowContext);
  		forInfo.removeLabel(null);
  	}
  
  
  	public void endVisit(IfStatement node) {
  		if (skipNode(node))
  			return;
  		IfFlowInfo info= createIf();
  		setFlowInfo(node, info);
  		info.mergeCondition(getFlowInfo(node.getExpression()), fFlowContext);
  		info.merge(getFlowInfo(node.getThenStatement()), getFlowInfo(node.getElseStatement()), fFlowContext);
  	}
  	
  	public void endVisit(ImportDeclaration node) {
  		if (skipNode(node))
  			return;
  		assignFlowInfo(node, node.getName());
  	}
  	
  	public void endVisit(InfixExpression node) {
  		if (skipNode(node))
  			return;
  		GenericSequentialFlowInfo info= processSequential(node, node.getLeftOperand(), node.getRightOperand());
  		process(info, node.extendedOperands());
  	}
  	
  	public void endVisit(InstanceofExpression node) {
  		if (skipNode(node))
  			return;
  		processSequential(node, node.getLeftOperand(), node.getRightOperand());
  	}
  	
  	public void endVisit(Initializer node) {
  		if (skipNode(node))
  			return;
  		assignFlowInfo(node, node.getBody());
  	}
  	
  	public void endVisit(JSdoc node) {
  		// no influence on flow analysis
  	}
  	
  	public void endVisit(LabeledStatement node) {
  		if (skipNode(node))
  			return;
  		FlowInfo info= assignFlowInfo(node, node.getBody());
  		if (info != null)
  			info.removeLabel(node.getLabel());
  	}
  	
  	public void endVisit(ListExpression node) {
  		if (skipNode(node))
  			return;
  		processSequential(node, node.expressions());
  	}
  	
  
  	
  	public void endVisit(FunctionDeclaration node) {
  		if (skipNode(node))
  			return;
  		GenericSequentialFlowInfo info= processSequential(node, node.getReturnType2());
  		process(info, node.parameters());
  		process(info, node.thrownExceptions());
  		process(info, node.getBody());
  	}
  	
  	public void endVisit(FunctionInvocation node) {
  		endVisitMethodInvocation(node, node.getExpression(), node.arguments(), getMethodBinding(node.getName()));
  	}
  
  	
  	public void endVisit(NullLiteral node) {
  		// Leaf node.
  	}
  	
  	public void endVisit(NumberLiteral node) {
  		// Leaf node.
  	}
  	
  	public void endVisit(PackageDeclaration node) {
  		if (skipNode(node))
  			return;
  		assignFlowInfo(node, node.getName());
  	}
  	
  	public void endVisit(ParenthesizedExpression node) {
  		if (skipNode(node))
  			return;
  		assignFlowInfo(node, node.getExpression());
  	}
  	
  	public void endVisit(PostfixExpression node) {
  		endVisitIncDecOperation(node, node.getOperand());
  	}
  	
  	public void endVisit(PrefixExpression node) {
  		PrefixExpression.Operator op= node.getOperator();
  		if (PrefixExpression.Operator.INCREMENT.equals(op) || PrefixExpression.Operator.DECREMENT.equals(op)) {
  			endVisitIncDecOperation(node, node.getOperand());
  		} else {
  			assignFlowInfo(node, node.getOperand());
  		}
  	}
  	
  	public void endVisit(PrimitiveType node) {
  		// Leaf node
  	}
  	
  	public void endVisit(QualifiedName node) {
  		if (skipNode(node))
  			return;
  		processSequential(node, node.getQualifier(), node.getName());
  	}
  	
  	public void endVisit(QualifiedType node) {
  		if (skipNode(node))
  			return;
  		processSequential(node, node.getQualifier(), node.getName());
  	}
  	
  	public void endVisit(ReturnStatement node) {
  		if (skipNode(node))
  			return;
  			
  		if (createReturnFlowInfo(node)) {
  			ReturnFlowInfo info= createReturn(node);
  			setFlowInfo(node, info);
  			info.merge(getFlowInfo(node.getExpression()), fFlowContext);
  		} else {
  			assignFlowInfo(node, node.getExpression());
  		}
  	}
  	
  	public void endVisit(SimpleName node) {
  		if (skipNode(node) || node.isDeclaration())
  			return;
  		IBinding binding= node.resolveBinding();
  		if (binding instanceof IVariableBinding) {
  			IVariableBinding variable= (IVariableBinding)binding;
  			if (!variable.isField() && !variable.isGlobal()) {
  				setFlowInfo(node, new LocalFlowInfo(
  					variable,
  					FlowInfo.READ,
  					fFlowContext));
  			}
  		}
  	}
  	
  	public void endVisit(SimpleType node) {
  		if (skipNode(node))
  			return;
  		assignFlowInfo(node, node.getName());
  	}
  	
  	
  	public void endVisit(SingleVariableDeclaration node) {
  		if (skipNode(node))
  			return;
  			
  		IVariableBinding binding= node.resolveBinding();
  		LocalFlowInfo nameInfo= null;
  		Expression initializer= node.getInitializer();
  		if (binding != null && !binding.isField() && initializer != null) {
  			nameInfo= new LocalFlowInfo(binding, FlowInfo.WRITE, fFlowContext);
  		}
  		GenericSequentialFlowInfo info= processSequential(node, node.getType(), initializer);
  		info.merge(nameInfo, fFlowContext);
  	}
  	
  	public void endVisit(StringLiteral node) {
  		// Leaf node
  	}
  	
  	public void endVisit(SuperConstructorInvocation node) {
  		endVisitMethodInvocation(node, node.getExpression(), node.arguments(), node.resolveConstructorBinding());
  	}
  	
  	public void endVisit(SuperFieldAccess node) {
  		if (skipNode(node))
  			return;
  		processSequential(node, node.getQualifier(), node.getName());
  	}
  	
  	public void endVisit(SuperMethodInvocation node) {
  		endVisitMethodInvocation(node, node.getQualifier(), node.arguments(), getMethodBinding(node.getName()));
  	}
  	
  	public void endVisit(SwitchCase node) {
  		endVisitNode(node);
  	}
  	
  	public void endVisit(SwitchStatement node) {
  		if (skipNode(node))
  			return;
  		endVisit(node, createSwitchData(node));
  	}
  	
  	
  	public void endVisit(ThisExpression node) {
  		if (skipNode(node))
  			return;
  		assignFlowInfo(node, node.getQualifier());
  	}
  	
  	public void endVisit(ThrowStatement node) {
  		if (skipNode(node))
  			return;
  		ThrowFlowInfo info= createThrow();
  		setFlowInfo(node, info);
  		Expression expression= node.getExpression();
  		info.merge(getFlowInfo(expression), fFlowContext);
  		info.mergeException(expression.resolveTypeBinding(), fFlowContext);
  	}
  	
  	public void endVisit(TryStatement node) {
  		if (skipNode(node))
  			return;
  		TryFlowInfo info= createTry();
  		setFlowInfo(node, info);
  		info.mergeTry(getFlowInfo(node.getBody()), fFlowContext);
  		info.removeExceptions(node);
  		for (Iterator iter= node.catchClauses().iterator(); iter.hasNext();) {
  			CatchClause element= (CatchClause)iter.next();
  			info.mergeCatch(getFlowInfo(element), fFlowContext);
  		}
  		info.mergeFinally(getFlowInfo(node.getFinally()), fFlowContext);
  	}
  	
  	// TODO account for enums and annotations
  
  	public void endVisit(TypeDeclaration node) {
  		if (skipNode(node))
  			return;
  		GenericSequentialFlowInfo info= processSequential(node, node.getSuperclassType());
  		process(info, node.bodyDeclarations());
  		info.setNoReturn();
  	}
  	
  	public void endVisit(TypeDeclarationStatement node) {
  		if (skipNode(node))
  			return;
  		assignFlowInfo(node, node.getDeclaration());
  	}
  	
  	public void endVisit(TypeLiteral node) {
  		if (skipNode(node))
  			return;
  		assignFlowInfo(node, node.getType());
  	}
  	
  	public void endVisit(VariableDeclarationExpression node) {
  		if (skipNode(node))
  			return;
  		GenericSequentialFlowInfo info= processSequential(node, node.getType());
  		process(info, node.fragments());
  	}
  	
  	public void endVisit(VariableDeclarationStatement node) {
  		if (skipNode(node))
  			return;
  		GenericSequentialFlowInfo info= processSequential(node, node.getType());
  		process(info, node.fragments());
  	}
  	
  	public void endVisit(VariableDeclarationFragment node) {
  		if (skipNode(node))
  			return;
  			
  		IVariableBinding binding= node.resolveBinding();
  		LocalFlowInfo nameInfo= null;
  		Expression initializer= node.getInitializer();
  		if (binding != null && !binding.isField() && initializer != null) {
  			nameInfo= new LocalFlowInfo(binding, FlowInfo.WRITE, fFlowContext);
  		}
  		GenericSequentialFlowInfo info= processSequential(node, initializer);
  		info.merge(nameInfo, fFlowContext);
  	}
  	
  	public void endVisit(WhileStatement node) {
  		if (skipNode(node))
  			return;
  		WhileFlowInfo info= createWhile();
  		setFlowInfo(node, info);
  		info.mergeCondition(getFlowInfo(node.getExpression()), fFlowContext);
  		info.mergeAction(getFlowInfo(node.getBody()), fFlowContext);
  		info.removeLabel(null);
  	}
  	public void endVisit(WithStatement node) {
  		if (skipNode(node))
  			return;
  		WithFlowInfo info= createWith();
  		setFlowInfo(node, info);
  		info.mergeCondition(getFlowInfo(node.getExpression()), fFlowContext);
  		info.mergeAction(getFlowInfo(node.getBody()), fFlowContext);
  		info.removeLabel(null);
  	}
  	
  	private void endVisitMethodInvocation(ASTNode node, ASTNode receiver, List arguments, IFunctionBinding binding) {
  		if (skipNode(node))
  			return;
  		MessageSendFlowInfo info= createMessageSendFlowInfo();
  		setFlowInfo(node, info);
  		for (Iterator iter= arguments.iterator(); iter.hasNext();) {
  			Expression arg= (Expression) iter.next();
  			info.mergeArgument(getFlowInfo(arg), fFlowContext);
  		}
  		info.mergeReceiver(getFlowInfo(receiver), fFlowContext);
  	}
  	
  	private void endVisitIncDecOperation(Expression node, Expression operand) {
  		if (skipNode(node))
  			return;
  		FlowInfo info= getFlowInfo(operand);
  		if (info instanceof LocalFlowInfo) {
  			// Normally we should do this in the parent node since the write access take place later.
  			// But I couldn't come up with a case where this influences the flow analysis. So I kept
  			// it here to simplify the code.
  			GenericSequentialFlowInfo result= createSequential(node);
  			result.merge(info, fFlowContext);
  			result.merge(
  				new LocalFlowInfo((LocalFlowInfo)info, FlowInfo.WRITE, fFlowContext), 
  				fFlowContext);
  		} else {
  			setFlowInfo(node, info);
  		}
  	}
  	
  	private IFunctionBinding getMethodBinding(Name name) {
  		if (name == null)
  			return null;
  		IBinding binding= name.resolveBinding();
  		if (binding instanceof IFunctionBinding)
  			return (IFunctionBinding)binding;
  		return null;
  	}		
  }
  