org.codinjutsu.tools.nosql.NoSqlConfigurable.java Source code

Java tutorial

Introduction

Here is the source code for org.codinjutsu.tools.nosql.NoSqlConfigurable.java

Source

/*
 * Copyright (c) 2015 David Boissier
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.codinjutsu.tools.nosql;

import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.execution.process.CapturingProcessHandler;
import com.intellij.execution.process.ProcessOutput;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
import com.intellij.openapi.options.BaseConfigurable;
import com.intellij.openapi.options.ConfigurationException;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicator;
import com.intellij.openapi.progress.ProgressManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.ui.LabeledComponent;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
import com.intellij.openapi.util.ThrowableComputable;
import com.intellij.openapi.vfs.CharsetToolkit;
import com.intellij.ui.*;
import com.intellij.ui.table.JBTable;
import org.apache.commons.lang.StringUtils;
import org.codinjutsu.tools.nosql.commons.view.ServerConfigurationPanelFactory;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableColumn;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeoutException;

import static org.apache.commons.lang.StringUtils.isBlank;

public class NoSqlConfigurable extends BaseConfigurable {

    private final Project project;

    private final NoSqlConfiguration configuration;

    private final List<ServerConfiguration> configurations;
    private final ServerConfigurationPanelFactory serverConfigurationPanelFactory;
    private final DatabaseVendorClientManager databaseVendorClientManager;

    private JPanel mainPanel;
    private JBTable table;
    private final NoSqlServerTableModel tableModel;
    private ShellPathPanel mongoShellPanel;
    private ShellPathPanel redisShellPanel;

    public NoSqlConfigurable(Project project) {
        this.project = project;
        this.configuration = NoSqlConfiguration.getInstance(project);
        databaseVendorClientManager = DatabaseVendorClientManager.getInstance(project);
        this.serverConfigurationPanelFactory = new ServerConfigurationPanelFactory(project,
                databaseVendorClientManager, DatabaseVendorUIManager.getInstance(project));
        configurations = new LinkedList<>(this.configuration.getServerConfigurations());
        tableModel = new NoSqlServerTableModel(configurations);
        mainPanel = new JPanel(new BorderLayout());
    }

    @Nls
    @Override
    public String getDisplayName() {
        return "NoSql Servers";
    }

    @Nullable
    @Override
    public String getHelpTopic() {
        return "preferences.noSqlOptions";
    }

    @Nullable
    @Override
    public JComponent createComponent() {
        JPanel databaseVendorShellOptionsPanel = new JPanel();
        databaseVendorShellOptionsPanel.setLayout(new BoxLayout(databaseVendorShellOptionsPanel, BoxLayout.Y_AXIS));
        mongoShellPanel = new ShellPathPanel(DatabaseVendor.MONGO, "--version");
        databaseVendorShellOptionsPanel.add(mongoShellPanel);
        redisShellPanel = new ShellPathPanel(DatabaseVendor.REDIS, "--version");
        databaseVendorShellOptionsPanel.add(redisShellPanel);

        mainPanel.add(databaseVendorShellOptionsPanel, BorderLayout.NORTH);

        PanelWithButtons panelWithButtons = new PanelWithButtons() {

            {
                initPanel();
            }

            @Nullable
            @Override
            protected String getLabelText() {
                return "Servers";
            }

            @Override
            protected JButton[] createButtons() {
                return new JButton[] {};
            }

            @Override
            protected JComponent createMainComponent() {
                table = new JBTable(tableModel);
                table.getEmptyText().setText("No server configuration set");
                table.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
                table.setDefaultRenderer(DatabaseVendor.class, new ColoredTableCellRenderer() {
                    @Override
                    protected void customizeCellRenderer(JTable jTable, Object value, boolean b, boolean b1, int i,
                            int i1) {
                        DatabaseVendor databaseVendor = (DatabaseVendor) value;
                        this.setIcon(databaseVendor.icon);
                        this.append(databaseVendor.name);
                    }
                });

                TableColumn autoConnectColumn = table.getColumnModel().getColumn(3);
                int autoConnectColumnWidth = table.getFontMetrics(table.getFont())
                        .stringWidth(table.getColumnName(3)) + 10;
                autoConnectColumn.setPreferredWidth(autoConnectColumnWidth);
                autoConnectColumn.setMaxWidth(autoConnectColumnWidth);
                autoConnectColumn.setMinWidth(autoConnectColumnWidth);

                return ToolbarDecorator.createDecorator(table).setAddAction(new AnActionButtonRunnable() {
                    @Override
                    public void run(AnActionButton button) {
                        stopEditing();

                        SelectDatabaseVendorDialog databaseVendorDialog = new SelectDatabaseVendorDialog(mainPanel);
                        databaseVendorDialog.setTitle("Add a NoSql Server");
                        databaseVendorDialog.show();
                        if (!databaseVendorDialog.isOK()) {
                            return;
                        }

                        DatabaseVendor selectedDatabaseVendor = databaseVendorDialog.getSelectedDatabaseVendor();
                        ServerConfiguration serverConfiguration = databaseVendorClientManager
                                .get(selectedDatabaseVendor).defaultConfiguration();
                        serverConfiguration.setDatabaseVendor(selectedDatabaseVendor);

                        ConfigurationDialog dialog = new ConfigurationDialog(mainPanel,
                                serverConfigurationPanelFactory, serverConfiguration);
                        dialog.setTitle("Add a NoSql Server");
                        dialog.show();
                        if (!dialog.isOK()) {
                            return;
                        }

                        configurations.add(serverConfiguration);
                        int index = configurations.size() - 1;
                        tableModel.fireTableRowsInserted(index, index);
                        table.getSelectionModel().setSelectionInterval(index, index);
                        table.scrollRectToVisible(table.getCellRect(index, 0, true));
                    }
                }).setAddActionName("addServer").setEditAction(new AnActionButtonRunnable() {
                    @Override
                    public void run(AnActionButton button) {
                        stopEditing();

                        int selectedIndex = table.getSelectedRow();
                        if (selectedIndex < 0 || selectedIndex >= tableModel.getRowCount()) {
                            return;
                        }
                        ServerConfiguration sourceConfiguration = configurations.get(selectedIndex);
                        ServerConfiguration copiedConfiguration = sourceConfiguration.clone();

                        ConfigurationDialog dialog = new ConfigurationDialog(mainPanel,
                                serverConfigurationPanelFactory, copiedConfiguration);
                        dialog.setTitle("Edit a NoSql Server");
                        dialog.show();
                        if (!dialog.isOK()) {
                            return;
                        }

                        configurations.set(selectedIndex, copiedConfiguration);
                        tableModel.fireTableRowsUpdated(selectedIndex, selectedIndex);
                        table.getSelectionModel().setSelectionInterval(selectedIndex, selectedIndex);
                    }
                }).setEditActionName("editServer").setRemoveAction(new AnActionButtonRunnable() {
                    @Override
                    public void run(AnActionButton button) {
                        stopEditing();

                        int selectedIndex = table.getSelectedRow();
                        if (selectedIndex < 0 || selectedIndex >= tableModel.getRowCount()) {
                            return;
                        }
                        TableUtil.removeSelectedItems(table);
                    }
                }).setRemoveActionName("removeServer").disableUpDownActions().createPanel();
            }
        };

        mainPanel.add(panelWithButtons, BorderLayout.CENTER);

        return mainPanel;
    }

    public boolean isModified() {
        return areConfigurationsModified() || isRedisShellPathModified();
    }

    @Override
    public void apply() throws ConfigurationException {
        stopEditing();
        if (areConfigurationsModified()) {
            configuration.setServerConfigurations(configurations);
        }

        if (isMongoShellPathModified()) {
            configuration.setShellPath(DatabaseVendor.MONGO, mongoShellPanel.getShellPath());
        }

        if (isRedisShellPathModified()) {
            configuration.setShellPath(DatabaseVendor.REDIS, redisShellPanel.getShellPath());
        }

        NoSqlWindowManager.getInstance(project).apply();
    }

    private boolean isMongoShellPathModified() {
        return mongoShellPanel
                .isShellPathModified(NoSqlConfiguration.getInstance(project).getShellPath(DatabaseVendor.MONGO));
    }

    private boolean isRedisShellPathModified() {
        return redisShellPanel
                .isShellPathModified(NoSqlConfiguration.getInstance(project).getShellPath(DatabaseVendor.REDIS));
    }

    private boolean areConfigurationsModified() {
        List<ServerConfiguration> existingConfigurations = NoSqlConfiguration.getInstance(project)
                .getServerConfigurations();

        if (configurations.size() != existingConfigurations.size()) {
            return true;
        }

        for (ServerConfiguration existingConfiguration : existingConfigurations) {
            if (!configurations.contains(existingConfiguration)) {
                return true;
            }
        }

        return false;
    }

    @Override
    public void reset() {
    }

    @Override
    public void disposeUIResources() {
        mainPanel = null;
        tableModel.removeTableModelListener(table);
        mongoShellPanel.dispose();
        redisShellPanel.dispose();
        table = null;
    }

    private void stopEditing() {
        if (table.isEditing()) {
            TableCellEditor editor = table.getCellEditor();
            if (editor != null) {
                editor.stopCellEditing();
            }
        }
    }

    private class ShellPathPanel extends JPanel implements Disposable {

        private static final int TIMEOUT_MS = 60 * 1000;
        private final String testParameter;
        private LabeledComponent<TextFieldWithBrowseButton> shellPathField;

        private ShellPathPanel(DatabaseVendor databaseVendor, String testParameter) {
            this.testParameter = testParameter;
            setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
            add(createLabel(databaseVendor.name));
            shellPathField = createShellPathField(databaseVendor);
            add(shellPathField);
            add(createTestButton(databaseVendor));
        }

        private JLabel createLabel(String databaseVendorName) {
            return new JLabel(String.format("Path to %s CLI:\t\t", databaseVendorName));
        }

        private JButton createTestButton(final DatabaseVendor databaseVendorName) {
            JButton testButton = new JButton("Test");
            testButton.addActionListener(new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent actionEvent) {
                    testPath(databaseVendorName);
                }
            });
            return testButton;
        }

        private void testPath(final DatabaseVendor databaseVendor) {
            ProcessOutput processOutput;
            try {
                processOutput = ProgressManager.getInstance()
                        .runProcessWithProgressSynchronously(new ThrowableComputable<ProcessOutput, Exception>() {
                            @Override
                            public ProcessOutput compute() throws Exception {
                                return checkShellPath(databaseVendor, getShellPath());
                            }
                        }, "Testing " + databaseVendor.name + " CLI Executable...", true,
                                NoSqlConfigurable.this.project);
            } catch (ProcessCanceledException pce) {
                return;
            } catch (Exception e) {
                Messages.showErrorDialog(mainPanel, e.getMessage(), "Something wrong happened");
                return;
            }
            if (processOutput != null && processOutput.getExitCode() == 0) {
                Messages.showInfoMessage(mainPanel, processOutput.getStdout(),
                        databaseVendor.name + " CLI Path Checked");
            }
        }

        public ProcessOutput checkShellPath(DatabaseVendor databaseVendor, String shellPath)
                throws ExecutionException, TimeoutException {
            if (isBlank(shellPath)) {
                return null;
            }

            GeneralCommandLine commandLine = new GeneralCommandLine();
            commandLine.setExePath(shellPath);
            if (testParameter != null) {
                commandLine.addParameter(testParameter);
            }
            CapturingProcessHandler handler = new CapturingProcessHandler(commandLine.createProcess(),
                    CharsetToolkit.getDefaultSystemCharset());
            ProgressIndicator indicator = ProgressManager.getInstance().getProgressIndicator();
            ProcessOutput result = indicator == null ? handler.runProcess(TIMEOUT_MS)
                    : handler.runProcessWithProgressIndicator(indicator);
            if (result.isTimeout()) {
                throw new TimeoutException(
                        "Couldn't check " + databaseVendor.name + " CLI executable - stopped by timeout.");
            } else if (result.isCancelled()) {
                throw new ProcessCanceledException();
            } else if (result.getExitCode() != 0 || !result.getStderr().isEmpty()) {
                throw new ExecutionException(String.format("Errors while executing %s. exitCode=%s errors: %s",
                        commandLine.toString(), result.getExitCode(), result.getStderr()));
            }
            return result;
        }

        public String getShellPath() {
            String shellPath = shellPathField.getComponent().getText();
            if (StringUtils.isNotBlank(shellPath)) {
                return shellPath;
            }

            return null;
        }

        public boolean isShellPathModified(String shellPath) {
            return !StringUtils.equals(shellPath, getShellPath());
        }

        private LabeledComponent<TextFieldWithBrowseButton> createShellPathField(DatabaseVendor databaseVendor) {
            LabeledComponent<TextFieldWithBrowseButton> shellPathField = new LabeledComponent<>();
            TextFieldWithBrowseButton component = new TextFieldWithBrowseButton();
            component.getChildComponent().setName("shellPathField");
            shellPathField.setComponent(component);
            shellPathField.getComponent().addBrowseFolderListener(
                    String.format("%s CLI configuration", databaseVendor.name), "", null,
                    new FileChooserDescriptor(true, false, false, false, false, false));

            shellPathField.getComponent().setText(configuration.getShellPath(databaseVendor));

            return shellPathField;
        }

        @Override
        public void dispose() {
            shellPathField = null;
        }
    }
}