content: /*******************************************************************************
   * Copyright (c) 2000, 2008 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.util;
  
  import java.util.ArrayList;
  import java.util.Iterator;
  import java.util.List;
  import java.util.Stack;
  
  import org.eclipse.wst.jsdt.core.dom.ASTVisitor;
  import org.eclipse.wst.jsdt.core.dom.AnonymousClassDeclaration;
  import org.eclipse.wst.jsdt.core.dom.CatchClause;
  import org.eclipse.wst.jsdt.core.dom.ClassInstanceCreation;
  import org.eclipse.wst.jsdt.core.dom.FunctionInvocation;
  import org.eclipse.wst.jsdt.core.dom.ITypeBinding;
  import org.eclipse.wst.jsdt.core.dom.ThrowStatement;
  import org.eclipse.wst.jsdt.core.dom.TryStatement;
  import org.eclipse.wst.jsdt.core.dom.TypeDeclaration;
  
  public abstract class AbstractExceptionAnalyzer extends ASTVisitor {
  	
  	private List fCurrentExceptions;	// Elements in this list are of type TypeBinding
  	private Stack fTryStack;
  
  	protected AbstractExceptionAnalyzer() {
  		fTryStack= new Stack();
  		fCurrentExceptions= new ArrayList(1);
  		fTryStack.push(fCurrentExceptions);
  	}
  
  	public abstract boolean visit(ThrowStatement node);
  	
  	public abstract boolean visit(FunctionInvocation node);
  	
  	public abstract boolean visit(ClassInstanceCreation node);
  	
  	public boolean visit(TypeDeclaration node) {
  		// Don't dive into a local type.
  		if (node.isLocalTypeDeclaration())
  			return false;
  		return true;
  	}
  
  	
  	public boolean visit(AnonymousClassDeclaration node) {
  		// Don't dive into a local type.
  		return false;
  	}
  	
  	public boolean visit(TryStatement node) {
  		fCurrentExceptions= new ArrayList(1);
  		fTryStack.push(fCurrentExceptions);
  		
  		// visit try block
  		node.getBody().accept(this);
  		
  		// Remove those exceptions that get catch by following catch blocks
  		List catchClauses= node.catchClauses();
  		if (!catchClauses.isEmpty())
  			handleCatchArguments(catchClauses);
  		List current= (List)fTryStack.pop();
  		fCurrentExceptions= (List)fTryStack.peek();
  		for (Iterator iter= current.iterator(); iter.hasNext();) {
  			addException((ITypeBinding)iter.next());
  		}
  		
  		// visit catch and finally
  		for (Iterator iter= catchClauses.iterator(); iter.hasNext(); ) {
  			((CatchClause)iter.next()).accept(this);
  		}
  		if (node.getFinally() != null)
  			node.getFinally().accept(this);
  			
  		// return false. We have visited the body by ourselves.	
  		return false;
  	}
  	
  	protected void addExceptions(ITypeBinding[] exceptions) {
  		if(exceptions == null)
  			return;
  		for (int i= 0; i < exceptions.length;i++) {
  			addException(exceptions[i]);
  		}			
  	}
  	
  	protected void addException(ITypeBinding exception) {
  		if (!fCurrentExceptions.contains(exception))
  			fCurrentExceptions.add(exception);
  	}
  	
  	protected List getCurrentExceptions() {
  		return fCurrentExceptions;
  	}
  	
  	private void handleCatchArguments(List catchClauses) {
  		for (Iterator iter= catchClauses.iterator(); iter.hasNext(); ) {
  			CatchClause clause= (CatchClause)iter.next();
  			ITypeBinding catchTypeBinding= clause.getException().getType().resolveBinding();
  			if (catchTypeBinding == null)	// No correct type resolve.
  				continue;
  			for (Iterator exceptions= new ArrayList(fCurrentExceptions).iterator(); exceptions.hasNext(); ) {
  				ITypeBinding throwTypeBinding= (ITypeBinding)exceptions.next();
  				if (catches(catchTypeBinding, throwTypeBinding))
  					fCurrentExceptions.remove(throwTypeBinding);
  			}
  		}
  	}
  	
  	private boolean catches(ITypeBinding catchTypeBinding, ITypeBinding throwTypeBinding) {
  		while(throwTypeBinding != null) {
  			if (throwTypeBinding == catchTypeBinding)
  				return true;
  			throwTypeBinding= throwTypeBinding.getSuperclass();	
  		}
  		return false;
  	}	
  }
  