001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.corext.refactoring.rename;
011:
012: import org.eclipse.core.runtime.Assert;
013: import org.eclipse.core.runtime.CoreException;
014: import org.eclipse.core.runtime.IProgressMonitor;
015: import org.eclipse.core.runtime.OperationCanceledException;
016: import org.eclipse.core.runtime.SubProgressMonitor;
017:
018: import org.eclipse.core.resources.IFile;
019:
020: import org.eclipse.ltk.core.refactoring.Change;
021: import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
022: import org.eclipse.ltk.core.refactoring.RefactoringStatus;
023: import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
024: import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments;
025: import org.eclipse.ltk.core.refactoring.participants.RenameArguments;
026:
027: import org.eclipse.jdt.core.ICompilationUnit;
028: import org.eclipse.jdt.core.IJavaElement;
029: import org.eclipse.jdt.core.IJavaProject;
030: import org.eclipse.jdt.core.IMember;
031: import org.eclipse.jdt.core.IMethod;
032: import org.eclipse.jdt.core.ISourceRange;
033: import org.eclipse.jdt.core.IType;
034: import org.eclipse.jdt.core.ITypeParameter;
035: import org.eclipse.jdt.core.dom.ASTNode;
036: import org.eclipse.jdt.core.dom.ASTVisitor;
037: import org.eclipse.jdt.core.dom.AnnotationTypeDeclaration;
038: import org.eclipse.jdt.core.dom.CompilationUnit;
039: import org.eclipse.jdt.core.dom.EnumDeclaration;
040: import org.eclipse.jdt.core.dom.IBinding;
041: import org.eclipse.jdt.core.dom.ITypeBinding;
042: import org.eclipse.jdt.core.dom.SimpleName;
043: import org.eclipse.jdt.core.dom.TypeDeclaration;
044: import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
045: import org.eclipse.jdt.core.refactoring.descriptors.RenameJavaElementDescriptor;
046:
047: import org.eclipse.jdt.internal.corext.SourceRange;
048: import org.eclipse.jdt.internal.corext.dom.Bindings;
049: import org.eclipse.jdt.internal.corext.dom.NodeFinder;
050: import org.eclipse.jdt.internal.corext.refactoring.Checks;
051: import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
052: import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
053: import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil;
054: import org.eclipse.jdt.internal.corext.refactoring.RefactoringAvailabilityTester;
055: import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
056: import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext;
057: import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange;
058: import org.eclipse.jdt.internal.corext.refactoring.code.ScriptableRefactoring;
059: import org.eclipse.jdt.internal.corext.refactoring.participants.JavaProcessors;
060: import org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil;
061: import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
062: import org.eclipse.jdt.internal.corext.refactoring.tagging.IReferenceUpdating;
063: import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
064: import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil;
065: import org.eclipse.jdt.internal.corext.util.Messages;
066:
067: import org.eclipse.jdt.ui.JavaElementLabels;
068:
069: import org.eclipse.jdt.internal.ui.JavaPlugin;
070: import org.eclipse.jdt.internal.ui.refactoring.RefactoringSaveHelper;
071:
072: /**
073: * Rename processor to rename type parameters.
074: */
075: public final class RenameTypeParameterProcessor extends
076: JavaRenameProcessor implements IReferenceUpdating {
077:
078: /**
079: * AST visitor which searches for occurrences of the type parameter.
080: */
081: public final class RenameTypeParameterVisitor extends ASTVisitor {
082:
083: /** The binding of the type parameter */
084: private final IBinding fBinding;
085:
086: /** The node of the type parameter name */
087: private final SimpleName fName;
088:
089: /** The compilation unit rewrite to use */
090: private final CompilationUnitRewrite fRewrite;
091:
092: /** The status of the visiting process */
093: private final RefactoringStatus fStatus;
094:
095: /**
096: * Creates a new rename type parameter visitor.
097: *
098: * @param rewrite
099: * the compilation unit rewrite to use
100: * @param range
101: * the source range of the type parameter
102: * @param status
103: * the status to update
104: */
105: public RenameTypeParameterVisitor(
106: final CompilationUnitRewrite rewrite,
107: final ISourceRange range, final RefactoringStatus status) {
108: super (true);
109: Assert.isNotNull(rewrite);
110: Assert.isNotNull(range);
111: Assert.isNotNull(status);
112: fRewrite = rewrite;
113: fName = (SimpleName) NodeFinder.perform(rewrite.getRoot(),
114: range);
115: fBinding = fName.resolveBinding();
116: fStatus = status;
117: }
118:
119: /**
120: * Returns the resulting change.
121: *
122: * @return the resulting change
123: * @throws CoreException
124: * if the change could not be created
125: */
126: public final Change getResult() throws CoreException {
127: return fRewrite.createChange();
128: }
129:
130: public final boolean visit(final AnnotationTypeDeclaration node) {
131: final String name = node.getName().getIdentifier();
132: if (name.equals(getNewElementName())) {
133: fStatus
134: .addError(
135: Messages
136: .format(
137: RefactoringCoreMessages.RenameTypeParameterRefactoring_type_parameter_inner_class_clash,
138: new String[] { name }),
139: JavaStatusContext.create(fTypeParameter
140: .getDeclaringMember()
141: .getCompilationUnit(),
142: new SourceRange(node)));
143: return false;
144: }
145: return true;
146: }
147:
148: public final boolean visit(final EnumDeclaration node) {
149: final String name = node.getName().getIdentifier();
150: if (name.equals(getNewElementName())) {
151: fStatus
152: .addError(
153: Messages
154: .format(
155: RefactoringCoreMessages.RenameTypeParameterRefactoring_type_parameter_inner_class_clash,
156: new String[] { name }),
157: JavaStatusContext.create(fTypeParameter
158: .getDeclaringMember()
159: .getCompilationUnit(),
160: new SourceRange(node)));
161: return false;
162: }
163: return true;
164: }
165:
166: public final boolean visit(final SimpleName node) {
167: final ITypeBinding binding = node.resolveTypeBinding();
168: if (binding != null
169: && binding.isTypeVariable()
170: && Bindings.equals(binding, fBinding)
171: && node.getIdentifier().equals(
172: fName.getIdentifier())) {
173: if (node != fName) {
174: if (fUpdateReferences)
175: fRewrite
176: .getASTRewrite()
177: .set(
178: node,
179: SimpleName.IDENTIFIER_PROPERTY,
180: getNewElementName(),
181: fRewrite
182: .createGroupDescription(RefactoringCoreMessages.RenameTypeParameterRefactoring_update_type_parameter_reference));
183: } else
184: fRewrite
185: .getASTRewrite()
186: .set(
187: node,
188: SimpleName.IDENTIFIER_PROPERTY,
189: getNewElementName(),
190: fRewrite
191: .createGroupDescription(RefactoringCoreMessages.RenameTypeParameterRefactoring_update_type_parameter_declaration));
192: }
193: return true;
194: }
195:
196: public final boolean visit(final TypeDeclaration node) {
197: final String name = node.getName().getIdentifier();
198: if (name.equals(getNewElementName())) {
199: fStatus
200: .addError(
201: Messages
202: .format(
203: RefactoringCoreMessages.RenameTypeParameterRefactoring_type_parameter_inner_class_clash,
204: new String[] { name }),
205: JavaStatusContext.create(fTypeParameter
206: .getDeclaringMember()
207: .getCompilationUnit(),
208: new SourceRange(node)));
209: return false;
210: }
211: return true;
212: }
213: }
214:
215: private static final String ATTRIBUTE_PARAMETER = "parameter"; //$NON-NLS-1$
216:
217: /** The identifier of this processor */
218: public static final String IDENTIFIER = "org.eclipse.jdt.ui.renameTypeParameterProcessor"; //$NON-NLS-1$
219:
220: /** The change object */
221: private Change fChange = null;
222:
223: /** The type parameter to rename */
224: private ITypeParameter fTypeParameter;
225:
226: /** Should references to the type parameter be updated? */
227: private boolean fUpdateReferences = true;
228:
229: /**
230: * Creates a new rename type parameter processor.
231: *
232: * @param parameter
233: * the type parameter to rename, or <code>null</code> if invoked by scripting
234: */
235: public RenameTypeParameterProcessor(final ITypeParameter parameter) {
236: fTypeParameter = parameter;
237: if (parameter != null)
238: setNewElementName(parameter.getElementName());
239: }
240:
241: public final boolean canEnableUpdateReferences() {
242: return true;
243: }
244:
245: protected RenameModifications computeRenameModifications()
246: throws CoreException {
247: RenameModifications result = new RenameModifications();
248: result.rename(fTypeParameter, new RenameArguments(
249: getNewElementName(), getUpdateReferences()));
250: return result;
251: }
252:
253: protected IFile[] getChangedFiles() throws CoreException {
254: return new IFile[] { ResourceUtil.getFile(fTypeParameter
255: .getDeclaringMember().getCompilationUnit()) };
256: }
257:
258: public int getSaveMode() {
259: return RefactoringSaveHelper.SAVE_NOTHING;
260: }
261:
262: protected final RefactoringStatus doCheckFinalConditions(
263: final IProgressMonitor monitor,
264: final CheckConditionsContext context) throws CoreException,
265: OperationCanceledException {
266: Assert.isNotNull(monitor);
267: Assert.isNotNull(context);
268: final RefactoringStatus status = new RefactoringStatus();
269: try {
270: monitor.beginTask("", 5); //$NON-NLS-1$
271: monitor
272: .setTaskName(RefactoringCoreMessages.RenameTypeParameterRefactoring_checking);
273: status.merge(Checks.checkIfCuBroken(fTypeParameter
274: .getDeclaringMember()));
275: monitor.worked(1);
276: if (!status.hasFatalError()) {
277: status.merge(checkNewElementName(getNewElementName()));
278: monitor.worked(1);
279: monitor
280: .setTaskName(RefactoringCoreMessages.RenameTypeParameterRefactoring_searching);
281: status
282: .merge(createRenameChanges(new SubProgressMonitor(
283: monitor, 2)));
284: monitor
285: .setTaskName(RefactoringCoreMessages.RenameTypeParameterRefactoring_checking);
286: if (status.hasFatalError())
287: return status;
288: monitor.worked(1);
289: }
290: } finally {
291: monitor.done();
292: }
293: return status;
294: }
295:
296: public final RefactoringStatus checkInitialConditions(
297: final IProgressMonitor monitor) throws CoreException,
298: OperationCanceledException {
299: Assert.isNotNull(monitor);
300: if (!fTypeParameter.exists())
301: return RefactoringStatus
302: .createFatalErrorStatus(Messages
303: .format(
304: RefactoringCoreMessages.RenameTypeParameterRefactoring_deleted,
305: fTypeParameter.getDeclaringMember()
306: .getCompilationUnit()
307: .getElementName()));
308: return Checks.checkIfCuBroken(fTypeParameter
309: .getDeclaringMember());
310: }
311:
312: public final RefactoringStatus checkNewElementName(final String name)
313: throws CoreException {
314: Assert.isNotNull(name);
315: final RefactoringStatus result = Checks.checkTypeParameterName(
316: name, fTypeParameter);
317: if (Checks.startsWithLowerCase(name))
318: result
319: .addWarning(RefactoringCoreMessages.RenameTypeParameterRefactoring_should_start_lowercase);
320: if (Checks.isAlreadyNamed(fTypeParameter, name))
321: result
322: .addFatalError(RefactoringCoreMessages.RenameTypeParameterRefactoring_another_name);
323:
324: final IMember member = fTypeParameter.getDeclaringMember();
325: if (member instanceof IType) {
326: final IType type = (IType) member;
327: if (type.getTypeParameter(name).exists())
328: result
329: .addFatalError(RefactoringCoreMessages.RenameTypeParameterRefactoring_class_type_parameter_already_defined);
330: } else if (member instanceof IMethod) {
331: final IMethod method = (IMethod) member;
332: if (method.getTypeParameter(name).exists())
333: result
334: .addFatalError(RefactoringCoreMessages.RenameTypeParameterRefactoring_method_type_parameter_already_defined);
335: } else {
336: JavaPlugin
337: .logErrorMessage("Unexpected sub-type of IMember: " + member.getClass().getName()); //$NON-NLS-1$
338: Assert.isTrue(false);
339: }
340: return result;
341: }
342:
343: public final Change createChange(final IProgressMonitor monitor)
344: throws CoreException, OperationCanceledException {
345: Assert.isNotNull(monitor);
346: try {
347: Change change = fChange;
348: if (change != null) {
349: String project = null;
350: IJavaProject javaProject = fTypeParameter
351: .getJavaProject();
352: if (javaProject != null)
353: project = javaProject.getElementName();
354: final String description = Messages
355: .format(
356: RefactoringCoreMessages.RenameTypeParameterProcessor_descriptor_description_short,
357: fTypeParameter.getElementName());
358: final String header = Messages
359: .format(
360: RefactoringCoreMessages.RenameTypeParameterProcessor_descriptor_description,
361: new String[] {
362: fTypeParameter.getElementName(),
363: JavaElementLabels
364: .getElementLabel(
365: fTypeParameter
366: .getDeclaringMember(),
367: JavaElementLabels.ALL_FULLY_QUALIFIED),
368: getNewElementName() });
369: final String comment = new JDTRefactoringDescriptorComment(
370: project, this , header).asString();
371: final RenameJavaElementDescriptor descriptor = new RenameJavaElementDescriptor(
372: IJavaRefactorings.RENAME_TYPE_PARAMETER);
373: descriptor.setProject(project);
374: descriptor.setDescription(description);
375: descriptor.setComment(comment);
376: descriptor.setFlags(RefactoringDescriptor.NONE);
377: descriptor.setJavaElement(fTypeParameter);
378: descriptor.setNewName(getNewElementName());
379: descriptor.setUpdateReferences(fUpdateReferences);
380: change = new DynamicValidationRefactoringChange(
381: descriptor,
382: RefactoringCoreMessages.RenameTypeParameterProcessor_change_name,
383: new Change[] { change });
384: }
385: return change;
386: } finally {
387: fChange = null;
388: monitor.done();
389: }
390: }
391:
392: /**
393: * Creates the necessary changes for the renaming of the type parameter.
394: *
395: * @param monitor
396: * the progress monitor to display progress
397: * @return the status of the operation
398: * @throws CoreException
399: * if the change could not be generated
400: */
401: private RefactoringStatus createRenameChanges(
402: final IProgressMonitor monitor) throws CoreException {
403: Assert.isNotNull(monitor);
404: final RefactoringStatus status = new RefactoringStatus();
405: try {
406: monitor
407: .beginTask(
408: RefactoringCoreMessages.RenameTypeParameterRefactoring_searching,
409: 2);
410: final ICompilationUnit cu = fTypeParameter
411: .getDeclaringMember().getCompilationUnit();
412: final CompilationUnit root = RefactoringASTParser
413: .parseWithASTProvider(cu, true, null);
414: final CompilationUnitRewrite rewrite = new CompilationUnitRewrite(
415: cu, root);
416: final IMember member = fTypeParameter.getDeclaringMember();
417: ASTNode declaration = null;
418: if (member instanceof IMethod) {
419: declaration = ASTNodeSearchUtil
420: .getMethodDeclarationNode((IMethod) member,
421: root);
422: } else if (member instanceof IType) {
423: declaration = ASTNodeSearchUtil
424: .getAbstractTypeDeclarationNode((IType) member,
425: root);
426: } else {
427: JavaPlugin
428: .logErrorMessage("Unexpected sub-type of IMember: " + member.getClass().getName()); //$NON-NLS-1$
429: Assert.isTrue(false);
430: }
431: monitor.worked(1);
432: final RenameTypeParameterVisitor visitor = new RenameTypeParameterVisitor(
433: rewrite, fTypeParameter.getNameRange(), status);
434: if (declaration != null)
435: declaration.accept(visitor);
436: fChange = visitor.getResult();
437: } finally {
438: monitor.done();
439: }
440: return status;
441: }
442:
443: protected final String[] getAffectedProjectNatures()
444: throws CoreException {
445: return JavaProcessors.computeAffectedNatures(fTypeParameter);
446: }
447:
448: public final String getCurrentElementName() {
449: return fTypeParameter.getElementName();
450: }
451:
452: public final Object[] getElements() {
453: return new Object[] { fTypeParameter };
454: }
455:
456: public final String getIdentifier() {
457: return IDENTIFIER;
458: }
459:
460: public final Object getNewElement() throws CoreException {
461: final IMember member = fTypeParameter.getDeclaringMember();
462: if (member instanceof IType) {
463: final IType type = (IType) member;
464: return type.getTypeParameter(getNewElementName());
465: } else if (member instanceof IMethod) {
466: final IMethod method = (IMethod) member;
467: return method.getTypeParameter(getNewElementName());
468: } else {
469: JavaPlugin
470: .logErrorMessage("Unexpected sub-type of IMember: " + member.getClass().getName()); //$NON-NLS-1$
471: Assert.isTrue(false);
472: }
473: return null;
474: }
475:
476: public final String getProcessorName() {
477: return RefactoringCoreMessages.RenameTypeParameterProcessor_name;
478: }
479:
480: public final boolean getUpdateReferences() {
481: return fUpdateReferences;
482: }
483:
484: public final RefactoringStatus initialize(
485: final RefactoringArguments arguments) {
486: if (arguments instanceof JavaRefactoringArguments) {
487: final JavaRefactoringArguments extended = (JavaRefactoringArguments) arguments;
488: final String parameter = extended
489: .getAttribute(ATTRIBUTE_PARAMETER);
490: if (parameter == null || "".equals(parameter)) //$NON-NLS-1$
491: return RefactoringStatus
492: .createFatalErrorStatus(Messages
493: .format(
494: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
495: ATTRIBUTE_PARAMETER));
496: final String handle = extended
497: .getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT);
498: if (handle != null) {
499: final IJavaElement element = JavaRefactoringDescriptorUtil
500: .handleToElement(extended.getProject(), handle,
501: false);
502: if (element == null || !element.exists())
503: return ScriptableRefactoring
504: .createInputFatalStatus(
505: element,
506: getRefactoring().getName(),
507: IJavaRefactorings.RENAME_TYPE_PARAMETER);
508: else {
509: if (element instanceof IMethod)
510: fTypeParameter = ((IMethod) element)
511: .getTypeParameter(parameter);
512: else if (element instanceof IType)
513: fTypeParameter = ((IType) element)
514: .getTypeParameter(parameter);
515: else
516: return RefactoringStatus
517: .createFatalErrorStatus(Messages
518: .format(
519: RefactoringCoreMessages.InitializableRefactoring_illegal_argument,
520: new Object[] {
521: handle,
522: JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT }));
523: if (fTypeParameter == null
524: || !fTypeParameter.exists())
525: return ScriptableRefactoring
526: .createInputFatalStatus(
527: fTypeParameter,
528: getRefactoring().getName(),
529: IJavaRefactorings.RENAME_TYPE_PARAMETER);
530: }
531: } else
532: return RefactoringStatus
533: .createFatalErrorStatus(Messages
534: .format(
535: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
536: JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT));
537: final String name = extended
538: .getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME);
539: if (name != null && !"".equals(name)) //$NON-NLS-1$
540: setNewElementName(name);
541: else
542: return RefactoringStatus
543: .createFatalErrorStatus(Messages
544: .format(
545: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
546: JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME));
547: final String references = extended
548: .getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_REFERENCES);
549: if (references != null) {
550: fUpdateReferences = Boolean.valueOf(references)
551: .booleanValue();
552: } else
553: return RefactoringStatus
554: .createFatalErrorStatus(Messages
555: .format(
556: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
557: JavaRefactoringDescriptorUtil.ATTRIBUTE_REFERENCES));
558: } else
559: return RefactoringStatus
560: .createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments);
561: return new RefactoringStatus();
562: }
563:
564: public final boolean isApplicable() throws CoreException {
565: return RefactoringAvailabilityTester
566: .isRenameAvailable(fTypeParameter);
567: }
568:
569: public final void setUpdateReferences(final boolean update) {
570: fUpdateReferences = update;
571: }
572: }
|