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.List;
0015:
0016: import org.eclipse.text.edits.ReplaceEdit;
0017: import org.eclipse.text.edits.TextEdit;
0018:
0019: import org.eclipse.core.runtime.Assert;
0020: import org.eclipse.core.runtime.CoreException;
0021: import org.eclipse.core.runtime.IProgressMonitor;
0022: import org.eclipse.core.runtime.NullProgressMonitor;
0023: import org.eclipse.core.runtime.SubProgressMonitor;
0024:
0025: import org.eclipse.core.resources.IFile;
0026:
0027: import org.eclipse.ltk.core.refactoring.Change;
0028: import org.eclipse.ltk.core.refactoring.GroupCategorySet;
0029: import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
0030: import org.eclipse.ltk.core.refactoring.RefactoringStatus;
0031: import org.eclipse.ltk.core.refactoring.TextChange;
0032: import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
0033: import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments;
0034: import org.eclipse.ltk.core.refactoring.participants.RenameArguments;
0035:
0036: import org.eclipse.jdt.core.Flags;
0037: import org.eclipse.jdt.core.ICompilationUnit;
0038: import org.eclipse.jdt.core.IField;
0039: import org.eclipse.jdt.core.IJavaElement;
0040: import org.eclipse.jdt.core.IJavaProject;
0041: import org.eclipse.jdt.core.IMethod;
0042: import org.eclipse.jdt.core.ISourceRange;
0043: import org.eclipse.jdt.core.IType;
0044: import org.eclipse.jdt.core.ITypeHierarchy;
0045: import org.eclipse.jdt.core.JavaModelException;
0046: import org.eclipse.jdt.core.WorkingCopyOwner;
0047: import org.eclipse.jdt.core.dom.FieldDeclaration;
0048: import org.eclipse.jdt.core.dom.MethodDeclaration;
0049: import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
0050: import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
0051: import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor;
0052: import org.eclipse.jdt.core.refactoring.descriptors.RenameJavaElementDescriptor;
0053: import org.eclipse.jdt.core.search.IJavaSearchConstants;
0054: import org.eclipse.jdt.core.search.IJavaSearchScope;
0055: import org.eclipse.jdt.core.search.SearchEngine;
0056: import org.eclipse.jdt.core.search.SearchMatch;
0057: import org.eclipse.jdt.core.search.SearchPattern;
0058:
0059: import org.eclipse.jdt.internal.corext.codemanipulation.GetterSetterUtil;
0060: import org.eclipse.jdt.internal.corext.refactoring.Checks;
0061: import org.eclipse.jdt.internal.corext.refactoring.CollectingSearchRequestor;
0062: import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
0063: import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
0064: import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil;
0065: import org.eclipse.jdt.internal.corext.refactoring.RefactoringAvailabilityTester;
0066: import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
0067: import org.eclipse.jdt.internal.corext.refactoring.RefactoringScopeFactory;
0068: import org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine;
0069: import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup;
0070: import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext;
0071: import org.eclipse.jdt.internal.corext.refactoring.changes.CompilationUnitChange;
0072: import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange;
0073: import org.eclipse.jdt.internal.corext.refactoring.changes.TextChangeCompatibility;
0074: import org.eclipse.jdt.internal.corext.refactoring.code.ScriptableRefactoring;
0075: import org.eclipse.jdt.internal.corext.refactoring.delegates.DelegateCreator;
0076: import org.eclipse.jdt.internal.corext.refactoring.delegates.DelegateFieldCreator;
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.tagging.ITextUpdating;
0084: import org.eclipse.jdt.internal.corext.refactoring.util.JavaElementUtil;
0085: import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil;
0086: import org.eclipse.jdt.internal.corext.refactoring.util.TextChangeManager;
0087: import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
0088: import org.eclipse.jdt.internal.corext.util.JdtFlags;
0089: import org.eclipse.jdt.internal.corext.util.Messages;
0090: import org.eclipse.jdt.internal.corext.util.SearchUtils;
0091:
0092: import org.eclipse.jdt.ui.JavaElementLabels;
0093: import org.eclipse.jdt.ui.refactoring.IRefactoringProcessorIds;
0094:
0095: import org.eclipse.jdt.internal.ui.JavaPlugin;
0096: import org.eclipse.jdt.internal.ui.refactoring.RefactoringSaveHelper;
0097:
0098: public class RenameFieldProcessor extends JavaRenameProcessor implements
0099: IReferenceUpdating, ITextUpdating, IDelegateUpdating {
0100:
0101: protected static final String ATTRIBUTE_TEXTUAL_MATCHES = "textual"; //$NON-NLS-1$
0102: private static final String ATTRIBUTE_RENAME_GETTER = "getter"; //$NON-NLS-1$
0103: private static final String ATTRIBUTE_RENAME_SETTER = "setter"; //$NON-NLS-1$
0104: private static final String ATTRIBUTE_DELEGATE = "delegate"; //$NON-NLS-1$
0105: private static final String ATTRIBUTE_DEPRECATE = "deprecate"; //$NON-NLS-1$
0106:
0107: protected IField fField;
0108: private SearchResultGroup[] fReferences;
0109: private TextChangeManager fChangeManager;
0110: protected boolean fUpdateReferences;
0111: protected boolean fUpdateTextualMatches;
0112: private boolean fRenameGetter;
0113: private boolean fRenameSetter;
0114: private boolean fIsComposite;
0115: private GroupCategorySet fCategorySet;
0116: private boolean fDelegateUpdating;
0117: private boolean fDelegateDeprecation;
0118:
0119: /**
0120: * Creates a new rename field processor.
0121: * @param field the field, or <code>null</code> if invoked by scripting
0122: */
0123: public RenameFieldProcessor(IField field) {
0124: this (field, new TextChangeManager(true), null);
0125: fIsComposite = false;
0126: }
0127:
0128: /**
0129: * Creates a new rename field processor.
0130: * <p>
0131: * This constructor is only used by <code>RenameTypeProcessor</code>.
0132: * </p>
0133: * @param field the field
0134: * @param manager the change manager
0135: * @param categorySet the group category set
0136: */
0137: RenameFieldProcessor(IField field, TextChangeManager manager,
0138: GroupCategorySet categorySet) {
0139: initialize(field);
0140: fChangeManager = manager;
0141: fCategorySet = categorySet;
0142: fDelegateUpdating = false;
0143: fDelegateDeprecation = true;
0144: fIsComposite = true;
0145: }
0146:
0147: private void initialize(IField field) {
0148: fField = field;
0149: if (fField != null)
0150: setNewElementName(fField.getElementName());
0151: fUpdateReferences = true;
0152: fUpdateTextualMatches = false;
0153:
0154: fRenameGetter = false;
0155: fRenameSetter = false;
0156: }
0157:
0158: public String getIdentifier() {
0159: return IRefactoringProcessorIds.RENAME_FIELD_PROCESSOR;
0160: }
0161:
0162: public boolean isApplicable() throws CoreException {
0163: return RefactoringAvailabilityTester
0164: .isRenameFieldAvailable(fField);
0165: }
0166:
0167: public String getProcessorName() {
0168: return RefactoringCoreMessages.RenameFieldRefactoring_name;
0169: }
0170:
0171: protected String[] getAffectedProjectNatures() throws CoreException {
0172: return JavaProcessors.computeAffectedNatures(fField);
0173: }
0174:
0175: public IField getField() {
0176: return fField;
0177: }
0178:
0179: public Object[] getElements() {
0180: return new Object[] { fField };
0181: }
0182:
0183: protected RenameModifications computeRenameModifications()
0184: throws CoreException {
0185: RenameModifications result = new RenameModifications();
0186: result.rename(fField, new RenameArguments(getNewElementName(),
0187: getUpdateReferences()));
0188: if (fRenameGetter) {
0189: IMethod getter = getGetter();
0190: if (getter != null) {
0191: result.rename(getter, new RenameArguments(
0192: getNewGetterName(), getUpdateReferences()));
0193: }
0194: }
0195: if (fRenameSetter) {
0196: IMethod setter = getSetter();
0197: if (setter != null) {
0198: result.rename(setter, new RenameArguments(
0199: getNewSetterName(), getUpdateReferences()));
0200: }
0201: }
0202: return result;
0203: }
0204:
0205: protected IFile[] getChangedFiles() {
0206: return ResourceUtil.getFiles(fChangeManager
0207: .getAllCompilationUnits());
0208: }
0209:
0210: //---- IRenameProcessor -------------------------------------
0211:
0212: public final String getCurrentElementName() {
0213: return fField.getElementName();
0214: }
0215:
0216: public final String getCurrentElementQualifier() {
0217: return JavaModelUtil.getFullyQualifiedName(fField
0218: .getDeclaringType());
0219: }
0220:
0221: public RefactoringStatus checkNewElementName(String newName)
0222: throws CoreException {
0223: Assert.isNotNull(newName, "new name"); //$NON-NLS-1$
0224: RefactoringStatus result = Checks.checkFieldName(newName,
0225: fField);
0226:
0227: if (isInstanceField(fField)
0228: && (!Checks.startsWithLowerCase(newName)))
0229: result
0230: .addWarning(fIsComposite ? Messages
0231: .format(
0232: RefactoringCoreMessages.RenameFieldRefactoring_should_start_lowercase2,
0233: new String[] {
0234: newName,
0235: fField.getDeclaringType()
0236: .getElementName() })
0237: : RefactoringCoreMessages.RenameFieldRefactoring_should_start_lowercase);
0238:
0239: if (Checks.isAlreadyNamed(fField, newName))
0240: result
0241: .addError(
0242: fIsComposite ? Messages
0243: .format(
0244: RefactoringCoreMessages.RenameFieldRefactoring_another_name2,
0245: new String[] {
0246: newName,
0247: fField
0248: .getDeclaringType()
0249: .getElementName() })
0250: : RefactoringCoreMessages.RenameFieldRefactoring_another_name,
0251: JavaStatusContext.create(fField));
0252:
0253: if (fField.getDeclaringType().getField(newName).exists())
0254: result
0255: .addError(
0256: fIsComposite ? Messages
0257: .format(
0258: RefactoringCoreMessages.RenameFieldRefactoring_field_already_defined2,
0259: new String[] {
0260: newName,
0261: fField
0262: .getDeclaringType()
0263: .getElementName() })
0264: : RefactoringCoreMessages.RenameFieldRefactoring_field_already_defined,
0265: JavaStatusContext.create(fField
0266: .getDeclaringType().getField(
0267: newName)));
0268: return result;
0269: }
0270:
0271: public Object getNewElement() {
0272: return fField.getDeclaringType().getField(getNewElementName());
0273: }
0274:
0275: //---- ITextUpdating2 ---------------------------------------------
0276:
0277: public boolean canEnableTextUpdating() {
0278: return true;
0279: }
0280:
0281: public boolean getUpdateTextualMatches() {
0282: return fUpdateTextualMatches;
0283: }
0284:
0285: public void setUpdateTextualMatches(boolean update) {
0286: fUpdateTextualMatches = update;
0287: }
0288:
0289: //---- IReferenceUpdating -----------------------------------
0290:
0291: public boolean canEnableUpdateReferences() {
0292: return true;
0293: }
0294:
0295: public void setUpdateReferences(boolean update) {
0296: fUpdateReferences = update;
0297: }
0298:
0299: public boolean getUpdateReferences() {
0300: return fUpdateReferences;
0301: }
0302:
0303: //-- getter/setter --------------------------------------------------
0304:
0305: /**
0306: * @return Error message or <code>null</code> if getter can be renamed.
0307: * @throws CoreException
0308: */
0309: public String canEnableGetterRenaming() throws CoreException {
0310: if (fField.getDeclaringType().isInterface())
0311: return getGetter() == null ? "" : null; //$NON-NLS-1$
0312:
0313: IMethod getter = getGetter();
0314: if (getter == null)
0315: return ""; //$NON-NLS-1$
0316: final NullProgressMonitor monitor = new NullProgressMonitor();
0317: if (MethodChecks.isVirtual(getter)) {
0318: final ITypeHierarchy hierarchy = getter.getDeclaringType()
0319: .newTypeHierarchy(monitor);
0320: if (MethodChecks.isDeclaredInInterface(getter, hierarchy,
0321: monitor) != null
0322: || MethodChecks.overridesAnotherMethod(getter,
0323: hierarchy) != null)
0324: return RefactoringCoreMessages.RenameFieldRefactoring_declared_in_super type;
0325: }
0326: return null;
0327: }
0328:
0329: /**
0330: * @return Error message or <code>null</code> if setter can be renamed.
0331: * @throws CoreException
0332: */
0333: public String canEnableSetterRenaming() throws CoreException {
0334: if (fField.getDeclaringType().isInterface())
0335: return getSetter() == null ? "" : null; //$NON-NLS-1$
0336:
0337: IMethod setter = getSetter();
0338: if (setter == null)
0339: return ""; //$NON-NLS-1$
0340: final NullProgressMonitor monitor = new NullProgressMonitor();
0341: if (MethodChecks.isVirtual(setter)) {
0342: final ITypeHierarchy hierarchy = setter.getDeclaringType()
0343: .newTypeHierarchy(monitor);
0344: if (MethodChecks.isDeclaredInInterface(setter, hierarchy,
0345: monitor) != null
0346: || MethodChecks.overridesAnotherMethod(setter,
0347: hierarchy) != null)
0348: return RefactoringCoreMessages.RenameFieldRefactoring_declared_in_super type;
0349: }
0350: return null;
0351: }
0352:
0353: public boolean getRenameGetter() {
0354: return fRenameGetter;
0355: }
0356:
0357: public void setRenameGetter(boolean renameGetter) {
0358: fRenameGetter = renameGetter;
0359: }
0360:
0361: public boolean getRenameSetter() {
0362: return fRenameSetter;
0363: }
0364:
0365: public void setRenameSetter(boolean renameSetter) {
0366: fRenameSetter = renameSetter;
0367: }
0368:
0369: public IMethod getGetter() throws CoreException {
0370: return GetterSetterUtil.getGetter(fField);
0371: }
0372:
0373: public IMethod getSetter() throws CoreException {
0374: return GetterSetterUtil.getSetter(fField);
0375: }
0376:
0377: public String getNewGetterName() throws CoreException {
0378: IMethod primaryGetterCandidate = JavaModelUtil.findMethod(
0379: GetterSetterUtil.getGetterName(fField, new String[0]),
0380: new String[0], false, fField.getDeclaringType());
0381: if (!JavaModelUtil.isBoolean(fField)
0382: || (primaryGetterCandidate != null && primaryGetterCandidate
0383: .exists()))
0384: return GetterSetterUtil.getGetterName(fField
0385: .getJavaProject(), getNewElementName(), fField
0386: .getFlags(), JavaModelUtil.isBoolean(fField), null);
0387: //bug 30906 describes why we need to look for other alternatives here
0388: return GetterSetterUtil.getGetterName(fField.getJavaProject(),
0389: getNewElementName(), fField.getFlags(), false, null);
0390: }
0391:
0392: public String getNewSetterName() throws CoreException {
0393: return GetterSetterUtil.getSetterName(fField.getJavaProject(),
0394: getNewElementName(), fField.getFlags(), JavaModelUtil
0395: .isBoolean(fField), null);
0396: }
0397:
0398: // ------------------- IDelegateUpdating ----------------------
0399:
0400: public boolean canEnableDelegateUpdating() {
0401: return (getDelegateCount() > 0);
0402: }
0403:
0404: public boolean getDelegateUpdating() {
0405: return fDelegateUpdating;
0406: }
0407:
0408: public void setDelegateUpdating(boolean update) {
0409: fDelegateUpdating = update;
0410: }
0411:
0412: public void setDeprecateDelegates(boolean deprecate) {
0413: fDelegateDeprecation = deprecate;
0414: }
0415:
0416: public boolean getDeprecateDelegates() {
0417: return fDelegateDeprecation;
0418: }
0419:
0420: /**
0421: * Returns the maximum number of delegates which can
0422: * be created for the input elements of this refactoring.
0423: *
0424: * @return maximum number of delegates
0425: */
0426: public int getDelegateCount() {
0427: int count = 0;
0428: try {
0429: if (RefactoringAvailabilityTester
0430: .isDelegateCreationAvailable(getField()))
0431: count++;
0432: if (fRenameGetter && getGetter() != null)
0433: count++;
0434: if (fRenameSetter && getSetter() != null)
0435: count++;
0436: } catch (CoreException e) {
0437: // no-op
0438: }
0439: return count;
0440: }
0441:
0442: public int getSaveMode() {
0443: return RefactoringSaveHelper.SAVE_NON_JAVA_UPDATES;
0444: }
0445:
0446: public RefactoringStatus checkInitialConditions(IProgressMonitor pm)
0447: throws CoreException {
0448: IField primary = (IField) fField.getPrimaryElement();
0449: if (primary == null || !primary.exists()) {
0450: String message = Messages
0451: .format(
0452: RefactoringCoreMessages.RenameFieldRefactoring_deleted,
0453: fField.getCompilationUnit()
0454: .getElementName());
0455: return RefactoringStatus.createFatalErrorStatus(message);
0456: }
0457: fField = primary;
0458:
0459: return Checks.checkIfCuBroken(fField);
0460: }
0461:
0462: protected RefactoringStatus doCheckFinalConditions(
0463: IProgressMonitor pm, CheckConditionsContext context)
0464: throws CoreException {
0465: try {
0466: pm.beginTask("", 18); //$NON-NLS-1$
0467: pm
0468: .setTaskName(RefactoringCoreMessages.RenameFieldRefactoring_checking);
0469: RefactoringStatus result = new RefactoringStatus();
0470: result.merge(Checks.checkIfCuBroken(fField));
0471: if (result.hasFatalError())
0472: return result;
0473: result.merge(checkNewElementName(getNewElementName()));
0474: pm.worked(1);
0475: result.merge(checkEnclosingHierarchy());
0476: pm.worked(1);
0477: result
0478: .merge(checkNestedHierarchy(fField
0479: .getDeclaringType()));
0480: pm.worked(1);
0481:
0482: if (fUpdateReferences) {
0483: pm
0484: .setTaskName(RefactoringCoreMessages.RenameFieldRefactoring_searching);
0485: fReferences = getReferences(new SubProgressMonitor(pm,
0486: 3), result);
0487: pm
0488: .setTaskName(RefactoringCoreMessages.RenameFieldRefactoring_checking);
0489: } else {
0490: fReferences = new SearchResultGroup[0];
0491: pm.worked(3);
0492: }
0493:
0494: if (fUpdateReferences)
0495: result.merge(analyzeAffectedCompilationUnits());
0496: else
0497: Checks.checkCompileErrorsInAffectedFile(result, fField
0498: .getResource());
0499:
0500: if (getGetter() != null && fRenameGetter) {
0501: result.merge(checkAccessor(
0502: new SubProgressMonitor(pm, 1), getGetter(),
0503: getNewGetterName()));
0504: result.merge(Checks.checkIfConstructorName(getGetter(),
0505: getNewGetterName(), fField.getDeclaringType()
0506: .getElementName()));
0507: } else {
0508: pm.worked(1);
0509: }
0510:
0511: if (getSetter() != null && fRenameSetter) {
0512: result.merge(checkAccessor(
0513: new SubProgressMonitor(pm, 1), getSetter(),
0514: getNewSetterName()));
0515: result.merge(Checks.checkIfConstructorName(getSetter(),
0516: getNewSetterName(), fField.getDeclaringType()
0517: .getElementName()));
0518: } else {
0519: pm.worked(1);
0520: }
0521:
0522: result.merge(createChanges(new SubProgressMonitor(pm, 10)));
0523: if (result.hasFatalError())
0524: return result;
0525:
0526: return result;
0527: } finally {
0528: pm.done();
0529: }
0530: }
0531:
0532: //----------
0533: private RefactoringStatus checkAccessor(IProgressMonitor pm,
0534: IMethod existingAccessor, String newAccessorName)
0535: throws CoreException {
0536: RefactoringStatus result = new RefactoringStatus();
0537: result.merge(checkAccessorDeclarations(pm, existingAccessor));
0538: result
0539: .merge(checkNewAccessor(existingAccessor,
0540: newAccessorName));
0541: return result;
0542: }
0543:
0544: private RefactoringStatus checkNewAccessor(
0545: IMethod existingAccessor, String newAccessorName)
0546: throws CoreException {
0547: RefactoringStatus result = new RefactoringStatus();
0548: IMethod accessor = JavaModelUtil.findMethod(newAccessorName,
0549: existingAccessor.getParameterTypes(), false, fField
0550: .getDeclaringType());
0551: if (accessor == null || !accessor.exists())
0552: return null;
0553:
0554: String message = Messages
0555: .format(
0556: RefactoringCoreMessages.RenameFieldRefactoring_already_exists,
0557: new String[] {
0558: JavaElementUtil
0559: .createMethodSignature(accessor),
0560: JavaModelUtil
0561: .getFullyQualifiedName(fField
0562: .getDeclaringType()) });
0563: result.addError(message, JavaStatusContext.create(accessor));
0564: return result;
0565: }
0566:
0567: private RefactoringStatus checkAccessorDeclarations(
0568: IProgressMonitor pm, IMethod existingAccessor)
0569: throws CoreException {
0570: RefactoringStatus result = new RefactoringStatus();
0571: SearchPattern pattern = SearchPattern.createPattern(
0572: existingAccessor, IJavaSearchConstants.DECLARATIONS,
0573: SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE);
0574: IJavaSearchScope scope = SearchEngine
0575: .createHierarchyScope(fField.getDeclaringType());
0576: SearchResultGroup[] groupDeclarations = RefactoringSearchEngine
0577: .search(pattern, scope, pm, result);
0578: Assert.isTrue(groupDeclarations.length > 0);
0579: if (groupDeclarations.length != 1) {
0580: String message = Messages
0581: .format(
0582: RefactoringCoreMessages.RenameFieldRefactoring_overridden,
0583: JavaElementUtil
0584: .createMethodSignature(existingAccessor));
0585: result.addError(message);
0586: } else {
0587: SearchResultGroup group = groupDeclarations[0];
0588: Assert.isTrue(group.getSearchResults().length > 0);
0589: if (group.getSearchResults().length != 1) {
0590: String message = Messages
0591: .format(
0592: RefactoringCoreMessages.RenameFieldRefactoring_overridden_or_overrides,
0593: JavaElementUtil
0594: .createMethodSignature(existingAccessor));
0595: result.addError(message);
0596: }
0597: }
0598: return result;
0599: }
0600:
0601: private static boolean isInstanceField(IField field)
0602: throws CoreException {
0603: if (JavaModelUtil.isInterfaceOrAnnotation(field
0604: .getDeclaringType()))
0605: return false;
0606: else
0607: return !JdtFlags.isStatic(field);
0608: }
0609:
0610: private RefactoringStatus checkNestedHierarchy(IType type)
0611: throws CoreException {
0612: IType[] nestedTypes = type.getTypes();
0613: if (nestedTypes == null)
0614: return null;
0615: RefactoringStatus result = new RefactoringStatus();
0616: for (int i = 0; i < nestedTypes.length; i++) {
0617: IField otherField = nestedTypes[i]
0618: .getField(getNewElementName());
0619: if (otherField.exists()) {
0620: String msg = Messages
0621: .format(
0622: RefactoringCoreMessages.RenameFieldRefactoring_hiding,
0623: new String[] {
0624: fField.getElementName(),
0625: getNewElementName(),
0626: JavaModelUtil
0627: .getFullyQualifiedName(nestedTypes[i]) });
0628: result.addWarning(msg, JavaStatusContext
0629: .create(otherField));
0630: }
0631: result.merge(checkNestedHierarchy(nestedTypes[i]));
0632: }
0633: return result;
0634: }
0635:
0636: private RefactoringStatus checkEnclosingHierarchy() {
0637: IType current = fField.getDeclaringType();
0638: if (Checks.isTopLevel(current))
0639: return null;
0640: RefactoringStatus result = new RefactoringStatus();
0641: while (current != null) {
0642: IField otherField = current.getField(getNewElementName());
0643: if (otherField.exists()) {
0644: String msg = Messages
0645: .format(
0646: RefactoringCoreMessages.RenameFieldRefactoring_hiding2,
0647: new String[] {
0648: getNewElementName(),
0649: JavaModelUtil
0650: .getFullyQualifiedName(current),
0651: otherField.getElementName() });
0652: result.addWarning(msg, JavaStatusContext
0653: .create(otherField));
0654: }
0655: current = current.getDeclaringType();
0656: }
0657: return result;
0658: }
0659:
0660: /*
0661: * (non java-doc)
0662: * Analyzes all compilation units in which type is referenced
0663: */
0664: private RefactoringStatus analyzeAffectedCompilationUnits()
0665: throws CoreException {
0666: RefactoringStatus result = new RefactoringStatus();
0667: fReferences = Checks.excludeCompilationUnits(fReferences,
0668: result);
0669: if (result.hasFatalError())
0670: return result;
0671:
0672: result.merge(Checks
0673: .checkCompileErrorsInAffectedFiles(fReferences));
0674: return result;
0675: }
0676:
0677: private SearchPattern createSearchPattern() {
0678: return SearchPattern.createPattern(fField,
0679: IJavaSearchConstants.REFERENCES);
0680: }
0681:
0682: private IJavaSearchScope createRefactoringScope()
0683: throws CoreException {
0684: return RefactoringScopeFactory.create(fField);
0685: }
0686:
0687: private SearchResultGroup[] getReferences(IProgressMonitor pm,
0688: RefactoringStatus status) throws CoreException {
0689: return RefactoringSearchEngine.search(createSearchPattern(),
0690: createRefactoringScope(), pm, status);
0691: }
0692:
0693: public Change createChange(IProgressMonitor monitor)
0694: throws CoreException {
0695: try {
0696: monitor
0697: .beginTask(
0698: RefactoringCoreMessages.RenameFieldRefactoring_checking,
0699: 1);
0700: TextChange[] changes = fChangeManager.getAllChanges();
0701: RenameJavaElementDescriptor descriptor = createRefactoringDescriptor();
0702: return new DynamicValidationRefactoringChange(descriptor,
0703: getProcessorName(), changes);
0704: } finally {
0705: monitor.done();
0706: }
0707: }
0708:
0709: /**
0710: * Overridden by subclasses.
0711: * @return return the refactoring descriptor for this refactoring
0712: */
0713: protected RenameJavaElementDescriptor createRefactoringDescriptor() {
0714: String project = null;
0715: IJavaProject javaProject = fField.getJavaProject();
0716: if (javaProject != null)
0717: project = javaProject.getElementName();
0718: int flags = JavaRefactoringDescriptor.JAR_MIGRATION
0719: | JavaRefactoringDescriptor.JAR_REFACTORING
0720: | RefactoringDescriptor.STRUCTURAL_CHANGE;
0721: try {
0722: if (!Flags.isPrivate(fField.getFlags()))
0723: flags |= RefactoringDescriptor.MULTI_CHANGE;
0724: } catch (JavaModelException exception) {
0725: JavaPlugin.log(exception);
0726: }
0727: final IType declaring = fField.getDeclaringType();
0728: try {
0729: if (declaring.isAnonymous() || declaring.isLocal())
0730: flags |= JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT;
0731: } catch (JavaModelException exception) {
0732: JavaPlugin.log(exception);
0733: }
0734: final String description = Messages
0735: .format(
0736: RefactoringCoreMessages.RenameFieldRefactoring_descriptor_description_short,
0737: fField.getElementName());
0738: final String header = Messages
0739: .format(
0740: RefactoringCoreMessages.RenameFieldProcessor_descriptor_description,
0741: new String[] {
0742: fField.getElementName(),
0743: JavaElementLabels
0744: .getElementLabel(
0745: fField.getParent(),
0746: JavaElementLabels.ALL_FULLY_QUALIFIED),
0747: getNewElementName() });
0748: final JDTRefactoringDescriptorComment comment = new JDTRefactoringDescriptorComment(
0749: project, this , header);
0750: if (fRenameGetter)
0751: comment
0752: .addSetting(RefactoringCoreMessages.RenameFieldRefactoring_setting_rename_getter);
0753: if (fRenameSetter)
0754: comment
0755: .addSetting(RefactoringCoreMessages.RenameFieldRefactoring_setting_rename_settter);
0756: final RenameJavaElementDescriptor descriptor = new RenameJavaElementDescriptor(
0757: IJavaRefactorings.RENAME_FIELD);
0758: descriptor.setProject(project);
0759: descriptor.setDescription(description);
0760: descriptor.setComment(comment.asString());
0761: descriptor.setFlags(flags);
0762: descriptor.setJavaElement(fField);
0763: descriptor.setNewName(getNewElementName());
0764: descriptor.setUpdateReferences(fUpdateReferences);
0765: descriptor.setUpdateTextualOccurrences(fUpdateTextualMatches);
0766: descriptor.setRenameGetters(fRenameGetter);
0767: descriptor.setRenameSetters(fRenameSetter);
0768: descriptor.setKeepOriginal(fDelegateUpdating);
0769: descriptor.setDeprecateDelegate(fDelegateDeprecation);
0770: return descriptor;
0771: }
0772:
0773: private RefactoringStatus createChanges(IProgressMonitor pm)
0774: throws CoreException {
0775: pm
0776: .beginTask(
0777: RefactoringCoreMessages.RenameFieldRefactoring_checking,
0778: 10);
0779: RefactoringStatus result = new RefactoringStatus();
0780: if (!fIsComposite)
0781: fChangeManager.clear();
0782:
0783: // Delegate creation requires ASTRewrite which
0784: // creates a new change -> do this first.
0785: if (fDelegateUpdating)
0786: result.merge(addDelegates());
0787:
0788: addDeclarationUpdate();
0789:
0790: if (fUpdateReferences) {
0791: addReferenceUpdates(new SubProgressMonitor(pm, 1));
0792: result.merge(analyzeRenameChanges(new SubProgressMonitor(
0793: pm, 2)));
0794: if (result.hasFatalError())
0795: return result;
0796: } else {
0797: pm.worked(3);
0798: }
0799:
0800: if (getGetter() != null && fRenameGetter) {
0801: addGetterOccurrences(new SubProgressMonitor(pm, 1), result);
0802: } else {
0803: pm.worked(1);
0804: }
0805:
0806: if (getSetter() != null && fRenameSetter) {
0807: addSetterOccurrences(new SubProgressMonitor(pm, 1), result);
0808: } else {
0809: pm.worked(1);
0810: }
0811:
0812: if (fUpdateTextualMatches) {
0813: addTextMatches(new SubProgressMonitor(pm, 5));
0814: } else {
0815: pm.worked(5);
0816: }
0817: pm.done();
0818: return result;
0819: }
0820:
0821: private void addDeclarationUpdate() throws CoreException {
0822: ISourceRange nameRange = fField.getNameRange();
0823: TextEdit textEdit = new ReplaceEdit(nameRange.getOffset(),
0824: nameRange.getLength(), getNewElementName());
0825: ICompilationUnit cu = fField.getCompilationUnit();
0826: String groupName = RefactoringCoreMessages.RenameFieldRefactoring_Update_field_declaration;
0827: addTextEdit(fChangeManager.get(cu), groupName, textEdit);
0828: }
0829:
0830: private RefactoringStatus addDelegates() throws JavaModelException,
0831: CoreException {
0832:
0833: RefactoringStatus status = new RefactoringStatus();
0834: CompilationUnitRewrite rewrite = new CompilationUnitRewrite(
0835: fField.getCompilationUnit());
0836: rewrite.setResolveBindings(true);
0837:
0838: // add delegate for the field
0839: if (RefactoringAvailabilityTester
0840: .isDelegateCreationAvailable(fField)) {
0841: FieldDeclaration fieldDeclaration = ASTNodeSearchUtil
0842: .getFieldDeclarationNode(fField, rewrite.getRoot());
0843: if (fieldDeclaration.fragments().size() > 1) {
0844: status
0845: .addWarning(
0846: Messages
0847: .format(
0848: RefactoringCoreMessages.DelegateCreator_cannot_create_field_delegate_more_than_one_fragment,
0849: fField.getElementName()),
0850: JavaStatusContext.create(fField));
0851: } else if (((VariableDeclarationFragment) fieldDeclaration
0852: .fragments().get(0)).getInitializer() == null) {
0853: status
0854: .addWarning(
0855: Messages
0856: .format(
0857: RefactoringCoreMessages.DelegateCreator_cannot_create_field_delegate_no_initializer,
0858: fField.getElementName()),
0859: JavaStatusContext.create(fField));
0860: } else {
0861: DelegateFieldCreator creator = new DelegateFieldCreator();
0862: creator.setDeclareDeprecated(fDelegateDeprecation);
0863: creator.setDeclaration(fieldDeclaration);
0864: creator.setNewElementName(getNewElementName());
0865: creator.setSourceRewrite(rewrite);
0866: creator.prepareDelegate();
0867: creator.createEdit();
0868: }
0869: }
0870:
0871: // add delegates for getter and setter methods
0872: // there may be getters even if the field is static final
0873: if (getGetter() != null && fRenameGetter)
0874: addMethodDelegate(getGetter(), getNewGetterName(), rewrite);
0875: if (getSetter() != null && fRenameSetter)
0876: addMethodDelegate(getSetter(), getNewSetterName(), rewrite);
0877:
0878: final CompilationUnitChange change = rewrite.createChange();
0879: if (change != null) {
0880: change.setKeepPreviewEdits(true);
0881: fChangeManager.manage(fField.getCompilationUnit(), change);
0882: }
0883:
0884: return status;
0885: }
0886:
0887: private void addMethodDelegate(IMethod getter, String newName,
0888: CompilationUnitRewrite rewrite) throws JavaModelException {
0889: MethodDeclaration declaration = ASTNodeSearchUtil
0890: .getMethodDeclarationNode(getter, rewrite.getRoot());
0891: DelegateCreator creator = new DelegateMethodCreator();
0892: creator.setDeclareDeprecated(fDelegateDeprecation);
0893: creator.setDeclaration(declaration);
0894: creator.setNewElementName(newName);
0895: creator.setSourceRewrite(rewrite);
0896: creator.prepareDelegate();
0897: creator.createEdit();
0898: }
0899:
0900: private void addTextEdit(TextChange change, String groupName,
0901: TextEdit textEdit) {
0902: if (fIsComposite)
0903: TextChangeCompatibility.addTextEdit(change, groupName,
0904: textEdit, fCategorySet);
0905: else
0906: TextChangeCompatibility.addTextEdit(change, groupName,
0907: textEdit);
0908:
0909: }
0910:
0911: private void addReferenceUpdates(IProgressMonitor pm) {
0912: pm.beginTask("", fReferences.length); //$NON-NLS-1$
0913: String editName = RefactoringCoreMessages.RenameFieldRefactoring_Update_field_reference;
0914: for (int i = 0; i < fReferences.length; i++) {
0915: ICompilationUnit cu = fReferences[i].getCompilationUnit();
0916: if (cu == null)
0917: continue;
0918: SearchMatch[] results = fReferences[i].getSearchResults();
0919: for (int j = 0; j < results.length; j++) {
0920: addTextEdit(fChangeManager.get(cu), editName,
0921: createTextChange(results[j]));
0922: }
0923: pm.worked(1);
0924: }
0925: }
0926:
0927: private TextEdit createTextChange(SearchMatch match) {
0928: return new ReplaceEdit(match.getOffset(), match.getLength(),
0929: getNewElementName());
0930: }
0931:
0932: private void addGetterOccurrences(IProgressMonitor pm,
0933: RefactoringStatus status) throws CoreException {
0934: addAccessorOccurrences(
0935: pm,
0936: getGetter(),
0937: RefactoringCoreMessages.RenameFieldRefactoring_Update_getter_occurrence,
0938: getNewGetterName(), status);
0939: }
0940:
0941: private void addSetterOccurrences(IProgressMonitor pm,
0942: RefactoringStatus status) throws CoreException {
0943: addAccessorOccurrences(
0944: pm,
0945: getSetter(),
0946: RefactoringCoreMessages.RenameFieldRefactoring_Update_setter_occurrence,
0947: getNewSetterName(), status);
0948: }
0949:
0950: private void addAccessorOccurrences(IProgressMonitor pm,
0951: IMethod accessor, String editName, String newAccessorName,
0952: RefactoringStatus status) throws CoreException {
0953: Assert.isTrue(accessor.exists());
0954:
0955: IJavaSearchScope scope = RefactoringScopeFactory
0956: .create(accessor);
0957: SearchPattern pattern = SearchPattern.createPattern(accessor,
0958: IJavaSearchConstants.ALL_OCCURRENCES,
0959: SearchUtils.GENERICS_AGNOSTIC_MATCH_RULE);
0960: SearchResultGroup[] groupedResults = RefactoringSearchEngine
0961: .search(pattern, scope, new MethodOccurenceCollector(
0962: accessor.getElementName()), pm, status);
0963:
0964: for (int i = 0; i < groupedResults.length; i++) {
0965: ICompilationUnit cu = groupedResults[i]
0966: .getCompilationUnit();
0967: if (cu == null)
0968: continue;
0969: SearchMatch[] results = groupedResults[i]
0970: .getSearchResults();
0971: for (int j = 0; j < results.length; j++) {
0972: SearchMatch searchResult = results[j];
0973: TextEdit edit = new ReplaceEdit(searchResult
0974: .getOffset(), searchResult.getLength(),
0975: newAccessorName);
0976: addTextEdit(fChangeManager.get(cu), editName, edit);
0977: }
0978: }
0979: }
0980:
0981: private void addTextMatches(IProgressMonitor pm)
0982: throws CoreException {
0983: TextMatchUpdater.perform(pm, createRefactoringScope(), this ,
0984: fChangeManager, fReferences);
0985: }
0986:
0987: //----------------
0988: private RefactoringStatus analyzeRenameChanges(IProgressMonitor pm)
0989: throws CoreException {
0990: ICompilationUnit[] newWorkingCopies = null;
0991: WorkingCopyOwner newWCOwner = new WorkingCopyOwner() { /* must subclass */
0992: };
0993: try {
0994: pm.beginTask("", 2); //$NON-NLS-1$
0995: RefactoringStatus result = new RefactoringStatus();
0996: SearchResultGroup[] oldReferences = fReferences;
0997:
0998: List compilationUnitsToModify = new ArrayList();
0999: if (fIsComposite) {
1000: // limited change set, no accessors.
1001: for (int i = 0; i < oldReferences.length; i++)
1002: compilationUnitsToModify.add(oldReferences[i]
1003: .getCompilationUnit());
1004: compilationUnitsToModify.add(fField
1005: .getCompilationUnit());
1006: } else {
1007: // include all cus, including accessors
1008: compilationUnitsToModify
1009: .addAll(Arrays.asList(fChangeManager
1010: .getAllCompilationUnits()));
1011: }
1012:
1013: newWorkingCopies = RenameAnalyzeUtil
1014: .createNewWorkingCopies(
1015: (ICompilationUnit[]) compilationUnitsToModify
1016: .toArray(new ICompilationUnit[compilationUnitsToModify
1017: .size()]), fChangeManager,
1018: newWCOwner, new SubProgressMonitor(pm, 1));
1019:
1020: SearchResultGroup[] newReferences = getNewReferences(
1021: new SubProgressMonitor(pm, 1), result, newWCOwner,
1022: newWorkingCopies);
1023: result.merge(RenameAnalyzeUtil.analyzeRenameChanges2(
1024: fChangeManager, oldReferences, newReferences,
1025: getNewElementName()));
1026: return result;
1027: } finally {
1028: pm.done();
1029: if (newWorkingCopies != null) {
1030: for (int i = 0; i < newWorkingCopies.length; i++) {
1031: newWorkingCopies[i].discardWorkingCopy();
1032: }
1033: }
1034: }
1035: }
1036:
1037: private SearchResultGroup[] getNewReferences(IProgressMonitor pm,
1038: RefactoringStatus status, WorkingCopyOwner owner,
1039: ICompilationUnit[] newWorkingCopies) throws CoreException {
1040: pm.beginTask("", 2); //$NON-NLS-1$
1041: ICompilationUnit declaringCuWorkingCopy = RenameAnalyzeUtil
1042: .findWorkingCopyForCu(newWorkingCopies, fField
1043: .getCompilationUnit());
1044: if (declaringCuWorkingCopy == null)
1045: return new SearchResultGroup[0];
1046:
1047: IField field = getFieldInWorkingCopy(declaringCuWorkingCopy,
1048: getNewElementName());
1049: if (field == null || !field.exists())
1050: return new SearchResultGroup[0];
1051:
1052: CollectingSearchRequestor requestor = null;
1053: if (fDelegateUpdating
1054: && RefactoringAvailabilityTester
1055: .isDelegateCreationAvailable(getField())) {
1056: // There will be two new matches inside the delegate (the invocation
1057: // and the javadoc) which are OK and must not be reported.
1058: final IField oldField = getFieldInWorkingCopy(
1059: declaringCuWorkingCopy, getCurrentElementName());
1060: requestor = new CollectingSearchRequestor() {
1061: public void acceptSearchMatch(SearchMatch match)
1062: throws CoreException {
1063: if (!oldField.equals(match.getElement()))
1064: super .acceptSearchMatch(match);
1065: }
1066: };
1067: } else
1068: requestor = new CollectingSearchRequestor();
1069:
1070: SearchPattern newPattern = SearchPattern.createPattern(field,
1071: IJavaSearchConstants.REFERENCES);
1072: return RefactoringSearchEngine.search(newPattern, owner,
1073: createRefactoringScope(), requestor,
1074: new SubProgressMonitor(pm, 1), status);
1075: }
1076:
1077: private IField getFieldInWorkingCopy(
1078: ICompilationUnit newWorkingCopyOfDeclaringCu,
1079: String elementName) throws CoreException {
1080: IType type = fField.getDeclaringType();
1081: IType typeWc = (IType) JavaModelUtil.findInCompilationUnit(
1082: newWorkingCopyOfDeclaringCu, type);
1083: if (typeWc == null)
1084: return null;
1085:
1086: return typeWc.getField(elementName);
1087: }
1088:
1089: public RefactoringStatus initialize(RefactoringArguments arguments) {
1090: if (arguments instanceof JavaRefactoringArguments) {
1091: final JavaRefactoringArguments extended = (JavaRefactoringArguments) arguments;
1092: final String handle = extended
1093: .getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT);
1094: if (handle != null) {
1095: final IJavaElement element = JavaRefactoringDescriptorUtil
1096: .handleToElement(extended.getProject(), handle,
1097: false);
1098: if (element == null
1099: || !element.exists()
1100: || element.getElementType() != IJavaElement.FIELD)
1101: return ScriptableRefactoring
1102: .createInputFatalStatus(element,
1103: getRefactoring().getName(),
1104: IJavaRefactorings.RENAME_FIELD);
1105: else
1106: fField = (IField) element;
1107: } else
1108: return RefactoringStatus
1109: .createFatalErrorStatus(Messages
1110: .format(
1111: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1112: JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT));
1113: final String name = extended
1114: .getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME);
1115: if (name != null && !"".equals(name)) //$NON-NLS-1$
1116: setNewElementName(name);
1117: else
1118: return RefactoringStatus
1119: .createFatalErrorStatus(Messages
1120: .format(
1121: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1122: JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME));
1123: final String references = extended
1124: .getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_REFERENCES);
1125: if (references != null) {
1126: fUpdateReferences = Boolean.valueOf(references)
1127: .booleanValue();
1128: } else
1129: return RefactoringStatus
1130: .createFatalErrorStatus(Messages
1131: .format(
1132: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1133: JavaRefactoringDescriptorUtil.ATTRIBUTE_REFERENCES));
1134: final String matches = extended
1135: .getAttribute(ATTRIBUTE_TEXTUAL_MATCHES);
1136: if (matches != null) {
1137: fUpdateTextualMatches = Boolean.valueOf(matches)
1138: .booleanValue();
1139: } else
1140: return RefactoringStatus
1141: .createFatalErrorStatus(Messages
1142: .format(
1143: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
1144: ATTRIBUTE_TEXTUAL_MATCHES));
1145: final String getters = extended
1146: .getAttribute(ATTRIBUTE_RENAME_GETTER);
1147: if (getters != null)
1148: fRenameGetter = Boolean.valueOf(getters).booleanValue();
1149: else
1150: fRenameGetter = false;
1151: final String setters = extended
1152: .getAttribute(ATTRIBUTE_RENAME_SETTER);
1153: if (setters != null)
1154: fRenameSetter = Boolean.valueOf(setters).booleanValue();
1155: else
1156: fRenameSetter = false;
1157: final String delegate = extended
1158: .getAttribute(ATTRIBUTE_DELEGATE);
1159: if (delegate != null) {
1160: fDelegateUpdating = Boolean.valueOf(delegate)
1161: .booleanValue();
1162: } else
1163: fDelegateUpdating = false;
1164: final String deprecate = extended
1165: .getAttribute(ATTRIBUTE_DEPRECATE);
1166: if (deprecate != null) {
1167: fDelegateDeprecation = Boolean.valueOf(deprecate)
1168: .booleanValue();
1169: } else
1170: fDelegateDeprecation = false;
1171: } else
1172: return RefactoringStatus
1173: .createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments);
1174: return new RefactoringStatus();
1175: }
1176:
1177: /**
1178: * {@inheritDoc}
1179: */
1180: public String getDelegateUpdatingTitle(boolean plural) {
1181: if (plural)
1182: return RefactoringCoreMessages.DelegateFieldCreator_keep_original_renamed_plural;
1183: else
1184: return RefactoringCoreMessages.DelegateFieldCreator_keep_original_renamed_singular;
1185: }
1186: }
|