/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.plugin.ij.usages;
import com.intellij.openapi.actionSystem.DataKey;
import com.intellij.openapi.actionSystem.DataSink;
import com.intellij.openapi.actionSystem.LangDataKeys;
import com.intellij.openapi.actionSystem.TypeSafeDataProvider;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Iconable;
import com.intellij.openapi.vcs.FileStatus;
import com.intellij.openapi.vcs.FileStatusManager;
import com.intellij.openapi.vcs.impl.FileStatusProvider;
import com.intellij.openapi.vcs.impl.VcsFileStatusProvider;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiSubstitutor;
import com.intellij.psi.SmartPointerManager;
import com.intellij.psi.SmartPsiElementPointer;
import com.intellij.psi.impl.source.tree.injected.InjectedLanguageUtil;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.usageView.UsageInfo;
import com.intellij.usages.Usage;
import com.intellij.usages.UsageGroup;
import com.intellij.usages.UsageView;
import com.intellij.usages.UsageViewSettings;
import com.intellij.usages.rules.PsiElementUsage;
import com.intellij.usages.rules.UsageGroupingRule;
import gw.plugin.ij.lang.psi.IGosuFile;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
public class GosuMethodGroupingRule implements UsageGroupingRule {
private static final Logger LOG = Logger.getInstance("#com.intellij.usages.impl.rules.MethodGroupingRule");
public UsageGroup groupUsage(@NotNull Usage usage) {
if (!(usage instanceof PsiElementUsage)) return null;
PsiElement psiElement = ((PsiElementUsage) usage).getElement();
PsiFile containingFile = psiElement.getContainingFile();
PsiFile topLevelFile = InjectedLanguageUtil.getTopLevelFile(containingFile);
if (topLevelFile instanceof IGosuFile) {
PsiElement containingMethod = topLevelFile == containingFile ? psiElement : containingFile.getContext();
do {
containingMethod = PsiTreeUtil.getParentOfType(containingMethod, PsiMethod.class, true);
if (containingMethod == null || ((PsiMethod) containingMethod).getContainingClass().getQualifiedName() != null)
break;
}
while (true);
if (containingMethod != null) {
return new MethodUsageGroup((PsiMethod) containingMethod);
}
}
return null;
}
private static class MethodUsageGroup implements UsageGroup, TypeSafeDataProvider {
@NotNull
private final SmartPsiElementPointer<PsiMethod> myMethodPointer;
private final String myName;
private Icon myIcon;
public MethodUsageGroup(@NotNull PsiMethod psiMethod) {
myName = PsiFormatUtil.formatMethod(
psiMethod,
PsiSubstitutor.EMPTY,
PsiFormatUtil.SHOW_NAME | PsiFormatUtil.SHOW_PARAMETERS,
PsiFormatUtil.SHOW_TYPE
);
myMethodPointer = SmartPointerManager.getInstance(psiMethod.getProject()).createLazyPointer(psiMethod);
update();
}
public void update() {
if (isValid()) {
myIcon = getIconImpl(getMethod());
}
}
private static Icon getIconImpl(@NotNull PsiMethod psiMethod) {
return psiMethod.getIcon(Iconable.ICON_FLAG_VISIBILITY | Iconable.ICON_FLAG_READ_STATUS);
}
public int hashCode() {
return myName.hashCode();
}
public boolean equals(@NotNull Object object) {
if (!(object instanceof MethodUsageGroup)) {
return false;
}
MethodUsageGroup group = (MethodUsageGroup) object;
if (isValid() && group.isValid()) {
return getMethod().getManager().areElementsEquivalent(getMethod(), group.getMethod());
}
return Comparing.equal(myName, ((MethodUsageGroup) object).myName);
}
public Icon getIcon(boolean isOpen) {
return myIcon;
}
@Nullable
private PsiMethod getMethod() {
return myMethodPointer.getElement();
}
@NotNull
public String getText(UsageView view) {
return myName;
}
public FileStatus getFileStatus() {
return isValid() ?
FileStatusManager.getInstance(getMethod().getProject()).getStatus(getMethod().getContainingFile().getVirtualFile())
: null;
}
public boolean isValid() {
final PsiMethod method = getMethod();
return method != null && method.isValid();
}
public void navigate(boolean focus) throws UnsupportedOperationException {
if (canNavigate()) {
getMethod().navigate(focus);
}
}
public boolean canNavigate() {
return isValid();
}
public boolean canNavigateToSource() {
return canNavigate();
}
public int compareTo(@NotNull UsageGroup usageGroup) {
if (!(usageGroup instanceof MethodUsageGroup)) {
LOG.error("MethodUsageGroup expected but " + usageGroup.getClass() + " found");
}
MethodUsageGroup other = (MethodUsageGroup) usageGroup;
PsiMethod myMethod = myMethodPointer.getElement();
PsiMethod otherMethod = other.myMethodPointer.getElement();
if (myMethod != null && otherMethod != null && myMethod != otherMethod && !UsageViewSettings.getInstance().IS_SORT_MEMBERS_ALPHABETICALLY) {
return myMethod.getTextOffset() < otherMethod.getTextOffset() ? -1 : 1;
}
return myName.compareTo(other.myName);
}
public void calcData(final DataKey key, @NotNull final DataSink sink) {
if (!isValid()) return;
if (LangDataKeys.PSI_ELEMENT == key) {
sink.put(LangDataKeys.PSI_ELEMENT, getMethod());
}
if (UsageView.USAGE_INFO_KEY == key) {
PsiMethod method = getMethod();
if (method != null) {
sink.put(UsageView.USAGE_INFO_KEY, new UsageInfo(method));
}
}
}
}
}