org.sonarlint.intellij.ui.AbstractIssuesPanel.java Source code

Java tutorial

Introduction

Here is the source code for org.sonarlint.intellij.ui.AbstractIssuesPanel.java

Source

/*
 * SonarLint for IntelliJ IDEA
 * Copyright (C) 2015 SonarSource
 * sonarlint@sonarsource.com
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
 */
package org.sonarlint.intellij.ui;

import com.intellij.ide.OccurenceNavigator;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.actionSystem.ActionGroup;
import com.intellij.openapi.actionSystem.ActionManager;
import com.intellij.openapi.actionSystem.ActionToolbar;
import com.intellij.openapi.actionSystem.AnAction;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.editor.RangeMarker;
import com.intellij.openapi.fileEditor.OpenFileDescriptor;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.SimpleToolWindowPanel;
import com.intellij.openapi.ui.Splitter;
import com.intellij.tools.SimpleActionGroup;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.components.JBTabbedPane;
import com.intellij.ui.treeStructure.Tree;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.Collection;
import java.util.Collections;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import javax.swing.Box;
import javax.swing.JComponent;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import org.sonarlint.intellij.core.ProjectBindingManager;
import org.sonarlint.intellij.editor.SonarLintHighlighting;
import org.sonarlint.intellij.issue.LiveIssue;
import org.sonarlint.intellij.ui.nodes.AbstractNode;
import org.sonarlint.intellij.ui.nodes.IssueNode;
import org.sonarlint.intellij.ui.nodes.LocationNode;
import org.sonarlint.intellij.ui.tree.FlowsTree;
import org.sonarlint.intellij.ui.tree.FlowsTreeModelBuilder;
import org.sonarlint.intellij.ui.tree.IssueTree;
import org.sonarlint.intellij.ui.tree.IssueTreeModelBuilder;

abstract class AbstractIssuesPanel extends SimpleToolWindowPanel implements OccurenceNavigator {
    private static final String ID = "SonarLint";
    protected final Project project;
    protected final SonarLintHighlighting highlighting;
    protected SonarLintRulePanel rulePanel;
    protected JBTabbedPane detailsTab;
    protected Tree tree;
    protected IssueTreeModelBuilder treeBuilder;
    protected FlowsTree flowsTree;
    protected FlowsTreeModelBuilder flowsTreeBuilder;
    protected ActionToolbar mainToolbar;

    AbstractIssuesPanel(Project project, ProjectBindingManager projectBindingManager) {
        super(false, true);
        this.project = project;
        this.highlighting = ServiceManager.getService(project, SonarLintHighlighting.class);

        createFlowsTree();
        createIssuesTree();
        createTabs(projectBindingManager);
    }

    public void refreshToolbar() {
        mainToolbar.updateActionsImmediately();
    }

    private void createTabs(ProjectBindingManager projectBindingManager) {
        // Flows panel with tree
        JScrollPane flowsPanel = ScrollPaneFactory.createScrollPane(flowsTree);
        flowsPanel.getVerticalScrollBar().setUnitIncrement(10);

        // Rule panel
        rulePanel = new SonarLintRulePanel(project, projectBindingManager);
        JScrollPane scrollableRulePanel = ScrollPaneFactory.createScrollPane(rulePanel.getPanel(),
                ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
        scrollableRulePanel.getVerticalScrollBar().setUnitIncrement(10);

        detailsTab = new JBTabbedPane();
        detailsTab.insertTab("Rule", null, scrollableRulePanel, "Details about the rule", 0);
        detailsTab.insertTab("Locations", null, flowsPanel, "All locations involved in the issue", 1);
    }

    protected JComponent createSplitter(JComponent c1, JComponent c2, String proportionProperty, boolean vertical,
            float defaultSplit) {
        float savedProportion = PropertiesComponent.getInstance(project).getFloat(proportionProperty, defaultSplit);

        final Splitter splitter = new Splitter(vertical);
        splitter.setFirstComponent(c1);
        splitter.setSecondComponent(c2);
        splitter.setProportion(savedProportion);
        splitter.setHonorComponentsMinimumSize(true);
        splitter.addPropertyChangeListener(Splitter.PROP_PROPORTION, evt -> PropertiesComponent.getInstance(project)
                .setValue(proportionProperty, Float.toString(splitter.getProportion())));

        return splitter;
    }

    protected void flowsTreeSelectionChanged() {
        LocationNode[] selectedNodes = flowsTree.getSelectedNodes(LocationNode.class, null);
        if (selectedNodes.length > 0) {
            LocationNode node = selectedNodes[0];
            highlighting.highlightFlowsWithHighlightersUtil(node.rangeMarker(), node.message(),
                    Collections.emptyList());
        }
    }

    protected void issueTreeSelectionChanged() {
        IssueNode[] selectedNodes = tree.getSelectedNodes(IssueNode.class, null);
        if (selectedNodes.length > 0) {
            LiveIssue issue = selectedNodes[0].issue();
            rulePanel.setRuleKey(issue);
            if (issue.getRange() != null) {
                highlighting.highlightFlowsWithHighlightersUtil(issue.getRange(), issue.getMessage(),
                        issue.flows());
            }
            flowsTree.getEmptyText().setText("Selected issue doesn't have flows");
            flowsTreeBuilder.setFlows(issue.flows(), issue.getRange(), issue.getMessage());
            flowsTree.expandAll();
        } else {
            flowsTreeBuilder.clearFlows();
            flowsTree.getEmptyText().setText("No issue selected");
            rulePanel.setRuleKey(null);
            highlighting.removeHighlightingFlows();
        }
    }

    protected void setToolbar(String id) {
        ActionGroup group = (ActionGroup) ActionManager.getInstance().getAction(id);
        setToolbar(group);
    }

    protected void setToolbar(Collection<AnAction> actions) {
        setToolbar(createActionGroup(actions));
    }

    protected void setToolbar(ActionGroup group) {
        if (mainToolbar != null) {
            mainToolbar.setTargetComponent(null);
            super.setToolbar(null);
            mainToolbar = null;
        }
        mainToolbar = ActionManager.getInstance().createActionToolbar(ID, group, false);
        mainToolbar.setTargetComponent(this);
        Box toolBarBox = Box.createHorizontalBox();
        toolBarBox.add(mainToolbar.getComponent());
        super.setToolbar(toolBarBox);
        mainToolbar.getComponent().setVisible(true);
    }

    private static ActionGroup createActionGroup(Collection<AnAction> actions) {
        SimpleActionGroup actionGroup = new SimpleActionGroup();
        actions.forEach(actionGroup::add);
        return actionGroup;
    }

    private void createFlowsTree() {
        flowsTreeBuilder = new FlowsTreeModelBuilder();
        DefaultTreeModel model = flowsTreeBuilder.createModel();
        flowsTree = new FlowsTree(project, model);
        flowsTreeBuilder.clearFlows();
        flowsTree.getEmptyText().setText("No issue selected");
        flowsTree.addTreeSelectionListener(e -> flowsTreeSelectionChanged());
    }

    private void createIssuesTree() {
        treeBuilder = new IssueTreeModelBuilder();
        DefaultTreeModel model = treeBuilder.createModel();
        tree = new IssueTree(project, model);
        tree.addTreeSelectionListener(e -> issueTreeSelectionChanged());
        tree.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if (KeyEvent.VK_ESCAPE == e.getKeyCode()) {
                    highlighting.removeHighlightingFlows();
                }
            }
        });
        tree.addFocusListener(new FocusAdapter() {
            @Override
            public void focusGained(FocusEvent e) {
                if (!e.isTemporary()) {
                    issueTreeSelectionChanged();
                }
            }
        });
    }

    @CheckForNull
    private OccurenceNavigator.OccurenceInfo occurrence(@Nullable IssueNode node) {
        if (node == null) {
            return null;
        }

        TreePath path = new TreePath(node.getPath());
        tree.getSelectionModel().setSelectionPath(path);
        tree.scrollPathToVisible(path);

        RangeMarker range = node.issue().getRange();
        int startOffset = (range != null) ? range.getStartOffset() : 0;
        return new OccurenceNavigator.OccurenceInfo(
                new OpenFileDescriptor(project, node.issue().psiFile().getVirtualFile(), startOffset), -1, -1);
    }

    @Override
    public boolean hasNextOccurence() {
        // relies on the assumption that a TreeNodes will always be the last row in the table view of the tree
        TreePath path = tree.getSelectionPath();
        if (path == null) {
            return false;
        }
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
        if (node instanceof IssueNode) {
            return tree.getRowCount() != tree.getRowForPath(path) + 1;
        } else {
            return node.getChildCount() > 0;
        }
    }

    @Override
    public boolean hasPreviousOccurence() {
        TreePath path = tree.getSelectionPath();
        if (path == null) {
            return false;
        }
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
        return (node instanceof IssueNode) && !isFirst(node);
    }

    private static boolean isFirst(final TreeNode node) {
        final TreeNode parent = node.getParent();
        return parent == null || (parent.getIndex(node) == 0 && isFirst(parent));
    }

    @CheckForNull
    @Override
    public OccurenceNavigator.OccurenceInfo goNextOccurence() {
        TreePath path = tree.getSelectionPath();
        if (path == null) {
            return null;
        }
        return occurrence(treeBuilder.getNextIssue((AbstractNode<?>) path.getLastPathComponent()));
    }

    @CheckForNull
    @Override
    public OccurenceNavigator.OccurenceInfo goPreviousOccurence() {
        TreePath path = tree.getSelectionPath();
        if (path == null) {
            return null;
        }
        return occurrence(treeBuilder.getPreviousIssue((AbstractNode<?>) path.getLastPathComponent()));
    }

    @Override
    public String getNextOccurenceActionName() {
        return "Next Issue";
    }

    @Override
    public String getPreviousOccurenceActionName() {
        return "Previous Issue";
    }
}