org.sonarlint.eclipse.ui.internal.bind.BindProjectsPage.java Source code

Java tutorial

Introduction

Here is the source code for org.sonarlint.eclipse.ui.internal.bind.BindProjectsPage.java

Source

/*
 * SonarLint for Eclipse
 * Copyright (C) 2015-2016 SonarSource SA
 * 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  02110-1301, USA.
 */
package org.sonarlint.eclipse.ui.internal.bind;

import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import org.eclipse.core.databinding.beans.BeanProperties;
import org.eclipse.core.databinding.observable.list.WritableList;
import org.eclipse.core.databinding.property.value.IValueProperty;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.bindings.keys.IKeyLookup;
import org.eclipse.jface.bindings.keys.KeyLookupFactory;
import org.eclipse.jface.databinding.viewers.ViewerSupport;
import org.eclipse.jface.dialogs.IMessageProvider;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.CheckboxTableViewer;
import org.eclipse.jface.viewers.ColumnViewerEditor;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent;
import org.eclipse.jface.viewers.ColumnViewerEditorActivationStrategy;
import org.eclipse.jface.viewers.ComboViewer;
import org.eclipse.jface.viewers.EditingSupport;
import org.eclipse.jface.viewers.FocusCellHighlighter;
import org.eclipse.jface.viewers.FocusCellOwnerDrawHighlighter;
import org.eclipse.jface.viewers.IBaseLabelProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.TableViewerColumn;
import org.eclipse.jface.viewers.TableViewerEditor;
import org.eclipse.jface.viewers.TableViewerFocusCellManager;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Link;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.widgets.Form;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.part.PageBook;
import org.sonarlint.eclipse.core.internal.jobs.ProjectUpdateJob;
import org.sonarlint.eclipse.core.internal.markers.MarkerUtils;
import org.sonarlint.eclipse.core.internal.resources.SonarLintProject;
import org.sonarlint.eclipse.core.internal.server.IServer;
import org.sonarlint.eclipse.core.internal.server.IServerLifecycleListener;
import org.sonarlint.eclipse.core.internal.server.ServersManager;
import org.sonarlint.eclipse.core.internal.utils.StringUtils;
import org.sonarlint.eclipse.ui.internal.Messages;
import org.sonarlint.eclipse.ui.internal.SonarLintImages;
import org.sonarlint.eclipse.ui.internal.SonarLintProjectDecorator;
import org.sonarlint.eclipse.ui.internal.server.wizard.NewServerLocationWizard;
import org.sonarsource.sonarlint.core.client.api.connected.RemoteModule;
import org.sonarsource.sonarlint.core.client.api.util.TextSearchIndex;

public class BindProjectsPage extends WizardPage {

    private final List<IProject> projects;
    private CheckboxTableViewer viewer;
    private Form noServersPage;
    private PageBook book;
    private IServerLifecycleListener serverListener;
    private IServer selectedServer;
    private Composite serverDropDownPage;
    private ComboViewer serverCombo;
    private Button autoBindBtn;
    private Button unassociateBtn;
    private Button checkAll;
    private Composite container;
    private Link updateServerLink;

    public BindProjectsPage(List<IProject> projects) {
        super("bindProjects", "Bind Eclipse projects to SonarQube projects", SonarLintImages.SONARWIZBAN_IMG);
        setDescription("SonarQube is an Open Source platform to manage code quality. "
                + "Bind your Eclipse projects to some SonarQube projects in order to get the same issues in Eclipse and in SonarQube.");
        this.projects = projects;
        if (!projects.isEmpty()) {
            selectedServer = ServersManager.getInstance()
                    .getServer(SonarLintProject.getInstance(projects.get(0)).getServerId());
        }
    }

    @Override
    public void dispose() {
        if (serverListener != null) {
            ServersManager.getInstance().removeServerLifecycleListener(serverListener);
        }
    }

    @Override
    public void createControl(Composite parent) {
        container = new Composite(parent, SWT.NONE);

        GridLayout layout = new GridLayout();
        layout.numColumns = 1;
        layout.marginHeight = 0;
        layout.marginWidth = 5;
        container.setLayout(layout);

        book = new PageBook(container, SWT.NONE);

        createNoServerForm(book);
        createServerDropDown(book);

        toggleServerPage();

        createCheckUncheckAllCb();

        viewer = CheckboxTableViewer.newCheckList(container,
                SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION);
        viewer.getTable().setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, true, 1, 3));

        viewer.getTable().setHeaderVisible(true);

        TableViewerColumn columnProject = new TableViewerColumn(viewer, SWT.LEFT);
        columnProject.getColumn().setText("Eclipse Project");
        columnProject.getColumn().setWidth(200);

        TableViewerColumn columnSonarProject = new TableViewerColumn(viewer, SWT.LEFT);
        columnSonarProject.getColumn().setText("SonarQube Project");
        columnSonarProject.getColumn().setWidth(600);

        columnSonarProject.setEditingSupport(new ProjectAssociationModelEditingSupport(viewer));

        List<ProjectBindModel> list = new ArrayList<>();
        for (IProject project : projects) {
            ProjectBindModel sonarProject = new ProjectBindModel(project);
            list.add(sonarProject);
        }

        ColumnViewerEditorActivationStrategy activationSupport = createActivationSupport();

        /*
         * Without focus highlighter, keyboard events will not be delivered to
         * ColumnViewerEditorActivationStragety#isEditorActivationEvent(...) (see above)
         */
        FocusCellHighlighter focusCellHighlighter = new FocusCellOwnerDrawHighlighter(viewer);
        TableViewerFocusCellManager focusCellManager = new TableViewerFocusCellManager(viewer,
                focusCellHighlighter);

        TableViewerEditor.create(viewer, focusCellManager, activationSupport,
                ColumnViewerEditor.TABBING_VERTICAL | ColumnViewerEditor.KEYBOARD_ACTIVATION);

        ViewerSupport.bind(viewer, new WritableList(list, ProjectBindModel.class), new IValueProperty[] {
                BeanProperties.value(ProjectBindModel.class, ProjectBindModel.PROPERTY_PROJECT_ECLIPSE_NAME),
                BeanProperties.value(ProjectBindModel.class, ProjectBindModel.PROPERTY_PROJECT_SONAR_FULLNAME) });

        Composite btnContainer = new Composite(container, SWT.NONE);

        FillLayout btnLayout = new FillLayout();
        btnContainer.setLayout(btnLayout);

        viewer.addSelectionChangedListener(new ISelectionChangedListener() {

            @Override
            public void selectionChanged(SelectionChangedEvent event) {
                updateState();
            }
        });

        createUnassociateBtn(btnContainer);

        createAutoBindBtn(btnContainer);

        viewer.setAllChecked(true);

        updateState();
        setControl(container);
    }

    private void createAutoBindBtn(Composite btnContainer) {
        autoBindBtn = new Button(btnContainer, SWT.PUSH);
        autoBindBtn.setText("Auto bind selected projects");
        autoBindBtn.addListener(SWT.Selection, new Listener() {

            @Override
            public void handleEvent(Event event) {
                TextSearchIndex<RemoteModule> moduleIndex = selectedServer.getModuleIndex();
                for (Object object : viewer.getCheckedElements()) {
                    ProjectBindModel bind = (ProjectBindModel) object;
                    List<RemoteModule> results = moduleIndex.search(bind.getEclipseName());
                    if (!results.isEmpty()) {
                        bind.associate(selectedServer.getId(), results.get(0).getName(), results.get(0).getKey());
                    } else {
                        bind.setAutoBindFailed(true);
                    }
                }
            }
        });
    }

    private void createUnassociateBtn(Composite btnContainer) {
        unassociateBtn = new Button(btnContainer, SWT.PUSH);
        unassociateBtn.setText("Unbind selected projects");
        unassociateBtn.setEnabled(viewer.getCheckedElements().length > 0);
        unassociateBtn.addListener(SWT.Selection, new Listener() {

            @Override
            public void handleEvent(Event event) {
                for (Object object : viewer.getCheckedElements()) {
                    ProjectBindModel bind = (ProjectBindModel) object;
                    bind.unassociate();
                }
            }
        });
    }

    private void createCheckUncheckAllCb() {
        checkAll = new Button(container, SWT.CHECK);
        checkAll.addSelectionListener(new SelectionAdapter() {

            @Override
            public void widgetSelected(SelectionEvent e) {
                viewer.setAllChecked(checkAll.getSelection());
                updateState();
            }

        });
    }

    private void createServerDropDown(Composite parent) {
        serverDropDownPage = new Composite(parent, SWT.NONE);
        GridData layoutData = new GridData();
        layoutData.horizontalSpan = 2;
        serverDropDownPage.setLayoutData(layoutData);

        GridLayout layout = new GridLayout();
        layout.numColumns = 3;
        layout.marginHeight = 0;
        layout.marginWidth = 5;
        serverDropDownPage.setLayout(layout);

        Label labelField = new Label(serverDropDownPage, SWT.NONE);
        labelField.setText("Select a SonarQube server: ");
        serverCombo = new ComboViewer(serverDropDownPage, SWT.READ_ONLY);

        serverCombo.setContentProvider(ArrayContentProvider.getInstance());

        serverCombo.setLabelProvider(new LabelProvider() {
            @Override
            public String getText(Object element) {
                IServer current = (IServer) element;
                return current.getId();
            }
        });

        updateServerLink = new Link(serverDropDownPage, SWT.NONE);
        updateServerLink.setText("<a>Refresh project list</a>");
        updateServerLink.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                updateSelectedServer();
            }
        });

        serverListener = new ServerChangeListener();
        ServersManager.getInstance().addServerLifecycleListener(serverListener);

        /* within the selection event, tell the object it was selected */
        serverCombo.addSelectionChangedListener(new ISelectionChangedListener() {
            @Override
            public void selectionChanged(SelectionChangedEvent event) {
                IStructuredSelection selection = (IStructuredSelection) event.getSelection();
                selectedServer = (IServer) selection.getFirstElement();
                serverCombo.refresh();
                updateState();
            }

        });
    }

    private void updateSelectedServer() {
        updateServerLink.setEnabled(false);
        try {
            final IServer server = (IServer) ((IStructuredSelection) serverCombo.getSelection()).getFirstElement();
            getContainer().run(true, true, new IRunnableWithProgress() {

                @Override
                public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
                    try {
                        server.update(monitor);
                    } finally {
                        Display.getDefault().asyncExec(new Runnable() {
                            @Override
                            public void run() {
                                updateState();
                            }
                        });
                    }
                }
            });
        } catch (InvocationTargetException ex) {
            throw new IllegalStateException(ex);
        } catch (InterruptedException e1) {
            // Job cancelled, ignore
        }
    }

    private void updateState() {
        if (viewer == null) {
            return;
        }
        updateServerLink.setVisible(selectedServer != null);
        updateServerLink.setEnabled(selectedServer != null && !selectedServer.isUpdating());
        boolean hasSelected = viewer.getCheckedElements().length > 0;
        checkAll.setSelection(hasSelected);
        checkAll.setGrayed(viewer.getCheckedElements().length < projects.size());
        checkAll.setText(hasSelected ? "Unselect all" : "Select all");
        unassociateBtn.setEnabled(hasSelected);
        if (selectedServer != null && !selectedServer.isUpdated()) {
            setMessage("No data for the selected server", IMessageProvider.WARNING);
        } else {
            setMessage(null);
        }
        if (autoBindBtn != null) {
            autoBindBtn.setEnabled(hasSelected && selectedServer != null && selectedServer.isUpdated());
        }
        container.layout(true, true);
    }

    private void createNoServerForm(Composite parent) {
        FormToolkit toolkit = new FormToolkit(parent.getDisplay());
        noServersPage = toolkit.createForm(book);
        GridData layoutData = new GridData();
        layoutData.horizontalSpan = 2;
        noServersPage.setLayoutData(layoutData);

        Composite body = noServersPage.getBody();
        GridLayout layout = new GridLayout(2, false);
        body.setLayout(layout);

        Link hlink = new Link(body, SWT.NONE);
        hlink.setText(Messages.ServersView_noServers);
        hlink.setBackground(book.getDisplay().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
        GridData gd = new GridData(SWT.LEFT, SWT.FILL, true, false);
        hlink.setLayoutData(gd);
        hlink.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                NewServerLocationWizard wizard = new NewServerLocationWizard();
                WizardDialog wd = new WizardDialog(book.getShell(), wizard);
                if (wd.open() == Window.OK) {
                    toggleServerPage();
                }
            }
        });
    }

    private void toggleServerPage() {
        List<IServer> servers = ServersManager.getInstance().getServers();
        if (servers.isEmpty()) {
            book.showPage(noServersPage);
            selectedServer = null;
        } else {
            book.showPage(serverDropDownPage);
            serverCombo.setInput(servers.toArray());
            if (!servers.contains(selectedServer)) {
                selectedServer = servers.get(0);
            }
            serverCombo.setSelection(new StructuredSelection(selectedServer));
        }
    }

    private ColumnViewerEditorActivationStrategy createActivationSupport() {
        ColumnViewerEditorActivationStrategy activationSupport = new ColumnViewerEditorActivationStrategy(viewer) {
            @Override
            protected boolean isEditorActivationEvent(ColumnViewerEditorActivationEvent event) {
                return event.eventType == ColumnViewerEditorActivationEvent.TRAVERSAL
                        || event.eventType == ColumnViewerEditorActivationEvent.MOUSE_CLICK_SELECTION
                        || event.eventType == ColumnViewerEditorActivationEvent.PROGRAMMATIC
                        || (event.eventType == ColumnViewerEditorActivationEvent.KEY_PRESSED
                                && event.keyCode == KeyLookupFactory.getDefault()
                                        .formalKeyLookup(IKeyLookup.F2_NAME));
            }
        };
        activationSupport.setEnableEditorActivationWithKeyboard(true);
        return activationSupport;
    }

    private final class ServerChangeListener implements IServerLifecycleListener {

        @Override
        public void serverRemoved(IServer server) {
            updateServerPage();
        }

        @Override
        public void serverChanged(IServer server) {
            updateServerPage();
        }

        @Override
        public void serverAdded(IServer server) {
            updateServerPage();
        }

        private void updateServerPage() {
            getContainer().getShell().getDisplay().asyncExec(new Runnable() {
                @Override
                public void run() {
                    toggleServerPage();
                    getContainer().getShell().layout(true, true);
                }
            });
        }
    }

    private class ProjectAssociationModelEditingSupport extends EditingSupport {

        public ProjectAssociationModelEditingSupport(TableViewer viewer) {
            super(viewer);
        }

        @Override
        protected boolean canEdit(Object element) {
            return selectedServer != null && element instanceof ProjectBindModel;
        }

        @Override
        protected CellEditor getCellEditor(Object element) {
            return new TextCellEditorWithContentProposal(viewer.getTable(),
                    new SearchEngineProvider(selectedServer, BindProjectsPage.this), (ProjectBindModel) element);
        }

        @Override
        protected Object getValue(Object element) {
            return StringUtils.trimToEmpty(((ProjectBindModel) element).getSonarFullName());
        }

        @Override
        protected void setValue(Object element, Object value) {
            // Don't set value as the model was already updated in the text adapter
        }

    }

    /**
     * Update all Eclipse projects when a binding was provided:
     */
    public boolean finish() {
        final ProjectBindModel[] projectBindings = getProjects();
        for (ProjectBindModel projectBinding : projectBindings) {
            updateProjectBinding(projectBinding);
        }
        return true;
    }

    private static void updateProjectBinding(ProjectBindModel projectBinding) {
        boolean changed = false;
        IProject project = projectBinding.getProject();
        SonarLintProject sonarProject = SonarLintProject.getInstance(project);
        String oldServerId = sonarProject.getServerId();
        if (!Objects.equals(projectBinding.getServerId(), oldServerId)) {
            sonarProject.setServerId(projectBinding.getServerId());
            changed = true;
        }
        if (!Objects.equals(projectBinding.getModuleKey(), sonarProject.getModuleKey())) {
            sonarProject.setModuleKey(projectBinding.getModuleKey());
            changed = true;
        }
        if (changed) {
            sonarProject.save();
            MarkerUtils.deleteIssuesMarkers(project);
            if (sonarProject.isBound()) {
                new ProjectUpdateJob(sonarProject).schedule();
            }
            if (oldServerId != null && !Objects.equals(projectBinding.getServerId(), oldServerId)) {
                IServer oldServer = ServersManager.getInstance().getServer(oldServerId);
                if (oldServer != null) {
                    oldServer.notifyAllListeners();
                }
            }
            if (projectBinding.getServerId() != null) {
                IServer server = ServersManager.getInstance().getServer(projectBinding.getServerId());
                if (server != null) {
                    server.notifyAllListeners();
                }
            }
            IBaseLabelProvider labelProvider = PlatformUI.getWorkbench().getDecoratorManager()
                    .getBaseLabelProvider(SonarLintProjectDecorator.ID);
            if (labelProvider != null) {
                ((SonarLintProjectDecorator) labelProvider)
                        .fireChange(new IProject[] { sonarProject.getProject() });
            }
        }
    }

    private ProjectBindModel[] getProjects() {
        WritableList projectAssociations = (WritableList) viewer.getInput();
        return (ProjectBindModel[]) projectAssociations.toArray(new ProjectBindModel[projectAssociations.size()]);
    }

}