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 java.util.StringTokenizer;
013:
014: import org.eclipse.text.edits.MultiTextEdit;
015: import org.eclipse.text.edits.ReplaceEdit;
016: import org.eclipse.text.edits.TextEdit;
017: import org.eclipse.text.edits.TextEditGroup;
018:
019: import org.eclipse.core.runtime.Assert;
020: import org.eclipse.core.runtime.CoreException;
021: import org.eclipse.core.runtime.IProgressMonitor;
022: import org.eclipse.core.runtime.OperationCanceledException;
023:
024: import org.eclipse.core.resources.IFile;
025:
026: import org.eclipse.ltk.core.refactoring.Change;
027: import org.eclipse.ltk.core.refactoring.GroupCategorySet;
028: import org.eclipse.ltk.core.refactoring.RefactoringChangeDescriptor;
029: import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
030: import org.eclipse.ltk.core.refactoring.RefactoringStatus;
031: import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
032: import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments;
033: import org.eclipse.ltk.core.refactoring.participants.RenameArguments;
034:
035: import org.eclipse.jdt.core.ICompilationUnit;
036: import org.eclipse.jdt.core.IJavaElement;
037: import org.eclipse.jdt.core.IJavaProject;
038: import org.eclipse.jdt.core.ILocalVariable;
039: import org.eclipse.jdt.core.IMethod;
040: import org.eclipse.jdt.core.ISourceRange;
041: import org.eclipse.jdt.core.JavaModelException;
042: import org.eclipse.jdt.core.dom.ASTNode;
043: import org.eclipse.jdt.core.dom.CompilationUnit;
044: import org.eclipse.jdt.core.dom.Initializer;
045: import org.eclipse.jdt.core.dom.MethodDeclaration;
046: import org.eclipse.jdt.core.dom.VariableDeclaration;
047: import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
048: import org.eclipse.jdt.core.refactoring.descriptors.RenameJavaElementDescriptor;
049:
050: import org.eclipse.jdt.internal.corext.dom.NodeFinder;
051: import org.eclipse.jdt.internal.corext.refactoring.Checks;
052: import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
053: import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
054: import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil;
055: import org.eclipse.jdt.internal.corext.refactoring.RefactoringAvailabilityTester;
056: import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
057: import org.eclipse.jdt.internal.corext.refactoring.changes.CompilationUnitChange;
058: import org.eclipse.jdt.internal.corext.refactoring.changes.TextChangeCompatibility;
059: import org.eclipse.jdt.internal.corext.refactoring.code.ScriptableRefactoring;
060: import org.eclipse.jdt.internal.corext.refactoring.participants.JavaProcessors;
061: import org.eclipse.jdt.internal.corext.refactoring.rename.RenameAnalyzeUtil.LocalAnalyzePackage;
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.refactoring.util.TextChangeManager;
066: import org.eclipse.jdt.internal.corext.util.Messages;
067:
068: import org.eclipse.jdt.ui.JavaElementLabels;
069:
070: import org.eclipse.jdt.internal.ui.JavaPlugin;
071: import org.eclipse.jdt.internal.ui.refactoring.RefactoringSaveHelper;
072:
073: public class RenameLocalVariableProcessor extends JavaRenameProcessor
074: implements IReferenceUpdating {
075:
076: private ILocalVariable fLocalVariable;
077: private ICompilationUnit fCu;
078:
079: //the following fields are set or modified after the construction
080: private boolean fUpdateReferences;
081: private String fCurrentName;
082: private String fNewName;
083: private CompilationUnit fCompilationUnitNode;
084: private VariableDeclaration fTempDeclarationNode;
085: private CompilationUnitChange fChange;
086:
087: private boolean fIsComposite;
088: private GroupCategorySet fCategorySet;
089: private TextChangeManager fChangeManager;
090: private RenameAnalyzeUtil.LocalAnalyzePackage fLocalAnalyzePackage;
091:
092: public static final String IDENTIFIER = "org.eclipse.jdt.ui.renameLocalVariableProcessor"; //$NON-NLS-1$
093:
094: /**
095: * Creates a new rename local variable processor.
096: * @param localVariable the local variable, or <code>null</code> if invoked by scripting
097: */
098: public RenameLocalVariableProcessor(ILocalVariable localVariable) {
099: fLocalVariable = localVariable;
100: fUpdateReferences = true;
101: if (localVariable != null)
102: fCu = (ICompilationUnit) localVariable
103: .getAncestor(IJavaElement.COMPILATION_UNIT);
104: fNewName = ""; //$NON-NLS-1$
105: fIsComposite = false;
106: }
107:
108: /**
109: * Creates a new rename local variable processor.
110: * <p>
111: * This constructor is only used by <code>RenameTypeProcessor</code>.
112: * </p>
113: *
114: * @param localVariable the local variable
115: * @param manager the change manager
116: * @param node the compilation unit node
117: * @param categorySet the group category set
118: */
119: RenameLocalVariableProcessor(ILocalVariable localVariable,
120: TextChangeManager manager, CompilationUnit node,
121: GroupCategorySet categorySet) {
122: this (localVariable);
123: fChangeManager = manager;
124: fCategorySet = categorySet;
125: fCompilationUnitNode = node;
126: fIsComposite = true;
127: }
128:
129: /*
130: * @see org.eclipse.jdt.internal.corext.refactoring.rename.JavaRenameProcessor#getAffectedProjectNatures()
131: */
132: protected final String[] getAffectedProjectNatures()
133: throws CoreException {
134: return JavaProcessors.computeAffectedNatures(fLocalVariable);
135: }
136:
137: /*
138: * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#getElements()
139: */
140: public Object[] getElements() {
141: return new Object[] { fLocalVariable };
142: }
143:
144: /*
145: * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#getIdentifier()
146: */
147: public String getIdentifier() {
148: return IDENTIFIER;
149: }
150:
151: /*
152: * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#getProcessorName()
153: */
154: public String getProcessorName() {
155: return RefactoringCoreMessages.RenameTempRefactoring_rename;
156: }
157:
158: /*
159: * @see org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor#isApplicable()
160: */
161: public boolean isApplicable() throws CoreException {
162: return RefactoringAvailabilityTester
163: .isRenameAvailable(fLocalVariable);
164: }
165:
166: /*
167: * @see org.eclipse.jdt.internal.corext.refactoring.tagging.IReferenceUpdating#canEnableUpdateReferences()
168: */
169: public boolean canEnableUpdateReferences() {
170: return true;
171: }
172:
173: /*
174: * @see org.eclipse.jdt.internal.corext.refactoring.rename.JavaRenameProcessor#getUpdateReferences()
175: */
176: public boolean getUpdateReferences() {
177: return fUpdateReferences;
178: }
179:
180: /*
181: * @see org.eclipse.jdt.internal.corext.refactoring.tagging.IReferenceUpdating#setUpdateReferences(boolean)
182: */
183: public void setUpdateReferences(boolean updateReferences) {
184: fUpdateReferences = updateReferences;
185: }
186:
187: /*
188: * @see org.eclipse.jdt.internal.corext.refactoring.tagging.INameUpdating#getCurrentElementName()
189: */
190: public String getCurrentElementName() {
191: return fCurrentName;
192: }
193:
194: /*
195: * @see org.eclipse.jdt.internal.corext.refactoring.tagging.INameUpdating#getNewElementName()
196: */
197: public String getNewElementName() {
198: return fNewName;
199: }
200:
201: /*
202: * @see org.eclipse.jdt.internal.corext.refactoring.tagging.INameUpdating#setNewElementName(java.lang.String)
203: */
204: public void setNewElementName(String newName) {
205: Assert.isNotNull(newName);
206: fNewName = newName;
207: }
208:
209: /*
210: * @see org.eclipse.jdt.internal.corext.refactoring.tagging.INameUpdating#getNewElement()
211: */
212: public Object getNewElement() {
213: return null; //cannot create an ILocalVariable
214: }
215:
216: public RefactoringStatus checkInitialConditions(IProgressMonitor pm)
217: throws CoreException {
218: initAST();
219: if (fTempDeclarationNode == null
220: || fTempDeclarationNode.resolveBinding() == null)
221: return RefactoringStatus
222: .createFatalErrorStatus(RefactoringCoreMessages.RenameTempRefactoring_must_select_local);
223: if (!Checks.isDeclaredIn(fTempDeclarationNode,
224: MethodDeclaration.class)
225: && !Checks.isDeclaredIn(fTempDeclarationNode,
226: Initializer.class))
227: return RefactoringStatus
228: .createFatalErrorStatus(RefactoringCoreMessages.RenameTempRefactoring_only_in_methods_and_initializers);
229:
230: initNames();
231: return new RefactoringStatus();
232: }
233:
234: private void initAST() throws JavaModelException {
235: if (!fIsComposite)
236: fCompilationUnitNode = RefactoringASTParser
237: .parseWithASTProvider(fCu, true, null);
238: ISourceRange sourceRange = fLocalVariable.getNameRange();
239: ASTNode name = NodeFinder.perform(fCompilationUnitNode,
240: sourceRange);
241: if (name == null)
242: return;
243: if (name.getParent() instanceof VariableDeclaration)
244: fTempDeclarationNode = (VariableDeclaration) name
245: .getParent();
246: }
247:
248: private void initNames() {
249: fCurrentName = fTempDeclarationNode.getName().getIdentifier();
250: }
251:
252: protected RenameModifications computeRenameModifications()
253: throws CoreException {
254: RenameModifications result = new RenameModifications();
255: result.rename(fLocalVariable, new RenameArguments(
256: getNewElementName(), getUpdateReferences()));
257: return result;
258: }
259:
260: protected IFile[] getChangedFiles() throws CoreException {
261: return new IFile[] { ResourceUtil.getFile(fCu) };
262: }
263:
264: public int getSaveMode() {
265: return RefactoringSaveHelper.SAVE_NOTHING;
266: }
267:
268: protected RefactoringStatus doCheckFinalConditions(
269: IProgressMonitor pm, CheckConditionsContext context)
270: throws CoreException, OperationCanceledException {
271: try {
272: pm.beginTask("", 1); //$NON-NLS-1$
273:
274: RefactoringStatus result = checkNewElementName(fNewName);
275: if (result.hasFatalError())
276: return result;
277: createEdits();
278: if (!fIsComposite) {
279: LocalAnalyzePackage[] localAnalyzePackages = new RenameAnalyzeUtil.LocalAnalyzePackage[] { fLocalAnalyzePackage };
280: result.merge(RenameAnalyzeUtil.analyzeLocalRenames(
281: localAnalyzePackages, fChange,
282: fCompilationUnitNode, true));
283: }
284: return result;
285: } finally {
286: pm.done();
287: if (fIsComposite) {
288: // end of life cycle for this processor
289: fChange = null;
290: fCompilationUnitNode = null;
291: fTempDeclarationNode = null;
292: }
293: }
294: }
295:
296: /*
297: * @see org.eclipse.jdt.internal.corext.refactoring.tagging.INameUpdating#checkNewElementName(java.lang.String)
298: */
299: public RefactoringStatus checkNewElementName(String newName)
300: throws JavaModelException {
301: RefactoringStatus result = Checks.checkFieldName(newName, fCu);
302: if (!Checks.startsWithLowerCase(newName))
303: if (fIsComposite) {
304: final String nameOfParent = (fLocalVariable.getParent() instanceof IMethod) ? fLocalVariable
305: .getParent().getElementName()
306: : RefactoringCoreMessages.JavaElementUtil_initializer;
307: final String nameOfType = fLocalVariable.getAncestor(
308: IJavaElement.TYPE).getElementName();
309: result
310: .addWarning(Messages
311: .format(
312: RefactoringCoreMessages.RenameTempRefactoring_lowercase2,
313: new String[] { newName,
314: nameOfParent,
315: nameOfType }));
316: } else {
317: result
318: .addWarning(RefactoringCoreMessages.RenameTempRefactoring_lowercase);
319: }
320: return result;
321: }
322:
323: private void createEdits() {
324: TextEdit declarationEdit = createRenameEdit(fTempDeclarationNode
325: .getName().getStartPosition());
326: TextEdit[] allRenameEdits = getAllRenameEdits(declarationEdit);
327:
328: TextEdit[] allUnparentedRenameEdits = new TextEdit[allRenameEdits.length];
329: TextEdit unparentedDeclarationEdit = null;
330:
331: fChange = new CompilationUnitChange(
332: RefactoringCoreMessages.RenameTempRefactoring_rename,
333: fCu);
334: MultiTextEdit rootEdit = new MultiTextEdit();
335: fChange.setEdit(rootEdit);
336: fChange.setKeepPreviewEdits(true);
337:
338: for (int i = 0; i < allRenameEdits.length; i++) {
339: if (fIsComposite) {
340: // Add a copy of the text edit (text edit may only have one
341: // parent) to keep problem reporting code clean
342: TextChangeCompatibility
343: .addTextEdit(
344: fChangeManager.get(fCu),
345: RefactoringCoreMessages.RenameTempRefactoring_changeName,
346: allRenameEdits[i].copy(), fCategorySet);
347:
348: // Add a separate copy for problem reporting
349: allUnparentedRenameEdits[i] = allRenameEdits[i].copy();
350: if (allRenameEdits[i].equals(declarationEdit))
351: unparentedDeclarationEdit = allUnparentedRenameEdits[i];
352: }
353: rootEdit.addChild(allRenameEdits[i]);
354: fChange
355: .addTextEditGroup(new TextEditGroup(
356: RefactoringCoreMessages.RenameTempRefactoring_changeName,
357: allRenameEdits[i]));
358: }
359:
360: // store information for analysis
361: if (fIsComposite) {
362: fLocalAnalyzePackage = new RenameAnalyzeUtil.LocalAnalyzePackage(
363: unparentedDeclarationEdit, allUnparentedRenameEdits);
364: } else
365: fLocalAnalyzePackage = new RenameAnalyzeUtil.LocalAnalyzePackage(
366: declarationEdit, allRenameEdits);
367: }
368:
369: private TextEdit[] getAllRenameEdits(TextEdit declarationEdit) {
370: if (!fUpdateReferences)
371: return new TextEdit[] { declarationEdit };
372:
373: TempOccurrenceAnalyzer fTempAnalyzer = new TempOccurrenceAnalyzer(
374: fTempDeclarationNode, true);
375: fTempAnalyzer.perform();
376: int[] referenceOffsets = fTempAnalyzer
377: .getReferenceAndJavadocOffsets();
378:
379: TextEdit[] allRenameEdits = new TextEdit[referenceOffsets.length + 1];
380: for (int i = 0; i < referenceOffsets.length; i++)
381: allRenameEdits[i] = createRenameEdit(referenceOffsets[i]);
382: allRenameEdits[referenceOffsets.length] = declarationEdit;
383: return allRenameEdits;
384: }
385:
386: private TextEdit createRenameEdit(int offset) {
387: return new ReplaceEdit(offset, fCurrentName.length(), fNewName);
388: }
389:
390: public Change createChange(IProgressMonitor monitor)
391: throws CoreException {
392: try {
393: monitor
394: .beginTask(
395: RefactoringCoreMessages.RenameTypeProcessor_creating_changes,
396: 1);
397:
398: RenameJavaElementDescriptor descriptor = createRefactoringDescriptor();
399: fChange.setDescriptor(new RefactoringChangeDescriptor(
400: descriptor));
401: return fChange;
402: } finally {
403: monitor.done();
404: }
405: }
406:
407: private RenameJavaElementDescriptor createRefactoringDescriptor() {
408: String project = null;
409: IJavaProject javaProject = fCu.getJavaProject();
410: if (javaProject != null)
411: project = javaProject.getElementName();
412: final String header = Messages
413: .format(
414: RefactoringCoreMessages.RenameLocalVariableProcessor_descriptor_description,
415: new String[] {
416: fCurrentName,
417: JavaElementLabels
418: .getElementLabel(
419: fLocalVariable
420: .getParent(),
421: JavaElementLabels.ALL_FULLY_QUALIFIED),
422: fNewName });
423: final String description = Messages
424: .format(
425: RefactoringCoreMessages.RenameLocalVariableProcessor_descriptor_description_short,
426: fCurrentName);
427: final String comment = new JDTRefactoringDescriptorComment(
428: project, this , header).asString();
429: final RenameJavaElementDescriptor descriptor = new RenameJavaElementDescriptor(
430: IJavaRefactorings.RENAME_LOCAL_VARIABLE);
431: descriptor.setProject(project);
432: descriptor.setDescription(description);
433: descriptor.setComment(comment);
434: descriptor.setFlags(RefactoringDescriptor.NONE);
435: descriptor.setJavaElement(fLocalVariable);
436: descriptor.setNewName(getNewElementName());
437: descriptor.setUpdateReferences(fUpdateReferences);
438: return descriptor;
439: }
440:
441: public RefactoringStatus initialize(RefactoringArguments arguments) {
442: if (arguments instanceof JavaRefactoringArguments) {
443: final JavaRefactoringArguments extended = (JavaRefactoringArguments) arguments;
444: final String handle = extended
445: .getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT);
446: if (handle != null) {
447: final IJavaElement element = JavaRefactoringDescriptorUtil
448: .handleToElement(extended.getProject(), handle,
449: false);
450: if (element != null && element.exists()) {
451: if (element.getElementType() == IJavaElement.COMPILATION_UNIT) {
452: fCu = (ICompilationUnit) element;
453: } else if (element.getElementType() == IJavaElement.LOCAL_VARIABLE) {
454: fLocalVariable = (ILocalVariable) element;
455: fCu = (ICompilationUnit) fLocalVariable
456: .getAncestor(IJavaElement.COMPILATION_UNIT);
457: if (fCu == null)
458: return ScriptableRefactoring
459: .createInputFatalStatus(
460: element,
461: getRefactoring().getName(),
462: IJavaRefactorings.RENAME_LOCAL_VARIABLE);
463: } else
464: return ScriptableRefactoring
465: .createInputFatalStatus(
466: element,
467: getRefactoring().getName(),
468: IJavaRefactorings.RENAME_LOCAL_VARIABLE);
469: } else
470: return ScriptableRefactoring
471: .createInputFatalStatus(
472: element,
473: getRefactoring().getName(),
474: IJavaRefactorings.RENAME_LOCAL_VARIABLE);
475: } else
476: return RefactoringStatus
477: .createFatalErrorStatus(Messages
478: .format(
479: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
480: JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT));
481: final String name = extended
482: .getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME);
483: if (name != null && !"".equals(name)) //$NON-NLS-1$
484: setNewElementName(name);
485: else
486: return RefactoringStatus
487: .createFatalErrorStatus(Messages
488: .format(
489: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
490: JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME));
491: if (fCu != null && fLocalVariable == null) {
492: final String selection = extended
493: .getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION);
494: if (selection != null) {
495: int offset = -1;
496: int length = -1;
497: final StringTokenizer tokenizer = new StringTokenizer(
498: selection);
499: if (tokenizer.hasMoreTokens())
500: offset = Integer.valueOf(tokenizer.nextToken())
501: .intValue();
502: if (tokenizer.hasMoreTokens())
503: length = Integer.valueOf(tokenizer.nextToken())
504: .intValue();
505: if (offset >= 0 && length >= 0) {
506: try {
507: final IJavaElement[] elements = fCu
508: .codeSelect(offset, length);
509: if (elements != null) {
510: for (int index = 0; index < elements.length; index++) {
511: final IJavaElement element = elements[index];
512: if (element instanceof ILocalVariable)
513: fLocalVariable = (ILocalVariable) element;
514: }
515: }
516: if (fLocalVariable == null)
517: return ScriptableRefactoring
518: .createInputFatalStatus(
519: null,
520: getRefactoring()
521: .getName(),
522: IJavaRefactorings.RENAME_LOCAL_VARIABLE);
523: } catch (JavaModelException exception) {
524: JavaPlugin.log(exception);
525: }
526: } else
527: return RefactoringStatus
528: .createFatalErrorStatus(Messages
529: .format(
530: RefactoringCoreMessages.InitializableRefactoring_illegal_argument,
531: new Object[] {
532: selection,
533: JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION }));
534: } else
535: return RefactoringStatus
536: .createFatalErrorStatus(Messages
537: .format(
538: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
539: JavaRefactoringDescriptorUtil.ATTRIBUTE_SELECTION));
540: }
541: final String references = extended
542: .getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_REFERENCES);
543: if (references != null) {
544: fUpdateReferences = Boolean.valueOf(references)
545: .booleanValue();
546: } else
547: return RefactoringStatus
548: .createFatalErrorStatus(Messages
549: .format(
550: RefactoringCoreMessages.InitializableRefactoring_argument_not_exist,
551: JavaRefactoringDescriptorUtil.ATTRIBUTE_REFERENCES));
552: } else
553: return RefactoringStatus
554: .createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments);
555: return new RefactoringStatus();
556: }
557:
558: public RenameAnalyzeUtil.LocalAnalyzePackage getLocalAnalyzePackage() {
559: return fLocalAnalyzePackage;
560: }
561: }
|