2 * Copyright 2000-2009 JetBrains s.r.o.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * created at Sep 17, 2001
21 package com.intellij.refactoring.changeSignature;
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;
49 public class ChangeSignatureProcessor extends BaseRefactoringProcessor {
50 private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.changeSignature.ChangeSignatureProcessor");
52 private final JavaChangeInfoImpl myChangeInfo;
53 private final PsiManager myManager;
55 public ChangeSignatureProcessor(Project project,
57 final boolean generateDelegate,
58 @Modifier String newVisibility,
61 @NotNull ParameterInfoImpl[] parameterInfo) {
62 this(project, method, generateDelegate, newVisibility, newName,
63 newType != null ? CanonicalTypes.createTypeWrapper(newType) : null,
64 parameterInfo, null, null, null);
67 public ChangeSignatureProcessor(Project project,
69 final boolean generateDelegate,
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);
80 public ChangeSignatureProcessor(Project project,
82 boolean generateDelegate,
83 @Modifier String newVisibility,
85 CanonicalTypes.Type newType,
86 @NotNull ParameterInfoImpl[] parameterInfo,
87 ThrownExceptionInfo[] thrownExceptions,
88 Set<PsiMethod> propagateParametersMethods,
89 Set<PsiMethod> propagateExceptionsMethods) {
91 myManager = PsiManager.getInstance(project);
93 Set<PsiMethod> myPropagateParametersMethods =
94 propagateParametersMethods != null ? propagateParametersMethods : new HashSet<PsiMethod>();
95 Set<PsiMethod> myPropagateExceptionsMethods =
96 propagateExceptionsMethods != null ? propagateExceptionsMethods : new HashSet<PsiMethod>();
98 LOG.assertTrue(method.isValid());
99 if (newVisibility == null) {
100 newVisibility = VisibilityUtil.getVisibilityModifier(method.getModifierList());
103 myChangeInfo = new JavaChangeInfoImpl(newVisibility, method, newName, newType, parameterInfo, thrownExceptions, generateDelegate, myPropagateParametersMethods, myPropagateExceptionsMethods);
104 LOG.assertTrue(myChangeInfo.getMethod().isValid());
107 protected UsageViewDescriptor createUsageViewDescriptor(UsageInfo[] usages) {
108 return new ChangeSignatureViewDescriptor(myChangeInfo.getMethod());
112 protected UsageInfo[] findUsages() {
113 List<UsageInfo> infos = new ArrayList<UsageInfo>();
115 final ChangeSignatureUsageProcessor[] processors = ChangeSignatureUsageProcessor.EP_NAME.getExtensions();
116 for (ChangeSignatureUsageProcessor processor : processors) {
117 infos.addAll(Arrays.asList(processor.findUsages(myChangeInfo)));
119 return infos.toArray(new UsageInfo[infos.size()]);
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]);
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);
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(){
151 if (!dialog.isOK()) {
152 if (dialog.isShowConflicts()) prepareSuccessful();
157 if (myChangeInfo.isReturnTypeChanged()) {
158 askToRemoveCovariantOverriders(usagesSet);
161 refUsages.set(usagesSet.toArray(new UsageInfo[usagesSet.size()]));
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);
177 type = substitutor.substitute(myChangeInfo.newReturnType.getType(myChangeInfo.getMethod(), myManager));
179 catch (IncorrectOperationException e) {
183 final PsiType overriderType = overrider.getReturnType();
184 if (overriderType != null && type.isAssignableFrom(overriderType)) {
185 covariantOverriderInfos.add(usageInfo);
190 // to be able to do filtering
191 preprocessCovariantOverriders(covariantOverriderInfos);
193 if (!covariantOverriderInfos.isEmpty()) {
194 if (ApplicationManager.getApplication().isUnitTestMode() || !isProcessCovariantOverriders()) {
195 for (UsageInfo usageInfo : covariantOverriderInfos) {
196 usages.remove(usageInfo);
203 protected void preprocessCovariantOverriders(final List<UsageInfo> covariantOverriderInfos) {
206 protected boolean isProcessCovariantOverriders() {
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;
213 protected void performRefactoring(UsageInfo[] usages) {
215 final ChangeSignatureUsageProcessor[] processors = ChangeSignatureUsageProcessor.EP_NAME.getExtensions();
217 for (UsageInfo usage : usages) {
218 for (ChangeSignatureUsageProcessor processor : processors) {
219 if (processor.processUsage(myChangeInfo, usage, true, usages)) break;
223 LOG.assertTrue(myChangeInfo.getMethod().isValid());
224 for (ChangeSignatureUsageProcessor processor : processors) {
225 if (processor.processPrimaryMethod(myChangeInfo)) break;
228 for (UsageInfo usage : usages) {
229 for (ChangeSignatureUsageProcessor processor : processors) {
230 if (processor.processUsage(myChangeInfo, usage, false, usages)) {
236 LOG.assertTrue(myChangeInfo.getMethod().isValid());
238 catch (IncorrectOperationException e) {
243 public static void makeEmptyBody(final PsiElementFactory factory, final PsiMethod delegate) throws IncorrectOperationException {
244 PsiCodeBlock body = delegate.getBody();
246 body.replace(factory.createCodeBlock());
249 delegate.add(factory.createCodeBlock());
251 PsiUtil.setModifierProperty(delegate, PsiModifier.ABSTRACT, false);
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();
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();
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();
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();
280 return callExpression;
283 protected String getCommandName() {
284 return RefactoringBundle.message("changing.signature.of.0", UsageViewUtil.getDescriptiveName(myChangeInfo.getMethod()));
287 public static PsiSubstitutor calculateSubstitutor(PsiMethod derivedMethod, PsiMethod baseMethod) {
288 PsiSubstitutor substitutor;
289 if (derivedMethod.getManager().areElementsEquivalent(derivedMethod, baseMethod)) {
290 substitutor = PsiSubstitutor.EMPTY;
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;
301 substitutor = PsiSubstitutor.EMPTY;