KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > corext > refactoring > rename > RenameAnalyzeUtil


1 /*******************************************************************************
2  * Copyright (c) 2000, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.jdt.internal.corext.refactoring.rename;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.Arrays JavaDoc;
15 import java.util.Collection JavaDoc;
16 import java.util.HashMap JavaDoc;
17 import java.util.HashSet JavaDoc;
18 import java.util.Iterator JavaDoc;
19 import java.util.Map JavaDoc;
20 import java.util.Map.Entry;
21
22 import org.eclipse.text.edits.TextEdit;
23
24 import org.eclipse.core.runtime.Assert;
25 import org.eclipse.core.runtime.CoreException;
26 import org.eclipse.core.runtime.NullProgressMonitor;
27 import org.eclipse.core.runtime.SubProgressMonitor;
28
29 import org.eclipse.core.resources.IResource;
30
31 import org.eclipse.jface.text.IRegion;
32 import org.eclipse.jface.text.Region;
33
34 import org.eclipse.ltk.core.refactoring.RefactoringStatus;
35 import org.eclipse.ltk.core.refactoring.RefactoringStatusContext;
36 import org.eclipse.ltk.core.refactoring.RefactoringStatusEntry;
37 import org.eclipse.ltk.core.refactoring.TextChange;
38 import org.eclipse.ltk.core.refactoring.TextEditChangeGroup;
39
40 import org.eclipse.jdt.core.ICompilationUnit;
41 import org.eclipse.jdt.core.IJavaElement;
42 import org.eclipse.jdt.core.ISourceRange;
43 import org.eclipse.jdt.core.ISourceReference;
44 import org.eclipse.jdt.core.JavaModelException;
45 import org.eclipse.jdt.core.WorkingCopyOwner;
46 import org.eclipse.jdt.core.compiler.IProblem;
47 import org.eclipse.jdt.core.dom.AST;
48 import org.eclipse.jdt.core.dom.ASTNode;
49 import org.eclipse.jdt.core.dom.ASTVisitor;
50 import org.eclipse.jdt.core.dom.CompilationUnit;
51 import org.eclipse.jdt.core.dom.IBinding;
52 import org.eclipse.jdt.core.dom.IVariableBinding;
53 import org.eclipse.jdt.core.dom.Name;
54 import org.eclipse.jdt.core.dom.SimpleName;
55 import org.eclipse.jdt.core.dom.VariableDeclaration;
56 import org.eclipse.jdt.core.search.FieldDeclarationMatch;
57 import org.eclipse.jdt.core.search.MethodDeclarationMatch;
58 import org.eclipse.jdt.core.search.SearchMatch;
59
60 import org.eclipse.jdt.internal.corext.SourceRange;
61 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
62 import org.eclipse.jdt.internal.corext.dom.NodeFinder;
63 import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
64 import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup;
65 import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext;
66 import org.eclipse.jdt.internal.corext.refactoring.base.JavaStringStatusContext;
67 import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
68 import org.eclipse.jdt.internal.corext.refactoring.util.TextChangeManager;
69 import org.eclipse.jdt.internal.corext.util.Messages;
70 import org.eclipse.jdt.internal.corext.util.SearchUtils;
71
72 class RenameAnalyzeUtil {
73     
74     private static class ProblemNodeFinder {
75         
76         private ProblemNodeFinder() {
77             //static
78
}
79         
80         public static SimpleName[] getProblemNodes(ASTNode methodNode, VariableDeclaration variableNode, TextEdit[] edits, TextChange change) {
81             String JavaDoc key= variableNode.resolveBinding().getKey();
82             NameNodeVisitor visitor= new NameNodeVisitor(edits, change, key);
83             methodNode.accept(visitor);
84             return visitor.getProblemNodes();
85         }
86         
87         private static class NameNodeVisitor extends ASTVisitor {
88     
89             private Collection JavaDoc fRanges;
90             private Collection JavaDoc fProblemNodes;
91             private String JavaDoc fKey;
92     
93             public NameNodeVisitor(TextEdit[] edits, TextChange change, String JavaDoc key) {
94                 Assert.isNotNull(edits);
95                 Assert.isNotNull(key);
96                 
97                 fRanges= new HashSet JavaDoc(Arrays.asList(RefactoringAnalyzeUtil.getNewRanges(edits, change)));
98                 fProblemNodes= new ArrayList JavaDoc(0);
99                 fKey= key;
100             }
101     
102             public SimpleName[] getProblemNodes() {
103                 return (SimpleName[]) fProblemNodes.toArray(new SimpleName[fProblemNodes.size()]);
104             }
105     
106             //----- visit methods
107

108             public boolean visit(SimpleName node) {
109                 VariableDeclaration decl= getVariableDeclaration(node);
110                 if (decl == null)
111                     return super.visit(node);
112                 
113                 IVariableBinding binding= decl.resolveBinding();
114                 if (binding == null)
115                     return super.visit(node);
116                 
117                 boolean keysEqual= fKey.equals(binding.getKey());
118                 boolean rangeInSet= fRanges.contains(new Region(node.getStartPosition(), node.getLength()));
119     
120                 if (keysEqual && !rangeInSet)
121                     fProblemNodes.add(node);
122     
123                 if (!keysEqual && rangeInSet)
124                     fProblemNodes.add(node);
125                 
126                 /*
127                  * if (!keyEquals && !rangeInSet)
128                  * ok, different local variable.
129                  *
130                  * if (keyEquals && rangeInSet)
131                  * ok, renamed local variable & has been renamed.
132                  */

133     
134                 return super.visit(node);
135             }
136         }
137     }
138
139     static class LocalAnalyzePackage {
140         public final TextEdit fDeclarationEdit;
141         public final TextEdit[] fOccurenceEdits;
142         
143         public LocalAnalyzePackage(final TextEdit declarationEdit, final TextEdit[] occurenceEdits) {
144             fDeclarationEdit = declarationEdit;
145             fOccurenceEdits = occurenceEdits;
146         }
147     }
148
149     private RenameAnalyzeUtil() {
150         //no instance
151
}
152     
153     static RefactoringStatus analyzeRenameChanges(TextChangeManager manager, SearchResultGroup[] oldOccurrences, SearchResultGroup[] newOccurrences) {
154         RefactoringStatus result= new RefactoringStatus();
155         for (int i= 0; i < oldOccurrences.length; i++) {
156             SearchResultGroup oldGroup= oldOccurrences[i];
157             SearchMatch[] oldSearchResults= oldGroup.getSearchResults();
158             ICompilationUnit cunit= oldGroup.getCompilationUnit();
159             if (cunit == null)
160                 continue;
161             for (int j= 0; j < oldSearchResults.length; j++) {
162                 SearchMatch oldSearchResult= oldSearchResults[j];
163                 if (! RenameAnalyzeUtil.existsInNewOccurrences(oldSearchResult, newOccurrences, manager)){
164                     addShadowsError(cunit, oldSearchResult, result);
165                 }
166             }
167         }
168         return result;
169     }
170
171     static ICompilationUnit findWorkingCopyForCu(ICompilationUnit[] newWorkingCopies, ICompilationUnit cu){
172         ICompilationUnit original= cu == null ? null : cu.getPrimary();
173         for (int i= 0; i < newWorkingCopies.length; i++) {
174             if (newWorkingCopies[i].getPrimary().equals(original))
175                 return newWorkingCopies[i];
176         }
177         return null;
178     }
179
180     static ICompilationUnit[] createNewWorkingCopies(ICompilationUnit[] compilationUnitsToModify, TextChangeManager manager, WorkingCopyOwner owner, SubProgressMonitor pm) throws CoreException {
181         pm.beginTask("", compilationUnitsToModify.length); //$NON-NLS-1$
182
ICompilationUnit[] newWorkingCopies= new ICompilationUnit[compilationUnitsToModify.length];
183         for (int i= 0; i < compilationUnitsToModify.length; i++) {
184             ICompilationUnit cu= compilationUnitsToModify[i];
185             newWorkingCopies[i]= createNewWorkingCopy(cu, manager, owner, new SubProgressMonitor(pm, 1));
186         }
187         pm.done();
188         return newWorkingCopies;
189     }
190     
191     static ICompilationUnit createNewWorkingCopy(ICompilationUnit cu, TextChangeManager manager,
192             WorkingCopyOwner owner, SubProgressMonitor pm) throws CoreException {
193         ICompilationUnit newWc= cu.getWorkingCopy(owner, null, null);
194         String JavaDoc previewContent= manager.get(cu).getPreviewContent(new NullProgressMonitor());
195         newWc.getBuffer().setContents(previewContent);
196         newWc.reconcile(ICompilationUnit.NO_AST, false, owner, pm);
197         return newWc;
198     }
199     
200     private static boolean existsInNewOccurrences(SearchMatch searchResult, SearchResultGroup[] newOccurrences, TextChangeManager manager) {
201         SearchResultGroup newGroup= findOccurrenceGroup(searchResult.getResource(), newOccurrences);
202         if (newGroup == null)
203             return false;
204         
205         IRegion oldEditRange= getCorrespondingEditChangeRange(searchResult, manager);
206         if (oldEditRange == null)
207             return false;
208         
209         SearchMatch[] newSearchResults= newGroup.getSearchResults();
210         int oldRangeOffset = oldEditRange.getOffset();
211         for (int i= 0; i < newSearchResults.length; i++) {
212             if (newSearchResults[i].getOffset() == oldRangeOffset)
213                 return true;
214         }
215         return false;
216     }
217     
218     private static IRegion getCorrespondingEditChangeRange(SearchMatch searchResult, TextChangeManager manager) {
219         TextChange change= getTextChange(searchResult, manager);
220         if (change == null)
221             return null;
222         
223         IRegion oldMatchRange= createTextRange(searchResult);
224         TextEditChangeGroup[] editChanges= change.getTextEditChangeGroups();
225         for (int i= 0; i < editChanges.length; i++) {
226             if (oldMatchRange.equals(editChanges[i].getRegion()))
227                 return TextEdit.getCoverage(change.getPreviewEdits(editChanges[i].getTextEdits()));
228         }
229         return null;
230     }
231     
232     private static TextChange getTextChange(SearchMatch searchResult, TextChangeManager manager) {
233         ICompilationUnit cu= SearchUtils.getCompilationUnit(searchResult);
234         if (cu == null)
235             return null;
236         return manager.get(cu);
237     }
238     
239     private static IRegion createTextRange(SearchMatch searchResult) {
240         return new Region(searchResult.getOffset(), searchResult.getLength());
241     }
242     
243     private static SearchResultGroup findOccurrenceGroup(IResource resource, SearchResultGroup[] newOccurrences) {
244         for (int i= 0; i < newOccurrences.length; i++) {
245             if (newOccurrences[i].getResource().equals(resource))
246                 return newOccurrences[i];
247         }
248         return null;
249     }
250     
251 //--- find missing changes in BOTH directions
252

253     //TODO: Currently filters out declarations (MethodDeclarationMatch, FieldDeclarationMatch).
254
//Long term solution: only pass reference search results in.
255
static RefactoringStatus analyzeRenameChanges2(TextChangeManager manager,
256             SearchResultGroup[] oldReferences, SearchResultGroup[] newReferences, String JavaDoc newElementName) {
257         RefactoringStatus result= new RefactoringStatus();
258         
259         HashMap JavaDoc cuToNewResults= new HashMap JavaDoc(newReferences.length);
260         for (int i1= 0; i1 < newReferences.length; i1++) {
261             ICompilationUnit cu= newReferences[i1].getCompilationUnit();
262             if (cu != null)
263                 cuToNewResults.put(cu.getPrimary(), newReferences[i1].getSearchResults());
264         }
265         
266         for (int i= 0; i < oldReferences.length; i++) {
267             SearchResultGroup oldGroup= oldReferences[i];
268             SearchMatch[] oldMatches= oldGroup.getSearchResults();
269             ICompilationUnit cu= oldGroup.getCompilationUnit();
270             if (cu == null)
271                 continue;
272             
273             SearchMatch[] newSearchMatches= (SearchMatch[]) cuToNewResults.remove(cu);
274             if (newSearchMatches == null) {
275                 for (int j = 0; j < oldMatches.length; j++) {
276                     SearchMatch oldMatch = oldMatches[j];
277                     addShadowsError(cu, oldMatch, result);
278                 }
279             } else {
280                 analyzeChanges(cu, manager.get(cu), oldMatches, newSearchMatches, newElementName, result);
281             }
282         }
283         
284         for (Iterator JavaDoc iter= cuToNewResults.entrySet().iterator(); iter.hasNext();) {
285             Map.Entry JavaDoc entry= (Entry) iter.next();
286             ICompilationUnit cu= (ICompilationUnit) entry.getKey();
287             SearchMatch[] newSearchMatches= (SearchMatch[]) entry.getValue();
288             for (int i= 0; i < newSearchMatches.length; i++) {
289                 SearchMatch newMatch= newSearchMatches[i];
290                 addReferenceShadowedError(cu, newMatch, newElementName, result);
291             }
292         }
293         return result;
294     }
295
296     private static void analyzeChanges(ICompilationUnit cu, TextChange change,
297             SearchMatch[] oldMatches, SearchMatch[] newMatches, String JavaDoc newElementName, RefactoringStatus result) {
298         Map JavaDoc updatedOldOffsets= getUpdatedChangeOffsets(change, oldMatches);
299         for (int i= 0; i < newMatches.length; i++) {
300             SearchMatch newMatch= newMatches[i];
301             Integer JavaDoc offsetInNew= new Integer JavaDoc(newMatch.getOffset());
302             SearchMatch oldMatch= (SearchMatch) updatedOldOffsets.remove(offsetInNew);
303             if (oldMatch == null) {
304                 addReferenceShadowedError(cu, newMatch, newElementName, result);
305             }
306         }
307         for (Iterator JavaDoc iter= updatedOldOffsets.values().iterator(); iter.hasNext();) {
308             // remaining old matches are not found any more -> they have been shadowed
309
SearchMatch oldMatch= (SearchMatch) iter.next();
310             addShadowsError(cu, oldMatch, result);
311         }
312     }
313     
314     /** @return Map &lt;Integer updatedOffset, SearchMatch oldMatch&gt; */
315     private static Map JavaDoc getUpdatedChangeOffsets(TextChange change, SearchMatch[] oldMatches) {
316         Map JavaDoc/*<Integer updatedOffset, SearchMatch oldMatch>*/ updatedOffsets= new HashMap JavaDoc();
317         Map JavaDoc oldToUpdatedOffsets= getEditChangeOffsetUpdates(change);
318         for (int i= 0; i < oldMatches.length; i++) {
319             SearchMatch oldMatch= oldMatches[i];
320             Integer JavaDoc updatedOffset= (Integer JavaDoc) oldToUpdatedOffsets.get(new Integer JavaDoc(oldMatch.getOffset()));
321             if (updatedOffset == null)
322                 updatedOffset= new Integer JavaDoc(-1); //match not updated
323
updatedOffsets.put(updatedOffset, oldMatch);
324         }
325         return updatedOffsets;
326     }
327
328     /** @return Map &lt;Integer oldOffset, Integer updatedOffset&gt; */
329     private static Map JavaDoc getEditChangeOffsetUpdates(TextChange change) {
330         TextEditChangeGroup[] editChanges= change.getTextEditChangeGroups();
331         Map JavaDoc/*<oldOffset, newOffset>*/ offsetUpdates= new HashMap JavaDoc(editChanges.length);
332         for (int i= 0; i < editChanges.length; i++) {
333             TextEditChangeGroup editChange= editChanges[i];
334             IRegion oldRegion= editChange.getRegion();
335             if (oldRegion == null)
336                 continue;
337             IRegion updatedRegion= TextEdit.getCoverage(change.getPreviewEdits(editChange.getTextEdits()));
338             if (updatedRegion == null)
339                 continue;
340             
341             offsetUpdates.put(new Integer JavaDoc(oldRegion.getOffset()), new Integer JavaDoc(updatedRegion.getOffset()));
342         }
343         return offsetUpdates;
344     }
345
346     private static void addReferenceShadowedError(ICompilationUnit cu, SearchMatch newMatch, String JavaDoc newElementName, RefactoringStatus result) {
347         //Found a new match with no corresponding old match.
348
//-> The new match is a reference which was pointing to another element,
349
//but that other element has been shadowed
350

351         //TODO: should not have to filter declarations:
352
if (newMatch instanceof MethodDeclarationMatch || newMatch instanceof FieldDeclarationMatch)
353             return;
354         ISourceRange range= getOldSourceRange(newMatch);
355         RefactoringStatusContext context= JavaStatusContext.create(cu, range);
356         String JavaDoc message= Messages.format(
357                 RefactoringCoreMessages.RenameAnalyzeUtil_reference_shadowed,
358                 new String JavaDoc[] {cu.getElementName(), newElementName});
359         result.addError(message, context);
360     }
361
362     private static ISourceRange getOldSourceRange(SearchMatch newMatch) {
363         // cannot transfom offset in preview to offset in original -> just show enclosing method
364
IJavaElement newMatchElement= (IJavaElement) newMatch.getElement();
365         IJavaElement primaryElement= newMatchElement.getPrimaryElement();
366         ISourceRange range= null;
367         if (primaryElement.exists() && primaryElement instanceof ISourceReference) {
368             try {
369                 range= ((ISourceReference) primaryElement).getSourceRange();
370             } catch (JavaModelException e) {
371                 // can live without source range
372
}
373         }
374         return range;
375     }
376
377     private static void addShadowsError(ICompilationUnit cu, SearchMatch oldMatch, RefactoringStatus result) {
378         // Old match not found in new matches -> reference has been shadowed
379

380         //TODO: should not have to filter declarations:
381
if (oldMatch instanceof MethodDeclarationMatch || oldMatch instanceof FieldDeclarationMatch)
382             return;
383         ISourceRange range= new SourceRange(oldMatch.getOffset(), oldMatch.getLength());
384         RefactoringStatusContext context= JavaStatusContext.create(cu, range);
385         String JavaDoc message= Messages.format(RefactoringCoreMessages.RenameAnalyzeUtil_shadows, cu.getElementName());
386         result.addError(message, context);
387     }
388
389     /**
390      * This method analyzes a set of local variable renames inside one cu. It checks whether
391      * any new compile errors have been introduced by the rename(s) and whether the correct
392      * node(s) has/have been renamed.
393      *
394      * @param analyzePackages the LocalAnalyzePackages containing the information about the local renames
395      * @param cuChange the TextChange containing all local variable changes to be applied.
396      * @param oldCUNode the fully (incl. bindings) resolved AST node of the original compilation unit
397      * @param statementsRecovery whether statements recovery should be performed when parsing the changed CU
398      * @return a RefactoringStatus containing errors if compile errors or wrongly renamed nodes are found
399      * @throws CoreException thrown if there was an error greating the preview content of the change
400      */

401     public static RefactoringStatus analyzeLocalRenames(LocalAnalyzePackage[] analyzePackages, TextChange cuChange, CompilationUnit oldCUNode, boolean statementsRecovery) throws CoreException {
402
403         RefactoringStatus result= new RefactoringStatus();
404         ICompilationUnit compilationUnit= (ICompilationUnit) oldCUNode.getJavaElement();
405
406         String JavaDoc newCuSource= cuChange.getPreviewContent(new NullProgressMonitor());
407         CompilationUnit newCUNode= new RefactoringASTParser(AST.JLS3).parse(newCuSource, compilationUnit, true, statementsRecovery, null);
408
409         result.merge(analyzeCompileErrors(newCuSource, newCUNode, oldCUNode));
410         if (result.hasError())
411             return result;
412
413         for (int i= 0; i < analyzePackages.length; i++) {
414             ASTNode enclosing= getEnclosingBlockOrMethod(analyzePackages[i].fDeclarationEdit, cuChange, newCUNode);
415
416             // get new declaration
417
IRegion newRegion= RefactoringAnalyzeUtil.getNewTextRange(analyzePackages[i].fDeclarationEdit, cuChange);
418             ASTNode newDeclaration= NodeFinder.perform(newCUNode, newRegion.getOffset(), newRegion.getLength());
419             Assert.isTrue(newDeclaration instanceof Name);
420
421             VariableDeclaration declaration= getVariableDeclaration((Name) newDeclaration);
422             Assert.isNotNull(declaration);
423
424             SimpleName[] problemNodes= ProblemNodeFinder.getProblemNodes(enclosing, declaration, analyzePackages[i].fOccurenceEdits, cuChange);
425             result.merge(RefactoringAnalyzeUtil.reportProblemNodes(newCuSource, problemNodes));
426         }
427         return result;
428     }
429
430     private static VariableDeclaration getVariableDeclaration(Name node) {
431         IBinding binding= node.resolveBinding();
432         if (binding == null && node.getParent() instanceof VariableDeclaration)
433             return (VariableDeclaration) node.getParent();
434
435         if (binding != null && binding.getKind() == IBinding.VARIABLE) {
436             CompilationUnit cu= (CompilationUnit) ASTNodes.getParent(node, CompilationUnit.class);
437             return ASTNodes.findVariableDeclaration( ((IVariableBinding) binding), cu);
438         }
439         return null;
440     }
441
442     private static ASTNode getEnclosingBlockOrMethod(TextEdit declarationEdit, TextChange change, CompilationUnit newCUNode) {
443         ASTNode enclosing= RefactoringAnalyzeUtil.getBlock(declarationEdit, change, newCUNode);
444         if (enclosing == null)
445             enclosing= RefactoringAnalyzeUtil.getMethodDeclaration(declarationEdit, change, newCUNode);
446         return enclosing;
447     }
448
449     private static RefactoringStatus analyzeCompileErrors(String JavaDoc newCuSource, CompilationUnit newCUNode, CompilationUnit oldCUNode) {
450         RefactoringStatus result= new RefactoringStatus();
451         IProblem[] newProblems= RefactoringAnalyzeUtil.getIntroducedCompileProblems(newCUNode, oldCUNode);
452         for (int i= 0; i < newProblems.length; i++) {
453             IProblem problem= newProblems[i];
454             if (problem.isError())
455                 result.addEntry(new RefactoringStatusEntry((problem.isError() ? RefactoringStatus.ERROR : RefactoringStatus.WARNING), problem.getMessage(), new JavaStringStatusContext(newCuSource, new SourceRange(problem))));
456         }
457         return result;
458     }
459 }
460
Popular Tags