org.sonarlint.intellij.config.global.SonarQubeServerMgmtPanel.java Source code

Java tutorial

Introduction

Here is the source code for org.sonarlint.intellij.config.global.SonarQubeServerMgmtPanel.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.config.global;

import com.intellij.codeInsight.hint.HintManager;
import com.intellij.codeInsight.hint.HintUtil;
import com.intellij.icons.AllIcons;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.project.ProjectManager;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.Splitter;
import com.intellij.ui.AnActionButton;
import com.intellij.ui.AnActionButtonRunnable;
import com.intellij.ui.CollectionListModel;
import com.intellij.ui.ColoredListCellRenderer;
import com.intellij.ui.HyperlinkAdapter;
import com.intellij.ui.HyperlinkLabel;
import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.SimpleTextAttributes;
import com.intellij.ui.ToolbarDecorator;
import com.intellij.ui.awt.RelativePoint;
import com.intellij.ui.components.JBLabel;
import com.intellij.ui.components.JBList;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.border.Border;
import javax.swing.event.HyperlinkEvent;

import org.apache.commons.lang.StringUtils;
import org.jetbrains.annotations.Nullable;
import org.sonarlint.intellij.config.project.SonarLintProjectSettings;
import org.sonarlint.intellij.core.ServerUpdateTask;
import org.sonarlint.intellij.core.SonarLintServerManager;
import org.sonarlint.intellij.messages.GlobalConfigurationListener;
import org.sonarlint.intellij.util.ResourceLoader;
import org.sonarlint.intellij.util.SonarLintUtils;
import org.sonarsource.sonarlint.core.client.api.connected.ConnectedSonarLintEngine;
import org.sonarsource.sonarlint.core.client.api.connected.GlobalUpdateStatus;
import org.sonarsource.sonarlint.core.client.api.connected.StateListener;

public class SonarQubeServerMgmtPanel implements Disposable {
    private static final Logger LOGGER = Logger.getInstance(SonarQubeServerMgmtPanel.class);
    private static final String LABEL_NO_SERVERS = "No servers";
    private static final String LIST_ICON = ResourceLoader.ICON_SONARQUBE_16;

    // UI
    private JPanel panel;
    private Splitter splitter;
    private JPanel serversPanel;
    private JBList serverList;
    private JPanel emptyPanel;
    private JLabel serverStatus;
    private JButton updateServerButton;

    // Model
    private GlobalConfigurationListener serverChangeListener;
    private SonarLintServerManager serverManager;
    private List<SonarQubeServer> servers = new ArrayList<>();
    private Set<String> deletedServerIds = new HashSet<>();
    private ConnectedSonarLintEngine engine = null;
    private StateListener engineListener;

    public SonarQubeServerMgmtPanel() {
        create();
    }

    public void create() {
        Application app = ApplicationManager.getApplication();
        serverManager = app.getComponent(SonarLintServerManager.class);
        serverChangeListener = app.getMessageBus()
                .syncPublisher(GlobalConfigurationListener.SONARLINT_GLOBAL_CONFIG_TOPIC);
        serverList = new JBList();
        serverList.getEmptyText().setText(LABEL_NO_SERVERS);
        serverList.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent evt) {
                if (evt.getClickCount() == 2) {
                    editServer();
                }
            }
        });
        serverList.addListSelectionListener(e -> {
            if (!e.getValueIsAdjusting()) {
                onServerSelect();
            }
        });

        serversPanel = new JPanel(new BorderLayout());

        ToolbarDecorator toolbarDecorator = ToolbarDecorator.createDecorator(serverList).setEditActionName("Edit")
                .setEditAction(e -> editServer()).disableUpDownActions();

        toolbarDecorator.setAddAction(new AddServerAction());
        toolbarDecorator.setRemoveAction(new RemoveServerAction());

        serversPanel.add(toolbarDecorator.createPanel(), BorderLayout.CENTER);
        splitter = new Splitter(true);
        splitter.setFirstComponent(serversPanel);
        splitter.setSecondComponent(createServerStatus());

        JBLabel emptyLabel = new JBLabel("No server selected", SwingConstants.CENTER);
        emptyPanel = new JPanel(new BorderLayout());
        emptyPanel.add(emptyLabel, BorderLayout.CENTER);

        Border b = IdeBorderFactory.createTitledBorder("SonarQube servers");
        panel = new JPanel(new BorderLayout());
        panel.setBorder(b);
        panel.add(splitter);

        serverList.setCellRenderer(new ColoredListCellRenderer() {
            @Override
            protected void customizeCellRenderer(JList list, Object value, int index, boolean selected,
                    boolean hasFocus) {
                SonarQubeServer server = (SonarQubeServer) value;
                try {
                    setIcon(ResourceLoader.getIcon(LIST_ICON));
                } catch (IOException e) {
                    LOGGER.error("Error loading SonarLint icon", e);
                }

                append(server.getName(), SimpleTextAttributes.REGULAR_ATTRIBUTES);
            }
        });
    }

    private JPanel createServerStatus() {
        JPanel serverStatusPanel = new JPanel(new GridBagLayout());

        JLabel serverStatusLabel = new JLabel("Local update: ");
        updateServerButton = new JButton();
        serverStatus = new JLabel();

        final HyperlinkLabel link = new HyperlinkLabel("");
        link.setIcon(AllIcons.General.Help_small);
        link.setUseIconAsLink(true);
        link.addHyperlinkListener(new HyperlinkAdapter() {
            @Override
            protected void hyperlinkActivated(HyperlinkEvent e) {
                final JLabel label = new JLabel(
                        "<html>Click to fetch data from the selected SonarQube server, such as the list of projects,<br>"
                                + " rules, quality profiles, etc. This needs to be done before being able to select a project.");
                label.setBorder(HintUtil.createHintBorder());
                label.setBackground(HintUtil.INFORMATION_COLOR);
                label.setOpaque(true);
                HintManager.getInstance().showHint(label, RelativePoint.getSouthWestOf(link),
                        HintManager.HIDE_BY_ANY_KEY | HintManager.HIDE_BY_TEXT_CHANGE, -1);
            }
        });

        JPanel flow1 = new JPanel(new FlowLayout(FlowLayout.LEADING));
        flow1.add(serverStatusLabel);
        flow1.add(serverStatus);

        JPanel flow2 = new JPanel(new FlowLayout(FlowLayout.LEADING));
        flow2.add(updateServerButton);
        flow2.add(link);

        serverStatusPanel.add(flow1, new GridBagConstraints(0, 0, 1, 1, 0.5, 1, GridBagConstraints.LINE_START, 0,
                new Insets(0, 0, 0, 0), 0, 0));
        serverStatusPanel.add(flow2, new GridBagConstraints(1, 0, 1, 1, 0.5, 1, GridBagConstraints.LINE_START, 0,
                new Insets(0, 0, 0, 0), 0, 0));

        updateServerButton.setAction(new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent e) {
                actionUpdateServerTask(false);
            }
        });
        updateServerButton.setText("Update binding");
        updateServerButton.setToolTipText("Update local data: quality profile, settings, ...");

        JPanel alignedPanel = new JPanel(new BorderLayout());
        alignedPanel.add(serverStatusPanel, BorderLayout.NORTH);
        return alignedPanel;
    }

    private void unbindRemovedServers() {
        if (deletedServerIds.isEmpty()) {
            return;
        }

        Project[] openProjects = ProjectManager.getInstance().getOpenProjects();

        for (Project p : openProjects) {
            SonarLintProjectSettings projectSettings = SonarLintUtils.get(p, SonarLintProjectSettings.class);
            if (projectSettings.getServerId() != null && deletedServerIds.contains(projectSettings.getServerId())) {
                projectSettings.setBindingEnabled(false);
                projectSettings.setServerId(null);
                projectSettings.setProjectKey(null);
            }
        }
    }

    public JComponent getComponent() {
        return panel;
    }

    public boolean isModified(SonarLintGlobalSettings settings) {
        return !servers.equals(settings.getSonarQubeServers());
    }

    public void save(SonarLintGlobalSettings settings) {
        List<SonarQubeServer> copyList = servers.stream().map(SonarQubeServer::new).collect(Collectors.toList());
        settings.setSonarQubeServers(copyList);

        //remove them even if a server with the same name was later added
        unbindRemovedServers();
    }

    public void load(SonarLintGlobalSettings settings) {
        servers.clear();
        deletedServerIds.clear();

        CollectionListModel<SonarQubeServer> listModel = new CollectionListModel<>(
                new ArrayList<SonarQubeServer>());

        for (SonarQubeServer s : settings.getSonarQubeServers()) {
            SonarQubeServer copy = new SonarQubeServer(s);
            listModel.add(copy);
            servers.add(copy);
        }

        serverList.setModel(listModel);

        if (!servers.isEmpty()) {
            serverList.setSelectedValue(servers.get(0), true);
        }
    }

    @Nullable
    private SonarQubeServer getSelectedServer() {
        return (SonarQubeServer) serverList.getSelectedValue();
    }

    private void onServerSelect() {
        switchTo(getSelectedServer());
    }

    private void switchTo(@Nullable SonarQubeServer server) {
        if (engineListener != null) {
            engine.removeStateListener(engineListener);
            engineListener = null;
            engine = null;
        }

        if (server != null) {
            engine = serverManager.getConnectedEngine(server.getName());
            engineListener = newState -> ApplicationManager.getApplication().invokeLater(() -> {
                // re-fetch state, as some time might have passed until it was assigned to the EDT and things might have changed
                if (engine == null) {
                    return;
                }
                setStatus(engine.getState());
            });
            ConnectedSonarLintEngine.State state = engine.getState();
            setStatus(state);
            engine.addStateListener(engineListener);
        } else {
            serverStatus.setText("[ no server selected ]");
            updateServerButton.setEnabled(false);
        }
    }

    private void setStatus(ConnectedSonarLintEngine.State state) {
        ConnectedSonarLintEngine.State currentState = engine.getState();
        StringBuilder builder = new StringBuilder();

        switch (currentState) {
        case NEVER_UPDATED:
            builder.append("never updated");
            break;
        case UPDATED:
            GlobalUpdateStatus updateStatus = engine.getUpdateStatus();
            if (updateStatus != null) {
                builder.append(SonarLintUtils.age(updateStatus.getLastUpdateDate().getTime()));
            } else {
                builder.append("up to date");
            }
            break;
        case UPDATING:
            builder.append("updating..");
            break;
        case NEED_UPDATE:
            builder.append("needs update");
            break;
        case UNKNOW:
        default:
            builder.append("unknown");
            break;
        }
        serverStatus.setText(builder.toString());
        updateServerButton.setEnabled(state != ConnectedSonarLintEngine.State.UPDATING);
    }

    private void actionUpdateServerTask(boolean background) {
        SonarQubeServer server = getSelectedServer();
        if (server == null || engine == null || engine.getState() == ConnectedSonarLintEngine.State.UPDATING) {
            return;
        }

        Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
        Set<String> projectKeys = new HashSet<>();

        for (Project p : openProjects) {
            SonarLintProjectSettings projectSettings = SonarLintUtils.get(p, SonarLintProjectSettings.class);
            if (projectSettings.isBindingEnabled() && server.getName().equals(projectSettings.getServerId())
                    && projectSettings.getProjectKey() != null) {
                projectKeys.add(projectSettings.getProjectKey());
            }
        }

        ServerUpdateTask task = new ServerUpdateTask(engine, server, projectKeys, false);
        if (background) {
            ProgressManager.getInstance().run(task.asBackground());
        } else {
            ProgressManager.getInstance().run(task.asModal());
        }
    }

    private void editServer() {
        SonarQubeServer selectedServer = getSelectedServer();
        if (selectedServer != null) {
            SonarQubeServerEditor serverEditor = new SonarQubeServerEditor(panel, servers, selectedServer, false);
            serverEditor.show();
        }
    }

    @Override
    public void dispose() {
        if (engineListener != null) {
            engine.removeStateListener(engineListener);
            engineListener = null;
            engine = null;
        }
    }

    private class AddServerAction implements AnActionButtonRunnable {
        @Override
        public void run(AnActionButton anActionButton) {
            SonarQubeServer newServer = new SonarQubeServer();
            SonarQubeServerEditor serverEditor = new SonarQubeServerEditor(panel, servers, newServer, true);

            if (serverEditor.showAndGet()) {
                servers.add(newServer);
                ((CollectionListModel) serverList.getModel()).add(newServer);
                serverList.setSelectedIndex(serverList.getModel().getSize() - 1);
                serverChangeListener.changed(servers);
                ServerUpdateTask task = new ServerUpdateTask(serverManager.getConnectedEngine(newServer.getName()),
                        newServer, Collections.emptySet(), false);
                ProgressManager.getInstance().run(task.asBackground());
            }
        }
    }

    private class RemoveServerAction implements AnActionButtonRunnable {
        @Override
        public void run(AnActionButton anActionButton) {
            SonarQubeServer server = getSelectedServer();
            int selectedIndex = serverList.getSelectedIndex();

            if (server == null) {
                return;
            }

            Project[] openProjects = ProjectManager.getInstance().getOpenProjects();
            List<String> projectsUsingNames = getOpenProjectNames(openProjects, server);

            if (!projectsUsingNames.isEmpty()) {
                int response = Messages.showYesNoDialog(serversPanel,
                        "<html>The following opened projects are bound to this server configuration:<br><b>"
                                + StringUtils.join(projectsUsingNames, "<br>")
                                + "</b><br>Delete the server?</html>",
                        "Server configuration in use", Messages.getWarningIcon());
                if (response == Messages.NO) {
                    return;
                }
            }

            CollectionListModel model = (CollectionListModel) serverList.getModel();
            // it's not removed from serverIds and editorList
            model.remove(server);
            servers.remove(server);
            serverChangeListener.changed(servers);

            if (model.getSize() > 0) {
                int newIndex = Math.min(model.getSize() - 1, Math.max(selectedIndex - 1, 0));
                serverList.setSelectedValue(model.getElementAt(newIndex), true);
            }
        }

        private List<String> getOpenProjectNames(Project[] openProjects, SonarQubeServer server) {
            List<String> projectsUsingNames = new LinkedList<>();

            for (Project p : openProjects) {
                SonarLintProjectSettings projectSettings = SonarLintUtils.get(p, SonarLintProjectSettings.class);
                String serverId = projectSettings.getServerId();
                if (projectSettings.getServerId() != null && serverId != null
                        && serverId.equals(server.getName())) {
                    projectsUsingNames.add(p.getName());
                }
            }
            return projectsUsingNames;
        }
    }
}