changeset 108:fde496a78a98

Add UI for editing options
author Omair Majid <omajid@redhat.com>
date Thu, 27 Feb 2014 15:40:52 -0500
parents 4dfa549ca00c
children cba7b8de5f76
files TODO.md com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/Messages.java com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/editor/CommandEditPage.java com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/editor/OptionsWizard.java com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/messages.properties com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/model/Group.java com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/model/ModelObject.java com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/model/Option.java com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/model/Options.java
diffstat 9 files changed, 414 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/TODO.md	Fri Feb 21 15:59:02 2014 -0500
+++ b/TODO.md	Thu Feb 27 15:40:52 2014 -0500
@@ -8,7 +8,7 @@
 
 - Fix up stale states
 
-- Implement support for options
+- Implement UI for grouping options
 
 - Modify the bundle loading dialog to be smarter. Offer auto-completion and
   automatically identify bundle names from all open projects and dependencies,
--- a/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/Messages.java	Fri Feb 21 15:59:02 2014 -0500
+++ b/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/Messages.java	Thu Feb 27 15:40:52 2014 -0500
@@ -16,6 +16,7 @@
     public static String CommandEditPage_cliCheckTitle;
     public static String CommandEditPage_commandDetailsSectionTitle;
     public static String CommandEditPage_descriptionLabel;
+    public static String CommandEditPage_editOptions;
     public static String CommandEditPage_environmentsTitle;
     public static String CommandEditPage_nameLabel;
     public static String CommandEditPage_removeButton;
@@ -43,6 +44,15 @@
     public static String NewNameDialog_message;
     public static String NewNameDialog_nameLabel;
     public static String NewNameDialog_title;
+    public static String OptionsWizard_createOptionsPageAddOption;
+    public static String OptionsWizard_createOptionsPageArgumentLabel;
+    public static String OptionsWizard_createOptionsPageDescription;
+    public static String OptionsWizard_createOptionsPageDescriptionLabel;
+    public static String OptionsWizard_createOptionsPageLongNameLabel;
+    public static String OptionsWizard_createOptionsPageRemoveOption;
+    public static String OptionsWizard_createOptionsPageRequiredLabel;
+    public static String OptionsWizard_createOptionsPageShortNameLabel;
+    public static String OptionsWizard_createOptionsPageTitle;
     public static String OverviewPage_bundlesAndExtensionsOverviewForm;
     public static String OverviewPage_commandsAndExtensionSectionTitle;
     public static String OverviewPage_formTitle;
--- a/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/editor/CommandEditPage.java	Fri Feb 21 15:59:02 2014 -0500
+++ b/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/editor/CommandEditPage.java	Thu Feb 27 15:40:52 2014 -0500
@@ -17,9 +17,11 @@
 import org.eclipse.jface.viewers.IStructuredSelection;
 import org.eclipse.jface.viewers.TableViewer;
 import org.eclipse.jface.window.Window;
+import org.eclipse.jface.wizard.WizardDialog;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.SelectionAdapter;
 import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.SelectionListener;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
 import org.eclipse.swt.layout.RowLayout;
@@ -43,6 +45,7 @@
 import com.redhat.thermostat.tools.eclipse.plugin.model.Bundle;
 import com.redhat.thermostat.tools.eclipse.plugin.model.Command;
 import com.redhat.thermostat.tools.eclipse.plugin.model.Environments;
+import com.redhat.thermostat.tools.eclipse.plugin.model.Options;
 import com.redhat.thermostat.tools.eclipse.plugin.model.Plugin;
 
 /**
@@ -210,13 +213,23 @@
         argumentsAndOptions.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
         argumentsAndOptions.setText(Messages.CommandEditPage_argumentsAndOptionsSectionTitle);
 
-        Composite contents = toolkit.createComposite(argumentsAndOptions);
+        final Composite contents = toolkit.createComposite(argumentsAndOptions);
         argumentsAndOptions.setClient(contents);
         contents.setLayout(new GridLayout(2, false));
 
         toolkit.createLabel(contents, Messages.CommandEditPage_argumentsLabel);
         commandArguments = toolkit.createText(contents, "<arguments>"); //$NON-NLS-1$
         commandArguments.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));
+
+        Button editOptions = toolkit.createButton(contents, Messages.CommandEditPage_editOptions, SWT.None);
+        editOptions.addSelectionListener(new SelectionAdapter() {
+            @Override
+            public void widgetSelected(SelectionEvent e) {
+                WizardDialog optionsEditDialog = new WizardDialog(contents.getShell(), new OptionsWizard(commandModel));
+                // wizard will update command model as a side-effect if it succeeds
+                optionsEditDialog.open();
+            }
+        });
     }
 
     private void createBundlesSection(final Composite sectionContents) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/editor/OptionsWizard.java	Thu Feb 27 15:40:52 2014 -0500
@@ -0,0 +1,330 @@
+package com.redhat.thermostat.tools.eclipse.plugin.editor;
+
+import java.util.Objects;
+
+import org.eclipse.core.databinding.DataBindingContext;
+import org.eclipse.core.databinding.beans.BeanProperties;
+import org.eclipse.core.databinding.observable.ChangeEvent;
+import org.eclipse.core.databinding.observable.IChangeListener;
+import org.eclipse.core.databinding.observable.list.IListChangeListener;
+import org.eclipse.core.databinding.observable.list.ListChangeEvent;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.jface.databinding.swt.WidgetProperties;
+import org.eclipse.jface.databinding.viewers.IViewerObservableValue;
+import org.eclipse.jface.databinding.viewers.ObservableListContentProvider;
+import org.eclipse.jface.databinding.viewers.ObservableMapLabelProvider;
+import org.eclipse.jface.databinding.viewers.ViewerProperties;
+import org.eclipse.jface.databinding.wizard.WizardPageSupport;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ListViewer;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.wizard.Wizard;
+import org.eclipse.jface.wizard.WizardPage;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionAdapter;
+import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.List;
+import org.eclipse.swt.widgets.Text;
+
+import com.redhat.thermostat.tools.eclipse.plugin.Messages;
+import com.redhat.thermostat.tools.eclipse.plugin.model.Command;
+import com.redhat.thermostat.tools.eclipse.plugin.model.Option;
+import com.redhat.thermostat.tools.eclipse.plugin.model.Options;
+
+/**
+ * A {@link Wizard} for editing the command line options for a command.
+ * <p>
+ * The wizrad allows adding/editing and removing options.
+ */
+public class OptionsWizard extends Wizard {
+
+    private Command commandModel;
+    private Options myOptions;
+
+    public OptionsWizard(Command commandModel) {
+        setNeedsProgressMonitor(true);
+
+        this.commandModel = commandModel;
+
+        if (commandModel.getOptions() != null) {
+            myOptions = new Options(commandModel.getOptions());
+        } else {
+            myOptions = new Options();
+        }
+    }
+
+    @Override
+    public void addPages() {
+        addPage(new CreateOptionsPage());
+    }
+
+    @Override
+    public boolean performFinish() {
+        commandModel.setOptions(myOptions);
+
+        return true;
+    }
+
+    class CreateOptionsPage extends WizardPage {
+
+        private DataBindingContext dataBindingContext;
+        private ListViewer optionsListViewer;
+        private Text longName;
+        private Text shortName;
+        private Text description;
+        private Text argument;
+        private Button required;
+        private WritableList allOptions;
+        private Button add;
+        private Button remove;
+
+        public CreateOptionsPage() {
+            super("Create Options"); //$NON-NLS-1$
+            setTitle(Messages.OptionsWizard_createOptionsPageTitle);
+            setDescription(Messages.OptionsWizard_createOptionsPageDescription);
+        }
+
+        @Override
+        public void createControl(Composite parent) {
+            Composite container = new Composite(parent, SWT.NONE);
+            container.setLayout(new GridLayout(2, false));
+
+            createMasterArea(container);
+            createDetailsArea(container);
+
+            initDataBindings();
+
+            createWidgetEnableDisableListeners();
+
+            setControl(container);
+        }
+
+        private void createMasterArea(Composite parent) {
+            Composite masterList = new Composite(parent, SWT.NONE);
+            masterList.setLayoutData(new GridData(SWT.BEGINNING, SWT.FILL, false, true));
+
+            masterList.setLayout(new GridLayout(2, false));
+
+            List optionsList = new List(masterList, SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
+            GridData listLayoutData = new GridData(SWT.BEGINNING, SWT.FILL, true, true);
+            listLayoutData.widthHint = 150;
+            optionsList.setLayoutData(listLayoutData);
+
+            optionsListViewer = new ListViewer(optionsList);
+
+            Composite buttonsComposite = new Composite(masterList, SWT.NONE);
+            buttonsComposite.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, false, true));
+
+            buttonsComposite.setLayout(new GridLayout(1, false));
+
+            add = new Button(buttonsComposite, SWT.PUSH);
+            add.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+            add.setText(Messages.OptionsWizard_createOptionsPageAddOption);
+            add.addSelectionListener(new SelectionAdapter() {
+                @Override
+                public void widgetSelected(SelectionEvent e) {
+                    Option option = new Option();
+                    allOptions.add(option);
+                    optionsListViewer.setSelection(new StructuredSelection(option), true);
+                }
+            });
+
+            remove = new Button(buttonsComposite, SWT.PUSH);
+            remove.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false));
+            remove.setText(Messages.OptionsWizard_createOptionsPageRemoveOption);
+            remove.addSelectionListener(new SelectionAdapter() {
+                @Override
+                public void widgetSelected(SelectionEvent e) {
+                    IStructuredSelection selection = (IStructuredSelection) optionsListViewer.getSelection();
+                    Option selectedOption = (Option) Objects.requireNonNull(selection.getFirstElement());
+                    allOptions.remove(selectedOption);
+                }
+            });
+        }
+
+        private void createDetailsArea(Composite parent) {
+            Composite details = new Composite(parent, SWT.NONE);
+            details.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+            details.setLayout(new GridLayout(2, false));
+
+            Label longNameLabel = new Label(details, SWT.NONE);
+            longNameLabel.setLayoutData(new GridData(SWT.END, SWT.BEGINNING, false, false));
+            longNameLabel.setText(Messages.OptionsWizard_createOptionsPageLongNameLabel);
+            longName = new Text(details, SWT.NONE);
+            longName.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));
+
+            Label shortNameLabel = new Label(details, SWT.NONE);
+            shortNameLabel.setLayoutData(new GridData(SWT.END, SWT.BEGINNING, false, false));
+            shortNameLabel.setText(Messages.OptionsWizard_createOptionsPageShortNameLabel);
+            shortName = new Text(details, SWT.NONE);
+            shortName.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));
+
+            Label descriptionLabel = new Label(details, SWT.NONE);
+            descriptionLabel.setLayoutData(new GridData(SWT.END, SWT.BEGINNING, false, false));
+            descriptionLabel.setText(Messages.OptionsWizard_createOptionsPageDescriptionLabel);
+            description = new Text(details, SWT.NONE);
+            description.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));
+
+            Label argumentLabel = new Label(details, SWT.NONE);
+            argumentLabel.setLayoutData(new GridData(SWT.END, SWT.BEGINNING, false, false));
+            argumentLabel.setText(Messages.OptionsWizard_createOptionsPageArgumentLabel);
+            argument = new Text(details, SWT.NONE);
+            argument.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));
+
+            Label requiredLabel = new Label(details, SWT.NONE);
+            requiredLabel.setLayoutData(new GridData(SWT.END, SWT.BEGINNING, false, false));
+            requiredLabel.setText(Messages.OptionsWizard_createOptionsPageRequiredLabel);
+            required = new Button(details, SWT.CHECK);
+            required.setLayoutData(new GridData(SWT.FILL, SWT.BEGINNING, true, false));
+        }
+
+        private void initDataBindings() {
+            dataBindingContext = new DataBindingContext();
+
+            ObservableListContentProvider contentProvider = new ObservableListContentProvider();
+            optionsListViewer.setContentProvider(contentProvider);
+            IObservableSet knownElements = contentProvider.getKnownElements();
+            IObservableMap longNames = BeanProperties.value(Option.class, "longName", String.class).observeDetail(knownElements); //$NON-NLS-1$
+            IObservableMap shortNames = BeanProperties.value(Option.class, "shortName", String.class).observeDetail(knownElements); //$NON-NLS-1$
+
+            IObservableMap[] attributeMaps = new IObservableMap[] { longNames, shortNames };
+            optionsListViewer.setLabelProvider(new OptionLabelProvider(attributeMaps));
+
+            allOptions = new WritableList(myOptions.getOptions(), Option.class);
+            optionsListViewer.setInput(allOptions);
+
+            IViewerObservableValue selectedOption = ViewerProperties.singleSelection().observe(optionsListViewer);
+
+            IObservableValue longNameWidgetValue = WidgetProperties.text(SWT.Modify).observe(longName);
+            IObservableValue longNameModelValue = BeanProperties.value(Option.class, "longName").observeDetail(selectedOption); //$NON-NLS-1$
+            dataBindingContext.bindValue(longNameWidgetValue, longNameModelValue);
+
+            IObservableValue shortNameWidgetValue = WidgetProperties.text(SWT.Modify).observe(shortName);
+            IObservableValue shortNameModelValue = BeanProperties.value(Option.class, "shortName").observeDetail(selectedOption); //$NON-NLS-1$
+            dataBindingContext.bindValue(shortNameWidgetValue, shortNameModelValue);
+
+            IObservableValue descriptionWidgetValue = WidgetProperties.text(SWT.Modify).observe(description);
+            IObservableValue descriptionModelValue = BeanProperties.value(Option.class, "description").observeDetail(selectedOption); //$NON-NLS-1$
+            dataBindingContext.bindValue(descriptionWidgetValue, descriptionModelValue);
+
+            IObservableValue argumentWidgetValue = WidgetProperties.text(SWT.Modify).observe(argument);
+            IObservableValue argumentModelValue = BeanProperties.value(Option.class, "argument").observeDetail(selectedOption); //$NON-NLS-1$
+            dataBindingContext.bindValue(argumentWidgetValue, argumentModelValue);
+
+            IObservableValue requiredWidgetValue = WidgetProperties.selection().observe(required);
+            IObservableValue requiredModelValue = BeanProperties.value(Option.class, "required").observeDetail(selectedOption); //$NON-NLS-1$
+            dataBindingContext.bindValue(requiredWidgetValue, requiredModelValue);
+
+            // FIXME enable this after identifying how to deal with null? master data
+            // WizardPageSupport.create(this, dataBindingContext);
+        }
+
+        @Override
+        public void dispose() {
+            super.dispose();
+
+            dataBindingContext.dispose();
+        }
+
+        private void createWidgetEnableDisableListeners() {
+            IListChangeListener enableDisableRemoveButton = new IListChangeListener() {
+                @Override
+                public void handleListChange(ListChangeEvent event) {
+                    int size = event.getObservableList().size();
+                    if (size == 0) {
+                        remove.setEnabled(false);
+                    } else {
+                        remove.setEnabled(true);
+                    }
+                }
+            };
+            allOptions.addListChangeListener(enableDisableRemoveButton);
+            enableDisableRemoveButton.handleListChange(new ListChangeEvent(allOptions, null));
+
+            IListChangeListener enableDisableDataWidgets = new IListChangeListener() {
+                @Override
+                public void handleListChange(ListChangeEvent event) {
+                    int size = event.getObservableList().size();
+                    boolean toEnable = true;
+                    if (size == 0) {
+                        toEnable = false;
+                    } else {
+                        toEnable = true;
+                    }
+                    longName.setEnabled(toEnable);
+                    shortName.setEnabled(toEnable);
+                    description.setEnabled(toEnable);
+                    argument.setEnabled(toEnable);
+                    required.setEnabled(toEnable);
+
+                }
+            };
+            allOptions.addListChangeListener(enableDisableDataWidgets);
+            enableDisableDataWidgets.handleListChange(new ListChangeEvent(allOptions, null));
+
+        }
+    }
+
+    // TODO implement a page to organize options into (exclusive) option groups
+    class GroupOptionsPage extends WizardPage {
+
+        public GroupOptionsPage() {
+            super("Group Options Page"); //$NON-NLS-1$
+        }
+
+        @Override
+        public void createControl(Composite parent) {
+            // TODO Auto-generated method stub
+        }
+
+    }
+
+    private static class OptionLabelProvider extends ObservableMapLabelProvider{
+        public OptionLabelProvider(IObservableMap[] attributeMaps) {
+            super(attributeMaps);
+        }
+
+        @Override
+        public String getText(Object element) {
+            Objects.requireNonNull(element);
+            Option option = (Option) element;
+
+            String longOption = null;
+            String longName = option.getLongName();
+            if (longName != null && !longName.isEmpty()) {
+                longOption = "--" + longName; //$NON-NLS-N$
+            }
+
+            String shortOption = null;
+            String shortName = option.getShortName();
+            if (shortName != null && !shortName.isEmpty()) {
+                shortOption = "-" + shortName; //$NON-NLS-N$
+            }
+
+            String result = null;
+
+            // FIXME does this need to be internationalized?
+            if (longOption != null && shortOption != null) {
+                result = longOption + ", " + shortOption;
+            } else if (longOption != null) {
+                result = longOption;
+            } else if (shortOption != null) {
+                result = shortOption;
+            } else {
+                result = "[unknown]";
+            }
+
+            return result;
+        }
+    }
+}
--- a/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/messages.properties	Fri Feb 21 15:59:02 2014 -0500
+++ b/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/messages.properties	Thu Feb 27 15:40:52 2014 -0500
@@ -8,6 +8,7 @@
 CommandEditPage_cliCheckTitle=CLI
 CommandEditPage_commandDetailsSectionTitle=Command details
 CommandEditPage_descriptionLabel=Description:
+CommandEditPage_editOptions=Edit Options
 CommandEditPage_environmentsTitle=Environments
 CommandEditPage_nameLabel=Name:
 CommandEditPage_removeButton=Remove
@@ -35,6 +36,15 @@
 NewNameDialog_message=This is the command users will use to trigger your plugin
 NewNameDialog_nameLabel=Name:
 NewNameDialog_title=Enter the name for the new command
+OptionsWizard_createOptionsPageAddOption=Add
+OptionsWizard_createOptionsPageArgumentLabel=Argument:
+OptionsWizard_createOptionsPageDescription=Create and edit options
+OptionsWizard_createOptionsPageDescriptionLabel=Description:
+OptionsWizard_createOptionsPageLongNameLabel=Long Name:
+OptionsWizard_createOptionsPageRemoveOption=Remove
+OptionsWizard_createOptionsPageRequiredLabel=Required:
+OptionsWizard_createOptionsPageShortNameLabel=Short Name:
+OptionsWizard_createOptionsPageTitle=Create Options
 OverviewPage_bundlesAndExtensionsOverviewForm = <form><p>Plugins provide new commands or extend existing ones.</p><li><a href=\"{0}\">Commands</a>: declares new commands provided by this plugin</li><li><a href=\"{1}\">Extensions</a>: declares which commands provided by other plugins or thermostat itself should be extended</li></form>
 OverviewPage_commandsAndExtensionSectionTitle=Commands and Extensions
 OverviewPage_formTitle=Overview: {0}
--- a/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/model/Group.java	Fri Feb 21 15:59:02 2014 -0500
+++ b/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/model/Group.java	Thu Feb 27 15:40:52 2014 -0500
@@ -8,6 +8,18 @@
     private boolean required;
     private List<Option> options = new ArrayList<>();
 
+    public Group() {
+        // empty default constructor
+    }
+
+    public Group(Group other) {
+        this.required = other.required;
+        this.options = new ArrayList<>(other.options.size());
+        for (Option opt : other.options) {
+            this.options.add(new Option(opt));
+        }
+    }
+
     public boolean getRequired() {
         return required;
     }
--- a/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/model/ModelObject.java	Fri Feb 21 15:59:02 2014 -0500
+++ b/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/model/ModelObject.java	Thu Feb 27 15:40:52 2014 -0500
@@ -3,7 +3,7 @@
 import java.beans.PropertyChangeListener;
 import java.beans.PropertyChangeSupport;
 
-public class ModelObject {
+public abstract class ModelObject {
 
     private PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
 
--- a/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/model/Option.java	Fri Feb 21 15:59:02 2014 -0500
+++ b/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/model/Option.java	Thu Feb 27 15:40:52 2014 -0500
@@ -12,7 +12,7 @@
         "argument",
         "required",
         "description"})
-public class Option {
+public class Option extends ModelObject {
 
     private String longName;
     private String shortName;
@@ -20,13 +20,25 @@
     private boolean required;
     private String description;
 
+    public Option() {
+        // empty no-arg constructor
+    }
+
+    public Option(Option other) {
+        this.longName = other.longName;
+        this.shortName = other.shortName;
+        this.argument = other.argument;
+        this.required = other.required;
+        this.description = other.description;
+    }
+
     @XmlElement(name="long")
     public String getLongName() {
         return longName;
     }
 
-    public void setLongName(String name) {
-        this.longName = name;
+    public void setLongName(String longName) {
+        firePropertyChange("longName", this.longName, this.longName = longName);
     }
 
     @XmlElement(name="short")
@@ -35,7 +47,7 @@
     }
 
     public void setShortName(String shortName) {
-        this.shortName = shortName;
+        firePropertyChange("shortName", this.shortName, this.shortName = shortName);
     }
 
     @XmlElement(name="argument")
@@ -44,7 +56,7 @@
     }
 
     public void setArgument(String argument) {
-        this.argument = argument;
+        firePropertyChange("argument", this.argument, this.argument = argument);
     }
 
     @XmlElement(name="required")
@@ -53,7 +65,7 @@
     }
 
     public void setRequired(boolean required) {
-        this.required = required;
+        firePropertyChange("required", this.required, this.required = required);
     }
 
     @XmlElement(name="description")
@@ -62,7 +74,6 @@
     }
 
     public void setDescription(String description) {
-        this.description = description;
+        firePropertyChange("description", this.description, this.description = description);
     }
-
 }
--- a/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/model/Options.java	Fri Feb 21 15:59:02 2014 -0500
+++ b/com.redhat.thermostat.tools.eclipse.plugin/src/com/redhat/thermostat/tools/eclipse/plugin/model/Options.java	Thu Feb 27 15:40:52 2014 -0500
@@ -10,6 +10,23 @@
     private Group group;
     private List<Option> options = new ArrayList<>();
 
+    public Options() {
+        // empty no-arg constructor
+    }
+
+    public Options(Options other) {
+        if (other.group != null) {
+            this.group = new Group(other.group);
+        }
+
+        if (other.options != null) {
+            this.options = new ArrayList<>(other.options.size());
+            for (Option option : other.options) {
+                this.options.add(new Option(option));
+            }
+        }
+    }
+
     public Group getGroup() {
         return group;
     }