blob: 901b98ca24d7693e9c39a9b0898d2ce9c94259ec [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2008 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.wst.jsdt.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.resources.IFile;
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.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.RefactoringArguments;
import org.eclipse.ltk.core.refactoring.participants.RefactoringParticipant;
import org.eclipse.ltk.core.refactoring.participants.SharableParticipants;
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.wst.jsdt.core.Flags;
import org.eclipse.wst.jsdt.core.IField;
import org.eclipse.wst.jsdt.core.IFunction;
import org.eclipse.wst.jsdt.core.IJavaScriptElement;
import org.eclipse.wst.jsdt.core.IJavaScriptProject;
import org.eclipse.wst.jsdt.core.IJavaScriptUnit;
import org.eclipse.wst.jsdt.core.IType;
import org.eclipse.wst.jsdt.core.ITypeHierarchy;
import org.eclipse.wst.jsdt.core.JavaScriptCore;
import org.eclipse.wst.jsdt.core.JavaScriptModelException;
import org.eclipse.wst.jsdt.core.dom.AST;
import org.eclipse.wst.jsdt.core.dom.ASTNode;
import org.eclipse.wst.jsdt.core.dom.ASTVisitor;
import org.eclipse.wst.jsdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.wst.jsdt.core.dom.AnonymousClassDeclaration;
import org.eclipse.wst.jsdt.core.dom.Assignment;
import org.eclipse.wst.jsdt.core.dom.Block;
import org.eclipse.wst.jsdt.core.dom.BodyDeclaration;
import org.eclipse.wst.jsdt.core.dom.ClassInstanceCreation;
import org.eclipse.wst.jsdt.core.dom.Expression;
import org.eclipse.wst.jsdt.core.dom.FieldAccess;
import org.eclipse.wst.jsdt.core.dom.FunctionDeclaration;
import org.eclipse.wst.jsdt.core.dom.FunctionInvocation;
import org.eclipse.wst.jsdt.core.dom.FunctionRef;
import org.eclipse.wst.jsdt.core.dom.FunctionRefParameter;
import org.eclipse.wst.jsdt.core.dom.IBinding;
import org.eclipse.wst.jsdt.core.dom.IFunctionBinding;
import org.eclipse.wst.jsdt.core.dom.ITypeBinding;
import org.eclipse.wst.jsdt.core.dom.IVariableBinding;
import org.eclipse.wst.jsdt.core.dom.JSdoc;
import org.eclipse.wst.jsdt.core.dom.Modifier;
import org.eclipse.wst.jsdt.core.dom.Name;
import org.eclipse.wst.jsdt.core.dom.NullLiteral;
import org.eclipse.wst.jsdt.core.dom.PostfixExpression;
import org.eclipse.wst.jsdt.core.dom.PrefixExpression;
import org.eclipse.wst.jsdt.core.dom.PrimitiveType;
import org.eclipse.wst.jsdt.core.dom.QualifiedName;
import org.eclipse.wst.jsdt.core.dom.SimpleName;
import org.eclipse.wst.jsdt.core.dom.SingleVariableDeclaration;
import org.eclipse.wst.jsdt.core.dom.SuperFieldAccess;
import org.eclipse.wst.jsdt.core.dom.SuperMethodInvocation;
import org.eclipse.wst.jsdt.core.dom.TagElement;
import org.eclipse.wst.jsdt.core.dom.ThisExpression;
import org.eclipse.wst.jsdt.core.dom.TypeDeclaration;
import org.eclipse.wst.jsdt.core.dom.TypeParameter;
import org.eclipse.wst.jsdt.core.dom.VariableDeclaration;
import org.eclipse.wst.jsdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.wst.jsdt.core.dom.rewrite.ListRewrite;
import org.eclipse.wst.jsdt.core.refactoring.IJavaScriptRefactorings;
import org.eclipse.wst.jsdt.core.refactoring.descriptors.JavaScriptRefactoringDescriptor;
import org.eclipse.wst.jsdt.core.search.IJavaScriptSearchConstants;
import org.eclipse.wst.jsdt.core.search.SearchMatch;
import org.eclipse.wst.jsdt.core.search.SearchPattern;
import org.eclipse.wst.jsdt.internal.corext.SourceRange;
import org.eclipse.wst.jsdt.internal.corext.codemanipulation.CodeGenerationSettings;
import org.eclipse.wst.jsdt.internal.corext.codemanipulation.GetterSetterUtil;
import org.eclipse.wst.jsdt.internal.corext.codemanipulation.StubUtility;
import org.eclipse.wst.jsdt.internal.corext.dom.ASTNodeFactory;
import org.eclipse.wst.jsdt.internal.corext.dom.ASTNodes;
import org.eclipse.wst.jsdt.internal.corext.dom.Bindings;
import org.eclipse.wst.jsdt.internal.corext.dom.ModifierRewrite;
import org.eclipse.wst.jsdt.internal.corext.dom.ScopeAnalyzer;
import org.eclipse.wst.jsdt.internal.corext.refactoring.Checks;
import org.eclipse.wst.jsdt.internal.corext.refactoring.JDTRefactoringDescriptor;
import org.eclipse.wst.jsdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
import org.eclipse.wst.jsdt.internal.corext.refactoring.JavaRefactoringArguments;
import org.eclipse.wst.jsdt.internal.corext.refactoring.RefactoringCoreMessages;
import org.eclipse.wst.jsdt.internal.corext.refactoring.RefactoringSearchEngine2;
import org.eclipse.wst.jsdt.internal.corext.refactoring.SearchResultGroup;
import org.eclipse.wst.jsdt.internal.corext.refactoring.base.JavaStatusContext;
import org.eclipse.wst.jsdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange;
import org.eclipse.wst.jsdt.internal.corext.refactoring.code.ScriptableRefactoring;
import org.eclipse.wst.jsdt.internal.corext.refactoring.delegates.DelegateMethodCreator;
import org.eclipse.wst.jsdt.internal.corext.refactoring.structure.MemberVisibilityAdjustor.IVisibilityAdjustment;
import org.eclipse.wst.jsdt.internal.corext.refactoring.tagging.ICommentProvider;
import org.eclipse.wst.jsdt.internal.corext.refactoring.tagging.IDelegateUpdating;
import org.eclipse.wst.jsdt.internal.corext.refactoring.tagging.IScriptableRefactoring;
import org.eclipse.wst.jsdt.internal.corext.refactoring.util.JavadocUtil;
import org.eclipse.wst.jsdt.internal.corext.refactoring.util.ResourceUtil;
import org.eclipse.wst.jsdt.internal.corext.refactoring.util.TextChangeManager;
import org.eclipse.wst.jsdt.internal.corext.util.JavaModelUtil;
import org.eclipse.wst.jsdt.internal.corext.util.JdtFlags;
import org.eclipse.wst.jsdt.internal.corext.util.Messages;
import org.eclipse.wst.jsdt.internal.corext.util.SearchUtils;
import org.eclipse.wst.jsdt.internal.corext.util.Strings;
import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin;
import org.eclipse.wst.jsdt.internal.ui.preferences.JavaPreferencesSettings;
import org.eclipse.wst.jsdt.internal.ui.viewsupport.BindingLabelProvider;
import org.eclipse.wst.jsdt.ui.JavaScriptElementLabels;
/**
* Refactoring processor to move instance methods.
*/
public final class MoveInstanceMethodProcessor extends MoveProcessor implements IScriptableRefactoring, IDelegateUpdating, ICommentProvider {
/**
* 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 FunctionDeclaration declaration) {
fDeclaringType= declaration.resolveBinding().getDeclaringClass();
}
public final void endVisit(final AnonymousClassDeclaration node) {
Assert.isNotNull(node);
if (fAnonymousClass > 0)
fAnonymousClass--;
super.endVisit(node);
}
public final boolean visit(final AnonymousClassDeclaration node) {
Assert.isNotNull(node);
fAnonymousClass++;
return super.visit(node);
}
public final boolean visit(final FunctionInvocation node) {
Assert.isNotNull(node);
if (fAnonymousClass > 0) {
final IFunctionBinding binding= node.resolveMethodBinding();
if (binding != null) {
if (node.getExpression() == null && !Modifier.isStatic(binding.getModifiers()))
fResult.add(node.getName());
}
}
return true;
}
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 fResult= new HashSet();
/** The status of the find operation */
protected final RefactoringStatus fStatus= new RefactoringStatus();
/**
* Returns the result set.
*
* @return the result set
*/
public final Set 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 fAdjustments;
private boolean fNeededInsertion;
private Map fRewrites;
public DelegateInstanceMethodCreator(Map adjustments, Map rewrites) {
super();
fAdjustments= adjustments;
fRewrites= rewrites;
}
protected ASTNode createBody(BodyDeclaration bd) throws JavaScriptModelException {
FunctionDeclaration methodDeclaration= (FunctionDeclaration) bd;
final FunctionInvocation invocation= getAst().newFunctionInvocation();
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.getJavaScriptUnit()))
fSourceRewrite.getImportRemover().registerRemovedNode(methodDeclaration.getBody());
return block;
}
protected ASTNode createDocReference(final BodyDeclaration declaration) throws JavaScriptModelException {
return MoveInstanceMethodProcessor.this.createMethodReference((FunctionDeclaration) 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 fEnclosingTypes= new ArrayList(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();
}
}
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 IFunctionBinding) {
final IFunctionBinding method= (IFunctionBinding) binding;
if (Flags.isStatic(method.getModifiers()))
return false;
declaring= method.getDeclaringClass();
}
if (declaring != null) {
ITypeBinding enclosing= null;
for (final Iterator iterator= fEnclosingTypes.iterator(); iterator.hasNext();) {
enclosing= (ITypeBinding) iterator.next();
if (Bindings.equals(enclosing, declaring)) {
fStatus.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveInstanceMethodProcessor_refers_enclosing_instances, JavaStatusContext.create(fMethod.getJavaScriptUnit(), node)));
fResult.add(node);
break;
}
}
}
return false;
}
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.getJavaScriptUnit(), 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 fBindings= new HashSet();
/**
* Creates a new generic reference finder.
*
* @param declaration
* the method declaration
*/
public GenericReferenceFinder(final FunctionDeclaration declaration) {
Assert.isNotNull(declaration);
ITypeBinding binding= null;
TypeParameter parameter= null;
for (final Iterator iterator= declaration.typeParameters().iterator(); iterator.hasNext();) {
parameter= (TypeParameter) iterator.next();
binding= parameter.resolveBinding();
if (binding != null)
fBindings.add(binding.getKey());
}
}
/*
* @see org.eclipse.wst.jsdt.core.dom.ASTVisitor#visit(org.eclipse.wst.jsdt.core.dom.SimpleName)
*/
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.getJavaScriptUnit(), 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 JavaScriptModelException
* if an error occurs
*/
public ASTNode getArgumentNode(IVariableBinding binding, boolean last) throws JavaScriptModelException;
/**
* Returns a target node for the current target.
*
* @return the corresponding node
* @throws JavaScriptModelException
* if an error occurs
*/
public ASTNode getTargetNode() throws JavaScriptModelException;
}
/**
* 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 FunctionDeclaration fDeclaration;
/** The set of handled method invocations */
protected final Set fMethodDeclarations= new HashSet();
/** The source ast rewrite to use */
protected final ASTRewrite fRewrite;
/** The existing static imports */
protected final Set fStaticImports= new HashSet();
/** 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 FunctionDeclaration sourceDeclaration) {
Assert.isNotNull(targetRewrite);
Assert.isNotNull(rewrite);
Assert.isNotNull(sourceDeclaration);
fTargetRewrite= targetRewrite;
fRewrite= rewrite;
fDeclaration= sourceDeclaration;
fStaticImports.clear();
ImportRewriteUtil.collectImports(fMethod.getJavaScriptProject(), sourceDeclaration, new HashSet(), fStaticImports, false);
}
public final void endVisit(final AnonymousClassDeclaration node) {
Assert.isNotNull(node);
if (fAnonymousClass > 0)
fAnonymousClass--;
super.endVisit(node);
}
public final boolean visit(final AnonymousClassDeclaration node) {
Assert.isNotNull(node);
fAnonymousClass++;
return super.visit(node);
}
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);
}
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;
}
}
if (expression instanceof FieldAccess) {
final FieldAccess access= (FieldAccess) expression;
final IBinding binding= access.getName().resolveBinding();
if ((access.getExpression() instanceof ThisExpression) && Bindings.equals(fTarget, binding)) {
fRewrite.replace(node, ast.newSimpleName(node.getName().getIdentifier()), null);
return false;
}
} else if (expression != null) {
final IFunctionBinding method= fDeclaration.resolveBinding();
if (variable != null && method != null && !JdtFlags.isStatic(variable) && Bindings.equals(method.getDeclaringClass(), variable.getDeclaringClass())) {
fRewrite.replace(expression, ast.newSimpleName(fTargetName), null);
return false;
}
}
return true;
}
public final void visit(final List nodes) {
Assert.isNotNull(nodes);
ASTNode node= null;
for (final Iterator iterator= nodes.iterator(); iterator.hasNext();) {
node= (ASTNode) iterator.next();
node.accept(this);
}
}
public final boolean visit(final FunctionInvocation node) {
Assert.isNotNull(node);
final Expression expression= node.getExpression();
final IFunctionBinding 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, FunctionInvocation.EXPRESSION_PROPERTY, ast.newSimpleName(fTargetName), null);
else
rewrite.set(node, FunctionInvocation.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(access.resolveFieldBinding(), fTarget)) {
rewrite.remove(expression, null);
visit(node.arguments());
return false;
}
} else if (expression instanceof Name) {
final Name name= (Name) expression;
if (Bindings.equals(name.resolveBinding(), fTarget)) {
rewrite.remove(expression, null);
visit(node.arguments());
return false;
}
}
}
}
return true;
}
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 String name= fTargetRewrite.getImportRewrite().addImport(type);
if (name != null && name.length() > 0) {
fRewrite.replace(node, ASTNodeFactory.newName(node.getAST(), name), null);
return false;
}
}
}
binding= node.getQualifier().resolveBinding();
if (Bindings.equals(binding, fTarget)) {
fRewrite.replace(node, fRewrite.createCopyTarget(node.getName()), null);
return false;
}
return true;
}
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);
if (name != null && name.length() > 0) {
fRewrite.replace(node, ASTNodeFactory.newName(ast, name), null);
return false;
}
}
}
if (Bindings.equals(binding, fTarget))
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 IFunctionBinding method= fDeclaration.resolveBinding();
final ITypeBinding declaring= variable.getDeclaringClass();
if (method != null) {
if (Bindings.equals(method.getDeclaringClass(), declaring)) {
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)) {
rewrite.replace(node, ast.newQualifiedName(ASTNodeFactory.newName(ast, fTargetRewrite.getImportRewrite().addImport(declaring)), ast.newSimpleName(node.getFullyQualifiedName())), null);
}
}
}
return false;
}
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 FunctionInvocation) {
final FunctionInvocation invocation= (FunctionInvocation) parent;
return invocation.getExpression() != null && invocation.getName().equals(name);
}
return false;
}
/** The list of found bindings */
protected final List fBindings= new LinkedList();
/** The keys of the found bindings */
protected final Set fFound= new HashSet();
/** The keys of the written bindings */
protected final Set fWritten= new HashSet();
/**
* 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 list= new LinkedList(fBindings);
for (final Iterator iterator= list.iterator(); iterator.hasNext();) {
binding= (IVariableBinding) iterator.next();
if (fWritten.contains(binding.getKey()))
iterator.remove();
}
final IVariableBinding[] result= new IVariableBinding[list.size()];
list.toArray(result);
return result;
}
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;
}
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;
}
public final boolean visit(final PostfixExpression node) {
final IVariableBinding binding= getFieldBinding(node.getOperand());
if (binding != null)
fWritten.add(binding.getKey());
return true;
}
public final boolean visit(final PrefixExpression node) {
final IVariableBinding binding= getFieldBinding(node.getOperand());
if (binding != null)
fWritten.add(binding.getKey());
return false;
}
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 IFunctionBinding fBinding;
/**
* Creates a new recursive call finder.
*
* @param declaration
* the method declaration
*/
public RecursiveCallFinder(final FunctionDeclaration declaration) {
Assert.isNotNull(declaration);
fBinding= declaration.resolveBinding();
}
public final boolean visit(final FunctionInvocation node) {
Assert.isNotNull(node);
final Expression expression= node.getExpression();
final IFunctionBinding 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.getJavaScriptUnit(), node)));
fResult.add(node);
return false;
}
return true;
}
}
/**
* AST visitor to find 'super' references.
*/
public final class SuperReferenceFinder extends AstNodeFinder {
public final boolean visit(final AnonymousClassDeclaration node) {
return false;
}
public final boolean visit(final SuperFieldAccess node) {
Assert.isNotNull(node);
fStatus.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveInstanceMethodProcessor_uses_super, JavaStatusContext.create(fMethod.getJavaScriptUnit(), node)));
fResult.add(node);
return false;
}
public final boolean visit(final SuperMethodInvocation node) {
Assert.isNotNull(node);
fStatus.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveInstanceMethodProcessor_uses_super, JavaStatusContext.create(fMethod.getJavaScriptUnit(), node)));
fResult.add(node);
return false;
}
public final boolean visit(final TypeDeclaration node) {
return false;
}
}
/**
* AST visitor to find references to 'this'.
*/
public final class ThisReferenceFinder extends AstNodeFinder {
public final boolean visit(final FunctionInvocation node) {
Assert.isNotNull(node);
final IFunctionBinding 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.getJavaScriptUnit(), node)));
}
return true;
}
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.getJavaScriptUnit(), node)));
}
return false;
}
public final boolean visit(final ThisExpression node) {
Assert.isNotNull(node);
fResult.add(node);
fStatus.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveInstanceMethodProcessor_this_reference, JavaStatusContext.create(fMethod.getJavaScriptUnit(), node)));
return false;
}
}
/**
* Argument factory which adjusts the visibilities of the argument types.
*/
public class VisibilityAdjustingArgumentFactory implements IArgumentFactory {
/** The visibility adjustments */
private final Map fAdjustments;
/** The ast to use for new nodes */
private final AST fAst;
/** The compilation unit rewrites */
private final Map 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 rewrites, final Map adjustments) {
Assert.isNotNull(ast);
Assert.isNotNull(rewrites);
Assert.isNotNull(adjustments);
fAst= ast;
fRewrites= rewrites;
fAdjustments= adjustments;
}
protected final void adjustTypeVisibility(final ITypeBinding binding) throws JavaScriptModelException {
Assert.isNotNull(binding);
final IJavaScriptElement 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.getJavaScriptUnit());
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(), JavaScriptElementLabels.ALL_FULLY_QUALIFIED), modifier }), JavaStatusContext.create(type.getJavaScriptUnit(), declaration))));
}
}
}
}
public ASTNode getArgumentNode(final IVariableBinding binding, final boolean last) throws JavaScriptModelException {
Assert.isNotNull(binding);
adjustTypeVisibility(binding.getType());
return fAst.newSimpleName(binding.getName());
}
public ASTNode getTargetNode() throws JavaScriptModelException {
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.wst.jsdt.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 FunctionDeclaration declaration) {
Assert.isNotNull(declaration);
final List parameters= new ArrayList(declaration.parameters().size());
VariableDeclaration variable= null;
IVariableBinding binding= null;
for (final Iterator iterator= declaration.parameters().iterator(); iterator.hasNext();) {
variable= (VariableDeclaration) iterator.next();
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 FunctionDeclaration declaration) {
Assert.isNotNull(declaration);
final IVariableBinding[] parameters= getArgumentBindings(declaration);
final List types= new ArrayList(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;
/** The comment */
private String fComment;
/** 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 IFunction 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 IFunction method, final CodeGenerationSettings settings) {
fSettings= settings;
fMethod= method;
if (method != null)
initialize(method);
}
/**
* {@inheritDoc}
*/
public boolean canEnableComment() {
return true;
}
/**
* {@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 JavaScriptModelException
* if the declared methods of the target type could not be
* retrieved
*/
protected void checkConflictingMethod(final IProgressMonitor monitor, final RefactoringStatus status) throws JavaScriptModelException {
Assert.isNotNull(monitor);
Assert.isNotNull(status);
final IFunction[] methods= fTargetType.getFunctions();
try {
monitor.beginTask("", methods.length); //$NON-NLS-1$
monitor.setTaskName(RefactoringCoreMessages.MoveInstanceMethodProcessor_checking);
IFunction method= null;
for (int index= 0; index < methods.length; index++) {
method= methods[index];
if (method.getElementName().equals(fMethodName) && method.getParameterTypes().length == fMethod.getParameterTypes().length - 1)
status.merge(RefactoringStatus.createErrorStatus(Messages.format(RefactoringCoreMessages.MoveInstanceMethodProcessor_method_already_exists, new String[] { fMethodName, fTargetType.getElementName() }), JavaStatusContext.create(method)));
monitor.worked(1);
}
if (fMethodName.equals(fTargetType.getElementName()))
status.merge(RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.MoveInstanceMethodProcessor_method_type_clash, 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 JavaScriptModelException
* if the method declaration of the method to move could not be
* found
*/
protected void checkConflictingTarget(final IProgressMonitor monitor, final RefactoringStatus status) throws JavaScriptModelException {
Assert.isNotNull(monitor);
Assert.isNotNull(status);
final FunctionDeclaration declaration= ASTNodeSearchUtil.getMethodDeclarationNode(fMethod, fSourceRewrite.getRoot());
VariableDeclaration variable= null;
final List parameters= declaration.parameters();
try {
monitor.beginTask("", parameters.size()); //$NON-NLS-1$
monitor.setTaskName(RefactoringCoreMessages.MoveInstanceMethodProcessor_checking);
for (final Iterator iterator= parameters.iterator(); iterator.hasNext();) {
variable= (VariableDeclaration) 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)
*/
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);
status.merge(Checks.validateModifiesFiles(computeModifiedFiles(fMethod.getJavaScriptUnit(), type.getJavaScriptUnit()), null));
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 FunctionDeclaration 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)
*/
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 FunctionDeclaration 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 FunctionDeclaration 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 IFunctionBinding 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 JavaScriptModelException
* if the method does not exist
*/
protected void checkMethodDeclaration(final IProgressMonitor monitor, final RefactoringStatus status) throws JavaScriptModelException {
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 FunctionDeclaration 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);
final RefactoringSearchEngine2 engine= new RefactoringSearchEngine2(SearchPattern.createPattern(fMethod, IJavaScriptSearchConstants.REFERENCES, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE));
engine.setStatus(status);
engine.searchPattern(new SubProgressMonitor(monitor, 1));
return (SearchResultGroup[]) engine.getResults();
} 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 IJavaScriptUnit source, final IJavaScriptUnit target) {
Assert.isNotNull(source);
Assert.isNotNull(target);
if (source.equals(target))
return ResourceUtil.getFiles(new IJavaScriptUnit[] { source });
return ResourceUtil.getFiles(new IJavaScriptUnit[] { source, target });
}
/**
* Returns the reserved identifiers in the method to move.
*
* @return the reserved identifiers
* @throws JavaScriptModelException
* if the method declaration could not be found
*/
protected String[] computeReservedIdentifiers() throws JavaScriptModelException {
final List names= new ArrayList();
final FunctionDeclaration declaration= ASTNodeSearchUtil.getMethodDeclarationNode(fMethod, fSourceRewrite.getRoot());
if (declaration != null) {
final List parameters= declaration.parameters();
VariableDeclaration variable= null;
for (int index= 0; index < parameters.size(); index++) {
variable= (VariableDeclaration) 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 FunctionDeclaration declaration) {
Assert.isNotNull(declaration);
if (fPossibleTargets.length == 0 || fCandidateTargets.length == 0) {
final List possibleTargets= new ArrayList(16);
final List candidateTargets= new ArrayList(16);
final IFunctionBinding 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 JavaScriptModelException
* if an error occurs while accessing the target expression
*/
protected Expression createAdjustedTargetExpression(final IJavaScriptElement enclosingElement, final Expression expression, final Map adjustments, final ASTRewrite rewrite) throws JavaScriptModelException {
Assert.isNotNull(enclosingElement);
Assert.isNotNull(adjustments);
Assert.isNotNull(rewrite);
final IJavaScriptElement element= fTarget.getJavaElement();
if (element != null && !Modifier.isPublic(fTarget.getModifiers())) {
final IField field= (IField) fTarget.getJavaElement();
if (field != null) {
boolean same= field.getAncestor(IJavaScriptElement.PACKAGE_FRAGMENT).equals(enclosingElement.getAncestor(IJavaScriptElement.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 IFunction getter= GetterSetterUtil.getGetter(field);
if (getter != null) {
final FunctionDeclaration method= ASTNodeSearchUtil.getMethodDeclarationNode(getter, fSourceRewrite.getRoot());
if (method != null) {
final IFunctionBinding 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, JavaScriptElementLabels.ALL_FULLY_QUALIFIED), modifier }), JavaStatusContext.create(getter))));
final FunctionInvocation invocation= rewrite.getAST().newFunctionInvocation();
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, JavaScriptElementLabels.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 JavaScriptModelException
* if an error occurs
*/
protected boolean createArgumentList(final FunctionDeclaration declaration, final List arguments, final IArgumentFactory factory) throws JavaScriptModelException {
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)
*/
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 list= new ArrayList(changes.length);
list.addAll(Arrays.asList(changes));
final Map arguments= new HashMap();
String project= null;
final IJavaScriptProject javaProject= fMethod.getJavaScriptProject();
if (javaProject != null)
project= javaProject.getElementName();
int flags= JavaScriptRefactoringDescriptor.JAR_REFACTORING | JavaScriptRefactoringDescriptor.JAR_SOURCE_ATTACHMENT | RefactoringDescriptor.STRUCTURAL_CHANGE | RefactoringDescriptor.MULTI_CHANGE;
final IType declaring= fMethod.getDeclaringType();
try {
if (declaring.isAnonymous() || declaring.isLocal())
flags|= JavaScriptRefactoringDescriptor.JAR_SOURCE_ATTACHMENT;
} catch (JavaScriptModelException exception) {
JavaScriptPlugin.log(exception);
}
final String description= Messages.format(RefactoringCoreMessages.MoveInstanceMethodProcessor_descriptor_description_short, fMethod.getElementName());
final String header= Messages.format(RefactoringCoreMessages.MoveInstanceMethodProcessor_descriptor_description, new String[] { JavaScriptElementLabels.getElementLabel(fMethod, JavaScriptElementLabels.ALL_FULLY_QUALIFIED), BindingLabelProvider.getBindingLabel(fTarget, JavaScriptElementLabels.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, JavaScriptElementLabels.ALL_FULLY_QUALIFIED)));
comment.addSetting(Messages.format(RefactoringCoreMessages.MoveInstanceMethodProcessor_method_name_pattern, getMethodName()));
if (needsTargetNode())
comment.addSetting(Messages.format(RefactoringCoreMessages.MoveInstanceMethodProcessor_parameter_name_pattern, getTargetName()));
final JDTRefactoringDescriptor descriptor= new JDTRefactoringDescriptor(IJavaScriptRefactorings.MOVE_METHOD, project, description, comment.asString(), arguments, flags);
arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_INPUT, descriptor.elementToHandle(fMethod));
arguments.put(JDTRefactoringDescriptor.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, (Change[]) 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 JavaScriptModelException
* 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 JavaScriptModelException, 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.getJavaScriptUnit().equals(getTargetType().getJavaScriptUnit()) ? fSourceRewrite : new CompilationUnitRewrite(getTargetType().getJavaScriptUnit());
final FunctionDeclaration declaration= ASTNodeSearchUtil.getMethodDeclarationNode(fMethod, fSourceRewrite.getRoot());
final SearchResultGroup[] references= computeMethodReferences(new SubProgressMonitor(monitor, 1), status);
final Map rewrites= new HashMap(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.getJavaScriptUnit().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) {
removable= createMethodDelegator(rewrites, declaration, references, adjustor.getAdjustments(), target, status, new SubProgressMonitor(monitor, 1));
if (fRemove && removable) {
fSourceRewrite.getASTRewrite().remove(declaration, fSourceRewrite.createGroupDescription(RefactoringCoreMessages.MoveInstanceMethodProcessor_remove_original_method));
if (!fSourceRewrite.getCu().equals(fTargetType.getJavaScriptUnit()))
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 IJavaScriptElement targetElement= fTarget.getJavaElement();
if (targetElement != null && targetElement instanceof IField && (Flags.isPrivate(fMethod.getFlags()) || !fInline)) {
final IVisibilityAdjustment adjustmentForTarget= (IVisibilityAdjustment) adjustor.getAdjustments().get(targetElement);
if (adjustmentForTarget != null)
adjustor.getAdjustments().remove(targetElement);
}
adjustor.rewriteVisibility(new SubProgressMonitor(monitor, 1));
sourceRewrite.rewriteAST(document, fMethod.getJavaScriptProject().getOptions(true));
createMethodSignature(document, declaration, sourceRewrite, rewrites);
IJavaScriptUnit unit= null;
CompilationUnitRewrite rewrite= null;
for (final Iterator iterator= rewrites.keySet().iterator(); iterator.hasNext();) {
unit= (IJavaScriptUnit) iterator.next();
rewrite= (CompilationUnitRewrite) rewrites.get(unit);
manager.manage(unit, rewrite.createChange());
}
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 JavaScriptModelException
* if a problem occurred while creating the inlined target
* expression for field targets
*/
protected boolean createInlinedMethodInvocation(final CompilationUnitRewrite rewriter, final FunctionDeclaration declaration, final SearchMatch match, final Map adjustments, final boolean target, final RefactoringStatus status) throws JavaScriptModelException {
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 FunctionInvocation) {
final FunctionInvocation invocation= (FunctionInvocation) node;
final ListRewrite list= rewrite.getListRewrite(invocation, FunctionInvocation.ARGUMENTS_PROPERTY);
if (fTarget.isField()) {
Expression access= null;
if (invocation.getExpression() != null) {
access= createInlinedTargetExpression(rewriter, (IJavaScriptElement) match.getElement(), invocation.getExpression(), adjustments, status);
rewrite.set(invocation, FunctionInvocation.EXPRESSION_PROPERTY, access, group);
} else
rewrite.set(invocation, FunctionInvocation.EXPRESSION_PROPERTY, rewrite.getAST().newSimpleName(fTarget.getName()), group);
if (target) {
if (access == null || !(access instanceof FieldAccess))
list.insertFirst(rewrite.getAST().newThisExpression(), null);
else
list.insertLast(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(), JavaScriptElementLabels.ALL_FULLY_QUALIFIED)), JavaStatusContext.create(rewriter.getCu(), invocation)));
result= false;
} else {
if (argument instanceof ThisExpression)
rewrite.remove(invocation.getExpression(), null);
else
rewrite.set(invocation, FunctionInvocation.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 IFunctionBinding 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, FunctionInvocation.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
* @throws JavaScriptModelException
* if a problem occurred while retrieving potential getter
* methods of the target
*/
protected Expression createInlinedTargetExpression(final CompilationUnitRewrite rewriter, final IJavaScriptElement enclosingElement, final Expression original, final Map adjustments, final RefactoringStatus status) throws JavaScriptModelException {
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 JavaScriptModelException
* if an error occurs while accessing the types of the arguments
*/
protected boolean createMethodArguments(final Map rewrites, final ASTRewrite rewrite, final FunctionDeclaration declaration, final Map adjustments, final RefactoringStatus status) throws JavaScriptModelException {
Assert.isNotNull(rewrites);
Assert.isNotNull(declaration);
Assert.isNotNull(rewrite);
Assert.isNotNull(adjustments);
Assert.isNotNull(status);
final CompilationUnitRewrite rewriter= getCompilationUnitRewrite(rewrites, getTargetType().getJavaScriptUnit());
final AST ast= rewriter.getRoot().getAST();
final AstNodeFinder finder= new AnonymousClassReferenceFinder(declaration);
declaration.accept(finder);
final List arguments= new ArrayList(declaration.parameters().size() + 1);
final boolean result= createArgumentList(declaration, arguments, new VisibilityAdjustingArgumentFactory(ast, rewrites, adjustments) {
public final ASTNode getArgumentNode(final IVariableBinding binding, final boolean last) throws JavaScriptModelException {
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 IFunctionBinding 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;
}
public final ASTNode getTargetNode() throws JavaScriptModelException {
final SingleVariableDeclaration variable= ast.newSingleVariableDeclaration();
final IFunctionBinding 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, FunctionDeclaration.PARAMETERS_PROPERTY);
ASTNode node= null;
for (final Iterator iterator= declaration.parameters().iterator(); iterator.hasNext();) {
node= (ASTNode) iterator.next();
list.remove(node, null);
}
for (final Iterator iterator= arguments.iterator(); iterator.hasNext();) {
node= (ASTNode) 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 FunctionDeclaration 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 JavaScriptModelException
* if the argument references could not be generated
*/
protected void createMethodComment(final ASTRewrite rewrite, final FunctionDeclaration declaration) throws JavaScriptModelException {
Assert.isNotNull(rewrite);
Assert.isNotNull(declaration);
final JSdoc comment= declaration.getJavadoc();
if (comment != null) {
final List tags= new LinkedList(comment.tags());
final IVariableBinding[] bindings= getArgumentBindings(declaration);
final Map elements= new HashMap(bindings.length);
String name= null;
List 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 iterator= comment.tags().iterator(); iterator.hasNext();) {
element= (TagElement) 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 iterator= comment.tags().iterator(); iterator.hasNext();) {
element= (TagElement) iterator.next();
name= element.getTagName();
fragments= element.fragments();
if (name != null && !name.equals(TagElement.TAG_PARAM))
reference= element;
}
}
final List arguments= new ArrayList(bindings.length + 1);
createArgumentList(declaration, arguments, new IArgumentFactory() {
public final ASTNode getArgumentNode(final IVariableBinding argument, final boolean last) throws JavaScriptModelException {
Assert.isNotNull(argument);
if (elements.containsKey(argument.getKey()))
return rewrite.createCopyTarget((ASTNode) elements.get(argument.getKey()));
return JavadocUtil.createParamTag(argument.getName(), declaration.getAST(), fMethod.getJavaScriptProject());
}
public final ASTNode getTargetNode() throws JavaScriptModelException {
return JavadocUtil.createParamTag(fTargetName, declaration.getAST(), fMethod.getJavaScriptProject());
}
});
final ListRewrite rewriter= rewrite.getListRewrite(comment, JSdoc.TAGS_PROPERTY);
ASTNode tag= null;
for (final Iterator iterator= comment.tags().iterator(); iterator.hasNext();) {
tag= (ASTNode) iterator.next();
if (!tags.contains(tag))
rewriter.remove(tag, null);
}
for (final Iterator iterator= arguments.iterator(); iterator.hasNext();) {
tag= (ASTNode) 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 FunctionDeclaration 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 IJavaScriptProject project= fMethod.getJavaScriptProject();
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 FunctionDeclaration declaration, final ASTRewrite rewrite, final Map rewrites, final Map 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().getJavaScriptUnit());
try {
rewrite.set(declaration, FunctionDeclaration.NAME_PROPERTY, rewrite.getAST().newSimpleName(fMethodName), null);
boolean same= false;
final IFunctionBinding 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.getJavaScriptUnit().equals(getTargetType().getJavaScriptUnit()))
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 FunctionDeclaration declaration, final Map rewrites, final Map 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 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 rewrites, final FunctionDeclaration declaration, final SearchResultGroup[] groups, final Map adjustments, final boolean target, 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;
IFunction 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(), JavaScriptElementLabels.ALL_FULLY_QUALIFIED)), JavaStatusContext.create(fMethod)));
result= false;
} else {
monitor.worked(1);
SearchMatch[] matches= null;
IJavaScriptElement element= null;
IJavaScriptUnit unit= null;
CompilationUnitRewrite rewrite= null;
SearchResultGroup group= null;
for (int index= 0; index < groups.length; index++) {
group= groups[index];
element= JavaScriptCore.create(group.getResource());
if (element instanceof IJavaScriptUnit) {
matches= group.getSearchResults();
unit= (IJavaScriptUnit) 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, unit.getCorrespondingResource().getName()), JavaStatusContext.create(unit, new SourceRange(match.getOffset(), match.getLength()))));
result= false;
} else if (!createInlinedMethodInvocation(rewrite, declaration, match, adjustments, target, status))
result= false;
}
} else if (element != null) {
status.merge(RefactoringStatus.createWarningStatus(Messages.format(RefactoringCoreMessages.MoveInstanceMethodProcessor_inline_binary_project, element.getJavaScriptProject().getElementName())));
result= false;
} else {
status.merge(RefactoringStatus.createWarningStatus(Messages.format(RefactoringCoreMessages.MoveInstanceMethodProcessor_inline_binary_resource, group.getResource().getName())));
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 FunctionDeclaration 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, declaration, new HashMap(), new HashMap(), 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 FunctionDeclaration 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 FunctionRef) {
final AST ast= node.getAST();
final FunctionRef successor= ast.newFunctionRef();
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 rewrites, final FunctionDeclaration 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);
try {
SearchMatch[] matches= null;
IJavaScriptElement element= null;
IJavaScriptUnit unit= null;
CompilationUnitRewrite rewrite= null;
SearchResultGroup group= null;
for (int index= 0; index < groups.length; index++) {
group= groups[index];
element= JavaScriptCore.create(group.getResource());
unit= group.getCompilationUnit();
if (element instanceof IJavaScriptUnit) {
matches= group.getSearchResults();
unit= (IJavaScriptUnit) 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, unit.getCorrespondingResource().getName()), JavaStatusContext.create(unit, new SourceRange(match.getOffset(), match.getLength()))));
} else
createMethodJavadocReference(rewrite, declaration, match, target, status);
}
} else if (element != null) {
status.merge(RefactoringStatus.createWarningStatus(Messages.format(RefactoringCoreMessages.MoveInstanceMethodProcessor_javadoc_binary_project, element.getJavaScriptProject().getElementName())));
} else {
status.merge(RefactoringStatus.createWarningStatus(Messages.format(RefactoringCoreMessages.MoveInstanceMethodProcessor_javadoc_binary_resource, group.getResource().getName())));
}
monitor.worked(1);
}
} catch (CoreException exception) {
status.merge(RefactoringStatus.create(exception.getStatus()));
}
} 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 JavaScriptModelException
* if an error occurs
*/
protected ASTNode createMethodReference(final FunctionDeclaration declaration, final AST ast) throws JavaScriptModelException {
Assert.isNotNull(ast);
Assert.isNotNull(declaration);
final FunctionRef reference= ast.newFunctionRef();
reference.setName(ast.newSimpleName(fMethodName));
reference.setQualifier(ASTNodeFactory.newName(ast, JavaModelUtil.getFullyQualifiedName(fTargetType)));
createArgumentList(declaration, reference.parameters(), new IArgumentFactory() {
public final ASTNode getArgumentNode(final IVariableBinding binding, final boolean last) {
Assert.isNotNull(binding);
final FunctionRefParameter parameter= ast.newFunctionRefParameter();
parameter.setType(ASTNodeFactory.newType(ast, binding.getType().getName()));
return parameter;
}
public final ASTNode getTargetNode() {
final FunctionRefParameter parameter= ast.newFunctionRefParameter();
final IFunctionBinding 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 JavaScriptModelException
* if the insertion point cannot be found
*/
protected void createMethodSignature(final IDocument document, final FunctionDeclaration declaration, final ASTRewrite rewrite, final Map rewrites) throws JavaScriptModelException {
Assert.isNotNull(document);
Assert.isNotNull(declaration);
Assert.isNotNull(rewrite);
Assert.isNotNull(rewrites);
try {
final CompilationUnitRewrite rewriter= getCompilationUnitRewrite(rewrites, getTargetType().getJavaScriptUnit());
final FunctionDeclaration stub= (FunctionDeclaration) rewriter.getASTRewrite().createStringPlaceholder(createMethodContent(document, declaration, rewrite), ASTNode.FUNCTION_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) {
JavaScriptPlugin.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 FunctionDeclaration declaration, final RefactoringStatus status) {
ITypeBinding binding= fTarget.getType();
if (binding != null && binding.isParameterizedType()) {
final IFunctionBinding method= declaration.resolveBinding();
if (method != null) {
final ITypeBinding[] parameters= method.getTypeParameters();
if (parameters.length > 0) {
final ListRewrite rewriter= rewrite.getListRewrite(declaration, FunctionDeclaration.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[] { parameters[offset].getName(), BindingLabelProvider.getBindingLabel(binding, JavaScriptElementLabels.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 FunctionDeclaration 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;
}
/**
* {@inheritDoc}
*/
public String getComment() {
return fComment;
}
/**
* 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 rewrites, final IJavaScriptUnit unit) {
Assert.isNotNull(rewrites);
Assert.isNotNull(unit);
CompilationUnitRewrite rewrite= (CompilationUnitRewrite) 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}
*/
public final Object[] getElements() {
return new Object[] { fMethod };
}
/**
* {@inheritDoc}
*/
public final String getIdentifier() {
return IDENTIFIER;
}
/**
* Returns the method to be moved.
*
* @return the method to be moved
*/
public final IFunction 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()
*/
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 JavaScriptModelException
* if the type does not exist
*/
protected IType getTargetType() throws JavaScriptModelException {
Assert.isNotNull(fTarget);
if (fTargetType == null) {
final ITypeBinding binding= fTarget.getType();
if (binding != null)
fTargetType= (IType) binding.getJavaElement();
else
throw new JavaScriptModelException(new CoreException(new Status(IStatus.ERROR, JavaScriptPlugin.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 IFunction method) {
Assert.isNotNull(method);
fSourceRewrite= new CompilationUnitRewrite(fMethod.getJavaScriptUnit());
fMethodName= method.getElementName();
fTargetName= suggestTargetName();
if (fSettings == null)
fSettings= JavaPreferencesSettings.getCodeGenerationSettings(fMethod.getJavaScriptProject());
}
/**
* {@inheritDoc}
*/
public RefactoringStatus initialize(final RefactoringArguments arguments) {
if (arguments instanceof JavaRefactoringArguments) {
final JavaRefactoringArguments extended= (JavaRefactoringArguments) arguments;
final String handle= extended.getAttribute(JDTRefactoringDescriptor.ATTRIBUTE_INPUT);
if (handle != null) {
final IJavaScriptElement element= JDTRefactoringDescriptor.handleToElement(extended.getProject(), handle, false);
if (element == null || !element.exists() || element.getElementType() != IJavaScriptElement.METHOD)
return ScriptableRefactoring.createInputFatalStatus(element, getRefactoring().getName(), IJavaScriptRefactorings.MOVE_METHOD);
else {
fMethod= (IFunction) element;
initialize(fMethod);
}
} else
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_INPUT));
final String name= extended.getAttribute(JDTRefactoringDescriptor.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, JDTRefactoringDescriptor.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 FunctionDeclaration 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 (JavaScriptModelException 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));
} else
return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments);
return new RefactoringStatus();
}
/*
* @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#isApplicable()
*/
public final boolean isApplicable() throws CoreException {
return fMethod.exists() && !fMethod.isConstructor() && !fMethod.isBinary() && !fMethod.isReadOnly() && fMethod.getJavaScriptUnit() != 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)
*/
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 void setComment(final String comment) {
fComment= comment;
}
/**
* {@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);
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 FunctionDeclaration declaration= ASTNodeSearchUtil.getMethodDeclarationNode(fMethod, fSourceRewrite.getRoot());
if (declaration != null) {
final AstNodeFinder finder= new ThisReferenceFinder();
declaration.accept(finder);
fTargetNode= !finder.getResult().isEmpty();
return;
}
} catch (JavaScriptModelException exception) {
JavaScriptPlugin.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);
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(),fMethod.getJavaScriptUnit(), computeReservedIdentifiers());
if (candidates.length > 0) {
if (candidates[0].indexOf('$') < 0)
return candidates[0];
}
} catch (JavaScriptModelException exception) {
JavaScriptPlugin.log(exception);
}
return "arg"; //$NON-NLS-1$
}
}