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: * Matt Chapman, mpchapman@gmail.com - 89977 Make JDT .java agnostic
0011: *******************************************************************************/package org.eclipse.jdt.internal.corext.refactoring.rename;
0012:
0013: import java.util.ArrayList;
0014: import java.util.Arrays;
0015: import java.util.HashMap;
0016: import java.util.HashSet;
0017: import java.util.Iterator;
0018: import java.util.LinkedHashMap;
0019: import java.util.List;
0020: import java.util.Map;
0021: import java.util.Set;
0022:
0023: import org.eclipse.text.edits.ReplaceEdit;
0024:
0025: import org.eclipse.core.runtime.Assert;
0026: import org.eclipse.core.runtime.CoreException;
0027: import org.eclipse.core.runtime.IConfigurationElement;
0028: import org.eclipse.core.runtime.IProduct;
0029: import org.eclipse.core.runtime.IProgressMonitor;
0030: import org.eclipse.core.runtime.IStatus;
0031: import org.eclipse.core.runtime.NullProgressMonitor;
0032: import org.eclipse.core.runtime.OperationCanceledException;
0033: import org.eclipse.core.runtime.Platform;
0034: import org.eclipse.core.runtime.SubProgressMonitor;
0035:
0036: import org.eclipse.core.resources.IContainer;
0037: import org.eclipse.core.resources.IFile;
0038: import org.eclipse.core.resources.IResource;
0039:
0040: import org.eclipse.ltk.core.refactoring.Change;
0041: import org.eclipse.ltk.core.refactoring.GroupCategory;
0042: import org.eclipse.ltk.core.refactoring.GroupCategorySet;
0043: import org.eclipse.ltk.core.refactoring.IResourceMapper;
0044: import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
0045: import org.eclipse.ltk.core.refactoring.RefactoringStatus;
0046: import org.eclipse.ltk.core.refactoring.RefactoringStatusContext;
0047: import org.eclipse.ltk.core.refactoring.TextChange;
0048: import org.eclipse.ltk.core.refactoring.TextFileChange;
0049: import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
0050: import org.eclipse.ltk.core.refactoring.participants.IParticipantDescriptorFilter;
0051: import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments;
0052: import org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor;
0053: import org.eclipse.ltk.core.refactoring.participants.RenameArguments;
0054:
0055: import org.eclipse.jdt.core.Flags;
0056: import org.eclipse.jdt.core.ICompilationUnit;
0057: import org.eclipse.jdt.core.IField;
0058: import org.eclipse.jdt.core.IImportDeclaration;
0059: import org.eclipse.jdt.core.IJavaElement;
0060: import org.eclipse.jdt.core.IJavaProject;
0061: import org.eclipse.jdt.core.ILocalVariable;
0062: import org.eclipse.jdt.core.IMember;
0063: import org.eclipse.jdt.core.IMethod;
0064: import org.eclipse.jdt.core.IPackageFragment;
0065: import org.eclipse.jdt.core.ISourceRange;
0066: import org.eclipse.jdt.core.IType;
0067: import org.eclipse.jdt.core.ITypeHierarchy;
0068: import org.eclipse.jdt.core.JavaCore;
0069: import org.eclipse.jdt.core.JavaModelException;
0070: import org.eclipse.jdt.core.Signature;
0071: import org.eclipse.jdt.core.dom.AST;
0072: import org.eclipse.jdt.core.dom.ASTVisitor;
0073: import org.eclipse.jdt.core.dom.CompilationUnit;
0074: import org.eclipse.jdt.core.dom.MethodDeclaration;
0075: import org.eclipse.jdt.core.dom.Modifier;
0076: import org.eclipse.jdt.core.dom.TypeDeclaration;
0077: import org.eclipse.jdt.core.refactoring.IJavaElementMapper;
0078: import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
0079: import org.eclipse.jdt.core.refactoring.RenameTypeArguments;
0080: import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor;
0081: import org.eclipse.jdt.core.refactoring.descriptors.RenameJavaElementDescriptor;
0082: import org.eclipse.jdt.core.search.IJavaSearchConstants;
0083: import org.eclipse.jdt.core.search.IJavaSearchScope;
0084: import org.eclipse.jdt.core.search.SearchMatch;
0085: import org.eclipse.jdt.core.search.SearchPattern;
0086: import org.eclipse.jdt.core.search.TypeReferenceMatch;
0087:
0088: import org.eclipse.jdt.internal.corext.refactoring.Checks;
0089: import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
0090: import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
0091: import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil;
0092: import org.eclipse.jdt.internal.corext.refactoring.RefactoringAvailabilityTester;
0093: import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
0094: import org.eclipse.jdt.internal.corext.refactoring.RefactoringScopeFactory;
0095: import org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine;
0096: import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup;
0097: import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext;
0098: import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange;
0099: import org.eclipse.jdt.internal.corext.refactoring.changes.RenameCompilationUnitChange;
0100: import org.eclipse.jdt.internal.corext.refactoring.changes.RenameResourceChange;
0101: import org.eclipse.jdt.internal.corext.refactoring.changes.TextChangeCompatibility;
0102: import org.eclipse.jdt.internal.corext.refactoring.code.ScriptableRefactoring;
0103: import org.eclipse.jdt.internal.corext.refactoring.participants.JavaProcessors;
0104: import org.eclipse.jdt.internal.corext.refactoring.tagging.IQualifiedNameUpdating;
0105: import org.eclipse.jdt.internal.corext.refactoring.tagging.IReferenceUpdating;
0106: import org.eclipse.jdt.internal.corext.refactoring.tagging.ISimilarDeclarationUpdating;
0107: import org.eclipse.jdt.internal.corext.refactoring.tagging.ITextUpdating;
0108: import org.eclipse.jdt.internal.corext.refactoring.util.Changes;
0109: import org.eclipse.jdt.internal.corext.refactoring.util.JavaElementUtil;
0110: import org.eclipse.jdt.internal.corext.refactoring.util.QualifiedNameFinder;
0111: import org.eclipse.jdt.internal.corext.refactoring.util.QualifiedNameSearchResult;
0112: import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
0113: import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil;
0114: import org.eclipse.jdt.internal.corext.refactoring.util.TextChangeManager;
0115: import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
0116: import org.eclipse.jdt.internal.corext.util.JdtFlags;
0117: import org.eclipse.jdt.internal.corext.util.Messages;
0118: import org.eclipse.jdt.internal.corext.util.SearchUtils;
0119:
0120: import org.eclipse.jdt.ui.JavaElementLabels;
0121: import org.eclipse.jdt.ui.refactoring.IRefactoringProcessorIds;
0122:
0123: import org.eclipse.jdt.internal.ui.JavaPlugin;
0124: import org.eclipse.jdt.internal.ui.refactoring.RefactoringSaveHelper;
0125:
0126: public class RenameTypeProcessor extends JavaRenameProcessor implements
0127: ITextUpdating, IReferenceUpdating, IQualifiedNameUpdating,
0128: ISimilarDeclarationUpdating, IResourceMapper,
0129: IJavaElementMapper {
0130:
0131: private static final String ATTRIBUTE_QUALIFIED = "qualified"; //$NON-NLS-1$
0132: private static final String ATTRIBUTE_TEXTUAL_MATCHES = "textual"; //$NON-NLS-1$
0133: private static final String ATTRIBUTE_PATTERNS = "patterns"; //$NON-NLS-1$
0134: private static final String ATTRIBUTE_SIMILAR_DECLARATIONS = "similarDeclarations"; //$NON-NLS-1$
0135: private static final String ATTRIBUTE_MATCHING_STRATEGY = "matchStrategy"; //$NON-NLS-1$
0136:
0137: private static final GroupCategorySet CATEGORY_TYPE_RENAME = new GroupCategorySet(
0138: new GroupCategory(
0139: "org.eclipse.jdt.internal.corext.refactoring.rename.renameType.type", RefactoringCoreMessages.RenameTypeProcessor_changeCategory_type, RefactoringCoreMessages.RenameTypeProcessor_changeCategory_type_description)); //$NON-NLS-1$
0140: private static final GroupCategorySet CATEGORY_METHOD_RENAME = new GroupCategorySet(
0141: new GroupCategory(
0142: "org.eclipse.jdt.internal.corext.refactoring.rename.renameType.method", RefactoringCoreMessages.RenameTypeProcessor_changeCategory_method, RefactoringCoreMessages.RenameTypeProcessor_changeCategory_method_description)); //$NON-NLS-1$
0143: private static final GroupCategorySet CATEGORY_FIELD_RENAME = new GroupCategorySet(
0144: new GroupCategory(
0145: "org.eclipse.jdt.internal.corext.refactoring.rename.renameType.field", RefactoringCoreMessages.RenameTypeProcessor_changeCategory_fields, RefactoringCoreMessages.RenameTypeProcessor_changeCategory_fields_description)); //$NON-NLS-1$
0146: private static final GroupCategorySet CATEGORY_LOCAL_RENAME = new GroupCategorySet(
0147: new GroupCategory(
0148: "org.eclipse.jdt.internal.corext.refactoring.rename.renameType.local", RefactoringCoreMessages.RenameTypeProcessor_changeCategory_local_variables, RefactoringCoreMessages.RenameTypeProcessor_changeCategory_local_variables_description)); //$NON-NLS-1$
0149:
0150: private IType fType;
0151: private SearchResultGroup[] fReferences;
0152: private TextChangeManager fChangeManager;
0153: private QualifiedNameSearchResult fQualifiedNameSearchResult;
0154:
0155: private boolean fUpdateReferences;
0156:
0157: private boolean fUpdateTextualMatches;
0158:
0159: private boolean fUpdateQualifiedNames;
0160: private String fFilePatterns;
0161:
0162: // --- similar elements
0163:
0164: private boolean fUpdateSimilarElements;
0165: private Map/* <IJavaElement, String> */fFinalSimilarElementToName = null;
0166: private int fRenamingStrategy;
0167:
0168: // Preloaded information for the UI.
0169: private LinkedHashMap/* <IJavaElement, String> */fPreloadedElementToName = null;
0170: private Map/* <IJavaElement, Boolean> */fPreloadedElementToSelection = null;
0171: private LinkedHashMap/* <IJavaElement, String> */fPreloadedElementToNameDefault = null;
0172:
0173: // Cache information to decide whether to
0174: // re-update references and preload info
0175: private String fCachedNewName = null;
0176: private boolean fCachedRenameSimilarElements = false;
0177: private int fCachedRenamingStrategy = -1;
0178: private RefactoringStatus fCachedRefactoringStatus = null;
0179:
0180: public static final class ParticipantDescriptorFilter implements
0181: IParticipantDescriptorFilter {
0182:
0183: public boolean select(IConfigurationElement element,
0184: RefactoringStatus status) {
0185: IConfigurationElement[] params = element.getChildren(PARAM);
0186: for (int i = 0; i < params.length; i++) {
0187: IConfigurationElement param = params[i];
0188: if ("handlesSimilarDeclarations".equals(param.getAttribute(NAME)) && //$NON-NLS-1$
0189: "false".equals(param.getAttribute(VALUE))) { //$NON-NLS-1$
0190: return false;
0191: }
0192: }
0193: return true;
0194: }
0195: }
0196:
0197: private class NoOverrideProgressMonitor extends SubProgressMonitor {
0198: public NoOverrideProgressMonitor(IProgressMonitor monitor,
0199: int ticks) {
0200: super (monitor, ticks,
0201: SubProgressMonitor.SUPPRESS_SUBTASK_LABEL);
0202: }
0203:
0204: public void setTaskName(String name) {
0205: // do nothing
0206: }
0207: }
0208:
0209: /**
0210: * Creates a new rename type processor.
0211: * @param type the type, or <code>null</code> if invoked by scripting
0212: */
0213: public RenameTypeProcessor(IType type) {
0214: fType = type;
0215: if (type != null)
0216: setNewElementName(type.getElementName());
0217: fUpdateReferences = true; //default is yes
0218: fUpdateTextualMatches = false;
0219: fUpdateSimilarElements = false; // default is no
0220: fRenamingStrategy = RenamingNameSuggestor.STRATEGY_EXACT;
0221: }
0222:
0223: public IType getType() {
0224: return fType;
0225: }
0226:
0227: public String getIdentifier() {
0228: return IRefactoringProcessorIds.RENAME_TYPE_PROCESSOR;
0229: }
0230:
0231: public boolean isApplicable() throws CoreException {
0232: return RefactoringAvailabilityTester.isRenameAvailable(fType);
0233: }
0234:
0235: public String getProcessorName() {
0236: return RefactoringCoreMessages.RenameTypeRefactoring_name;
0237: }
0238:
0239: protected String[] getAffectedProjectNatures() throws CoreException {
0240: return JavaProcessors.computeAffectedNatures(fType);
0241: }
0242:
0243: public Object[] getElements() {
0244: return new Object[] { fType };
0245: }
0246:
0247: protected RenameModifications computeRenameModifications() {
0248: RenameModifications result = new RenameModifications();
0249: result.rename(fType, new RenameTypeArguments(
0250: getNewElementName(), getUpdateReferences(),
0251: getUpdateSimilarDeclarations(), getSimilarElements()),
0252: createParticipantDescriptorFilter());
0253: if (isPrimaryType()) {
0254: ICompilationUnit cu = fType.getCompilationUnit();
0255: String newCUName = getNewCompilationUnit().getElementName();
0256: result.rename(cu, new RenameArguments(newCUName,
0257: getUpdateReferences()));
0258: }
0259: return result;
0260: }
0261:
0262: /*
0263: * Note: this is a handle-only method!
0264: */
0265: private boolean isPrimaryType() {
0266: String cuName = fType.getCompilationUnit().getElementName();
0267: String typeName = fType.getElementName();
0268: return Checks.isTopLevel(fType)
0269: && JavaCore.removeJavaLikeExtension(cuName).equals(
0270: typeName);
0271: }
0272:
0273: //---- IRenameProcessor ----------------------------------------------
0274:
0275: public String getCurrentElementName() {
0276: return fType.getElementName();
0277: }
0278:
0279: public String getCurrentElementQualifier() {
0280: return JavaModelUtil.getTypeContainerName(fType);
0281: }
0282:
0283: public RefactoringStatus checkNewElementName(String newName) {
0284: Assert.isNotNull(newName, "new name"); //$NON-NLS-1$
0285: RefactoringStatus result = Checks.checkTypeName(newName, fType);
0286: if (Checks.isAlreadyNamed(fType, newName))
0287: result
0288: .addFatalError(RefactoringCoreMessages.RenameTypeRefactoring_choose_another_name);
0289: return result;
0290: }
0291:
0292: public Object getNewElement() {
0293: if (Checks.isTopLevel(fType)) {
0294: return getNewCompilationUnit().getType(getNewElementName());
0295: } else {
0296: return fType.getDeclaringType()
0297: .getType(getNewElementName());
0298: }
0299: }
0300:
0301: private ICompilationUnit getNewCompilationUnit() {
0302: ICompilationUnit cu = fType.getCompilationUnit();
0303: if (isPrimaryType()) {
0304: IPackageFragment parent = fType.getPackageFragment();
0305: String renamedCUName = JavaModelUtil.getRenamedCUName(cu,
0306: getNewElementName());
0307: return parent.getCompilationUnit(renamedCUName);
0308: } else {
0309: return cu;
0310: }
0311: }
0312:
0313: //---- JavaRenameProcessor -------------------------------------------
0314:
0315: protected RenameArguments createRenameArguments() {
0316: return new RenameTypeArguments(getNewElementName(),
0317: getUpdateReferences(), getUpdateSimilarDeclarations(),
0318: getSimilarElements());
0319: }
0320:
0321: protected IParticipantDescriptorFilter createParticipantDescriptorFilter() {
0322: if (!getUpdateSimilarDeclarations())
0323: return null;
0324: return new ParticipantDescriptorFilter();
0325: }
0326:
0327: protected IFile[] getChangedFiles() throws CoreException {
0328: List result = new ArrayList();
0329: result.addAll(Arrays.asList(ResourceUtil
0330: .getFiles(fChangeManager.getAllCompilationUnits())));
0331: if (fQualifiedNameSearchResult != null)
0332: result.addAll(Arrays.asList(fQualifiedNameSearchResult
0333: .getAllFiles()));
0334: if (willRenameCU())
0335: result
0336: .add(ResourceUtil.getFile(fType
0337: .getCompilationUnit()));
0338: return (IFile[]) result.toArray(new IFile[result.size()]);
0339: }
0340:
0341: public int getSaveMode() {
0342: return RefactoringSaveHelper.SAVE_NON_JAVA_UPDATES;
0343: }
0344:
0345: //---- ITextUpdating -------------------------------------------------
0346:
0347: public boolean canEnableTextUpdating() {
0348: return true;
0349: }
0350:
0351: public boolean getUpdateTextualMatches() {
0352: return fUpdateTextualMatches;
0353: }
0354:
0355: public void setUpdateTextualMatches(boolean update) {
0356: fUpdateTextualMatches = update;
0357: }
0358:
0359: //---- IReferenceUpdating --------------------------------------
0360:
0361: public void setUpdateReferences(boolean update) {
0362: fUpdateReferences = update;
0363: }
0364:
0365: public boolean canEnableUpdateReferences() {
0366: return true;
0367: }
0368:
0369: public boolean getUpdateReferences() {
0370: return fUpdateReferences;
0371: }
0372:
0373: //---- IQualifiedNameUpdating ----------------------------------
0374:
0375: public boolean canEnableQualifiedNameUpdating() {
0376: return !fType.getPackageFragment().isDefaultPackage()
0377: && !(fType.getParent() instanceof IType);
0378: }
0379:
0380: public boolean getUpdateQualifiedNames() {
0381: return fUpdateQualifiedNames;
0382: }
0383:
0384: public void setUpdateQualifiedNames(boolean update) {
0385: fUpdateQualifiedNames = update;
0386: }
0387:
0388: public String getFilePatterns() {
0389: return fFilePatterns;
0390: }
0391:
0392: public void setFilePatterns(String patterns) {
0393: Assert.isNotNull(patterns);
0394: fFilePatterns = patterns;
0395: }
0396:
0397: // ---- ISimilarDeclarationUpdating
0398:
0399: public boolean canEnableSimilarDeclarationUpdating() {
0400:
0401: IProduct product = Platform.getProduct();
0402: if (product != null) {
0403: String property = product
0404: .getProperty("org.eclipse.jdt.ui.refactoring.handlesSimilarDeclarations"); //$NON-NLS-1$
0405: if ("false".equalsIgnoreCase(property)) //$NON-NLS-1$
0406: return false;
0407: }
0408:
0409: return true;
0410: }
0411:
0412: public void setUpdateSimilarDeclarations(boolean update) {
0413: fUpdateSimilarElements = update;
0414: }
0415:
0416: public boolean getUpdateSimilarDeclarations() {
0417: return fUpdateSimilarElements;
0418: }
0419:
0420: public int getMatchStrategy() {
0421: return fRenamingStrategy;
0422:
0423: }
0424:
0425: public void setMatchStrategy(int selectedStrategy) {
0426: fRenamingStrategy = selectedStrategy;
0427: }
0428:
0429: /**
0430: * @return the similar elements of the type, i.e. IFields, IMethods, and
0431: * ILocalVariables. Returns <code>null</code> iff similar declaration updating
0432: * is not requested.
0433: */
0434: public IJavaElement[] getSimilarElements() {
0435: if (fFinalSimilarElementToName == null)
0436: return null;
0437: Set keys = fFinalSimilarElementToName.keySet();
0438: return (IJavaElement[]) keys.toArray(new IJavaElement[keys
0439: .size()]);
0440: }
0441:
0442: /**
0443: * {@inheritDoc}
0444: */
0445: public IResource getRefactoredResource(IResource element) {
0446: if (element instanceof IFile) {
0447: if (Checks.isTopLevel(fType)
0448: && element.equals(fType.getResource()))
0449: return getNewCompilationUnit().getResource();
0450: }
0451: return element;
0452: }
0453:
0454: /**
0455: * {@inheritDoc}
0456: */
0457: public IJavaElement getRefactoredJavaElement(IJavaElement element) {
0458: if (element instanceof ICompilationUnit) {
0459: if (Checks.isTopLevel(fType)
0460: && element.equals(fType.getCompilationUnit()))
0461: return getNewCompilationUnit();
0462: } else if (element instanceof IMember) {
0463: final IType newType = (IType) getNewElement();
0464: final RefactoringHandleTransplanter transplanter = new RefactoringHandleTransplanter(
0465: fType, newType, fFinalSimilarElementToName);
0466: return transplanter.transplantHandle((IMember) element);
0467: }
0468: return element;
0469: }
0470:
0471: public RefactoringStatus checkInitialConditions(IProgressMonitor pm)
0472: throws CoreException {
0473: IType primary = (IType) fType.getPrimaryElement();
0474: if (primary == null || !primary.exists()) {
0475: String message = Messages
0476: .format(
0477: RefactoringCoreMessages.RenameTypeRefactoring_does_not_exist,
0478: new String[] {
0479: JavaModelUtil
0480: .getFullyQualifiedName(fType),
0481: fType.getCompilationUnit()
0482: .getElementName() });
0483: return RefactoringStatus.createFatalErrorStatus(message);
0484: }
0485: fType = primary;
0486: return Checks.checkIfCuBroken(fType);
0487: }
0488:
0489: protected RefactoringStatus doCheckFinalConditions(
0490: IProgressMonitor pm, CheckConditionsContext context)
0491: throws CoreException {
0492: Assert.isNotNull(fType, "type"); //$NON-NLS-1$
0493: Assert.isNotNull(getNewElementName(), "newName"); //$NON-NLS-1$
0494: RefactoringStatus result = new RefactoringStatus();
0495:
0496: int referenceSearchTicks = fUpdateReferences
0497: || fUpdateSimilarElements ? 15 : 0;
0498: int affectedCusTicks = fUpdateReferences
0499: || fUpdateSimilarElements ? 10 : 1;
0500: int similarElementTicks = fUpdateSimilarElements ? 85 : 0;
0501: int createChangeTicks = 5;
0502: int qualifiedNamesTicks = fUpdateQualifiedNames ? 50 : 0;
0503:
0504: try {
0505: pm
0506: .beginTask(
0507: "", 12 + referenceSearchTicks + affectedCusTicks + similarElementTicks + createChangeTicks + qualifiedNamesTicks); //$NON-NLS-1$
0508: pm
0509: .setTaskName(RefactoringCoreMessages.RenameTypeRefactoring_checking);
0510:
0511: fChangeManager = new TextChangeManager(true);
0512:
0513: result.merge(checkNewElementName(getNewElementName()));
0514: if (result.hasFatalError())
0515: return result;
0516: result.merge(Checks.checkIfCuBroken(fType));
0517: if (result.hasFatalError())
0518: return result;
0519: pm.worked(1);
0520:
0521: result.merge(checkTypesInCompilationUnit());
0522: pm.worked(1);
0523:
0524: result.merge(checkForMethodsWithConstructorNames());
0525: pm.worked(1);
0526:
0527: result.merge(checkImportedTypes());
0528: pm.worked(1);
0529:
0530: if (Checks.isTopLevel(fType) && (JdtFlags.isPublic(fType)))
0531: result.merge(Checks.checkCompilationUnitNewName(fType
0532: .getCompilationUnit(), getNewElementName()));
0533: pm.worked(1);
0534:
0535: if (isPrimaryType())
0536: result.merge(checkNewPathValidity());
0537: pm.worked(1);
0538:
0539: result.merge(checkEnclosingTypes());
0540: pm.worked(1);
0541:
0542: result.merge(checkEnclosedTypes());
0543: pm.worked(1);
0544:
0545: result.merge(checkTypesInPackage());
0546: pm.worked(1);
0547:
0548: result.merge(checkTypesImportedInCu());
0549: pm.worked(1);
0550:
0551: result.merge(Checks.checkForMainAndNativeMethods(fType));
0552: pm.worked(1);
0553:
0554: // before doing any expensive analysis
0555: if (result.hasFatalError())
0556: return result;
0557:
0558: result.merge(analyseEnclosedTypes());
0559: pm.worked(1);
0560: // before doing _the really_ expensive analysis
0561: if (result.hasFatalError())
0562: return result;
0563:
0564: // Load references, including similarly named elements
0565: if (fUpdateReferences || fUpdateSimilarElements) {
0566: pm
0567: .setTaskName(RefactoringCoreMessages.RenameTypeRefactoring_searching);
0568: result
0569: .merge(initializeReferences(new SubProgressMonitor(
0570: pm, referenceSearchTicks)));
0571: } else {
0572: fReferences = new SearchResultGroup[0];
0573: }
0574:
0575: pm
0576: .setTaskName(RefactoringCoreMessages.RenameTypeRefactoring_checking);
0577: if (pm.isCanceled())
0578: throw new OperationCanceledException();
0579:
0580: if (fUpdateReferences || fUpdateSimilarElements) {
0581: result
0582: .merge(analyzeAffectedCompilationUnits(new SubProgressMonitor(
0583: pm, affectedCusTicks)));
0584: } else {
0585: Checks.checkCompileErrorsInAffectedFile(result, fType
0586: .getResource());
0587: pm.worked(affectedCusTicks);
0588: }
0589:
0590: if (result.hasFatalError())
0591: return result;
0592:
0593: if (fUpdateSimilarElements) {
0594: result
0595: .merge(initializeSimilarElementsRenameProcessors(
0596: new SubProgressMonitor(pm,
0597: similarElementTicks), context));
0598: if (result.hasFatalError())
0599: return result;
0600: }
0601:
0602: createChanges(new SubProgressMonitor(pm, createChangeTicks));
0603:
0604: if (fUpdateQualifiedNames)
0605: computeQualifiedNameMatches(new SubProgressMonitor(pm,
0606: qualifiedNamesTicks));
0607:
0608: return result;
0609: } finally {
0610: pm.done();
0611: }
0612: }
0613:
0614: /**
0615: * Initializes the references to the type and the similarly named elements. This
0616: * method creates both the fReferences and the fPreloadedElementToName
0617: * fields.
0618: *
0619: * May be called from the UI.
0620: * @param monitor
0621: * @return initialization status
0622: * @throws JavaModelException some fundamental error with the underlying model
0623: * @throws OperationCanceledException if user canceled the task
0624: *
0625: */
0626: public RefactoringStatus initializeReferences(
0627: IProgressMonitor monitor) throws JavaModelException,
0628: OperationCanceledException {
0629:
0630: Assert.isNotNull(fType);
0631: Assert.isNotNull(getNewElementName());
0632:
0633: // Do not search again if the preconditions have not changed.
0634: // Search depends on the type, the new name, the similarly named elements, and
0635: // the strategy
0636:
0637: if (fReferences != null
0638: && (getNewElementName().equals(fCachedNewName))
0639: && (fCachedRenameSimilarElements == getUpdateSimilarDeclarations())
0640: && (fCachedRenamingStrategy == fRenamingStrategy))
0641: return fCachedRefactoringStatus;
0642:
0643: fCachedNewName = getNewElementName();
0644: fCachedRenameSimilarElements = fUpdateSimilarElements;
0645: fCachedRenamingStrategy = fRenamingStrategy;
0646: fCachedRefactoringStatus = new RefactoringStatus();
0647:
0648: try {
0649: SearchPattern pattern = SearchPattern.createPattern(fType,
0650: IJavaSearchConstants.REFERENCES,
0651: SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE);
0652: fReferences = RefactoringSearchEngine.search(pattern,
0653: RefactoringScopeFactory.create(fType),
0654: new TypeOccurrenceCollector(fType), monitor,
0655: fCachedRefactoringStatus);
0656: fReferences = Checks.excludeCompilationUnits(fReferences,
0657: fCachedRefactoringStatus);
0658:
0659: fPreloadedElementToName = new LinkedHashMap();
0660: fPreloadedElementToSelection = new HashMap();
0661:
0662: final String unQualifiedTypeName = fType.getElementName();
0663:
0664: monitor.beginTask("", fReferences.length); //$NON-NLS-1$
0665:
0666: if (getUpdateSimilarDeclarations()) {
0667:
0668: RenamingNameSuggestor sugg = new RenamingNameSuggestor(
0669: fRenamingStrategy);
0670:
0671: for (int i = 0; i < fReferences.length; i++) {
0672: final ICompilationUnit cu = fReferences[i]
0673: .getCompilationUnit();
0674: if (cu == null)
0675: continue;
0676:
0677: final SearchMatch[] results = fReferences[i]
0678: .getSearchResults();
0679:
0680: for (int j = 0; j < results.length; j++) {
0681:
0682: if (!(results[j] instanceof TypeReferenceMatch))
0683: continue;
0684:
0685: final TypeReferenceMatch match = (TypeReferenceMatch) results[j];
0686: final List matches = new ArrayList();
0687:
0688: if (match.getLocalElement() != null)
0689: matches.add(match.getLocalElement());
0690: else
0691: matches.add(match.getElement());
0692:
0693: final IJavaElement[] others = match
0694: .getOtherElements();
0695: if (others != null)
0696: matches.addAll(Arrays.asList(others));
0697:
0698: for (Iterator iter = matches.iterator(); iter
0699: .hasNext();) {
0700: final IJavaElement element = (IJavaElement) iter
0701: .next();
0702:
0703: if (!(element instanceof IMethod)
0704: && !(element instanceof IField)
0705: && !(element instanceof ILocalVariable))
0706: continue;
0707:
0708: if (!isInDeclaredType(match.getOffset(),
0709: element))
0710: continue;
0711:
0712: if (element instanceof IField) {
0713: final IField currentField = (IField) element;
0714: final String newFieldName = sugg
0715: .suggestNewFieldName(
0716: currentField
0717: .getJavaProject(),
0718: currentField
0719: .getElementName(),
0720: Flags
0721: .isStatic(currentField
0722: .getFlags()),
0723: unQualifiedTypeName,
0724: getNewElementName());
0725:
0726: if (newFieldName != null)
0727: fPreloadedElementToName.put(
0728: currentField, newFieldName);
0729: }
0730:
0731: if (element instanceof IMethod) {
0732: final IMethod currentMethod = (IMethod) element;
0733: addMethodRename(unQualifiedTypeName,
0734: sugg, currentMethod);
0735: }
0736:
0737: if (element instanceof ILocalVariable) {
0738: final ILocalVariable currentLocal = (ILocalVariable) element;
0739: final boolean isParameter;
0740:
0741: if (JavaModelUtil
0742: .isParameter(currentLocal)) {
0743: addMethodRename(
0744: unQualifiedTypeName, sugg,
0745: (IMethod) currentLocal
0746: .getParent());
0747: isParameter = true;
0748: } else
0749: isParameter = false;
0750:
0751: final String newLocalName = sugg
0752: .suggestNewLocalName(
0753: currentLocal
0754: .getJavaProject(),
0755: currentLocal
0756: .getElementName(),
0757: isParameter,
0758: unQualifiedTypeName,
0759: getNewElementName());
0760:
0761: if (newLocalName != null)
0762: fPreloadedElementToName.put(
0763: currentLocal, newLocalName);
0764: }
0765: }
0766: }
0767: if (monitor.isCanceled())
0768: throw new OperationCanceledException();
0769: }
0770: }
0771:
0772: for (Iterator iter = fPreloadedElementToName.keySet()
0773: .iterator(); iter.hasNext();) {
0774: IJavaElement element = (IJavaElement) iter.next();
0775: fPreloadedElementToSelection.put(element, Boolean.TRUE);
0776: }
0777: fPreloadedElementToNameDefault = (LinkedHashMap) fPreloadedElementToName
0778: .clone();
0779:
0780: } catch (OperationCanceledException e) {
0781: fReferences = null;
0782: fPreloadedElementToName = null;
0783: throw new OperationCanceledException();
0784: }
0785: return fCachedRefactoringStatus;
0786: }
0787:
0788: /**
0789: * @param matchOffset
0790: * @param parentElement
0791: * @return true iff the given search match offset (must be a match of a type
0792: * reference) lies before the element name of its enclosing java element,
0793: * false if not. In other words: If this method returns true, the match is
0794: * the declared type (or return type) of the enclosing element.
0795: * @throws JavaModelException
0796: *
0797: */
0798: private boolean isInDeclaredType(int matchOffset,
0799: IJavaElement parentElement) throws JavaModelException {
0800: if (parentElement != null) {
0801: int enclosingNameOffset = 0;
0802: if (parentElement instanceof IMethod
0803: || parentElement instanceof IField)
0804: enclosingNameOffset = ((IMember) parentElement)
0805: .getNameRange().getOffset();
0806: else if (parentElement instanceof ILocalVariable)
0807: enclosingNameOffset = ((ILocalVariable) parentElement)
0808: .getNameRange().getOffset();
0809:
0810: return (matchOffset < enclosingNameOffset);
0811: }
0812: return false;
0813: }
0814:
0815: private void addMethodRename(final String unQualifiedTypeName,
0816: RenamingNameSuggestor sugg, final IMethod currentMethod)
0817: throws JavaModelException {
0818: if (!currentMethod.isConstructor()) {
0819: final String newMethodName = sugg.suggestNewMethodName(
0820: currentMethod.getElementName(),
0821: unQualifiedTypeName, getNewElementName());
0822:
0823: if (newMethodName != null)
0824: fPreloadedElementToName.put(currentMethod,
0825: newMethodName);
0826: }
0827: }
0828:
0829: private RefactoringStatus checkNewPathValidity() {
0830: IContainer c = fType.getCompilationUnit().getResource()
0831: .getParent();
0832:
0833: String notRename = RefactoringCoreMessages.RenameTypeRefactoring_will_not_rename;
0834: IStatus status = c.getWorkspace().validateName(
0835: getNewElementName(), IResource.FILE);
0836: if (status.getSeverity() == IStatus.ERROR)
0837: return RefactoringStatus.createWarningStatus(status
0838: .getMessage()
0839: + ". " + notRename); //$NON-NLS-1$
0840:
0841: status = c.getWorkspace().validatePath(
0842: createNewPath(getNewElementName()), IResource.FILE);
0843: if (status.getSeverity() == IStatus.ERROR)
0844: return RefactoringStatus.createWarningStatus(status
0845: .getMessage()
0846: + ". " + notRename); //$NON-NLS-1$
0847:
0848: return new RefactoringStatus();
0849: }
0850:
0851: private String createNewPath(String newName) {
0852: return fType.getCompilationUnit().getResource().getFullPath()
0853: .removeLastSegments(1).append(newName).toString();
0854: }
0855:
0856: private RefactoringStatus checkTypesImportedInCu()
0857: throws CoreException {
0858: IImportDeclaration imp = getImportedType(fType
0859: .getCompilationUnit(), getNewElementName());
0860:
0861: if (imp == null)
0862: return null;
0863:
0864: String msg = Messages.format(
0865: RefactoringCoreMessages.RenameTypeRefactoring_imported,
0866: new Object[] {
0867: getNewElementName(),
0868: fType.getCompilationUnit().getResource()
0869: .getFullPath() });
0870: IJavaElement grandParent = imp.getParent().getParent();
0871: if (grandParent instanceof ICompilationUnit)
0872: return RefactoringStatus.createErrorStatus(msg,
0873: JavaStatusContext.create(imp));
0874:
0875: return null;
0876: }
0877:
0878: private RefactoringStatus checkTypesInPackage()
0879: throws CoreException {
0880: IType type = Checks.findTypeInPackage(fType
0881: .getPackageFragment(), getNewElementName());
0882: if (type == null || !type.exists())
0883: return null;
0884: String msg = Messages.format(
0885: RefactoringCoreMessages.RenameTypeRefactoring_exists,
0886: new String[] { getNewElementName(),
0887: fType.getPackageFragment().getElementName() });
0888: return RefactoringStatus.createErrorStatus(msg,
0889: JavaStatusContext.create(type));
0890: }
0891:
0892: private RefactoringStatus checkEnclosedTypes() throws CoreException {
0893: IType enclosedType = findEnclosedType(fType,
0894: getNewElementName());
0895: if (enclosedType == null)
0896: return null;
0897: String msg = Messages.format(
0898: RefactoringCoreMessages.RenameTypeRefactoring_encloses,
0899: new String[] {
0900: JavaModelUtil.getFullyQualifiedName(fType),
0901: getNewElementName() });
0902: return RefactoringStatus.createErrorStatus(msg,
0903: JavaStatusContext.create(enclosedType));
0904: }
0905:
0906: private RefactoringStatus checkEnclosingTypes() {
0907: IType enclosingType = findEnclosingType(fType,
0908: getNewElementName());
0909: if (enclosingType == null)
0910: return null;
0911:
0912: String msg = Messages.format(
0913: RefactoringCoreMessages.RenameTypeRefactoring_enclosed,
0914: new String[] {
0915: JavaModelUtil.getFullyQualifiedName(fType),
0916: getNewElementName() });
0917: return RefactoringStatus.createErrorStatus(msg,
0918: JavaStatusContext.create(enclosingType));
0919: }
0920:
0921: private static IType findEnclosedType(IType type, String newName)
0922: throws CoreException {
0923: IType[] enclosedTypes = type.getTypes();
0924: for (int i = 0; i < enclosedTypes.length; i++) {
0925: if (newName.equals(enclosedTypes[i].getElementName())
0926: || findEnclosedType(enclosedTypes[i], newName) != null)
0927: return enclosedTypes[i];
0928: }
0929: return null;
0930: }
0931:
0932: private static IType findEnclosingType(IType type, String newName) {
0933: IType enclosing = type.getDeclaringType();
0934: while (enclosing != null) {
0935: if (newName.equals(enclosing.getElementName()))
0936: return enclosing;
0937: else
0938: enclosing = enclosing.getDeclaringType();
0939: }
0940: return null;
0941: }
0942:
0943: private static IImportDeclaration getImportedType(
0944: ICompilationUnit cu, String typeName) throws CoreException {
0945: IImportDeclaration[] imports = cu.getImports();
0946: String dotTypeName = "." + typeName; //$NON-NLS-1$
0947: for (int i = 0; i < imports.length; i++) {
0948: if (imports[i].getElementName().endsWith(dotTypeName))
0949: return imports[i];
0950: }
0951: return null;
0952: }
0953:
0954: private RefactoringStatus checkForMethodsWithConstructorNames()
0955: throws CoreException {
0956: IMethod[] methods = fType.getMethods();
0957: for (int i = 0; i < methods.length; i++) {
0958: if (methods[i].isConstructor())
0959: continue;
0960: RefactoringStatus check = Checks.checkIfConstructorName(
0961: methods[i], methods[i].getElementName(),
0962: getNewElementName());
0963: if (check != null)
0964: return check;
0965: }
0966: return null;
0967: }
0968:
0969: private RefactoringStatus checkImportedTypes() throws CoreException {
0970: RefactoringStatus result = new RefactoringStatus();
0971: IImportDeclaration[] imports = fType.getCompilationUnit()
0972: .getImports();
0973: for (int i = 0; i < imports.length; i++)
0974: analyzeImportDeclaration(imports[i], result);
0975: return result;
0976: }
0977:
0978: private RefactoringStatus checkTypesInCompilationUnit() {
0979: RefactoringStatus result = new RefactoringStatus();
0980: if (!Checks.isTopLevel(fType)) { //the other case checked in checkTypesInPackage
0981: IType siblingType = fType.getDeclaringType().getType(
0982: getNewElementName());
0983: if (siblingType.exists()) {
0984: String msg = Messages
0985: .format(
0986: RefactoringCoreMessages.RenameTypeRefactoring_member_type_exists,
0987: new String[] {
0988: getNewElementName(),
0989: JavaModelUtil
0990: .getFullyQualifiedName(fType
0991: .getDeclaringType()) });
0992: result.addError(msg, JavaStatusContext
0993: .create(siblingType));
0994: }
0995: }
0996: return result;
0997: }
0998:
0999: private RefactoringStatus analyseEnclosedTypes()
1000: throws CoreException {
1001: final ISourceRange typeRange = fType.getSourceRange();
1002: final RefactoringStatus result = new RefactoringStatus();
1003: CompilationUnit cuNode = new RefactoringASTParser(AST.JLS3)
1004: .parse(fType.getCompilationUnit(), false);
1005: cuNode.accept(new ASTVisitor() {
1006:
1007: public boolean visit(TypeDeclaration node) { // enums and annotations can't be local
1008: if (node.getStartPosition() <= typeRange.getOffset())
1009: return true;
1010: if (node.getStartPosition() > typeRange.getOffset()
1011: + typeRange.getLength())
1012: return true;
1013:
1014: if (getNewElementName().equals(
1015: node.getName().getIdentifier())) {
1016: RefactoringStatusContext context = JavaStatusContext
1017: .create(fType.getCompilationUnit(), node);
1018: String msg = null;
1019: if (node.isLocalTypeDeclaration()) {
1020: msg = Messages
1021: .format(
1022: RefactoringCoreMessages.RenameTypeRefactoring_local_type,
1023: new String[] {
1024: JavaElementUtil
1025: .createSignature(fType),
1026: getNewElementName() });
1027: } else if (node.isMemberTypeDeclaration()) {
1028: msg = Messages
1029: .format(
1030: RefactoringCoreMessages.RenameTypeRefactoring_member_type,
1031: new String[] {
1032: JavaElementUtil
1033: .createSignature(fType),
1034: getNewElementName() });
1035: }
1036: if (msg != null)
1037: result.addError(msg, context);
1038: }
1039:
1040: MethodDeclaration[] methods = node.getMethods();
1041: for (int i = 0; i < methods.length; i++) {
1042: if (Modifier.isNative(methods[i].getModifiers())) {
1043: RefactoringStatusContext context = JavaStatusContext
1044: .create(fType.getCompilationUnit(),
1045: methods[i]);
1046: String msg = Messages
1047: .format(
1048: RefactoringCoreMessages.RenameTypeRefactoring_enclosed_type_native,
1049: node.getName().getIdentifier());
1050: result.addWarning(msg, context);
1051: }
1052: }
1053: return true;
1054: }
1055: });
1056: return result;
1057: }
1058:
1059: private static ICompilationUnit getCompilationUnit(
1060: IImportDeclaration imp) {
1061: return (ICompilationUnit) imp.getParent().getParent();
1062: }
1063:
1064: private void analyzeImportedTypes(IType[] types,
1065: RefactoringStatus result, IImportDeclaration imp)
1066: throws CoreException {
1067: for (int i = 0; i < types.length; i++) {
1068: //could this be a problem (same package imports)?
1069: if (JdtFlags.isPublic(types[i])
1070: && types[i].getElementName().equals(
1071: getNewElementName())) {
1072: String msg = Messages
1073: .format(
1074: RefactoringCoreMessages.RenameTypeRefactoring_name_conflict1,
1075: new Object[] {
1076: JavaModelUtil
1077: .getFullyQualifiedName(types[i]),
1078: getFullPath(getCompilationUnit(imp)) });
1079: result.addError(msg, JavaStatusContext.create(imp));
1080: }
1081: }
1082: }
1083:
1084: private static IJavaElement convertFromImportDeclaration(
1085: IImportDeclaration declaration) throws CoreException {
1086: if (declaration.isOnDemand()) {
1087: String packageName = declaration.getElementName()
1088: .substring(0,
1089: declaration.getElementName().length() - 2);
1090: return JavaModelUtil.findTypeContainer(declaration
1091: .getJavaProject(), packageName);
1092: } else
1093: return JavaModelUtil.findTypeContainer(declaration
1094: .getJavaProject(), declaration.getElementName());
1095: }
1096:
1097: private void analyzeImportDeclaration(IImportDeclaration imp,
1098: RefactoringStatus result) throws CoreException {
1099: if (!imp.isOnDemand())
1100: return; //analyzed earlier
1101:
1102: IJavaElement imported = convertFromImportDeclaration(imp);
1103: if (imported == null)
1104: return;
1105:
1106: if (imported instanceof IPackageFragment) {
1107: ICompilationUnit[] cus = ((IPackageFragment) imported)
1108: .getCompilationUnits();
1109: for (int i = 0; i < cus.length; i++) {
1110: analyzeImportedTypes(cus[i].getTypes(), result, imp);
1111: }
1112: } else {
1113: //cast safe: see JavaModelUtility.convertFromImportDeclaration
1114: analyzeImportedTypes(((IType) imported).getTypes(), result,
1115: imp);
1116: }
1117: }
1118:
1119: /*
1120: * Analyzes all compilation units in which type is referenced
1121: */
1122: private RefactoringStatus analyzeAffectedCompilationUnits(
1123: IProgressMonitor pm) throws CoreException {
1124: RefactoringStatus result = new RefactoringStatus();
1125:
1126: result.merge(Checks.checkCompileErrorsInAffectedFiles(
1127: fReferences, fType.getResource()));
1128:
1129: pm.beginTask("", fReferences.length); //$NON-NLS-1$
1130: result.merge(checkConflictingTypes(pm));
1131: return result;
1132: }
1133:
1134: private RefactoringStatus checkConflictingTypes(IProgressMonitor pm)
1135: throws CoreException {
1136: RefactoringStatus result = new RefactoringStatus();
1137: IJavaSearchScope scope = RefactoringScopeFactory.create(fType);
1138: SearchPattern pattern = SearchPattern.createPattern(
1139: getNewElementName(), IJavaSearchConstants.TYPE,
1140: IJavaSearchConstants.ALL_OCCURRENCES,
1141: SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE);
1142: ICompilationUnit[] cusWithReferencesToConflictingTypes = RefactoringSearchEngine
1143: .findAffectedCompilationUnits(pattern, scope, pm,
1144: result);
1145: if (cusWithReferencesToConflictingTypes.length == 0)
1146: return result;
1147: ICompilationUnit[] cusWithReferencesToRenamedType = getCus(fReferences);
1148:
1149: ICompilationUnit[] intersection = isIntersectionEmpty(
1150: cusWithReferencesToRenamedType,
1151: cusWithReferencesToConflictingTypes);
1152: if (intersection.length == 0)
1153: return result;
1154:
1155: for (int i = 0; i < intersection.length; i++) {
1156: RefactoringStatusContext context = JavaStatusContext
1157: .create(intersection[i]);
1158: String message = Messages
1159: .format(
1160: RefactoringCoreMessages.RenameTypeRefactoring_another_type,
1161: new String[] { getNewElementName(),
1162: intersection[i].getElementName() });
1163: result.addError(message, context);
1164: }
1165: return result;
1166: }
1167:
1168: private static ICompilationUnit[] isIntersectionEmpty(
1169: ICompilationUnit[] a1, ICompilationUnit[] a2) {
1170: Set set1 = new HashSet(Arrays.asList(a1));
1171: Set set2 = new HashSet(Arrays.asList(a2));
1172: set1.retainAll(set2);
1173: return (ICompilationUnit[]) set1
1174: .toArray(new ICompilationUnit[set1.size()]);
1175: }
1176:
1177: private static ICompilationUnit[] getCus(
1178: SearchResultGroup[] searchResultGroups) {
1179: List cus = new ArrayList(searchResultGroups.length);
1180: for (int i = 0; i < searchResultGroups.length; i++) {
1181: ICompilationUnit cu = searchResultGroups[i]
1182: .getCompilationUnit();
1183: if (cu != null)
1184: cus.add(cu);
1185: }
1186: return (ICompilationUnit[]) cus
1187: .toArray(new ICompilationUnit[cus.size()]);
1188: }
1189:
1190: private static String getFullPath(ICompilationUnit cu) {
1191: Assert.isTrue(cu.exists());
1192: return cu.getResource().getFullPath().toString();
1193: }
1194:
1195: public Change createChange(IProgressMonitor monitor)
1196: throws CoreException {
1197: try {
1198: monitor
1199: .beginTask(
1200: RefactoringCoreMessages.RenameTypeRefactoring_creating_change,
1201: 4);
1202: String project = null;
1203: IJavaProject javaProject = fType.getJavaProject();
1204: if (javaProject != null)
1205: project = javaProject.getElementName();
1206: int flags = JavaRefactoringDescriptor.JAR_MIGRATION
1207: | JavaRefactoringDescriptor.JAR_REFACTORING
1208: | RefactoringDescriptor.STRUCTURAL_CHANGE;
1209: try {
1210: if (!Flags.isPrivate(fType.getFlags()))
1211: flags |= RefactoringDescriptor.MULTI_CHANGE;
1212: if (fType.isAnonymous() || fType.isLocal())
1213: flags |= JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT;
1214: } catch (JavaModelException exception) {
1215: JavaPlugin.log(exception);
1216: }
1217: final String description = Messages
1218: .format(
1219: RefactoringCoreMessages.RenameTypeProcessor_descriptor_description_short,
1220: fType.getElementName());
1221: final String header = Messages
1222: .format(
1223: RefactoringCoreMessages.RenameTypeProcessor_descriptor_description,
1224: new String[] {
1225: JavaElementLabels
1226: .getElementLabel(
1227: fType,
1228: JavaElementLabels.ALL_FULLY_QUALIFIED),
1229: getNewElementName() });
1230: final String comment = new JDTRefactoringDescriptorComment(
1231: project, this , header).asString();
1232: final RenameJavaElementDescriptor descriptor = new RenameJavaElementDescriptor(
1233: IJavaRefactorings.RENAME_TYPE);
1234: descriptor.setProject(project);
1235: descriptor.setDescription(description);
1236: descriptor.setComment(comment);
1237: descriptor.setFlags(flags);
1238: descriptor.setJavaElement(fType);
1239: descriptor.setNewName(getNewElementName());
1240: descriptor.setUpdateQualifiedNames(fUpdateQualifiedNames);
1241: descriptor
1242: .setUpdateTextualOccurrences(fUpdateTextualMatches);
1243: descriptor.setUpdateReferences(fUpdateReferences);
1244: if (fUpdateQualifiedNames && fFilePatterns != null
1245: && !"".equals(fFilePatterns)) //$NON-NLS-1$
1246: descriptor.setFileNamePatterns(fFilePatterns);
1247: descriptor
1248: .setUpdateSimilarDeclarations(fUpdateSimilarElements);
1249: descriptor.setMatchStrategy(fRenamingStrategy);
1250: final DynamicValidationRefactoringChange result = new DynamicValidationRefactoringChange(
1251: descriptor,
1252: RefactoringCoreMessages.RenameTypeProcessor_change_name);
1253:
1254: if (fChangeManager.containsChangesIn(fType
1255: .getCompilationUnit())) {
1256: TextChange textChange = fChangeManager.get(fType
1257: .getCompilationUnit());
1258: if (textChange instanceof TextFileChange) {
1259: ((TextFileChange) textChange)
1260: .setSaveMode(TextFileChange.FORCE_SAVE);
1261: }
1262: }
1263: result.addAll(fChangeManager.getAllChanges());
1264: if (willRenameCU()) {
1265: IResource resource = fType.getCompilationUnit()
1266: .getResource();
1267: if (resource != null && resource.isLinked()) {
1268: String ext = resource.getFileExtension();
1269: String renamedResourceName;
1270: if (ext == null)
1271: renamedResourceName = getNewElementName();
1272: else
1273: renamedResourceName = getNewElementName() + '.'
1274: + ext;
1275: result.add(new RenameResourceChange(null, fType
1276: .getCompilationUnit().getResource(),
1277: renamedResourceName, comment));
1278: } else {
1279: String renamedCUName = JavaModelUtil
1280: .getRenamedCUName(fType
1281: .getCompilationUnit(),
1282: getNewElementName());
1283: result.add(new RenameCompilationUnitChange(fType
1284: .getCompilationUnit(), renamedCUName));
1285: }
1286: }
1287: monitor.worked(1);
1288: return result;
1289: } finally {
1290: fChangeManager = null;
1291: }
1292: }
1293:
1294: public Change postCreateChange(Change[] participantChanges,
1295: IProgressMonitor pm) throws CoreException {
1296: if (fQualifiedNameSearchResult != null) {
1297: try {
1298: return fQualifiedNameSearchResult
1299: .getSingleChange(Changes
1300: .getModifiedFiles(participantChanges));
1301: } finally {
1302: fQualifiedNameSearchResult = null;
1303: }
1304: } else {
1305: return null;
1306: }
1307: }
1308:
1309: private boolean willRenameCU() throws CoreException {
1310: String name = JavaCore.removeJavaLikeExtension(fType
1311: .getCompilationUnit().getElementName());
1312: if (!(Checks.isTopLevel(fType) && name.equals(fType
1313: .getElementName())))
1314: return false;
1315: if (!checkNewPathValidity().isOK())
1316: return false;
1317: if (!Checks.checkCompilationUnitNewName(
1318: fType.getCompilationUnit(), getNewElementName()).isOK())
1319: return false;
1320: return true;
1321: }
1322:
1323: private void createChanges(IProgressMonitor pm)
1324: throws CoreException {
1325: try {
1326: pm.beginTask("", 12); //$NON-NLS-1$
1327: pm
1328: .setTaskName(RefactoringCoreMessages.RenameTypeProcessor_creating_changes);
1329:
1330: if (fUpdateReferences)
1331: addReferenceUpdates(fChangeManager,
1332: new SubProgressMonitor(pm, 3));
1333:
1334: // Similar names updates have already been added.
1335:
1336: pm.worked(1);
1337:
1338: IResource resource = fType.getCompilationUnit()
1339: .getResource();
1340: // if we have a linked resource then we don't use CU renaming
1341: // directly. So we have to update the code by ourselves.
1342: if ((resource != null && resource.isLinked())
1343: || !willRenameCU()) {
1344: addTypeDeclarationUpdate(fChangeManager);
1345: pm.worked(1);
1346:
1347: addConstructorRenames(fChangeManager);
1348: pm.worked(1);
1349: } else {
1350: pm.worked(2);
1351: }
1352:
1353: if (fUpdateTextualMatches) {
1354: pm
1355: .subTask(RefactoringCoreMessages.RenameTypeRefactoring_searching_text);
1356: TextMatchUpdater.perform(new SubProgressMonitor(pm, 1),
1357: RefactoringScopeFactory.create(fType), this ,
1358: fChangeManager, fReferences);
1359: if (fUpdateSimilarElements)
1360: addSimilarElementsTextualUpdates(fChangeManager,
1361: new SubProgressMonitor(pm, 3));
1362: }
1363:
1364: } finally {
1365: pm.done();
1366: }
1367: }
1368:
1369: private void addTypeDeclarationUpdate(TextChangeManager manager)
1370: throws CoreException {
1371: String name = RefactoringCoreMessages.RenameTypeRefactoring_update;
1372: int typeNameLength = fType.getElementName().length();
1373: ICompilationUnit cu = fType.getCompilationUnit();
1374: TextChangeCompatibility.addTextEdit(manager.get(cu), name,
1375: new ReplaceEdit(fType.getNameRange().getOffset(),
1376: typeNameLength, getNewElementName()));
1377: }
1378:
1379: private void addConstructorRenames(TextChangeManager manager)
1380: throws CoreException {
1381: ICompilationUnit cu = fType.getCompilationUnit();
1382: IMethod[] methods = fType.getMethods();
1383: int typeNameLength = fType.getElementName().length();
1384: for (int i = 0; i < methods.length; i++) {
1385: if (methods[i].isConstructor()) {
1386: /*
1387: * constructor declarations cannot be fully qualified so we can use simple replace here
1388: *
1389: * if (methods[i].getNameRange() == null), then it's a binary file so it's wrong anyway
1390: * (checked as a precondition)
1391: */
1392: String name = RefactoringCoreMessages.RenameTypeRefactoring_rename_constructor;
1393: TextChangeCompatibility.addTextEdit(manager.get(cu),
1394: name, new ReplaceEdit(methods[i].getNameRange()
1395: .getOffset(), typeNameLength,
1396: getNewElementName()));
1397: }
1398: }
1399: }
1400:
1401: private void addReferenceUpdates(TextChangeManager manager,
1402: IProgressMonitor pm) {
1403: pm.beginTask("", fReferences.length); //$NON-NLS-1$
1404: for (int i = 0; i < fReferences.length; i++) {
1405: ICompilationUnit cu = fReferences[i].getCompilationUnit();
1406: if (cu == null)
1407: continue;
1408:
1409: String name = RefactoringCoreMessages.RenameTypeRefactoring_update_reference;
1410: SearchMatch[] results = fReferences[i].getSearchResults();
1411:
1412: for (int j = 0; j < results.length; j++) {
1413: SearchMatch match = results[j];
1414: ReplaceEdit replaceEdit = new ReplaceEdit(match
1415: .getOffset(), match.getLength(),
1416: getNewElementName());
1417: TextChangeCompatibility.addTextEdit(manager.get(cu),
1418: name, replaceEdit, CATEGORY_TYPE_RENAME);
1419: }
1420: pm.worked(1);
1421: }
1422: }
1423:
1424: private void computeQualifiedNameMatches(IProgressMonitor pm)
1425: throws CoreException {
1426: IPackageFragment fragment = fType.getPackageFragment();
1427: if (fQualifiedNameSearchResult == null)
1428: fQualifiedNameSearchResult = new QualifiedNameSearchResult();
1429: QualifiedNameFinder.process(fQualifiedNameSearchResult, fType
1430: .getFullyQualifiedName(), fragment.getElementName()
1431: + "." + getNewElementName(), //$NON-NLS-1$
1432: fFilePatterns, fType.getJavaProject().getProject(), pm);
1433: }
1434:
1435: public RefactoringStatus initialize(RefactoringArguments arguments) {
1436: if (arguments instanceof JavaRefactoringArguments) {
1437: final JavaRefactoringArguments extended = (JavaRefactoringArguments) arguments;
1438: final String handle = extended
1439: .getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT);
1440: if (handle != null) {
1441: final IJavaElement element = JavaRefactoringDescriptorUtil
1442: .handleToElement(extended.getProject(), handle,
1443: false);
1444: if (element == null
1445: || !element.exists()
1446: || element.getElementType() != IJavaElement.TYPE)
1447: return ScriptableRefactoring
1448: .createInputFatalStatus(element,
1449: getRefactoring().getName(),
1450: IJavaRefactorings.RENAME_TYPE);
1451: else
1452: fType = (IType) element;
1453: } else
1454: return RefactoringStatus
1455: .createFatalErrorStatus(Messages
1456: .format(
1457: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1458: JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT));
1459: final String name = extended
1460: .getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME);
1461: if (name != null && !"".equals(name)) //$NON-NLS-1$
1462: setNewElementName(name);
1463: else
1464: return RefactoringStatus
1465: .createFatalErrorStatus(Messages
1466: .format(
1467: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1468: JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME));
1469: final String patterns = extended
1470: .getAttribute(ATTRIBUTE_PATTERNS);
1471: if (patterns != null && !"".equals(patterns)) //$NON-NLS-1$
1472: fFilePatterns = patterns;
1473: final String references = extended
1474: .getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_REFERENCES);
1475: if (references != null) {
1476: fUpdateReferences = Boolean.valueOf(references)
1477: .booleanValue();
1478: } else
1479: return RefactoringStatus
1480: .createFatalErrorStatus(Messages
1481: .format(
1482: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1483: JavaRefactoringDescriptorUtil.ATTRIBUTE_REFERENCES));
1484: final String matches = extended
1485: .getAttribute(ATTRIBUTE_TEXTUAL_MATCHES);
1486: if (matches != null) {
1487: fUpdateTextualMatches = Boolean.valueOf(matches)
1488: .booleanValue();
1489: } else
1490: return RefactoringStatus
1491: .createFatalErrorStatus(Messages
1492: .format(
1493: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1494: ATTRIBUTE_TEXTUAL_MATCHES));
1495: final String qualified = extended
1496: .getAttribute(ATTRIBUTE_QUALIFIED);
1497: if (qualified != null) {
1498: fUpdateQualifiedNames = Boolean.valueOf(qualified)
1499: .booleanValue();
1500: } else
1501: return RefactoringStatus
1502: .createFatalErrorStatus(Messages
1503: .format(
1504: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1505: ATTRIBUTE_QUALIFIED));
1506: final String similarDeclarations = extended
1507: .getAttribute(ATTRIBUTE_SIMILAR_DECLARATIONS);
1508: if (similarDeclarations != null)
1509: fUpdateSimilarElements = Boolean.valueOf(
1510: similarDeclarations).booleanValue();
1511: else
1512: return RefactoringStatus
1513: .createFatalErrorStatus(Messages
1514: .format(
1515: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1516: ATTRIBUTE_SIMILAR_DECLARATIONS));
1517: final String similarDeclarationsMatchingStrategy = extended
1518: .getAttribute(ATTRIBUTE_MATCHING_STRATEGY);
1519: if (similarDeclarationsMatchingStrategy != null) {
1520: try {
1521: fRenamingStrategy = Integer.valueOf(
1522: similarDeclarationsMatchingStrategy)
1523: .intValue();
1524: } catch (NumberFormatException e) {
1525: return RefactoringStatus
1526: .createFatalErrorStatus(Messages
1527: .format(
1528: RefactoringCoreMessages.InitializableRefactoring_illegal_argument,
1529: new String[] {
1530: similarDeclarationsMatchingStrategy,
1531: ATTRIBUTE_QUALIFIED }));
1532: }
1533: } else
1534: return RefactoringStatus
1535: .createFatalErrorStatus(Messages
1536: .format(
1537: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1538: ATTRIBUTE_MATCHING_STRATEGY));
1539: } else
1540: return RefactoringStatus
1541: .createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments);
1542: return new RefactoringStatus();
1543: }
1544:
1545: // --------- Similar names
1546:
1547: /**
1548: * Creates and initializes the refactoring processors for similarly named elements
1549: * @param progressMonitor
1550: * @param context
1551: * @return status
1552: * @throws CoreException
1553: */
1554: private RefactoringStatus initializeSimilarElementsRenameProcessors(
1555: IProgressMonitor progressMonitor,
1556: CheckConditionsContext context) throws CoreException {
1557:
1558: Assert.isNotNull(fPreloadedElementToName);
1559: Assert.isNotNull(fPreloadedElementToSelection);
1560:
1561: final RefactoringStatus status = new RefactoringStatus();
1562: final Set handledTopLevelMethods = new HashSet();
1563: final Set warnings = new HashSet();
1564: final List processors = new ArrayList();
1565: fFinalSimilarElementToName = new HashMap();
1566:
1567: CompilationUnit currentResolvedCU = null;
1568: ICompilationUnit currentCU = null;
1569:
1570: int current = 0;
1571: final int max = fPreloadedElementToName.size();
1572:
1573: progressMonitor.beginTask("", max * 3); //$NON-NLS-1$
1574: progressMonitor
1575: .setTaskName(RefactoringCoreMessages.RenameTypeProcessor_checking_similarly_named_declarations_refactoring_conditions);
1576:
1577: for (Iterator iter = fPreloadedElementToName.keySet()
1578: .iterator(); iter.hasNext();) {
1579:
1580: final IJavaElement element = (IJavaElement) iter.next();
1581:
1582: current++;
1583: progressMonitor.worked(3);
1584:
1585: // not selected? -> skip
1586: if (!((Boolean) (fPreloadedElementToSelection.get(element)))
1587: .booleanValue())
1588: continue;
1589:
1590: // already registered? (may happen with overridden methods) -> skip
1591: if (fFinalSimilarElementToName.containsKey(element))
1592: continue;
1593:
1594: // CompilationUnit changed? (note: fPreloadedElementToName is sorted by CompilationUnit)
1595: ICompilationUnit newCU = (ICompilationUnit) element
1596: .getAncestor(IJavaElement.COMPILATION_UNIT);
1597:
1598: if (!newCU.equals(currentCU)) {
1599:
1600: checkCUCompleteConditions(status, currentResolvedCU,
1601: currentCU, processors);
1602:
1603: if (status.hasFatalError())
1604: return status;
1605:
1606: // reset values
1607: currentResolvedCU = null;
1608: currentCU = newCU;
1609: processors.clear();
1610: }
1611:
1612: final String newName = (String) fPreloadedElementToName
1613: .get(element);
1614: RefactoringProcessor processor = null;
1615:
1616: if (element instanceof ILocalVariable) {
1617: final ILocalVariable currentLocal = (ILocalVariable) element;
1618:
1619: if (currentResolvedCU == null)
1620: currentResolvedCU = new RefactoringASTParser(
1621: AST.JLS3).parse(currentCU, true);
1622:
1623: processor = createLocalRenameProcessor(currentLocal,
1624: newName, currentResolvedCU);
1625:
1626: // don't check for conflicting rename => is done by #checkCUCompleteConditions().
1627:
1628: if (status.hasFatalError())
1629: return status;
1630: fFinalSimilarElementToName.put(currentLocal, newName);
1631: }
1632: if (element instanceof IField) {
1633: final IField currentField = (IField) element;
1634: processor = createFieldRenameProcessor(currentField,
1635: newName);
1636:
1637: status.merge(checkForConflictingRename(currentField,
1638: newName));
1639: if (status.hasFatalError())
1640: return status;
1641: fFinalSimilarElementToName.put(currentField, newName);
1642: }
1643: if (element instanceof IMethod) {
1644: IMethod currentMethod = (IMethod) element;
1645: if (MethodChecks.isVirtual(currentMethod)) {
1646:
1647: final IType declaringType = currentMethod
1648: .getDeclaringType();
1649: ITypeHierarchy hierarchy = null;
1650: if (!declaringType.isInterface())
1651: hierarchy = declaringType
1652: .newTypeHierarchy(new NullProgressMonitor());
1653:
1654: final IMethod topmost = MethodChecks
1655: .getTopmostMethod(currentMethod, hierarchy,
1656: new NullProgressMonitor());
1657: if (topmost != null)
1658: currentMethod = topmost;
1659: if (handledTopLevelMethods.contains(currentMethod))
1660: continue;
1661: handledTopLevelMethods.add(currentMethod);
1662: final IMethod[] ripples = MethodChecks
1663: .getOverriddenMethods(currentMethod,
1664: new NullProgressMonitor());
1665:
1666: if (checkForWarnings(warnings, newName, ripples))
1667: continue;
1668:
1669: status.merge(checkForConflictingRename(ripples,
1670: newName));
1671: if (status.hasFatalError())
1672: return status;
1673:
1674: processor = createVirtualMethodRenameProcessor(
1675: currentMethod, newName, ripples, hierarchy);
1676: fFinalSimilarElementToName.put(currentMethod,
1677: newName);
1678: for (int i = 0; i < ripples.length; i++) {
1679: fFinalSimilarElementToName.put(ripples[i],
1680: newName);
1681: }
1682: } else {
1683:
1684: status.merge(checkForConflictingRename(
1685: new IMethod[] { currentMethod }, newName));
1686: if (status.hasFatalError())
1687: break;
1688:
1689: fFinalSimilarElementToName.put(currentMethod,
1690: newName);
1691:
1692: processor = createNonVirtualMethodRenameProcessor(
1693: currentMethod, newName);
1694: }
1695: }
1696:
1697: progressMonitor
1698: .subTask(Messages
1699: .format(
1700: RefactoringCoreMessages.RenameTypeProcessor_progress_current_total,
1701: new Object[] {
1702: String.valueOf(current),
1703: String.valueOf(max) }));
1704:
1705: status
1706: .merge(processor
1707: .checkInitialConditions(new NoOverrideProgressMonitor(
1708: progressMonitor, 1)));
1709:
1710: if (status.hasFatalError())
1711: return status;
1712:
1713: status.merge(processor.checkFinalConditions(
1714: new NoOverrideProgressMonitor(progressMonitor, 1),
1715: context));
1716:
1717: if (status.hasFatalError())
1718: return status;
1719:
1720: processors.add(processor);
1721:
1722: progressMonitor.worked(1);
1723:
1724: if (progressMonitor.isCanceled())
1725: throw new OperationCanceledException();
1726: }
1727:
1728: // check last CU
1729: checkCUCompleteConditions(status, currentResolvedCU, currentCU,
1730: processors);
1731:
1732: status.merge(addWarnings(warnings));
1733:
1734: progressMonitor.done();
1735: return status;
1736: }
1737:
1738: private void checkCUCompleteConditions(
1739: final RefactoringStatus status,
1740: CompilationUnit currentResolvedCU,
1741: ICompilationUnit currentCU, List processors)
1742: throws CoreException {
1743:
1744: // check local variable conditions
1745: List locals = getProcessorsOfType(processors,
1746: RenameLocalVariableProcessor.class);
1747: if (!locals.isEmpty()) {
1748: RenameAnalyzeUtil.LocalAnalyzePackage[] analyzePackages = new RenameAnalyzeUtil.LocalAnalyzePackage[locals
1749: .size()];
1750: TextChangeManager manager = new TextChangeManager();
1751: int current = 0;
1752: TextChange textChange = manager.get(currentCU);
1753: textChange.setKeepPreviewEdits(true);
1754: for (Iterator iterator = locals.iterator(); iterator
1755: .hasNext();) {
1756: RenameLocalVariableProcessor localProcessor = (RenameLocalVariableProcessor) iterator
1757: .next();
1758: RenameAnalyzeUtil.LocalAnalyzePackage analyzePackage = localProcessor
1759: .getLocalAnalyzePackage();
1760: analyzePackages[current] = analyzePackage;
1761: for (int i = 0; i < analyzePackage.fOccurenceEdits.length; i++) {
1762: TextChangeCompatibility
1763: .addTextEdit(
1764: textChange,
1765: "", analyzePackage.fOccurenceEdits[i], GroupCategorySet.NONE); //$NON-NLS-1$
1766: }
1767: current++;
1768: }
1769: status.merge(RenameAnalyzeUtil.analyzeLocalRenames(
1770: analyzePackages, textChange, currentResolvedCU,
1771: false));
1772: }
1773:
1774: /*
1775: * There is room for performance improvement here: One could move
1776: * shadowing analyzes out of the field and method processors and perform
1777: * it here, thus saving on working copy creation. Drawback is increased
1778: * heap consumption.
1779: */
1780: }
1781:
1782: private List getProcessorsOfType(List processors, Class type) {
1783: List tmp = new ArrayList();
1784: for (Iterator iter = processors.iterator(); iter.hasNext();) {
1785: RefactoringProcessor element = (RefactoringProcessor) iter
1786: .next();
1787: if (element.getClass().equals(type))
1788: tmp.add(element);
1789: }
1790: return tmp;
1791: }
1792:
1793: // ------------------ Error checking -------------
1794:
1795: /**
1796: * Checks whether one of the given methods, which will all be renamed to
1797: * "newName", shares a type with another already registered method which is
1798: * renamed to the same new name and shares the same parameters.
1799: * @param methods
1800: * @param newName
1801: * @return status
1802: *
1803: * @see #checkForConflictingRename(IField, String)
1804: */
1805: private RefactoringStatus checkForConflictingRename(
1806: IMethod[] methods, String newName) {
1807: RefactoringStatus status = new RefactoringStatus();
1808: for (Iterator iter = fFinalSimilarElementToName.keySet()
1809: .iterator(); iter.hasNext();) {
1810: IJavaElement element = (IJavaElement) iter.next();
1811: if (element instanceof IMethod) {
1812: IMethod alreadyRegisteredMethod = (IMethod) element;
1813: String alreadyRegisteredMethodName = (String) fFinalSimilarElementToName
1814: .get(element);
1815: for (int i = 0; i < methods.length; i++) {
1816: IMethod method2 = methods[i];
1817: if ((alreadyRegisteredMethodName.equals(newName))
1818: && (method2.getDeclaringType()
1819: .equals(alreadyRegisteredMethod
1820: .getDeclaringType()))
1821: && (sameParams(alreadyRegisteredMethod,
1822: method2))) {
1823: String message = Messages
1824: .format(
1825: RefactoringCoreMessages.RenameTypeProcessor_cannot_rename_methods_same_new_name,
1826: new String[] {
1827: alreadyRegisteredMethod
1828: .getElementName(),
1829: method2
1830: .getElementName(),
1831: alreadyRegisteredMethod
1832: .getDeclaringType()
1833: .getFullyQualifiedName(),
1834: newName });
1835: status.addFatalError(message);
1836: return status;
1837: }
1838: }
1839: }
1840: }
1841: return status;
1842: }
1843:
1844: private static boolean sameParams(IMethod method, IMethod method2) {
1845:
1846: if (method.getNumberOfParameters() != method2
1847: .getNumberOfParameters())
1848: return false;
1849:
1850: String[] params = method.getParameterTypes();
1851: String[] params2 = method2.getParameterTypes();
1852:
1853: for (int i = 0; i < params.length; i++) {
1854: String t1 = Signature.getSimpleName(Signature
1855: .toString(params[i]));
1856: String t2 = Signature.getSimpleName(Signature
1857: .toString(params2[i]));
1858: if (!t1.equals(t2)) {
1859: return false;
1860: }
1861: }
1862: return true;
1863: }
1864:
1865: /**
1866: * If suffix matching is enabled, the refactoring may suggest two fields to
1867: * have the same name which reside in the same type. Same thing may also
1868: * happen if the user makes poor choices for the field names.
1869: *
1870: * Consider: FooBarThing fFooBarThing; FooBarThing fBarThing;
1871: *
1872: * Rename "FooBarThing" to "DifferentHunk". Suggestion for both fields is
1873: * "fDifferentHunk" (and rightly so).
1874: * @param currentField
1875: * @param newName
1876: * @return status
1877: */
1878: private RefactoringStatus checkForConflictingRename(
1879: IField currentField, String newName) {
1880: RefactoringStatus status = new RefactoringStatus();
1881: for (Iterator iter = fFinalSimilarElementToName.keySet()
1882: .iterator(); iter.hasNext();) {
1883: IJavaElement element = (IJavaElement) iter.next();
1884: if (element instanceof IField) {
1885: IField alreadyRegisteredField = (IField) element;
1886: String alreadyRegisteredFieldName = (String) fFinalSimilarElementToName
1887: .get(element);
1888: if (alreadyRegisteredFieldName.equals(newName)) {
1889: if (alreadyRegisteredField.getDeclaringType()
1890: .equals(currentField.getDeclaringType())) {
1891:
1892: String message = Messages
1893: .format(
1894: RefactoringCoreMessages.RenameTypeProcessor_cannot_rename_fields_same_new_name,
1895: new String[] {
1896: alreadyRegisteredField
1897: .getElementName(),
1898: currentField
1899: .getElementName(),
1900: alreadyRegisteredField
1901: .getDeclaringType()
1902: .getFullyQualifiedName(),
1903: newName });
1904: status.addFatalError(message);
1905: return status;
1906: }
1907: }
1908: }
1909: }
1910: return status;
1911: }
1912:
1913: private RefactoringStatus addWarnings(final Set warnings) {
1914: RefactoringStatus status = new RefactoringStatus();
1915:
1916: // Remove deleted ripple methods from user selection and add warnings
1917: for (Iterator iter = warnings.iterator(); iter.hasNext();) {
1918: final Warning warning = (Warning) iter.next();
1919: final IMethod[] elements = warning.getRipple();
1920: if (warning.isSelectionWarning()) {
1921: String message = Messages
1922: .format(
1923: RefactoringCoreMessages.RenameTypeProcessor_deselected_method_is_overridden,
1924: new String[] {
1925: JavaElementLabels
1926: .getElementLabel(
1927: elements[0],
1928: JavaElementLabels.ALL_DEFAULT),
1929: JavaElementLabels
1930: .getElementLabel(
1931: elements[0]
1932: .getDeclaringType(),
1933: JavaElementLabels.ALL_DEFAULT) });
1934: status.addWarning(message);
1935: }
1936: if (warning.isNameWarning()) {
1937: String message = Messages
1938: .format(
1939: RefactoringCoreMessages.RenameTypeProcessor_renamed_method_is_overridden,
1940: new String[] {
1941: JavaElementLabels
1942: .getElementLabel(
1943: elements[0],
1944: JavaElementLabels.ALL_DEFAULT),
1945: JavaElementLabels
1946: .getElementLabel(
1947: elements[0]
1948: .getDeclaringType(),
1949: JavaElementLabels.ALL_DEFAULT) });
1950: status.addWarning(message);
1951: }
1952: for (int i = 0; i < elements.length; i++)
1953: fPreloadedElementToSelection.put(elements[i],
1954: Boolean.FALSE);
1955: }
1956: return status;
1957: }
1958:
1959: /*
1960: * If one of the methods of this ripple was deselected or renamed by
1961: * the user, deselect the whole chain and add warnings.
1962: */
1963: private boolean checkForWarnings(final Set warnings,
1964: final String newName, final IMethod[] ripples) {
1965:
1966: boolean addSelectionWarning = false;
1967: boolean addNameWarning = false;
1968: for (int i = 0; i < ripples.length; i++) {
1969: String newNameOfRipple = (String) fPreloadedElementToName
1970: .get(ripples[i]);
1971: Boolean selected = (Boolean) fPreloadedElementToSelection
1972: .get(ripples[i]);
1973:
1974: // selected may be null here due to supermethods like
1975: // setSomeClass(Object class) (subsignature match)
1976: // Don't add a warning.
1977: if (selected == null)
1978: continue;
1979:
1980: if (!selected.booleanValue())
1981: addSelectionWarning = true;
1982:
1983: if (!newName.equals(newNameOfRipple))
1984: addNameWarning = true;
1985: }
1986: if (addSelectionWarning || addNameWarning)
1987: warnings.add(new Warning(ripples, addSelectionWarning,
1988: addNameWarning));
1989:
1990: return (addSelectionWarning || addNameWarning);
1991: }
1992:
1993: private class Warning {
1994:
1995: private IMethod[] fRipple;
1996: private boolean fSelectionWarning;
1997: private boolean fNameWarning;
1998:
1999: public Warning(IMethod[] ripple, boolean isSelectionWarning,
2000: boolean isNameWarning) {
2001: fRipple = ripple;
2002: fSelectionWarning = isSelectionWarning;
2003: fNameWarning = isNameWarning;
2004: }
2005:
2006: public boolean isNameWarning() {
2007: return fNameWarning;
2008: }
2009:
2010: public IMethod[] getRipple() {
2011: return fRipple;
2012: }
2013:
2014: public boolean isSelectionWarning() {
2015: return fSelectionWarning;
2016: }
2017: }
2018:
2019: // ----------------- Processor creation --------
2020:
2021: private RenameMethodProcessor createVirtualMethodRenameProcessor(
2022: IMethod currentMethod, String newMethodName,
2023: IMethod[] ripples, ITypeHierarchy hierarchy)
2024: throws JavaModelException {
2025: RenameMethodProcessor processor = new RenameVirtualMethodProcessor(
2026: currentMethod, ripples, fChangeManager, hierarchy,
2027: CATEGORY_METHOD_RENAME);
2028: initMethodProcessor(processor, newMethodName);
2029: return processor;
2030: }
2031:
2032: private RenameMethodProcessor createNonVirtualMethodRenameProcessor(
2033: IMethod currentMethod, String newMethodName) {
2034: RenameMethodProcessor processor = new RenameNonVirtualMethodProcessor(
2035: currentMethod, fChangeManager, CATEGORY_METHOD_RENAME);
2036: initMethodProcessor(processor, newMethodName);
2037: return processor;
2038: }
2039:
2040: private void initMethodProcessor(RenameMethodProcessor processor,
2041: String newMethodName) {
2042: processor.setNewElementName(newMethodName);
2043: processor.setUpdateReferences(getUpdateReferences());
2044: }
2045:
2046: private RenameFieldProcessor createFieldRenameProcessor(
2047: final IField field, final String newName) {
2048: final RenameFieldProcessor processor = new RenameFieldProcessor(
2049: field, fChangeManager, CATEGORY_FIELD_RENAME);
2050: processor.setNewElementName(newName);
2051: processor.setRenameGetter(false);
2052: processor.setRenameSetter(false);
2053: processor.setUpdateReferences(getUpdateReferences());
2054: processor.setUpdateTextualMatches(false);
2055: return processor;
2056: }
2057:
2058: private RenameLocalVariableProcessor createLocalRenameProcessor(
2059: final ILocalVariable local, final String newName,
2060: final CompilationUnit compilationUnit) {
2061: final RenameLocalVariableProcessor processor = new RenameLocalVariableProcessor(
2062: local, fChangeManager, compilationUnit,
2063: CATEGORY_LOCAL_RENAME);
2064: processor.setNewElementName(newName);
2065: processor.setUpdateReferences(getUpdateReferences());
2066: return processor;
2067: }
2068:
2069: // ----------- Edit creation -----------
2070:
2071: /**
2072: * Updates textual matches for fields.
2073: *
2074: * Strategy for matching text matches: Match and replace all fully qualified
2075: * field names, but non-qualified field names only iff there are no fields
2076: * which have the same original, but a different new name. Don't add java
2077: * references; duplicate edits may be created but do not matter.
2078: * @param manager
2079: * @param monitor
2080: * @throws CoreException
2081: */
2082: private void addSimilarElementsTextualUpdates(
2083: TextChangeManager manager, IProgressMonitor monitor)
2084: throws CoreException {
2085:
2086: final Map simpleNames = new HashMap();
2087: final List forbiddenSimpleNames = new ArrayList();
2088:
2089: for (Iterator iter = fFinalSimilarElementToName.keySet()
2090: .iterator(); iter.hasNext();) {
2091: final IJavaElement element = (IJavaElement) iter.next();
2092: if (element instanceof IField) {
2093:
2094: if (forbiddenSimpleNames.contains(element
2095: .getElementName()))
2096: continue;
2097:
2098: final String registeredNewName = (String) simpleNames
2099: .get(element.getElementName());
2100: final String newNameToCheck = (String) fFinalSimilarElementToName
2101: .get(element);
2102: if (registeredNewName == null)
2103: simpleNames.put(element.getElementName(),
2104: newNameToCheck);
2105: else if (!registeredNewName.equals(newNameToCheck))
2106: forbiddenSimpleNames.add(element.getElementName());
2107: }
2108: }
2109:
2110: for (Iterator iter = fFinalSimilarElementToName.keySet()
2111: .iterator(); iter.hasNext();) {
2112: final IJavaElement element = (IJavaElement) iter.next();
2113: if (element instanceof IField) {
2114: final IField field = (IField) element;
2115: final String newName = (String) fFinalSimilarElementToName
2116: .get(field);
2117: TextMatchUpdater.perform(monitor,
2118: RefactoringScopeFactory.create(field), field
2119: .getElementName(), field
2120: .getDeclaringType()
2121: .getFullyQualifiedName(), newName,
2122: manager, new SearchResultGroup[0],
2123: forbiddenSimpleNames.contains(field
2124: .getElementName()));
2125: }
2126: }
2127: }
2128:
2129: // ------ UI interaction
2130:
2131: /**
2132: * @return the map of similarly named elements (IJavaElement -> String with new name)
2133: * This map is live. Callers may change the new names of the elements; they
2134: * may not change the key set.
2135: */
2136: public Map/* <IJavaElement, String> */getSimilarElementsToNewNames() {
2137: return fPreloadedElementToName;
2138: }
2139:
2140: /**
2141: * @return the map of similarly named elements (IJavaElement -> Boolean if selected) This
2142: * map is live. Callers may change the selection status of the elements;
2143: * they may not change the key set.
2144: */
2145: public Map/* <IJavaElement, Boolean> */getSimilarElementsToSelection() {
2146: return fPreloadedElementToSelection;
2147: }
2148:
2149: /**
2150: * Resets the element maps back to the original status. This affects the
2151: * maps returned in {@link #getSimilarElementsToNewNames() } and
2152: * {@link #getSimilarElementsToSelection() }. All new names are reset to
2153: * the calculated ones and every element gets selected.
2154: */
2155: public void resetSelectedSimilarElements() {
2156: Assert.isNotNull(fPreloadedElementToName);
2157: for (Iterator iter = fPreloadedElementToNameDefault.keySet()
2158: .iterator(); iter.hasNext();) {
2159: final IJavaElement element = (IJavaElement) iter.next();
2160: fPreloadedElementToName.put(element,
2161: fPreloadedElementToNameDefault.get(element));
2162: fPreloadedElementToSelection.put(element, Boolean.TRUE);
2163: }
2164: }
2165:
2166: /**
2167: * @return true iff the "update similarly named elements" flag is set AND the
2168: * search yielded some elements to be renamed.
2169: */
2170: public boolean hasSimilarElementsToRename() {
2171: if (!fUpdateSimilarElements)
2172: return false;
2173: if (fPreloadedElementToName == null)
2174: return false;
2175: if (fPreloadedElementToName.size() == 0)
2176: return false;
2177: return true;
2178: }
2179: }
|