/*
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.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.File;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFormattedTextField;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ListSelectionModel;
import javax.swing.WindowConstants;
import javax.swing.filechooser.FileFilter;
import org.biomart.builder.controller.MartConstructor;
import org.biomart.builder.controller.SaveDDLMartConstructor;
import org.biomart.builder.controller.MartConstructor.ConstructorRunnable;
import org.biomart.builder.controller.MartConstructor.MartConstructorListener;
import org.biomart.builder.exceptions.ListenerException;
import org.biomart.builder.model.DataSet;
import org.biomart.builder.model.MartConstructorAction;
import org.biomart.builder.model.DataSet.DataSetColumn;
import org.biomart.builder.view.gui.MartTabSet.MartTab;
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.ViewTextDialog;
/**
* A dialog which allows the user to choose some options about creating DDL over
* a given set of datasets, then lets them actually do it. The options include
* whether to output to file or to screen.
*
* @author Richard Holland <holland@ebi.ac.uk>
* @version $Revision: 1.37 $, $Date: 2008-03-19 09:54:16 $, modified by
* $Author: rh4 $
* @since 0.5
*/
public class SaveDDLDialog extends JDialog {
private static final long serialVersionUID = 1;
private JList datasetsList;
private JList prefixesList;
private MartTab martTab;
private JTextField targetDatabaseName;
private JTextField targetSchemaName;
private JComboBox outputFormat;
private JFileChooser outputFileChooser;
private JTextField outputFileLocation;
private JButton outputFileLocationButton;
private JTextField runDDLHost;
private JTextField runDDLPort;
private JTextField overrideHost;
private JTextField overridePort;
/**
* Constant referring to running DDL.
*/
public static final String RUN_DDL = Resources.get("runDDL");
/**
* Constant referring to viewing DDL.
*/
public static final String VIEW_DDL = Resources.get("viewDDL");
/**
* Constant referring to saving DDL.
*/
public static final String SAVE_DDL = Resources.get("filePerTableDDL");
/**
* Creates (but does not display) a dialog centred on the given tab, which
* allows DDL generation for the given datasets. When the OK button is
* chosen, the DDL is generated in the background.
*
* @param martTab
* the tab in which this will be displayed.
* @param datasets
* the datasets to list.
* @param prefixes
* the schema partition prefixes to list.
* @param generateOption
* the option to select in the dropdown.
*/
public SaveDDLDialog(final MartTab martTab, final Collection datasets,
final Collection prefixes, final String generateOption) {
// Create the base dialog.
super();
this.setTitle(Resources.get("saveDDLDialogTitle"));
this.setModal(true);
this.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
// Remember the tabset that the schema we are working with is part of
// (or will be part of if it's not been created yet).
this.martTab = martTab;
// 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 input fields for target schema name and granularity,
// and for run ddl host/port.
this.targetDatabaseName = new JTextField(20);
this.targetDatabaseName.setText(martTab.getMart().getOutputDatabase());
this.targetSchemaName = new JTextField(20);
this.targetSchemaName.setText(martTab.getMart().getOutputSchema());
this.outputFormat = new JComboBox();
this.outputFormat.addItem(SaveDDLDialog.SAVE_DDL);
this.outputFormat.addItem(SaveDDLDialog.VIEW_DDL);
this.outputFormat.addItem(SaveDDLDialog.RUN_DDL);
// Create the list for choosing datasets.
this.datasetsList = new JList(datasets.toArray(new DataSet[0]));
this.datasetsList
.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
this.datasetsList.setSelectionInterval(0, datasets.size() - 1);
this.datasetsList.setVisibleRowCount(4); // Arbitrary.
// Set the list to 30-characters wide. Longer than this and it will
// show a horizontal scrollbar.
this.datasetsList
.setPrototypeCellValue("012345678901234567890123456789");
// Create the list for choosing partitions.
this.prefixesList = new JList(prefixes.toArray(new String[0]));
this.prefixesList
.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
this.prefixesList.setSelectionInterval(0, prefixes.size() - 1);
this.prefixesList.setVisibleRowCount(4); // Arbitrary.
// Set the list to 30-characters wide. Longer than this and it will
// show a horizontal scrollbar.
this.prefixesList
.setPrototypeCellValue("012345678901234567890123456789");
// Create a file chooser for finding the DDL file we will save.
this.outputFileChooser = new JFileChooser() {
private static final long serialVersionUID = 1L;
public File getSelectedFile() {
File file = super.getSelectedFile();
if (file != null && !file.exists()) {
final String filename = file.getName();
final String extension = Resources.get("zipExtension");
if (!filename.endsWith(extension)
&& filename.indexOf('.') < 0)
file = new File(file.getParentFile(), filename
+ extension);
}
return file;
}
};
final String currentDir = Settings.getProperty("currentSaveDir");
this.outputFileChooser.setCurrentDirectory(currentDir == null ? null
: new File(currentDir));
this.outputFileChooser.setFileFilter(new FileFilter() {
// Accepts only files ending in ".zip".
public boolean accept(final File f) {
return f.isDirectory()
|| f.getName().toLowerCase().endsWith(
Resources.get("zipExtension"));
}
public String getDescription() {
return Resources.get("ZIPDDLFileFilterDescription");
}
});
final JLabel outputFileLabel = new JLabel(Resources
.get("saveDDLFileLocationLabel"));
this.outputFileLocation = new JTextField(20);
this.outputFileLocationButton = new JButton(Resources
.get("browseButton"));
// Attach the file chooser to the output file location button.
this.outputFileLocationButton.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent e) {
if (SaveDDLDialog.this.outputFileChooser
.showSaveDialog(content) == JFileChooser.APPROVE_OPTION) {
Settings.setProperty("currentSaveDir",
SaveDDLDialog.this.outputFileChooser
.getCurrentDirectory().getPath());
final File file = SaveDDLDialog.this.outputFileChooser
.getSelectedFile();
// When a file is chosen, put its name in the driver
// class location field.
if (file != null)
SaveDDLDialog.this.outputFileLocation.setText(file
.toString());
}
}
});
// Create the host/port label/fields.
final JLabel outputHostLabel = new JLabel(Resources
.get("runDDLHostLabel"));
this.runDDLHost = new JTextField(20);
this.runDDLHost.setText(martTab.getMart().getOutputHost());
final JLabel outputPortLabel = new JLabel(Resources
.get("runDDLPortLabel"));
this.runDDLPort = new JFormattedTextField(new DecimalFormat("0"));
this.runDDLPort.setColumns(5);
this.runDDLPort.setText(martTab.getMart().getOutputPort());
final JLabel overrideHostLabel = new JLabel(Resources
.get("overrideHostLabel"));
this.overrideHost = new JTextField(20);
this.overrideHost.setText(martTab.getMart().getOverrideHost());
final JLabel overridePortLabel = new JLabel(Resources
.get("overridePortLabel"));
this.overridePort = new JFormattedTextField(new DecimalFormat("0"));
this.overridePort.setColumns(5);
this.overridePort.setText(martTab.getMart().getOverridePort());
// Add listeners to view DDL options which show/hide additional stuff.
this.outputFormat.addItemListener(new ItemListener() {
public void itemStateChanged(final ItemEvent e) {
if (SaveDDLDialog.this.outputFormat.getSelectedItem().equals(
Resources.get("filePerTableDDL"))) {
outputFileLabel.setVisible(true);
SaveDDLDialog.this.outputFileLocation.setVisible(true);
SaveDDLDialog.this.outputFileLocationButton
.setVisible(true);
} else {
outputFileLabel.setVisible(false);
SaveDDLDialog.this.outputFileLocation.setVisible(false);
SaveDDLDialog.this.outputFileLocationButton
.setVisible(false);
}
if (SaveDDLDialog.this.outputFormat.getSelectedItem().equals(
Resources.get("runDDL"))) {
outputHostLabel.setVisible(true);
outputPortLabel.setVisible(true);
overrideHostLabel.setVisible(true);
overridePortLabel.setVisible(true);
SaveDDLDialog.this.runDDLHost.setVisible(true);
SaveDDLDialog.this.runDDLPort.setVisible(true);
SaveDDLDialog.this.overrideHost.setVisible(true);
SaveDDLDialog.this.overridePort.setVisible(true);
} else {
outputHostLabel.setVisible(false);
outputPortLabel.setVisible(false);
overrideHostLabel.setVisible(false);
overridePortLabel.setVisible(false);
SaveDDLDialog.this.runDDLHost.setVisible(false);
SaveDDLDialog.this.runDDLPort.setVisible(false);
SaveDDLDialog.this.overrideHost.setVisible(false);
SaveDDLDialog.this.overridePort.setVisible(false);
}
SaveDDLDialog.this.pack();
}
});
// Lay out the window.
// Add the dataset lists.
JLabel label = new JLabel(Resources.get("selectedDataSetsLabel"));
content.add(label, labelConstraints);
JPanel field = new JPanel();
field.add(new JScrollPane(this.datasetsList));
content.add(field, fieldConstraints);
// Add the prefix lists.
label = new JLabel(Resources.get("selectedPrefixesLabel"));
content.add(label, labelConstraints);
field = new JPanel();
field.add(new JScrollPane(this.prefixesList));
content.add(field, fieldConstraints);
// Add the target database settings label and field.
label = new JLabel(Resources.get("targetDatabaseLabel"));
content.add(label, labelConstraints);
field = new JPanel();
field.add(this.targetDatabaseName);
content.add(field, fieldConstraints);
// Add the target schema settings label and field.
label = new JLabel(Resources.get("targetSchemaLabel"));
content.add(label, labelConstraints);
field = new JPanel();
field.add(this.targetSchemaName);
content.add(field, fieldConstraints);
// Add the format field.
label = new JLabel(Resources.get("outputFormatLabel"));
content.add(label, labelConstraints);
field = new JPanel();
field.add(this.outputFormat);
content.add(field, fieldConstraints);
// Add the output location label, field and file chooser button.
content.add(outputFileLabel, labelConstraints);
field = new JPanel();
field.add(this.outputFileLocation);
field.add(this.outputFileLocationButton);
content.add(field, fieldConstraints);
// Add the output host/port etc..
content.add(outputHostLabel, labelConstraints);
field = new JPanel();
field.add(this.runDDLHost);
content.add(field, fieldConstraints);
content.add(outputPortLabel, labelConstraints);
field = new JPanel();
field.add(this.runDDLPort);
content.add(field, fieldConstraints);
content.add(overrideHostLabel, labelConstraints);
field = new JPanel();
field.add(this.overrideHost);
content.add(field, fieldConstraints);
content.add(overridePortLabel, labelConstraints);
field = new JPanel();
field.add(this.overridePort);
content.add(field, fieldConstraints);
// The close and execute buttons.
final JButton cancel = new JButton(Resources.get("cancelButton"));
final JButton execute = new JButton(Resources.get("saveDDLButton"));
// Intercept the close button, which closes the dialog
// without taking any action.
cancel.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent e) {
SaveDDLDialog.this.setVisible(false);
}
});
// Intercept the execute button, which validates the fields
// then creates the DDL and closes the dialog.
execute.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent e) {
if (SaveDDLDialog.this.validateFields()) {
SaveDDLDialog.this.createDDL();
SaveDDLDialog.this.setVisible(false);
}
}
});
// Add the buttons.
label = new JLabel();
content.add(label, labelLastRowConstraints);
field = new JPanel();
field.add(cancel);
field.add(execute);
content.add(field, fieldLastRowConstraints);
// Make execute the default button.
this.getRootPane().setDefaultButton(execute);
// Set a default value of View SQL.
SaveDDLDialog.this.outputFormat.setSelectedItem(generateOption);
// Set size of window.
this.pack();
// Move ourselves.
this.setLocationRelativeTo(null);
}
/**
* This method takes the settings from the dialog, having already been
* validated, and uses them to set up and start the DDL generation process.
*/
private void createDDL() {
// What datasets are we making DDL for?
final Collection selectedDataSets = Arrays.asList(this.datasetsList
.getSelectedValues());
final Collection selectedPrefixes = Arrays.asList(this.prefixesList
.getSelectedValues());
// Make a stringbuffer in case we want screen output.
final StringBuffer sb = new StringBuffer();
// Make the constructor object which will create the DDL.
MartConstructor constructor;
if (this.outputFormat.getSelectedItem().equals(
Resources.get("filePerTableDDL")))
constructor = new SaveDDLMartConstructor(new File(
this.outputFileLocation.getText()));
else if (this.outputFormat.getSelectedItem().equals(
Resources.get("runDDL")))
constructor = new SaveDDLMartConstructor(this.runDDLHost.getText(),
this.runDDLPort.getText(), this.overrideHost.getText(),
this.overridePort.getText());
else
constructor = new SaveDDLMartConstructor(sb);
try {
// Obtain the DDL generator from the constructor object.
this.martTab.getMartTabSet().requestSetOutputHost(
this.runDDLHost.getText());
this.martTab.getMartTabSet().requestSetOutputPort(
this.runDDLPort.getText());
this.martTab.getMartTabSet().requestSetOverrideHost(
this.overrideHost.getText());
this.martTab.getMartTabSet().requestSetOverridePort(
this.overridePort.getText());
final String outputDatabase = this.targetDatabaseName.getText();
final String outputSchema = this.targetSchemaName.getText();
this.martTab.getMartTabSet().requestSetOutputDatabase(
outputDatabase);
this.martTab.getMartTabSet().requestSetOutputSchema(outputSchema);
final ConstructorRunnable cr = constructor.getConstructorRunnable(
outputDatabase, outputSchema, selectedDataSets,
selectedPrefixes);
// If we want screen output, add a listener that listens for
// completion of construction. When completed, use the
// stringbuffer, which will contain the DDL, to pop up a simple
// text dialog for the user to view it with. Also if we want
// remote host output, a remote host monitor dialog pops up instead.
cr.addMartConstructorListener(new MartConstructorListener() {
public void martConstructorEventOccurred(final int event,
final Object data, final MartConstructorAction action)
throws ListenerException {
if (event == MartConstructorListener.CONSTRUCTION_ENDED
&& cr.getFailureException() == null)
if (SaveDDLDialog.this.outputFormat.getSelectedItem()
.equals(SaveDDLDialog.VIEW_DDL))
ViewTextDialog
.displayText(Resources
.get("mcViewDDLWindowTitle"), sb
.toString());
else if (SaveDDLDialog.this.outputFormat
.getSelectedItem()
.equals(SaveDDLDialog.RUN_DDL))
SaveDDLDialog.this.martTab.getMartTabSet()
.requestMonitorRemoteHost(
SaveDDLDialog.this.runDDLHost
.getText(),
SaveDDLDialog.this.runDDLPort
.getText());
}
});
this.martTab.getMartTabSet().requestMonitorConstructorRunnable(cr);
} catch (final Throwable t) {
StackTrace.showStackTrace(t);
JOptionPane.showMessageDialog(null, Resources
.get("martConstructionFailed"), Resources
.get("messageTitle"), JOptionPane.WARNING_MESSAGE);
}
}
private boolean isEmpty(final String string) {
// Strings are empty if they are null or all whitespace.
return string == null || string.trim().length() == 0;
}
private boolean validateFields() {
// List of messages to display, if any are necessary.
final List messages = new ArrayList();
// Must have a target schema.
if (this.isEmpty(this.targetDatabaseName.getText()))
messages.add(Resources.get("fieldIsEmpty", Resources
.get("targetDatabase")));
// Must have a target schema.
if (this.isEmpty(this.targetSchemaName.getText()))
messages.add(Resources.get("fieldIsEmpty", Resources
.get("targetSchema")));
// Must have an output file.
if (this.outputFormat.getSelectedItem().equals(SaveDDLDialog.SAVE_DDL)
&& this.isEmpty(this.outputFileLocation.getText()))
messages.add(Resources.get("fieldIsEmpty", Resources
.get("saveDDLFileLocation")));
// Must have an output host/port.
if (this.outputFormat.getSelectedItem().equals(SaveDDLDialog.RUN_DDL)) {
if (this.isEmpty(this.runDDLHost.getText()))
messages.add(Resources.get("fieldIsEmpty", Resources
.get("runDDLHost")));
if (this.isEmpty(this.runDDLPort.getText()))
messages.add(Resources.get("fieldIsEmpty", Resources
.get("runDDLPort")));
// Both or neither override settings = EOR = XOR.
if (this.isEmpty(this.overrideHost.getText())
^ this.isEmpty(this.overridePort.getText()))
if (this.isEmpty(this.overrideHost.getText()))
messages.add(Resources.get("fieldIsEmpty", Resources
.get("overrideHost")));
else
messages.add(Resources.get("fieldIsEmpty", Resources
.get("overridePort")));
}
// Must have at least one dataset selected.
if (this.datasetsList.getSelectedValues().length == 0)
messages.add(Resources.get("fieldIsEmpty", Resources
.get("selectedDataSets")));
// Must have at least one prefix selected.
if (this.prefixesList.getModel().getSize() > 0
&& this.prefixesList.getSelectedValues().length == 0)
messages.add(Resources.get("fieldIsEmpty", Resources
.get("selectedPrefixes")));
// Check it has a _key column on main table in every dataset.
for (int i = 0; i < this.datasetsList.getSelectedValues().length; i++) {
final DataSet dataset = (DataSet) this.datasetsList
.getSelectedValues()[i];
boolean hasKeyCol = false;
for (final Iterator j = dataset.getMainTable().getColumns()
.values().iterator(); !hasKeyCol && j.hasNext();)
hasKeyCol |= ((DataSetColumn) j.next()).getModifiedName()
.endsWith(Resources.get("keySuffix"));
if (!hasKeyCol) {
// Prompt.
final int option = JOptionPane.showConfirmDialog(this,
Resources.get("datasetNoKeyColConfirm", dataset
.getName()), Resources.get("questionTitle"),
JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
// If prompt says NO, add to the failure messages.
if (option != JOptionPane.YES_OPTION)
messages.add(Resources.get("datasetNoKeyCol", dataset
.getName()));
}
}
// Any messages to display? Show them.
if (!messages.isEmpty())
JOptionPane.showMessageDialog(null,
messages.toArray(new String[0]), Resources
.get("validationTitle"),
JOptionPane.INFORMATION_MESSAGE);
// Validation succeeds if there are no messages.
return messages.isEmpty();
}
}