0001: /*******************************************************************************
0002: * Copyright (c) 2000, 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.jdt.internal.corext.refactoring.rename;
0011:
0012: import java.util.ArrayList;
0013: import java.util.Arrays;
0014: import java.util.HashSet;
0015: import java.util.Iterator;
0016: import java.util.List;
0017: import java.util.Set;
0018:
0019: import org.eclipse.text.edits.ReplaceEdit;
0020:
0021: import org.eclipse.core.runtime.Assert;
0022: import org.eclipse.core.runtime.CoreException;
0023: import org.eclipse.core.runtime.IProgressMonitor;
0024: import org.eclipse.core.runtime.OperationCanceledException;
0025: import org.eclipse.core.runtime.SubProgressMonitor;
0026:
0027: import org.eclipse.core.resources.IFile;
0028:
0029: import org.eclipse.ltk.core.refactoring.Change;
0030: import org.eclipse.ltk.core.refactoring.GroupCategorySet;
0031: import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
0032: import org.eclipse.ltk.core.refactoring.RefactoringStatus;
0033: import org.eclipse.ltk.core.refactoring.TextChange;
0034: import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
0035: import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments;
0036: import org.eclipse.ltk.core.refactoring.participants.RenameArguments;
0037:
0038: import org.eclipse.jdt.core.Flags;
0039: import org.eclipse.jdt.core.ICompilationUnit;
0040: import org.eclipse.jdt.core.IJavaElement;
0041: import org.eclipse.jdt.core.IJavaProject;
0042: import org.eclipse.jdt.core.IMember;
0043: import org.eclipse.jdt.core.IMethod;
0044: import org.eclipse.jdt.core.IType;
0045: import org.eclipse.jdt.core.ITypeHierarchy;
0046: import org.eclipse.jdt.core.JavaCore;
0047: import org.eclipse.jdt.core.JavaModelException;
0048: import org.eclipse.jdt.core.WorkingCopyOwner;
0049: import org.eclipse.jdt.core.dom.MethodDeclaration;
0050: import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
0051: import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
0052: import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor;
0053: import org.eclipse.jdt.core.refactoring.descriptors.RenameJavaElementDescriptor;
0054: import org.eclipse.jdt.core.search.IJavaSearchConstants;
0055: import org.eclipse.jdt.core.search.IJavaSearchScope;
0056: import org.eclipse.jdt.core.search.MethodDeclarationMatch;
0057: import org.eclipse.jdt.core.search.SearchEngine;
0058: import org.eclipse.jdt.core.search.SearchMatch;
0059: import org.eclipse.jdt.core.search.SearchParticipant;
0060: import org.eclipse.jdt.core.search.SearchPattern;
0061: import org.eclipse.jdt.core.search.SearchRequestor;
0062:
0063: import org.eclipse.jdt.internal.corext.refactoring.Checks;
0064: import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
0065: import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
0066: import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil;
0067: import org.eclipse.jdt.internal.corext.refactoring.RefactoringAvailabilityTester;
0068: import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
0069: import org.eclipse.jdt.internal.corext.refactoring.RefactoringScopeFactory;
0070: import org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine;
0071: import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup;
0072: import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext;
0073: import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange;
0074: import org.eclipse.jdt.internal.corext.refactoring.changes.TextChangeCompatibility;
0075: import org.eclipse.jdt.internal.corext.refactoring.code.ScriptableRefactoring;
0076: import org.eclipse.jdt.internal.corext.refactoring.delegates.DelegateCreator;
0077: import org.eclipse.jdt.internal.corext.refactoring.delegates.DelegateMethodCreator;
0078: import org.eclipse.jdt.internal.corext.refactoring.participants.JavaProcessors;
0079: import org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil;
0080: import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
0081: import org.eclipse.jdt.internal.corext.refactoring.tagging.IDelegateUpdating;
0082: import org.eclipse.jdt.internal.corext.refactoring.tagging.IReferenceUpdating;
0083: import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil;
0084: import org.eclipse.jdt.internal.corext.refactoring.util.TextChangeManager;
0085: import org.eclipse.jdt.internal.corext.util.JavaConventionsUtil;
0086: import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
0087: import org.eclipse.jdt.internal.corext.util.JdtFlags;
0088: import org.eclipse.jdt.internal.corext.util.Messages;
0089: import org.eclipse.jdt.internal.corext.util.SearchUtils;
0090:
0091: import org.eclipse.jdt.ui.JavaElementLabels;
0092: import org.eclipse.jdt.ui.refactoring.IRefactoringProcessorIds;
0093:
0094: import org.eclipse.jdt.internal.ui.JavaPlugin;
0095: import org.eclipse.jdt.internal.ui.refactoring.RefactoringSaveHelper;
0096:
0097: public abstract class RenameMethodProcessor extends JavaRenameProcessor
0098: implements IReferenceUpdating, IDelegateUpdating {
0099:
0100: private static final String ATTRIBUTE_DELEGATE = "delegate"; //$NON-NLS-1$
0101: private static final String ATTRIBUTE_DEPRECATE = "deprecate"; //$NON-NLS-1$
0102:
0103: private SearchResultGroup[] fOccurrences;
0104: private boolean fUpdateReferences;
0105: private IMethod fMethod;
0106: private Set/*<IMethod>*/fMethodsToRename;
0107: private TextChangeManager fChangeManager;
0108: private WorkingCopyOwner fWorkingCopyOwner;
0109: private boolean fIsComposite;
0110: private GroupCategorySet fCategorySet;
0111: private boolean fDelegateUpdating;
0112: private boolean fDelegateDeprecation;
0113: protected boolean fInitialized = false;
0114:
0115: /**
0116: * Creates a new rename method processor.
0117: * @param method the method, or <code>null</code> if invoked by scripting
0118: */
0119: protected RenameMethodProcessor(IMethod method) {
0120: this (method, new TextChangeManager(true), null);
0121: fIsComposite = false;
0122: }
0123:
0124: /**
0125: * Creates a new rename method processor.
0126: * <p>
0127: * This constructor is only invoked by <code>RenameTypeProcessor</code>.
0128: * </p>
0129: *
0130: * @param method the method
0131: * @param manager the change manager
0132: * @param categorySet the group category set
0133: */
0134: protected RenameMethodProcessor(IMethod method,
0135: TextChangeManager manager, GroupCategorySet categorySet) {
0136: initialize(method);
0137: fChangeManager = manager;
0138: fCategorySet = categorySet;
0139: fDelegateUpdating = false;
0140: fDelegateDeprecation = true;
0141: fIsComposite = true;
0142: }
0143:
0144: protected void initialize(IMethod method) {
0145: fMethod = method;
0146: if (!fInitialized) {
0147: if (method != null)
0148: setNewElementName(method.getElementName());
0149: fUpdateReferences = true;
0150: initializeWorkingCopyOwner();
0151: }
0152: }
0153:
0154: protected void initializeWorkingCopyOwner() {
0155: fWorkingCopyOwner = new WorkingCopyOwner() {/*must subclass*/
0156: };
0157: }
0158:
0159: protected void setData(RenameMethodProcessor other) {
0160: fUpdateReferences = other.fUpdateReferences;
0161: setNewElementName(other.getNewElementName());
0162: }
0163:
0164: public String getIdentifier() {
0165: return IRefactoringProcessorIds.RENAME_METHOD_PROCESSOR;
0166: }
0167:
0168: public boolean isApplicable() throws CoreException {
0169: return RefactoringAvailabilityTester.isRenameAvailable(fMethod);
0170: }
0171:
0172: public String getProcessorName() {
0173: return RefactoringCoreMessages.RenameMethodRefactoring_name;
0174: }
0175:
0176: protected String[] getAffectedProjectNatures() throws CoreException {
0177: return JavaProcessors.computeAffectedNatures(fMethod);
0178: }
0179:
0180: public Object[] getElements() {
0181: return new Object[] { fMethod };
0182: }
0183:
0184: protected RenameModifications computeRenameModifications()
0185: throws CoreException {
0186: RenameModifications result = new RenameModifications();
0187: RenameArguments args = new RenameArguments(getNewElementName(),
0188: getUpdateReferences());
0189: for (Iterator iter = fMethodsToRename.iterator(); iter
0190: .hasNext();) {
0191: IMethod method = (IMethod) iter.next();
0192: result.rename(method, args);
0193: }
0194: return result;
0195: }
0196:
0197: protected IFile[] getChangedFiles() throws CoreException {
0198: return ResourceUtil.getFiles(fChangeManager
0199: .getAllCompilationUnits());
0200: }
0201:
0202: public int getSaveMode() {
0203: return RefactoringSaveHelper.SAVE_NON_JAVA_UPDATES;
0204: }
0205:
0206: //---- INameUpdating -------------------------------------
0207:
0208: public final String getCurrentElementName() {
0209: return fMethod.getElementName();
0210: }
0211:
0212: public final RefactoringStatus checkNewElementName(String newName) {
0213: Assert.isNotNull(newName, "new name"); //$NON-NLS-1$
0214:
0215: RefactoringStatus status = Checks.checkName(newName,
0216: JavaConventionsUtil
0217: .validateMethodName(newName, fMethod));
0218: if (status.isOK() && Checks.startsWithUpperCase(newName))
0219: status = RefactoringStatus
0220: .createWarningStatus(fIsComposite ? Messages
0221: .format(
0222: RefactoringCoreMessages.Checks_method_names_lowercase2,
0223: new String[] {
0224: newName,
0225: fMethod.getDeclaringType()
0226: .getElementName() })
0227: : RefactoringCoreMessages.Checks_method_names_lowercase);
0228:
0229: if (Checks.isAlreadyNamed(fMethod, newName))
0230: status
0231: .addFatalError(
0232: fIsComposite ? Messages
0233: .format(
0234: RefactoringCoreMessages.RenameMethodRefactoring_same_name2,
0235: new String[] {
0236: newName,
0237: fMethod
0238: .getDeclaringType()
0239: .getElementName() })
0240: : RefactoringCoreMessages.RenameMethodRefactoring_same_name,
0241: JavaStatusContext.create(fMethod));
0242: return status;
0243: }
0244:
0245: public Object getNewElement() {
0246: return fMethod.getDeclaringType().getMethod(
0247: getNewElementName(), fMethod.getParameterTypes());
0248: }
0249:
0250: public final IMethod getMethod() {
0251: return fMethod;
0252: }
0253:
0254: private void initializeMethodsToRename(IProgressMonitor pm)
0255: throws CoreException {
0256: if (fMethodsToRename == null)
0257: fMethodsToRename = new HashSet(Arrays.asList(MethodChecks
0258: .getOverriddenMethods(getMethod(), pm)));
0259: }
0260:
0261: protected void setMethodsToRename(IMethod[] methods) {
0262: fMethodsToRename = new HashSet(Arrays.asList(methods));
0263: }
0264:
0265: protected Set getMethodsToRename() {
0266: return fMethodsToRename;
0267: }
0268:
0269: //---- IReferenceUpdating -----------------------------------
0270:
0271: public boolean canEnableUpdateReferences() {
0272: return true;
0273: }
0274:
0275: public final void setUpdateReferences(boolean update) {
0276: fUpdateReferences = update;
0277: }
0278:
0279: public boolean getUpdateReferences() {
0280: return fUpdateReferences;
0281: }
0282:
0283: //------------------- IDelegateUpdating ----------------------
0284:
0285: public boolean canEnableDelegateUpdating() {
0286: return true;
0287: }
0288:
0289: public boolean getDelegateUpdating() {
0290: return fDelegateUpdating;
0291: }
0292:
0293: public void setDelegateUpdating(boolean updating) {
0294: fDelegateUpdating = updating;
0295: }
0296:
0297: public boolean getDeprecateDelegates() {
0298: return fDelegateDeprecation;
0299: }
0300:
0301: public void setDeprecateDelegates(boolean deprecate) {
0302: fDelegateDeprecation = deprecate;
0303: }
0304:
0305: //----------- preconditions ------------------
0306:
0307: public RefactoringStatus checkInitialConditions(IProgressMonitor pm)
0308: throws CoreException {
0309: if (!fMethod.exists()) {
0310: String message = Messages
0311: .format(
0312: RefactoringCoreMessages.RenameMethodRefactoring_deleted,
0313: fMethod.getCompilationUnit()
0314: .getElementName());
0315: return RefactoringStatus.createFatalErrorStatus(message);
0316: }
0317:
0318: RefactoringStatus result = Checks.checkAvailability(fMethod);
0319: if (result.hasFatalError())
0320: return result;
0321: result.merge(Checks.checkIfCuBroken(fMethod));
0322: if (JdtFlags.isNative(fMethod))
0323: result
0324: .addError(RefactoringCoreMessages.RenameMethodRefactoring_no_native);
0325: return result;
0326: }
0327:
0328: protected RefactoringStatus doCheckFinalConditions(
0329: IProgressMonitor pm, CheckConditionsContext context)
0330: throws CoreException {
0331: try {
0332: RefactoringStatus result = new RefactoringStatus();
0333: pm.beginTask("", 9); //$NON-NLS-1$
0334: // TODO workaround for https://bugs.eclipse.org/bugs/show_bug.cgi?id=40367
0335: if (!Checks.isAvailable(fMethod)) {
0336: result
0337: .addFatalError(
0338: RefactoringCoreMessages.RenameMethodProcessor_is_binary,
0339: JavaStatusContext.create(fMethod));
0340: return result;
0341: }
0342: result.merge(Checks.checkIfCuBroken(fMethod));
0343: if (result.hasFatalError())
0344: return result;
0345: pm
0346: .setTaskName(RefactoringCoreMessages.RenameMethodRefactoring_taskName_checkingPreconditions);
0347: result.merge(checkNewElementName(getNewElementName()));
0348: if (result.hasFatalError())
0349: return result;
0350:
0351: boolean mustAnalyzeShadowing;
0352: IMethod[] newNameMethods = searchForDeclarationsOfClashingMethods(new SubProgressMonitor(
0353: pm, 1));
0354: if (newNameMethods.length == 0) {
0355: mustAnalyzeShadowing = false;
0356: pm.worked(1);
0357: } else {
0358: IType[] outerTypes = searchForOuterTypesOfReferences(
0359: newNameMethods, new SubProgressMonitor(pm, 1));
0360: if (outerTypes.length > 0) {
0361: //There exists a reference to a clashing method, where the reference is in a nested type.
0362: //That nested type could be a type in a ripple method's hierarchy, which could
0363: //cause the reference to bind to the new ripple method instead of to
0364: //its old binding (a method of an enclosing scope).
0365: //-> Getting *more* references than before -> Semantics not preserved.
0366: //Example: RenamePrivateMethodTests#testFail6()
0367: //TODO: could pass declaringTypes to the RippleMethodFinder and check whether
0368: //a hierarchy contains one of outerTypes (or an outer type of an outerType, recursively).
0369: mustAnalyzeShadowing = true;
0370:
0371: } else {
0372: boolean hasOldRefsInInnerTypes = true;
0373: //TODO: to implement this optimization:
0374: //- move search for references to before this check.
0375: //- collect references in inner types.
0376: //- for each reference, check for all supertypes and their enclosing types
0377: //(recursively), whether they declare a rippleMethod
0378: if (hasOldRefsInInnerTypes) {
0379: //There exists a reference to a ripple method in a nested type
0380: //of a type in the hierarchy of any ripple method.
0381: //When that reference is renamed, and one of the supertypes of the
0382: //nested type declared a method matching the new name, then
0383: //the renamed reference will bind to the method in its supertype,
0384: //since inherited methods bind stronger than methods from enclosing scopes.
0385: //Getting *less* references than before -> Semantics not preserved.
0386: //Examples: RenamePrivateMethodTests#testFail2(), RenamePrivateMethodTests#testFail5()
0387: mustAnalyzeShadowing = true;
0388: } else {
0389: mustAnalyzeShadowing = false;
0390: }
0391: }
0392: }
0393:
0394: initializeMethodsToRename(new SubProgressMonitor(pm, 1));
0395: pm
0396: .setTaskName(RefactoringCoreMessages.RenameMethodRefactoring_taskName_searchingForReferences);
0397: fOccurrences = getOccurrences(
0398: new SubProgressMonitor(pm, 3), result);
0399: pm
0400: .setTaskName(RefactoringCoreMessages.RenameMethodRefactoring_taskName_checkingPreconditions);
0401:
0402: if (fUpdateReferences)
0403: result.merge(checkRelatedMethods());
0404:
0405: result.merge(analyzeCompilationUnits()); //removes CUs with syntax errors
0406: pm.worked(1);
0407:
0408: if (result.hasFatalError())
0409: return result;
0410:
0411: createChanges(new SubProgressMonitor(pm, 1), result);
0412: if (fUpdateReferences & mustAnalyzeShadowing)
0413: result
0414: .merge(analyzeRenameChanges(new SubProgressMonitor(
0415: pm, 1)));
0416: else
0417: pm.worked(1);
0418:
0419: return result;
0420: } finally {
0421: pm.done();
0422: }
0423: }
0424:
0425: private IType[] searchForOuterTypesOfReferences(
0426: IMethod[] newNameMethods, IProgressMonitor pm)
0427: throws CoreException {
0428: final Set outerTypesOfReferences = new HashSet();
0429: SearchPattern pattern = RefactoringSearchEngine
0430: .createOrPattern(newNameMethods,
0431: IJavaSearchConstants.REFERENCES);
0432: IJavaSearchScope scope = createRefactoringScope(getMethod());
0433: SearchRequestor requestor = new SearchRequestor() {
0434: public void acceptSearchMatch(SearchMatch match)
0435: throws CoreException {
0436: IMember member = (IMember) match.getElement();
0437: IType declaring = member.getDeclaringType();
0438: if (declaring == null)
0439: return;
0440: IType outer = declaring.getDeclaringType();
0441: if (outer != null)
0442: outerTypesOfReferences.add(declaring);
0443: }
0444: };
0445: new SearchEngine().search(pattern, SearchUtils
0446: .getDefaultSearchParticipants(), scope, requestor, pm);
0447: return (IType[]) outerTypesOfReferences
0448: .toArray(new IType[outerTypesOfReferences.size()]);
0449: }
0450:
0451: private IMethod[] searchForDeclarationsOfClashingMethods(
0452: IProgressMonitor pm) throws CoreException {
0453: final List results = new ArrayList();
0454: SearchPattern pattern = createNewMethodPattern();
0455: IJavaSearchScope scope = RefactoringScopeFactory
0456: .create(getMethod().getJavaProject());
0457: SearchRequestor requestor = new SearchRequestor() {
0458: public void acceptSearchMatch(SearchMatch match)
0459: throws CoreException {
0460: Object method = match.getElement();
0461: if (method instanceof IMethod) // check for bug 90138: [refactoring] [rename] Renaming method throws internal exception
0462: results.add(method);
0463: else
0464: JavaPlugin
0465: .logErrorMessage("Unexpected element in search match: " + match.toString()); //$NON-NLS-1$
0466: }
0467: };
0468: new SearchEngine().search(pattern, SearchUtils
0469: .getDefaultSearchParticipants(), scope, requestor, pm);
0470: return (IMethod[]) results.toArray(new IMethod[results.size()]);
0471: }
0472:
0473: private SearchPattern createNewMethodPattern()
0474: throws JavaModelException {
0475: StringBuffer stringPattern = new StringBuffer(
0476: getNewElementName()).append('(');
0477: int paramCount = getMethod().getNumberOfParameters();
0478: for (int i = 0; i < paramCount; i++) {
0479: if (i > 0)
0480: stringPattern.append(',');
0481: stringPattern.append('*');
0482: }
0483: stringPattern.append(')');
0484:
0485: return SearchPattern.createPattern(stringPattern.toString(),
0486: IJavaSearchConstants.METHOD,
0487: IJavaSearchConstants.DECLARATIONS,
0488: SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE);
0489: }
0490:
0491: protected final IJavaSearchScope createRefactoringScope()
0492: throws CoreException {
0493: return createRefactoringScope(fMethod);
0494: }
0495:
0496: //TODO: shouldn't scope take all ripple methods into account?
0497: protected static final IJavaSearchScope createRefactoringScope(
0498: IMethod method) throws CoreException {
0499: return RefactoringScopeFactory.create(method);
0500: }
0501:
0502: SearchPattern createOccurrenceSearchPattern() {
0503: HashSet methods = new HashSet(fMethodsToRename);
0504: methods.add(fMethod);
0505: IMethod[] ms = (IMethod[]) methods.toArray(new IMethod[methods
0506: .size()]);
0507: return RefactoringSearchEngine.createOrPattern(ms,
0508: IJavaSearchConstants.ALL_OCCURRENCES);
0509: }
0510:
0511: SearchResultGroup[] getOccurrences() {
0512: return fOccurrences;
0513: }
0514:
0515: /*
0516: * XXX made protected to allow overriding and working around bug 39700
0517: */
0518: protected SearchResultGroup[] getOccurrences(IProgressMonitor pm,
0519: RefactoringStatus status) throws CoreException {
0520: SearchPattern pattern = createOccurrenceSearchPattern();
0521: return RefactoringSearchEngine.search(pattern,
0522: createRefactoringScope(), new MethodOccurenceCollector(
0523: getMethod().getElementName()), pm, status);
0524: }
0525:
0526: private RefactoringStatus checkRelatedMethods()
0527: throws CoreException {
0528: RefactoringStatus result = new RefactoringStatus();
0529: for (Iterator iter = fMethodsToRename.iterator(); iter
0530: .hasNext();) {
0531: IMethod method = (IMethod) iter.next();
0532:
0533: result.merge(Checks.checkIfConstructorName(method,
0534: getNewElementName(), method.getDeclaringType()
0535: .getElementName()));
0536:
0537: String[] msgData = new String[] {
0538: method.getElementName(),
0539: JavaModelUtil.getFullyQualifiedName(method
0540: .getDeclaringType()) };
0541: if (!method.exists()) {
0542: result
0543: .addFatalError(Messages
0544: .format(
0545: RefactoringCoreMessages.RenameMethodRefactoring_not_in_model,
0546: msgData));
0547: continue;
0548: }
0549: if (method.isBinary())
0550: result
0551: .addFatalError(Messages
0552: .format(
0553: RefactoringCoreMessages.RenameMethodRefactoring_no_binary,
0554: msgData));
0555: if (method.isReadOnly())
0556: result
0557: .addFatalError(Messages
0558: .format(
0559: RefactoringCoreMessages.RenameMethodRefactoring_no_read_only,
0560: msgData));
0561: if (JdtFlags.isNative(method))
0562: result
0563: .addError(Messages
0564: .format(
0565: RefactoringCoreMessages.RenameMethodRefactoring_no_native_1,
0566: msgData));
0567: }
0568: return result;
0569: }
0570:
0571: private RefactoringStatus analyzeCompilationUnits()
0572: throws CoreException {
0573: if (fOccurrences.length == 0)
0574: return null;
0575:
0576: RefactoringStatus result = new RefactoringStatus();
0577: fOccurrences = Checks.excludeCompilationUnits(fOccurrences,
0578: result);
0579: if (result.hasFatalError())
0580: return result;
0581:
0582: result.merge(Checks
0583: .checkCompileErrorsInAffectedFiles(fOccurrences));
0584:
0585: return result;
0586: }
0587:
0588: //-------
0589:
0590: private RefactoringStatus analyzeRenameChanges(IProgressMonitor pm)
0591: throws CoreException {
0592: ICompilationUnit[] newDeclarationWCs = null;
0593: try {
0594: pm.beginTask("", 4); //$NON-NLS-1$
0595: RefactoringStatus result = new RefactoringStatus();
0596: ICompilationUnit[] declarationCUs = getDeclarationCUs();
0597: newDeclarationWCs = RenameAnalyzeUtil
0598: .createNewWorkingCopies(declarationCUs,
0599: fChangeManager, fWorkingCopyOwner,
0600: new SubProgressMonitor(pm, 1));
0601:
0602: IMethod[] wcOldMethods = new IMethod[fMethodsToRename
0603: .size()];
0604: IMethod[] wcNewMethods = new IMethod[fMethodsToRename
0605: .size()];
0606: int i = 0;
0607: for (Iterator iter = fMethodsToRename.iterator(); iter
0608: .hasNext(); i++) {
0609: IMethod method = (IMethod) iter.next();
0610: ICompilationUnit newCu = RenameAnalyzeUtil
0611: .findWorkingCopyForCu(newDeclarationWCs, method
0612: .getCompilationUnit());
0613: IType typeWc = (IType) JavaModelUtil
0614: .findInCompilationUnit(newCu, method
0615: .getDeclaringType());
0616: if (typeWc == null)
0617: continue;
0618: wcOldMethods[i] = getMethodInWorkingCopy(method,
0619: getCurrentElementName(), typeWc);
0620: wcNewMethods[i] = getMethodInWorkingCopy(method,
0621: getNewElementName(), typeWc);
0622: }
0623:
0624: // SearchResultGroup[] newOccurrences= findNewOccurrences(newMethods, newDeclarationWCs, new SubProgressMonitor(pm, 3));
0625: SearchResultGroup[] newOccurrences = batchFindNewOccurrences(
0626: wcNewMethods, wcOldMethods, newDeclarationWCs,
0627: new SubProgressMonitor(pm, 3), result);
0628:
0629: result.merge(RenameAnalyzeUtil.analyzeRenameChanges2(
0630: fChangeManager, fOccurrences, newOccurrences,
0631: getNewElementName()));
0632: return result;
0633: } finally {
0634: pm.done();
0635: if (newDeclarationWCs != null) {
0636: for (int i = 0; i < newDeclarationWCs.length; i++) {
0637: newDeclarationWCs[i].discardWorkingCopy();
0638: }
0639: }
0640: }
0641: }
0642:
0643: //Lower memory footprint than batchFindNewOccurrences. Not used because it is too slow.
0644: //Final solution is maybe to do searches in chunks of ~ 50 CUs.
0645: // private SearchResultGroup[] findNewOccurrences(IMethod[] newMethods, ICompilationUnit[] newDeclarationWCs, IProgressMonitor pm) throws CoreException {
0646: // pm.beginTask("", fOccurrences.length * 2); //$NON-NLS-1$
0647: //
0648: // SearchPattern refsPattern= RefactoringSearchEngine.createOrPattern(newMethods, IJavaSearchConstants.REFERENCES);
0649: // SearchParticipant[] searchParticipants= SearchUtils.getDefaultSearchParticipants();
0650: // IJavaSearchScope scope= RefactoringScopeFactory.create(newMethods);
0651: // MethodOccurenceCollector requestor= new MethodOccurenceCollector(getNewElementName());
0652: // SearchEngine searchEngine= new SearchEngine(fWorkingCopyOwner);
0653: //
0654: // //TODO: should process only references
0655: // for (int j= 0; j < fOccurrences.length; j++) { //should be getReferences()
0656: // //cut memory peak by holding only one reference CU at a time in memory
0657: // ICompilationUnit originalCu= fOccurrences[j].getCompilationUnit();
0658: // ICompilationUnit newWc= null;
0659: // try {
0660: // ICompilationUnit wc= RenameAnalyzeUtil.findWorkingCopyForCu(newDeclarationWCs, originalCu);
0661: // if (wc == null) {
0662: // newWc= RenameAnalyzeUtil.createNewWorkingCopy(originalCu, fChangeManager, fWorkingCopyOwner,
0663: // new SubProgressMonitor(pm, 1));
0664: // }
0665: // searchEngine.search(refsPattern, searchParticipants, scope, requestor, new SubProgressMonitor(pm, 1));
0666: // } finally {
0667: // if (newWc != null)
0668: // newWc.discardWorkingCopy();
0669: // }
0670: // }
0671: // SearchResultGroup[] newResults= RefactoringSearchEngine.groupByResource(requestor.getResults());
0672: // pm.done();
0673: // return newResults;
0674: // }
0675:
0676: private SearchResultGroup[] batchFindNewOccurrences(
0677: IMethod[] wcNewMethods, final IMethod[] wcOldMethods,
0678: ICompilationUnit[] newDeclarationWCs, IProgressMonitor pm,
0679: RefactoringStatus status) throws CoreException {
0680: pm.beginTask("", 2); //$NON-NLS-1$
0681:
0682: SearchPattern refsPattern = RefactoringSearchEngine
0683: .createOrPattern(wcNewMethods,
0684: IJavaSearchConstants.REFERENCES);
0685: SearchParticipant[] searchParticipants = SearchUtils
0686: .getDefaultSearchParticipants();
0687: IJavaSearchScope scope = RefactoringScopeFactory
0688: .create(wcNewMethods);
0689:
0690: MethodOccurenceCollector requestor;
0691: if (getDelegateUpdating()) {
0692: // There will be two new matches inside the delegate(s) (the invocation
0693: // and the javadoc) which are OK and must not be reported.
0694: // Note that except these ocurrences, the delegate bodies are empty
0695: // (as they were created this way).
0696: requestor = new MethodOccurenceCollector(
0697: getNewElementName()) {
0698: public void acceptSearchMatch(ICompilationUnit unit,
0699: SearchMatch match) throws CoreException {
0700: for (int i = 0; i < wcOldMethods.length; i++)
0701: if (wcOldMethods[i].equals(match.getElement()))
0702: return;
0703: super .acceptSearchMatch(unit, match);
0704: }
0705: };
0706: } else
0707: requestor = new MethodOccurenceCollector(
0708: getNewElementName());
0709:
0710: SearchEngine searchEngine = new SearchEngine(fWorkingCopyOwner);
0711:
0712: ArrayList needWCs = new ArrayList();
0713: HashSet declaringCUs = new HashSet(newDeclarationWCs.length);
0714: for (int i = 0; i < newDeclarationWCs.length; i++)
0715: declaringCUs.add(newDeclarationWCs[i].getPrimary());
0716: for (int i = 0; i < fOccurrences.length; i++) {
0717: ICompilationUnit cu = fOccurrences[i].getCompilationUnit();
0718: if (!declaringCUs.contains(cu))
0719: needWCs.add(cu);
0720: }
0721: ICompilationUnit[] otherWCs = null;
0722: try {
0723: otherWCs = RenameAnalyzeUtil.createNewWorkingCopies(
0724: (ICompilationUnit[]) needWCs
0725: .toArray(new ICompilationUnit[needWCs
0726: .size()]), fChangeManager,
0727: fWorkingCopyOwner, new SubProgressMonitor(pm, 1));
0728: searchEngine.search(refsPattern, searchParticipants, scope,
0729: requestor, new SubProgressMonitor(pm, 1));
0730: } finally {
0731: pm.done();
0732: if (otherWCs != null) {
0733: for (int i = 0; i < otherWCs.length; i++) {
0734: otherWCs[i].discardWorkingCopy();
0735: }
0736: }
0737: }
0738: SearchResultGroup[] newResults = RefactoringSearchEngine
0739: .groupByCu(requestor.getResults(), status);
0740: return newResults;
0741: }
0742:
0743: private ICompilationUnit[] getDeclarationCUs() {
0744: Set cus = new HashSet();
0745: for (Iterator iter = fMethodsToRename.iterator(); iter
0746: .hasNext();) {
0747: IMethod method = (IMethod) iter.next();
0748: cus.add(method.getCompilationUnit());
0749: }
0750: return (ICompilationUnit[]) cus
0751: .toArray(new ICompilationUnit[cus.size()]);
0752: }
0753:
0754: private IMethod getMethodInWorkingCopy(IMethod method,
0755: String elementName, IType typeWc) throws CoreException {
0756: String[] paramTypeSignatures = method.getParameterTypes();
0757: return typeWc.getMethod(elementName, paramTypeSignatures);
0758: }
0759:
0760: //-------
0761: private static IMethod[] classesDeclareMethodName(
0762: ITypeHierarchy hier, List classes, IMethod method,
0763: String newName) throws CoreException {
0764: Set result = new HashSet();
0765: IType type = method.getDeclaringType();
0766: List subtypes = Arrays.asList(hier.getAllSubtypes(type));
0767:
0768: int parameterCount = method.getParameterTypes().length;
0769: boolean isMethodPrivate = JdtFlags.isPrivate(method);
0770:
0771: for (Iterator iter = classes.iterator(); iter.hasNext();) {
0772: IType clazz = (IType) iter.next();
0773: IMethod[] methods = clazz.getMethods();
0774: boolean isSubclass = subtypes.contains(clazz);
0775: for (int j = 0; j < methods.length; j++) {
0776: IMethod foundMethod = Checks.findMethod(newName,
0777: parameterCount, false,
0778: new IMethod[] { methods[j] });
0779: if (foundMethod == null)
0780: continue;
0781: if (isSubclass || type.equals(clazz))
0782: result.add(foundMethod);
0783: else if ((!isMethodPrivate)
0784: && (!JdtFlags.isPrivate(methods[j])))
0785: result.add(foundMethod);
0786: }
0787: }
0788: return (IMethod[]) result.toArray(new IMethod[result.size()]);
0789: }
0790:
0791: final static IMethod[] hierarchyDeclaresMethodName(
0792: IProgressMonitor pm, ITypeHierarchy hierarchy,
0793: IMethod method, String newName) throws CoreException {
0794: Set result = new HashSet();
0795: IType type = method.getDeclaringType();
0796: IMethod foundMethod = Checks.findMethod(newName, method
0797: .getParameterTypes().length, false, type);
0798: if (foundMethod != null)
0799: result.add(foundMethod);
0800:
0801: IMethod[] foundInHierarchyClasses = classesDeclareMethodName(
0802: hierarchy, Arrays.asList(hierarchy.getAllClasses()),
0803: method, newName);
0804: if (foundInHierarchyClasses != null)
0805: result.addAll(Arrays.asList(foundInHierarchyClasses));
0806:
0807: IType[] implementingClasses = hierarchy
0808: .getImplementingClasses(type);
0809: IMethod[] foundInImplementingClasses = classesDeclareMethodName(
0810: hierarchy, Arrays.asList(implementingClasses), method,
0811: newName);
0812: if (foundInImplementingClasses != null)
0813: result.addAll(Arrays.asList(foundInImplementingClasses));
0814: return (IMethod[]) result.toArray(new IMethod[result.size()]);
0815: }
0816:
0817: public Change createChange(IProgressMonitor monitor)
0818: throws CoreException {
0819: try {
0820: final TextChange[] changes = fChangeManager.getAllChanges();
0821: final List list = new ArrayList(changes.length);
0822: list.addAll(Arrays.asList(changes));
0823: String project = null;
0824: IJavaProject javaProject = fMethod.getJavaProject();
0825: if (javaProject != null)
0826: project = javaProject.getElementName();
0827: int flags = JavaRefactoringDescriptor.JAR_MIGRATION
0828: | JavaRefactoringDescriptor.JAR_REFACTORING
0829: | RefactoringDescriptor.STRUCTURAL_CHANGE;
0830: try {
0831: if (!Flags.isPrivate(fMethod.getFlags()))
0832: flags |= RefactoringDescriptor.MULTI_CHANGE;
0833: } catch (JavaModelException exception) {
0834: JavaPlugin.log(exception);
0835: }
0836: final IType declaring = fMethod.getDeclaringType();
0837: try {
0838: if (declaring.isAnonymous() || declaring.isLocal())
0839: flags |= JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT;
0840: } catch (JavaModelException exception) {
0841: JavaPlugin.log(exception);
0842: }
0843: final String description = Messages
0844: .format(
0845: RefactoringCoreMessages.RenameMethodProcessor_descriptor_description_short,
0846: fMethod.getElementName());
0847: final String header = Messages
0848: .format(
0849: RefactoringCoreMessages.RenameMethodProcessor_descriptor_description,
0850: new String[] {
0851: JavaElementLabels
0852: .getTextLabel(
0853: fMethod,
0854: JavaElementLabels.ALL_FULLY_QUALIFIED),
0855: getNewElementName() });
0856: final String comment = new JDTRefactoringDescriptorComment(
0857: project, this , header).asString();
0858: final RenameJavaElementDescriptor descriptor = new RenameJavaElementDescriptor(
0859: IJavaRefactorings.RENAME_METHOD);
0860: descriptor.setProject(project);
0861: descriptor.setDescription(description);
0862: descriptor.setComment(comment);
0863: descriptor.setFlags(flags);
0864: descriptor.setJavaElement(fMethod);
0865: descriptor.setNewName(getNewElementName());
0866: descriptor.setUpdateReferences(fUpdateReferences);
0867: descriptor.setKeepOriginal(fDelegateUpdating);
0868: descriptor.setDeprecateDelegate(fDelegateDeprecation);
0869: return new DynamicValidationRefactoringChange(
0870: descriptor,
0871: RefactoringCoreMessages.RenameMethodProcessor_change_name,
0872: (Change[]) list.toArray(new Change[list.size()]));
0873: } finally {
0874: monitor.done();
0875: }
0876: }
0877:
0878: private TextChangeManager createChanges(IProgressMonitor pm,
0879: RefactoringStatus status) throws CoreException {
0880: if (!fIsComposite)
0881: fChangeManager.clear();
0882: addOccurrences(fChangeManager, pm, status);
0883: return fChangeManager;
0884: }
0885:
0886: void addOccurrences(TextChangeManager manager, IProgressMonitor pm,
0887: RefactoringStatus status) throws CoreException/*thrown in subtype*/{
0888: pm.beginTask("", fOccurrences.length); //$NON-NLS-1$
0889: for (int i = 0; i < fOccurrences.length; i++) {
0890: ICompilationUnit cu = fOccurrences[i].getCompilationUnit();
0891: if (cu == null)
0892: continue;
0893:
0894: SearchMatch[] results = fOccurrences[i].getSearchResults();
0895:
0896: // Split matches into declaration and non-declaration matches
0897:
0898: List declarationsInThisCu = new ArrayList();
0899: List referencesInThisCu = new ArrayList();
0900:
0901: for (int j = 0; j < results.length; j++) {
0902: if (results[j] instanceof MethodDeclarationMatch)
0903: declarationsInThisCu.add(results[j]);
0904: else
0905: referencesInThisCu.add(results[j]);
0906: }
0907:
0908: // First, handle the declarations
0909: if (declarationsInThisCu.size() > 0) {
0910:
0911: if (fDelegateUpdating) {
0912: // Update with delegates
0913: CompilationUnitRewrite rewrite = new CompilationUnitRewrite(
0914: cu);
0915: rewrite.setResolveBindings(true);
0916:
0917: for (Iterator iter = declarationsInThisCu
0918: .iterator(); iter.hasNext();) {
0919: SearchMatch element = (SearchMatch) iter.next();
0920: MethodDeclaration method = ASTNodeSearchUtil
0921: .getMethodDeclarationNode(
0922: (IMethod) element.getElement(),
0923: rewrite.getRoot());
0924: DelegateCreator creator = new DelegateMethodCreator();
0925: creator
0926: .setDeclareDeprecated(fDelegateDeprecation);
0927: creator.setDeclaration(method);
0928: creator.setSourceRewrite(rewrite);
0929: creator.setNewElementName(getNewElementName());
0930: creator.prepareDelegate();
0931: creator.createEdit();
0932: }
0933: // Need to handle all delegates first as this
0934: // creates a completely new change object.
0935: TextChange changeForThisCu = rewrite.createChange();
0936: changeForThisCu.setKeepPreviewEdits(true);
0937: manager.manage(cu, changeForThisCu);
0938: }
0939:
0940: // Update the normal methods
0941: for (Iterator iter = declarationsInThisCu.iterator(); iter
0942: .hasNext();) {
0943: SearchMatch element = (SearchMatch) iter.next();
0944: simpleUpdate(element, cu, manager.get(cu));
0945: }
0946: }
0947:
0948: // Second, handle references
0949: if (fUpdateReferences) {
0950: for (Iterator iter = referencesInThisCu.iterator(); iter
0951: .hasNext();) {
0952: SearchMatch element = (SearchMatch) iter.next();
0953: simpleUpdate(element, cu, manager.get(cu));
0954: }
0955: }
0956:
0957: pm.worked(1);
0958: if (pm.isCanceled())
0959: throw new OperationCanceledException();
0960: }
0961: pm.done();
0962: }
0963:
0964: private void simpleUpdate(SearchMatch element, ICompilationUnit cu,
0965: TextChange textChange) {
0966: String editName = RefactoringCoreMessages.RenameMethodRefactoring_update_occurrence;
0967: ReplaceEdit replaceEdit = createReplaceEdit(element, cu);
0968: addTextEdit(textChange, editName, replaceEdit);
0969: }
0970:
0971: protected final ReplaceEdit createReplaceEdit(
0972: SearchMatch searchResult, ICompilationUnit cu) {
0973: if (searchResult.isImplicit()) { // handle Annotation Element references, see bug 94062
0974: StringBuffer sb = new StringBuffer(getNewElementName());
0975: if (JavaCore.INSERT
0976: .equals(cu
0977: .getJavaProject()
0978: .getOption(
0979: DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_BEFORE_ASSIGNMENT_OPERATOR,
0980: true)))
0981: sb.append(' ');
0982: sb.append('=');
0983: if (JavaCore.INSERT
0984: .equals(cu
0985: .getJavaProject()
0986: .getOption(
0987: DefaultCodeFormatterConstants.FORMATTER_INSERT_SPACE_AFTER_ASSIGNMENT_OPERATOR,
0988: true)))
0989: sb.append(' ');
0990: return new ReplaceEdit(searchResult.getOffset(), 0, sb
0991: .toString());
0992: } else {
0993: return new ReplaceEdit(searchResult.getOffset(),
0994: searchResult.getLength(), getNewElementName());
0995: }
0996: }
0997:
0998: public RefactoringStatus initialize(RefactoringArguments arguments) {
0999: if (arguments instanceof JavaRefactoringArguments) {
1000: fInitialized = true;
1001: final JavaRefactoringArguments extended = (JavaRefactoringArguments) arguments;
1002: final String handle = extended
1003: .getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT);
1004: if (handle != null) {
1005: final IJavaElement element = JavaRefactoringDescriptorUtil
1006: .handleToElement(extended.getProject(), handle,
1007: false);
1008: final String refactoring = getRefactoring().getName();
1009: if (element instanceof IMethod) {
1010: final IMethod method = (IMethod) element;
1011: final IType declaring = method.getDeclaringType();
1012: if (declaring != null && declaring.exists()) {
1013: final IMethod[] methods = declaring
1014: .findMethods(method);
1015: if (methods != null && methods.length == 1
1016: && methods[0] != null) {
1017: if (!methods[0].exists())
1018: return ScriptableRefactoring
1019: .createInputFatalStatus(
1020: methods[0],
1021: refactoring,
1022: IJavaRefactorings.RENAME_METHOD);
1023: fMethod = methods[0];
1024: initializeWorkingCopyOwner();
1025: } else
1026: return ScriptableRefactoring
1027: .createInputFatalStatus(
1028: null,
1029: refactoring,
1030: IJavaRefactorings.RENAME_METHOD);
1031: } else
1032: return ScriptableRefactoring
1033: .createInputFatalStatus(element,
1034: refactoring,
1035: IJavaRefactorings.RENAME_METHOD);
1036: } else
1037: return ScriptableRefactoring
1038: .createInputFatalStatus(element,
1039: refactoring,
1040: IJavaRefactorings.RENAME_METHOD);
1041: } else
1042: return RefactoringStatus
1043: .createFatalErrorStatus(Messages
1044: .format(
1045: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1046: JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT));
1047: final String name = extended
1048: .getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME);
1049: if (name != null && !"".equals(name)) //$NON-NLS-1$
1050: setNewElementName(name);
1051: else
1052: return RefactoringStatus
1053: .createFatalErrorStatus(Messages
1054: .format(
1055: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1056: JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME));
1057: final String references = extended
1058: .getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_REFERENCES);
1059: if (references != null) {
1060: fUpdateReferences = Boolean.valueOf(references)
1061: .booleanValue();
1062: } else
1063: return RefactoringStatus
1064: .createFatalErrorStatus(Messages
1065: .format(
1066: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1067: JavaRefactoringDescriptorUtil.ATTRIBUTE_REFERENCES));
1068: final String delegate = extended
1069: .getAttribute(ATTRIBUTE_DELEGATE);
1070: if (delegate != null) {
1071: fDelegateUpdating = Boolean.valueOf(delegate)
1072: .booleanValue();
1073: } else
1074: return RefactoringStatus
1075: .createFatalErrorStatus(Messages
1076: .format(
1077: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1078: ATTRIBUTE_DELEGATE));
1079: final String deprecate = extended
1080: .getAttribute(ATTRIBUTE_DEPRECATE);
1081: if (deprecate != null) {
1082: fDelegateDeprecation = Boolean.valueOf(deprecate)
1083: .booleanValue();
1084: } else
1085: return RefactoringStatus
1086: .createFatalErrorStatus(Messages
1087: .format(
1088: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1089: ATTRIBUTE_DEPRECATE));
1090: } else
1091: return RefactoringStatus
1092: .createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments);
1093: return new RefactoringStatus();
1094: }
1095:
1096: protected void addTextEdit(TextChange change, String editName,
1097: ReplaceEdit replaceEdit) {
1098: if (fIsComposite)
1099: TextChangeCompatibility.addTextEdit(change, editName,
1100: replaceEdit, fCategorySet);
1101: else
1102: TextChangeCompatibility.addTextEdit(change, editName,
1103: replaceEdit);
1104:
1105: }
1106: }
|