blob: 4266b66cb6eddb23c7f2463117a8cb12579d8c55 [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 com.intellij.codeInspection.inheritance.search;
import com.intellij.openapi.util.Couple;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.PsiClass;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.DirectClassInheritorsSearch;
import com.intellij.util.Processor;
import org.jetbrains.annotations.NotNull;
import java.util.*;
/**
* @author Dmitry Batkovich <dmitry.batkovich@jetbrains.com>
*/
public class InheritorsStatisticalDataSearch {
/**
* search for most used inheritors of superClass in scope
*
* @param aClass - class that excluded from inheritors of superClass
* @param minPercentRatio - head volume
* @return - search results in relevant ordering (frequency descent)
*/
public static List<InheritorsStatisticsSearchResult> search(final @NotNull PsiClass superClass,
final @NotNull PsiClass aClass,
final @NotNull GlobalSearchScope scope,
final int minPercentRatio) {
final String superClassName = superClass.getName();
final String aClassName = aClass.getName();
final Set<String> disabledNames = new HashSet<String>();
disabledNames.add(aClassName);
disabledNames.add(superClassName);
final Set<InheritorsCountData> collector = new TreeSet<InheritorsCountData>();
final Couple<Integer> collectingResult = collectInheritorsInfo(superClass, collector, disabledNames);
final int allAnonymousInheritors = collectingResult.getSecond();
final int allInheritors = collectingResult.getFirst() + allAnonymousInheritors - 1;
final List<InheritorsStatisticsSearchResult> result = new ArrayList<InheritorsStatisticsSearchResult>();
Integer firstPercent = null;
for (final InheritorsCountData data : collector) {
final int inheritorsCount = data.getInheritorsCount();
if (inheritorsCount < allAnonymousInheritors) {
break;
}
final int percent = (inheritorsCount * 100) / allInheritors;
if (percent < 1) {
break;
}
if (firstPercent == null) {
firstPercent = percent;
}
else if (percent * minPercentRatio < firstPercent) {
break;
}
final PsiClass psiClass = data.getPsiClass();
final VirtualFile file = psiClass.getContainingFile().getVirtualFile();
if (file != null && scope.contains(file)) {
result.add(new InheritorsStatisticsSearchResult(psiClass, percent));
}
}
return result;
}
private static Couple<Integer> collectInheritorsInfo(final PsiClass superClass,
final Set<InheritorsCountData> collector,
final Set<String> disabledNames) {
return collectInheritorsInfo(superClass, collector, disabledNames, new HashSet<String>(), new HashSet<String>());
}
private static Couple<Integer> collectInheritorsInfo(final PsiClass aClass,
final Set<InheritorsCountData> collector,
final Set<String> disabledNames,
final Set<String> processedElements,
final Set<String> allNotAnonymousInheritors) {
final String className = aClass.getName();
if (!processedElements.add(className)) return Couple.of(0, 0);
final MyInheritorsInfoProcessor processor = new MyInheritorsInfoProcessor(collector, disabledNames, processedElements);
DirectClassInheritorsSearch.search(aClass).forEach(processor);
allNotAnonymousInheritors.addAll(processor.getAllNotAnonymousInheritors());
final int allInheritorsCount = processor.getAllNotAnonymousInheritors().size() + processor.getAnonymousInheritorsCount();
if (!aClass.isInterface() && allInheritorsCount != 0 && !disabledNames.contains(className)) {
collector.add(new InheritorsCountData(aClass, allInheritorsCount));
}
return Couple.of(allNotAnonymousInheritors.size(), processor.getAnonymousInheritorsCount());
}
private static class MyInheritorsInfoProcessor implements Processor<PsiClass> {
private final Set<InheritorsCountData> myCollector;
private final Set<String> myDisabledNames;
private final Set<String> myProcessedElements;
private final Set<String> myAllNotAnonymousInheritors;
private MyInheritorsInfoProcessor(Set<InheritorsCountData> collector, Set<String> disabledNames, Set<String> processedElements) {
myCollector = collector;
myDisabledNames = disabledNames;
myProcessedElements = processedElements;
myAllNotAnonymousInheritors = new HashSet<String>();
}
private int myAnonymousInheritorsCount = 0;
private Set<String> getAllNotAnonymousInheritors() {
return myAllNotAnonymousInheritors;
}
private int getAnonymousInheritorsCount() {
return myAnonymousInheritorsCount;
}
@Override
public boolean process(final PsiClass psiClass) {
final String inheritorName = psiClass.getName();
if (inheritorName == null) {
myAnonymousInheritorsCount++;
}
else {
final Couple<Integer> res = collectInheritorsInfo(psiClass,
myCollector,
myDisabledNames,
myProcessedElements,
myAllNotAnonymousInheritors);
myAnonymousInheritorsCount += res.getSecond();
if (!psiClass.isInterface()) {
myAllNotAnonymousInheritors.add(inheritorName);
}
}
return true;
}
}
}