Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt')
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/ASTNodeTextRange.java39
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/ASTTools.java206
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/AbstractAnnotationAdapter.java165
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/AbstractDeclarationAnnotationAdapter.java153
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/AbstractExpressionConverter.java82
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/AbstractNestedDeclarationAnnotationAdapter.java422
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/AnnotationStringArrayExpressionConverter.java96
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/BooleanExpressionConverter.java51
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/CharacterStringExpressionConverter.java53
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/CombinationIndexedDeclarationAnnotationAdapter.java496
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/ConversionDeclarationAnnotationElementAdapter.java203
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/DefaultAnnotationEditFormatter.java219
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/EnumArrayDeclarationAnnotationElementAdapter.java159
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/EnumDeclarationAnnotationElementAdapter.java128
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/ExpressionDeclarationAnnotationElementAdapter.java362
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/GenericVisitor.java791
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTAttribute.java66
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTFieldAttribute.java205
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTMember.java236
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTMethodAttribute.java256
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTModifiedDeclaration.java537
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTTools.java92
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTType.java238
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JPTTools.java341
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/MemberAnnotationAdapter.java27
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/MemberAnnotationElementAdapter.java99
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/MemberIndexedAnnotationAdapter.java74
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NameStringExpressionConverter.java57
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NestedDeclarationAnnotationAdapter.java95
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NestedIndexedDeclarationAnnotationAdapter.java334
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NullAnnotationEditFormatter.java42
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NullDeclarationAnnotationAdapter.java82
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NullDeclarationAnnotationElementAdapter.java60
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NullExpressionConverter.java54
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NumberIntegerExpressionConverter.java51
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/PrimitiveTypeStringExpressionConverter.java61
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/ShortCircuitAnnotationElementAdapter.java104
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/ShortCircuitArrayAnnotationElementAdapter.java43
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/SimpleDeclarationAnnotationAdapter.java62
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/SimpleTypeStringExpressionConverter.java63
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/StringArrayExpressionConverter.java111
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/StringExpressionConverter.java52
-rw-r--r--jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/TypeStringExpressionConverter.java55
43 files changed, 7122 insertions, 0 deletions
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/ASTNodeTextRange.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/ASTNodeTextRange.java
new file mode 100644
index 0000000000..1cb404987c
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/ASTNodeTextRange.java
@@ -0,0 +1,39 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 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.utility.jdt;
+
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jpt.core.utility.AbstractTextRange;
+
+/**
+ * Adapt an ASTNode to the TextRange interface.
+ */
+public class ASTNodeTextRange extends AbstractTextRange {
+ private final ASTNode astNode;
+
+ public ASTNodeTextRange(ASTNode astNode) {
+ super();
+ this.astNode = astNode;
+ }
+
+ public int getOffset() {
+ return this.astNode.getStartPosition();
+ }
+
+ public int getLength() {
+ return this.astNode.getLength();
+ }
+
+ public int getLineNumber() {
+ return ((CompilationUnit) this.astNode.getRoot()).getLineNumber(this.getOffset());
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/ASTTools.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/ASTTools.java
new file mode 100644
index 0000000000..531ae81be7
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/ASTTools.java
@@ -0,0 +1,206 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2010 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.utility.jdt;
+
+import java.util.HashSet;
+import java.util.List;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTParser;
+import org.eclipse.jdt.core.dom.Annotation;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.IAnnotationBinding;
+import org.eclipse.jdt.core.dom.IBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.TypeLiteral;
+import org.eclipse.jpt.utility.JavaType;
+import org.eclipse.jpt.utility.MethodSignature;
+import org.eclipse.jpt.utility.internal.SimpleJavaType;
+import org.eclipse.jpt.utility.internal.SimpleMethodSignature;
+
+/**
+ * Convenience methods for dealing with JDT ASTs.
+ */
+public class ASTTools {
+
+ /**
+ * Build an AST without method bodies for the specified compilation unit
+ * with its bindings resolved (and the resultant performance hit).
+ */
+ public static CompilationUnit buildASTRoot(ICompilationUnit compilationUnit) {
+ ASTParser parser = ASTParser.newParser(AST.JLS3);
+ parser.setSource(compilationUnit);
+ parser.setIgnoreMethodBodies(true); // we don't need method bodies
+ parser.setResolveBindings(true);
+ parser.setBindingsRecovery(true); // see bugs 196200, 222735
+ return (CompilationUnit) parser.createAST(null);
+ }
+
+
+ // ********** JDT DOM **********
+
+ public static String resolveEnum(Expression expression) {
+ if (expression == null) {
+ return null;
+ }
+ switch (expression.getNodeType()) {
+ case ASTNode.QUALIFIED_NAME:
+ case ASTNode.SIMPLE_NAME:
+ return resolveEnum((Name) expression);
+ default:
+ return null;
+ }
+ }
+
+ public static String resolveEnum(Name enumExpression) {
+ IBinding binding = enumExpression.resolveBinding();
+ if (binding == null) {
+ return null; // TODO figure out why this is null sometimes
+ }
+ if (binding.getKind() != IBinding.VARIABLE) {
+ return null;
+ }
+ IVariableBinding variableBinding = (IVariableBinding) binding;
+ return variableBinding.getType().getQualifiedName() + '.' + variableBinding.getName();
+ }
+
+ /**
+ * Return the fully-qualified name of the specified node's annotation
+ * class.
+ */
+ public static String resolveAnnotation(Annotation node) {
+ IAnnotationBinding annotationBinding = node.resolveAnnotationBinding();
+ if (annotationBinding == null) {
+ return null;
+ }
+ ITypeBinding annotationTypeBinding = annotationBinding.getAnnotationType();
+ return (annotationTypeBinding == null) ? null : annotationTypeBinding.getQualifiedName();
+ }
+
+ /**
+ * If the specified expression is a type literal, return the type's fully-
+ * qualified name. Return null otherwise.
+ */
+ public static String resolveFullyQualifiedName(Expression expression) {
+ ITypeBinding resolvedTypeBinding = resolveTypeBinding(expression);
+ return (resolvedTypeBinding == null) ? null : resolvedTypeBinding.getQualifiedName();
+ }
+
+ /**
+ * If the specified expression is a type literal, return the corresponding
+ * type binding.
+ */
+ public static ITypeBinding resolveTypeBinding(Expression expression) {
+ if (expression.getNodeType() == ASTNode.TYPE_LITERAL) {
+ return ((TypeLiteral) expression).getType().resolveBinding();
+ }
+ return null;
+ }
+
+ public static MethodSignature buildMethodSignature(MethodDeclaration methodDeclaration) {
+ return new SimpleMethodSignature(
+ methodDeclaration.getName().getFullyQualifiedName(),
+ buildParameterTypes(methodDeclaration)
+ );
+ }
+
+ public static JavaType[] buildParameterTypes(MethodDeclaration methodDeclaration) {
+ List<SingleVariableDeclaration> parameters = parameters(methodDeclaration);
+ int len = parameters.size();
+ JavaType[] parameterTypes = new JavaType[len];
+ for (int i = 0; i < len; i++) {
+ ITypeBinding type = parameters.get(i).getType().resolveBinding();
+ parameterTypes[i] = new SimpleJavaType(type.getQualifiedName(), type.getDimensions());
+ }
+ return parameterTypes;
+ }
+
+ // minimize scope of suppressed warnings
+ @SuppressWarnings("unchecked")
+ private static List<SingleVariableDeclaration> parameters(MethodDeclaration methodDeclaration) {
+ return methodDeclaration.parameters();
+ }
+
+ /**
+ * Return whether the specified expression is a type literal and the type binding
+ * corresponding to the specified type name exists in the type
+ * literal's inheritance hierarchy (superclasses and interfaces).
+ * Return null otherwise.
+ */
+ public static boolean typeIsSubTypeOf(Expression expression, String searchTypeName) {
+ return findTypeInHierarchy(expression, searchTypeName) != null;
+ }
+
+ /**
+ * If the specified expression is a type literal, return the type binding
+ * corresponding to the specified type name if it exists in the type
+ * literal's inheritance hierarchy (superclasses and interfaces).
+ * Return null otherwise.
+ */
+ public static ITypeBinding findTypeInHierarchy(Expression expression, String searchTypeName) {
+ ITypeBinding typeBinding = resolveTypeBinding(expression);
+ return (typeBinding == null) ? null : findTypeInHierarchy(typeBinding, searchTypeName);
+ }
+
+ /**
+ * Return whether a type binding with the specified type name exists in
+ * the specified type binding's inheritance hierarchy (superclasses
+ * and interfaces).
+ */
+ public static boolean typeIsSubTypeOf(ITypeBinding typeBinding, String searchTypeName) {
+ return findTypeInHierarchy(typeBinding, searchTypeName) != null;
+ }
+
+ /**
+ * Return the type binding corresponding to the specified type name if it
+ * exists in the specified type binding's inheritance hierarchy (superclasses
+ * and interfaces). Return null otherwise.
+ */
+ public static ITypeBinding findTypeInHierarchy(ITypeBinding typeBinding, String searchTypeName) {
+ return findTypeInHierarchy(typeBinding, searchTypeName, new HashSet<String>());
+ }
+
+ private static ITypeBinding findTypeInHierarchy(ITypeBinding typeBinding, String searchTypeName, HashSet<String> visited) {
+ String typeName = typeBinding.getQualifiedName();
+ if (visited.contains(typeName)) {
+ return null;
+ }
+ if (typeName.equals(searchTypeName)) {
+ return typeBinding;
+ }
+ visited.add(typeName);
+
+ ITypeBinding[] interfaceBindings = typeBinding.getInterfaces();
+ for (ITypeBinding interfaceBinding : interfaceBindings) { // recurse up interfaces
+ ITypeBinding result = findTypeInHierarchy(interfaceBinding, searchTypeName, visited);
+ if (result != null) {
+ return result;
+ }
+ }
+
+ ITypeBinding superBinding = typeBinding.getSuperclass();
+ if (superBinding != null) { // recurse up superclasses
+ ITypeBinding result = findTypeInHierarchy(superBinding, searchTypeName, visited);
+ if (result != null) {
+ return result;
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/AbstractAnnotationAdapter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/AbstractAnnotationAdapter.java
new file mode 100644
index 0000000000..09631dee2e
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/AbstractAnnotationAdapter.java
@@ -0,0 +1,165 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 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.utility.jdt;
+
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.Annotation;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jpt.core.utility.jdt.AnnotationAdapter;
+import org.eclipse.jpt.core.utility.jdt.DeclarationAnnotationAdapter;
+import org.eclipse.jpt.core.utility.jdt.Member;
+import org.eclipse.jpt.core.utility.jdt.ModifiedDeclaration;
+import org.eclipse.jpt.utility.internal.StringTools;
+
+/**
+ * Adapt a member and a declaration annotation adapter.
+ */
+public abstract class AbstractAnnotationAdapter implements AnnotationAdapter {
+ private final Member member;
+ private final DeclarationAnnotationAdapter daa;
+
+
+ // ********** constructor **********
+
+ public AbstractAnnotationAdapter(Member member, DeclarationAnnotationAdapter daa) {
+ super();
+ this.member = member;
+ this.daa = daa;
+ }
+
+
+ // ********** AnnotationAdapter implementation **********
+
+ public Annotation getAnnotation(CompilationUnit astRoot) {
+ return this.daa.getAnnotation(this.member.getModifiedDeclaration(astRoot));
+ }
+
+ public void newMarkerAnnotation() {
+ this.edit(this.buildNewMarkerAnnotationEditor());
+ }
+
+ public void newSingleMemberAnnotation() {
+ this.edit(this.buildNewSingleMemberAnnotationEditor());
+ }
+
+ public void newNormalAnnotation() {
+ this.edit(this.buildNewNormalAnnotationEditor());
+ }
+
+ public void removeAnnotation() {
+ this.edit(this.buildRemoveAnnotationEditor());
+ }
+
+ public ASTNode getAstNode(CompilationUnit astRoot) {
+ return this.daa.getAstNode(this.member.getModifiedDeclaration(astRoot));
+ }
+
+ @Override
+ public String toString() {
+ return StringTools.buildToStringFor(this, this.daa);
+ }
+
+
+ // ********** internal methods **********
+
+ protected void edit(Member.Editor editor) {
+ this.member.edit(editor);
+ }
+
+
+ // ********** factory methods **********
+
+ protected Member.Editor buildNewMarkerAnnotationEditor() {
+ return new NewMarkerAnnotationEditor(this.daa);
+ }
+
+ protected Member.Editor buildNewSingleMemberAnnotationEditor() {
+ return new NewSingleMemberAnnotationEditor(this.daa);
+ }
+
+ protected Member.Editor buildNewNormalAnnotationEditor() {
+ return new NewNormalAnnotationEditor(this.daa);
+ }
+
+ protected Member.Editor buildRemoveAnnotationEditor() {
+ return new RemoveAnnotationEditor(this.daa);
+ }
+
+
+ // ********** member classes **********
+
+ protected static class NewMarkerAnnotationEditor implements Member.Editor {
+ private final DeclarationAnnotationAdapter daa;
+
+ NewMarkerAnnotationEditor(DeclarationAnnotationAdapter daa) {
+ super();
+ this.daa = daa;
+ }
+ public void edit(ModifiedDeclaration declaration) {
+ this.daa.newMarkerAnnotation(declaration);
+ }
+ @Override
+ public String toString() {
+ return StringTools.buildToStringFor(this);
+ }
+ }
+
+
+ protected static class NewSingleMemberAnnotationEditor implements Member.Editor {
+ private final DeclarationAnnotationAdapter daa;
+
+ NewSingleMemberAnnotationEditor(DeclarationAnnotationAdapter daa) {
+ super();
+ this.daa = daa;
+ }
+ public void edit(ModifiedDeclaration declaration) {
+ this.daa.newSingleMemberAnnotation(declaration);
+ }
+ @Override
+ public String toString() {
+ return StringTools.buildToStringFor(this);
+ }
+ }
+
+
+ protected static class NewNormalAnnotationEditor implements Member.Editor {
+ private final DeclarationAnnotationAdapter daa;
+
+ NewNormalAnnotationEditor(DeclarationAnnotationAdapter daa) {
+ super();
+ this.daa = daa;
+ }
+ public void edit(ModifiedDeclaration declaration) {
+ this.daa.newNormalAnnotation(declaration);
+ }
+ @Override
+ public String toString() {
+ return StringTools.buildToStringFor(this);
+ }
+ }
+
+
+ protected static class RemoveAnnotationEditor implements Member.Editor {
+ private final DeclarationAnnotationAdapter daa;
+
+ RemoveAnnotationEditor(DeclarationAnnotationAdapter daa) {
+ super();
+ this.daa = daa;
+ }
+ public void edit(ModifiedDeclaration declaration) {
+ this.daa.removeAnnotation(declaration);
+ }
+ @Override
+ public String toString() {
+ return StringTools.buildToStringFor(this);
+ }
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/AbstractDeclarationAnnotationAdapter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/AbstractDeclarationAnnotationAdapter.java
new file mode 100644
index 0000000000..85c4983f16
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/AbstractDeclarationAnnotationAdapter.java
@@ -0,0 +1,153 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 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.utility.jdt;
+
+import java.util.List;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.Annotation;
+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;
+import org.eclipse.jpt.core.utility.jdt.DeclarationAnnotationAdapter;
+import org.eclipse.jpt.core.utility.jdt.ModifiedDeclaration;
+import org.eclipse.jpt.utility.internal.StringTools;
+
+/**
+ *
+ */
+public abstract class AbstractDeclarationAnnotationAdapter implements DeclarationAnnotationAdapter {
+ private final String annotationName;
+
+
+ // ********** constructors **********
+
+ protected AbstractDeclarationAnnotationAdapter(String annotationName) {
+ super();
+ this.annotationName = annotationName;
+ }
+
+
+ // ********** DeclarationAnnotationAdapter implementation **********
+
+ public MarkerAnnotation newMarkerAnnotation(ModifiedDeclaration declaration) {
+ MarkerAnnotation annotation = this.newMarkerAnnotation(declaration.getAst());
+ this.addAnnotationAndImport(declaration, annotation);
+ return annotation;
+ }
+
+ public SingleMemberAnnotation newSingleMemberAnnotation(ModifiedDeclaration declaration) {
+ SingleMemberAnnotation annotation = this.newSingleMemberAnnotation(declaration.getAst());
+ this.addAnnotationAndImport(declaration, annotation);
+ return annotation;
+ }
+
+ public NormalAnnotation newNormalAnnotation(ModifiedDeclaration declaration) {
+ NormalAnnotation annotation = this.newNormalAnnotation(declaration.getAst());
+ this.addAnnotationAndImport(declaration, annotation);
+ return annotation;
+ }
+
+ /**
+ * Add the appropriate import statement, then shorten the annotation's
+ * name before adding it to the declaration.
+ */
+ protected void addAnnotationAndImport(ModifiedDeclaration declaration, Annotation annotation) {
+ annotation.setTypeName(declaration.getAst().newName(this.getSourceCodeAnnotationName(declaration)));
+ this.addAnnotation(declaration, annotation);
+ }
+
+ /**
+ * Return the annotation's name as it can be used in source code;
+ * i.e. if we can add it to the compilation unit's imports, return the short
+ * name; if we can't (because of a collision), return the fully-qualified name.
+ * NB: an import may be added as a side-effect :-(
+ */
+ protected String getSourceCodeAnnotationName(ModifiedDeclaration declaration) {
+ return declaration.addImport(this.annotationName) ? this.getShortAnnotationName() : this.annotationName;
+ }
+
+ @Override
+ public String toString() {
+ return StringTools.buildToStringFor(this, this.annotationName);
+ }
+
+ /**
+ * Add the specified annotation to the specified declaration,
+ * replacing the original annotation if present.
+ */
+ protected abstract void addAnnotation(ModifiedDeclaration declaration, Annotation annotation);
+
+
+ // ********** public methods **********
+
+ /**
+ * This is 'public' because we use it in CombinationIndexedDeclarationAnnotationAdapter
+ * to get the annotation name from a NestedIndexedDeclarationAnnotationAdapter.
+ */
+ public String getAnnotationName() {
+ return this.annotationName;
+ }
+
+
+ // ********** helper methods **********
+
+ protected boolean nameMatches(ModifiedDeclaration declaration, Annotation annotation) {
+ return this.nameMatches(declaration, annotation, this.annotationName);
+ }
+
+ protected boolean nameMatches(ModifiedDeclaration declaration, Annotation annotation, String name) {
+ return declaration.annotationIsNamed(annotation, name);
+ }
+
+ protected MarkerAnnotation newMarkerAnnotation(AST ast) {
+ return this.newMarkerAnnotation(ast, this.annotationName);
+ }
+
+ protected MarkerAnnotation newMarkerAnnotation(AST ast, String name) {
+ MarkerAnnotation annotation = ast.newMarkerAnnotation();
+ annotation.setTypeName(ast.newName(name));
+ return annotation;
+ }
+
+ protected SingleMemberAnnotation newSingleMemberAnnotation(AST ast) {
+ return this.newSingleMemberAnnotation(ast, this.annotationName);
+ }
+
+ protected SingleMemberAnnotation newSingleMemberAnnotation(AST ast, String name) {
+ SingleMemberAnnotation annotation = ast.newSingleMemberAnnotation();
+ annotation.setTypeName(ast.newName(name));
+ return annotation;
+ }
+
+ protected NormalAnnotation newNormalAnnotation(AST ast) {
+ return this.newNormalAnnotation(ast, this.annotationName);
+ }
+
+ protected NormalAnnotation newNormalAnnotation(AST ast, String name) {
+ NormalAnnotation annotation = ast.newNormalAnnotation();
+ annotation.setTypeName(ast.newName(name));
+ return annotation;
+ }
+
+ protected String getShortAnnotationName() {
+ return convertToShortName(this.annotationName);
+ }
+
+ protected static String convertToShortName(String name) {
+ return name.substring(name.lastIndexOf('.') + 1);
+ }
+
+ @SuppressWarnings("unchecked")
+ protected List<MemberValuePair> values(NormalAnnotation na) {
+ return na.values();
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/AbstractExpressionConverter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/AbstractExpressionConverter.java
new file mode 100644
index 0000000000..a4bde672a8
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/AbstractExpressionConverter.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 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.utility.jdt;
+
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jpt.core.utility.jdt.ExpressionConverter;
+import org.eclipse.jpt.utility.internal.StringTools;
+
+/**
+ * Gather together the common implementation behavior.
+ * T is the type of the object to be converted to and from an expression.
+ *
+ * We're still figuring out Java Generics here.... The methods in this abstract
+ * class work fine with any subclass of Expression E; but a ClassCastException
+ * will occur as soon as we call any method implemented by a subclass
+ * (e.g. StringExpressionConverter) that expects a particular subclass of
+ * Expression (e.g. StringLiteral).
+ */
+public abstract class AbstractExpressionConverter<T>
+ implements ExpressionConverter<T>
+{
+
+ protected AbstractExpressionConverter() {
+ super();
+ }
+
+
+ // ********** object -> expression **********
+
+ public Expression convert(T object, AST ast) {
+ return (object == null) ? this.convertNull(ast) : this.convertObject(object, ast);
+ }
+
+ /**
+ * Return the expression for a null object. By default, a null object will
+ * be converted into a null expression.
+ */
+ protected Expression convertNull(@SuppressWarnings("unused") AST ast) {
+ return null;
+ }
+
+ /**
+ * The specified object is not null.
+ * @see #convert(T, AST)
+ */
+ protected abstract Expression convertObject(T object, AST ast);
+
+
+ // ********** expression -> object **********
+
+ public T convert(Expression expression) {
+ return (expression == null) ? this.convertNull() : this.convertExpression(expression);
+ }
+
+ /**
+ * Return the object for a null expression. By default, a null expression will
+ * be converted into a null object.
+ */
+ protected T convertNull() {
+ return null;
+ }
+
+ /**
+ * The specified expression is not null.
+ * @see #convert(Expression)
+ */
+ protected abstract T convertExpression(Expression expression);
+
+ @Override
+ public String toString() {
+ return StringTools.buildToStringFor(this);
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/AbstractNestedDeclarationAnnotationAdapter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/AbstractNestedDeclarationAnnotationAdapter.java
new file mode 100644
index 0000000000..9606e65926
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/AbstractNestedDeclarationAnnotationAdapter.java
@@ -0,0 +1,422 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 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.utility.jdt;
+
+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;
+import org.eclipse.jpt.core.utility.jdt.DeclarationAnnotationAdapter;
+import org.eclipse.jpt.core.utility.jdt.ModifiedDeclaration;
+
+/**
+ * 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;
+
+ // reduce NLS checks
+ protected static final String VALUE = "value"; //$NON-NLS-1$
+
+
+ // ********** 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;
+ }
+
+
+ // ********** DeclarationAnnotationAdapter implementation **********
+
+ 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;
+ }
+
+ 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);
+ }
+ }
+
+ public ASTNode getAstNode(ModifiedDeclaration declaration) {
+ // if the annotation is missing, return the outer annotation's node
+ Annotation annotation = this.getAnnotation(declaration);
+ return (annotation != null) ? annotation : this.outerAnnotationAdapter.getAstNode(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); //$NON-NLS-1$
+ }
+ }
+
+
+ // ********** 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); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * 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>
+ * &#64;Outer(name="Fred", foo=&#64;Inner) => &#64;Outer(name="Fred")
+ * &#64;Outer(foo=&#64;Inner) => &#64;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, @SuppressWarnings("unused") 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, @SuppressWarnings("unused") 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>
+ * &#64;Outer(&#64;Inner)
+ * </pre>
+ * or
+ * <pre>
+ * &#64;Outer(foo=&#64;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, @SuppressWarnings("unused") 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>
+ * &#64;Outer("lorem ipsum") => &#64;Outer(value="lorem ipsum", foo=&#64;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>
+ * &#64;Outer(bar="lorem ipsum") => &#64;Outer(bar="lorem ipsum", foo=&#64;Inner)
+ * </pre>
+ * or
+ * <pre>
+ * &#64;Outer(foo=&#64;Inner("lorem ipsum")) => &#64;Outer(foo=&#64;Inner)
+ * </pre>
+ */
+ protected void modifyAnnotation(@SuppressWarnings("unused") 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);
+ }
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/AnnotationStringArrayExpressionConverter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/AnnotationStringArrayExpressionConverter.java
new file mode 100644
index 0000000000..889b59ea63
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/AnnotationStringArrayExpressionConverter.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 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.utility.jdt;
+
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ArrayInitializer;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jpt.core.utility.jdt.ExpressionConverter;
+
+/**
+ * Convert an array initializer or single expression to/from an array of
+ * strings (e.g. {"text0", "text1"}).
+ * E is the type of the expressions to be found either standalone or
+ * as elements in the array initializer.
+ */
+public class AnnotationStringArrayExpressionConverter
+ extends AbstractExpressionConverter<String[]>
+{
+ private final ExpressionConverter<String> elementConverter;
+ private final StringArrayExpressionConverter arrayConverter;
+
+
+ /**
+ * The default behavior is to remove the array initializer if it is empty.
+ */
+ public AnnotationStringArrayExpressionConverter(ExpressionConverter<String> elementConverter) {
+ this(elementConverter, true);
+ }
+
+ public AnnotationStringArrayExpressionConverter(ExpressionConverter<String> elementConverter, boolean removeArrayInitializerWhenEmpty) {
+ super();
+ this.elementConverter = elementConverter;
+ this.arrayConverter = new StringArrayExpressionConverter(elementConverter, removeArrayInitializerWhenEmpty);
+ }
+
+ /**
+ * if we only have a single string in the array return the single expression,
+ * without braces, instead of an array initializer
+ */
+ @Override
+ protected Expression convertObject(String[] strings, AST ast) {
+ return (strings.length == 1) ?
+ this.elementConverter.convert(strings[0], ast)
+ :
+ this.arrayConverter.convertObject(strings, ast);
+ }
+
+ @Override
+ protected String[] convertNull() {
+ return this.arrayConverter.convertNull();
+ }
+
+ /**
+ * check for a single expression with no surrounding braces, implying a
+ * single-entry array
+ */
+ @Override
+ protected String[] convertExpression(Expression expression) {
+ return (expression.getNodeType() == ASTNode.ARRAY_INITIALIZER) ?
+ this.arrayConverter.convertArrayInitializer((ArrayInitializer) expression)
+ :
+ new String[] {this.elementConverter.convert(expression)};
+ }
+
+
+ // ********** factory methods **********
+
+ /**
+ * Build an expression converter for an annotation element of type String[].
+ * @Foo(bar={"text0", "text1"})
+ * or
+ * @Foo(bar="text0")
+ */
+ public static AnnotationStringArrayExpressionConverter forStrings() {
+ return new AnnotationStringArrayExpressionConverter(StringExpressionConverter.instance());
+ }
+
+ /**
+ * Build an expression converter for an annotation element of type <enum>[].
+ * @Foo(bar={BAZ, BAT})
+ * or
+ * @Foo(bar=BAZ)
+ */
+ public static AnnotationStringArrayExpressionConverter forNames() {
+ return new AnnotationStringArrayExpressionConverter(NameStringExpressionConverter.instance());
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/BooleanExpressionConverter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/BooleanExpressionConverter.java
new file mode 100644
index 0000000000..d6fc40d886
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/BooleanExpressionConverter.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 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.utility.jdt;
+
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.BooleanLiteral;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jpt.core.utility.jdt.ExpressionConverter;
+
+/**
+ * Convert a boolean literal to/from a Boolean
+ * (e.g. Boolean.TRUE).
+ */
+public final class BooleanExpressionConverter
+ extends AbstractExpressionConverter<Boolean>
+{
+ private static final ExpressionConverter<Boolean> INSTANCE = new BooleanExpressionConverter();
+
+ /**
+ * Return the singleton.
+ */
+ public static ExpressionConverter<Boolean> instance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Ensure single instance.
+ */
+ private BooleanExpressionConverter() {
+ super();
+ }
+
+ @Override
+ protected BooleanLiteral convertObject(Boolean booleanObject, AST ast) {
+ return ast.newBooleanLiteral(booleanObject.booleanValue());
+ }
+
+ @Override
+ protected Boolean convertExpression(Expression expression) {
+ Object value = expression.resolveConstantExpressionValue();
+ return (value instanceof Boolean) ? ((Boolean) value) : null;
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/CharacterStringExpressionConverter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/CharacterStringExpressionConverter.java
new file mode 100644
index 0000000000..1da206b188
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/CharacterStringExpressionConverter.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 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.utility.jdt;
+
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.CharacterLiteral;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jpt.core.utility.jdt.ExpressionConverter;
+
+/**
+ * Convert a character literal to/from a string representation of a character
+ * (e.g. "A").
+ */
+public final class CharacterStringExpressionConverter
+ extends AbstractExpressionConverter<String>
+{
+ private static final ExpressionConverter<String> INSTANCE = new CharacterStringExpressionConverter();
+
+ /**
+ * Return the singleton.
+ */
+ public static ExpressionConverter<String> instance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Ensure single instance.
+ */
+ private CharacterStringExpressionConverter() {
+ super();
+ }
+
+ @Override
+ protected CharacterLiteral convertObject(String string, AST ast) {
+ CharacterLiteral characterLiteral = ast.newCharacterLiteral();
+ characterLiteral.setCharValue(string.charAt(0));
+ return characterLiteral;
+ }
+
+ @Override
+ protected String convertExpression(Expression expression) {
+ Object value = expression.resolveConstantExpressionValue();
+ return (value instanceof Character) ? ((Character) value).toString() : null;
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/CombinationIndexedDeclarationAnnotationAdapter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/CombinationIndexedDeclarationAnnotationAdapter.java
new file mode 100644
index 0000000000..e43a30e97c
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/CombinationIndexedDeclarationAnnotationAdapter.java
@@ -0,0 +1,496 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 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.utility.jdt;
+
+import java.util.List;
+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;
+import org.eclipse.jpt.core.utility.jdt.DeclarationAnnotationAdapter;
+import org.eclipse.jpt.core.utility.jdt.IndexedDeclarationAnnotationAdapter;
+import org.eclipse.jpt.core.utility.jdt.ModifiedDeclaration;
+import org.eclipse.jpt.utility.internal.StringTools;
+
+/**
+ * Manipulate an annotation that either occurs stand-alone, e.g.
+ * <pre>
+ * &#64;Inner("zero")
+ * private int id;
+ * </pre>
+ *
+ * or is embedded in an element array within another annotation, e.g.
+ * <pre>
+ * &#64;Outer(foo={&#64;Inner("zero"), &#64;Inner("one"), &#64;Inner("two")})
+ * private int id;
+ *
+ * annotationName = "Inner"
+ * containerAnnotationName = "Outer"
+ * elementName = "foo"
+ * index = 0-2
+ * </pre>
+ *
+ * This is a useful pattern because a declaration cannot have more
+ * than one annotation of the same type, and allowing the stand-alone
+ * configuration reduces clutter.
+ * <br>
+ * NB: This configuration only makes sense for "top-level" annotations, as
+ * opposed to "nested" annotations. This is because annotation elements
+ * can only be declared with a type of a single annotation and annotations
+ * cannot be part of an inheritance hierarchy.
+ * For example, the following configurations cannot *both* be supported:
+ * <pre>
+ * &#64;Foo(bar=&#64;Outer(...))
+ * private int id;
+ *
+ * &#64;Foo(bar=&#64;Inner(...)) // not allowed
+ * private int id;
+ * </pre>
+ *
+ * NB: Behavior is undefined when both the stand-alone and the nested
+ * configurations annotate the same declaration, e.g.
+ * <pre>
+ * // undefined behavior
+ * &#64;Inner("zero")
+ * &#64;Outer(foo={&#64;Inner("zero"), &#64;Inner("one"), &#64;Inner("two")})
+ * private int id;
+ * </pre>
+ */
+public class CombinationIndexedDeclarationAnnotationAdapter
+ implements IndexedDeclarationAnnotationAdapter
+{
+
+ /**
+ * this adapter is used when the annotation is "stand-alone":
+ * <pre>
+ * &#64;Inner("zero")
+ * </pre>
+ * and is only used when the index is 0 or 1
+ */
+ private final SimpleDeclarationAnnotationAdapter standAloneAnnotationAdapter;
+
+ /**
+ * this adapter is used when the annotation is "nested":
+ * <pre>
+ * &#64;Outer(foo={&#64;Inner("zero"), &#64;Inner("one")})
+ * </pre>
+ */
+ private final NestedIndexedDeclarationAnnotationAdapter nestedAnnotationAdapter;
+
+ /**
+ * this adapter is for the "nested" annotation at the zero index;
+ * and is only used when the index is 1
+ */
+ private final NestedIndexedDeclarationAnnotationAdapter zeroNestedAnnotationAdapter;
+
+ // reduce NLS checks
+ protected static final String VALUE = "value"; //$NON-NLS-1$
+
+
+ // ********** constructors **********
+
+ /**
+ * default element name is "value"
+ * <pre>
+ * &#64;Inner("zero")
+ * &#64;Outer({&#64;Inner("zero"), &#64;Inner("one")})
+ * </pre>
+ */
+ public CombinationIndexedDeclarationAnnotationAdapter(String annotationName, String containerAnnotationName, int index) {
+ this(annotationName, containerAnnotationName, VALUE, index);
+ }
+
+ public CombinationIndexedDeclarationAnnotationAdapter(String annotationName, String containerAnnotationName, String elementName, int index) {
+ this(new SimpleDeclarationAnnotationAdapter(annotationName), new SimpleDeclarationAnnotationAdapter(containerAnnotationName), elementName, index, annotationName);
+ }
+
+ /**
+ * default element name is "value"
+ */
+ public CombinationIndexedDeclarationAnnotationAdapter(
+ SimpleDeclarationAnnotationAdapter standAloneAnnotationAdapter,
+ SimpleDeclarationAnnotationAdapter containerAnnotationAdapter,
+ int index,
+ String nestedAnnotationName
+ ) {
+ this(standAloneAnnotationAdapter, containerAnnotationAdapter, VALUE, index, nestedAnnotationName);
+ }
+
+ public CombinationIndexedDeclarationAnnotationAdapter(
+ SimpleDeclarationAnnotationAdapter standAloneAnnotationAdapter,
+ SimpleDeclarationAnnotationAdapter containerAnnotationAdapter,
+ String elementName,
+ int index,
+ String nestedAnnotationName
+ ) {
+ super();
+ this.standAloneAnnotationAdapter = standAloneAnnotationAdapter;
+ this.nestedAnnotationAdapter = new NestedIndexedDeclarationAnnotationAdapter(containerAnnotationAdapter, elementName, index, nestedAnnotationName);
+ this.zeroNestedAnnotationAdapter = new NestedIndexedDeclarationAnnotationAdapter(containerAnnotationAdapter, elementName, 0, nestedAnnotationName);
+ }
+
+
+ // ********** DeclarationAnnotationAdapter implementation **********
+
+ public Annotation getAnnotation(ModifiedDeclaration declaration) {
+ if (this.getIndex() == 0) {
+ // check for the stand-alone annotation
+ Annotation standAloneAnnotation = this.getStandAloneAnnotation(declaration);
+ if (standAloneAnnotation != null) {
+ return standAloneAnnotation;
+ }
+ }
+ return this.getNestedAnnotation(declaration);
+ }
+
+ /**
+ * <pre>
+ * [none] => &#64;Inner
+ * or
+ * &#64;Inner("lorem ipsum") => &#64;Inner
+ * or
+ * &#64;Inner(text="lorem ipsum") => &#64;Inner
+ * or
+ * &#64;Outer(foo={&#64;Inner, &#64;Inner}) => &#64;Outer(foo={&#64;Inner, &#64;Inner, &#64;Inner})
+ * or
+ * &#64;Outer(foo=&#64;Inner) => &#64;Outer(foo={&#64;Inner, &#64;Inner})
+ * or
+ * &#64;Inner => &#64;Outer(foo={&#64;Inner, &#64;Inner})
+ * etc.
+ * </pre>
+ */
+ public MarkerAnnotation newMarkerAnnotation(ModifiedDeclaration declaration) {
+ return (MarkerAnnotation) this.newAnnotation(MARKER_ANNOTATION_FACTORY, declaration);
+ }
+
+ public SingleMemberAnnotation newSingleMemberAnnotation(ModifiedDeclaration declaration) {
+ return (SingleMemberAnnotation) this.newAnnotation(SINGLE_MEMBER_ANNOTATION_FACTORY, declaration);
+ }
+
+ public NormalAnnotation newNormalAnnotation(ModifiedDeclaration declaration) {
+ return (NormalAnnotation) this.newAnnotation(NORMAL_ANNOTATION_FACTORY, declaration);
+ }
+
+ public void removeAnnotation(ModifiedDeclaration declaration) {
+ if (this.getIndex() == 0) {
+ // check for the stand-alone annotation
+ if (this.standAloneAnnotationIsPresent(declaration)) {
+ this.removeStandAloneAnnotation(declaration);
+ return;
+ }
+ }
+ this.removeNestedAnnotation(declaration);
+ if (this.nestedElementCanBeConvertedToStandAlone(declaration)) {
+ this.convertLastElementAnnotationToStandAloneAnnotation(declaration);
+ }
+ }
+
+ public ASTNode getAstNode(ModifiedDeclaration declaration) {
+ // if the annotation is missing, delegate to the nested annotation adapter
+ Annotation annotation = this.getAnnotation(declaration);
+ return (annotation != null) ? annotation : this.nestedAnnotationAdapter.getAstNode(declaration);
+ }
+
+ @Override
+ public String toString() {
+ return StringTools.buildToStringFor(this, this.getAnnotationName());
+ }
+
+
+ // ********** IndexedDeclarationAnnotationAdapter implementation **********
+
+ public int getIndex() {
+ return this.nestedAnnotationAdapter.getIndex();
+ }
+
+ public void moveAnnotation(int newIndex, ModifiedDeclaration declaration) {
+ int oldIndex = this.getIndex();
+ if (newIndex == oldIndex) {
+ return;
+ }
+
+ Annotation standAloneAnnotation = this.getStandAloneAnnotation(declaration);
+ if (standAloneAnnotation == null) {
+ this.moveNestedAnnotation(newIndex, declaration);
+ if (this.nestedElementCanBeConvertedToStandAlone(declaration)) {
+ this.convertLastElementAnnotationToStandAloneAnnotation(declaration);
+ }
+ } else {
+ if ((oldIndex == 0) && (newIndex == 1)) {
+ // this is one of two situations where we transition from standalone to container
+ this.moveStandAloneAnnotationToContainerAnnotation(standAloneAnnotation, declaration);
+ this.moveNestedAnnotation(newIndex, declaration);
+ } else if (newIndex == 0) {
+ // we are moving a 'null' entry on top of the standalone, so remove it
+ this.removeStandAloneAnnotation(declaration);
+ } else {
+ throw new IllegalStateException("old index = " + oldIndex + "; new index = " + newIndex); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ }
+ }
+
+
+ // ********** internal methods **********
+
+ /**
+ * build the appropriate new annotation,
+ * which may require moving the 0th annotation from "stand-alone" to "nested"
+ */
+ private Annotation newAnnotation(AnnotationFactory annotationFactory, ModifiedDeclaration declaration) {
+ if (this.getIndex() == 0) {
+ return this.newZeroAnnotation(annotationFactory, declaration);
+ }
+ if (this.zeroNestedAnnotationIsPresent(declaration)) {
+ // manipulate the container annotation - ignore the stand-alone annotation(?)
+ // @Outer(foo=@Inner("zero")) => @Outer(foo={@Inner("zero"), @Inner})
+ // or
+ // @Outer(foo={@Inner("zero"), @Inner("one")}) => @Outer(foo={@Inner("zero"), @Inner})
+ return annotationFactory.newAnnotation(this.nestedAnnotationAdapter, declaration);
+ }
+
+ // this is one of two situations where we transition from standalone to container
+ this.moveStandAloneAnnotationToContainerAnnotation(declaration);
+ // once the stand-alone annotation is moved to index=0, build the new annotation at index=1
+ return annotationFactory.newAnnotation(this.nestedAnnotationAdapter, declaration);
+ }
+
+ /**
+ * the index is 0 - build the appropriate new annotation,
+ * which may be either "stand-alone" or "nested"
+ */
+ private Annotation newZeroAnnotation(AnnotationFactory annotationFactory, ModifiedDeclaration declaration) {
+ if (this.standAloneAnnotationIsPresent(declaration)) {
+ // replace the stand-alone annotation - ignore the container annotation(?)
+ // @Inner(text="lorem ipsum") => @Inner
+ return annotationFactory.newAnnotation(this.standAloneAnnotationAdapter, declaration);
+ }
+ if (this.containerAnnotationIsPresent(declaration)) {
+ // manipulate the container annotation
+ // @Outer(foo=@Inner(text="lorem ipsum")) => @Outer(foo=@Inner)
+ return annotationFactory.newAnnotation(this.nestedAnnotationAdapter, declaration);
+ }
+ // neither annotation is present - add a new stand-alone annotation
+ return annotationFactory.newAnnotation(this.standAloneAnnotationAdapter, declaration);
+ }
+
+ /**
+ * move the stand-alone annotation to the container annotation at index=0
+ */
+ private void moveStandAloneAnnotationToContainerAnnotation(ModifiedDeclaration declaration) {
+ Annotation standAloneAnnotation = this.getStandAloneAnnotation(declaration);
+ if (standAloneAnnotation == null) {
+ throw new IllegalStateException("the stand-alone annotation is missing"); //$NON-NLS-1$
+ }
+ this.moveStandAloneAnnotationToContainerAnnotation(standAloneAnnotation, declaration);
+ }
+
+ /**
+ * move the specified, non-null, stand-alone annotation to
+ * the container annotation at index=0
+ */
+ private void moveStandAloneAnnotationToContainerAnnotation(Annotation standAloneAnnotation, ModifiedDeclaration declaration) {
+ if (standAloneAnnotation.isMarkerAnnotation()) {
+ this.zeroNestedAnnotationAdapter.newMarkerAnnotation(declaration);
+ } else if (standAloneAnnotation.isSingleMemberAnnotation()) {
+ Expression vv = ((SingleMemberAnnotation) standAloneAnnotation).getValue();
+ vv = (Expression) ASTNode.copySubtree(vv.getAST(), vv);
+ this.zeroNestedAnnotationAdapter.newSingleMemberAnnotation(declaration).setValue(vv);
+ } else if (standAloneAnnotation.isNormalAnnotation()) {
+ NormalAnnotation newNA = this.zeroNestedAnnotationAdapter.newNormalAnnotation(declaration);
+ List<MemberValuePair> values = this.values(newNA);
+ for (MemberValuePair pair : this.values((NormalAnnotation) standAloneAnnotation)) {
+ values.add((MemberValuePair) ASTNode.copySubtree(pair.getAST(), pair));
+ }
+ } else {
+ throw new IllegalStateException("unknown annotation type: " + standAloneAnnotation); //$NON-NLS-1$
+ }
+ this.removeStandAloneAnnotation(declaration);
+ }
+
+ /**
+ * return whether the "nested" annotation container has been reduced to
+ * a single element (and the array initializer is converted to just
+ * the single remaining element) and can be further converted to the
+ * "stand-alone" annotation:
+ * <pre>
+ * &#64;Outer(foo={&#64;Inner("zero"), &#64;Inner("one")}) =>
+ * &#64;Outer(foo=&#64;Inner("zero")) =>
+ * &#64;Inner("zero")
+ * </pre>
+ */
+ private boolean nestedElementCanBeConvertedToStandAlone(ModifiedDeclaration declaration) {
+ Annotation containerAnnotation = this.getContainerAnnotation(declaration);
+ if (containerAnnotation == null) {
+ return false;
+ }
+ if (containerAnnotation.isMarkerAnnotation()) {
+ return false;
+ }
+ if (containerAnnotation.isSingleMemberAnnotation()) {
+ if (this.getElementName().equals(VALUE)) {
+ return (((SingleMemberAnnotation) containerAnnotation).getValue().getNodeType() != ASTNode.ARRAY_INITIALIZER)
+ && (this.zeroNestedAnnotationAdapter.getAnnotation(declaration) != null);
+ }
+ return false;
+ }
+ if (containerAnnotation.isNormalAnnotation()) {
+ NormalAnnotation na = (NormalAnnotation) containerAnnotation;
+ if (na.values().size() == 0) {
+ return false; // there are no elements present
+ }
+ if (na.values().size() != 1) {
+ return false; // there are other elements present - leave them all alone
+ }
+ MemberValuePair pair = (MemberValuePair) na.values().get(0);
+ if (this.getElementName().equals(pair.getName().getFullyQualifiedName())) {
+ return (pair.getValue().getNodeType() != ASTNode.ARRAY_INITIALIZER)
+ && (this.zeroNestedAnnotationAdapter.getAnnotation(declaration) != null);
+ }
+ return false;
+ }
+ throw new IllegalStateException("unknown annotation type: " + containerAnnotation); //$NON-NLS-1$
+ }
+
+ /**
+ * move the annotation in the container annotation at index=0
+ * to the stand-alone annotation
+ */
+ private void convertLastElementAnnotationToStandAloneAnnotation(ModifiedDeclaration declaration) {
+ Annotation last = this.zeroNestedAnnotationAdapter.getAnnotation(declaration);
+ if (last == null) {
+ throw new IllegalStateException("the last nested annotation is missing"); //$NON-NLS-1$
+ } else if (last.isMarkerAnnotation()) {
+ this.newStandAloneMarkerAnnotation(declaration);
+ } else if (last.isSingleMemberAnnotation()) {
+ Expression vv = ((SingleMemberAnnotation) last).getValue();
+ vv = (Expression) ASTNode.copySubtree(vv.getAST(), vv);
+ this.newStandAloneSingleMemberAnnotation(declaration).setValue(vv);
+ } else if (last.isNormalAnnotation()) {
+ NormalAnnotation newNA = this.newStandAloneNormalAnnotation(declaration);
+ List<MemberValuePair> values = this.values(newNA);
+ for (MemberValuePair pair : this.values((NormalAnnotation) last)) {
+ values.add((MemberValuePair) ASTNode.copySubtree(pair.getAST(), pair));
+ }
+ } else {
+ throw new IllegalStateException("unknown annotation type: " + last); //$NON-NLS-1$
+ }
+ this.zeroNestedAnnotationAdapter.removeAnnotation(declaration);
+ }
+
+ private boolean standAloneAnnotationIsPresent(ModifiedDeclaration declaration) {
+ return this.getStandAloneAnnotation(declaration) != null;
+ }
+
+ private Annotation getStandAloneAnnotation(ModifiedDeclaration declaration) {
+ return this.standAloneAnnotationAdapter.getAnnotation(declaration);
+ }
+
+ private MarkerAnnotation newStandAloneMarkerAnnotation(ModifiedDeclaration declaration) {
+ return this.standAloneAnnotationAdapter.newMarkerAnnotation(declaration);
+ }
+
+ private SingleMemberAnnotation newStandAloneSingleMemberAnnotation(ModifiedDeclaration declaration) {
+ return this.standAloneAnnotationAdapter.newSingleMemberAnnotation(declaration);
+ }
+
+ private NormalAnnotation newStandAloneNormalAnnotation(ModifiedDeclaration declaration) {
+ return this.standAloneAnnotationAdapter.newNormalAnnotation(declaration);
+ }
+
+ private void removeStandAloneAnnotation(ModifiedDeclaration declaration) {
+ this.standAloneAnnotationAdapter.removeAnnotation(declaration);
+ }
+
+ private Annotation getNestedAnnotation(ModifiedDeclaration declaration) {
+ return this.nestedAnnotationAdapter.getAnnotation(declaration);
+ }
+
+ private void moveNestedAnnotation(int newIndex, ModifiedDeclaration declaration) {
+ this.nestedAnnotationAdapter.moveAnnotation(newIndex, declaration);
+ }
+
+ private void removeNestedAnnotation(ModifiedDeclaration declaration) {
+ this.nestedAnnotationAdapter.removeAnnotation(declaration);
+ }
+
+ private boolean containerAnnotationIsPresent(ModifiedDeclaration declaration) {
+ return this.getContainerAnnotation(declaration) != null;
+ }
+
+ private Annotation getContainerAnnotation(ModifiedDeclaration declaration) {
+ return this.nestedAnnotationAdapter.getOuterAnnotationAdapter().getAnnotation(declaration);
+ }
+
+ private boolean zeroNestedAnnotationIsPresent(ModifiedDeclaration declaration) {
+ return this.getZeroNestedAnnotation(declaration) != null;
+ }
+
+ private Annotation getZeroNestedAnnotation(ModifiedDeclaration declaration) {
+ return this.zeroNestedAnnotationAdapter.getAnnotation(declaration);
+ }
+
+ private String getAnnotationName() {
+ return this.nestedAnnotationAdapter.getAnnotationName();
+ }
+
+ private String getElementName() {
+ return this.nestedAnnotationAdapter.getElementName();
+ }
+
+ @SuppressWarnings("unchecked")
+ protected List<MemberValuePair> values(NormalAnnotation na) {
+ return na.values();
+ }
+
+
+ // ********** annotation factories **********
+
+ /**
+ * define interface that allows us to "re-use" the nasty code in
+ * #newAnnotation(AnnotationFactory, ModifiedDeclaration)
+ */
+ private interface AnnotationFactory {
+ Annotation newAnnotation(DeclarationAnnotationAdapter adapter, ModifiedDeclaration declaration);
+ }
+
+ private static final AnnotationFactory MARKER_ANNOTATION_FACTORY = new AnnotationFactory() {
+ public Annotation newAnnotation(DeclarationAnnotationAdapter adapter, ModifiedDeclaration declaration) {
+ return adapter.newMarkerAnnotation(declaration);
+ }
+ @Override
+ public String toString() {
+ return "MarkerAnnotationFactory"; //$NON-NLS-1$
+ }
+ };
+
+ private static final AnnotationFactory SINGLE_MEMBER_ANNOTATION_FACTORY = new AnnotationFactory() {
+ public Annotation newAnnotation(DeclarationAnnotationAdapter adapter, ModifiedDeclaration declaration) {
+ return adapter.newSingleMemberAnnotation(declaration);
+ }
+ @Override
+ public String toString() {
+ return "SingleMemberAnnotationFactory"; //$NON-NLS-1$
+ }
+ };
+
+ private static final AnnotationFactory NORMAL_ANNOTATION_FACTORY = new AnnotationFactory() {
+ public Annotation newAnnotation(DeclarationAnnotationAdapter adapter, ModifiedDeclaration declaration) {
+ return adapter.newNormalAnnotation(declaration);
+ }
+ @Override
+ public String toString() {
+ return "NormalAnnotationFactory"; //$NON-NLS-1$
+ }
+ };
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/ConversionDeclarationAnnotationElementAdapter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/ConversionDeclarationAnnotationElementAdapter.java
new file mode 100644
index 0000000000..8a70c2230c
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/ConversionDeclarationAnnotationElementAdapter.java
@@ -0,0 +1,203 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 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.utility.jdt;
+
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jpt.core.utility.jdt.DeclarationAnnotationAdapter;
+import org.eclipse.jpt.core.utility.jdt.DeclarationAnnotationElementAdapter;
+import org.eclipse.jpt.core.utility.jdt.ExpressionConverter;
+import org.eclipse.jpt.core.utility.jdt.ModifiedDeclaration;
+import org.eclipse.jpt.utility.internal.StringTools;
+
+/**
+ * Wrap a declaration annotation element adapter that deals with AST
+ * expressions, converting them to/from various other objects.
+ * T is the type of the object to be passed to and returned by the adapter.
+ */
+public class ConversionDeclarationAnnotationElementAdapter<T>
+ implements DeclarationAnnotationElementAdapter<T>
+{
+ /**
+ * The wrapped adapter that returns and takes AST expressions.
+ */
+ private final DeclarationAnnotationElementAdapter<Expression> adapter;
+
+ /**
+ * The converter that converts AST expressions to other objects
+ * (e.g. Strings).
+ */
+ private final ExpressionConverter<T> converter;
+
+
+ // ********** constructors **********
+
+ /**
+ * The default element name is "value"; the default behavior is to
+ * remove the annotation when the last element is removed.
+ */
+ public ConversionDeclarationAnnotationElementAdapter(DeclarationAnnotationAdapter annotationAdapter, ExpressionConverter<T> converter) {
+ this(new ExpressionDeclarationAnnotationElementAdapter<Expression>(annotationAdapter), converter);
+ }
+
+ /**
+ * The default behavior is to remove the annotation when the last
+ * element is removed.
+ */
+ public ConversionDeclarationAnnotationElementAdapter(DeclarationAnnotationAdapter annotationAdapter, String elementName, ExpressionConverter<T> converter) {
+ this(new ExpressionDeclarationAnnotationElementAdapter<Expression>(annotationAdapter, elementName), converter);
+ }
+
+ public ConversionDeclarationAnnotationElementAdapter(DeclarationAnnotationAdapter annotationAdapter, String elementName, boolean removeAnnotationWhenEmpty, ExpressionConverter<T> converter) {
+ this(new ExpressionDeclarationAnnotationElementAdapter<Expression>(annotationAdapter, elementName, removeAnnotationWhenEmpty), converter);
+ }
+
+ public ConversionDeclarationAnnotationElementAdapter(DeclarationAnnotationElementAdapter<Expression> adapter, ExpressionConverter<T> converter) {
+ super();
+ this.adapter = adapter;
+ this.converter = converter;
+ }
+
+
+ // ********** DeclarationAnnotationElementAdapter implementation **********
+
+ public T getValue(ModifiedDeclaration declaration) {
+ Expression expression = this.adapter.getValue(declaration);
+ return this.converter.convert(expression);
+ }
+
+ public void setValue(T value, ModifiedDeclaration declaration) {
+ Expression expression;
+ try {
+ expression = this.converter.convert(value, declaration.getAst());
+ } catch (IllegalArgumentException ex) {
+ // if there is a problem converting the 'value' to an Expression we get this exception
+ return; // don't set the value if it is "illegal"
+ }
+ this.adapter.setValue(expression, declaration);
+ }
+
+ public Expression getExpression(ModifiedDeclaration declaration) {
+ return this.adapter.getExpression(declaration);
+ }
+
+ public ASTNode getAstNode(ModifiedDeclaration declaration) {
+ return this.adapter.getAstNode(declaration);
+ }
+
+ @Override
+ public String toString() {
+ return StringTools.buildToStringFor(this, this.adapter);
+ }
+
+
+ // ********** factory static methods **********
+
+ /**
+ * The default element name is "value"; the default behavior is to
+ * remove the annotation when the last element is removed;
+ * the default expression converter expects string constant expressions.
+ */
+ public static ConversionDeclarationAnnotationElementAdapter<String> forStrings(DeclarationAnnotationAdapter annotationAdapter) {
+ return new ConversionDeclarationAnnotationElementAdapter<String>(annotationAdapter, StringExpressionConverter.instance());
+ }
+
+ /**
+ * The default behavior is to remove the annotation when the last
+ * element is removed; the default expression converter expects
+ * string constant expressions.
+ */
+ public static ConversionDeclarationAnnotationElementAdapter<String> forStrings(DeclarationAnnotationAdapter annotationAdapter, String elementName) {
+ return new ConversionDeclarationAnnotationElementAdapter<String>(annotationAdapter, elementName, StringExpressionConverter.instance());
+ }
+
+ /**
+ * The default expression converter expects string constant expressions.
+ */
+ public static ConversionDeclarationAnnotationElementAdapter<String> forStrings(DeclarationAnnotationAdapter annotationAdapter, String elementName, boolean removeAnnotationWhenEmpty) {
+ return new ConversionDeclarationAnnotationElementAdapter<String>(annotationAdapter, elementName, removeAnnotationWhenEmpty, StringExpressionConverter.instance());
+ }
+
+ /**
+ * The default element name is "value"; the default behavior is to
+ * remove the annotation when the last element is removed;
+ * the default expression converter expects number constant expressions.
+ */
+ public static ConversionDeclarationAnnotationElementAdapter<Integer> forNumbers(DeclarationAnnotationAdapter annotationAdapter) {
+ return new ConversionDeclarationAnnotationElementAdapter<Integer>(annotationAdapter, NumberIntegerExpressionConverter.instance());
+ }
+
+ /**
+ * The default behavior is to remove the annotation when the last
+ * element is removed; the default expression converter expects
+ * number constant expressions.
+ */
+ public static ConversionDeclarationAnnotationElementAdapter<Integer> forNumbers(DeclarationAnnotationAdapter annotationAdapter, String elementName) {
+ return new ConversionDeclarationAnnotationElementAdapter<Integer>(annotationAdapter, elementName, NumberIntegerExpressionConverter.instance());
+ }
+
+ /**
+ * The default expression converter expects number constant expressions.
+ */
+ public static ConversionDeclarationAnnotationElementAdapter<Integer> forNumbers(DeclarationAnnotationAdapter annotationAdapter, String elementName, boolean removeAnnotationWhenEmpty) {
+ return new ConversionDeclarationAnnotationElementAdapter<Integer>(annotationAdapter, elementName, removeAnnotationWhenEmpty, NumberIntegerExpressionConverter.instance());
+ }
+
+ /**
+ * The default element name is "value"; the default behavior is to
+ * remove the annotation when the last element is removed;
+ * the default expression converter expects boolean constant expressions.
+ */
+ public static ConversionDeclarationAnnotationElementAdapter<Boolean> forBooleans(DeclarationAnnotationAdapter annotationAdapter) {
+ return new ConversionDeclarationAnnotationElementAdapter<Boolean>(annotationAdapter, BooleanExpressionConverter.instance());
+ }
+
+ /**
+ * The default behavior is to remove the annotation when the last
+ * element is removed; the default expression converter expects
+ * boolean constant expressions.
+ */
+ public static ConversionDeclarationAnnotationElementAdapter<Boolean> forBooleans(DeclarationAnnotationAdapter annotationAdapter, String elementName) {
+ return new ConversionDeclarationAnnotationElementAdapter<Boolean>(annotationAdapter, elementName, BooleanExpressionConverter.instance());
+ }
+
+ /**
+ * The default expression converter expects boolean constant expressions.
+ */
+ public static ConversionDeclarationAnnotationElementAdapter<Boolean> forBooleans(DeclarationAnnotationAdapter annotationAdapter, String elementName, boolean removeAnnotationWhenEmpty) {
+ return new ConversionDeclarationAnnotationElementAdapter<Boolean>(annotationAdapter, elementName, removeAnnotationWhenEmpty, BooleanExpressionConverter.instance());
+ }
+
+ /**
+ * The default element name is "value"; the default behavior is to
+ * remove the annotation when the last element is removed;
+ * the default expression converter expects character constant expressions.
+ */
+ public static ConversionDeclarationAnnotationElementAdapter<String> forCharacters(DeclarationAnnotationAdapter annotationAdapter) {
+ return new ConversionDeclarationAnnotationElementAdapter<String>(annotationAdapter, CharacterStringExpressionConverter.instance());
+ }
+
+ /**
+ * The default behavior is to remove the annotation when the last
+ * element is removed; the default expression converter expects
+ * character constant expressions.
+ */
+ public static ConversionDeclarationAnnotationElementAdapter<String> forCharacters(DeclarationAnnotationAdapter annotationAdapter, String elementName) {
+ return new ConversionDeclarationAnnotationElementAdapter<String>(annotationAdapter, elementName, CharacterStringExpressionConverter.instance());
+ }
+
+ /**
+ * The default expression converter expects character constant expressions.
+ */
+ public static ConversionDeclarationAnnotationElementAdapter<String> forCharacters(DeclarationAnnotationAdapter annotationAdapter, String elementName, boolean removeAnnotationWhenEmpty) {
+ return new ConversionDeclarationAnnotationElementAdapter<String>(annotationAdapter, elementName, removeAnnotationWhenEmpty, CharacterStringExpressionConverter.instance());
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/DefaultAnnotationEditFormatter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/DefaultAnnotationEditFormatter.java
new file mode 100644
index 0000000000..0f6cfd917d
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/DefaultAnnotationEditFormatter.java
@@ -0,0 +1,219 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2008 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.utility.jdt;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jpt.core.utility.jdt.AnnotationEditFormatter;
+import org.eclipse.text.edits.InsertEdit;
+import org.eclipse.text.edits.MalformedTreeException;
+import org.eclipse.text.edits.MultiTextEdit;
+import org.eclipse.text.edits.ReplaceEdit;
+import org.eclipse.text.edits.TextEdit;
+
+/**
+ * This implementation will clean up some of the nasty Eclipse annotation
+ * formatting (or lack thereof); e.g. arrays of annotations.
+ */
+public final class DefaultAnnotationEditFormatter
+ implements AnnotationEditFormatter
+{
+ private static final DefaultAnnotationEditFormatter INSTANCE = new DefaultAnnotationEditFormatter();
+
+ /**
+ * Return the singleton.
+ */
+ public static DefaultAnnotationEditFormatter instance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Ensure single instance.
+ */
+ private DefaultAnnotationEditFormatter() {
+ super();
+ }
+
+ /**
+ * TODO
+ */
+ public void format(IDocument doc, TextEdit editTree) throws MalformedTreeException, BadLocationException {
+ TextEdit[] edits = editTree.getChildren();
+ int len = edits.length;
+ if (len == 0) {
+ return;
+ }
+
+ MultiTextEdit extraEdits = new MultiTextEdit();
+ for (int i = 0; i < len; i++) {
+ TextEdit edit1 = edits[i];
+ if ( ! (edit1 instanceof InsertEdit)) {
+ continue; // if the edit is not an insert, skip to the next edit
+ }
+ InsertEdit insert1 = (InsertEdit) edit1;
+ int j = i + 1;
+ if (j < len) {
+ TextEdit edit2 = edits[j];
+ if (edit2 instanceof InsertEdit) {
+ InsertEdit insert2 = (InsertEdit) edit2;
+ String text1 = insert1.getText();
+ String text2 = insert2.getText();
+ int offset1 = insert1.getOffset();
+ int offset2 = insert2.getOffset();
+ if (this.stringIsAnnotation(text1) && text2.equals(" ")) {
+ // an annotation was inserted before something on the same line;
+ // replace the trailing space with a newline and appropriate indent
+ extraEdits.addChild(new ReplaceEdit(offset2, 1, this.buildCR(doc, offset2)));
+ i++; // jump the index past 'edit2'
+ continue; // go to the next edit
+ }
+ int comma1Length = this.commaLength(text1);
+ if ((comma1Length != 0) && this.stringIsAnnotation(text2)) {
+ // an annotation was inserted in an array initializer on the
+ // same line as the previous array element;
+ // replace the preceding space with a newline and appropriate indent
+ extraEdits.addChild(new ReplaceEdit(offset1 + comma1Length, text1.length() - comma1Length, this.buildCR(doc, offset1)));
+ i++; // jump the index past 'edit2'
+ continue; // go to the next edit
+ }
+ }
+ }
+ this.formatArrayInitializer(doc, insert1, extraEdits);
+ }
+ extraEdits.apply(doc, TextEdit.NONE);
+ }
+
+ /**
+ * If the insert edit is inserting an annotation containing an array of annotations as
+ * its value then format them nicely.
+ */
+ private void formatArrayInitializer(IDocument doc, InsertEdit insertEdit, MultiTextEdit extraEdits) throws BadLocationException {
+ String s = insertEdit.getText();
+ if ( ! this.stringIsAnnotation(s)) {
+ return;
+ }
+ int len = s.length();
+ int pos = 1; // skip '@'
+ while (pos < len) {
+ char c = s.charAt(pos);
+ pos++; // bump to just past first '('
+ if (c == '(') {
+ break;
+ }
+ }
+ if (pos == len) {
+ return; // reached end of string
+ }
+ while (pos < len) {
+ char c = s.charAt(pos);
+ pos++; // bump to just past first '{'
+ if (c == '{') {
+ break;
+ }
+ if (c != ' ') {
+ return;
+ }
+ }
+ if (pos == len) {
+ return; // reached end of string
+ }
+ // now look for '@' not inside parentheses and put in
+ // line delimeter and indent string before each
+ int offset = insertEdit.getOffset();
+ String indent = null;
+ int parenDepth = 0;
+ while (pos < len) {
+ switch (s.charAt(pos)) {
+ case '(' :
+ parenDepth++;
+ break;
+ case ')' :
+ parenDepth--;
+ break;
+ case '@' :
+ if (parenDepth == 0) {
+ if (indent == null) {
+ indent = this.buildCR(doc, offset, "\t"); // TODO use tab preference?
+ }
+ extraEdits.addChild(new InsertEdit(offset + pos, indent));
+ }
+ break;
+ case '}' :
+ if (parenDepth == 0) {
+ extraEdits.addChild(new InsertEdit(offset + pos, this.buildCR(doc, offset)));
+ }
+ break;
+ }
+ pos++;
+ }
+ }
+
+ /**
+ * Build a string containing a line delimeter and indenting characters
+ * matching the indent level of the line containing the character offset
+ * (i.e. the new line's indent matches the current line).
+ */
+ private String buildCR(IDocument doc, int offset) throws BadLocationException {
+ return this.buildCR(doc, offset, "");
+ }
+
+ private String buildCR(IDocument doc, int offset, String suffix) throws BadLocationException {
+ int line = doc.getLineOfOffset(offset);
+ StringBuilder sb = new StringBuilder();
+ sb.append(doc.getLineDelimiter(line)); // use same CR as current line
+
+ int o = doc.getLineOffset(line); // match the whitespace of the current line
+ char c = doc.getChar(o++);
+ while ((c == ' ') || (c == '\t')) {
+ sb.append(c);
+ c = doc.getChar(o++);
+ }
+ sb.append(suffix);
+ return sb.toString();
+ }
+
+ /**
+ * Return whether the specified string is an annotation.
+ */
+ private boolean stringIsAnnotation(String string) {
+ return (string.length() > 1) && string.charAt(0) == '@';
+ }
+
+ /**
+ * If the specified string is a single comma, possibly surrounded by
+ * spaces, return the length of the substring containing the
+ * initial spaces and the comma.
+ */
+ private int commaLength(String string) {
+ boolean comma = false;
+ int len = string.length();
+ int result = 0;
+ for (int i = 0; i < len; i++) {
+ switch (string.charAt(i)) {
+ case ' ' :
+ if ( ! comma) {
+ result++; // space preceding comma
+ }
+ break;
+ case ',' :
+ if (comma) {
+ return 0; // second comma!
+ }
+ comma = true;
+ result++;
+ break;
+ default:
+ return 0; // non-comma, non-space char
+ }
+ }
+ return result;
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/EnumArrayDeclarationAnnotationElementAdapter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/EnumArrayDeclarationAnnotationElementAdapter.java
new file mode 100644
index 0000000000..92c8728bc5
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/EnumArrayDeclarationAnnotationElementAdapter.java
@@ -0,0 +1,159 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 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.utility.jdt;
+
+import java.util.List;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ArrayInitializer;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jpt.core.utility.jdt.DeclarationAnnotationAdapter;
+import org.eclipse.jpt.core.utility.jdt.DeclarationAnnotationElementAdapter;
+import org.eclipse.jpt.core.utility.jdt.ExpressionConverter;
+import org.eclipse.jpt.core.utility.jdt.ModifiedDeclaration;
+
+/**
+ * Wrap a declaration annotation element adapter and simply
+ * add an import for the enums when necessary.
+ */
+public class EnumArrayDeclarationAnnotationElementAdapter
+ implements DeclarationAnnotationElementAdapter<String[]>
+{
+ /**
+ * The wrapped adapter that returns and takes name strings (enums).
+ */
+ private final ConversionDeclarationAnnotationElementAdapter<String[]> adapter;
+
+ private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+
+ // ********** constructors **********
+
+ /**
+ * The default element name is "value"; the default behavior is to
+ * remove the annotation when the last element is removed.
+ */
+ public EnumArrayDeclarationAnnotationElementAdapter(DeclarationAnnotationAdapter annotationAdapter) {
+ this(annotationAdapter, VALUE);
+ }
+
+ /**
+ * The default behavior is to remove the annotation when the last
+ * element is removed and remove the array initializer if it is empty.
+ */
+ public EnumArrayDeclarationAnnotationElementAdapter(DeclarationAnnotationAdapter annotationAdapter, String elementName) {
+ this(annotationAdapter, elementName, true);
+ }
+
+ /**
+ * The default behavior is to remove the array initializer if it is empty.
+ */
+ public EnumArrayDeclarationAnnotationElementAdapter(DeclarationAnnotationAdapter annotationAdapter, String elementName, boolean removeAnnotationWhenEmpty) {
+ this(annotationAdapter, elementName, removeAnnotationWhenEmpty, true);
+ }
+
+ public EnumArrayDeclarationAnnotationElementAdapter(DeclarationAnnotationAdapter annotationAdapter, String elementName, boolean removeAnnotationWhenEmpty, boolean removeArrayInitializerWhenEmpty) {
+ this(new ConversionDeclarationAnnotationElementAdapter<String[]>(annotationAdapter, elementName, removeAnnotationWhenEmpty, buildExpressionConverter(removeArrayInitializerWhenEmpty)));
+ }
+
+ private static ExpressionConverter<String[]> buildExpressionConverter(boolean removeArrayInitializerWhenEmpty) {
+ return new AnnotationStringArrayExpressionConverter(NameStringExpressionConverter.instance(), removeArrayInitializerWhenEmpty);
+ }
+
+ protected EnumArrayDeclarationAnnotationElementAdapter(ConversionDeclarationAnnotationElementAdapter<String[]> adapter) {
+ super();
+ this.adapter = adapter;
+ }
+
+
+ // ********** DeclarationAnnotationElementAdapter implementation **********
+
+ public String[] getValue(ModifiedDeclaration declaration) {
+ // ignore the adapter's getValue() - we want the expression
+ return this.resolve(this.adapter.getExpression(declaration), declaration);
+ }
+
+ public void setValue(String[] value, ModifiedDeclaration declaration) {
+ this.adapter.setValue(this.convertToSourceCodeNames(value, declaration), declaration);
+ }
+
+ public Expression getExpression(ModifiedDeclaration declaration) {
+ return this.adapter.getExpression(declaration);
+ }
+
+ public ASTNode getAstNode(ModifiedDeclaration declaration) {
+ return this.adapter.getAstNode(declaration);
+ }
+
+
+ // ********** internal methods **********
+
+ /**
+ * resolve the enums, which can be
+ * null
+ * or
+ * {FOO, BAR, BAZ}
+ * or
+ * FOO
+ */
+ protected String[] resolve(Expression expression, ModifiedDeclaration declaration) {
+ if (expression == null) {
+ return EMPTY_STRING_ARRAY;
+ } else if (expression.getNodeType() == ASTNode.ARRAY_INITIALIZER) {
+ return this.resolveArray((ArrayInitializer) expression, declaration);
+ } else {
+ return this.resolveSingleElement(expression, declaration);
+ }
+ }
+
+ protected String[] resolveArray(ArrayInitializer ai, @SuppressWarnings("unused") ModifiedDeclaration declaration) {
+ List<Expression> expressions = this.expressions(ai);
+ int len = expressions.size();
+ String[] enums = new String[len];
+ for (int i = len; i-- > 0; ) {
+ enums[i] = this.resolveEnum(expressions.get(i));
+ }
+ return enums;
+ }
+
+ protected String[] resolveSingleElement(Expression enumExpression, @SuppressWarnings("unused") ModifiedDeclaration declaration) {
+ return new String[] {this.resolveEnum(enumExpression)};
+ }
+
+ protected String resolveEnum(Expression expression) {
+ return ASTTools.resolveEnum(expression);
+ }
+
+ // minimize scope of suppressd warnings
+ @SuppressWarnings("unchecked")
+ private List<Expression> expressions(ArrayInitializer arrayInitializer) {
+ return arrayInitializer.expressions();
+ }
+
+ /**
+ * convert the fully-qualified enums to names that can be inserted in source code
+ * NB: imports may be added as a side-effect :-(
+ */
+ protected String[] convertToSourceCodeNames(String[] enums, ModifiedDeclaration declaration) {
+ if (enums == null) {
+ return null;
+ }
+ int len = enums.length;
+ String[] sourceCodeNames = new String[len];
+ for (int i = 0; i < len; i++) {
+ sourceCodeNames[i] = this.convertToSourceCodeName(enums[i], declaration);
+ }
+ return sourceCodeNames;
+ }
+
+ protected String convertToSourceCodeName(String enum_, ModifiedDeclaration declaration) {
+ return EnumDeclarationAnnotationElementAdapter.convertToSourceCodeName(enum_, declaration);
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/EnumDeclarationAnnotationElementAdapter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/EnumDeclarationAnnotationElementAdapter.java
new file mode 100644
index 0000000000..c9ccb8d0a0
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/EnumDeclarationAnnotationElementAdapter.java
@@ -0,0 +1,128 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2010 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.utility.jdt;
+
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jpt.core.utility.jdt.DeclarationAnnotationAdapter;
+import org.eclipse.jpt.core.utility.jdt.DeclarationAnnotationElementAdapter;
+import org.eclipse.jpt.core.utility.jdt.ModifiedDeclaration;
+
+/**
+ * Wrap a declaration annotation element adapter and simply
+ * add an import for the enum when necessary.
+ */
+public class EnumDeclarationAnnotationElementAdapter
+ implements DeclarationAnnotationElementAdapter<String>
+{
+ /**
+ * The wrapped adapter that returns and takes name strings (enums).
+ */
+ private final ConversionDeclarationAnnotationElementAdapter<String> adapter;
+
+
+ // ********** constructors **********
+
+ /**
+ * The default element name is "value"; the default behavior is to
+ * remove the annotation when the last element is removed.
+ */
+ public EnumDeclarationAnnotationElementAdapter(DeclarationAnnotationAdapter annotationAdapter) {
+ this(annotationAdapter, VALUE);
+ }
+
+ /**
+ * The default behavior is to remove the annotation when the last
+ * element is removed.
+ */
+ public EnumDeclarationAnnotationElementAdapter(DeclarationAnnotationAdapter annotationAdapter, String elementName) {
+ this(annotationAdapter, elementName, true);
+ }
+
+ public EnumDeclarationAnnotationElementAdapter(DeclarationAnnotationAdapter annotationAdapter, String elementName, boolean removeAnnotationWhenEmpty) {
+ this(new ConversionDeclarationAnnotationElementAdapter<String>(annotationAdapter, elementName, removeAnnotationWhenEmpty, NameStringExpressionConverter.instance()));
+ }
+
+ protected EnumDeclarationAnnotationElementAdapter(ConversionDeclarationAnnotationElementAdapter<String> adapter) {
+ super();
+ this.adapter = adapter;
+ }
+
+
+ // ********** DeclarationAnnotationElementAdapter implementation **********
+
+ public String getValue(ModifiedDeclaration declaration) {
+ return this.resolve(this.adapter.getExpression(declaration));
+ }
+
+ public void setValue(String value, ModifiedDeclaration declaration) {
+ this.adapter.setValue(convertToSourceCodeName(value, declaration), declaration);
+ }
+
+ public Expression getExpression(ModifiedDeclaration declaration) {
+ return this.adapter.getExpression(declaration);
+ }
+
+ public ASTNode getAstNode(ModifiedDeclaration declaration) {
+ return this.adapter.getAstNode(declaration);
+ }
+
+
+ // ********** internal methods **********
+
+ /**
+ * resolve the enum
+ */
+ protected String resolve(Expression expression) {
+ return ASTTools.resolveEnum(expression);
+ }
+
+ /**
+ * convert the fully-qualified enum constant to a static import and the constant's short name, e.g.
+ * static import javax.persistence.FetchType.EAGER;
+ * return "EAGER"
+ * if that doesn't work, convert to a normal import and the constant's partially-qualified name, e.g.
+ * import javax.persistence.FetchType;
+ * return "FetchType.EAGER"
+ * if that doesn't work, simply return the constant's fully-qualified name, e.g.
+ * return "javax.persistence.FetchType.EAGER"
+ * NB: an import may be added as a side-effect :-(
+ */
+ protected static String convertToSourceCodeName(String enumConstantName, ModifiedDeclaration declaration) {
+ return (enumConstantName == null) ? null : convertToSourceCodeName_(enumConstantName, declaration);
+ }
+
+ /**
+ * pre-condition: enum constant name is non-null;
+ * convert it to its short version if we can add a static import etc.
+ */
+ protected static String convertToSourceCodeName_(String enumConstantName, ModifiedDeclaration declaration) {
+ if (declaration.addStaticImport(enumConstantName)) {
+ return convertToShortName(enumConstantName);
+ }
+ if (declaration.addImport(convertToTypeName(enumConstantName))) {
+ return convertToPartiallyQualifiedName(enumConstantName);
+ }
+ return enumConstantName;
+ }
+
+ protected static String convertToShortName(String name) {
+ return name.substring(name.lastIndexOf('.') + 1);
+ }
+
+ protected static String convertToTypeName(String name) {
+ return name.substring(0, name.lastIndexOf('.'));
+ }
+
+ protected static String convertToPartiallyQualifiedName(String name) {
+ return name.substring(name.lastIndexOf('.', name.lastIndexOf('.') - 1) + 1);
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/ExpressionDeclarationAnnotationElementAdapter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/ExpressionDeclarationAnnotationElementAdapter.java
new file mode 100644
index 0000000000..b96a7de027
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/ExpressionDeclarationAnnotationElementAdapter.java
@@ -0,0 +1,362 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 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.utility.jdt;
+
+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;
+import org.eclipse.jpt.core.utility.jdt.DeclarationAnnotationAdapter;
+import org.eclipse.jpt.core.utility.jdt.DeclarationAnnotationElementAdapter;
+import org.eclipse.jpt.core.utility.jdt.ModifiedDeclaration;
+import org.eclipse.jpt.utility.internal.StringTools;
+
+/**
+ * Most obvious implementation of the interface.
+ * Assume the element's value is an Expression.
+ */
+public class ExpressionDeclarationAnnotationElementAdapter<E extends Expression>
+ implements DeclarationAnnotationElementAdapter<E>
+{
+ /**
+ * Adapter used to manipulate the element's annotation.
+ */
+ private final DeclarationAnnotationAdapter annotationAdapter;
+
+ /**
+ * The name of the relevant annotation element.
+ */
+ private final String elementName;
+
+ /**
+ * Flag to indicate whether the element's annotation is to be
+ * completely removed if, when the element itself is removed,
+ * the annotation has no remaining elements.
+ */
+ private final boolean removeAnnotationWhenEmpty;
+
+
+ // ********** constructors **********
+
+ /**
+ * The default element name is "value"; the default behavior is to
+ * remove the annotation when the last element is removed.
+ */
+ public ExpressionDeclarationAnnotationElementAdapter(DeclarationAnnotationAdapter annotationAdapter) {
+ this(annotationAdapter, VALUE);
+ }
+
+ /**
+ * The default element name is "value".
+ */
+ public ExpressionDeclarationAnnotationElementAdapter(DeclarationAnnotationAdapter annotationAdapter, boolean removeAnnotationWhenEmpty) {
+ this(annotationAdapter, VALUE, removeAnnotationWhenEmpty);
+ }
+
+ /**
+ * The default behavior is to remove the annotation when the last
+ * element is removed.
+ */
+ public ExpressionDeclarationAnnotationElementAdapter(DeclarationAnnotationAdapter annotationAdapter, String elementName) {
+ this(annotationAdapter, elementName, true);
+ }
+
+ public ExpressionDeclarationAnnotationElementAdapter(DeclarationAnnotationAdapter annotationAdapter, String elementName, boolean removeAnnotationWhenEmpty) {
+ super();
+ this.annotationAdapter = annotationAdapter;
+ this.elementName = elementName;
+ this.removeAnnotationWhenEmpty = removeAnnotationWhenEmpty;
+ }
+
+
+ // ********** DeclarationAnnotationElementAdapter implementation **********
+
+ public E getValue(ModifiedDeclaration declaration) {
+ // return the expression unmodified
+ return this.getExpression(declaration);
+ }
+
+ public void setValue(E value, ModifiedDeclaration declaration) {
+ this.setValue(value, this.annotationAdapter.getAnnotation(declaration), declaration);
+ }
+
+ public E getExpression(ModifiedDeclaration declaration) {
+ return this.expression(this.annotationAdapter.getAnnotation(declaration));
+ }
+
+ public ASTNode getAstNode(ModifiedDeclaration declaration) {
+ Expression exp = this.getExpression(declaration);
+ return (exp != null) ? exp : this.annotationAdapter.getAstNode(declaration);
+ }
+
+ @Override
+ public String toString() {
+ return StringTools.buildToStringFor(this, this.elementName);
+ }
+
+
+ // ********** expression **********
+
+ /**
+ * Return the expression 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 E expression(Annotation annotation) {
+ if (annotation == null) {
+ return this.expressionNoAnnotation();
+ }
+ if (annotation.isMarkerAnnotation()) {
+ return this.expressionMarkerAnnotation((MarkerAnnotation) annotation);
+ }
+ if (annotation.isSingleMemberAnnotation()) {
+ return this.expressionSingleMemberAnnotation((SingleMemberAnnotation) annotation);
+ }
+ if (annotation.isNormalAnnotation()) {
+ return this.expressionNormalAnnotation((NormalAnnotation) annotation);
+ }
+ throw new IllegalArgumentException("unknown annotation type: " + annotation); //$NON-NLS-1$
+ }
+
+ protected E expressionNoAnnotation() {
+ return null;
+ }
+
+ /**
+ * Return the expression value of the *first* annotation element
+ * with the adapter's element name.
+ * Return null if the annotation has no such element.
+ */
+ protected E expressionMarkerAnnotation(@SuppressWarnings("unused") MarkerAnnotation annotation) {
+ return null;
+ }
+
+ /**
+ * Return the expression value of the *first* annotation element
+ * with the adapter's element name.
+ * Return null if the annotation has no such element.
+ */
+ protected E expressionSingleMemberAnnotation(SingleMemberAnnotation annotation) {
+ return this.downcast(this.elementName.equals(VALUE) ? annotation.getValue() : null);
+ }
+
+ @SuppressWarnings("unchecked")
+ private E downcast(Expression e) {
+ return (E) e;
+ }
+
+ /**
+ * Return the expression value of the *first* annotation element
+ * with the adapter's element name.
+ * Return null if the annotation has no such element.
+ */
+ protected E expressionNormalAnnotation(NormalAnnotation annotation) {
+ MemberValuePair pair = this.memberValuePair(annotation);
+ return this.downcast((pair == null) ? null : pair.getValue());
+ }
+
+
+ // ********** set value **********
+
+ /**
+ * set non-null, non-empty value
+ */
+ protected void setValue(Expression value, Annotation annotation, ModifiedDeclaration declaration) {
+ if (value == null) {
+ this.removeElement(annotation, declaration);
+ }
+ else if (annotation == null) {
+ this.setValueNoAnnotation(value, declaration);
+ }
+ else if (annotation.isMarkerAnnotation()) {
+ this.setValueMarkerAnnotation(value, (MarkerAnnotation) annotation, declaration);
+ }
+ else if (annotation.isSingleMemberAnnotation()) {
+ this.setValueSingleMemberAnnotation(value, (SingleMemberAnnotation) annotation, declaration);
+ }
+ else if (annotation.isNormalAnnotation()) {
+ this.setValueNormalAnnotation(value, (NormalAnnotation) annotation, declaration);
+ }
+ else {
+ throw new IllegalArgumentException("unknown annotation type: " + annotation); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * add non-null, non-empty value
+ */
+ protected void setValueNoAnnotation(Expression value, ModifiedDeclaration declaration) {
+ if (this.elementName.equals(VALUE)) {
+ // @Foo("xxx")
+ this.annotationAdapter.newSingleMemberAnnotation(declaration).setValue(value);
+ } else {
+ // @Foo(bar="xxx")
+ this.addValue(value, this.annotationAdapter.newNormalAnnotation(declaration));
+ }
+ }
+
+ protected void addValue(Expression value, NormalAnnotation annotation) {
+ this.addValue(value, annotation, this.elementName);
+ }
+
+ protected void addValue(Expression value, NormalAnnotation annotation, String annotationElementName) {
+ AST ast = annotation.getAST();
+ MemberValuePair pair = ast.newMemberValuePair();
+ pair.setName(ast.newSimpleName(annotationElementName));
+ pair.setValue(value);
+ List<MemberValuePair> values = this.values(annotation);
+ values.add(pair);
+ }
+
+ protected void setValueMarkerAnnotation(Expression value, @SuppressWarnings("unused") MarkerAnnotation annotation, ModifiedDeclaration declaration) {
+ // @Foo => @Foo("xxx")
+ // or
+ // @Foo => @Foo(bar="xxx")
+ this.setValueNoAnnotation(value, declaration);
+ }
+
+ protected void setValueSingleMemberAnnotation(Expression value, SingleMemberAnnotation annotation, ModifiedDeclaration declaration) {
+ if (this.elementName.equals(VALUE)) {
+ // @Foo("yyy") => @Foo("xxx")
+ annotation.setValue(value);
+ } else {
+ // @Foo("yyy") => @Foo(value="yyy", bar="xxx")
+ Expression vv = annotation.getValue();
+ vv = (Expression) ASTNode.copySubtree(vv.getAST(), vv);
+ NormalAnnotation normalAnnotation = this.annotationAdapter.newNormalAnnotation(declaration);
+ this.addValue(vv, normalAnnotation, VALUE);
+ this.addValue(value, normalAnnotation);
+ }
+ }
+
+ protected void setValueNormalAnnotation(Expression value, NormalAnnotation annotation, @SuppressWarnings("unused") ModifiedDeclaration declaration) {
+ MemberValuePair pair = this.memberValuePair(annotation);
+ if (pair == null) {
+ this.addValue(value, annotation);
+ } else {
+ pair.setValue(value);
+ }
+ }
+
+
+ // ********** remove element **********
+
+ protected void removeElement(Annotation annotation, ModifiedDeclaration declaration) {
+ if (annotation == null) {
+ this.removeElementNoAnnotation(declaration);
+ }
+ else if (annotation.isMarkerAnnotation()) {
+ this.removeElementMarkerAnnotation((MarkerAnnotation) annotation, declaration);
+ }
+ else if (annotation.isSingleMemberAnnotation()) {
+ this.removeElementSingleMemberAnnotation((SingleMemberAnnotation) annotation, declaration);
+ }
+ else if (annotation.isNormalAnnotation()) {
+ this.removeElementNormalAnnotation((NormalAnnotation) annotation, declaration);
+ }
+ else {
+ throw new IllegalArgumentException("unknown annotation type: " + annotation); //$NON-NLS-1$
+ }
+ }
+
+ protected void removeElementNoAnnotation(@SuppressWarnings("unused") ModifiedDeclaration declaration) {
+ // the element is already gone (?)
+ }
+
+ protected void removeElementMarkerAnnotation(@SuppressWarnings("unused") MarkerAnnotation annotation, @SuppressWarnings("unused") ModifiedDeclaration declaration) {
+ // the element is already gone (?)
+ }
+
+ protected void removeElementSingleMemberAnnotation(@SuppressWarnings("unused") SingleMemberAnnotation annotation, ModifiedDeclaration declaration) {
+ if (this.elementName.equals(VALUE)) {
+ if (this.removeAnnotationWhenEmpty) {
+ // @Foo("xxx") =>
+ this.annotationAdapter.removeAnnotation(declaration);
+ } else {
+ // @Foo("xxx") => @Foo
+ this.annotationAdapter.newMarkerAnnotation(declaration);
+ }
+ } else {
+ // the [non-'value'] element is already gone (?)
+ }
+ }
+
+ protected void removeElementNormalAnnotation(NormalAnnotation annotation, ModifiedDeclaration declaration) {
+ List<MemberValuePair> values = this.values(annotation);
+ if ((values.size() == 1) && values.get(0).getName().getFullyQualifiedName().equals(this.elementName)) {
+ if (this.removeAnnotationWhenEmpty) {
+ // @Foo(bar="xxx") =>
+ this.annotationAdapter.removeAnnotation(declaration);
+ } else {
+ // @Foo(bar="xxx") => @Foo
+ this.annotationAdapter.newMarkerAnnotation(declaration);
+ }
+ } else {
+ this.removeElement(annotation);
+ if (values.size() == 1) {
+ MemberValuePair pair = values.get(0);
+ if (pair.getName().getFullyQualifiedName().equals(VALUE)) {
+ // @Foo(bar="xxx", value="yyy") => @Foo("yyy")
+ Expression vv = pair.getValue();
+ vv = (Expression) ASTNode.copySubtree(vv.getAST(), vv);
+ this.annotationAdapter.newSingleMemberAnnotation(declaration).setValue(vv);
+ } else {
+ // @Foo(bar="xxx", baz="yyy") => @Foo(baz="yyy")
+ }
+ } else {
+ // @Foo(bar="xxx", baz="yyy", joo="xxx") => @Foo(baz="yyy", joo="xxx")
+ }
+ }
+ }
+
+ /**
+ * Remove the *first* member value pair from the specified annotation 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();
+ }
+ }
+ }
+
+
+ // ********** convenience methods **********
+
+ /**
+ * Return the *first* member value pair for the specified annotation element
+ * with the adapter's element name.
+ * Return null if the 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;
+ }
+
+ @SuppressWarnings("unchecked")
+ protected List<MemberValuePair> values(NormalAnnotation na) {
+ return na.values();
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/GenericVisitor.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/GenericVisitor.java
new file mode 100644
index 0000000000..284d615e26
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/GenericVisitor.java
@@ -0,0 +1,791 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2008 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
+ *******************************************************************************/
+package org.eclipse.jpt.core.internal.utility.jdt;
+
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ASTVisitor;
+import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
+import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration;
+import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
+import org.eclipse.jdt.core.dom.ArrayAccess;
+import org.eclipse.jdt.core.dom.ArrayCreation;
+import org.eclipse.jdt.core.dom.ArrayInitializer;
+import org.eclipse.jdt.core.dom.ArrayType;
+import org.eclipse.jdt.core.dom.AssertStatement;
+import org.eclipse.jdt.core.dom.Assignment;
+import org.eclipse.jdt.core.dom.Block;
+import org.eclipse.jdt.core.dom.BlockComment;
+import org.eclipse.jdt.core.dom.BooleanLiteral;
+import org.eclipse.jdt.core.dom.BreakStatement;
+import org.eclipse.jdt.core.dom.CastExpression;
+import org.eclipse.jdt.core.dom.CatchClause;
+import org.eclipse.jdt.core.dom.CharacterLiteral;
+import org.eclipse.jdt.core.dom.ClassInstanceCreation;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.ConditionalExpression;
+import org.eclipse.jdt.core.dom.ConstructorInvocation;
+import org.eclipse.jdt.core.dom.ContinueStatement;
+import org.eclipse.jdt.core.dom.DoStatement;
+import org.eclipse.jdt.core.dom.EmptyStatement;
+import org.eclipse.jdt.core.dom.EnhancedForStatement;
+import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
+import org.eclipse.jdt.core.dom.EnumDeclaration;
+import org.eclipse.jdt.core.dom.ExpressionStatement;
+import org.eclipse.jdt.core.dom.FieldAccess;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.ForStatement;
+import org.eclipse.jdt.core.dom.IfStatement;
+import org.eclipse.jdt.core.dom.ImportDeclaration;
+import org.eclipse.jdt.core.dom.InfixExpression;
+import org.eclipse.jdt.core.dom.Initializer;
+import org.eclipse.jdt.core.dom.InstanceofExpression;
+import org.eclipse.jdt.core.dom.Javadoc;
+import org.eclipse.jdt.core.dom.LabeledStatement;
+import org.eclipse.jdt.core.dom.LineComment;
+import org.eclipse.jdt.core.dom.MarkerAnnotation;
+import org.eclipse.jdt.core.dom.MemberRef;
+import org.eclipse.jdt.core.dom.MemberValuePair;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.MethodInvocation;
+import org.eclipse.jdt.core.dom.MethodRef;
+import org.eclipse.jdt.core.dom.MethodRefParameter;
+import org.eclipse.jdt.core.dom.Modifier;
+import org.eclipse.jdt.core.dom.NormalAnnotation;
+import org.eclipse.jdt.core.dom.NullLiteral;
+import org.eclipse.jdt.core.dom.NumberLiteral;
+import org.eclipse.jdt.core.dom.PackageDeclaration;
+import org.eclipse.jdt.core.dom.ParameterizedType;
+import org.eclipse.jdt.core.dom.ParenthesizedExpression;
+import org.eclipse.jdt.core.dom.PostfixExpression;
+import org.eclipse.jdt.core.dom.PrefixExpression;
+import org.eclipse.jdt.core.dom.PrimitiveType;
+import org.eclipse.jdt.core.dom.QualifiedName;
+import org.eclipse.jdt.core.dom.QualifiedType;
+import org.eclipse.jdt.core.dom.ReturnStatement;
+import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SimpleType;
+import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.StringLiteral;
+import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
+import org.eclipse.jdt.core.dom.SuperFieldAccess;
+import org.eclipse.jdt.core.dom.SuperMethodInvocation;
+import org.eclipse.jdt.core.dom.SwitchCase;
+import org.eclipse.jdt.core.dom.SwitchStatement;
+import org.eclipse.jdt.core.dom.SynchronizedStatement;
+import org.eclipse.jdt.core.dom.TagElement;
+import org.eclipse.jdt.core.dom.TextElement;
+import org.eclipse.jdt.core.dom.ThisExpression;
+import org.eclipse.jdt.core.dom.ThrowStatement;
+import org.eclipse.jdt.core.dom.TryStatement;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jdt.core.dom.TypeDeclarationStatement;
+import org.eclipse.jdt.core.dom.TypeLiteral;
+import org.eclipse.jdt.core.dom.TypeParameter;
+import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
+import org.eclipse.jdt.core.dom.WhileStatement;
+import org.eclipse.jdt.core.dom.WildcardType;
+
+/**
+ * copied from org.eclipse.jdt.internal.corext.dom.GenericVisitor
+ */
+public class GenericVisitor extends ASTVisitor {
+
+ public GenericVisitor() {
+ super();
+ }
+
+ public GenericVisitor(boolean visitJavadocTags) {
+ super(visitJavadocTags);
+ }
+
+ // ********** hooks for subclasses **********
+
+ protected boolean visit_(@SuppressWarnings("unused") ASTNode node) {
+ return true;
+ }
+
+ protected void endVisit_(@SuppressWarnings("unused") ASTNode node) {
+ // do nothing
+ }
+
+ // ********** overrides **********
+
+ @Override
+ public boolean visit(AnonymousClassDeclaration node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(ArrayAccess node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(ArrayCreation node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(ArrayInitializer node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(ArrayType node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(AssertStatement node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(Assignment node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(Block node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(BooleanLiteral node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(BreakStatement node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(CastExpression node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(CatchClause node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(CharacterLiteral node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(ClassInstanceCreation node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(CompilationUnit node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(ConditionalExpression node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(ConstructorInvocation node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(ContinueStatement node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(DoStatement node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(EmptyStatement node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(ExpressionStatement node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(FieldAccess node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(FieldDeclaration node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(ForStatement node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(IfStatement node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(ImportDeclaration node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(InfixExpression node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(InstanceofExpression node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(Initializer node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(Javadoc node) {
+ return (super.visit(node)) ? visit_(node) : false;
+ }
+ @Override
+ public boolean visit(LabeledStatement node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(MethodDeclaration node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(MethodInvocation node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(NullLiteral node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(NumberLiteral node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(PackageDeclaration node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(ParenthesizedExpression node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(PostfixExpression node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(PrefixExpression node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(PrimitiveType node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(QualifiedName node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(ReturnStatement node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(SimpleName node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(SimpleType node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(StringLiteral node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(SuperConstructorInvocation node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(SuperFieldAccess node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(SuperMethodInvocation node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(SwitchCase node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(SwitchStatement node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(SynchronizedStatement node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(ThisExpression node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(ThrowStatement node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(TryStatement node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(TypeDeclaration node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(TypeDeclarationStatement node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(TypeLiteral node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(SingleVariableDeclaration node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(VariableDeclarationExpression node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(VariableDeclarationStatement node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(VariableDeclarationFragment node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(WhileStatement node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(AnnotationTypeDeclaration node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(AnnotationTypeMemberDeclaration node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(BlockComment node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(EnhancedForStatement node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(EnumConstantDeclaration node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(EnumDeclaration node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(LineComment node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(MarkerAnnotation node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(MemberRef node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(MemberValuePair node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(MethodRef node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(MethodRefParameter node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(Modifier node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(NormalAnnotation node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(ParameterizedType node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(QualifiedType node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(SingleMemberAnnotation node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(TagElement node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(TextElement node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(TypeParameter node) {
+ return visit_(node);
+ }
+ @Override
+ public boolean visit(WildcardType node) {
+ return visit_(node);
+ }
+
+ @Override
+ public void endVisit(AnonymousClassDeclaration node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(ArrayAccess node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(ArrayCreation node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(ArrayInitializer node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(ArrayType node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(AssertStatement node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(Assignment node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(Block node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(BooleanLiteral node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(BreakStatement node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(CastExpression node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(CatchClause node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(CharacterLiteral node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(ClassInstanceCreation node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(CompilationUnit node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(ConditionalExpression node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(ConstructorInvocation node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(ContinueStatement node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(DoStatement node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(EmptyStatement node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(ExpressionStatement node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(FieldAccess node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(FieldDeclaration node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(ForStatement node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(IfStatement node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(ImportDeclaration node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(InfixExpression node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(InstanceofExpression node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(Initializer node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(Javadoc node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(LabeledStatement node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(MethodDeclaration node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(MethodInvocation node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(NullLiteral node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(NumberLiteral node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(PackageDeclaration node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(ParenthesizedExpression node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(PostfixExpression node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(PrefixExpression node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(PrimitiveType node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(QualifiedName node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(ReturnStatement node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(SimpleName node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(SimpleType node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(StringLiteral node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(SuperConstructorInvocation node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(SuperFieldAccess node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(SuperMethodInvocation node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(SwitchCase node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(SwitchStatement node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(SynchronizedStatement node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(ThisExpression node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(ThrowStatement node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(TryStatement node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(TypeDeclaration node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(TypeDeclarationStatement node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(TypeLiteral node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(SingleVariableDeclaration node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(VariableDeclarationExpression node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(VariableDeclarationStatement node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(VariableDeclarationFragment node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(WhileStatement node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(AnnotationTypeDeclaration node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(AnnotationTypeMemberDeclaration node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(BlockComment node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(EnhancedForStatement node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(EnumConstantDeclaration node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(EnumDeclaration node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(LineComment node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(MarkerAnnotation node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(MemberRef node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(MemberValuePair node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(MethodRef node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(MethodRefParameter node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(Modifier node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(NormalAnnotation node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(ParameterizedType node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(QualifiedType node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(SingleMemberAnnotation node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(TagElement node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(TextElement node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(TypeParameter node) {
+ endVisit_(node);
+ }
+ @Override
+ public void endVisit(WildcardType node) {
+ endVisit_(node);
+ }
+
+}
+
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTAttribute.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTAttribute.java
new file mode 100644
index 0000000000..342aebf7e0
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTAttribute.java
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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.utility.jdt;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jpt.core.utility.jdt.AnnotationEditFormatter;
+import org.eclipse.jpt.core.utility.jdt.Attribute;
+import org.eclipse.jpt.core.utility.jdt.Type;
+import org.eclipse.jpt.utility.CommandExecutor;
+
+/**
+ * Combine behavior common to JDTFieldAttribute and JDTMethodAttribute.
+ * Not so sure this is useful....
+ */
+public abstract class JDTAttribute
+ extends JDTMember
+ implements Attribute
+{
+
+ // ********** constructors **********
+
+ protected JDTAttribute(
+ Type declaringType,
+ String name,
+ int occurrence,
+ ICompilationUnit compilationUnit,
+ CommandExecutor modifySharedDocumentCommandExecutor) {
+ super(declaringType, name, occurrence, compilationUnit, modifySharedDocumentCommandExecutor);
+ }
+
+ protected JDTAttribute(
+ Type declaringType,
+ String name,
+ int occurrence,
+ ICompilationUnit compilationUnit,
+ CommandExecutor modifySharedDocumentCommandExecutor,
+ AnnotationEditFormatter annotationEditFormatter) {
+ super(declaringType, name, occurrence, compilationUnit, modifySharedDocumentCommandExecutor, annotationEditFormatter);
+ }
+
+
+ // ********** Member/Attribute implementation **********
+
+ public boolean isField() {
+ return false;
+ }
+
+
+ // ********** internal **********
+
+ protected TypeDeclaration getDeclaringTypeDeclaration(CompilationUnit astRoot) {
+ // assume the declaring type is not an enum or annotation
+ // since they do not have field or method declarations
+ return this.getDeclaringType().getBodyDeclaration(astRoot);
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTFieldAttribute.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTFieldAttribute.java
new file mode 100644
index 0000000000..1c18f297cb
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTFieldAttribute.java
@@ -0,0 +1,205 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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.utility.jdt;
+
+import java.util.List;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
+import org.eclipse.jpt.core.utility.TextRange;
+import org.eclipse.jpt.core.utility.jdt.AnnotationEditFormatter;
+import org.eclipse.jpt.core.utility.jdt.FieldAttribute;
+import org.eclipse.jpt.core.utility.jdt.Type;
+import org.eclipse.jpt.utility.CommandExecutor;
+
+/**
+ * Adapt and extend a JDT field.
+ * Attribute based on a Java field, e.g.
+ * private int foo;
+ */
+public class JDTFieldAttribute
+ extends JDTAttribute
+ implements FieldAttribute
+{
+
+ // ********** constructors **********
+
+ public JDTFieldAttribute(
+ Type declaringType,
+ String name,
+ int occurrence,
+ ICompilationUnit compilationUnit,
+ CommandExecutor modifySharedDocumentCommandExecutor) {
+ this(declaringType, name, occurrence, compilationUnit, modifySharedDocumentCommandExecutor, DefaultAnnotationEditFormatter.instance());
+ }
+
+ public JDTFieldAttribute(
+ Type declaringType,
+ String name,
+ int occurrence,
+ ICompilationUnit compilationUnit,
+ CommandExecutor modifySharedDocumentCommandExecutor,
+ AnnotationEditFormatter annotationEditFormatter) {
+ super(declaringType, name, occurrence, compilationUnit, modifySharedDocumentCommandExecutor, annotationEditFormatter);
+ }
+
+ /**
+ * constructor for testing
+ */
+ public JDTFieldAttribute(Type declaringType, String name, int occurrence, ICompilationUnit compilationUnit) {
+ this(declaringType, name, occurrence, compilationUnit, CommandExecutor.Default.instance(), DefaultAnnotationEditFormatter.instance());
+ }
+
+
+ // ********** Member/Attribute/FieldAttribute implementation **********
+
+ public IVariableBinding getBinding(CompilationUnit astRoot) {
+ return this.getFragment(astRoot).resolveBinding();
+ }
+
+ public FieldDeclaration getBodyDeclaration(CompilationUnit astRoot) {
+ return this.getSelectedDeclaration(astRoot, FIELD_DECLARATION_SELECTOR);
+ }
+
+ public TextRange getNameTextRange(CompilationUnit astRoot) {
+ return new ASTNodeTextRange(this.getFragment(astRoot).getName());
+ }
+
+ public String getAttributeName() {
+ return this.getName_();
+ }
+
+ public ITypeBinding getTypeBinding(CompilationUnit astRoot) {
+ return this.getBodyDeclaration(astRoot).getType().resolveBinding();
+ }
+
+ @Override
+ public boolean isField() {
+ return true;
+ }
+
+ public boolean isPersistable(CompilationUnit astRoot) {
+ IVariableBinding binding = this.getBinding(astRoot);
+ return (binding == null) ? false : JPTTools.fieldIsPersistable(new JPTToolsAdapter(binding));
+ }
+
+
+ // ********** internal **********
+
+ protected VariableDeclarationFragment getFragment(CompilationUnit astRoot) {
+ return this.getSelectedDeclaration(astRoot, VARIABLE_DECLARATION_FRAGMENT_SELECTOR);
+ }
+
+ /**
+ * return either a FieldDeclaration or a VariableDeclarationFragment,
+ * depending on the specified selector;
+ *
+ * handle multiple fields declared in a single statement:
+ * private int foo, bar;
+ */
+ protected <T extends ASTNode> T getSelectedDeclaration(CompilationUnit astRoot, Selector<T> selector) {
+ String name = this.getName_();
+ int occurrence = this.getOccurrence();
+ int count = 0;
+ for (FieldDeclaration fieldDeclaration : this.getDeclaringTypeFieldDeclarations(astRoot)) {
+ for (VariableDeclarationFragment fragment : fragments(fieldDeclaration)) {
+ if (fragment.getName().getFullyQualifiedName().equals(name)) {
+ count++;
+ if (count == occurrence) {
+ return selector.select(fieldDeclaration, fragment);
+ }
+ }
+ }
+ }
+ // return null if the field is no longer in the source code;
+ // this can happen when the context model has not yet
+ // been synchronized with the resource model but is still
+ // asking for an ASTNode (e.g. during a selection event)
+ return null;
+ }
+
+ protected FieldDeclaration[] getDeclaringTypeFieldDeclarations(CompilationUnit astRoot) {
+ return this.getDeclaringTypeDeclaration(astRoot).getFields();
+ }
+
+ // minimize scope of suppressed warnings
+ @SuppressWarnings("unchecked")
+ protected static List<VariableDeclarationFragment> fragments(FieldDeclaration fd) {
+ return fd.fragments();
+ }
+
+
+ // ********** Selector **********
+
+ // I'm not quite sure this interface is worth the resulting obfuscation,
+ // but, then, I kept changing both methods, so... ~bjv
+ protected interface Selector<T extends ASTNode> {
+ T select(FieldDeclaration fieldDeclaration, VariableDeclarationFragment variableDeclarationFragment);
+ String getDescription();
+ }
+
+ protected static final Selector<FieldDeclaration> FIELD_DECLARATION_SELECTOR =
+ new Selector<FieldDeclaration>() {
+ public FieldDeclaration select(FieldDeclaration fieldDeclaration, VariableDeclarationFragment variableDeclarationFragment) {
+ return fieldDeclaration;
+ }
+ public String getDescription() {
+ return "field declaration"; //$NON-NLS-1$
+ }
+ @Override
+ public String toString() {
+ return "FIELD_DECLARATION_SELECTOR"; //$NON-NLS-1$
+ }
+ };
+
+ protected static final Selector<VariableDeclarationFragment> VARIABLE_DECLARATION_FRAGMENT_SELECTOR =
+ new Selector<VariableDeclarationFragment>() {
+ public VariableDeclarationFragment select(FieldDeclaration fieldDeclaration, VariableDeclarationFragment variableDeclarationFragment) {
+ return variableDeclarationFragment;
+ }
+ public String getDescription() {
+ return "variable declaration fragment"; //$NON-NLS-1$
+ }
+ @Override
+ public String toString() {
+ return "VARIABLE_DECLARATION_FRAGMENT_SELECTOR"; //$NON-NLS-1$
+ }
+ };
+
+
+ // ********** JPTTools adapter **********
+
+ /**
+ * JPTTools needs an adapter so it can work with either an IField
+ * or an IVariableBinding etc.
+ */
+ protected static class JPTToolsAdapter implements JPTTools.FieldAdapter {
+ private final IVariableBinding fieldBinding;
+
+ protected JPTToolsAdapter(IVariableBinding fieldBinding) {
+ super();
+ if (fieldBinding == null) {
+ throw new NullPointerException();
+ }
+ this.fieldBinding = fieldBinding;
+ }
+
+ public int getModifiers() {
+ return this.fieldBinding.getModifiers();
+ }
+
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTMember.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTMember.java
new file mode 100644
index 0000000000..c7c5ebbdb6
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTMember.java
@@ -0,0 +1,236 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2010 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.utility.jdt;
+
+import org.eclipse.core.filebuffers.FileBuffers;
+import org.eclipse.core.filebuffers.ITextFileBuffer;
+import org.eclipse.core.filebuffers.LocationKind;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.Document;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jpt.core.utility.jdt.AnnotationEditFormatter;
+import org.eclipse.jpt.core.utility.jdt.Member;
+import org.eclipse.jpt.core.utility.jdt.ModifiedDeclaration;
+import org.eclipse.jpt.core.utility.jdt.Type;
+import org.eclipse.jpt.utility.Command;
+import org.eclipse.jpt.utility.CommandExecutor;
+import org.eclipse.jpt.utility.internal.StringTools;
+import org.eclipse.text.edits.MalformedTreeException;
+import org.eclipse.text.edits.TextEdit;
+
+/**
+ * Adapt and extend a JDT member with simplified annotation handling.
+ */
+public abstract class JDTMember
+ implements Member
+{
+ /** this will be null for the primary type */
+ private final Type declaringType;
+
+ /** the member's name (duh) */
+ private final String name;
+
+ /**
+ * members can occur more than once in non-compiling source;
+ * count starts at 1; the primary type will have occurrence 1
+ */
+ private final int occurrence;
+
+ /**
+ * the compilation unit (file) containing the member;
+ * used for building an AST when we modify the member
+ */
+ private final ICompilationUnit compilationUnit;
+
+ /**
+ * this allows clients to provide a way to modify the compilation unit
+ * (file) when it is open in an editor and should be modified on the UI
+ * thread
+ */
+ private final CommandExecutor modifySharedDocumentCommandExecutor;
+
+ /** this will format the member's annotations a bit */
+ private final AnnotationEditFormatter annotationEditFormatter;
+
+
+ // ********** constructors **********
+
+ protected JDTMember(
+ Type declaringType,
+ String name,
+ int occurrence,
+ ICompilationUnit compilationUnit,
+ CommandExecutor modifySharedDocumentCommandExecutor) {
+ this(declaringType, name, occurrence, compilationUnit, modifySharedDocumentCommandExecutor, DefaultAnnotationEditFormatter.instance());
+ }
+
+ protected JDTMember(
+ Type declaringType,
+ String name,
+ int occurrence,
+ ICompilationUnit compilationUnit,
+ CommandExecutor modifySharedDocumentCommandExecutor,
+ AnnotationEditFormatter annotationEditFormatter) {
+ super();
+ this.declaringType = declaringType;
+ this.name = name;
+ this.occurrence = occurrence;
+ this.compilationUnit = compilationUnit;
+ this.modifySharedDocumentCommandExecutor = modifySharedDocumentCommandExecutor;
+ this.annotationEditFormatter = annotationEditFormatter;
+ }
+
+
+ // ********** Member implementation **********
+
+ public ModifiedDeclaration getModifiedDeclaration(CompilationUnit astRoot) {
+ return new JDTModifiedDeclaration(this.getBodyDeclaration(astRoot));
+ }
+
+ public ModifiedDeclaration getModifiedDeclaration() {
+ return this.getModifiedDeclaration(this.buildASTRoot());
+ }
+
+ public boolean matches(String memberName, int occur) {
+ return memberName.equals(this.name) && (occur == this.occurrence);
+ }
+
+ @Override
+ public String toString() {
+ return StringTools.buildToStringFor(this, this.name);
+ }
+
+
+ // ********** internal **********
+
+ protected String getName_() {
+ return this.name;
+ }
+
+ protected int getOccurrence() {
+ return this.occurrence;
+ }
+
+ /**
+ * this will return null for a top-level type
+ */
+ protected Type getDeclaringType() {
+ return this.declaringType;
+ }
+
+
+ // ********** editing **********
+
+ /**
+ * Edit the member with the specified editor.
+ * The editor will be invoked once the member's compilation unit
+ * is in an editable state.
+ */
+ public void edit(Editor editor) {
+ try {
+ this.edit_(editor);
+ } catch (JavaModelException ex) {
+ throw new RuntimeException(ex);
+ } catch (BadLocationException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ /**
+ * NB: Be careful changing this method.
+ * Things to look out for:
+ * - when editing via the JavaEditor there is no need to create a working copy
+ * - when editing without an editor or via a simple text editor, a "working copy" must be created.
+ * (at least as far as I can tell ~kfm)
+ * - sharedDocument is only ever false in tests (headless mode). In the UI, even if the file
+ * is not open in an editor, sharedDocument is still true (buffer is not null)
+ * - if a working copy is created, then we must discard it
+ */
+ protected void edit_(Editor editor) throws JavaModelException, BadLocationException {
+ boolean createWorkingCopy = ! this.compilationUnit.isWorkingCopy();
+ if (createWorkingCopy) {
+ this.compilationUnit.becomeWorkingCopy(null);
+ }
+
+ ITextFileBuffer buffer = FileBuffers.getTextFileBufferManager().getTextFileBuffer(this.compilationUnit.getResource().getFullPath(), LocationKind.NORMALIZE);
+ boolean sharedDocument = (buffer != null); // documents are typically shared when they are already open in an editor
+ IDocument doc = sharedDocument ?
+ buffer.getDocument() :
+ new Document(this.compilationUnit.getBuffer().getContents());
+
+ try {
+ CompilationUnit astRoot = this.buildASTRoot();
+ astRoot.recordModifications();
+
+ editor.edit(this.getModifiedDeclaration(astRoot));
+
+ TextEdit edits = astRoot.rewrite(doc, this.compilationUnit.getJavaProject().getOptions(true));
+ if (sharedDocument) {
+ this.modifySharedDocumentCommandExecutor.execute(new ModifySharedDocumentCommand(edits, doc));
+ } else {
+ this.applyEdits(edits, doc);
+ }
+ }
+ finally {
+ if (createWorkingCopy) {
+ //discardWorkingCopy must be called every time becomeWorkingCopy is called.
+ this.compilationUnit.getBuffer().setContents(doc.get());
+ this.compilationUnit.commitWorkingCopy(true, null); // true="force"
+ this.compilationUnit.discardWorkingCopy();
+ }
+ }
+ }
+
+ /**
+ * apply the specified edits to the specified document,
+ * reformatting the document if necessary
+ */
+ protected void applyEdits(TextEdit edits, IDocument doc) throws MalformedTreeException, BadLocationException {
+ edits.apply(doc, TextEdit.UPDATE_REGIONS);
+ this.annotationEditFormatter.format(doc, edits);
+ }
+
+ protected CompilationUnit buildASTRoot() {
+ return ASTTools.buildASTRoot(this.compilationUnit);
+ }
+
+
+ // ********** modify shared document command class **********
+
+ /**
+ * simple command that calls back to the member to apply the edits
+ * in the same way as if the document were not shared
+ */
+ protected class ModifySharedDocumentCommand implements Command {
+ private final TextEdit edits;
+ private final IDocument doc;
+
+ protected ModifySharedDocumentCommand(TextEdit edits, IDocument doc) {
+ super();
+ this.edits = edits;
+ this.doc = doc;
+ }
+
+ public void execute() {
+ try {
+ JDTMember.this.applyEdits(this.edits, this.doc);
+ } catch (MalformedTreeException ex) {
+ throw new RuntimeException(ex);
+ } catch (BadLocationException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTMethodAttribute.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTMethodAttribute.java
new file mode 100644
index 0000000000..4316d6c198
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTMethodAttribute.java
@@ -0,0 +1,256 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2010 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.utility.jdt;
+
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.IMethodBinding;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jpt.core.utility.TextRange;
+import org.eclipse.jpt.core.utility.jdt.AnnotationEditFormatter;
+import org.eclipse.jpt.core.utility.jdt.MethodAttribute;
+import org.eclipse.jpt.core.utility.jdt.Type;
+import org.eclipse.jpt.utility.CommandExecutor;
+import org.eclipse.jpt.utility.JavaType;
+import org.eclipse.jpt.utility.MethodSignature;
+import org.eclipse.jpt.utility.internal.NameTools;
+import org.eclipse.jpt.utility.internal.SimpleMethodSignature;
+
+/**
+ * Adapt and extend a JDT method.
+ * Attribute based on a Java property, e.g.
+ * private int getFoo() {
+ * return foo;
+ * }
+ * private void setFoo(int foo) {
+ * this.foo = foo;
+ * }
+ */
+public class JDTMethodAttribute
+ extends JDTAttribute
+ implements MethodAttribute
+{
+ /** we need the parameter types to build the method signature */
+ private final JavaType[] parameterTypes;
+
+
+ // ********** constructors **********
+
+ public static JDTMethodAttribute newInstance(
+ Type declaringType,
+ MethodSignature signature,
+ int occurrence,
+ ICompilationUnit compilationUnit,
+ CommandExecutor modifySharedDocumentCommandExecutor) {
+ return newInstance(declaringType, signature, occurrence, compilationUnit, modifySharedDocumentCommandExecutor, DefaultAnnotationEditFormatter.instance());
+ }
+
+ public static JDTMethodAttribute newInstance(
+ Type declaringType,
+ MethodSignature signature,
+ int occurrence,
+ ICompilationUnit compilationUnit,
+ CommandExecutor modifySharedDocumentCommandExecutor,
+ AnnotationEditFormatter annotationEditFormatter) {
+ return new JDTMethodAttribute(declaringType, signature, occurrence, compilationUnit, modifySharedDocumentCommandExecutor, annotationEditFormatter);
+ }
+
+ public JDTMethodAttribute(
+ Type declaringType,
+ MethodSignature methodSignature,
+ int occurrence,
+ ICompilationUnit compilationUnit,
+ CommandExecutor modifySharedDocumentCommandExecutor) {
+ this(declaringType, methodSignature, occurrence, compilationUnit, modifySharedDocumentCommandExecutor, DefaultAnnotationEditFormatter.instance());
+ }
+
+ public JDTMethodAttribute(
+ Type declaringType,
+ MethodSignature methodSignature,
+ int occurrence,
+ ICompilationUnit compilationUnit,
+ CommandExecutor modifySharedDocumentCommandExecutor,
+ AnnotationEditFormatter annotationEditFormatter) {
+ super(declaringType, methodSignature.getName(), occurrence, compilationUnit, modifySharedDocumentCommandExecutor, annotationEditFormatter);
+ this.parameterTypes = methodSignature.getParameterTypes();
+ }
+
+ /**
+ * constructor for testing
+ */
+ public JDTMethodAttribute(Type declaringType, String name, String[] parameterTypeNames, int occurrence, ICompilationUnit compilationUnit) {
+ this(declaringType, new SimpleMethodSignature(name, parameterTypeNames), occurrence, compilationUnit, CommandExecutor.Default.instance(), DefaultAnnotationEditFormatter.instance());
+ }
+
+
+ // ********** Member/Attribute/MethodAttribute implementation **********
+
+ public IMethodBinding getBinding(CompilationUnit astRoot) {
+ return this.getBodyDeclaration(astRoot).resolveBinding();
+ }
+
+ public MethodDeclaration getBodyDeclaration(CompilationUnit astRoot) {
+ int count = 0;
+ for (MethodDeclaration methodDeclaration : this.getDeclaringTypeMethodDeclarations(astRoot)) {
+ if (this.matches(methodDeclaration)) {
+ count++;
+ if (count == this.getOccurrence()) {
+ return methodDeclaration;
+ }
+ }
+ }
+ // return null if the method is no longer in the source code;
+ // this can happen when the context model has not yet
+ // been synchronized with the resource model but is still
+ // asking for an ASTNode (e.g. during a selection event)
+ return null;
+ }
+
+ public boolean matches(MethodSignature signature, int occurrence) {
+ return this.matches(signature) && (occurrence == this.getOccurrence());
+ }
+
+ protected boolean matches(MethodSignature signature) {
+ return signature.getName().equals(this.getName_())
+ && Arrays.equals(this.parameterTypes, signature.getParameterTypes());
+ }
+
+ protected boolean matches(MethodDeclaration methodDeclaration) {
+ return this.matches(ASTTools.buildMethodSignature(methodDeclaration));
+ }
+
+ // minimize scope of suppressed warnings
+ @SuppressWarnings("unchecked")
+ protected static List<SingleVariableDeclaration> parameters(MethodDeclaration methodDeclaration) {
+ return methodDeclaration.parameters();
+ }
+
+ @Override
+ public boolean matches(String memberName, int occurrence) {
+ throw new UnsupportedOperationException("Use #matches(MethodSignature, int)."); //$NON-NLS-1$
+ }
+
+ public TextRange getNameTextRange(CompilationUnit astRoot) {
+ return new ASTNodeTextRange(this.getBodyDeclaration(astRoot).getName());
+ }
+
+ /**
+ * return "foo" for a method named "getFoo" or "isFoo"
+ */
+ public String getAttributeName() {
+ return NameTools.convertGetterMethodNameToPropertyName(this.getName_());
+ }
+
+ public ITypeBinding getTypeBinding(CompilationUnit astRoot) {
+ IMethodBinding methodBinding = getBodyDeclaration(astRoot).resolveBinding();
+ return (methodBinding == null) ? null : methodBinding.getReturnType();
+ }
+
+ public boolean isPersistable(CompilationUnit astRoot) {
+ IMethodBinding binding = this.getBinding(astRoot);
+ return (binding == null) ? false : JPTTools.methodIsPersistablePropertyGetter(new JPTToolsAdapter(binding));
+ }
+
+
+ // ********** internal **********
+
+ protected MethodDeclaration[] getDeclaringTypeMethodDeclarations(CompilationUnit astRoot) {
+ return this.getDeclaringTypeDeclaration(astRoot).getMethods();
+ }
+
+
+ // ********** JPTTools adapter **********
+
+ /**
+ * JPTTools needs an adapter so it can work with either an IMethod
+ * or an IMethodBinding etc.
+ */
+ protected static class SimpleJPTToolsAdapter
+ implements JPTTools.SimpleMethodAdapter
+ {
+ protected final IMethodBinding methodBinding;
+
+ protected SimpleJPTToolsAdapter(IMethodBinding methodBinding) {
+ super();
+ if (methodBinding == null) {
+ throw new NullPointerException();
+ }
+ this.methodBinding = methodBinding;
+ }
+
+ public int getModifiers() {
+ return this.methodBinding.getModifiers();
+ }
+
+ public String getReturnTypeErasureName() {
+ ITypeBinding returnType = this.methodBinding.getReturnType();
+ return (returnType == null) ? null : returnType.getTypeDeclaration().getErasure().getQualifiedName();
+ }
+
+ public boolean isConstructor() {
+ return this.methodBinding.isConstructor();
+ }
+
+ }
+
+ protected static class JPTToolsAdapter
+ extends SimpleJPTToolsAdapter
+ implements JPTTools.MethodAdapter
+ {
+ protected JPTToolsAdapter(IMethodBinding methodBinding) {
+ super(methodBinding);
+ }
+
+ public String getName() {
+ return this.methodBinding.getName();
+ }
+
+ public int getParametersLength() {
+ return this.methodBinding.getParameterTypes().length;
+ }
+
+ public JPTTools.SimpleMethodAdapter getSibling(String name) {
+ ITypeBinding typeBinding = this.methodBinding.getDeclaringClass();
+ if (typeBinding == null) {
+ return null;
+ }
+ for (IMethodBinding sibling : typeBinding.getDeclaredMethods()) {
+ if ((sibling.getParameterTypes().length == 0)
+ && sibling.getName().equals(name)) {
+ return new SimpleJPTToolsAdapter(sibling);
+ }
+ }
+ return null;
+ }
+
+ public JPTTools.SimpleMethodAdapter getSibling(String name, String parameterTypeErasureName) {
+ ITypeBinding typeBinding = this.methodBinding.getDeclaringClass();
+ if (typeBinding == null) {
+ return null;
+ }
+ for (IMethodBinding sibling : typeBinding.getDeclaredMethods()) {
+ ITypeBinding[] siblingParmTypes = sibling.getParameterTypes();
+ if ((siblingParmTypes.length == 1)
+ && sibling.getName().equals(name)
+ && siblingParmTypes[0].getTypeDeclaration().getErasure().getQualifiedName().equals(parameterTypeErasureName)) {
+ return new SimpleJPTToolsAdapter(sibling);
+ }
+ }
+ return null;
+ }
+
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTModifiedDeclaration.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTModifiedDeclaration.java
new file mode 100644
index 0000000000..96f2c739e7
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTModifiedDeclaration.java
@@ -0,0 +1,537 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2010 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.utility.jdt;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+import org.eclipse.jdt.core.IField;
+import org.eclipse.jdt.core.IType;
+import org.eclipse.jdt.core.JavaModelException;
+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.BodyDeclaration;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.IExtendedModifier;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.ImportDeclaration;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.VariableDeclarationExpression;
+import org.eclipse.jdt.core.dom.VariableDeclarationStatement;
+import org.eclipse.jpt.core.utility.jdt.ModifiedDeclaration;
+import org.eclipse.jpt.utility.internal.StringTools;
+import org.eclipse.jpt.utility.internal.iterators.FilteringIterator;
+import org.eclipse.jpt.utility.internal.iterators.SubIteratorWrapper;
+
+/**
+ * Wrap any of the AST nodes that have modifiers (specifically, annotations);
+ * i.e. BodyDeclaration, SingleVariableDeclaration, VariableDeclarationExpression,
+ * and VariableDeclarationStatement.
+ */
+public class JDTModifiedDeclaration
+ implements ModifiedDeclaration
+{
+ private final Adapter adapter;
+
+
+ // ********** constructors **********
+
+ public JDTModifiedDeclaration(Adapter adapter) {
+ super();
+ this.adapter = adapter;
+ }
+
+ public JDTModifiedDeclaration(BodyDeclaration declaration) {
+ this(new BodyDeclarationAdapter(declaration));
+ }
+
+ public JDTModifiedDeclaration(SingleVariableDeclaration declaration) {
+ this(new SingleVariableDeclarationAdapter(declaration));
+ }
+
+ public JDTModifiedDeclaration(VariableDeclarationExpression declaration) {
+ this(new VariableDeclarationExpressionAdapter(declaration));
+ }
+
+ public JDTModifiedDeclaration(VariableDeclarationStatement declaration) {
+ this(new VariableDeclarationStatementAdapter(declaration));
+ }
+
+
+ // ********** annotations **********
+
+ public Annotation getAnnotationNamed(String annotationName) {
+ for (Iterator<Annotation> stream = this.annotations(); stream.hasNext(); ) {
+ Annotation annotation = stream.next();
+ if (this.annotationIsNamed(annotation, annotationName)) {
+ return annotation;
+ }
+ }
+ return null;
+ }
+
+ public void removeAnnotationNamed(String annotationName) {
+ for (Iterator<IExtendedModifier> stream = this.getModifiers().iterator(); stream.hasNext(); ) {
+ IExtendedModifier modifier = stream.next();
+ if (modifier.isAnnotation()) {
+ if (this.annotationIsNamed((Annotation) modifier, annotationName)) {
+ stream.remove();
+ break;
+ }
+ }
+ }
+ }
+
+ public void replaceAnnotationNamed(String oldAnnotationName, Annotation newAnnotation) {
+ List<IExtendedModifier> modifiers = this.getModifiers();
+ for (ListIterator<IExtendedModifier> stream = modifiers.listIterator(); stream.hasNext(); ) {
+ IExtendedModifier modifier = stream.next();
+ if (modifier.isAnnotation()) {
+ if (this.annotationIsNamed((Annotation) modifier, oldAnnotationName)) {
+ stream.set(newAnnotation);
+ return;
+ }
+ }
+ }
+ this.addAnnotation(newAnnotation);
+ }
+
+ /**
+ * Add the specified annotation to the declaration.
+ * By convention annotations precede the "standard" (JLS2) modifiers;
+ * though, technically, they can be interspersed.
+ */
+ protected void addAnnotation(Annotation annotation) {
+ List<IExtendedModifier> modifiers = this.getModifiers();
+ for (ListIterator<IExtendedModifier> stream = modifiers.listIterator(); stream.hasNext(); ) {
+ if (stream.next().isModifier()) {
+ stream.previous(); // put the annotation *before* the first "standard" (JLS2) modifier
+ stream.add(annotation);
+ return;
+ }
+ }
+ modifiers.add(annotation); // just tack it on to the end
+ }
+
+ /**
+ * Return the declaration's annotations.
+ */
+ protected Iterator<Annotation> annotations() {
+ return new SubIteratorWrapper<IExtendedModifier, Annotation>(this.annotations_());
+ }
+
+ protected Iterator<IExtendedModifier> annotations_() {
+ return new FilteringIterator<IExtendedModifier>(this.getModifiers().iterator()) {
+ @Override
+ protected boolean accept(IExtendedModifier next) {
+ return next.isAnnotation();
+ }
+ };
+ }
+
+
+ // ********** add import **********
+
+ public boolean addImport(String className) {
+ if (className.indexOf('.') == -1) {
+ return true; // the class is in the default package - no need for import
+ }
+ return this.addImport(className, false);
+ }
+
+ public boolean addStaticImport(String enumConstantName) {
+ int index1 = enumConstantName.indexOf('.');
+ if (index1 == -1) {
+ throw new IllegalArgumentException(enumConstantName); // shouldn't happen?
+ }
+ int index2 = enumConstantName.indexOf('.', index1 + 1);
+ if (index2 == -1) {
+ return true; // the enum is in the default package - no need for import
+ }
+ return this.addImport(enumConstantName, true);
+ }
+
+ public boolean addImport(String importName, boolean staticImport) {
+ Boolean include = this.importsInclude(importName, staticImport);
+ if (include != null) {
+ return include.booleanValue();
+ }
+
+ ImportDeclaration importDeclaration = this.getAst().newImportDeclaration();
+ importDeclaration.setName(this.getAst().newName(importName));
+ importDeclaration.setStatic(staticImport);
+ this.getImports().add(importDeclaration);
+ return true;
+ }
+
+ /**
+ * Just a bit hacky:
+ * Return Boolean.TRUE if the import is already present.
+ * Return Boolean.FALSE if a colliding import is already present.
+ * Return null if a new import may be added.
+ * This hackery allows us to loop through the imports only once
+ * (and compose our methods).
+ * Pre-condition: 'importName' is not in the "default" package (i.e. it *is* qualified)
+ */
+ protected Boolean importsInclude(String importName, boolean staticImport) {
+ int period = importName.lastIndexOf('.'); // should not be -1
+ String importNameQualifier = importName.substring(0, period);
+ String shortImportName = importName.substring(period + 1);
+ return this.importsInclude(importName, importNameQualifier, shortImportName, staticImport);
+ }
+
+ /**
+ * pre-calculate the qualifier and short name
+ */
+ protected Boolean importsInclude(String importName, String importNameQualifier, String shortImportName, boolean staticImport) {
+ for (ImportDeclaration importDeclaration : this.getImports()) {
+ if (importDeclaration.isStatic() == staticImport) {
+ Boolean match = this.importMatches(importDeclaration, importName, importNameQualifier, shortImportName);
+ if (match != null) {
+ return match;
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * we should be able to rely on the JDT model here, since we are looking
+ * at objects that should not be changing underneath us...
+ */
+ protected Boolean importMatches(ImportDeclaration importDeclaration, String importName, String importNameQualifier, String shortImportName) {
+ // examples:
+ // 'importName' is "java.util.Date"
+ // or
+ // 'importName' is "java.lang.annotation.ElementType.TYPE"
+ String idn = importDeclaration.getName().getFullyQualifiedName();
+ if (importName.equals(idn)) {
+ // import java.util.Date; => "Date" will resolve to "java.util.Date"
+ // import static java.lang.annotation.ElementType.TYPE; => "TYPE" will resolve to "java.lang.annotation.ElementType.TYPE"
+ return Boolean.TRUE;
+ }
+
+ String shortIDN = idn.substring(idn.lastIndexOf('.') + 1);
+ if (shortImportName.equals(shortIDN)) {
+ // import java.sql.Date; => ambiguous resolution of "Date"
+ // import static org.foo.Bar.TYPE; => ambiguous resolution of "TYPE"
+ return Boolean.FALSE;
+ }
+
+ if (importDeclaration.isOnDemand()) {
+ if (importNameQualifier.equals(idn)) {
+ // import java.util.*; => "Date" will resolve to "java.util.Date"
+ // import static java.lang.annotation.ElementType.*; => "TYPE" will resolve to "java.lang.annotation.ElementType.TYPE"
+ return Boolean.TRUE;
+ }
+ if (importDeclaration.isStatic()) {
+ if (this.enumResolves(idn, shortImportName)) {
+ // import static org.foo.Bar.*; => ambiguous resolution of "TYPE"
+ return Boolean.FALSE;
+ }
+ } else {
+ if (this.typeResolves(idn + '.' + shortImportName)) {
+ // import java.sql.*; => ambiguous resolution of "Date"
+ return Boolean.FALSE;
+ }
+ }
+ }
+ // no matches - OK to add explicit import
+ return null;
+ }
+
+ protected boolean enumResolves(String enumTypeName, String enumConstantName) {
+ try {
+ return this.enumResolves_(enumTypeName, enumConstantName);
+ } catch (JavaModelException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ protected boolean enumResolves_(String enumTypeName, String enumConstantName) throws JavaModelException {
+ IType jdtType = this.findType_(enumTypeName);
+ if (jdtType == null) {
+ return false;
+ }
+ if ( ! jdtType.isEnum()) {
+ return false;
+ }
+ for (IField jdtField : jdtType.getFields()) {
+ if (jdtField.isEnumConstant() && jdtField.getElementName().equals(enumConstantName)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ protected boolean typeResolves(String name) {
+ return this.findType(name) != null;
+ }
+
+ protected IType findType(String name) {
+ try {
+ return this.findType_(name);
+ } catch (JavaModelException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ protected IType findType_(String name) throws JavaModelException {
+ return this.getCompilationUnit().getJavaElement().getJavaProject().findType(name);
+ }
+
+ protected List<ImportDeclaration> getImports() {
+ return this.imports(this.getCompilationUnit());
+ }
+
+ // minimize scope of suppressed warnings
+ @SuppressWarnings("unchecked")
+ protected List<ImportDeclaration> imports(CompilationUnit astRoot) {
+ return astRoot.imports();
+ }
+
+
+ // ********** annotation name resolution **********
+
+ public boolean annotationIsNamed(Annotation annotation, String name) {
+ return this.getQualifiedName(annotation).equals(name);
+ }
+
+ /**
+ * Simply return the annotation's unqualified name if we can't "resolve" it.
+ */
+ protected String getQualifiedName(Annotation annotation) {
+ ITypeBinding typeBinding = annotation.resolveTypeBinding();
+ if (typeBinding != null) {
+ String resolvedName = typeBinding.getQualifiedName();
+ if (resolvedName != null) {
+ return resolvedName;
+ }
+ }
+ // hack(?): check for a matching import because when moving a stand-alone
+ // annotation to its container in CombinationIndexedDeclarationAnnotationAdapter
+ // the container's import is added but then it won't "resolve" upon
+ // subsequent lookups (because the parser hasn't had time to run?)... :-(
+ return this.convertToFullClassName(annotation.getTypeName().getFullyQualifiedName());
+ }
+
+ /**
+ * If necessary, use the declaration's imports to calculate a guess as to
+ * the specified name's fully-qualified form.
+ * Simply return the unqualified name if we can't "resolve" it.
+ */
+ protected String convertToFullClassName(String name) {
+ // check for fully-qualified name
+ return (name.lastIndexOf('.') != -1) ? name : this.resolveAgainstImports(name, false);
+ }
+
+ /**
+ * If necessary, use the declaration's imports to calculate a guess as to
+ * the specified name's fully-qualified form.
+ * Simply return the unqualified name if we can't "resolve" it.
+ */
+ protected String convertToFullEnumConstantName(String name) {
+ int index1 = name.indexOf('.');
+ if (index1 == -1) {
+ // short name, e.g. "TYPE"
+ // true = look for static import of enum constant
+ return this.resolveAgainstImports(name, true);
+ }
+
+ int index2 = name.indexOf('.', index1 + 1);
+ if (index2 == -1) {
+ // partially-qualified name, e.g. "ElementType.TYPE"
+ // false = look regular import of enum class, not static import of enum constant
+ return this.resolveAgainstImports(name, false);
+ }
+
+ // fully-qualified name, e.g. "java.lang.annotation.ElementType.TYPE"
+ return name;
+ }
+
+ /**
+ * Attempt to resolve the specified "short" name against the declaration's
+ * imports. Return the name unchanged if we can't resolve it (perhaps it is
+ * in the "default" package).
+ */
+ protected String resolveAgainstImports(String shortName, boolean static_) {
+ for (ImportDeclaration importDeclaration : this.getImports()) {
+ if (importDeclaration.isStatic() == static_) {
+ String resolvedName = this.resolveAgainstImport(importDeclaration, shortName);
+ if (resolvedName != null) {
+ return resolvedName;
+ }
+ }
+ }
+ return shortName; // "default" package or unknown
+ }
+
+ /**
+ * Attempt to resolve the specified "short" name against the specified
+ * import. Return the resolved name if the import resolves it; otherwise
+ * return null.
+ */
+ protected String resolveAgainstImport(ImportDeclaration importDeclaration, String shortName) {
+ String idn = importDeclaration.getName().getFullyQualifiedName();
+ if (importDeclaration.isOnDemand()) {
+ String candidate = idn + '.' + shortName;
+ if (importDeclaration.isStatic()) {
+ if (this.enumResolves(idn, shortName)) {
+ return candidate;
+ }
+ } else {
+ if (this.typeResolves(candidate)) {
+ return candidate;
+ }
+ }
+ // no match
+ return null;
+ }
+
+ // explicit import - see whether its end matches 'shortName'
+ int period = idn.length() - shortName.length() - 1;
+ if (period < 1) {
+ // something must precede period
+ return null;
+ }
+ if ((idn.charAt(period) == '.') && idn.endsWith(shortName)) {
+ return idn; // probable exact match
+ }
+ return null;
+ }
+
+
+ // ********** miscellaneous methods **********
+
+ public ASTNode getDeclaration() {
+ return this.adapter.getDeclaration();
+ }
+
+ /**
+ * Return the declaration's list of modifiers.
+ * Element type: org.eclipse.jdt.core.dom.IExtendedModifier
+ */
+ protected List<IExtendedModifier> getModifiers() {
+ return this.adapter.getModifiers();
+ }
+
+ public AST getAst() {
+ return this.getDeclaration().getAST();
+ }
+
+ protected CompilationUnit getCompilationUnit() {
+ return (CompilationUnit) this.getDeclaration().getRoot();
+ }
+
+ @Override
+ public String toString() {
+ return StringTools.buildToStringFor(this, this.adapter.toString());
+ }
+
+
+ // ********** declaration adapter interface and implementations **********
+
+ /**
+ * Define common protocol among the various "declarations".
+ */
+ public interface Adapter {
+
+ /**
+ * Return the adapted "declaration".
+ */
+ ASTNode getDeclaration();
+
+ /**
+ * Return the "declaration"'s list of modifiers.
+ * Element type: org.eclipse.jdt.core.dom.IExtendedModifier
+ */
+ List<IExtendedModifier> getModifiers();
+
+ }
+
+ public static class BodyDeclarationAdapter implements Adapter {
+ private final BodyDeclaration declaration;
+ public BodyDeclarationAdapter(BodyDeclaration declaration) {
+ super();
+ this.declaration = declaration;
+ }
+ public ASTNode getDeclaration() {
+ return this.declaration;
+ }
+ @SuppressWarnings("unchecked")
+ public List<IExtendedModifier> getModifiers() {
+ return this.declaration.modifiers();
+ }
+ @Override
+ public String toString() {
+ return StringTools.buildToStringFor(this, this.declaration.toString());
+ }
+ }
+
+ public static class SingleVariableDeclarationAdapter implements Adapter {
+ private final SingleVariableDeclaration declaration;
+ public SingleVariableDeclarationAdapter(SingleVariableDeclaration declaration) {
+ super();
+ this.declaration = declaration;
+ }
+ public ASTNode getDeclaration() {
+ return this.declaration;
+ }
+ @SuppressWarnings("unchecked")
+ public List<IExtendedModifier> getModifiers() {
+ return this.declaration.modifiers();
+ }
+ @Override
+ public String toString() {
+ return StringTools.buildToStringFor(this, this.declaration.toString());
+ }
+ }
+
+ public static class VariableDeclarationExpressionAdapter implements Adapter {
+ private final VariableDeclarationExpression declaration;
+ public VariableDeclarationExpressionAdapter(VariableDeclarationExpression declaration) {
+ super();
+ this.declaration = declaration;
+ }
+ public ASTNode getDeclaration() {
+ return this.declaration;
+ }
+ @SuppressWarnings("unchecked")
+ public List<IExtendedModifier> getModifiers() {
+ return this.declaration.modifiers();
+ }
+ @Override
+ public String toString() {
+ return StringTools.buildToStringFor(this, this.declaration.toString());
+ }
+ }
+
+ public static class VariableDeclarationStatementAdapter implements Adapter {
+ private final VariableDeclarationStatement declaration;
+ public VariableDeclarationStatementAdapter(VariableDeclarationStatement declaration) {
+ super();
+ this.declaration = declaration;
+ }
+ public ASTNode getDeclaration() {
+ return this.declaration;
+ }
+ @SuppressWarnings("unchecked")
+ public List<IExtendedModifier> getModifiers() {
+ return this.declaration.modifiers();
+ }
+ @Override
+ public String toString() {
+ return StringTools.buildToStringFor(this, this.declaration.toString());
+ }
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTTools.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTTools.java
new file mode 100644
index 0000000000..bf1e5661a8
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTTools.java
@@ -0,0 +1,92 @@
+/*******************************************************************************
+* Copyright (c) 2010 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.utility.jdt;
+
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.JavaModelException;
+import org.eclipse.jpt.core.JptCorePlugin;
+import org.eclipse.jpt.utility.Filter;
+import org.eclipse.jpt.utility.internal.iterables.ArrayIterable;
+import org.eclipse.jpt.utility.internal.iterables.EmptyIterable;
+import org.eclipse.jpt.utility.internal.iterables.FilteringIterable;
+
+/**
+ * Convenience methods for dealing with JDT core
+ */
+public final class JDTTools
+{
+
+ public static boolean packageFragmentRootIsSourceFolder(IPackageFragmentRoot pfr) {
+ try {
+ return packageFragmentRootIsSourceFolder_(pfr);
+ } catch (JavaModelException ex) {
+ JptCorePlugin.log(ex);
+ return false;
+ }
+ }
+
+ protected static boolean packageFragmentRootIsSourceFolder_(IPackageFragmentRoot pfr) throws JavaModelException {
+ return pfr.exists() && (pfr.getKind() == IPackageFragmentRoot.K_SOURCE);
+ }
+
+ //TODO move this method to JpaProject once API freeze is over
+ public static Iterable<IPackageFragmentRoot> getJavaSourceFolders(IJavaProject javaProject) {
+ try {
+ return new FilteringIterable<IPackageFragmentRoot>(
+ getPackageFragmentRoots(javaProject),
+ SOURCE_PACKAGE_FRAGMENT_ROOT_FILTER
+ );
+ } catch (JavaModelException ex) {
+ JptCorePlugin.log(ex);
+ return EmptyIterable.instance();
+ }
+ }
+
+ /**
+ * This returns the first package fragment root found on this project.
+ * I am not completely sure why, but the JavaTypeCompletionProcessor works with this.
+ */
+ //TODO move this method to JpaProject once API freeze is over
+ public static IPackageFragmentRoot getCodeCompletionContextRoot(IJavaProject javaProject) {
+ try {
+ return javaProject.getPackageFragmentRoots()[0];
+ }
+ catch (JavaModelException e) {
+ JptCorePlugin.log(e);
+ return null;
+ }
+ }
+
+ protected static Iterable<IPackageFragmentRoot> getJavaSourceFolders_(IJavaProject javaProject) throws JavaModelException {
+ return new FilteringIterable<IPackageFragmentRoot>(
+ getPackageFragmentRoots(javaProject),
+ SOURCE_PACKAGE_FRAGMENT_ROOT_FILTER
+ );
+ }
+
+ protected static final Filter<IPackageFragmentRoot> SOURCE_PACKAGE_FRAGMENT_ROOT_FILTER =
+ new Filter<IPackageFragmentRoot>() {
+ public boolean accept(IPackageFragmentRoot pfr) {
+ try {
+ return this.accept_(pfr);
+ } catch (JavaModelException ex) {
+ return false;
+ }
+ }
+ private boolean accept_(IPackageFragmentRoot pfr) throws JavaModelException {
+ return packageFragmentRootIsSourceFolder_(pfr);
+ }
+ };
+
+ protected static Iterable<IPackageFragmentRoot> getPackageFragmentRoots(IJavaProject javaProject) throws JavaModelException {
+ return new ArrayIterable<IPackageFragmentRoot>(javaProject.getPackageFragmentRoots());
+ }
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTType.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTType.java
new file mode 100644
index 0000000000..f3438cfe8f
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JDTType.java
@@ -0,0 +1,238 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2010 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.utility.jdt;
+
+import java.util.List;
+
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.FieldDeclaration;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.TypeDeclaration;
+import org.eclipse.jpt.core.utility.TextRange;
+import org.eclipse.jpt.core.utility.jdt.AnnotationEditFormatter;
+import org.eclipse.jpt.core.utility.jdt.Type;
+import org.eclipse.jpt.utility.CommandExecutor;
+
+/**
+ * Adapt and extend a JDT type.
+ */
+public class JDTType
+ extends JDTMember
+ implements Type
+{
+
+ /**
+ * constructor for the compilation unit's primary type
+ */
+ public JDTType(
+ TypeDeclaration typeDeclaration, // exclude annotations and enums
+ ICompilationUnit compilationUnit,
+ CommandExecutor modifySharedDocumentCommandExecutor) {
+ this(typeDeclaration, compilationUnit, modifySharedDocumentCommandExecutor, DefaultAnnotationEditFormatter.instance());
+ }
+
+ /**
+ * constructor for the compilation unit's primary type
+ */
+ public JDTType(
+ TypeDeclaration typeDeclaration, // exclude annotations and enums
+ ICompilationUnit compilationUnit,
+ CommandExecutor modifySharedDocumentCommandExecutor,
+ AnnotationEditFormatter annotationEditFormatter) {
+ this(null, typeDeclaration, 1, compilationUnit, modifySharedDocumentCommandExecutor, annotationEditFormatter);
+ }
+
+ /**
+ * constructor for nested types
+ */
+ public JDTType(
+ Type declaringType,
+ TypeDeclaration typeDeclaration, // exclude annotations and enums
+ int occurrence,
+ ICompilationUnit compilationUnit,
+ CommandExecutor modifySharedDocumentCommandExecutor) {
+ this(declaringType, typeDeclaration, occurrence, compilationUnit, modifySharedDocumentCommandExecutor, DefaultAnnotationEditFormatter.instance());
+ }
+
+ /**
+ * constructor for nested types
+ */
+ public JDTType(
+ Type declaringType,
+ TypeDeclaration typeDeclaration, // exclude annotations and enums
+ int occurrence,
+ ICompilationUnit compilationUnit,
+ CommandExecutor modifySharedDocumentCommandExecutor,
+ AnnotationEditFormatter annotationEditFormatter) {
+ super(declaringType, typeDeclaration.getName().getFullyQualifiedName(), occurrence, compilationUnit, modifySharedDocumentCommandExecutor, annotationEditFormatter);
+ }
+
+ /**
+ * constructor for testing
+ */
+ public JDTType(Type declaringType, String name, int occurrence, ICompilationUnit compilationUnit) {
+ super(declaringType, name, occurrence, compilationUnit, CommandExecutor.Default.instance(), DefaultAnnotationEditFormatter.instance());
+ }
+
+
+ // ********** Member/Type implementation **********
+
+ public ITypeBinding getBinding(CompilationUnit astRoot) {
+ TypeDeclaration td = this.getBodyDeclaration(astRoot);
+ return (td == null) ? null : td.resolveBinding();
+ }
+
+ /**
+ * find the type's body declaration in the specified AST
+ */
+ public TypeDeclaration getBodyDeclaration(CompilationUnit astRoot) {
+ Type declaringType = this.getDeclaringType();
+ if (declaringType == null) {
+ return this.getTopLevelTypeDeclaration(astRoot);
+ }
+ TypeDeclaration typeDeclaration = declaringType.getBodyDeclaration(astRoot);
+ // the type declaration can be null when the source is completely hosed
+ return (typeDeclaration == null) ? null : this.getNestedTypeDeclaration(typeDeclaration);
+ }
+
+ public boolean isPersistable(CompilationUnit astRoot) {
+ ITypeBinding binding = this.getBinding(astRoot);
+ return (binding == null) ? false : JPTTools.typeIsPersistable(new JPTToolsAdapter(binding));
+ }
+
+ public TextRange getNameTextRange(CompilationUnit astRoot) {
+ return new ASTNodeTextRange(this.getBodyDeclaration(astRoot).getName());
+ }
+
+ public TypeDeclaration[] getTypes(CompilationUnit astRoot) {
+ return this.getBodyDeclaration(astRoot).getTypes();
+ }
+
+ public FieldDeclaration[] getFields(CompilationUnit astRoot) {
+ return this.getBodyDeclaration(astRoot).getFields();
+ }
+
+ public MethodDeclaration[] getMethods(CompilationUnit astRoot) {
+ return this.getBodyDeclaration(astRoot).getMethods();
+ }
+
+
+ // ********** internal **********
+
+ /**
+ * return the first top-level type in the specified AST with a matching name
+ */
+ protected TypeDeclaration getTopLevelTypeDeclaration(CompilationUnit astRoot) {
+ return this.getTypeDeclaration(types(astRoot));
+ }
+
+ protected TypeDeclaration getTypeDeclaration(List<AbstractTypeDeclaration> typeDeclarations) {
+ return this.getTypeDeclaration(typeDeclarations.toArray(new AbstractTypeDeclaration[typeDeclarations.size()]));
+ }
+
+ /**
+ * return the nested type with a matching name and occurrence
+ */
+ protected TypeDeclaration getNestedTypeDeclaration(TypeDeclaration declaringTypeDeclaration) {
+ return this.getTypeDeclaration(declaringTypeDeclaration.getTypes());
+ }
+
+ /**
+ * return the type declaration corresponding to the type from the specified
+ * set of type declarations (match name and occurrence)
+ */
+ protected TypeDeclaration getTypeDeclaration(AbstractTypeDeclaration[] typeDeclarations) {
+ String name = this.getName_();
+ int occurrence = this.getOccurrence();
+ int count = 0;
+ for (AbstractTypeDeclaration typeDeclaration : typeDeclarations) {
+ if (typeDeclaration.getName().getFullyQualifiedName().equals(name)) {
+ count++;
+ if (count == occurrence) {
+ return (typeDeclaration.getNodeType() == ASTNode.TYPE_DECLARATION) ? (TypeDeclaration) typeDeclaration : null;
+ }
+ }
+ }
+ // return null if the type is no longer in the source code;
+ // this can happen when the context model has not yet
+ // been synchronized with the resource model but is still
+ // asking for an ASTNode (e.g. during a selection event)
+ return null;
+ }
+
+ /**
+ * we only instantiate a single top-level, non-enum, non-annotation
+ * type per compilation unit (i.e. a class or interface); and, since
+ * enums and annotations can only be top-level types (i.e. they cannot
+ * be nested within another type) we will always have TypeDeclarations
+ * in the CompilationUnit
+ */
+ // minimize scope of suppressed warnings
+ @SuppressWarnings("unchecked")
+ protected static List<AbstractTypeDeclaration> types(CompilationUnit astRoot) {
+ return astRoot.types();
+ }
+
+
+ // ********** JPT tools adapter **********
+
+ protected static class JPTToolsAdapter implements JPTTools.TypeAdapter {
+ private final ITypeBinding typeBinding;
+ protected JPTToolsAdapter(ITypeBinding typeBinding) {
+ super();
+ if (typeBinding == null) {
+ throw new NullPointerException();
+ }
+ this.typeBinding = typeBinding;
+ }
+
+ public int getModifiers() {
+ return this.typeBinding.getModifiers();
+ }
+
+ public boolean isAnnotation() {
+ return this.typeBinding.isAnnotation();
+ }
+
+ public boolean isAnonymous() {
+ return this.typeBinding.isAnonymous();
+ }
+
+ public boolean isArray() {
+ return this.typeBinding.isArray();
+ }
+
+ public boolean isEnum() {
+ return this.typeBinding.isEnum();
+ }
+
+ public boolean isInterface() {
+ return this.typeBinding.isInterface();
+ }
+
+ public boolean isLocal() {
+ return this.typeBinding.isLocal();
+ }
+
+ public boolean isMember() {
+ return this.typeBinding.isMember();
+ }
+
+ public boolean isPrimitive() {
+ return this.typeBinding.isPrimitive();
+ }
+
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JPTTools.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JPTTools.java
new file mode 100644
index 0000000000..73b19d863b
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/JPTTools.java
@@ -0,0 +1,341 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 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.utility.jdt;
+
+import java.lang.reflect.Modifier;
+import java.util.Iterator;
+import org.eclipse.jpt.core.resource.java.AccessType;
+import org.eclipse.jpt.core.resource.java.JavaResourcePersistentAttribute;
+import org.eclipse.jpt.core.resource.java.JavaResourcePersistentType;
+
+/**
+ * Convenience methods for JPA-related queries concerning JDT objects.
+ */
+public class JPTTools {
+
+ // ********** type **********
+
+ /**
+ * Return whether the specified type can be "persisted", i.e. marked as
+ * Entity, MappedSuperclass, Embeddable
+ */
+ // TODO check for no-arg constructor (or should that just be validation?)
+ // TODO move other checks to validation (e.g. 'final', 'static')?
+ public static boolean typeIsPersistable(TypeAdapter typeAdapter) {
+ if (typeAdapter.isInterface()) {
+ return false;
+ }
+ if (typeAdapter.isAnnotation()) {
+ return false;
+ }
+ if (typeAdapter.isEnum()) {
+ return false;
+ }
+ if (typeAdapter.isLocal()) {
+ return false;
+ }
+ if (typeAdapter.isAnonymous()) {
+ return false;
+ }
+ if (typeAdapter.isPrimitive()) {
+ return false; // should never get here(?)
+ }
+ if (typeAdapter.isArray()) {
+ return false; // should never get here(?)
+ }
+ int modifiers = typeAdapter.getModifiers();
+ if (typeAdapter.isMember()) {
+ if ( ! Modifier.isStatic(modifiers)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Queries needed to calculate whether a type is "persistable".
+ * Adapted to ITypeBinding and IType.
+ */
+ public interface TypeAdapter {
+ int getModifiers();
+ boolean isAnnotation();
+ boolean isAnonymous();
+ boolean isArray();
+ boolean isEnum();
+ boolean isInterface();
+ boolean isLocal();
+ boolean isMember();
+ boolean isPrimitive();
+ }
+
+
+ // ********** field **********
+
+ /**
+ * Return whether the specified field may be "persisted".
+ * According to the spec, "All non-transient instance variables that are not
+ * annotated with the Transient annotation are persistent."
+ */
+ public static boolean fieldIsPersistable(FieldAdapter fieldAdapter) {
+ int modifiers = fieldAdapter.getModifiers();
+ if (Modifier.isStatic(modifiers)) {
+ return false;
+ }
+ if (Modifier.isTransient(modifiers)) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Queries needed to calculate whether a field is "persistable".
+ * Adapted to IVariableBinding and IField.
+ */
+ public interface FieldAdapter {
+ /**
+ * Return the field's modifiers. We use these to check whether the
+ * field is static or transient.
+ */
+ int getModifiers();
+ }
+
+
+ // ********** method **********
+
+ /**
+ * Return whether the specified method is a "getter" method that
+ * represents a property that may be "persisted".
+ */
+ public static boolean methodIsPersistablePropertyGetter(MethodAdapter methodAdapter) {
+ if (methodHasInvalidModifiers(methodAdapter)) {
+ return false;
+ }
+ if (methodAdapter.isConstructor()) {
+ return false;
+ }
+
+ String returnTypeName = methodAdapter.getReturnTypeErasureName();
+ if (returnTypeName == null) {
+ return false; // DOM method bindings can have a null name
+ }
+ if (returnTypeName.equals("void")) { //$NON-NLS-1$
+ return false;
+ }
+ if (methodHasParameters(methodAdapter)) {
+ return false;
+ }
+
+ String name = methodAdapter.getName();
+ int beginIndex = 0;
+ boolean booleanGetter = false;
+ if (name.startsWith("is")) { //$NON-NLS-1$
+ if (returnTypeName.equals("boolean")) { //$NON-NLS-1$
+ beginIndex = 2;
+ } else {
+ return false;
+ }
+ } else if (name.startsWith("get")) { //$NON-NLS-1$
+ beginIndex = 3;
+ if (returnTypeName.equals("boolean")) { //$NON-NLS-1$
+ booleanGetter = true;
+ }
+ } else {
+ return false;
+ }
+
+ String capitalizedAttributeName = name.substring(beginIndex);
+ // if the type has both methods:
+ // boolean isProperty()
+ // boolean getProperty()
+ // then #isProperty() takes precedence and we ignore #getProperty();
+ // but only having #getProperty() is OK too
+ // (see the JavaBeans spec 1.01)
+ if (booleanGetter && methodHasValidSiblingIsMethod(methodAdapter, capitalizedAttributeName)) {
+ return false; // since the type also defines #isProperty(), ignore #getProperty()
+ }
+ return methodHasValidSiblingSetMethod(methodAdapter, capitalizedAttributeName, returnTypeName);
+ }
+
+ /**
+ * Return whether the method's modifiers prevent it
+ * from being a getter or setter for a "persistent" property.
+ */
+ private static boolean methodHasInvalidModifiers(SimpleMethodAdapter methodAdapter) {
+ int modifiers = methodAdapter.getModifiers();
+ if (Modifier.isStatic(modifiers)) {
+ return true;
+ }
+ if (Modifier.isFinal(modifiers)) {
+ return true;
+ }
+ if ( ! (Modifier.isPublic(modifiers) || Modifier.isProtected(modifiers))) {
+ return true;
+ }
+ return false;
+ }
+
+ private static boolean methodHasParameters(MethodAdapter methodAdapter) {
+ return methodAdapter.getParametersLength() != 0;
+ }
+
+ /**
+ * Return whether the method has a sibling "is" method for the specified
+ * property and that method is valid for a "persistable" property.
+ * Pre-condition: the method is a "boolean getter" (e.g. 'public boolean getProperty()');
+ * this prevents us from returning true when the method itself is an
+ * "is" method.
+ */
+ private static boolean methodHasValidSiblingIsMethod(MethodAdapter methodAdapter, String capitalizedAttributeName) {
+ SimpleMethodAdapter isMethodAdapter = methodAdapter.getSibling("is" + capitalizedAttributeName); //$NON-NLS-1$
+ return methodIsValidSibling(isMethodAdapter, "boolean"); //$NON-NLS-1$
+ }
+
+ /**
+ * Return whether the method has a sibling "set" method
+ * and that method is valid for a "persistable" property.
+ */
+ private static boolean methodHasValidSiblingSetMethod(MethodAdapter methodAdapter, String capitalizedAttributeName, String parameterTypeErasureName) {
+ SimpleMethodAdapter setMethodAdapter = methodAdapter.getSibling("set" + capitalizedAttributeName, parameterTypeErasureName); //$NON-NLS-1$
+ return methodIsValidSibling(setMethodAdapter, "void"); //$NON-NLS-1$
+ }
+
+ /**
+ * Return whether the specified method is a valid sibling with the
+ * specified return type.
+ */
+ private static boolean methodIsValidSibling(SimpleMethodAdapter methodAdapter, String returnTypeName) {
+ if (methodAdapter == null) {
+ return false;
+ }
+ if (methodHasInvalidModifiers(methodAdapter)) {
+ return false;
+ }
+ if (methodAdapter.isConstructor()) {
+ return false;
+ }
+ String rtName = methodAdapter.getReturnTypeErasureName();
+ if (rtName == null) {
+ return false; // DOM method bindings can have a null name
+ }
+ return rtName.equals(returnTypeName);
+ }
+
+ /**
+ * Queries needed to calculate whether a method is "persistable".
+ * Adapted to IMethodBinding and IMethod.
+ */
+ public interface SimpleMethodAdapter {
+ /**
+ * Return the method's modifiers.
+ * We use these to check whether the method is static, final, etc.
+ */
+ int getModifiers();
+
+ /**
+ * Return the name of the method's return type erasure.
+ * We use this to check for
+ * - boolean getters
+ * - void return types
+ * - matching getters and setters
+ */
+ String getReturnTypeErasureName();
+
+ /**
+ * Return whether the method is a constructor.
+ */
+ boolean isConstructor();
+ }
+
+ /**
+ * Queries needed to calculate whether a method is "persistable".
+ * Adapted to IMethodBinding and IMethod.
+ */
+ public interface MethodAdapter extends SimpleMethodAdapter {
+ /**
+ * Return the method's name.
+ * We use this to determine
+ * - whether the method is a "getter"
+ * - the property name implied by the getter's name
+ */
+ String getName();
+
+ /**
+ * Return the number of paramters declared by the method.
+ * We use this to determine whether the method is a "getter".
+ */
+ int getParametersLength();
+
+ /**
+ * Return the method's "sibling" with the specified name and no parameters.
+ * We use this to find an "is" boolean getter that would take precedence
+ * over a "get" boolean getter.
+ */
+ SimpleMethodAdapter getSibling(String name);
+
+ /**
+ * Return the method's "sibling" with the specified name and single parameter.
+ * We use this to find a matching "setter" for a possible "getter".
+ */
+ SimpleMethodAdapter getSibling(String name, String parameterTypeErasureName);
+ }
+
+
+ // ********** Access type **********
+
+ /**
+ * Return the AccessType currently implied by the Java source code
+ * or class file:
+ * - if only Fields are annotated => FIELD
+ * - if only Properties are annotated => PROPERTY
+ * - if both Fields and Properties are annotated => FIELD
+ * - if nothing is annotated
+ * - and fields exist => FIELD
+ * - and properties exist, but no fields exist => PROPERTY
+ * - and neither fields nor properties exist => null at this level (FIELD in the context model)
+ */
+ public static AccessType buildAccess(JavaResourcePersistentType jrpType) {
+ boolean hasPersistableFields = false;
+ for (Iterator<JavaResourcePersistentAttribute> stream = jrpType.persistableFields(); stream.hasNext(); ) {
+ hasPersistableFields = true;
+ if (stream.next().isAnnotated()) {
+ // any field is annotated => FIELD
+ return AccessType.FIELD;
+ }
+ }
+
+ boolean hasPersistableProperties = false;
+ for (Iterator<JavaResourcePersistentAttribute> stream = jrpType.persistableProperties(); stream.hasNext(); ) {
+ hasPersistableProperties = true;
+ if (stream.next().isAnnotated()) {
+ // none of the fields are annotated and a getter is annotated => PROPERTY
+ return AccessType.PROPERTY;
+ }
+ }
+
+ if (hasPersistableProperties && ! hasPersistableFields) {
+ return AccessType.PROPERTY;
+ }
+
+ // if no annotations exist, access is null at the resource model level
+ return null;
+ }
+
+
+ // ********** suppressed constructor **********
+
+ /**
+ * Suppress default constructor, ensuring non-instantiability.
+ */
+ private JPTTools() {
+ super();
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/MemberAnnotationAdapter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/MemberAnnotationAdapter.java
new file mode 100644
index 0000000000..5b92fa57d8
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/MemberAnnotationAdapter.java
@@ -0,0 +1,27 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 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.utility.jdt;
+
+import org.eclipse.jpt.core.utility.jdt.DeclarationAnnotationAdapter;
+import org.eclipse.jpt.core.utility.jdt.Member;
+
+/**
+ * Adapt a member and a declaration annotation adapter.
+ */
+public class MemberAnnotationAdapter extends AbstractAnnotationAdapter {
+
+
+ // ********** constructor **********
+
+ public MemberAnnotationAdapter(Member member, DeclarationAnnotationAdapter daa) {
+ super(member, daa);
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/MemberAnnotationElementAdapter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/MemberAnnotationElementAdapter.java
new file mode 100644
index 0000000000..89f854f2e5
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/MemberAnnotationElementAdapter.java
@@ -0,0 +1,99 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 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.utility.jdt;
+
+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.core.utility.jdt.AnnotationElementAdapter;
+import org.eclipse.jpt.core.utility.jdt.DeclarationAnnotationElementAdapter;
+import org.eclipse.jpt.core.utility.jdt.Member;
+import org.eclipse.jpt.core.utility.jdt.ModifiedDeclaration;
+import org.eclipse.jpt.utility.internal.StringTools;
+
+ /**
+ * Adapt a member and a declaration annotation element adapter.
+ */
+public class MemberAnnotationElementAdapter<T>
+ implements AnnotationElementAdapter<T>
+{
+ private final Member member;
+ private final DeclarationAnnotationElementAdapter<T> daea;
+
+
+ // ********** constructor **********
+
+ public MemberAnnotationElementAdapter(Member member, DeclarationAnnotationElementAdapter<T> daea) {
+ super();
+ this.member = member;
+ this.daea = daea;
+ }
+
+
+ // ********** AnnotationElementAdapter implementation **********
+
+ public T getValue() {
+ return this.daea.getValue(this.member.getModifiedDeclaration());
+ }
+
+ public T getValue(CompilationUnit astRoot) {
+ return this.daea.getValue(this.member.getModifiedDeclaration(astRoot));
+ }
+
+ public void setValue(T value) {
+ this.edit(this.buildSetValueEditor(value));
+ }
+
+ public Expression getExpression(CompilationUnit astRoot) {
+ return this.daea.getExpression(this.member.getModifiedDeclaration(astRoot));
+ }
+
+ public ASTNode getAstNode(CompilationUnit astRoot) {
+ return this.daea.getAstNode(this.member.getModifiedDeclaration(astRoot));
+ }
+
+ @Override
+ public String toString() {
+ return StringTools.buildToStringFor(this, this.daea);
+ }
+
+
+ // ********** internal methods **********
+
+ protected void edit(Member.Editor editor) {
+ this.member.edit(editor);
+ }
+
+ protected Member.Editor buildSetValueEditor(T value) {
+ return new SetValueEditor<T>(value, this.daea);
+ }
+
+
+ // ********** member classes **********
+
+ protected static class SetValueEditor<T> implements Member.Editor {
+ private final DeclarationAnnotationElementAdapter<T> daea;
+ private final T value;
+
+ SetValueEditor(T value, DeclarationAnnotationElementAdapter<T> daea) {
+ super();
+ this.value = value;
+ this.daea = daea;
+ }
+ public void edit(ModifiedDeclaration declaration) {
+ this.daea.setValue(this.value, declaration);
+ }
+ @Override
+ public String toString() {
+ return StringTools.buildToStringFor(this);
+ }
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/MemberIndexedAnnotationAdapter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/MemberIndexedAnnotationAdapter.java
new file mode 100644
index 0000000000..aceeb48616
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/MemberIndexedAnnotationAdapter.java
@@ -0,0 +1,74 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 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.utility.jdt;
+
+import org.eclipse.jpt.core.utility.jdt.IndexedAnnotationAdapter;
+import org.eclipse.jpt.core.utility.jdt.IndexedDeclarationAnnotationAdapter;
+import org.eclipse.jpt.core.utility.jdt.Member;
+import org.eclipse.jpt.core.utility.jdt.ModifiedDeclaration;
+import org.eclipse.jpt.utility.internal.StringTools;
+
+/**
+ * Adapt a member and an indexed declaration annotation adapter.
+ */
+public class MemberIndexedAnnotationAdapter
+ extends AbstractAnnotationAdapter
+ implements IndexedAnnotationAdapter
+{
+ private final IndexedDeclarationAnnotationAdapter idaa;
+
+
+ // ********** constructor **********
+
+ public MemberIndexedAnnotationAdapter(Member member, IndexedDeclarationAnnotationAdapter idaa) {
+ super(member, idaa);
+ this.idaa = idaa;
+ }
+
+
+ // ********** IndexedAnnotationAdapter implementation **********
+
+ public int getIndex() {
+ return this.idaa.getIndex();
+ }
+
+ public void moveAnnotation(int newIndex) {
+ this.edit(this.buildMoveAnnotationEditor(newIndex));
+ }
+
+
+ // ********** factory methods **********
+
+ protected Member.Editor buildMoveAnnotationEditor(int newIndex) {
+ return new MoveAnnotationEditor(this.idaa, newIndex);
+ }
+
+
+ // ********** member classes **********
+
+ protected static class MoveAnnotationEditor implements Member.Editor {
+ private final IndexedDeclarationAnnotationAdapter idaa;
+ private int index;
+
+ MoveAnnotationEditor(IndexedDeclarationAnnotationAdapter idaa, int index) {
+ super();
+ this.idaa = idaa;
+ this.index = index;
+ }
+ public void edit(ModifiedDeclaration declaration) {
+ this.idaa.moveAnnotation(this.index, declaration);
+ }
+ @Override
+ public String toString() {
+ return StringTools.buildToStringFor(this);
+ }
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NameStringExpressionConverter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NameStringExpressionConverter.java
new file mode 100644
index 0000000000..56fd65a386
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NameStringExpressionConverter.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 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.utility.jdt;
+
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jpt.core.utility.jdt.ExpressionConverter;
+
+/**
+ * Convert a name to/from a string representation of a name/identifier
+ * (e.g. "com.xxx.Foo.VALUE1" or "value").
+ */
+public final class NameStringExpressionConverter
+ extends AbstractExpressionConverter<String>
+{
+ private static final ExpressionConverter<String> INSTANCE = new NameStringExpressionConverter();
+
+ /**
+ * Return the singleton.
+ */
+ public static ExpressionConverter<String> instance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Ensure single instance.
+ */
+ private NameStringExpressionConverter() {
+ super();
+ }
+
+ @Override
+ protected Name convertObject(String string, AST ast) {
+ return ast.newName(string);
+ }
+
+ @Override
+ protected String convertExpression(Expression expression) {
+ switch (expression.getNodeType()) {
+ case ASTNode.QUALIFIED_NAME:
+ case ASTNode.SIMPLE_NAME:
+ return ((Name) expression).getFullyQualifiedName();
+ default:
+ return null;
+ }
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NestedDeclarationAnnotationAdapter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NestedDeclarationAnnotationAdapter.java
new file mode 100644
index 0000000000..2b991a7c2e
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NestedDeclarationAnnotationAdapter.java
@@ -0,0 +1,95 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 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.utility.jdt;
+
+import org.eclipse.jdt.core.dom.Annotation;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.MemberValuePair;
+import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
+import org.eclipse.jpt.core.utility.jdt.DeclarationAnnotationAdapter;
+import org.eclipse.jpt.core.utility.jdt.ModifiedDeclaration;
+
+/**
+ * Manipulate an annotation that is embedded as an element within
+ * another annotation, e.g.
+ * <pre>
+ * &#64;Outer(foo=&#64;Inner)
+ * private int id;
+ * outerAnnotationAdapter = AnnotationAdapter<&#64;Outer>
+ * elementName = "foo"
+ * annotationName = "Inner"
+ * </pre>
+ */
+public class NestedDeclarationAnnotationAdapter extends AbstractNestedDeclarationAnnotationAdapter {
+
+
+ // ********** constructors **********
+
+ /**
+ * default element name is "value";
+ * default behavior is to remove the outer annotation when it is empty
+ */
+ public NestedDeclarationAnnotationAdapter(DeclarationAnnotationAdapter outerAnnotationAdapter, String annotationName) {
+ super(outerAnnotationAdapter, annotationName);
+ }
+
+ /**
+ * default behavior is to remove the outer annotation when it is empty
+ */
+ public NestedDeclarationAnnotationAdapter(DeclarationAnnotationAdapter outerAnnotationAdapter, String elementName, String annotationName) {
+ super(outerAnnotationAdapter, elementName, annotationName);
+ }
+
+ public NestedDeclarationAnnotationAdapter(DeclarationAnnotationAdapter outerAnnotationAdapter, String elementName, String annotationName, boolean removeOuterAnnotationWhenEmpty) {
+ super(outerAnnotationAdapter, elementName, annotationName, removeOuterAnnotationWhenEmpty);
+ }
+
+
+ // ********** AbstractNestedDeclarationAnnotationAdapter implementation **********
+
+ @Override
+ protected Annotation getAnnotation(Expression value) {
+ return this.annotationValue(value);
+ }
+
+ @Override
+ protected Expression buildNewInnerExpression(Annotation inner) {
+ return inner;
+ }
+
+ /**
+ * the annotation is the expression itself, so the annotation cannot be
+ * "removed" from itself - return 'false'
+ */
+ @Override
+ protected boolean removeAnnotation(ModifiedDeclaration declaration, Annotation outer, Expression value) {
+ return false;
+ }
+
+ /**
+ * <pre>
+ * &#64;Outer("lorem ipsum") => &#64;Outer(&#64;Inner)
+ * </pre>
+ */
+ @Override
+ protected void modifyAnnotationValue(SingleMemberAnnotation outer, Annotation inner) {
+ // replace(?) the current element value
+ outer.setValue(inner);
+ }
+
+ /**
+ * Simply set the pair's value.
+ */
+ @Override
+ protected void modifyMemberValuePair(MemberValuePair pair, Annotation inner) {
+ pair.setValue(inner);
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NestedIndexedDeclarationAnnotationAdapter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NestedIndexedDeclarationAnnotationAdapter.java
new file mode 100644
index 0000000000..8f39485e0e
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NestedIndexedDeclarationAnnotationAdapter.java
@@ -0,0 +1,334 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 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.utility.jdt;
+
+import java.util.List;
+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.Expression;
+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.utility.jdt.DeclarationAnnotationAdapter;
+import org.eclipse.jpt.core.utility.jdt.IndexedDeclarationAnnotationAdapter;
+import org.eclipse.jpt.core.utility.jdt.ModifiedDeclaration;
+
+/**
+ * Manipulate an annotation that is embedded in an element array within
+ * another annotation, e.g.
+ * <pre>
+ * &#64;Outer(foo={&#64;Inner("zero"), &#64;Inner("one"), &#64;Inner("two")})
+ * private int id;
+ * outerAnnotationAdapter = AnnotationAdapter<&#64;Outer>
+ * elementName = "foo"
+ * index = 0-2
+ * annotationName = "Inner"
+ * </pre>
+ */
+public class NestedIndexedDeclarationAnnotationAdapter
+ extends AbstractNestedDeclarationAnnotationAdapter
+ implements IndexedDeclarationAnnotationAdapter
+{
+ private int index;
+
+
+ // ********** constructors **********
+
+ /**
+ * default element name is "value";
+ * default behavior is to remove the outer annotation when it is empty
+ */
+ public NestedIndexedDeclarationAnnotationAdapter(DeclarationAnnotationAdapter annotationAdapter, int index, String annotationName) {
+ super(annotationAdapter, annotationName);
+ this.index = index;
+ }
+
+ /**
+ * default behavior is to remove the outer annotation when it is empty
+ */
+ public NestedIndexedDeclarationAnnotationAdapter(DeclarationAnnotationAdapter annotationAdapter, String elementName, int index, String annotationName) {
+ super(annotationAdapter, elementName, annotationName);
+ this.index = index;
+ }
+
+ public NestedIndexedDeclarationAnnotationAdapter(DeclarationAnnotationAdapter annotationAdapter, String elementName, int index, String annotationName, boolean removeOuterAnnotationWhenEmpty) {
+ super(annotationAdapter, elementName, annotationName, removeOuterAnnotationWhenEmpty);
+ this.index = index;
+ }
+
+
+ // ********** AbstractNestedDeclarationAnnotationAdapter implementation **********
+
+ @Override
+ protected Annotation getAnnotation(Expression value) {
+ if (value.getNodeType() == ASTNode.ARRAY_INITIALIZER) {
+ return this.annotation((ArrayInitializer) value);
+ }
+ return (this.index == 0) ? this.annotationValue(value) : null;
+ }
+
+ @Override
+ protected Expression buildNewInnerExpression(Annotation inner) {
+ return (this.index == 0) ? inner : (Expression) this.buildNewInnerArrayInitializer(inner);
+ }
+
+ @Override
+ protected boolean removeAnnotation(ModifiedDeclaration declaration, Annotation outer, Expression value) {
+ if (value.getNodeType() == ASTNode.ARRAY_INITIALIZER) {
+ this.removeAnnotation(declaration, outer, (ArrayInitializer) value);
+ return true;
+ }
+ // if our index is greater than zero, but we don't have an array,
+ // then the annotation must already be gone
+ return (this.index != 0);
+ }
+
+ /**
+ * <pre>
+ * &#64;Outer({&#64;Inner(0), &#64;Inner(1)}) => &#64;Outer({&#64;Inner(0), &#64;Inner(1), &#64;Inner(2)})
+ * or
+ * &#64;Outer("lorem ipsum") => &#64;Outer(&#64;Inner(0))
+ * or
+ * &#64;Outer(&#64;Inner(0)) => &#64;Outer({&#64;Inner(0), &#64;Inner(1)})
+ * </pre>
+ */
+ @Override
+ protected void modifyAnnotationValue(SingleMemberAnnotation outer, Annotation inner) {
+ this.modifyExpression(outer, SINGLE_MEMBER_ANNOTATION_EXPRESSION_PROVIDER, inner);
+ }
+
+ /**
+ * <pre>
+ * &#64;Outer(text="lorem ipsum") => &#64;Outer(text="lorem ipsum", foo=&#64;Inner(0))
+ * or
+ * &#64;Outer(foo={&#64;Inner(0), &#64;Inner(1)}) => &#64;Outer(foo={&#64;Inner(0), &#64;Inner(1), &#64;Inner(2)})
+ * or
+ * &#64;Outer(foo="lorem ipsum") => &#64;Outer(foo=&#64;Inner(0))
+ * or
+ * &#64;Outer(foo=&#64;NotInner) => &#64;Outer(foo=&#64;Inner(0))
+ * or
+ * &#64;Outer(foo=&#64;Inner(0)) => &#64;Outer(foo={&#64;Inner(0), &#64;Inner(1)})
+ * </pre>
+ */
+ @Override
+ protected void modifyMemberValuePair(MemberValuePair pair, Annotation inner) {
+ this.modifyExpression(pair, MEMBER_VALUE_PAIR_EXPRESSION_PROVIDER, inner);
+ }
+
+
+ // ********** IndexedDeclarationAnnotationAdapter implementation **********
+
+ public int getIndex() {
+ return this.index;
+ }
+
+ /**
+ * Move the annotation to the specified index, leaving its original
+ * position cleared out.
+ */
+ public void moveAnnotation(int newIndex, ModifiedDeclaration declaration) {
+ int oldIndex = this.index;
+ if (newIndex == oldIndex) {
+ return;
+ }
+
+ Annotation original = this.getAnnotation(declaration);
+ if (original == null) {
+ this.index = newIndex;
+ this.removeAnnotation(declaration); // clear out the new location (?)
+ } else {
+ Annotation copy = (Annotation) ASTNode.copySubtree(original.getAST(), original);
+ this.index = newIndex;
+ this.addAnnotation(declaration, copy); // install the copy in the new location
+ this.index = oldIndex;
+ this.removeAnnotation(declaration); // go back and clear out the original location (AFTER the move)
+ this.index = newIndex;
+ }
+ }
+
+
+ // ********** internal methods **********
+
+ /**
+ * Return the adapter's annotation from the specified array initializer.
+ */
+ private Annotation annotation(ArrayInitializer value) {
+ List<Expression> expressions = this.expressions(value);
+ return (this.index >= expressions.size()) ? null : this.annotationValue(expressions.get(this.index));
+ }
+
+ /**
+ * Build a new array initializer to hold the specified annotation,
+ * padding it with 'null' literals as necessary
+ */
+ private ArrayInitializer buildNewInnerArrayInitializer(Annotation inner) {
+ ArrayInitializer ai = inner.getAST().newArrayInitializer();
+ this.addInnerToExpressions(inner, this.expressions(ai));
+ return ai;
+ }
+
+ /**
+ * Add the specified annotation to the specified array initializer,
+ * padding it with 'null' literals as necessary
+ */
+ private void addInnerToExpressions(Annotation inner, List<Expression> expressions) {
+ if (expressions.size() > this.index) {
+ throw new IllegalStateException("expressions size is greater than index - size: " + expressions.size() + " - index: " + this.index); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ while (expressions.size() < this.index) {
+ expressions.add(inner.getAST().newNullLiteral());
+ }
+ expressions.add(inner);
+ }
+
+ /**
+ * Remove the adapter's annotation from the specified array initializer.
+ */
+ private void removeAnnotation(ModifiedDeclaration declaration, Annotation outer, ArrayInitializer value) {
+ List<Expression> expressions = this.expressions(value);
+ if (this.index >= expressions.size()) {
+ return; // avoid IndexOutOfBoundsException(?)
+ }
+ Annotation inner = this.annotationValue(expressions.get(this.index));
+ if (inner == null) {
+ return;
+ }
+ if ( ! this.nameMatches(declaration, inner)) {
+ return;
+ }
+ if (this.index == (expressions.size() - 1)) {
+ expressions.remove(this.index);
+ } else {
+ expressions.set(this.index, value.getAST().newNullLiteral());
+ }
+ this.trimExpressions(declaration, outer, expressions);
+ }
+
+ /**
+ * Strip all the null literals off the end of the specified list of expressions
+ * and normalize the specified outer annotation.
+ */
+ private void trimExpressions(ModifiedDeclaration declaration, Annotation outer, List<Expression> expressions) {
+ // start at the end of the list
+ for (int i = expressions.size(); i-- > 0; ) {
+ if (expressions.get(i).getNodeType() == ASTNode.NULL_LITERAL) {
+ expressions.remove(i);
+ } else {
+ break; // stop with the first non-null literal encountered
+ }
+ }
+ switch (expressions.size()) {
+ case 0:
+ this.removeElementAndNormalize(declaration, outer);
+ break;
+ case 1:
+ this.convertArrayToLastRemainingExpression(outer, expressions.get(0));
+ break;
+ default:
+ break;
+ }
+ }
+
+ /**
+ * When there is only a single element in an array initializer, convert the
+ * expression to be just the single element; e.g.
+ * <pre>
+ * &#64;Foo(xxx={"abc"}) => &#64;Foo(xxx="abc")
+ * or
+ * &#64;Foo({"abc"}) => &#64;Foo("abc")
+ * </pre>
+ */
+ private void convertArrayToLastRemainingExpression(Annotation outer, Expression lastValue) {
+ lastValue = (Expression) ASTNode.copySubtree(lastValue.getAST(), lastValue);
+ if (outer.isNormalAnnotation()) {
+ this.memberValuePair((NormalAnnotation) outer).setValue(lastValue);
+ } else if (outer.isSingleMemberAnnotation()) {
+ ((SingleMemberAnnotation) outer).setValue(lastValue);
+ } else {
+ throw new IllegalArgumentException("unexpected annotation type: " + outer); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Manipulate the specified expression appropriately.
+ * If it is an array initializer, add the specified annotation to it.
+ * If it is not, replace the expression or convert it into an array
+ * initializer.
+ */
+ private void modifyExpression(ASTNode node, ExpressionProvider expProvider, Annotation inner) {
+ Expression value = expProvider.getExpression(node);
+ if (value.getNodeType() == ASTNode.ARRAY_INITIALIZER) {
+ // ignore the other entries in the array initializer(?) - they may not be matching Annotations...
+ List<Expression> expressions = this.expressions((ArrayInitializer) value);
+ if (this.index >= expressions.size()) {
+ this.addInnerToExpressions(inner, expressions);
+ } else {
+ expressions.set(this.index, inner);
+ }
+ } else {
+ if (this.index == 0) {
+ // replace whatever was there before
+ expProvider.setExpression(node, inner);
+ } else {
+ // convert to an array
+ ArrayInitializer ai = inner.getAST().newArrayInitializer();
+ List<Expression> expressions = this.expressions(ai);
+ expressions.add((Expression) ASTNode.copySubtree(value.getAST(), value));
+ this.addInnerToExpressions(inner, expressions);
+ expProvider.setExpression(node, ai);
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected List<Expression> expressions(ArrayInitializer ai) {
+ return ai.expressions();
+ }
+
+
+ // ********** expression providers **********
+
+ /**
+ * define interface that allows us to "re-use" the code in
+ * #modifyExpression(ASTNode, ExpressionProvider, Annotation)
+ */
+ private interface ExpressionProvider {
+ Expression getExpression(ASTNode node);
+ void setExpression(ASTNode node, Expression expression);
+ }
+
+ private static final ExpressionProvider MEMBER_VALUE_PAIR_EXPRESSION_PROVIDER = new ExpressionProvider() {
+ public Expression getExpression(ASTNode node) {
+ return ((MemberValuePair) node).getValue();
+ }
+ public void setExpression(ASTNode node, Expression expression) {
+ ((MemberValuePair) node).setValue(expression);
+ }
+ @Override
+ public String toString() {
+ return "MemberValuePairExpressionProvider"; //$NON-NLS-1$
+ }
+ };
+
+ private static final ExpressionProvider SINGLE_MEMBER_ANNOTATION_EXPRESSION_PROVIDER = new ExpressionProvider() {
+ public Expression getExpression(ASTNode node) {
+ return ((SingleMemberAnnotation) node).getValue();
+ }
+ public void setExpression(ASTNode node, Expression expression) {
+ ((SingleMemberAnnotation) node).setValue(expression);
+ }
+ @Override
+ public String toString() {
+ return "SingleMemberAnnotationExpressionProvider"; //$NON-NLS-1$
+ }
+ };
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NullAnnotationEditFormatter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NullAnnotationEditFormatter.java
new file mode 100644
index 0000000000..3c632f36c3
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NullAnnotationEditFormatter.java
@@ -0,0 +1,42 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 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.utility.jdt;
+
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jpt.core.utility.jdt.AnnotationEditFormatter;
+import org.eclipse.text.edits.MalformedTreeException;
+import org.eclipse.text.edits.TextEdit;
+
+public final class NullAnnotationEditFormatter
+ implements AnnotationEditFormatter
+{
+
+ private static final NullAnnotationEditFormatter INSTANCE = new NullAnnotationEditFormatter();
+
+ /**
+ * Return the singleton.
+ */
+ public static AnnotationEditFormatter instance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Ensure single instance.
+ */
+ private NullAnnotationEditFormatter() {
+ super();
+ }
+
+ public void format(IDocument doc, TextEdit editTree) throws MalformedTreeException, BadLocationException {
+ // no formatting
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NullDeclarationAnnotationAdapter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NullDeclarationAnnotationAdapter.java
new file mode 100644
index 0000000000..d81448ce1b
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NullDeclarationAnnotationAdapter.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 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.utility.jdt;
+
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.Annotation;
+import org.eclipse.jdt.core.dom.MarkerAnnotation;
+import org.eclipse.jdt.core.dom.NormalAnnotation;
+import org.eclipse.jdt.core.dom.SingleMemberAnnotation;
+import org.eclipse.jpt.core.utility.jdt.IndexedDeclarationAnnotationAdapter;
+import org.eclipse.jpt.core.utility.jdt.ModifiedDeclaration;
+
+/**
+ * Behaviorless implementation.
+ */
+public final class NullDeclarationAnnotationAdapter
+ implements IndexedDeclarationAnnotationAdapter
+{
+
+ // singleton
+ private static final NullDeclarationAnnotationAdapter INSTANCE = new NullDeclarationAnnotationAdapter();
+
+ /**
+ * Return the singleton.
+ */
+ public static IndexedDeclarationAnnotationAdapter instance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Ensure single instance.
+ */
+ private NullDeclarationAnnotationAdapter() {
+ super();
+ }
+
+
+ // ********** DeclarationAnnotationAdapter implementation **********
+
+ public Annotation getAnnotation(ModifiedDeclaration declaration) {
+ return null;
+ }
+
+ public MarkerAnnotation newMarkerAnnotation(ModifiedDeclaration declaration) {
+ return null;
+ }
+
+ public SingleMemberAnnotation newSingleMemberAnnotation(ModifiedDeclaration declaration) {
+ return null;
+ }
+
+ public NormalAnnotation newNormalAnnotation(ModifiedDeclaration declaration) {
+ return null;
+ }
+
+ public void removeAnnotation(ModifiedDeclaration declaration) {
+ // do nothing
+ }
+
+ public ASTNode getAstNode(ModifiedDeclaration declaration) {
+ return declaration.getDeclaration();
+ }
+
+
+ // ********** IndexedDeclarationAnnotationAdapter implementation **********
+
+ public int getIndex() {
+ return -1;
+ }
+
+ public void moveAnnotation(int newIndex, ModifiedDeclaration declaration) {
+ // do nothing
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NullDeclarationAnnotationElementAdapter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NullDeclarationAnnotationElementAdapter.java
new file mode 100644
index 0000000000..c53a69546a
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NullDeclarationAnnotationElementAdapter.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 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.utility.jdt;
+
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jpt.core.utility.jdt.DeclarationAnnotationElementAdapter;
+import org.eclipse.jpt.core.utility.jdt.ModifiedDeclaration;
+
+/**
+ * Behaviorless implementation.
+ */
+public class NullDeclarationAnnotationElementAdapter<T>
+ implements DeclarationAnnotationElementAdapter<T>
+{
+
+ // singleton
+ @SuppressWarnings("unchecked")
+ private static final DeclarationAnnotationElementAdapter INSTANCE
+ = new NullDeclarationAnnotationElementAdapter();
+
+ /**
+ * Return the singleton.
+ */
+ @SuppressWarnings("unchecked")
+ public static <S> DeclarationAnnotationElementAdapter<S> instance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Ensure single instance.
+ */
+ private NullDeclarationAnnotationElementAdapter() {
+ super();
+ }
+
+ public T getValue(ModifiedDeclaration declaration) {
+ return null;
+ }
+
+ public void setValue(T value, ModifiedDeclaration declaration) {
+ // do nothing
+ }
+
+ public ASTNode getAstNode(ModifiedDeclaration declaration) {
+ return declaration.getDeclaration();
+ }
+
+ public Expression getExpression(ModifiedDeclaration declaration) {
+ return null;
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NullExpressionConverter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NullExpressionConverter.java
new file mode 100644
index 0000000000..803b983910
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NullExpressionConverter.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 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.utility.jdt;
+
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jpt.core.utility.jdt.ExpressionConverter;
+import org.eclipse.jpt.utility.internal.StringTools;
+
+/**
+ * No conversion.
+ */
+public final class NullExpressionConverter
+ implements ExpressionConverter<Expression>
+{
+
+ // singleton
+ private static final ExpressionConverter<Expression> INSTANCE = new NullExpressionConverter();
+
+ /**
+ * Return the singleton.
+ */
+ public static ExpressionConverter<Expression> instance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Ensure single instance.
+ */
+ private NullExpressionConverter() {
+ super();
+ }
+
+ public Expression convert(Expression expression, AST ast) {
+ return expression;
+ }
+
+ public Expression convert(Expression expression) {
+ return expression;
+ }
+
+ @Override
+ public String toString() {
+ return StringTools.buildToStringFor(this);
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NumberIntegerExpressionConverter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NumberIntegerExpressionConverter.java
new file mode 100644
index 0000000000..526e1fe4aa
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/NumberIntegerExpressionConverter.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 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.utility.jdt;
+
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.NumberLiteral;
+import org.eclipse.jpt.core.utility.jdt.ExpressionConverter;
+
+/**
+ * Convert a number literal to/from an Integer
+ * (e.g. 5).
+ */
+public final class NumberIntegerExpressionConverter
+ extends AbstractExpressionConverter<Integer>
+{
+ private static final ExpressionConverter<Integer> INSTANCE = new NumberIntegerExpressionConverter();
+
+ /**
+ * Return the singleton.
+ */
+ public static ExpressionConverter<Integer> instance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Ensure single instance.
+ */
+ private NumberIntegerExpressionConverter() {
+ super();
+ }
+
+ @Override
+ protected NumberLiteral convertObject(Integer integer, AST ast) {
+ return ast.newNumberLiteral(integer.toString());
+ }
+
+ @Override
+ protected Integer convertExpression(Expression expression) {
+ Object value = expression.resolveConstantExpressionValue();
+ return (value instanceof Integer) ? ((Integer) value) : null;
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/PrimitiveTypeStringExpressionConverter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/PrimitiveTypeStringExpressionConverter.java
new file mode 100644
index 0000000000..3f9990680f
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/PrimitiveTypeStringExpressionConverter.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 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.utility.jdt;
+
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.PrimitiveType;
+import org.eclipse.jdt.core.dom.TypeLiteral;
+import org.eclipse.jpt.core.utility.jdt.ExpressionConverter;
+
+/**
+ * Convert a type literal to/from a string representation of a primitive type
+ * (e.g. "int").
+ */
+public final class PrimitiveTypeStringExpressionConverter
+ extends AbstractExpressionConverter<String>
+{
+ private static final ExpressionConverter<String> INSTANCE = new PrimitiveTypeStringExpressionConverter();
+
+ /**
+ * Return the singleton.
+ */
+ public static ExpressionConverter<String> instance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Ensure single instance.
+ */
+ private PrimitiveTypeStringExpressionConverter() {
+ super();
+ }
+
+ @Override
+ protected TypeLiteral convertObject(String string, AST ast) {
+ org.eclipse.jdt.core.dom.Type type = ast.newPrimitiveType(PrimitiveType.toCode(string));
+ TypeLiteral typeLiteral = ast.newTypeLiteral();
+ typeLiteral.setType(type);
+ return typeLiteral;
+ }
+
+ @Override
+ protected String convertExpression(Expression expression) {
+ if (expression.getNodeType() == ASTNode.TYPE_LITERAL) {
+ org.eclipse.jdt.core.dom.Type type = ((TypeLiteral) expression).getType();
+ if (type.getNodeType() == ASTNode.PRIMITIVE_TYPE) {
+ return ((PrimitiveType) type).getPrimitiveTypeCode().toString();
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/ShortCircuitAnnotationElementAdapter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/ShortCircuitAnnotationElementAdapter.java
new file mode 100644
index 0000000000..44c27535c8
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/ShortCircuitAnnotationElementAdapter.java
@@ -0,0 +1,104 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 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.utility.jdt;
+
+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.core.utility.jdt.AnnotationElementAdapter;
+import org.eclipse.jpt.core.utility.jdt.DeclarationAnnotationElementAdapter;
+import org.eclipse.jpt.core.utility.jdt.Member;
+import org.eclipse.jpt.utility.internal.StringTools;
+
+/**
+ * Wrap another annotation element adapter and short-circuit the
+ * #setValue method if the value has not changed.
+ */
+public class ShortCircuitAnnotationElementAdapter<T>
+ implements AnnotationElementAdapter<T>
+{
+ /** the wrapped adapter */
+ private final AnnotationElementAdapter<T> adapter;
+
+
+ // ********** constructor **********
+
+ public ShortCircuitAnnotationElementAdapter(Member member, DeclarationAnnotationElementAdapter<T> daea) {
+ this(new MemberAnnotationElementAdapter<T>(member, daea));
+ }
+
+ public ShortCircuitAnnotationElementAdapter(AnnotationElementAdapter<T> adapter) {
+ super();
+ this.adapter = adapter;
+ }
+
+
+ // ********** AnnotationElementAdapter implementation **********
+
+ public T getValue() {
+ return this.adapter.getValue();
+ }
+
+ public T getValue(CompilationUnit astRoot) {
+ return this.adapter.getValue(astRoot);
+ }
+
+ public void setValue(T value) {
+ this.setValue(this.adapter.getValue(), value);
+ }
+
+ public Expression getExpression(CompilationUnit astRoot) {
+ return this.adapter.getExpression(astRoot);
+ }
+
+ public ASTNode getAstNode(CompilationUnit astRoot) {
+ return this.adapter.getAstNode(astRoot);
+ }
+
+ @Override
+ public String toString() {
+ return StringTools.buildToStringFor(this, this.adapter);
+ }
+
+
+ // ********** internal methods **********
+
+ /**
+ * set the adapter's value to the specified new value if it
+ * is different from the specified old value
+ */
+ protected void setValue(T oldValue, T newValue) {
+ if (oldValue == null) {
+ if (newValue == null) { // null => null
+ // do nothing
+ } else { // null => object
+ this.adapter.setValue(newValue);
+ }
+ } else {
+ if (newValue == null) { // object => null
+ this.adapter.setValue(null);
+ } else { // object => object
+ if (this.valuesAreEqual(oldValue, newValue)) {
+ // do nothing
+ } else {
+ this.adapter.setValue(newValue);
+ }
+ }
+ }
+ }
+
+ /**
+ * both values are non-null when this method is called
+ */
+ protected boolean valuesAreEqual(T oldValue, T newValue) {
+ return newValue.equals(oldValue);
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/ShortCircuitArrayAnnotationElementAdapter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/ShortCircuitArrayAnnotationElementAdapter.java
new file mode 100644
index 0000000000..01952fe059
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/ShortCircuitArrayAnnotationElementAdapter.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 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.utility.jdt;
+
+import java.util.Arrays;
+import org.eclipse.jpt.core.utility.jdt.AnnotationElementAdapter;
+import org.eclipse.jpt.core.utility.jdt.DeclarationAnnotationElementAdapter;
+import org.eclipse.jpt.core.utility.jdt.Member;
+
+/**
+ * Wrap another annotation element adapter and short-circuit the
+ * #setValue method if the value has not changed. Overrides #valuesAreEqual()
+ * to check equality on arrays
+ */
+public class ShortCircuitArrayAnnotationElementAdapter<T>
+ extends ShortCircuitAnnotationElementAdapter<T[]>
+{
+ // ********** constructor **********
+
+ public ShortCircuitArrayAnnotationElementAdapter(Member member, DeclarationAnnotationElementAdapter<T[]> daea) {
+ super(member, daea);
+ }
+
+ public ShortCircuitArrayAnnotationElementAdapter(AnnotationElementAdapter<T[]> adapter) {
+ super(adapter);
+ }
+
+
+ // ********** AnnotationElementAdapter implementation **********
+
+ @Override
+ protected boolean valuesAreEqual(T[] oldValue, T[] newValue) {
+ return Arrays.equals(newValue, oldValue);
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/SimpleDeclarationAnnotationAdapter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/SimpleDeclarationAnnotationAdapter.java
new file mode 100644
index 0000000000..16a657437b
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/SimpleDeclarationAnnotationAdapter.java
@@ -0,0 +1,62 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 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.utility.jdt;
+
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.Annotation;
+import org.eclipse.jpt.core.utility.jdt.ModifiedDeclaration;
+
+/**
+ * Manipulate an annotation with a specific name, e.g.
+ * <pre>
+ * &#64;Foo
+ * private int id;
+ * </pre>
+ *
+ * NB:
+ * If the declaration contains more than one annotation with the same
+ * name, the adapter will correspond to the first annotation with the specified
+ * name. (Also note that the compiler will not allow a declaration to be
+ * modified by multiple annotations with the same name, i.e. of the same type;
+ * so if there *are* multiple annotations of the same type, there are bigger
+ * problems to worry about than which annotation the adapter manipulates.)
+ */
+public class SimpleDeclarationAnnotationAdapter extends AbstractDeclarationAnnotationAdapter {
+
+
+ // ********** constructors **********
+
+ public SimpleDeclarationAnnotationAdapter(String annotationName) {
+ super(annotationName);
+ }
+
+
+ // ********** DeclarationAnnotationAdapter implementation **********
+
+ public Annotation getAnnotation(ModifiedDeclaration declaration) {
+ return declaration.getAnnotationNamed(this.getAnnotationName());
+ }
+
+ public void removeAnnotation(ModifiedDeclaration declaration) {
+ declaration.removeAnnotationNamed(this.getAnnotationName());
+ }
+
+ @Override
+ protected void addAnnotation(ModifiedDeclaration declaration, Annotation annotation) {
+ declaration.replaceAnnotationNamed(this.getAnnotationName(), annotation);
+ }
+
+ public ASTNode getAstNode(ModifiedDeclaration declaration) {
+ // if the annotation is missing, return the declaration
+ Annotation annotation = this.getAnnotation(declaration);
+ return (annotation != null) ? annotation : declaration.getDeclaration();
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/SimpleTypeStringExpressionConverter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/SimpleTypeStringExpressionConverter.java
new file mode 100644
index 0000000000..f622562554
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/SimpleTypeStringExpressionConverter.java
@@ -0,0 +1,63 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 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.utility.jdt;
+
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.Name;
+import org.eclipse.jdt.core.dom.SimpleType;
+import org.eclipse.jdt.core.dom.TypeLiteral;
+import org.eclipse.jpt.core.utility.jdt.ExpressionConverter;
+
+/**
+ * Convert a type literal to/from a string representation of a simple type
+ * (e.g. "java.lang.Object").
+ */
+public final class SimpleTypeStringExpressionConverter
+ extends AbstractExpressionConverter<String>
+{
+ private static final ExpressionConverter<String> INSTANCE = new SimpleTypeStringExpressionConverter();
+
+ /**
+ * Return the singleton.
+ */
+ public static ExpressionConverter<String> instance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Ensure single instance.
+ */
+ private SimpleTypeStringExpressionConverter() {
+ super();
+ }
+
+ @Override
+ protected TypeLiteral convertObject(String string, AST ast) {
+ Name name = ast.newName(string);
+ org.eclipse.jdt.core.dom.Type type = ast.newSimpleType(name);
+ TypeLiteral typeLiteral = ast.newTypeLiteral();
+ typeLiteral.setType(type);
+ return typeLiteral;
+ }
+
+ @Override
+ protected String convertExpression(Expression expression) {
+ if (expression.getNodeType() == ASTNode.TYPE_LITERAL) {
+ org.eclipse.jdt.core.dom.Type type = ((TypeLiteral) expression).getType();
+ if (type.getNodeType() == ASTNode.SIMPLE_TYPE) {
+ return ((SimpleType) type).getName().getFullyQualifiedName();
+ }
+ }
+ return null;
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/StringArrayExpressionConverter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/StringArrayExpressionConverter.java
new file mode 100644
index 0000000000..35751f6e52
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/StringArrayExpressionConverter.java
@@ -0,0 +1,111 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 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.utility.jdt;
+
+import java.util.List;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTNode;
+import org.eclipse.jdt.core.dom.ArrayInitializer;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jpt.core.utility.jdt.ExpressionConverter;
+
+/**
+ * Convert an array initializer to/from an array of strings (e.g. {"text0", "text1"}).
+ *
+ * Do NOT use this class for converting array initializers in annotation elements.
+ * Java5 has a bit of syntactic sugar that allows a single-element array
+ * initializer to not have curly braces. This converter will return null if it encounters
+ * anything other than an array initializer.
+ *
+ * Invalid entries in the array initializer will result in null elements in the
+ * resulting string array. This allows clients to manipulate elements at
+ * the appropriate index.
+ */
+public class StringArrayExpressionConverter
+ extends AbstractExpressionConverter<String[]>
+{
+ private final ExpressionConverter<String> elementConverter;
+ private final boolean removeArrayInitializerWhenEmpty;
+
+ private static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+
+ /**
+ * The default behavior is to remove the array initializer if it is empty.
+ */
+ public StringArrayExpressionConverter(ExpressionConverter<String> elementConverter) {
+ this(elementConverter, true);
+ }
+
+ public StringArrayExpressionConverter(ExpressionConverter<String> elementConverter, boolean removeArrayInitializerWhenEmpty) {
+ super();
+ this.elementConverter = elementConverter;
+ this.removeArrayInitializerWhenEmpty = removeArrayInitializerWhenEmpty;
+ }
+
+ @Override
+ /*
+ * this method is 'public' so it can be called by
+ * AnnotationStringArrayExpressionConverter
+ */
+ public ArrayInitializer convertObject(String[] strings, AST ast) {
+ if ((strings.length == 0) && this.removeArrayInitializerWhenEmpty) {
+ return null;
+ }
+ ArrayInitializer arrayInitializer = ast.newArrayInitializer();
+ List<Expression> expressions = this.expressions(arrayInitializer);
+ for (String string : strings) {
+ expressions.add(this.elementConverter.convert(string, ast));
+ }
+ return arrayInitializer;
+ }
+
+ @SuppressWarnings("unchecked")
+ private List<Expression> expressions(ArrayInitializer arrayInitializer) {
+ return arrayInitializer.expressions();
+ }
+
+ /*
+ * this method is 'public' so it can be called by
+ * AnnotationStringArrayExpressionConverter
+ */
+ @Override
+ public String[] convertNull() {
+ return EMPTY_STRING_ARRAY;
+ }
+
+ @Override
+ protected String[] convertExpression(Expression expression) {
+ return (expression.getNodeType() == ASTNode.ARRAY_INITIALIZER) ?
+ this.convertArrayInitializer((ArrayInitializer) expression)
+ :
+ EMPTY_STRING_ARRAY;
+ }
+
+ /*
+ * this method is 'public' so it can be called by
+ * AnnotationStringArrayExpressionConverter
+ */
+ public String[] convertArrayInitializer(ArrayInitializer arrayInitializer) {
+ List<Expression> expressions = this.downcastExpressions(arrayInitializer);
+ int len = expressions.size();
+ String[] strings = new String[len];
+ for (int i = len; i-- > 0; ) {
+ strings[i] = this.elementConverter.convert(expressions.get(i));
+ }
+ return strings;
+ }
+
+ @SuppressWarnings("unchecked")
+ private List<Expression> downcastExpressions(ArrayInitializer arrayInitializer) {
+ return arrayInitializer.expressions();
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/StringExpressionConverter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/StringExpressionConverter.java
new file mode 100644
index 0000000000..b5fee1e52d
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/StringExpressionConverter.java
@@ -0,0 +1,52 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 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.utility.jdt;
+
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.StringLiteral;
+import org.eclipse.jpt.core.utility.jdt.ExpressionConverter;
+
+/**
+ * Convert a string literal to/from a string (e.g. "text").
+ */
+public final class StringExpressionConverter
+ extends AbstractExpressionConverter<String>
+{
+ private static final ExpressionConverter<String> INSTANCE = new StringExpressionConverter();
+
+ /**
+ * Return the singleton.
+ */
+ public static ExpressionConverter<String> instance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Ensure single instance.
+ */
+ private StringExpressionConverter() {
+ super();
+ }
+
+ @Override
+ protected StringLiteral convertObject(String string, AST ast) {
+ StringLiteral stringLiteral = ast.newStringLiteral();
+ stringLiteral.setLiteralValue(string);
+ return stringLiteral;
+ }
+
+ @Override
+ protected String convertExpression(Expression expression) {
+ Object value = expression.resolveConstantExpressionValue();
+ return (value instanceof String) ? (String) value : null;
+ }
+
+}
diff --git a/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/TypeStringExpressionConverter.java b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/TypeStringExpressionConverter.java
new file mode 100644
index 0000000000..4ca6248449
--- /dev/null
+++ b/jpa/plugins/org.eclipse.jpt.core/src/org/eclipse/jpt/core/internal/utility/jdt/TypeStringExpressionConverter.java
@@ -0,0 +1,55 @@
+/*******************************************************************************
+ * Copyright (c) 2008 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.utility.jdt;
+
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.Expression;
+import org.eclipse.jdt.core.dom.PrimitiveType;
+import org.eclipse.jdt.core.dom.TypeLiteral;
+import org.eclipse.jpt.core.utility.jdt.ExpressionConverter;
+
+/**
+ * Convert a type literal to/from a string representation of a
+ * simple type (e.g. "java.lang.Object") or primitive type (e.g. "int").
+ */
+public final class TypeStringExpressionConverter
+ extends AbstractExpressionConverter<String>
+{
+ private static final ExpressionConverter<String> INSTANCE = new TypeStringExpressionConverter();
+
+ /**
+ * Return the singleton.
+ */
+ public static ExpressionConverter<String> instance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Ensure single instance.
+ */
+ private TypeStringExpressionConverter() {
+ super();
+ }
+
+ @Override
+ protected TypeLiteral convertObject(String string, AST ast) {
+ if (PrimitiveType.toCode(string) != null) {
+ return (TypeLiteral) PrimitiveTypeStringExpressionConverter.instance().convert(string, ast);
+ }
+ return (TypeLiteral) SimpleTypeStringExpressionConverter.instance().convert(string, ast);
+ }
+
+ @Override
+ protected String convertExpression(Expression expression) {
+ String name = SimpleTypeStringExpressionConverter.instance().convert(expression);
+ return name != null ? name : PrimitiveTypeStringExpressionConverter.instance().convert(expression);
+ }
+
+}

Back to the top