blob: 322717774437cab75998dba0417963565c94391a [file] [log] [blame]
/*******************************************************************************
* 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.codemanipulation;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.eclipse.jface.text.Region;
import org.eclipse.wst.jsdt.core.IJavaScriptProject;
import org.eclipse.wst.jsdt.core.dom.AST;
import org.eclipse.wst.jsdt.core.dom.ASTNode;
import org.eclipse.wst.jsdt.core.dom.ArrayType;
import org.eclipse.wst.jsdt.core.dom.ClassInstanceCreation;
import org.eclipse.wst.jsdt.core.dom.Expression;
import org.eclipse.wst.jsdt.core.dom.FieldAccess;
import org.eclipse.wst.jsdt.core.dom.FunctionDeclaration;
import org.eclipse.wst.jsdt.core.dom.FunctionInvocation;
import org.eclipse.wst.jsdt.core.dom.FunctionRef;
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.JavaScriptUnit;
import org.eclipse.wst.jsdt.core.dom.MemberRef;
import org.eclipse.wst.jsdt.core.dom.Modifier;
import org.eclipse.wst.jsdt.core.dom.Name;
import org.eclipse.wst.jsdt.core.dom.PackageDeclaration;
import org.eclipse.wst.jsdt.core.dom.QualifiedName;
import org.eclipse.wst.jsdt.core.dom.QualifiedType;
import org.eclipse.wst.jsdt.core.dom.SimpleName;
import org.eclipse.wst.jsdt.core.dom.SimpleType;
import org.eclipse.wst.jsdt.core.dom.SuperConstructorInvocation;
import org.eclipse.wst.jsdt.core.dom.TagElement;
import org.eclipse.wst.jsdt.core.dom.ThisExpression;
import org.eclipse.wst.jsdt.core.dom.TypeDeclaration;
import org.eclipse.wst.jsdt.internal.corext.dom.GenericVisitor;
import org.eclipse.wst.jsdt.internal.corext.dom.ScopeAnalyzer;
import org.eclipse.wst.jsdt.internal.corext.util.JavaModelUtil;
/**
*
* Provisional API: This class/interface is part of an interim API that is still under development and expected to
* change significantly before reaching stability. It is being made available at this early stage to solicit feedback
* from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken
* (repeatedly) as the API evolves.
*/
public class ImportReferencesCollector extends GenericVisitor {
public static void collect(ASTNode node, IJavaScriptProject project, Region rangeLimit, Collection resultingTypeImports, Collection resultingStaticImports) {
JavaScriptUnit astRoot= (JavaScriptUnit) node.getRoot();
node.accept(new ImportReferencesCollector(project, astRoot, rangeLimit, resultingTypeImports, resultingStaticImports));
}
private JavaScriptUnit fASTRoot;
private Region fSubRange;
private Collection/*<Name>*/ fTypeImports;
private Collection/*<Name>*/ fStaticImports;
private ImportReferencesCollector(IJavaScriptProject project, JavaScriptUnit astRoot, Region rangeLimit, Collection resultingTypeImports, Collection resultingStaticImports) {
super(true);
fTypeImports= resultingTypeImports;
fStaticImports= resultingStaticImports;
fSubRange= rangeLimit;
if (project == null || !JavaModelUtil.is50OrHigher(project)) {
fStaticImports= null; // do not collect
}
fASTRoot= astRoot;
}
public ImportReferencesCollector(IJavaScriptProject project, Region rangeLimit, Collection resultingTypeImports, Collection resultingStaticImports) {
this(project, null, rangeLimit, resultingTypeImports, resultingStaticImports);
}
public JavaScriptUnit getASTRoot(ASTNode node) {
if (fASTRoot == null) {
fASTRoot= (JavaScriptUnit) node.getRoot();
}
return fASTRoot;
}
private boolean isAffected(ASTNode node) {
if (fSubRange == null) {
return true;
}
int nodeStart= node.getStartPosition();
int offset= fSubRange.getOffset();
return nodeStart + node.getLength() > offset && (offset + fSubRange.getLength()) > nodeStart;
}
private void addReference(SimpleName name) {
if (isAffected(name)) {
fTypeImports.add(name);
}
}
private void typeRefFound(Name node) {
if (node != null) {
while (node.isQualifiedName()) {
node= ((QualifiedName) node).getQualifier();
}
addReference((SimpleName) node);
}
}
private void possibleTypeRefFound(Name node) {
while (node.isQualifiedName()) {
node= ((QualifiedName) node).getQualifier();
}
IBinding binding= node.resolveBinding();
if (binding == null || binding.getKind() == IBinding.TYPE) {
// if the binding is null, we cannot determine if
// we have a type binding or not, so we will assume
// we do.
addReference((SimpleName) node);
}
}
private void possibleStaticImportFound(Name name) {
if (fStaticImports == null) {
return;
}
while (name.isQualifiedName()) {
name= ((QualifiedName) name).getQualifier();
}
if (!isAffected(name)) {
return;
}
IBinding binding= name.resolveBinding();
if (binding == null || binding instanceof ITypeBinding || !Modifier.isStatic(binding.getModifiers()) || ((SimpleName) name).isDeclaration()) {
return;
}
if (binding instanceof IVariableBinding) {
IVariableBinding varBinding= (IVariableBinding) binding;
if (varBinding.isField()) {
varBinding= varBinding.getVariableDeclaration();
ITypeBinding declaringClass= varBinding.getDeclaringClass();
if (declaringClass != null && !declaringClass.isLocal()) {
if (new ScopeAnalyzer(getASTRoot(name)).isDeclaredInScope(varBinding, (SimpleName)name, ScopeAnalyzer.VARIABLES | ScopeAnalyzer.CHECK_VISIBILITY))
return;
fStaticImports.add(name);
}
}
} else if (binding instanceof IFunctionBinding) {
IFunctionBinding methodBinding= ((IFunctionBinding) binding).getMethodDeclaration();
ITypeBinding declaringClass= methodBinding.getDeclaringClass();
if (declaringClass != null && !declaringClass.isLocal()) {
if (new ScopeAnalyzer(getASTRoot(name)).isDeclaredInScope(methodBinding, (SimpleName)name, ScopeAnalyzer.METHODS | ScopeAnalyzer.CHECK_VISIBILITY))
return;
fStaticImports.add(name);
}
}
}
private void doVisitChildren(List elements) {
int nElements= elements.size();
for (int i= 0; i < nElements; i++) {
((ASTNode) elements.get(i)).accept(this);
}
}
private void doVisitNode(ASTNode node) {
if (node != null) {
node.accept(this);
}
}
/* (non-Javadoc)
* @see org.eclipse.wst.jsdt.internal.corext.dom.GenericVisitor#visitNode(org.eclipse.wst.jsdt.core.dom.ASTNode)
*/
protected boolean visitNode(ASTNode node) {
return isAffected(node);
}
/*
* @see ASTVisitor#visit(ArrayType)
*/
public boolean visit(ArrayType node) {
doVisitNode(node.getElementType());
return false;
}
/*
* @see ASTVisitor#visit(SimpleType)
*/
public boolean visit(SimpleType node) {
typeRefFound(node.getName());
return false;
}
/*
* @see ASTVisitor#visit(QualifiedType)
*/
public boolean visit(QualifiedType node) {
// nothing to do here, let the qualifier be visited
return true;
}
/*
* @see ASTVisitor#visit(QualifiedName)
*/
public boolean visit(QualifiedName node) {
possibleTypeRefFound(node); // possible ref
possibleStaticImportFound(node);
return false;
}
/*
* @see ASTVisitor#visit(ImportDeclaration)
*/
public boolean visit(ImportDeclaration node) {
return false;
}
/*
* @see ASTVisitor#visit(PackageDeclaration)
*/
public boolean visit(PackageDeclaration node) {
if (node.getAST().apiLevel() >= AST.JLS3) {
doVisitChildren(node.annotations());
}
return false;
}
/*
* @see ASTVisitor#visit(ThisExpression)
*/
public boolean visit(ThisExpression node) {
typeRefFound(node.getQualifier());
return false;
}
private void evalQualifyingExpression(Expression expr, Name selector) {
if (expr != null) {
if (expr instanceof Name) {
Name name= (Name) expr;
possibleTypeRefFound(name);
possibleStaticImportFound(name);
} else {
expr.accept(this);
}
} else if (selector != null) {
possibleStaticImportFound(selector);
}
}
/*
* @see ASTVisitor#visit(ClassInstanceCreation)
*/
public boolean visit(ClassInstanceCreation node) {
doVisitChildren(node.typeArguments());
doVisitNode(node.getType());
evalQualifyingExpression(node.getExpression(), null);
if (node.getAnonymousClassDeclaration() != null) {
node.getAnonymousClassDeclaration().accept(this);
}
doVisitChildren(node.arguments());
return false;
}
/*
* @see ASTVisitor#endVisit(FunctionInvocation)
*/
public boolean visit(FunctionInvocation node) {
evalQualifyingExpression(node.getExpression(), node.getName());
doVisitChildren(node.typeArguments());
doVisitChildren(node.arguments());
return false;
}
/*
* @see ASTVisitor#visit(SuperConstructorInvocation)
*/
public boolean visit(SuperConstructorInvocation node) {
if (!isAffected(node)) {
return false;
}
evalQualifyingExpression(node.getExpression(), null);
doVisitChildren(node.typeArguments());
doVisitChildren(node.arguments());
return false;
}
/*
* @see ASTVisitor#visit(FieldAccess)
*/
public boolean visit(FieldAccess node) {
evalQualifyingExpression(node.getExpression(), node.getName());
return false;
}
/*
* @see ASTVisitor#visit(SimpleName)
*/
public boolean visit(SimpleName node) {
// if the call gets here, it can only be a variable reference
possibleStaticImportFound(node);
return false;
}
/*
* @see ASTVisitor#visit(TypeDeclaration)
*/
public boolean visit(TypeDeclaration node) {
if (!isAffected(node)) {
return false;
}
return true;
}
/*
* @see ASTVisitor#visit(FunctionDeclaration)
*/
public boolean visit(FunctionDeclaration node) {
if (!isAffected(node)) {
return false;
}
doVisitNode(node.getJavadoc());
if (node.getAST().apiLevel() >= AST.JLS3) {
doVisitChildren(node.modifiers());
doVisitChildren(node.typeParameters());
}
if (!node.isConstructor()) {
doVisitNode(node.getReturnType2());
}
doVisitChildren(node.parameters());
Iterator iter=node.thrownExceptions().iterator();
while (iter.hasNext()) {
typeRefFound((Name) iter.next());
}
doVisitNode(node.getBody());
return false;
}
public boolean visit(TagElement node) {
String tagName= node.getTagName();
List list= node.fragments();
int idx= 0;
if (tagName != null && !list.isEmpty()) {
Object first= list.get(0);
if (first instanceof Name) {
if ("@throws".equals(tagName) || "@exception".equals(tagName)) { //$NON-NLS-1$//$NON-NLS-2$
typeRefFound((Name) first);
} else if ("@see".equals(tagName) || "@link".equals(tagName) || "@linkplain".equals(tagName)) { //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$
Name name= (Name) first;
possibleTypeRefFound(name);
}
idx++;
}
}
for (int i= idx; i < list.size(); i++) {
doVisitNode((ASTNode) list.get(i));
}
return false;
}
public boolean visit(MemberRef node) {
Name qualifier= node.getQualifier();
if (qualifier != null) {
typeRefFound(qualifier);
}
return false;
}
public boolean visit(FunctionRef node) {
Name qualifier= node.getQualifier();
if (qualifier != null) {
typeRefFound(qualifier);
}
List list= node.parameters();
if (list != null) {
doVisitChildren(list); // visit FunctionRefParameter with Type
}
return false;
}
}