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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package org.eclipse.jdt.internal.corext.refactoring.code;
13 import java.lang.reflect.Modifier;
14 import java.util.ArrayList;
15 import java.util.Arrays;
16 import java.util.HashMap;
17 import java.util.Iterator;
18 import java.util.List;
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;
28 import org.eclipse.core.resources.IFile;
30 import org.eclipse.ltk.core.refactoring.Change;
31 import org.eclipse.ltk.core.refactoring.Refactoring;
32 import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
33 import org.eclipse.ltk.core.refactoring.RefactoringStatus;
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.IExtendedModifier;
58 import org.eclipse.jdt.core.dom.IMethodBinding;
59 import org.eclipse.jdt.core.dom.ITypeBinding;
60 import org.eclipse.jdt.core.dom.Javadoc;
61 import org.eclipse.jdt.core.dom.MethodDeclaration;
62 import org.eclipse.jdt.core.dom.MethodInvocation;
63 import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
64 import org.eclipse.jdt.core.dom.Name;
65 import org.eclipse.jdt.core.dom.NodeFinder;
66 import org.eclipse.jdt.core.dom.ParameterizedType;
67 import org.eclipse.jdt.core.dom.PrimitiveType;
68 import org.eclipse.jdt.core.dom.ReturnStatement;
69 import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
70 import org.eclipse.jdt.core.dom.Statement;
71 import org.eclipse.jdt.core.dom.SuperMethodInvocation;
72 import org.eclipse.jdt.core.dom.ThisExpression;
73 import org.eclipse.jdt.core.dom.Type;
74 import org.eclipse.jdt.core.dom.TypeParameter;
75 import org.eclipse.jdt.core.dom.rewrite.ImportRewrite.ImportRewriteContext;
76 import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
77 import org.eclipse.jdt.core.refactoring.CompilationUnitChange;
78 import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
79 import org.eclipse.jdt.core.refactoring.descriptors.IntroduceIndirectionDescriptor;
80 import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor;
81 import org.eclipse.jdt.core.search.IJavaSearchConstants;
82 import org.eclipse.jdt.core.search.IJavaSearchScope;
83 import org.eclipse.jdt.core.search.SearchMatch;
84 import org.eclipse.jdt.core.search.SearchPattern;
86 import org.eclipse.jdt.internal.core.refactoring.descriptors.RefactoringSignatureDescriptorFactory;
87 import org.eclipse.jdt.internal.corext.codemanipulation.ContextSensitiveImportRewriteContext;
88 import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
89 import org.eclipse.jdt.internal.corext.dom.ASTNodeFactory;
90 import org.eclipse.jdt.internal.corext.dom.ASTNodes;
91 import org.eclipse.jdt.internal.corext.dom.Bindings;
92 import org.eclipse.jdt.internal.corext.refactoring.Checks;
93 import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
94 import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringArguments;
95 import org.eclipse.jdt.internal.corext.refactoring.JavaRefactoringDescriptorUtil;
96 import org.eclipse.jdt.internal.corext.refactoring.RefactoringAvailabilityTester;
97 import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
98 import org.eclipse.jdt.internal.corext.refactoring.RefactoringScopeFactory;
99 import org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine;
100 import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup;
101 import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext;
102 import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange;
103 import org.eclipse.jdt.internal.corext.refactoring.rename.RippleMethodFinder2;
104 import org.eclipse.jdt.internal.corext.refactoring.structure.ASTNodeSearchUtil;
105 import org.eclipse.jdt.internal.corext.refactoring.structure.CompilationUnitRewrite;
106 import org.eclipse.jdt.internal.corext.refactoring.structure.MemberVisibilityAdjustor;
107 import org.eclipse.jdt.internal.corext.refactoring.structure.MemberVisibilityAdjustor.IncomingMemberVisibilityAdjustment;
108 import org.eclipse.jdt.internal.corext.refactoring.util.ResourceUtil;
109 import org.eclipse.jdt.internal.corext.refactoring.util.TextChangeManager;
110 import org.eclipse.jdt.internal.corext.util.JdtFlags;
111 import org.eclipse.jdt.internal.corext.util.Messages;
112 import org.eclipse.jdt.internal.corext.util.MethodOverrideTester;
114 import org.eclipse.jdt.ui.CodeGeneration;
115 import org.eclipse.jdt.ui.JavaElementLabels;
117 import org.eclipse.jdt.internal.ui.JavaPlugin;
118 import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider;
119 import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
123 * This refactoring creates a wrapper around a certain method and redirects callers of the original
124 * method to the newly created method (called "intermediary method").
126 * If invoked on a method call, the user may select whether to only update the selected call or
127 * all other calls as well. If invoked on a method declaration, the user may select whether to
128 * update calls at all. An intermediary method will be created in both cases.
130 * Creating indirections is possible for both source and binary methods. Select an invocation of the method or
131 * the declaring method itself, for example in the outline view.
133 * Note that in case of methods inside generic types, the parameters of the declaring type of the selected method
134 * will be added to the method definition, rendering it generic as well.
136 * If any of the calls cannot see the intermediary method due to visibility problems with enclosing types
137 * of the intermediary method, visibility will be adjusted. If the intermediary method is not able to
138 * see the target method, this refactoring will try to adjust the visibility of the target method and
139 * enclosing types as well. However, the latter is only possible if the target method is from source.
144 public class IntroduceIndirectionRefactoring extends Refactoring {
147 * The compilation unit in which the user invoked this refactoring (if any)
149 private ICompilationUnit fSelectionCompilationUnit;
151 * The class file (with source) in which the user invoked this refactoring (if any)
153 private IClassFile fSelectionClassFile;
155 * The start of the user selection inside the selected
156 * compilation unit (if any)
158 private int fSelectionStart;
160 * The length of the user selection inside the selected
161 * compilation unit (if any)
163 private int fSelectionLength;
165 * The selected MethodInvocation (if any). This field is used
166 * to update this particular invocation in non-reference mode.
168 private MethodInvocation fSelectionMethodInvocation;
170 // Intermediary information:
173 * The class in which to place the intermediary method
175 private IType fIntermediaryClass;
177 * The binding of the intermediary class
179 private ITypeBinding fIntermediaryClassBinding;
181 * The name of the intermediary method
183 private String fIntermediaryMethodName;
185 * The type for the additional parameter for the intermediary. This
186 * type is determined from all known references.
188 private ITypeBinding fIntermediaryFirstParameterType;
190 // Target information:
193 * The originally selected target method (i.e., the one to be encapsulated)
195 private IMethod fTargetMethod;
197 * The binding of the originally selected target method
199 private IMethodBinding fTargetMethodBinding;
201 // Other information:
204 * If true, all references to the target method are replaced with calls to
207 private boolean fUpdateReferences;
209 * CompilationUnitRewrites for all affected cus
211 private Map<ICompilationUnit, CompilationUnitRewrite> fRewrites;
213 * Text change manager (actually a CompilationUnitChange manager) which
214 * manages all changes.
216 private TextChangeManager fTextChangeManager;
221 * The visibility adjustor
223 private MemberVisibilityAdjustor fAdjustor;
225 * Visibility adjustments for the intermediary
227 private Map<IMember, IncomingMemberVisibilityAdjustment> fIntermediaryAdjustments;
230 private class NoOverrideProgressMonitor extends SubProgressMonitor {
232 public NoOverrideProgressMonitor(IProgressMonitor monitor, int ticks) {
233 super(monitor, ticks, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL);
237 public void setTaskName(String name) {
242 // ********* CONSTRUCTORS AND CLASS CREATION ************
244 public IntroduceIndirectionRefactoring(ICompilationUnit unit, int offset, int length) {
245 fSelectionCompilationUnit= unit;
246 initialize(offset, length);
249 public IntroduceIndirectionRefactoring(IClassFile file, int offset, int length) {
250 fSelectionClassFile= file;
251 initialize(offset, length);
254 public IntroduceIndirectionRefactoring(IMethod method) {
255 fTargetMethod= method;
259 public IntroduceIndirectionRefactoring(JavaRefactoringArguments arguments, RefactoringStatus status) {
260 this((ICompilationUnit) null, 0, 0);
261 RefactoringStatus initializeStatus= initialize(arguments);
262 status.merge(initializeStatus);
265 private void initialize(int offset, int length) {
266 fSelectionStart= offset;
267 fSelectionLength= length;
268 fUpdateReferences= true;
271 // ********* UI INTERACTION AND STARTUP OPTIONS ************
274 public String getName() {
275 return RefactoringCoreMessages.IntroduceIndirectionRefactoring_introduce_indirection_name;
278 public IJavaProject getProject() {
279 if (fSelectionCompilationUnit != null)
280 return fSelectionCompilationUnit.getJavaProject();
281 if (fSelectionClassFile != null)
282 return fSelectionClassFile.getJavaProject();
283 if (fTargetMethod != null)
284 return fTargetMethod.getJavaProject();
288 public IPackageFragment getInvocationPackage() {
289 return fSelectionCompilationUnit != null ? (IPackageFragment) fSelectionCompilationUnit.getAncestor(IJavaElement.PACKAGE_FRAGMENT) : null;
292 public boolean canEnableUpdateReferences() {
296 public void setEnableUpdateReferences(boolean updateReferences) {
297 fUpdateReferences= updateReferences;
300 public RefactoringStatus setIntermediaryMethodName(String newMethodName) {
301 Assert.isNotNull(newMethodName);
302 fIntermediaryMethodName= newMethodName;
303 IJavaElement context= fIntermediaryClass != null ? fIntermediaryClass : (IMember) fTargetMethod;
304 RefactoringStatus stat= Checks.checkMethodName(newMethodName, context);
305 stat.merge(checkOverloading());
309 private RefactoringStatus checkOverloading() {
311 if (fIntermediaryClass != null) {
312 IMethod[] toCheck= fIntermediaryClass.getMethods();
313 for (int i= 0; i < toCheck.length; i++) {
314 IMethod method= toCheck[i];
315 if (method.getElementName().equals(fIntermediaryMethodName))
316 return RefactoringStatus.createWarningStatus(Messages.format(RefactoringCoreMessages.IntroduceIndirectionRefactoring_duplicate_method_name_in_declaring_class_error,
317 BasicElementLabels.getJavaElementName(fIntermediaryMethodName)));
320 } catch (JavaModelException e) {
321 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_could_not_parse_declaring_class_error);
323 return new RefactoringStatus();
326 public String getIntermediaryMethodName() {
327 return fIntermediaryMethodName;
331 * @param fullyQualifiedTypeName the fully qualified name of the intermediary method
332 * @return status for type name. Use {@link #setIntermediaryMethodName(String)} to check for overridden methods.
334 public RefactoringStatus setIntermediaryClassName(String fullyQualifiedTypeName) {
338 if (fullyQualifiedTypeName.length() == 0)
339 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_class_not_selected_error);
341 // find type (now including secondaries)
342 target= getProject().findType(fullyQualifiedTypeName, new NullProgressMonitor());
343 if (target == null || !target.exists())
344 return RefactoringStatus.createErrorStatus(Messages.format(RefactoringCoreMessages.IntroduceIndirectionRefactoring_class_does_not_exist_error, BasicElementLabels.getJavaElementName(fullyQualifiedTypeName)));
345 if (target.isAnnotation())
346 return RefactoringStatus.createErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_cannot_create_in_annotation);
347 if (target.isInterface())
348 return RefactoringStatus.createErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_cannot_create_on_interface);
349 } catch (JavaModelException e) {
350 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_unable_determine_declaring_type);
353 if (target.isReadOnly())
354 return RefactoringStatus.createErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_cannot_create_in_readonly);
356 if (target.isBinary())
357 return RefactoringStatus.createErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_cannot_create_in_binary);
359 fIntermediaryClass= target;
361 return new RefactoringStatus();
365 * Returns the class name of the intermediary class, or the empty string if none has been set yet.
366 * @return the intermediary class name or the empty string
368 public String getIntermediaryClassName() {
369 return fIntermediaryClass != null ? fIntermediaryClass.getFullyQualifiedName('.') : ""; //$NON-NLS-1$
372 // ********** CONDITION CHECKING **********
375 public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException {
377 pm.beginTask(RefactoringCoreMessages.IntroduceIndirectionRefactoring_checking_activation, 1);
378 fRewrites= new HashMap<ICompilationUnit, CompilationUnitRewrite>();
380 // This refactoring has been invoked on
381 // (1) a TextSelection inside an ICompilationUnit or inside an IClassFile (definitely with source), or
382 // (2) an IMethod inside a ICompilationUnit or inside an IClassFile (with or without source)
384 if (fTargetMethod == null) {
385 // (1) invoked on a text selection
387 if (fSelectionStart == 0)
388 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_not_available_on_this_selection);
390 // if a text selection exists, source is available.
391 CompilationUnit selectionCURoot;
392 ASTNode selectionNode;
393 if (fSelectionCompilationUnit != null) {
394 // compilation unit - could use CuRewrite later on
395 selectionCURoot= getCachedCURewrite(fSelectionCompilationUnit).getRoot();
396 selectionNode= getSelectedNode(fSelectionCompilationUnit, selectionCURoot, fSelectionStart, fSelectionLength);
398 // binary class file - no cu rewrite
399 ASTParser parser= ASTParser.newParser(ASTProvider.SHARED_AST_LEVEL);
400 parser.setResolveBindings(true);
401 parser.setSource(fSelectionClassFile);
402 selectionCURoot= (CompilationUnit) parser.createAST(null);
403 selectionNode= getSelectedNode(null, selectionCURoot, fSelectionStart, fSelectionLength);
406 if (selectionNode == null)
407 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_not_available_on_this_selection);
409 IMethodBinding targetMethodBinding= null;
411 if (selectionNode.getNodeType() == ASTNode.METHOD_INVOCATION) {
412 targetMethodBinding= ((MethodInvocation) selectionNode).resolveMethodBinding();
413 } else if (selectionNode.getNodeType() == ASTNode.METHOD_DECLARATION) {
414 targetMethodBinding= ((MethodDeclaration) selectionNode).resolveBinding();
415 } else if (selectionNode.getNodeType() == ASTNode.SUPER_METHOD_INVOCATION) {
416 // Allow invocation on super methods calls. makes sense as other
417 // calls or even only the declaration can be updated.
418 targetMethodBinding= ((SuperMethodInvocation) selectionNode).resolveMethodBinding();
420 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_not_available_on_this_selection);
422 fTargetMethodBinding= targetMethodBinding.getMethodDeclaration(); // resolve generics
423 fTargetMethod= (IMethod) fTargetMethodBinding.getJavaElement();
425 //allow single updating mode if an invocation was selected and the invocation can be updated
426 if (selectionNode instanceof MethodInvocation && fSelectionCompilationUnit != null)
427 fSelectionMethodInvocation= (MethodInvocation) selectionNode;
430 // (2) invoked on an IMethod: Source may not be available
432 if (fTargetMethod.getDeclaringType().isAnnotation())
433 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_not_available_on_annotation);
435 if (fTargetMethod.getCompilationUnit() != null) {
437 CompilationUnit selectionCURoot= getCachedCURewrite(fTargetMethod.getCompilationUnit()).getRoot();
438 MethodDeclaration declaration= ASTNodeSearchUtil.getMethodDeclarationNode(fTargetMethod, selectionCURoot);
439 fTargetMethodBinding= declaration.resolveBinding().getMethodDeclaration();
441 // binary method - no CURewrite available (and none needed as we cannot update the method anyway)
442 ASTParser parser= ASTParser.newParser(ASTProvider.SHARED_AST_LEVEL);
443 parser.setProject(fTargetMethod.getJavaProject());
444 IBinding[] bindings= parser.createBindings(new IJavaElement[] { fTargetMethod }, null);
445 fTargetMethodBinding= ((IMethodBinding) bindings[0]).getMethodDeclaration();
449 if (fTargetMethod == null || fTargetMethodBinding == null || (!RefactoringAvailabilityTester.isIntroduceIndirectionAvailable(fTargetMethod)))
450 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_not_available_on_this_selection);
452 if (fTargetMethod.getDeclaringType().isLocal() || fTargetMethod.getDeclaringType().isAnonymous())
453 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_not_available_for_local_or_anonymous_types);
455 if (fTargetMethod.isConstructor())
456 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_not_available_for_constructors);
458 if (fIntermediaryMethodName == null)
459 fIntermediaryMethodName= fTargetMethod.getElementName();
461 if (fIntermediaryClass == null) {
462 if (fSelectionCompilationUnit != null && !fSelectionCompilationUnit.isReadOnly())
463 fIntermediaryClass= getEnclosingInitialSelectionMember().getDeclaringType();
464 else if (!fTargetMethod.isBinary() && !fTargetMethod.isReadOnly())
465 fIntermediaryClass= fTargetMethod.getDeclaringType();
468 return new RefactoringStatus();
475 public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException {
477 RefactoringStatus result= new RefactoringStatus();
478 fTextChangeManager= new TextChangeManager();
479 fIntermediaryFirstParameterType= null;
480 fIntermediaryClassBinding= null;
481 for (Iterator<CompilationUnitRewrite> iter= fRewrites.values().iterator(); iter.hasNext();)
482 iter.next().clearASTAndImportRewrites();
485 int hierarchyTicks= 5;
486 int visibilityTicks= 5;
487 int referenceTicks= fUpdateReferences ? 30 : 5;
488 int creationTicks= 5;
490 pm.beginTask("", startupTicks + hierarchyTicks + visibilityTicks + referenceTicks + creationTicks); //$NON-NLS-1$
491 pm.setTaskName(RefactoringCoreMessages.IntroduceIndirectionRefactoring_checking_conditions);
493 result.merge(Checks.checkMethodName(fIntermediaryMethodName, fIntermediaryClass));
494 if (result.hasFatalError())
497 if (fIntermediaryClass == null)
498 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_cannot_run_without_intermediary_type);
500 // intermediary class is already non binary/non-enum/non-interface.
501 CompilationUnitRewrite imRewrite= getCachedCURewrite(fIntermediaryClass.getCompilationUnit());
502 fIntermediaryClassBinding= typeToBinding(fIntermediaryClass, imRewrite.getRoot());
504 fAdjustor= new MemberVisibilityAdjustor(fIntermediaryClass, fIntermediaryClass);
505 fIntermediaryAdjustments= new HashMap<IMember, IncomingMemberVisibilityAdjustment>();
507 // check static method in non-static nested type
508 if (fIntermediaryClassBinding.isNested() && !Modifier.isStatic(fIntermediaryClassBinding.getModifiers()))
509 return RefactoringStatus.createFatalErrorStatus(RefactoringCoreMessages.IntroduceIndirectionRefactoring_cannot_create_in_nested_nonstatic, JavaStatusContext.create(fIntermediaryClass));
511 pm.worked(startupTicks);
513 throw new OperationCanceledException();
515 if (fUpdateReferences) {
516 pm.setTaskName(RefactoringCoreMessages.IntroduceIndirectionRefactoring_checking_conditions + " " + RefactoringCoreMessages.IntroduceIndirectionRefactoring_looking_for_references); //$NON-NLS-1$
517 result.merge(updateReferences(new NoOverrideProgressMonitor(pm, referenceTicks)));
518 pm.setTaskName(RefactoringCoreMessages.IntroduceIndirectionRefactoring_checking_conditions);
520 // only update the declaration and/or a selected method invocation
521 if (fSelectionMethodInvocation != null) {
522 fIntermediaryFirstParameterType= getExpressionType(fSelectionMethodInvocation);
523 final IMember enclosing= getEnclosingInitialSelectionMember();
524 // create an edit for this particular call
525 result.merge(updateMethodInvocation(fSelectionMethodInvocation, enclosing, getCachedCURewrite(fSelectionCompilationUnit)));
527 if (!isRewriteKept(fSelectionCompilationUnit))
528 createChangeAndDiscardRewrite(fSelectionCompilationUnit);
530 // does call see the intermediary method?
531 // => increase visibility of the type of the intermediary method.
532 result.merge(adjustVisibility(fIntermediaryClass, enclosing.getDeclaringType(), new NoOverrideProgressMonitor(pm, 0)));
534 pm.worked(referenceTicks);
538 throw new OperationCanceledException();
540 if (fIntermediaryFirstParameterType == null)
541 fIntermediaryFirstParameterType= fTargetMethodBinding.getDeclaringClass();
543 // The target type and method may have changed - update them
545 IType actualTargetType= (IType) fIntermediaryFirstParameterType.getJavaElement();
546 if (!fTargetMethod.getDeclaringType().equals(actualTargetType)) {
547 IMethod actualTargetMethod= new MethodOverrideTester(actualTargetType, actualTargetType.newSupertypeHierarchy(null)).findOverriddenMethodInHierarchy(actualTargetType, fTargetMethod);
548 fTargetMethod= actualTargetMethod;
549 fTargetMethodBinding= findMethodBindingInHierarchy(fIntermediaryFirstParameterType, actualTargetMethod);
550 Assert.isNotNull(fTargetMethodBinding);
553 result.merge(checkCanCreateIntermediaryMethod());
554 createIntermediaryMethod();
555 pm.worked(creationTicks);
557 pm.setTaskName(RefactoringCoreMessages.IntroduceIndirectionRefactoring_checking_conditions + " " + RefactoringCoreMessages.IntroduceIndirectionRefactoring_adjusting_visibility); //$NON-NLS-1$
558 result.merge(updateTargetVisibility(new NoOverrideProgressMonitor(pm, 0)));
559 result.merge(updateIntermediaryVisibility(new NoOverrideProgressMonitor(pm, 0)));
560 pm.worked(visibilityTicks);
561 pm.setTaskName(RefactoringCoreMessages.IntroduceIndirectionRefactoring_checking_conditions);
563 createChangeAndDiscardRewrite(fIntermediaryClass.getCompilationUnit());
565 result.merge(Checks.validateModifiesFiles(getAllFilesToModify(), getValidationContext()));
571 private RefactoringStatus updateTargetVisibility(IProgressMonitor monitor) throws JavaModelException, CoreException {
573 RefactoringStatus result= new RefactoringStatus();
575 // Adjust the visibility of the method and of the referenced type. Note that
576 // the target method may not be in the target type; and in this case, the type
577 // of the target method does not need a visibility adjustment.
579 // This method is called after all other changes have been
580 // created. Changes induced by this method will be attached to those changes.
582 result.merge(adjustVisibility((IType) fIntermediaryFirstParameterType.getJavaElement(), fIntermediaryClass, monitor));
583 if (result.hasError())
584 return result; // binary
586 ModifierKeyword neededVisibility= getNeededVisibility(fTargetMethod, fIntermediaryClass);
587 if (neededVisibility != null) {
589 result.merge(adjustVisibility(fTargetMethod, neededVisibility, monitor));
590 if (result.hasError())
591 return result; // binary
593 // Need to adjust the overridden methods of the target method.
594 ITypeHierarchy hierarchy= fTargetMethod.getDeclaringType().newTypeHierarchy(null);
595 MethodOverrideTester tester= new MethodOverrideTester(fTargetMethod.getDeclaringType(), hierarchy);
596 IType[] subtypes= hierarchy.getAllSubtypes(fTargetMethod.getDeclaringType());
597 for (int i= 0; i < subtypes.length; i++) {
598 IMethod method= tester.findOverridingMethodInType(subtypes[i], fTargetMethod);
599 if (method != null && method.exists()) {
600 result.merge(adjustVisibility(method, neededVisibility, monitor));
601 if (monitor.isCanceled())
602 throw new OperationCanceledException();
604 if (result.hasError())
605 return result; // binary
613 private RefactoringStatus updateIntermediaryVisibility(NoOverrideProgressMonitor monitor) throws JavaModelException {
614 return rewriteVisibility(fIntermediaryAdjustments, fRewrites, monitor);
617 private RefactoringStatus updateReferences(IProgressMonitor monitor) throws CoreException {
619 RefactoringStatus result= new RefactoringStatus();
621 monitor.beginTask("", 90); //$NON-NLS-1$
623 if (monitor.isCanceled())
624 throw new OperationCanceledException();
626 IMethod[] ripple= RippleMethodFinder2.getRelatedMethods(fTargetMethod, false, new NoOverrideProgressMonitor(monitor, 10), null);
628 if (monitor.isCanceled())
629 throw new OperationCanceledException();
631 SearchResultGroup[] references= Checks.excludeCompilationUnits(getReferences(ripple, new NoOverrideProgressMonitor(monitor, 10), result), result);
633 if (result.hasFatalError())
636 result.merge(Checks.checkCompileErrorsInAffectedFiles(references));
638 if (monitor.isCanceled())
639 throw new OperationCanceledException();
641 int ticksPerCU= references.length == 0 ? 0 : 70 / references.length;
643 for (int i= 0; i < references.length; i++) {
644 SearchResultGroup group= references[i];
645 SearchMatch[] searchResults= group.getSearchResults();
646 CompilationUnitRewrite currentCURewrite= getCachedCURewrite(group.getCompilationUnit());
648 for (int j= 0; j < searchResults.length; j++) {
650 SearchMatch match= searchResults[j];
651 if (match.isInsideDocComment())
654 IMember enclosingMember= (IMember) match.getElement();
655 ASTNode target= getSelectedNode(group.getCompilationUnit(), currentCURewrite.getRoot(), match.getOffset(), match.getLength());
657 if (target instanceof SuperMethodInvocation) {
658 // Cannot retarget calls to super - add a warning
659 result.merge(createWarningAboutCall(enclosingMember, target, RefactoringCoreMessages.IntroduceIndirectionRefactoring_call_warning_super_keyword));
663 Assert.isTrue(target instanceof MethodInvocation, "Element of call should be a MethodInvocation."); //$NON-NLS-1$
665 MethodInvocation invocation= (MethodInvocation) target;
666 ITypeBinding typeBinding= getExpressionType(invocation);
668 if (fIntermediaryFirstParameterType == null) {
669 // no highest type yet
670 fIntermediaryFirstParameterType= typeBinding.getTypeDeclaration();
672 // check if current type is higher
673 result.merge(findCommonParent(typeBinding.getTypeDeclaration()));
676 if (result.hasFatalError())
679 // create an edit for this particular call
680 result.merge(updateMethodInvocation(invocation, enclosingMember, currentCURewrite));
682 // does call see the intermediary method?
683 // => increase visibility of the type of the intermediary method.
684 result.merge(adjustVisibility(fIntermediaryClass, enclosingMember.getDeclaringType(), new NoOverrideProgressMonitor(monitor, 0)));
686 if (monitor.isCanceled())
687 throw new OperationCanceledException();
690 if (!isRewriteKept(group.getCompilationUnit()))
691 createChangeAndDiscardRewrite(group.getCompilationUnit());
693 monitor.worked(ticksPerCU);
700 private RefactoringStatus findCommonParent(ITypeBinding typeBinding) {
702 RefactoringStatus status= new RefactoringStatus();
704 ITypeBinding highest= fIntermediaryFirstParameterType;
705 ITypeBinding current= typeBinding;
707 if (current.equals(highest) || Bindings.isSuperType(highest, current))
708 // current is the same as highest or highest is already a supertype of current in the same hierarchy => no change
711 // find lowest common supertype with the method
712 // search in bottom-up order
713 ITypeBinding[] currentAndSupers= getTypeAndAllSuperTypes(current);
714 ITypeBinding[] highestAndSupers= getTypeAndAllSuperTypes(highest);
716 ITypeBinding foundBinding= null;
717 for (int i1= 0; i1 < currentAndSupers.length; i1++) {
718 for (int i2= 0; i2 < highestAndSupers.length; i2++) {
719 if (highestAndSupers[i2].isEqualTo(currentAndSupers[i1])
720 && (Bindings.findMethodInHierarchy(highestAndSupers[i2], fTargetMethodBinding.getName(), fTargetMethodBinding.getParameterTypes()) != null)) {
721 foundBinding= highestAndSupers[i2];
725 if (foundBinding != null)
729 if (foundBinding != null) {
730 fIntermediaryFirstParameterType= foundBinding;
732 String type1= BasicElementLabels.getJavaElementName(fIntermediaryFirstParameterType.getQualifiedName());
733 String type2= BasicElementLabels.getJavaElementName(current.getQualifiedName());
734 status.addFatalError(Messages.format(RefactoringCoreMessages.IntroduceIndirectionRefactoring_open_hierarchy_error, new String[] { type1, type2 }));
740 // ******************** CHANGE CREATION ***********************
743 public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException {
744 final Map<String, String> arguments= new HashMap<String, String>();
745 String project= null;
746 IJavaProject javaProject= fTargetMethod.getJavaProject();
747 if (javaProject != null)
748 project= javaProject.getElementName();
749 int flags= JavaRefactoringDescriptor.JAR_MIGRATION | JavaRefactoringDescriptor.JAR_REFACTORING | RefactoringDescriptor.STRUCTURAL_CHANGE | RefactoringDescriptor.MULTI_CHANGE;
750 final IType declaring= fTargetMethod.getDeclaringType();
752 if (declaring.isLocal() || declaring.isAnonymous())
753 flags|= JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT;
754 } catch (JavaModelException exception) {
755 JavaPlugin.log(exception);
757 final String description= Messages.format(RefactoringCoreMessages.IntroduceIndirectionRefactoring_descriptor_description_short, BasicElementLabels.getJavaElementName(fTargetMethod.getElementName()));
758 final String header= Messages.format(RefactoringCoreMessages.IntroduceIndirectionRefactoring_descriptor_description, new String[] { JavaElementLabels.getTextLabel(fTargetMethod, JavaElementLabels.ALL_FULLY_QUALIFIED), JavaElementLabels.getTextLabel(declaring, JavaElementLabels.ALL_FULLY_QUALIFIED)});
759 final JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(project, this, header);
760 comment.addSetting(Messages.format(RefactoringCoreMessages.IntroduceIndirectionRefactoring_original_pattern, JavaElementLabels.getTextLabel(fTargetMethod, JavaElementLabels.ALL_FULLY_QUALIFIED)));
761 comment.addSetting(Messages.format(RefactoringCoreMessages.IntroduceIndirectionRefactoring_method_pattern, BasicElementLabels.getJavaElementName(fIntermediaryMethodName)));
762 comment.addSetting(Messages.format(RefactoringCoreMessages.IntroduceIndirectionRefactoring_declaring_pattern, JavaElementLabels.getTextLabel(fIntermediaryClass, JavaElementLabels.ALL_FULLY_QUALIFIED)));
763 if (fUpdateReferences)
764 comment.addSetting(RefactoringCoreMessages.JavaRefactoringDescriptor_update_references);
765 final IntroduceIndirectionDescriptor descriptor= RefactoringSignatureDescriptorFactory.createIntroduceIndirectionDescriptor(project, description, comment.asString(), arguments, flags);
766 arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT, JavaRefactoringDescriptorUtil.elementToHandle(project, fTargetMethod));
767 arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME, fIntermediaryMethodName);
768 arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + 1, JavaRefactoringDescriptorUtil.elementToHandle(project, fIntermediaryClass));
769 arguments.put(JavaRefactoringDescriptorUtil.ATTRIBUTE_REFERENCES, Boolean.valueOf(fUpdateReferences).toString());
770 return new DynamicValidationRefactoringChange(descriptor, RefactoringCoreMessages.IntroduceIndirectionRefactoring_introduce_indirection, fTextChangeManager.getAllChanges());
773 // ******************* CREATE INTERMEDIARY **********************
776 * Checks whether the target method can be created. Note that this
777 * can only be done after fDelegateParameterType has been initialized.
778 * @return resulting status
779 * @throws JavaModelException should not happen
781 private RefactoringStatus checkCanCreateIntermediaryMethod() throws JavaModelException {
782 // check if method already exists:
783 List<ITypeBinding> parameterBindings= new ArrayList<ITypeBinding>();
784 if (!isStaticTarget())
785 parameterBindings.add(fIntermediaryFirstParameterType);
786 parameterBindings.addAll(Arrays.asList(fTargetMethodBinding.getParameterTypes()));
787 return Checks.checkMethodInType(fIntermediaryClassBinding, fIntermediaryMethodName, parameterBindings.toArray(new ITypeBinding[parameterBindings.size()]));
790 private void createIntermediaryMethod() throws CoreException {
792 CompilationUnitRewrite imRewrite= getCachedCURewrite(fIntermediaryClass.getCompilationUnit());
793 AST ast= imRewrite.getAST();
794 MethodDeclaration intermediary= ast.newMethodDeclaration();
796 // Intermediary class is non-anonymous
797 AbstractTypeDeclaration type= (AbstractTypeDeclaration)typeToDeclaration(fIntermediaryClass, imRewrite.getRoot());
800 intermediary.setName(ast.newSimpleName(fIntermediaryMethodName));
803 List<IExtendedModifier> modifiers= intermediary.modifiers();
804 modifiers.add(imRewrite.getAST().newModifier(ModifierKeyword.PUBLIC_KEYWORD));
805 modifiers.add(imRewrite.getAST().newModifier(ModifierKeyword.STATIC_KEYWORD));
808 String targetParameterName= StubUtility.suggestArgumentName(getProject(), fIntermediaryFirstParameterType.getName(), fTargetMethod.getParameterNames());
810 ImportRewriteContext context= new ContextSensitiveImportRewriteContext(type, imRewrite.getImportRewrite());
811 if (!isStaticTarget()) {
813 SingleVariableDeclaration parameter= imRewrite.getAST().newSingleVariableDeclaration();
814 Type t= imRewrite.getImportRewrite().addImport(fIntermediaryFirstParameterType, imRewrite.getAST(), context);
815 if (fIntermediaryFirstParameterType.isGenericType()) {
816 ParameterizedType parameterized= imRewrite.getAST().newParameterizedType(t);
817 ITypeBinding[] typeParameters= fIntermediaryFirstParameterType.getTypeParameters();
818 for (int i= 0; i < typeParameters.length; i++)
819 parameterized.typeArguments().add(imRewrite.getImportRewrite().addImport(typeParameters[i], imRewrite.getAST()));
822 parameter.setType(t);
823 parameter.setName(imRewrite.getAST().newSimpleName(targetParameterName));
824 intermediary.parameters().add(parameter);
827 copyArguments(intermediary, imRewrite);
829 // Add type parameters of declaring class (and enclosing classes)
830 if (!isStaticTarget() && fIntermediaryFirstParameterType.isGenericType())
831 addTypeParameters(imRewrite, intermediary.typeParameters(), fIntermediaryFirstParameterType);
833 // Add type params of method
834 copyTypeParameters(intermediary, imRewrite);
837 intermediary.setReturnType2(imRewrite.getImportRewrite().addImport(fTargetMethodBinding.getReturnType(), ast, context));
840 copyExceptions(intermediary, imRewrite);
843 MethodInvocation invocation= imRewrite.getAST().newMethodInvocation();
844 invocation.setName(imRewrite.getAST().newSimpleName(fTargetMethod.getElementName()));
845 if (isStaticTarget()) {
846 Type importedType= imRewrite.getImportRewrite().addImport(fTargetMethodBinding.getDeclaringClass(), ast, context);
847 invocation.setExpression(ASTNodeFactory.newName(ast, ASTNodes.asString(importedType)));
849 invocation.setExpression(imRewrite.getAST().newSimpleName(targetParameterName));
851 copyInvocationParameters(invocation, ast);
852 Statement call= encapsulateInvocation(intermediary, invocation);
854 final Block body= imRewrite.getAST().newBlock();
855 body.statements().add(call);
856 intermediary.setBody(body);
859 ICompilationUnit targetCU= imRewrite.getCu();
860 if (StubUtility.doAddComments(targetCU.getJavaProject())) {
861 String comment= CodeGeneration.getMethodComment(targetCU, getIntermediaryClassName(), intermediary, null, StubUtility.getLineDelimiterUsed(targetCU));
862 if (comment != null) {
863 Javadoc javadoc= (Javadoc) imRewrite.getASTRewrite().createStringPlaceholder(comment, ASTNode.JAVADOC);
864 intermediary.setJavadoc(javadoc);
868 // Add the completed method to the intermediary type:
869 ChildListPropertyDescriptor typeBodyDeclarationsProperty= typeToBodyDeclarationProperty(fIntermediaryClass, imRewrite.getRoot());
871 ListRewrite bodyDeclarationsListRewrite= imRewrite.getASTRewrite().getListRewrite(type, typeBodyDeclarationsProperty);
872 bodyDeclarationsListRewrite.insertAt(intermediary, ASTNodes.getInsertionIndex(intermediary, type.bodyDeclarations()), imRewrite
873 .createGroupDescription(RefactoringCoreMessages.IntroduceIndirectionRefactoring_group_description_create_new_method));
876 private void addTypeParameters(CompilationUnitRewrite imRewrite, List<TypeParameter> list, ITypeBinding parent) {
878 ITypeBinding enclosing= parent.getDeclaringClass();
879 if (enclosing != null)
880 addTypeParameters(imRewrite, list, enclosing);
882 ITypeBinding[] typeParameters= parent.getTypeParameters();
883 for (int i= 0; i < typeParameters.length; i++) {
884 TypeParameter ntp= imRewrite.getAST().newTypeParameter();
885 ntp.setName(imRewrite.getAST().newSimpleName(typeParameters[i].getName()));
886 ITypeBinding[] bounds= typeParameters[i].getTypeBounds();
887 for (int j= 0; j < bounds.length; j++)
888 if (!"java.lang.Object".equals(bounds[j].getQualifiedName())) //$NON-NLS-1$
889 ntp.typeBounds().add(imRewrite.getImportRewrite().addImport(bounds[j], imRewrite.getAST()));
894 private Statement encapsulateInvocation(MethodDeclaration declaration, MethodInvocation invocation) {
895 final Type type= declaration.getReturnType2();
897 if (type == null || (type instanceof PrimitiveType && PrimitiveType.VOID.equals( ((PrimitiveType) type).getPrimitiveTypeCode())))
898 return invocation.getAST().newExpressionStatement(invocation);
900 ReturnStatement statement= invocation.getAST().newReturnStatement();
901 statement.setExpression(invocation);
905 private void copyInvocationParameters(MethodInvocation invocation, AST ast) throws JavaModelException {
906 String[] names= fTargetMethod.getParameterNames();
907 for (int i= 0; i < names.length; i++)
908 invocation.arguments().add(ast.newSimpleName(names[i]));
911 private void copyArguments(MethodDeclaration intermediary, CompilationUnitRewrite rew) throws JavaModelException {
912 String[] names= fTargetMethod.getParameterNames();
913 ITypeBinding[] types= fTargetMethodBinding.getParameterTypes();
914 for (int i= 0; i < names.length; i++) {
915 ITypeBinding typeBinding= types[i];
916 SingleVariableDeclaration newElement= rew.getAST().newSingleVariableDeclaration();
917 newElement.setName(rew.getAST().newSimpleName(names[i]));
919 if (i == (names.length - 1) && fTargetMethodBinding.isVarargs()) {
920 newElement.setVarargs(true);
921 if (typeBinding.isArray())
922 typeBinding= typeBinding.getComponentType();
925 newElement.setType(rew.getImportRewrite().addImport(typeBinding, rew.getAST()));
926 intermediary.parameters().add(newElement);
930 private void copyTypeParameters(MethodDeclaration intermediary, CompilationUnitRewrite rew) {
931 ITypeBinding[] typeParameters= fTargetMethodBinding.getTypeParameters();
932 for (int i= 0; i < typeParameters.length; i++) {
933 ITypeBinding current= typeParameters[i];
935 TypeParameter parameter= rew.getAST().newTypeParameter();
936 parameter.setName(rew.getAST().newSimpleName(current.getName()));
937 ITypeBinding[] bounds= current.getTypeBounds();
938 for (int j= 0; j < bounds.length; j++)
939 if (!"java.lang.Object".equals(bounds[j].getQualifiedName())) //$NON-NLS-1$
940 parameter.typeBounds().add(rew.getImportRewrite().addImport(bounds[j], rew.getAST()));
942 intermediary.typeParameters().add(parameter);
946 private void copyExceptions(MethodDeclaration intermediary, CompilationUnitRewrite imRewrite) {
947 ITypeBinding[] exceptionTypes= fTargetMethodBinding.getExceptionTypes();
948 for (int i= 0; i < exceptionTypes.length; i++) {
949 final String qualifiedName= imRewrite.getImportRewrite().addImport(exceptionTypes[i]);
950 intermediary.thrownExceptions().add(ASTNodeFactory.newName(imRewrite.getAST(), qualifiedName));
954 // ******************* UPDATE CALLS **********************
956 private RefactoringStatus updateMethodInvocation(MethodInvocation originalInvocation, IMember enclosing, CompilationUnitRewrite unitRewriter) throws JavaModelException {
958 RefactoringStatus status= new RefactoringStatus();
960 // If the method invocation utilizes type arguments, skip this
961 // call as the new target method may have additional parameters
962 if (originalInvocation.typeArguments().size() > 0)
963 return createWarningAboutCall(enclosing, originalInvocation, RefactoringCoreMessages.IntroduceIndirectionRefactoring_call_warning_type_arguments);
965 MethodInvocation newInvocation= unitRewriter.getAST().newMethodInvocation();
966 List<Expression> newInvocationArgs= newInvocation.arguments();
967 List<Expression> originalInvocationArgs= originalInvocation.arguments();
969 // static call => always use a qualifier
970 String qualifier= unitRewriter.getImportRewrite().addImport(fIntermediaryClassBinding);
971 newInvocation.setExpression(ASTNodeFactory.newName(unitRewriter.getAST(), qualifier));
972 newInvocation.setName(unitRewriter.getAST().newSimpleName(getIntermediaryMethodName()));
974 final Expression expression= originalInvocation.getExpression();
976 if (!isStaticTarget()) {
977 // Add the expression as the first parameter
978 if (expression == null) {
979 // There is no expression for this call. Use a (possibly qualified) "this" expression.
980 ThisExpression expr= unitRewriter.getAST().newThisExpression();
981 RefactoringStatus qualifierStatus= qualifyThisExpression(expr, originalInvocation, enclosing, unitRewriter);
982 status.merge(qualifierStatus);
983 if (qualifierStatus.hasEntries())
984 // warning means don't include this invocation
986 newInvocationArgs.add(expr);
988 Expression expressionAsParam= (Expression) unitRewriter.getASTRewrite().createMoveTarget(expression);
989 newInvocationArgs.add(expressionAsParam);
992 if (expression != null) {
993 // Check if expression is the class name. If not, there may
994 // be side effects (e.g. inside methods) -> don't update
995 if (! (expression instanceof Name) || ASTNodes.getTypeBinding((Name) expression) == null)
996 return createWarningAboutCall(enclosing, originalInvocation, RefactoringCoreMessages.IntroduceIndirectionRefactoring_call_warning_static_expression_access);
1000 for (int i= 0; i < originalInvocationArgs.size(); i++) {
1001 Expression originalInvocationArg= originalInvocationArgs.get(i);
1002 Expression movedArg= (Expression) unitRewriter.getASTRewrite().createMoveTarget(originalInvocationArg);
1003 newInvocationArgs.add(movedArg);
1006 unitRewriter.getASTRewrite().replace(originalInvocation, newInvocation,
1007 unitRewriter.createGroupDescription(RefactoringCoreMessages.IntroduceIndirectionRefactoring_group_description_replace_call));
1013 * Attempts to qualify a "this" expression for a method invocation with an appropriate qualifier.
1014 * The invoked method is analyzed according to the following specs:
1016 * 'this' must be qualified iff method is declared in an enclosing type or a supertype of an enclosing type
1018 * 1) The method is declared somewhere outside of the cu of the invocation
1019 * 1a) inside a supertype of the current type
1020 * 1b) inside a supertype of an enclosing type
1021 * 2) The method is declared inside of the cu of the invocation
1022 * 2a) inside the type of the invocation
1023 * 2b) outside the type of the invocation
1025 * In case of 1a) and 2b), qualify with the enclosing type.
1026 * @param expr a {@link ThisExpression}
1027 * @param originalInvocation the original method invocation
1028 * @param enclosing the enclosing member of the original method invocation
1029 * @param unitRewriter the rewrite
1030 * @return resulting status
1033 private RefactoringStatus qualifyThisExpression(ThisExpression expr, MethodInvocation originalInvocation, IMember enclosing, CompilationUnitRewrite unitRewriter) {
1035 RefactoringStatus status= new RefactoringStatus();
1037 IMethodBinding methodBinding= originalInvocation.resolveMethodBinding();
1038 MethodDeclaration methodDeclaration= (MethodDeclaration) ASTNodes.findDeclaration(methodBinding, originalInvocation.getRoot());
1040 ITypeBinding currentTypeBinding= null;
1041 if (methodDeclaration != null) {
1042 // Case 1) : Declaring class is inside this cu => use its name if it's declared in an enclosing type
1043 if (ASTNodes.isParent(originalInvocation, methodDeclaration.getParent()))
1044 currentTypeBinding= methodBinding.getDeclaringClass();
1046 currentTypeBinding= ASTNodes.getEnclosingType(originalInvocation);
1048 // Case 2) : Declaring class is outside of this cu => find subclass in this cu
1049 ASTNode currentTypeDeclaration= getEnclosingTypeDeclaration(originalInvocation);
1050 currentTypeBinding= ASTNodes.getEnclosingType(currentTypeDeclaration);
1051 while (currentTypeDeclaration != null && (Bindings.findMethodInHierarchy(currentTypeBinding, methodBinding.getName(), methodBinding.getParameterTypes()) == null)) {
1052 currentTypeDeclaration= getEnclosingTypeDeclaration(currentTypeDeclaration.getParent());
1053 currentTypeBinding= ASTNodes.getEnclosingType(currentTypeDeclaration);
1057 if (currentTypeBinding == null) {
1058 status.merge(createWarningAboutCall(enclosing, originalInvocation, RefactoringCoreMessages.IntroduceIndirectionRefactoring_call_warning_declaring_type_not_found));
1062 currentTypeBinding= currentTypeBinding.getTypeDeclaration();
1064 ITypeBinding typeOfCall= ASTNodes.getEnclosingType(originalInvocation);
1065 if (!typeOfCall.equals(currentTypeBinding)) {
1066 if (currentTypeBinding.isAnonymous()) {
1067 // Cannot qualify, see bug 115277
1068 status.merge(createWarningAboutCall(enclosing, originalInvocation, RefactoringCoreMessages.IntroduceIndirectionRefactoring_call_warning_anonymous_cannot_qualify));
1070 expr.setQualifier(unitRewriter.getAST().newSimpleName(currentTypeBinding.getName()));
1073 // do not qualify, only use "this.".
1079 // ********* SMALL HELPERS ********************
1082 * Helper method for finding an IMethod inside a binding hierarchy
1084 private IMethodBinding findMethodBindingInHierarchy(ITypeBinding currentTypeBinding, IMethod methodDeclaration) {
1085 IMethodBinding[] bindings= currentTypeBinding.getDeclaredMethods();
1086 for (int i= 0; i < bindings.length; i++)
1087 if (methodDeclaration.equals(bindings[i].getJavaElement()))
1090 ITypeBinding superClass= currentTypeBinding.getSuperclass();
1091 if (superClass != null) {
1092 IMethodBinding b= findMethodBindingInHierarchy(superClass, methodDeclaration);
1096 ITypeBinding[] interfaces= currentTypeBinding.getInterfaces();
1097 for (int i= 0; i < interfaces.length; i++) {
1098 IMethodBinding b= findMethodBindingInHierarchy(interfaces[i], methodDeclaration);
1106 * Helper method for retrieving a *bottom-up* list of super type bindings
1108 private ITypeBinding[] getTypeAndAllSuperTypes(ITypeBinding type) {
1109 List<ITypeBinding> result= new ArrayList<ITypeBinding>();
1110 collectSuperTypes(type, result);
1111 return result.toArray(new ITypeBinding[result.size()]);
1114 private void collectSuperTypes(ITypeBinding curr, List<ITypeBinding> list) {
1115 if (list.add(curr.getTypeDeclaration())) {
1116 ITypeBinding[] interfaces= curr.getInterfaces();
1117 for (int i= 0; i < interfaces.length; i++) {
1118 collectSuperTypes(interfaces[i], list);
1120 ITypeBinding superClass= curr.getSuperclass();
1121 if (superClass != null) {
1122 collectSuperTypes(superClass, list);
1127 private CompilationUnitRewrite getCachedCURewrite(ICompilationUnit unit) {
1128 CompilationUnitRewrite rewrite= fRewrites.get(unit);
1129 if (rewrite == null) {
1130 rewrite= new CompilationUnitRewrite(unit);
1131 fRewrites.put(unit, rewrite);
1136 private boolean isRewriteKept(ICompilationUnit compilationUnit) {
1137 return fIntermediaryClass.getCompilationUnit().equals(compilationUnit);
1140 private void createChangeAndDiscardRewrite(ICompilationUnit compilationUnit) throws CoreException {
1141 CompilationUnitRewrite rewrite= fRewrites.get(compilationUnit);
1142 if (rewrite != null) {
1143 fTextChangeManager.manage(compilationUnit, rewrite.createChange(true));
1144 fRewrites.remove(compilationUnit);
1148 private SearchResultGroup[] getReferences(IMethod[] methods, IProgressMonitor pm, RefactoringStatus status) throws CoreException {
1149 SearchPattern pattern= RefactoringSearchEngine.createOrPattern(methods, IJavaSearchConstants.REFERENCES);
1150 IJavaSearchScope scope= RefactoringScopeFactory.create(fIntermediaryClass, false);
1151 return RefactoringSearchEngine.search(pattern, scope, pm, status);
1154 private ITypeBinding typeToBinding(IType type, CompilationUnit root) throws JavaModelException {
1155 ASTNode typeNode= typeToDeclaration(type, root);
1156 if (type.isAnonymous()) {
1157 return ((AnonymousClassDeclaration) typeNode).resolveBinding();
1159 return ((AbstractTypeDeclaration) typeNode).resolveBinding();
1163 private ASTNode typeToDeclaration(IType type, CompilationUnit root) throws JavaModelException {
1164 Name intermediateName= (Name) NodeFinder.perform(root, type.getNameRange());
1165 if (type.isAnonymous()) {
1166 return ASTNodes.getParent(intermediateName, AnonymousClassDeclaration.class);
1168 return ASTNodes.getParent(intermediateName, AbstractTypeDeclaration.class);
1172 private ASTNode getEnclosingTypeDeclaration(ASTNode node) {
1173 while (node != null) {
1174 if (node instanceof AbstractTypeDeclaration) {
1176 } else if (node instanceof AnonymousClassDeclaration) {
1179 node= node.getParent();
1184 private ChildListPropertyDescriptor typeToBodyDeclarationProperty(IType type, CompilationUnit root) throws JavaModelException {
1185 ASTNode typeDeclaration= typeToDeclaration(type, root);
1186 if (typeDeclaration instanceof AbstractTypeDeclaration)
1187 return ((AbstractTypeDeclaration) typeDeclaration).getBodyDeclarationsProperty();
1188 else if (typeDeclaration instanceof AnonymousClassDeclaration)
1189 return AnonymousClassDeclaration.BODY_DECLARATIONS_PROPERTY;
1191 Assert.isTrue(false);
1195 private RefactoringStatus createWarningAboutCall(IMember enclosing, ASTNode concreteNode, String message) {
1196 String name= JavaElementLabels.getElementLabel(enclosing, JavaElementLabels.ALL_DEFAULT);
1197 String container= JavaElementLabels.getElementLabel(enclosing.getDeclaringType(), JavaElementLabels.ALL_FULLY_QUALIFIED);
1198 return RefactoringStatus.createWarningStatus(Messages.format(message, new String[] { name, container }), JavaStatusContext.create(enclosing.getCompilationUnit(), concreteNode));
1201 private ITypeBinding getExpressionType(MethodInvocation invocation) {
1202 Expression expression= invocation.getExpression();
1203 ITypeBinding typeBinding= null;
1204 if (expression == null) {
1205 typeBinding= invocation.resolveMethodBinding().getDeclaringClass();
1207 typeBinding= expression.resolveTypeBinding();
1210 Assert.isNotNull(typeBinding, "Type binding of target expression may not be null"); //$NON-NLS-1$
1214 private IFile[] getAllFilesToModify() {
1215 List<ICompilationUnit> cus= new ArrayList<ICompilationUnit>();
1216 cus.addAll(Arrays.asList(fTextChangeManager.getAllCompilationUnits()));
1217 return ResourceUtil.getFiles(cus.toArray(new ICompilationUnit[cus.size()]));
1220 private boolean isStaticTarget() throws JavaModelException {
1221 return Flags.isStatic(fTargetMethod.getFlags());
1224 private IMember getEnclosingInitialSelectionMember() throws JavaModelException {
1225 return (IMember) fSelectionCompilationUnit.getElementAt(fSelectionStart);
1228 private static ASTNode getSelectedNode(ICompilationUnit unit, CompilationUnit root, int offset, int length) {
1232 node= checkNode(NodeFinder.perform(root, offset, length, unit));
1234 node= checkNode(NodeFinder.perform(root, offset, length));
1235 } catch (JavaModelException e) {
1240 return checkNode(NodeFinder.perform(root, offset, length));
1243 private static ASTNode checkNode(ASTNode node) {
1246 if (node.getNodeType() == ASTNode.SIMPLE_NAME) {
1247 node= node.getParent();
1248 } else if (node.getNodeType() == ASTNode.EXPRESSION_STATEMENT) {
1249 node= ((ExpressionStatement) node).getExpression();
1251 switch (node.getNodeType()) {
1252 case ASTNode.METHOD_INVOCATION:
1253 case ASTNode.METHOD_DECLARATION:
1254 case ASTNode.SUPER_METHOD_INVOCATION:
1260 // ***************** VISIBILITY ********************
1262 private ModifierKeyword getNeededVisibility(IMember whoToAdjust, IMember fromWhereToLook) throws JavaModelException {
1263 return fAdjustor.getVisibilityThreshold(fromWhereToLook, whoToAdjust, new NullProgressMonitor());
1266 private RefactoringStatus adjustVisibility(IMember whoToAdjust, IMember fromWhereToLook, IProgressMonitor monitor) throws CoreException {
1267 return adjustVisibility(whoToAdjust, getNeededVisibility(whoToAdjust, fromWhereToLook), true, monitor);
1270 private RefactoringStatus adjustVisibility(IMember whoToAdjust, ModifierKeyword neededVisibility, IProgressMonitor monitor) throws CoreException {
1271 return adjustVisibility(whoToAdjust, neededVisibility, false, monitor);
1274 private RefactoringStatus adjustVisibility(IMember whoToAdjust, ModifierKeyword neededVisibility, boolean alsoIncreaseEnclosing, IProgressMonitor monitor) throws CoreException {
1276 Map<IMember, IncomingMemberVisibilityAdjustment> adjustments;
1277 if (isRewriteKept(whoToAdjust.getCompilationUnit()))
1278 adjustments= fIntermediaryAdjustments;
1280 adjustments= new HashMap<IMember, IncomingMemberVisibilityAdjustment>();
1282 int existingAdjustments= adjustments.size();
1283 addAdjustment(whoToAdjust, neededVisibility, adjustments);
1285 if (alsoIncreaseEnclosing)
1286 while (whoToAdjust.getDeclaringType() != null) {
1287 whoToAdjust= whoToAdjust.getDeclaringType();
1288 addAdjustment(whoToAdjust, neededVisibility, adjustments);
1291 boolean hasNewAdjustments= (adjustments.size() - existingAdjustments) > 0;
1292 if (hasNewAdjustments && ( (whoToAdjust.isReadOnly() || whoToAdjust.isBinary())))
1293 return RefactoringStatus.createErrorStatus(Messages.format(RefactoringCoreMessages.IntroduceIndirectionRefactoring_cannot_update_binary_target_visibility, new String[] { JavaElementLabels
1294 .getElementLabel(whoToAdjust, JavaElementLabels.ALL_DEFAULT) }), JavaStatusContext.create(whoToAdjust));
1296 RefactoringStatus status= new RefactoringStatus();
1298 // Don't create a rewrite if it is not necessary
1299 if (!hasNewAdjustments)
1303 monitor.beginTask(RefactoringCoreMessages.MemberVisibilityAdjustor_adjusting, 2);
1304 Map<ICompilationUnit, CompilationUnitRewrite> rewrites;
1305 if (!isRewriteKept(whoToAdjust.getCompilationUnit())) {
1306 CompilationUnitRewrite rewrite= new CompilationUnitRewrite(whoToAdjust.getCompilationUnit());
1307 rewrite.setResolveBindings(false);
1308 rewrites= new HashMap<ICompilationUnit, CompilationUnitRewrite>();
1309 rewrites.put(whoToAdjust.getCompilationUnit(), rewrite);
1310 status.merge(rewriteVisibility(adjustments, rewrites, new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL)));
1311 rewrite.attachChange((CompilationUnitChange) fTextChangeManager.get(whoToAdjust.getCompilationUnit()), true, new SubProgressMonitor(monitor, 1, SubProgressMonitor.SUPPRESS_SUBTASK_LABEL));
1319 private RefactoringStatus rewriteVisibility(Map<IMember, IncomingMemberVisibilityAdjustment> adjustments, Map<ICompilationUnit, CompilationUnitRewrite> rewrites, IProgressMonitor monitor) throws JavaModelException {
1320 RefactoringStatus status= new RefactoringStatus();
1321 fAdjustor.setRewrites(rewrites);
1322 fAdjustor.setAdjustments(adjustments);
1323 fAdjustor.setStatus(status);
1324 fAdjustor.rewriteVisibility(monitor);
1328 private void addAdjustment(IMember whoToAdjust, ModifierKeyword neededVisibility, Map<IMember, IncomingMemberVisibilityAdjustment> adjustments) throws JavaModelException {
1329 ModifierKeyword currentVisibility= ModifierKeyword.fromFlagValue(JdtFlags.getVisibilityCode(whoToAdjust));
1330 if (MemberVisibilityAdjustor.hasLowerVisibility(currentVisibility, neededVisibility)
1331 && MemberVisibilityAdjustor.needsVisibilityAdjustments(whoToAdjust, neededVisibility, adjustments))
1332 adjustments.put(whoToAdjust, new MemberVisibilityAdjustor.IncomingMemberVisibilityAdjustment(whoToAdjust, neededVisibility,
1333 RefactoringStatus.createWarningStatus(Messages.format(MemberVisibilityAdjustor.getMessage(whoToAdjust), new String[] {
1334 MemberVisibilityAdjustor.getLabel(whoToAdjust), MemberVisibilityAdjustor.getLabel(neededVisibility) }), JavaStatusContext
1335 .create(whoToAdjust))));
1338 private RefactoringStatus initialize(JavaRefactoringArguments arguments) {
1339 String handle= arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT);
1340 if (handle != null) {
1341 final IJavaElement element= JavaRefactoringDescriptorUtil.handleToElement(arguments.getProject(), handle, false);
1342 if (element == null || !element.exists() || element.getElementType() != IJavaElement.METHOD)
1343 return JavaRefactoringDescriptorUtil.createInputFatalStatus(element, getName(), IJavaRefactorings.INTRODUCE_INDIRECTION);
1345 fTargetMethod= (IMethod) element;
1347 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_INPUT));
1348 handle= arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + 1);
1349 if (handle != null) {
1350 final IJavaElement element= JavaRefactoringDescriptorUtil.handleToElement(arguments.getProject(), handle, false);
1351 if (element == null || !element.exists() || element.getElementType() != IJavaElement.TYPE)
1352 return JavaRefactoringDescriptorUtil.createInputFatalStatus(element, getName(), IJavaRefactorings.INTRODUCE_INDIRECTION);
1354 fIntermediaryClass= (IType) element;
1356 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_ELEMENT + 1));
1357 final String references= arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_REFERENCES);
1358 if (references != null) {
1359 fUpdateReferences= Boolean.valueOf(references).booleanValue();
1361 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_REFERENCES));
1362 final String name= arguments.getAttribute(JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME);
1363 if (name != null && !"".equals(name)) //$NON-NLS-1$
1364 return setIntermediaryMethodName(name);
1366 return RefactoringStatus.createFatalErrorStatus(Messages.format(RefactoringCoreMessages.InitializableRefactoring_argument_not_exist, JavaRefactoringDescriptorUtil.ATTRIBUTE_NAME));