/* Copyright (C) 2006 EBI This library 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 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the itmplied 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 library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package org.biomart.builder.view.gui.dialogs; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Properties; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import org.biomart.builder.model.Mart; import org.biomart.builder.model.Schema; import org.biomart.builder.model.Schema.JDBCSchema; import org.biomart.builder.view.gui.panels.SchemaConnectionPanel; import org.biomart.builder.view.gui.panels.SchemaConnectionPanel.JDBCSchemaConnectionPanel; import org.biomart.common.exceptions.BioMartError; import org.biomart.common.resources.Resources; import org.biomart.common.resources.Settings; import org.biomart.common.view.gui.dialogs.StackTrace; import org.biomart.common.view.gui.dialogs.TransactionalDialog; /** * This dialog box allows the user to define or modify a schema, by giving it a * name, choosing a type, then displaying the appropriate * {@link SchemaConnectionPanel} according to the type chosen. The connection * panel then is given the job of actually creating or modifying the schema, * before the result is returned to the caller. * * @author Richard Holland <holland@ebi.ac.uk> * @version $Revision: 1.13 $, $Date: 2007-11-27 10:47:01 $, modified by * $Author: rh4 $ * @since 0.5 */ public class SchemaConnectionDialog extends TransactionalDialog { private static final long serialVersionUID = 1; /** * Pop up a dialog asking the user for details for a new schema, then create * and return that schema. * * @param mart * the mart to create the schema in. * * @return the newly created schema, or null if it was cancelled. */ public static Schema createSchema(final Mart mart) { final SchemaConnectionDialog dialog = new SchemaConnectionDialog(mart, Resources.get("newSchemaDialogTitle"), Resources .get("addButton"), null); dialog.setLocationRelativeTo(null); dialog.setVisible(true); final Schema sch = dialog.schema; dialog.dispose(); return sch; } /** * Pop up a dialog asking the user to modify details for a schema, then * modify that schema. Returns whether it was successful or not. * * @param schema * the schema to modify. * @return <tt>true</tt> if modification was successful, <tt>false</tt> * if not. */ public static boolean modifySchema(final Schema schema) { final SchemaConnectionDialog dialog = new SchemaConnectionDialog(schema .getMart(), Resources.get("modifySchemaDialogTitle"), Resources .get("modifyButton"), schema); dialog.setLocationRelativeTo(null); dialog.setVisible(true); final boolean result; if (dialog.schema != null && dialog.schema instanceof JDBCSchema) result = ((JDBCSchemaConnectionPanel) dialog.connectionPanel) .copySettingsToExistingSchema(schema) != null; else result = false; dialog.dispose(); return result; } private JButton cancel; private SchemaConnectionPanel connectionPanel; private JButton execute; private JComboBox name; private Schema schema; private JButton test; private JComboBox type; private SchemaConnectionDialog(final Mart mart, final String title, final String executeButtonText, final Schema template) { // Create the basic dialog centred on the main mart builder window. super(); this.setTitle(title); this.setModal(true); // Create the content pane for the dialog, ie. the bit that will hold // all the various questions and answers. final JPanel content = new JPanel(new GridBagLayout()); this.setContentPane(content); // Create some constraints for labels, except those on the last row // of the dialog. final GridBagConstraints labelConstraints = new GridBagConstraints(); labelConstraints.gridwidth = GridBagConstraints.RELATIVE; labelConstraints.fill = GridBagConstraints.HORIZONTAL; labelConstraints.anchor = GridBagConstraints.LINE_END; labelConstraints.insets = new Insets(0, 2, 0, 0); // Create some constraints for fields, except those on the last row // of the dialog. final GridBagConstraints fieldConstraints = new GridBagConstraints(); fieldConstraints.gridwidth = GridBagConstraints.REMAINDER; fieldConstraints.fill = GridBagConstraints.NONE; fieldConstraints.anchor = GridBagConstraints.LINE_START; fieldConstraints.insets = new Insets(0, 1, 0, 2); // Create some constraints for labels on the last row of the dialog. final GridBagConstraints labelLastRowConstraints = (GridBagConstraints) labelConstraints .clone(); labelLastRowConstraints.gridheight = GridBagConstraints.REMAINDER; // Create some constraints for fields on the last row of the dialog. final GridBagConstraints fieldLastRowConstraints = (GridBagConstraints) fieldConstraints .clone(); fieldLastRowConstraints.gridheight = GridBagConstraints.REMAINDER; // Create the input fields for the type, and the // holder for the connection panel details. this.type = new JComboBox(new String[] { Resources.get("jdbcSchema") }); final JPanel connectionPanelHolder = new JPanel(); this.type.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { // JDBC specific stuff. if (SchemaConnectionDialog.this.type.getSelectedItem().equals( Resources.get("jdbcSchema"))) if (!(SchemaConnectionDialog.this.connectionPanel instanceof JDBCSchemaConnectionPanel)) { connectionPanelHolder.removeAll(); SchemaConnectionDialog.this.connectionPanel = new JDBCSchemaConnectionPanel( mart); connectionPanelHolder .add(SchemaConnectionDialog.this.connectionPanel); SchemaConnectionDialog.this.pack(); } // General stuff for all schema types, including populating // the name combo-box with all historical schema objects // of the same class as the currently selected type. // We have to reverse this otherwise the last-accessed (ie. // last-added) item gets moved to the top of the list and // the whole list is reversed simply by displaying it. SchemaConnectionDialog.this.name.removeAllItems(); final List names = new ArrayList( Settings .getHistoryNamesForClass(SchemaConnectionDialog.this.connectionPanel .getSchemaClass())); Collections.reverse(names); for (final Iterator i = names.iterator(); i.hasNext();) SchemaConnectionDialog.this.name.addItem(i.next()); SchemaConnectionDialog.this.name.setSelectedItem(null); } }); // Build a combo box that allows the user to change the name // of a schema, or select one from history to copy settings from. this.name = new JComboBox(); this.name.setEditable(true); this.name.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { // Identify which schema to copy settings from. final Object obj = SchemaConnectionDialog.this.name .getSelectedItem(); // Load the schema settings from our history. final Properties historyProps = Settings.getHistoryProperties( SchemaConnectionDialog.this.connectionPanel .getSchemaClass(), (String) obj); // Copy the settings, if we found any that matched. if (historyProps != null) SchemaConnectionDialog.this.connectionPanel .copySettingsFromProperties(historyProps); } }); // Make a default selection for the connection panel holder. Use JDBC // as it is the most obvious choice. We have to do something here else // the box won't size properly without one. this.type.setSelectedItem(Resources.get("jdbcSchema")); // Create buttons in dialog. this.test = new JButton(Resources.get("testButton")); this.cancel = new JButton(Resources.get("cancelButton")); this.execute = new JButton(executeButtonText); // Add the name label and name field. JLabel label = new JLabel(Resources.get("nameLabel")); content.add(label, labelConstraints); // In the name field, also include the type label and field, to save // space. JPanel field = new JPanel(); field.add(this.name); label = new JLabel(Resources.get("typeLabel")); field.add(label); field.add(this.type); content.add(field, fieldConstraints); // Add the connection panel holder. content.add(connectionPanelHolder, fieldConstraints); // Add the buttons. label = new JLabel(); content.add(label, labelLastRowConstraints); field = new JPanel(); field.add(this.test); field.add(this.cancel); field.add(this.execute); content.add(field, fieldLastRowConstraints); // Intercept the cancel button, which closes the dialog // without taking any action. this.cancel.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { SchemaConnectionDialog.this.schema = null; SchemaConnectionDialog.this.setVisible(false); } }); // Intercept the test button, which causes the schema // details as currently entered to be used to create // a temporary schema object, which is then tested. this.test.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { final Schema testSchema = SchemaConnectionDialog.this .createSchemaFromSettings(); if (testSchema != null) SchemaConnectionDialog.this.requestTestSchema(testSchema); } }); // Intercept the execute button, which causes the // schema to be created as a temporary schema object. If // successful, the dialog closes. this.execute.addActionListener(new ActionListener() { public void actionPerformed(final ActionEvent e) { SchemaConnectionDialog.this.schema = SchemaConnectionDialog.this .createSchemaFromSettings(); if (SchemaConnectionDialog.this.schema != null) { // Assume we've failed. boolean passedTest = false; try { // Attempt to pass the test. passedTest = SchemaConnectionDialog.this.schema.test(); } catch (final Throwable t) { // If we get an exception, we failed the test, and // should // tell the user why. passedTest = false; StackTrace.showStackTrace(t); } // Tell the user if we passed or failed. if (passedTest) { SchemaConnectionDialog.this.schema.storeInHistory(); SchemaConnectionDialog.this.setVisible(false); } else JOptionPane.showMessageDialog(null, Resources .get("schemaTestFailed"), Resources .get("testTitle"), JOptionPane.ERROR_MESSAGE); } } }); // Make the execute button the default button. this.getRootPane().setDefaultButton(this.execute); // Reset the fields to their default values. this.copySettingsFromSchema(template); // Pack and resize the window. this.pack(); // Move ourselves. this.setLocationRelativeTo(null); } private void requestTestSchema(final Schema schema) { // Assume we've failed. boolean passedTest = false; try { // Attempt to pass the test. passedTest = schema.test(); } catch (final Throwable t) { // If we get an exception, we failed the test, and should // tell the user why. passedTest = false; StackTrace.showStackTrace(t); } // Tell the user if we passed or failed. if (passedTest) JOptionPane.showMessageDialog(null, Resources .get("schemaTestPassed"), Resources.get("testTitle"), JOptionPane.INFORMATION_MESSAGE); else JOptionPane.showMessageDialog(null, Resources .get("schemaTestFailed"), Resources.get("testTitle"), JOptionPane.ERROR_MESSAGE); } private Schema createSchemaFromSettings() { // Refuse to create a temporary schema object if we can't validate it. if (!this.validateFields()) return null; try { // Look up the type and use the appropriate schema type to // actually create the object. final String type = (String) this.type.getSelectedItem(); if (type.equals(Resources.get("jdbcSchema"))) return ((JDBCSchemaConnectionPanel) this.connectionPanel) .createSchemaFromSettings((String) this.name .getSelectedItem()); // What kind of type is it then?? else throw new BioMartError(); } catch (final Throwable t) { StackTrace.showStackTrace(t); } // If we got here, something went wrong with creation, so we // have nothing to return. return null; } private boolean isEmpty(final String string) { // Return true if the string is null or empty. return string == null || string.trim().length() == 0; } private void copySettingsFromSchema(final Schema template) { // If we are modifying something, use it to fill the details // in the dialog. if (template != null) { // Select the schema type. if (template instanceof JDBCSchema) this.type.setSelectedItem(Resources.get("jdbcSchema")); // Unknown schema type! else throw new BioMartError(); this.type.setEnabled(false); // Gray out as we can't change this. // Set the name. this.name.setSelectedItem(template.getName()); this.name.setEnabled(false); // Gray out as we can't change this. } // Otherwise, use some sensible defaults. else { this.type.setSelectedIndex(0); this.name.setSelectedItem(null); } // Update the connection panel. this.connectionPanel.copySettingsFromSchema(template); } private boolean validateFields() { // Make a list to hold messages. final List messages = new ArrayList(); // We don't like missing names. if (this.isEmpty(this.name.getSelectedItem().toString())) messages.add(Resources.get("fieldIsEmpty", Resources.get("name"))); // We don't like missing types either. if (this.type.getSelectedIndex() == -1) messages.add(Resources.get("fieldIsEmpty", Resources.get("type"))); // If we have any messages, show them. if (!messages.isEmpty()) JOptionPane.showMessageDialog(null, messages.toArray(new String[0]), Resources .get("validationTitle"), JOptionPane.INFORMATION_MESSAGE); // If there were no messages, then validated OK if the connection // panel also validated OK. return messages.isEmpty() && this.connectionPanel.validateFields(true); } }