Java tutorial
/* * 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; } } }