blob: e61570812525022ec3bb09e8e8d5b59cde539e9f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2012 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.jdt.internal.compiler.apt.dispatch;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import org.eclipse.jdt.internal.compiler.ASTVisitor;
import org.eclipse.jdt.internal.compiler.apt.model.ElementImpl;
import org.eclipse.jdt.internal.compiler.apt.model.Factory;
import org.eclipse.jdt.internal.compiler.apt.util.ManyToMany;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
import org.eclipse.jdt.internal.compiler.lookup.AptSourceLocalVariableBinding;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
/**
* This class is used to visit the JDT compiler internal AST to discover annotations,
* in the course of dispatching to annotation processors.
*/
public class AnnotationDiscoveryVisitor extends ASTVisitor {
final BaseProcessingEnvImpl _env;
final Factory _factory;
/**
* Collects a many-to-many map of annotation types to
* the elements they appear on.
*/
final ManyToMany<TypeElement, Element> _annoToElement;
public AnnotationDiscoveryVisitor(BaseProcessingEnvImpl env) {
_env = env;
_factory = env.getFactory();
_annoToElement = new ManyToMany<TypeElement, Element>();
}
@Override
public boolean visit(Argument argument, BlockScope scope) {
Annotation[] annotations = argument.annotations;
ReferenceContext referenceContext = scope.referenceContext();
if (referenceContext instanceof AbstractMethodDeclaration) {
MethodBinding binding = ((AbstractMethodDeclaration) referenceContext).binding;
if (binding != null) {
TypeDeclaration typeDeclaration = scope.referenceType();
typeDeclaration.binding.resolveTypesFor(binding);
if (argument.binding != null) {
argument.binding = new AptSourceLocalVariableBinding(argument.binding, binding);
}
}
if (annotations != null) {
this.resolveAnnotations(
scope,
annotations,
argument.binding);
}
}
return false;
}
@Override
public boolean visit(ConstructorDeclaration constructorDeclaration, ClassScope scope) {
Annotation[] annotations = constructorDeclaration.annotations;
if (annotations != null) {
MethodBinding constructorBinding = constructorDeclaration.binding;
if (constructorBinding == null) {
return false;
}
((SourceTypeBinding) constructorBinding.declaringClass).resolveTypesFor(constructorBinding);
this.resolveAnnotations(
constructorDeclaration.scope,
annotations,
constructorBinding);
}
TypeParameter[] typeParameters = constructorDeclaration.typeParameters;
if (typeParameters != null) {
int typeParametersLength = typeParameters.length;
for (int i = 0; i < typeParametersLength; i++) {
typeParameters[i].traverse(this, constructorDeclaration.scope);
}
}
Argument[] arguments = constructorDeclaration.arguments;
if (arguments != null) {
int argumentLength = arguments.length;
for (int i = 0; i < argumentLength; i++) {
arguments[i].traverse(this, constructorDeclaration.scope);
}
}
return false;
}
@Override
public boolean visit(FieldDeclaration fieldDeclaration, MethodScope scope) {
Annotation[] annotations = fieldDeclaration.annotations;
if (annotations != null) {
FieldBinding fieldBinding = fieldDeclaration.binding;
if (fieldBinding == null) {
return false;
}
((SourceTypeBinding) fieldBinding.declaringClass).resolveTypeFor(fieldBinding);
this.resolveAnnotations(scope, annotations, fieldBinding);
}
return false;
}
@Override
public boolean visit(TypeParameter typeParameter, ClassScope scope) {
Annotation[] annotations = typeParameter.annotations;
if (annotations != null) {
TypeVariableBinding binding = typeParameter.binding;
if (binding == null) {
return false;
}
this.resolveAnnotations(scope.referenceContext.initializerScope, annotations, binding);
}
return false;
}
@Override
public boolean visit(TypeParameter typeParameter, BlockScope scope) {
Annotation[] annotations = typeParameter.annotations;
if (annotations != null) {
TypeVariableBinding binding = typeParameter.binding;
if (binding == null) {
return false;
}
// when we get here, it is guaranteed that class type parameters are connected, but method type parameters may not be.
MethodBinding methodBinding = (MethodBinding) binding.declaringElement;
((SourceTypeBinding) methodBinding.declaringClass).resolveTypesFor(methodBinding);
this.resolveAnnotations(scope, annotations, binding);
}
return false;
}
@Override
public boolean visit(MethodDeclaration methodDeclaration, ClassScope scope) {
Annotation[] annotations = methodDeclaration.annotations;
if (annotations != null) {
MethodBinding methodBinding = methodDeclaration.binding;
if (methodBinding == null) {
return false;
}
((SourceTypeBinding) methodBinding.declaringClass).resolveTypesFor(methodBinding);
this.resolveAnnotations(
methodDeclaration.scope,
annotations,
methodBinding);
}
TypeParameter[] typeParameters = methodDeclaration.typeParameters;
if (typeParameters != null) {
int typeParametersLength = typeParameters.length;
for (int i = 0; i < typeParametersLength; i++) {
typeParameters[i].traverse(this, methodDeclaration.scope);
}
}
Argument[] arguments = methodDeclaration.arguments;
if (arguments != null) {
int argumentLength = arguments.length;
for (int i = 0; i < argumentLength; i++) {
arguments[i].traverse(this, methodDeclaration.scope);
}
}
return false;
}
@Override
public boolean visit(TypeDeclaration memberTypeDeclaration, ClassScope scope) {
SourceTypeBinding binding = memberTypeDeclaration.binding;
if (binding == null) {
return false;
}
Annotation[] annotations = memberTypeDeclaration.annotations;
if (annotations != null) {
this.resolveAnnotations(
memberTypeDeclaration.staticInitializerScope,
annotations,
binding);
}
return true;
}
@Override
public boolean visit(TypeDeclaration typeDeclaration, CompilationUnitScope scope) {
SourceTypeBinding binding = typeDeclaration.binding;
if (binding == null) {
return false;
}
Annotation[] annotations = typeDeclaration.annotations;
if (annotations != null) {
this.resolveAnnotations(
typeDeclaration.staticInitializerScope,
annotations,
binding);
}
return true;
}
private void resolveAnnotations(BlockScope scope, Annotation[] annotations, Binding currentBinding) {
int length = annotations == null ? 0 : annotations.length;
if (length == 0)
return;
boolean old = scope.insideTypeAnnotation;
scope.insideTypeAnnotation = true;
ASTNode.resolveAnnotations(scope, annotations, currentBinding);
scope.insideTypeAnnotation = old;
ElementImpl element = (ElementImpl) _factory.newElement(currentBinding);
AnnotationBinding [] annotationBindings = element.getPackedAnnotationBindings(); // discovery is never in terms of repeating annotation.
for (AnnotationBinding binding : annotationBindings) {
if (binding != null) { // binding should be resolved, but in case it's not, ignore it: it could have been wrapped into a container.
TypeElement anno = (TypeElement)_factory.newElement(binding.getAnnotationType());
_annoToElement.put(anno, element);
}
}
}
}