Merge branch 'master' into changeSignature
[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.rename.RenameUtil;
35 import com.intellij.refactoring.ui.ConflictsDialog;
36 import com.intellij.refactoring.util.CanonicalTypes;
37 import com.intellij.usageView.UsageInfo;
38 import com.intellij.usageView.UsageViewDescriptor;
39 import com.intellij.usageView.UsageViewUtil;
40 import com.intellij.util.IncorrectOperationException;
41 import com.intellij.util.VisibilityUtil;
42 import com.intellij.util.containers.HashSet;
43 import com.intellij.util.containers.MultiMap;
44 import org.jetbrains.annotations.NotNull;
45 import org.jetbrains.annotations.Nullable;
46
47 import java.util.*;
48
49 public class ChangeSignatureProcessor extends BaseRefactoringProcessor {
50   private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.changeSignature.ChangeSignatureProcessor");
51
52   private final JavaChangeInfoImpl myChangeInfo;
53   private final PsiManager myManager;
54
55   public ChangeSignatureProcessor(Project project,
56                                   PsiMethod method,
57                                   final boolean generateDelegate,
58                                   @Modifier String newVisibility,
59                                   String newName,
60                                   PsiType newType,
61                                   @NotNull ParameterInfoImpl[] parameterInfo) {
62     this(project, method, generateDelegate, newVisibility, newName,
63          newType != null ? CanonicalTypes.createTypeWrapper(newType) : null,
64          parameterInfo, null, null, null);
65   }
66
67   public ChangeSignatureProcessor(Project project,
68                                   PsiMethod method,
69                                   final boolean generateDelegate,
70                                   String newVisibility,
71                                   String newName,
72                                   PsiType newType,
73                                   ParameterInfoImpl[] parameterInfo,
74                                   ThrownExceptionInfo[] exceptionInfos) {
75     this(project, method, generateDelegate, newVisibility, newName,
76          newType != null ? CanonicalTypes.createTypeWrapper(newType) : null,
77          parameterInfo, exceptionInfos, null, null);
78   }
79
80   public ChangeSignatureProcessor(Project project,
81                                   PsiMethod method,
82                                   boolean generateDelegate,
83                                   @Modifier String newVisibility,
84                                   String newName,
85                                   CanonicalTypes.Type newType,
86                                   @NotNull ParameterInfoImpl[] parameterInfo,
87                                   ThrownExceptionInfo[] thrownExceptions,
88                                   Set<PsiMethod> propagateParametersMethods,
89                                   Set<PsiMethod> propagateExceptionsMethods) {
90     super(project);
91     myManager = PsiManager.getInstance(project);
92
93     Set<PsiMethod> myPropagateParametersMethods =
94       propagateParametersMethods != null ? propagateParametersMethods : new HashSet<PsiMethod>();
95     Set<PsiMethod> myPropagateExceptionsMethods =
96       propagateExceptionsMethods != null ? propagateExceptionsMethods : new HashSet<PsiMethod>();
97
98     LOG.assertTrue(method.isValid());
99     if (newVisibility == null) {
100       newVisibility = VisibilityUtil.getVisibilityModifier(method.getModifierList());
101     } 
102
103     myChangeInfo = new JavaChangeInfoImpl(newVisibility, method, newName, newType, parameterInfo, thrownExceptions, generateDelegate, myPropagateParametersMethods, myPropagateExceptionsMethods);
104     LOG.assertTrue(myChangeInfo.getMethod().isValid());
105   }
106
107   protected UsageViewDescriptor createUsageViewDescriptor(UsageInfo[] usages) {
108     return new ChangeSignatureViewDescriptor(myChangeInfo.getMethod());
109   }
110
111   @NotNull
112   protected UsageInfo[] findUsages() {
113     List<UsageInfo> infos = new ArrayList<UsageInfo>();
114
115     final ChangeSignatureUsageProcessor[] processors = ChangeSignatureUsageProcessor.EP_NAME.getExtensions();
116     for (ChangeSignatureUsageProcessor processor : processors) {
117       infos.addAll(Arrays.asList(processor.findUsages(myChangeInfo)));
118     }
119     return infos.toArray(new UsageInfo[infos.size()]);
120   }
121
122
123   protected void refreshElements(PsiElement[] elements) {
124     boolean condition = elements.length == 1 && elements[0] instanceof PsiMethod;
125     LOG.assertTrue(condition);
126     myChangeInfo.updateMethod((PsiMethod) elements[0]);
127   }
128
129   protected boolean preprocessUsages(Ref<UsageInfo[]> refUsages) {
130     MultiMap<PsiElement, String> conflictDescriptions = new MultiMap<PsiElement, String>();
131     for (ChangeSignatureUsageProcessor usageProcessor : ChangeSignatureUsageProcessor.EP_NAME.getExtensions()) {
132       final MultiMap<PsiElement, String> conflicts = usageProcessor.findConflicts(myChangeInfo, refUsages);
133       for (PsiElement key : conflicts.keySet()) {
134         Collection<String> collection = conflictDescriptions.get(key);
135         if (collection.size() == 0) collection = new HashSet<String>();
136         collection.addAll(conflicts.get(key));
137         conflictDescriptions.put(key, collection);
138       }
139     }
140
141     final UsageInfo[] usagesIn = refUsages.get();
142     Set<UsageInfo> usagesSet = new HashSet<UsageInfo>(Arrays.asList(usagesIn));
143     RenameUtil.removeConflictUsages(usagesSet);
144     if (myPrepareSuccessfulSwingThreadCallback != null && !conflictDescriptions.isEmpty()) {
145       ConflictsDialog dialog = new ConflictsDialog(myProject, conflictDescriptions, new Runnable(){
146         public void run() {
147           execute(usagesIn);
148         }
149       });
150       dialog.show();
151       if (!dialog.isOK()) {
152         if (dialog.isShowConflicts()) prepareSuccessful();
153         return false;
154       }
155     }
156
157     if (myChangeInfo.isReturnTypeChanged()) {
158       askToRemoveCovariantOverriders(usagesSet);
159     }
160
161     refUsages.set(usagesSet.toArray(new UsageInfo[usagesSet.size()]));
162     prepareSuccessful();
163     return true;
164   }
165
166   private void askToRemoveCovariantOverriders(Set<UsageInfo> usages) {
167     if (PsiUtil.isLanguageLevel5OrHigher(myChangeInfo.getMethod())) {
168       List<UsageInfo> covariantOverriderInfos = new ArrayList<UsageInfo>();
169       for (UsageInfo usageInfo : usages) {
170         if (usageInfo instanceof OverriderUsageInfo) {
171           final OverriderUsageInfo info = (OverriderUsageInfo)usageInfo;
172           PsiMethod overrider = info.getElement();
173           PsiMethod baseMethod = info.getBaseMethod();
174           PsiSubstitutor substitutor = calculateSubstitutor(overrider, baseMethod);
175           PsiType type;
176           try {
177             type = substitutor.substitute(myChangeInfo.newReturnType.getType(myChangeInfo.getMethod(), myManager));
178           }
179           catch (IncorrectOperationException e) {
180             LOG.error(e);
181             return;
182           }
183           final PsiType overriderType = overrider.getReturnType();
184           if (overriderType != null && type.isAssignableFrom(overriderType)) {
185             covariantOverriderInfos.add(usageInfo);
186           }
187         }
188       }
189
190       // to be able to do filtering
191       preprocessCovariantOverriders(covariantOverriderInfos);
192
193       if (!covariantOverriderInfos.isEmpty()) {
194         if (ApplicationManager.getApplication().isUnitTestMode() || !isProcessCovariantOverriders()) {
195           for (UsageInfo usageInfo : covariantOverriderInfos) {
196             usages.remove(usageInfo);
197           }
198         }
199       }
200     }
201   }
202
203   protected void preprocessCovariantOverriders(final List<UsageInfo> covariantOverriderInfos) {
204   }
205
206   protected boolean isProcessCovariantOverriders() {
207     return Messages
208       .showYesNoDialog(myProject, RefactoringBundle.message("do.you.want.to.process.overriding.methods.with.covariant.return.type"),
209                        JavaChangeSignatureHandler.REFACTORING_NAME, Messages.getQuestionIcon())
210            == DialogWrapper.OK_EXIT_CODE;
211   }
212
213   protected void performRefactoring(UsageInfo[] usages) {
214     try {
215       final ChangeSignatureUsageProcessor[] processors = ChangeSignatureUsageProcessor.EP_NAME.getExtensions();
216
217       for (UsageInfo usage : usages) {
218         for (ChangeSignatureUsageProcessor processor : processors) {
219           if (processor.processUsage(myChangeInfo, usage, true, usages)) break;
220         }
221       }
222
223       LOG.assertTrue(myChangeInfo.getMethod().isValid());
224       for (ChangeSignatureUsageProcessor processor : processors) {
225         if (processor.processPrimaryMethod(myChangeInfo)) break;
226       }
227
228       for (UsageInfo usage : usages) {
229         for (ChangeSignatureUsageProcessor processor : processors) {
230           if (processor.processUsage(myChangeInfo, usage, false, usages)) {
231             break;
232           }
233         }
234       }
235
236       LOG.assertTrue(myChangeInfo.getMethod().isValid());
237     }
238     catch (IncorrectOperationException e) {
239       LOG.error(e);
240     }
241   }
242
243   public static void makeEmptyBody(final PsiElementFactory factory, final PsiMethod delegate) throws IncorrectOperationException {
244     PsiCodeBlock body = delegate.getBody();
245     if (body != null) {
246       body.replace(factory.createCodeBlock());
247     }
248     else {
249       delegate.add(factory.createCodeBlock());
250     }
251     PsiUtil.setModifierProperty(delegate, PsiModifier.ABSTRACT, false);
252   }
253
254   @Nullable
255   public static PsiCallExpression addDelegatingCallTemplate(final PsiMethod delegate, final String newName) throws IncorrectOperationException {
256     Project project = delegate.getProject();
257     PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
258     PsiCodeBlock body = delegate.getBody();
259     assert body != null;
260     final PsiCallExpression callExpression;
261     if (delegate.isConstructor()) {
262       PsiElement callStatement = factory.createStatementFromText("this();", null);
263       callStatement = CodeStyleManager.getInstance(project).reformat(callStatement);
264       callStatement = body.add(callStatement);
265       callExpression = (PsiCallExpression)((PsiExpressionStatement) callStatement).getExpression();
266     } else {
267       if (PsiType.VOID.equals(delegate.getReturnType())) {
268         PsiElement callStatement = factory.createStatementFromText(newName + "();", null);
269         callStatement = CodeStyleManager.getInstance(project).reformat(callStatement);
270         callStatement = body.add(callStatement);
271         callExpression = (PsiCallExpression)((PsiExpressionStatement) callStatement).getExpression();
272       }
273       else {
274         PsiElement callStatement = factory.createStatementFromText("return " + newName + "();", null);
275         callStatement = CodeStyleManager.getInstance(project).reformat(callStatement);
276         callStatement = body.add(callStatement);
277         callExpression = (PsiCallExpression)((PsiReturnStatement) callStatement).getReturnValue();
278       }
279     }
280     return callExpression;
281   }
282
283   protected String getCommandName() {
284     return RefactoringBundle.message("changing.signature.of.0", UsageViewUtil.getDescriptiveName(myChangeInfo.getMethod()));
285   }
286
287   public static PsiSubstitutor calculateSubstitutor(PsiMethod derivedMethod, PsiMethod baseMethod) {
288     PsiSubstitutor substitutor;
289     if (derivedMethod.getManager().areElementsEquivalent(derivedMethod, baseMethod)) {
290       substitutor = PsiSubstitutor.EMPTY;
291     } else {
292       final PsiClass baseClass = baseMethod.getContainingClass();
293       final PsiClass derivedClass = derivedMethod.getContainingClass();
294       if(baseClass != null && derivedClass != null && InheritanceUtil.isInheritorOrSelf(derivedClass, baseClass, true)) {
295         final PsiSubstitutor superClassSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(baseClass, derivedClass, PsiSubstitutor.EMPTY);
296         final MethodSignature superMethodSignature = baseMethod.getSignature(superClassSubstitutor);
297         final MethodSignature methodSignature = derivedMethod.getSignature(PsiSubstitutor.EMPTY);
298         final PsiSubstitutor superMethodSubstitutor = MethodSignatureUtil.getSuperMethodSignatureSubstitutor(methodSignature, superMethodSignature);
299         substitutor = superMethodSubstitutor != null ? superMethodSubstitutor : superClassSubstitutor;
300       } else {
301         substitutor = PsiSubstitutor.EMPTY;
302       }
303     }
304     return substitutor;
305   }
306
307
308
309 }