blob: 0a831d2d8efb9b0d5a7f7748e503130fa63e48b9 [file] [log] [blame]
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.refactoring.changeSignature;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.CodeStyleManager;
import com.intellij.psi.util.*;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.rename.RenameUtil;
import com.intellij.refactoring.ui.ConflictsDialog;
import com.intellij.refactoring.util.CanonicalTypes;
import com.intellij.usageView.UsageInfo;
import com.intellij.usageView.UsageViewDescriptor;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.VisibilityUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashSet;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import static com.intellij.util.ObjectUtils.assertNotNull;
/**
* @author Jeka
* @since Sep 17, 2001
*/
public class ChangeSignatureProcessor extends ChangeSignatureProcessorBase {
private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.changeSignature.ChangeSignatureProcessor");
public ChangeSignatureProcessor(Project project,
PsiMethod method,
final boolean generateDelegate,
@PsiModifier.ModifierConstant String newVisibility,
String newName,
PsiType newType,
@NotNull ParameterInfoImpl[] parameterInfo) {
this(project, method, generateDelegate, newVisibility, newName,
newType != null ? CanonicalTypes.createTypeWrapper(newType) : null,
parameterInfo, null, null, null);
}
public ChangeSignatureProcessor(Project project,
PsiMethod method,
final boolean generateDelegate,
@PsiModifier.ModifierConstant String newVisibility,
String newName,
PsiType newType,
ParameterInfoImpl[] parameterInfo,
ThrownExceptionInfo[] exceptionInfos) {
this(project, method, generateDelegate, newVisibility, newName,
newType != null ? CanonicalTypes.createTypeWrapper(newType) : null,
parameterInfo, exceptionInfos, null, null);
}
public ChangeSignatureProcessor(Project project,
PsiMethod method,
boolean generateDelegate,
@PsiModifier.ModifierConstant String newVisibility,
String newName,
CanonicalTypes.Type newType,
@NotNull ParameterInfoImpl[] parameterInfo,
ThrownExceptionInfo[] thrownExceptions,
Set<PsiMethod> propagateParametersMethods,
Set<PsiMethod> propagateExceptionsMethods) {
this(project, generateChangeInfo(method, generateDelegate, newVisibility, newName, newType, parameterInfo, thrownExceptions,
propagateParametersMethods, propagateExceptionsMethods));
}
public ChangeSignatureProcessor(Project project, final JavaChangeInfo changeInfo) {
super(project, changeInfo);
LOG.assertTrue(myChangeInfo.getMethod().isValid());
}
private static JavaChangeInfo generateChangeInfo(PsiMethod method,
boolean generateDelegate,
@PsiModifier.ModifierConstant String newVisibility,
String newName,
CanonicalTypes.Type newType,
@NotNull ParameterInfoImpl[] parameterInfo,
ThrownExceptionInfo[] thrownExceptions,
Set<PsiMethod> propagateParametersMethods,
Set<PsiMethod> propagateExceptionsMethods) {
LOG.assertTrue(method.isValid());
if (propagateParametersMethods == null) {
propagateParametersMethods = ContainerUtil.newHashSet();
}
if (propagateExceptionsMethods == null) {
propagateExceptionsMethods = ContainerUtil.newHashSet();
}
if (newVisibility == null) {
newVisibility = VisibilityUtil.getVisibilityModifier(method.getModifierList());
}
return new JavaChangeInfoImpl(newVisibility, method, newName, newType, parameterInfo, thrownExceptions, generateDelegate,
propagateParametersMethods, propagateExceptionsMethods);
}
@NotNull
protected UsageViewDescriptor createUsageViewDescriptor(UsageInfo[] usages) {
return new ChangeSignatureViewDescriptor(getChangeInfo().getMethod());
}
@Override
public JavaChangeInfoImpl getChangeInfo() {
return (JavaChangeInfoImpl)super.getChangeInfo();
}
protected void refreshElements(PsiElement[] elements) {
boolean condition = elements.length == 1 && elements[0] instanceof PsiMethod;
LOG.assertTrue(condition);
getChangeInfo().updateMethod((PsiMethod) elements[0]);
}
protected boolean preprocessUsages(Ref<UsageInfo[]> refUsages) {
for (ChangeSignatureUsageProcessor processor : ChangeSignatureUsageProcessor.EP_NAME.getExtensions()) {
if (!processor.setupDefaultValues(myChangeInfo, refUsages, myProject)) return false;
}
MultiMap<PsiElement, String> conflictDescriptions = new MultiMap<PsiElement, String>();
for (ChangeSignatureUsageProcessor usageProcessor : ChangeSignatureUsageProcessor.EP_NAME.getExtensions()) {
final MultiMap<PsiElement, String> conflicts = usageProcessor.findConflicts(myChangeInfo, refUsages);
for (PsiElement key : conflicts.keySet()) {
Collection<String> collection = conflictDescriptions.get(key);
if (collection.size() == 0) collection = new HashSet<String>();
collection.addAll(conflicts.get(key));
conflictDescriptions.put(key, collection);
}
}
final UsageInfo[] usagesIn = refUsages.get();
RenameUtil.addConflictDescriptions(usagesIn, conflictDescriptions);
Set<UsageInfo> usagesSet = new HashSet<UsageInfo>(Arrays.asList(usagesIn));
RenameUtil.removeConflictUsages(usagesSet);
if (!conflictDescriptions.isEmpty()) {
if (ApplicationManager.getApplication().isUnitTestMode()) {
throw new ConflictsInTestsException(conflictDescriptions.values());
}
if (myPrepareSuccessfulSwingThreadCallback != null) {
ConflictsDialog dialog = prepareConflictsDialog(conflictDescriptions, usagesIn);
dialog.show();
if (!dialog.isOK()) {
if (dialog.isShowConflicts()) prepareSuccessful();
return false;
}
}
}
if (myChangeInfo.isReturnTypeChanged()) {
askToRemoveCovariantOverriders(usagesSet);
}
refUsages.set(usagesSet.toArray(new UsageInfo[usagesSet.size()]));
prepareSuccessful();
return true;
}
private void askToRemoveCovariantOverriders(Set<UsageInfo> usages) {
if (PsiUtil.isLanguageLevel5OrHigher(myChangeInfo.getMethod())) {
List<UsageInfo> covariantOverriderInfos = new ArrayList<UsageInfo>();
for (UsageInfo usageInfo : usages) {
if (usageInfo instanceof OverriderUsageInfo) {
final OverriderUsageInfo info = (OverriderUsageInfo)usageInfo;
PsiMethod overrider = assertNotNull(info.getElement());
PsiMethod baseMethod = info.getBaseMethod();
PsiSubstitutor substitutor = calculateSubstitutor(overrider, baseMethod);
PsiType type;
try {
type = substitutor.substitute(getChangeInfo().newReturnType.getType(myChangeInfo.getMethod(), myManager));
}
catch (IncorrectOperationException e) {
LOG.error(e);
return;
}
final PsiType overriderType = overrider.getReturnType();
if (overriderType != null && type.isAssignableFrom(overriderType)) {
covariantOverriderInfos.add(usageInfo);
}
}
}
// to be able to do filtering
preprocessCovariantOverriders(covariantOverriderInfos);
if (!covariantOverriderInfos.isEmpty()) {
if (ApplicationManager.getApplication().isUnitTestMode() || !isProcessCovariantOverriders()) {
for (UsageInfo usageInfo : covariantOverriderInfos) {
usages.remove(usageInfo);
}
}
}
}
}
protected void preprocessCovariantOverriders(final List<UsageInfo> covariantOverriderInfos) {
}
protected boolean isProcessCovariantOverriders() {
String message = RefactoringBundle.message("do.you.want.to.process.overriding.methods.with.covariant.return.type");
return Messages.showYesNoDialog(myProject, message, ChangeSignatureHandler.REFACTORING_NAME, Messages.getQuestionIcon()) == Messages.YES;
}
public static void makeEmptyBody(final PsiElementFactory factory, final PsiMethod delegate) throws IncorrectOperationException {
PsiCodeBlock body = delegate.getBody();
if (body != null) {
body.replace(factory.createCodeBlock());
}
else {
delegate.add(factory.createCodeBlock());
}
PsiUtil.setModifierProperty(delegate, PsiModifier.ABSTRACT, false);
}
@Nullable
public static PsiCallExpression addDelegatingCallTemplate(PsiMethod delegate, String newName) throws IncorrectOperationException {
Project project = delegate.getProject();
PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();
PsiCodeBlock body = delegate.getBody();
assert body != null;
final PsiCallExpression callExpression;
if (delegate.isConstructor()) {
PsiElement callStatement = factory.createStatementFromText("this();", null);
callStatement = CodeStyleManager.getInstance(project).reformat(callStatement);
callStatement = body.add(callStatement);
callExpression = (PsiCallExpression)((PsiExpressionStatement) callStatement).getExpression();
}
else {
if (PsiType.VOID.equals(delegate.getReturnType())) {
PsiElement callStatement = factory.createStatementFromText(newName + "();", null);
callStatement = CodeStyleManager.getInstance(project).reformat(callStatement);
callStatement = body.add(callStatement);
callExpression = (PsiCallExpression)((PsiExpressionStatement) callStatement).getExpression();
}
else {
PsiElement callStatement = factory.createStatementFromText("return " + newName + "();", null);
callStatement = CodeStyleManager.getInstance(project).reformat(callStatement);
callStatement = body.add(callStatement);
callExpression = (PsiCallExpression)((PsiReturnStatement) callStatement).getReturnValue();
}
}
return callExpression;
}
public static PsiSubstitutor calculateSubstitutor(PsiMethod derivedMethod, PsiMethod baseMethod) {
PsiSubstitutor substitutor;
if (derivedMethod.getManager().areElementsEquivalent(derivedMethod, baseMethod)) {
substitutor = PsiSubstitutor.EMPTY;
}
else {
PsiClass baseClass = baseMethod.getContainingClass();
PsiClass derivedClass = derivedMethod.getContainingClass();
if (baseClass != null && derivedClass != null && InheritanceUtil.isInheritorOrSelf(derivedClass, baseClass, true)) {
PsiSubstitutor superClassSubstitutor = TypeConversionUtil.getSuperClassSubstitutor(baseClass, derivedClass, PsiSubstitutor.EMPTY);
MethodSignature superMethodSignature = baseMethod.getSignature(superClassSubstitutor);
MethodSignature methodSignature = derivedMethod.getSignature(PsiSubstitutor.EMPTY);
PsiSubstitutor superMethodSubstitutor = MethodSignatureUtil.getSuperMethodSignatureSubstitutor(methodSignature, superMethodSignature);
substitutor = superMethodSubstitutor != null ? superMethodSubstitutor : superClassSubstitutor;
}
else {
substitutor = PsiSubstitutor.EMPTY;
}
}
return substitutor;
}
}