| /******************************************************************************* |
| * 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 java.util.ListIterator; |
| 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.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; |
| |
| /** |
| * |
| */ |
| 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)); |
| } |
| |
| |
| // ********** public methods ********** |
| |
| public ASTNode getDeclaration() { |
| return this.adapter.declaration(); |
| } |
| |
| /** |
| * Return the declaration's list of modifiers. |
| * Element type: org.eclipse.jdt.core.dom.IExtendedModifier |
| */ |
| public List<IExtendedModifier> modifiers() { |
| return this.adapter.modifiers(); |
| } |
| |
| public AST getAst() { |
| return this.getDeclaration().getAST(); |
| } |
| |
| public CompilationUnit getCompilationUnit() { |
| return (CompilationUnit) this.getDeclaration().getRoot(); |
| } |
| |
| public ICompilationUnit getICompilationUnit() { |
| return (ICompilationUnit) this.getCompilationUnit().getJavaElement(); |
| } |
| |
| /** |
| * Return the declaration's annotations. |
| */ |
| public Iterator<Annotation> annotations() { |
| return new FilteringIterator<IExtendedModifier, Annotation>(this.modifiers().iterator()) { |
| @Override |
| protected boolean accept(IExtendedModifier next) { |
| return next.isAnnotation(); |
| } |
| }; |
| } |
| |
| 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; |
| } |
| |
| /** |
| * Return whether the declaration has an annotation with the specified name. |
| */ |
| public boolean containsAnnotationNamed(String annotationName) { |
| return this.getAnnotationNamed(annotationName) != null; |
| } |
| |
| /** |
| * Add the specified annotation to the declaration. |
| * By convention annotations precede the "standard" (JLS2) modifiers; |
| * though, technically, they can be interspersed. |
| */ |
| public void addAnnotation(Annotation annotation) { |
| List<IExtendedModifier> modifiers = this.modifiers(); |
| 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 |
| } |
| |
| public void removeAnnotationNamed(String annotationName) { |
| for (Iterator<IExtendedModifier> stream = this.modifiers().iterator(); stream.hasNext(); ) { |
| IExtendedModifier modifier = stream.next(); |
| if (modifier.isAnnotation()) { |
| if (this.annotationIsNamed((Annotation) modifier, annotationName)) { |
| stream.remove(); |
| break; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Remove the specified annotation from the declaration. |
| */ |
| public void removeAnnotation(Annotation annotation) { |
| if ( ! this.modifiers().remove(annotation)) { |
| throw new IllegalArgumentException("invalid annotation: " + annotation); |
| } |
| } |
| |
| /** |
| * Replace the specified old annotation with the specified new annotation. |
| */ |
| public void replaceAnnotation(Annotation oldAnnotation, Annotation newAnnotation) { |
| for (ListIterator<IExtendedModifier> stream = this.modifiers().listIterator(); stream.hasNext(); ) { |
| if (stream.next().equals(oldAnnotation)) { |
| stream.set(newAnnotation); |
| return; |
| } |
| } |
| throw new IllegalArgumentException("invalid old annotation: " + oldAnnotation); |
| } |
| |
| public void replaceAnnotationNamed(String oldAnnotationName, Annotation newAnnotation) { |
| List<IExtendedModifier> modifiers = this.modifiers(); |
| 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); |
| } |
| |
| public void addImport(String importName) { |
| this.addImport(importName, false); |
| } |
| |
| public void addStaticImport(String importName) { |
| this.addImport(importName, true); |
| } |
| |
| public void addImport(String importName, boolean static_) { |
| if (importName.indexOf('.') != -1) { |
| this.addImportTo(this.getCompilationUnit(), importName, static_); |
| } |
| } |
| |
| public String getImportFor(String shortName) { |
| return this.getImportFor(shortName, false); |
| } |
| |
| public String getStaticImportFor(String shortName) { |
| return this.getImportFor(shortName, true); |
| } |
| |
| // TODO handle wildcards |
| public String getImportFor(String shortName, boolean static_) { |
| if (shortName.indexOf('.') != -1) { |
| return shortName; |
| } |
| List<ImportDeclaration> imports = this.imports(this.getCompilationUnit()); |
| for (ImportDeclaration importDeclaration : imports) { |
| if (this.importIsFor(importDeclaration, shortName, static_)) { |
| return importDeclaration.getName().getFullyQualifiedName(); |
| } |
| } |
| return null; |
| } |
| |
| protected boolean importIsFor(ImportDeclaration importDeclaration, String shortName, boolean static_) { |
| if (importDeclaration.isStatic() != static_) { |
| return false; |
| } |
| String importDeclarationName = importDeclaration.getName().getFullyQualifiedName(); |
| return importDeclarationName.endsWith(shortName); |
| } |
| |
| @Override |
| public String toString() { |
| return StringTools.buildToStringFor(this, this.adapter.toString()); |
| } |
| |
| |
| // ********** internal methods ********** |
| |
| public boolean annotationIsNamed(Annotation annotation, String name) { |
| String qualifiedName = this.qualifiedName(annotation); |
| return (qualifiedName != null) && qualifiedName.equals(name); |
| } |
| |
| // TODO |
| private String qualifiedName(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... :-( |
| return this.getImportFor(annotation.getTypeName().getFullyQualifiedName()); // look for a matching import |
| |
| // OLD METHOD SOURCE: |
| // String name = annotation.getTypeName().getFullyQualifiedName(); |
| // if (name.indexOf('.') != -1) { |
| // return name; // name is already qualified |
| // } |
| // String resolvedName = JDTTools.resolve(name, this.type()); |
| // // 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... :-( |
| // return this.importFor(name); // look for a matching import |
| } |
| |
| /** |
| * Return whether the specified import was added without a collision. |
| */ |
| // TODO handle collisions (e.g. java.util.Date vs. java.sql.Date) |
| protected void addImportTo(CompilationUnit astRoot, String importName, boolean static_) { |
| List<ImportDeclaration> imports = this.imports(astRoot); |
| if (this.importsInclude(imports, importName, static_)) { |
| return; |
| } |
| AST ast = astRoot.getAST(); |
| ImportDeclaration import_ = ast.newImportDeclaration(); |
| import_.setName(ast.newName(importName)); |
| import_.setStatic(static_); |
| imports.add(import_); |
| } |
| |
| @SuppressWarnings("unchecked") |
| protected List<ImportDeclaration> imports(CompilationUnit astRoot) { |
| return astRoot.imports(); |
| } |
| |
| protected boolean importsInclude(List<ImportDeclaration> imports, String importName, boolean static_) { |
| for (ImportDeclaration importDeclaration : imports) { |
| if (this.importIncludes(importDeclaration, importName, static_)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| protected boolean importIncludes(ImportDeclaration importDeclaration, String importName, boolean static_) { |
| if (importDeclaration.isStatic() != static_) { |
| return false; |
| } |
| String importDeclarationName = importDeclaration.getName().getFullyQualifiedName(); |
| if (importName.equals(importDeclarationName)) { |
| return true; |
| } |
| if (importDeclaration.isOnDemand() |
| && this.onDemandNameFor(importName).equals(importDeclarationName)) { |
| return true; |
| } |
| return false; |
| } |
| |
| protected String onDemandNameFor(String importName) { |
| int lastPeriod = importName.lastIndexOf('.'); |
| return (lastPeriod == -1) ? "" : importName.substring(0, lastPeriod); |
| } |
| |
| |
| // ********** declaration adapter interface and implementations ********** |
| |
| /** |
| * Define common protocol among the various "declarations". |
| */ |
| public interface Adapter { |
| |
| /** |
| * Return the adapted "declaration". |
| */ |
| ASTNode declaration(); |
| |
| /** |
| * Return the "declaration"'s list of modifiers. |
| * Element type: org.eclipse.jdt.core.dom.IExtendedModifier |
| */ |
| List<IExtendedModifier> modifiers(); |
| |
| } |
| |
| public static class BodyDeclarationAdapter implements Adapter { |
| private final BodyDeclaration declaration; |
| public BodyDeclarationAdapter(BodyDeclaration declaration) { |
| super(); |
| this.declaration = declaration; |
| } |
| public ASTNode declaration() { |
| return this.declaration; |
| } |
| @SuppressWarnings("unchecked") |
| public List<IExtendedModifier> modifiers() { |
| 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 declaration() { |
| return this.declaration; |
| } |
| @SuppressWarnings("unchecked") |
| public List<IExtendedModifier> modifiers() { |
| 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 declaration() { |
| return this.declaration; |
| } |
| @SuppressWarnings("unchecked") |
| public List<IExtendedModifier> modifiers() { |
| 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 declaration() { |
| return this.declaration; |
| } |
| @SuppressWarnings("unchecked") |
| public List<IExtendedModifier> modifiers() { |
| return this.declaration.modifiers(); |
| } |
| @Override |
| public String toString() { |
| return StringTools.buildToStringFor(this, this.declaration.toString()); |
| } |
| } |
| |
| } |