blob: 1d57d32de491dc10b46b2c1de73450a415a2af4c [file] [log] [blame]
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() +
'}';
}
}