| /******************************************************************************* |
| * Copyright (c) 2000, 2006 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.jdt.internal.corext.refactoring.rename; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| 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 org.eclipse.text.edits.ReplaceEdit; |
| |
| 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.core.resources.IFile; |
| |
| import org.eclipse.ltk.core.refactoring.Change; |
| import org.eclipse.ltk.core.refactoring.GroupCategorySet; |
| import org.eclipse.ltk.core.refactoring.RefactoringDescriptor; |
| import org.eclipse.ltk.core.refactoring.RefactoringStatus; |
| import org.eclipse.ltk.core.refactoring.TextChange; |
| import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext; |
| import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments; |
| import org.eclipse.ltk.core.refactoring.participants.RenameArguments; |
| |
| import org.eclipse.jdt.core.Flags; |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.IMember; |
| import org.eclipse.jdt.core.IMethod; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.ITypeHierarchy; |
| import org.eclipse.jdt.core.JavaConventions; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.WorkingCopyOwner; |
| import org.eclipse.jdt.core.dom.MethodDeclaration; |
| import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants; |
| import org.eclipse.jdt.core.search.IJavaSearchConstants; |
| import org.eclipse.jdt.core.search.IJavaSearchScope; |
| import org.eclipse.jdt.core.search.MethodDeclarationMatch; |
| import org.eclipse.jdt.core.search.SearchEngine; |
| import org.eclipse.jdt.core.search.SearchMatch; |
| import org.eclipse.jdt.core.search.SearchParticipant; |
| import org.eclipse.jdt.core.search.SearchPattern; |
| import org.eclipse.jdt.core.search.SearchRequestor; |
| |
| import org.eclipse.jdt.internal.corext.refactoring.Checks; |
| import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments; |
| import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptor; |
| import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorComment; |
| import org.eclipse.jdt.internal.corext.refactoring.RefactoringAvailabilityTester; |
| import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages; |
| import org.eclipse.jdt.internal.corext.refactoring.RefactoringScopeFactory; |
| import org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine; |
| import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup; |
| import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext; |
| import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange; |
| import org.eclipse.jdt.internal.corext.refactoring.changes.TextChangeCompatibility; |
| import org.eclipse.jdt.internal.corext.refactoring.code.ScriptableRefactoring; |
| import org.eclipse.jdt.internal.corext.refactoring.delegates.DelegateCreator; |
| import org.eclipse.jdt.internal.corext.refactoring.delegates.DelegateMethodCreator; |
| import org.eclipse.jdt.internal.corext.refactoring.participants.JavaProcessors; |
| import org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil; |
| import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite; |
| import org.eclipse.jdt.internal.corext.refactoring.tagging.IDelegateUpdating; |
| import org.eclipse.jdt.internal.corext.refactoring.tagging.IReferenceUpdating; |
| import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil; |
| import org.eclipse.jdt.internal.corext.refactoring.util.TextChangeManager; |
| import org.eclipse.jdt.internal.corext.util.JavaModelUtil; |
| import org.eclipse.jdt.internal.corext.util.JdtFlags; |
| import org.eclipse.jdt.internal.corext.util.Messages; |
| import org.eclipse.jdt.internal.corext.util.SearchUtils; |
| |
| import org.eclipse.jdt.ui.JavaElementLabels; |
| |
| import org.eclipse.jdt.internal.ui.JavaPlugin; |
| |
| public abstract class RenameMethodProcessor extends JavaRenameProcessor implements IReferenceUpdating, IDelegateUpdating { |
| |
| public static final String ID_RENAME_METHOD= "org.eclipse.jdt.ui.rename.method"; //$NON-NLS-1$ |
| private static final String ATTRIBUTE_REFERENCES= "references"; //$NON-NLS-1$ |
| private static final String ATTRIBUTE_DELEGATE= "delegate"; //$NON-NLS-1$ |
| private static final String ATTRIBUTE_DEPRECATE= "deprecate"; //$NON-NLS-1$ |
| |
| private SearchResultGroup[] fOccurrences; |
| private boolean fUpdateReferences; |
| private IMethod fMethod; |
| private Set/*<IMethod>*/ fMethodsToRename; |
| private TextChangeManager fChangeManager; |
| private WorkingCopyOwner fWorkingCopyOwner; |
| private boolean fIsComposite; |
| private GroupCategorySet fCategorySet; |
| private boolean fDelegateUpdating; |
| private boolean fDelegateDeprecation; |
| protected boolean fInitialized= false; |
| |
| public static final String IDENTIFIER= "org.eclipse.jdt.ui.renameMethodProcessor"; //$NON-NLS-1$ |
| |
| /** |
| * Creates a new rename method processor. |
| * @param method the method, or <code>null</code> if invoked by scripting |
| */ |
| protected RenameMethodProcessor(IMethod method) { |
| this(method, new TextChangeManager(true), null); |
| fIsComposite= false; |
| } |
| |
| /** |
| * Creates a new rename method processor. |
| * <p> |
| * This constructor is only invoked by <code>RenameTypeProcessor</code>. |
| * </p> |
| * |
| * @param method the method |
| * @param manager the change manager |
| * @param categorySet the group category set |
| */ |
| protected RenameMethodProcessor(IMethod method, TextChangeManager manager, GroupCategorySet categorySet) { |
| initialize(method); |
| fChangeManager= manager; |
| fCategorySet= categorySet; |
| fDelegateUpdating= false; |
| fDelegateDeprecation= true; |
| fIsComposite= true; |
| } |
| |
| protected void initialize(IMethod method) { |
| fMethod= method; |
| if (!fInitialized) { |
| if (method != null) |
| setNewElementName(method.getElementName()); |
| fUpdateReferences= true; |
| initializeWorkingCopyOwner(); |
| } |
| } |
| |
| protected void initializeWorkingCopyOwner() { |
| fWorkingCopyOwner= new WorkingCopyOwner() {/*must subclass*/}; |
| } |
| |
| protected void setData(RenameMethodProcessor other) { |
| fUpdateReferences= other.fUpdateReferences; |
| setNewElementName(other.getNewElementName()); |
| } |
| |
| public String getIdentifier() { |
| return IDENTIFIER; |
| } |
| |
| public boolean isApplicable() throws CoreException { |
| return RefactoringAvailabilityTester.isRenameAvailable(fMethod); |
| } |
| |
| public String getProcessorName() { |
| return RefactoringCoreMessages.RenameMethodRefactoring_name; |
| } |
| |
| protected String[] getAffectedProjectNatures() throws CoreException { |
| return JavaProcessors.computeAffectedNatures(fMethod); |
| } |
| |
| public Object[] getElements() { |
| return new Object[] {fMethod}; |
| } |
| |
| protected RenameModifications computeRenameModifications() throws CoreException { |
| RenameModifications result= new RenameModifications(); |
| RenameArguments args= new RenameArguments(getNewElementName(), getUpdateReferences()); |
| for (Iterator iter= fMethodsToRename.iterator(); iter.hasNext();) { |
| IMethod method= (IMethod) iter.next(); |
| result.rename(method, args); |
| } |
| return result; |
| } |
| |
| protected IFile[] getChangedFiles() throws CoreException { |
| return ResourceUtil.getFiles(fChangeManager.getAllCompilationUnits()); |
| } |
| |
| //---- IRenameProcessor ------------------------------------- |
| |
| public final String getCurrentElementName(){ |
| return fMethod.getElementName(); |
| } |
| |
| public final RefactoringStatus checkNewElementName(String newName) { |
| Assert.isNotNull(newName, "new name"); //$NON-NLS-1$ |
| |
| RefactoringStatus status= Checks.checkName(newName, JavaConventions.validateMethodName(newName)); |
| if (status.isOK() && Checks.startsWithUpperCase(newName)) |
| status= RefactoringStatus.createWarningStatus(fIsComposite |
| ? Messages.format(RefactoringCoreMessages.Checks_method_names_lowercase2, new String[] { newName, fMethod.getDeclaringType().getElementName()}) |
| : RefactoringCoreMessages.Checks_method_names_lowercase); |
| |
| if (Checks.isAlreadyNamed(fMethod, newName)) |
| status.addFatalError(fIsComposite |
| ? Messages.format(RefactoringCoreMessages.RenameMethodRefactoring_same_name2, new String[] { newName, fMethod.getDeclaringType().getElementName() } ) |
| : RefactoringCoreMessages.RenameMethodRefactoring_same_name, |
| JavaStatusContext.create(fMethod)); |
| return status; |
| } |
| |
| public Object getNewElement() { |
| return fMethod.getDeclaringType().getMethod(getNewElementName(), fMethod.getParameterTypes()); |
| } |
| |
| public final IMethod getMethod() { |
| return fMethod; |
| } |
| |
| private void initializeMethodsToRename(IProgressMonitor pm) throws CoreException { |
| if (fMethodsToRename == null) |
| fMethodsToRename= new HashSet(Arrays.asList(MethodChecks.getOverriddenMethods(getMethod(), pm))); |
| } |
| |
| protected void setMethodsToRename(IMethod[] methods) { |
| fMethodsToRename= new HashSet(Arrays.asList(methods)); |
| } |
| |
| protected Set getMethodsToRename() { |
| return fMethodsToRename; |
| } |
| |
| //---- IReferenceUpdating ----------------------------------- |
| |
| public boolean canEnableUpdateReferences() { |
| return true; |
| } |
| |
| public final void setUpdateReferences(boolean update) { |
| fUpdateReferences= update; |
| } |
| |
| public boolean getUpdateReferences() { |
| return fUpdateReferences; |
| } |
| |
| //------------------- IDelegateUpdating ---------------------- |
| |
| public boolean canEnableDelegateUpdating() { |
| return true; |
| } |
| |
| public boolean getDelegateUpdating() { |
| return fDelegateUpdating; |
| } |
| |
| public void setDelegateUpdating(boolean updating) { |
| fDelegateUpdating= updating; |
| } |
| |
| public boolean getDeprecateDelegates() { |
| return fDelegateDeprecation; |
| } |
| |
| public void setDeprecateDelegates(boolean deprecate) { |
| fDelegateDeprecation= deprecate; |
| } |
| |
| //----------- preconditions ------------------ |
| |
| public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException { |
| if (fMethod == null || ! fMethod.exists()){ |
| String message= Messages.format(RefactoringCoreMessages.RenameMethodRefactoring_deleted, |
| fMethod.getCompilationUnit().getElementName()); |
| return RefactoringStatus.createFatalErrorStatus(message); |
| } |
| |
| RefactoringStatus result= Checks.checkAvailability(fMethod); |
| if (result.hasFatalError()) |
| return result; |
| result.merge(Checks.checkIfCuBroken(fMethod)); |
| if (JdtFlags.isNative(fMethod)) |
| result.addError(RefactoringCoreMessages.RenameMethodRefactoring_no_native); |
| return result; |
| } |
| |
| protected RefactoringStatus doCheckFinalConditions(IProgressMonitor pm, CheckConditionsContext context) throws CoreException { |
| try{ |
| RefactoringStatus result= new RefactoringStatus(); |
| pm.beginTask("", 9); //$NON-NLS-1$ |
| // TODO workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=40367 |
| if (!Checks.isAvailable(fMethod)) { |
| result.addFatalError(RefactoringCoreMessages.RenameMethodProcessor_is_binary, JavaStatusContext.create(fMethod)); |
| return result; |
| } |
| result.merge(Checks.checkIfCuBroken(fMethod)); |
| if (result.hasFatalError()) |
| return result; |
| pm.setTaskName(RefactoringCoreMessages.RenameMethodRefactoring_taskName_checkingPreconditions); |
| result.merge(checkNewElementName(getNewElementName())); |
| |
| boolean mustAnalyzeShadowing; |
| IMethod[] newNameMethods= searchForDeclarationsOfClashingMethods(new SubProgressMonitor(pm, 1)); |
| if (newNameMethods.length == 0) { |
| mustAnalyzeShadowing= false; |
| pm.worked(1); |
| } else { |
| IType[] outerTypes= searchForOuterTypesOfReferences(newNameMethods, new SubProgressMonitor(pm, 1)); |
| if (outerTypes.length > 0) { |
| //There exists a reference to a clashing method, where the reference is in a nested type. |
| //That nested type could be a type in a ripple method's hierarchy, which could |
| //cause the reference to bind to the new ripple method instead of to |
| //its old binding (a method of an enclosing scope). |
| //-> Getting *more* references than before -> Semantics not preserved. |
| //Example: RenamePrivateMethodTests#testFail6() |
| //TODO: could pass declaringTypes to the RippleMethodFinder and check whether |
| //a hierarchy contains one of outerTypes (or an outer type of an outerType, recursively). |
| mustAnalyzeShadowing= true; |
| |
| } else { |
| boolean hasOldRefsInInnerTypes= true; |
| //TODO: to implement this optimization: |
| //- move search for references to before this check. |
| //- collect references in inner types. |
| //- for each reference, check for all supertypes and their enclosing types |
| //(recursively), whether they declare a rippleMethod |
| if (hasOldRefsInInnerTypes) { |
| //There exists a reference to a ripple method in a nested type |
| //of a type in the hierarchy of any ripple method. |
| //When that reference is renamed, and one of the supertypes of the |
| //nested type declared a method matching the new name, then |
| //the renamed reference will bind to the method in its supertype, |
| //since inherited methods bind stronger than methods from enclosing scopes. |
| //Getting *less* references than before -> Semantics not preserved. |
| //Examples: RenamePrivateMethodTests#testFail2(), RenamePrivateMethodTests#testFail5() |
| mustAnalyzeShadowing= true; |
| } else { |
| mustAnalyzeShadowing= false; |
| } |
| } |
| } |
| |
| initializeMethodsToRename(new SubProgressMonitor(pm, 1)); |
| pm.setTaskName(RefactoringCoreMessages.RenameMethodRefactoring_taskName_searchingForReferences); |
| fOccurrences= getOccurrences(new SubProgressMonitor(pm, 3), result); |
| pm.setTaskName(RefactoringCoreMessages.RenameMethodRefactoring_taskName_checkingPreconditions); |
| |
| if (fUpdateReferences) |
| result.merge(checkRelatedMethods()); |
| |
| result.merge(analyzeCompilationUnits()); //removes CUs with syntax errors |
| pm.worked(1); |
| |
| if (result.hasFatalError()) |
| return result; |
| |
| createChanges(new SubProgressMonitor(pm, 1), result); |
| if (fUpdateReferences & mustAnalyzeShadowing) |
| result.merge(analyzeRenameChanges(new SubProgressMonitor(pm, 1))); |
| else |
| pm.worked(1); |
| |
| return result; |
| } finally{ |
| pm.done(); |
| } |
| } |
| |
| private IType[] searchForOuterTypesOfReferences(IMethod[] newNameMethods, IProgressMonitor pm) throws CoreException { |
| final Set outerTypesOfReferences= new HashSet(); |
| SearchPattern pattern= RefactoringSearchEngine.createOrPattern(newNameMethods, IJavaSearchConstants.REFERENCES); |
| IJavaSearchScope scope= createRefactoringScope(getMethod()); |
| SearchRequestor requestor= new SearchRequestor() { |
| public void acceptSearchMatch(SearchMatch match) throws CoreException { |
| if (RefactoringSearchEngine.isFiltered(match)) |
| return; |
| IMember member= (IMember) match.getElement(); |
| IType declaring= member.getDeclaringType(); |
| if (declaring == null) |
| return; |
| IType outer= declaring.getDeclaringType(); |
| if (outer != null) |
| outerTypesOfReferences.add(declaring); |
| } |
| }; |
| new SearchEngine().search(pattern, SearchUtils.getDefaultSearchParticipants(), |
| scope, requestor, pm); |
| return (IType[]) outerTypesOfReferences.toArray(new IType[outerTypesOfReferences.size()]); |
| } |
| |
| private IMethod[] searchForDeclarationsOfClashingMethods(IProgressMonitor pm) throws CoreException { |
| final List results= new ArrayList(); |
| SearchPattern pattern= createNewMethodPattern(); |
| IJavaSearchScope scope= RefactoringScopeFactory.create(getMethod().getJavaProject()); |
| SearchRequestor requestor= new SearchRequestor() { |
| public void acceptSearchMatch(SearchMatch match) throws CoreException { |
| if (RefactoringSearchEngine.isFiltered(match)) |
| return; |
| Object method= match.getElement(); |
| if (method instanceof IMethod) // check for bug 90138: [refactoring] [rename] Renaming method throws internal exception |
| results.add(method); |
| else |
| JavaPlugin.logErrorMessage("Unexpected element in search match: " + match.toString()); //$NON-NLS-1$ |
| } |
| }; |
| new SearchEngine().search(pattern, SearchUtils.getDefaultSearchParticipants(), scope, requestor, pm); |
| return (IMethod[]) results.toArray(new IMethod[results.size()]); |
| } |
| |
| private SearchPattern createNewMethodPattern() throws JavaModelException { |
| StringBuffer stringPattern= new StringBuffer(getNewElementName()).append('('); |
| int paramCount= getMethod().getParameterNames().length; |
| while (paramCount > 1) { |
| stringPattern.append("*,"); //$NON-NLS-1$ |
| --paramCount; |
| } |
| if (paramCount > 0) |
| stringPattern.append('*'); |
| stringPattern.append(')'); |
| |
| return SearchPattern.createPattern(stringPattern.toString(), IJavaSearchConstants.METHOD, |
| IJavaSearchConstants.DECLARATIONS, SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE); |
| } |
| |
| protected final IJavaSearchScope createRefactoringScope() throws CoreException { |
| return createRefactoringScope(fMethod); |
| } |
| //TODO: shouldn't scope take all ripple methods into account? |
| protected static final IJavaSearchScope createRefactoringScope(IMethod method) throws CoreException { |
| return RefactoringScopeFactory.create(method); |
| } |
| |
| /** */ |
| SearchPattern createOccurrenceSearchPattern() { |
| HashSet methods= new HashSet(fMethodsToRename); |
| methods.add(fMethod); |
| IMethod[] ms= (IMethod[]) methods.toArray(new IMethod[methods.size()]); |
| return RefactoringSearchEngine.createOrPattern(ms, IJavaSearchConstants.ALL_OCCURRENCES); |
| } |
| |
| SearchResultGroup[] getOccurrences(){ |
| return fOccurrences; |
| } |
| |
| /* |
| * XXX made protected to allow overriding and working around bug 39700 |
| */ |
| protected SearchResultGroup[] getOccurrences(IProgressMonitor pm, RefactoringStatus status) throws CoreException { |
| SearchPattern pattern= createOccurrenceSearchPattern(); |
| return RefactoringSearchEngine.search(pattern, createRefactoringScope(), |
| new MethodOccurenceCollector(getMethod().getElementName()), pm, status); |
| } |
| |
| private RefactoringStatus checkRelatedMethods() throws CoreException { |
| RefactoringStatus result= new RefactoringStatus(); |
| for (Iterator iter= fMethodsToRename.iterator(); iter.hasNext(); ) { |
| IMethod method= (IMethod)iter.next(); |
| |
| result.merge(Checks.checkIfConstructorName(method, getNewElementName(), method.getDeclaringType().getElementName())); |
| |
| String[] msgData= new String[]{method.getElementName(), JavaModelUtil.getFullyQualifiedName(method.getDeclaringType())}; |
| if (! method.exists()){ |
| result.addFatalError(Messages.format(RefactoringCoreMessages.RenameMethodRefactoring_not_in_model, msgData)); |
| continue; |
| } |
| if (method.isBinary()) |
| result.addFatalError(Messages.format(RefactoringCoreMessages.RenameMethodRefactoring_no_binary, msgData)); |
| if (method.isReadOnly()) |
| result.addFatalError(Messages.format(RefactoringCoreMessages.RenameMethodRefactoring_no_read_only, msgData)); |
| if (JdtFlags.isNative(method)) |
| result.addError(Messages.format(RefactoringCoreMessages.RenameMethodRefactoring_no_native_1, msgData)); |
| } |
| return result; |
| } |
| |
| private RefactoringStatus analyzeCompilationUnits() throws CoreException { |
| if (fOccurrences.length == 0) |
| return null; |
| |
| RefactoringStatus result= new RefactoringStatus(); |
| fOccurrences= Checks.excludeCompilationUnits(fOccurrences, result); |
| if (result.hasFatalError()) |
| return result; |
| |
| result.merge(Checks.checkCompileErrorsInAffectedFiles(fOccurrences)); |
| |
| return result; |
| } |
| |
| //------- |
| |
| private RefactoringStatus analyzeRenameChanges(IProgressMonitor pm) throws CoreException { |
| ICompilationUnit[] newDeclarationWCs= null; |
| try { |
| pm.beginTask("", 4); //$NON-NLS-1$ |
| RefactoringStatus result= new RefactoringStatus(); |
| ICompilationUnit[] declarationCUs= getDeclarationCUs(); |
| newDeclarationWCs= RenameAnalyzeUtil.createNewWorkingCopies(declarationCUs, |
| fChangeManager, fWorkingCopyOwner, new SubProgressMonitor(pm, 1)); |
| |
| IMethod[] wcOldMethods= new IMethod[fMethodsToRename.size()]; |
| IMethod[] wcNewMethods= new IMethod[fMethodsToRename.size()]; |
| int i= 0; |
| for (Iterator iter= fMethodsToRename.iterator(); iter.hasNext(); i++) { |
| IMethod method= (IMethod) iter.next(); |
| ICompilationUnit newCu= RenameAnalyzeUtil.findWorkingCopyForCu(newDeclarationWCs, method.getCompilationUnit()); |
| IType typeWc= (IType) JavaModelUtil.findInCompilationUnit(newCu, method.getDeclaringType()); |
| if (typeWc == null) |
| continue; |
| wcOldMethods[i]= getMethodInWorkingCopy(method, getCurrentElementName(), typeWc); |
| wcNewMethods[i]= getMethodInWorkingCopy(method, getNewElementName(), typeWc); |
| } |
| |
| // SearchResultGroup[] newOccurrences= findNewOccurrences(newMethods, newDeclarationWCs, new SubProgressMonitor(pm, 3)); |
| SearchResultGroup[] newOccurrences= batchFindNewOccurrences(wcNewMethods, wcOldMethods, newDeclarationWCs, new SubProgressMonitor(pm, 3), result); |
| |
| result.merge(RenameAnalyzeUtil.analyzeRenameChanges2(fChangeManager, fOccurrences, newOccurrences, getNewElementName())); |
| return result; |
| } finally{ |
| pm.done(); |
| if (newDeclarationWCs != null){ |
| for (int i= 0; i < newDeclarationWCs.length; i++) { |
| newDeclarationWCs[i].discardWorkingCopy(); |
| } |
| } |
| } |
| } |
| |
| //Lower memory footprint than batchFindNewOccurrences. Not used because it is too slow. |
| //Final solution is maybe to do searches in chunks of ~ 50 CUs. |
| // private SearchResultGroup[] findNewOccurrences(IMethod[] newMethods, ICompilationUnit[] newDeclarationWCs, IProgressMonitor pm) throws CoreException { |
| // pm.beginTask("", fOccurrences.length * 2); //$NON-NLS-1$ |
| // |
| // SearchPattern refsPattern= RefactoringSearchEngine.createOrPattern(newMethods, IJavaSearchConstants.REFERENCES); |
| // SearchParticipant[] searchParticipants= SearchUtils.getDefaultSearchParticipants(); |
| // IJavaSearchScope scope= RefactoringScopeFactory.create(newMethods); |
| // MethodOccurenceCollector requestor= new MethodOccurenceCollector(getNewElementName()); |
| // SearchEngine searchEngine= new SearchEngine(fWorkingCopyOwner); |
| // |
| // //TODO: should process only references |
| // for (int j= 0; j < fOccurrences.length; j++) { //should be getReferences() |
| // //cut memory peak by holding only one reference CU at a time in memory |
| // ICompilationUnit originalCu= fOccurrences[j].getCompilationUnit(); |
| // ICompilationUnit newWc= null; |
| // try { |
| // ICompilationUnit wc= RenameAnalyzeUtil.findWorkingCopyForCu(newDeclarationWCs, originalCu); |
| // if (wc == null) { |
| // newWc= RenameAnalyzeUtil.createNewWorkingCopy(originalCu, fChangeManager, fWorkingCopyOwner, |
| // new SubProgressMonitor(pm, 1)); |
| // } |
| // searchEngine.search(refsPattern, searchParticipants, scope, requestor, new SubProgressMonitor(pm, 1)); |
| // } finally { |
| // if (newWc != null) |
| // newWc.discardWorkingCopy(); |
| // } |
| // } |
| // SearchResultGroup[] newResults= RefactoringSearchEngine.groupByResource(requestor.getResults()); |
| // pm.done(); |
| // return newResults; |
| // } |
| |
| private SearchResultGroup[] batchFindNewOccurrences(IMethod[] wcNewMethods, final IMethod[] wcOldMethods, ICompilationUnit[] newDeclarationWCs, IProgressMonitor pm, RefactoringStatus status) throws CoreException { |
| pm.beginTask("", 2); //$NON-NLS-1$ |
| |
| SearchPattern refsPattern= RefactoringSearchEngine.createOrPattern(wcNewMethods, IJavaSearchConstants.REFERENCES); |
| SearchParticipant[] searchParticipants= SearchUtils.getDefaultSearchParticipants(); |
| IJavaSearchScope scope= RefactoringScopeFactory.create(wcNewMethods); |
| |
| MethodOccurenceCollector requestor; |
| if (getDelegateUpdating()) { |
| // There will be two new matches inside the delegate(s) (the invocation |
| // and the javadoc) which are OK and must not be reported. |
| // Note that except these ocurrences, the delegate bodies are empty |
| // (as they were created this way). |
| requestor= new MethodOccurenceCollector(getNewElementName()) { |
| public void acceptSearchMatch(SearchMatch match) throws CoreException { |
| for (int i= 0; i < wcOldMethods.length; i++) |
| if (wcOldMethods[i].equals(match.getElement())) |
| return; |
| super.acceptSearchMatch(match); |
| } |
| }; |
| } else |
| requestor= new MethodOccurenceCollector(getNewElementName()); |
| |
| SearchEngine searchEngine= new SearchEngine(fWorkingCopyOwner); |
| |
| ArrayList needWCs= new ArrayList(); |
| HashSet declaringCUs= new HashSet(newDeclarationWCs.length); |
| for (int i= 0; i < newDeclarationWCs.length; i++) |
| declaringCUs.add(newDeclarationWCs[i].getPrimary()); |
| for (int i= 0; i < fOccurrences.length; i++) { |
| ICompilationUnit cu= fOccurrences[i].getCompilationUnit(); |
| if (! declaringCUs.contains(cu)) |
| needWCs.add(cu); |
| } |
| ICompilationUnit[] otherWCs= null; |
| try { |
| otherWCs= RenameAnalyzeUtil.createNewWorkingCopies( |
| (ICompilationUnit[]) needWCs.toArray(new ICompilationUnit[needWCs.size()]), |
| fChangeManager, fWorkingCopyOwner, new SubProgressMonitor(pm, 1)); |
| searchEngine.search(refsPattern, searchParticipants, scope, requestor, new SubProgressMonitor(pm, 1)); |
| } finally { |
| pm.done(); |
| if (otherWCs != null) { |
| for (int i= 0; i < otherWCs.length; i++) { |
| otherWCs[i].discardWorkingCopy(); |
| } |
| } |
| } |
| SearchResultGroup[] newResults= RefactoringSearchEngine.groupByCu(requestor.getResults(), status); |
| return newResults; |
| } |
| |
| private ICompilationUnit[] getDeclarationCUs() { |
| Set cus= new HashSet(); |
| for (Iterator iter= fMethodsToRename.iterator(); iter.hasNext();) { |
| IMethod method= (IMethod) iter.next(); |
| cus.add(method.getCompilationUnit()); |
| } |
| return (ICompilationUnit[]) cus.toArray(new ICompilationUnit[cus.size()]); |
| } |
| |
| private IMethod getMethodInWorkingCopy(IMethod method, String elementName, IType typeWc) throws CoreException{ |
| String[] paramTypeSignatures= method.getParameterTypes(); |
| return typeWc.getMethod(elementName, paramTypeSignatures); |
| } |
| |
| //------- |
| private static IMethod[] classesDeclareMethodName(ITypeHierarchy hier, List classes, IMethod method, String newName) throws CoreException { |
| Set result= new HashSet(); |
| IType type= method.getDeclaringType(); |
| List subtypes= Arrays.asList(hier.getAllSubtypes(type)); |
| |
| int parameterCount= method.getParameterTypes().length; |
| boolean isMethodPrivate= JdtFlags.isPrivate(method); |
| |
| for (Iterator iter= classes.iterator(); iter.hasNext(); ){ |
| IType clazz= (IType) iter.next(); |
| IMethod[] methods= clazz.getMethods(); |
| boolean isSubclass= subtypes.contains(clazz); |
| for (int j= 0; j < methods.length; j++) { |
| IMethod foundMethod= Checks.findMethod(newName, parameterCount, false, new IMethod[] {methods[j]}); |
| if (foundMethod == null) |
| continue; |
| if (isSubclass || type.equals(clazz)) |
| result.add(foundMethod); |
| else if ((! isMethodPrivate) && (! JdtFlags.isPrivate(methods[j]))) |
| result.add(foundMethod); |
| } |
| } |
| return (IMethod[]) result.toArray(new IMethod[result.size()]); |
| } |
| |
| final static IMethod[] hierarchyDeclaresMethodName(IProgressMonitor pm, ITypeHierarchy hierarchy, IMethod method, String newName) throws CoreException { |
| Set result= new HashSet(); |
| IType type= method.getDeclaringType(); |
| IMethod foundMethod= Checks.findMethod(newName, method.getParameterTypes().length, false, type); |
| if (foundMethod != null) |
| result.add(foundMethod); |
| |
| IMethod[] foundInHierarchyClasses= classesDeclareMethodName(hierarchy, Arrays.asList(hierarchy.getAllClasses()), method, newName); |
| if (foundInHierarchyClasses != null) |
| result.addAll(Arrays.asList(foundInHierarchyClasses)); |
| |
| IType[] implementingClasses= hierarchy.getImplementingClasses(type); |
| IMethod[] foundInImplementingClasses= classesDeclareMethodName(hierarchy, Arrays.asList(implementingClasses), method, newName); |
| if (foundInImplementingClasses != null) |
| result.addAll(Arrays.asList(foundInImplementingClasses)); |
| return (IMethod[]) result.toArray(new IMethod[result.size()]); |
| } |
| |
| //-------- changes ----- |
| |
| public Change createChange(IProgressMonitor monitor) throws CoreException { |
| try { |
| final TextChange[] changes= fChangeManager.getAllChanges(); |
| final List list= new ArrayList(changes.length); |
| list.addAll(Arrays.asList(changes)); |
| final Map arguments= new HashMap(); |
| String project= null; |
| IJavaProject javaProject= fMethod.getJavaProject(); |
| if (javaProject != null) |
| project= javaProject.getElementName(); |
| int flags= JavaRefactoringDescriptor.JAR_IMPORTABLE | JavaRefactoringDescriptor.JAR_REFACTORABLE | RefactoringDescriptor.STRUCTURAL_CHANGE; |
| try { |
| if (!Flags.isPrivate(fMethod.getFlags())) |
| flags|= RefactoringDescriptor.MULTI_CHANGE; |
| } catch (JavaModelException exception) { |
| JavaPlugin.log(exception); |
| } |
| final IType declaring= fMethod.getDeclaringType(); |
| try { |
| if (declaring.isAnonymous() || declaring.isLocal()) |
| flags|= JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT; |
| } catch (JavaModelException exception) { |
| JavaPlugin.log(exception); |
| } |
| final String description= Messages.format(RefactoringCoreMessages.RenameMethodProcessor_descriptor_description_short, fMethod.getElementName()); |
| final String header= Messages.format(RefactoringCoreMessages.RenameMethodProcessor_descriptor_description, new String[] { JavaElementLabels.getTextLabel(fMethod, JavaElementLabels.ALL_FULLY_QUALIFIED), getNewElementName()}); |
| final String comment= new JavaRefactoringDescriptorComment(this, header).asString(); |
| final JavaRefactoringDescriptor descriptor= new JavaRefactoringDescriptor(ID_RENAME_METHOD, project, description, comment, arguments, flags); |
| arguments.put(JavaRefactoringDescriptor.ATTRIBUTE_INPUT, descriptor.elementToHandle(fMethod)); |
| arguments.put(JavaRefactoringDescriptor.ATTRIBUTE_NAME, getNewElementName()); |
| arguments.put(ATTRIBUTE_REFERENCES, Boolean.valueOf(fUpdateReferences).toString()); |
| arguments.put(ATTRIBUTE_DELEGATE, Boolean.valueOf(fDelegateUpdating).toString()); |
| arguments.put(ATTRIBUTE_DEPRECATE, Boolean.valueOf(fDelegateDeprecation).toString()); |
| return new DynamicValidationRefactoringChange(descriptor, RefactoringCoreMessages.RenameMethodProcessor_change_name, (Change[]) list.toArray(new Change[list.size()])); |
| } finally { |
| monitor.done(); |
| } |
| } |
| |
| private TextChangeManager createChanges(IProgressMonitor pm, RefactoringStatus status) throws CoreException { |
| if (!fIsComposite) |
| fChangeManager.clear(); |
| addOccurrences(fChangeManager, pm, status); |
| return fChangeManager; |
| } |
| |
| void addOccurrences(TextChangeManager manager, IProgressMonitor pm, RefactoringStatus status) throws CoreException/*thrown in subtype*/{ |
| pm.beginTask("", fOccurrences.length); //$NON-NLS-1$ |
| for (int i= 0; i < fOccurrences.length; i++){ |
| ICompilationUnit cu= fOccurrences[i].getCompilationUnit(); |
| if (cu == null) |
| continue; |
| |
| SearchMatch[] results= fOccurrences[i].getSearchResults(); |
| |
| // Split matches into declaration and non-declaration matches |
| |
| List declarationsInThisCu= new ArrayList(); |
| List referencesInThisCu= new ArrayList(); |
| |
| for (int j= 0; j < results.length; j++) { |
| if (results[j] instanceof MethodDeclarationMatch) |
| declarationsInThisCu.add(results[j]); |
| else |
| referencesInThisCu.add(results[j]); |
| } |
| |
| // First, handle the declarations |
| if (declarationsInThisCu.size() > 0) { |
| |
| if (fDelegateUpdating) { |
| // Update with delegates |
| CompilationUnitRewrite rewrite= new CompilationUnitRewrite(cu); |
| rewrite.setResolveBindings(true); |
| |
| for (Iterator iter= declarationsInThisCu.iterator(); iter.hasNext();) { |
| SearchMatch element= (SearchMatch) iter.next(); |
| MethodDeclaration method= ASTNodeSearchUtil.getMethodDeclarationNode((IMethod) element.getElement(), rewrite.getRoot()); |
| DelegateCreator creator= new DelegateMethodCreator(); |
| creator.setDeclareDeprecated(fDelegateDeprecation); |
| creator.setDeclaration(method); |
| creator.setSourceRewrite(rewrite); |
| creator.setNewElementName(getNewElementName()); |
| creator.prepareDelegate(); |
| creator.createEdit(); |
| } |
| // Need to handle all delegates first as this |
| // creates a completely new change object. |
| TextChange changeForThisCu= rewrite.createChange(); |
| changeForThisCu.setKeepPreviewEdits(true); |
| manager.manage(cu, changeForThisCu); |
| } |
| |
| // Update the normal methods |
| for (Iterator iter= declarationsInThisCu.iterator(); iter.hasNext();) { |
| SearchMatch element= (SearchMatch) iter.next(); |
| simpleUpdate(element, cu, manager.get(cu)); |
| } |
| } |
| |
| // Second, handle references |
| if (fUpdateReferences) { |
| for (Iterator iter= referencesInThisCu.iterator(); iter.hasNext();) { |
| SearchMatch element= (SearchMatch) iter.next(); |
| simpleUpdate(element, cu, manager.get(cu)); |
| } |
| } |
| |
| pm.worked(1); |
| if (pm.isCanceled()) |
| throw new OperationCanceledException(); |
| } |
| pm.done(); |
| } |
| |
| private void simpleUpdate(SearchMatch element, ICompilationUnit cu, TextChange textChange) { |
| String editName= RefactoringCoreMessages.RenameMethodRefactoring_update_occurrence; |
| ReplaceEdit replaceEdit= createReplaceEdit(element, cu); |
| addTextEdit(textChange, editName, replaceEdit); |
| } |
| |
| protected final ReplaceEdit createReplaceEdit(SearchMatch searchResult, ICompilationUnit cu) { |
| if (searchResult.isImplicit()) { // handle Annotation Element references, see bug 94062 |
| StringBuffer sb= new StringBuffer(getNewElementName()); |
| if (JavaCore.INSERT.equals(cu.getJavaProject().getOption(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_ASSIGNMENT_OPERATOR, true))) |
| sb.append(' '); |
| sb.append('='); |
| if (JavaCore.INSERT.equals(cu.getJavaProject().getOption(DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_ASSIGNMENT_OPERATOR, true))) |
| sb.append(' '); |
| return new ReplaceEdit(searchResult.getOffset(), 0, sb.toString()); |
| } else { |
| return new ReplaceEdit(searchResult.getOffset(), searchResult.getLength(), getNewElementName()); |
| } |
| } |
| |
| public RefactoringStatus initialize(RefactoringArguments arguments) { |
| if (arguments instanceof JavaRefactoringArguments) { |
| fInitialized= true; |
| final JavaRefactoringArguments extended= (JavaRefactoringArguments) arguments; |
| final String handle= extended.getAttribute(JavaRefactoringDescriptor.ATTRIBUTE_INPUT); |
| if (handle != null) { |
| final IJavaElement element= JavaRefactoringDescriptor.handleToElement(extended.getProject(), handle, false); |
| final String refactoring= getRefactoring().getName(); |
| if (element instanceof IMethod) { |
| final IMethod method= (IMethod) element; |
| final IType declaring= method.getDeclaringType(); |
| if (declaring != null && declaring.exists()) { |
| final IMethod[] methods= declaring.findMethods(method); |
| if (methods != null && methods.length == 1 && methods[0] != null) { |
| if (!methods[0].exists()) |
| return ScriptableRefactoring.createInputFatalStatus(methods[0], refactoring, ID_RENAME_METHOD); |
| fMethod= methods[0]; |
| initializeWorkingCopyOwner(); |
| } else |
| return ScriptableRefactoring.createInputFatalStatus(null, refactoring, ID_RENAME_METHOD); |
| } else |
| return ScriptableRefactoring.createInputFatalStatus(element, refactoring, ID_RENAME_METHOD); |
| } else |
| return ScriptableRefactoring.createInputFatalStatus(element, refactoring, ID_RENAME_METHOD); |
| } else |
| return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptor.ATTRIBUTE_INPUT)); |
| final String name= extended.getAttribute(JavaRefactoringDescriptor.ATTRIBUTE_NAME); |
| if (name != null && !"".equals(name)) //$NON-NLS-1$ |
| setNewElementName(name); |
| else |
| return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptor.ATTRIBUTE_NAME)); |
| final String references= extended.getAttribute(ATTRIBUTE_REFERENCES); |
| if (references != null) { |
| fUpdateReferences= Boolean.valueOf(references).booleanValue(); |
| } else |
| return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_REFERENCES)); |
| final String delegate= extended.getAttribute(ATTRIBUTE_DELEGATE); |
| if (delegate != null) { |
| fDelegateUpdating= Boolean.valueOf(delegate).booleanValue(); |
| } else |
| return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_DELEGATE)); |
| final String deprecate= extended.getAttribute(ATTRIBUTE_DEPRECATE); |
| if (deprecate != null) { |
| fDelegateDeprecation= Boolean.valueOf(deprecate).booleanValue(); |
| } else |
| return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, ATTRIBUTE_DEPRECATE)); |
| } else |
| return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments); |
| return new RefactoringStatus(); |
| } |
| |
| protected void addTextEdit(TextChange change, String editName, ReplaceEdit replaceEdit) { |
| if (fIsComposite) |
| TextChangeCompatibility.addTextEdit(change, editName, replaceEdit, fCategorySet); |
| else |
| TextChangeCompatibility.addTextEdit(change, editName, replaceEdit); |
| |
| } |
| } |