blob: 8e62b92589f045e299eb3b8bb17d0f637ba5fe66 [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 org.jetbrains.plugins.groovy.refactoring.changeSignature;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.*;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.MethodReferencesSearch;
import com.intellij.psi.search.searches.OverridingMethodsSearch;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.refactoring.RefactoringBundle;
import com.intellij.refactoring.changeSignature.*;
import com.intellij.refactoring.rename.UnresolvableCollisionUsageInfo;
import com.intellij.refactoring.util.MoveRenameUsageInfo;
import com.intellij.refactoring.util.RefactoringUIUtil;
import com.intellij.refactoring.util.RefactoringUtil;
import com.intellij.refactoring.util.usageInfo.DefaultConstructorImplicitUsageInfo;
import com.intellij.refactoring.util.usageInfo.NoConstructorClassUsageInfo;
import com.intellij.usageView.UsageInfo;
import com.intellij.usageView.UsageViewUtil;
import com.intellij.util.containers.ContainerUtil;
import com.intellij.util.containers.HashSet;
import org.jetbrains.plugins.groovy.GroovyLanguage;
import org.jetbrains.plugins.groovy.lang.groovydoc.psi.api.GrDocTagValueToken;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.blocks.GrOpenBlock;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.GrAnonymousClassDefinition;
import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod;
import org.jetbrains.plugins.groovy.lang.psi.util.PsiUtil;
import java.util.ArrayList;
import java.util.Set;
/**
* @author Maxim.Medvedev
*/
class GrChageSignatureUsageSearcher {
private final JavaChangeInfo myChangeInfo;
private static final Logger LOG =
Logger.getInstance("org.jetbrains.plugins.groovy.refactoring.changeSignature.GrChageSignatureUsageSearcher");
GrChageSignatureUsageSearcher(JavaChangeInfo changeInfo) {
this.myChangeInfo = changeInfo;
}
public UsageInfo[] findUsages() {
ArrayList<UsageInfo> result = new ArrayList<UsageInfo>();
final PsiElement element = myChangeInfo.getMethod();
if (element instanceof PsiMethod) {
final PsiMethod method = (PsiMethod)element;
findSimpleUsages(method, result);
final UsageInfo[] usageInfos = result.toArray(new UsageInfo[result.size()]);
return UsageViewUtil.removeDuplicatedUsages(usageInfos);
}
return UsageInfo.EMPTY_ARRAY;
}
private void findSimpleUsages(final PsiMethod method, final ArrayList<UsageInfo> result) {
PsiMethod[] overridingMethods = findSimpleUsagesWithoutParameters(method, result, true, true, true);
//findUsagesInCallers(result); todo
//Parameter name changes are not propagated
findParametersUsage(method, result, overridingMethods);
}
/* todo
private void findUsagesInCallers(final ArrayList<UsageInfo> usages) {
if (myChangeInfo instanceof JavaChangeInfoImpl) {
JavaChangeInfoImpl changeInfo = (JavaChangeInfoImpl)myChangeInfo;
for (PsiMethod caller : changeInfo.propagateParametersMethods) {
usages.add(new CallerUsageInfo(caller, true, changeInfo.propagateExceptionsMethods.contains(caller)));
}
for (PsiMethod caller : changeInfo.propagateExceptionsMethods) {
usages.add(new CallerUsageInfo(caller, changeInfo.propagateParametersMethods.contains(caller), true));
}
Set<PsiMethod> merged = new HashSet<PsiMethod>();
merged.addAll(changeInfo.propagateParametersMethods);
merged.addAll(changeInfo.propagateExceptionsMethods);
for (final PsiMethod method : merged) {
findSimpleUsagesWithoutParameters(method, usages, changeInfo.propagateParametersMethods.contains(method),
changeInfo.propagateExceptionsMethods.contains(method), false);
}
}
}
*/
private void detectLocalsCollisionsInMethod(final GrMethod method, final ArrayList<UsageInfo> result, boolean isOriginal) {
if (!GroovyLanguage.INSTANCE.equals(method.getLanguage())) return;
final PsiParameter[] parameters = method.getParameterList().getParameters();
final Set<PsiParameter> deletedOrRenamedParameters = new HashSet<PsiParameter>();
if (isOriginal) {
ContainerUtil.addAll(deletedOrRenamedParameters, parameters);
for (ParameterInfo parameterInfo : myChangeInfo.getNewParameters()) {
if (parameterInfo.getOldIndex() >= 0) {
final PsiParameter parameter = parameters[parameterInfo.getOldIndex()];
if (parameterInfo.getName().equals(parameter.getName())) {
deletedOrRenamedParameters.remove(parameter);
}
}
}
}
final GrOpenBlock block = method.getBlock();
for (ParameterInfo parameterInfo : myChangeInfo.getNewParameters()) {
final int oldParameterIndex = parameterInfo.getOldIndex();
final String newName = parameterInfo.getName();
if (oldParameterIndex >= 0) {
if (isOriginal) { //Name changes take place only in primary method
final PsiParameter parameter = parameters[oldParameterIndex];
if (!newName.equals(parameter.getName())) {
final GrUnresolvableLocalCollisionDetector.CollidingVariableVisitor collidingVariableVisitor =
new GrUnresolvableLocalCollisionDetector.CollidingVariableVisitor() {
@Override
public void visitCollidingVariable(final PsiVariable collidingVariable) {
if (!deletedOrRenamedParameters.contains(collidingVariable)) {
result.add(new RenamedParameterCollidesWithLocalUsageInfo(parameter, collidingVariable, method));
}
}
};
if (block != null) {
GrUnresolvableLocalCollisionDetector.visitLocalsCollisions(parameter, newName, block, collidingVariableVisitor);
}
}
}
}
else {
final GrUnresolvableLocalCollisionDetector.CollidingVariableVisitor variableVisitor =
new GrUnresolvableLocalCollisionDetector.CollidingVariableVisitor() {
@Override
public void visitCollidingVariable(PsiVariable collidingVariable) {
if (!deletedOrRenamedParameters.contains(collidingVariable)) {
result.add(new NewParameterCollidesWithLocalUsageInfo(collidingVariable, collidingVariable, method));
}
}
};
if (block != null) {
GrUnresolvableLocalCollisionDetector.visitLocalsCollisions(method, newName, block, variableVisitor);
}
}
}
}
private void findParametersUsage(final PsiMethod method, ArrayList<UsageInfo> result, PsiMethod[] overriders) {
if (!GroovyLanguage.INSTANCE.equals(method.getLanguage())) return;
PsiParameter[] parameters = method.getParameterList().getParameters();
for (ParameterInfo info : myChangeInfo.getNewParameters()) {
if (info.getOldIndex() >= 0) {
PsiParameter parameter = parameters[info.getOldIndex()];
if (!info.getName().equals(parameter.getName())) {
addParameterUsages(parameter, result, info);
for (PsiMethod overrider : overriders) {
if (!GroovyLanguage.INSTANCE.equals(overrider.getLanguage())) continue;
PsiParameter parameter1 = overrider.getParameterList().getParameters()[info.getOldIndex()];
if (parameter.getName().equals(parameter1.getName())) {
addParameterUsages(parameter1, result, info);
}
}
}
}
}
}
private PsiMethod[] findSimpleUsagesWithoutParameters(final PsiMethod method,
final ArrayList<UsageInfo> result,
boolean isToModifyArgs,
boolean isToThrowExceptions,
boolean isOriginal) {
GlobalSearchScope projectScope = GlobalSearchScope.projectScope(method.getProject());
PsiMethod[] overridingMethods = OverridingMethodsSearch.search(method, true).toArray(PsiMethod.EMPTY_ARRAY);
for (PsiMethod overridingMethod : overridingMethods) {
if (GroovyLanguage.INSTANCE.equals(overridingMethod.getLanguage())) {
result.add(new OverriderUsageInfo(overridingMethod, method, isOriginal, isToModifyArgs, isToThrowExceptions));
}
}
boolean needToChangeCalls =
!myChangeInfo.isGenerateDelegate() && (myChangeInfo.isNameChanged() ||
myChangeInfo.isParameterSetOrOrderChanged() ||
myChangeInfo.isExceptionSetOrOrderChanged() ||
myChangeInfo.isVisibilityChanged()/*for checking inaccessible*/);
if (needToChangeCalls) {
PsiReference[] refs = MethodReferencesSearch.search(method, projectScope, true).toArray(PsiReference.EMPTY_ARRAY);
for (PsiReference ref : refs) {
PsiElement element = ref.getElement();
if (!GroovyLanguage.INSTANCE.equals(element.getLanguage())) continue;
boolean isToCatchExceptions = isToThrowExceptions && needToCatchExceptions(RefactoringUtil.getEnclosingMethod(element));
if (PsiUtil.isMethodUsage(element)) {
result.add(new GrMethodCallUsageInfo(element, isToModifyArgs, isToCatchExceptions, method));
}
else if (element instanceof GrDocTagValueToken) {
result.add(new UsageInfo(ref.getElement()));
}
else if (element instanceof GrMethod && ((GrMethod)element).isConstructor()) {
DefaultConstructorImplicitUsageInfo implicitUsageInfo =
new DefaultConstructorImplicitUsageInfo((GrMethod)element, ((GrMethod)element).getContainingClass(), method);
result.add(implicitUsageInfo);
}
else if (element instanceof PsiClass) {
LOG.assertTrue(method.isConstructor());
final PsiClass psiClass = (PsiClass)element;
if (psiClass instanceof GrAnonymousClassDefinition) {
result.add(new GrMethodCallUsageInfo(element, isToModifyArgs, isToCatchExceptions, method));
continue;
}
/*if (!(myChangeInfo instanceof JavaChangeInfoImpl)) continue; todo propagate methods
if (shouldPropagateToNonPhysicalMethod(method, result, psiClass,
((JavaChangeInfoImpl)myChangeInfo).propagateParametersMethods)) {
continue;
}
if (shouldPropagateToNonPhysicalMethod(method, result, psiClass,
((JavaChangeInfoImpl)myChangeInfo).propagateExceptionsMethods)) {
continue;
}*/
result.add(new NoConstructorClassUsageInfo(psiClass));
}
else if (ref instanceof PsiCallReference) {
result.add(new CallReferenceUsageInfo((PsiCallReference)ref));
}
else {
result.add(new MoveRenameUsageInfo(element, ref, method));
}
}
}
else if (myChangeInfo.isParameterTypesChanged()) {
PsiReference[] refs = MethodReferencesSearch.search(method, projectScope, true).toArray(PsiReference.EMPTY_ARRAY);
for (PsiReference reference : refs) {
final PsiElement element = reference.getElement();
if (element instanceof GrDocTagValueToken) {
result.add(new UsageInfo(reference));
}
}
}
// Conflicts
if (method instanceof GrMethod) {
detectLocalsCollisionsInMethod((GrMethod)method, result, isOriginal);
}
for (final PsiMethod overridingMethod : overridingMethods) {
if (overridingMethod instanceof GrMethod) {
detectLocalsCollisionsInMethod((GrMethod)overridingMethod, result, isOriginal);
}
}
return overridingMethods;
}
private static void addParameterUsages(PsiParameter parameter, ArrayList<UsageInfo> results, ParameterInfo info) {
for (PsiReference psiReference : ReferencesSearch.search(parameter)) {
PsiElement parmRef = psiReference.getElement();
UsageInfo usageInfo = new ChangeSignatureParameterUsageInfo(parmRef, parameter.getName(), info.getName());
results.add(usageInfo);
}
if (info.getName() != parameter.getName()) {
}
}
private boolean needToCatchExceptions(PsiMethod caller) {
/*if (myChangeInfo instanceof JavaChangeInfoImpl) { //todo propagate methods
return myChangeInfo.isExceptionSetOrOrderChanged() &&
!((JavaChangeInfoImpl)myChangeInfo).propagateExceptionsMethods.contains(caller);
}
else {*/
return myChangeInfo.isExceptionSetOrOrderChanged();
}
private static class RenamedParameterCollidesWithLocalUsageInfo extends UnresolvableCollisionUsageInfo {
private final PsiElement myCollidingElement;
private final PsiMethod myMethod;
public RenamedParameterCollidesWithLocalUsageInfo(PsiParameter parameter, PsiElement collidingElement, PsiMethod method) {
super(parameter, collidingElement);
myCollidingElement = collidingElement;
myMethod = method;
}
@Override
public String getDescription() {
return RefactoringBundle.message("there.is.already.a.0.in.the.1.it.will.conflict.with.the.renamed.parameter",
RefactoringUIUtil.getDescription(myCollidingElement, true),
RefactoringUIUtil.getDescription(myMethod, true));
}
}
}