blob: 22b57520c5ef8a6e4e3198d8299e57feca2189ff [file] [log] [blame]
/*
* Copyright 2000-2009 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.ide.util.gotoByName;
import com.intellij.ide.util.DefaultPsiElementCellRenderer;
import com.intellij.navigation.ChooseByNameContributorEx;
import com.intellij.navigation.NavigationItem;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.psi.*;
import com.intellij.psi.impl.PsiSuperMethodImplUtil;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.PsiSearchScopeUtil;
import com.intellij.psi.search.PsiShortNamesCache;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ArrayUtil;
import com.intellij.util.Processor;
import com.intellij.util.containers.HashSet;
import com.intellij.util.indexing.FindSymbolParameters;
import com.intellij.util.indexing.IdFilter;
import gnu.trove.THashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
public class DefaultSymbolNavigationContributor implements ChooseByNameContributorEx {
private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.gotoByName.DefaultSymbolNavigationContributor");
@Override
@NotNull
public String[] getNames(Project project, boolean includeNonProjectItems) {
PsiShortNamesCache cache = PsiShortNamesCache.getInstance(project);
HashSet<String> set = new HashSet<String>();
cache.getAllMethodNames(set);
cache.getAllFieldNames(set);
cache.getAllClassNames(set);
return ArrayUtil.toStringArray(set);
}
@Override
@NotNull
public NavigationItem[] getItemsByName(String name, final String pattern, Project project, boolean includeNonProjectItems) {
GlobalSearchScope scope = includeNonProjectItems ? GlobalSearchScope.allScope(project) : GlobalSearchScope.projectScope(project);
PsiShortNamesCache cache = PsiShortNamesCache.getInstance(project);
List<PsiMember> result = new ArrayList<PsiMember>();
for (PsiMethod method : cache.getMethodsByName(name, scope)) {
if (!method.isConstructor() && isOpenable(method) && !hasSuperMethod(method, scope)) {
result.add(method);
}
}
for (PsiField field : cache.getFieldsByName(name, scope)) {
if (isOpenable(field)) {
result.add(field);
}
}
for (PsiClass aClass : cache.getClassesByName(name, scope)) {
if (isOpenable(aClass)) {
result.add(aClass);
}
}
PsiMember[] array = result.toArray(new PsiMember[result.size()]);
Arrays.sort(array, MyComparator.INSTANCE);
return array;
}
private static boolean isOpenable(PsiMember member) {
return member.getContainingFile().getVirtualFile() != null;
}
private static boolean hasSuperMethod(PsiMethod method, GlobalSearchScope scope) {
PsiClass containingClass = method.getContainingClass();
if (containingClass == null) return false;
for (PsiMethod candidate : containingClass.findMethodsByName(method.getName(), true)) {
if (candidate.getContainingClass() != containingClass &&
PsiSearchScopeUtil.isInScope(scope, candidate) &&
PsiSuperMethodImplUtil.isSuperMethodSmart(method, candidate)) {
return true;
}
}
return false;
}
public void processNames(@NotNull Processor<String> processor, @NotNull GlobalSearchScope scope, @Nullable IdFilter filter) {
PsiShortNamesCache cache = PsiShortNamesCache.getInstance(scope.getProject());
cache.processAllClassNames(processor, scope, filter);
cache.processAllFieldNames(processor, scope, filter);
cache.processAllMethodNames(processor, scope, filter);
}
@Override
public void processElementsWithName(@NotNull String name,
@NotNull final Processor<NavigationItem> processor,
@NotNull FindSymbolParameters parameters) {
GlobalSearchScope scope = parameters.getSearchScope();
IdFilter filter = parameters.getIdFilter();
PsiShortNamesCache cache = PsiShortNamesCache.getInstance(scope.getProject());
//noinspection UnusedDeclaration
final Set<PsiMethod> collectedMethods = new THashSet<PsiMethod>();
boolean success = cache.processFieldsWithName(name, new Processor<PsiField>() {
@Override
public boolean process(PsiField field) {
if (isOpenable(field)) return processor.process(field);
return true;
}
}, scope, filter) &&
cache.processClassesWithName(name, new Processor<PsiClass>() {
@Override
public boolean process(PsiClass aClass) {
if (isOpenable(aClass)) return processor.process(aClass);
return true;
}
}, scope, filter) &&
cache.processMethodsWithName(name, new Processor<PsiMethod>() {
@Override
public boolean process(PsiMethod method) {
if(!method.isConstructor() && isOpenable(method)) {
collectedMethods.add(method);
}
return true;
}
}, scope, filter);
if (success) {
// hashSuperMethod accesses index and can not be invoked without risk of the deadlock in processMethodsWithName
Iterator<PsiMethod> iterator = collectedMethods.iterator();
while(iterator.hasNext()) {
PsiMethod method = iterator.next();
if (!hasSuperMethod(method, scope) && !processor.process(method)) return;
ProgressManager.checkCanceled();
iterator.remove();
}
}
}
private static class MyComparator implements Comparator<PsiModifierListOwner>{
public static final MyComparator INSTANCE = new MyComparator();
private final DefaultPsiElementCellRenderer myRenderer = new DefaultPsiElementCellRenderer();
@Override
public int compare(PsiModifierListOwner element1, PsiModifierListOwner element2) {
if (element1 == element2) return 0;
PsiModifierList modifierList1 = element1.getModifierList();
PsiModifierList modifierList2 = element2.getModifierList();
int level1 = modifierList1 == null ? PsiUtil.ACCESS_LEVEL_PUBLIC : PsiUtil.getAccessLevel(modifierList1);
int level2 = modifierList2 == null ? PsiUtil.ACCESS_LEVEL_PUBLIC : PsiUtil.getAccessLevel(modifierList2);
if (level1 != level2) return level2 - level1;
int kind1 = getElementTypeLevel(element1);
int kind2 = getElementTypeLevel(element2);
if (kind1 != kind2) return kind1 - kind2;
if (element1 instanceof PsiMethod){
LOG.assertTrue(element2 instanceof PsiMethod);
PsiParameter[] parms1 = ((PsiMethod)element1).getParameterList().getParameters();
PsiParameter[] parms2 = ((PsiMethod)element2).getParameterList().getParameters();
if (parms1.length != parms2.length) return parms1.length - parms2.length;
}
String text1 = myRenderer.getElementText(element1);
String text2 = myRenderer.getElementText(element2);
if (!text1.equals(text2)) return text1.compareTo(text2);
String containerText1 = myRenderer.getContainerText(element1, text1);
String containerText2 = myRenderer.getContainerText(element2, text2);
if (containerText1 == null) containerText1 = "";
if (containerText2 == null) containerText2 = "";
return containerText1.compareTo(containerText2);
}
private static int getElementTypeLevel(PsiElement element){
if (element instanceof PsiMethod){
return 1;
}
else if (element instanceof PsiField){
return 2;
}
else if (element instanceof PsiClass){
return 3;
}
else{
LOG.error(element);
return 0;
}
}
}
}