blob: 5832cabff61281bd2754f45b48b1c0db56b2ad06 [file] [log] [blame]
/*********************************************************************
* Copyright (c) 2009, 2012 SpringSource, a division of VMware, Inc.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
**********************************************************************/
package org.eclipse.virgo.ide.bundlor.jdt.core;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import org.apache.commons.lang.StringUtils;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
import org.eclipse.jdt.core.dom.CastExpression;
import org.eclipse.jdt.core.dom.CatchClause;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.EnumDeclaration;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.IPackageBinding;
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.InstanceofExpression;
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.TypeDeclaration;
import org.eclipse.virgo.bundlor.support.partialmanifest.PartialManifest;
/**
* {@link MethodVisitor} that is based on JDT's AST
* </p>
* <strong>Concurrent Semantics</strong><br />
* Not thread safe.
*
* @author Christian Dupuis
* @author Glyn Normington
*/
public class ArtifactAnalyserTypeVisitor extends ASTVisitor {
private final PartialManifest partialManifest;
private final Set<String> referencedTypes = new HashSet<String>();
private String typePackage;
public ArtifactAnalyserTypeVisitor(PartialManifest model) {
this.partialManifest = model;
}
/**
* {@inheritDoc}
*/
@Override
public boolean visit(CastExpression node) {
recordTypeBinding(node.resolveTypeBinding());
return true;
}
/**
* {@inheritDoc}
*/
@Override
public boolean visit(CatchClause node) {
recordTypeBinding(node.getException().getType().resolveBinding());
return true;
}
/**
* {@inheritDoc}
*/
@Override
public boolean visit(ClassInstanceCreation node) {
recordTypeBinding(node.resolveTypeBinding());
return true;
}
/**
* {@inheritDoc}
*/
@Override
public boolean visit(FieldDeclaration node) {
recordTypeBinding(node.getType().resolveBinding());
return true;
}
/**
* {@inheritDoc}
*/
@Override
public boolean visit(ImportDeclaration node) {
if (node.resolveBinding() != null && !node.resolveBinding().isRecovered()) {
IBinding binding = node.resolveBinding();
if (binding instanceof IPackageBinding) {
this.partialManifest.recordReferencedPackage(((IPackageBinding) binding).getName());
} else if (binding instanceof ITypeBinding) {
recordTypeBinding((ITypeBinding) binding);
}
} else {
if (!node.isOnDemand() && !node.isStatic()) {
Name importElementName = node.getName();
recordFullyQualifiedName(importElementName.getFullyQualifiedName());
} else if (node.isOnDemand() && !node.isStatic()) {
Name importElementName = node.getName();
this.partialManifest.recordReferencedPackage(importElementName.getFullyQualifiedName());
} else if (!node.isOnDemand() && node.isStatic()) {
Name importElementName = node.getName();
String fqn = importElementName.getFullyQualifiedName().substring(0, importElementName.getFullyQualifiedName().lastIndexOf('.'));
recordFullyQualifiedName(fqn);
}
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public boolean visit(InstanceofExpression node) {
recordTypeBinding(node.getRightOperand().resolveBinding());
return true;
}
/**
* {@inheritDoc}
*/
@Override
public boolean visit(MarkerAnnotation node) {
ITypeBinding binding = node.resolveTypeBinding();
recordTypeBinding(binding);
this.partialManifest.recordUsesPackage(this.typePackage, getPackageBinding(binding).getName());
return true;
}
/**
* {@inheritDoc}
*/
@Override
public boolean visit(MethodDeclaration node) {
IMethodBinding binding = node.resolveBinding();
if (binding == null) {
return true;
}
// return value
if (binding.getReturnType() != null) {
String fqn = binding.getReturnType().getQualifiedName();
if (!"void".equals(fqn)) {
recordTypeBinding(binding.getReturnType());
if (!Modifier.isPrivate(node.getModifiers())) {
IPackageBinding packageBinding = getPackageBinding(binding.getReturnType());
if (packageBinding != null) {
this.partialManifest.recordUsesPackage(this.typePackage, packageBinding.getName());
}
}
}
}
// throws clause
if (binding.getExceptionTypes() != null) {
for (ITypeBinding exceptionBinding : binding.getExceptionTypes()) {
recordTypeBinding(exceptionBinding);
if (!Modifier.isPrivate(node.getModifiers())) {
IPackageBinding packageBinding = getPackageBinding(exceptionBinding);
if (packageBinding != null) {
this.partialManifest.recordUsesPackage(this.typePackage, packageBinding.getName());
}
}
}
}
// parameter
if (binding.getParameterTypes() != null) {
for (ITypeBinding parameterBinding : binding.getParameterTypes()) {
recordTypeBinding(parameterBinding);
if (!Modifier.isPrivate(node.getModifiers())) {
IPackageBinding packageBinding = getPackageBinding(parameterBinding);
if (packageBinding != null) {
this.partialManifest.recordUsesPackage(this.typePackage, packageBinding.getName());
}
}
}
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
public boolean visit(MethodInvocation node) {
IMethodBinding binding = node.resolveMethodBinding();
if (binding != null) {
recordTypeBinding(binding.getDeclaringClass());
// return value
if (binding.getReturnType() != null) {
recordTypeBinding(binding.getReturnType());
}
// parameter
if (binding.getParameterTypes() != null) {
for (ITypeBinding parameterBinding : binding.getParameterTypes()) {
recordTypeBinding(parameterBinding);
}
}
} else {
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
public boolean visit(NormalAnnotation node) {
ITypeBinding binding = node.resolveTypeBinding();
recordTypeBinding(binding);
this.partialManifest.recordUsesPackage(this.typePackage, getPackageBinding(binding).getName());
return true;
}
/**
* {@inheritDoc}
*/
@Override
public boolean visit(QualifiedName node) {
IBinding binding = node.resolveBinding();
if (isValid(binding)) {
// Record the declared type.
recordTypeBinding(node.resolveTypeBinding());
// Record the declaring type.
if (binding.getKind() == IBinding.VARIABLE) {
recordTypeBinding(((IVariableBinding) binding).getDeclaringClass());
}
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
public boolean visit(SimpleName node) {
String fqn = node.getFullyQualifiedName();
recordFullyQualifiedName(fqn);
return true;
}
/**
* It is important to record the type binding only if it is not recovered. Recovered bindings occur for invalid
* source code and incomplete class paths.
*
* @param binding the binding which may be recorded
* @return <code>true</code> if and only if the binding should be recorded
*/
private boolean isValid(IBinding binding) {
return binding != null && !binding.isRecovered();
}
/**
* {@inheritDoc}
*/
@Override
public boolean visit(SimpleType node) {
if (isValid(node.resolveBinding())) {
recordTypeBinding(node.resolveBinding());
} else {
Name simpleName = node.getName();
String fqn = simpleName.getFullyQualifiedName();
recordFullyQualifiedName(fqn);
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
public boolean visit(SingleMemberAnnotation node) {
ITypeBinding binding = node.resolveTypeBinding();
recordTypeBinding(binding);
this.partialManifest.recordUsesPackage(this.typePackage, getPackageBinding(binding).getName());
return true;
}
/**
* {@inheritDoc}
*/
@Override
public boolean visit(TypeDeclaration node) {
ITypeBinding binding = node.resolveBinding();
if (binding == null) {
return false;
}
String name = binding.getBinaryName();
this.typePackage = getPackageBinding(binding).getName();
if (!node.isLocalTypeDeclaration()) {
this.partialManifest.recordType(name);
}
for (String referencedType : this.referencedTypes) {
recordFullyQualifiedName(referencedType);
}
if (node.getSuperclassType() != null) {
ITypeBinding superClassBinding = node.getSuperclassType().resolveBinding();
recordTypeBinding(superClassBinding);
this.partialManifest.recordUsesPackage(this.typePackage, getPackageBinding(superClassBinding).getName());
}
for (int i = 0; i < node.superInterfaceTypes().size(); i++) {
org.eclipse.jdt.core.dom.Type interfaceType = (org.eclipse.jdt.core.dom.Type) node.superInterfaceTypes().get(i);
ITypeBinding interfaceBinding = interfaceType.resolveBinding();
recordTypeBinding(interfaceBinding);
this.partialManifest.recordUsesPackage(this.typePackage, getPackageBinding(interfaceBinding).getName());
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
public boolean visit(AnonymousClassDeclaration node) {
ITypeBinding binding = node.resolveBinding();
if (binding == null) {
return false;
}
this.partialManifest.recordType(binding.getBinaryName());
ITypeBinding superClassBinding = binding.getSuperclass();
if (superClassBinding != null) {
recordTypeBinding(superClassBinding);
this.partialManifest.recordUsesPackage(this.typePackage, getPackageBinding(superClassBinding).getName());
}
ITypeBinding[] interfaceBindings = binding.getInterfaces();
if (interfaceBindings != null) {
for (ITypeBinding interfaceBinding : interfaceBindings) {
recordTypeBinding(interfaceBinding);
this.partialManifest.recordUsesPackage(this.typePackage, getPackageBinding(interfaceBinding).getName());
}
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
public boolean visit(AnnotationTypeDeclaration node) {
ITypeBinding binding = node.resolveBinding();
if (binding == null) {
return false;
}
String name = binding.getBinaryName();
this.typePackage = getPackageBinding(binding).getName();
this.partialManifest.recordType(name);
return true;
}
/**
* {@inheritDoc}
*/
@Override
public boolean visit(EnumDeclaration node) {
ITypeBinding binding = node.resolveBinding();
if (binding == null) {
return false;
}
String name = binding.getBinaryName();
this.typePackage = getPackageBinding(binding).getName();
this.partialManifest.recordType(name);
return true;
}
private IPackageBinding getPackageBinding(ITypeBinding binding) {
if (binding != null) {
if (binding.isArray()) {
return getPackageBinding(binding.getComponentType());
} else {
return binding.getPackage();
}
}
return null;
}
private String recordFullyQualifiedName(String fqn) {
if (!"void".equals(fqn)) {
if (this.typePackage == null) {
this.referencedTypes.add(fqn);
return fqn;
} else if (fqn != null && !this.typePackage.equals(getPackageName(fqn))) {
// This is required to get FQCNs of the form
// org.springframework.util.ReflectionUtils.MethodCallback correctly detected
StringTokenizer segments = new StringTokenizer(fqn, ".");
if (segments.countTokens() > 1) {
List<String> newSegments = new ArrayList<String>();
while (segments.hasMoreTokens()) {
String segment = segments.nextToken();
newSegments.add(segment);
if (!Character.isLowerCase(segment.charAt(0))) {
break;
}
}
fqn = StringUtils.join(newSegments, ".");
}
this.partialManifest.recordReferencedType(fqn);
return fqn;
}
}
return "";
}
private String getPackageName(String fullyQualifiedTypeName) {
if (fullyQualifiedTypeName == null) {
return "";
}
int index = fullyQualifiedTypeName.lastIndexOf('.');
if (index > -1) {
return fullyQualifiedTypeName.substring(0, index);
}
return "";
}
private String recordTypeBinding(ITypeBinding binding) {
if (binding != null) {
if (binding.isArray()) {
return recordTypeBinding(binding.getComponentType());
} else {
String fqn = binding.getBinaryName();
return recordFullyQualifiedName(fqn);
}
}
return "";
}
}