package net.atos.optimus.common.tools.ltk;
import java.util.ArrayList;
import java.util.List;
import net.atos.optimus.common.tools.jdt.jstcomp.ASTThrownExceptionsHelper;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.Annotation;
import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.ImportDeclaration;
import org.eclipse.jdt.core.dom.MarkerAnnotation;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
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.QualifiedName;
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.StructuralPropertyDescriptor;
/**
* Implementation of AST visitor that parses source code and refactors it to
* puts fully qualified names everywhere & remove the imports.
*
* @author mvanbesien (mvaawl@gmail.com)
*
*/
public class ImportsRemovalVisitor extends ASTVisitor {
/*
* To prevent external instanciation. Use apply method instead
*/
private ImportsRemovalVisitor() {
}
/**
* True if source code has been modified here
*/
private boolean modified = false;
/**
* Instanciates a visitor and executes it against the provided compilation
* unit.
*
* @param compilationUnit
* @return true if source has been modified during process.
*/
public static boolean apply(CompilationUnit compilationUnit) {
ImportsRemovalVisitor importsRemovalVisitor = new ImportsRemovalVisitor();
compilationUnit.accept(importsRemovalVisitor);
return importsRemovalVisitor.modified;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.
* CompilationUnit)
*/
@Override
public boolean visit(CompilationUnit node) {
// We assume there is nothing to do if there are no imports in the
// source code...
return node.imports().size() > 0;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.SimpleType
* )
*/
@Override
public boolean visit(SimpleType node) {
ITypeBinding resolvedBinding = node.resolveBinding();
if (resolvedBinding == null) {
return true;
}
String qualifiedName = null;
if (resolvedBinding.isParameterizedType()) {
ITypeBinding erasure = resolvedBinding.getErasure();
if (erasure != null && !erasure.isRecovered()) {
qualifiedName = erasure.getQualifiedName();
}
} else if (!resolvedBinding.isRecovered()) {
qualifiedName = resolvedBinding.getQualifiedName();
}
if (qualifiedName != null) {
node.setName(node.getAST().newName(qualifiedName));
this.modified = true;
}
return true;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.
* MethodInvocation)
*/
@Override
public boolean visit(MethodInvocation node) {
// For the static methods invocation
IMethodBinding resolvedMethodBinding = node.resolveMethodBinding();
if (resolvedMethodBinding != null && !resolvedMethodBinding.isRecovered()
&& Modifier.isStatic(resolvedMethodBinding.getModifiers())) {
Expression expression = node.getExpression();
if (expression instanceof SimpleName) {
ITypeBinding typeBinding = expression.resolveTypeBinding();
overrideValueInParent(expression, typeBinding);
}
}
return super.visit(node);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.
* QualifiedName)
*/
@Override
public boolean visit(QualifiedName node) {
// For the static fields & enumeration literals invocation
if (node.resolveBinding() instanceof IVariableBinding) {
IVariableBinding binding = (IVariableBinding) node.resolveBinding();
if (!binding.isRecovered() && Modifier.isStatic(binding.getModifiers())) {
if (node.getQualifier() instanceof SimpleName) {
ITypeBinding typeBinding = node.getQualifier().resolveTypeBinding();
Name qualifier = node.getQualifier();
overrideValueInParent(qualifier, typeBinding);
}
}
}
return super.visit(node);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.
* MarkerAnnotation)
*/
@Override
public boolean visit(MarkerAnnotation node) {
return visitAnnotation(node);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.
* SingleMemberAnnotation)
*/
@Override
public boolean visit(SingleMemberAnnotation node) {
return visitAnnotation(node);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.
* NormalAnnotation)
*/
@Override
public boolean visit(NormalAnnotation node) {
return visitAnnotation(node);
}
/**
* Processes the annotation to replace its name by FQN
*
* @param node
* @return
*/
private boolean visitAnnotation(Annotation node) {
if (node.getTypeName().isSimpleName()) {
ITypeBinding resolvedBinding = node.resolveTypeBinding();
if (resolvedBinding != null) {
String qualifiedName = null;
if (resolvedBinding.isParameterizedType()) {
ITypeBinding erasure = resolvedBinding.getErasure();
if (erasure != null && !erasure.isRecovered()) {
qualifiedName = erasure.getQualifiedName();
}
} else if (!resolvedBinding.isRecovered()) {
qualifiedName = resolvedBinding.getQualifiedName();
}
if (qualifiedName != null) {
node.setTypeName(node.getAST().newName(qualifiedName));
this.modified = true;
}
}
}
return true;
}
/**
* This method is called when it is necessary to remove a name from a parent
* AST node.
*
* @param node
* @param typeBinding
*/
@SuppressWarnings("unchecked")
private void overrideValueInParent(ASTNode node, ITypeBinding typeBinding) {
String qualifiedName = null;
if (typeBinding.isParameterizedType()) {
ITypeBinding erasure = typeBinding.getErasure();
if (erasure != null && !erasure.isRecovered()) {
qualifiedName = erasure.getQualifiedName();
}
} else if (!typeBinding.isRecovered()) {
qualifiedName = typeBinding.getQualifiedName();
}
if (qualifiedName != null) {
StructuralPropertyDescriptor locationInParent = node.getLocationInParent();
if (locationInParent.isChildListProperty()) {
ChildListPropertyDescriptor clpd = (ChildListPropertyDescriptor) locationInParent;
List<ASTNode> astNodes = (List<ASTNode>) node.getParent().getStructuralProperty(clpd);
try {
int index = astNodes.indexOf(node);
astNodes.remove(node);
astNodes.add(index, node.getAST().newName(qualifiedName));
} catch (Exception e) {
e.printStackTrace();
}
} else {
try {
node.getParent().setStructuralProperty(locationInParent, node.getAST().newName(qualifiedName));
} catch (Exception e) {
e.printStackTrace();
}
}
this.modified = true;
}
}
@Override
public boolean visit(MethodDeclaration node) {
List<Name> thrownExceptions = new ArrayList<Name>();
thrownExceptions.addAll(ASTThrownExceptionsHelper.getThrownExceptionNames(node));
for (Name o : thrownExceptions) {
if (o instanceof SimpleName) {
SimpleName name = (SimpleName) o;
overrideValueInParent(name, name.resolveTypeBinding());
}
}
return true;
}
/*
* (non-Javadoc)
*
* @see
* org.eclipse.jdt.core.dom.ASTVisitor#endVisit(org.eclipse.jdt.core.dom
* .CompilationUnit)
*/
@SuppressWarnings("unchecked")
@Override
public void endVisit(CompilationUnit node) {
List<ImportDeclaration> toRemove = new ArrayList<ImportDeclaration>();
for (Object o : node.imports()) {
ImportDeclaration declaration = (ImportDeclaration) o;
IBinding resolvedBinding = declaration.resolveBinding();
if (resolvedBinding != null && !resolvedBinding.isRecovered()) {
toRemove.add(declaration);
}
}
// To clean the imports from the source code.
node.imports().removeAll(toRemove);
}
}