blob: 94fb0c6d4450cf1bb86db458ad3d078a5223dc7b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2012 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.structure;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.resources.IFile;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.RangeMarker;
import org.eclipse.text.edits.TextEdit;
import org.eclipse.text.edits.TextEditGroup;
import org.eclipse.text.edits.TextEditProcessor;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.text.TextUtilities;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.RefactoringStatusEntry;
import org.eclipse.ltk.core.refactoring.TextChange;
import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
import org.eclipse.ltk.core.refactoring.participants.MoveProcessor;
import org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant;
import org.eclipse.ltk.core.refactoring.participants.SharableParticipants;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMember;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.SourceRange;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
import org.eclipse.jdt.core.dom.Assignment;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.EnumDeclaration;
import org.eclipse.jdt.core.dom.Expression;
import org.eclipse.jdt.core.dom.FieldAccess;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.Javadoc;
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.Name;
import org.eclipse.jdt.core.dom.NullLiteral;
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.SimpleName;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.SuperFieldAccess;
import org.eclipse.jdt.core.dom.SuperMethodInvocation;
import org.eclipse.jdt.core.dom.TagElement;
import org.eclipse.jdt.core.dom.ThisExpression;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.TypeParameter;
import org.eclipse.jdt.core.dom.VariableDeclaration;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor;
import org.eclipse.jdt.core.refactoring.descriptors.MoveMethodDescriptor;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchMatch;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.internal.core.refactoring.descriptors.RefactoringSignatureDescriptorFactory;
import org.eclipse.jdt.internal.corext.codemanipulation.CodeGenerationSettings;
import org.eclipse.jdt.internal.corext.codemanipulation.GetterSetterUtil;
import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
import org.eclipse.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.jdt.internal.corext.dom.Bindings;
import org.eclipse.jdt.internal.corext.dom.ModifierRewrite;
import org.eclipse.jdt.internal.corext.dom.ScopeAnalyzer;
import org.eclipse.jdt.internal.corext.refactoring.Checks;
import org.eclipse.jdt.internal.corext.refactoring.CollectingSearchRequestor;
import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil;
import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
import org.eclipse.jdt.internal.corext.refactoring.RefactoringScopeFactory;
import org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine;
import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup;
import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext;
import org.eclipse.jdt.internal.corext.refactoring.base.ReferencesInBinaryContext;
import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange;
import org.eclipse.jdt.internal.corext.refactoring.delegates.DelegateMethodCreator;
import org.eclipse.jdt.internal.corext.refactoring.structure.MemberVisibilityAdjustor.IVisibilityAdjustment;
import org.eclipse.jdt.internal.corext.refactoring.structure.MemberVisibilityAdjustor.IncomingMemberVisibilityAdjustment;
import org.eclipse.jdt.internal.corext.refactoring.tagging.IDelegateUpdating;
import org.eclipse.jdt.internal.corext.refactoring.util.JavadocUtil;
import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil;
import org.eclipse.jdt.internal.corext.refactoring.util.TextChangeManager;
import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
import org.eclipse.jdt.internal.corext.util.JdtFlags;
import org.eclipse.jdt.internal.corext.util.Messages;
import org.eclipse.jdt.internal.corext.util.SearchUtils;
import org.eclipse.jdt.internal.corext.util.Strings;
import org.eclipse.jdt.ui.JavaElementLabels;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.preferences.JavaPreferencesSettings;
import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
import org.eclipse.jdt.internal.ui.viewsupport.BindingLabelProvider;
/**
* Refactoring processor to move instance methods.
*/
public final class MoveInstanceMethodProcessor extends MoveProcessor implements IDelegateUpdating {
/**
* AST visitor to find references to parameters occurring in anonymous
* classes of a method body.
*/
public final class AnonymousClassReferenceFinder extends AstNodeFinder {
/** The anonymous class nesting counter */
protected int fAnonymousClass= 0;
/** The declaring type of the method declaration */
protected final ITypeBinding fDeclaringType;
/**
* Creates a new anonymous class reference finder.
*
* @param declaration
* the method declaration to search for references
*/
public AnonymousClassReferenceFinder(final MethodDeclaration declaration) {
fDeclaringType= declaration.resolveBinding().getDeclaringClass();
}
@Override
public final void endVisit(final AnonymousClassDeclaration node) {
Assert.isNotNull(node);
if (fAnonymousClass > 0)
fAnonymousClass--;
super.endVisit(node);
}
@Override
public final boolean visit(final AnonymousClassDeclaration node) {
Assert.isNotNull(node);
fAnonymousClass++;
return super.visit(node);
}
@Override
public final boolean visit(final MethodInvocation node) {
Assert.isNotNull(node);
if (fAnonymousClass > 0) {
final IMethodBinding binding= node.resolveMethodBinding();
if (binding != null) {
if (node.getExpression() == null && !Modifier.isStatic(binding.getModifiers()))
fResult.add(node.getName());
}
}
return true;
}
@Override
public boolean visit(final SimpleName node) {
Assert.isNotNull(node);
if (fAnonymousClass > 0) {
if (!(node.getParent() instanceof FieldAccess)) {
final IBinding binding= node.resolveBinding();
if (binding instanceof IVariableBinding) {
final IVariableBinding variable= (IVariableBinding) binding;
final ITypeBinding declaring= variable.getDeclaringClass();
if (declaring != null && Bindings.equals(declaring, fDeclaringType))
fResult.add(node);
}
}
}
return false;
}
}
/**
* Partial implementation of an ast node finder.
*/
protected static class AstNodeFinder extends ASTVisitor {
/** The found ast nodes */
protected final Set<Expression> fResult= new HashSet<Expression>();
/** The status of the find operation */
protected final RefactoringStatus fStatus= new RefactoringStatus();
/**
* Returns the result set.
*
* @return the result set
*/
public final Set<Expression> getResult() {
return fResult;
}
/**
* Returns the status of the find operation.
*
* @return the status of the operation
*/
public final RefactoringStatus getStatus() {
return fStatus;
}
}
class DelegateInstanceMethodCreator extends DelegateMethodCreator {
private Map<IMember, IncomingMemberVisibilityAdjustment> fAdjustments;
private boolean fNeededInsertion;
private Map<ICompilationUnit, CompilationUnitRewrite> fRewrites;
public DelegateInstanceMethodCreator(Map<IMember, IncomingMemberVisibilityAdjustment> adjustments, Map<ICompilationUnit, CompilationUnitRewrite> rewrites) {
super();
fAdjustments= adjustments;
fRewrites= rewrites;
}
@Override
protected ASTNode createBody(BodyDeclaration bd) throws JavaModelException {
MethodDeclaration methodDeclaration= (MethodDeclaration) bd;
final MethodInvocation invocation= getAst().newMethodInvocation();
invocation.setName(getAst().newSimpleName(getNewElementName()));
invocation.setExpression(createSimpleTargetAccessExpression(methodDeclaration));
fNeededInsertion= createArgumentList(methodDeclaration, invocation.arguments(), new VisibilityAdjustingArgumentFactory(getAst(), fRewrites, fAdjustments));
final Block block= getAst().newBlock();
block.statements().add(createMethodInvocation(methodDeclaration, invocation));
if (!fSourceRewrite.getCu().equals(fTargetType.getCompilationUnit()))
fSourceRewrite.getImportRemover().registerRemovedNode(methodDeclaration.getBody());
return block;
}
@Override
protected ASTNode createDocReference(final BodyDeclaration declaration) throws JavaModelException {
return MoveInstanceMethodProcessor.this.createMethodReference((MethodDeclaration) declaration, getAst());
}
protected boolean getNeededInsertion() {
return fNeededInsertion;
}
}
/**
* AST visitor to find 'this' references to enclosing instances.
*/
public final class EnclosingInstanceReferenceFinder extends AstNodeFinder {
/** The list of enclosing types */
private final List<ITypeBinding> fEnclosingTypes= new ArrayList<ITypeBinding>(3);
/**
* Creates a new enclosing instance reference finder.
*
* @param binding
* the declaring type
*/
public EnclosingInstanceReferenceFinder(final ITypeBinding binding) {
Assert.isNotNull(binding);
ITypeBinding declaring= binding.getDeclaringClass();
while (declaring != null) {
fEnclosingTypes.add(declaring);
declaring= declaring.getDeclaringClass();
}
}
@Override
public final boolean visit(final SimpleName node) {
Assert.isNotNull(node);
final IBinding binding= node.resolveBinding();
ITypeBinding declaring= null;
if (binding instanceof IVariableBinding) {
final IVariableBinding variable= (IVariableBinding) binding;
if (Flags.isStatic(variable.getModifiers()))
return false;
declaring= variable.getDeclaringClass();
} else if (binding instanceof IMethodBinding) {
final IMethodBinding method= (IMethodBinding) binding;
if (Flags.isStatic(method.getModifiers()))
return false;
declaring= method.getDeclaringClass();
}
if (declaring != null) {
ITypeBinding enclosing= null;
for (final Iterator<ITypeBinding> iterator= fEnclosingTypes.iterator(); iterator.hasNext();) {
enclosing= iterator.next();
if (Bindings.equals(enclosing, declaring)) {
fStatus.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveInstanceMethodProcessor_refers_enclosing_instances, JavaStatusContext.create(fMethod.getCompilationUnit(), node)));
fResult.add(node);
break;
}
}
}
return false;
}
@Override
public final boolean visit(final ThisExpression node) {
Assert.isNotNull(node);
if (node.getQualifier() != null) {
fStatus.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveInstanceMethodProcessor_refers_enclosing_instances, JavaStatusContext.create(fMethod.getCompilationUnit(), node)));
fResult.add(node);
}
return false;
}
}
/**
* AST visitor to find references to type variables or generic types.
*/
public final class GenericReferenceFinder extends AstNodeFinder {
/** The type parameter binding keys */
protected final Set<String> fBindings= new HashSet<String>();
/**
* Creates a new generic reference finder.
*
* @param declaration
* the method declaration
*/
public GenericReferenceFinder(final MethodDeclaration declaration) {
Assert.isNotNull(declaration);
ITypeBinding binding= null;
TypeParameter parameter= null;
for (final Iterator<TypeParameter> iterator= declaration.typeParameters().iterator(); iterator.hasNext();) {
parameter= iterator.next();
binding= parameter.resolveBinding();
if (binding != null)
fBindings.add(binding.getKey());
}
}
/*
* @see org.eclipse.jdt.core.dom.ASTVisitor#visit(org.eclipse.jdt.core.dom.SimpleName)
*/
@Override
public final boolean visit(final SimpleName node) {
Assert.isNotNull(node);
final IBinding binding= node.resolveBinding();
if (binding instanceof ITypeBinding) {
final ITypeBinding type= (ITypeBinding) binding;
if (!fBindings.contains(type.getKey()) && type.isTypeVariable()) {
fResult.add(node);
fStatus.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveInstanceMethodProcessor_no_type_variables, JavaStatusContext.create(fMethod.getCompilationUnit(), node)));
return false;
}
}
return true;
}
}
/**
* Factory for method argument declaration or expression nodes.
*/
protected static interface IArgumentFactory {
/**
* Returns a argument node for the specified variable binding.
*
* @param binding
* the binding to create a argument node for
* @param last
* <code>true</code> if the argument represented by this
* node is the last one in its declaring method
* @return the corresponding node
* @throws JavaModelException
* if an error occurs
*/
public ASTNode getArgumentNode(IVariableBinding binding, boolean last) throws JavaModelException;
/**
* Returns a target node for the current target.
*
* @return the corresponding node
* @throws JavaModelException
* if an error occurs
*/
public ASTNode getTargetNode() throws JavaModelException;
}
/**
* AST visitor to rewrite the body of the moved method.
*/
public final class MethodBodyRewriter extends ASTVisitor {
/** The anonymous class nesting counter */
protected int fAnonymousClass= 0;
/** The method declaration to rewrite */
protected final MethodDeclaration fDeclaration;
/** The source ast rewrite to use */
protected final ASTRewrite fRewrite;
/** The existing static imports */
protected final Set<IBinding> fStaticImports= new HashSet<IBinding>();
/** The refactoring status */
protected final RefactoringStatus fStatus= new RefactoringStatus();
/** The target compilation unit rewrite to use */
protected final CompilationUnitRewrite fTargetRewrite;
/**
* Creates a new method body rewriter.
*
* @param targetRewrite
* the target compilation unit rewrite to use
* @param rewrite
* the source ast rewrite to use
* @param sourceDeclaration
* the source method declaration
*/
public MethodBodyRewriter(final CompilationUnitRewrite targetRewrite, final ASTRewrite rewrite, final MethodDeclaration sourceDeclaration) {
Assert.isNotNull(targetRewrite);
Assert.isNotNull(rewrite);
Assert.isNotNull(sourceDeclaration);
fTargetRewrite= targetRewrite;
fRewrite= rewrite;
fDeclaration= sourceDeclaration;
fStaticImports.clear();
ImportRewriteUtil.collectImports(fMethod.getJavaProject(), sourceDeclaration, new HashSet<ITypeBinding>(), fStaticImports, false);
}
private boolean isParameterName(String name) {
List<SingleVariableDeclaration> parameters= fDeclaration.parameters();
for (Iterator<SingleVariableDeclaration> iterator= parameters.iterator(); iterator.hasNext();) {
SingleVariableDeclaration decl= iterator.next();
if (name.equals(decl.getName().getIdentifier())) {
return true;
}
}
return false;
}
@Override
public final void endVisit(final AnonymousClassDeclaration node) {
Assert.isNotNull(node);
if (fAnonymousClass > 0)
fAnonymousClass--;
super.endVisit(node);
}
@Override
public final boolean visit(final AnonymousClassDeclaration node) {
Assert.isNotNull(node);
fAnonymousClass++;
return super.visit(node);
}
@Override
public final boolean visit(final ClassInstanceCreation node) {
Assert.isNotNull(node);
if (node.getParent() instanceof ClassInstanceCreation) {
final AnonymousClassDeclaration declaration= node.getAnonymousClassDeclaration();
if (declaration != null)
visit(declaration);
return false;
}
return super.visit(node);
}
private ASTNode getFieldReference(SimpleName oldNameNode, ASTRewrite rewrite) {
String name= oldNameNode.getIdentifier();
AST ast= rewrite.getAST();
if (isParameterName(name) || StubUtility.useThisForFieldAccess(fTargetRewrite.getCu().getJavaProject())) {
FieldAccess fieldAccess= ast.newFieldAccess();
fieldAccess.setExpression(ast.newThisExpression());
fieldAccess.setName((SimpleName) rewrite.createMoveTarget(oldNameNode));
return fieldAccess;
}
return rewrite.createMoveTarget(oldNameNode);
}
@Override
public final boolean visit(final FieldAccess node) {
Assert.isNotNull(node);
final Expression expression= node.getExpression();
final IVariableBinding variable= node.resolveFieldBinding();
final AST ast= fRewrite.getAST();
if (expression instanceof ThisExpression) {
if (Bindings.equals(fTarget, variable)) {
if (fAnonymousClass > 0) {
final ThisExpression target= ast.newThisExpression();
target.setQualifier(ast.newSimpleName(fTargetType.getElementName()));
fRewrite.replace(node, target, null);
} else
fRewrite.replace(node, ast.newThisExpression(), null);
return false;
} else {
expression.accept(this);
return false;
}
} else if (expression instanceof FieldAccess) {
final FieldAccess access= (FieldAccess) expression;
final IBinding binding= access.getName().resolveBinding();
if (access.getExpression() instanceof ThisExpression && Bindings.equals(fTarget, binding)) {
ASTNode newFieldAccess= getFieldReference(node.getName(), fRewrite);
fRewrite.replace(node, newFieldAccess, null);
return false;
}
} else if (expression != null) {
expression.accept(this);
return false;
}
return true;
}
public final void visit(final List<ASTNode> nodes) {
Assert.isNotNull(nodes);
ASTNode node= null;
for (final Iterator<ASTNode> iterator= nodes.iterator(); iterator.hasNext();) {
node= iterator.next();
node.accept(this);
}
}
@Override
public final boolean visit(final MethodInvocation node) {
Assert.isNotNull(node);
final Expression expression= node.getExpression();
final IMethodBinding method= node.resolveMethodBinding();
if (method != null) {
final ASTRewrite rewrite= fRewrite;
if (expression == null) {
final AST ast= node.getAST();
if (!JdtFlags.isStatic(method))
rewrite.set(node, MethodInvocation.EXPRESSION_PROPERTY, ast.newSimpleName(fTargetName), null);
else
rewrite.set(node, MethodInvocation.EXPRESSION_PROPERTY, ast.newSimpleType(ast.newSimpleName(fMethod.getDeclaringType().getElementName())), null);
return true;
} else {
if (expression instanceof FieldAccess) {
final FieldAccess access= (FieldAccess) expression;
if (Bindings.equals(fTarget, access.resolveFieldBinding())) {
rewrite.remove(expression, null);
visit(node.arguments());
return false;
}
} else if (expression instanceof Name) {
final Name name= (Name) expression;
if (Bindings.equals(fTarget, name.resolveBinding())) {
rewrite.remove(expression, null);
visit(node.arguments());
return false;
}
}
}
}
return true;
}
@Override
public final boolean visit(final QualifiedName node) {
Assert.isNotNull(node);
IBinding binding= node.resolveBinding();
if (binding instanceof ITypeBinding) {
final ITypeBinding type= (ITypeBinding) binding;
if (type.isClass() && type.getDeclaringClass() != null) {
final Type newType= fTargetRewrite.getImportRewrite().addImport(type, node.getAST());
fRewrite.replace(node, newType, null);
return false;
}
}
binding= node.getQualifier().resolveBinding();
if (Bindings.equals(fTarget, binding)) {
fRewrite.replace(node, getFieldReference(node.getName(), fRewrite), null);
return false;
}
node.getQualifier().accept(this);
return false;
}
@Override
public final boolean visit(final SimpleName node) {
Assert.isNotNull(node);
final AST ast= node.getAST();
final ASTRewrite rewrite= fRewrite;
final IBinding binding= node.resolveBinding();
if (binding instanceof ITypeBinding) {
final ITypeBinding type= (ITypeBinding) binding;
if (type.isClass() && type.getDeclaringClass() != null) {
final String name= fTargetRewrite.getImportRewrite().addImport(type.getTypeDeclaration());
if (name != null && name.length() > 0) {
fRewrite.replace(node, ASTNodeFactory.newName(ast, name), null);
return false;
}
}
}
if (Bindings.equals(fTarget, binding))
if (fAnonymousClass > 0) {
final ThisExpression target= ast.newThisExpression();
target.setQualifier(ast.newSimpleName(fTargetType.getElementName()));
fRewrite.replace(node, target, null);
} else
rewrite.replace(node, ast.newThisExpression(), null);
else if (binding instanceof IVariableBinding) {
final IVariableBinding variable= (IVariableBinding) binding;
final IMethodBinding method= fDeclaration.resolveBinding();
ITypeBinding declaring= variable.getDeclaringClass();
if (method != null) {
if (Bindings.equals(method.getDeclaringClass(), declaring)) {
declaring= declaring.getTypeDeclaration();
if (JdtFlags.isStatic(variable))
rewrite.replace(node, ast.newQualifiedName(ASTNodeFactory.newName(ast, fTargetRewrite.getImportRewrite().addImport(declaring)), ast.newSimpleName(node.getFullyQualifiedName())), null);
else {
final FieldAccess access= ast.newFieldAccess();
access.setExpression(ast.newSimpleName(fTargetName));
access.setName(ast.newSimpleName(node.getFullyQualifiedName()));
rewrite.replace(node, access, null);
}
} else if (!(node.getParent() instanceof QualifiedName) && JdtFlags.isStatic(variable) && !fStaticImports.contains(variable) && !Checks.isEnumCase(node.getParent())) {
rewrite.replace(node, ast.newQualifiedName(ASTNodeFactory.newName(ast, fTargetRewrite.getImportRewrite().addImport(declaring)), ast.newSimpleName(node.getFullyQualifiedName())), null);
}
}
}
return false;
}
@Override
public final boolean visit(final ThisExpression node) {
Assert.isNotNull(node);
fRewrite.replace(node, node.getAST().newSimpleName(fTargetName), null);
return false;
}
}
/**
* AST visitor to find read-only fields of the declaring class of 'this'.
*/
public static class ReadyOnlyFieldFinder extends ASTVisitor {
/**
* Returns the field binding associated with this expression.
*
* @param expression
* the expression to get the field binding for
* @return the field binding, if the expression denotes a field access
* or a field name, <code>null</code> otherwise
*/
protected static IVariableBinding getFieldBinding(final Expression expression) {
Assert.isNotNull(expression);
if (expression instanceof FieldAccess)
return (IVariableBinding) ((FieldAccess) expression).getName().resolveBinding();
if (expression instanceof Name) {
final IBinding binding= ((Name) expression).resolveBinding();
if (binding instanceof IVariableBinding) {
final IVariableBinding variable= (IVariableBinding) binding;
if (variable.isField())
return variable;
}
}
return null;
}
/**
* Is the specified name a qualified entity, e.g. preceded by 'this',
* 'super' or part of a method invocation?
*
* @param name
* the name to check
* @return <code>true</code> if this entity is qualified,
* <code>false</code> otherwise
*/
protected static boolean isQualifiedEntity(final Name name) {
Assert.isNotNull(name);
final ASTNode parent= name.getParent();
if (parent instanceof QualifiedName && ((QualifiedName) parent).getName().equals(name) || parent instanceof FieldAccess && ((FieldAccess) parent).getName().equals(name) || parent instanceof SuperFieldAccess)
return true;
else if (parent instanceof MethodInvocation) {
final MethodInvocation invocation= (MethodInvocation) parent;
return invocation.getExpression() != null && invocation.getName().equals(name);
}
return false;
}
/** The list of found bindings */
protected final List<IVariableBinding> fBindings= new LinkedList<IVariableBinding>();
/** The keys of the found binding keys */
protected final Set<String> fFound= new HashSet<String>();
/** The keys of the written binding keys */
protected final Set<String> fWritten= new HashSet<String>();
/**
* Creates a new read only field finder.
*
* @param binding
* The declaring class of the method declaring to find fields
* for
*/
public ReadyOnlyFieldFinder(final ITypeBinding binding) {
Assert.isNotNull(binding);
final IVariableBinding[] bindings= binding.getDeclaredFields();
IVariableBinding variable= null;
for (int index= 0; index < bindings.length; index++) {
variable= bindings[index];
if (!variable.isSynthetic() && !fFound.contains(variable.getKey())) {
fFound.add(variable.getKey());
fBindings.add(variable);
}
}
}
/**
* Returns all fields of the declaring class plus the ones references in
* the visited method declaration.
*
* @return all fields of the declaring class plus the references ones
*/
public final IVariableBinding[] getDeclaredFields() {
final IVariableBinding[] result= new IVariableBinding[fBindings.size()];
fBindings.toArray(result);
return result;
}
/**
* Returns all fields of the declaring class which are not written by
* the visited method declaration.
*
* @return all fields which are not written
*/
public final IVariableBinding[] getReadOnlyFields() {
IVariableBinding binding= null;
final List<IVariableBinding> list= new LinkedList<IVariableBinding>(fBindings);
for (final Iterator<IVariableBinding> iterator= list.iterator(); iterator.hasNext();) {
binding= iterator.next();
if (fWritten.contains(binding.getKey()))
iterator.remove();
}
final IVariableBinding[] result= new IVariableBinding[list.size()];
list.toArray(result);
return result;
}
@Override
public final boolean visit(final Assignment node) {
Assert.isNotNull(node);
final IVariableBinding binding= getFieldBinding(node.getLeftHandSide());
if (binding != null)
fWritten.add(binding.getKey());
return true;
}
@Override
public final boolean visit(final FieldAccess node) {
Assert.isNotNull(node);
if (node.getExpression() instanceof ThisExpression) {
final IVariableBinding binding= (IVariableBinding) node.getName().resolveBinding();
if (binding != null) {
final String key= binding.getKey();
if (!fFound.contains(key)) {
fFound.add(key);
fBindings.add(binding);
}
}
}
return true;
}
@Override
public final boolean visit(final PostfixExpression node) {
final IVariableBinding binding= getFieldBinding(node.getOperand());
if (binding != null)
fWritten.add(binding.getKey());
return true;
}
@Override
public final boolean visit(final PrefixExpression node) {
final IVariableBinding binding= getFieldBinding(node.getOperand());
if (binding != null)
fWritten.add(binding.getKey());
return false;
}
@Override
public final boolean visit(final SimpleName node) {
Assert.isNotNull(node);
final IBinding binding= node.resolveBinding();
if (binding != null)
if (isFieldAccess(node) && !isQualifiedEntity(node)) {
final IVariableBinding variable= (IVariableBinding) binding;
final String key= variable.getKey();
if (!fFound.contains(key)) {
fFound.add(key);
fBindings.add(variable);
}
}
return false;
}
}
/**
* AST visitor to find recursive calls to the method.
*/
public final class RecursiveCallFinder extends AstNodeFinder {
/** The method binding */
protected final IMethodBinding fBinding;
/**
* Creates a new recursive call finder.
*
* @param declaration
* the method declaration
*/
public RecursiveCallFinder(final MethodDeclaration declaration) {
Assert.isNotNull(declaration);
fBinding= declaration.resolveBinding();
}
@Override
public final boolean visit(final MethodInvocation node) {
Assert.isNotNull(node);
final Expression expression= node.getExpression();
final IMethodBinding binding= node.resolveMethodBinding();
if (binding == null || !Modifier.isStatic(binding.getModifiers()) && Bindings.equals(binding, fBinding) && (expression == null || expression instanceof ThisExpression)) {
fStatus.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveInstanceMethodProcessor_potentially_recursive, JavaStatusContext.create(fMethod.getCompilationUnit(), node)));
fResult.add(node);
return false;
}
return true;
}
}
/**
* AST visitor to find 'super' references.
*/
public final class SuperReferenceFinder extends AstNodeFinder {
@Override
public final boolean visit(final AnnotationTypeDeclaration node) {
return false;
}
@Override
public final boolean visit(final AnonymousClassDeclaration node) {
return false;
}
@Override
public final boolean visit(final EnumDeclaration node) {
return false;
}
@Override
public final boolean visit(final SuperFieldAccess node) {
Assert.isNotNull(node);
fStatus.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveInstanceMethodProcessor_uses_super, JavaStatusContext.create(fMethod.getCompilationUnit(), node)));
fResult.add(node);
return false;
}
@Override
public final boolean visit(final SuperMethodInvocation node) {
Assert.isNotNull(node);
fStatus.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveInstanceMethodProcessor_uses_super, JavaStatusContext.create(fMethod.getCompilationUnit(), node)));
fResult.add(node);
return false;
}
@Override
public final boolean visit(final TypeDeclaration node) {
return false;
}
}
/**
* AST visitor to find references to 'this'.
*/
public final class ThisReferenceFinder extends AstNodeFinder {
@Override
public final boolean visit(final MethodInvocation node) {
Assert.isNotNull(node);
final IMethodBinding binding= node.resolveMethodBinding();
if (binding != null && !JdtFlags.isStatic(binding) && node.getExpression() == null) {
fResult.add(node);
fStatus.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveInstanceMethodProcessor_this_reference, JavaStatusContext.create(fMethod.getCompilationUnit(), node)));
}
return true;
}
@Override
public final boolean visit(final SimpleName node) {
Assert.isNotNull(node);
if (isFieldAccess(node) && !isTargetAccess(node)) {
fResult.add(node);
fStatus.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveInstanceMethodProcessor_this_reference, JavaStatusContext.create(fMethod.getCompilationUnit(), node)));
}
return false;
}
@Override
public final boolean visit(final ThisExpression node) {
Assert.isNotNull(node);
fResult.add(node);
fStatus.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveInstanceMethodProcessor_this_reference, JavaStatusContext.create(fMethod.getCompilationUnit(), node)));
return false;
}
}
/**
* Argument factory which adjusts the visibilities of the argument types.
*/
public class VisibilityAdjustingArgumentFactory implements IArgumentFactory {
/** The visibility adjustments */
private final Map<IMember, IncomingMemberVisibilityAdjustment> fAdjustments;
/** The ast to use for new nodes */
private final AST fAst;
/** The compilation unit rewrites */
private final Map<ICompilationUnit, CompilationUnitRewrite> fRewrites;
/**
* Creates a new visibility adjusting argument factory.
*
* @param ast
* the ast to use for new nodes
* @param rewrites
* the compilation unit rewrites
* @param adjustments
* the map of elements to visibility adjustments
*/
public VisibilityAdjustingArgumentFactory(final AST ast, final Map<ICompilationUnit, CompilationUnitRewrite> rewrites, final Map<IMember, IncomingMemberVisibilityAdjustment> adjustments) {
Assert.isNotNull(ast);
Assert.isNotNull(rewrites);
Assert.isNotNull(adjustments);
fAst= ast;
fRewrites= rewrites;
fAdjustments= adjustments;
}
protected final void adjustTypeVisibility(final ITypeBinding binding) throws JavaModelException {
Assert.isNotNull(binding);
final IJavaElement element= binding.getJavaElement();
if (element instanceof IType) {
final IType type= (IType) element;
if (!type.isBinary() && !type.isReadOnly() && !Flags.isPublic(type.getFlags())) {
boolean same= false;
final CompilationUnitRewrite rewrite= getCompilationUnitRewrite(fRewrites, type.getCompilationUnit());
final AbstractTypeDeclaration declaration= ASTNodeSearchUtil.getAbstractTypeDeclarationNode(type, rewrite.getRoot());
if (declaration != null) {
final ITypeBinding declaring= declaration.resolveBinding();
if (declaring != null && Bindings.equals(binding.getPackage(), fTarget.getType().getPackage()))
same= true;
final Modifier.ModifierKeyword keyword= same ? null : Modifier.ModifierKeyword.PUBLIC_KEYWORD;
final String modifier= same ? RefactoringCoreMessages.MemberVisibilityAdjustor_change_visibility_default : RefactoringCoreMessages.MemberVisibilityAdjustor_change_visibility_public;
if (MemberVisibilityAdjustor.hasLowerVisibility(binding.getModifiers(), same ? Modifier.NONE : keyword == null ? Modifier.NONE : keyword.toFlagValue()) && MemberVisibilityAdjustor.needsVisibilityAdjustments(type, keyword, fAdjustments))
fAdjustments.put(type, new MemberVisibilityAdjustor.OutgoingMemberVisibilityAdjustment(type, keyword, RefactoringStatus.createWarningStatus(Messages.format(RefactoringCoreMessages.MemberVisibilityAdjustor_change_visibility_type_warning, new String[] { BindingLabelProvider.getBindingLabel(declaration.resolveBinding(), JavaElementLabels.ALL_FULLY_QUALIFIED), modifier }), JavaStatusContext.create(type.getCompilationUnit(), declaration))));
}
}
}
}
public ASTNode getArgumentNode(final IVariableBinding binding, final boolean last) throws JavaModelException {
Assert.isNotNull(binding);
adjustTypeVisibility(binding.getType());
return fAst.newSimpleName(binding.getName());
}
public ASTNode getTargetNode() throws JavaModelException {
return fAst.newThisExpression();
}
}
private static final String ATTRIBUTE_DEPRECATE= "deprecate"; //$NON-NLS-1$
private static final String ATTRIBUTE_INLINE= "inline"; //$NON-NLS-1$
private static final String ATTRIBUTE_REMOVE= "remove"; //$NON-NLS-1$
private static final String ATTRIBUTE_TARGET_INDEX= "targetIndex"; //$NON-NLS-1$
private static final String ATTRIBUTE_TARGET_NAME= "targetName"; //$NON-NLS-1$
private static final String ATTRIBUTE_USE_GETTER= "getter"; //$NON-NLS-1$
private static final String ATTRIBUTE_USE_SETTER= "setter"; //$NON-NLS-1$
/** The identifier of this processor */
public static final String IDENTIFIER= "org.eclipse.jdt.ui.moveInstanceMethodProcessor"; //$NON-NLS-1$
/**
* Returns the bindings of the method arguments of the specified
* declaration.
*
* @param declaration
* the method declaration
* @return the array of method argument variable bindings
*/
protected static IVariableBinding[] getArgumentBindings(final MethodDeclaration declaration) {
Assert.isNotNull(declaration);
final List<IVariableBinding> parameters= new ArrayList<IVariableBinding>(declaration.parameters().size());
for (final Iterator<SingleVariableDeclaration> iterator= declaration.parameters().iterator(); iterator.hasNext();) {
VariableDeclaration variable= iterator.next();
IVariableBinding binding= variable.resolveBinding();
if (binding == null)
return new IVariableBinding[0];
parameters.add(binding);
}
final IVariableBinding[] result= new IVariableBinding[parameters.size()];
parameters.toArray(result);
return result;
}
/**
* Returns the bindings of the method argument types of the specified
* declaration.
*
* @param declaration
* the method declaration
* @return the array of method argument variable bindings
*/
protected static ITypeBinding[] getArgumentTypes(final MethodDeclaration declaration) {
Assert.isNotNull(declaration);
final IVariableBinding[] parameters= getArgumentBindings(declaration);
final List<ITypeBinding> types= new ArrayList<ITypeBinding>(parameters.length);
IVariableBinding binding= null;
ITypeBinding type= null;
for (int index= 0; index < parameters.length; index++) {
binding= parameters[index];
type= binding.getType();
if (type != null)
types.add(type);
}
final ITypeBinding[] result= new ITypeBinding[types.size()];
types.toArray(result);
return result;
}
/**
* Is the specified name a field access?
*
* @param name
* the name to check
* @return <code>true</code> if this name is a field access,
* <code>false</code> otherwise
*/
protected static boolean isFieldAccess(final SimpleName name) {
Assert.isNotNull(name);
final IBinding binding= name.resolveBinding();
if (!(binding instanceof IVariableBinding))
return false;
final IVariableBinding variable= (IVariableBinding) binding;
if (!variable.isField())
return false;
if ("length".equals(name.getIdentifier())) { //$NON-NLS-1$
final ASTNode parent= name.getParent();
if (parent instanceof QualifiedName) {
final QualifiedName qualified= (QualifiedName) parent;
final ITypeBinding type= qualified.getQualifier().resolveTypeBinding();
if (type != null && type.isArray())
return false;
}
}
return !Modifier.isStatic(variable.getModifiers());
}
/** The candidate targets */
private IVariableBinding[] fCandidateTargets= new IVariableBinding[0];
/** The text change manager */
private TextChangeManager fChangeManager= null;
/** Should the delegator be deprecated? */
private boolean fDelegateDeprecation= true;
private boolean fDelegatingUpdating;
/** Should the delegator be inlined? */
private boolean fInline= false;
/** The method to move */
private IMethod fMethod;
/** The name of the new method to generate */
private String fMethodName;
/** The possible targets */
private IVariableBinding[] fPossibleTargets= new IVariableBinding[0];
/** Should the delegator be removed after inlining? */
private boolean fRemove= false;
/** The code generation settings to apply */
private CodeGenerationSettings fSettings;
/** The source compilation unit rewrite */
private CompilationUnitRewrite fSourceRewrite;
/** The new target */
private IVariableBinding fTarget= null;
/** The name of the new target */
private String fTargetName;
/** Does the move method need a target node? */
private boolean fTargetNode= true;
/** The target type */
private IType fTargetType= null;
/** Should getter methods be used to resolve visibility issues? */
private boolean fUseGetters= true;
/** Should setter methods be used to resolve visibility issues? */
private boolean fUseSetters= true;
/**
* Creates a new move instance method processor.
*
* @param method
* the method to move, or <code>null</code> if invoked by
* scripting
* @param settings
* the code generation settings to apply, or <code>null</code>
* if invoked by scripting
*/
public MoveInstanceMethodProcessor(final IMethod method, final CodeGenerationSettings settings) {
fSettings= settings;
fMethod= method;
if (method != null)
initialize(method);
}
public MoveInstanceMethodProcessor(JavaRefactoringArguments arguments, RefactoringStatus status) {
RefactoringStatus initializeStatus= initialize(arguments);
status.merge(initializeStatus);
}
/**
* {@inheritDoc}
*/
public final boolean canEnableDelegateUpdating() {
return true;
}
/**
* Checks whether a method with the proposed name already exists in the
* target type.
*
* @param monitor
* the progress monitor to display progress
* @param status
* the status of the condition checking
* @throws JavaModelException
* if the declared methods of the target type could not be
* retrieved
*/
protected void checkConflictingMethod(final IProgressMonitor monitor, final RefactoringStatus status) throws JavaModelException {
Assert.isNotNull(monitor);
Assert.isNotNull(status);
final IMethod[] methods= fTargetType.getMethods();
try {
monitor.beginTask("", methods.length); //$NON-NLS-1$
monitor.setTaskName(RefactoringCoreMessages.MoveInstanceMethodProcessor_checking);
IMethod method= null;
for (int index= 0; index < methods.length; index++) {
method= methods[index];
int newParamCount= fMethod.getParameterTypes().length;
if (needsTargetNode())
newParamCount--;
if (method.getElementName().equals(fMethodName) && method.getParameterTypes().length == newParamCount)
status.merge(RefactoringStatus.createErrorStatus(Messages.format(RefactoringCoreMessages.MoveInstanceMethodProcessor_method_already_exists, new String[] { BasicElementLabels.getJavaElementName(fMethodName), BasicElementLabels.getJavaElementName(fTargetType.getElementName()) }), JavaStatusContext.create(method)));
monitor.worked(1);
}
if (fMethodName.equals(fTargetType.getElementName()))
status.merge(RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.MoveInstanceMethodProcessor_method_type_clash, BasicElementLabels.getJavaElementName(fMethodName)), JavaStatusContext.create(fTargetType)));
} finally {
monitor.done();
}
}
/**
* Checks whether the new target name conflicts with an already existing
* method parameter.
*
* @param monitor
* the progress monitor to display progress
* @param status
* the status of the condition checking
* @throws JavaModelException
* if the method declaration of the method to move could not be
* found
*/
protected void checkConflictingTarget(final IProgressMonitor monitor, final RefactoringStatus status) throws JavaModelException {
Assert.isNotNull(monitor);
Assert.isNotNull(status);
final MethodDeclaration declaration= ASTNodeSearchUtil.getMethodDeclarationNode(fMethod, fSourceRewrite.getRoot());
VariableDeclaration variable= null;
final List<SingleVariableDeclaration> parameters= declaration.parameters();
try {
monitor.beginTask("", parameters.size()); //$NON-NLS-1$
monitor.setTaskName(RefactoringCoreMessages.MoveInstanceMethodProcessor_checking);
for (final Iterator<SingleVariableDeclaration> iterator= parameters.iterator(); iterator.hasNext();) {
variable= iterator.next();
if (fTargetName.equals(variable.getName().getIdentifier())) {
status.merge(RefactoringStatus.createErrorStatus(RefactoringCoreMessages.MoveInstanceMethodProcessor_target_name_already_used, JavaStatusContext.create(fMethod)));
break;
}
monitor.worked(1);
}
} finally {
monitor.done();
}
}
/*
* @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#checkFinalConditions(org.eclipse.core.runtime.IProgressMonitor,
* org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext)
*/
@Override
public final RefactoringStatus checkFinalConditions(final IProgressMonitor monitor, final CheckConditionsContext context) throws CoreException, OperationCanceledException {
Assert.isNotNull(monitor);
Assert.isNotNull(context);
Assert.isNotNull(fTarget);
final RefactoringStatus status= new RefactoringStatus();
fChangeManager= new TextChangeManager();
try {
monitor.beginTask("", 4); //$NON-NLS-1$
monitor.setTaskName(RefactoringCoreMessages.MoveInstanceMethodProcessor_checking);
status.merge(Checks.checkIfCuBroken(fMethod));
if (!status.hasError()) {
checkGenericTarget(new SubProgressMonitor(monitor, 1), status);
if (status.isOK()) {
final IType type= getTargetType();
if (type != null) {
if (type.isBinary() || type.isReadOnly() || !fMethod.exists() || fMethod.isBinary() || fMethod.isReadOnly())
status.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveInstanceMethodProcessor_no_binary, JavaStatusContext.create(fMethod)));
else {
status.merge(Checks.checkIfCuBroken(type));
if (!status.hasError()) {
if (!type.exists() || type.isBinary() || type.isReadOnly())
status.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveInstanceMethodProcessor_no_binary, JavaStatusContext.create(fMethod)));
checkConflictingTarget(new SubProgressMonitor(monitor, 1), status);
checkConflictingMethod(new SubProgressMonitor(monitor, 1), status);
Checks.addModifiedFilesToChecker(computeModifiedFiles(fMethod.getCompilationUnit(), type.getCompilationUnit()), context);
monitor.worked(1);
if (!status.hasFatalError())
fChangeManager= createChangeManager(status, new SubProgressMonitor(monitor, 1));
}
}
} else
status.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveInstanceMethodProcessor_no_resolved_target, JavaStatusContext.create(fMethod)));
}
}
} finally {
monitor.done();
}
return status;
}
/**
* Checks whether the target is a type variable or a generic type.
*
* @param monitor
* the progress monitor to display progress
* @param status
* the refactoring status
*/
protected void checkGenericTarget(final IProgressMonitor monitor, final RefactoringStatus status) {
Assert.isNotNull(monitor);
Assert.isNotNull(status);
try {
monitor.beginTask("", 1); //$NON-NLS-1$
monitor.setTaskName(RefactoringCoreMessages.MoveInstanceMethodProcessor_checking);
final ITypeBinding binding= fTarget.getType();
if (binding == null || binding.isTypeVariable())
status.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveInstanceMethodProcessor_no_generic_targets, JavaStatusContext.create(fMethod)));
} finally {
monitor.done();
}
}
/**
* Checks whether the method has references to type variables or generic
* types.
*
* @param monitor
* the progress monitor to display progress
* @param declaration
* the method declaration to check for generic types
* @param status
* the status of the condition checking
*/
protected void checkGenericTypes(final IProgressMonitor monitor, final MethodDeclaration declaration, final RefactoringStatus status) {
Assert.isNotNull(monitor);
Assert.isNotNull(declaration);
Assert.isNotNull(status);
try {
monitor.beginTask("", 1); //$NON-NLS-1$
monitor.setTaskName(RefactoringCoreMessages.MoveInstanceMethodProcessor_checking);
final AstNodeFinder finder= new GenericReferenceFinder(declaration);
declaration.accept(finder);
if (!finder.getStatus().isOK())
status.merge(finder.getStatus());
} finally {
monitor.done();
}
}
/*
* @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#checkInitialConditions(org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
public final RefactoringStatus checkInitialConditions(final IProgressMonitor monitor) throws CoreException, OperationCanceledException {
Assert.isNotNull(monitor);
final RefactoringStatus status= new RefactoringStatus();
try {
monitor.beginTask("", 4); //$NON-NLS-1$
monitor.setTaskName(RefactoringCoreMessages.MoveInstanceMethodProcessor_checking);
status.merge(Checks.checkIfCuBroken(fMethod));
if (!status.hasError()) {
checkMethodDeclaration(new SubProgressMonitor(monitor, 1), status);
if (status.isOK()) {
final MethodDeclaration declaration= ASTNodeSearchUtil.getMethodDeclarationNode(fMethod, fSourceRewrite.getRoot());
checkGenericTypes(new SubProgressMonitor(monitor, 1), declaration, status);
checkMethodBody(new SubProgressMonitor(monitor, 1), declaration, status);
checkPossibleTargets(new SubProgressMonitor(monitor, 1), declaration, status);
}
}
} finally {
monitor.done();
}
return status;
}
/**
* Checks whether the instance method body is compatible with this
* refactoring.
*
* @param monitor
* the progress monitor to display progress
* @param declaration
* the method declaration whose body to check
* @param status
* the status of the condition checking
*/
protected void checkMethodBody(final IProgressMonitor monitor, final MethodDeclaration declaration, final RefactoringStatus status) {
Assert.isNotNull(monitor);
Assert.isNotNull(declaration);
Assert.isNotNull(status);
try {
monitor.beginTask("", 3); //$NON-NLS-1$
monitor.setTaskName(RefactoringCoreMessages.MoveInstanceMethodProcessor_checking);
AstNodeFinder finder= new SuperReferenceFinder();
declaration.accept(finder);
if (!finder.getStatus().isOK())
status.merge(finder.getStatus());
monitor.worked(1);
finder= null;
final IMethodBinding binding= declaration.resolveBinding();
if (binding != null) {
final ITypeBinding declaring= binding.getDeclaringClass();
if (declaring != null)
finder= new EnclosingInstanceReferenceFinder(declaring);
}
if (finder != null) {
declaration.accept(finder);
if (!finder.getStatus().isOK())
status.merge(finder.getStatus());
monitor.worked(1);
finder= new RecursiveCallFinder(declaration);
declaration.accept(finder);
if (!finder.getStatus().isOK())
status.merge(finder.getStatus());
monitor.worked(1);
}
} finally {
monitor.done();
}
}
/**
* Checks whether the instance method declaration is compatible with this
* refactoring.
*
* @param monitor
* the progress monitor to display progress
* @param status
* the status of the condition checking
* @throws JavaModelException
* if the method does not exist
*/
protected void checkMethodDeclaration(final IProgressMonitor monitor, final RefactoringStatus status) throws JavaModelException {
Assert.isNotNull(monitor);
Assert.isNotNull(status);
try {
monitor.beginTask("", 5); //$NON-NLS-1$
monitor.setTaskName(RefactoringCoreMessages.MoveInstanceMethodProcessor_checking);
final int flags= fMethod.getFlags();
if (Flags.isStatic(flags))
status.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveInstanceMethodProcessor_no_static_methods, JavaStatusContext.create(fMethod)));
else if (Flags.isAbstract(flags))
status.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveInstanceMethodProcessor_single_implementation, JavaStatusContext.create(fMethod)));
monitor.worked(1);
if (Flags.isNative(flags))
status.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveInstanceMethodProcessor_no_native_methods, JavaStatusContext.create(fMethod)));
monitor.worked(1);
if (Flags.isSynchronized(flags))
status.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveInstanceMethodProcessor_no_synchronized_methods, JavaStatusContext.create(fMethod)));
monitor.worked(1);
if (fMethod.isConstructor())
status.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveInstanceMethodProcessor_no_constructors, JavaStatusContext.create(fMethod)));
monitor.worked(1);
if (fMethod.getDeclaringType().isAnnotation())
status.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveInstanceMethodProcessor_no_annotation, JavaStatusContext.create(fMethod)));
else if (fMethod.getDeclaringType().isInterface())
status.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveInstanceMethodProcessor_no_interface, JavaStatusContext.create(fMethod)));
monitor.worked(1);
} finally {
monitor.done();
}
}
/**
* Checks whether the method has possible targets to be moved to
*
* @param monitor
* the progress monitor to display progress
* @param declaration
* the method declaration to check
* @param status
* the status of the condition checking
*/
protected void checkPossibleTargets(final IProgressMonitor monitor, final MethodDeclaration declaration, final RefactoringStatus status) {
Assert.isNotNull(monitor);
Assert.isNotNull(declaration);
Assert.isNotNull(status);
try {
monitor.beginTask("", 1); //$NON-NLS-1$
monitor.setTaskName(RefactoringCoreMessages.MoveInstanceMethodProcessor_checking);
if (computeTargetCategories(declaration).length < 1)
status.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveInstanceMethodProcessor_cannot_be_moved, JavaStatusContext.create(fMethod)));
} finally {
monitor.done();
}
}
/**
* Searches for references to the original method.
*
* @param monitor
* the progress monitor to use
* @param status
* the refactoring status to use
* @return the array of search result groups
* @throws CoreException
* if an error occurred during search
*/
protected SearchResultGroup[] computeMethodReferences(final IProgressMonitor monitor, final RefactoringStatus status) throws CoreException {
Assert.isNotNull(monitor);
Assert.isNotNull(status);
try {
monitor.beginTask("", 1); //$NON-NLS-1$
monitor.setTaskName(RefactoringCoreMessages.MoveInstanceMethodProcessor_checking);
SearchPattern pattern= SearchPattern.createPattern(fMethod, IJavaSearchConstants.REFERENCES, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE);
IJavaSearchScope scope= RefactoringScopeFactory.create(fMethod, true, false);
String binaryRefsDescription= Messages.format(RefactoringCoreMessages.ReferencesInBinaryContext_ref_in_binaries_description , BasicElementLabels.getJavaElementName(fMethod.getElementName()));
ReferencesInBinaryContext binaryRefs= new ReferencesInBinaryContext(binaryRefsDescription);
CollectingSearchRequestor requestor= new CollectingSearchRequestor(binaryRefs);
SearchResultGroup[] result= RefactoringSearchEngine.search(pattern, scope, requestor, new SubProgressMonitor(monitor, 1), status);
binaryRefs.addErrorIfNecessary(status);
return result;
} finally {
monitor.done();
}
}
/**
* Computes the files that are being modified by this refactoring.
*
* @param source
* the source compilation unit
* @param target
* the target compilation unit
* @return the modified files
*/
protected IFile[] computeModifiedFiles(final ICompilationUnit source, final ICompilationUnit target) {
Assert.isNotNull(source);
Assert.isNotNull(target);
if (source.equals(target))
return ResourceUtil.getFiles(new ICompilationUnit[] { source });
return ResourceUtil.getFiles(new ICompilationUnit[] { source, target });
}
/**
* Returns the reserved identifiers in the method to move.
*
* @return the reserved identifiers
* @throws JavaModelException
* if the method declaration could not be found
*/
protected String[] computeReservedIdentifiers() throws JavaModelException {
final List<String> names= new ArrayList<String>();
final MethodDeclaration declaration= ASTNodeSearchUtil.getMethodDeclarationNode(fMethod, fSourceRewrite.getRoot());
if (declaration != null) {
final List<SingleVariableDeclaration> parameters= declaration.parameters();
VariableDeclaration variable= null;
for (int index= 0; index < parameters.size(); index++) {
variable= parameters.get(index);
names.add(variable.getName().getIdentifier());
}
final Block body= declaration.getBody();
if (body != null) {
final IBinding[] bindings= new ScopeAnalyzer(fSourceRewrite.getRoot()).getDeclarationsAfter(body.getStartPosition(), ScopeAnalyzer.VARIABLES);
for (int index= 0; index < bindings.length; index++)
names.add(bindings[index].getName());
}
}
final String[] result= new String[names.size()];
names.toArray(result);
return result;
}
/**
* Computes the target categories for the method to move.
*
* @param declaration
* the method declaration
* @return the possible targets as variable bindings of read-only fields and
* parameters
*/
protected IVariableBinding[] computeTargetCategories(final MethodDeclaration declaration) {
Assert.isNotNull(declaration);
if (fPossibleTargets.length == 0 || fCandidateTargets.length == 0) {
final List<IVariableBinding> possibleTargets= new ArrayList<IVariableBinding>(16);
final List<IVariableBinding> candidateTargets= new ArrayList<IVariableBinding>(16);
final IMethodBinding method= declaration.resolveBinding();
if (method != null) {
final ITypeBinding declaring= method.getDeclaringClass();
IVariableBinding[] bindings= getArgumentBindings(declaration);
ITypeBinding binding= null;
for (int index= 0; index < bindings.length; index++) {
binding= bindings[index].getType();
if ((binding.isClass() || binding.isEnum()) && binding.isFromSource()) {
possibleTargets.add(bindings[index]);
candidateTargets.add(bindings[index]);
}
}
final ReadyOnlyFieldFinder visitor= new ReadyOnlyFieldFinder(declaring);
declaration.accept(visitor);
bindings= visitor.getReadOnlyFields();
for (int index= 0; index < bindings.length; index++) {
binding= bindings[index].getType();
if (binding.isClass() && binding.isFromSource())
possibleTargets.add(bindings[index]);
}
bindings= visitor.getDeclaredFields();
for (int index= 0; index < bindings.length; index++) {
binding= bindings[index].getType();
if (binding.isClass() && binding.isFromSource())
candidateTargets.add(bindings[index]);
}
}
fPossibleTargets= new IVariableBinding[possibleTargets.size()];
possibleTargets.toArray(fPossibleTargets);
fCandidateTargets= new IVariableBinding[candidateTargets.size()];
candidateTargets.toArray(fCandidateTargets);
}
return fPossibleTargets;
}
/**
* Creates a visibility-adjusted target expression taking advantage of
* existing accessor methods.
*
* @param enclosingElement
* the java element which encloses the current method access.
* @param expression
* the expression to access the target, or <code>null</code>
* @param adjustments
* the map of elements to visibility adjustments
* @param rewrite
* the ast rewrite to use
* @return an adjusted target expression, or <code>null</code> if the
* access did not have to be changed
* @throws JavaModelException
* if an error occurs while accessing the target expression
*/
protected Expression createAdjustedTargetExpression(final IJavaElement enclosingElement, final Expression expression, final Map<IMember, IncomingMemberVisibilityAdjustment> adjustments, final ASTRewrite rewrite) throws JavaModelException {
Assert.isNotNull(enclosingElement);
Assert.isNotNull(adjustments);
Assert.isNotNull(rewrite);
final IJavaElement element= fTarget.getJavaElement();
if (element != null && !Modifier.isPublic(fTarget.getModifiers())) {
final IField field= (IField) fTarget.getJavaElement();
if (field != null) {
boolean same= field.getAncestor(IJavaElement.PACKAGE_FRAGMENT).equals(enclosingElement.getAncestor(IJavaElement.PACKAGE_FRAGMENT));
final Modifier.ModifierKeyword keyword= same ? null : Modifier.ModifierKeyword.PUBLIC_KEYWORD;
final String modifier= same ? RefactoringCoreMessages.MemberVisibilityAdjustor_change_visibility_default : RefactoringCoreMessages.MemberVisibilityAdjustor_change_visibility_public;
if (fUseGetters) {
final IMethod getter= GetterSetterUtil.getGetter(field);
if (getter != null) {
final MethodDeclaration method= ASTNodeSearchUtil.getMethodDeclarationNode(getter, fSourceRewrite.getRoot());
if (method != null) {
final IMethodBinding binding= method.resolveBinding();
if (binding != null && MemberVisibilityAdjustor.hasLowerVisibility(getter.getFlags(), same ? Modifier.NONE : keyword == null ? Modifier.NONE : keyword.toFlagValue()) && MemberVisibilityAdjustor.needsVisibilityAdjustments(getter, keyword, adjustments))
adjustments.put(getter, new MemberVisibilityAdjustor.OutgoingMemberVisibilityAdjustment(getter, keyword, RefactoringStatus.createWarningStatus(Messages.format(RefactoringCoreMessages.MemberVisibilityAdjustor_change_visibility_method_warning, new String[] { BindingLabelProvider.getBindingLabel(binding, JavaElementLabels.ALL_FULLY_QUALIFIED), modifier }), JavaStatusContext.create(getter))));
final MethodInvocation invocation= rewrite.getAST().newMethodInvocation();
invocation.setExpression(expression);
invocation.setName(rewrite.getAST().newSimpleName(getter.getElementName()));
return invocation;
}
}
}
if (MemberVisibilityAdjustor.hasLowerVisibility(field.getFlags(), (keyword == null ? Modifier.NONE : keyword.toFlagValue())) && MemberVisibilityAdjustor.needsVisibilityAdjustments(field, keyword, adjustments))
adjustments.put(field, new MemberVisibilityAdjustor.OutgoingMemberVisibilityAdjustment(field, keyword, RefactoringStatus.createWarningStatus(Messages.format(RefactoringCoreMessages.MemberVisibilityAdjustor_change_visibility_field_warning, new String[] { BindingLabelProvider.getBindingLabel(fTarget, JavaElementLabels.ALL_FULLY_QUALIFIED), modifier }), JavaStatusContext.create(field))));
}
}
return null;
}
/**
* Creates a generic argument list of the refactored moved method
*
* @param declaration
* the method declaration of the method to move
* @param arguments
* the argument list to create
* @param factory
* the argument factory to use
* @return <code>true</code> if a target node had to be inserted as first
* argument, <code>false</code> otherwise
* @throws JavaModelException
* if an error occurs
*/
protected boolean createArgumentList(final MethodDeclaration declaration, final List<ASTNode> arguments, final IArgumentFactory factory) throws JavaModelException {
Assert.isNotNull(declaration);
Assert.isNotNull(arguments);
Assert.isNotNull(factory);
final AstNodeFinder finder= new ThisReferenceFinder();
declaration.accept(finder);
IVariableBinding binding= null;
VariableDeclaration variable= null;
boolean added= false;
final int size= declaration.parameters().size();
for (int index= 0; index < size; index++) {
variable= (VariableDeclaration) declaration.parameters().get(index);
binding= variable.resolveBinding();
if (binding != null) {
if (!Bindings.equals(binding, fTarget))
arguments.add(factory.getArgumentNode(binding, index == size - 1));
else if (!finder.getStatus().isOK()) {
arguments.add(factory.getTargetNode());
added= true;
}
} else
arguments.add(factory.getArgumentNode(binding, index == size - 1));
}
if (!finder.getStatus().isOK() && !added) {
arguments.add(0, factory.getTargetNode());
added= true;
}
return added;
}
/*
* @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#createChange(org.eclipse.core.runtime.IProgressMonitor)
*/
@Override
public final Change createChange(final IProgressMonitor monitor) throws CoreException, OperationCanceledException {
Assert.isNotNull(monitor);
try {
monitor.beginTask("", 6); //$NON-NLS-1$
monitor.setTaskName(RefactoringCoreMessages.MoveInstanceMethodProcessor_creating);
final TextChange[] changes= fChangeManager.getAllChanges();
if (changes.length == 1)
return changes[0];
final List<TextChange> list= new ArrayList<TextChange>(changes.length);
list.addAll(Arrays.asList(changes));
final Map<String, String> arguments= new HashMap<String, String>();
String project= null;
final IJavaProject javaProject= fMethod.getJavaProject();
if (javaProject != null)
project= javaProject.getElementName();
int flags= JavaRefactoringDescriptor.JAR_REFACTORING | JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT | RefactoringDescriptor.STRUCTURAL_CHANGE | RefactoringDescriptor.MULTI_CHANGE;
final IType declaring= fMethod.getDeclaringType();
try {
if (declaring.isAnonymous() || declaring.isLocal())
flags|= JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT;
} catch (JavaModelException exception) {
JavaPlugin.log(exception);
}
final String description= Messages.format(RefactoringCoreMessages.MoveInstanceMethodProcessor_descriptor_description_short, BasicElementLabels.getJavaElementName(fMethod.getElementName()));
final String header= Messages.format(RefactoringCoreMessages.MoveInstanceMethodProcessor_descriptor_description, new String[] { JavaElementLabels.getElementLabel(fMethod, JavaElementLabels.ALL_FULLY_QUALIFIED), BindingLabelProvider.getBindingLabel(fTarget, JavaElementLabels.ALL_FULLY_QUALIFIED) });
final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header);
comment.addSetting(Messages.format(RefactoringCoreMessages.MoveInstanceMethodProcessor_moved_element_pattern, RefactoringCoreMessages.JavaRefactoringDescriptor_not_available));
comment.addSetting(Messages.format(RefactoringCoreMessages.MoveInstanceMethodProcessor_target_element_pattern, BindingLabelProvider.getBindingLabel(fTarget, JavaElementLabels.ALL_FULLY_QUALIFIED)));
comment.addSetting(Messages.format(RefactoringCoreMessages.MoveInstanceMethodProcessor_method_name_pattern, BasicElementLabels.getJavaElementName(getMethodName())));
if (needsTargetNode())
comment.addSetting(Messages.format(RefactoringCoreMessages.MoveInstanceMethodProcessor_parameter_name_pattern, BasicElementLabels.getJavaElementName(getTargetName())));
final MoveMethodDescriptor descriptor= RefactoringSignatureDescriptorFactory.createMoveMethodDescriptor(project, description, comment.asString(), arguments, flags);
arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT, JavaRefactoringDescriptorUtil.elementToHandle(project, fMethod));
arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME, fMethodName);
arguments.put(ATTRIBUTE_TARGET_NAME, fTargetName);
arguments.put(ATTRIBUTE_DEPRECATE, Boolean.valueOf(fDelegateDeprecation).toString());
arguments.put(ATTRIBUTE_REMOVE, Boolean.valueOf(fRemove).toString());
arguments.put(ATTRIBUTE_INLINE, Boolean.valueOf(fInline).toString());
arguments.put(ATTRIBUTE_USE_GETTER, Boolean.valueOf(fUseGetters).toString());
arguments.put(ATTRIBUTE_USE_SETTER, Boolean.valueOf(fUseSetters).toString());
arguments.put(ATTRIBUTE_TARGET_INDEX, new Integer(getTargetIndex()).toString());
return new DynamicValidationRefactoringChange(descriptor, RefactoringCoreMessages.MoveInstanceMethodRefactoring_name, list.toArray(new Change[list.size()]));
} finally {
monitor.done();
}
}
/**
* Creates the text change manager for this processor.
*
* @param status
* the refactoring status
* @param monitor
* the progress monitor to display progress
* @return the created text change manager
* @throws JavaModelException
* if the method declaration could not be found
* @throws CoreException
* if the changes could not be generated
*/
protected TextChangeManager createChangeManager(final RefactoringStatus status, final IProgressMonitor monitor) throws JavaModelException, CoreException {
Assert.isNotNull(status);
Assert.isNotNull(monitor);
try {
monitor.beginTask("", 7); //$NON-NLS-1$
monitor.setTaskName(RefactoringCoreMessages.MoveInstanceMethodProcessor_creating);
fSourceRewrite.clearASTAndImportRewrites();
final TextChangeManager manager= new TextChangeManager();
final CompilationUnitRewrite targetRewrite= fMethod.getCompilationUnit().equals(getTargetType().getCompilationUnit()) ? fSourceRewrite : new CompilationUnitRewrite(getTargetType().getCompilationUnit());
final MethodDeclaration declaration= ASTNodeSearchUtil.getMethodDeclarationNode(fMethod, fSourceRewrite.getRoot());
final SearchResultGroup[] references= computeMethodReferences(new SubProgressMonitor(monitor, 1), status);
final Map<ICompilationUnit, CompilationUnitRewrite> rewrites= new HashMap<ICompilationUnit, CompilationUnitRewrite>(2);
rewrites.put(fSourceRewrite.getCu(), fSourceRewrite);
if (!fSourceRewrite.getCu().equals(targetRewrite.getCu()))
rewrites.put(targetRewrite.getCu(), targetRewrite);
final ASTRewrite sourceRewrite= ASTRewrite.create(fSourceRewrite.getRoot().getAST());
final MemberVisibilityAdjustor adjustor= new MemberVisibilityAdjustor(fTargetType, fMethod);
adjustor.setStatus(status);
adjustor.setVisibilitySeverity(RefactoringStatus.WARNING);
adjustor.setFailureSeverity(RefactoringStatus.WARNING);
adjustor.setRewrites(rewrites);
adjustor.setRewrite(sourceRewrite, fSourceRewrite.getRoot());
adjustor.adjustVisibility(new SubProgressMonitor(monitor, 1));
final IDocument document= new Document(fMethod.getCompilationUnit().getBuffer().getContents());
final boolean target= createMethodCopy(document, declaration, sourceRewrite, rewrites, adjustor.getAdjustments(), status, new SubProgressMonitor(monitor, 1));
createMethodJavadocReferences(rewrites, declaration, references, target, status, new SubProgressMonitor(monitor, 1));
if (!fSourceRewrite.getCu().equals(targetRewrite.getCu()))
createMethodImports(targetRewrite, declaration, new SubProgressMonitor(monitor, 1), status);
boolean removable= false;
if (fInline) {
String binaryRefsDescription= Messages.format(RefactoringCoreMessages.ReferencesInBinaryContext_ref_in_binaries_description , BasicElementLabels.getJavaElementName(getMethod().getElementName()));
ReferencesInBinaryContext binaryRefs= new ReferencesInBinaryContext(binaryRefsDescription);
removable= createMethodDelegator(rewrites, declaration, references, adjustor.getAdjustments(), target, binaryRefs, status, new SubProgressMonitor(monitor, 1));
binaryRefs.addErrorIfNecessary(status);
if (fRemove && removable) {
fSourceRewrite.getASTRewrite().remove(declaration, fSourceRewrite.createGroupDescription(RefactoringCoreMessages.MoveInstanceMethodProcessor_remove_original_method));
if (!fSourceRewrite.getCu().equals(fTargetType.getCompilationUnit()))
fSourceRewrite.getImportRemover().registerRemovedNode(declaration);
}
}
if (!fRemove || !removable)
createMethodDelegation(declaration, rewrites, adjustor.getAdjustments(), status, new SubProgressMonitor(monitor, 1));
// Do not adjust visibility of a target field; references to the
// field will be removed anyway.
final IJavaElement targetElement= fTarget.getJavaElement();
if (targetElement != null && targetElement instanceof IField && (Flags.isPrivate(fMethod.getFlags()) || !fInline)) {
final IVisibilityAdjustment adjustmentForTarget= adjustor.getAdjustments().get(targetElement);
if (adjustmentForTarget != null)
adjustor.getAdjustments().remove(targetElement);
}
adjustor.rewriteVisibility(new SubProgressMonitor(monitor, 1));
sourceRewrite.rewriteAST(document, fMethod.getJavaProject().getOptions(true));
createMethodSignature(document, declaration, sourceRewrite, rewrites);
ICompilationUnit unit= null;
CompilationUnitRewrite rewrite= null;
for (final Iterator<ICompilationUnit> iterator= rewrites.keySet().iterator(); iterator.hasNext();) {
unit= iterator.next();
rewrite= rewrites.get(unit);
manager.manage(unit, rewrite.createChange(true));
}
return manager;
} finally {
monitor.done();
}
}
/**
* Creates the necessary change to inline a method invocation represented by
* a search match.
*
* @param rewriter
* the current compilation unit rewrite
* @param declaration
* the source method declaration
* @param match
* the search match representing the method invocation
* @param adjustments
* the map of elements to visibility adjustments
* @param target
* <code>true</code> if a target node had to be inserted as
* first argument, <code>false</code> otherwise
* @param status
* the refactoring status
* @return <code>true</code> if the inline change could be performed,
* <code>false</code> otherwise
* @throws JavaModelException
* if a problem occurred while creating the inlined target
* expression for field targets
*/
protected boolean createInlinedMethodInvocation(final CompilationUnitRewrite rewriter, final MethodDeclaration declaration, final SearchMatch match, final Map<IMember, IncomingMemberVisibilityAdjustment> adjustments, final boolean target, final RefactoringStatus status) throws JavaModelException {
Assert.isNotNull(rewriter);
Assert.isNotNull(declaration);
Assert.isNotNull(match);
Assert.isNotNull(adjustments);
Assert.isNotNull(status);
boolean result= true;
final ASTRewrite rewrite= rewriter.getASTRewrite();
final ASTNode node= ASTNodeSearchUtil.findNode(match, rewriter.getRoot());
final TextEditGroup group= rewriter.createGroupDescription(RefactoringCoreMessages.MoveInstanceMethodProcessor_inline_method_invocation);
if (node instanceof MethodInvocation) {
final MethodInvocation invocation= (MethodInvocation) node;
final ListRewrite list= rewrite.getListRewrite(invocation, MethodInvocation.ARGUMENTS_PROPERTY);
if (fTarget.isField()) {
Expression access= null;
if (invocation.getExpression() != null) {
access= createInlinedTargetExpression(rewriter, (IJavaElement) match.getElement(), invocation.getExpression(), adjustments, status);
rewrite.set(invocation, MethodInvocation.EXPRESSION_PROPERTY, access, group);
} else
rewrite.set(invocation, MethodInvocation.EXPRESSION_PROPERTY, rewrite.getAST().newSimpleName(fTarget.getName()), group);
if (target) {
if (access == null || !(access instanceof FieldAccess))
list.insertFirst(rewrite.getAST().newThisExpression(), null);
else
list.insertFirst(rewrite.createCopyTarget(invocation.getExpression()), null);
}
} else {
final IVariableBinding[] bindings= getArgumentBindings(declaration);
if (bindings.length > 0) {
int index= 0;
for (; index < bindings.length; index++)
if (Bindings.equals(bindings[index], fTarget))
break;
if (index < bindings.length && invocation.arguments().size() > index) {
final Expression argument= (Expression) invocation.arguments().get(index);
if (argument instanceof NullLiteral) {
status.merge(RefactoringStatus.createErrorStatus(Messages.format(RefactoringCoreMessages.MoveInstanceMethodProcessor_no_null_argument, BindingLabelProvider.getBindingLabel(declaration.resolveBinding(), JavaElementLabels.ALL_FULLY_QUALIFIED)), JavaStatusContext.create(rewriter.getCu(), invocation)));
result= false;
} else {
if (argument instanceof ThisExpression)
rewrite.remove(invocation.getExpression(), null);
else
rewrite.set(invocation, MethodInvocation.EXPRESSION_PROPERTY, rewrite.createCopyTarget(argument), group);
if (target) {
if (invocation.getExpression() != null)
list.replace(argument, rewrite.createCopyTarget(invocation.getExpression()), group);
else {
final ThisExpression expression= rewrite.getAST().newThisExpression();
final AbstractTypeDeclaration member= (AbstractTypeDeclaration) ASTNodes.getParent(invocation, AbstractTypeDeclaration.class);
if (member != null) {
final ITypeBinding resolved= member.resolveBinding();
if (ASTNodes.getParent(invocation, AnonymousClassDeclaration.class) != null || resolved != null && resolved.isMember()) {
final IMethodBinding method= declaration.resolveBinding();
if (method != null) {
final ITypeBinding declaring= method.getDeclaringClass();
if (declaring != null)
expression.setQualifier(rewrite.getAST().newSimpleName(declaring.getName()));
}
}
}
list.replace(argument, expression, group);
}
} else
list.remove(argument, group);
}
}
}
}
if (result)
rewrite.set(invocation, MethodInvocation.NAME_PROPERTY, rewrite.getAST().newSimpleName(fMethodName), group);
}
return result;
}
/**
* Creates the target field expression for the inline method invocation.
*
* @param rewriter
* the current compilation unit rewrite
* @param enclosingElement
* the enclosing java element of the method invocation.
* @param original
* the original method invocation expression
* @param adjustments
* the map of elements to visibility adjustments
* @param status
* the refactoring status
* @return
* returns the target expression
* @throws JavaModelException
* if a problem occurred while retrieving potential getter
* methods of the target
*/
protected Expression createInlinedTargetExpression(final CompilationUnitRewrite rewriter, final IJavaElement enclosingElement, final Expression original, final Map<IMember, IncomingMemberVisibilityAdjustment> adjustments, final RefactoringStatus status) throws JavaModelException {
Assert.isNotNull(rewriter);
Assert.isNotNull(enclosingElement);
Assert.isNotNull(original);
Assert.isNotNull(adjustments);
Assert.isNotNull(status);
Assert.isTrue(fTarget.isField());
final Expression expression= (Expression) ASTNode.copySubtree(fSourceRewrite.getASTRewrite().getAST(), original);
final Expression result= createAdjustedTargetExpression(enclosingElement, expression, adjustments, fSourceRewrite.getASTRewrite());
if (result == null) {
final FieldAccess access= fSourceRewrite.getASTRewrite().getAST().newFieldAccess();
access.setExpression(expression);
access.setName(fSourceRewrite.getASTRewrite().getAST().newSimpleName(fTarget.getName()));
return access;
}
return result;
}
/**
* Creates the method arguments for the target method declaration.
*
* @param rewrites
* the compilation unit rewrites
* @param rewrite
* the source ast rewrite
* @param declaration
* the source method declaration
* @param adjustments
* the map of elements to visibility adjustments
* @param status
* the refactoring status
* @return <code>true</code> if a target node had to be inserted as method
* argument, <code>false</code> otherwise
* @throws JavaModelException
* if an error occurs while accessing the types of the arguments
*/
protected boolean createMethodArguments(final Map<ICompilationUnit, CompilationUnitRewrite> rewrites, final ASTRewrite rewrite, final MethodDeclaration declaration, final Map<IMember, IncomingMemberVisibilityAdjustment> adjustments, final RefactoringStatus status) throws JavaModelException {
Assert.isNotNull(rewrites);
Assert.isNotNull(declaration);
Assert.isNotNull(rewrite);
Assert.isNotNull(adjustments);
Assert.isNotNull(status);
final CompilationUnitRewrite rewriter= getCompilationUnitRewrite(rewrites, getTargetType().getCompilationUnit());
final AST ast= rewriter.getRoot().getAST();
final AstNodeFinder finder= new AnonymousClassReferenceFinder(declaration);
declaration.accept(finder);
final List<ASTNode> arguments= new ArrayList<ASTNode>(declaration.parameters().size() + 1);
final boolean result= createArgumentList(declaration, arguments, new VisibilityAdjustingArgumentFactory(ast, rewrites, adjustments) {
@Override
public final ASTNode getArgumentNode(final IVariableBinding binding, final boolean last) throws JavaModelException {
Assert.isNotNull(binding);
final SingleVariableDeclaration variable= ast.newSingleVariableDeclaration();
final ITypeBinding type= binding.getType();
adjustTypeVisibility(type);
variable.setName(ast.newSimpleName(binding.getName()));
variable.modifiers().addAll(ast.newModifiers(binding.getModifiers()));
final IMethodBinding method= binding.getDeclaringMethod();
if (last && method != null && method.isVarargs()) {
variable.setVarargs(true);
String name= null;
if (type.isArray()) {
name= type.getElementType().getName();
if (PrimitiveType.toCode(name) != null)
variable.setType(ast.newPrimitiveType(PrimitiveType.toCode(name)));
else
variable.setType(ast.newSimpleType(ast.newSimpleName(name)));
} else {
name= type.getName();
if (PrimitiveType.toCode(name) != null)
variable.setType(ast.newPrimitiveType(PrimitiveType.toCode(name)));
else
variable.setType(ast.newSimpleType(ast.newSimpleName(name)));
}
} else
variable.setType(rewriter.getImportRewrite().addImport(type, ast));
return variable;
}
@Override
public final ASTNode getTargetNode() throws JavaModelException {
final SingleVariableDeclaration variable= ast.newSingleVariableDeclaration();
final IMethodBinding method= declaration.resolveBinding();
if (method != null) {
final ITypeBinding declaring= method.getDeclaringClass();
if (declaring != null) {
adjustTypeVisibility(declaring);
variable.setType(rewriter.getImportRewrite().addImport(declaring, ast));
variable.setName(ast.newSimpleName(fTargetName));
if (finder.getResult().size() > 0)
variable.modifiers().add(ast.newModifier(Modifier.ModifierKeyword.FINAL_KEYWORD));
}
}
return variable;
}
});
final ListRewrite list= rewrite.getListRewrite(declaration, MethodDeclaration.PARAMETERS_PROPERTY);
ASTNode node= null;
for (final Iterator<SingleVariableDeclaration> iterator= declaration.parameters().iterator(); iterator.hasNext();) {
node= iterator.next();
list.remove(node, null);
}
for (final Iterator<ASTNode> iterator= arguments.iterator(); iterator.hasNext();) {
node= iterator.next();
list.insertLast(node, null);
}
return result;
}
/**
* Creates the method body for the target method declaration.
*
* @param rewriter
* the target compilation unit rewrite
* @param rewrite
* the source ast rewrite
* @param declaration
* the source method declaration
*/
protected void createMethodBody(final CompilationUnitRewrite rewriter, final ASTRewrite rewrite, final MethodDeclaration declaration) {
Assert.isNotNull(declaration);
declaration.getBody().accept(new MethodBodyRewriter(rewriter, rewrite, declaration));
}
/**
* Creates the method comment for the target method declaration.
*
* @param rewrite
* the source ast rewrite
* @param declaration
* the source method declaration
* @throws JavaModelException
* if the argument references could not be generated
*/
protected void createMethodComment(final ASTRewrite rewrite, final MethodDeclaration declaration) throws JavaModelException {
Assert.isNotNull(rewrite);
Assert.isNotNull(declaration);
final Javadoc comment= declaration.getJavadoc();
if (comment != null) {
final List<TagElement> tags= new LinkedList<TagElement>(comment.tags());
final IVariableBinding[] bindings= getArgumentBindings(declaration);
final Map<String, TagElement> elements= new HashMap<String, TagElement>(bindings.length);
String name= null;
List<? extends ASTNode> fragments= null;
TagElement element= null;
TagElement reference= null;
IVariableBinding binding= null;
for (int index= 0; index < bindings.length; index++) {
binding= bindings[index];
for (final Iterator<TagElement> iterator= comment.tags().iterator(); iterator.hasNext();) {
element= iterator.next();
name= element.getTagName();
fragments= element.fragments();
if (name != null) {
if (name.equals(TagElement.TAG_PARAM) && !fragments.isEmpty() && fragments.get(0) instanceof SimpleName) {
final SimpleName simple= (SimpleName) fragments.get(0);
if (binding.getName().equals(simple.getIdentifier())) {
elements.put(binding.getKey(), element);
tags.remove(element);
}
} else if (reference == null)
reference= element;
}
}
}
if (bindings.length == 0 && reference == null) {
for (final Iterator<TagElement> iterator= comment.tags().iterator(); iterator.hasNext();) {
element= iterator.next();
name= element.getTagName();
fragments= element.fragments();
if (name != null && !name.equals(TagElement.TAG_PARAM))
reference= element;
}
}
final List<ASTNode> arguments= new ArrayList<ASTNode>(bindings.length + 1);
createArgumentList(declaration, arguments, new IArgumentFactory() {
public final ASTNode getArgumentNode(final IVariableBinding argument, final boolean last) throws JavaModelException {
Assert.isNotNull(argument);
if (elements.containsKey(argument.getKey()))
return rewrite.createCopyTarget(elements.get(argument.getKey()));
return JavadocUtil.createParamTag(argument.getName(), declaration.getAST(), fMethod.getJavaProject());
}
public final ASTNode getTargetNode() throws JavaModelException {
return JavadocUtil.createParamTag(fTargetName, declaration.getAST(), fMethod.getJavaProject());
}
});
final ListRewrite rewriter= rewrite.getListRewrite(comment, Javadoc.TAGS_PROPERTY);
ASTNode tag= null;
for (final Iterator<TagElement> iterator= comment.tags().iterator(); iterator.hasNext();) {
tag= iterator.next();
if (!tags.contains(tag))
rewriter.remove(tag, null);
}
for (final Iterator<ASTNode> iterator= arguments.iterator(); iterator.hasNext();) {
tag= iterator.next();
if (reference != null)
rewriter.insertBefore(tag, reference, null);
else
rewriter.insertLast(tag, null);
}
}
}
/**
* Creates the method content of the moved method.
*
* @param document
* the document representing the source compilation unit
* @param declaration
* the source method declaration
* @param rewrite
* the ast rewrite to use
* @return the string representing the moved method body
* @throws BadLocationException
* if an offset into the document is invalid
*/
protected String createMethodContent(final IDocument document, final MethodDeclaration declaration, final ASTRewrite rewrite) throws BadLocationException {
Assert.isNotNull(document);
Assert.isNotNull(declaration);
Assert.isNotNull(rewrite);
final IRegion range= new Region(declaration.getStartPosition(), declaration.getLength());
final RangeMarker marker= new RangeMarker(range.getOffset(), range.getLength());
final IJavaProject project= fMethod.getJavaProject();
final TextEdit[] edits= rewrite.rewriteAST(document, project.getOptions(true)).removeChildren();
for (int index= 0; index < edits.length; index++)
marker.addChild(edits[index]);
final MultiTextEdit result= new MultiTextEdit();
result.addChild(marker);
final TextEditProcessor processor= new TextEditProcessor(document, new MultiTextEdit(0, document.getLength()), TextEdit.UPDATE_REGIONS);
processor.getRoot().addChild(result);
processor.performEdits();
final IRegion region= document.getLineInformation(document.getLineOfOffset(marker.getOffset()));
return Strings.changeIndent(document.get(marker.getOffset(), marker.getLength()), Strings.computeIndentUnits(document.get(region.getOffset(), region.getLength()), project), project, "", TextUtilities.getDefaultLineDelimiter(document)); //$NON-NLS-1$
}
/**
* Creates the necessary changes to create the delegate method with the
* original method body.
*
* @param document
* the buffer containing the source of the source compilation
* unit
* @param declaration
* the method declaration to use as source
* @param rewrite
* the ast rewrite to use for the copy of the method body
* @param rewrites
* the compilation unit rewrites
* @param adjustments
* the map of elements to visibility adjustments
* @param status
* the refactoring status
* @param monitor
* the progress monitor to display progress
* @throws CoreException
* if an error occurs
* @return <code>true</code> if a target node had to be inserted as first
* argument, <code>false</code> otherwise
*/
protected boolean createMethodCopy(final IDocument document, final MethodDeclaration declaration, final ASTRewrite rewrite, final Map<ICompilationUnit, CompilationUnitRewrite> rewrites, final Map<IMember, IncomingMemberVisibilityAdjustment> adjustments, final RefactoringStatus status, final IProgressMonitor monitor) throws CoreException {
Assert.isNotNull(document);
Assert.isNotNull(declaration);
Assert.isNotNull(rewrite);
Assert.isNotNull(rewrites);
Assert.isNotNull(adjustments);
Assert.isNotNull(status);
Assert.isNotNull(monitor);
boolean target= false;
final CompilationUnitRewrite rewriter= getCompilationUnitRewrite(rewrites, getTargetType().getCompilationUnit());
try {
rewrite.set(declaration, MethodDeclaration.NAME_PROPERTY, rewrite.getAST().newSimpleName(fMethodName), null);
boolean same= false;
final IMethodBinding binding= declaration.resolveBinding();
if (binding != null) {
final ITypeBinding declaring= binding.getDeclaringClass();
if (declaring != null && Bindings.equals(declaring.getPackage(), fTarget.getType().getPackage()))
same= true;
final Modifier.ModifierKeyword keyword= same ? null : Modifier.ModifierKeyword.PUBLIC_KEYWORD;
if (MemberVisibilityAdjustor.hasLowerVisibility(binding.getModifiers(), same ? Modifier.NONE : keyword == null ? Modifier.NONE : keyword.toFlagValue()) && MemberVisibilityAdjustor.needsVisibilityAdjustments(fMethod, keyword, adjustments)) {
final MemberVisibilityAdjustor.IncomingMemberVisibilityAdjustment adjustment= new MemberVisibilityAdjustor.IncomingMemberVisibilityAdjustment(fMethod, keyword, RefactoringStatus.createStatus(RefactoringStatus.WARNING, Messages.format(RefactoringCoreMessages.MemberVisibilityAdjustor_change_visibility_method_warning, new String[] { MemberVisibilityAdjustor.getLabel(fMethod), MemberVisibilityAdjustor.getLabel(keyword) }), JavaStatusContext.create(fMethod), null, RefactoringStatusEntry.NO_CODE, null));
ModifierRewrite.create(rewrite, declaration).setVisibility(keyword == null ? Modifier.NONE : keyword.toFlagValue(), null);
adjustment.setNeedsRewriting(false);
adjustments.put(fMethod, adjustment);
}
}
target= createMethodArguments(rewrites, rewrite, declaration, adjustments, status);
createMethodTypeParameters(rewrite, declaration, status);
createMethodComment(rewrite, declaration);
createMethodBody(rewriter, rewrite, declaration);
} finally {
if (fMethod.getCompilationUnit().equals(getTargetType().getCompilationUnit()))
rewriter.clearImportRewrites();
}
return target;
}
/**
* Creates the necessary changes to replace the body of the method
* declaration with an expression to invoke the delegate.
*
* @param declaration
* the method declaration to replace its body
* @param rewrites
* the compilation unit rewrites
* @param adjustments
* the map of elements to visibility adjustments
* @param status
* the refactoring status
* @param monitor
* the progress monitor to display progress
* @throws CoreException
* if the change could not be generated
* @return <code>true</code> if a target node had to be inserted as first
* argument, <code>false</code> otherwise
*/
protected boolean createMethodDelegation(final MethodDeclaration declaration, final Map<ICompilationUnit, CompilationUnitRewrite> rewrites, final Map<IMember, IncomingMemberVisibilityAdjustment> adjustments, final RefactoringStatus status, final IProgressMonitor monitor) throws CoreException {
Assert.isNotNull(declaration);
Assert.isNotNull(monitor);
final DelegateInstanceMethodCreator creator= new DelegateInstanceMethodCreator(adjustments, rewrites);
creator.setSourceRewrite(fSourceRewrite);
creator.setCopy(false);
creator.setDeclareDeprecated(fDelegateDeprecation);
creator.setDeclaration(declaration);
creator.setNewElementName(fMethodName);
creator.prepareDelegate();
creator.createEdit();
return creator.getNeededInsertion();
}
/**
* Creates the necessary changes to inline the method invocations to the
* original method.
*
* @param rewrites
* the map of compilation units to compilation unit rewrites
* @param declaration
* the source method declaration
* @param groups
* the search result groups representing all references to the
* moved method, including references in comments
* @param adjustments
* the map of elements to visibility adjustments
* @param target
* <code>true</code> if a target node must be inserted as first
* argument, <code>false</code> otherwise
* @param binaryRefs
* the binary references context
* @param status
* the refactoring status
* @param monitor
* the progress monitor to use
* @return <code>true</code> if all method invocations to the original
* method declaration could be inlined, <code>false</code>
* otherwise
*/
protected boolean createMethodDelegator(final Map<ICompilationUnit, CompilationUnitRewrite> rewrites, final MethodDeclaration declaration, final SearchResultGroup[] groups, final Map<IMember, IncomingMemberVisibilityAdjustment> adjustments, final boolean target, ReferencesInBinaryContext binaryRefs, final RefactoringStatus status, final IProgressMonitor monitor) {
Assert.isNotNull(rewrites);
Assert.isNotNull(declaration);
Assert.isNotNull(groups);
Assert.isNotNull(adjustments);
Assert.isNotNull(status);
Assert.isNotNull(monitor);
try {
monitor.beginTask("", groups.length); //$NON-NLS-1$
monitor.setTaskName(RefactoringCoreMessages.MoveInstanceMethodProcessor_creating);
try {
boolean result= true;
boolean found= false;
final ITypeHierarchy hierarchy= fMethod.getDeclaringType().newTypeHierarchy(new SubProgressMonitor(monitor, 1));
IType type= null;
IMethod method= null;
IType[] types= hierarchy.getAllSubtypes(fMethod.getDeclaringType());
for (int index= 0; index < types.length && !found; index++) {
type= types[index];
method= JavaModelUtil.findMethod(fMethod.getElementName(), fMethod.getParameterTypes(), false, type);
if (method != null)
found= true;
}
types= hierarchy.getAllSupertypes(fMethod.getDeclaringType());
for (int index= 0; index < types.length && !found; index++) {
type= types[index];
method= JavaModelUtil.findMethod(fMethod.getElementName(), fMethod.getParameterTypes(), false, type);
if (method != null)
found= true;
}
if (found) {
status.merge(RefactoringStatus.createWarningStatus(Messages.format(RefactoringCoreMessages.MoveInstanceMethodProcessor_inline_overridden, BindingLabelProvider.getBindingLabel(declaration.resolveBinding(), JavaElementLabels.ALL_FULLY_QUALIFIED)), JavaStatusContext.create(fMethod)));
result= false;
} else {
monitor.worked(1);
SearchMatch[] matches= null;
IJavaElement element= null;
ICompilationUnit unit= null;
CompilationUnitRewrite rewrite= null;
SearchResultGroup group= null;
for (int index= 0; index < groups.length; index++) {
group= groups[index];
element= JavaCore.create(group.getResource());
if (element instanceof ICompilationUnit) {
matches= group.getSearchResults();
unit= (ICompilationUnit) element;
rewrite= getCompilationUnitRewrite(rewrites, unit);
SearchMatch match= null;
for (int offset= 0; offset < matches.length; offset++) {
match= matches[offset];
if (match.getAccuracy() == SearchMatch.A_INACCURATE) {
status.merge(RefactoringStatus.createWarningStatus(Messages.format(RefactoringCoreMessages.MoveInstanceMethodProcessor_inline_inaccurate, BasicElementLabels.getFileName(unit)), JavaStatusContext.create(unit, new SourceRange(match.getOffset(), match.getLength()))));
result= false;
} else if (!createInlinedMethodInvocation(rewrite, declaration, match, adjustments, target, status))
result= false;
}
} else {
result= false;
}
}
monitor.worked(1);
}
return result;
} catch (CoreException exception) {
status.merge(RefactoringStatus.create(exception.getStatus()));
return false;
}
} finally {
monitor.done();
}
}
/**
* Creates the necessary imports for the copied method in the target
* compilation unit.
*
* @param rewrite
* the target compilation unit rewrite
* @param declaration
* the source method declaration
* @param monitor
* the progress monitor to use
* @param status
* the refactoring status to use
* @throws CoreException
* if an error occurs
*/
protected void createMethodImports(final CompilationUnitRewrite rewrite, final MethodDeclaration declaration, final IProgressMonitor monitor, final RefactoringStatus status) throws CoreException {
Assert.isNotNull(rewrite);
Assert.isNotNull(declaration);
Assert.isNotNull(monitor);
Assert.isNotNull(status);
monitor.beginTask("", 1); //$NON-NLS-1$
monitor.setTaskName(RefactoringCoreMessages.MoveInstanceMethodProcessor_creating);
try {
ImportRewriteUtil.addImports(rewrite, null, declaration, new HashMap<Name, String>(), new HashMap<Name, String>(), false);
} finally {
monitor.done();
}
}
/**
* Creates the necessary change to updated a comment reference represented
* by a search match.
*
* @param rewrite
* the current compilation unit rewrite
* @param declaration
* the source method declaration
* @param match
* the search match representing the method reference
* @param targetNode
* <code>true</code> if a target node had to be inserted as
* first argument, <code>false</code> otherwise
* @param status
* the refactoring status
*/
protected void createMethodJavadocReference(final CompilationUnitRewrite rewrite, final MethodDeclaration declaration, final SearchMatch match, final boolean targetNode, final RefactoringStatus status) {
Assert.isNotNull(rewrite);
Assert.isNotNull(declaration);
Assert.isNotNull(match);
Assert.isNotNull(status);
final ASTNode node= ASTNodeSearchUtil.findNode(match, rewrite.getRoot());
if (node instanceof MethodRef) {
final AST ast= node.getAST();
final MethodRef successor= ast.newMethodRef();
rewrite.getASTRewrite().replace(node, successor, null);
}
}
/**
* Creates the necessary changes to update tag references to the original
* method.
*
* @param rewrites
* the map of compilation units to compilation unit rewrites
* @param declaration
* the source method declaration
* @param groups
* the search result groups representing all references to the
* moved method, including references in comments
* @param target
* <code>true</code> if a target node must be inserted as first
* argument, <code>false</code> otherwise
* @param status
* the refactoring status
* @param monitor
* the progress monitor to use
*/
protected void createMethodJavadocReferences(final Map<ICompilationUnit, CompilationUnitRewrite> rewrites, final MethodDeclaration declaration, final SearchResultGroup[] groups, final boolean target, final RefactoringStatus status, final IProgressMonitor monitor) {
Assert.isNotNull(rewrites);
Assert.isNotNull(declaration);
Assert.isNotNull(status);
Assert.isNotNull(monitor);
try {
monitor.beginTask("", groups.length); //$NON-NLS-1$
monitor.setTaskName(RefactoringCoreMessages.MoveInstanceMethodProcessor_creating);
SearchMatch[] matches= null;
IJavaElement element= null;
ICompilationUnit unit= null;
CompilationUnitRewrite rewrite= null;
SearchResultGroup group= null;
for (int index= 0; index < groups.length; index++) {
group= groups[index];
element= JavaCore.create(group.getResource());
unit= group.getCompilationUnit();
if (element instanceof ICompilationUnit) {
matches= group.getSearchResults();
unit= (ICompilationUnit) element;
rewrite= getCompilationUnitRewrite(rewrites, unit);
SearchMatch match= null;
for (int offset= 0; offset < matches.length; offset++) {
match= matches[offset];
if (match.getAccuracy() == SearchMatch.A_INACCURATE) {
status.merge(RefactoringStatus.createWarningStatus(Messages.format(RefactoringCoreMessages.MoveInstanceMethodProcessor_inline_inaccurate, BasicElementLabels.getFileName(unit)), JavaStatusContext.create(unit, new SourceRange(match.getOffset(), match.getLength()))));
} else
createMethodJavadocReference(rewrite, declaration, match, target, status);
}
}
monitor.worked(1);
}
} finally {
monitor.done();
}
}
/**
* Creates a comment method reference to the moved method
*
* @param declaration
* the method declaration of the original method
* @param ast
* the ast to create the method reference for
* @return the created link tag to reference the method
* @throws JavaModelException
* if an error occurs
*/
protected ASTNode createMethodReference(final MethodDeclaration declaration, final AST ast) throws JavaModelException {
Assert.isNotNull(ast);
Assert.isNotNull(declaration);
final MethodRef reference= ast.newMethodRef();
reference.setName(ast.newSimpleName(fMethodName));
reference.setQualifier(ASTNodeFactory.newName(ast, fTargetType.getFullyQualifiedName('.')));
createArgumentList(declaration, reference.parameters(), new IArgumentFactory() {
public final ASTNode getArgumentNode(final IVariableBinding binding, final boolean last) {
Assert.isNotNull(binding);
final MethodRefParameter parameter= ast.newMethodRefParameter();
parameter.setType(ASTNodeFactory.newType(ast, binding.getType().getName()));
return parameter;
}
public final ASTNode getTargetNode() {
final MethodRefParameter parameter= ast.newMethodRefParameter();
final IMethodBinding method= declaration.resolveBinding();
if (method != null) {
final ITypeBinding declaring= method.getDeclaringClass();
if (declaring != null)
parameter.setType(ASTNodeFactory.newType(ast, Bindings.getFullyQualifiedName(declaring)));
}
return parameter;
}
});
return reference;
}
/**
* @param document
* the buffer containing the source of the source compilation
* unit
* @param declaration
* the method declaration to use as source
* @param rewrite
* the ast rewrite to use for the copy of the method body
* @param rewrites
* the compilation unit rewrites
* @throws JavaModelException
* if the insertion point cannot be found
*/
protected void createMethodSignature(final IDocument document, final MethodDeclaration declaration, final ASTRewrite rewrite, final Map<ICompilationUnit, CompilationUnitRewrite> rewrites) throws JavaModelException {
Assert.isNotNull(document);
Assert.isNotNull(declaration);
Assert.isNotNull(rewrite);
Assert.isNotNull(rewrites);
try {
final CompilationUnitRewrite rewriter= getCompilationUnitRewrite(rewrites, getTargetType().getCompilationUnit());
final MethodDeclaration stub= (MethodDeclaration) rewriter.getASTRewrite().createStringPlaceholder(createMethodContent(document, declaration, rewrite), ASTNode.METHOD_DECLARATION);
final AbstractTypeDeclaration type= ASTNodeSearchUtil.getAbstractTypeDeclarationNode(getTargetType(), rewriter.getRoot());
rewriter.getASTRewrite().getListRewrite(type, type.getBodyDeclarationsProperty()).insertAt(stub, ASTNodes.getInsertionIndex(stub, type.bodyDeclarations()), rewriter.createGroupDescription(RefactoringCoreMessages.MoveInstanceMethodProcessor_add_moved_method));
} catch (BadLocationException exception) {
JavaPlugin.log(exception);
}
}
/**
* Creates the necessary changes to remove method type parameters if they
* match with enclosing type parameters.
*
* @param rewrite
* the ast rewrite to use
* @param declaration
* the method declaration to remove type parameters
* @param status
* the refactoring status
*/
protected void createMethodTypeParameters(final ASTRewrite rewrite, final MethodDeclaration declaration, final RefactoringStatus status) {
ITypeBinding binding= fTarget.getType();
if (binding != null && binding.isParameterizedType()) {
final IMethodBinding method= declaration.resolveBinding();
if (method != null) {
final ITypeBinding[] parameters= method.getTypeParameters();
if (parameters.length > 0) {
final ListRewrite rewriter= rewrite.getListRewrite(declaration, MethodDeclaration.TYPE_PARAMETERS_PROPERTY);
boolean foundStatic= false;
while (binding != null && !foundStatic) {
if (Flags.isStatic(binding.getModifiers()))
foundStatic= true;
final ITypeBinding[] bindings= binding.getTypeArguments();
for (int index= 0; index < bindings.length; index++) {
for (int offset= 0; offset < parameters.length; offset++) {
if (parameters[offset].getName().equals(bindings[index].getName())) {
rewriter.remove((ASTNode) rewriter.getOriginalList().get(offset), null);
status.addWarning(Messages.format(RefactoringCoreMessages.MoveInstanceMethodProcessor_present_type_parameter_warning, new Object[] { BasicElementLabels.getJavaElementName(parameters[offset].getName()), BindingLabelProvider.getBindingLabel(binding, JavaElementLabels.ALL_FULLY_QUALIFIED) }), JavaStatusContext.create(fMethod));
}
}
}
binding= binding.getDeclaringClass();
}
}
}
}
}
/**
* Creates the expression to access the new target.
*
* @param declaration
* the method declaration where to access the target
* @return the corresponding expression
*/
protected Expression createSimpleTargetAccessExpression(final MethodDeclaration declaration) {
Assert.isNotNull(declaration);
Expression expression= null;
final AST ast= declaration.getAST();
final ITypeBinding type= fTarget.getDeclaringClass();
if (type != null) {
boolean shadows= false;
final IVariableBinding[] bindings= getArgumentBindings(declaration);
IVariableBinding variable= null;
for (int index= 0; index < bindings.length; index++) {
variable= bindings[index];
if (fMethod.getDeclaringType().getField(variable.getName()).exists()) {
shadows= true;
break;
}
}
if (fSettings.useKeywordThis || shadows) {
final FieldAccess access= ast.newFieldAccess();
access.setName(ast.newSimpleName(fTarget.getName()));
access.setExpression(ast.newThisExpression());
expression= access;
} else
expression= ast.newSimpleName(fTarget.getName());
} else
expression= ast.newSimpleName(fTarget.getName());
return expression;
}
/**
* Returns the candidate targets for the method to move.
*
* @return the candidate targets as variable bindings of fields and
* parameters
*/
public final IVariableBinding[] getCandidateTargets() {
Assert.isNotNull(fCandidateTargets);
return fCandidateTargets;
}
/**
* Returns a compilation unit rewrite for the specified compilation unit.
*
* @param rewrites
* the compilation unit rewrite map
* @param unit
* the compilation unit
* @return the corresponding compilation unit rewrite
*/
protected CompilationUnitRewrite getCompilationUnitRewrite(final Map<ICompilationUnit, CompilationUnitRewrite> rewrites, final ICompilationUnit unit) {
Assert.isNotNull(rewrites);
Assert.isNotNull(unit);
CompilationUnitRewrite rewrite= rewrites.get(unit);
if (rewrite == null) {
rewrite= new CompilationUnitRewrite(unit);
rewrites.put(unit, rewrite);
}
return rewrite;
}
/**
* {@inheritDoc}
*/
public final boolean getDelegateUpdating() {
return fDelegatingUpdating;
}
/**
* {@inheritDoc}
*/
public String getDelegateUpdatingTitle(boolean plural) {
if (plural)
return RefactoringCoreMessages.DelegateMethodCreator_keep_original_moved_plural;
else
return RefactoringCoreMessages.DelegateMethodCreator_keep_original_moved_singular;
}
/**
* {@inheritDoc}
*/
public final boolean getDeprecateDelegates() {
return fDelegateDeprecation;
}
/**
* {@inheritDoc}
*/
@Override
public final Object[] getElements() {
return new Object[] { fMethod };
}
/**
* {@inheritDoc}
*/
@Override
public final String getIdentifier() {
return IDENTIFIER;
}
/**
* Returns the method to be moved.
*
* @return the method to be moved
*/
public final IMethod getMethod() {
return fMethod;
}
/**
* Returns the new method name.
*
* @return the name of the new method
*/
public final String getMethodName() {
return fMethodName;
}
/**
* Returns the possible targets for the method to move.
*
* @return the possible targets as variable bindings of read-only fields and
* parameters
*/
public final IVariableBinding[] getPossibleTargets() {
Assert.isNotNull(fPossibleTargets);
return fPossibleTargets;
}
/*
* @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#getProcessorName()
*/
@Override
public final String getProcessorName() {
return RefactoringCoreMessages.MoveInstanceMethodProcessor_name;
}
/**
* Returns the index of the chosen target.
*
* @return the target index
*/
protected final int getTargetIndex() {
final IVariableBinding[] targets= getPossibleTargets();
int result= -1;
for (int index= 0; index < targets.length; index++) {
if (Bindings.equals(fTarget, targets[index])) {
result= index;
break;
}
}
return result;
}
/**
* Returns the new target name.
*
* @return the name of the new target
*/
public final String getTargetName() {
return fTargetName;
}
/**
* Returns the type of the new target.
*
* @return the type of the new target
* @throws JavaModelException
* if the type does not exist
*/
protected IType getTargetType() throws JavaModelException {
Assert.isNotNull(fTarget);
if (fTargetType == null) {
final ITypeBinding binding= fTarget.getType();
if (binding != null)
fTargetType= (IType) binding.getJavaElement();
else
throw new JavaModelException(new CoreException(new Status(IStatus.ERROR, JavaPlugin.getPluginId(), 0, RefactoringCoreMessages.MoveInstanceMethodProcessor_cannot_be_moved, null)));
}
return fTargetType;
}
/**
* Initializes the refactoring with the given input.
*
* @param method
* the method to move
*/
protected void initialize(final IMethod method) {
Assert.isNotNull(method);
fSourceRewrite= new CompilationUnitRewrite(fMethod.getCompilationUnit());
fMethodName= method.getElementName();
fTargetName= suggestTargetName();
if (fSettings == null)
fSettings= JavaPreferencesSettings.getCodeGenerationSettings(fMethod.getJavaProject());
}
private RefactoringStatus initialize(JavaRefactoringArguments extended) {
final String handle= extended.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT);
if (handle != null) {
final IJavaElement element= JavaRefactoringDescriptorUtil.handleToElement(extended.getProject(), handle, false);
if (element == null || !element.exists() || element.getElementType() != IJavaElement.METHOD)
return JavaRefactoringDescriptorUtil.createInputFatalStatus(element, getProcessorName(), IJavaRefactorings.MOVE_METHOD);
else {
fMethod= (IMethod) element;
initialize(fMethod);
}
} else
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT));
final String name= extended.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME);
if (name != null) {
final RefactoringStatus status= setMethodName(name);
if (status.hasError())
return status;
} else
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME));
final String deprecate= extended.getAttribute(ATTRIBUTE_DEPRECATE);
if (deprecate != null) {
fDelegateDeprecation= Boolean.valueOf(deprecate).booleanValue();
} else
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_DEPRECATE));
final String remove= extended.getAttribute(ATTRIBUTE_REMOVE);
if (remove != null) {
fRemove= Boolean.valueOf(remove).booleanValue();
} else
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_REMOVE));
final String inline= extended.getAttribute(ATTRIBUTE_INLINE);
if (inline != null) {
fInline= Boolean.valueOf(inline).booleanValue();
} else
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_INLINE));
final String getter= extended.getAttribute(ATTRIBUTE_USE_GETTER);
if (getter != null)
fUseGetters= Boolean.valueOf(getter).booleanValue();
else
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_USE_GETTER));
final String setter= extended.getAttribute(ATTRIBUTE_USE_SETTER);
if (setter != null)
fUseSetters= Boolean.valueOf(setter).booleanValue();
else
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_USE_SETTER));
final String target= extended.getAttribute(ATTRIBUTE_TARGET_NAME);
if (target != null) {
final RefactoringStatus status= setTargetName(target);
if (status.hasError())
return status;
} else
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_TARGET_NAME));
final String value= extended.getAttribute(ATTRIBUTE_TARGET_INDEX);
if (value != null) {
try {
final int index= Integer.valueOf(value).intValue();
if (index >= 0) {
final MethodDeclaration declaration= ASTNodeSearchUtil.getMethodDeclarationNode(fMethod, fSourceRewrite.getRoot());
if (declaration != null) {
final IVariableBinding[] bindings= computeTargetCategories(declaration);
if (bindings != null && index < bindings.length)
setTarget(bindings[index]);
}
}
} catch (NumberFormatException exception) {
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, new String[] { value, ATTRIBUTE_TARGET_INDEX }));
} catch (JavaModelException exception) {
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, new String[] { value, ATTRIBUTE_TARGET_INDEX }));
}
} else
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_TARGET_INDEX));
return new RefactoringStatus();
}
/*
* @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#isApplicable()
*/
@Override
public final boolean isApplicable() throws CoreException {
return fMethod.exists() && !fMethod.isConstructor() && !fMethod.isBinary() && !fMethod.isReadOnly() && fMethod.getCompilationUnit() != null && !JdtFlags.isStatic(fMethod);
}
/**
* Is the specified name a target access?
*
* @param name
* the name to check
* @return <code>true</code> if this name is a target access,
* <code>false</code> otherwise
*/
protected boolean isTargetAccess(final Name name) {
Assert.isNotNull(name);
final IBinding binding= name.resolveBinding();
if (Bindings.equals(fTarget, binding))
return true;
if (name.getParent() instanceof FieldAccess) {
final FieldAccess access= (FieldAccess) name.getParent();
final Expression expression= access.getExpression();
if (expression instanceof Name)
return isTargetAccess((Name) expression);
} else if (name instanceof QualifiedName) {
final QualifiedName qualified= (QualifiedName) name;
if (qualified.getQualifier() != null)
return isTargetAccess(qualified.getQualifier());
}
return false;
}
/*
* @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#loadParticipants(org.eclipse.ltk.core.refactoring.RefactoringStatus,
* org.eclipse.ltk.core.refactoring.participants.SharableParticipants)
*/
@Override
public final RefactoringParticipant[] loadParticipants(final RefactoringStatus status, final SharableParticipants participants) throws CoreException {
return new RefactoringParticipant[0];
}
/**
* Does the moved method need a target node?
*
* @return <code>true</code> if it needs a target node, <code>false</code>
* otherwise
*/
public final boolean needsTargetNode() {
return fTargetNode;
}
/**
* {@inheritDoc}
*/
public final void setDelegateUpdating(final boolean updating) {
fDelegatingUpdating= updating;
setInlineDelegator(!updating);
setRemoveDelegator(!updating);
}
/**
* {@inheritDoc}
*/
public final void setDeprecateDelegates(final boolean deprecate) {
fDelegateDeprecation= deprecate;
}
/**
* Determines whether the delegator has to be inlined.
*
* @param inline
* <code>true</code> to inline the delegator,
* <code>false</code> otherwise
*/
public final void setInlineDelegator(final boolean inline) {
fInline= inline;
}
/**
* Sets the new method name.
*
* @param name
* the name to set
* @return the status of the operation
*/
public final RefactoringStatus setMethodName(final String name) {
Assert.isNotNull(name);
RefactoringStatus status= Checks.checkMethodName(name, fTargetType);
if (status.hasFatalError())
return status;
fMethodName= name;
return status;
}
/**
* Determines whether the delegator has to be removed after inlining. Note
* that the option to inline the delegator has to be enabled if this method
* is called with the argument <code>true</code>.
*
* @param remove
* <code>true</code> if it should be removed,
* <code>false</code> otherwise
*/
public final void setRemoveDelegator(final boolean remove) {
Assert.isTrue(!remove || fInline);
fRemove= remove;
}
/**
* Sets the new target.
*
* @param target
* the target to set
*/
public final void setTarget(final IVariableBinding target) {
Assert.isNotNull(target);
fTarget= target;
fTargetType= null;
try {
final MethodDeclaration declaration= ASTNodeSearchUtil.getMethodDeclarationNode(fMethod, fSourceRewrite.getRoot());
if (declaration != null) {
final AstNodeFinder finder= new ThisReferenceFinder();
declaration.accept(finder);
fTargetNode= !finder.getResult().isEmpty();
return;
}
} catch (JavaModelException exception) {
JavaPlugin.log(exception);
}
fTargetNode= true;
}
/**
* Sets the new target name.
*
* @param name
* the name to set
* @return the status of the operation
*/
public final RefactoringStatus setTargetName(final String name) {
Assert.isNotNull(name);
final RefactoringStatus status= Checks.checkTempName(name, fMethod);
if (status.hasFatalError())
return status;
fTargetName= name;
return status;
}
/**
* Determines whether getter methods should be used to resolve visibility
* issues.
*
* @param use
* <code>true</code> if getter methods should be used,
* <code>false</code> otherwise
*/
public final void setUseGetters(final boolean use) {
fUseGetters= use;
}
/**
* Determines whether setter methods should be used to resolve visibility
* issues.
*
* @param use
* <code>true</code> if setter methods should be used,
* <code>false</code> otherwise
*/
public final void setUseSetters(final boolean use) {
fUseSetters= use;
}
/**
* Should getter methods be used to resolve visibility issues?
*
* @return <code>true</code> if getter methods should be used,
* <code>false</code> otherwise
*/
public final boolean shouldUseGetters() {
return fUseGetters;
}
/**
* Should setter methods be used to resolve visibility issues?
*
* @return <code>true</code> if setter methods should be used,
* <code>false</code> otherwise
*/
public final boolean shouldUseSetters() {
return fUseSetters;
}
/**
* Returns a best guess for the name of the new target.
*
* @return a best guess for the name
*/
protected String suggestTargetName() {
try {
final String[] candidates= StubUtility.getArgumentNameSuggestions(fMethod.getDeclaringType(), computeReservedIdentifiers());
if (candidates.length > 0) {
if (candidates[0].indexOf('$') < 0)
return candidates[0];
}
} catch (JavaModelException exception) {
JavaPlugin.log(exception);
}
return "arg"; //$NON-NLS-1$
}
}