ru.crazyproger.plugins.webtoper.nls.codeinsight.NlsLineMarkerProvider.java Source code

Java tutorial

Introduction

Here is the source code for ru.crazyproger.plugins.webtoper.nls.codeinsight.NlsLineMarkerProvider.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.nls.codeinsight;

import com.google.common.base.Function;
import com.google.common.base.Predicates;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.intellij.codeInsight.daemon.LineMarkerInfo;
import com.intellij.codeInsight.daemon.LineMarkerProvider;
import com.intellij.codeInsight.navigation.NavigationGutterIconBuilder;
import com.intellij.icons.AllIcons;
import com.intellij.lang.properties.IProperty;
import com.intellij.lang.properties.psi.Property;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiReference;
import com.intellij.refactoring.psi.SearchUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import ru.crazyproger.plugins.webtoper.nls.psi.NlsFileImpl;

import javax.swing.Icon;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import static com.google.common.collect.Collections2.filter;
import static com.google.common.collect.Collections2.transform;
import static com.google.common.collect.Sets.newHashSet;
import static ru.crazyproger.plugins.webtoper.WebtoperBundle.message;
import static ru.crazyproger.plugins.webtoper.nls.psi.NlsFileImpl.Property2PsiElementFunction;
import static ru.crazyproger.plugins.webtoper.nls.psi.NlsFileImpl.PropertyKeyEqualsPredicate;

public class NlsLineMarkerProvider 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 Property) {
                collectOverridingLineMarkers((Property) element, result);
                collectOverriddenLineMarkers((Property) element, result);
            }
        }
    }

    private void collectOverridingLineMarkers(final Property element, Collection<LineMarkerInfo> result) {
        final String key = element.getKey();
        if (key == null)
            return;

        NlsFileImpl currentFile = (NlsFileImpl) element.getContainingFile();
        Collection<NlsFileImpl> includedFiles = currentFile.getIncludedFiles();
        if (includedFiles.isEmpty())
            return;

        Set<IProperty> parentProperties = new HashSet<IProperty>();
        for (NlsFileImpl parent : includedFiles) {
            Collection<IProperty> sameKey = findOverriddenProperties(key, parent,
                    Sets.<PsiFile>newHashSet(currentFile));
            parentProperties.addAll(sameKey);
        }
        if (parentProperties.isEmpty())
            return;

        MarkerInfo info = new MarkerInfo(element, parentProperties, result, AllIcons.General.OverridingMethod,
                "nls.lineMarker.overrides.popupTitle", "nls.lineMarker.overrides.tooltip.multiple",
                "nls.lineMarker.overrides.tooltip.oneBundle");
        fillLineMarkers(info);
    }

    @NotNull
    private Collection<IProperty> findOverriddenProperties(@NotNull final String key, @NotNull NlsFileImpl rootFile,
            @NotNull Collection<PsiFile> excludes) {
        if (excludes.contains(rootFile)) {
            return Collections.emptyList();
        }
        List<IProperty> fileProperties = rootFile.getProperties();
        Collection<IProperty> withSameKey = filter(fileProperties, new PropertyKeyEqualsPredicate(key));
        if (!withSameKey.isEmpty()) {
            return withSameKey;
        }

        Collection<NlsFileImpl> includedFiles = rootFile.getIncludedFiles();
        if (includedFiles.isEmpty())
            return Collections.emptyList();

        Collection<IProperty> result = new LinkedList<IProperty>();
        Set<PsiFile> newExcludes = newHashSet(excludes);
        newExcludes.add(rootFile);

        for (NlsFileImpl nlsFile : includedFiles) {
            result.addAll(findOverriddenProperties(key, nlsFile, newExcludes));
        }
        return result;
    }

    private void collectOverriddenLineMarkers(Property element, Collection<LineMarkerInfo> result) {
        final String key = element.getKey();
        if (key == null)
            return;

        NlsFileImpl currentFile = (NlsFileImpl) element.getContainingFile();
        Collection<NlsFileImpl> referencing = getReferencingFiles(currentFile);
        Set<IProperty> properties = newHashSet();
        for (NlsFileImpl child : referencing) {
            Collection<IProperty> overridingProperties = findOverridingProperties(key, child,
                    Sets.<PsiFile>newHashSet(currentFile));
            properties.addAll(overridingProperties);
        }

        if (properties.isEmpty())
            return;

        MarkerInfo info = new MarkerInfo(element, properties, result, AllIcons.General.OverridenMethod,
                "nls.lineMarker.overridden.popupTitle", "nls.lineMarker.overridden.tooltip.multiple",
                "nls.lineMarker.overridden.tooltip.oneBundle");
        fillLineMarkers(info);
    }

    private Collection<NlsFileImpl> getReferencingFiles(NlsFileImpl currentFile) {
        Iterable<PsiReference> references = SearchUtils.findAllReferences(currentFile);

        Collection<NlsFileImpl> directChilds = transform(Lists.<PsiReference>newArrayList(references),
                new Reference2ContainedFileFunction());

        return filter(directChilds, Predicates.notNull());
    }

    private Collection<IProperty> findOverridingProperties(String key, NlsFileImpl currentFile,
            Set<PsiFile> excludes) {
        if (excludes.contains(currentFile)) {
            return Collections.emptyList();
        }
        List<IProperty> properties = currentFile.getProperties();
        List<IProperty> result = new LinkedList<IProperty>();
        Collection<IProperty> withSameKey = filter(properties, new PropertyKeyEqualsPredicate(key));
        result.addAll(withSameKey);

        excludes.add(currentFile);
        Collection<NlsFileImpl> children = getReferencingFiles(currentFile);
        for (NlsFileImpl child : children) {
            result.addAll(findOverridingProperties(key, child, excludes));
        }
        return result;
    }

    private void fillLineMarkers(MarkerInfo info) {
        Collection<PsiElement> targets = transform(info.getNavigationTargets(), new Property2PsiElementFunction());
        NavigationGutterIconBuilder<PsiElement> builder = NavigationGutterIconBuilder.create(info.getIcon());
        targets = filter(targets, Predicates.notNull());
        builder.setTargets(targets);
        String tooltipText;
        if (targets.size() > 1) { // todo #WT-39
            tooltipText = message(info.getMultiBundleKey());
        } else {
            PsiFile psiFile = targets.iterator().next().getContainingFile();
            assert psiFile instanceof NlsFileImpl;
            tooltipText = message(info.getOneBundleKey(), ((NlsFileImpl) psiFile).getNlsName());
        }
        builder.setTooltipText(tooltipText);
        builder.setPopupTitle(message(info.getToolTipKey()));
        info.getResult().add(builder.createLineMarkerInfo(info.getTarget()));
    }

    /**
     * parameter object
     */
    private static class MarkerInfo {
        private final Property target;
        private final Collection<IProperty> navigationTargets;
        private final Collection<LineMarkerInfo> result;
        private final Icon icon;
        private final String toolTipKey;
        private final String multiBundleKey;
        private final String oneBundleKey;

        private MarkerInfo(Property target, Collection<IProperty> navigationTargets,
                Collection<LineMarkerInfo> result, Icon icon, String toolTipKey, String multiBundleKey,
                String oneBundleKey) {
            this.target = target;
            this.navigationTargets = navigationTargets;
            this.result = result;
            this.icon = icon;
            this.toolTipKey = toolTipKey;
            this.multiBundleKey = multiBundleKey;
            this.oneBundleKey = oneBundleKey;
        }

        public Property getTarget() {
            return target;
        }

        public Collection<IProperty> getNavigationTargets() {
            return navigationTargets;
        }

        public Collection<LineMarkerInfo> getResult() {
            return result;
        }

        public Icon getIcon() {
            return icon;
        }

        public String getToolTipKey() {
            return toolTipKey;
        }

        public String getMultiBundleKey() {
            return multiBundleKey;
        }

        public String getOneBundleKey() {
            return oneBundleKey;
        }
    }

    public static class Reference2ContainedFileFunction implements Function<PsiReference, NlsFileImpl> {
        @Override
        public NlsFileImpl apply(@Nullable PsiReference psiReference) {
            if (psiReference != null) {
                PsiElement element = psiReference.getElement();
                if (element != null && element.isValid()) {
                    PsiFile psiFile = element.getContainingFile();
                    if (psiFile instanceof NlsFileImpl) {
                        return (NlsFileImpl) psiFile;
                    }
                }
            }
            return null;
        }
    }
}