GroovyChangeSignatureUsageProcessor
[idea/community.git] / java / java-impl / src / com / intellij / refactoring / changeSignature / ChangeSignatureProcessor.java
1 /*
2  * Copyright 2000-2009 JetBrains s.r.o.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /**
18  * created at Sep 17, 2001
19  * @author Jeka
20  */
21 package com.intellij.refactoring.changeSignature;
22
23 import com.intellij.openapi.application.ApplicationManager;
24 import com.intellij.openapi.diagnostic.Logger;
25 import com.intellij.openapi.project.Project;
26 import com.intellij.openapi.ui.DialogWrapper;
27 import com.intellij.openapi.ui.Messages;
28 import com.intellij.openapi.util.Ref;
29 import com.intellij.psi.*;
30 import com.intellij.psi.codeStyle.CodeStyleManager;
31 import com.intellij.psi.util.*;
32 import com.intellij.refactoring.BaseRefactoringProcessor;
33 import com.intellij.refactoring.RefactoringBundle;
34 import com.intellij.refactoring.ui.ConflictsDialog;
35 import com.intellij.refactoring.util.CanonicalTypes;
36 import com.intellij.usageView.UsageInfo;
37 import com.intellij.usageView.UsageViewDescriptor;
38 import com.intellij.usageView.UsageViewUtil;
39 import com.intellij.util.IncorrectOperationException;
40 import com.intellij.util.VisibilityUtil;
41 import com.intellij.util.containers.HashSet;
42 import com.intellij.util.containers.MultiMap;
43 import org.jetbrains.annotations.NotNull;
44 import org.jetbrains.annotations.Nullable;
45
46 import java.util.*;
47
48 public class ChangeSignatureProcessor extends BaseRefactoringProcessor {
49   private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.changeSignature.ChangeSignatureProcessor");
50
51   private final JavaChangeInfoImpl myChangeInfo;
52   private final PsiManager myManager;
53
54   public ChangeSignatureProcessor(Project project,
55                                   PsiMethod method,
56                                   final boolean generateDelegate,
57                                   @Modifier String newVisibility,
58                                   String newName,
59                                   PsiType newType,
60                                   @NotNull ParameterInfoImpl[] parameterInfo) {
61     this(project, method, generateDelegate, newVisibility, newName,
62          newType != null ? CanonicalTypes.createTypeWrapper(newType) : null,
63          parameterInfo, null, null, null);
64   }
65
66   public ChangeSignatureProcessor(Project project,
67                                   PsiMethod method,
68                                   final boolean generateDelegate,
69                                   String newVisibility,
70                                   String newName,
71                                   PsiType newType,
72                                   ParameterInfoImpl[] parameterInfo,
73                                   ThrownExceptionInfo[] exceptionInfos) {
74     this(project, method, generateDelegate, newVisibility, newName,
75          newType != null ? CanonicalTypes.createTypeWrapper(newType) : null,
76          parameterInfo, exceptionInfos, null, null);
77   }
78
79   public ChangeSignatureProcessor(Project project,
80                                   PsiMethod method,
81                                   boolean generateDelegate,
82                                   @Modifier String newVisibility,
83                                   String newName,
84                                   CanonicalTypes.Type newType,
85                                   @NotNull ParameterInfoImpl[] parameterInfo,
86                                   ThrownExceptionInfo[] thrownExceptions,
87                                   Set<PsiMethod> propagateParametersMethods,
88                                   Set<PsiMethod> propagateExceptionsMethods) {
89     super(project);
90     myManager = PsiManager.getInstance(project);
91
92     Set<PsiMethod> myPropagateParametersMethods =
93       propagateParametersMethods != null ? propagateParametersMethods : new HashSet<PsiMethod>();
94     Set<PsiMethod> myPropagateExceptionsMethods =
95       propagateExceptionsMethods != null ? propagateExceptionsMethods : new HashSet<PsiMethod>();
96
97     LOG.assertTrue(method.isValid());
98     if (newVisibility == null) {
99       newVisibility = VisibilityUtil.getVisibilityModifier(method.getModifierList());
100     } 
101
102     myChangeInfo = new JavaChangeInfoImpl(newVisibility, method, newName, newType, parameterInfo, thrownExceptions, generateDelegate, myPropagateParametersMethods, myPropagateExceptionsMethods);
103     LOG.assertTrue(myChangeInfo.getMethod().isValid());
104   }
105
106   protected UsageViewDescriptor createUsageViewDescriptor(UsageInfo[] usages) {
107     return new ChangeSignatureViewDescriptor(myChangeInfo.getMethod());
108   }
109
110   @NotNull
111   protected UsageInfo[] findUsages() {
112     List<UsageInfo> infos = new ArrayList<UsageInfo>();
113
114     final ChangeSignatureUsageProcessor[] processors = ChangeSignatureUsageProcessor.EP_NAME.getExtensions();
115     for (ChangeSignatureUsageProcessor processor : processors) {
116       infos.addAll(Arrays.asList(processor.findUsages(myChangeInfo)));
117     }
118     return infos.toArray(new UsageInfo[infos.size()]);
119   }
120
121
122   protected void refreshElements(PsiElement[] elements) {
123     boolean condition = elements.length == 1 && elements[0] instanceof PsiMethod;
124     LOG.assertTrue(condition);
125     myChangeInfo.updateMethod((PsiMethod) elements[0]);
126   }
127
128   protected boolean preprocessUsages(Ref<UsageInfo[]> refUsages) {
129     MultiMap<PsiElement, String> conflictDescriptions = new MultiMap<PsiElement, String>();
130     for (ChangeSignatureUsageProcessor usageProcessor : ChangeSignatureUsageProcessor.EP_NAME.getExtensions()) {
131       final MultiMap<PsiElement, String> conflicts = usageProcessor.findConflicts(myChangeInfo, refUsages);
132       for (PsiElement key : conflicts.keySet()) {
133         Collection<String> collection = conflictDescriptions.get(key);
134         if (collection.size() == 0) collection = new HashSet<String>();
135         collection.addAll(conflicts.get(key));
136         conflictDescriptions.put(key, collection);
137       }
138     }
139
140     final Set<UsageInfo> usagesSet = new HashSet<UsageInfo>(Arrays.asList(refUsages.get()));
141     if (myPrepareSuccessfulSwingThreadCallback != null && !conflictDescriptions.isEmpty()) {
142       ConflictsDialog dialog = new ConflictsDialog(myProject, conflictDescriptions);
143       dialog.show();
144       if (!dialog.isOK()) {
145         if (dialog.isShowConflicts()) prepareSuccessful();
146         return false;
147       }
148     }
149
150     if (myChangeInfo.isReturnTypeChanged()) {
151       askToRemoveCovariantOverriders(usagesSet);
152     }
153
154     refUsages.set(usagesSet.toArray(new UsageInfo[usagesSet.size()]));
155     prepareSuccessful();
156     return true;
157   }
158
159   private void askToRemoveCovariantOverriders(Set<UsageInfo> usages) {
160     if (PsiUtil.isLanguageLevel5OrHigher(myChangeInfo.getMethod())) {
161       List<UsageInfo> covariantOverriderInfos = new ArrayList<UsageInfo>();
162       for (UsageInfo usageInfo : usages) {
163         if (usageInfo instanceof OverriderUsageInfo) {
164           final OverriderUsageInfo info = (OverriderUsageInfo)usageInfo;
165           PsiMethod overrider = info.getElement();
166           PsiMethod baseMethod = info.getBaseMethod();
167           PsiSubstitutor substitutor = calculateSubstitutor(overrider, baseMethod);
168           PsiType type;
169           try {
170             type = substitutor.substitute(myChangeInfo.newReturnType.getType(myChangeInfo.getMethod(), myManager));
171           }
172           catch (IncorrectOperationException e) {
173             LOG.error(e);
174             return;
175           }
176           final PsiType overriderType = overrider.getReturnType();
177           if (overriderType != null && type.isAssignableFrom(overriderType)) {
178             covariantOverriderInfos.add(usageInfo);
179           }
180         }
181       }
182
183       // to be able to do filtering
184       preprocessCovariantOverriders(covariantOverriderInfos);
185
186       if (!covariantOverriderInfos.isEmpty()) {
187         if (ApplicationManager.getApplication().isUnitTestMode() || !isProcessCovariantOverriders()) {
188           for (UsageInfo usageInfo : covariantOverriderInfos) {
189             usages.remove(usageInfo);
190           }
191         }
192       }
193     }
194   }
195
196   protected void preprocessCovariantOverriders(final List<UsageInfo> covariantOverriderInfos) {
197   }
198
199   protected boolean isProcessCovariantOverriders() {
200     return Messages
201       .showYesNoDialog(myProject, RefactoringBundle.message("do.you.want.to.process.overriding.methods.with.covariant.return.type"),
202                        JavaChangeSignatureHandler.REFACTORING_NAME, Messages.getQuestionIcon())
203            == DialogWrapper.OK_EXIT_CODE;
204   }
205
206   protected void performRefactoring(UsageInfo[] usages) {
207     try {
208       final ChangeSignatureUsageProcessor[] processors = ChangeSignatureUsageProcessor.EP_NAME.getExtensions();
209
210       for (UsageInfo usage : usages) {
211         for (ChangeSignatureUsageProcessor processor : processors) {
212           if (processor.processUsage(myChangeInfo, usage, true, usages)) break;
213         }
214       }
215
216       LOG.assertTrue(myChangeInfo.getMethod().isValid());
217       for (ChangeSignatureUsageProcessor processor : processors) {
218         if (processor.processPrimaryMethod(myChangeInfo)) break;
219       }
220
221       for (UsageInfo usage : usages) {
222         for (ChangeSignatureUsageProcessor processor : processors) {
223           if (processor.processUsage(myChangeInfo, usage, false, usages)) {
224             break;
225           }
226         }
227       }
228
229       LOG.assertTrue(myChangeInfo.getMethod().isValid());
230     }
231     catch (IncorrectOperationException e) {
232       LOG.error(e);
233     }
234   }
235
236   public static void makeEmptyBody(final PsiElementFactory factory, final PsiMethod delegate) throws IncorrectOperationException {
237     PsiCodeBlock body = delegate.getBody();
238     if (body != null) {
239       body.replace(factory.createCodeBlock());
240     }
241     else {
242       delegate.add(factory.createCodeBlock());
243     }
244     PsiUtil.setModifierProperty(delegate, PsiModifier.ABSTRACT, false);
245   }
246
247   @Nullable
248   public static PsiCallExpression addDelegatingCallTemplate(final PsiMethod delegate, final String newName) throws IncorrectOperationException {
249     Project project = delegate.getProject();
250     PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
251     PsiCodeBlock body = delegate.getBody();
252     assert body != null;
253     final PsiCallExpression callExpression;
254     if (delegate.isConstructor()) {
255       PsiElement callStatement = factory.createStatementFromText("this();", null);
256       callStatement = CodeStyleManager.getInstance(project).reformat(callStatement);
257       callStatement = body.add(callStatement);
258       callExpression = (PsiCallExpression)((PsiExpressionStatement) callStatement).getExpression();
259     } else {
260       if (PsiType.VOID.equals(delegate.getReturnType())) {
261         PsiElement callStatement = factory.createStatementFromText(newName + "();", null);
262         callStatement = CodeStyleManager.getInstance(project).reformat(callStatement);
263         callStatement = body.add(callStatement);
264         callExpression = (PsiCallExpression)((PsiExpressionStatement) callStatement).getExpression();
265       }
266       else {
267         PsiElement callStatement = factory.createStatementFromText("return " + newName + "();", null);
268         callStatement = CodeStyleManager.getInstance(project).reformat(callStatement);
269         callStatement = body.add(callStatement);
270         callExpression = (PsiCallExpression)((PsiReturnStatement) callStatement).getReturnValue();
271       }
272     }
273     return callExpression;
274   }
275
276   protected String getCommandName() {
277     return RefactoringBundle.message("changing.signature.of.0", UsageViewUtil.getDescriptiveName(myChangeInfo.getMethod()));
278   }
279
280   public static PsiSubstitutor calculateSubstitutor(PsiMethod derivedMethod, PsiMethod baseMethod) {
281     PsiSubstitutor substitutor;
282     if (derivedMethod.getManager().areElementsEquivalent(derivedMethod, baseMethod)) {
283       substitutor = PsiSubstitutor.EMPTY;
284     } else {
285       final PsiClass baseClass = baseMethod.getContainingClass();
286       final PsiClass derivedClass = derivedMethod.getContainingClass();
287       if(baseClass != null && derivedClass != null && InheritanceUtil.isInheritorOrSelf(derivedClass, baseClass, true)) {
288         final PsiSubstitutor superClassSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(baseClass, derivedClass, PsiSubstitutor.EMPTY);
289         final MethodSignature superMethodSignature = baseMethod.getSignature(superClassSubstitutor);
290         final MethodSignature methodSignature = derivedMethod.getSignature(PsiSubstitutor.EMPTY);
291         final PsiSubstitutor superMethodSubstitutor = MethodSignatureUtil.getSuperMethodSignatureSubstitutor(methodSignature, superMethodSignature);
292         substitutor = superMethodSubstitutor != null ? superMethodSubstitutor : superClassSubstitutor;
293       } else {
294         substitutor = PsiSubstitutor.EMPTY;
295       }
296     }
297     return substitutor;
298   }
299
300
301
302 }