| /******************************************************************************* |
| * Copyright (c) 2007, 2009 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.resource.java.source; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.ListIterator; |
| |
| import org.eclipse.jdt.core.dom.ASTNode; |
| import org.eclipse.jdt.core.dom.Annotation; |
| import org.eclipse.jdt.core.dom.ArrayInitializer; |
| import org.eclipse.jdt.core.dom.CompilationUnit; |
| import org.eclipse.jdt.core.dom.Expression; |
| import org.eclipse.jdt.core.dom.ITypeBinding; |
| import org.eclipse.jdt.core.dom.MemberValuePair; |
| import org.eclipse.jdt.core.dom.NormalAnnotation; |
| import org.eclipse.jdt.core.dom.SingleMemberAnnotation; |
| import org.eclipse.jpt.core.resource.java.AnnotationContainer; |
| import org.eclipse.jpt.core.resource.java.NestableAnnotation; |
| import org.eclipse.jpt.utility.internal.CollectionTools; |
| |
| /** |
| * Utility methods for manipulating annotation containers. |
| */ |
| public final class AnnotationContainerTools { |
| |
| /** |
| * Add a nested annotation to the specified annotation container |
| * at the specified index. |
| * This method modifies both the model annotation container and the |
| * AST; with the appropriate change notification occurring afterwards. |
| */ |
| public static <T extends NestableAnnotation> NestableAnnotation addNestedAnnotation(int index, AnnotationContainer<T> annotationContainer) { |
| // add a new annotation to the end of the list... |
| int sourceIndex = annotationContainer.nestedAnnotationsSize(); |
| T nestedAnnotation = annotationContainer.addNestedAnnotationInternal(); |
| nestedAnnotation.newAnnotation(); |
| // ...then move it to the specified index... |
| annotationContainer.moveNestedAnnotationInternal(index, sourceIndex); |
| synchJavaAnnotationsAfterMove(index, sourceIndex, annotationContainer, nestedAnnotation); |
| // ...then, when all is settled, tell the container to fire change notification |
| annotationContainer.nestedAnnotationAdded(index, nestedAnnotation); |
| return nestedAnnotation; |
| } |
| |
| /** |
| * Move the nested annotation at the specified source index in the |
| * specified annotation container to the specified target index. |
| * This method modifies both the model annotation container and the |
| * AST; with the appropriate change notification occurring afterwards. |
| */ |
| public static <T extends NestableAnnotation> void moveNestedAnnotation(int targetIndex, int sourceIndex, AnnotationContainer<T> annotationContainer) { |
| NestableAnnotation nestedAnnotation = annotationContainer.moveNestedAnnotationInternal(targetIndex, sourceIndex); |
| synchJavaAnnotationsAfterMove(targetIndex, sourceIndex, annotationContainer, nestedAnnotation); |
| annotationContainer.nestedAnnotationMoved(targetIndex, sourceIndex); |
| } |
| |
| /** |
| * An annotation was moved within the specified annotation container from |
| * the specified source index to the specified target index. |
| * Synchronize the AST annotations with the model annotation container, |
| * starting with the lower index to prevent overlap. |
| */ |
| private static <T extends NestableAnnotation> void synchJavaAnnotationsAfterMove(int targetIndex, int sourceIndex, AnnotationContainer<T> annotationContainer, NestableAnnotation nestedAnnotationAnnotation) { |
| // move the Java annotation to the end of the list... |
| nestedAnnotationAnnotation.moveAnnotation(annotationContainer.nestedAnnotationsSize()); |
| // ...then shift the other Java annotations over one slot... |
| List<T> nestableAnnotations = CollectionTools.list(annotationContainer.nestedAnnotations()); |
| if (sourceIndex < targetIndex) { |
| for (int i = sourceIndex; i < targetIndex; i++) { |
| nestableAnnotations.get(i).moveAnnotation(i); |
| } |
| } else { |
| for (int i = sourceIndex; i > targetIndex; i-- ) { |
| nestableAnnotations.get(i).moveAnnotation(i); |
| } |
| } |
| // ...then move the Java annotation to the now empty slot at the target index |
| nestedAnnotationAnnotation.moveAnnotation(targetIndex); |
| } |
| |
| /** |
| * Remove the nested annotation at the specified index in the |
| * specified annotation container. |
| * This method modifies both the model annotation container and the |
| * AST; with the appropriate change notification occurring afterwards. |
| */ |
| public static <T extends NestableAnnotation> void removeNestedAnnotation(int index, AnnotationContainer<T> annotationContainer) { |
| T nestedAnnotation = annotationContainer.removeNestedAnnotationInternal(index); |
| nestedAnnotation.removeAnnotation(); |
| synchJavaAnnotationsAfterRemove(index, annotationContainer); |
| annotationContainer.nestedAnnotationRemoved(index, nestedAnnotation); |
| } |
| |
| /** |
| * An annotation was removed from the specified annotation container at the |
| * specified index. |
| * Synchronize the AST annotations with the model annotation container, |
| * starting at the specified index to prevent overlap. |
| */ |
| private static <T extends NestableAnnotation> void synchJavaAnnotationsAfterRemove(int index, AnnotationContainer<T> annotationContainer) { |
| List<T> nestableAnnotations = CollectionTools.list(annotationContainer.nestedAnnotations()); |
| for (int i = index; i < nestableAnnotations.size(); i++) { |
| // the indices are the same because the model annotations are |
| // already in the proper locations - it's the Java annotations that |
| // need to be moved to the same location |
| nestableAnnotations.get(i).moveAnnotation(i); |
| } |
| } |
| |
| /** |
| * Initialize the specified annotation container to be in synch with the |
| * specified AST. No change notification will occur. |
| */ |
| public static <T extends NestableAnnotation> void initialize(AnnotationContainer<T> annotationContainer, CompilationUnit astRoot) { |
| // ignore the nested AST annotations themselves |
| // (maybe someday we can use them during initialization...) |
| int size = getNestedAstAnnotations(astRoot, annotationContainer).size(); |
| for (int i = 0; i < size; i++) { |
| T nestedAnnotation = annotationContainer.addNestedAnnotationInternal(); |
| nestedAnnotation.initialize(astRoot); |
| } |
| } |
| |
| /** |
| * Return a list of the nested AST annotations. |
| */ |
| private static <T extends NestableAnnotation> ArrayList<Annotation> getNestedAstAnnotations(CompilationUnit astRoot, AnnotationContainer<T> annotationContainer) { |
| ArrayList<Annotation> result = new ArrayList<Annotation>(); |
| Annotation containerAstAnnotation = annotationContainer.getContainerJdtAnnotation(astRoot); |
| if (containerAstAnnotation.isMarkerAnnotation()) { |
| // no nested annotations |
| } |
| else if (containerAstAnnotation.isSingleMemberAnnotation()) { |
| if (annotationContainer.getElementName().equals("value")) { //$NON-NLS-1$ |
| Expression ex = ((SingleMemberAnnotation) containerAstAnnotation).getValue(); |
| addAstAnnotationsTo(ex, annotationContainer.getNestableAnnotationName(), result); |
| } else { |
| // no nested annotations |
| } |
| } |
| else if (containerAstAnnotation.isNormalAnnotation()) { |
| MemberValuePair pair = getMemberValuePair((NormalAnnotation) containerAstAnnotation, annotationContainer.getElementName()); |
| if (pair == null) { |
| // no nested annotations |
| } else { |
| addAstAnnotationsTo(pair.getValue(), annotationContainer.getNestableAnnotationName(), result); |
| } |
| } |
| return result; |
| } |
| |
| /** |
| * Add whatever annotations are represented by the specified expression to |
| * the specified bag. Add null to the bag for any non-annotation expression. |
| */ |
| private static void addAstAnnotationsTo(Expression expression, String annotationName, ArrayList<Annotation> astAnnotations) { |
| if (expression == null) { |
| astAnnotations.add(null); // not sure how we would get here... |
| } |
| else if (expression.getNodeType() == ASTNode.ARRAY_INITIALIZER) { |
| addAstAnnotationsTo((ArrayInitializer) expression, annotationName, astAnnotations); |
| } |
| else { |
| astAnnotations.add(getAstAnnotation_(expression, annotationName)); |
| } |
| } |
| |
| private static void addAstAnnotationsTo(ArrayInitializer arrayInitializer, String annotationName, ArrayList<Annotation> astAnnotations) { |
| @SuppressWarnings("unchecked") |
| List<Expression> expressions = arrayInitializer.expressions(); |
| for (Expression expression : expressions) { |
| astAnnotations.add(getAstAnnotation(expression, annotationName)); |
| } |
| } |
| |
| /** |
| * If the specified expression is an annotation with the specified name, return it; |
| * otherwise return null. |
| */ |
| private static Annotation getAstAnnotation(Expression expression, String annotationName) { |
| // not sure how the expression could be null... |
| return (expression == null) ? null : getAstAnnotation_(expression, annotationName); |
| } |
| |
| /** |
| * pre-condition: expression is not null |
| */ |
| private static Annotation getAstAnnotation_(Expression expression, String annotationName) { |
| switch (expression.getNodeType()) { |
| case ASTNode.NORMAL_ANNOTATION: |
| case ASTNode.SINGLE_MEMBER_ANNOTATION: |
| case ASTNode.MARKER_ANNOTATION: |
| Annotation astAnnotation = (Annotation) expression; |
| if (getQualifiedName(astAnnotation).equals(annotationName)) { |
| return astAnnotation; |
| } |
| return null; |
| default: |
| return null; |
| } |
| } |
| |
| private static String getQualifiedName(Annotation astAnnotation) { |
| ITypeBinding typeBinding = astAnnotation.resolveTypeBinding(); |
| if (typeBinding != null) { |
| String resolvedName = typeBinding.getQualifiedName(); |
| if (resolvedName != null) { |
| return resolvedName; |
| } |
| } |
| return astAnnotation.getTypeName().getFullyQualifiedName(); |
| } |
| |
| private static MemberValuePair getMemberValuePair(NormalAnnotation annotation, String elementName) { |
| @SuppressWarnings("unchecked") |
| List<MemberValuePair> pairs = annotation.values(); |
| for (MemberValuePair pair : pairs) { |
| if (pair.getName().getFullyQualifiedName().equals(elementName)) { |
| return pair; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Update the annotations in the specified annotation container to be in |
| * synch with those in the specified AST. The appropriate change |
| * notification will occur. |
| */ |
| public static <T extends NestableAnnotation> void update(AnnotationContainer<T> annotationContainer, CompilationUnit astRoot) { |
| ListIterator<Annotation> astAnnotations = getNestedAstAnnotations(astRoot, annotationContainer).listIterator(); |
| |
| for (ListIterator<T> nestedAnnotations = annotationContainer.nestedAnnotations(); nestedAnnotations.hasNext(); ) { |
| T nestedAnnotation = nestedAnnotations.next(); |
| if (astAnnotations.hasNext()) { |
| // matching AST annotation is present - update the nested annotation |
| astAnnotations.next(); // maybe someday we can pass this to the update |
| nestedAnnotation.update(astRoot); |
| } else { |
| // no more AST annotations - remove the nested annotation at the end of the container's list |
| int last = annotationContainer.nestedAnnotationsSize() - 1; |
| T remove = annotationContainer.removeNestedAnnotationInternal(last); |
| annotationContainer.nestedAnnotationRemoved(last, remove); |
| } |
| } |
| |
| // add nested annotations for the remaining AST annotations |
| int i = annotationContainer.nestedAnnotationsSize(); |
| while (astAnnotations.hasNext()) { |
| astAnnotations.next(); // maybe someday we can pass this to the initialize |
| T nestedAnnotation = annotationContainer.addNestedAnnotationInternal(); |
| nestedAnnotation.initialize(astRoot); |
| annotationContainer.nestedAnnotationAdded(i++, nestedAnnotation); |
| } |
| } |
| |
| private AnnotationContainerTools() { |
| super(); |
| throw new UnsupportedOperationException(); |
| } |
| |
| } |