| /******************************************************************************* |
| * Copyright (c) 2000, 2010 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.dltk.internal.javascript.corext.refactoring.code; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.SubProgressMonitor; |
| import org.eclipse.dltk.core.IScriptProject; |
| import org.eclipse.dltk.core.ISourceModule; |
| import org.eclipse.dltk.core.ModelException; |
| 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.JavascriptManipulationPlugin; |
| 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.RefactoringCoreMessages; |
| import org.eclipse.dltk.javascript.core.dom.BlockStatement; |
| 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.Identifier; |
| import org.eclipse.dltk.javascript.core.dom.Node; |
| 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.NodeFinder; |
| 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.ExtractLocalDescriptor; |
| import org.eclipse.dltk.javascript.parser.JavaScriptParserUtil; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.common.util.TreeIterator; |
| import org.eclipse.emf.ecore.EAttribute; |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.EReference; |
| import org.eclipse.emf.ecore.EStructuralFeature; |
| 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.text.edits.TextEdit; |
| import org.eclipse.text.edits.TextEditGroup; |
| |
| /** |
| * Extract Local Variable (from selected expression inside method or |
| * initializer). |
| */ |
| public class ExtractTempRefactoring extends Refactoring { |
| |
| //private static final String ATTRIBUTE_REPLACE = "replace"; //$NON-NLS-1$ |
| //private static final String ATTRIBUTE_FINAL = "final"; //$NON-NLS-1$ |
| |
| /*private static final class ForStatementChecker extends ASTVisitor { |
| |
| private final Collection fForInitializerVariables; |
| |
| private boolean fReferringToForVariable = false; |
| |
| public ForStatementChecker(Collection forInitializerVariables) { |
| Assert.isNotNull(forInitializerVariables); |
| fForInitializerVariables = forInitializerVariables; |
| } |
| |
| public boolean isReferringToForVariable() { |
| return fReferringToForVariable; |
| } |
| |
| public boolean visit(SimpleName node) { |
| IBinding binding = node.resolveBinding(); |
| if (binding != null && fForInitializerVariables.contains(binding)) { |
| fReferringToForVariable = true; |
| } |
| return false; |
| } |
| } |
| |
| private static boolean allArraysEqual(Object[][] arrays, int position) { |
| Object element = arrays[0][position]; |
| for (int i = 0; i < arrays.length; i++) { |
| Object[] array = arrays[i]; |
| if (!element.equals(array[position])) |
| return false; |
| } |
| return true; |
| } |
| |
| private static boolean canReplace(IASTFragment fragment) { |
| ASTNode node = fragment.getAssociatedNode(); |
| ASTNode parent = node.getParent(); |
| if (parent instanceof VariableDeclarationFragment) { |
| VariableDeclarationFragment vdf = (VariableDeclarationFragment) parent; |
| if (node.equals(vdf.getName())) |
| return false; |
| } |
| if (isMethodParameter(node)) |
| return false; |
| if (isThrowableInCatchBlock(node)) |
| return false; |
| if (parent instanceof ExpressionStatement) |
| return false; |
| if (isLeftValue(node)) |
| return false; |
| if (isReferringToLocalVariableFromFor((Expression) node)) |
| return false; |
| if (isUsedInForInitializerOrUpdater((Expression) node)) |
| return false; |
| if (parent instanceof SwitchCase) |
| return false; |
| return true; |
| } |
| |
| private static Object[] getArrayPrefix(Object[] array, int prefixLength) { |
| Assert.isTrue(prefixLength <= array.length); |
| Assert.isTrue(prefixLength >= 0); |
| Object[] prefix = new Object[prefixLength]; |
| for (int i = 0; i < prefix.length; i++) { |
| prefix[i] = array[i]; |
| } |
| return prefix; |
| } |
| |
| // return List<IVariableBinding> |
| private static List getForInitializedVariables( |
| VariableDeclarationExpression variableDeclarations) { |
| List forInitializerVariables = new ArrayList(1); |
| for (Iterator iter = variableDeclarations.fragments().iterator(); iter |
| .hasNext();) { |
| VariableDeclarationFragment fragment = (VariableDeclarationFragment) iter |
| .next(); |
| IVariableBinding binding = fragment.resolveBinding(); |
| if (binding != null) |
| forInitializerVariables.add(binding); |
| } |
| return forInitializerVariables; |
| } |
| |
| private static Object[] getLongestArrayPrefix(Object[][] arrays) { |
| int length = -1; |
| if (arrays.length == 0) |
| return new Object[0]; |
| int minArrayLength = arrays[0].length; |
| for (int i = 1; i < arrays.length; i++) |
| minArrayLength = Math.min(minArrayLength, arrays[i].length); |
| |
| for (int i = 0; i < minArrayLength; i++) { |
| if (!allArraysEqual(arrays, i)) |
| break; |
| length++; |
| } |
| if (length == -1) |
| return new Object[0]; |
| return getArrayPrefix(arrays[0], length + 1); |
| } |
| |
| private static ASTNode[] getParents(ASTNode node) { |
| ASTNode current = node; |
| List parents = new ArrayList(); |
| do { |
| parents.add(current.getParent()); |
| current = current.getParent(); |
| } while (current.getParent() != null); |
| Collections.reverse(parents); |
| return (ASTNode[]) parents.toArray(new ASTNode[parents.size()]); |
| } |
| |
| private static boolean isLeftValue(ASTNode node) { |
| ASTNode parent = node.getParent(); |
| if (parent instanceof Assignment) { |
| Assignment assignment = (Assignment) parent; |
| if (assignment.getLeftHandSide() == node) |
| return true; |
| } |
| if (parent instanceof PostfixExpression) |
| return true; |
| if (parent instanceof PrefixExpression) { |
| PrefixExpression.Operator op = ((PrefixExpression) parent) |
| .getOperator(); |
| if (op.equals(PrefixExpression.Operator.DECREMENT)) |
| return true; |
| if (op.equals(PrefixExpression.Operator.INCREMENT)) |
| return true; |
| return false; |
| } |
| return false; |
| } |
| |
| private static boolean isMethodParameter(ASTNode node) { |
| return (node instanceof SimpleName) |
| && (node.getParent() instanceof SingleVariableDeclaration) |
| && (node.getParent().getParent() instanceof MethodDeclaration); |
| }*/ |
| |
| /*private static boolean isThrowableInCatchBlock(ASTNode node) { |
| return (node instanceof SimpleName) |
| && (node.getParent() instanceof SingleVariableDeclaration) |
| && (node.getParent().getParent() instanceof CatchClause); |
| }*/ |
| |
| private static boolean isUsedInFor(Expression expression) { |
| EStructuralFeature sf = expression.eContainingFeature(); |
| if (sf == DomPackage.eINSTANCE.getForStatement_Initialization()) return true; |
| if (sf == DomPackage.eINSTANCE.getForStatement_Condition()) return true; |
| if (sf == DomPackage.eINSTANCE.getForStatement_Increment()) return true; |
| if (sf == DomPackage.eINSTANCE.getForInStatement_Item()) return true; |
| if (sf == DomPackage.eINSTANCE.getForEachInStatement_Item()) return true; |
| return false; |
| } |
| |
| /*private static IASTFragment[] retainOnlyReplacableMatches( |
| IASTFragment[] allMatches) { |
| List result = new ArrayList(allMatches.length); |
| for (int i = 0; i < allMatches.length; i++) { |
| if (canReplace(allMatches[i])) |
| result.add(allMatches[i]); |
| } |
| return (IASTFragment[]) result.toArray(new IASTFragment[result.size()]); |
| }*/ |
| |
| private Source fCompilationUnitNode; |
| |
| //private CompilationUnitRewrite fCURewrite; |
| |
| private ISourceModule fCu; |
| private String fSource; |
| |
| //private boolean fDeclareFinal; |
| |
| private String[] fExcludedVariableNames; |
| |
| private boolean fReplaceAllOccurrences; |
| |
| // caches: |
| private Expression fSelectedExpression; |
| |
| private int fSelectionLength; |
| |
| private int fSelectionStart; |
| |
| private String fTempName; |
| /*private String[] fGuessedTempNames; |
| |
| private boolean fCheckResultForCompileProblems;*/ |
| |
| private SourceModuleChange fChange; |
| |
| //private LinkedProposalModel fLinkedProposalModel; |
| |
| //private static final String KEY_NAME = "name"; //$NON-NLS-1$ |
| //private static final String KEY_TYPE = "type"; //$NON-NLS-1$ |
| |
| /** |
| * Creates a new extract temp refactoring |
| * |
| * @param unit |
| * the compilation unit, or <code>null</code> if invoked by |
| * scripting |
| * @param selectionStart |
| * start of selection |
| * @param selectionLength |
| * length of selection |
| */ |
| public ExtractTempRefactoring(ISourceModule unit, int selectionStart, |
| int selectionLength) { |
| Assert.isTrue(selectionStart >= 0); |
| Assert.isTrue(selectionLength >= 0); |
| fSelectionStart = selectionStart; |
| fSelectionLength = selectionLength; |
| fCu = unit; |
| fCompilationUnitNode = null; |
| |
| fReplaceAllOccurrences = true; // default |
| //fDeclareFinal = false; // default |
| fTempName = ""; //$NON-NLS-1$ |
| |
| //fLinkedProposalModel = null; |
| //fCheckResultForCompileProblems = true; |
| } |
| |
| /*public ExtractTempRefactoring(CompilationUnit astRoot, int selectionStart, |
| int selectionLength) { |
| Assert.isTrue(selectionStart >= 0); |
| Assert.isTrue(selectionLength >= 0); |
| Assert.isTrue(astRoot.getTypeRoot() instanceof ISourceModule); |
| |
| fSelectionStart = selectionStart; |
| fSelectionLength = selectionLength; |
| fCu = (ISourceModule) astRoot.getTypeRoot(); |
| fCompilationUnitNode = astRoot; |
| |
| fReplaceAllOccurrences = true; // default |
| fDeclareFinal = false; // default |
| fTempName = ""; //$NON-NLS-1$ |
| |
| fLinkedProposalModel = null; |
| fCheckResultForCompileProblems = true; |
| } |
| |
| public ExtractTempRefactoring(JavaRefactoringArguments arguments, |
| RefactoringStatus status) { |
| this((ISourceModule) null, 0, 0); |
| RefactoringStatus initializeStatus = initialize(arguments); |
| status.merge(initializeStatus); |
| } |
| |
| public void setCheckResultForCompileProblems( |
| boolean checkResultForCompileProblems) { |
| fCheckResultForCompileProblems = checkResultForCompileProblems; |
| } |
| |
| public void setLinkedProposalModel(LinkedProposalModel linkedProposalModel) { |
| fLinkedProposalModel = linkedProposalModel; |
| } |
| |
| private void addReplaceExpressionWithTemp() throws JavaModelException { |
| IASTFragment[] fragmentsToReplace = retainOnlyReplacableMatches(getMatchingFragments()); |
| // TODO: should not have to prune duplicates here... |
| ASTRewrite rewrite = fCURewrite.getASTRewrite(); |
| HashSet seen = new HashSet(); |
| for (int i = 0; i < fragmentsToReplace.length; i++) { |
| IASTFragment fragment = fragmentsToReplace[i]; |
| if (!seen.add(fragment)) |
| continue; |
| SimpleName tempName = fCURewrite.getAST().newSimpleName(fTempName); |
| TextEditGroup description = fCURewrite |
| .createGroupDescription(RefactoringCoreMessages.ExtractTempRefactoring_replace); |
| |
| fragment.replace(rewrite, tempName, description); |
| if (fLinkedProposalModel != null) |
| fLinkedProposalModel.getPositionGroup(KEY_NAME, true) |
| .addPosition(rewrite.track(tempName), false); |
| } |
| }*/ |
| |
| // !! Same as in ExtractConstantRefactoring |
| /*private RefactoringStatus checkExpressionFragmentIsRValue() throws ModelException { |
| switch (Checks.checkExpressionIsRValue(getSelectedExpression() |
| .getAssociatedExpression())) { |
| case Checks.NOT_RVALUE_MISC: |
| return RefactoringStatus |
| .createStatus( |
| RefactoringStatus.FATAL, |
| RefactoringCoreMessages.ExtractTempRefactoring_select_expression, |
| null, Corext.getPluginId(), |
| RefactoringStatusCodes.EXPRESSION_NOT_RVALUE, null); |
| case Checks.NOT_RVALUE_VOID: |
| return RefactoringStatus.createStatus(RefactoringStatus.FATAL, |
| RefactoringCoreMessages.ExtractTempRefactoring_no_void, |
| null, Corext.getPluginId(), |
| RefactoringStatusCodes.EXPRESSION_NOT_RVALUE_VOID, null); |
| case Checks.IS_RVALUE_GUESSED: |
| case Checks.IS_RVALUE: |
| return new RefactoringStatus(); |
| default: |
| Assert.isTrue(false); |
| return null; |
| } |
| }*/ |
| |
| /*private ITypeBinding guessBindingForReference(Expression expression) { |
| ITypeBinding binding = expression.resolveTypeBinding(); |
| if (binding == null) { |
| binding = ASTResolving.guessBindingForReference(expression); |
| } |
| return binding; |
| }*/ |
| |
| public RefactoringStatus checkFinalConditions(IProgressMonitor pm) |
| throws CoreException { |
| try { |
| pm.beginTask( |
| RefactoringCoreMessages.ExtractTempRefactoring_checking_preconditions, |
| 4); |
| ChangeRecorder cr = new ChangeRecorder(fCompilationUnitNode); |
| // Expression |
| Expression selected = getSelectedExpression(); |
| Expression[] expressions; |
| if (fReplaceAllOccurrences) { |
| expressions = findSimilarExpressions(selected); |
| } else { |
| expressions = new Expression[]{ selected }; |
| } |
| // Statement |
| Statement stmt = findFirstStatement(expressions[0],expressions[expressions.length-1]); |
| EReference stmtRef = stmt.eContainmentFeature(); |
| // Expression -> Reference |
| for(Expression expr : expressions) { |
| Identifier refId = DomFactory.eINSTANCE.createIdentifier(); |
| refId.setName(fTempName); |
| VariableReference varRef = DomFactory.eINSTANCE.createVariableReference(); |
| varRef.setVariable(refId); |
| EReference exprRef = expr.eContainmentFeature(); |
| if (exprRef.isMany()) { |
| EList<Expression> exprList = (EList<Expression>)expr.eContainer().eGet(exprRef); |
| exprList.set(exprList.lastIndexOf(expr), varRef); |
| } else { |
| expr.eContainer().eSet(exprRef, varRef); |
| } |
| } |
| // Declaration |
| Identifier declId = DomFactory.eINSTANCE.createIdentifier(); |
| declId.setName(fTempName); |
| VariableDeclaration decl = DomFactory.eINSTANCE.createVariableDeclaration(); |
| decl.setIdentifier(declId); |
| decl.setInitializer(selected); |
| VariableStatement varStmt = DomFactory.eINSTANCE.createVariableStatement(); |
| varStmt.getDeclarations().add(decl); |
| // << Declaration |
| if (stmtRef.isMany()) { |
| EList<Statement> stmtList = (EList<Statement>)stmt.eContainer().eGet(stmtRef); |
| stmtList.add(stmtList.lastIndexOf(stmt), varStmt); |
| } else { |
| BlockStatement block = DomFactory.eINSTANCE.createBlockStatement(); |
| block.getStatements().add(varStmt); |
| stmt.eContainer().eSet(stmtRef, block); |
| block.getStatements().add(stmt); |
| } |
| ChangeDescription cd = cr.endRecording(); |
| final SourceModuleChange change = new SourceModuleChange(RefactoringCoreMessages.ExtractTempRefactoring_name, fCu); |
| final TextEditGroup addDecl = new TextEditGroup(RefactoringCoreMessages.ExtractTempRefactoring_declare_local_variable); |
| RewriteAnalyzer ra = new RewriteAnalyzer(cd, fCu.getSource()) { |
| @Override |
| protected void addEdit(TextEdit edit, Node node) { |
| if (node instanceof VariableReference) |
| change.addTextEditGroup(new TextEditGroup(RefactoringCoreMessages.ExtractTempRefactoring_replace, edit)); |
| else |
| addDecl.addTextEdit(edit); |
| super.addEdit(edit, node); |
| } |
| }; |
| change.setEdit(ra.getEdit()); |
| ra.rewrite(fCompilationUnitNode); |
| change.addTextEditGroup(addDecl); |
| cd.apply(); |
| fChange = change; |
| return new RefactoringStatus(); |
| } finally { |
| pm.done(); |
| } |
| } |
| |
| private static Expression[] findSimilarExpressions(Expression expr) { |
| List<Expression> res = new ArrayList<Expression>(); |
| Node root = (Node)expr.eContainer(); |
| while (true) { |
| int cl = root.eClass().getClassifierID(); |
| if (cl == DomPackage.GETTER_ASSIGNMENT |
| || cl == DomPackage.SETTER_ASSIGNMENT |
| || cl == DomPackage.FUNCTION_EXPRESSION |
| || cl == DomPackage.SOURCE) break; |
| root = (Node)root.eContainer(); |
| } |
| Set<Identifier> set = new HashSet<Identifier>(); |
| set.addAll(VariableLookup.findReferences(root, VariableLookup.getVisibleNames(expr))); |
| TreeIterator<EObject> it = root.eAllContents(); |
| while(it.hasNext()) { |
| Node cur = (Node)it.next(); |
| if (match(expr,cur,set)) { |
| res.add((Expression)cur); |
| it.prune(); |
| } |
| } |
| return res.toArray(new Expression[res.size()]); |
| } |
| |
| private static boolean match(Node one, Node another, Set<Identifier> resolved) { |
| if (one == null) |
| return another == null; |
| EClass cl = one.eClass(); |
| if (cl != another.eClass()) { |
| return false; |
| } |
| if (resolved.contains(one) != resolved.contains(another)) |
| return false; |
| for(EAttribute attr : cl.getEAllAttributes()) { |
| if (attr.getFeatureID() == DomPackage.NODE__BEGIN |
| || attr.getFeatureID() == DomPackage.NODE__END |
| || attr == DomPackage.eINSTANCE.getBinaryExpression_OperatorPosition() |
| || attr == DomPackage.eINSTANCE.getFunctionExpression_ParametersPosition()) continue; |
| if (!one.eGet(attr).equals(another.eGet(attr))) |
| return false; |
| } |
| for(EReference ref : cl.getEAllReferences()) { |
| if (ref.isMany()) { |
| List<? extends Node> first = (List<? extends Node>)one.eGet(ref); |
| List<? extends Node> second = (List<? extends Node>)another.eGet(ref); |
| if (first.size() != second.size()) |
| return false; |
| for(int i=0;i<first.size();i++) |
| if (!match(first.get(i),second.get(i),resolved)) |
| return false; |
| } else |
| if (!match((Node)one.eGet(ref),(Node)another.eGet(ref),resolved)) |
| return false; |
| } |
| return true; |
| } |
| |
| private static Statement findFirstStatement(EObject first,EObject last) { |
| int x = getDepth(first); |
| int y = getDepth(last); |
| while (x>y) { |
| x--; |
| first = first.eContainer(); |
| } |
| while (y>x) { |
| y--; |
| last = last.eContainer(); |
| } |
| while (first.eContainer() != last.eContainer()) { |
| first = first.eContainer(); |
| last = last.eContainer(); |
| } |
| while (first.eContainmentFeature().getEReferenceType() != DomPackage.eINSTANCE.getStatement()) |
| first = first.eContainer(); |
| return (Statement)first; |
| } |
| |
| private static int getDepth(EObject node) { |
| int i=0; |
| while (node != null) { |
| i++; |
| node = node.eContainer(); |
| } |
| return i; |
| } |
| |
| private ExtractLocalDescriptor createRefactoringDescriptor() { |
| String project = null; |
| IScriptProject scriptProject = fCu.getScriptProject(); |
| if (scriptProject != null) |
| project = scriptProject.getElementName(); |
| //final String description = Messages.format(RefactoringCoreMessages.ExtractTempRefactoring_descriptor_description_short, |
| // BasicElementLabels.getModelElementName(fTempName)); |
| final String description = Messages.format(RefactoringCoreMessages.ExtractTempRefactoring_descriptor_description_short,fTempName); |
| //final String expression = ASTNodes.asString(fSelectedExpression.getAssociatedExpression()); |
| //final String header = Messages |
| // .format(RefactoringCoreMessages.ExtractTempRefactoring_descriptor_description, |
| // new String[] { |
| // BasicElementLabels |
| // .getJavaElementName(fTempName), |
| // BasicElementLabels |
| // .getJavaCodeString(expression) }); |
| //final ScriptRefactoringDescriptorComment comment = new JDTRefactoringDescriptorComment( |
| // project, this, header); |
| //comment.addSetting(Messages.format( |
| // RefactoringCoreMessages.ExtractTempRefactoring_name_pattern, |
| // BasicElementLabels.getJavaElementName(fTempName))); |
| //final BodyDeclaration decl = (BodyDeclaration) ASTNodes.getParent( |
| // fSelectedExpression.getAssociatedExpression(), |
| // BodyDeclaration.class); |
| //if (decl instanceof MethodDeclaration) { |
| // final IMethodBinding method = ((MethodDeclaration) decl) |
| // .resolveBinding(); |
| // final String label = method != null ? BindingLabelProvider |
| // .getBindingLabel(method, |
| // JavaElementLabels.ALL_FULLY_QUALIFIED) |
| // : BasicElementLabels |
| // .getJavaElementName('{' + JavaElementLabels.ELLIPSIS_STRING + '}'); |
| // comment.addSetting(Messages |
| // .format(RefactoringCoreMessages.ExtractTempRefactoring_destination_pattern, |
| // label)); |
| //} |
| //comment.addSetting(Messages |
| // .format(RefactoringCoreMessages.ExtractTempRefactoring_expression_pattern, |
| // BasicElementLabels.getJavaCodeString(expression))); |
| //if (fReplaceAllOccurrences) |
| // comment.addSetting(RefactoringCoreMessages.ExtractTempRefactoring_replace_occurrences); |
| //if (fDeclareFinal) |
| // comment.addSetting(RefactoringCoreMessages.ExtractTempRefactoring_declare_final); |
| final Map arguments = new HashMap(); |
| arguments.put(ScriptRefactoringDescriptor.ATTRIBUTE_INPUT, |
| ScriptRefactoringDescriptor.elementToHandle(project, fCu)); |
| arguments.put(ScriptRefactoringDescriptor.ATTRIBUTE_NAME, fTempName); |
| arguments.put(ScriptRefactoringDescriptor.ATTRIBUTE_SELECTION, |
| new Integer(fSelectionStart).toString() |
| + " " + new Integer(fSelectionLength).toString()); //$NON-NLS-1$ |
| //arguments.put(ATTRIBUTE_REPLACE, Boolean |
| // .valueOf(fReplaceAllOccurrences).toString()); |
| //arguments.put(ATTRIBUTE_FINAL, Boolean.valueOf(fDeclareFinal) |
| // .toString()); |
| final ExtractLocalDescriptor descriptor = new ExtractLocalDescriptor(project, description, /*comment.asString()*/ "", arguments, RefactoringDescriptor.NONE); |
| return descriptor; |
| } |
| |
| /*private void doCreateChange(IProgressMonitor pm) throws CoreException { |
| try { |
| pm.beginTask( |
| RefactoringCoreMessages.ExtractTempRefactoring_checking_preconditions, |
| 1); |
| try { |
| createTempDeclaration(); |
| } catch (CoreException exception) { |
| JavaPlugin.log(exception); |
| } |
| addReplaceExpressionWithTemp(); |
| } finally { |
| pm.done(); |
| } |
| } |
| |
| private void checkNewSource(SubProgressMonitor monitor, |
| RefactoringStatus result) throws CoreException { |
| String newCuSource = fChange |
| .getPreviewContent(new NullProgressMonitor()); |
| CompilationUnit newCUNode = new RefactoringASTParser(AST.JLS3).parse( |
| newCuSource, fCu, true, true, monitor); |
| IProblem[] newProblems = RefactoringAnalyzeUtil |
| .getIntroducedCompileProblems(newCUNode, fCompilationUnitNode); |
| for (int i = 0; i < newProblems.length; i++) { |
| IProblem problem = newProblems[i]; |
| if (problem.isError()) |
| result.addEntry(new RefactoringStatusEntry( |
| (problem.isError() ? RefactoringStatus.ERROR |
| : RefactoringStatus.WARNING), problem |
| .getMessage(), |
| new JavaStringStatusContext(newCuSource, |
| SourceRangeFactory.create(problem)))); |
| } |
| }*/ |
| |
| public RefactoringStatus checkInitialConditions(IProgressMonitor pm) |
| throws CoreException { |
| try { |
| pm.beginTask("", 6); //$NON-NLS-1$ |
| |
| RefactoringStatus result = RefactoringChecks.validateModifiesFiles( |
| ResourceUtil.getFiles(new ISourceModule[] { fCu }), |
| getValidationContext()); |
| if (result.hasFatalError()) |
| return result; |
| |
| if (fCompilationUnitNode == null) { |
| fCompilationUnitNode = (Source)ASTConverter.convert(JavaScriptParserUtil.parse(fCu)); |
| } |
| pm.worked(3); |
| |
| fSource = fCu.getSource(); |
| result.merge(checkSelection(new SubProgressMonitor(pm, 3))); |
| //if (!result.hasFatalError() && isLiteralNodeSelected()) |
| // fReplaceAllOccurrences = false; |
| return result; |
| |
| } finally { |
| pm.done(); |
| } |
| } |
| |
| /*private RefactoringStatus checkMatchingFragments() |
| throws JavaModelException { |
| RefactoringStatus result = new RefactoringStatus(); |
| IASTFragment[] matchingFragments = getMatchingFragments(); |
| for (int i = 0; i < matchingFragments.length; i++) { |
| ASTNode node = matchingFragments[i].getAssociatedNode(); |
| if (isLeftValue(node) |
| && !isReferringToLocalVariableFromFor((Expression) node)) { |
| String msg = RefactoringCoreMessages.ExtractTempRefactoring_assigned_to; |
| result.addWarning(msg, JavaStatusContext.create(fCu, node)); |
| } |
| } |
| return result; |
| }*/ |
| |
| private RefactoringStatus checkSelection(IProgressMonitor pm) throws ModelException { |
| try { |
| pm.beginTask("", 2); //$NON-NLS-1$ |
| |
| Expression selectedExpression = getSelectedExpression(); |
| |
| if (selectedExpression == null) { |
| String message = RefactoringCoreMessages.ExtractTempRefactoring_select_expression; |
| //return CodeRefactoringUtil.checkMethodSyntaxErrors( |
| // fSelectionStart, fSelectionLength, |
| // fCompilationUnitNode, message); |
| return RefactoringStatus.createFatalErrorStatus(message); |
| } |
| pm.worked(1); |
| |
| if (isUsedInFor(getSelectedExpression())) |
| return RefactoringStatus |
| .createFatalErrorStatus(RefactoringCoreMessages.ExtractTempRefactoring_for_initializer_updater); |
| pm.worked(1); |
| |
| return new RefactoringStatus(); |
| } finally { |
| pm.done(); |
| } |
| } |
| |
| public RefactoringStatus checkTempName(String newName) { |
| RefactoringStatus status = Checks.validateIdentifier(newName); |
| if (status.hasFatalError()) |
| return status; |
| if (Arrays.asList(getExcludedVariableNames()).contains(newName)) |
| status.addWarning(Messages |
| .format(RefactoringCoreMessages.ExtractTempRefactoring_another_variable, |
| newName /*BasicElementLabels.getJavaElementName(newName)*/)); |
| return status; |
| } |
| |
| /*private void createAndInsertTempDeclaration() throws CoreException { |
| Expression initializer = getSelectedExpression().createCopyTarget( |
| fCURewrite.getASTRewrite(), true); |
| VariableDeclarationStatement vds = createTempDeclaration(initializer); |
| |
| if ((!fReplaceAllOccurrences) |
| || (retainOnlyReplacableMatches(getMatchingFragments()).length <= 1)) { |
| insertAt(getSelectedExpression().getAssociatedNode(), vds); |
| return; |
| } |
| |
| ASTNode[] firstReplaceNodeParents = getParents(getFirstReplacedExpression() |
| .getAssociatedNode()); |
| ASTNode[] commonPath = findDeepestCommonSuperNodePathForReplacedNodes(); |
| Assert.isTrue(commonPath.length <= firstReplaceNodeParents.length); |
| |
| ASTNode deepestCommonParent = firstReplaceNodeParents[commonPath.length - 1]; |
| if (deepestCommonParent instanceof Block) |
| insertAt(firstReplaceNodeParents[commonPath.length], vds); |
| else |
| insertAt(deepestCommonParent, vds); |
| } |
| |
| private VariableDeclarationStatement createTempDeclaration( |
| Expression initializer) throws CoreException { |
| AST ast = fCURewrite.getAST(); |
| |
| VariableDeclarationFragment vdf = ast.newVariableDeclarationFragment(); |
| vdf.setName(ast.newSimpleName(fTempName)); |
| vdf.setInitializer(initializer); |
| |
| VariableDeclarationStatement vds = ast |
| .newVariableDeclarationStatement(vdf); |
| if (fDeclareFinal) { |
| vds.modifiers().add(ast.newModifier(ModifierKeyword.FINAL_KEYWORD)); |
| } |
| vds.setType(createTempType()); |
| |
| if (fLinkedProposalModel != null) { |
| ASTRewrite rewrite = fCURewrite.getASTRewrite(); |
| LinkedProposalPositionGroup nameGroup = fLinkedProposalModel |
| .getPositionGroup(KEY_NAME, true); |
| nameGroup.addPosition(rewrite.track(vdf.getName()), true); |
| |
| String[] nameSuggestions = guessTempNames(); |
| if (nameSuggestions.length > 0 |
| && !nameSuggestions[0].equals(fTempName)) { |
| nameGroup.addProposal(fTempName, null, |
| nameSuggestions.length + 1); |
| } |
| for (int i = 0; i < nameSuggestions.length; i++) { |
| nameGroup.addProposal(nameSuggestions[i], null, |
| nameSuggestions.length - i); |
| } |
| } |
| return vds; |
| } |
| |
| private void insertAt(ASTNode target, Statement declaration) { |
| ASTRewrite rewrite = fCURewrite.getASTRewrite(); |
| TextEditGroup groupDescription = fCURewrite |
| .createGroupDescription(RefactoringCoreMessages.ExtractTempRefactoring_declare_local_variable); |
| |
| ASTNode parent = target.getParent(); |
| StructuralPropertyDescriptor locationInParent = target |
| .getLocationInParent(); |
| while (locationInParent != Block.STATEMENTS_PROPERTY |
| && locationInParent != SwitchStatement.STATEMENTS_PROPERTY) { |
| if (locationInParent == IfStatement.THEN_STATEMENT_PROPERTY |
| || locationInParent == IfStatement.ELSE_STATEMENT_PROPERTY |
| || locationInParent == ForStatement.BODY_PROPERTY |
| || locationInParent == EnhancedForStatement.BODY_PROPERTY |
| || locationInParent == DoStatement.BODY_PROPERTY |
| || locationInParent == WhileStatement.BODY_PROPERTY) { |
| // create intermediate block if target was the body property of |
| // a control statement: |
| Block replacement = rewrite.getAST().newBlock(); |
| ListRewrite replacementRewrite = rewrite.getListRewrite( |
| replacement, Block.STATEMENTS_PROPERTY); |
| replacementRewrite.insertFirst(declaration, null); |
| replacementRewrite.insertLast(rewrite.createMoveTarget(target), |
| null); |
| rewrite.replace(target, replacement, groupDescription); |
| return; |
| } |
| target = parent; |
| parent = parent.getParent(); |
| locationInParent = target.getLocationInParent(); |
| } |
| ListRewrite listRewrite = rewrite.getListRewrite(parent, |
| (ChildListPropertyDescriptor) locationInParent); |
| listRewrite.insertBefore(declaration, target, groupDescription); |
| }*/ |
| |
| public Change createChange(IProgressMonitor pm) throws CoreException { |
| try { |
| pm.beginTask( |
| RefactoringCoreMessages.ExtractTempRefactoring_checking_preconditions, |
| 1); |
| |
| ExtractLocalDescriptor descriptor = createRefactoringDescriptor(); |
| fChange.setDescriptor(new RefactoringChangeDescriptor(descriptor)); |
| return fChange; |
| } finally { |
| pm.done(); |
| } |
| } |
| |
| private String[] getExcludedVariableNames() { |
| if (fExcludedVariableNames == null) { |
| try { |
| //ScopeLookup look = new ScopeLookup(); |
| //look.findNamesInScope(getSelectedExpression()); |
| Set<String> visible = VariableLookup.getVisibleNames(getSelectedExpression()); |
| fExcludedVariableNames = visible.toArray(new String[visible.size()]); |
| } catch (ModelException e) { |
| JavascriptManipulationPlugin.log(e); //who cares, but still |
| fExcludedVariableNames = new String[0]; |
| } |
| } |
| return fExcludedVariableNames; |
| } |
| |
| /*private IExpressionFragment getFirstReplacedExpression() |
| throws JavaModelException { |
| if (!fReplaceAllOccurrences) |
| return getSelectedExpression(); |
| IASTFragment[] nodesToReplace = retainOnlyReplacableMatches(getMatchingFragments()); |
| if (nodesToReplace.length == 0) |
| return getSelectedExpression(); |
| Comparator comparator = new Comparator() { |
| |
| public int compare(Object o1, Object o2) { |
| return ((IASTFragment) o1).getStartPosition() |
| - ((IASTFragment) o2).getStartPosition(); |
| } |
| }; |
| Arrays.sort(nodesToReplace, comparator); |
| return (IExpressionFragment) nodesToReplace[0]; |
| } |
| |
| private IASTFragment[] getMatchingFragments() throws JavaModelException { |
| if (fReplaceAllOccurrences) { |
| IASTFragment[] allMatches = ASTFragmentFactory |
| .createFragmentForFullSubtree(getEnclosingBodyNode()) |
| .getSubFragmentsMatching(getSelectedExpression()); |
| return allMatches; |
| } else |
| return new IASTFragment[] { getSelectedExpression() }; |
| } |
| |
| private ASTNode[] getMatchNodes() throws JavaModelException { |
| IASTFragment[] matches = retainOnlyReplacableMatches(getMatchingFragments()); |
| ASTNode[] result = new ASTNode[matches.length]; |
| for (int i = 0; i < matches.length; i++) |
| result[i] = matches[i].getAssociatedNode(); |
| return result; |
| }*/ |
| |
| public String getName() { |
| return RefactoringCoreMessages.ExtractTempRefactoring_name; |
| } |
| |
| private Expression getSelectedExpression() throws ModelException { |
| if (fSelectedExpression != null) |
| return fSelectedExpression; |
| Node node = NodeFinder.findNode(fCompilationUnitNode, fSelectionStart, fSelectionStart+fSelectionLength); |
| if (node == null) return null; |
| for(int i=fSelectionStart;i<node.getBegin();i++) |
| if (!Character.isWhitespace(fSource.charAt(i))) return null; |
| for(int i=node.getEnd();i<fSelectionStart+fSelectionLength;i++) |
| if (!Character.isWhitespace(fSource.charAt(i))) return null; |
| if (node instanceof ExpressionStatement) |
| return fSelectedExpression = ((ExpressionStatement)node).getExpression(); |
| if (node instanceof Expression) |
| return fSelectedExpression = (Expression)node; |
| return null; |
| /*IASTFragment selectedFragment = ASTFragmentFactory |
| .createFragmentForSourceRange(new SourceRange(fSelectionStart, |
| fSelectionLength), fCompilationUnitNode, fCu); |
| |
| if (selectedFragment instanceof IExpressionFragment |
| && !Checks |
| .isInsideJavadoc(selectedFragment.getAssociatedNode())) { |
| fSelectedExpression = (IExpressionFragment) selectedFragment; |
| } else if (selectedFragment != null) { |
| if (selectedFragment.getAssociatedNode() instanceof ExpressionStatement) { |
| ExpressionStatement exprStatement = (ExpressionStatement) selectedFragment |
| .getAssociatedNode(); |
| Expression expression = exprStatement.getExpression(); |
| fSelectedExpression = (IExpressionFragment) ASTFragmentFactory |
| .createFragmentForFullSubtree(expression); |
| } else if (selectedFragment.getAssociatedNode() instanceof Assignment) { |
| Assignment assignment = (Assignment) selectedFragment |
| .getAssociatedNode(); |
| fSelectedExpression = (IExpressionFragment) ASTFragmentFactory |
| .createFragmentForFullSubtree(assignment); |
| } |
| } |
| |
| if (fSelectedExpression != null |
| && Checks.isEnumCase(fSelectedExpression |
| .getAssociatedExpression().getParent())) { |
| fSelectedExpression = null; |
| } |
| |
| return fSelectedExpression;*/ |
| } |
| |
| /*private Type createTempType() throws CoreException { |
| Expression expression = getSelectedExpression() |
| .getAssociatedExpression(); |
| |
| Type resultingType = null; |
| ITypeBinding typeBinding = expression.resolveTypeBinding(); |
| |
| ASTRewrite rewrite = fCURewrite.getASTRewrite(); |
| AST ast = rewrite.getAST(); |
| |
| if (expression instanceof ClassInstanceCreation) { |
| resultingType = (Type) rewrite |
| .createCopyTarget(((ClassInstanceCreation) expression) |
| .getType()); |
| } else if (expression instanceof CastExpression) { |
| resultingType = (Type) rewrite |
| .createCopyTarget(((CastExpression) expression).getType()); |
| } else { |
| if (typeBinding == null) { |
| typeBinding = ASTResolving.guessBindingForReference(expression); |
| } |
| if (typeBinding != null) { |
| typeBinding = Bindings.normalizeForDeclarationUse(typeBinding, |
| ast); |
| ImportRewrite importRewrite = fCURewrite.getImportRewrite(); |
| ImportRewriteContext context = new ContextSensitiveImportRewriteContext( |
| expression, importRewrite); |
| resultingType = importRewrite.addImport(typeBinding, ast, |
| context); |
| } else { |
| resultingType = ast.newSimpleType(ast.newSimpleName("Object")); //$NON-NLS-1$ |
| } |
| } |
| if (fLinkedProposalModel != null) { |
| LinkedProposalPositionGroup typeGroup = fLinkedProposalModel |
| .getPositionGroup(KEY_TYPE, true); |
| typeGroup.addPosition(rewrite.track(resultingType), false); |
| if (typeBinding != null) { |
| ITypeBinding[] relaxingTypes = ASTResolving.getNarrowingTypes( |
| ast, typeBinding); |
| for (int i = 0; i < relaxingTypes.length; i++) { |
| typeGroup.addProposal(relaxingTypes[i], fCURewrite.getCu(), |
| relaxingTypes.length - i); |
| } |
| } |
| } |
| return resultingType; |
| } |
| |
| public String guessTempName() { |
| String[] proposals = guessTempNames(); |
| if (proposals.length == 0) |
| return fTempName; |
| else |
| return proposals[0]; |
| }*/ |
| |
| /** |
| * @return proposed variable names (may be empty, but not null). The first |
| * proposal should be used as "best guess" (if it exists). |
| */ |
| /*public String[] guessTempNames() { |
| if (fGuessedTempNames == null) { |
| try { |
| Expression expression = getSelectedExpression() |
| .getAssociatedExpression(); |
| if (expression != null) { |
| ITypeBinding binding = guessBindingForReference(expression); |
| fGuessedTempNames = StubUtility.getVariableNameSuggestions( |
| NamingConventions.VK_LOCAL, fCu.getJavaProject(), |
| binding, expression, |
| Arrays.asList(getExcludedVariableNames())); |
| } |
| } catch (JavaModelException e) { |
| } |
| if (fGuessedTempNames == null) |
| fGuessedTempNames = new String[0]; |
| } |
| return fGuessedTempNames; |
| } |
| |
| private boolean isLiteralNodeSelected() throws JavaModelException { |
| IExpressionFragment fragment = getSelectedExpression(); |
| if (fragment == null) |
| return false; |
| Expression expression = fragment.getAssociatedExpression(); |
| if (expression == null) |
| return false; |
| switch (expression.getNodeType()) { |
| case ASTNode.BOOLEAN_LITERAL: |
| case ASTNode.CHARACTER_LITERAL: |
| case ASTNode.NULL_LITERAL: |
| case ASTNode.NUMBER_LITERAL: |
| return true; |
| |
| default: |
| return false; |
| } |
| } |
| |
| private boolean isUsedInExplicitConstructorCall() throws JavaModelException { |
| Expression selectedExpression = getSelectedExpression() |
| .getAssociatedExpression(); |
| if (ASTNodes.getParent(selectedExpression, ConstructorInvocation.class) != null) |
| return true; |
| if (ASTNodes.getParent(selectedExpression, |
| SuperConstructorInvocation.class) != null) |
| return true; |
| return false; |
| }*/ |
| |
| public boolean replaceAllOccurrences() { |
| return fReplaceAllOccurrences; |
| } |
| |
| /*private void replaceSelectedExpressionWithTempDeclaration() |
| throws CoreException { |
| ASTRewrite rewrite = fCURewrite.getASTRewrite(); |
| Expression selectedExpression = getSelectedExpression() |
| .getAssociatedExpression(); // whole expression selected |
| |
| Expression initializer = (Expression) rewrite |
| .createMoveTarget(selectedExpression); |
| ASTNode replacement = createTempDeclaration(initializer); // creates a |
| // VariableDeclarationStatement |
| |
| ExpressionStatement parent = (ExpressionStatement) selectedExpression |
| .getParent(); |
| if (ASTNodes.isControlStatementBody(parent.getLocationInParent())) { |
| Block block = rewrite.getAST().newBlock(); |
| block.statements().add(replacement); |
| replacement = block; |
| |
| } |
| rewrite.replace( |
| parent, |
| replacement, |
| fCURewrite |
| .createGroupDescription(RefactoringCoreMessages.ExtractTempRefactoring_declare_local_variable)); |
| } |
| |
| public void setDeclareFinal(boolean declareFinal) { |
| fDeclareFinal = declareFinal; |
| }*/ |
| |
| public void setReplaceAllOccurrences(boolean replaceAllOccurrences) { |
| fReplaceAllOccurrences = replaceAllOccurrences; |
| } |
| |
| public void setTempName(String newName) { |
| fTempName = newName; |
| } |
| |
| /*private boolean shouldReplaceSelectedExpressionWithTempDeclaration() |
| throws JavaModelException { |
| IExpressionFragment selectedFragment = getSelectedExpression(); |
| return selectedFragment.getAssociatedNode().getParent() instanceof ExpressionStatement |
| && selectedFragment.matches(ASTFragmentFactory |
| .createFragmentForFullSubtree(selectedFragment |
| .getAssociatedNode())); |
| } |
| |
| private RefactoringStatus initialize(RefactoringArguments arguments) { |
| final String selection = arguments |
| .getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION); |
| if (selection != null) { |
| 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) { |
| fSelectionStart = offset; |
| fSelectionLength = length; |
| } else |
| return RefactoringStatus |
| .createFatalErrorStatus(Messages |
| .format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, |
| new Object[] { |
| selection, |
| JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION })); |
| } else |
| return RefactoringStatus |
| .createFatalErrorStatus(Messages |
| .format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, |
| JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION)); |
| final String handle = arguments |
| .getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT); |
| if (handle != null) { |
| final IJavaElement element = JavaRefactoringDescriptorUtil |
| .handleToElement(arguments.getProject(), handle, false); |
| if (element == null |
| || !element.exists() |
| || element.getElementType() != IJavaElement.COMPILATION_UNIT) |
| return JavaRefactoringDescriptorUtil.createInputFatalStatus( |
| element, getName(), |
| IJavaRefactorings.EXTRACT_LOCAL_VARIABLE); |
| else |
| fCu = (ISourceModule) element; |
| } else |
| return RefactoringStatus |
| .createFatalErrorStatus(Messages |
| .format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, |
| JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT)); |
| final String name = arguments |
| .getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME); |
| if (name != null && !"".equals(name)) //$NON-NLS-1$ |
| fTempName = name; |
| else |
| return RefactoringStatus |
| .createFatalErrorStatus(Messages |
| .format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, |
| JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME)); |
| final String replace = arguments.getAttribute(ATTRIBUTE_REPLACE); |
| if (replace != null) { |
| fReplaceAllOccurrences = Boolean.valueOf(replace).booleanValue(); |
| } else |
| return RefactoringStatus |
| .createFatalErrorStatus(Messages |
| .format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, |
| ATTRIBUTE_REPLACE)); |
| final String declareFinal = arguments.getAttribute(ATTRIBUTE_FINAL); |
| if (declareFinal != null) { |
| fDeclareFinal = Boolean.valueOf(declareFinal).booleanValue(); |
| } else |
| return RefactoringStatus |
| .createFatalErrorStatus(Messages |
| .format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, |
| ATTRIBUTE_FINAL)); |
| return new RefactoringStatus(); |
| }*/ |
| } |