package mmdb.gui; import gui.ObjectList; import gui.SecondoObject; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.util.List; import java.util.concurrent.CancellationException; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.SwingWorker; import javax.swing.SwingWorker.StateValue; import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.table.AbstractTableModel; import javax.swing.table.DefaultTableCellRenderer; import mmdb.error.inout.ExportException; import mmdb.error.inout.ImportException; import mmdb.service.ObjectExport; import mmdb.service.ObjectImport; import tools.Reporter; /** * A class managing any gui components needed while importing or exporting main * memory objects. * * @author Bjoern Clasen */ public class ImportExportGui extends JDialog { private static final long serialVersionUID = 5538523860631830202L; /** * A value for a filepath to indicate that no file has been selected. */ public static final String NO_FILE = "NO_FILE"; /** * The JFileChooser to use for choosing import and export files. */ private static JFileChooser fileChooser; /** * The table containing the object information. */ private JTable table; /** * Buttons for user interaction */ private JButton buttonOK, buttonCANCEL; /** * Progressbar showing progess for currently processed element */ private JProgressBar progressBar; /** * The table's content */ private String[][] objectHeaders; /** * The current SwingWorker */ private SwingWorker swingWorker; /** * Parent component to display relative to */ private Component component; /** * Constructor initializing the static FileChooser if necessary. */ public ImportExportGui(Component component) { this.component = component; // FileChooser if (ImportExportGui.fileChooser == null) { ImportExportGui.fileChooser = new JFileChooser(); ImportExportGui.fileChooser.setMultiSelectionEnabled(false); ImportExportGui.fileChooser .setFileSelectionMode(JFileChooser.FILES_ONLY); FileNameExtensionFilter filter = new FileNameExtensionFilter( "Secondo MM Files", ObjectExport.FILE_EXTENSION, ObjectExport.FILE_EXTENSION.toUpperCase()); ImportExportGui.fileChooser.setFileFilter(filter); } } /** * Processes an export command sent by the menu button. * * @param sobjects * The objects selected in the objectlist */ public void processExportCommand(SecondoObject[] sobjects) { if (sobjects.length > 0) { String filePath = getExportFilePath(); if (filePath == ImportExportGui.NO_FILE) { return; } try { exportObjects(sobjects, filePath); } catch (ExportException e) { Reporter.showError(e.getMessage()); } } } /** * Processes an import command sent by the menu button. * * @param objectList * the ObjectList to add results to. */ public void processImportCommand(ObjectList objectList) { String filePath = getImportFilePath(); if (filePath == ImportExportGui.NO_FILE) { return; } try { importObjects(filePath, objectList); } catch (ImportException e) { Reporter.showError(e.getMessage()); } } /** * Processes a {@code setProgress(...)} call from the * {@link ImportExportGui#swingWorker}.
* Updates the corresponding table cells and repaints the table. * * @param processedIndices * a list of table row indices representing objects whose * procession has been finished. */ public void processProgressUpdate(List processedIndices) { for (int index : processedIndices) { this.objectHeaders[index][3] = "Finished!"; if (index < this.objectHeaders.length - 1) { this.objectHeaders[index + 1][3] = "Working..."; } } ((AbstractTableModel) this.table.getModel()).fireTableDataChanged(); } /** * Exports the given SecondoObjects to a file located under filePath.
* Uses the SwingWorker {@link ObjectExport} for background procession. * * @param sobjects * the SecondoObjects to export * @param filePath * the path to export to * @throws ExportException * if export could not be started */ private void exportObjects(SecondoObject[] sobjects, String filePath) throws ExportException { ObjectExport.checkObjectsExportable(sobjects); ObjectExport objectExport = new ObjectExport(sobjects, filePath, this); this.addPropertyChangeListenerToWorker(objectExport, null); // Read headers this.objectHeaders = objectExport.getObjectHeaders(); this.objectHeaders[0][3] = "Working..."; // Dialog this.setTitle("Exporting MMObjects..."); this.initDialog(); this.swingWorker = objectExport; this.swingWorker.execute(); this.setVisible(true); } /** * Imports all SecondoObjects stored in a file located under filePath.
* Adds them to the objectList.
* Uses the SwingWorker {@link ObjectImport} for background procession. * * @param filePath * the path to the file to import * @param objectList * the ObjectList to add results to * @throws ImportException * if file was invalid or not readable */ private void importObjects(String filePath, final ObjectList objectList) throws ImportException { ObjectImport.checkFile(filePath); ObjectImport objectImport = new ObjectImport(filePath, this); this.addPropertyChangeListenerToWorker(objectImport, objectList); // Read headers this.objectHeaders = objectImport.getObjectHeaders(); this.objectHeaders[0][3] = "Working..."; // Dialog this.setTitle("Importing MMObjects..."); this.initDialog(); this.swingWorker = objectImport; this.swingWorker.execute(); this.setVisible(true); } /** * Adds a PropertyChangeListener to the given SwingWorker.
* The PropertyChangeListener will:
* - handle progress events (updating JProgressbar)
* - handle done state (Cancel/Exception/Success).
* ObjectList must be {@code null} for export. * * @param worker * the worker to add the PropertyChangeListener to * @param objectList * for export: {@code null}
* for import: the ObjectList to add imports to. */ private void addPropertyChangeListenerToWorker( final SwingWorker worker, final ObjectList objectList) { worker.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { if ("progress".equals(evt.getPropertyName())) { ImportExportGui.this.progressBar.setValue((Integer) evt .getNewValue()); } else if ("state".equals(evt.getPropertyName())) { if ((StateValue) evt.getNewValue() == StateValue.DONE) { try { if (objectList == null) { worker.get(); } else { SecondoObject[] sobjects = (SecondoObject[]) worker .get(); for (SecondoObject sobject : sobjects) { objectList.addEntry(sobject); } } ImportExportGui.this.buttonCANCEL.setEnabled(false); ImportExportGui.this.buttonOK.setEnabled(true); } catch (CancellationException e) { JOptionPane .showMessageDialog(ImportExportGui.this, "Procession has been cancelled!\nAlready processed data is deleted."); ImportExportGui.this.setVisible(false); ImportExportGui.this.dispose(); } catch (Exception e) { if (e.getCause() instanceof ExportException || e.getCause() instanceof ImportException) { JOptionPane .showMessageDialog( ImportExportGui.this, "An Exception occured:\n" + e.getCause() .getMessage(), "Exception!", JOptionPane.ERROR_MESSAGE); } else { JOptionPane.showMessageDialog( ImportExportGui.this, "An unknown error occured!", "Unexcepted Exception!", JOptionPane.ERROR_MESSAGE); } ImportExportGui.this.setVisible(false); ImportExportGui.this.dispose(); } } } } }); } /** * Opens up a Filechooser opendialog to retrieve a filepath to open. * * @return the filepath selected in the filechooser or {@value #NO_FILE} if * none was selected. */ private String getImportFilePath() { int retVal = fileChooser.showOpenDialog(null); if (retVal == JFileChooser.APPROVE_OPTION) { File selectedFile = fileChooser.getSelectedFile(); if (selectedFile != null) { return selectedFile.getAbsolutePath(); } } return NO_FILE; } /** * Opens up a Filechooser davedialog to retrieve a filepath to save to. * * @return the filepath selected in the filechooser or {@value #NO_FILE} if * none was selected. */ private String getExportFilePath() { int retVal = fileChooser.showSaveDialog(null); if (retVal == JFileChooser.APPROVE_OPTION) { File selectedFile = fileChooser.getSelectedFile(); if (selectedFile != null) { return selectedFile.getAbsolutePath(); } } return NO_FILE; } /** * Initializes the dialog and adds necessary components to it. */ private void initDialog() { Container container = this.getContentPane(); container.setLayout(new BoxLayout(container, BoxLayout.Y_AXIS)); this.addTablePanel(container); this.addProgressPanel(container); this.addControlPanel(container); this.setLocationRelativeTo(this.component); this.setModal(true); this.pack(); this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); this.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { if (ImportExportGui.this.buttonOK.isEnabled()) { ImportExportGui.this.handleOK(); } else { ImportExportGui.this.handleCANCEL(); } } }); } /** * Handles a "OK" button press: Closes the window. */ private void handleOK() { this.setVisible(false); this.dispose(); } /** * Handles a "CANCEL" button press: asks for confirmation and if given * cancels the {@link ImportExportGui#swingWorker}. */ private void handleCANCEL() { int cancel = JOptionPane .showConfirmDialog( this, "Do you really want to cancel?\nAlready processed data will be deleted!", "Cancel?", JOptionPane.YES_NO_OPTION); if (cancel == JOptionPane.YES_OPTION) { if (!this.buttonOK.isEnabled()) { this.swingWorker.cancel(true); } else { JOptionPane.showMessageDialog(this, "Procession finished before cancellation!", "Finished", JOptionPane.WARNING_MESSAGE); } } } /** * Adds the table panel to the container.
* Make sure {@link ImportExportGui#objectHeaders} have been initialized * beforehand. * * @param container * the container to add the table to. */ private void addTablePanel(Container container) { String[] columnNames = { "NAME", "TYPE", "TUPLES", "STATUS" }; this.table = new JTable(this.objectHeaders, columnNames); this.table.setPreferredScrollableViewportSize(new Dimension(600, 200)); this.table.setFillsViewportHeight(true); DefaultTableCellRenderer centerRenderer = new DefaultTableCellRenderer(); centerRenderer.setHorizontalAlignment(JLabel.CENTER); for (int i = 0; i <= 3; i++) { this.table.getColumnModel().getColumn(i) .setCellRenderer(centerRenderer); } ((DefaultTableCellRenderer) this.table.getTableHeader() .getDefaultRenderer()).setHorizontalAlignment(JLabel.CENTER); this.table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS); this.table.getColumn("NAME").setMinWidth(250); JScrollPane scrollPane = new JScrollPane(this.table); scrollPane.setOpaque(true); container.add(scrollPane); } /** * Adds the JProgressbar to the container. * * @param container * the container to add the JProgressbar to. */ private void addProgressPanel(Container container) { this.progressBar = new JProgressBar(0, 100); this.progressBar.setStringPainted(true); container.add(this.progressBar); } /** * Adds the control panel to the container.
* The control panel contains the buttons for GUI interaction. * * @param container * the container to add the control panel to. */ private void addControlPanel(Container container) { JPanel panel = new JPanel(new FlowLayout()); this.buttonOK = new JButton("OK"); this.buttonOK.setEnabled(false); this.buttonOK.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { ImportExportGui.this.handleOK(); } }); this.buttonCANCEL = new JButton("CANCEL"); this.buttonCANCEL.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { ImportExportGui.this.handleCANCEL(); } }); panel.add(this.buttonOK); panel.add(this.buttonCANCEL); panel.setMinimumSize(new Dimension(100, 60)); panel.setMaximumSize(new Dimension(2000, 60)); panel.setOpaque(true); container.add(panel); } }