/* * Copyright 2000-2016 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.util; import com.intellij.lang.findUsages.DescriptiveNameUtil; import com.intellij.psi.*; import com.intellij.psi.impl.source.resolve.FileContextUtil; import com.intellij.psi.search.searches.ClassInheritorsSearch; import com.intellij.psi.util.MethodSignature; import com.intellij.psi.util.MethodSignatureUtil; import com.intellij.psi.util.PsiFormatUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.refactoring.RefactoringBundle; import com.intellij.util.Processor; import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public class ConflictsUtil { private ConflictsUtil() { } @NotNull public static PsiElement getContainer(PsiElement place) { PsiElement parent = place; while (true) { if (parent instanceof PsiMember && !(parent instanceof PsiTypeParameter)) { return parent; } if (parent instanceof PsiFile) { PsiElement host = FileContextUtil.getFileContext((PsiFile)parent); if (host == null) { return parent; } parent = host; } parent = parent.getParent(); } } public static void checkMethodConflicts(@Nullable PsiClass aClass, @Nullable PsiMethod refactoredMethod, final PsiMethod prototype, final MultiMap<PsiElement,String> conflicts) { if (prototype == null) return; String protoMethodInfo = getMethodPrototypeString(prototype); PsiMethod method = aClass != null ? aClass.findMethodBySignature(prototype, true) : null; if (method == null && aClass != null) { final MethodSignature signature = prototype.getSignature(PsiSubstitutor.EMPTY); for (PsiMethod classMethod : aClass.getMethods()) { if (MethodSignatureUtil.areSignaturesErasureEqual(signature, classMethod.getSignature(PsiSubstitutor.EMPTY))) { method = classMethod; protoMethodInfo = "with same erasure"; break; } } } if (method != null && method != refactoredMethod && !isStaticInterfaceMethods(aClass, refactoredMethod, method)) { if (aClass.equals(method.getContainingClass())) { final String classDescr = aClass instanceof PsiAnonymousClass ? RefactoringBundle.message("current.class") : RefactoringUIUtil.getDescription(aClass, false); conflicts.putValue(method, RefactoringBundle.message("method.0.is.already.defined.in.the.1", protoMethodInfo, classDescr)); } else { // method somewhere in base class if (JavaPsiFacade.getInstance(method.getProject()).getResolveHelper().isAccessible(method, aClass, null)) { String className = CommonRefactoringUtil.htmlEmphasize(DescriptiveNameUtil.getDescriptiveName(method.getContainingClass())); if (PsiUtil.getAccessLevel(prototype.getModifierList()) >= PsiUtil.getAccessLevel(method.getModifierList()) ) { boolean isMethodAbstract = method.hasModifierProperty(PsiModifier.ABSTRACT); boolean isMyMethodAbstract = refactoredMethod != null && refactoredMethod.hasModifierProperty(PsiModifier.ABSTRACT); final String conflict = isMethodAbstract != isMyMethodAbstract ? RefactoringBundle.message("method.0.will.implement.method.of.the.base.class", protoMethodInfo, className) : RefactoringBundle.message("method.0.will.override.a.method.of.the.base.class", protoMethodInfo, className); conflicts.putValue(method, conflict); } else { // prototype is private, will be compile-error conflicts.putValue(method, RefactoringBundle.message("method.0.will.hide.method.of.the.base.class", protoMethodInfo, className)); } } } } if (aClass != null && prototype.hasModifierProperty(PsiModifier.PRIVATE)) { ClassInheritorsSearch.search(aClass).forEach(aClass1 -> { final PsiMethod[] methods = aClass1.findMethodsBySignature(prototype, false); for (PsiMethod method1 : methods) { conflicts.putValue(method1, "Method " + RefactoringUIUtil.getDescription(method1, true) + " will override method of the base class " + RefactoringUIUtil.getDescription( aClass1, false)); } return true; }); } } private static boolean isStaticInterfaceMethods(PsiClass aClass, PsiMethod refactoredMethod, PsiMethod method) { return aClass.isInterface() && method.hasModifierProperty(PsiModifier.STATIC) && refactoredMethod != null && refactoredMethod.hasModifierProperty(PsiModifier.STATIC); } private static String getMethodPrototypeString(final PsiMethod prototype) { return PsiFormatUtil.formatMethod( prototype, PsiSubstitutor.EMPTY, PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_PARAMETERS, PsiFormatUtil.SHOW_TYPE ); } public static void checkFieldConflicts(@Nullable PsiClass aClass, String newName, final MultiMap<PsiElement, String> conflicts) { PsiField existingField = aClass != null ? aClass.findFieldByName(newName, true) : null; if (existingField != null) { if (aClass.equals(existingField.getContainingClass())) { String className = aClass instanceof PsiAnonymousClass ? RefactoringBundle.message("current.class") : RefactoringUIUtil.getDescription(aClass, false); final String conflict = RefactoringBundle.message("field.0.is.already.defined.in.the.1", existingField.getName(), className); conflicts.putValue(existingField, conflict); } else { // method somewhere in base class if (!existingField.hasModifierProperty(PsiModifier.PRIVATE)) { String fieldInfo = PsiFormatUtil.formatVariable(existingField, PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_TYPE | PsiFormatUtil.TYPE_AFTER, PsiSubstitutor.EMPTY); String className = RefactoringUIUtil.getDescription(existingField.getContainingClass(), false); final String descr = RefactoringBundle.message("field.0.will.hide.field.1.of.the.base.class", newName, fieldInfo, className); conflicts.putValue(existingField, descr); } } } } }