/* * Copyright 2013 Vladimir Rudev * * 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 ru.crazyproger.plugins.webtoper.component; import com.google.common.collect.Iterables; import com.intellij.codeInsight.daemon.LineMarkerInfo; import com.intellij.codeInsight.daemon.LineMarkerProvider; import com.intellij.codeInsight.navigation.NavigationGutterIconBuilder; import com.intellij.ide.util.PsiElementListCellRenderer; import com.intellij.openapi.module.Module; import com.intellij.openapi.roots.ProjectFileIndex; import com.intellij.openapi.roots.ProjectRootManager; import com.intellij.openapi.util.Iconable; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiIdentifier; import com.intellij.psi.PsiJavaFile; import com.intellij.psi.PsiReference; import com.intellij.psi.presentation.java.SymbolPresentationUtil; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.xml.XmlAttribute; import com.intellij.psi.xml.XmlTag; import com.intellij.util.NullableFunction; import com.intellij.util.xml.DomElement; import com.intellij.util.xml.DomManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import ru.crazyproger.plugins.webtoper.Utils; import ru.crazyproger.plugins.webtoper.component.dom.schema.pseudo.DomIconable; import ru.crazyproger.plugins.webtoper.component.dom.schema.pseudo.IdentifiedById; import ru.crazyproger.plugins.webtoper.component.dom.schema.pseudo.PrimaryElement; import ru.crazyproger.plugins.webtoper.config.Icons; import javax.swing.Icon; import java.util.ArrayList; import java.util.Collection; import java.util.List; import static ru.crazyproger.plugins.webtoper.WebtoperBundle.message; public class ClassLineMarkerProvider implements LineMarkerProvider { @Nullable @Override public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement element) { return null; } @Override public void collectSlowLineMarkers(@NotNull List<PsiElement> elements, @NotNull Collection<LineMarkerInfo> result) { for (PsiElement element : elements) { if (element instanceof PsiIdentifier && element.getParent() instanceof PsiClass && element.getContainingFile() instanceof PsiJavaFile) { collectClassLineMarkers((PsiClass) element.getParent(), result); } } } private void collectClassLineMarkers(PsiClass element, Collection<LineMarkerInfo> result) { ProjectFileIndex fileIndex = ProjectRootManager.getInstance(element.getProject()).getFileIndex(); PsiFile containingFile = element.getContainingFile(); PsiIdentifier identifier = PsiTreeUtil.getChildOfType(element, PsiIdentifier.class); if (identifier == null) return; if (containingFile == null) return; VirtualFile virtualFile = containingFile.getVirtualFile(); if (virtualFile == null) return; Module module = fileIndex.getModuleForFile(virtualFile); if (module == null) return; Collection<PsiElement> elements = getNavigatablePsiElements(element, module); if (elements.isEmpty()) return; Icon icon; MyPsiElementCellRenderer renderer = new MyPsiElementCellRenderer(DomManager.getDomManager(element.getProject())); if (elements.size() > 1) { icon = Icons.W16; } else { icon = renderer.getIcon(Iterables.getFirst(elements, null)); } NavigationGutterIconBuilder<PsiElement> builder = NavigationGutterIconBuilder.create(icon); builder.setTargets(elements); builder.setPopupTitle(message("class.lineMarker.popupTitle")); builder.setTooltipTitle(message("class.lineMarker.tooltip.title")); builder.setNamer(new XmlFileByElementNamer()); builder.setCellRenderer(renderer); result.add(builder.createLineMarkerInfo(identifier)); } private Collection<PsiElement> getNavigatablePsiElements(PsiClass element, Module module) { Collection<PsiElement> elements = new ArrayList<PsiElement>(); PsiReference[] references = ReferencesSearch.search(element, Utils.getXmlConfigsScope(module), false).toArray(new PsiReference[0]); DomManager domManager = DomManager.getDomManager(element.getProject()); for (PsiReference reference : references) { PsiElement referenceElement = reference.getElement(); DomElement domElement = getDomElement(referenceElement, domManager); if (domElement == null) continue; PrimaryElement primary = domElement.getParentOfType(PrimaryElement.class, false); if (primary == null) continue; elements.add(referenceElement); } return elements; } private DomElement getDomElement(PsiElement element, DomManager domManager) { XmlAttribute parentAttribute = PsiTreeUtil.getParentOfType(element, XmlAttribute.class); if (parentAttribute != null) return domManager.getDomElement(parentAttribute); XmlTag parentTag = PsiTreeUtil.getParentOfType(element, XmlTag.class); if (parentTag != null) return domManager.getDomElement(parentTag); return null; } private static class XmlFileByElementNamer implements NullableFunction<PsiElement, String> { @Nullable @Override public String fun(PsiElement param) { PsiFile containingFile = param.getContainingFile(); if (containingFile != null) { return containingFile.getName(); } return null; } } private static class MyPsiElementCellRenderer extends PsiElementListCellRenderer<PsiElement> { private DomManager domManager; private MyPsiElementCellRenderer(DomManager domManager) { this.domManager = domManager; } @Override protected Icon getIcon(PsiElement element) { XmlTag tag = PsiTreeUtil.getParentOfType(element, XmlTag.class); DomElement domElement = domManager.getDomElement(tag); if (domElement != null) { DomIconable parent = domElement.getParentOfType(DomIconable.class, false); if (parent != null) { return parent.getIcon(getIconFlags()); } } return super.getIcon(tag); } @Override public String getElementText(PsiElement element) { XmlTag tag = PsiTreeUtil.getParentOfType(element, XmlTag.class); DomElement domElement = domManager.getDomElement(tag); if (domElement != null) { IdentifiedById byId = domElement.getParentOfType(IdentifiedById.class, false); if (byId != null && byId.isValid() && byId.getId().getValue() != null) { return message("class.lineMarker.byId.element.text", byId.getId().getValue()); } } return SymbolPresentationUtil.getSymbolPresentableText(element); } public String getContainerText(PsiElement element, final String name) { return SymbolPresentationUtil.getSymbolContainerText(element); } @Override protected int getIconFlags() { return Iconable.ICON_FLAG_VISIBILITY; } } }