| /******************************************************************************* |
| * Copyright (c) 2000, 2008 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.wst.jsdt.internal.corext.refactoring.code; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.Stack; |
| |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.SubProgressMonitor; |
| import org.eclipse.ltk.core.refactoring.RefactoringStatus; |
| import org.eclipse.wst.jsdt.core.IFunction; |
| import org.eclipse.wst.jsdt.core.IJavaScriptElement; |
| import org.eclipse.wst.jsdt.core.IJavaScriptUnit; |
| import org.eclipse.wst.jsdt.core.IType; |
| import org.eclipse.wst.jsdt.core.JavaScriptModelException; |
| import org.eclipse.wst.jsdt.core.dom.AST; |
| import org.eclipse.wst.jsdt.core.dom.ASTNode; |
| import org.eclipse.wst.jsdt.core.dom.ASTVisitor; |
| import org.eclipse.wst.jsdt.core.dom.AbstractTypeDeclaration; |
| import org.eclipse.wst.jsdt.core.dom.BodyDeclaration; |
| import org.eclipse.wst.jsdt.core.dom.ClassInstanceCreation; |
| import org.eclipse.wst.jsdt.core.dom.ConstructorInvocation; |
| import org.eclipse.wst.jsdt.core.dom.FieldDeclaration; |
| import org.eclipse.wst.jsdt.core.dom.FunctionDeclaration; |
| import org.eclipse.wst.jsdt.core.dom.FunctionInvocation; |
| import org.eclipse.wst.jsdt.core.dom.IBinding; |
| import org.eclipse.wst.jsdt.core.dom.IFunctionBinding; |
| import org.eclipse.wst.jsdt.core.dom.ITypeBinding; |
| import org.eclipse.wst.jsdt.core.dom.Initializer; |
| import org.eclipse.wst.jsdt.core.dom.SuperMethodInvocation; |
| import org.eclipse.wst.jsdt.core.dom.TypeDeclaration; |
| import org.eclipse.wst.jsdt.core.search.IJavaScriptSearchConstants; |
| import org.eclipse.wst.jsdt.core.search.SearchMatch; |
| import org.eclipse.wst.jsdt.core.search.SearchPattern; |
| import org.eclipse.wst.jsdt.internal.corext.SourceRange; |
| import org.eclipse.wst.jsdt.internal.corext.dom.ASTNodes; |
| import org.eclipse.wst.jsdt.internal.corext.refactoring.IRefactoringSearchRequestor; |
| import org.eclipse.wst.jsdt.internal.corext.refactoring.RefactoringCoreMessages; |
| import org.eclipse.wst.jsdt.internal.corext.refactoring.RefactoringScopeFactory; |
| import org.eclipse.wst.jsdt.internal.corext.refactoring.RefactoringSearchEngine2; |
| import org.eclipse.wst.jsdt.internal.corext.refactoring.base.JavaStatusContext; |
| import org.eclipse.wst.jsdt.internal.corext.refactoring.util.RefactoringASTParser; |
| import org.eclipse.wst.jsdt.internal.corext.util.SearchUtils; |
| |
| /** |
| * A TargetProvider provides all targets that have to be adapted, i.e. all method invocations that should be inlined. |
| */ |
| abstract class TargetProvider { |
| |
| public static final boolean BUG_CORE_130317= true; // https://bugs.eclipse.org/bugs/show_bug.cgi?id=130317 |
| |
| protected SourceProvider fSourceProvider; |
| |
| //TODO: not used... |
| public void setSourceProvider(SourceProvider sourceProvider) { |
| Assert.isNotNull(sourceProvider); |
| fSourceProvider= sourceProvider; |
| } |
| |
| public abstract void initialize(); |
| |
| public abstract IJavaScriptUnit[] getAffectedCompilationUnits(RefactoringStatus status, IProgressMonitor pm) throws JavaScriptModelException; |
| |
| public abstract BodyDeclaration[] getAffectedBodyDeclarations(IJavaScriptUnit unit, IProgressMonitor pm); |
| |
| // constructor invocation is not an expression but a statement |
| public abstract ASTNode[] getInvocations(BodyDeclaration declaration, IProgressMonitor pm); |
| |
| public abstract RefactoringStatus checkActivation() throws JavaScriptModelException; |
| |
| public abstract int getStatusSeverity(); |
| |
| public boolean isSingle() { |
| return false; |
| } |
| |
| public static TargetProvider create(IJavaScriptUnit cu, FunctionInvocation invocation) { |
| return new SingleCallTargetProvider(cu, invocation); |
| } |
| |
| public static TargetProvider create(IJavaScriptUnit cu, SuperMethodInvocation invocation) { |
| return new SingleCallTargetProvider(cu, invocation); |
| } |
| |
| public static TargetProvider create(IJavaScriptUnit cu, ConstructorInvocation invocation) { |
| return new SingleCallTargetProvider(cu, invocation); |
| } |
| |
| public static TargetProvider create(FunctionDeclaration declaration) { |
| IFunctionBinding 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.getJavaScriptUnit(), declaration); |
| } |
| } else { |
| return new MemberTypeTargetProvider(declaration.resolveBinding()); |
| } |
| } |
| |
| public static TargetProvider create(IFunctionBinding methodBinding) { |
| return new MemberTypeTargetProvider(methodBinding); |
| } |
| |
| 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 JavaScriptModelException { |
| return fErrorStatus; |
| } |
| public void initialize() { |
| } |
| public IJavaScriptUnit[] getAffectedCompilationUnits(RefactoringStatus status, IProgressMonitor pm) throws JavaScriptModelException { |
| return null; |
| } |
| public BodyDeclaration[] getAffectedBodyDeclarations(IJavaScriptUnit 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 IJavaScriptUnit fCUnit; |
| private ASTNode fInvocation; |
| private boolean fIterated; |
| public SingleCallTargetProvider(IJavaScriptUnit cu, ASTNode invocation) { |
| Assert.isNotNull(cu); |
| Assert.isNotNull(invocation); |
| Assert.isTrue(Invocations.isInvocation(invocation)); |
| fCUnit= cu; |
| fInvocation= invocation; |
| } |
| public void initialize() { |
| fIterated= false; |
| } |
| public IJavaScriptUnit[] getAffectedCompilationUnits(RefactoringStatus status, IProgressMonitor pm) { |
| return new IJavaScriptUnit[] { fCUnit }; |
| } |
| public BodyDeclaration[] getAffectedBodyDeclarations(IJavaScriptUnit unit, IProgressMonitor pm) { |
| Assert.isTrue(unit == fCUnit); |
| if (fIterated) |
| return new BodyDeclaration[0]; |
| fastDone(pm); |
| return new BodyDeclaration[] { |
| (BodyDeclaration)ASTNodes.getParent(fInvocation, BodyDeclaration.class) |
| }; |
| } |
| |
| public ASTNode[] getInvocations(BodyDeclaration declaration, IProgressMonitor pm) { |
| fastDone(pm); |
| if (fIterated) |
| return null; |
| fIterated= true; |
| return new ASTNode[] { fInvocation }; |
| } |
| public RefactoringStatus checkActivation() throws JavaScriptModelException { |
| return new RefactoringStatus(); |
| } |
| public int getStatusSeverity() { |
| return RefactoringStatus.FATAL; |
| } |
| public boolean isSingle() { |
| return true; |
| } |
| } |
| |
| private static class BodyData { |
| public BodyDeclaration fBody; |
| private List fInvocations; |
| public BodyData(BodyDeclaration declaration) { |
| fBody= declaration; |
| } |
| public void addInvocation(ASTNode node) { |
| if (fInvocations == null) |
| fInvocations= new ArrayList(2); |
| fInvocations.add(node); |
| } |
| public ASTNode[] getInvocations() { |
| return (ASTNode[])fInvocations.toArray(new ASTNode[fInvocations.size()]); |
| } |
| public boolean hasInvocations() { |
| return fInvocations != null && !fInvocations.isEmpty(); |
| } |
| public BodyDeclaration getDeclaration() { |
| return fBody; |
| } |
| } |
| |
| private static class InvocationFinder extends ASTVisitor { |
| Map/*<BodyDeclaration, BodyData>*/ result= new HashMap(2); |
| Stack/*<BodyData>*/ fBodies= new Stack(); |
| BodyData fCurrent; |
| private IFunctionBinding fBinding; |
| public InvocationFinder(IFunctionBinding binding) { |
| Assert.isNotNull(binding); |
| fBinding= binding.getMethodDeclaration(); |
| Assert.isNotNull(fBinding); |
| } |
| public boolean visit(FunctionInvocation 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) { |
| endVisitType(); |
| } |
| |
| private boolean visitType() { |
| fBodies.add(fCurrent); |
| fCurrent= null; |
| return true; |
| } |
| private void endVisitType() { |
| fCurrent= (BodyData)fBodies.remove(fBodies.size() - 1); |
| } |
| public boolean visit(FieldDeclaration node) { |
| fBodies.add(fCurrent); |
| fCurrent= new BodyData(node); |
| return true; |
| } |
| public void endVisit(FieldDeclaration node) { |
| if (fCurrent.hasInvocations()) { |
| result.put(node, fCurrent); |
| } |
| endVisitType(); |
| } |
| public boolean visit(FunctionDeclaration node) { |
| fBodies.add(fCurrent); |
| fCurrent= new BodyData(node); |
| return true; |
| } |
| public void endVisit(FunctionDeclaration node) { |
| if (fCurrent.hasInvocations()) { |
| result.put(node, fCurrent); |
| } |
| endVisitType(); |
| |
| } |
| public boolean visit(Initializer node) { |
| fBodies.add(fCurrent); |
| fCurrent= new BodyData(node); |
| return true; |
| } |
| public void endVisit(Initializer node) { |
| if (fCurrent.hasInvocations()) { |
| result.put(node, fCurrent); |
| } |
| endVisitType(); |
| } |
| private boolean matches(IBinding binding) { |
| if (!(binding instanceof IFunctionBinding)) |
| return false; |
| if (BUG_CORE_130317) |
| return fBinding.getKey().equals(((IFunctionBinding)binding).getMethodDeclaration().getKey()); |
| else |
| return fBinding.isEqualTo(((IFunctionBinding)binding).getMethodDeclaration()); |
| } |
| } |
| |
| private static class LocalTypeTargetProvider extends TargetProvider { |
| private IJavaScriptUnit fCUnit; |
| private FunctionDeclaration fDeclaration; |
| private Map fBodies; |
| public LocalTypeTargetProvider(IJavaScriptUnit unit, FunctionDeclaration declaration) { |
| Assert.isNotNull(unit); |
| Assert.isNotNull(declaration); |
| fCUnit= unit; |
| fDeclaration= declaration; |
| } |
| public void initialize() { |
| InvocationFinder finder= new InvocationFinder(fDeclaration.resolveBinding()); |
| ASTNode type= ASTNodes.getParent(fDeclaration, AbstractTypeDeclaration.class); |
| type.accept(finder); |
| fBodies= finder.result; |
| } |
| public IJavaScriptUnit[] getAffectedCompilationUnits(RefactoringStatus status, IProgressMonitor pm) { |
| fastDone(pm); |
| return new IJavaScriptUnit[] { fCUnit }; |
| } |
| |
| public BodyDeclaration[] getAffectedBodyDeclarations(IJavaScriptUnit 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 JavaScriptModelException { |
| return new RefactoringStatus(); |
| } |
| |
| public int getStatusSeverity() { |
| return RefactoringStatus.ERROR; |
| } |
| } |
| |
| private static class MemberTypeTargetProvider extends TargetProvider { |
| private final IFunctionBinding fMethodBinding; |
| private Map fCurrentBodies; |
| public MemberTypeTargetProvider(IFunctionBinding methodBinding) { |
| Assert.isNotNull(methodBinding); |
| fMethodBinding= methodBinding; |
| } |
| public void initialize() { |
| // do nothing. |
| } |
| |
| public IJavaScriptUnit[] getAffectedCompilationUnits(final RefactoringStatus status, IProgressMonitor pm) throws JavaScriptModelException { |
| IFunction method= (IFunction)fMethodBinding.getJavaElement(); |
| Assert.isTrue(method != null); |
| final RefactoringSearchEngine2 engine= new RefactoringSearchEngine2(SearchPattern.createPattern(method, IJavaScriptSearchConstants.REFERENCES, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE)); |
| engine.setGranularity(RefactoringSearchEngine2.GRANULARITY_COMPILATION_UNIT); |
| engine.setFiltering(true, true); |
| engine.setScope(RefactoringScopeFactory.create(method)); |
| engine.setRequestor(new IRefactoringSearchRequestor() { |
| public SearchMatch acceptSearchMatch(SearchMatch match) { |
| if (match.isInsideDocComment()) |
| return null; |
| if (match.getAccuracy() == SearchMatch.A_INACCURATE) { |
| Object element= match.getElement(); |
| if (element instanceof IJavaScriptElement) { |
| IJavaScriptElement jElement= (IJavaScriptElement)element; |
| IJavaScriptUnit unit= (IJavaScriptUnit)jElement.getAncestor(IJavaScriptElement.JAVASCRIPT_UNIT); |
| if (unit != null) { |
| status.addError(RefactoringCoreMessages.TargetProvider_inaccurate_match, |
| JavaStatusContext.create(unit, new SourceRange(match.getOffset(), match.getLength()))); |
| return null; |
| } |
| } |
| status.addError(RefactoringCoreMessages.TargetProvider_inaccurate_match); |
| return null; |
| } else { |
| return match; |
| } |
| } |
| }); |
| engine.searchPattern(new SubProgressMonitor(pm, 1)); |
| return engine.getAffectedCompilationUnits(); |
| } |
| |
| public BodyDeclaration[] getAffectedBodyDeclarations(IJavaScriptUnit unit, IProgressMonitor pm) { |
| ASTNode root= new RefactoringASTParser(AST.JLS3).parse(unit, true); |
| InvocationFinder finder= new InvocationFinder(fMethodBinding); |
| root.accept(finder); |
| fCurrentBodies= finder.result; |
| Set result= fCurrentBodies.keySet(); |
| fastDone(pm); |
| return (BodyDeclaration[])result.toArray(new BodyDeclaration[result.size()]); |
| } |
| |
| public ASTNode[] getInvocations(BodyDeclaration declaration, IProgressMonitor pm) { |
| BodyData data= (BodyData)fCurrentBodies.get(declaration); |
| Assert.isNotNull(data); |
| fastDone(pm); |
| return data.getInvocations(); |
| } |
| |
| public RefactoringStatus checkActivation() throws JavaScriptModelException { |
| return new RefactoringStatus(); |
| } |
| |
| public int getStatusSeverity() { |
| return RefactoringStatus.ERROR; |
| } |
| } |
| } |