blob: 71e301aa91f63d12ccc5d6f62a3779284a54d4dd [file] [log] [blame]
/*
* Copyright 2000-2013 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.codeInsight.highlighting;
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.ide.util.NavigationItemListCellRenderer;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.ui.popup.JBPopup;
import com.intellij.openapi.ui.popup.PopupChooserBuilder;
import com.intellij.psi.*;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.ui.components.JBList;
import com.intellij.util.Consumer;
import com.intellij.util.Function;
import org.jetbrains.annotations.NotNull;
import javax.swing.*;
import java.util.*;
/**
* @author Bas Leijdekkers
*/
public class HighlightImportedElementsHandler extends HighlightUsagesHandlerBase<PsiMember> {
private final PsiElement myTarget;
private final PsiImportStatementBase myImportStatement;
private final boolean myImportStatic;
private Map<PsiMember,List<PsiElement>> myClassReferenceListMap = null;
public HighlightImportedElementsHandler(Editor editor, PsiFile file, PsiElement target, PsiImportStatementBase importStatement) {
super(editor, file);
myTarget = target;
myImportStatement = importStatement;
myImportStatic = myImportStatement instanceof PsiImportStaticStatement;
}
@Override
public List<PsiMember> getTargets() {
final PsiJavaCodeReferenceElement importReference = myImportStatement.getImportReference();
if (importReference == null) {
return Collections.emptyList();
}
final PsiJavaFile javaFile = PsiTreeUtil.getParentOfType(importReference, PsiJavaFile.class);
if (javaFile == null) {
return Collections.emptyList();
}
final JavaResolveResult[] resolveResults = importReference.multiResolve(false);
if (resolveResults.length == 0) {
return Collections.emptyList();
}
final PsiElement[] importedElements = new PsiElement[resolveResults.length];
for (int i = 0; i < resolveResults.length; i++) {
final JavaResolveResult resolveResult = resolveResults[i];
importedElements[i] = resolveResult.getElement();
}
final ReferenceCollector collector = new ReferenceCollector(importedElements, myImportStatement.isOnDemand(), myImportStatic);
javaFile.accept(collector);
myClassReferenceListMap = collector.getClassReferenceListMap();
if (myClassReferenceListMap.isEmpty()) {
return Collections.emptyList();
}
return new ArrayList<PsiMember>(myClassReferenceListMap.keySet());
}
@Override
protected void selectTargets(final List<PsiMember> targets, final Consumer<List<PsiMember>> selectionConsumer) {
if (targets.isEmpty()) {
selectionConsumer.consume(Collections.<PsiMember>emptyList());
return;
}
if (targets.size() == 1) {
selectionConsumer.consume(Collections.singletonList(targets.get(0)));
return;
}
if (ApplicationManager.getApplication().isUnitTestMode()) {
selectionConsumer.consume(targets);
return;
}
Collections.sort(targets, new PsiMemberComparator());
final List<Object> model = new ArrayList<Object>();
model.add(CodeInsightBundle.message("highlight.thrown.exceptions.chooser.all.entry"));
model.addAll(targets);
final JList list = new JBList(model);
list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
final ListCellRenderer renderer = new NavigationItemListCellRenderer();
list.setCellRenderer(renderer);
final PopupChooserBuilder builder = new PopupChooserBuilder(list);
builder.setFilteringEnabled(new Function<Object, String>() {
@Override
public String fun(Object o) {
if (o instanceof PsiMember) {
final PsiMember member = (PsiMember)o;
return member.getName();
}
return o.toString();
}
});
if (myImportStatic) {
builder.setTitle(CodeInsightBundle.message("highlight.imported.members.chooser.title"));
} else {
builder.setTitle(CodeInsightBundle.message("highlight.imported.classes.chooser.title"));
}
builder.setItemChoosenCallback(new Runnable() {
@Override
public void run() {
final int index= list.getSelectedIndex();
if (index == 0) {
selectionConsumer.consume(targets);
}
else {
selectionConsumer.consume(Collections.singletonList(targets.get(index - 1)));
}
}
});
final JBPopup popup = builder.createPopup();
popup.showInBestPositionFor(myEditor);
}
@Override
public void computeUsages(List<PsiMember> targets) {
if (targets.isEmpty()) {
buildStatusText("import", 0);
return;
}
if (myClassReferenceListMap == null) {
return;
}
addOccurrence(myTarget);
for (PsiMember target : targets) {
final List<PsiElement> elements = myClassReferenceListMap.get(target);
for (PsiElement element : elements) {
addOccurrence(element);
}
}
buildStatusText("import", myReadUsages.size() - 1 /* exclude target */);
}
static class ReferenceCollector extends JavaRecursiveElementVisitor {
private final Map<PsiMember, List<PsiElement>> classReferenceListMap = new HashMap<PsiMember, List<PsiElement>>();
private final PsiElement[] myImportTargets;
private final boolean myOnDemand;
private final boolean myImportStatic;
ReferenceCollector(@NotNull PsiElement[] importTargets, boolean onDemand, boolean importStatic) {
this.myImportTargets = importTargets;
this.myOnDemand = onDemand;
this.myImportStatic = importStatic;
}
@Override
public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
super.visitReferenceElement(reference);
if (!myImportStatic && reference.getText().equals(reference.getQualifiedName())) {
return;
}
PsiElement parent = reference.getParent();
if (parent instanceof PsiImportStatementBase) {
return;
}
while (parent instanceof PsiJavaCodeReferenceElement) {
parent = parent.getParent();
if (parent instanceof PsiImportStatementBase) {
return;
}
}
if (myImportStatic) {
checkStaticImportReference(reference);
}
else {
checkImportReference(reference);
}
}
private void checkStaticImportReference(PsiJavaCodeReferenceElement reference) {
if (reference.isQualified()) {
return;
}
final PsiElement target = reference.resolve();
if (!(target instanceof PsiMethod) && !(target instanceof PsiClass) && !(target instanceof PsiField)) {
return;
}
final PsiMember member = (PsiMember)target;
for (PsiElement importTarget : myImportTargets) {
if (importTarget instanceof PsiMethod) {
if (member.equals(importTarget)) {
addReference(member, reference);
}
}
else if (importTarget instanceof PsiClass) {
final PsiClass importClass = (PsiClass)importTarget;
if (myOnDemand) {
final PsiClass containingClass = member.getContainingClass();
if (InheritanceUtil.isInheritorOrSelf(importClass, containingClass, true)) {
addReference(member, reference);
}
}
else {
if (importTarget.equals(member)) {
addReference(member, reference);
}
}
}
}
}
private void checkImportReference(PsiJavaCodeReferenceElement reference) {
final PsiElement element = reference.resolve();
if (!(element instanceof PsiClass)) {
return;
}
final PsiClass referencedClass = (PsiClass)element;
for (PsiElement importTarget : myImportTargets) {
if (importTarget instanceof PsiPackage) {
if (referencedClass.getContainingClass() != null) {
return;
}
final PsiFile file = referencedClass.getContainingFile();
if (!(file instanceof PsiJavaFile)) {
return;
}
final PsiJavaFile javaFile = (PsiJavaFile)file;
final PsiPackage aPackage = (PsiPackage)importTarget;
final String packageName = aPackage.getQualifiedName();
final String filePackage = javaFile.getPackageName();
if (filePackage.equals(packageName)) {
addReference(referencedClass, reference);
}
}
else if (importTarget instanceof PsiClass) {
final PsiClass aClass = (PsiClass)importTarget;
final String name = aClass.getQualifiedName();
if (name == null) {
return;
}
if (!myOnDemand) {
if (name.equals(referencedClass.getQualifiedName())) {
addReference(referencedClass, reference);
}
}
else {
final PsiClass containingClass = referencedClass.getContainingClass();
if (containingClass == null) {
return;
}
if (name.equals(containingClass.getQualifiedName())) {
addReference(referencedClass, reference);
}
}
}
}
}
private void addReference(PsiMember referencedMember, PsiJavaCodeReferenceElement reference) {
List<PsiElement> referenceList = classReferenceListMap.get(referencedMember);
if (referenceList == null) {
referenceList = new ArrayList<PsiElement>();
classReferenceListMap.put(referencedMember, referenceList);
}
referenceList.add(reference.getReferenceNameElement());
}
public Map<PsiMember, List<PsiElement>> getClassReferenceListMap() {
return classReferenceListMap;
}
}
static class PsiMemberComparator implements Comparator<PsiMember> {
@Override
public int compare(PsiMember member1, PsiMember member2) {
final String name1 = member1.getName();
if (name1 == null) {
return -1;
}
final String name2 = member2.getName();
if (name2 == null) {
return 1;
}
final int i = name1.compareTo(name2);
if (i != 0) {
return i;
}
final PsiJavaFile file1 = (PsiJavaFile)member1.getContainingFile();
final PsiJavaFile file2 = (PsiJavaFile)member2.getContainingFile();
final String packageName1 = file1.getPackageName();
final String packageName2 = file2.getPackageName();
return packageName1.compareTo(packageName2);
}
}
}