blob: e9c18ae31da690aab1318990546f934c20cb995d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2010 IBM Corporation and others.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
* Benjamin Muskalla <bmuskalla@eclipsesource.com> - [extract method] Does not replace similar code in parent class of anonymous class - https://bugs.eclipse.org/bugs/show_bug.cgi?id=160853
* Benjamin Muskalla <bmuskalla@eclipsesource.com> - [extract method] Extract method and continue https://bugs.eclipse.org/bugs/show_bug.cgi?id=48056
* Benjamin Muskalla <bmuskalla@eclipsesource.com> - [extract method] should declare method static if extracted from anonymous in static method - https://bugs.eclipse.org/bugs/show_bug.cgi?id=152004
*******************************************************************************/
package org.eclipse.dltk.internal.javascript.corext.refactoring.code;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.dltk.core.IScriptProject;
import org.eclipse.dltk.core.ISourceModule;
import org.eclipse.dltk.core.manipulation.RefactoringChecks;
import org.eclipse.dltk.core.manipulation.SourceModuleChange;
import org.eclipse.dltk.internal.corext.refactoring.ScriptRefactoringDescriptor;
import org.eclipse.dltk.internal.corext.refactoring.util.ResourceUtil;
import org.eclipse.dltk.internal.javascript.core.manipulation.Messages;
import org.eclipse.dltk.internal.javascript.corext.refactoring.Checks;
import org.eclipse.dltk.internal.javascript.corext.refactoring.ParameterInfo;
import org.eclipse.dltk.internal.javascript.corext.refactoring.RefactoringCoreMessages;
import org.eclipse.dltk.internal.javascript.corext.refactoring.code.flow.VariableBinding;
import org.eclipse.dltk.internal.javascript.corext.refactoring.util.Selection;
import org.eclipse.dltk.javascript.core.dom.BinaryExpression;
import org.eclipse.dltk.javascript.core.dom.BinaryOperator;
import org.eclipse.dltk.javascript.core.dom.BlockStatement;
import org.eclipse.dltk.javascript.core.dom.CallExpression;
import org.eclipse.dltk.javascript.core.dom.DomFactory;
import org.eclipse.dltk.javascript.core.dom.DomPackage;
import org.eclipse.dltk.javascript.core.dom.Expression;
import org.eclipse.dltk.javascript.core.dom.ExpressionStatement;
import org.eclipse.dltk.javascript.core.dom.FunctionExpression;
import org.eclipse.dltk.javascript.core.dom.Identifier;
import org.eclipse.dltk.javascript.core.dom.Node;
import org.eclipse.dltk.javascript.core.dom.Parameter;
import org.eclipse.dltk.javascript.core.dom.ReturnStatement;
import org.eclipse.dltk.javascript.core.dom.Source;
import org.eclipse.dltk.javascript.core.dom.Statement;
import org.eclipse.dltk.javascript.core.dom.VariableDeclaration;
import org.eclipse.dltk.javascript.core.dom.VariableReference;
import org.eclipse.dltk.javascript.core.dom.VariableStatement;
import org.eclipse.dltk.javascript.core.dom.rewrite.ASTConverter;
import org.eclipse.dltk.javascript.core.dom.rewrite.Generator;
import org.eclipse.dltk.javascript.core.dom.rewrite.RewriteAnalyzer;
import org.eclipse.dltk.javascript.core.dom.rewrite.VariableLookup;
import org.eclipse.dltk.javascript.core.refactoring.descriptors.ExtractMethodDescriptor;
import org.eclipse.dltk.javascript.parser.JavaScriptParserUtil;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.change.ChangeDescription;
import org.eclipse.emf.ecore.change.util.ChangeRecorder;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.Refactoring;
import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor;
import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.TextFileChange;
import org.eclipse.ltk.core.refactoring.participants.ResourceChangeChecker;
import org.eclipse.text.edits.InsertEdit;
import org.eclipse.text.edits.TextEdit;
/**
* Extracts a method in a compilation unit based on a text selection range.
*/
public class ExtractMethodRefactoring extends Refactoring {
//private static final String ATTRIBUTE_VISIBILITY= "visibility"; //$NON-NLS-1$
private static final String ATTRIBUTE_DESTINATION= "destination"; //$NON-NLS-1$
/*private static final String ATTRIBUTE_COMMENTS= "comments"; //$NON-NLS-1$
private static final String ATTRIBUTE_REPLACE= "replace"; //$NON-NLS-1$
private static final String ATTRIBUTE_EXCEPTIONS= "exceptions"; //$NON-NLS-1$*/
private ISourceModule fCUnit;
private String fSource;
private Source fRoot;
//private ImportRewrite fImportRewriter;
private int fSelectionStart;
private int fSelectionLength;
/*private AST fAST;
private ASTRewrite fRewriter;*/
private ExtractMethodAnalyzer fAnalyzer;
//private int fVisibility;
private String fMethodName;
//private boolean fThrowRuntimeExceptions;
private List<ParameterInfo> fParameterInfos;
private Set<String> fUsedNames;
/*private boolean fGenerateScriptdoc;
private boolean fReplaceDuplicates;
private SnippetFinder.Match[] fDuplicates;*/
private int fDestinationIndex= 0;
private Node fDestination;
private Node[] fDestinations;
//private LinkedProposalModel fLinkedProposalModel;
private static final String EMPTY= ""; //$NON-NLS-1$
/*private static final String KEY_TYPE= "type"; //$NON-NLS-1$
private static final String KEY_NAME= "name"; //$NON-NLS-1$
private static class UsedNamesCollector extends ASTVisitor {
private Set result= new HashSet();
private Set fIgnore= new HashSet();
public static Set perform(ASTNode[] nodes) {
UsedNamesCollector collector= new UsedNamesCollector();
for (int i= 0; i < nodes.length; i++) {
nodes[i].accept(collector);
}
return collector.result;
}
public boolean visit(FieldAccess node) {
Expression exp= node.getExpression();
if (exp != null)
fIgnore.add(node.getName());
return true;
}
public void endVisit(FieldAccess node) {
fIgnore.remove(node.getName());
}
public boolean visit(MethodInvocation node) {
Expression exp= node.getExpression();
if (exp != null)
fIgnore.add(node.getName());
return true;
}
public void endVisit(MethodInvocation node) {
fIgnore.remove(node.getName());
}
public boolean visit(QualifiedName node) {
fIgnore.add(node.getName());
return true;
}
public void endVisit(QualifiedName node) {
fIgnore.remove(node.getName());
}
public boolean visit(SimpleName node) {
if (!fIgnore.contains(node))
result.add(node.getIdentifier());
return true;
}
public boolean visit(TypeDeclaration node) {
return visitType(node);
}
public boolean visit(AnnotationTypeDeclaration node) {
return visitType(node);
}
public boolean visit(EnumDeclaration node) {
return visitType(node);
}
private boolean visitType(AbstractTypeDeclaration node) {
result.add(node.getName().getIdentifier());
// don't dive into type declaration since they open a new
// context.
return false;
}
}
/**
* Creates a new extract method refactoring
* @param unit the compilation unit, or <code>null</code> if invoked by scripting
* @param selectionStart selection start
* @param selectionLength selection end
*/
public ExtractMethodRefactoring(ISourceModule unit, int selectionStart, int selectionLength) {
fCUnit= unit;
fRoot= null;
fMethodName= "extracted"; //$NON-NLS-1$
fSelectionStart= selectionStart;
fSelectionLength= selectionLength;
//fVisibility= -1;
}
/*public ExtractMethodRefactoring(ScriptRefactoringArguments arguments, RefactoringStatus status) {
this((ISourceModule) null, 0, 0);
RefactoringStatus initializeStatus= initialize(arguments);
status.merge(initializeStatus);
}
/**
* Creates a new extract method refactoring
* @param astRoot the AST root of an AST created from a compilation unit
* @param selectionStart start
* @param selectionLength length
*
public ExtractMethodRefactoring(Source astRoot, int selectionStart, int selectionLength) {
this((ISourceModule) astRoot.getTypeRoot(), selectionStart, selectionLength);
fRoot= astRoot;
}
public void setLinkedProposalModel(LinkedProposalModel linkedProposalModel) {
fLinkedProposalModel= linkedProposalModel;
}*/
public String getName() {
return RefactoringCoreMessages.ExtractMethodRefactoring_name;
}
/**
* Checks if the refactoring can be activated. Activation typically means, if a
* corresponding menu entry can be added to the UI.
*
* @param pm a progress monitor to report progress during activation checking.
* @return the refactoring status describing the result of the activation check.
* @throws CoreException if checking fails
*/
public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException {
RefactoringStatus result= new RefactoringStatus();
pm.beginTask("", 100); //$NON-NLS-1$
if (fSelectionStart < 0 || fSelectionLength == 0) {
result.addFatalError(RefactoringCoreMessages.ExtractMethodRefactoring_no_set_of_statements);
return result;
}
IFile[] changedFiles= ResourceUtil.getFiles(new ISourceModule[]{fCUnit});
result.merge(RefactoringChecks.validateModifiesFiles(changedFiles, getValidationContext()));
if (result.hasFatalError())
return result;
result.merge(ResourceChangeChecker.checkFilesToBeChanged(changedFiles, new SubProgressMonitor(pm, 1)));
if (fRoot == null) {
fRoot = (Source)ASTConverter.convert(JavaScriptParserUtil.parse(fCUnit));
}
//fImportRewriter= StubUtility.createImportRewrite(fRoot, true);
//fAST= fRoot.getAST();
fAnalyzer= new ExtractMethodAnalyzer(fCUnit, Selection.createFromStartLength(fSelectionStart, fSelectionLength));
//fRoot.accept(fAnalyzer);
result.merge(fAnalyzer.checkInitialConditions(fRoot));
if (result.hasFatalError())
return result;
fSource = fCUnit.getSource();
Node[] nodes = fAnalyzer.getSelectedNodes();
boolean badSelection = false;
for(int i=fSelectionStart;i<nodes[0].getBegin();i++)
if (!Character.isWhitespace(fSource.charAt(i))) badSelection = true;
for(int i=nodes[nodes.length-1].getEnd();i<fSelectionStart+fSelectionLength;i++)
if (!Character.isWhitespace(fSource.charAt(i))) badSelection = true;
if (badSelection) {
result.addFatalError(RefactoringCoreMessages.StatementAnalyzer_doesNotCover);
return result;
}
/*if (fVisibility == -1) {
setVisibility(Modifier.PRIVATE);
}*/
initializeParameterInfos();
initializeUsedNames();
//initializeDuplicates();
initializeDestinations();
return result;
}
/**
* Sets the method name to be used for the extracted method.
*
* @param name the new method name.
*/
public void setMethodName(String name) {
fMethodName= name;
}
/**
* Returns the method name to be used for the extracted method.
* @return the method name to be used for the extracted method.
*
public String getMethodName() {
return fMethodName;
}
/**
* Sets the visibility of the new method.
*
* @param visibility the visibility of the new method. Valid values are
* "public", "protected", "", and "private"
*
public void setVisibility(int visibility) {
fVisibility= visibility;
}
/**
* Returns the visibility of the new method.
*
* @return the visibility of the new method
*
public int getVisibility() {
return fVisibility;
}
/**
* Returns the parameter infos.
* @return a list of parameter infos.
*/
public List<ParameterInfo> getParameterInfos() {
return fParameterInfos;
}
/**
* Sets whether the new method signature throws runtime exceptions.
*
* @param throwRuntimeExceptions flag indicating if the new method
* throws runtime exceptions
*
public void setThrowRuntimeExceptions(boolean throwRuntimeExceptions) {
fThrowRuntimeExceptions= throwRuntimeExceptions;
}
/**
* Checks if the new method name is a valid method name. This method doesn't
* check if a method with the same name already exists in the hierarchy. This
* check is done in <code>checkInput</code> since it is expensive.
* @return validation status
*/
public RefactoringStatus checkMethodName() {
return Checks.validateIdentifier(fMethodName);
}
public Node[] getDestinations() {
return fDestinations;
}
public void setDestination(int index) {
fDestination= fDestinations[index];
fDestinationIndex= index;
}
/**
* Checks if the parameter names are valid.
* @return validation status
*/
public RefactoringStatus checkParameterNames() {
RefactoringStatus result= new RefactoringStatus();
for (ParameterInfo parameter : fParameterInfos) {
result.merge(Checks.validateIdentifier(parameter.getNewName()));
for (ParameterInfo other : fParameterInfos) {
if (parameter != other && other.getNewName().equals(parameter.getNewName())) {
result.addError(Messages.format(
RefactoringCoreMessages.ExtractMethodRefactoring_error_sameParameter,
//BasicElementLabels.getScriptElementName(other.getNewName())
other.getNewName()));
return result;
}
}
if (parameter.isRenamed() && fUsedNames.contains(parameter.getNewName())) {
result.addError(Messages.format(
RefactoringCoreMessages.ExtractMethodRefactoring_error_nameInUse,
//BasicElementLabels.getScriptElementName(parameter.getNewName())
parameter.getNewName()));
return result;
}
}
return result;
}
/**
* Checks if varargs are ordered correctly.
* @return validation status
*
public RefactoringStatus checkVarargOrder() {
for (Iterator iter= fParameterInfos.iterator(); iter.hasNext();) {
ParameterInfo info= (ParameterInfo)iter.next();
if (info.isOldVarargs() && iter.hasNext()) {
return RefactoringStatus.createFatalErrorStatus(Messages.format(
RefactoringCoreMessages.ExtractMethodRefactoring_error_vararg_ordering,
BasicElementLabels.getScriptElementName(info.getOldName())));
}
}
return new RefactoringStatus();
}
/**
* Returns the names already in use in the selected statements/expressions.
*
* @return names already in use.
*
public Set getUsedNames() {
return fUsedNames;
}
/* (non-Scriptdoc)
* Method declared in Refactoring
*/
public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException {
pm.beginTask(RefactoringCoreMessages.ExtractMethodRefactoring_checking_new_name, 1);
pm.subTask(EMPTY);
RefactoringStatus result= checkMethodName();
result.merge(checkParameterNames());
/*result.merge(checkVarargOrder());
pm.worked(1);
if (pm.isCanceled())
throw new OperationCanceledException();
Node node= fAnalyzer.getEnclosingNode();
if (node != null) {
fAnalyzer.checkInput(result, fMethodName);
pm.worked(1);
}*/
pm.done();
return result;
}
/* (non-Scriptdoc)
* Method declared in IRefactoring
*/
public Change createChange(IProgressMonitor pm) throws CoreException {
if (fMethodName == null)
return null;
pm.beginTask("", 2); //$NON-NLS-1$
try {
ChangeRecorder cr = new ChangeRecorder(fRoot);
Node[] nodes = fAnalyzer.getSelectedNodes();
boolean isExpr = fAnalyzer.isExpressionSelected();
// replace method parameter names
Map<String,String> renamings = new HashMap<String,String>();
for (ParameterInfo parameter : fParameterInfos)
if (parameter.isRenamed())
renamings.put(parameter.getOldName(),parameter.getNewName());
for (Node node : nodes) {
List<Identifier> oldNames = VariableLookup.findReferences(node, renamings.keySet());
for (Identifier ref : oldNames) {
ref.setName(renamings.get(ref.getName()));
}
}
// create call
CallExpression invocation= DomFactory.eINSTANCE.createCallExpression();
{
VariableReference ref = DomFactory.eINSTANCE.createVariableReference();
Identifier id = DomFactory.eINSTANCE.createIdentifier();
id.setName(fMethodName);
ref.setVariable(id);
invocation.setApplicant(ref);
}
for (ParameterInfo parameter : fParameterInfos) {
Identifier name = DomFactory.eINSTANCE.createIdentifier();
name.setName(parameter.getOldName());
VariableReference local = DomFactory.eINSTANCE.createVariableReference();
local.setVariable(name);
invocation.getArguments().add(local);
}
if (isExpr) {
// replace with call
EReference ref = nodes[0].eContainmentFeature();
if (ref.isMany()) {
EList<Expression> exprList = (EList<Expression>)nodes[0].eContainer().eGet(ref);
exprList.set(exprList.lastIndexOf(nodes[0]), (Expression)invocation);
} else {
nodes[0].eContainer().eSet(ref, invocation);
}
} else {
// create replacement
Node call;
int returnKind= fAnalyzer.getReturnKind();
switch (returnKind) {
case ExtractMethodAnalyzer.ACCESS_TO_LOCAL:
VariableBinding binding= fAnalyzer.getReturnLocal();
if (binding != null) {
call = createDeclaration(binding, invocation);
} else {
BinaryExpression assignment= DomFactory.eINSTANCE.createBinaryExpression();
assignment.setOperation(BinaryOperator.ASSIGN);
VariableReference retVar = DomFactory.eINSTANCE.createVariableReference();
Identifier id = DomFactory.eINSTANCE.createIdentifier();
id.setName(fAnalyzer.getReturnValue().getName());
retVar.setVariable(id);
assignment.setLeft(retVar);
assignment.setRight(invocation);
call = assignment;
}
break;
case ExtractMethodAnalyzer.RETURN_STATEMENT_VALUE:
ReturnStatement rs=DomFactory.eINSTANCE.createReturnStatement();
rs.setExpression(invocation);
call = rs;
break;
default:
call = invocation;
}
if (call instanceof Expression) {
ExpressionStatement stmt = DomFactory.eINSTANCE.createExpressionStatement();
stmt.setExpression((Expression)call);
call = stmt;
}
List<Statement> callNodes= new ArrayList<Statement>(2);
callNodes.add((Statement)call);
if (returnKind == ExtractMethodAnalyzer.RETURN_STATEMENT_VOID && !fAnalyzer.isLastStatementSelected()) {
callNodes.add(DomFactory.eINSTANCE.createReturnStatement());
}
for (VariableBinding local : fAnalyzer.getCallerLocals())
callNodes.add(createDeclaration(local, null));
// replace with call
EReference ref = nodes[0].eContainmentFeature();
if (ref.isMany()) {
EList<Statement> list = (EList<Statement>)nodes[0].eContainer().eGet(ref);
int index = list.lastIndexOf(nodes[0]);
for(int i=nodes.length-1;i>=0;i--)
list.remove(index+i);
list.addAll(index, callNodes);
} else if (callNodes.size() == 1) {
nodes[0].eContainer().eSet(ref, callNodes.get(0));
} else {
BlockStatement block = DomFactory.eINSTANCE.createBlockStatement();
block.getStatements().addAll(callNodes);
nodes[0].eContainer().eSet(ref, block);
}
}
// create declaration
FunctionExpression mm = createNewMethodDeclaration();
List<Statement> statements = mm.getBody().getStatements();
VariableBinding[] methodLocals= fAnalyzer.getMethodLocals();
for (int i= 0; i < methodLocals.length; i++) {
if (methodLocals[i] != null)
statements.add(createDeclaration(methodLocals[i],null));
}
if (isExpr) {
ReturnStatement rs = DomFactory.eINSTANCE.createReturnStatement();
rs.setExpression((Expression)nodes[0]);
statements.add(rs);
} else {
for(Node node : nodes)
statements.add((Statement)node);
VariableBinding returnValue= fAnalyzer.getReturnValue();
if (returnValue != null) {
ReturnStatement rs = DomFactory.eINSTANCE.createReturnStatement();
VariableReference ret = DomFactory.eINSTANCE.createVariableReference();
Identifier rid = DomFactory.eINSTANCE.createIdentifier();
String name = returnValue.getName();
if (renamings.containsKey(name))
name = renamings.get(name);
rid.setName(name);
ret.setVariable(rid);
rs.setExpression(ret);
statements.add(rs);
}
}
// add declaration
final ExpressionStatement stmt = DomFactory.eINSTANCE.createExpressionStatement();
stmt.setExpression(mm);
Node enclosing = fDestination;
EReference ref = enclosing.eContainmentFeature();
assert ref.isMany();
EList<Statement> list = (EList<Statement>)enclosing.eContainer().eGet(ref);
list.add(list.lastIndexOf(enclosing)+1,stmt);
// TODO replace branches
ChangeDescription cd = cr.endRecording();
RewriteAnalyzer ra = new RewriteAnalyzer(cd, fSource) {
@Override
protected void addEdit(TextEdit edit, Node node) {
if (node == stmt) {
edit = new InsertEdit(edit.getOffset(),
lineDelimiter+((InsertEdit)edit).getText());
}
super.addEdit(edit, node);
}
};
SourceModuleChange result= new SourceModuleChange(RefactoringCoreMessages.ExtractMethodRefactoring_change_name, fCUnit);
ra.rewrite(fRoot);
result.setSaveMode(TextFileChange.KEEP_SAVE_STATE);
result.setDescriptor(new RefactoringChangeDescriptor(getRefactoringDescriptor()));
result.setEdit(ra.getEdit());
cd.apply();
return result;
} finally {
pm.done();
}
}
/*private void replaceBranches(final SourceModuleChange result) {
ASTNode[] selectedNodes= fAnalyzer.getSelectedNodes();
for (int i= 0; i < selectedNodes.length; i++) {
ASTNode astNode= selectedNodes[i];
astNode.accept(new ASTVisitor() {
private LinkedList fOpenLoopLabels= new LinkedList();
private void registerLoopLabel(Statement node) {
String identifier;
if (node.getParent() instanceof LabeledStatement) {
LabeledStatement labeledStatement= (LabeledStatement)node.getParent();
identifier= labeledStatement.getLabel().getIdentifier();
} else {
identifier= null;
}
fOpenLoopLabels.add(identifier);
}
public boolean visit(ForStatement node) {
registerLoopLabel(node);
return super.visit(node);
}
public void endVisit(ForStatement node) {
fOpenLoopLabels.removeLast();
}
public boolean visit(WhileStatement node) {
registerLoopLabel(node);
return super.visit(node);
}
public void endVisit(WhileStatement node) {
fOpenLoopLabels.removeLast();
}
public boolean visit(EnhancedForStatement node) {
registerLoopLabel(node);
return super.visit(node);
}
public void endVisit(EnhancedForStatement node) {
fOpenLoopLabels.removeLast();
}
public boolean visit(DoStatement node) {
registerLoopLabel(node);
return super.visit(node);
}
public void endVisit(DoStatement node) {
fOpenLoopLabels.removeLast();
}
public void endVisit(ContinueStatement node) {
final SimpleName label= node.getLabel();
if (fOpenLoopLabels.isEmpty() || (label != null && !fOpenLoopLabels.contains(label.getIdentifier()))) {
TextEditGroup description= new TextEditGroup(RefactoringCoreMessages.ExtractMethodRefactoring_replace_continue);
result.addTextEditGroup(description);
ReturnStatement rs= fAST.newReturnStatement();
IVariableBinding returnValue= fAnalyzer.getReturnValue();
if (returnValue != null) {
rs.setExpression(fAST.newSimpleName(getName(returnValue)));
}
fRewriter.replace(node, rs, description);
}
}
});
}
}*/
private ExtractMethodDescriptor getRefactoringDescriptor() {
final Map arguments= new HashMap();
String project= null;
IScriptProject ScriptProject= fCUnit.getScriptProject();
if (ScriptProject != null)
project= ScriptProject.getElementName();
/*ITypeBinding type= null;
if (fDestination instanceof AbstractTypeDeclaration) {
final AbstractTypeDeclaration decl= (AbstractTypeDeclaration) fDestination;
type= decl.resolveBinding();
} else if (fDestination instanceof AnonymousClassDeclaration) {
final AnonymousClassDeclaration decl= (AnonymousClassDeclaration) fDestination;
type= decl.resolveBinding();
}
IMethodBinding method= null;
final Node enclosing= fAnalyzer.getEnclosingNode();
if (enclosing instanceof MethodDeclaration) {
final MethodDeclaration node= (MethodDeclaration) enclosing;
method= node.resolveBinding();
}*/
final int flags= RefactoringDescriptor.STRUCTURAL_CHANGE | ScriptRefactoringDescriptor.ARCHIVE_REFACTORABLE | ScriptRefactoringDescriptor.ARCHIVE_IMPORTABLE;
final String description= Messages.format(RefactoringCoreMessages.ExtractMethodRefactoring_descriptor_description_short, fMethodName);
//final String label= method != null ? BindingLabelProvider.getBindingLabel(method, ScriptElementLabels.ALL_FULLY_QUALIFIED) : '{' + ScriptElementLabels.ELLIPSIS_STRING + '}';
//final String header= Messages.format(RefactoringCoreMessages.ExtractMethodRefactoring_descriptor_description, new String[] { BasicElementLabels.getScriptElementName(getSignature()), label, BindingLabelProvider.getBindingLabel(type, ScriptElementLabels.ALL_FULLY_QUALIFIED)});
//final ScriptRefactoringDescriptorComment comment= new ScriptRefactoringDescriptorComment(project, this, header);
//comment.addSetting(Messages.format(RefactoringCoreMessages.ExtractMethodRefactoring_name_pattern, BasicElementLabels.getScriptElementName(fMethodName)));
//comment.addSetting(Messages.format(RefactoringCoreMessages.ExtractMethodRefactoring_destination_pattern, BindingLabelProvider.getBindingLabel(type, ScriptElementLabels.ALL_FULLY_QUALIFIED)));
//String visibility= JdtFlags.getVisibilityString(fVisibility);
//if ("".equals(visibility)) //$NON-NLS-1$
// visibility= RefactoringCoreMessages.ExtractMethodRefactoring_default_visibility;
//comment.addSetting(Messages.format(RefactoringCoreMessages.ExtractMethodRefactoring_visibility_pattern, visibility));
//if (fThrowRuntimeExceptions)
// comment.addSetting(RefactoringCoreMessages.ExtractMethodRefactoring_declare_thrown_exceptions);
//if (fReplaceDuplicates)
// comment.addSetting(RefactoringCoreMessages.ExtractMethodRefactoring_replace_occurrences);
//if (fGenerateScriptdoc)
// comment.addSetting(RefactoringCoreMessages.ExtractMethodRefactoring_generate_comment);
final ExtractMethodDescriptor descriptor= new ExtractMethodDescriptor(project, description, ""/*comment.asString()*/, arguments, flags);
arguments.put(ScriptRefactoringDescriptor.ATTRIBUTE_INPUT, ScriptRefactoringDescriptor.elementToHandle(project, fCUnit));
arguments.put(ScriptRefactoringDescriptor.ATTRIBUTE_NAME, fMethodName);
arguments.put(ScriptRefactoringDescriptor.ATTRIBUTE_SELECTION, new Integer(fSelectionStart).toString() + " " + new Integer(fSelectionLength).toString()); //$NON-NLS-1$
//arguments.put(ATTRIBUTE_VISIBILITY, new Integer(fVisibility).toString());
arguments.put(ATTRIBUTE_DESTINATION, new Integer(fDestinationIndex).toString());
//arguments.put(ATTRIBUTE_EXCEPTIONS, Boolean.valueOf(fThrowRuntimeExceptions).toString());
//arguments.put(ATTRIBUTE_COMMENTS, Boolean.valueOf(fGenerateScriptdoc).toString());
//arguments.put(ATTRIBUTE_REPLACE, Boolean.valueOf(fReplaceDuplicates).toString());
return descriptor;
}
/**
* Returns the signature of the new method.
*
* @return the signature of the extracted method
*/
public String getSignature() {
return getSignature(fMethodName);
}
/**
* Returns the signature of the new method.
*
* @param methodName the method name used for the new method
* @return the signature of the extracted method
*/
public String getSignature(String methodName) {
FunctionExpression methodDecl= createNewMethodDeclaration();
String str = new Generator(null,"",0,"").generate(methodDecl).toString();
return str.substring(0, str.indexOf('{')-1);
}
/*
* Returns the number of duplicate code snippets found.
*
* @return the number of duplicate code fragments
*
public int getNumberOfDuplicates() {
if (fDuplicates == null)
return 0;
int result=0;
for (int i= 0; i < fDuplicates.length; i++) {
if (!fDuplicates[i].isMethodBody())
result++;
}
return result;
}
public boolean getReplaceDuplicates() {
return fReplaceDuplicates;
}
public void setReplaceDuplicates(boolean replace) {
fReplaceDuplicates= replace;
}
public void setGenerateScriptdoc(boolean generate) {
fGenerateScriptdoc= generate;
}
public boolean getGenerateScriptdoc() {
return fGenerateScriptdoc;
}*/
//---- Helper methods ------------------------------------------------------------------------
private void initializeParameterInfos() {
VariableBinding[] arguments= fAnalyzer.getArguments();
fParameterInfos= new ArrayList<ParameterInfo>(arguments.length);
for (int i= 0; i < arguments.length; i++) {
String argument = arguments[i].getName();
String type = arguments[i].getTypeName();
if (type == null)
type = "";
fParameterInfos.add(new ParameterInfo(type, argument, i));
}
}
private void initializeUsedNames() {
// TODO might need more thinking
fUsedNames = VariableLookup.getVisibleNames(fAnalyzer.getSelectedNodes()[0]);
//fUsedNames= UsedNamesCollector.perform(fAnalyzer.getSelectedNodes());
for (ParameterInfo parameter : fParameterInfos) {
fUsedNames.remove(parameter.getOldName());
}
}
/*private void initializeDuplicates() {
ASTNode start= fAnalyzer.getEnclosingBodyDeclaration();
while (!(start instanceof AbstractTypeDeclaration)) {
start= start.getParent();
}
fDuplicates= SnippetFinder.perform(start, fAnalyzer.getSelectedNodes());
fReplaceDuplicates= fDuplicates.length > 0 && ! fAnalyzer.isLiteralNodeSelected();
}*/
private void initializeDestinations() {
List<Node> result= new ArrayList<Node>();
Node node= fAnalyzer.getEnclosingNode();
Node parent = node == null ? null : (Node)node.eContainer();
Node grandparent = parent == null ? null : (Node)parent.eContainer();
if (node instanceof Source) {
Node cur = fAnalyzer.getSelectedNodes()[0];
while (cur.eContainer() != node)
cur = (Node)cur.eContainer();
result.add(cur);
} else {
while (node != null) {
if (parent instanceof Source) break;
boolean stop = false;
switch(grandparent.eClass().getClassifierID()) {
case DomPackage.GETTER_ASSIGNMENT:
case DomPackage.SETTER_ASSIGNMENT:
case DomPackage.FUNCTION_EXPRESSION:
stop = true;
}
if (stop) break;
node = parent;
parent = grandparent;
grandparent = parent == null ? null : (Node)parent.eContainer();
}
if (node != null) result.add(node);
}
// TODO understand what we need here or we can just move manually after refactoring
/*if (decl instanceof FunctionExpression) {
//ITypeBinding binding= ASTNodes.getEnclosingType(current);
Node next= getNextParent(current);
while (next != null && binding != null && binding.isNested()) {
result.add(next);
current= next;
binding= ASTNodes.getEnclosingType(current);
next= getNextParent(next);
}
}*/
fDestinations= (Node[])result.toArray(new Node[result.size()]);
fDestination= fDestinations[fDestinationIndex];
}
private FunctionExpression createNewMethodDeclaration() {
FunctionExpression result= DomFactory.eINSTANCE.createFunctionExpression();
//int modifiers= fVisibility;
/*Node enclosingBodyDeclaration= fAnalyzer.getEnclosingNode();
while (enclosingBodyDeclaration != null && enclosingBodyDeclaration.eContainer() != fDestination) {
enclosingBodyDeclaration= (Node)enclosingBodyDeclaration.eContainer();
}
if (enclosingBodyDeclaration instanceof BodyDeclaration) { // should always be the case
int enclosingModifiers= ((BodyDeclaration)enclosingBodyDeclaration).getModifiers();
boolean shouldBeStatic= Modifier.isStatic(enclosingModifiers) || fAnalyzer.getForceStatic();
if (shouldBeStatic) {
modifiers|= Modifier.STATIC;
}
}
ITypeBinding[] typeVariables= computeLocalTypeVariables();
List typeParameters= result.typeParameters();
for (int i= 0; i < typeVariables.length; i++) {
TypeParameter parameter= fAST.newTypeParameter();
parameter.setName(fAST.newSimpleName(typeVariables[i].getName()));
typeParameters.add(parameter);
}
result.modifiers().addAll(ASTNodeFactory.newModifiers(fAST, modifiers));
result.setReturnType2((Type)ASTNode.copySubtree(fAST, fAnalyzer.getReturnType()));*/
if (fAnalyzer.getReturnTypeName() != null) {
// Type type = DomFactory.eINSTANCE.createType();
// type.setName(fAnalyzer.getReturnTypeName());
// result.setReturnType(type);
}
Identifier id = DomFactory.eINSTANCE.createIdentifier();
id.setName(fMethodName);
result.setIdentifier(id);
List<Parameter> parameters= result.getParameters();
for (int i= 0; i < fParameterInfos.size(); i++) {
ParameterInfo info= (ParameterInfo)fParameterInfos.get(i);
Parameter parameter= DomFactory.eINSTANCE.createParameter();
Identifier prmName = DomFactory.eINSTANCE.createIdentifier();
prmName.setName(info.getNewName());
parameter.setName(prmName);
if (!"".equals(info.getNewTypeName())) { //$NON-NLS-1
// Type type = DomFactory.eINSTANCE.createType();
// type.setName(info.getNewTypeName());
// parameter.setType(type);
}
parameters.add(parameter);
}
/*List exceptions= result.thrownExceptions();
ITypeBinding[] exceptionTypes= fAnalyzer.getExceptions(fThrowRuntimeExceptions);
ImportRewriteContext context= new ContextSensitiveImportRewriteContext(enclosingBodyDeclaration, fImportRewriter);
for (int i= 0; i < exceptionTypes.length; i++) {
ITypeBinding exceptionType= exceptionTypes[i];
exceptions.add(ASTNodeFactory.newName(fAST, fImportRewriter.addImport(exceptionType, context)));
}*/
result.setBody(DomFactory.eINSTANCE.createBlockStatement());
return result;
}
/*private ITypeBinding[] computeLocalTypeVariables() {
List result= new ArrayList(Arrays.asList(fAnalyzer.getTypeVariables()));
for (int i= 0; i < fParameterInfos.size(); i++) {
ParameterInfo info= (ParameterInfo)fParameterInfos.get(i);
processVariable(result, info.getOldBinding());
}
IVariableBinding[] methodLocals= fAnalyzer.getMethodLocals();
for (int i= 0; i < methodLocals.length; i++) {
processVariable(result, methodLocals[i]);
}
return (ITypeBinding[])result.toArray(new ITypeBinding[result.size()]);
}
private void processVariable(List result, IVariableBinding variable) {
if (variable == null)
return;
ITypeBinding binding= variable.getType();
if (binding != null && binding.isParameterizedType()) {
ITypeBinding[] typeArgs= binding.getTypeArguments();
for (int args= 0; args < typeArgs.length; args++) {
ITypeBinding arg= typeArgs[args];
if (arg.isTypeVariable() && !result.contains(arg)) {
ASTNode decl= fRoot.findDeclaringNode(arg);
if (decl != null && decl.getParent() instanceof MethodDeclaration) {
result.add(arg);
}
}
}
}
}*/
/*private String getName(IVariableBinding binding) {
for (Iterator iter= fParameterInfos.iterator(); iter.hasNext();) {
ParameterInfo info= (ParameterInfo)iter.next();
if (Bindings.equals(binding, info.getOldBinding())) {
return info.getNewName();
}
}
return binding.getName();
}
private VariableDeclaration getVariableDeclaration(ParameterInfo parameter) {
return ASTNodes.findVariableDeclaration(parameter.getOldBinding(), fAnalyzer.getEnclosingBodyDeclaration());
}*/
private VariableStatement createDeclaration(VariableBinding name, Expression initializer) {
Identifier id = DomFactory.eINSTANCE.createIdentifier();
id.setName(name.getName());
VariableDeclaration decl = DomFactory.eINSTANCE.createVariableDeclaration();
decl.setIdentifier(id);
if (name.getTypeName() != null) {
// Type type = DomFactory.eINSTANCE.createType();
// type.setName(name.getTypeName());
}
if (initializer != null)
decl.setInitializer(initializer);
VariableStatement result = DomFactory.eINSTANCE.createVariableStatement();
result.getDeclarations().add(decl);
return result;
}
/*public ISourceModule getSource() {
return fCUnit;
}
private RefactoringStatus initialize(ScriptRefactoringArguments arguments) {
final String selection= arguments.getAttribute(ScriptRefactoringDescriptorUtil.ATTRIBUTE_SELECTION);
if (selection == null)
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ScriptRefactoringDescriptorUtil.ATTRIBUTE_SELECTION));
int offset= -1;
int length= -1;
final StringTokenizer tokenizer= new StringTokenizer(selection);
if (tokenizer.hasMoreTokens())
offset= Integer.valueOf(tokenizer.nextToken()).intValue();
if (tokenizer.hasMoreTokens())
length= Integer.valueOf(tokenizer.nextToken()).intValue();
if (offset < 0 || length < 0)
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, new Object[] { selection, ScriptRefactoringDescriptorUtil.ATTRIBUTE_SELECTION}));
fSelectionStart= offset;
fSelectionLength= length;
final String handle= arguments.getAttribute(ScriptRefactoringDescriptorUtil.ATTRIBUTE_INPUT);
if (handle == null)
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ScriptRefactoringDescriptorUtil.ATTRIBUTE_INPUT));
IScriptElement element= ScriptRefactoringDescriptorUtil.handleToElement(arguments.getProject(), handle, false);
if (element == null || !element.exists() || element.getElementType() != IScriptElement.COMPILATION_UNIT)
return ScriptRefactoringDescriptorUtil.createInputFatalStatus(element, getName(), IScriptRefactorings.EXTRACT_METHOD);
fCUnit= (ISourceModule) element;
final String visibility= arguments.getAttribute(ATTRIBUTE_VISIBILITY);
if (visibility != null && visibility.length() != 0) {
int flag= 0;
try {
flag= Integer.parseInt(visibility);
} catch (NumberFormatException exception) {
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_VISIBILITY));
}
fVisibility= flag;
}
final String name= arguments.getAttribute(ScriptRefactoringDescriptorUtil.ATTRIBUTE_NAME);
if (name == null || name.length() == 0)
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ScriptRefactoringDescriptorUtil.ATTRIBUTE_NAME));
fMethodName= name;
final String destination= arguments.getAttribute(ATTRIBUTE_DESTINATION);
if (destination != null && destination.length() == 0) {
int index= 0;
try {
index= Integer.parseInt(destination);
} catch (NumberFormatException exception) {
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_DESTINATION));
}
fDestinationIndex= index;
}
final String replace= arguments.getAttribute(ATTRIBUTE_REPLACE);
if (replace == null)
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_REPLACE));
fReplaceDuplicates= Boolean.valueOf(replace).booleanValue();
final String comments= arguments.getAttribute(ATTRIBUTE_COMMENTS);
if (comments == null)
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_COMMENTS));
fGenerateScriptdoc= Boolean.valueOf(comments).booleanValue();
final String exceptions= arguments.getAttribute(ATTRIBUTE_EXCEPTIONS);
if (exceptions == null)
return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_EXCEPTIONS));
fThrowRuntimeExceptions= Boolean.valueOf(exceptions).booleanValue();
return new RefactoringStatus();
}*/
}