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.ui.search;
  
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.List;
  
  import org.eclipse.jface.text.BadLocationException;
  import org.eclipse.jface.text.IDocument;
  import org.eclipse.jface.text.IRegion;
  import org.eclipse.search.ui.text.Match;
  import org.eclipse.wst.jsdt.core.IJavaScriptElement;
  import org.eclipse.wst.jsdt.core.dom.ASTNode;
  import org.eclipse.wst.jsdt.core.dom.ASTVisitor;
  import org.eclipse.wst.jsdt.core.dom.Assignment;
  import org.eclipse.wst.jsdt.core.dom.ClassInstanceCreation;
  import org.eclipse.wst.jsdt.core.dom.JavaScriptUnit;
  import org.eclipse.wst.jsdt.core.dom.Expression;
  import org.eclipse.wst.jsdt.core.dom.FieldAccess;
  import org.eclipse.wst.jsdt.core.dom.IBinding;
  import org.eclipse.wst.jsdt.core.dom.IFunctionBinding;
  import org.eclipse.wst.jsdt.core.dom.ITypeBinding;
  import org.eclipse.wst.jsdt.core.dom.IVariableBinding;
  import org.eclipse.wst.jsdt.core.dom.ImportDeclaration;
  import org.eclipse.wst.jsdt.core.dom.FunctionInvocation;
  import org.eclipse.wst.jsdt.core.dom.Modifier;
  import org.eclipse.wst.jsdt.core.dom.Name;
  import org.eclipse.wst.jsdt.core.dom.PostfixExpression;
  import org.eclipse.wst.jsdt.core.dom.PrefixExpression;
  import org.eclipse.wst.jsdt.core.dom.QualifiedName;
  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.Type;
  import org.eclipse.wst.jsdt.core.dom.VariableDeclarationFragment;
  import org.eclipse.wst.jsdt.core.dom.PrefixExpression.Operator;
  import org.eclipse.wst.jsdt.internal.corext.dom.ASTNodes;
  import org.eclipse.wst.jsdt.internal.corext.dom.Bindings;
  import org.eclipse.wst.jsdt.internal.corext.dom.NodeFinder;
  
  public class OccurrencesFinder extends ASTVisitor implements IOccurrencesFinder {
  	
  	public static final String IS_WRITEACCESS= "writeAccess"; //$NON-NLS-1$
  	public static final String IS_VARIABLE= "variable"; //$NON-NLS-1$
  	
  	private JavaScriptUnit fRoot;
  	private Name fSelectedNode;
  	private IBinding fTarget;
  	private List fUsages= new ArrayList/*<ASTNode>*/();
  	private List fWriteUsages= new ArrayList/*<ASTNode>*/();
  	private boolean fTargetIsStaticMethodImport;
  
  	public OccurrencesFinder(IBinding target) {
  		super(true);
  		fTarget= target;
  	}
  	
  	public OccurrencesFinder() {
  		super(true);
  	}
  	
  	public String initialize(JavaScriptUnit root, int offset, int length) {
  		return initialize(root, NodeFinder.perform(root, offset, length));
  	}
  	
  	public String initialize(JavaScriptUnit root, ASTNode node) {
  		if (!(node instanceof Name))
  			return SearchMessages.OccurrencesFinder_no_element; 
  		fRoot= root;
  		fSelectedNode= (Name)node;
  		fTarget= fSelectedNode.resolveBinding();
  		if (fTarget == null)
  			return SearchMessages.OccurrencesFinder_no_binding; 
  		fTarget= getBindingDeclaration(fTarget);
  		
  		fTargetIsStaticMethodImport= isStaticImport(fSelectedNode.getParent());
  		return null;
  	}
  	
  	public List perform() {
  		fRoot.accept(this);
  		return fUsages;
  	}
  	
  	public void collectOccurrenceMatches(IJavaScriptElement element, IDocument document, Collection resultingMatches) {
  		boolean isVariable= fTarget instanceof IVariableBinding;
  		HashMap lineToGroup= new HashMap();
  		
  		for (Iterator iter= fUsages.iterator(); iter.hasNext();) {
  			ASTNode node= (ASTNode) iter.next();
  			int startPosition= node.getStartPosition();
  			int length= node.getLength();
  			try {
  				boolean isWriteAccess= fWriteUsages.contains(node);
  				int line= document.getLineOfOffset(startPosition);
  				Integer lineInteger= Integer.valueOf(line);
  				OccurrencesGroupKey groupKey= (OccurrencesGroupKey) lineToGroup.get(lineInteger);
  				if (groupKey == null) {
  					IRegion region= document.getLineInformation(line);
  					String lineContents= document.get(region.getOffset(), region.getLength()).trim();
  					groupKey= new OccurrencesGroupKey(element, line, lineContents, isWriteAccess, isVariable);
  					lineToGroup.put(lineInteger, groupKey);
  				} else if (isWriteAccess) {
  					// a line with read an write access is considered as write access:
  					groupKey.setWriteAccess(true);
  				}
  				Match match= new Match(groupKey, startPosition, length);
  				resultingMatches.add(match);
  			} catch (BadLocationException e) {
  				//nothing
  			}
  		}
  	}
  	
  	/*
  	 * @see org.eclipse.wst.jsdt.internal.ui.search.IOccurrencesFinder#getJobLabel()
  	 */
  	public String getJobLabel() {
  		return SearchMessages.OccurrencesFinder_searchfor ; 
  	}
  	
  	public String getElementName() {
  		if (fSelectedNode != null) {
  			return ASTNodes.asString(fSelectedNode);
  		}
  		return null;
  	}
  	
  	public String getUnformattedPluralLabel() {
  		return SearchMessages.OccurrencesFinder_label_plural;
  	}
  	
  	public String getUnformattedSingularLabel() {
  		return SearchMessages.OccurrencesFinder_label_singular;
  	}
  	
  	public boolean visit(QualifiedName node) {
  		final IBinding binding= node.resolveBinding();
  		if (binding instanceof IVariableBinding && ((IVariableBinding)binding).isField()) {
  			SimpleName name= node.getName();
  			return !match(name, fUsages, name.resolveBinding());
  		}
  		if (binding instanceof IFunctionBinding) {
  			if (isStaticImport(node)) {
  				SimpleName name= node.getName();
  				return !matchStaticImport(name, fUsages, (IFunctionBinding)binding);
  			}
  		}
  		return !match(node, fUsages, binding);
  	}
  	
  	private static boolean isStaticImport(ASTNode node) {
  		if (!(node instanceof QualifiedName))
  			return false;
  		
  		ASTNode parent= ((QualifiedName)node).getParent();
  		return parent  instanceof ImportDeclaration && ((ImportDeclaration)parent).isStatic();
  	}
  
  	public boolean visit(FunctionInvocation node) {
  		if (fTargetIsStaticMethodImport)
  			return !matchStaticImport(node.getName(), fUsages, node.resolveMethodBinding());
  		
  		return true;
  	}
  	
  	public boolean visit(SimpleName node) {
  		return !match(node, fUsages, node.resolveBinding());
  	}
  
  	/*
  	 * @see org.eclipse.wst.jsdt.core.dom.ASTVisitor#visit(org.eclipse.wst.jsdt.core.dom.ConstructorInvocation)
  	 */
  	public boolean visit(ClassInstanceCreation node) {
  		// match with the constructor and the type.
  		Type type= node.getType();
  		if (type!=null)
  		{
  			if (type instanceof SimpleType) {
  				Name name= ((SimpleType) type).getName();
  				if (name instanceof QualifiedName)
  					name= ((QualifiedName)name).getName();
  				match(name, fUsages, node.resolveConstructorBinding());
  			}
  			
  		}
  		else
  		{
  			Expression member = node.getMember();
  			if (member instanceof SimpleName)
  			{
  				SimpleName name=(SimpleName)member;
  				match(name,fUsages,node.resolveConstructorBinding());
  			}
  		}
  		
  		return super.visit(node);
  	}
  	
  	public boolean visit(Assignment node) {
  		Expression lhs= node.getLeftHandSide();
  		SimpleName name= getSimpleName(lhs);
  		if (name != null) 
  			match(name, fWriteUsages, name.resolveBinding());	
  		lhs.accept(this);
  		node.getRightHandSide().accept(this);
  		return false;
  	}
  	
  	public boolean visit(SingleVariableDeclaration node) {
  		match(node.getName(), fWriteUsages, node.resolveBinding());
  		return super.visit(node);
  	}
  	
  	public boolean visit(VariableDeclarationFragment node) {
  		if (node.getParent().getNodeType() == ASTNode.FIELD_DECLARATION || node.getInitializer() != null)
  			match(node.getName(), fWriteUsages, node.resolveBinding());
  		return super.visit(node);
  	}
  
  	public boolean visit(PrefixExpression node) {
  		PrefixExpression.Operator operator= node.getOperator();	
  		if (operator == Operator.INCREMENT || operator == Operator.DECREMENT) {
  			Expression operand= node.getOperand();
  			SimpleName name= getSimpleName(operand);
  			if (name != null) 
  				match(name, fWriteUsages, name.resolveBinding());				
  		}
  		return super.visit(node);
  	}
  
  	public boolean visit(PostfixExpression node) {
  		Expression operand= node.getOperand();
  		SimpleName name= getSimpleName(operand);
  		if (name != null) 
  			match(name, fWriteUsages, name.resolveBinding());
  		return super.visit(node);
  	}
  	
  	private boolean match(Name node, List result, IBinding binding) {
  		if (binding != null && Bindings.equals(getBindingDeclaration(binding), fTarget)) {
  			result.add(node);
  			return true;
  		}
  		return false;
  	}
  	
  	private boolean matchStaticImport(Name node, List result, IFunctionBinding binding) {
  		if (binding == null || node == null || !(fTarget instanceof IFunctionBinding) || !Modifier.isStatic(binding.getModifiers()))
  			return false;
  		
  		IFunctionBinding targetMethodBinding= (IFunctionBinding)fTarget;
  		if ((fTargetIsStaticMethodImport || Modifier.isStatic(targetMethodBinding.getModifiers())) && (targetMethodBinding.getDeclaringClass().getTypeDeclaration() == binding.getDeclaringClass().getTypeDeclaration())) {
  			if (node.getFullyQualifiedName().equals(targetMethodBinding.getName())) {
  				result.add(node);
  				return true;
  			}
  		}
  		return false;
  	}
  
  	private SimpleName getSimpleName(Expression expression) {
  		if (expression instanceof SimpleName)
  			return ((SimpleName)expression);
  		else if (expression instanceof QualifiedName)
  			return (((QualifiedName) expression).getName());
  		else if (expression instanceof FieldAccess)
  			return ((FieldAccess)expression).getName();
  		return null;
  	}
  	
  	private IBinding getBindingDeclaration(IBinding binding) {
  		switch (binding.getKind()) {
  			case IBinding.TYPE :
  				return ((ITypeBinding)binding).getTypeDeclaration();
  			case IBinding.METHOD :
  				return ((IFunctionBinding)binding).getMethodDeclaration();
  			case IBinding.VARIABLE :
  				return ((IVariableBinding)binding).getVariableDeclaration();
  			default:
  				return binding;
  		}
  	}
  }
  