/* * 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.binding.ConversionException; import com.bc.ceres.binding.Property; import com.bc.ceres.binding.PropertyDescriptor; import com.bc.ceres.binding.PropertySet; import com.bc.ceres.binding.ValidationException; import com.bc.ceres.binding.ValueSet; import com.bc.ceres.core.SubProgressMonitor; import com.bc.ceres.swing.binding.BindingContext; import com.bc.ceres.swing.progress.ProgressMonitorSwingWorker; import com.bc.ceres.swing.selection.AbstractSelectionChangeListener; import com.bc.ceres.swing.selection.Selection; import com.bc.ceres.swing.selection.SelectionChangeEvent; import org.esa.snap.core.datamodel.Product; import org.esa.snap.core.datamodel.ProductNodeEvent; import org.esa.snap.core.datamodel.ProductNodeListener; import org.esa.snap.core.datamodel.RasterDataNode; import org.esa.snap.core.gpf.GPF; import org.esa.snap.core.gpf.Operator; import org.esa.snap.core.gpf.OperatorSpi; import org.esa.snap.core.gpf.common.WriteOp; import org.esa.snap.core.gpf.descriptor.OperatorDescriptor; import org.esa.snap.core.gpf.internal.OperatorExecutor; import org.esa.snap.core.gpf.internal.OperatorProductReader; import org.esa.snap.core.gpf.internal.RasterDataNodeValues; import org.esa.snap.core.gpf.ui.DefaultIOParametersPanel; import org.esa.snap.core.gpf.ui.OperatorMenu; import org.esa.snap.core.gpf.ui.OperatorParameterSupport; import org.esa.snap.core.gpf.ui.ParameterUpdater; import org.esa.snap.core.gpf.ui.SingleTargetProductDialog; import org.esa.snap.core.gpf.ui.SourceProductSelector; import org.esa.snap.core.gpf.ui.TargetProductSelectorModel; import org.esa.snap.engine_utilities.db.CommonReaders; import org.esa.snap.engine_utilities.util.MemUtils; import org.esa.snap.engine_utilities.util.ProductFunctions; import org.esa.snap.graphbuilder.gpf.ui.OperatorUI; import org.esa.snap.graphbuilder.gpf.ui.OperatorUIRegistry; import org.esa.snap.graphbuilder.gpf.ui.UIValidation; import org.esa.snap.rcp.SnapApp; import org.esa.snap.rcp.actions.file.SaveProductAsAction; import org.esa.snap.rcp.util.Dialogs; import org.esa.snap.ui.AppContext; import org.esa.snap.ui.UIUtils; import javax.media.jai.JAI; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JScrollPane; import javax.swing.JTabbedPane; import javax.swing.border.EmptyBorder; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.io.File; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ExecutionException; /** */ public class SingleOperatorDialog extends SingleTargetProductDialog { private final OperatorUI opUI; private JLabel statusLabel; private JComponent parametersPanel; private final String operatorName; private final OperatorDescriptor operatorDescriptor; private DefaultIOParametersPanel ioParametersPanel; private final OperatorParameterSupport parameterSupport; private final BindingContext bindingContext; private JTabbedPane form; private PropertyDescriptor[] rasterDataNodeTypeProperties; private String targetProductNameSuffix; private ProductChangedHandler productChangedHandler; public SingleOperatorDialog(String operatorName, AppContext appContext, String title, String helpID) { super(appContext, title, ID_APPLY_CLOSE, helpID); this.operatorName = operatorName; targetProductNameSuffix = ""; OperatorSpi operatorSpi = GPF.getDefaultInstance().getOperatorSpiRegistry().getOperatorSpi(operatorName); if (operatorSpi == null) { throw new IllegalArgumentException("No SPI found for operator name '" + operatorName + "'"); } operatorDescriptor = operatorSpi.getOperatorDescriptor(); ioParametersPanel = new DefaultIOParametersPanel(getAppContext(), operatorDescriptor, getTargetProductSelector()); parameterSupport = new OperatorParameterSupport(operatorDescriptor, null, null, new GraphBuilderParameterUpdater()); final ArrayList<SourceProductSelector> sourceProductSelectorList = ioParametersPanel.getSourceProductSelectorList(); final PropertySet propertySet = parameterSupport.getPropertySet(); bindingContext = new BindingContext(propertySet); if (propertySet.getProperties().length > 0) { if (!sourceProductSelectorList.isEmpty()) { Property[] properties = propertySet.getProperties(); List<PropertyDescriptor> rdnTypeProperties = new ArrayList<>(properties.length); for (Property property : properties) { PropertyDescriptor parameterDescriptor = property.getDescriptor(); if (parameterDescriptor.getAttribute(RasterDataNodeValues.ATTRIBUTE_NAME) != null) { rdnTypeProperties.add(parameterDescriptor); } } rasterDataNodeTypeProperties = rdnTypeProperties.toArray( new PropertyDescriptor[rdnTypeProperties.size()]); } } productChangedHandler = new ProductChangedHandler(); if (!sourceProductSelectorList.isEmpty()) { sourceProductSelectorList.get(0).addSelectionChangeListener(productChangedHandler); } opUI = OperatorUIRegistry.CreateOperatorUI(operatorName); addParameters(); getJDialog().setMinimumSize(new Dimension(450, 450)); statusLabel = new JLabel(""); statusLabel.setForeground(new Color(255, 0, 0)); this.getJDialog().getContentPane().add(statusLabel, BorderLayout.NORTH); } @Override public int show() { ioParametersPanel.initSourceProductSelectors(); if (form == null) { initForm(); if (getJDialog().getJMenuBar() == null) { final OperatorMenu operatorMenu = createDefaultMenuBar(); getJDialog().setJMenuBar(operatorMenu.createDefaultMenu()); } } setContent(form); return super.show(); } @Override public void hide() { productChangedHandler.releaseProduct(); ioParametersPanel.releaseSourceProductSelectors(); super.hide(); } @Override protected Product createTargetProduct() throws Exception { if (validateUI()) { MemUtils.freeAllMemory(); opUI.updateParameters(); final HashMap<String, Product> sourceProducts = ioParametersPanel.createSourceProductsMap(); return GPF.createProduct(operatorName, parameterSupport.getParameterMap(), sourceProducts); } return null; } public String getTargetProductNameSuffix() { return targetProductNameSuffix; } public void setTargetProductNameSuffix(String suffix) { targetProductNameSuffix = suffix; } public BindingContext getBindingContext() { return bindingContext; } private void initForm() { form = new JTabbedPane(); form.add("I/O Parameters", ioParametersPanel); //if (bindingContext.getPropertySet().getProperties().length > 0) { // final PropertyPane parametersPane = new PropertyPane(bindingContext); // final JPanel parametersPanel = parametersPane.createPanel(); // parametersPanel.setBorder(new EmptyBorder(4, 4, 4, 4)); // form.add("Processing Parameters", new JScrollPane(parametersPanel)); // updateSourceProduct(); //} parametersPanel = opUI.CreateOpTab(operatorName, parameterSupport.getParameterMap(), appContext); parametersPanel.setBorder(new EmptyBorder(4, 4, 4, 4)); form.add("Processing Parameters", new JScrollPane(parametersPanel)); } private OperatorMenu createDefaultMenuBar() { return new OperatorMenu(getJDialog(), operatorDescriptor, parameterSupport, getAppContext(), getHelpID()); } private void updateSourceProduct() { try { Property property = bindingContext.getPropertySet().getProperty(UIUtils.PROPERTY_SOURCE_PRODUCT); if (property != null) { property.setValue(productChangedHandler.currentProduct); } } catch (ValidationException e) { throw new IllegalStateException("Property '" + UIUtils.PROPERTY_SOURCE_PRODUCT + "' must be of type " + Product.class + ".", e); } } private void addParameters() { final PropertySet propertySet = parameterSupport.getPropertySet(); final List<SourceProductSelector> sourceProductSelectorList = ioParametersPanel.getSourceProductSelectorList(); if (sourceProductSelectorList.isEmpty()) { Dialogs.showError("SourceProduct @Parameter not found in operator"); } else { sourceProductSelectorList.get(0).addSelectionChangeListener(new AbstractSelectionChangeListener() { @Override public void selectionChanged(SelectionChangeEvent event) { final Product selectedProduct = (Product) event.getSelection().getSelectedValue(); if (selectedProduct != null) { //&& form != null) { final TargetProductSelectorModel targetProductSelectorModel = getTargetProductSelector().getModel(); targetProductSelectorModel.setProductName(selectedProduct.getName() + getTargetProductNameSuffix()); opUI.setSourceProducts(new Product[]{selectedProduct}); } } }); } if (propertySet.getProperties().length > 0) { if (!sourceProductSelectorList.isEmpty()) { Property[] properties = propertySet.getProperties(); List<PropertyDescriptor> rdnTypeProperties = new ArrayList<>(properties.length); for (Property property : properties) { PropertyDescriptor parameterDescriptor = property.getDescriptor(); if (parameterDescriptor.getAttribute(RasterDataNodeValues.ATTRIBUTE_NAME) != null) { rdnTypeProperties.add(parameterDescriptor); } } rasterDataNodeTypeProperties = rdnTypeProperties.toArray( new PropertyDescriptor[rdnTypeProperties.size()]); } } } private boolean validateUI() { final UIValidation validation = opUI.validateParameters(); if (validation.getState() == UIValidation.State.WARNING) { final String msg = "Warning: " + validation.getMsg() + "\n\nWould you like to continue?"; return Dialogs.requestDecision("Warning", msg, false, null) == Dialogs.Answer.YES; } else if (validation.getState() == UIValidation.State.ERROR) { final String msg = "Error: " + validation.getMsg(); Dialogs.showError(msg); return false; } return true; } @Override protected void onApply() { if (!canApply()) { return; } String productDir = targetProductSelector.getModel().getProductDir().getAbsolutePath(); SnapApp.getDefault().getPreferences().put(SaveProductAsAction.PREFERENCES_KEY_LAST_PRODUCT_DIR, productDir); statusLabel.setText(""); Product targetProduct = null; try { targetProduct = createTargetProduct(); //if (targetProduct == null) { //throw new NullPointerException("Target product is null."); //} } catch (Throwable t) { handleInitialisationError(t); } if (targetProduct == null) { return; } targetProduct.setName(targetProductSelector.getModel().getProductName()); if (targetProductSelector.getModel().isSaveToFileSelected()) { targetProduct.setFileLocation(targetProductSelector.getModel().getProductFile()); final ProgressMonitorSwingWorker worker = new ProductWriterWorker(targetProduct); //worker.executeWithBlocking(); worker.execute(); } else if (targetProductSelector.getModel().isOpenInAppSelected()) { appContext.getProductManager().addProduct(targetProduct); showOpenInAppInfo(); } } private class ProductWriterWorker extends ProgressMonitorSwingWorker<Product, Object> { private final Product targetProduct; private Date executeStartTime; private ProductWriterWorker(Product targetProduct) { super(getJDialog(), "Writing Target Product"); this.targetProduct = targetProduct; } @Override protected Product doInBackground(com.bc.ceres.core.ProgressMonitor pm) throws Exception { final TargetProductSelectorModel model = getTargetProductSelector().getModel(); pm.beginTask("Writing...", model.isOpenInAppSelected() ? 100 : 95); Product product = null; try { // free cache // NESTMOD JAI.getDefaultInstance().getTileCache().flush(); System.gc(); executeStartTime = Calendar.getInstance().getTime(); long t0 = System.currentTimeMillis(); Operator operator = null; if (targetProduct.getProductReader() instanceof OperatorProductReader) { final OperatorProductReader opReader = (OperatorProductReader) targetProduct.getProductReader(); Operator op = opReader.getOperatorContext().getOperator(); OperatorDescriptor descriptor = op.getSpi().getOperatorDescriptor(); if (descriptor.isAutoWriteDisabled()) { operator = op; } } if (operator == null) { WriteOp writeOp = new WriteOp(targetProduct, model.getProductFile(), model.getFormatName()); writeOp.setDeleteOutputOnFailure(true); writeOp.setWriteEntireTileRows(true); writeOp.setClearCacheAfterRowWrite(false); operator = writeOp; } final OperatorExecutor executor = OperatorExecutor.create(operator); executor.execute(SubProgressMonitor.create(pm, 95)); File targetFile = model.getProductFile(); if (model.isOpenInAppSelected() && targetFile.exists()) { product = CommonReaders.readProduct(targetFile); if (product == null) { product = targetProduct; // todo - check - this cannot be ok!!! (nf) } pm.worked(5); } } finally { // free cache JAI.getDefaultInstance().getTileCache().flush(); System.gc(); pm.done(); if (product != targetProduct) { targetProduct.dispose(); } } return product; } @Override protected void done() { final TargetProductSelectorModel model = getTargetProductSelector().getModel(); try { final Product targetProduct = get(); if(targetProduct != null) { final Date now = Calendar.getInstance().getTime(); final long totalSeconds = (now.getTime() - executeStartTime.getTime()) / 1000; final long totalBytes = ProductFunctions.getRawStorageSize(targetProduct); final long totalPixels = ProductFunctions.getTotalPixels(targetProduct); statusLabel.setText(ProductFunctions.getProcessingStatistics(totalSeconds, totalBytes, totalPixels)); if (model.isOpenInAppSelected()) { appContext.getProductManager().addProduct(targetProduct); //showSaveAndOpenInAppInfo(saveTime); } else { //showSaveInfo(saveTime); } } } catch (InterruptedException e) { // ignore } catch (ExecutionException e) { handleProcessingError(e.getCause()); } catch (Throwable t) { handleProcessingError(t); } } } private class ProductChangedHandler extends AbstractSelectionChangeListener implements ProductNodeListener { private Product currentProduct; public void releaseProduct() { if (currentProduct != null) { currentProduct.removeProductNodeListener(this); currentProduct = null; updateSourceProduct(); } } @Override public void selectionChanged(SelectionChangeEvent event) { Selection selection = event.getSelection(); if (selection != null) { final Product selectedProduct = (Product) selection.getSelectedValue(); if (selectedProduct != currentProduct) { if (currentProduct != null) { currentProduct.removeProductNodeListener(this); } currentProduct = selectedProduct; if (currentProduct != null) { currentProduct.addProductNodeListener(this); } updateTargetProductName(); updateValueSets(currentProduct); updateSourceProduct(); } } } @Override public void nodeAdded(ProductNodeEvent event) { handleProductNodeEvent(); } @Override public void nodeChanged(ProductNodeEvent event) { handleProductNodeEvent(); } @Override public void nodeDataChanged(ProductNodeEvent event) { handleProductNodeEvent(); } @Override public void nodeRemoved(ProductNodeEvent event) { handleProductNodeEvent(); } private void updateTargetProductName() { String productName = ""; if (currentProduct != null) { productName = currentProduct.getName(); } final TargetProductSelectorModel targetProductSelectorModel = getTargetProductSelector().getModel(); targetProductSelectorModel.setProductName(productName + getTargetProductNameSuffix()); } private void handleProductNodeEvent() { updateValueSets(currentProduct); } private void updateValueSets(Product product) { if (rasterDataNodeTypeProperties != null) { for (PropertyDescriptor propertyDescriptor : rasterDataNodeTypeProperties) { updateValueSet(propertyDescriptor, product); } } } } private static void updateValueSet(PropertyDescriptor propertyDescriptor, Product product) { String[] values = new String[0]; if (product != null) { Object object = propertyDescriptor.getAttribute(RasterDataNodeValues.ATTRIBUTE_NAME); if (object != null) { @SuppressWarnings("unchecked") Class<? extends RasterDataNode> rasterDataNodeType = (Class<? extends RasterDataNode>) object; boolean includeEmptyValue = !propertyDescriptor.isNotNull() && !propertyDescriptor.isNotEmpty() && !propertyDescriptor.getType().isArray(); values = RasterDataNodeValues.getNames(product, rasterDataNodeType, includeEmptyValue); } } propertyDescriptor.setValueSet(new ValueSet(values)); } private class GraphBuilderParameterUpdater implements ParameterUpdater { @Override public void handleParameterSaveRequest(Map<String, Object> parameterMap) { opUI.updateParameters(); } @Override public void handleParameterLoadRequest(Map<String, Object> parameterMap) throws ValidationException, ConversionException { opUI.initParameters(); } } }