| /******************************************************************************* |
| * 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 |
| *******************************************************************************/ |
| package org.eclipse.wst.jsdt.internal.corext.refactoring.code; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.StringTokenizer; |
| |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.SubProgressMonitor; |
| import org.eclipse.jface.text.BadLocationException; |
| import org.eclipse.jface.text.Document; |
| import org.eclipse.jface.text.IDocument; |
| import org.eclipse.jface.text.IRegion; |
| import org.eclipse.jface.text.TextUtilities; |
| import org.eclipse.ltk.core.refactoring.Change; |
| import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; |
| import org.eclipse.ltk.core.refactoring.RefactoringStatus; |
| import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments; |
| import org.eclipse.text.edits.MalformedTreeException; |
| import org.eclipse.text.edits.RangeMarker; |
| import org.eclipse.text.edits.TextEdit; |
| import org.eclipse.text.edits.TextEditGroup; |
| import org.eclipse.wst.jsdt.core.Flags; |
| import org.eclipse.wst.jsdt.core.IField; |
| import org.eclipse.wst.jsdt.core.IJavaScriptElement; |
| import org.eclipse.wst.jsdt.core.IJavaScriptProject; |
| import org.eclipse.wst.jsdt.core.IJavaScriptUnit; |
| import org.eclipse.wst.jsdt.core.ISourceRange; |
| 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.ASTParser; |
| import org.eclipse.wst.jsdt.core.dom.AbstractTypeDeclaration; |
| import org.eclipse.wst.jsdt.core.dom.AnonymousClassDeclaration; |
| import org.eclipse.wst.jsdt.core.dom.ArrayCreation; |
| import org.eclipse.wst.jsdt.core.dom.ArrayInitializer; |
| import org.eclipse.wst.jsdt.core.dom.ArrayType; |
| import org.eclipse.wst.jsdt.core.dom.Assignment; |
| import org.eclipse.wst.jsdt.core.dom.BodyDeclaration; |
| import org.eclipse.wst.jsdt.core.dom.Expression; |
| import org.eclipse.wst.jsdt.core.dom.FieldAccess; |
| import org.eclipse.wst.jsdt.core.dom.FieldDeclaration; |
| 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.IVariableBinding; |
| import org.eclipse.wst.jsdt.core.dom.ImportDeclaration; |
| import org.eclipse.wst.jsdt.core.dom.JavaScriptUnit; |
| import org.eclipse.wst.jsdt.core.dom.Modifier; |
| import org.eclipse.wst.jsdt.core.dom.Name; |
| import org.eclipse.wst.jsdt.core.dom.ParenthesizedExpression; |
| import org.eclipse.wst.jsdt.core.dom.QualifiedName; |
| import org.eclipse.wst.jsdt.core.dom.SimpleName; |
| import org.eclipse.wst.jsdt.core.dom.SingleVariableDeclaration; |
| import org.eclipse.wst.jsdt.core.dom.SuperMethodInvocation; |
| import org.eclipse.wst.jsdt.core.dom.Type; |
| import org.eclipse.wst.jsdt.core.dom.TypeDeclarationStatement; |
| import org.eclipse.wst.jsdt.core.dom.VariableDeclaration; |
| import org.eclipse.wst.jsdt.core.dom.VariableDeclarationFragment; |
| import org.eclipse.wst.jsdt.core.dom.rewrite.ASTRewrite; |
| import org.eclipse.wst.jsdt.core.dom.rewrite.ListRewrite; |
| import org.eclipse.wst.jsdt.core.refactoring.IJavaScriptRefactorings; |
| import org.eclipse.wst.jsdt.core.refactoring.descriptors.JavaScriptRefactoringDescriptor; |
| 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.Corext; |
| import org.eclipse.wst.jsdt.internal.corext.codemanipulation.ImportReferencesCollector; |
| import org.eclipse.wst.jsdt.internal.corext.dom.ASTNodeFactory; |
| import org.eclipse.wst.jsdt.internal.corext.dom.ASTNodes; |
| import org.eclipse.wst.jsdt.internal.corext.dom.HierarchicalASTVisitor; |
| import org.eclipse.wst.jsdt.internal.corext.dom.NodeFinder; |
| import org.eclipse.wst.jsdt.internal.corext.dom.fragments.ASTFragmentFactory; |
| import org.eclipse.wst.jsdt.internal.corext.dom.fragments.IExpressionFragment; |
| import org.eclipse.wst.jsdt.internal.corext.refactoring.Checks; |
| import org.eclipse.wst.jsdt.internal.corext.refactoring.IRefactoringSearchRequestor; |
| import org.eclipse.wst.jsdt.internal.corext.refactoring.JDTRefactoringDescriptor; |
| import org.eclipse.wst.jsdt.internal.corext.refactoring.JDTRefactoringDescriptorComment; |
| import org.eclipse.wst.jsdt.internal.corext.refactoring.JavaRefactoringArguments; |
| 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.SearchResultGroup; |
| import org.eclipse.wst.jsdt.internal.corext.refactoring.base.RefactoringStatusCodes; |
| import org.eclipse.wst.jsdt.internal.corext.refactoring.changes.CompilationUnitChange; |
| import org.eclipse.wst.jsdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange; |
| import org.eclipse.wst.jsdt.internal.corext.refactoring.structure.ASTNodeSearchUtil; |
| import org.eclipse.wst.jsdt.internal.corext.refactoring.structure.CompilationUnitRewrite; |
| import org.eclipse.wst.jsdt.internal.corext.refactoring.util.ResourceUtil; |
| import org.eclipse.wst.jsdt.internal.corext.refactoring.util.TightSourceRangeComputer; |
| import org.eclipse.wst.jsdt.internal.corext.util.JavaModelUtil; |
| import org.eclipse.wst.jsdt.internal.corext.util.Messages; |
| import org.eclipse.wst.jsdt.internal.corext.util.Strings; |
| import org.eclipse.wst.jsdt.internal.ui.JavaScriptPlugin; |
| import org.eclipse.wst.jsdt.ui.JavaScriptElementLabels; |
| |
| public class InlineConstantRefactoring extends ScriptableRefactoring { |
| |
| private static final String ATTRIBUTE_REPLACE= "replace"; //$NON-NLS-1$ |
| private static final String ATTRIBUTE_REMOVE= "remove"; //$NON-NLS-1$ |
| |
| private static class InlineTargetCompilationUnit { |
| |
| private static class InitializerTraversal extends HierarchicalASTVisitor { |
| |
| private static boolean areInSameType(ASTNode one, ASTNode other) { |
| ASTNode onesContainer= getContainingTypeDeclaration(one); |
| ASTNode othersContainer= getContainingTypeDeclaration(other); |
| |
| if (onesContainer == null || othersContainer == null) |
| return false; |
| |
| ITypeBinding onesContainerBinding= getTypeBindingForTypeDeclaration(onesContainer); |
| ITypeBinding othersContainerBinding= getTypeBindingForTypeDeclaration(othersContainer); |
| |
| Assert.isNotNull(onesContainerBinding); |
| Assert.isNotNull(othersContainerBinding); |
| |
| String onesKey= onesContainerBinding.getKey(); |
| String othersKey= othersContainerBinding.getKey(); |
| |
| if (onesKey == null || othersKey == null) |
| return false; |
| |
| return onesKey.equals(othersKey); |
| } |
| |
| private static boolean isStaticAccess(SimpleName memberName) { |
| IBinding binding= memberName.resolveBinding(); |
| Assert.isTrue(binding instanceof IVariableBinding || binding instanceof IFunctionBinding || binding instanceof ITypeBinding); |
| |
| if (binding instanceof ITypeBinding) |
| return true; |
| |
| if (binding instanceof IVariableBinding) |
| return ((IVariableBinding) binding).isField(); |
| |
| int modifiers= binding.getModifiers(); |
| return Modifier.isStatic(modifiers); |
| } |
| |
| private static ASTNode getContainingTypeDeclaration(ASTNode node) { |
| while (node != null && !(node instanceof AbstractTypeDeclaration) && !(node instanceof AnonymousClassDeclaration)) { |
| node= node.getParent(); |
| } |
| return node; |
| } |
| |
| private static ITypeBinding getTypeBindingForTypeDeclaration(ASTNode declaration) { |
| if (declaration instanceof AnonymousClassDeclaration) |
| return ((AnonymousClassDeclaration) declaration).resolveBinding(); |
| |
| if (declaration instanceof AbstractTypeDeclaration) |
| return ((AbstractTypeDeclaration) declaration).resolveBinding(); |
| |
| Assert.isTrue(false); |
| return null; |
| } |
| |
| private final Expression fInitializer; |
| private ASTRewrite fInitializerRewrite; |
| private final HashSet fStaticImportsInInitializer2; |
| |
| // cache: |
| private Set fNamesDeclaredLocallyAtNewLocation; |
| |
| private final Expression fNewLocation; |
| private final HashSet fStaticImportsInReference; |
| private final CompilationUnitRewrite fNewLocationCuRewrite; |
| |
| public InitializerTraversal(Expression initializer, HashSet staticImportsInInitializer, Expression newLocation, HashSet staticImportsInReference, CompilationUnitRewrite newLocationCuRewrite) { |
| fInitializer= initializer; |
| fInitializerRewrite= ASTRewrite.create(initializer.getAST()); |
| fStaticImportsInInitializer2= staticImportsInInitializer; |
| |
| fNewLocation= newLocation; |
| fStaticImportsInReference= staticImportsInReference; |
| fNewLocationCuRewrite= newLocationCuRewrite; |
| |
| perform(initializer); |
| } |
| |
| /** |
| * @param scope not a TypeDeclaration |
| * @return Set containing Strings representing simple names |
| */ |
| private Set getLocallyDeclaredNames(BodyDeclaration scope) { |
| Assert.isTrue(!(scope instanceof AbstractTypeDeclaration)); |
| |
| final Set result= new HashSet(); |
| |
| if (scope instanceof FieldDeclaration) |
| return result; |
| |
| scope.accept(new HierarchicalASTVisitor() { |
| |
| public boolean visit(AbstractTypeDeclaration node) { |
| Assert.isTrue(node.getParent() instanceof TypeDeclarationStatement); |
| |
| result.add(node.getName().getIdentifier()); |
| return false; |
| } |
| |
| public boolean visit(AnonymousClassDeclaration anonDecl) { |
| return false; |
| } |
| |
| public boolean visit(VariableDeclaration varDecl) { |
| result.add(varDecl.getName().getIdentifier()); |
| return false; |
| } |
| }); |
| return result; |
| } |
| |
| public ASTRewrite getInitializerRewrite() { |
| return fInitializerRewrite; |
| } |
| |
| private void perform(Expression initializer) { |
| initializer.accept(this); |
| if (initializer instanceof FunctionInvocation || initializer instanceof SuperMethodInvocation) { |
| addExplicitTypeArgumentsIfNecessary(initializer); |
| } |
| } |
| |
| private void addExplicitTypeArgumentsIfNecessary(Expression invocation) { |
| if (Invocations.isResolvedTypeInferredFromExpectedType(invocation)) { |
| ASTNode referenceContext= fNewLocation.getParent(); |
| if (! (referenceContext instanceof VariableDeclarationFragment |
| || referenceContext instanceof SingleVariableDeclaration |
| || referenceContext instanceof Assignment)) { |
| IFunctionBinding methodBinding= Invocations.resolveBinding(invocation); |
| ITypeBinding[] typeArguments= methodBinding.getTypeArguments(); |
| ListRewrite typeArgsRewrite= fInitializerRewrite.getListRewrite(invocation, Invocations.getTypeArgumentsProperty(invocation)); |
| for (int i= 0; i < typeArguments.length; i++) { |
| Type typeArgument= fNewLocationCuRewrite.getImportRewrite().addImport(typeArguments[i], fNewLocationCuRewrite.getAST()); |
| fNewLocationCuRewrite.getImportRemover().registerAddedImports(typeArgument); |
| typeArgsRewrite.insertLast(typeArgument, null); |
| } |
| } |
| } |
| } |
| |
| public boolean visit(FieldAccess fieldAccess) { |
| fieldAccess.getExpression().accept(this); |
| return false; |
| } |
| |
| public boolean visit(FunctionInvocation invocation) { |
| if (invocation.getExpression() == null) |
| qualifyUnqualifiedMemberNameIfNecessary(invocation.getName()); |
| else |
| invocation.getExpression().accept(this); |
| |
| for (Iterator it= invocation.arguments().iterator(); it.hasNext();) |
| ((Expression) it.next()).accept(this); |
| |
| return false; |
| } |
| |
| public boolean visit(Name name) { |
| SimpleName leftmost= getLeftmost(name); |
| |
| IBinding leftmostBinding= leftmost.resolveBinding(); |
| if (leftmostBinding instanceof IVariableBinding || leftmostBinding instanceof IFunctionBinding || leftmostBinding instanceof ITypeBinding) { |
| if (shouldUnqualify(leftmost)) |
| unqualifyMemberName(leftmost); |
| else |
| qualifyUnqualifiedMemberNameIfNecessary(leftmost); |
| } |
| |
| if (leftmostBinding instanceof ITypeBinding) { |
| String addedImport= fNewLocationCuRewrite.getImportRewrite().addImport((ITypeBinding) leftmostBinding); |
| fNewLocationCuRewrite.getImportRemover().registerAddedImport(addedImport); |
| } |
| |
| return false; |
| } |
| |
| private void qualifyUnqualifiedMemberNameIfNecessary(SimpleName memberName) { |
| if (shouldQualify(memberName)) |
| qualifyMemberName(memberName); |
| } |
| |
| private boolean shouldUnqualify(SimpleName memberName) { |
| if (areInSameType(memberName, fNewLocation)) |
| return ! mayBeShadowedByLocalDeclaration(memberName); |
| |
| return false; |
| } |
| |
| private void unqualifyMemberName(SimpleName memberName) { |
| if (doesParentQualify(memberName)) |
| fInitializerRewrite.replace(memberName.getParent(), memberName, null); |
| } |
| |
| private boolean shouldQualify(SimpleName memberName) { |
| if (! areInSameType(fInitializer, fNewLocation)) |
| return true; |
| |
| return mayBeShadowedByLocalDeclaration(memberName); |
| } |
| |
| private boolean mayBeShadowedByLocalDeclaration(SimpleName memberName) { |
| return getNamesDeclaredLocallyAtNewLocation().contains(memberName.getIdentifier()); |
| } |
| |
| private Set getNamesDeclaredLocallyAtNewLocation() { |
| if (fNamesDeclaredLocallyAtNewLocation != null) |
| return fNamesDeclaredLocallyAtNewLocation; |
| |
| BodyDeclaration enclosingBodyDecl= (BodyDeclaration) ASTNodes.getParent(fNewLocation, BodyDeclaration.class); |
| Assert.isTrue(!(enclosingBodyDecl instanceof AbstractTypeDeclaration)); |
| |
| return fNamesDeclaredLocallyAtNewLocation= getLocallyDeclaredNames(enclosingBodyDecl); |
| } |
| |
| private void qualifyMemberName(SimpleName memberName) { |
| if (isStaticAccess(memberName)) { |
| IBinding memberBinding= memberName.resolveBinding(); |
| |
| if (memberBinding instanceof IVariableBinding || memberBinding instanceof IFunctionBinding) { |
| if (fStaticImportsInReference.contains(fNewLocation)) { // use static import if reference location used static import |
| importStatically(memberName, memberBinding); |
| return; |
| } else if (fStaticImportsInInitializer2.contains(memberName)) { // use static import if already imported statically in initializer |
| importStatically(memberName, memberBinding); |
| return; |
| } |
| } |
| qualifyToTopLevelClass(memberName); //otherwise: qualify and import non-static |
| } |
| } |
| |
| private void importStatically(SimpleName toImport, IBinding binding) { |
| String newName= fNewLocationCuRewrite.getImportRewrite().addStaticImport(binding); |
| fNewLocationCuRewrite.getImportRemover().registerAddedStaticImport(binding); |
| |
| Name newReference= ASTNodeFactory.newName(fInitializerRewrite.getAST(), newName); |
| fInitializerRewrite.replace(toImport, newReference, null); |
| } |
| |
| private void qualifyToTopLevelClass(SimpleName toQualify) { |
| ITypeBinding declaringClass= getDeclaringClassBinding(toQualify); |
| if (declaringClass == null) |
| return; |
| |
| Type newQualification= fNewLocationCuRewrite.getImportRewrite().addImport(declaringClass, fInitializerRewrite.getAST()); |
| fNewLocationCuRewrite.getImportRemover().registerAddedImports(newQualification); |
| |
| SimpleName newToQualify= (SimpleName) fInitializerRewrite.createMoveTarget(toQualify); |
| Type newType= fInitializerRewrite.getAST().newQualifiedType(newQualification, newToQualify); |
| fInitializerRewrite.replace(toQualify, newType, null); |
| } |
| |
| private static ITypeBinding getDeclaringClassBinding(SimpleName memberName) { |
| |
| IBinding binding= memberName.resolveBinding(); |
| if (binding instanceof IFunctionBinding) |
| return ((IFunctionBinding) binding).getDeclaringClass(); |
| |
| if (binding instanceof IVariableBinding) |
| return ((IVariableBinding) binding).getDeclaringClass(); |
| |
| if (binding instanceof ITypeBinding) |
| return ((ITypeBinding) binding).getDeclaringClass(); |
| |
| Assert.isTrue(false); |
| return null; |
| |
| } |
| |
| } |
| |
| private final Expression fInitializer; |
| private final IJavaScriptUnit fInitializerUnit; |
| private final VariableDeclarationFragment fOriginalDeclaration; |
| |
| /** The references in this compilation unit, represented as AST Nodes in the parsed representation of the compilation unit */ |
| private final Expression[] fReferences; |
| private final VariableDeclarationFragment fDeclarationToRemove; |
| private final CompilationUnitRewrite fCuRewrite; |
| private final TightSourceRangeComputer fSourceRangeComputer; |
| private final HashSet fStaticImportsInInitializer; |
| private final boolean fIs15; |
| |
| private InlineTargetCompilationUnit(CompilationUnitRewrite cuRewrite, Name[] references, InlineConstantRefactoring refactoring, HashSet staticImportsInInitializer) throws JavaScriptModelException { |
| fInitializer= refactoring.getInitializer(); |
| fInitializerUnit= refactoring.getDeclaringCompilationUnit(); |
| |
| fCuRewrite= cuRewrite; |
| fSourceRangeComputer= new TightSourceRangeComputer(); |
| fCuRewrite.getASTRewrite().setTargetSourceRangeComputer(fSourceRangeComputer); |
| if (refactoring.getRemoveDeclaration() && refactoring.getReplaceAllReferences() && cuRewrite.getCu().equals(fInitializerUnit)) |
| fDeclarationToRemove= refactoring.getDeclaration(); |
| else |
| fDeclarationToRemove= null; |
| |
| fOriginalDeclaration= refactoring.getDeclaration(); |
| |
| fReferences= new Expression[references.length]; |
| for (int i= 0; i < references.length; i++) |
| fReferences[i]= getQualifiedReference(references[i]); |
| |
| fIs15= JavaModelUtil.is50OrHigher(cuRewrite.getCu().getJavaScriptProject()); |
| fStaticImportsInInitializer= fIs15 ? staticImportsInInitializer : new HashSet(0); |
| } |
| |
| private static Expression getQualifiedReference(Name fieldName) { |
| if (doesParentQualify(fieldName)) |
| return (Expression) fieldName.getParent(); |
| |
| return fieldName; |
| } |
| |
| private static boolean doesParentQualify(Name fieldName) { |
| ASTNode parent= fieldName.getParent(); |
| Assert.isNotNull(parent); |
| |
| if (parent instanceof FieldAccess && ((FieldAccess) parent).getName() == fieldName) |
| return true; |
| |
| if (parent instanceof QualifiedName && ((QualifiedName) parent).getName() == fieldName) |
| return true; |
| |
| if (parent instanceof FunctionInvocation && ((FunctionInvocation) parent).getName() == fieldName) |
| return true; |
| |
| return false; |
| } |
| |
| public CompilationUnitChange getChange() throws CoreException { |
| for (int i= 0; i < fReferences.length; i++) |
| inlineReference(fReferences[i]); |
| |
| removeConstantDeclarationIfNecessary(); |
| |
| return fCuRewrite.createChange(); |
| } |
| |
| private void inlineReference(Expression reference) throws CoreException { |
| ASTNode importDecl= ASTNodes.getParent(reference, ImportDeclaration.class); |
| if (importDecl != null) |
| return; // don't inline into static imports |
| |
| String modifiedInitializer= prepareInitializerForLocation(reference); |
| if (modifiedInitializer == null) |
| return; |
| |
| TextEditGroup msg= fCuRewrite.createGroupDescription(RefactoringCoreMessages.InlineConstantRefactoring_Inline); |
| Expression newReference= (Expression) fCuRewrite.getASTRewrite().createStringPlaceholder(modifiedInitializer, reference.getNodeType()); |
| |
| if (fInitializer instanceof ArrayInitializer) { |
| ArrayCreation arrayCreation= fCuRewrite.getAST().newArrayCreation(); |
| ArrayType arrayType= (ArrayType) ASTNodeFactory.newType(fCuRewrite.getAST(), fOriginalDeclaration); |
| arrayCreation.setType(arrayType); |
| |
| ArrayInitializer newArrayInitializer= (ArrayInitializer) fCuRewrite.getASTRewrite().createStringPlaceholder(modifiedInitializer, |
| ASTNode.ARRAY_INITIALIZER); |
| arrayCreation.setInitializer(newArrayInitializer); |
| newReference= arrayCreation; |
| |
| ITypeBinding typeToAddToImport= ASTNodes.getType(fOriginalDeclaration).resolveBinding(); |
| fCuRewrite.getImportRewrite().addImport(typeToAddToImport); |
| fCuRewrite.getImportRemover().registerAddedImport(typeToAddToImport.getName()); |
| } |
| |
| if (shouldParenthesizeSubstitute(fInitializer, reference)) { |
| ParenthesizedExpression parenthesized= fCuRewrite.getAST().newParenthesizedExpression(); |
| parenthesized.setExpression(newReference); |
| newReference= parenthesized; |
| } |
| fCuRewrite.getASTRewrite().replace(reference, newReference, msg); |
| fSourceRangeComputer.addTightSourceNode(reference); |
| fCuRewrite.getImportRemover().registerRemovedNode(reference); |
| } |
| |
| private String prepareInitializerForLocation(Expression location) throws CoreException { |
| HashSet staticImportsInReference= new HashSet(); |
| final IJavaScriptProject project= fCuRewrite.getCu().getJavaScriptProject(); |
| if (fIs15) |
| location.accept(new ImportReferencesCollector(project, null, new ArrayList(), staticImportsInReference)); |
| InitializerTraversal traversal= new InitializerTraversal(fInitializer, fStaticImportsInInitializer, location, staticImportsInReference, fCuRewrite); |
| ASTRewrite initializerRewrite= traversal.getInitializerRewrite(); |
| IDocument document= new Document(fInitializerUnit.getBuffer().getContents()); // could reuse document when generating and applying undo edits |
| |
| final RangeMarker marker= new RangeMarker(fInitializer.getStartPosition(), fInitializer.getLength()); |
| TextEdit[] rewriteEdits= initializerRewrite.rewriteAST(document, fInitializerUnit.getJavaScriptProject().getOptions(true)).removeChildren(); |
| marker.addChildren(rewriteEdits); |
| try { |
| marker.apply(document, TextEdit.UPDATE_REGIONS); |
| String rewrittenInitializer= document.get(marker.getOffset(), marker.getLength()); |
| IRegion region= document.getLineInformation(document.getLineOfOffset(marker.getOffset())); |
| int oldIndent= Strings.computeIndentUnits(document.get(region.getOffset(), region.getLength()), project); |
| return Strings.changeIndent(rewrittenInitializer, oldIndent, project, "", TextUtilities.getDefaultLineDelimiter(document)); //$NON-NLS-1$ |
| } catch (MalformedTreeException e) { |
| JavaScriptPlugin.log(e); |
| } catch (BadLocationException e) { |
| JavaScriptPlugin.log(e); |
| } |
| return fInitializerUnit.getBuffer().getText(fInitializer.getStartPosition(), fInitializer.getLength()); |
| } |
| |
| private static boolean shouldParenthesizeSubstitute(Expression substitute, Expression location) { |
| if (substitute instanceof Assignment) // for esthetic reasons |
| return true; |
| else |
| return ASTNodes.substituteMustBeParenthesized(substitute, location); |
| } |
| |
| private void removeConstantDeclarationIfNecessary() throws CoreException { |
| if (fDeclarationToRemove == null) |
| return; |
| |
| FieldDeclaration parentDeclaration= (FieldDeclaration) fDeclarationToRemove.getParent(); |
| ASTNode toRemove; |
| if (parentDeclaration.fragments().size() == 1) |
| toRemove= parentDeclaration; |
| else |
| toRemove= fDeclarationToRemove; |
| |
| TextEditGroup msg= fCuRewrite.createGroupDescription(RefactoringCoreMessages.InlineConstantRefactoring_remove_declaration); |
| fCuRewrite.getASTRewrite().remove(toRemove, msg); |
| fCuRewrite.getImportRemover().registerRemovedNode(toRemove); |
| } |
| } |
| |
| // ---- End InlineTargetCompilationUnit ---------------------------------------------------------------------------------------------- |
| |
| private static SimpleName getLeftmost(Name name) { |
| if (name instanceof SimpleName) |
| return (SimpleName) name; |
| |
| return getLeftmost(((QualifiedName) name).getQualifier()); |
| } |
| |
| private int fSelectionStart; |
| private int fSelectionLength; |
| |
| private IJavaScriptUnit fSelectionCu; |
| private CompilationUnitRewrite fSelectionCuRewrite; |
| private Name fSelectedConstantName; |
| |
| private IField fField; |
| private CompilationUnitRewrite fDeclarationCuRewrite; |
| private VariableDeclarationFragment fDeclaration; |
| private boolean fDeclarationSelected; |
| private boolean fDeclarationSelectedChecked= false; |
| private boolean fInitializerAllStaticFinal; |
| private boolean fInitializerChecked= false; |
| |
| private boolean fRemoveDeclaration= false; |
| private boolean fReplaceAllReferences= true; |
| |
| private CompilationUnitChange[] fChanges; |
| |
| /** |
| * Creates a new inline constant refactoring. |
| * <p> |
| * This constructor is only used by <code>DelegateCreator</code>. |
| * </p> |
| * |
| * @param field the field to inline |
| */ |
| public InlineConstantRefactoring(IField field) { |
| Assert.isNotNull(field); |
| Assert.isTrue(!field.isBinary()); |
| fField= field; |
| } |
| |
| /** |
| * Creates a new inline constant refactoring. |
| * @param unit the compilation unit, or <code>null</code> if invoked by scripting |
| * @param node the compilation unit node, or <code>null</code> if invoked by scripting |
| * @param selectionStart |
| * @param selectionLength |
| */ |
| public InlineConstantRefactoring(IJavaScriptUnit unit, JavaScriptUnit node, int selectionStart, int selectionLength) { |
| Assert.isTrue(selectionStart >= 0); |
| Assert.isTrue(selectionLength >= 0); |
| fSelectionCu= unit; |
| fSelectionStart= selectionStart; |
| fSelectionLength= selectionLength; |
| if (unit != null) |
| initialize(unit, node); |
| } |
| |
| private void initialize(IJavaScriptUnit cu, JavaScriptUnit node) { |
| fSelectionCuRewrite= new CompilationUnitRewrite(cu, node); |
| fSelectedConstantName= findConstantNameNode(); |
| } |
| |
| private Name findConstantNameNode() { |
| ASTNode node= NodeFinder.perform(fSelectionCuRewrite.getRoot(), fSelectionStart, fSelectionLength); |
| if (node == null) |
| return null; |
| if (node instanceof FieldAccess) |
| node= ((FieldAccess) node).getName(); |
| if (!(node instanceof Name)) |
| return null; |
| Name name= (Name) node; |
| IBinding binding= name.resolveBinding(); |
| if (!(binding instanceof IVariableBinding)) |
| return null; |
| IVariableBinding variableBinding= (IVariableBinding) binding; |
| if (!variableBinding.isField() || variableBinding.isEnumConstant()) |
| return null; |
| int modifiers= binding.getModifiers(); |
| if (! (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers))) |
| return null; |
| |
| return name; |
| } |
| |
| public RefactoringStatus checkStaticFinalConstantNameSelected() { |
| if (fSelectedConstantName == null) |
| return RefactoringStatus.createStatus(RefactoringStatus.FATAL, RefactoringCoreMessages.InlineConstantRefactoring_static_final_field, null, Corext.getPluginId(), RefactoringStatusCodes.NOT_STATIC_FINAL_SELECTED, null); |
| |
| return new RefactoringStatus(); |
| } |
| |
| public String getName() { |
| return RefactoringCoreMessages.InlineConstantRefactoring_name; |
| } |
| |
| public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException { |
| try { |
| pm.beginTask("", 3); //$NON-NLS-1$ |
| |
| if (!fSelectionCu.isStructureKnown()) |
| return RefactoringStatus.createStatus(RefactoringStatus.FATAL, RefactoringCoreMessages.InlineConstantRefactoring_syntax_errors, null, Corext.getPluginId(), RefactoringStatusCodes.SYNTAX_ERRORS, null); |
| |
| RefactoringStatus result= checkStaticFinalConstantNameSelected(); |
| if (result.hasFatalError()) |
| return result; |
| |
| result.merge(findField()); |
| if (result.hasFatalError()) |
| return result; |
| pm.worked(1); |
| |
| result.merge(findDeclaration()); |
| if (result.hasFatalError()) |
| return result; |
| pm.worked(1); |
| |
| result.merge(checkInitializer()); |
| if (result.hasFatalError()) |
| return result; |
| pm.worked(1); |
| |
| return result; |
| |
| } finally { |
| pm.done(); |
| } |
| } |
| |
| private RefactoringStatus findField() throws JavaScriptModelException { |
| fField= (IField) ((IVariableBinding) fSelectedConstantName.resolveBinding()).getJavaElement(); |
| if (fField != null && ! fField.exists()) |
| return RefactoringStatus.createStatus(RefactoringStatus.FATAL, RefactoringCoreMessages.InlineConstantRefactoring_local_anonymous_unsupported, null, Corext.getPluginId(), RefactoringStatusCodes.LOCAL_AND_ANONYMOUS_NOT_SUPPORTED, null); |
| |
| return null; |
| } |
| private RefactoringStatus findDeclaration() throws JavaScriptModelException { |
| fDeclarationSelectedChecked= true; |
| fDeclarationSelected= false; |
| ASTNode parent= fSelectedConstantName.getParent(); |
| if (parent instanceof VariableDeclarationFragment) { |
| VariableDeclarationFragment parentDeclaration= (VariableDeclarationFragment) parent; |
| if (parentDeclaration.getName() == fSelectedConstantName) { |
| fDeclarationSelected= true; |
| fDeclarationCuRewrite= fSelectionCuRewrite; |
| fDeclaration= (VariableDeclarationFragment) fSelectedConstantName.getParent(); |
| return null; |
| } |
| } |
| |
| VariableDeclarationFragment declaration= (VariableDeclarationFragment) fSelectionCuRewrite.getRoot().findDeclaringNode(fSelectedConstantName.resolveBinding()); |
| if (declaration != null) { |
| fDeclarationCuRewrite= fSelectionCuRewrite; |
| fDeclaration= declaration; |
| return null; |
| } |
| |
| if (fField.getJavaScriptUnit() == null) |
| return RefactoringStatus.createStatus(RefactoringStatus.FATAL, RefactoringCoreMessages.InlineConstantRefactoring_binary_file, null, Corext.getPluginId(), RefactoringStatusCodes.DECLARED_IN_CLASSFILE, null); |
| |
| fDeclarationCuRewrite= new CompilationUnitRewrite(fField.getJavaScriptUnit()); |
| fDeclaration= ASTNodeSearchUtil.getFieldDeclarationFragmentNode(fField, fDeclarationCuRewrite.getRoot()); |
| return null; |
| } |
| |
| private RefactoringStatus checkInitializer() { |
| Expression initializer= getInitializer(); |
| if (initializer == null) |
| return RefactoringStatus.createStatus(RefactoringStatus.FATAL, RefactoringCoreMessages.InlineConstantRefactoring_blank_finals, null, Corext.getPluginId(), RefactoringStatusCodes.CANNOT_INLINE_BLANK_FINAL, null); |
| |
| fInitializerAllStaticFinal= ConstantChecks.isStaticFinalConstant((IExpressionFragment) ASTFragmentFactory.createFragmentForFullSubtree(initializer)); |
| fInitializerChecked= true; |
| return new RefactoringStatus(); |
| } |
| |
| private VariableDeclarationFragment getDeclaration() throws JavaScriptModelException { |
| return fDeclaration; |
| } |
| |
| private Expression getInitializer() { |
| return fDeclaration.getInitializer(); |
| } |
| |
| private IJavaScriptUnit getDeclaringCompilationUnit() throws JavaScriptModelException { |
| return fField.getJavaScriptUnit(); |
| } |
| |
| public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException { |
| RefactoringStatus result= new RefactoringStatus(); |
| pm.beginTask("", 3); //$NON-NLS-1$ |
| |
| try { |
| List/*<CompilationUnitChange>*/changes= new ArrayList(); |
| HashSet staticImportsInInitializer= new HashSet(); |
| ImportReferencesCollector importReferencesCollector= new ImportReferencesCollector(fField.getJavaScriptProject(), null, new ArrayList(), staticImportsInInitializer); |
| getInitializer().accept(importReferencesCollector); |
| |
| if (getReplaceAllReferences()) { |
| SearchResultGroup[] searchResultGroups= findReferences(pm, result); |
| for (int i= 0; i < searchResultGroups.length; i++) { |
| if (pm.isCanceled()) |
| throw new OperationCanceledException(); |
| SearchResultGroup group= searchResultGroups[i]; |
| IJavaScriptUnit cu= group.getCompilationUnit(); |
| |
| CompilationUnitRewrite cuRewrite= getCuRewrite(cu); |
| Name[] references= extractReferenceNodes(group.getSearchResults(), cuRewrite.getRoot()); |
| InlineTargetCompilationUnit targetCompilationUnit= new InlineTargetCompilationUnit( |
| cuRewrite, references, this, staticImportsInInitializer); |
| changes.add(targetCompilationUnit.getChange()); |
| } |
| |
| } else { |
| Assert.isTrue(! isDeclarationSelected()); |
| InlineTargetCompilationUnit targetForOnlySelectedReference= new InlineTargetCompilationUnit( |
| fSelectionCuRewrite, new Name[] { fSelectedConstantName }, this, staticImportsInInitializer); |
| changes.add(targetForOnlySelectedReference.getChange()); |
| } |
| |
| if (result.hasFatalError()) |
| return result; |
| |
| if (getRemoveDeclaration() && getReplaceAllReferences()) { |
| boolean declarationRemoved= false; |
| for (Iterator iter= changes.iterator(); iter.hasNext();) { |
| CompilationUnitChange change= (CompilationUnitChange) iter.next(); |
| if (change.getCompilationUnit().equals(fDeclarationCuRewrite.getCu())) { |
| declarationRemoved= true; |
| break; |
| } |
| } |
| if (! declarationRemoved) { |
| InlineTargetCompilationUnit targetForDeclaration= new InlineTargetCompilationUnit(fDeclarationCuRewrite, new Name[0], this, staticImportsInInitializer); |
| CompilationUnitChange change= targetForDeclaration.getChange(); |
| if (change != null) |
| changes.add(change); |
| } |
| } |
| |
| IJavaScriptUnit[] cus= new IJavaScriptUnit[changes.size()]; |
| for (int i= 0; i < changes.size(); i++) { |
| CompilationUnitChange change= (CompilationUnitChange) changes.get(i); |
| cus[i]= change.getCompilationUnit(); |
| } |
| result.merge(Checks.validateModifiesFiles(ResourceUtil.getFiles(cus), getValidationContext())); |
| |
| pm.worked(1); |
| |
| fChanges= (CompilationUnitChange[]) changes.toArray(new CompilationUnitChange[changes.size()]); |
| |
| return result; |
| |
| } finally { |
| fSelectionCuRewrite= null; |
| fSelectedConstantName= null; |
| fDeclarationCuRewrite= null; |
| fDeclaration= null; |
| pm.done(); |
| } |
| } |
| |
| private Name[] extractReferenceNodes(SearchMatch[] searchResults, JavaScriptUnit cuNode) { |
| Name[] references= new Name[searchResults.length]; |
| for (int i= 0; i < searchResults.length; i++) |
| references[i]= (Name) NodeFinder.perform(cuNode, searchResults[i].getOffset(), searchResults[i].getLength()); |
| return references; |
| } |
| |
| private CompilationUnitRewrite getCuRewrite(IJavaScriptUnit cu) { |
| CompilationUnitRewrite cuRewrite; |
| if (cu.equals(fSelectionCu)) { |
| cuRewrite= fSelectionCuRewrite; |
| } else if (cu.equals(fField.getJavaScriptUnit())) { |
| cuRewrite= fDeclarationCuRewrite; |
| } else { |
| cuRewrite= new CompilationUnitRewrite(cu); |
| } |
| return cuRewrite; |
| } |
| |
| private SearchResultGroup[] findReferences(IProgressMonitor pm, RefactoringStatus status) throws JavaScriptModelException { |
| final RefactoringSearchEngine2 engine= new RefactoringSearchEngine2(SearchPattern.createPattern(fField, IJavaScriptSearchConstants.REFERENCES)); |
| engine.setFiltering(true, true); |
| engine.setScope(RefactoringScopeFactory.create(fField)); |
| engine.setStatus(status); |
| engine.setRequestor(new IRefactoringSearchRequestor() { |
| public SearchMatch acceptSearchMatch(SearchMatch match) { |
| return match.isInsideDocComment() ? null : match; |
| } |
| }); |
| engine.searchPattern(new SubProgressMonitor(pm, 1)); |
| return (SearchResultGroup[]) engine.getResults(); |
| } |
| |
| public Change createChange(IProgressMonitor pm) throws CoreException { |
| try { |
| pm.beginTask(RefactoringCoreMessages.InlineConstantRefactoring_preview, 2); |
| final Map arguments= new HashMap(); |
| String project= null; |
| IJavaScriptProject javaProject= fSelectionCu.getJavaScriptProject(); |
| if (javaProject != null) |
| project= javaProject.getElementName(); |
| int flags= RefactoringDescriptor.STRUCTURAL_CHANGE | JavaScriptRefactoringDescriptor.JAR_REFACTORING | JavaScriptRefactoringDescriptor.JAR_SOURCE_ATTACHMENT; |
| try { |
| if (!Flags.isPrivate(fField.getFlags())) |
| flags|= RefactoringDescriptor.MULTI_CHANGE; |
| } catch (JavaScriptModelException exception) { |
| JavaScriptPlugin.log(exception); |
| } |
| final String description= Messages.format(RefactoringCoreMessages.InlineConstantRefactoring_descriptor_description_short, fField.getElementName()); |
| final String header= Messages.format(RefactoringCoreMessages.InlineConstantRefactoring_descriptor_description, new String[] { JavaScriptElementLabels.getElementLabel(fField, JavaScriptElementLabels.ALL_FULLY_QUALIFIED), JavaScriptElementLabels.getElementLabel(fField.getParent(), JavaScriptElementLabels.ALL_FULLY_QUALIFIED)}); |
| final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header); |
| comment.addSetting(Messages.format(RefactoringCoreMessages.InlineConstantRefactoring_original_pattern, JavaScriptElementLabels.getElementLabel(fField, JavaScriptElementLabels.ALL_FULLY_QUALIFIED))); |
| if (fRemoveDeclaration) |
| comment.addSetting(RefactoringCoreMessages.InlineConstantRefactoring_remove_declaration); |
| if (fReplaceAllReferences) |
| comment.addSetting(RefactoringCoreMessages.InlineConstantRefactoring_replace_references); |
| final JDTRefactoringDescriptor descriptor= new JDTRefactoringDescriptor(IJavaScriptRefactorings.INLINE_CONSTANT, project, description, comment.asString(), arguments, flags); |
| arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_INPUT, descriptor.elementToHandle(fSelectionCu)); |
| arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_SELECTION, new Integer(fSelectionStart).toString() + " " + new Integer(fSelectionLength).toString()); //$NON-NLS-1$ |
| arguments.put(ATTRIBUTE_REMOVE, Boolean.valueOf(fRemoveDeclaration).toString()); |
| arguments.put(ATTRIBUTE_REPLACE, Boolean.valueOf(fReplaceAllReferences).toString()); |
| return new DynamicValidationRefactoringChange(descriptor, RefactoringCoreMessages.InlineConstantRefactoring_inline, fChanges); |
| } finally { |
| pm.done(); |
| fChanges= null; |
| } |
| } |
| |
| private void checkInvariant() { |
| if (isDeclarationSelected()) |
| Assert.isTrue(fReplaceAllReferences); |
| } |
| |
| public boolean getRemoveDeclaration() { |
| return fRemoveDeclaration; |
| } |
| |
| public boolean getReplaceAllReferences() { |
| checkInvariant(); |
| return fReplaceAllReferences; |
| } |
| |
| public boolean isDeclarationSelected() { |
| Assert.isTrue(fDeclarationSelectedChecked); |
| return fDeclarationSelected; |
| } |
| |
| public boolean isInitializerAllStaticFinal() { |
| Assert.isTrue(fInitializerChecked); |
| return fInitializerAllStaticFinal; |
| } |
| |
| public void setRemoveDeclaration(boolean removeDeclaration) { |
| fRemoveDeclaration= removeDeclaration; |
| } |
| |
| public void setReplaceAllReferences(boolean replaceAllReferences) { |
| fReplaceAllReferences= replaceAllReferences; |
| checkInvariant(); |
| } |
| |
| public RefactoringStatus initialize(final RefactoringArguments arguments) { |
| if (arguments instanceof JavaRefactoringArguments) { |
| final JavaRefactoringArguments extended= (JavaRefactoringArguments) arguments; |
| final String selection= extended.getAttribute(JDTRefactoringDescriptor.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, JDTRefactoringDescriptor.ATTRIBUTE_SELECTION})); |
| } |
| final String handle= extended.getAttribute(JDTRefactoringDescriptor.ATTRIBUTE_INPUT); |
| if (handle != null) { |
| final IJavaScriptElement element= JDTRefactoringDescriptor.handleToElement(extended.getProject(), handle, false); |
| if (element == null || !element.exists()) |
| return createInputFatalStatus(element, IJavaScriptRefactorings.INLINE_CONSTANT); |
| else { |
| if (element instanceof IJavaScriptUnit) { |
| fSelectionCu= (IJavaScriptUnit) element; |
| if (selection == null) |
| return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_SELECTION)); |
| } else if (element instanceof IField) { |
| final IField field= (IField) element; |
| try { |
| final ISourceRange range= field.getNameRange(); |
| if (range != null) { |
| fSelectionStart= range.getOffset(); |
| fSelectionLength= range.getLength(); |
| } else |
| return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, IJavaScriptRefactorings.INLINE_CONSTANT)); |
| } catch (JavaScriptModelException exception) { |
| return createInputFatalStatus(element, IJavaScriptRefactorings.INLINE_CONSTANT); |
| } |
| fSelectionCu= field.getJavaScriptUnit(); |
| } else |
| return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_illegal_argument, new Object[] { handle, JDTRefactoringDescriptor.ATTRIBUTE_INPUT})); |
| final ASTParser parser= ASTParser.newParser(AST.JLS3); |
| parser.setResolveBindings(true); |
| parser.setSource(fSelectionCu); |
| final JavaScriptUnit unit= (JavaScriptUnit) parser.createAST(null); |
| initialize(fSelectionCu, unit); |
| if (checkStaticFinalConstantNameSelected().hasFatalError()) |
| return createInputFatalStatus(element, IJavaScriptRefactorings.INLINE_CONSTANT); |
| } |
| } else |
| return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_INPUT)); |
| final String replace= extended.getAttribute(ATTRIBUTE_REPLACE); |
| if (replace != null) { |
| fReplaceAllReferences= Boolean.valueOf(replace).booleanValue(); |
| } else |
| return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_REPLACE)); |
| final String remove= extended.getAttribute(ATTRIBUTE_REMOVE); |
| if (remove != null) |
| fRemoveDeclaration= Boolean.valueOf(remove).booleanValue(); |
| else |
| return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_REMOVE)); |
| } else |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments); |
| return new RefactoringStatus(); |
| } |
| } |