/* * Copyright 2000-2013 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.psi.*; import com.intellij.psi.util.MethodSignature; import com.intellij.refactoring.util.RefactoringUIUtil; import com.intellij.util.containers.MultiMap; import org.jetbrains.plugins.groovy.lang.documentation.GroovyPresentationUtil; import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrClosureSignature; import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrRecursiveSignatureVisitor; import org.jetbrains.plugins.groovy.lang.psi.api.signatures.GrSignature; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrAccessorMethod; import org.jetbrains.plugins.groovy.lang.psi.api.statements.typedef.members.GrMethod; import org.jetbrains.plugins.groovy.lang.psi.api.types.GrClosureParameter; import org.jetbrains.plugins.groovy.lang.psi.impl.GrClosureType; import org.jetbrains.plugins.groovy.lang.psi.impl.signatures.GrClosureSignatureUtil; import org.jetbrains.plugins.groovy.lang.psi.impl.statements.expressions.TypesUtil; import org.jetbrains.plugins.groovy.lang.psi.util.GroovyPropertyUtils; import org.jetbrains.plugins.groovy.refactoring.GroovyRefactoringBundle; import java.util.List; /** * @author Maxim.Medvedev */ public class GrMethodConflictUtil { private GrMethodConflictUtil() { } public static void checkMethodConflicts(PsiClass clazz, GrMethod prototype, GrMethod refactoredMethod, final MultiMap<PsiElement, String> conflicts, boolean excludeJavaConflicts) { List<MethodSignature> prototypeSignatures = GrClosureSignatureUtil.generateAllSignaturesForMethod(prototype, PsiSubstitutor.EMPTY); checkForClosurePropertySignatureOverload(clazz, prototype, refactoredMethod, conflicts, prototypeSignatures); checkForMethodSignatureOverload(clazz, prototype, refactoredMethod, conflicts, excludeJavaConflicts, prototypeSignatures); checkForAccessorOverloading(clazz, prototype, conflicts); } private static void checkForClosurePropertySignatureOverload(PsiClass clazz, GrMethod prototype, final GrMethod refactoredMethod, final MultiMap<PsiElement, String> conflicts, final List<MethodSignature> prototypeSignatures) { final boolean isStatic = prototype.hasModifierProperty(PsiModifier.STATIC); final String name = prototype.getName(); if (!GroovyPropertyUtils.isProperty(clazz, name, isStatic)) return; final PsiMethod getter = GroovyPropertyUtils.findPropertyGetter(clazz, name, isStatic, true); final PsiType returnType; if (getter instanceof GrMethod) { returnType = ((GrMethod)getter).getInferredReturnType(); } else if (getter instanceof GrAccessorMethod) { returnType = ((GrAccessorMethod)getter).getInferredReturnType(); } else { return; } if (!(returnType instanceof GrClosureType)) return; final GrSignature signature = ((GrClosureType)returnType).getSignature(); signature.accept(new GrRecursiveSignatureVisitor() { @Override public void visitClosureSignature(GrClosureSignature signature) { NextSignature: for (MethodSignature prototypeSignature : prototypeSignatures) { final GrClosureParameter[] params = signature.getParameters(); final PsiType[] types = prototypeSignature.getParameterTypes(); if (types.length != params.length) continue; for (int i = 0; i < types.length; i++) { if (!TypesUtil.isAssignableByMethodCallConversion(types[i], params[i].getType(), refactoredMethod.getParameterList())) { continue NextSignature; } } conflicts.putValue(getter, GroovyRefactoringBundle.message("refactored.method.will.cover.closure.property", name, RefactoringUIUtil.getDescription(getter.getContainingClass(), false))); } } }); } private static void checkForMethodSignatureOverload(PsiClass clazz, GrMethod prototype, GrMethod refactoredMethod, MultiMap<PsiElement, String> conflicts, boolean excludeJavaConflicts, List<MethodSignature> prototypeSignatures) { if (excludeJavaConflicts) { prototypeSignatures.remove(prototype.getSignature(PsiSubstitutor.EMPTY)); } String newName = prototype.getName(); PsiMethod[] methods = clazz.findMethodsByName(newName, false); MultiMap<MethodSignature, PsiMethod> signatures = GrClosureSignatureUtil.findRawMethodSignatures(methods, clazz); for (MethodSignature prototypeSignature : prototypeSignatures) { for (PsiMethod method : signatures.get(prototypeSignature)) { if (method != refactoredMethod) { String signaturePresentation = GroovyPresentationUtil.getSignaturePresentation(prototypeSignature); conflicts.putValue(method, GroovyRefactoringBundle.message("method.duplicate", signaturePresentation, RefactoringUIUtil.getDescription(clazz, false))); break; } } } } private static void checkForAccessorOverloading(PsiClass clazz, GrMethod prototype, MultiMap<PsiElement, String> conflicts) { if (GroovyPropertyUtils.isSimplePropertySetter(prototype)) { String propertyName = GroovyPropertyUtils.getPropertyNameBySetter(prototype); PsiMethod setter = GroovyPropertyUtils.findPropertySetter(clazz, propertyName, prototype.hasModifierProperty(PsiModifier.STATIC), false); if (setter instanceof GrAccessorMethod) { conflicts.putValue(setter, GroovyRefactoringBundle.message("replace.setter.for.property", propertyName)); } } else if (GroovyPropertyUtils.isSimplePropertyGetter(prototype)) { boolean isStatic = prototype.hasModifierProperty(PsiModifier.STATIC); String propertyName = GroovyPropertyUtils.getPropertyNameByGetter(prototype); PsiMethod getter = GroovyPropertyUtils.findPropertyGetter(clazz, propertyName, isStatic, false); if (getter instanceof GrAccessorMethod) { conflicts.putValue(getter, GroovyRefactoringBundle.message("replace.getter.for.property", propertyName)); } } } }