| /******************************************************************************* |
| * 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(); |
| }*/ |
| } |