/* * * * Copyright (C) 2016 CS ROMANIA * * * * 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.ui.tooladapter.dialogs; import com.bc.ceres.binding.Property; import com.bc.ceres.binding.PropertyContainer; import com.bc.ceres.binding.PropertyDescriptor; import com.bc.ceres.binding.ValueSet; import com.bc.ceres.swing.binding.BindingContext; import com.bc.ceres.swing.binding.PropertyEditor; import com.bc.ceres.swing.binding.PropertyEditorRegistry; import org.esa.snap.core.gpf.descriptor.OSFamily; import org.esa.snap.core.gpf.descriptor.SystemVariable; import org.esa.snap.core.gpf.descriptor.TemplateParameterDescriptor; import org.esa.snap.core.gpf.descriptor.dependency.Bundle; import org.esa.snap.core.gpf.descriptor.dependency.BundleLocation; import org.esa.snap.core.gpf.descriptor.dependency.BundleType; import org.esa.snap.ui.AbstractDialog; import org.esa.snap.ui.AppContext; import org.esa.snap.ui.GridBagUtils; import javax.swing.BorderFactory; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JTabbedPane; import javax.swing.JTextField; import javax.swing.border.Border; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.plaf.basic.BasicTabbedPaneUI; import java.awt.Component; import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.awt.event.ItemEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import static org.esa.snap.utils.SpringUtilities.DEFAULT_PADDING; /** * Form for editing the bundle properties for an adapter * * @author Cosmin Cara */ public class BundleForm extends JPanel { private Map<OSFamily, Bundle> original; private Map<OSFamily, Bundle> modified; private final Map<OSFamily, PropertyContainer> propertyContainers; private final Map<OSFamily, BindingContext> bindingContexts; private final Map<OSFamily, List<JComponent>> controls; private List<SystemVariable> variables; private PropertyChangeListener listener; private AppContext appContext; private JTabbedPane bundleTabPane; private JComponent variablesCombo; public BundleForm(AppContext appContext, Bundle windowsBundle, Bundle linuxBundle, Bundle macosxBundle, List<SystemVariable> variables) { this.original = new HashMap<OSFamily, Bundle>() {{ put(OSFamily.windows, windowsBundle); put(OSFamily.linux, linuxBundle); put(OSFamily.macosx, macosxBundle); }}; this.appContext = appContext; try { this.modified = copy(this.original); } catch (Exception e) { e.printStackTrace(); } this.variables = variables; propertyContainers = new HashMap<>(); bindingContexts = new HashMap<>(); this.bundleTabPane = new JTabbedPane(JTabbedPane.TOP); this.bundleTabPane.setBorder(BorderFactory.createEmptyBorder()); this.controls = new HashMap<>(); Bundle bundle = modified.get(OSFamily.windows); PropertyContainer propertyContainer = PropertyContainer.createObjectBacked(bundle); propertyContainers.put(OSFamily.windows, propertyContainer); bindingContexts.put(OSFamily.windows, new BindingContext(propertyContainer)); controls.put(OSFamily.windows, new ArrayList<>()); bundle = modified.get(OSFamily.linux); propertyContainer = PropertyContainer.createObjectBacked(bundle); propertyContainers.put(OSFamily.linux, propertyContainer); bindingContexts.put(OSFamily.linux, new BindingContext(propertyContainer)); controls.put(OSFamily.linux, new ArrayList<>()); bundle = modified.get(OSFamily.macosx); propertyContainer = PropertyContainer.createObjectBacked(bundle); propertyContainers.put(OSFamily.macosx, propertyContainer); bindingContexts.put(OSFamily.macosx, new BindingContext(propertyContainer)); controls.put(OSFamily.macosx, new ArrayList<>()); buildUI(); addChangeListeners(); toggleControls(OSFamily.windows); toggleControls(OSFamily.linux); toggleControls(OSFamily.macosx); } /** * Updates the source bundle object and returns the modified bundle object. */ public Map<OSFamily, Bundle> applyChanges() { try { this.original = copy(this.modified); } catch (Exception e) { e.printStackTrace(); } return this.original; } public void setPropertyChangeListener(PropertyChangeListener listener) { this.listener = listener; } private void buildUI() { setLayout(new GridBagLayout()); GridBagConstraints gbc = new GridBagConstraints(); gbc.fill = GridBagConstraints.HORIZONTAL; gbc.anchor = GridBagConstraints.NORTHWEST; gbc.insets = new Insets(DEFAULT_PADDING, DEFAULT_PADDING, DEFAULT_PADDING, DEFAULT_PADDING); gbc.weighty = 0.01; addTab("Windows Bundle", createTab(OSFamily.windows)); addTab("Linux Bundle", createTab(OSFamily.linux)); addTab("MacOSX Bundle", createTab(OSFamily.macosx)); GridBagUtils.addToPanel(this, this.bundleTabPane, gbc, "gridx=0, gridy=0, gridwidth=11, weightx=1"); GridBagUtils.addToPanel(this, new JLabel("Reflect in Variable:"), gbc, "gridx=0, gridy=1, gridwidth=1, weightx=0"); variablesCombo = getEditorComponent(OSFamily.all, "updateVariable", this.variables.stream().map(SystemVariable::getKey).toArray()); GridBagUtils.addToPanel(this, variablesCombo, gbc, "gridx=1, gridy=1, gridwidth=10, weightx=0"); GridBagUtils.addToPanel(this, new JLabel(" "), gbc, "gridx=0, gridy=2, gridwidth=11, weighty=1"); int selected = 0; switch (Bundle.getCurrentOS()) { case windows: selected = 0; break; case linux: selected = 1; break; case macosx: selected = 2; break; } this.bundleTabPane.setSelectedIndex(selected); this.bundleTabPane.setUI(new BasicTabbedPaneUI()); } public void setVariables(List<SystemVariable> variables) { this.variables = variables; PropertyContainer propertyContainer = propertyContainers.get(OSFamily.windows); BindingContext bindingContext = bindingContexts.get(OSFamily.windows); PropertyDescriptor propertyDescriptor = propertyContainer.getDescriptor("updateVariable"); propertyDescriptor.setValueSet(new ValueSet(this.variables.stream().map(SystemVariable::getKey).toArray())); PropertyEditor propertyEditor = PropertyEditorRegistry.getInstance().findPropertyEditor(propertyDescriptor); variablesCombo = propertyEditor.createEditorComponent(propertyDescriptor, bindingContext); repaint(); } private JPanel createTab(OSFamily osFamily) { JPanel panel = new JPanel(new GridBagLayout()); GridBagConstraints c = new GridBagConstraints(); c.fill = GridBagConstraints.HORIZONTAL; c.anchor = GridBagConstraints.NORTHWEST; c.insets = new Insets(DEFAULT_PADDING, DEFAULT_PADDING, DEFAULT_PADDING, DEFAULT_PADDING); c.weighty = 0.01; List<JComponent> controlGroup = controls.get(osFamily); final Bundle bundle = modified.get(osFamily); GridBagUtils.addToPanel(panel, new JLabel("Type:"), c, "gridx=0, gridy=0, gridwidth=1, weightx=0"); JComponent type = getEditorComponent(osFamily, "bundleType"); GridBagUtils.addToPanel(panel, type, c, "gridx=1, gridy=0, gridwidth=10, weightx=1"); GridBagUtils.addToPanel(panel, new JLabel("Location:"), c, "gridx=0, gridy=1, gridwidth=1, weightx=0"); ButtonGroup rbGroup = new ButtonGroup(); JRadioButton rbLocal = new JRadioButton(BundleLocation.LOCAL.toString()); rbGroup.add(rbLocal); JRadioButton rbRemote = new JRadioButton(BundleLocation.REMOTE.toString()); rbGroup.add(rbRemote); if (BundleLocation.LOCAL.equals(bundle.getLocation())) { rbLocal.setSelected(true); } else if (BundleLocation.REMOTE.equals(bundle.getLocation())) { rbRemote.setSelected(true); } rbLocal.addItemListener(e -> { if (e.getStateChange() == ItemEvent.SELECTED) { //this is first because, for some reason, otherwise is not triggering the editing event propertyContainers.get(osFamily).setValue("downloadURL", ""); bundle.setLocation(BundleLocation.LOCAL); propertyContainers.get(osFamily).setValue("bundleLocation", BundleLocation.LOCAL); toggleControls(osFamily); firePropertyChange(new PropertyChangeEvent(bundle, "bundleLocation", null, BundleLocation.LOCAL)); } }); rbRemote.addItemListener(e -> { if (e.getStateChange() == ItemEvent.SELECTED) { //this is first because, for some reason, otherwise is not triggering the editing event propertyContainers.get(osFamily).setValue("source", null); bundle.setLocation(BundleLocation.REMOTE); propertyContainers.get(osFamily).setValue("bundleLocation", BundleLocation.REMOTE); toggleControls(osFamily); firePropertyChange(new PropertyChangeEvent(bundle, "bundleLocation", null, BundleLocation.REMOTE)); } }); GridBagUtils.addToPanel(panel, rbLocal, c, "gridx=1, gridy=1, gridwidth=4, weightx=1"); GridBagUtils.addToPanel(panel, rbRemote, c, "gridx=5, gridy=1, gridwidth=5, weightx=1"); controlGroup.add(rbLocal); controlGroup.add(rbRemote); GridBagUtils.addToPanel(panel, new JLabel("Source File:"), c, "gridx=0, gridy=2, gridwidth=1, weightx=0"); JComponent component = getEditorComponent(osFamily, "source"); GridBagUtils.addToPanel(panel, component, c, "gridx=1, gridy=2, gridwidth=10, weightx=1"); controlGroup.add(component); GridBagUtils.addToPanel(panel, new JLabel("URL:"), c, "gridx=0, gridy=3, gridwidth=1, weightx=0"); final JComponent downloadURL = getEditorComponent(osFamily, "downloadURL", "url"); ((JTextField) downloadURL).getDocument().addDocumentListener(new DocumentListener() { @Override public void insertUpdate(DocumentEvent e) { } @Override public void removeUpdate(DocumentEvent e) { } @Override public void changedUpdate(DocumentEvent e) { try { String url = ((JTextField) downloadURL).getText(); URL checkedURL = new URL(url); bundle.setDownloadURL(checkedURL.toString()); } catch (Exception ex) { ex.printStackTrace(); } } }); component.addFocusListener(new FocusAdapter() { @Override public void focusLost(FocusEvent e) { try { String url = ((JTextField) downloadURL).getText(); URL checkedURL = new URL(url); bundle.setDownloadURL(checkedURL.toString()); super.focusLost(e); } catch (Exception ex) { ex.printStackTrace(); } } }); GridBagUtils.addToPanel(panel, downloadURL, c, "gridx=1, gridy=3, gridwidth=10, weightx=1"); controlGroup.add(downloadURL); GridBagUtils.addToPanel(panel, new JLabel("Arguments:"), c, "gridx=0, gridy=4, gridwidth=1, weightx=0"); final JTextField argumentsParam = new JTextField(bundle.getCommandLine()); GridBagUtils.addToPanel(panel, argumentsParam, c, "gridx=1, gridy=4, gridwidth=9, weightx=0"); controlGroup.add(argumentsParam); argumentsParam.setEditable(false); JButton argumentDetailsButton = new JButton("..."); argumentDetailsButton.setMaximumSize(new Dimension(32, argumentDetailsButton.getHeight())); argumentDetailsButton.addActionListener(e -> { TemplateParameterEditorDialog parameterEditorDialog = new TemplateParameterEditorDialog(appContext, bundle.getArgumentsParameter(), bundle.getParent()); int returnCode = parameterEditorDialog.show(); if (returnCode == AbstractDialog.ID_OK) { argumentsParam.setText(bundle.getCommandLine()); } }); GridBagUtils.addToPanel(panel, argumentDetailsButton, c, "gridx=10, gridy=4, gridwidth=1, weightx=0"); controlGroup.add(argumentDetailsButton); GridBagUtils.addToPanel(panel, new JLabel("Target Folder:"), c, "gridx=0, gridy=5, gridwidth=1, weightx=0"); component = getEditorComponent(osFamily, "targetLocation", true); GridBagUtils.addToPanel(panel, component, c, "gridx=1, gridy=5, gridwidth=10, weightx=1"); controlGroup.add(component); toggleControls(osFamily); return panel; } private void addTab(String text, JPanel contents) { JLabel tabText = new JLabel(text, JLabel.LEFT); Border titledBorder = BorderFactory.createEmptyBorder(); contents.setBorder(titledBorder); this.bundleTabPane.addTab(null, contents); this.bundleTabPane.setTabComponentAt(this.bundleTabPane.getTabCount() - 1, tabText); } private void addChangeListeners() { propertyContainers.entrySet().forEach(entry -> { OSFamily osFamily = entry.getKey(); Bundle bundle = modified.get(osFamily); PropertyContainer propertyContainer = entry.getValue(); Property property = propertyContainer.getProperty("bundleType"); property.addPropertyChangeListener(evt -> { bundle.setBundleType((BundleType) evt.getNewValue()); toggleControls(osFamily); }); property = propertyContainer.getProperty("updateVariable"); property.addPropertyChangeListener(evt -> { Object value = evt.getNewValue(); if (value != null) { modified.values().forEach(b -> b.setUpdateVariable(value.toString())); } }); property = propertyContainer.getProperty("bundleLocation"); property.addPropertyChangeListener(evt -> { bundle.setLocation(Enum.valueOf(BundleLocation.class, (String) evt.getNewValue())); toggleControls(osFamily); }); property = propertyContainer.getProperty("source"); property.addPropertyChangeListener(evt -> { bundle.setSource((File) evt.getNewValue()); firePropertyChange(new PropertyChangeEvent(bundle, "source", evt.getOldValue(), evt.getNewValue())); }); property = propertyContainer.getProperty("downloadURL"); property.addPropertyChangeListener(evt -> { try { String url = String.valueOf(evt.getNewValue()); if(url != null && url.length() > 0) { URL checkedURL = new URL(url); bundle.setDownloadURL(checkedURL.toString()); }else { bundle.setDownloadURL(null); } firePropertyChange(new PropertyChangeEvent(bundle, "source", evt.getOldValue(), evt.getNewValue())); } catch (Exception ex) { ex.printStackTrace(); } }); property = propertyContainer.getProperty("targetLocation"); property.addPropertyChangeListener(evt -> { bundle.setTargetLocation(String.valueOf(evt.getNewValue())); firePropertyChange(new PropertyChangeEvent(bundle, "source", evt.getOldValue(), evt.getNewValue())); }); property = propertyContainer.getProperty("templateparameter"); property.addPropertyChangeListener(evt -> bundle.setArgumentsParameter((TemplateParameterDescriptor) evt.getNewValue())); }); } private void firePropertyChange(PropertyChangeEvent event) { if (this.listener != null) { this.listener.propertyChange(event); } } private void toggleControls(OSFamily osFamily) { Bundle bundle = modified.get(osFamily); boolean canSelect = bundle.getBundleType() != BundleType.NONE; BundleLocation location = bundle.getLocation(); boolean remoteCondition = canSelect && location == BundleLocation.REMOTE; boolean localCondition = canSelect && location == BundleLocation.LOCAL; for (JComponent component : controls.get(osFamily)) { if ("url".equals(component.getName())) { component.setEnabled(remoteCondition); } else { component.setEnabled(canSelect); } } BindingContext bindingContext = bindingContexts.get(osFamily); JComponent[] components = bindingContext.getBinding("source").getComponents(); for (JComponent component : components) { component.setEnabled(localCondition); } for (Component jcomponent : components[0].getParent().getComponents()) { jcomponent.setEnabled(localCondition); } components = bindingContext.getBinding("targetLocation").getComponents(); for (JComponent component : components) { component.setEnabled(canSelect); } repaint(); } private JComponent getEditorComponent(OSFamily osFamily, String propertyName) { return getEditorComponent(osFamily, propertyName, null, false); } private JComponent getEditorComponent(OSFamily osFamily, String propertyName, boolean isFolder) { return getEditorComponent(osFamily, propertyName, null, isFolder); } private JComponent getEditorComponent(OSFamily osFamily, String propertyName, String controlName) { return getEditorComponent(osFamily, propertyName, controlName, false); } private JComponent getEditorComponent(OSFamily osFamily, String propertyName, Object[] items) { if (osFamily == OSFamily.all) { osFamily = OSFamily.windows; } PropertyContainer propertyContainer = propertyContainers.get(osFamily); BindingContext bindingContext = bindingContexts.get(osFamily); PropertyDescriptor propertyDescriptor = propertyContainer.getDescriptor(propertyName); if (items != null) { propertyDescriptor.setValueSet(new ValueSet(items)); } PropertyEditor propertyEditor = PropertyEditorRegistry.getInstance().findPropertyEditor(propertyDescriptor); return propertyEditor.createEditorComponent(propertyDescriptor, bindingContext); } private JComponent getEditorComponent(OSFamily osFamily, String propertyName,String controlName, boolean isFolder) { if (osFamily == OSFamily.all) { osFamily = OSFamily.windows; } PropertyContainer propertyContainer = propertyContainers.get(osFamily); BindingContext bindingContext = bindingContexts.get(osFamily); PropertyDescriptor propertyDescriptor = propertyContainer.getDescriptor(propertyName); if (isFolder) { propertyDescriptor.setAttribute("directory", true); } PropertyEditor propertyEditor = PropertyEditorRegistry.getInstance().findPropertyEditor(propertyDescriptor); JComponent editorComponent = propertyEditor.createEditorComponent(propertyDescriptor, bindingContext); if (controlName != null) { editorComponent.setName(controlName); } return editorComponent; } private Map<OSFamily, Bundle> copy(Map<OSFamily, Bundle> sources) throws Exception { if (sources == null) { throw new IllegalArgumentException("source cannot be null"); } Map<OSFamily, Bundle> bundles = new HashMap<>(); for (Map.Entry<OSFamily, Bundle> entry : sources.entrySet()) { Bundle source = entry.getValue(); Bundle target = new Bundle(source); bundles.put(entry.getKey(), target); } return bundles; } }