| /******************************************************************************* |
| * Copyright (c) 2000, 2011 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.jdt.internal.corext.refactoring.delegates; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| |
| import org.eclipse.core.runtime.Assert; |
| |
| import org.eclipse.text.edits.TextEdit; |
| |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.Document; |
| import org.eclipse.jface.text.IDocument; |
| |
| import org.eclipse.ltk.core.refactoring.CategorizedTextEditGroup; |
| import org.eclipse.ltk.core.refactoring.GroupCategory; |
| import org.eclipse.ltk.core.refactoring.GroupCategorySet; |
| |
| 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.AbstractTypeDeclaration; |
| import org.eclipse.jdt.core.dom.AnonymousClassDeclaration; |
| import org.eclipse.jdt.core.dom.BodyDeclaration; |
| import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor; |
| import org.eclipse.jdt.core.dom.ChildPropertyDescriptor; |
| import org.eclipse.jdt.core.dom.Expression; |
| import org.eclipse.jdt.core.dom.IBinding; |
| import org.eclipse.jdt.core.dom.ITypeBinding; |
| import org.eclipse.jdt.core.dom.Javadoc; |
| import org.eclipse.jdt.core.dom.Name; |
| import org.eclipse.jdt.core.dom.TagElement; |
| import org.eclipse.jdt.core.dom.TextElement; |
| import org.eclipse.jdt.core.dom.Type; |
| import org.eclipse.jdt.core.dom.rewrite.ITrackedNodePosition; |
| import org.eclipse.jdt.core.dom.rewrite.ListRewrite; |
| |
| import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings; |
| import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory; |
| import org.eclipse.jdt.internal.corext.dom.ASTNodes; |
| import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages; |
| import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite; |
| import org.eclipse.jdt.internal.corext.util.Strings; |
| |
| import org.eclipse.jdt.internal.ui.JavaPlugin; |
| import org.eclipse.jdt.internal.ui.preferences.JavaPreferencesSettings; |
| |
| /** |
| * <p> |
| * This class implements functionality for creating delegates of renamed or moved |
| * members pointing to the original element and containing a |
| * deprecation message. The delegate is always created in the source file of the |
| * original element and is either added directly after the original element or |
| * replaces it. Use this class as follows: |
| * </p> |
| * |
| * <pre> |
| * DelegateCreator myCreator= new DelegateMethodCreator(); |
| * myCreator.setRewrite(... cu-rewrite of modified file...); |
| * myCreator.setDeclaration(... declaration of moved/renamed member ...); |
| * |
| * ... additional initialization methods, see below... |
| * |
| * myCreator.prepareDelegate(); |
| * |
| * ... do something with the delegate before the edit is created ... |
| * |
| * myCreator.createEdit(); |
| * </pre> |
| * |
| * <p> |
| * Before prepareDelegate(), depending on whether the member is moved or |
| * renamed, or both, as well as other concerns, the following methods may be |
| * called: |
| * </p> |
| * |
| * <pre> |
| * myCreator.setNewLocation(... new location where the member was moved ...); |
| * myCreator.setNewName(... new name of the member if renamed ...); |
| * myCreator.setDeclareDeprecated(... false or true ...); |
| * myCreator.setCopy(... false or true...); |
| * </pre> |
| * |
| * <p> |
| * Note that removing or adding imports related to delegate creation is |
| * generally the responsibility of the caller. As an exception, the import for a |
| * target type set via setNewLocation() will be added by this class. |
| * </p> |
| * |
| * <p> |
| * This class is intended to be subclassed by specialized creators for concrete |
| * element types like methods or fields. |
| * </p> |
| * |
| * @since 3.2 |
| * |
| */ |
| public abstract class DelegateCreator { |
| |
| public static final GroupCategorySet CATEGORY_DELEGATE= new GroupCategorySet(new GroupCategory("org.eclipse.jdt.internal.corext.refactoring.delegates.delegate", RefactoringCoreMessages.DelegateCreator_change_category_title, RefactoringCoreMessages.DelegateCreator_change_category_description)); //$NON-NLS-1$ |
| |
| /* |
| * We are dealing with two CURewrites here: |
| * |
| * 1) The original rewrite which is passed in from the outside. One import may |
| * be registered with this CURewrite in case setNewLocation() is called. |
| * On createEdit(), the complete delegate code will be added to the CURewrite's |
| * ASTRewrite (add or replace). |
| * |
| * 2) A new CuRewrite from which we'll only use the ASTRewrite to build the new delegate. |
| * |
| */ |
| private CompilationUnitRewrite fOriginalRewrite; |
| private CompilationUnitRewrite fDelegateRewrite; |
| |
| private boolean fIsMoveToAnotherFile; |
| private boolean fCopy; |
| private boolean fDeclareDeprecated; |
| private boolean fInsertBefore; |
| |
| private BodyDeclaration fDeclaration; |
| private String fNewElementName; |
| private ITypeBinding fDestinationTypeBinding; |
| private Type fDestinationType; |
| private ITrackedNodePosition fTrackedPosition; |
| |
| private CodeGenerationSettings fPreferences; |
| |
| public DelegateCreator() { |
| fCopy= true; |
| fDeclareDeprecated= true; |
| fInsertBefore= false; |
| } |
| |
| /** |
| * Sets the compilation unit rewrite of the declaration to create a delegate |
| * for. Must always be called prior to prepareDelegate(). Bindings need not |
| * be resolved. |
| * |
| * @param rewrite the CompilationUnitRewrite. |
| */ |
| public void setSourceRewrite(CompilationUnitRewrite rewrite) { |
| fOriginalRewrite= rewrite; |
| fPreferences= JavaPreferencesSettings.getCodeGenerationSettings(rewrite.getCu().getJavaProject()); |
| |
| fDelegateRewrite= new CompilationUnitRewrite(rewrite.getCu(), rewrite.getRoot()); |
| fDelegateRewrite.getASTRewrite().setTargetSourceRangeComputer(rewrite.getASTRewrite().getExtendedSourceRangeComputer()); |
| } |
| |
| /** |
| * Sets the old member declaration. Must always be called prior to |
| * prepareDelegate(). |
| * |
| * @param declaration the BodyDeclaration |
| */ |
| public void setDeclaration(BodyDeclaration declaration) { |
| fDeclaration= declaration; |
| } |
| |
| /** |
| * Set the name of the new element. This is optional, but if set it must be |
| * called prior to prepareDelegate(). |
| * |
| * @param newName the String with the new name |
| */ |
| public void setNewElementName(String newName) { |
| fNewElementName= newName; |
| } |
| |
| /** |
| * Set the location of the new element. This is optional, but if set it must |
| * be called prior to prepareDelegate(). |
| * |
| * @param binding the ITypeBinding of the old type |
| */ |
| public void setNewLocation(ITypeBinding binding) { |
| fDestinationTypeBinding= binding; |
| } |
| |
| /** |
| * Set whether the existing element should be copied and the copy made |
| * delegate (true), or the original element should be changed to become the |
| * delegate (false). This is optional, but if set it must be called prior to |
| * prepareDelegate(). |
| * |
| * The default is true (create a copy). |
| * |
| * @param isCopy true if a copy should be created |
| */ |
| public void setCopy(boolean isCopy) { |
| fCopy= isCopy; |
| } |
| |
| /** |
| * Sets whether a deprecation message including a link to the new element |
| * should be added to the javadoc of the delegate. This is optional, but if |
| * set it must be called prior to prepareDelegate(). |
| * |
| * The default is true (create deprecation message). |
| * |
| * @param declareDeprecated true if the member should be deprecated |
| */ |
| public void setDeclareDeprecated(boolean declareDeprecated) { |
| fDeclareDeprecated= declareDeprecated; |
| } |
| |
| /** |
| * When in copy mode, use this method to control the insertion point of the |
| * delegate. If the parameter is true, the delegate gets inserted before the |
| * original declaration. If false, the delegate gets inserted after the |
| * original declaration. |
| * |
| * The default is false (do not insert before). |
| * |
| * @param insertBefore insertion point |
| */ |
| public void setInsertBefore(boolean insertBefore) { |
| fInsertBefore= insertBefore; |
| } |
| |
| // Methods to be overridden by subclasses |
| |
| /** |
| * Initializes the creator. Must set the "new" name of the element if not |
| * already set. |
| * |
| */ |
| protected abstract void initialize(); |
| |
| /** |
| * |
| * Creates the body of the delegate. |
| * |
| * @param declaration the member declaration |
| * @return the body of the delegate |
| * @throws JavaModelException |
| */ |
| protected abstract ASTNode createBody(BodyDeclaration declaration) throws JavaModelException; |
| |
| /** |
| * Creates the javadoc reference to the old member to be put inside the |
| * javadoc comment. |
| * |
| * This method is only called if isDeclareDeprecated() == true. |
| * |
| * @param declaration the member declaration |
| * @return the javadoc link node |
| * @throws JavaModelException |
| */ |
| protected abstract ASTNode createDocReference(BodyDeclaration declaration) throws JavaModelException; |
| |
| /** |
| * Returns the node of the declaration on which to add the body. |
| * |
| * @param declaration the member declaration |
| * @return the body head |
| */ |
| protected abstract ASTNode getBodyHead(BodyDeclaration declaration); |
| |
| /** |
| * Returns the javadoc property descriptor. The javadoc will be added using |
| * this descriptor. |
| * |
| * @return property descriptor |
| */ |
| protected abstract ChildPropertyDescriptor getJavaDocProperty(); |
| |
| /** |
| * Returns the body property descriptor. The body of the delegate will be |
| * added using this descriptor. |
| * |
| * @return property descriptor |
| */ |
| protected abstract ChildPropertyDescriptor getBodyProperty(); |
| |
| |
| // Getters for subclasses |
| |
| protected boolean isMoveToAnotherFile() { |
| return fIsMoveToAnotherFile; |
| } |
| |
| protected AST getAst() { |
| return fDelegateRewrite.getAST(); |
| } |
| |
| protected BodyDeclaration getDeclaration() { |
| return fDeclaration; |
| } |
| |
| protected String getNewElementName() { |
| return fNewElementName; |
| } |
| |
| /** |
| * Prepares the delegate member. The delegate member will have the same |
| * signature as the old member and contain a call to the new member and a |
| * javadoc reference with a reference to the new member. |
| * |
| * All references to the new member will contain the new name of the member |
| * and/or new declaring type, if any. |
| * |
| */ |
| public void prepareDelegate() throws JavaModelException { |
| Assert.isNotNull(fDelegateRewrite); |
| Assert.isNotNull(fDeclaration); |
| |
| initialize(); |
| |
| // Moving to a new type? |
| if (fDestinationTypeBinding != null) { |
| fDestinationType= fOriginalRewrite.getImportRewrite().addImport(fDestinationTypeBinding, getAst()); |
| fIsMoveToAnotherFile= true; |
| } else |
| fIsMoveToAnotherFile= false; |
| |
| fTrackedPosition= fDelegateRewrite.getASTRewrite().track(fDeclaration); |
| |
| ASTNode delegateBody= createBody(fDeclaration); |
| if (delegateBody != null) { |
| // is null for interface and abstract methods |
| fDelegateRewrite.getASTRewrite().set(getBodyHead(fDeclaration), getBodyProperty(), delegateBody, null); |
| } |
| |
| if (fDeclareDeprecated) { |
| createJavadoc(); |
| } |
| } |
| |
| /** |
| * Creates the javadoc for the delegate. |
| * |
| * @throws JavaModelException |
| */ |
| private void createJavadoc() throws JavaModelException { |
| TagElement tag= getDelegateJavadocTag(fDeclaration); |
| |
| Javadoc comment= fDeclaration.getJavadoc(); |
| if (comment == null) { |
| comment= getAst().newJavadoc(); |
| comment.tags().add(tag); |
| fDelegateRewrite.getASTRewrite().set(fDeclaration, getJavaDocProperty(), comment, null); |
| } else |
| fDelegateRewrite.getASTRewrite().getListRewrite(comment, Javadoc.TAGS_PROPERTY).insertLast(tag, null); |
| } |
| |
| /** |
| * Performs the actual rewriting and adds an edit to the ASTRewrite set with |
| * {@link #setSourceRewrite(CompilationUnitRewrite)}. |
| * |
| * @throws JavaModelException |
| */ |
| public void createEdit() throws JavaModelException { |
| try { |
| IDocument document= new Document(fDelegateRewrite.getCu().getBuffer().getContents()); |
| TextEdit edit= fDelegateRewrite.getASTRewrite().rewriteAST(document, fDelegateRewrite.getCu().getJavaProject().getOptions(true)); |
| edit.apply(document, TextEdit.UPDATE_REGIONS); |
| |
| String newSource= Strings.trimIndentation(document.get(fTrackedPosition.getStartPosition(), fTrackedPosition.getLength()), |
| fPreferences.tabWidth, fPreferences.indentWidth, false); |
| |
| ASTNode placeholder= fOriginalRewrite.getASTRewrite().createStringPlaceholder(newSource, fDeclaration.getNodeType()); |
| |
| CategorizedTextEditGroup groupDescription= fOriginalRewrite.createCategorizedGroupDescription(getTextEditGroupLabel(), CATEGORY_DELEGATE); |
| ListRewrite bodyDeclarationsListRewrite= fOriginalRewrite.getASTRewrite().getListRewrite(fDeclaration.getParent(), getTypeBodyDeclarationsProperty()); |
| if (fCopy) |
| if (fInsertBefore) |
| bodyDeclarationsListRewrite.insertBefore(placeholder, fDeclaration, groupDescription); |
| else |
| bodyDeclarationsListRewrite.insertAfter(placeholder, fDeclaration, groupDescription); |
| else |
| bodyDeclarationsListRewrite.replace(fDeclaration, placeholder, groupDescription); |
| |
| } catch (BadLocationException e) { |
| JavaPlugin.log(e); |
| } |
| } |
| |
| protected abstract String getTextEditGroupLabel(); |
| |
| /** |
| * Returns the binding of the declaration. |
| * |
| * @return the binding of the declaration |
| */ |
| protected abstract IBinding getDeclarationBinding(); |
| |
| /** |
| * Returns a new rewrite with the delegate changes registered. This rewrite |
| * can be used in-between calls to prepareDelegate() and createEdit() to add |
| * additional changes to the delegate. |
| * |
| * @return CompilationUnitRewrite the new rewrite |
| */ |
| public CompilationUnitRewrite getDelegateRewrite() { |
| return fDelegateRewrite; |
| } |
| |
| // ******************* INTERNAL HELPERS *************************** |
| |
| private TagElement getDelegateJavadocTag(BodyDeclaration declaration) throws JavaModelException { |
| Assert.isNotNull(declaration); |
| |
| String msg= RefactoringCoreMessages.DelegateCreator_use_member_instead; |
| int firstParam= msg.indexOf("{0}"); //$NON-NLS-1$ |
| Assert.isTrue(firstParam != -1); |
| |
| List<ASTNode> fragments= new ArrayList<ASTNode>(); |
| TextElement text= getAst().newTextElement(); |
| text.setText(msg.substring(0, firstParam).trim()); |
| fragments.add(text); |
| |
| fragments.add(createJavadocMemberReferenceTag(declaration, getAst())); |
| |
| text= getAst().newTextElement(); |
| text.setText(msg.substring(firstParam + 3).trim()); |
| fragments.add(text); |
| |
| final TagElement tag= getAst().newTagElement(); |
| tag.setTagName(TagElement.TAG_DEPRECATED); |
| tag.fragments().addAll(fragments); |
| return tag; |
| } |
| |
| private TagElement createJavadocMemberReferenceTag(BodyDeclaration declaration, final AST ast) throws JavaModelException { |
| Assert.isNotNull(ast); |
| Assert.isNotNull(declaration); |
| ASTNode javadocReference= createDocReference(declaration); |
| final TagElement element= ast.newTagElement(); |
| element.setTagName(TagElement.TAG_LINK); |
| element.fragments().add(javadocReference); |
| return element; |
| } |
| |
| protected Expression getAccess() { |
| return isMoveToAnotherFile() ? createDestinationTypeName() : null; |
| } |
| |
| protected Name createDestinationTypeName() { |
| return ASTNodeFactory.newName(getAst(), ASTNodes.asString(fDestinationType)); |
| } |
| |
| private ChildListPropertyDescriptor getTypeBodyDeclarationsProperty() { |
| ASTNode parent= fDeclaration.getParent(); |
| |
| if (parent instanceof AbstractTypeDeclaration) |
| return ((AbstractTypeDeclaration) parent).getBodyDeclarationsProperty(); |
| else if (parent instanceof AnonymousClassDeclaration) |
| return AnonymousClassDeclaration.BODY_DECLARATIONS_PROPERTY; |
| |
| Assert.isTrue(false); |
| return null; |
| } |
| } |