org.twodividedbyzero.idea.findbugs.gui.settings.PluginTablePane.java Source code

Java tutorial

Introduction

Here is the source code for org.twodividedbyzero.idea.findbugs.gui.settings.PluginTablePane.java

Source

/*
 * Copyright 2016 Andre Pfeiler
 *
 * This file is part of FindBugs-IDEA.
 *
 * FindBugs-IDEA is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * FindBugs-IDEA 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with FindBugs-IDEA.  If not, see <http://www.gnu.org/licenses/>.
 */
package org.twodividedbyzero.idea.findbugs.gui.settings;

import com.intellij.ide.DataManager;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.DefaultActionGroup;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.fileChooser.FileChooser;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.popup.JBPopupFactory;
import com.intellij.openapi.ui.popup.ListPopup;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.vfs.VfsUtilCore;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.ui.AnActionButton;
import com.intellij.ui.AnActionButtonRunnable;
import com.intellij.ui.ToolbarDecorator;
import com.intellij.ui.table.JBTable;
import edu.umd.cs.findbugs.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.twodividedbyzero.idea.findbugs.common.util.FindBugsCustomPluginUtil;
import org.twodividedbyzero.idea.findbugs.common.util.GuiUtil;
import org.twodividedbyzero.idea.findbugs.common.util.IdeaUtilImpl;
import org.twodividedbyzero.idea.findbugs.common.util.New;
import org.twodividedbyzero.idea.findbugs.core.AbstractSettings;
import org.twodividedbyzero.idea.findbugs.core.PluginSettings;
import org.twodividedbyzero.idea.findbugs.plugins.AbstractPluginLoader;
import org.twodividedbyzero.idea.findbugs.plugins.PluginInfo;
import org.twodividedbyzero.idea.findbugs.resources.ResourcesLoader;

import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellRenderer;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.Collections;
import java.util.List;
import java.util.Set;

final class PluginTablePane extends JPanel {
    private static final Logger LOGGER = Logger.getInstance(PluginTablePane.class);

    private JBTable table;
    private String lastPluginError;
    private DetectorTablePane detectorTablePane;
    private List<PluginInfo> bundled;

    PluginTablePane() {
        super(new BorderLayout());
        setBorder(GuiUtil.createTitledBorder(ResourcesLoader.getString("plugins.title")));
        table = GuiUtil.createCheckboxTable(new Model(New.<PluginInfo>arrayList()), Model.IS_ENABLED_COLUMN,
                new ActionListener() {
                    @Override
                    public void actionPerformed(final ActionEvent e) {
                        swapEnabled();
                    }
                });
        table.getColumnModel().getColumn(Model.NAME_COLUMN).setCellRenderer(new TableCellRenderer() {
            private PluginPane pane;
            private PluginErrorPane errorPane;

            @Override
            public Component getTableCellRendererComponent(final JTable table, final Object value,
                    final boolean isSelected, final boolean hasFocus, final int row, final int column) {
                final PluginInfo pluginInfo = (PluginInfo) value;
                if (pluginInfo.errorMessage != null) {
                    if (errorPane == null) {
                        errorPane = new PluginErrorPane();
                    }
                    errorPane.load(pluginInfo);
                    return errorPane;
                } else {
                    if (pane == null) {
                        pane = new PluginPane();
                    }
                    pane.load(pluginInfo);
                    return pane;
                }
            }
        });

        final JPanel tablePane = ToolbarDecorator.createDecorator(table).setAddAction(new AnActionButtonRunnable() {
            @Override
            public void run(@NotNull final AnActionButton anActionButton) {
                doAdd(anActionButton);
            }
        }).setRemoveAction(new AnActionButtonRunnable() {
            @Override
            public void run(@NotNull final AnActionButton anActionButton) {
                doRemove();
            }
        }).setAsUsualTopToolbar().createPanel();

        add(tablePane);
    }

    @NotNull
    private Model getModel() {
        return (Model) table.getModel();
    }

    private void swapEnabled() {
        final int rows[] = table.getSelectedRows();
        for (final int row : rows) {
            getModel().rows.get(row).settings.enabled = !getModel().rows.get(row).settings.enabled;
        }
        getModel().fireTableDataChanged();
    }

    private void doAdd(@NotNull final AnActionButton anActionButton) {
        final Project project = IdeaUtilImpl.getProject(anActionButton.getDataContext());
        if (bundled.isEmpty()) {
            doAddWithFileChooser(project);
        } else {
            // like com.intellij.codeInsight.template.impl.TemplateListPanel#addTemplateOrGroup
            final DefaultActionGroup group = new DefaultActionGroup();
            for (final PluginInfo pluginInfo : bundled) {
                group.add(
                        new DumbAwareAction(ResourcesLoader.getString("plugins.add", pluginInfo.shortDescription)) {
                            @Override
                            public void actionPerformed(@NotNull final AnActionEvent e) {
                                final Set<PluginSettings> settings = New.set();
                                pluginInfo.settings.enabled = true;
                                for (final PluginInfo other : getModel().rows) {
                                    settings.add(other.settings);
                                    if (other.settings.enabled) {
                                        if (other.settings.id.equals(pluginInfo.settings.id)) {
                                            pluginInfo.settings.enabled = false;
                                            break;
                                        }
                                    }
                                }
                                bundled.remove(pluginInfo);
                                settings.add(pluginInfo.settings);
                                load(settings);
                            }
                        });
            }
            group.add(new DumbAwareAction(ResourcesLoader.getString("plugins.addFromDisk")) {
                @Override
                public void actionPerformed(@NotNull AnActionEvent e) {
                    doAddWithFileChooser(project);
                }
            });
            final DataContext context = DataManager.getInstance()
                    .getDataContext(anActionButton.getContextComponent());
            final ListPopup popup = JBPopupFactory.getInstance().createActionGroupPopup(null, group, context,
                    JBPopupFactory.ActionSelectionAid.ALPHA_NUMBERING, true, null);
            popup.show(anActionButton.getPreferredPopupPoint());
        }
    }

    private void doAddWithFileChooser(@Nullable final Project project) {
        final FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, true, true, false, true);
        descriptor.setTitle(ResourcesLoader.getString("plugins.choose.title"));
        descriptor.setDescription(ResourcesLoader.getString("plugins.choose.description"));
        descriptor.withFileFilter(new Condition<VirtualFile>() {
            @Override
            public boolean value(final VirtualFile virtualFile) {
                return "jar".equalsIgnoreCase(virtualFile.getExtension());
            }
        });

        final VirtualFile[] files = FileChooser.chooseFiles(descriptor, this, project, null);
        if (files.length > 0) {

            // collect current plugin settings
            final Set<PluginSettings> settings = New.set();
            for (final PluginInfo plugin : getModel().rows) {
                settings.add(plugin.settings);
            }

            // unload all plugin
            for (final Plugin plugin : Plugin.getAllPlugins()) {
                if (!plugin.isCorePlugin()) {
                    FindBugsCustomPluginUtil.unload(plugin);
                }
            }

            // load choosed plugins
            StringBuilder errors = new StringBuilder();
            for (final VirtualFile virtualFile : files) {
                final File file = VfsUtilCore.virtualToIoFile(virtualFile);
                Plugin plugin = null;
                try {
                    plugin = FindBugsCustomPluginUtil.loadTemporary(file);
                    final PluginSettings pluginSettings = new PluginSettings();
                    pluginSettings.id = plugin.getPluginId();
                    pluginSettings.bundled = false;
                    pluginSettings.enabled = true; // enable ; do not use plugin.isEnabledByDefault();
                    pluginSettings.url = FindBugsCustomPluginUtil.getAsString(plugin);
                    for (final PluginSettings other : settings) {
                        if (other.id.equals(pluginSettings.id)) {
                            pluginSettings.enabled = false;
                        }
                    }
                    settings.add(pluginSettings);
                } catch (final Exception e) {
                    LOGGER.warn(String.valueOf(file), e);
                    errors.append("\n    - ").append(e.getMessage());
                } finally {
                    if (plugin != null) {
                        FindBugsCustomPluginUtil.unload(plugin);
                    }
                }
            }

            // reload all plugins
            load(settings);
            if (errors.length() > 0) {
                Messages.showErrorDialog(this,
                        ResourcesLoader.getString("plugins.load.error.text") + errors.toString(),
                        StringUtil.capitalizeWords(ResourcesLoader.getString("plugins.load.error.title"), true));
            }
        }
    }

    private void doRemove() {
        final int[] index = table.getSelectedRows();
        if (index != null && index.length > 0) {
            final Set<PluginSettings> settings = New.set();
            for (final PluginInfo plugin : getModel().rows) {
                settings.add(plugin.settings);
            }
            for (final int idx : index) {
                settings.remove(getModel().rows.get(idx).settings);
            }
            load(settings);
        }
    }

    @Override
    public void setEnabled(final boolean enabled) {
        super.setEnabled(enabled);
        table.setEnabled(enabled);
    }

    boolean isModified(@NotNull final AbstractSettings settings) {
        final Set<PluginSettings> plugins = New.set();
        for (final PluginInfo pluginInfo : getModel().rows) {
            plugins.add(pluginInfo.settings);
        }
        return !settings.plugins.equals(plugins);
    }

    void apply(@NotNull final AbstractSettings settings) throws ConfigurationException {
        if (lastPluginError != null) {
            throw new ConfigurationException(lastPluginError, ResourcesLoader.getString("plugins.error.title"));
        }
        settings.plugins.clear();
        for (final PluginInfo pluginInfo : getModel().rows) {
            settings.plugins.add(pluginInfo.settings);
        }
    }

    void reset(@NotNull final AbstractSettings settings) {
        lastPluginError = null;
        load(settings.plugins);
    }

    private void load(@NotNull final Set<PluginSettings> settings) {
        getModel().rows.clear();
        final PluginLoaderImpl pluginLoader = new PluginLoaderImpl();
        pluginLoader.load(settings);
        Collections.sort(pluginLoader.configured, PluginInfo.ByShortDescription);
        Collections.sort(pluginLoader.bundled, PluginInfo.ByShortDescription);
        bundled = pluginLoader.bundled;
        getModel().rows.addAll(pluginLoader.configured);
        getModel().fireTableDataChanged();
        if (detectorTablePane != null) {
            detectorTablePane.reload(false);
        }
    }

    void setDetectorTablePane(@Nullable final DetectorTablePane detectorTablePane) {
        this.detectorTablePane = detectorTablePane;
    }

    private class Model extends AbstractTableModel {
        private static final int IS_ENABLED_COLUMN = 0;
        private static final int NAME_COLUMN = 1;
        @NotNull
        private final List<PluginInfo> rows;

        private Model(@NotNull final List<PluginInfo> rows) {
            this.rows = rows;
        }

        @Override
        public int getRowCount() {
            return rows.size();
        }

        @Override
        public int getColumnCount() {
            return 2;
        }

        @Override
        public boolean isCellEditable(final int rowIndex, final int columnIndex) {
            return columnIndex == IS_ENABLED_COLUMN;
        }

        @Override
        public Class<?> getColumnClass(final int columnIndex) {
            switch (columnIndex) {
            case IS_ENABLED_COLUMN:
                return Boolean.class;
            case NAME_COLUMN:
                return Component.class;
            default:
                throw new IllegalArgumentException("Column " + columnIndex);
            }
        }

        @Override
        public Object getValueAt(final int rowIndex, final int columnIndex) {
            switch (columnIndex) {
            case IS_ENABLED_COLUMN:
                return rows.get(rowIndex).settings.enabled;
            case NAME_COLUMN:
                return rows.get(rowIndex);
            default:
                throw new IllegalArgumentException("Column " + columnIndex);
            }
        }

        @Override
        public void setValueAt(final Object aValue, final int rowIndex, final int columnIndex) {
            if (columnIndex == IS_ENABLED_COLUMN) {
                final PluginSettings settings = rows.get(rowIndex).settings;
                boolean enabled = (Boolean) aValue;
                if (enabled) {
                    for (final PluginInfo other : rows) {
                        if (other.settings != settings) {
                            if (other.settings.id.equals(settings.id) && other.settings.enabled) {
                                Messages.showInfoMessage(PluginTablePane.this,
                                        ResourcesLoader.getString("plugins.duplicate.text"),
                                        StringUtil.capitalizeWords(
                                                ResourcesLoader.getString("plugins.duplicate.title"), true));
                                enabled = false;
                            }
                        }
                    }
                }
                rows.get(rowIndex).settings.enabled = enabled;
            }
        }
    }

    private class PluginLoaderImpl extends AbstractPluginLoader {

        @NotNull
        private final List<PluginInfo> configured;

        @NotNull
        private final List<PluginInfo> bundled;

        PluginLoaderImpl() {
            super(false);
            configured = New.arrayList();
            bundled = New.arrayList();
        }

        @Override
        protected void seenBundledPlugin(@NotNull final PluginInfo plugin) {
            bundled.add(plugin);
        }

        @Override
        protected void seenConfiguredPlugin(@NotNull final PluginInfo plugin) {
            configured.add(plugin);
        }

        @Override
        protected void handleError(@NotNull String message) {
            // do not log ; PluginErrorPane is used in this case
        }
    }
}