blob: dfb509060dd001df735cd3f0728d4db73386fc7f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2012 Oracle. 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:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.common.core.internal.resource.java.binary;
import java.util.Hashtable;
import java.util.Vector;
import org.eclipse.jdt.core.IAnnotation;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMemberValuePair;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jpt.common.core.AnnotationProvider;
import org.eclipse.jpt.common.core.JptCommonCorePlugin;
import org.eclipse.jpt.common.core.resource.java.Annotation;
import org.eclipse.jpt.common.core.resource.java.JavaResourceAnnotatedElement;
import org.eclipse.jpt.common.core.resource.java.JavaResourceNode;
import org.eclipse.jpt.common.core.resource.java.NestableAnnotation;
import org.eclipse.jpt.common.core.utility.TextRange;
import org.eclipse.jpt.common.utility.internal.CollectionTools;
import org.eclipse.jpt.common.utility.internal.Transformer;
import org.eclipse.jpt.common.utility.internal.TransformerAdapter;
import org.eclipse.jpt.common.utility.internal.iterables.CompositeIterable;
import org.eclipse.jpt.common.utility.internal.iterables.EmptyListIterable;
import org.eclipse.jpt.common.utility.internal.iterables.ListIterable;
import org.eclipse.jpt.common.utility.internal.iterables.LiveCloneIterable;
import org.eclipse.jpt.common.utility.internal.iterables.LiveCloneListIterable;
import org.eclipse.jpt.common.utility.internal.iterables.TransformationIterable;
/**
* Java binary annotated element
*/
abstract class BinaryAnnotatedElement
extends BinaryNode
implements JavaResourceAnnotatedElement
{
/** JDT annotated element adapter */
final Adapter adapter;
/**
* Annotations keyed by annotation name;
* no duplicates (the Java compiler does not allow duplicate annotations).
*/
private final Hashtable<String, Annotation> annotations = new Hashtable<String, Annotation>();
/**
* Annotation containers keyed by <em>nestable</em> annotation name.
* This is used to store annotations that can be both standalone and nested
* and are moved back and forth between the two.
*/
private final Hashtable<String, AnnotationContainer> annotationContainers = new Hashtable<String, AnnotationContainer>();
/**
* These are built as needed.
*/
private final Hashtable<String, Annotation> nullAnnotationsCache = new Hashtable<String, Annotation>();
// ********** construction/initialization **********
BinaryAnnotatedElement(JavaResourceNode parent, Adapter adapter) {
super(parent);
this.adapter = adapter;
this.initializeAnnotations();
}
private void initializeAnnotations() {
for (IAnnotation annotation : this.getJdtAnnotations()) {
this.addAnnotation(annotation);
}
}
private void addAnnotation(IAnnotation jdtAnnotation) {
String jdtAnnotationName = jdtAnnotation.getElementName();
// check whether the annotation is a valid container annotation first
// because container validations are also valid annotations
// TODO remove container annotations from list of annotations???
if (this.annotationIsValidContainer(jdtAnnotationName)) {
String nestableAnnotationName = this.getAnnotationProvider().getNestableAnnotationName(jdtAnnotationName);
AnnotationContainer container = new AnnotationContainer();
container.initializeFromContainerAnnotation(jdtAnnotation);
this.annotationContainers.put(nestableAnnotationName, container);
}
else if (this.annotationIsValid(jdtAnnotationName)) {
this.annotations.put(jdtAnnotationName, this.buildAnnotation(jdtAnnotation));
}
else if (this.annotationIsValidNestable(jdtAnnotationName)) {
// if we already have an annotation container (because there was a container annotation)
// ignore the standalone nestable annotation
if (this.annotationContainers.get(jdtAnnotationName) == null) {
AnnotationContainer container = new AnnotationContainer();
container.initializeFromStandaloneAnnotation(jdtAnnotation);
this.annotationContainers.put(jdtAnnotationName, container);
}
}
}
private boolean annotationIsValid(String annotationName) {
return CollectionTools.contains(this.getAnnotationProvider().getAnnotationNames(), annotationName);
}
private boolean annotationIsValidContainer(String annotationName) {
return CollectionTools.contains(this.getAnnotationProvider().getContainerAnnotationNames(), annotationName);
}
private boolean annotationIsValidNestable(String annotationName) {
return CollectionTools.contains(this.getAnnotationProvider().getNestableAnnotationNames(), annotationName);
}
// ********** updating **********
@Override
public void update() {
super.update();
this.updateAnnotations();
}
// TODO
private void updateAnnotations() {
throw new UnsupportedOperationException();
}
// ********** annotations **********
public Iterable<Annotation> getAnnotations() {
return new LiveCloneIterable<Annotation>(this.annotations.values());
}
public int getAnnotationsSize() {
return this.annotations.size();
}
public Annotation getAnnotation(String annotationName) {
return this.annotations.get(annotationName);
}
public Annotation getNonNullAnnotation(String annotationName) {
Annotation annotation = this.getAnnotation(annotationName);
return (annotation != null) ? annotation : this.getNullAnnotation(annotationName);
}
private Annotation getNullAnnotation(String annotationName) {
synchronized (this.nullAnnotationsCache) {
Annotation annotation = this.nullAnnotationsCache.get(annotationName);
if (annotation == null) {
annotation = this.buildNullAnnotation(annotationName);
this.nullAnnotationsCache.put(annotationName, annotation);
}
return annotation;
}
}
private Annotation buildNullAnnotation(String annotationName) {
return this.getAnnotationProvider().buildNullAnnotation(this, annotationName);
}
/* CU private */ Annotation buildAnnotation(IAnnotation jdtAnnotation) {
return this.getAnnotationProvider().buildAnnotation(this, jdtAnnotation);
}
// ********** combination annotations **********
private Iterable<NestableAnnotation> getNestableAnnotations() {
return new CompositeIterable<NestableAnnotation>(this.getNestableAnnotationLists());
}
private Iterable<Iterable<NestableAnnotation>> getNestableAnnotationLists() {
return new TransformationIterable<AnnotationContainer, Iterable<NestableAnnotation>>(this.getAnnotationContainers(), ANNOTATION_CONTAINER_NESTED_ANNOTATIONS_TRANSFORMER);
}
private static final Transformer<AnnotationContainer, Iterable<NestableAnnotation>> ANNOTATION_CONTAINER_NESTED_ANNOTATIONS_TRANSFORMER = new AnnotationContainerNestedAnnotationsTransformer();
static final class AnnotationContainerNestedAnnotationsTransformer
extends TransformerAdapter<AnnotationContainer, Iterable<NestableAnnotation>>
{
@Override
public Iterable<NestableAnnotation> transform(AnnotationContainer container) {
return container.getNestedAnnotations();
}
}
private Iterable<AnnotationContainer> getAnnotationContainers() {
return new LiveCloneIterable<AnnotationContainer>(this.annotationContainers.values());
}
public ListIterable<NestableAnnotation> getAnnotations(String nestableAnnotationName) {
AnnotationContainer container = this.annotationContainers.get(nestableAnnotationName);
return (container != null) ? container.getNestedAnnotations() : EmptyListIterable.<NestableAnnotation> instance();
}
public int getAnnotationsSize(String nestableAnnotationName) {
AnnotationContainer container = this.annotationContainers.get(nestableAnnotationName);
return (container == null) ? 0 : container.getNestedAnnotationsSize();
}
public NestableAnnotation getAnnotation(int index, String nestableAnnotationName) {
AnnotationContainer container = this.annotationContainers.get(nestableAnnotationName);
return (container == null) ? null : container.getNestedAnnotation(index);
}
private Iterable<Annotation> getContainerOrStandaloneNestableAnnotations() {
return new TransformationIterable<AnnotationContainer, Annotation>(this.getAnnotationContainers(), TOP_LEVEL_ANNOTATION_CONTAINER_TRANSFORMER);
}
private static final Transformer<AnnotationContainer, Annotation> TOP_LEVEL_ANNOTATION_CONTAINER_TRANSFORMER = new TopLevelAnnotationContainerTransformer();
/* CU private */ static final class TopLevelAnnotationContainerTransformer
extends TransformerAdapter<AnnotationContainer, Annotation>
{
@Override
public Annotation transform(AnnotationContainer container) {
Annotation containerAnnotation = container.getContainerAnnotation();
return (containerAnnotation != null) ? containerAnnotation : container.getNestedAnnotation(0);
}
}
/* CU private */ NestableAnnotation buildAnnotation(IAnnotation jdtAnnotation, int index) {
return this.getAnnotationProvider().buildAnnotation(this, jdtAnnotation, index);
}
// ***** all annotations *****
@SuppressWarnings("unchecked")
public Iterable<Annotation> getTopLevelAnnotations() {
return new CompositeIterable<Annotation>(
this.getAnnotations(),
this.getContainerOrStandaloneNestableAnnotations()
);
}
public boolean isAnnotated() {
return ! this.isUnannotated();
}
private boolean isUnannotated() {
return this.annotations.isEmpty() && this.annotationContainers.isEmpty();
}
public boolean isAnnotatedWithAnyOf(Iterable<String> annotationNames) {
for (Annotation annotation : this.getSignificantAnnotations()) {
if (CollectionTools.contains(annotationNames, annotation.getAnnotationName())) {
return true;
}
}
return false;
}
/**
* Return the "significant" annotations;
* i.e. ignore the container annotations (they have no semantics).
*/
@SuppressWarnings("unchecked")
private Iterable<Annotation> getSignificantAnnotations() {
return new CompositeIterable<Annotation>(
this.getAnnotations(),
this.getNestableAnnotations()
);
}
// ********** misc **********
@Override
protected AnnotationProvider getAnnotationProvider() {
return super.getAnnotationProvider();
}
private IAnnotation[] getJdtAnnotations() {
try {
return this.adapter.getAnnotations();
} catch (JavaModelException ex) {
JptCommonCorePlugin.log(ex);
return EMPTY_JDT_ANNOTATION_ARRAY;
}
}
private static final IAnnotation[] EMPTY_JDT_ANNOTATION_ARRAY = new IAnnotation[0];
// ********** IJavaElement adapter **********
interface Adapter {
/**
* Return the adapter's JDT element (IPackageFragment, IType, IField, IMethod).
*/
IJavaElement getElement();
/**
* Return the adapter's element's JDT annotations.
*/
IAnnotation[] getAnnotations() throws JavaModelException;
}
// ********** unsupported JavaResourcePersistentMember implementation **********
public Annotation addAnnotation(String annotationName) {
throw new UnsupportedOperationException();
}
public NestableAnnotation addAnnotation(int index, String nestableAnnotationName) {
throw new UnsupportedOperationException();
}
public void moveAnnotation(int targetIndex, int sourceIndex, String nestableAnnotationName) {
throw new UnsupportedOperationException();
}
public void removeAnnotation(String annotationName) {
throw new UnsupportedOperationException();
}
public void removeAnnotation(int index, String nestableAnnotationName) {
throw new UnsupportedOperationException();
}
public TextRange getNameTextRange(CompilationUnit astRoot) {
throw new UnsupportedOperationException();
}
public TextRange getTextRange(String nestableAnnotationName, CompilationUnit astRoot) {
throw new UnsupportedOperationException();
}
/* CU private */ static final IMemberValuePair[] EMPTY_MEMBER_VALUE_PAIR_ARRAY = new IMemberValuePair[0];
/* CU private */ static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
// ********** annotation container **********
/* CU private */ class AnnotationContainer {
private Annotation containerAnnotation;
private final Vector<NestableAnnotation> nestedAnnotations = new Vector<NestableAnnotation>();
AnnotationContainer() {
super();
}
void initializeFromContainerAnnotation(IAnnotation jdtContainerAnnotation) {
this.initializeNestedAnnotations(jdtContainerAnnotation);
this.containerAnnotation = BinaryAnnotatedElement.this.buildAnnotation(jdtContainerAnnotation);
}
void initializeNestedAnnotations(IAnnotation jdtContainerAnnotation) {
Object[] jdtNestedAnnotations = this.getJdtNestedAnnotations(jdtContainerAnnotation);
int len = jdtNestedAnnotations.length;
for (int i = 0; i < len; i++) {
IAnnotation jdtNestedAnnotation = (IAnnotation) jdtNestedAnnotations[i];
this.addAnnotation(jdtNestedAnnotation, i);
}
}
private Object[] getJdtNestedAnnotations(IAnnotation jdtContainerAnnotation) {
return BinaryAnnotatedElement.this.getJdtMemberValues(jdtContainerAnnotation, "value"); //$NON-NLS-1$
}
void initializeFromStandaloneAnnotation(IAnnotation jdtNestableAnnotation) {
this.addAnnotation(jdtNestableAnnotation, 0);
}
private void addAnnotation(IAnnotation jdtNestableAnnotation, int index) {
this.nestedAnnotations.add(BinaryAnnotatedElement.this.buildAnnotation(jdtNestableAnnotation, index));
}
Annotation getContainerAnnotation() {
return this.containerAnnotation;
}
ListIterable<NestableAnnotation> getNestedAnnotations() {
return new LiveCloneListIterable<NestableAnnotation>(this.nestedAnnotations);
}
int getNestedAnnotationsSize() {
return this.nestedAnnotations.size();
}
NestableAnnotation getNestedAnnotation(int index) {
return this.nestedAnnotations.get(index);
}
}
}