| package com.intellij.codeInsight.generation; |
| |
| import com.intellij.openapi.util.text.StringUtil; |
| import com.intellij.psi.*; |
| import com.intellij.psi.impl.source.PsiExtensibleClass; |
| import com.intellij.psi.infos.CandidateInfo; |
| import com.intellij.psi.search.searches.ClassInheritorsSearch; |
| import com.intellij.ui.SimpleColoredComponent; |
| import com.intellij.ui.SimpleTextAttributes; |
| import com.intellij.util.containers.ContainerUtil; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.jetbrains.annotations.TestOnly; |
| |
| import javax.swing.*; |
| import java.util.*; |
| |
| /** |
| * @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com> |
| */ |
| public class PsiMethodWithOverridingPercentMember extends PsiMethodMember { |
| |
| private final int myOverridingPercent; |
| |
| public PsiMethodWithOverridingPercentMember(final CandidateInfo info, final int overridingPercent) { |
| super(info); |
| myOverridingPercent = overridingPercent; |
| } |
| |
| @Override |
| public void renderTreeNode(final SimpleColoredComponent component, final JTree tree) { |
| component.append(myOverridingPercent + "% ", SimpleTextAttributes.GRAY_ATTRIBUTES); |
| super.renderTreeNode(component, tree); |
| } |
| |
| @TestOnly |
| public int getOverridingPercent() { |
| return myOverridingPercent; |
| } |
| |
| public static final Comparator<PsiMethodMember> COMPARATOR = new Comparator<PsiMethodMember>() { |
| @Override |
| public int compare(PsiMethodMember e1, PsiMethodMember e2) { |
| if (!(e1 instanceof PsiMethodWithOverridingPercentMember)) { |
| if (!(e2 instanceof PsiMethodWithOverridingPercentMember)) { |
| return e1.equals(e2) ? 0 : -1; |
| } else { |
| return -1; |
| } |
| } |
| |
| |
| if (!(e2 instanceof PsiMethodWithOverridingPercentMember)) { |
| return 1; |
| } |
| int sub = |
| ((PsiMethodWithOverridingPercentMember)e2).myOverridingPercent - ((PsiMethodWithOverridingPercentMember)e1).myOverridingPercent; |
| if (sub != 0) return sub; |
| return String.CASE_INSENSITIVE_ORDER.compare(e1.getText(), e2.getText()); |
| } |
| }; |
| |
| @NotNull |
| public static PsiMethodWithOverridingPercentMember[] calculateOverridingPercents(@NotNull final Collection<CandidateInfo> candidateInfos) { |
| final List<PsiMethodWithOverridingPercentMember> result = new ArrayList<PsiMethodWithOverridingPercentMember>(candidateInfos.size()); |
| final Map<String, Collection<PsiClass>> classShortNames2Inheritors = new HashMap<String, Collection<PsiClass>>(); |
| for (final CandidateInfo candidateInfo : candidateInfos) { |
| final PsiMethod method = (PsiMethod)candidateInfo.getElement(); |
| if (!method.hasModifierProperty(PsiModifier.FINAL) && |
| !method.isConstructor() && |
| !method.isDeprecated() && |
| !EXCLUDED_JAVA_LANG_OBJECT_METHOD_NAMES.contains(method.getName())) { |
| final PsiClass containingClass = method.getContainingClass(); |
| if (containingClass == null) { |
| continue; |
| } |
| |
| final String classShortName = containingClass.getName(); |
| |
| Collection<PsiClass> allInheritors = classShortNames2Inheritors.get(classShortName); |
| if (allInheritors == null) { |
| allInheritors = ClassInheritorsSearch.search(containingClass, true).findAll(); |
| classShortNames2Inheritors.put(classShortName, allInheritors); |
| } |
| |
| final int allInheritorsCount = allInheritors.size() - 1; |
| if (allInheritorsCount > 0) { |
| final int percent = searchForOverridingCount(method, allInheritors) * 100 / allInheritorsCount; |
| if (percent > 1) { |
| result.add(new PsiMethodWithOverridingPercentMember(candidateInfo, percent)); |
| } |
| } |
| } |
| } |
| return result.toArray(new PsiMethodWithOverridingPercentMember[result.size()]); |
| } |
| |
| private static int searchForOverridingCount(final PsiMethod method, final Collection<PsiClass> containingClassInheritors) { |
| int counter = 0; |
| for (final PsiClass inheritor : containingClassInheritors) { |
| if (inheritor instanceof PsiExtensibleClass) { |
| final List<PsiMethod> ownMethods = ((PsiExtensibleClass)inheritor).getOwnMethods(); |
| for (PsiMethod ownMethod : ownMethods) { |
| if (maybeSuper(method, ownMethod)) { |
| counter++; |
| break; |
| } |
| } |
| |
| } |
| } |
| return counter; |
| } |
| |
| private static boolean maybeSuper(@NotNull final PsiMethod superMethod, @NotNull final PsiMethod method) { |
| if (!superMethod.getName().equals(method.getName())) { |
| return false; |
| } |
| final PsiParameterList superMethodParameterList = superMethod.getParameterList(); |
| final PsiParameterList methodParameterList = method.getParameterList(); |
| if (superMethodParameterList.getParametersCount() != methodParameterList.getParametersCount()) { |
| return false; |
| } |
| final PsiParameter[] superMethodParameters = superMethodParameterList.getParameters(); |
| final PsiParameter[] methodParameters = methodParameterList.getParameters(); |
| for (int i = 0; i < methodParameters.length; i++) { |
| if (!StringUtil.equals(getTypeShortName(superMethodParameters[i].getType()), getTypeShortName(methodParameters[i].getType()))) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| @Nullable |
| private static String getTypeShortName(@NotNull final PsiType type) { |
| if (type instanceof PsiPrimitiveType) { |
| return ((PsiPrimitiveType)type).getBoxedTypeName(); |
| } |
| if (type instanceof PsiClassType) { |
| return ((PsiClassType)type).getClassName(); |
| } |
| if (type instanceof PsiArrayType) { |
| return getTypeShortName(((PsiArrayType)type).getComponentType()) + "[]"; |
| } |
| return null; |
| } |
| |
| private static final Set<String> EXCLUDED_JAVA_LANG_OBJECT_METHOD_NAMES = |
| ContainerUtil.newHashSet("hashCode", "finalize", "clone", "equals", "toString"); |
| |
| @Override |
| public String toString() { |
| return "PsiMethodWithOverridingPercentMember{" + |
| "myOverridingPercent=" + myOverridingPercent + ", myElement=" + getElement() + |
| '}'; |
| } |
| } |