KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > internal > corext > refactoring > code > IntroduceIndirectionRefactoring


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 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.code;
12
13 import java.lang.reflect.Modifier JavaDoc;
14 import java.util.ArrayList JavaDoc;
15 import java.util.Arrays JavaDoc;
16 import java.util.HashMap JavaDoc;
17 import java.util.Iterator JavaDoc;
18 import java.util.List JavaDoc;
19 import java.util.Map JavaDoc;
20
21 import org.eclipse.core.runtime.Assert;
22 import org.eclipse.core.runtime.CoreException;
23 import org.eclipse.core.runtime.IProgressMonitor;
24 import org.eclipse.core.runtime.NullProgressMonitor;
25 import org.eclipse.core.runtime.OperationCanceledException;
26 import org.eclipse.core.runtime.SubProgressMonitor;
27
28 import org.eclipse.core.resources.IFile;
29
30 import org.eclipse.ltk.core.refactoring.Change;
31 import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
32 import org.eclipse.ltk.core.refactoring.RefactoringStatus;
33 import org.eclipse.ltk.core.refactoring.participants.RefactoringArguments;
34
35 import org.eclipse.jdt.core.Flags;
36 import org.eclipse.jdt.core.IClassFile;
37 import org.eclipse.jdt.core.ICompilationUnit;
38 import org.eclipse.jdt.core.IJavaElement;
39 import org.eclipse.jdt.core.IJavaProject;
40 import org.eclipse.jdt.core.IMember;
41 import org.eclipse.jdt.core.IMethod;
42 import org.eclipse.jdt.core.IPackageFragment;
43 import org.eclipse.jdt.core.IType;
44 import org.eclipse.jdt.core.ITypeHierarchy;
45 import org.eclipse.jdt.core.JavaModelException;
46 import org.eclipse.jdt.core.dom.AST;
47 import org.eclipse.jdt.core.dom.ASTNode;
48 import org.eclipse.jdt.core.dom.ASTParser;
49 import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
50 import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
51 import org.eclipse.jdt.core.dom.Block;
52 import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor;
53 import org.eclipse.jdt.core.dom.CompilationUnit;
54 import org.eclipse.jdt.core.dom.Expression;
55 import org.eclipse.jdt.core.dom.ExpressionStatement;
56 import org.eclipse.jdt.core.dom.IBinding;
57 import org.eclipse.jdt.core.dom.IMethodBinding;
58 import org.eclipse.jdt.core.dom.ITypeBinding;
59 import org.eclipse.jdt.core.dom.Javadoc;
60 import org.eclipse.jdt.core.dom.MethodDeclaration;
61 import org.eclipse.jdt.core.dom.MethodInvocation;
62 import org.eclipse.jdt.core.dom.Name;
63 import org.eclipse.jdt.core.dom.ParameterizedType;
64 import org.eclipse.jdt.core.dom.PrimitiveType;
65 import org.eclipse.jdt.core.dom.ReturnStatement;
66 import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
67 import org.eclipse.jdt.core.dom.Statement;
68 import org.eclipse.jdt.core.dom.SuperMethodInvocation;
69 import org.eclipse.jdt.core.dom.ThisExpression;
70 import org.eclipse.jdt.core.dom.Type;
71 import org.eclipse.jdt.core.dom.TypeParameter;
72 import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
73 import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
74 import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
75 import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor;
76 import org.eclipse.jdt.core.search.IJavaSearchConstants;
77 import org.eclipse.jdt.core.search.IJavaSearchScope;
78 import org.eclipse.jdt.core.search.SearchMatch;
79 import org.eclipse.jdt.core.search.SearchPattern;
80
81 import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
82 import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
83 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
84 import org.eclipse.jdt.internal.corext.dom.Bindings;
85 import org.eclipse.jdt.internal.corext.dom.NodeFinder;
86 import org.eclipse.jdt.internal.corext.refactoring.Checks;
87 import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
88 import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptor;
89 import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
90 import org.eclipse.jdt.internal.corext.refactoring.RefactoringAvailabilityTester;
91 import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
92 import org.eclipse.jdt.internal.corext.refactoring.RefactoringScopeFactory;
93 import org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine;
94 import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup;
95 import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext;
96 import org.eclipse.jdt.internal.corext.refactoring.changes.CompilationUnitChange;
97 import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange;
98 import org.eclipse.jdt.internal.corext.refactoring.rename.RippleMethodFinder2;
99 import org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil;
100 import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
101 import org.eclipse.jdt.internal.corext.refactoring.structure.MemberVisibilityAdjustor;
102 import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil;
103 import org.eclipse.jdt.internal.corext.refactoring.util.TextChangeManager;
104 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
105 import org.eclipse.jdt.internal.corext.util.JdtFlags;
106 import org.eclipse.jdt.internal.corext.util.Messages;
107 import org.eclipse.jdt.internal.corext.util.MethodOverrideTester;
108
109 import org.eclipse.jdt.ui.CodeGeneration;
110 import org.eclipse.jdt.ui.JavaElementLabels;
111
112 import org.eclipse.jdt.internal.ui.JavaPlugin;
113
114 /**
115  *
116  * This refactoring creates a wrapper around a certain method and redirects callers of the original
117  * method to the newly created method (called "intermediary method").
118  *
119  * If invoked on a method call, the user may select whether to only update the selected call or
120  * all other calls as well. If invoked on a method declaration, the user may select whether to
121  * update calls at all. An intermediary method will be created in both cases.
122  *
123  * Creating indirections is possible for both source and binary methods. Select an invocation of the method or
124  * the declaring method itself, for example in the outline view.
125  *
126  * Note that in case of methods inside generic types, the parameters of the declaring type of the selected method
127  * will be added to the method definition, rendering it generic as well.
128  *
129  * If any of the calls cannot see the intermediary method due to visibility problems with enclosing types
130  * of the intermediary method, visibility will be adjusted. If the intermediary method is not able to
131  * see the target method, this refactoring will try to adjust the visibility of the target method and
132  * enclosing types as well. However, the latter is only possible if the target method is from source.
133  *
134  * @since 3.2
135  *
136  */

137 public class IntroduceIndirectionRefactoring extends ScriptableRefactoring {
138
139     /**
140      * The compilation unit in which the user invoked this refactoring (if any)
141      */

142     private ICompilationUnit fSelectionCompilationUnit;
143     /**
144      * The class file (with source) in which the user invoked this refactoring (if any)
145      */

146     private IClassFile fSelectionClassFile;
147     /**
148      * The start of the user selection inside the selected
149      * compilation unit (if any)
150      */

151     private int fSelectionStart;
152     /**
153      * The length of the user selection inside the selected
154      * compilation unit (if any)
155      */

156     private int fSelectionLength;
157     /**
158      * The selected MethodInvocation (if any). This field is used
159      * to update this particular invocation in non-reference mode.
160      */

161     private MethodInvocation fSelectionMethodInvocation;
162
163     // Intermediary information:
164

165     /**
166      * The class in which to place the intermediary method
167      */

168     private IType fIntermediaryClass;
169     /**
170      * The binding of the intermediary class
171      */

172     private ITypeBinding fIntermediaryClassBinding;
173     /**
174      * The name of the intermediary method
175      */

176     private String JavaDoc fIntermediaryMethodName;
177     /**
178      * The type for the additional parameter for the intermediary. This
179      * type is determined from all known references.
180      */

181     private ITypeBinding fIntermediaryFirstParameterType;
182
183     // Target information:
184

185     /**
186      * The originally selected target method (i.e., the one to be encapsulated)
187      */

188     private IMethod fTargetMethod;
189     /**
190      * The binding of the originally selected target method
191      */

192     private IMethodBinding fTargetMethodBinding;
193
194     // Other information:
195

196     /**
197      * If true, all references to the target method are replaced with calls to
198      * the intermediary.
199      */

200     private boolean fUpdateReferences;
201     /**
202      * CompilationUnitRewrites for all affected cus
203      */

204     private Map JavaDoc/* <ICompilationUnit,CompilationUnitRewrite> */fRewrites;
205     /**
206      * Text change manager (actually a CompilationUnitChange manager) which
207      * manages all changes.
208      */

209     private TextChangeManager fTextChangeManager;
210     
211     // Visibility
212

213     /**
214      * The visibility adjustor
215      */

216     private MemberVisibilityAdjustor fAdjustor;
217     /**
218      * Visibility adjustments for the intermediary
219      */

220     private Map JavaDoc/*IMember, IVisibilityAdjustment*/ fIntermediaryAdjustments;
221
222
223     private class NoOverrideProgressMonitor extends SubProgressMonitor {
224
225         public NoOverrideProgressMonitor(IProgressMonitor monitor, int ticks) {
226             super(monitor, ticks, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL);
227         }
228
229         public void setTaskName(String JavaDoc name) {
230             // do nothing
231
}
232     }
233
234     // ********* CONSTRUCTORS AND CLASS CREATION ************
235

236     public IntroduceIndirectionRefactoring(ICompilationUnit unit, int offset, int length) {
237         fSelectionCompilationUnit= unit;
238         initialize(offset, length);
239     }
240
241     public IntroduceIndirectionRefactoring(IClassFile file, int offset, int length) {
242         fSelectionClassFile= file;
243         initialize(offset, length);
244     }
245
246     public IntroduceIndirectionRefactoring(IMethod method) {
247         fTargetMethod= method;
248         initialize(0, 0);
249     }
250
251     private void initialize(int offset, int length) {
252         fSelectionStart= offset;
253         fSelectionLength= length;
254         fUpdateReferences= true;
255     }
256
257     // ********* UI INTERACTION AND STARTUP OPTIONS ************
258

259     public String JavaDoc getName() {
260         return RefactoringCoreMessages.IntroduceIndirectionRefactoring_introduce_indirection_name;
261     }
262
263     public IJavaProject getProject() {
264         if (fSelectionCompilationUnit != null)
265             return fSelectionCompilationUnit.getJavaProject();
266         if (fSelectionClassFile != null)
267             return fSelectionClassFile.getJavaProject();
268         if (fTargetMethod != null)
269             return fTargetMethod.getJavaProject();
270         return null;
271     }
272
273     public IPackageFragment getInvocationPackage() {
274         return fSelectionCompilationUnit != null ? (IPackageFragment) fSelectionCompilationUnit.getAncestor(IJavaElement.PACKAGE_FRAGMENT) : null;
275     }
276
277     public boolean canEnableUpdateReferences() {
278         return true;
279     }
280
281     public void setEnableUpdateReferences(boolean updateReferences) {
282         fUpdateReferences= updateReferences;
283     }
284
285     public RefactoringStatus setIntermediaryMethodName(String JavaDoc newMethodName) {
286         Assert.isNotNull(newMethodName);
287         fIntermediaryMethodName= newMethodName;
288         RefactoringStatus stat= Checks.checkMethodName(newMethodName);
289         stat.merge(checkOverloading());
290         return stat;
291     }
292
293     private RefactoringStatus checkOverloading() {
294         try {
295             if (fIntermediaryClass != null) {
296                 IMethod[] toCheck= fIntermediaryClass.getMethods();
297                 for (int i= 0; i < toCheck.length; i++) {
298                     IMethod method= toCheck[i];
299                     if (method.getElementName().equals(fIntermediaryMethodName))
300                         return RefactoringStatus.createWarningStatus(Messages.format(RefactoringCoreMessages.IntroduceIndirectionRefactoring_duplicate_method_name_in_declaring_class_error,
301                                 fIntermediaryMethodName));
302                 }
303             }
304         } catch (JavaModelException e) {
305             return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_could_not_parse_declaring_class_error);
306         }
307         return new RefactoringStatus();
308     }
309
310     public String JavaDoc getIntermediaryMethodName() {
311         return fIntermediaryMethodName;
312     }
313
314     /**
315      * @param fullyQualifiedTypeName
316      * @return status for type name. Use {@link #setIntermediaryMethodName(String)} to check for overridden methods.
317      */

318     public RefactoringStatus setIntermediaryClassName(String JavaDoc fullyQualifiedTypeName) {
319         IType target= null;
320
321         try {
322             if (fullyQualifiedTypeName.length() == 0)
323                 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_class_not_selected_error);
324             
325             // find type (now including secondaries)
326
target= getProject().findType(fullyQualifiedTypeName, new NullProgressMonitor());
327             if (target == null || !target.exists())
328                 return RefactoringStatus.createErrorStatus(Messages.format(RefactoringCoreMessages.IntroduceIndirectionRefactoring_class_does_not_exist_error, fullyQualifiedTypeName));
329             if (target.isAnnotation())
330                 return RefactoringStatus.createErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_cannot_create_in_annotation);
331             if (target.isInterface())
332                 return RefactoringStatus.createErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_cannot_create_on_interface);
333         } catch (JavaModelException e) {
334             return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_unable_determine_declaring_type);
335         }
336
337         if (target.isReadOnly())
338             return RefactoringStatus.createErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_cannot_create_in_readonly);
339
340         if (target.isBinary())
341             return RefactoringStatus.createErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_cannot_create_in_binary);
342
343         fIntermediaryClass= target;
344
345         return new RefactoringStatus();
346     }
347
348     /**
349      * Returns the class name of the intermediary class, or the empty string if none has been set yet.
350      * @return the intermediary class name or the empty string
351      */

352     public String JavaDoc getIntermediaryClassName() {
353         return fIntermediaryClass != null ? JavaModelUtil.getFullyQualifiedName(fIntermediaryClass) : ""; //$NON-NLS-1$
354
}
355
356     // ********** CONDITION CHECKING **********
357

358     public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException {
359         try {
360             pm.beginTask(RefactoringCoreMessages.IntroduceIndirectionRefactoring_checking_activation, 1);
361             fRewrites= new HashMap JavaDoc();
362
363             // This refactoring has been invoked on
364
// (1) a TextSelection inside an ICompilationUnit or inside an IClassFile (definitely with source), or
365
// (2) an IMethod inside a ICompilationUnit or inside an IClassFile (with or without source)
366

367             if (fTargetMethod == null) {
368                 // (1) invoked on a text selection
369

370                 if (fSelectionStart == 0)
371                     return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_not_available_on_this_selection);
372
373                 // if a text selection exists, source is available.
374
CompilationUnit selectionCURoot;
375                 ASTNode selectionNode;
376                 if (fSelectionCompilationUnit != null) {
377                     // compilation unit - could use CuRewrite later on
378
selectionCURoot= getCachedCURewrite(fSelectionCompilationUnit).getRoot();
379                     selectionNode= getSelectedNode(fSelectionCompilationUnit, selectionCURoot, fSelectionStart, fSelectionLength);
380                 } else {
381                     // binary class file - no cu rewrite
382
ASTParser parser= ASTParser.newParser(AST.JLS3);
383                     parser.setResolveBindings(true);
384                     parser.setSource(fSelectionClassFile);
385                     selectionCURoot= (CompilationUnit) parser.createAST(null);
386                     selectionNode= getSelectedNode(null, selectionCURoot, fSelectionStart, fSelectionLength);
387                 }
388
389                 if (selectionNode == null)
390                     return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_not_available_on_this_selection);
391
392                 IMethodBinding targetMethodBinding= null;
393
394                 if (selectionNode.getNodeType() == ASTNode.METHOD_INVOCATION) {
395                     targetMethodBinding= ((MethodInvocation) selectionNode).resolveMethodBinding();
396                 } else if (selectionNode.getNodeType() == ASTNode.METHOD_DECLARATION) {
397                     targetMethodBinding= ((MethodDeclaration) selectionNode).resolveBinding();
398                 } else if (selectionNode.getNodeType() == ASTNode.SUPER_METHOD_INVOCATION) {
399                     // Allow invocation on super methods calls. makes sense as other
400
// calls or even only the declaration can be updated.
401
targetMethodBinding= ((SuperMethodInvocation) selectionNode).resolveMethodBinding();
402                 } else {
403                     return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_not_available_on_this_selection);
404                 }
405                 fTargetMethodBinding= targetMethodBinding.getMethodDeclaration(); // resolve generics
406
fTargetMethod= (IMethod) fTargetMethodBinding.getJavaElement();
407
408                 //allow single updating mode if an invocation was selected and the invocation can be updated
409
if (selectionNode instanceof MethodInvocation && fSelectionCompilationUnit != null)
410                     fSelectionMethodInvocation= (MethodInvocation) selectionNode;
411
412             } else {
413                 // (2) invoked on an IMethod: Source may not be available
414

415                 if (fTargetMethod.getDeclaringType().isAnnotation())
416                     return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_not_available_on_annotation);
417
418                 if (fTargetMethod.getCompilationUnit() != null) {
419                     // source method
420
CompilationUnit selectionCURoot= getCachedCURewrite(fTargetMethod.getCompilationUnit()).getRoot();
421                     MethodDeclaration declaration= ASTNodeSearchUtil.getMethodDeclarationNode(fTargetMethod, selectionCURoot);
422                     fTargetMethodBinding= declaration.resolveBinding().getMethodDeclaration();
423                 } else {
424                     // binary method - no CURewrite available (and none needed as we cannot update the method anyway)
425
ASTParser parser= ASTParser.newParser(AST.JLS3);
426                     parser.setProject(fTargetMethod.getJavaProject());
427                     IBinding[] bindings= parser.createBindings(new IJavaElement[] { fTargetMethod }, null);
428                     fTargetMethodBinding= ((IMethodBinding) bindings[0]).getMethodDeclaration();
429                 }
430             }
431
432             if (fTargetMethod == null || fTargetMethodBinding == null || (!RefactoringAvailabilityTester.isIntroduceIndirectionAvailable(fTargetMethod)))
433                 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_not_available_on_this_selection);
434
435             if (fTargetMethod.getDeclaringType().isLocal() || fTargetMethod.getDeclaringType().isAnonymous())
436                 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_not_available_for_local_or_anonymous_types);
437
438             if (fTargetMethod.isConstructor())
439                 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_not_available_for_constructors);
440
441             if (fIntermediaryMethodName == null)
442                 fIntermediaryMethodName= fTargetMethod.getElementName();
443
444             if (fIntermediaryClass == null) {
445                 if (fSelectionCompilationUnit != null && !fSelectionCompilationUnit.isReadOnly())
446                     fIntermediaryClass= getEnclosingInitialSelectionMember().getDeclaringType();
447                 else if (!fTargetMethod.isBinary() && !fTargetMethod.isReadOnly())
448                     fIntermediaryClass= fTargetMethod.getDeclaringType();
449             }
450
451             return new RefactoringStatus();
452         } finally {
453             pm.done();
454         }
455     }
456
457     public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException {
458
459         RefactoringStatus result= new RefactoringStatus();
460         fTextChangeManager= new TextChangeManager();
461         fIntermediaryFirstParameterType= null;
462         fIntermediaryClassBinding= null;
463         for (Iterator JavaDoc iter= fRewrites.values().iterator(); iter.hasNext();)
464             ((CompilationUnitRewrite) iter.next()).clearASTAndImportRewrites();
465
466         int startupTicks= 5;
467         int hierarchyTicks= 5;
468         int visibilityTicks= 5;
469         int referenceTicks= fUpdateReferences ? 30 : 5;
470         int creationTicks= 5;
471
472         pm.beginTask("", startupTicks + hierarchyTicks + visibilityTicks + referenceTicks + creationTicks); //$NON-NLS-1$
473
pm.setTaskName(RefactoringCoreMessages.IntroduceIndirectionRefactoring_checking_conditions);
474
475         result.merge(Checks.checkMethodName(fIntermediaryMethodName));
476         if (result.hasFatalError())
477             return result;
478         
479         if (fIntermediaryClass == null)
480             return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_cannot_run_without_intermediary_type);
481
482         // intermediary class is already non binary/non-enum/non-interface.
483
CompilationUnitRewrite imRewrite= getCachedCURewrite(fIntermediaryClass.getCompilationUnit());
484         fIntermediaryClassBinding= typeToBinding(fIntermediaryClass, imRewrite.getRoot());
485
486         fAdjustor= new MemberVisibilityAdjustor(fIntermediaryClass, fIntermediaryClass);
487         fIntermediaryAdjustments= new HashMap JavaDoc();
488         
489         // check static method in non-static nested type
490
if (fIntermediaryClassBinding.isNested() && !Modifier.isStatic(fIntermediaryClassBinding.getModifiers()))
491             return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_cannot_create_in_nested_nonstatic, JavaStatusContext.create(fIntermediaryClass));
492
493         pm.worked(startupTicks);
494         if (pm.isCanceled())
495             throw new OperationCanceledException();
496
497         if (fUpdateReferences) {
498             pm.setTaskName(RefactoringCoreMessages.IntroduceIndirectionRefactoring_checking_conditions + " " + RefactoringCoreMessages.IntroduceIndirectionRefactoring_looking_for_references); //$NON-NLS-1$
499
result.merge(updateReferences(new NoOverrideProgressMonitor(pm, referenceTicks)));
500             pm.setTaskName(RefactoringCoreMessages.IntroduceIndirectionRefactoring_checking_conditions);
501         } else {
502             // only update the declaration and/or a selected method invocation
503
if (fSelectionMethodInvocation != null) {
504                 fIntermediaryFirstParameterType= getExpressionType(fSelectionMethodInvocation);
505                 final IMember enclosing= getEnclosingInitialSelectionMember();
506                 // create an edit for this particular call
507
result.merge(updateMethodInvocation(fSelectionMethodInvocation, enclosing, getCachedCURewrite(fSelectionCompilationUnit)));
508
509                 if (!isRewriteKept(fSelectionCompilationUnit))
510                     createChangeAndDiscardRewrite(fSelectionCompilationUnit);
511
512                 // does call see the intermediary method?
513
// => increase visibility of the type of the intermediary method.
514
result.merge(adjustVisibility(fIntermediaryClass, enclosing.getDeclaringType(), new NoOverrideProgressMonitor(pm, 0)));
515             }
516             pm.worked(referenceTicks);
517         }
518
519         if (pm.isCanceled())
520             throw new OperationCanceledException();
521
522         if (fIntermediaryFirstParameterType == null)
523             fIntermediaryFirstParameterType= fTargetMethodBinding.getDeclaringClass();
524
525         // The target type and method may have changed - update them
526

527         IType actualTargetType= (IType) fIntermediaryFirstParameterType.getJavaElement();
528         if (!fTargetMethod.getDeclaringType().equals(actualTargetType)) {
529             IMethod actualTargetMethod= new MethodOverrideTester(actualTargetType, actualTargetType.newSupertypeHierarchy(null)).findOverriddenMethodInHierarchy(actualTargetType, fTargetMethod);
530             fTargetMethod= actualTargetMethod;
531             fTargetMethodBinding= findMethodBindingInHierarchy(fIntermediaryFirstParameterType, actualTargetMethod);
532             Assert.isNotNull(fTargetMethodBinding);
533         }
534
535         result.merge(checkCanCreateIntermediaryMethod());
536         createIntermediaryMethod();
537         pm.worked(creationTicks);
538
539         pm.setTaskName(RefactoringCoreMessages.IntroduceIndirectionRefactoring_checking_conditions + " " + RefactoringCoreMessages.IntroduceIndirectionRefactoring_adjusting_visibility); //$NON-NLS-1$
540
result.merge(updateTargetVisibility(new NoOverrideProgressMonitor(pm, 0)));
541         result.merge(updateIntermediaryVisibility(new NoOverrideProgressMonitor(pm, 0)));
542         pm.worked(visibilityTicks);
543         pm.setTaskName(RefactoringCoreMessages.IntroduceIndirectionRefactoring_checking_conditions);
544
545         createChangeAndDiscardRewrite(fIntermediaryClass.getCompilationUnit());
546
547         result.merge(Checks.validateModifiesFiles(getAllFilesToModify(), getValidationContext()));
548         pm.done();
549
550         return result;
551     }
552
553     private RefactoringStatus updateTargetVisibility(IProgressMonitor monitor) throws JavaModelException, CoreException {
554
555         RefactoringStatus result= new RefactoringStatus();
556
557         // Adjust the visibility of the method and of the referenced type. Note that
558
// the target method may not be in the target type; and in this case, the type
559
// of the target method does not need a visibility adjustment.
560

561         // This method is called after all other changes have been
562
// created. Changes induced by this method will be attached to those changes.
563

564         result.merge(adjustVisibility((IType) fIntermediaryFirstParameterType.getJavaElement(), fIntermediaryClass, monitor));
565         if (result.hasError())
566             return result; // binary
567

568         ModifierKeyword neededVisibility= getNeededVisibility(fTargetMethod, fIntermediaryClass);
569         if (neededVisibility != null) {
570
571             result.merge(adjustVisibility(fTargetMethod, neededVisibility, monitor));
572             if (result.hasError())
573                 return result; // binary
574

575             // Need to adjust the overridden methods of the target method.
576
ITypeHierarchy hierarchy= fTargetMethod.getDeclaringType().newTypeHierarchy(null);
577             MethodOverrideTester tester= new MethodOverrideTester(fTargetMethod.getDeclaringType(), hierarchy);
578             IType[] subtypes= hierarchy.getAllSubtypes(fTargetMethod.getDeclaringType());
579             for (int i= 0; i < subtypes.length; i++) {
580                 IMethod method= tester.findOverridingMethodInType(subtypes[i], fTargetMethod);
581                 if (method != null && method.exists()) {
582                     result.merge(adjustVisibility(method, neededVisibility, monitor));
583                     if (monitor.isCanceled())
584                         throw new OperationCanceledException();
585                     
586                     if (result.hasError())
587                         return result; // binary
588
}
589             }
590         }
591
592         return result;
593     }
594     
595     private RefactoringStatus updateIntermediaryVisibility(NoOverrideProgressMonitor monitor) throws JavaModelException {
596         return rewriteVisibility(fIntermediaryAdjustments, fRewrites, monitor);
597     }
598
599     private RefactoringStatus updateReferences(IProgressMonitor monitor) throws CoreException {
600
601         RefactoringStatus result= new RefactoringStatus();
602
603         monitor.beginTask("", 90); //$NON-NLS-1$
604

605         if (monitor.isCanceled())
606             throw new OperationCanceledException();
607
608         IMethod[] ripple= RippleMethodFinder2.getRelatedMethods(fTargetMethod, false, new NoOverrideProgressMonitor(monitor, 10), null);
609
610         if (monitor.isCanceled())
611             throw new OperationCanceledException();
612
613         SearchResultGroup[] references= Checks.excludeCompilationUnits(getReferences(ripple, new NoOverrideProgressMonitor(monitor, 10), result), result);
614
615         if (result.hasFatalError())
616             return result;
617
618         result.merge(Checks.checkCompileErrorsInAffectedFiles(references));
619
620         if (monitor.isCanceled())
621             throw new OperationCanceledException();
622
623         int ticksPerCU= references.length == 0 ? 0 : 70 / references.length;
624
625         for (int i= 0; i < references.length; i++) {
626             SearchResultGroup group= references[i];
627             SearchMatch[] searchResults= group.getSearchResults();
628             CompilationUnitRewrite currentCURewrite= getCachedCURewrite(group.getCompilationUnit());
629
630             for (int j= 0; j < searchResults.length; j++) {
631
632                 SearchMatch match= searchResults[j];
633                 if (match.isInsideDocComment())
634                     continue;
635
636                 IMember enclosingMember= (IMember) match.getElement();
637                 ASTNode target= getSelectedNode(group.getCompilationUnit(), currentCURewrite.getRoot(), match.getOffset(), match.getLength());
638
639                 if (target instanceof SuperMethodInvocation) {
640                     // Cannot retarget calls to super - add a warning
641
result.merge(createWarningAboutCall(enclosingMember, target, RefactoringCoreMessages.IntroduceIndirectionRefactoring_call_warning_super_keyword));
642                     continue;
643                 }
644
645                 Assert.isTrue(target instanceof MethodInvocation, "Element of call should be a MethodInvocation."); //$NON-NLS-1$
646

647                 MethodInvocation invocation= (MethodInvocation) target;
648                 ITypeBinding typeBinding= getExpressionType(invocation);
649
650                 if (fIntermediaryFirstParameterType == null) {
651                     // no highest type yet
652
fIntermediaryFirstParameterType= typeBinding.getTypeDeclaration();
653                 } else {
654                     // check if current type is higher
655
result.merge(findCommonParent(typeBinding.getTypeDeclaration()));
656                 }
657
658                 if (result.hasFatalError())
659                     return result;
660
661                 // create an edit for this particular call
662
result.merge(updateMethodInvocation(invocation, enclosingMember, currentCURewrite));
663
664                 // does call see the intermediary method?
665
// => increase visibility of the type of the intermediary method.
666
result.merge(adjustVisibility(fIntermediaryClass, enclosingMember.getDeclaringType(), new NoOverrideProgressMonitor(monitor, 0)));
667
668                 if (monitor.isCanceled())
669                     throw new OperationCanceledException();
670             }
671
672             if (!isRewriteKept(group.getCompilationUnit()))
673                 createChangeAndDiscardRewrite(group.getCompilationUnit());
674
675             monitor.worked(ticksPerCU);
676         }
677
678         monitor.done();
679         return result;
680     }
681
682     private RefactoringStatus findCommonParent(ITypeBinding typeBinding) throws JavaModelException {
683
684         RefactoringStatus status= new RefactoringStatus();
685
686         ITypeBinding highest= fIntermediaryFirstParameterType;
687         ITypeBinding current= typeBinding;
688
689         if (current.equals(highest) || Bindings.isSuperType(highest, current))
690             // current is the same as highest or highest is already a supertype of current in the same hierarchy => no change
691
return status;
692
693         // find lowest common supertype with the method
694
// search in bottom-up order
695
ITypeBinding[] currentAndSupers= getTypeAndAllSuperTypes(current);
696         ITypeBinding[] highestAndSupers= getTypeAndAllSuperTypes(highest);
697
698         ITypeBinding foundBinding= null;
699         for (int i1= 0; i1 < currentAndSupers.length; i1++) {
700             for (int i2= 0; i2 < highestAndSupers.length; i2++) {
701                 if (highestAndSupers[i2].isEqualTo(currentAndSupers[i1])
702                         && (Bindings.findMethodInHierarchy(highestAndSupers[i2], fTargetMethodBinding.getName(), fTargetMethodBinding.getParameterTypes()) != null)) {
703                     foundBinding= highestAndSupers[i2];
704                     break;
705                 }
706             }
707             if (foundBinding != null)
708                 break;
709         }
710
711         if (foundBinding != null) {
712             fIntermediaryFirstParameterType= foundBinding;
713         } else {
714             String JavaDoc type1= fIntermediaryFirstParameterType.getQualifiedName();
715             String JavaDoc type2= current.getQualifiedName();
716             status.addFatalError(Messages.format(RefactoringCoreMessages.IntroduceIndirectionRefactoring_open_hierarchy_error, new String JavaDoc[] { type1, type2 }));
717         }
718
719         return status;
720     }
721
722     // ******************** CHANGE CREATION ***********************
723

724     public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException {
725         final Map JavaDoc arguments= new HashMap JavaDoc();
726         String JavaDoc project= null;
727         IJavaProject javaProject= fTargetMethod.getJavaProject();
728         if (javaProject != null)
729             project= javaProject.getElementName();
730         int flags= JavaRefactoringDescriptor.JAR_MIGRATION | JavaRefactoringDescriptor.JAR_REFACTORING | RefactoringDescriptor.STRUCTURAL_CHANGE | RefactoringDescriptor.MULTI_CHANGE;
731         final IType declaring= fTargetMethod.getDeclaringType();
732         try {
733             if (declaring.isLocal() || declaring.isAnonymous())
734                 flags|= JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT;
735         } catch (JavaModelException exception) {
736             JavaPlugin.log(exception);
737         }
738         final String JavaDoc description= Messages.format(RefactoringCoreMessages.IntroduceIndirectionRefactoring_descriptor_description_short, fTargetMethod.getElementName());
739         final String JavaDoc header= Messages.format(RefactoringCoreMessages.IntroduceIndirectionRefactoring_descriptor_description, new String JavaDoc[] { JavaElementLabels.getTextLabel(fTargetMethod, JavaElementLabels.ALL_FULLY_QUALIFIED), JavaElementLabels.getTextLabel(declaring, JavaElementLabels.ALL_FULLY_QUALIFIED)});
740         final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header);
741         comment.addSetting(Messages.format(RefactoringCoreMessages.IntroduceIndirectionRefactoring_original_pattern, JavaElementLabels.getTextLabel(fTargetMethod, JavaElementLabels.ALL_FULLY_QUALIFIED)));
742         comment.addSetting(Messages.format(RefactoringCoreMessages.IntroduceIndirectionRefactoring_method_pattern, fIntermediaryMethodName));
743         comment.addSetting(Messages.format(RefactoringCoreMessages.IntroduceIndirectionRefactoring_declaring_pattern, JavaElementLabels.getTextLabel(fIntermediaryClass, JavaElementLabels.ALL_FULLY_QUALIFIED)));
744         if (fUpdateReferences)
745             comment.addSetting(RefactoringCoreMessages.JavaRefactoringDescriptor_update_references);
746         final JDTRefactoringDescriptor descriptor= new JDTRefactoringDescriptor(IJavaRefactorings.INTRODUCE_INDIRECTION, project, description, comment.asString(), arguments, flags);
747         arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_INPUT, descriptor.elementToHandle(fTargetMethod));
748         arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_NAME, fIntermediaryMethodName);
749         arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_ELEMENT + 1, descriptor.elementToHandle(fIntermediaryClass));
750         arguments.put(JDTRefactoringDescriptor.ATTRIBUTE_REFERENCES, Boolean.valueOf(fUpdateReferences).toString());
751         return new DynamicValidationRefactoringChange(descriptor, RefactoringCoreMessages.IntroduceIndirectionRefactoring_introduce_indirection, fTextChangeManager.getAllChanges());
752     }
753
754     // ******************* CREATE INTERMEDIARY **********************
755

756     /**
757      * Checks whether the target method can be created. Note that this
758      * can only be done after fDelegateParameterType has been initialized.
759      * @return resulting status
760      * @throws JavaModelException
761      */

762     private RefactoringStatus checkCanCreateIntermediaryMethod() throws JavaModelException {
763         // check if method already exists:
764
List JavaDoc parameterBindings= new ArrayList JavaDoc();
765         if (!isStaticTarget())
766             parameterBindings.add(fIntermediaryFirstParameterType);
767         parameterBindings.addAll(Arrays.asList(fTargetMethodBinding.getParameterTypes()));
768         return Checks.checkMethodInType(fIntermediaryClassBinding, fIntermediaryMethodName, (ITypeBinding[]) parameterBindings.toArray(new ITypeBinding[parameterBindings.size()]));
769     }
770
771     private void createIntermediaryMethod() throws CoreException {
772
773         CompilationUnitRewrite imRewrite= getCachedCURewrite(fIntermediaryClass.getCompilationUnit());
774         AST ast= imRewrite.getAST();
775         MethodDeclaration intermediary= ast.newMethodDeclaration();
776
777         // Name
778
intermediary.setName(ast.newSimpleName(fIntermediaryMethodName));
779
780         // Flags
781
List JavaDoc modifiers= intermediary.modifiers();
782         modifiers.add(imRewrite.getAST().newModifier(ModifierKeyword.PUBLIC_KEYWORD));
783         modifiers.add(imRewrite.getAST().newModifier(ModifierKeyword.STATIC_KEYWORD));
784
785         // Parameters
786
String JavaDoc targetParameterName= StubUtility.suggestArgumentName(getProject(), fIntermediaryFirstParameterType.getName(), fTargetMethod.getParameterNames());
787
788         if (!isStaticTarget()) {
789             // Add first param
790
SingleVariableDeclaration parameter= imRewrite.getAST().newSingleVariableDeclaration();
791             Type t= imRewrite.getImportRewrite().addImport(fIntermediaryFirstParameterType, imRewrite.getAST());
792             if (fIntermediaryFirstParameterType.isGenericType()) {
793                 ParameterizedType parameterized= imRewrite.getAST().newParameterizedType(t);
794                 ITypeBinding[] typeParameters= fIntermediaryFirstParameterType.getTypeParameters();
795                 for (int i= 0; i < typeParameters.length; i++)
796                     parameterized.typeArguments().add(imRewrite.getImportRewrite().addImport(typeParameters[i], imRewrite.getAST()));
797                 t= parameterized;
798             }
799             parameter.setType(t);
800             parameter.setName(imRewrite.getAST().newSimpleName(targetParameterName));
801             intermediary.parameters().add(parameter);
802         }
803         // Add other params
804
copyArguments(intermediary, imRewrite);
805
806         // Add type parameters of declaring class (and enclosing classes)
807
if (!isStaticTarget() && fIntermediaryFirstParameterType.isGenericType())
808             addTypeParameters(imRewrite, intermediary.typeParameters(), fIntermediaryFirstParameterType);
809
810         // Add type params of method
811
copyTypeParameters(intermediary, imRewrite);
812
813         // Return type
814
intermediary.setReturnType2(imRewrite.getImportRewrite().addImport(fTargetMethodBinding.getReturnType(), ast));
815
816         // Exceptions
817
copyExceptions(intermediary, imRewrite);
818
819         // Body
820
MethodInvocation invocation= imRewrite.getAST().newMethodInvocation();
821         invocation.setName(imRewrite.getAST().newSimpleName(fTargetMethod.getElementName()));
822         if (isStaticTarget()) {
823             Type type= imRewrite.getImportRewrite().addImport(fTargetMethodBinding.getDeclaringClass(), ast);
824             invocation.setExpression(ASTNodeFactory.newName(ast, ASTNodes.asString(type)));
825         } else {
826             invocation.setExpression(imRewrite.getAST().newSimpleName(targetParameterName));
827         }
828         copyInvocationParameters(invocation, ast);
829         Statement call= encapsulateInvocation(intermediary, invocation);
830
831         final Block body= imRewrite.getAST().newBlock();
832         body.statements().add(call);
833         intermediary.setBody(body);
834
835         // method comment
836
ICompilationUnit targetCU= imRewrite.getCu();
837         if (StubUtility.doAddComments(targetCU.getJavaProject())) {
838             String JavaDoc comment= CodeGeneration.getMethodComment(targetCU, getIntermediaryClassName(), intermediary, null, StubUtility.getLineDelimiterUsed(targetCU));
839             if (comment != null) {
840                 Javadoc javadoc= (Javadoc) imRewrite.getASTRewrite().createStringPlaceholder(comment, ASTNode.JAVADOC);
841                 intermediary.setJavadoc(javadoc);
842             }
843         }
844
845         // Add the completed method to the intermediary type:
846

847         // Intermediary class is non-anonymous
848
AbstractTypeDeclaration type= (AbstractTypeDeclaration) typeToDeclaration(fIntermediaryClass, imRewrite.getRoot());
849         ChildListPropertyDescriptor typeBodyDeclarationsProperty= typeToBodyDeclarationProperty(fIntermediaryClass, imRewrite.getRoot());
850
851         ListRewrite bodyDeclarationsListRewrite= imRewrite.getASTRewrite().getListRewrite(type, typeBodyDeclarationsProperty);
852         bodyDeclarationsListRewrite.insertAt(intermediary, ASTNodes.getInsertionIndex(intermediary, type.bodyDeclarations()), imRewrite
853                 .createGroupDescription(RefactoringCoreMessages.IntroduceIndirectionRefactoring_group_description_create_new_method));
854     }
855
856     private void addTypeParameters(CompilationUnitRewrite imRewrite, List JavaDoc newTypeParameters, ITypeBinding parent) {
857
858         ITypeBinding enclosing= parent.getDeclaringClass();
859         if (enclosing != null)
860             addTypeParameters(imRewrite, newTypeParameters, enclosing);
861
862         ITypeBinding[] typeParameters= parent.getTypeParameters();
863         for (int i= 0; i < typeParameters.length; i++) {
864             TypeParameter ntp= imRewrite.getAST().newTypeParameter();
865             ntp.setName(imRewrite.getAST().newSimpleName(typeParameters[i].getName()));
866             ITypeBinding[] bounds= typeParameters[i].getTypeBounds();
867             for (int j= 0; j < bounds.length; j++)
868                 if (!"java.lang.Object".equals(bounds[j].getQualifiedName())) //$NON-NLS-1$
869
ntp.typeBounds().add(imRewrite.getImportRewrite().addImport(bounds[j], imRewrite.getAST()));
870             newTypeParameters.add(ntp);
871         }
872     }
873
874     private Statement encapsulateInvocation(MethodDeclaration declaration, MethodInvocation invocation) {
875         final Type type= declaration.getReturnType2();
876
877         if (type == null || (type instanceof PrimitiveType && PrimitiveType.VOID.equals( ((PrimitiveType) type).getPrimitiveTypeCode())))
878             return invocation.getAST().newExpressionStatement(invocation);
879
880         ReturnStatement statement= invocation.getAST().newReturnStatement();
881         statement.setExpression(invocation);
882         return statement;
883     }
884
885     private void copyInvocationParameters(MethodInvocation invocation, AST ast) throws JavaModelException {
886         String JavaDoc[] names= fTargetMethod.getParameterNames();
887         for (int i= 0; i < names.length; i++)
888             invocation.arguments().add(ast.newSimpleName(names[i]));
889     }
890
891     private void copyArguments(MethodDeclaration intermediary, CompilationUnitRewrite rew) throws JavaModelException {
892         String JavaDoc[] names= fTargetMethod.getParameterNames();
893         ITypeBinding[] types= fTargetMethodBinding.getParameterTypes();
894         for (int i= 0; i < names.length; i++) {
895             ITypeBinding typeBinding= types[i];
896             SingleVariableDeclaration newElement= rew.getAST().newSingleVariableDeclaration();
897             newElement.setName(rew.getAST().newSimpleName(names[i]));
898
899             if (i == (names.length - 1) && fTargetMethodBinding.isVarargs()) {
900                 newElement.setVarargs(true);
901                 if (typeBinding.isArray())
902                     typeBinding= typeBinding.getComponentType();
903             }
904
905             newElement.setType(rew.getImportRewrite().addImport(typeBinding, rew.getAST()));
906             intermediary.parameters().add(newElement);
907         }
908     }
909
910     private void copyTypeParameters(MethodDeclaration intermediary, CompilationUnitRewrite rew) {
911         ITypeBinding[] typeParameters= fTargetMethodBinding.getTypeParameters();
912         for (int i= 0; i < typeParameters.length; i++) {
913             ITypeBinding current= typeParameters[i];
914
915             TypeParameter parameter= rew.getAST().newTypeParameter();
916             parameter.setName(rew.getAST().newSimpleName(current.getName()));
917             ITypeBinding[] bounds= current.getTypeBounds();
918             for (int j= 0; j < bounds.length; j++)
919                 if (!"java.lang.Object".equals(bounds[j].getQualifiedName())) //$NON-NLS-1$
920
parameter.typeBounds().add(rew.getImportRewrite().addImport(bounds[j], rew.getAST()));
921
922             intermediary.typeParameters().add(parameter);
923         }
924     }
925
926     private void copyExceptions(MethodDeclaration intermediary, CompilationUnitRewrite imRewrite) {
927         ITypeBinding[] exceptionTypes= fTargetMethodBinding.getExceptionTypes();
928         for (int i= 0; i < exceptionTypes.length; i++) {
929             final String JavaDoc qualifiedName= imRewrite.getImportRewrite().addImport(exceptionTypes[i]);
930             intermediary.thrownExceptions().add(ASTNodeFactory.newName(imRewrite.getAST(), qualifiedName));
931         }
932     }
933
934     // ******************* UPDATE CALLS **********************
935

936     private RefactoringStatus updateMethodInvocation(MethodInvocation originalInvocation, IMember enclosing, CompilationUnitRewrite unitRewriter) throws JavaModelException {
937
938         RefactoringStatus status= new RefactoringStatus();
939
940         // If the method invocation utilizes type arguments, skip this
941
// call as the new target method may have additional parameters
942
if (originalInvocation.typeArguments().size() > 0)
943             return createWarningAboutCall(enclosing, originalInvocation, RefactoringCoreMessages.IntroduceIndirectionRefactoring_call_warning_type_arguments);
944
945         MethodInvocation newInvocation= unitRewriter.getAST().newMethodInvocation();
946         List JavaDoc newInvocationArgs= newInvocation.arguments();
947         List JavaDoc originalInvocationArgs= originalInvocation.arguments();
948
949         // static call => always use a qualifier
950
String JavaDoc qualifier= unitRewriter.getImportRewrite().addImport(fIntermediaryClassBinding);
951         newInvocation.setExpression(ASTNodeFactory.newName(unitRewriter.getAST(), qualifier));
952         newInvocation.setName(unitRewriter.getAST().newSimpleName(getIntermediaryMethodName()));
953
954         final Expression expression= originalInvocation.getExpression();
955
956         if (!isStaticTarget()) {
957             // Add the expression as the first parameter
958
if (expression == null) {
959                 // There is no expression for this call. Use a (possibly qualified) "this" expression.
960
ThisExpression expr= unitRewriter.getAST().newThisExpression();
961                 RefactoringStatus qualifierStatus= qualifyThisExpression(expr, originalInvocation, enclosing, unitRewriter);
962                 status.merge(qualifierStatus);
963                 if (qualifierStatus.hasEntries())
964                     // warning means don't include this invocation
965
return status;
966                 newInvocationArgs.add(expr);
967             } else {
968                 ASTNode expressionAsParam= unitRewriter.getASTRewrite().createMoveTarget(expression);
969                 newInvocationArgs.add(expressionAsParam);
970             }
971         } else {
972             if (expression != null) {
973                 // Check if expression is the class name. If not, there may
974
// be side effects (e.g. inside methods) -> don't update
975
if (! (expression instanceof Name) || ASTNodes.getTypeBinding((Name) expression) == null)
976                     return createWarningAboutCall(enclosing, originalInvocation, RefactoringCoreMessages.IntroduceIndirectionRefactoring_call_warning_static_expression_access);
977             }
978         }
979
980         for (int i= 0; i < originalInvocationArgs.size(); i++) {
981             Expression originalInvocationArg= (Expression) originalInvocationArgs.get(i);
982             ASTNode movedArg= unitRewriter.getASTRewrite().createMoveTarget(originalInvocationArg);
983             newInvocationArgs.add(movedArg);
984         }
985
986         unitRewriter.getASTRewrite().replace(originalInvocation, newInvocation,
987                 unitRewriter.createGroupDescription(RefactoringCoreMessages.IntroduceIndirectionRefactoring_group_description_replace_call));
988
989         return status;
990     }
991
992     /**
993      * Attempts to qualify a "this" expression for a method invocation with an appropriate qualifier.
994      * The invoked method is analyzed according to the following specs:
995      *
996      * 'this' must be qualified iff method is declared in an enclosing type or a supertype of an enclosing type
997      *
998      * 1) The method is declared somewhere outside of the cu of the invocation
999      * 1a) inside a supertype of the current type
1000     * 1b) inside a supertype of an enclosing type
1001     * 2) The method is declared inside of the cu of the invocation
1002     * 2a) inside the type of the invocation
1003     * 2b) outside the type of the invocation
1004     *
1005     * In case of 1a) and 2b), qualify with the enclosing type.
1006     * @param expr
1007     * @param originalInvocation
1008     * @param enclosing
1009     * @param unitRewriter
1010     * @return resulting status
1011     *
1012     */

1013    private RefactoringStatus qualifyThisExpression(ThisExpression expr, MethodInvocation originalInvocation, IMember enclosing, CompilationUnitRewrite unitRewriter) {
1014
1015        RefactoringStatus status= new RefactoringStatus();
1016
1017        IMethodBinding methodBinding= originalInvocation.resolveMethodBinding();
1018        MethodDeclaration methodDeclaration= (MethodDeclaration) ASTNodes.findDeclaration(methodBinding, originalInvocation.getRoot());
1019
1020        ITypeBinding currentTypeBinding= null;
1021        if (methodDeclaration != null) {
1022            // Case 1) : Declaring class is inside this cu => use its name if it's declared in an enclosing type
1023
if (ASTNodes.isParent(originalInvocation, methodDeclaration.getParent()))
1024                currentTypeBinding= methodBinding.getDeclaringClass();
1025            else
1026                currentTypeBinding= ASTNodes.getEnclosingType(originalInvocation);
1027        } else {
1028            // Case 2) : Declaring class is outside of this cu => find subclass in this cu
1029
ASTNode currentTypeDeclaration= getEnclosingTypeDeclaration(originalInvocation);
1030            currentTypeBinding= ASTNodes.getEnclosingType(currentTypeDeclaration);
1031            while (currentTypeDeclaration != null && (Bindings.findMethodInHierarchy(currentTypeBinding, methodBinding.getName(), methodBinding.getParameterTypes()) == null)) {
1032                currentTypeDeclaration= getEnclosingTypeDeclaration(currentTypeDeclaration.getParent());
1033                currentTypeBinding= ASTNodes.getEnclosingType(currentTypeDeclaration);
1034            }
1035        }
1036
1037        if (currentTypeBinding == null) {
1038            status.merge(createWarningAboutCall(enclosing, originalInvocation, RefactoringCoreMessages.IntroduceIndirectionRefactoring_call_warning_declaring_type_not_found));
1039            return status;
1040        }
1041
1042        ITypeBinding typeOfCall= ASTNodes.getEnclosingType(originalInvocation);
1043        if (!typeOfCall.equals(currentTypeBinding)) {
1044            if (currentTypeBinding.isAnonymous()) {
1045                // Cannot qualify, see bug 115277
1046
status.merge(createWarningAboutCall(enclosing, originalInvocation, RefactoringCoreMessages.IntroduceIndirectionRefactoring_call_warning_anonymous_cannot_qualify));
1047            } else {
1048                expr.setQualifier(unitRewriter.getAST().newSimpleName(currentTypeBinding.getName()));
1049            }
1050        } else {
1051            // do not qualify, only use "this.".
1052
}
1053
1054        return status;
1055    }
1056
1057    // ********* SMALL HELPERS ********************
1058

1059    /*
1060     * Helper method for finding an IMethod inside a binding hierarchy
1061     */

1062    private IMethodBinding findMethodBindingInHierarchy(ITypeBinding currentTypeBinding, IMethod methodDeclaration) {
1063        IMethodBinding[] bindings= currentTypeBinding.getDeclaredMethods();
1064        for (int i= 0; i < bindings.length; i++)
1065            if (methodDeclaration.equals(bindings[i].getJavaElement()))
1066                return bindings[i];
1067
1068        ITypeBinding superClass= currentTypeBinding.getSuperclass();
1069        if (superClass != null) {
1070            IMethodBinding b= findMethodBindingInHierarchy(superClass, methodDeclaration);
1071            if (b != null)
1072                return b;
1073        }
1074        ITypeBinding[] interfaces= currentTypeBinding.getInterfaces();
1075        for (int i= 0; i < interfaces.length; i++) {
1076            IMethodBinding b= findMethodBindingInHierarchy(interfaces[i], methodDeclaration);
1077            if (b != null)
1078                return b;
1079        }
1080        return null;
1081    }
1082
1083    /*
1084     * Helper method for retrieving a *bottom-up* list of super type bindings
1085     */

1086    private ITypeBinding[] getTypeAndAllSuperTypes(ITypeBinding type) {
1087        List JavaDoc result= new ArrayList JavaDoc();
1088        collectSuperTypes(type, result);
1089        return (ITypeBinding[]) result.toArray(new ITypeBinding[result.size()]);
1090    }
1091
1092    private void collectSuperTypes(ITypeBinding curr, List JavaDoc list) {
1093        if (list.add(curr.getTypeDeclaration())) {
1094            ITypeBinding[] interfaces= curr.getInterfaces();
1095            for (int i= 0; i < interfaces.length; i++) {
1096                collectSuperTypes(interfaces[i], list);
1097            }
1098            ITypeBinding superClass= curr.getSuperclass();
1099            if (superClass != null) {
1100                collectSuperTypes(superClass, list);
1101            }
1102        }
1103    }
1104
1105    private CompilationUnitRewrite getCachedCURewrite(ICompilationUnit unit) {
1106        CompilationUnitRewrite rewrite= (CompilationUnitRewrite) fRewrites.get(unit);
1107        if (rewrite == null) {
1108            rewrite= new CompilationUnitRewrite(unit);
1109            fRewrites.put(unit, rewrite);
1110        }
1111        return rewrite;
1112    }
1113
1114    private boolean isRewriteKept(ICompilationUnit compilationUnit) {
1115        return fIntermediaryClass.getCompilationUnit().equals(compilationUnit);
1116    }
1117
1118    private void createChangeAndDiscardRewrite(ICompilationUnit compilationUnit) throws CoreException {
1119        CompilationUnitRewrite rewrite= (CompilationUnitRewrite) fRewrites.get(compilationUnit);
1120        if (rewrite != null) {
1121            fTextChangeManager.manage(compilationUnit, rewrite.createChange());
1122            fRewrites.remove(compilationUnit);
1123        }
1124    }
1125
1126    private SearchResultGroup[] getReferences(IMethod[] methods, IProgressMonitor pm, RefactoringStatus status) throws CoreException {
1127        SearchPattern pattern= RefactoringSearchEngine.createOrPattern(methods, IJavaSearchConstants.REFERENCES);
1128        IJavaSearchScope scope= RefactoringScopeFactory.create(fIntermediaryClass, false);
1129        return RefactoringSearchEngine.search(pattern, scope, pm, status);
1130    }
1131
1132    private ITypeBinding typeToBinding(IType type, CompilationUnit root) throws JavaModelException {
1133        ASTNode typeNode= typeToDeclaration(type, root);
1134        if (type.isAnonymous()) {
1135            return ((AnonymousClassDeclaration) typeNode).resolveBinding();
1136        } else {
1137            return ((AbstractTypeDeclaration) typeNode).resolveBinding();
1138        }
1139    }
1140
1141    private ASTNode typeToDeclaration(IType type, CompilationUnit root) throws JavaModelException {
1142        Name intermediateName= (Name) NodeFinder.perform(root, type.getNameRange());
1143        if (type.isAnonymous()) {
1144            return ASTNodes.getParent(intermediateName, AnonymousClassDeclaration.class);
1145        } else {
1146            return ASTNodes.getParent(intermediateName, AbstractTypeDeclaration.class);
1147        }
1148    }
1149
1150    private ASTNode getEnclosingTypeDeclaration(ASTNode node) {
1151        while (node != null) {
1152            if (node instanceof AbstractTypeDeclaration) {
1153                return node;
1154            } else if (node instanceof AnonymousClassDeclaration) {
1155                return node;
1156            }
1157            node= node.getParent();
1158        }
1159        return null;
1160    }
1161
1162    private ChildListPropertyDescriptor typeToBodyDeclarationProperty(IType type, CompilationUnit root) throws JavaModelException {
1163        ASTNode typeDeclaration= typeToDeclaration(type, root);
1164        if (typeDeclaration instanceof AbstractTypeDeclaration)
1165            return ((AbstractTypeDeclaration) typeDeclaration).getBodyDeclarationsProperty();
1166        else if (typeDeclaration instanceof AnonymousClassDeclaration)
1167            return AnonymousClassDeclaration.BODY_DECLARATIONS_PROPERTY;
1168
1169        Assert.isTrue(false);
1170        return null;
1171    }
1172
1173    private RefactoringStatus createWarningAboutCall(IMember enclosing, ASTNode concreteNode, String JavaDoc message) {
1174        String JavaDoc name= JavaElementLabels.getElementLabel(enclosing, JavaElementLabels.ALL_DEFAULT);
1175        String JavaDoc container= JavaElementLabels.getElementLabel(enclosing.getDeclaringType(), JavaElementLabels.ALL_FULLY_QUALIFIED);
1176        return RefactoringStatus.createWarningStatus(Messages.format(message, new String JavaDoc[] { name, container }), JavaStatusContext.create(enclosing.getCompilationUnit(), concreteNode));
1177    }
1178
1179    private ITypeBinding getExpressionType(MethodInvocation invocation) {
1180        Expression expression= invocation.getExpression();
1181        ITypeBinding typeBinding= null;
1182        if (expression == null) {
1183            typeBinding= invocation.resolveMethodBinding().getDeclaringClass();
1184        } else {
1185            typeBinding= expression.resolveTypeBinding();
1186        }
1187
1188        Assert.isNotNull(typeBinding, "Type binding of target expression may not be null"); //$NON-NLS-1$
1189
return typeBinding;
1190    }
1191
1192    private IFile[] getAllFilesToModify() {
1193        List JavaDoc cus= new ArrayList JavaDoc();
1194        cus.addAll(Arrays.asList(fTextChangeManager.getAllCompilationUnits()));
1195        return ResourceUtil.getFiles((ICompilationUnit[]) cus.toArray(new ICompilationUnit[cus.size()]));
1196    }
1197
1198    private boolean isStaticTarget() throws JavaModelException {
1199        return Flags.isStatic(fTargetMethod.getFlags());
1200    }
1201
1202    private IMember getEnclosingInitialSelectionMember() throws JavaModelException {
1203        return (IMember) fSelectionCompilationUnit.getElementAt(fSelectionStart);
1204    }
1205
1206    private static ASTNode getSelectedNode(ICompilationUnit unit, CompilationUnit root, int offset, int length) {
1207        ASTNode node= null;
1208        try {
1209            if (unit != null)
1210                node= checkNode(NodeFinder.perform(root, offset, length, unit));
1211            else
1212                node= checkNode(NodeFinder.perform(root, offset, length));
1213        } catch (JavaModelException e) {
1214            // Do nothing
1215
}
1216        if (node != null)
1217            return node;
1218        return checkNode(NodeFinder.perform(root, offset, length));
1219    }
1220
1221    private static ASTNode checkNode(ASTNode node) {
1222        if (node == null)
1223            return null;
1224        if (node.getNodeType() == ASTNode.SIMPLE_NAME) {
1225            node= node.getParent();
1226        } else if (node.getNodeType() == ASTNode.EXPRESSION_STATEMENT) {
1227            node= ((ExpressionStatement) node).getExpression();
1228        }
1229        switch (node.getNodeType()) {
1230            case ASTNode.METHOD_INVOCATION:
1231            case ASTNode.METHOD_DECLARATION:
1232            case ASTNode.SUPER_METHOD_INVOCATION:
1233                return node;
1234        }
1235        return null;
1236    }
1237
1238    // ***************** VISIBILITY ********************
1239

1240    private ModifierKeyword getNeededVisibility(IMember whoToAdjust, IMember fromWhereToLook) throws JavaModelException {
1241        return fAdjustor.getVisibilityThreshold(fromWhereToLook, whoToAdjust, new NullProgressMonitor());
1242    }
1243
1244    private RefactoringStatus adjustVisibility(IMember whoToAdjust, IMember fromWhereToLook, IProgressMonitor monitor) throws CoreException {
1245        return adjustVisibility(whoToAdjust, getNeededVisibility(whoToAdjust, fromWhereToLook), true, monitor);
1246    }
1247    
1248    private RefactoringStatus adjustVisibility(IMember whoToAdjust, ModifierKeyword neededVisibility, IProgressMonitor monitor) throws CoreException {
1249        return adjustVisibility(whoToAdjust, neededVisibility, false, monitor);
1250    }
1251    
1252    private RefactoringStatus adjustVisibility(IMember whoToAdjust, ModifierKeyword neededVisibility, boolean alsoIncreaseEnclosing, IProgressMonitor monitor) throws CoreException {
1253        
1254        Map JavaDoc adjustments;
1255        if (isRewriteKept(whoToAdjust.getCompilationUnit()))
1256            adjustments= fIntermediaryAdjustments;
1257        else
1258            adjustments= new HashMap JavaDoc();
1259        
1260        int existingAdjustments= adjustments.size();
1261        addAdjustment(whoToAdjust, neededVisibility, adjustments);
1262
1263        if (alsoIncreaseEnclosing)
1264            while (whoToAdjust.getDeclaringType() != null) {
1265                whoToAdjust= whoToAdjust.getDeclaringType();
1266                addAdjustment(whoToAdjust, neededVisibility, adjustments);
1267            }
1268
1269        boolean hasNewAdjustments= (adjustments.size() - existingAdjustments) > 0;
1270        if (hasNewAdjustments && ( (whoToAdjust.isReadOnly() || whoToAdjust.isBinary())))
1271            return RefactoringStatus.createErrorStatus(Messages.format(RefactoringCoreMessages.IntroduceIndirectionRefactoring_cannot_update_binary_target_visibility, new String JavaDoc[] { JavaElementLabels
1272                    .getElementLabel(whoToAdjust, JavaElementLabels.ALL_DEFAULT) }), JavaStatusContext.create(whoToAdjust));
1273
1274        RefactoringStatus status= new RefactoringStatus();
1275        
1276        // Don't create a rewrite if it is not necessary
1277
if (!hasNewAdjustments)
1278            return status;
1279
1280        try {
1281            monitor.beginTask(RefactoringCoreMessages.MemberVisibilityAdjustor_adjusting, 2);
1282            Map JavaDoc rewrites;
1283            if (!isRewriteKept(whoToAdjust.getCompilationUnit())) {
1284                CompilationUnitRewrite rewrite= new CompilationUnitRewrite(whoToAdjust.getCompilationUnit());
1285                rewrite.setResolveBindings(false);
1286                rewrites= new HashMap JavaDoc();
1287                rewrites.put(whoToAdjust.getCompilationUnit(), rewrite);
1288                status.merge(rewriteVisibility(adjustments, rewrites, new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL)));
1289                rewrite.attachChange((CompilationUnitChange) fTextChangeManager.get(whoToAdjust.getCompilationUnit()), true, new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL));
1290            }
1291        } finally {
1292            monitor.done();
1293        }
1294        return status;
1295    }
1296
1297    private RefactoringStatus rewriteVisibility(Map JavaDoc adjustments, Map JavaDoc rewrites, IProgressMonitor monitor) throws JavaModelException {
1298        RefactoringStatus status= new RefactoringStatus();
1299        fAdjustor.setRewrites(rewrites);
1300        fAdjustor.setAdjustments(adjustments);
1301        fAdjustor.setStatus(status);
1302        fAdjustor.rewriteVisibility(monitor);
1303        return status;
1304    }
1305
1306    private void addAdjustment(IMember whoToAdjust, ModifierKeyword neededVisibility, Map JavaDoc adjustments) throws JavaModelException {
1307        ModifierKeyword currentVisibility= ModifierKeyword.fromFlagValue(JdtFlags.getVisibilityCode(whoToAdjust));
1308        if (MemberVisibilityAdjustor.hasLowerVisibility(currentVisibility, neededVisibility)
1309                && MemberVisibilityAdjustor.needsVisibilityAdjustments(whoToAdjust, neededVisibility, adjustments))
1310            adjustments.put(whoToAdjust, new MemberVisibilityAdjustor.IncomingMemberVisibilityAdjustment(whoToAdjust, neededVisibility,
1311                    RefactoringStatus.createWarningStatus(Messages.format(MemberVisibilityAdjustor.getMessage(whoToAdjust), new String JavaDoc[] {
1312                            MemberVisibilityAdjustor.getLabel(whoToAdjust), MemberVisibilityAdjustor.getLabel(neededVisibility) }), JavaStatusContext
1313                            .create(whoToAdjust))));
1314    }
1315
1316    public RefactoringStatus initialize(final RefactoringArguments arguments) {
1317        if (arguments instanceof JavaRefactoringArguments) {
1318            final JavaRefactoringArguments extended= (JavaRefactoringArguments) arguments;
1319            String JavaDoc handle= extended.getAttribute(JDTRefactoringDescriptor.ATTRIBUTE_INPUT);
1320            if (handle != null) {
1321                final IJavaElement element= JDTRefactoringDescriptor.handleToElement(extended.getProject(), handle, false);
1322                if (element == null || !element.exists() || element.getElementType() != IJavaElement.METHOD)
1323                    return createInputFatalStatus(element, IJavaRefactorings.INTRODUCE_INDIRECTION);
1324                else
1325                    fTargetMethod= (IMethod) element;
1326            } else
1327                return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_INPUT));
1328            handle= extended.getAttribute(JDTRefactoringDescriptor.ATTRIBUTE_ELEMENT + 1);
1329            if (handle != null) {
1330                final IJavaElement element= JDTRefactoringDescriptor.handleToElement(extended.getProject(), handle, false);
1331                if (element == null || !element.exists() || element.getElementType() != IJavaElement.TYPE)
1332                    return createInputFatalStatus(element, IJavaRefactorings.INTRODUCE_INDIRECTION);
1333                else
1334                    fIntermediaryClass= (IType) element;
1335            } else
1336                return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_ELEMENT + 1));
1337            final String JavaDoc references= extended.getAttribute(JDTRefactoringDescriptor.ATTRIBUTE_REFERENCES);
1338            if (references != null) {
1339                fUpdateReferences= Boolean.valueOf(references).booleanValue();
1340            } else
1341                return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_REFERENCES));
1342            final String JavaDoc name= extended.getAttribute(JDTRefactoringDescriptor.ATTRIBUTE_NAME);
1343            if (name != null && !"".equals(name)) //$NON-NLS-1$
1344
return setIntermediaryMethodName(name);
1345            else
1346                return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JDTRefactoringDescriptor.ATTRIBUTE_NAME));
1347        } else
1348            return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.InitializableRefactoring_inacceptable_arguments);
1349    }
1350}
1351
Popular Tags