blob: cc7f47e155ef621ec0a07c280a4767d7c8a2615e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2011 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.source;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jpt.common.core.internal.utility.jdt.ASTTools;
import org.eclipse.jpt.common.core.internal.utility.jdt.AnnotatedElementAnnotationElementAdapter;
import org.eclipse.jpt.common.core.internal.utility.jdt.ConversionDeclarationAnnotationElementAdapter;
import org.eclipse.jpt.common.core.internal.utility.jdt.ElementAnnotationAdapter;
import org.eclipse.jpt.common.core.resource.java.Annotation;
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.core.utility.jdt.AnnotatedElement;
import org.eclipse.jpt.common.core.utility.jdt.AnnotationAdapter;
import org.eclipse.jpt.common.core.utility.jdt.AnnotationElementAdapter;
import org.eclipse.jpt.common.core.utility.jdt.DeclarationAnnotationAdapter;
import org.eclipse.jpt.common.core.utility.jdt.DeclarationAnnotationElementAdapter;
import org.eclipse.jpt.common.core.utility.jdt.IndexedAnnotationAdapter;
import org.eclipse.jpt.common.core.utility.jdt.IndexedDeclarationAnnotationElementAdapter;
/**
* some common state and behavior for Java source annotations;
* and lots of convenience methods
*/
public abstract class SourceAnnotation
extends SourceNode
implements Annotation
{
protected final AnnotatedElement annotatedElement;
// TODO - make 'final' if we start using combination annotation adapters(?)
protected DeclarationAnnotationAdapter daa;
// TODO - make 'final' if we start using combination annotation adapters(?)
protected AnnotationAdapter annotationAdapter;
/**
* constructor for straight member annotation
*/
protected SourceAnnotation(JavaResourceNode parent, AnnotatedElement element, DeclarationAnnotationAdapter daa) {
this(parent, element, daa, new ElementAnnotationAdapter(element, daa));
}
/**
* constructor for nested annotation (typically)
*/
protected SourceAnnotation(JavaResourceNode parent, AnnotatedElement element, DeclarationAnnotationAdapter daa, AnnotationAdapter annotationAdapter) {
super(parent);
this.annotatedElement = element;
this.daa = daa;
this.annotationAdapter = annotationAdapter;
}
// ********** JavaResourceNode implementation **********
public TextRange getTextRange(CompilationUnit astRoot) {
return this.getAnnotationTextRange(astRoot);
}
// ********** Annotation implementation **********
public org.eclipse.jdt.core.dom.Annotation getAstAnnotation(CompilationUnit astRoot) {
return this.annotationAdapter.getAnnotation(astRoot);
}
public void newAnnotation() {
this.annotationAdapter.newMarkerAnnotation();
}
public void removeAnnotation() {
this.annotationAdapter.removeAnnotation();
}
public boolean isUnset() {
return true;
}
// ********** convenience methods **********
protected DeclarationAnnotationElementAdapter<String> buildStringElementAdapter(String elementName) {
return ConversionDeclarationAnnotationElementAdapter.forStrings(this.daa, elementName);
}
protected DeclarationAnnotationElementAdapter<Boolean> buildBooleanElementAdapter(String elementName) {
return ConversionDeclarationAnnotationElementAdapter.forBooleans(this.daa, elementName);
}
protected DeclarationAnnotationElementAdapter<Integer> buildIntegerElementAdapter(String elementName) {
return ConversionDeclarationAnnotationElementAdapter.forNumbers(this.daa, elementName);
}
protected AnnotationElementAdapter<String> buildStringElementAdapter(DeclarationAnnotationElementAdapter<String> daea) {
return new AnnotatedElementAnnotationElementAdapter<String>(this.annotatedElement, daea);
}
protected AnnotationElementAdapter<Boolean> buildBooleanElementAdapter(DeclarationAnnotationElementAdapter<Boolean> daea) {
return new AnnotatedElementAnnotationElementAdapter<Boolean>(this.annotatedElement, daea);
}
protected AnnotationElementAdapter<Integer> buildIntegerElementAdapter(DeclarationAnnotationElementAdapter<Integer> daea) {
return new AnnotatedElementAnnotationElementAdapter<Integer>(this.annotatedElement, daea);
}
/**
/**
* Return the text range corresponding to the annotation.
* If the annotation is missing, return <code>null</code>.
*/
protected TextRange getAnnotationTextRange(CompilationUnit astRoot) {
// the AST is null for virtual Java attributes
// TODO remove the AST null check once we start storing text ranges
// in the resource model
return (astRoot == null) ? null : this.getTextRange(this.getAstAnnotation(astRoot));
}
/**
* Convenience method.
* Return the text range corresponding to the specified element.
* If the specified element is missing, return the annotation's text range instead.
*/
protected TextRange getElementTextRange(DeclarationAnnotationElementAdapter<?> elementAdapter, CompilationUnit astRoot) {
return this.getElementTextRange(this.getAnnotationElementTextRange(elementAdapter, astRoot), astRoot);
}
/**
* Convenience method. If the specified element text range is null
* return the member's text range instead.
*/
protected TextRange getElementTextRange(TextRange elementTextRange, CompilationUnit astRoot) {
return (elementTextRange != null) ? elementTextRange : this.getAnnotationTextRange(astRoot);
}
/**
* Convenience method. Return whether the specified position exists and
* touches the specified element.
*/
protected boolean elementTouches(DeclarationAnnotationElementAdapter<?> elementAdapter, int pos, CompilationUnit astRoot) {
return this.textRangeTouches(this.getAnnotationElementTextRange(elementAdapter, astRoot), pos);
}
/**
* Convenience method. Return whether the specified text range is not
* null (meaning the corresponding AST node exists) and the specified position touches it.
*/
protected boolean textRangeTouches(TextRange textRange, int pos) {
return (textRange != null) && textRange.touches(pos);
}
/**
* Return the text range corresponding to the specified element.
* If the element is missing, return null.
*/
protected TextRange getAnnotationElementTextRange(DeclarationAnnotationElementAdapter<?> adapter, CompilationUnit astRoot) {
// the AST is null for virtual Java attributes
// TODO remove the AST null check once we start storing text ranges
// in the resource model
return (astRoot == null) ? null : this.getTextRange(this.getAnnotationElementExpression(adapter, astRoot));
}
/**
* Return the specified AST DOM element.
*/
protected Expression getAnnotationElementExpression(DeclarationAnnotationElementAdapter<?> adapter, CompilationUnit astRoot) {
return adapter.getExpression(this.annotatedElement.getModifiedDeclaration(astRoot));
}
/**
* Return the text range corresponding to the element's indexed subvalue
* @throws ArrayIndexOutOfBoundsException if the index is out of range
*/
protected TextRange getAnnotationElementSubvalueTextRange(IndexedDeclarationAnnotationElementAdapter<?> adapter, int index, CompilationUnit astRoot) {
// the AST is null for virtual Java attributes
// TODO remove the AST null check once we start storing text ranges
// in the resource model
return (astRoot == null) ? null : this.getTextRange(this.getAnnotationElementSubvalueExpression(adapter, index, astRoot));
}
/**
* Return the expression corresponding to the element's indexed subvalue.
*/
protected Expression getAnnotationElementSubvalueExpression(IndexedDeclarationAnnotationElementAdapter<?> adapter, int index, CompilationUnit astRoot) {
return adapter.getSubvalueExpression(index, this.annotatedElement.getModifiedDeclaration(astRoot));
}
/**
* Return the text range corresponding to the specified AST node.
* If the AST node is null, return null.
*/
protected TextRange getTextRange(ASTNode astNode) {
return (astNode == null) ? null : ASTTools.buildTextRange(astNode);
}
//*********** NestableAnnotation implementation ****************
/**
* convenience implementation of method from NestableAnnotation interface
* for subclasses
*/
public void moveAnnotation(int newIndex) {
this.getIndexedAnnotationAdapter().moveAnnotation(newIndex);
}
private IndexedAnnotationAdapter getIndexedAnnotationAdapter() {
return (IndexedAnnotationAdapter) this.annotationAdapter;
}
/**
* A container for nested annotations. The owner of the AnnotationContainer
* needs to call initialize(CompilationUnit) on it.
* @param <T> the type of the resource nestable annotations
*/
public abstract class AnnotationContainer<T extends NestableAnnotation> extends SourceNode.AnnotationContainer<T>
{
protected AnnotationContainer() {
super();
}
/**
* Return the annotations property name for firing property change notification
*/
protected abstract String getAnnotationsPropertyName();
@Override
protected void fireItemAdded(int index, T addedItem) {
SourceAnnotation.this.fireItemAdded(this.getAnnotationsPropertyName(), index, addedItem);
}
@Override
protected void fireItemsRemoved(int index, java.util.List<T> removedItems) {
SourceAnnotation.this.fireItemsRemoved(this.getAnnotationsPropertyName(), index, removedItems);
}
}
}