/*
* Copyright (C) 2014 by Array Systems Computing Inc. http://www.array.ca
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 3 of the License, or (at your option)
* any later version.
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, see http://www.gnu.org/licenses/
*/
package org.esa.snap.graphbuilder.rcp.dialogs;
import com.bc.ceres.core.ProgressMonitor;
import com.bc.ceres.core.SubProgressMonitor;
import org.esa.snap.core.datamodel.Product;
import org.esa.snap.core.gpf.GPF;
import org.esa.snap.core.gpf.OperatorException;
import org.esa.snap.core.gpf.graph.GraphException;
import org.esa.snap.engine_utilities.db.CommonReaders;
import org.esa.snap.engine_utilities.util.MemUtils;
import org.esa.snap.graphbuilder.rcp.dialogs.support.GraphExecuter;
import org.esa.snap.graphbuilder.rcp.progress.LabelBarProgressMonitor;
import org.esa.snap.rcp.SnapApp;
import org.esa.snap.ui.AppContext;
import org.esa.snap.ui.ModelessDialog;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JTabbedPane;
import javax.swing.SwingWorker;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
/**
* Provides the dialog for excuting multiple graph from one user interface
*/
public abstract class MultiGraphDialog extends ModelessDialog implements LabelBarProgressMonitor.ProgressBarListener {
protected final AppContext appContext;
protected final IOPanel ioPanel;
protected final List<GraphExecuter> graphExecuterList = new ArrayList<>(3);
private final JPanel mainPanel;
protected final JTabbedPane tabbedPane;
private final JLabel statusLabel;
private final JPanel progressPanel;
private final JProgressBar progressBar;
private LabelBarProgressMonitor progBarMonitor = null;
private boolean isProcessing = false;
protected static final String TMP_FILENAME = "tmp_intermediate";
public MultiGraphDialog(final AppContext theAppContext, final String title, final String helpID,
final boolean useSourceSelector) {
super(theAppContext.getApplicationWindow(), title, ID_APPLY_CLOSE_HELP, helpID);
appContext = theAppContext;
mainPanel = new JPanel(new BorderLayout(4, 4));
tabbedPane = new JTabbedPane();
tabbedPane.addChangeListener(new ChangeListener() {
public void stateChanged(final ChangeEvent e) {
ValidateAllNodes();
}
});
mainPanel.add(tabbedPane, BorderLayout.CENTER);
ioPanel = new IOPanel(appContext, tabbedPane, useSourceSelector);
// status
statusLabel = new JLabel("");
statusLabel.setForeground(new Color(255, 0, 0));
mainPanel.add(statusLabel, BorderLayout.NORTH);
// progress Bar
progressBar = new JProgressBar();
progressBar.setName(getClass().getName() + "progressBar");
progressBar.setStringPainted(true);
progressPanel = new JPanel();
progressPanel.setLayout(new BorderLayout(2, 2));
progressPanel.add(progressBar, BorderLayout.CENTER);
progBarMonitor = new LabelBarProgressMonitor(progressBar);
progBarMonitor.addListener(this);
final JButton progressCancelBtn = new JButton("Cancel");
progressCancelBtn.addActionListener(new ActionListener() {
public void actionPerformed(final ActionEvent e) {
CancelProcessing();
}
});
progressPanel.add(progressCancelBtn, BorderLayout.EAST);
progressPanel.setVisible(false);
mainPanel.add(progressPanel, BorderLayout.SOUTH);
getButton(ID_APPLY).setText("Run");
super.getJDialog().setMinimumSize(new Dimension(500, 300));
}
@Override
public int show() {
ioPanel.initProducts();
setContent(mainPanel);
initGraphs();
return super.show();
}
@Override
public void hide() {
ioPanel.releaseProducts();
super.hide();
}
@Override
protected void onApply() {
if (isProcessing) return;
ioPanel.onApply();
try {
DoProcessing();
} catch (Exception e) {
statusLabel.setText(e.getMessage());
}
}
@Override
protected void onClose() {
CancelProcessing();
super.onClose();
}
void initGraphs() {
try {
deleteGraphs();
createGraphs();
} catch (Exception e) {
statusLabel.setText(e.getMessage());
}
}
/**
* Validates the input and then call the GPF to execute the graph
*
* @throws GraphException on assignParameters
*/
private void DoProcessing() {
if (ValidateAllNodes()) {
MemUtils.freeAllMemory();
progressBar.setValue(0);
final SwingWorker processThread = new ProcessThread(progBarMonitor);
processThread.execute();
} else {
showErrorDialog(statusLabel.getText());
}
}
public void notifyProgressStart() {
progressPanel.setVisible(true);
}
public void notifyProgressDone() {
progressPanel.setVisible(false);
}
private void CancelProcessing() {
if (progBarMonitor != null)
progBarMonitor.setCanceled(true);
}
private void deleteGraphs() {
for (GraphExecuter gex : graphExecuterList) {
gex.ClearGraph();
}
graphExecuterList.clear();
}
/**
* Loads a new graph from a file
*
* @param executer the GraphExcecuter
* @param file the graph file to load
*/
public void LoadGraph(final GraphExecuter executer, final File file) {
try {
executer.loadGraph(new FileInputStream(file), file, true, true);
} catch (Exception e) {
showErrorDialog(e.getMessage());
}
}
protected abstract void createGraphs() throws GraphException;
protected abstract void assignParameters() throws GraphException;
protected abstract void cleanUpTempFiles();
private boolean ValidateAllNodes() {
if (isProcessing) return false;
if (ioPanel == null || graphExecuterList.isEmpty())
return false;
boolean result;
statusLabel.setText("");
try {
// check the all files have been saved
final Product srcProduct = ioPanel.getSelectedSourceProduct();
if (srcProduct != null && (srcProduct.isModified() || srcProduct.getFileLocation() == null)) {
throw new OperatorException("The source product has been modified. Please save it before using it in " + getTitle());
}
assignParameters();
// first graph must pass
result = graphExecuterList.get(0).InitGraph();
} catch (Exception e) {
statusLabel.setText(e.getMessage());
result = false;
}
return result;
}
private void openTargetProducts(final List<File> fileList) {
if (!fileList.isEmpty()) {
for (File file : fileList) {
try {
final Product product = CommonReaders.readProduct(file);
if (product != null) {
appContext.getProductManager().addProduct(product);
}
} catch (Exception e) {
showErrorDialog(e.getMessage());
}
}
}
}
protected IOPanel getIOPanel() {
return ioPanel;
}
public void setTargetProductNameSuffix(final String suffix) {
ioPanel.setTargetProductNameSuffix(suffix);
}
/**
* For running graphs in unit tests
*
* @throws Exception when failing validation
*/
public void testRunGraph() throws Exception {
ioPanel.initProducts();
initGraphs();
if (ValidateAllNodes()) {
for (GraphExecuter graphEx : graphExecuterList) {
final String desc = graphEx.getGraphDescription();
if (desc != null && !desc.isEmpty())
System.out.println("Processing " + graphEx.getGraphDescription());
graphEx.InitGraph();
graphEx.executeGraph(ProgressMonitor.NULL);
graphEx.disposeGraphContext();
}
cleanUpTempFiles();
} else {
throw new OperatorException(statusLabel.getText());
}
}
/////
private class ProcessThread extends SwingWorker<Boolean, Object> {
private final ProgressMonitor pm;
private Date executeStartTime = null;
private boolean errorOccured = false;
public ProcessThread(final ProgressMonitor pm) {
this.pm = pm;
}
@Override
protected Boolean doInBackground() throws Exception {
pm.beginTask("Processing Graph...", 100 * graphExecuterList.size());
try {
executeStartTime = Calendar.getInstance().getTime();
isProcessing = true;
for (GraphExecuter graphEx : graphExecuterList) {
final String desc = graphEx.getGraphDescription();
if (desc != null && !desc.isEmpty())
statusLabel.setText("Processing " + graphEx.getGraphDescription());
graphEx.InitGraph();
graphEx.executeGraph(SubProgressMonitor.create(pm, 100));
graphEx.disposeGraphContext();
}
} catch (Exception e) {
System.out.print(e.getMessage());
if (e.getMessage() != null && !e.getMessage().isEmpty())
statusLabel.setText(e.getMessage());
else
statusLabel.setText(e.toString());
errorOccured = true;
} finally {
isProcessing = false;
pm.done();
if (SnapApp.getDefault().getPreferences().getBoolean(GPF.BEEP_AFTER_PROCESSING_PROPERTY, false)) {
Toolkit.getDefaultToolkit().beep();
}
}
return true;
}
@Override
public void done() {
if (!errorOccured) {
final Date now = Calendar.getInstance().getTime();
final long diff = (now.getTime() - executeStartTime.getTime()) / 1000;
if (diff > 120) {
final float minutes = diff / 60f;
statusLabel.setText("Processing completed in " + minutes + " minutes");
} else {
statusLabel.setText("Processing completed in " + diff + " seconds");
}
MemUtils.freeAllMemory();
if (ioPanel.isOpenInAppSelected()) {
final GraphExecuter graphEx = graphExecuterList.get(graphExecuterList.size() - 1);
openTargetProducts(graphEx.getProductsToOpenInDAT());
}
}
cleanUpTempFiles();
}
}
}