| /******************************************************************************* |
| * Copyright (c) 2000, 2009 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| * Dmitry Stalnov (dstalnov@fusionone.com) - contributed fixes for: |
| * o Allow 'this' constructor to be inlined |
| * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38093) |
| *******************************************************************************/ |
| 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.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.IMethod; |
| import org.eclipse.dltk.core.ISourceModule; |
| import org.eclipse.dltk.core.SourceRange; |
| import org.eclipse.dltk.core.search.IDLTKSearchConstants; |
| import org.eclipse.dltk.core.search.IDLTKSearchScope; |
| import org.eclipse.dltk.core.search.SearchEngine; |
| import org.eclipse.dltk.core.search.SearchMatch; |
| import org.eclipse.dltk.core.search.SearchPattern; |
| import org.eclipse.dltk.internal.corext.refactoring.CollectingSearchRequestor; |
| import org.eclipse.dltk.internal.corext.refactoring.RefactoringScopeFactory; |
| import org.eclipse.dltk.internal.corext.refactoring.base.ScriptStatusContext; |
| import org.eclipse.dltk.internal.corext.util.SearchUtils; |
| import org.eclipse.dltk.internal.javascript.corext.refactoring.RefactoringCoreMessages; |
| import org.eclipse.dltk.javascript.core.JavaScriptLanguageToolkit; |
| import org.eclipse.dltk.javascript.core.dom.CallExpression; |
| import org.eclipse.dltk.javascript.core.dom.Node; |
| import org.eclipse.dltk.javascript.core.dom.Source; |
| 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.RefactoringUtils; |
| import org.eclipse.dltk.javascript.parser.JavaScriptParserUtil; |
| import org.eclipse.ltk.core.refactoring.RefactoringStatus; |
| |
| |
| /** |
| * A TargetProvider provides all targets that have to be adapted, i.e. all method invocations that should be inlined. |
| */ |
| abstract class TargetProvider { |
| |
| //public abstract void initialize(); |
| |
| public abstract List<ISourceModule> getAffectedSourceModules(RefactoringStatus status, IProgressMonitor pm) throws CoreException; |
| |
| public abstract Source getRoot(ISourceModule unit); |
| |
| public abstract Node[] getAffectedBodyDeclarations(ISourceModule unit, IProgressMonitor pm); |
| |
| // constructor invocation is not an expression but a statement |
| public abstract CallExpression[] getInvocations(Node declaration, IProgressMonitor pm); |
| |
| public abstract int getStatusSeverity(); |
| |
| /*public boolean isSingle() { |
| return false; |
| }*/ |
| |
| public static TargetProvider create(IMethod method) { |
| return new MemberTypeTargetProvider(method); |
| } |
| |
| public static TargetProvider create(ISourceModule cu, int offset, int length) { |
| return new SingleCallTargetProvider(cu, offset, length); |
| } |
| |
| /*public static TargetProvider create(MethodDeclaration declaration) { |
| IMethodBinding method= declaration.resolveBinding(); |
| if (method == null) |
| return new ErrorTargetProvider(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.TargetProvider_method_declaration_not_unique)); |
| ITypeBinding type= method.getDeclaringClass(); |
| if (type.isLocal()) { |
| if (((IType) type.getJavaElement()).isBinary()) { |
| return new ErrorTargetProvider(RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.TargetProvider_cannot_local_method_in_binary)); |
| } else { |
| IType declaringClassOfLocal= (IType) type.getDeclaringClass().getJavaElement(); |
| return new LocalTypeTargetProvider(declaringClassOfLocal.getCompilationUnit(), declaration); |
| } |
| } else { |
| return new MemberTypeTargetProvider(declaration.resolveBinding()); |
| } |
| }*/ |
| |
| static void fastDone(IProgressMonitor pm) { |
| if (pm == null) |
| return; |
| pm.beginTask("", 1); //$NON-NLS-1$ |
| pm.worked(1); |
| pm.done(); |
| } |
| |
| /*static class ErrorTargetProvider extends TargetProvider { |
| private RefactoringStatus fErrorStatus; |
| public ErrorTargetProvider(RefactoringStatus status) { |
| fErrorStatus= status; |
| } |
| public RefactoringStatus checkActivation() throws JavaModelException { |
| return fErrorStatus; |
| } |
| public void initialize() { |
| } |
| public ICompilationUnit[] getAffectedCompilationUnits(RefactoringStatus status, ReferencesInBinaryContext binaryRefs, IProgressMonitor pm) throws JavaModelException { |
| return null; |
| } |
| public BodyDeclaration[] getAffectedBodyDeclarations(ICompilationUnit unit, IProgressMonitor pm) { |
| return null; |
| } |
| public ASTNode[] getInvocations(BodyDeclaration declaration, IProgressMonitor pm) { |
| return null; |
| } |
| public int getStatusSeverity() { |
| return 0; |
| } |
| }*/ |
| |
| static class SingleCallTargetProvider extends TargetProvider { |
| private ISourceModule cu; |
| private Source root; |
| private CallExpression invocation; |
| //private boolean fIterated; |
| public SingleCallTargetProvider(ISourceModule cu, int offset, int length) { |
| this.cu = cu; |
| root = (Source)ASTConverter.convert(JavaScriptParserUtil.parse(cu)); |
| invocation = RefactoringUtils.getFunctionReference(NodeFinder.findNode(root, offset, offset+length)); |
| } |
| /*public void initialize() { |
| fIterated= false; |
| }*/ |
| @Override |
| public List<ISourceModule> getAffectedSourceModules(RefactoringStatus status, IProgressMonitor pm) { |
| List<ISourceModule> res= new ArrayList<ISourceModule>(1); |
| res.add(cu); |
| return res; |
| } |
| |
| @Override |
| public Source getRoot(ISourceModule unit) { |
| Assert.isTrue(unit == cu); |
| return root; |
| } |
| |
| @Override |
| public Node[] getAffectedBodyDeclarations(ISourceModule unit, IProgressMonitor pm) { |
| Assert.isTrue(unit == cu); |
| //if (fIterated) |
| // return new BodyDeclaration[0]; |
| fastDone(pm); |
| return new Node[] { NodeFinder.findEnclosingNode(invocation) }; |
| } |
| |
| @Override |
| public CallExpression[] getInvocations(Node declaration, IProgressMonitor pm) { |
| fastDone(pm); |
| //if (fIterated) |
| // return null; |
| //fIterated= true; |
| return new CallExpression[] { invocation }; |
| } |
| @Override |
| public int getStatusSeverity() { |
| return RefactoringStatus.FATAL; |
| } |
| /*public boolean isSingle() { |
| return true; |
| }*/ |
| } |
| |
| /*private static class BodyData { |
| private List<Identifier> fInvocations; |
| |
| public BodyData() { |
| } |
| public void addInvocation(Identifier node) { |
| if (fInvocations == null) |
| fInvocations= new ArrayList(2); |
| fInvocations.add(node); |
| } |
| public Node[] getInvocations() { |
| return (Node[])fInvocations.toArray(new ASTNode[fInvocations.size()]); |
| } |
| public boolean hasInvocations() { |
| return fInvocations != null && !fInvocations.isEmpty(); |
| } |
| } |
| |
| private static class InvocationFinder extends ASTVisitor { |
| Map/*<BodyDeclaration, BodyData>* result= new HashMap(2); |
| Stack/*<BodyData>* fBodies= new Stack(); |
| BodyData fCurrent; |
| private IMethodBinding fBinding; |
| public InvocationFinder(IMethodBinding binding) { |
| Assert.isNotNull(binding); |
| fBinding= binding.getMethodDeclaration(); |
| Assert.isNotNull(fBinding); |
| } |
| public boolean visit(MethodInvocation node) { |
| if (matches(node.getName().resolveBinding()) && fCurrent != null) { |
| fCurrent.addInvocation(node); |
| } |
| return true; |
| } |
| public boolean visit(SuperMethodInvocation node) { |
| if (matches(node.getName().resolveBinding()) && fCurrent != null) { |
| fCurrent.addInvocation(node); |
| } |
| return true; |
| } |
| public boolean visit(ConstructorInvocation node) { |
| if (matches(node.resolveConstructorBinding()) && fCurrent != null) { |
| fCurrent.addInvocation(node); |
| } |
| return true; |
| } |
| public boolean visit(ClassInstanceCreation node) { |
| if (matches(node.resolveConstructorBinding()) && fCurrent != null) { |
| fCurrent.addInvocation(node); |
| } |
| return true; |
| } |
| public boolean visit(TypeDeclaration node) { |
| return visitType(); |
| } |
| public void endVisit(TypeDeclaration node) { |
| endVisitBodyDeclaration(); |
| } |
| public boolean visit(EnumDeclaration node) { |
| return visitType(); |
| } |
| public void endVisit(EnumDeclaration node) { |
| endVisitBodyDeclaration(); |
| } |
| public boolean visit(AnnotationTypeDeclaration node) { |
| return visitType(); |
| } |
| public void endVisit(AnnotationTypeDeclaration node) { |
| endVisitBodyDeclaration(); |
| } |
| private boolean visitType() { |
| fBodies.add(fCurrent); |
| fCurrent= null; |
| return true; |
| } |
| protected boolean visitNonTypeBodyDeclaration() { |
| fBodies.add(fCurrent); |
| fCurrent= new BodyData(); |
| return true; |
| } |
| protected void endVisitBodyDeclaration() { |
| fCurrent= (BodyData)fBodies.remove(fBodies.size() - 1); |
| } |
| public boolean visit(FieldDeclaration node) { |
| return visitNonTypeBodyDeclaration(); |
| } |
| public void endVisit(FieldDeclaration node) { |
| if (fCurrent.hasInvocations()) { |
| result.put(node, fCurrent); |
| } |
| endVisitBodyDeclaration(); |
| } |
| public boolean visit(MethodDeclaration node) { |
| return visitNonTypeBodyDeclaration(); |
| } |
| public void endVisit(MethodDeclaration node) { |
| if (fCurrent.hasInvocations()) { |
| result.put(node, fCurrent); |
| } |
| endVisitBodyDeclaration(); |
| |
| } |
| public boolean visit(Initializer node) { |
| return visitNonTypeBodyDeclaration(); |
| } |
| public void endVisit(Initializer node) { |
| if (fCurrent.hasInvocations()) { |
| result.put(node, fCurrent); |
| } |
| endVisitBodyDeclaration(); |
| } |
| private boolean matches(IBinding binding) { |
| if (!(binding instanceof IMethodBinding)) |
| return false; |
| return fBinding.isEqualTo(((IMethodBinding)binding).getMethodDeclaration()); |
| } |
| } |
| |
| private static class LocalTypeTargetProvider extends TargetProvider { |
| private ICompilationUnit fCUnit; |
| private MethodDeclaration fDeclaration; |
| private Map fBodies; |
| public LocalTypeTargetProvider(ICompilationUnit unit, MethodDeclaration declaration) { |
| Assert.isNotNull(unit); |
| Assert.isNotNull(declaration); |
| fCUnit= unit; |
| fDeclaration= declaration; |
| } |
| public void initialize() { |
| IMethodBinding methodBinding= fDeclaration.resolveBinding(); |
| InvocationFinder finder; |
| ASTNode type= ASTNodes.getParent(fDeclaration, AbstractTypeDeclaration.class); |
| if (methodBinding.getDeclaringClass().isAnonymous()) { |
| finder= new InvocationFinder(methodBinding); |
| type.accept(finder); |
| } else { |
| //scope of local class is enclosing block |
| ASTNode block= type.getParent().getParent(); |
| finder= new InvocationFinder(methodBinding) { |
| public boolean visit(Block node) { |
| return visitNonTypeBodyDeclaration(); |
| } |
| public void endVisit(Block node) { |
| if (fCurrent.hasInvocations()) { |
| result.put(ASTNodes.getParent(node, BodyDeclaration.class), fCurrent); |
| } |
| endVisitBodyDeclaration(); |
| } |
| }; |
| block.accept(finder); |
| } |
| fBodies= finder.result; |
| } |
| public ICompilationUnit[] getAffectedCompilationUnits(RefactoringStatus status, ReferencesInBinaryContext binaryRefs, IProgressMonitor pm) { |
| fastDone(pm); |
| return new ICompilationUnit[] { fCUnit }; |
| } |
| |
| public BodyDeclaration[] getAffectedBodyDeclarations(ICompilationUnit unit, IProgressMonitor pm) { |
| Assert.isTrue(unit == fCUnit); |
| Set result= fBodies.keySet(); |
| fastDone(pm); |
| return (BodyDeclaration[])result.toArray(new BodyDeclaration[result.size()]); |
| } |
| |
| public ASTNode[] getInvocations(BodyDeclaration declaration, IProgressMonitor pm) { |
| BodyData data= (BodyData)fBodies.get(declaration); |
| Assert.isNotNull(data); |
| fastDone(pm); |
| return data.getInvocations(); |
| } |
| |
| public RefactoringStatus checkActivation() throws JavaModelException { |
| return new RefactoringStatus(); |
| } |
| |
| public int getStatusSeverity() { |
| return RefactoringStatus.ERROR; |
| } |
| }*/ |
| |
| private static class MemberTypeTargetProvider extends TargetProvider { |
| private final IMethod method; |
| private final Map<ISourceModule,Source> roots = new HashMap<ISourceModule,Source>(); |
| private Map<ISourceModule,List<SearchMatch>> calls; |
| private Map<Node,List<CallExpression>> currentBodies; |
| public MemberTypeTargetProvider(IMethod method) { |
| Assert.isNotNull(method); |
| this.method = method; |
| } |
| |
| @Override |
| public List<ISourceModule> getAffectedSourceModules(final RefactoringStatus status, IProgressMonitor pm) throws CoreException { |
| SearchPattern pattern= SearchPattern.createPattern(method, IDLTKSearchConstants.REFERENCES, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE, JavaScriptLanguageToolkit.getDefault()); |
| IDLTKSearchScope scope= RefactoringScopeFactory.create(method, true, false); |
| //SearchPattern pattern= SearchPattern.createPattern(method.getElementName(), IDLTKSearchConstants.METHOD, IDLTKSearchConstants.REFERENCES, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE, JavaScriptLanguageToolkit.getDefault()); |
| //IDLTKSearchScope scope = SearchEngine.createWorkspaceScope(JavaScriptLanguageToolkit.getDefault()); |
| if (calls == null) { |
| calls = new HashMap<ISourceModule,List<SearchMatch>>(); |
| CollectingSearchRequestor requestor= new CollectingSearchRequestor() { |
| public void acceptSearchMatch(SearchMatch match) throws CoreException { |
| if (filterMatch(match)) |
| return; |
| if (match.isInsideDocComment()) |
| return; // TODO: should warn user |
| ISourceModule unit= SearchUtils.getSourceModule(match); |
| if (match.getAccuracy() == SearchMatch.A_INACCURATE) { |
| if (unit != null) { |
| status.addError(RefactoringCoreMessages.TargetProvider_inaccurate_match, |
| ScriptStatusContext.create(unit, new SourceRange(match.getOffset(), match.getLength()))); |
| } else { |
| status.addError(RefactoringCoreMessages.TargetProvider_inaccurate_match); |
| } |
| } else if (unit != null) { |
| List<SearchMatch> list = calls.get(unit); |
| if (list == null) { |
| list = new ArrayList<SearchMatch>(); |
| calls.put(unit,list); |
| } |
| list.add(match); |
| } |
| } |
| }; |
| new SearchEngine().search(pattern, SearchUtils.getDefaultSearchParticipants(), scope, requestor, new SubProgressMonitor(pm, 1)); |
| } |
| return new ArrayList<ISourceModule>(calls.keySet()); |
| } |
| @Override |
| public Source getRoot(ISourceModule unit) { |
| Source root = roots.get(unit); |
| if (root == null) { |
| root = (Source)ASTConverter.convert(JavaScriptParserUtil.parse(unit)); |
| roots.put(unit,root); |
| } |
| return root; |
| } |
| |
| @Override |
| public Node[] getAffectedBodyDeclarations(ISourceModule unit, IProgressMonitor pm) { |
| Source root=roots.get(unit); |
| currentBodies = new HashMap<Node,List<CallExpression>>(); |
| for(Node node : NodeFinder.findNodes(root, calls.get(unit))) { |
| CallExpression expr = RefactoringUtils.getFunctionReference(node); |
| // TODO something with bad references |
| if (expr == null) |
| continue; |
| Node enclosing = NodeFinder.findEnclosingNode(expr); |
| List<CallExpression> list = currentBodies.get(enclosing); |
| if (list == null) { |
| list = new ArrayList<CallExpression>(); |
| currentBodies.put(enclosing,list); |
| } |
| list.add(expr); |
| } |
| Set<Node> result= currentBodies.keySet(); |
| fastDone(pm); |
| return (Node[])result.toArray(new Node[result.size()]); |
| } |
| |
| @Override |
| public CallExpression[] getInvocations(Node declaration, IProgressMonitor pm) { |
| List<CallExpression> list = currentBodies.get(declaration); |
| return list.toArray(new CallExpression[list.size()]); |
| } |
| |
| @Override |
| public int getStatusSeverity() { |
| return RefactoringStatus.ERROR; |
| } |
| } |
| } |