diff options
Diffstat (limited to 'jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/jdtutility/AbstractNestedDeclarationAnnotationAdapter.java')
-rw-r--r-- | jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/jdtutility/AbstractNestedDeclarationAnnotationAdapter.java | 420 |
1 files changed, 420 insertions, 0 deletions
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/jdtutility/AbstractNestedDeclarationAnnotationAdapter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/jdtutility/AbstractNestedDeclarationAnnotationAdapter.java new file mode 100644 index 0000000000..b9cdce5a9f --- /dev/null +++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/jdtutility/AbstractNestedDeclarationAnnotationAdapter.java @@ -0,0 +1,420 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 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.core.internal.jdtutility; + +import java.util.Iterator; +import java.util.List; +import org.eclipse.jdt.core.dom.AST; +import org.eclipse.jdt.core.dom.ASTNode; +import org.eclipse.jdt.core.dom.Annotation; +import org.eclipse.jdt.core.dom.Expression; +import org.eclipse.jdt.core.dom.MarkerAnnotation; +import org.eclipse.jdt.core.dom.MemberValuePair; +import org.eclipse.jdt.core.dom.NormalAnnotation; +import org.eclipse.jdt.core.dom.SingleMemberAnnotation; + +/** + * Pull together some of the behavior common to NestedDeclarationAnnotationAdapter + * and IndexedNestedDeclarationAnnotationAdapter + */ +public abstract class AbstractNestedDeclarationAnnotationAdapter extends AbstractDeclarationAnnotationAdapter { + private final DeclarationAnnotationAdapter outerAnnotationAdapter; + private final String elementName; + private final boolean removeOuterAnnotationWhenEmpty; + + + // ********** constructors ********** + + /** + * default element name is "value"; + * default behavior is to remove the outer annotation when it is empty + */ + protected AbstractNestedDeclarationAnnotationAdapter(DeclarationAnnotationAdapter outerAnnotationAdapter, String annotationName) { + this(outerAnnotationAdapter, "value", annotationName); + } + + /** + * default behavior is to remove the outer annotation when it is empty + */ + protected AbstractNestedDeclarationAnnotationAdapter(DeclarationAnnotationAdapter outerAnnotationAdapter, String elementName, String annotationName) { + this(outerAnnotationAdapter, elementName, annotationName, true); + } + + protected AbstractNestedDeclarationAnnotationAdapter(DeclarationAnnotationAdapter outerAnnotationAdapter, String elementName, String annotationName, boolean removeOuterAnnotationWhenEmpty) { + super(annotationName); + this.outerAnnotationAdapter = outerAnnotationAdapter; + this.elementName = elementName; + this.removeOuterAnnotationWhenEmpty = removeOuterAnnotationWhenEmpty; + } + + + // ********** AbstractDeclarationAnnotationAdapter implementation ********** + + @Override + public Annotation getAnnotation(ModifiedDeclaration declaration) { + Annotation outer = this.outerAnnotationAdapter.getAnnotation(declaration); + if (outer == null) { + return null; + } + Expression value = this.elementValue(outer); + if (value == null) { + return null; + } + Annotation inner = this.getAnnotation(value); + if (inner == null) { + return null; + } + // return the annotation only if it has a matching name(?) + return this.nameMatches(declaration, inner) ? inner : null; + } + + @Override + public void removeAnnotation(ModifiedDeclaration declaration) { + Annotation outer = this.outerAnnotationAdapter.getAnnotation(declaration); + if (outer == null) { + return; + } + Expression value = this.elementValue(outer); + if (value == null) { + return; + } + // hack to allow short-circuit when the value is an array initializer + if (this.removeAnnotation(declaration, outer, value)) { + return; + } + Annotation inner = this.annotationValue(value); + if (inner == null) { + return; + } + // remove the annotation only if it has a matching name(?) + if (this.nameMatches(declaration, inner)) { + this.removeElementAndNormalize(declaration, outer); + } + } + + @Override + public ASTNode astNode(ModifiedDeclaration declaration) { + // if the annotation is missing, return the outer annotation's node + Annotation annotation = this.getAnnotation(declaration); + return (annotation != null) ? annotation : this.outerAnnotationAdapter.astNode(declaration); + } + + @Override + protected void addAnnotation(ModifiedDeclaration declaration, Annotation inner) { + Annotation outer = this.outerAnnotationAdapter.getAnnotation(declaration); + if (outer == null) { + this.buildNewOuterAnnotation(declaration, inner); + } else if (outer.isMarkerAnnotation()) { + this.modifyAnnotation(declaration, (MarkerAnnotation) outer, inner); + } else if (outer.isSingleMemberAnnotation()) { + this.modifyAnnotation(declaration, (SingleMemberAnnotation) outer, inner); + } else if (outer.isNormalAnnotation()) { + this.modifyAnnotation(declaration, (NormalAnnotation) outer, inner); + } else { + throw new IllegalStateException("unknown annotation type: " + outer); + } + } + + + // ********** abstract methods ********** + + /** + * Return an annotation extracted from the specified expression, + * which is the value of the adapter's element. + */ + protected abstract Annotation getAnnotation(Expression value); + + /** + * Remove the annotation from the specified expression, + * which is the value of the adapter's element. + * Return whether the removal was successful. + */ + protected abstract boolean removeAnnotation(ModifiedDeclaration declaration, Annotation outer, Expression value); + + /** + * Set the value of the specified outer annotation to the + * specified inner annotation. + */ + protected abstract void modifyAnnotationValue(SingleMemberAnnotation outer, Annotation inner); + + /** + * Set the value of the specified member value pair to the + * specified inner annotation. + */ + protected abstract void modifyMemberValuePair(MemberValuePair pair, Annotation inner); + + + // ********** public methods ********** + + public DeclarationAnnotationAdapter getOuterAnnotationAdapter() { + return this.outerAnnotationAdapter; + } + + public String getElementName() { + return this.elementName; + } + + + // ********** internal methods ********** + + /** + * If the specified expression is an annotation, cast it to an annotation; + * otherwise return null. + */ + protected Annotation annotationValue(Expression expression) { + switch (expression.getNodeType()) { + case ASTNode.NORMAL_ANNOTATION: + case ASTNode.SINGLE_MEMBER_ANNOTATION: + case ASTNode.MARKER_ANNOTATION: + return (Annotation) expression; + default: + return null; + } + } + + /** + * Remove the *first* annotation element with the specified name + * from the specified annotation, converting the annotation as appropriate. + */ + protected void removeElementAndNormalize(ModifiedDeclaration declaration, Annotation outer) { + if (outer.isNormalAnnotation()) { + this.removeElementAndNormalize(declaration, (NormalAnnotation) outer); + } else if (outer.isSingleMemberAnnotation()) { + this.removeElementAndNormalize(declaration, (SingleMemberAnnotation) outer); + } else if (outer.isMarkerAnnotation()) { + this.removeElementAndNormalize(declaration, (MarkerAnnotation) outer); + } else { + throw new IllegalArgumentException("unknown annotation type: " + outer); + } + } + + /** + * Remove the *first* annotation element with the adapter's element name + * from the specified annotation. Convert the annotation to + * a marker annotation or single member annotation if appropriate. + * <pre> + * @Outer(name="Fred", foo=@Inner) => @Outer(name="Fred") + * @Outer(foo=@Inner) => @Outer + * </pre> + */ + protected void removeElementAndNormalize(ModifiedDeclaration declaration, NormalAnnotation outer) { + this.removeElement(outer); + this.normalizeAnnotation(declaration, outer); + } + + /** + * Remove from the specified annotation the element with + * the adapter's element name. + */ + protected void removeElement(NormalAnnotation annotation) { + for (Iterator<MemberValuePair> stream = this.values(annotation).iterator(); stream.hasNext(); ) { + MemberValuePair pair = stream.next(); + if (pair.getName().getFullyQualifiedName().equals(this.elementName)) { + stream.remove(); + break; + } + } + } + + /** + * Convert the specified normal annotation to a marker annotation or + * single member annotation if appropriate. + */ + protected void normalizeAnnotation(ModifiedDeclaration declaration, NormalAnnotation outer) { + List<MemberValuePair> values = this.values(outer); + switch (values.size()) { + case 0: + // if the elements are all gone, remove the annotation or convert it to a marker annotation + if (this.removeOuterAnnotationWhenEmpty) { + this.outerAnnotationAdapter.removeAnnotation(declaration); + } else { + // convert the annotation to a marker annotation + this.outerAnnotationAdapter.newMarkerAnnotation(declaration); + } + break; + case 1: + MemberValuePair pair = values.get(0); + if (pair.getName().getFullyQualifiedName().equals("value")) { + // if the last remaining element is 'value', convert the annotation to a single member annotation + Expression vv = pair.getValue(); + vv = (Expression) ASTNode.copySubtree(vv.getAST(), vv); + this.outerAnnotationAdapter.newSingleMemberAnnotation(declaration).setValue(vv); + } + break; + default: + // do nothing + break; + } + } + + /** + * Convert the specified single member annotation to a marker annotation + * if the adapter's element name is "value". + */ + protected void removeElementAndNormalize(ModifiedDeclaration declaration, SingleMemberAnnotation outer) { + if (this.elementName.equals("value")) { + if (this.removeOuterAnnotationWhenEmpty) { + this.outerAnnotationAdapter.removeAnnotation(declaration); + } else { + // convert the annotation to a marker annotation + this.outerAnnotationAdapter.newMarkerAnnotation(declaration); + } + } + } + + protected void removeElementAndNormalize(ModifiedDeclaration declaration, MarkerAnnotation outer) { + if (this.removeOuterAnnotationWhenEmpty) { + this.outerAnnotationAdapter.removeAnnotation(declaration); + } + } + + /** + * Return the value of the *first* annotation element + * with the adapter's element name. + * Return null if the annotation has no such element. + * (An element name of "value" will return the value of a single + * member annotation.) + */ + protected Expression elementValue(Annotation annotation) { + if (annotation.isNormalAnnotation()) { + return this.elementValue((NormalAnnotation) annotation); + } + if (annotation.isSingleMemberAnnotation()) { + return this.elementValue((SingleMemberAnnotation) annotation); + } + return null; + } + + protected Expression elementValue(NormalAnnotation annotation) { + MemberValuePair pair = this.memberValuePair(annotation); + return (pair == null) ? null : pair.getValue(); + } + + /** + * If the adapter's element name is "value", return the value of the + * annotation, otherwise return null. + */ + protected Expression elementValue(SingleMemberAnnotation annotation) { + return this.elementName.equals("value") ? annotation.getValue() : null; + } + + /** + * Return the *first* member value pair for the adapter's element name. + * Return null if the specified annotation has no such element. + */ + protected MemberValuePair memberValuePair(NormalAnnotation annotation) { + for (MemberValuePair pair : this.values(annotation)) { + if (pair.getName().getFullyQualifiedName().equals(this.elementName)) { + return pair; + } + } + return null; + } + + /** + * Build a new outer annotation and add the specified + * inner annotation to it: + * <pre> + * @Outer(@Inner) + * </pre> + * or + * <pre> + * @Outer(foo=@Inner) + * </pre> + */ + protected void buildNewOuterAnnotation(ModifiedDeclaration declaration, Annotation inner) { + if (this.elementName.equals("value")) { + this.outerAnnotationAdapter.newSingleMemberAnnotation(declaration).setValue(this.buildNewInnerExpression(inner)); + } else { + List<MemberValuePair> values = this.values(this.outerAnnotationAdapter.newNormalAnnotation(declaration)); + values.add(this.newMemberValuePair(this.buildNewInnerExpression(inner))); + } + } + + /** + * Build an expression to be added to a new outer annotation + * for the specified inner annotation. + */ + protected abstract Expression buildNewInnerExpression(Annotation inner); + + /** + * Build a new member value pair with the specified name and value. + */ + protected MemberValuePair newMemberValuePair(String name, Expression value) { + AST ast = value.getAST(); + MemberValuePair pair = ast.newMemberValuePair(); + pair.setName(ast.newSimpleName(name)); + pair.setValue(value); + return pair; + } + + /** + * Build a new member value pair with the adapter's element name + * and the specified inner annotation. + */ + protected MemberValuePair newMemberValuePair(Expression value) { + return this.newMemberValuePair(this.elementName, value); + } + + /** + * Add the specified inner annotation to the marker annotation. + */ + protected void modifyAnnotation(ModifiedDeclaration declaration, MarkerAnnotation outer, Annotation inner) { + this.buildNewOuterAnnotation(declaration, inner); + } + + /** + * Add the specified inner annotation to the single member annotation. + */ + protected void modifyAnnotation(ModifiedDeclaration declaration, SingleMemberAnnotation outer, Annotation inner) { + if (this.elementName.equals("value")) { + this.modifyAnnotationValue(outer, inner); + } else { + this.modifyAnnotationNonValue(declaration, outer, inner); + } + } + + /** + * Add the specified inner annotation to the single member annotation, + * converting it to a normal annotation: + * <pre> + * @Outer("lorem ipsum") => @Outer(value="lorem ipsum", foo=@Inner) + * </pre> + */ + protected void modifyAnnotationNonValue(ModifiedDeclaration declaration, SingleMemberAnnotation outer, Annotation inner) { + Expression vv = outer.getValue(); + vv = (Expression) ASTNode.copySubtree(vv.getAST(), vv); + NormalAnnotation newOuter = this.outerAnnotationAdapter.newNormalAnnotation(declaration); + List<MemberValuePair> values = this.values(newOuter); + values.add(this.newMemberValuePair("value", vv)); + values.add(this.newMemberValuePair(this.buildNewInnerExpression(inner))); + } + + /** + * Add the specified inner annotation to the normal annotation: + * <pre> + * @Outer(bar="lorem ipsum") => @Outer(bar="lorem ipsum", foo=@Inner) + * </pre> + * or + * <pre> + * @Outer(foo=@Inner("lorem ipsum")) => @Outer(foo=@Inner) + * </pre> + */ + protected void modifyAnnotation(ModifiedDeclaration declaration, NormalAnnotation outer, Annotation inner) { + MemberValuePair pair = this.memberValuePair(outer); + if (pair == null) { + List<MemberValuePair> values = this.values(outer); + values.add(this.newMemberValuePair(inner)); + } else { + this.modifyMemberValuePair(pair, inner); + } + } + +} |