blob: 3a2c107d5d395782a8dbd4673279caf7ad931304 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2006 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.text.edits.TextEdit;
import org.eclipse.core.runtime.Assert;
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 fragments= new ArrayList();
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;
}
}