]> git.uio.no Git - ifi-stolz-refaktor.git/blame - case-study/jdt-after/core refactoring/org/eclipse/jdt/internal/corext/refactoring/rename/RenameAnalyzeUtil.java
Case Study: adding data and statistics
[ifi-stolz-refaktor.git] / case-study / jdt-after / core refactoring / org / eclipse / jdt / internal / corext / refactoring / rename / RenameAnalyzeUtil.java
CommitLineData
1b2798f6
EK
1/*******************************************************************************
2 * Copyright (c) 2000, 2011 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 *******************************************************************************/
11package org.eclipse.jdt.internal.corext.refactoring.rename;
12
13import java.util.ArrayList;
14import java.util.Arrays;
15import java.util.Collection;
16import java.util.HashMap;
17import java.util.HashSet;
18import java.util.Iterator;
19import java.util.Map;
20import java.util.Map.Entry;
21
22import org.eclipse.core.runtime.Assert;
23import org.eclipse.core.runtime.CoreException;
24import org.eclipse.core.runtime.NullProgressMonitor;
25import org.eclipse.core.runtime.SubProgressMonitor;
26
27import org.eclipse.core.resources.IResource;
28
29import org.eclipse.text.edits.TextEdit;
30
31import org.eclipse.jface.text.IRegion;
32import org.eclipse.jface.text.Region;
33
34import org.eclipse.ltk.core.refactoring.RefactoringStatus;
35import org.eclipse.ltk.core.refactoring.RefactoringStatusContext;
36import org.eclipse.ltk.core.refactoring.RefactoringStatusEntry;
37import org.eclipse.ltk.core.refactoring.TextChange;
38import org.eclipse.ltk.core.refactoring.TextEditChangeGroup;
39
40import org.eclipse.jdt.core.ICompilationUnit;
41import org.eclipse.jdt.core.IJavaElement;
42import org.eclipse.jdt.core.ISourceRange;
43import org.eclipse.jdt.core.ISourceReference;
44import org.eclipse.jdt.core.JavaModelException;
45import org.eclipse.jdt.core.SourceRange;
46import org.eclipse.jdt.core.WorkingCopyOwner;
47import org.eclipse.jdt.core.compiler.IProblem;
48import org.eclipse.jdt.core.dom.ASTNode;
49import org.eclipse.jdt.core.dom.ASTVisitor;
50import org.eclipse.jdt.core.dom.CompilationUnit;
51import org.eclipse.jdt.core.dom.IBinding;
52import org.eclipse.jdt.core.dom.IVariableBinding;
53import org.eclipse.jdt.core.dom.Name;
54import org.eclipse.jdt.core.dom.NodeFinder;
55import org.eclipse.jdt.core.dom.SimpleName;
56import org.eclipse.jdt.core.dom.VariableDeclaration;
57import org.eclipse.jdt.core.search.FieldDeclarationMatch;
58import org.eclipse.jdt.core.search.MethodDeclarationMatch;
59import org.eclipse.jdt.core.search.SearchMatch;
60
61import org.eclipse.jdt.internal.corext.SourceRangeFactory;
62import org.eclipse.jdt.internal.corext.dom.ASTNodes;
63import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
64import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup;
65import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext;
66import org.eclipse.jdt.internal.corext.refactoring.base.JavaStringStatusContext;
67import org.eclipse.jdt.internal.corext.refactoring.util.RefactoringASTParser;
68import org.eclipse.jdt.internal.corext.refactoring.util.TextChangeManager;
69import org.eclipse.jdt.internal.corext.util.Messages;
70import org.eclipse.jdt.internal.corext.util.SearchUtils;
71
72import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider;
73import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
74
75class RenameAnalyzeUtil {
76
77 private static class ProblemNodeFinder {
78
79 private ProblemNodeFinder() {
80 //static
81 }
82
83 public static SimpleName[] getProblemNodes(ASTNode methodNode, VariableDeclaration variableNode, TextEdit[] edits, TextChange change) {
84 String key= variableNode.resolveBinding().getKey();
85 NameNodeVisitor visitor= new NameNodeVisitor(edits, change, key);
86 methodNode.accept(visitor);
87 return visitor.getProblemNodes();
88 }
89
90 private static class NameNodeVisitor extends ASTVisitor {
91
92 private Collection<IRegion> fRanges;
93 private Collection<SimpleName> fProblemNodes;
94 private String fKey;
95
96 public NameNodeVisitor(TextEdit[] edits, TextChange change, String key) {
97 Assert.isNotNull(edits);
98 Assert.isNotNull(key);
99
100 fRanges= new HashSet<IRegion>(Arrays.asList(RefactoringAnalyzeUtil.getNewRanges(edits, change)));
101 fProblemNodes= new ArrayList<SimpleName>(0);
102 fKey= key;
103 }
104
105 public SimpleName[] getProblemNodes() {
106 return fProblemNodes.toArray(new SimpleName[fProblemNodes.size()]);
107 }
108
109 //----- visit methods
110
111 @Override
112 public boolean visit(SimpleName node) {
113 VariableDeclaration decl= getVariableDeclaration(node);
114 if (decl == null)
115 return super.visit(node);
116
117 IVariableBinding binding= decl.resolveBinding();
118 if (binding == null)
119 return super.visit(node);
120
121 boolean keysEqual= fKey.equals(binding.getKey());
122 boolean rangeInSet= fRanges.contains(new Region(node.getStartPosition(), node.getLength()));
123
124 if (keysEqual && !rangeInSet)
125 fProblemNodes.add(node);
126
127 if (!keysEqual && rangeInSet)
128 fProblemNodes.add(node);
129
130 /*
131 * if (!keyEquals && !rangeInSet)
132 * ok, different local variable.
133 *
134 * if (keyEquals && rangeInSet)
135 * ok, renamed local variable & has been renamed.
136 */
137
138 return super.visit(node);
139 }
140 }
141 }
142
143 static class LocalAnalyzePackage {
144 public final TextEdit fDeclarationEdit;
145 public final TextEdit[] fOccurenceEdits;
146
147 public LocalAnalyzePackage(final TextEdit declarationEdit, final TextEdit[] occurenceEdits) {
148 fDeclarationEdit = declarationEdit;
149 fOccurenceEdits = occurenceEdits;
150 }
151 }
152
153 private RenameAnalyzeUtil() {
154 //no instance
155 }
156
157 static RefactoringStatus analyzeRenameChanges(TextChangeManager manager, SearchResultGroup[] oldOccurrences, SearchResultGroup[] newOccurrences) {
158 RefactoringStatus result= new RefactoringStatus();
159 for (int i= 0; i < oldOccurrences.length; i++) {
160 SearchResultGroup oldGroup= oldOccurrences[i];
161 SearchMatch[] oldSearchResults= oldGroup.getSearchResults();
162 ICompilationUnit cunit= oldGroup.getCompilationUnit();
163 if (cunit == null)
164 continue;
165 for (int j= 0; j < oldSearchResults.length; j++) {
166 SearchMatch oldSearchResult= oldSearchResults[j];
167 if (! RenameAnalyzeUtil.existsInNewOccurrences(oldSearchResult, newOccurrences, manager)){
168 addShadowsError(cunit, oldSearchResult, result);
169 }
170 }
171 }
172 return result;
173 }
174
175 static ICompilationUnit findWorkingCopyForCu(ICompilationUnit[] newWorkingCopies, ICompilationUnit cu){
176 ICompilationUnit original= cu == null ? null : cu.getPrimary();
177 for (int i= 0; i < newWorkingCopies.length; i++) {
178 if (newWorkingCopies[i].getPrimary().equals(original))
179 return newWorkingCopies[i];
180 }
181 return null;
182 }
183
184 static ICompilationUnit[] createNewWorkingCopies(ICompilationUnit[] compilationUnitsToModify, TextChangeManager manager, WorkingCopyOwner owner, SubProgressMonitor pm) throws CoreException {
185 pm.beginTask("", compilationUnitsToModify.length); //$NON-NLS-1$
186 ICompilationUnit[] newWorkingCopies= new ICompilationUnit[compilationUnitsToModify.length];
187 for (int i= 0; i < compilationUnitsToModify.length; i++) {
188 ICompilationUnit cu= compilationUnitsToModify[i];
189 newWorkingCopies[i]= createNewWorkingCopy(cu, manager, owner, new SubProgressMonitor(pm, 1));
190 }
191 pm.done();
192 return newWorkingCopies;
193 }
194
195 static ICompilationUnit createNewWorkingCopy(ICompilationUnit cu, TextChangeManager manager,
196 WorkingCopyOwner owner, SubProgressMonitor pm) throws CoreException {
197 ICompilationUnit newWc= cu.getWorkingCopy(owner, null);
198 String previewContent= manager.get(cu).getPreviewContent(new NullProgressMonitor());
199 newWc.getBuffer().setContents(previewContent);
200 newWc.reconcile(ICompilationUnit.NO_AST, false, owner, pm);
201 return newWc;
202 }
203
204 private static boolean existsInNewOccurrences(SearchMatch searchResult, SearchResultGroup[] newOccurrences, TextChangeManager manager) {
205 SearchResultGroup newGroup= findOccurrenceGroup(searchResult.getResource(), newOccurrences);
206 if (newGroup == null)
207 return false;
208
209 IRegion oldEditRange= getCorrespondingEditChangeRange(searchResult, manager);
210 if (oldEditRange == null)
211 return false;
212
213 SearchMatch[] newSearchResults= newGroup.getSearchResults();
214 int oldRangeOffset = oldEditRange.getOffset();
215 for (int i= 0; i < newSearchResults.length; i++) {
216 if (newSearchResults[i].getOffset() == oldRangeOffset)
217 return true;
218 }
219 return false;
220 }
221
222 private static IRegion getCorrespondingEditChangeRange(SearchMatch searchResult, TextChangeManager manager) {
223 TextChange change= getTextChange(searchResult, manager);
224 if (change == null)
225 return null;
226
227 IRegion oldMatchRange= createTextRange(searchResult);
228 TextEditChangeGroup[] editChanges= change.getTextEditChangeGroups();
229 for (int i= 0; i < editChanges.length; i++) {
230 if (oldMatchRange.equals(editChanges[i].getRegion()))
231 return TextEdit.getCoverage(change.getPreviewEdits(editChanges[i].getTextEdits()));
232 }
233 return null;
234 }
235
236 private static TextChange getTextChange(SearchMatch searchResult, TextChangeManager manager) {
237 ICompilationUnit cu= SearchUtils.getCompilationUnit(searchResult);
238 if (cu == null)
239 return null;
240 return manager.get(cu);
241 }
242
243 private static IRegion createTextRange(SearchMatch searchResult) {
244 return new Region(searchResult.getOffset(), searchResult.getLength());
245 }
246
247 private static SearchResultGroup findOccurrenceGroup(IResource resource, SearchResultGroup[] newOccurrences) {
248 for (int i= 0; i < newOccurrences.length; i++) {
249 if (newOccurrences[i].getResource().equals(resource))
250 return newOccurrences[i];
251 }
252 return null;
253 }
254
255//--- find missing changes in BOTH directions
256
257 //TODO: Currently filters out declarations (MethodDeclarationMatch, FieldDeclarationMatch).
258 //Long term solution: only pass reference search results in.
259 static RefactoringStatus analyzeRenameChanges2(TextChangeManager manager,
260 SearchResultGroup[] oldReferences, SearchResultGroup[] newReferences, String newElementName) {
261 RefactoringStatus result= new RefactoringStatus();
262
263 HashMap<ICompilationUnit, SearchMatch[]> cuToNewResults= new HashMap<ICompilationUnit, SearchMatch[]>(newReferences.length);
264 for (int i1= 0; i1 < newReferences.length; i1++) {
265 ICompilationUnit cu= newReferences[i1].getCompilationUnit();
266 if (cu != null)
267 cuToNewResults.put(cu.getPrimary(), newReferences[i1].getSearchResults());
268 }
269
270 for (int i= 0; i < oldReferences.length; i++) {
271 SearchResultGroup oldGroup= oldReferences[i];
272 SearchMatch[] oldMatches= oldGroup.getSearchResults();
273 ICompilationUnit cu= oldGroup.getCompilationUnit();
274 if (cu == null)
275 continue;
276
277 SearchMatch[] newSearchMatches= cuToNewResults.remove(cu);
278 if (newSearchMatches == null) {
279 for (int j = 0; j < oldMatches.length; j++) {
280 SearchMatch oldMatch = oldMatches[j];
281 addShadowsError(cu, oldMatch, result);
282 }
283 } else {
284 analyzeChanges(cu, manager.get(cu), oldMatches, newSearchMatches, newElementName, result);
285 }
286 }
287
288 for (Iterator<Entry<ICompilationUnit, SearchMatch[]>> iter= cuToNewResults.entrySet().iterator(); iter.hasNext();) {
289 Entry<ICompilationUnit, SearchMatch[]> entry= iter.next();
290 ICompilationUnit cu= entry.getKey();
291 SearchMatch[] newSearchMatches= entry.getValue();
292 for (int i= 0; i < newSearchMatches.length; i++) {
293 SearchMatch newMatch= newSearchMatches[i];
294 addReferenceShadowedError(cu, newMatch, newElementName, result);
295 }
296 }
297 return result;
298 }
299
300 private static void analyzeChanges(ICompilationUnit cu, TextChange change,
301 SearchMatch[] oldMatches, SearchMatch[] newMatches, String newElementName, RefactoringStatus result) {
302 Map<Integer, SearchMatch> updatedOldOffsets= getUpdatedChangeOffsets(change, oldMatches);
303 for (int i= 0; i < newMatches.length; i++) {
304 SearchMatch newMatch= newMatches[i];
305 Integer offsetInNew= new Integer(newMatch.getOffset());
306 SearchMatch oldMatch= updatedOldOffsets.remove(offsetInNew);
307 if (oldMatch == null) {
308 addReferenceShadowedError(cu, newMatch, newElementName, result);
309 }
310 }
311 for (Iterator<SearchMatch> iter= updatedOldOffsets.values().iterator(); iter.hasNext();) {
312 // remaining old matches are not found any more -> they have been shadowed
313 SearchMatch oldMatch= iter.next();
314 addShadowsError(cu, oldMatch, result);
315 }
316 }
317
318 /**
319 *
320 * @param change
321 * @param oldMatches
322 * @return Map &lt;Integer updatedOffset, SearchMatch oldMatch&gt;
323 */
324 private static Map<Integer, SearchMatch> getUpdatedChangeOffsets(TextChange change, SearchMatch[] oldMatches) {
325 Map<Integer, SearchMatch> updatedOffsets= new HashMap<Integer, SearchMatch>();
326 Map<Integer, Integer> oldToUpdatedOffsets= getEditChangeOffsetUpdates(change);
327 for (int i= 0; i < oldMatches.length; i++) {
328 SearchMatch oldMatch= oldMatches[i];
329 Integer updatedOffset= oldToUpdatedOffsets.get(new Integer(oldMatch.getOffset()));
330 if (updatedOffset == null)
331 updatedOffset= new Integer(-1); //match not updated
332 updatedOffsets.put(updatedOffset, oldMatch);
333 }
334 return updatedOffsets;
335 }
336
337 /**
338 *
339 * @param change
340 * @return Map &lt;Integer oldOffset, Integer updatedOffset&gt;
341 */
342 private static Map<Integer, Integer> getEditChangeOffsetUpdates(TextChange change) {
343 TextEditChangeGroup[] editChanges= change.getTextEditChangeGroups();
344 Map<Integer, Integer> offsetUpdates= new HashMap<Integer, Integer>(editChanges.length);
345 for (int i= 0; i < editChanges.length; i++) {
346 TextEditChangeGroup editChange= editChanges[i];
347 IRegion oldRegion= editChange.getRegion();
348 if (oldRegion == null)
349 continue;
350 IRegion updatedRegion= TextEdit.getCoverage(change.getPreviewEdits(editChange.getTextEdits()));
351 if (updatedRegion == null)
352 continue;
353
354 offsetUpdates.put(new Integer(oldRegion.getOffset()), new Integer(updatedRegion.getOffset()));
355 }
356 return offsetUpdates;
357 }
358
359 private static void addReferenceShadowedError(ICompilationUnit cu, SearchMatch newMatch, String newElementName, RefactoringStatus result) {
360 //Found a new match with no corresponding old match.
361 //-> The new match is a reference which was pointing to another element,
362 //but that other element has been shadowed
363
364 //TODO: should not have to filter declarations:
365 if (newMatch instanceof MethodDeclarationMatch || newMatch instanceof FieldDeclarationMatch)
366 return;
367 ISourceRange range= getOldSourceRange(newMatch);
368 RefactoringStatusContext context= JavaStatusContext.create(cu, range);
369 String message= Messages.format(
370 RefactoringCoreMessages.RenameAnalyzeUtil_reference_shadowed,
371 new String[] {BasicElementLabels.getFileName(cu), BasicElementLabels.getJavaElementName(newElementName)});
372 result.addError(message, context);
373 }
374
375 private static ISourceRange getOldSourceRange(SearchMatch newMatch) {
376 // cannot transfom offset in preview to offset in original -> just show enclosing method
377 IJavaElement newMatchElement= (IJavaElement) newMatch.getElement();
378 IJavaElement primaryElement= newMatchElement.getPrimaryElement();
379 ISourceRange range= null;
380 if (primaryElement.exists() && primaryElement instanceof ISourceReference) {
381 try {
382 range= ((ISourceReference) primaryElement).getSourceRange();
383 } catch (JavaModelException e) {
384 // can live without source range
385 }
386 }
387 return range;
388 }
389
390 private static void addShadowsError(ICompilationUnit cu, SearchMatch oldMatch, RefactoringStatus result) {
391 // Old match not found in new matches -> reference has been shadowed
392
393 //TODO: should not have to filter declarations:
394 if (oldMatch instanceof MethodDeclarationMatch || oldMatch instanceof FieldDeclarationMatch)
395 return;
396 ISourceRange range= new SourceRange(oldMatch.getOffset(), oldMatch.getLength());
397 RefactoringStatusContext context= JavaStatusContext.create(cu, range);
398 String message= Messages.format(RefactoringCoreMessages.RenameAnalyzeUtil_shadows, BasicElementLabels.getFileName(cu));
399 result.addError(message, context);
400 }
401
402 /**
403 * This method analyzes a set of local variable renames inside one cu. It checks whether
404 * any new compile errors have been introduced by the rename(s) and whether the correct
405 * node(s) has/have been renamed.
406 *
407 * @param analyzePackages the LocalAnalyzePackages containing the information about the local renames
408 * @param cuChange the TextChange containing all local variable changes to be applied.
409 * @param oldCUNode the fully (incl. bindings) resolved AST node of the original compilation unit
410 * @param recovery whether statements and bindings recovery should be performed when parsing the changed CU
411 * @return a RefactoringStatus containing errors if compile errors or wrongly renamed nodes are found
412 * @throws CoreException thrown if there was an error greating the preview content of the change
413 */
414 public static RefactoringStatus analyzeLocalRenames(LocalAnalyzePackage[] analyzePackages, TextChange cuChange, CompilationUnit oldCUNode, boolean recovery) throws CoreException {
415
416 RefactoringStatus result= new RefactoringStatus();
417 ICompilationUnit compilationUnit= (ICompilationUnit) oldCUNode.getJavaElement();
418
419 String newCuSource= cuChange.getPreviewContent(new NullProgressMonitor());
420 CompilationUnit newCUNode= new RefactoringASTParser(ASTProvider.SHARED_AST_LEVEL).parse(newCuSource, compilationUnit, true, recovery, null);
421
422 result.merge(analyzeCompileErrors(newCuSource, newCUNode, oldCUNode));
423 if (result.hasError())
424 return result;
425
426 for (int i= 0; i < analyzePackages.length; i++) {
427 ASTNode enclosing= getEnclosingBlockOrMethod(analyzePackages[i].fDeclarationEdit, cuChange, newCUNode);
428
429 // get new declaration
430 IRegion newRegion= RefactoringAnalyzeUtil.getNewTextRange(analyzePackages[i].fDeclarationEdit, cuChange);
431 ASTNode newDeclaration= NodeFinder.perform(newCUNode, newRegion.getOffset(), newRegion.getLength());
432 Assert.isTrue(newDeclaration instanceof Name);
433
434 VariableDeclaration declaration= getVariableDeclaration((Name) newDeclaration);
435 Assert.isNotNull(declaration);
436
437 SimpleName[] problemNodes= ProblemNodeFinder.getProblemNodes(enclosing, declaration, analyzePackages[i].fOccurenceEdits, cuChange);
438 result.merge(RefactoringAnalyzeUtil.reportProblemNodes(newCuSource, problemNodes));
439 }
440 return result;
441 }
442
443 private static VariableDeclaration getVariableDeclaration(Name node) {
444 IBinding binding= node.resolveBinding();
445 if (binding == null && node.getParent() instanceof VariableDeclaration)
446 return (VariableDeclaration) node.getParent();
447
448 if (binding != null && binding.getKind() == IBinding.VARIABLE) {
449 CompilationUnit cu= (CompilationUnit) ASTNodes.getParent(node, CompilationUnit.class);
450 return ASTNodes.findVariableDeclaration( ((IVariableBinding) binding), cu);
451 }
452 return null;
453 }
454
455 private static ASTNode getEnclosingBlockOrMethod(TextEdit declarationEdit, TextChange change, CompilationUnit newCUNode) {
456 ASTNode enclosing= RefactoringAnalyzeUtil.getBlock(declarationEdit, change, newCUNode);
457 if (enclosing == null)
458 enclosing= RefactoringAnalyzeUtil.getMethodDeclaration(declarationEdit, change, newCUNode);
459 return enclosing;
460 }
461
462 private static RefactoringStatus analyzeCompileErrors(String newCuSource, CompilationUnit newCUNode, CompilationUnit oldCUNode) {
463 RefactoringStatus result= new RefactoringStatus();
464 IProblem[] newProblems= RefactoringAnalyzeUtil.getIntroducedCompileProblems(newCUNode, oldCUNode);
465 for (int i= 0; i < newProblems.length; i++) {
466 IProblem problem= newProblems[i];
467 if (problem.isError())
468 result.addEntry(new RefactoringStatusEntry((problem.isError() ? RefactoringStatus.ERROR : RefactoringStatus.WARNING), problem.getMessage(), new JavaStringStatusContext(newCuSource,
469 SourceRangeFactory.create(problem))));
470 }
471 return result;
472 }
473}