org.sonar.scanner.protocol.viewer.ScannerReportViewerApp.java Source code

Java tutorial

Introduction

Here is the source code for org.sonar.scanner.protocol.viewer.ScannerReportViewerApp.java

Source

/*
 * SonarQube
 * Copyright (C) 2009-2017 SonarSource SA
 * mailto:info AT sonarsource DOT 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  02110-1301, USA.
 */
package org.sonar.scanner.protocol.viewer;

import java.awt.BorderLayout;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Scanner;

import javax.annotation.CheckForNull;
import javax.swing.JEditorPane;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTree;
import javax.swing.UIManager;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeSelectionModel;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang.StringUtils;
import org.sonar.core.util.CloseableIterator;
import org.sonar.scanner.protocol.output.FileStructure.Domain;
import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.scanner.protocol.output.ScannerReport.Changesets;
import org.sonar.scanner.protocol.output.ScannerReport.Changesets.Changeset;
import org.sonar.scanner.protocol.output.ScannerReport.Component;
import org.sonar.scanner.protocol.output.ScannerReport.Issue;
import org.sonar.scanner.protocol.output.ScannerReport.Metadata;
import org.sonar.scanner.protocol.output.ScannerReportReader;

public class ScannerReportViewerApp {

    private JFrame frame;
    private ScannerReportReader reader;
    private Metadata metadata;
    private SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");
    private JTree componentTree;
    private JSplitPane splitPane;
    private JTabbedPane tabbedPane;
    private JScrollPane treeScrollPane;
    private JScrollPane componentDetailsTab;
    private JScrollPane highlightingTab;
    private JScrollPane symbolTab;
    private JEditorPane componentEditor;
    private JEditorPane highlightingEditor;
    private JEditorPane symbolEditor;
    private JScrollPane sourceTab;
    private JEditorPane sourceEditor;
    private JScrollPane coverageTab;
    private JEditorPane coverageEditor;
    private JScrollPane testsTab;
    private JEditorPane testsEditor;
    private TextLineNumber textLineNumber;
    private JScrollPane duplicationTab;
    private JEditorPane duplicationEditor;
    private JScrollPane issuesTab;
    private JEditorPane issuesEditor;
    private JScrollPane measuresTab;
    private JEditorPane measuresEditor;
    private JScrollPane scmTab;
    private JEditorPane scmEditor;
    private JScrollPane activeRuleTab;
    private JEditorPane activeRuleEditor;

    /**
     * Create the application.
     */
    public ScannerReportViewerApp() {
        initialize();
    }

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    ScannerReportViewerApp window = new ScannerReportViewerApp();
                    window.frame.setVisible(true);

                    window.loadReport();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

        });
    }

    private void loadReport() {
        final JFileChooser fc = new JFileChooser();
        fc.setDialogTitle("Choose scanner report directory");
        File lastReport = getLastUsedReport();
        if (lastReport != null) {
            fc.setCurrentDirectory(lastReport);
        }
        fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        fc.setFileHidingEnabled(false);
        fc.setApproveButtonText("Open scanner report");
        int returnVal = fc.showOpenDialog(frame);
        if (returnVal == JFileChooser.APPROVE_OPTION) {
            File file = fc.getSelectedFile();
            try {
                setLastUsedReport(file);
                loadReport(file);
            } catch (Exception e) {
                JOptionPane.showMessageDialog(frame, e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
                exit();
            }
        } else {
            exit();
        }

    }

    @CheckForNull
    private File getLastUsedReport() {
        File f = new File(System.getProperty("java.io.tmpdir"), ".last_batch_report_dir");
        if (f.exists()) {
            String path;
            try {
                path = FileUtils.readFileToString(f, StandardCharsets.UTF_8);
            } catch (IOException e) {
                return null;
            }
            File lastReport = new File(path);
            if (lastReport.exists() && lastReport.isDirectory()) {
                return lastReport;
            }
        }
        return null;
    }

    private void setLastUsedReport(File lastReport) throws IOException {

        File f = new File(System.getProperty("java.io.tmpdir"), ".last_batch_report_dir");
        String fullPath = lastReport.getAbsolutePath();
        FileUtils.write(f, fullPath, StandardCharsets.UTF_8);
    }

    private void exit() {
        frame.setVisible(false);
        frame.dispose();
    }

    private void loadReport(File file) {
        reader = new ScannerReportReader(file);
        metadata = reader.readMetadata();
        updateTitle();
        loadComponents();
        updateActiveRules();
    }

    private void loadComponents() {
        int rootComponentRef = metadata.getRootComponentRef();
        Component component = reader.readComponent(rootComponentRef);
        DefaultMutableTreeNode project = createNode(component);
        loadChildren(component, project);
        getComponentTree().setModel(new DefaultTreeModel(project));
    }

    private static DefaultMutableTreeNode createNode(Component component) {
        return new DefaultMutableTreeNode(component) {
            @Override
            public String toString() {
                return getNodeName((Component) getUserObject());
            }
        };
    }

    private static String getNodeName(Component component) {
        switch (component.getType()) {
        case PROJECT:
        case MODULE:
            return component.getName();
        case DIRECTORY:
        case FILE:
            return component.getPath();
        default:
            throw new IllegalArgumentException("Unknow component type: " + component.getType());
        }
    }

    private void loadChildren(Component parentComponent, DefaultMutableTreeNode parentNode) {
        for (int ref : parentComponent.getChildRefList()) {
            Component child = reader.readComponent(ref);
            DefaultMutableTreeNode childNode = createNode(child);
            parentNode.add(childNode);
            loadChildren(child, childNode);
        }

    }

    private void updateTitle() {
        frame.setTitle(metadata.getProjectKey()
                + (StringUtils.isNotEmpty(metadata.getBranch()) ? (" (" + metadata.getBranch() + ")") : "") + " "
                + sdf.format(new Date(metadata.getAnalysisDate())));
    }

    private void updateDetails(Component component) {
        componentEditor.setText(component.toString());
        updateHighlighting(component);
        updateSymbols(component);
        updateSource(component);
        updateCoverage(component);
        updateTests(component);
        updateDuplications(component);
        updateIssues(component);
        updateMeasures(component);
        updateScm(component);
    }

    private void updateDuplications(Component component) {
        duplicationEditor.setText("");
        if (reader.hasCoverage(component.getRef())) {
            try (CloseableIterator<ScannerReport.Duplication> it = reader
                    .readComponentDuplications(component.getRef())) {
                while (it.hasNext()) {
                    ScannerReport.Duplication dup = it.next();
                    duplicationEditor.getDocument().insertString(
                            duplicationEditor.getDocument().getEndPosition().getOffset(), dup + "\n", null);
                }
            } catch (Exception e) {
                throw new IllegalStateException("Can't read duplications for " + getNodeName(component), e);
            }
        }
    }

    private void updateIssues(Component component) {
        issuesEditor.setText("");
        try (CloseableIterator<Issue> it = reader.readComponentIssues(component.getRef())) {
            while (it.hasNext()) {
                Issue issue = it.next();
                int offset = issuesEditor.getDocument().getEndPosition().getOffset();
                issuesEditor.getDocument().insertString(offset, issue.toString(), null);
            }
        } catch (Exception e) {
            throw new IllegalStateException("Can't read issues for " + getNodeName(component), e);
        }
    }

    private void updateCoverage(Component component) {
        coverageEditor.setText("");
        try (CloseableIterator<ScannerReport.LineCoverage> it = reader.readComponentCoverage(component.getRef())) {
            while (it.hasNext()) {
                ScannerReport.LineCoverage coverage = it.next();
                coverageEditor.getDocument().insertString(coverageEditor.getDocument().getEndPosition().getOffset(),
                        coverage + "\n", null);
            }
        } catch (Exception e) {
            throw new IllegalStateException("Can't read code coverage for " + getNodeName(component), e);
        }
    }

    private void updateTests(Component component) {
        testsEditor.setText("");
        File tests = reader.readTests(component.getRef());
        if (tests == null) {
            return;
        }
        try (InputStream inputStream = FileUtils.openInputStream(tests)) {
            ScannerReport.Test test = ScannerReport.Test.parser().parseDelimitedFrom(inputStream);
            while (test != null) {
                testsEditor.getDocument().insertString(testsEditor.getDocument().getEndPosition().getOffset(),
                        test + "\n", null);
                test = ScannerReport.Test.parser().parseDelimitedFrom(inputStream);
            }
        } catch (Exception e) {
            throw new IllegalStateException(e);
        }
    }

    private void updateSource(Component component) {
        File sourceFile = reader.getFileStructure().fileFor(Domain.SOURCE, component.getRef());
        sourceEditor.setText("");

        if (sourceFile.exists()) {
            try (Scanner s = new Scanner(sourceFile, StandardCharsets.UTF_8.name()).useDelimiter("\\Z")) {
                if (s.hasNext()) {
                    sourceEditor.setText(s.next());
                }
            } catch (IOException ex) {
                StringWriter errors = new StringWriter();
                ex.printStackTrace(new PrintWriter(errors));
                sourceEditor.setText(errors.toString());
            }
        }
    }

    private void updateActiveRules() {
        activeRuleEditor.setText("");

        StringBuilder builder = new StringBuilder();
        try (CloseableIterator<ScannerReport.ActiveRule> activeRuleCloseableIterator = reader.readActiveRules()) {
            while (activeRuleCloseableIterator.hasNext()) {
                builder.append(activeRuleCloseableIterator.next().toString()).append("\n");
            }
            activeRuleEditor.setText(builder.toString());
        }
    }

    private void updateHighlighting(Component component) {
        highlightingEditor.setText("");
        try (CloseableIterator<ScannerReport.SyntaxHighlightingRule> it = reader
                .readComponentSyntaxHighlighting(component.getRef())) {
            while (it.hasNext()) {
                ScannerReport.SyntaxHighlightingRule rule = it.next();
                highlightingEditor.getDocument().insertString(
                        highlightingEditor.getDocument().getEndPosition().getOffset(), rule + "\n", null);
            }
        } catch (Exception e) {
            throw new IllegalStateException("Can't read syntax highlighting for " + getNodeName(component), e);
        }
    }

    private void updateMeasures(Component component) {
        measuresEditor.setText("");
        try (CloseableIterator<ScannerReport.Measure> it = reader.readComponentMeasures(component.getRef())) {
            while (it.hasNext()) {
                ScannerReport.Measure measure = it.next();
                measuresEditor.getDocument().insertString(measuresEditor.getDocument().getEndPosition().getOffset(),
                        measure + "\n", null);
            }
        } catch (Exception e) {
            throw new IllegalStateException("Can't read measures for " + getNodeName(component), e);
        }
    }

    private void updateScm(Component component) {
        scmEditor.setText("");
        Changesets changesets = reader.readChangesets(component.getRef());
        if (changesets == null) {
            return;
        }
        List<Integer> changesetIndexByLine = changesets.getChangesetIndexByLineList();
        try {
            int index = 0;
            for (Changeset changeset : changesets.getChangesetList()) {
                scmEditor.getDocument().insertString(scmEditor.getDocument().getEndPosition().getOffset(),
                        Integer.toString(index) + "\n", null);
                scmEditor.getDocument().insertString(scmEditor.getDocument().getEndPosition().getOffset(),
                        changeset + "\n", null);
                index++;
            }

            scmEditor.getDocument().insertString(scmEditor.getDocument().getEndPosition().getOffset(), "\n", null);
            int line = 1;
            for (Integer idx : changesetIndexByLine) {
                scmEditor.getDocument().insertString(scmEditor.getDocument().getEndPosition().getOffset(),
                        Integer.toString(line) + ": " + idx + "\n", null);
                line++;
            }

        } catch (Exception e) {
            throw new IllegalStateException("Can't read SCM for " + getNodeName(component), e);
        }
    }

    private void updateSymbols(Component component) {
        symbolEditor.setText("");
        try (CloseableIterator<ScannerReport.Symbol> it = reader.readComponentSymbols(component.getRef())) {
            while (it.hasNext()) {
                ScannerReport.Symbol symbol = it.next();
                symbolEditor.getDocument().insertString(symbolEditor.getDocument().getEndPosition().getOffset(),
                        symbol + "\n", null);
            }
        } catch (Exception e) {
            throw new IllegalStateException("Can't read symbol references for " + getNodeName(component), e);
        }
    }

    /**
     * Initialize the contents of the frame.
     */
    private void initialize() {
        try {
            for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (Exception e) {
            // If Nimbus is not available, you can set the GUI to another look and feel.
        }
        frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        splitPane = new JSplitPane();
        frame.getContentPane().add(splitPane, BorderLayout.CENTER);

        tabbedPane = new JTabbedPane(JTabbedPane.TOP);
        tabbedPane.setPreferredSize(new Dimension(500, 7));
        splitPane.setRightComponent(tabbedPane);

        componentDetailsTab = new JScrollPane();
        tabbedPane.addTab("Component details", null, componentDetailsTab, null);

        componentEditor = new JEditorPane();
        componentDetailsTab.setViewportView(componentEditor);

        sourceTab = new JScrollPane();
        tabbedPane.addTab("Source", null, sourceTab, null);

        sourceEditor = createSourceEditor();
        sourceEditor.setEditable(false);
        sourceTab.setViewportView(sourceEditor);

        textLineNumber = createTextLineNumber();
        sourceTab.setRowHeaderView(textLineNumber);

        highlightingTab = new JScrollPane();
        tabbedPane.addTab("Highlighting", null, highlightingTab, null);

        highlightingEditor = new JEditorPane();
        highlightingTab.setViewportView(highlightingEditor);

        symbolTab = new JScrollPane();
        tabbedPane.addTab("Symbol references", null, symbolTab, null);

        symbolEditor = new JEditorPane();
        symbolTab.setViewportView(symbolEditor);

        coverageTab = new JScrollPane();
        tabbedPane.addTab("Coverage", null, coverageTab, null);

        coverageEditor = new JEditorPane();
        coverageTab.setViewportView(coverageEditor);

        duplicationTab = new JScrollPane();
        tabbedPane.addTab("Duplications", null, duplicationTab, null);

        duplicationEditor = new JEditorPane();
        duplicationTab.setViewportView(duplicationEditor);

        testsTab = new JScrollPane();
        tabbedPane.addTab("Tests", null, testsTab, null);

        testsEditor = new JEditorPane();
        testsTab.setViewportView(testsEditor);

        issuesTab = new JScrollPane();
        tabbedPane.addTab("Issues", null, issuesTab, null);

        issuesEditor = new JEditorPane();
        issuesTab.setViewportView(issuesEditor);

        measuresTab = new JScrollPane();
        tabbedPane.addTab("Measures", null, measuresTab, null);

        measuresEditor = new JEditorPane();
        measuresTab.setViewportView(measuresEditor);

        scmTab = new JScrollPane();
        tabbedPane.addTab("SCM", null, scmTab, null);

        scmEditor = new JEditorPane();
        scmTab.setViewportView(scmEditor);

        activeRuleTab = new JScrollPane();
        tabbedPane.addTab("ActiveRules", null, activeRuleTab, null);

        activeRuleEditor = new JEditorPane();
        activeRuleTab.setViewportView(activeRuleEditor);

        treeScrollPane = new JScrollPane();
        treeScrollPane.setPreferredSize(new Dimension(200, 400));
        splitPane.setLeftComponent(treeScrollPane);

        componentTree = new JTree();
        componentTree.setModel(new DefaultTreeModel(new DefaultMutableTreeNode("empty") {
            {
            }
        }));
        treeScrollPane.setViewportView(componentTree);
        componentTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
        componentTree.addTreeSelectionListener(new TreeSelectionListener() {
            @Override
            public void valueChanged(TreeSelectionEvent e) {
                DefaultMutableTreeNode node = (DefaultMutableTreeNode) componentTree.getLastSelectedPathComponent();

                if (node == null) {
                    // Nothing is selected.
                    return;
                }

                frame.setCursor(new Cursor(Cursor.WAIT_CURSOR));
                updateDetails((Component) node.getUserObject());
                frame.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));

            }

        });
        frame.pack();
    }

    public JTree getComponentTree() {
        return componentTree;
    }

    /**
     * @wbp.factory
     */
    public static JPanel createComponentPanel() {
        JPanel panel = new JPanel();
        return panel;
    }

    protected JEditorPane getComponentEditor() {
        return componentEditor;
    }

    /**
     * @wbp.factory
     */
    public static JEditorPane createSourceEditor() {
        JEditorPane editorPane = new JEditorPane();
        return editorPane;
    }

    /**
     * @wbp.factory
     */
    public TextLineNumber createTextLineNumber() {
        TextLineNumber textLineNumber = new TextLineNumber(sourceEditor);
        return textLineNumber;
    }
}