blob: 2ef03e1a630987b0080ee61a2b99159cdda97eb2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2014 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
* IBM Corporation - Fix for bug 328575
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.apt.dispatch;
import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.ElementFilter;
import org.eclipse.jdt.internal.compiler.apt.model.Factory;
import org.eclipse.jdt.internal.compiler.apt.model.TypeElementImpl;
import org.eclipse.jdt.internal.compiler.apt.util.ManyToMany;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.lookup.AnnotationBinding;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TagBits;
public class RoundEnvImpl implements RoundEnvironment
{
private final BaseProcessingEnvImpl _processingEnv;
private final boolean _isLastRound;
private final CompilationUnitDeclaration[] _units;
private final ManyToMany<TypeElement, Element> _annoToUnit;
private final ReferenceBinding[] _binaryTypes;
private final Factory _factory;
private Set<Element> _rootElements = null;
public RoundEnvImpl(CompilationUnitDeclaration[] units, ReferenceBinding[] binaryTypeBindings, boolean isLastRound, BaseProcessingEnvImpl env) {
_processingEnv = env;
_isLastRound = isLastRound;
_units = units;
_factory = _processingEnv.getFactory();
// Discover the annotations that will be passed to Processor.process()
AnnotationDiscoveryVisitor visitor = new AnnotationDiscoveryVisitor(_processingEnv);
if (_units != null) {
for (CompilationUnitDeclaration unit : _units) {
unit.scope.suppressImportErrors = true;
unit.traverse(visitor, unit.scope);
unit.scope.suppressImportErrors = false;
}
}
_annoToUnit = visitor._annoToElement;
if (binaryTypeBindings != null) collectAnnotations(binaryTypeBindings);
_binaryTypes = binaryTypeBindings;
}
private void collectAnnotations(ReferenceBinding[] referenceBindings) {
for (ReferenceBinding referenceBinding : referenceBindings) {
// collect all annotations from the binary types
if (referenceBinding instanceof ParameterizedTypeBinding) {
referenceBinding = ((ParameterizedTypeBinding) referenceBinding).genericType();
}
AnnotationBinding[] annotationBindings = Factory.getPackedAnnotationBindings(referenceBinding.getAnnotations());
for (AnnotationBinding annotationBinding : annotationBindings) {
TypeElement anno = (TypeElement)_factory.newElement(annotationBinding.getAnnotationType());
Element element = _factory.newElement(referenceBinding);
_annoToUnit.put(anno, element);
}
FieldBinding[] fieldBindings = referenceBinding.fields();
for (FieldBinding fieldBinding : fieldBindings) {
annotationBindings = Factory.getPackedAnnotationBindings(fieldBinding.getAnnotations());
for (AnnotationBinding annotationBinding : annotationBindings) {
TypeElement anno = (TypeElement)_factory.newElement(annotationBinding.getAnnotationType());
Element element = _factory.newElement(fieldBinding);
_annoToUnit.put(anno, element);
}
}
MethodBinding[] methodBindings = referenceBinding.methods();
for (MethodBinding methodBinding : methodBindings) {
annotationBindings = Factory.getPackedAnnotationBindings(methodBinding.getAnnotations());
for (AnnotationBinding annotationBinding : annotationBindings) {
TypeElement anno = (TypeElement)_factory.newElement(annotationBinding.getAnnotationType());
Element element = _factory.newElement(methodBinding);
_annoToUnit.put(anno, element);
}
}
ReferenceBinding[] memberTypes = referenceBinding.memberTypes();
collectAnnotations(memberTypes);
}
}
/**
* Return the set of annotation types that were discovered on the root elements.
* This does not include inherited annotations, only those directly on the root
* elements.
* @return a set of annotation types, possibly empty.
*/
public Set<TypeElement> getRootAnnotations()
{
return Collections.unmodifiableSet(_annoToUnit.getKeySet());
}
@Override
public boolean errorRaised()
{
return _processingEnv.errorRaised();
}
/**
* From the set of root elements and their enclosed elements, return the subset that are annotated
* with {@code a}. If {@code a} is annotated with the {@link java.lang.annotation.Inherited}
* annotation, include those elements that inherit the annotation from their superclasses.
* Note that {@link java.lang.annotation.Inherited} only applies to classes (i.e. TypeElements).
*/
@Override
public Set<? extends Element> getElementsAnnotatedWith(TypeElement a)
{
if (a.getKind() != ElementKind.ANNOTATION_TYPE) {
throw new IllegalArgumentException("Argument must represent an annotation type"); //$NON-NLS-1$
}
Binding annoBinding = ((TypeElementImpl)a)._binding;
if (0 != (annoBinding.getAnnotationTagBits() & TagBits.AnnotationInherited)) {
Set<Element> annotatedElements = new HashSet<Element>(_annoToUnit.getValues(a));
// For all other root elements that are TypeElements, and for their recursively enclosed
// types, add each element if it has a superclass are annotated with 'a'
ReferenceBinding annoTypeBinding = (ReferenceBinding) annoBinding;
for (TypeElement element : ElementFilter.typesIn(getRootElements())) {
ReferenceBinding typeBinding = (ReferenceBinding)((TypeElementImpl)element)._binding;
addAnnotatedElements(annoTypeBinding, typeBinding, annotatedElements);
}
return Collections.unmodifiableSet(annotatedElements);
}
return Collections.unmodifiableSet(_annoToUnit.getValues(a));
}
/**
* For every type in types that is a class and that is annotated with anno, either directly or by inheritance,
* add that type to result. Recursively descend on each types's child classes as well.
* @param anno the compiler binding for an annotation type
* @param type a type, not necessarily a class
* @param result must be a modifiable Set; will accumulate annotated classes
*/
private void addAnnotatedElements(ReferenceBinding anno, ReferenceBinding type, Set<Element> result) {
if (type.isClass()) {
if (inheritsAnno(type, anno)) {
result.add(_factory.newElement(type));
}
}
for (ReferenceBinding element : type.memberTypes()) {
addAnnotatedElements(anno, element, result);
}
}
/**
* Check whether an element has a superclass that is annotated with an @Inherited annotation.
* @param element must be a class (not an interface, enum, etc.).
* @param anno must be an annotation type, and must be @Inherited
* @return true if element has a superclass that is annotated with anno
*/
private boolean inheritsAnno(ReferenceBinding element, ReferenceBinding anno) {
ReferenceBinding searchedElement = element;
do {
if (searchedElement instanceof ParameterizedTypeBinding) {
searchedElement = ((ParameterizedTypeBinding) searchedElement).genericType();
}
AnnotationBinding[] annos = Factory.getPackedAnnotationBindings(searchedElement.getAnnotations());
for (AnnotationBinding annoBinding : annos) {
if (annoBinding.getAnnotationType() == anno) { //$IDENTITY-COMPARISON$
// element is annotated with anno
return true;
}
}
} while (null != (searchedElement = searchedElement.superclass()));
return false;
}
@Override
public Set<? extends Element> getElementsAnnotatedWith(Class<? extends Annotation> a)
{
String canonicalName = a.getCanonicalName();
if (canonicalName == null) {
// null for anonymous and local classes or an array of those
throw new IllegalArgumentException("Argument must represent an annotation type"); //$NON-NLS-1$
}
TypeElement annoType = _processingEnv.getElementUtils().getTypeElement(canonicalName);
return getElementsAnnotatedWith(annoType);
}
@Override
public Set<? extends Element> getRootElements()
{
if (_units == null) {
return Collections.emptySet();
}
if (_rootElements == null) {
Set<Element> elements = new HashSet<Element>(_units.length);
for (CompilationUnitDeclaration unit : _units) {
if (null == unit.scope || null == unit.scope.topLevelTypes)
continue;
for (SourceTypeBinding binding : unit.scope.topLevelTypes) {
Element element = _factory.newElement(binding);
if (null == element) {
throw new IllegalArgumentException("Top-level type binding could not be converted to element: " + binding); //$NON-NLS-1$
}
elements.add(element);
}
}
if (this._binaryTypes != null) {
for (ReferenceBinding typeBinding : _binaryTypes) {
Element element = _factory.newElement(typeBinding);
if (null == element) {
throw new IllegalArgumentException("Top-level type binding could not be converted to element: " + typeBinding); //$NON-NLS-1$
}
elements.add(element);
}
}
_rootElements = elements;
}
return _rootElements;
}
@Override
public boolean processingOver()
{
return _isLastRound;
}
}