ru.crazyproger.plugins.webtoper.component.ClassLineMarkerProvider.java Source code

Java tutorial

Introduction

Here is the source code for ru.crazyproger.plugins.webtoper.component.ClassLineMarkerProvider.java

Source

/*
 * 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;
        }
    }
}