/******************************************************************************* * Copyright (c) 2000, 2009 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.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); } /* * @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(); 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 (!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) 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("", 2); //$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 (fMethod.isConstructor()) status.merge(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.MoveInstanceMethodProcessor_no_constructors, 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.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, Integer.valueOf(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); 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.getAllSuperclasses(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 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$ } }