Becoming a D2 developer on the job

Step 5: Create the appropriate views

Create the main view

In the org.decisiondeck.mytest.view package, the file SampleMenuItem.java describe the main view associated to our plug-in.

Let us modify the createComponent function, so that it create a JTabbedPane with two panels, one for the weights (i.e. the parameters of the method) and the other for the scores (i.e. the "results" of the method).

    protected Component createComponent(UserType userType)
    {
        JPanel panel = new JPanel();
        panel.setLayout(new GridBagLayout());
        panel.setBackground(Color.WHITE);    
        
        JTabbedPane pane = new JTabbedPane();
        
        MyBeanPanel myBeanPanel = new MyBeanPanel(userType);
        WsResultPanel wsResultPanel = new WsResultPanel(userType);
        
        pane.addTab("Weights", myBeanPanel);
        pane.addTab("Scores", wsResultPanel);
        
        GridBagConstraints c = new GridBagConstraints();
        c.fill = GridBagConstraints.BOTH;
        c.weightx = 1.0;
        c.weighty = 1.0;
        
        panel.add(pane, c);
        
        return panel;
    }

It is necessary to add the following imports

import javax.swing.JTabbedPane;
import org.decisiondeck.mytest.view.MyBeanPanel;
import org.decisiondeck.mytest.view.WsResultPanel;

You may also want to modify the getLabel function that determines the interface label for our plug-in. It is a good idea to use the Messages system, that allows you to gather the strings in a config file: src/main/resources/org.decisiondeck.mytest/messages.properties. Therefore, you may redefine the getLabel function as follows, and then modify the appropriate string in the messages.properties file.

    public String getLabel()
    {
        return MytestMessages.getString("label.text");
    }

Now, Eclipse should warn you that both MyBeanPanel and WsResultPanel are not defined! These are our following tasks...

Each view, either for the parameters or for the results, will build a table. Each of these tables will be modeled by a table model. Therefore, for the parameters and for the results, we will have 3 files

  1. the view,
  2. the table,
  3. the table model.

Let's start with the parameters.

Create the parameters view

In the org.decisiondeck.mytest.view package, create a new class, named MyBeanPanel and extending MultiDeciderTabPanel. The latter abstract class is designed to cope with several DM and to allows either the automatic selection of relevant DM or the browsing among them. Its behavior depends on your log-in status, either DM or coordinator.

You need then to implement a constructor: this constructor needs to add itself as a listener to any change in the list of criteria. The getTabComponent function is responsible for creating the content of the panel; here a MyBeanTable still to define.

package org.decisiondeck.mytest.view;

import org.decisiondeck.computation.view.MultiDeciderTabPanel;
import org.decisiondeck.model.User;
import org.decisiondeck.view.common.table.AbstractEvalTable;

import java.beans.PropertyChangeEvent;
import org.decisiondeck.model.UserType;
import org.decisiondeck.service.CriteriaService;
import org.decisiondeck.mytest.view.MyBeanTable;

public class MyBeanPanel extends MultiDeciderTabPanel {

        private static final long serialVersionUID = 1;
        
        public MyBeanPanel(UserType userType)
        {
                super(userType);
                CriteriaService.getAllCriterias().addCriteriaListChangedListener(this);
                
        }
        
        @Override
        protected String canShowPanel() {
                if (CriteriaService.getAllCriterias().size()==0)
                {
                        return "No criterion defined yet";
                }
                return null;
        }

        @Override
        protected AbstractEvalTable getTabComponent(User u) {
                final AbstractEvalTable table = new MyBeanTable(u);
                return table;
        }

        @Override
        protected String getTableTitle() {
                return "Parameters";
        }
        
        public void propertyChange(PropertyChangeEvent evt)
        {
                super.propertyChange(evt);
                reloadCurrentComponent();
        }
}

Create the parameters table

In the same package, create a new class, named MyBeanTable, extending AbstractEvalTable and implementing IReloadable.

The definition of the table is rather simple: declare the selected table model, set some table properties (as sorting capability), and link the reload function of the table to the reload function of the table model. That's it !

package org.decisiondeck.mytest.view;

import org.decisiondeck.computation.view.IReloadable;
import org.decisiondeck.view.common.table.AbstractEvalTable;

import org.decisiondeck.model.User;
import org.decisiondeck.util.Messages;
import org.decisiondeck.mytest.view.MyBeanTableModel;

public class MyBeanTable extends AbstractEvalTable implements IReloadable {
        
        private static final long serialVersionUID = 1L;
        public static final String TABLE_NAME = Messages.getInstance(Messages.DEFAULT_BUNDLE_NAME, MyBeanTable.class).getString("EvalUI.EvaluatorTable.evaluators");
        
        public MyBeanTable(User user)
        {
                super(new MyBeanTableModel(user));
                setSortingCapability();
                setBlankLineVisible(false);
                setColumnHeader();
        }

        @Override
        protected void createPopupMenu()
        {
                
        }
        
        public void reloadData() {
                ((MyBeanTableModel) getModel()).reloadData();
        }
}

Create the parameters table model

In the same package, create a new class, named MyBeanTableModel, extending AbstractEvalTableModel.

This task is little bit longer, since we have to define

  • the basic functions of the table: in our case, dummy add, remove, ...
  • the column name as well as the column and row counts
  • the value at each position in the table, and its class

We have also to add change listener, that will reload the table is some relevant information changes.

package org.decisiondeck.mytest.view;

import org.decisiondeck.view.common.table.AbstractEvalTableModel;

import org.decisiondeck.model.User;
import org.decisiondeck.model.UserType;
import java.util.ArrayList;
import java.util.List;
import org.decisiondeck.service.CriteriaService;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import org.decisiondeck.mytest.service.MyBeanService;
import org.decisiondeck.util.ObjectUtils;
import org.decisiondeck.view.kernel.Workbench;
import org.decisiondeck.model.evaluation.ICriteria;
import org.decisiondeck.mytest.model.MyBean;

public class MyBeanTableModel extends AbstractEvalTableModel {

        private static final long serialVersionUID = 1L;
        
        private boolean loading;
        private boolean needReload;
        private User user;
        private List entities = new ArrayList();
        
        public MyBeanTableModel(User user)
        {
                this.user = user;
                needReload = true;
                reloadData();
                CriteriaService.getAllCriterias().addCriteriaListChangedListener(new PropertyChangeListener()
                        {
                                public void propertyChange(PropertyChangeEvent evt)
                                {
                                        needReload=true;
                                }
                        });
        }
        
        public void reloadData()
        {
                if (needReload && !loading)
                {
                        loading = true;
                        CriteriaService.reload();
                        entities = MyBeanService.getInstance().getAll(user);
                        fireTableDataChanged();
                        loading = false;
                        needReload = false;
                }
        }
        
        
        
        @Override
        public void addRow(int col, Object value) {
                // nothing
        }

        @Override
        public void copyRow(int row) {
                // nothing
        }

        @Override
        public void deleteRow(int row) {
                // nothing
        }

        
        @Override
        public String getColumnName(int col) {
                switch(col)
                {
                case 0:
                        return "";
                case 1:
                        return "Weight";
                }
                return null;
        }

        @Override
        public boolean isCellEditable(int row, int col) {
                if (col!=0 && !ObjectUtils.areEqual(UserType.COORDINATOR,Workbench.getKernel().getUserType()))
                {
                        return true;
                }
                return false;
        }

        public int getColumnCount() {
                return 2;
        }

        public int getRowCount() {
                return CriteriaService.getAllCriterias().size();
        }

        public Object getValueAt(int rowIndex, int colIndex) {
                ICriteria criterion = getCriteriaAt(rowIndex);
                MyBean bean = getMyBean(criterion);
                switch (colIndex)
                {
                case 0:
                        return criterion.getName();
                case 1:
                        return bean==null ?  null : bean.getWeight();
                }
                return null;
        }

        @SuppressWarnings("unchecked")
        public void setValueAt(Object aValue, int rowIndex, int colIndex)
        {
                ICriteria criterion = getCriteriaAt(rowIndex);
                MyBean bean = getMyBean(criterion);
                
                if (bean==null)
                {
                        bean = MyBeanService.getInstance().createEntity(user, false);
                        bean.setCriterion(criterion);
                        entities.add(bean);
                }
                
                switch (colIndex)
                {
                case 1:
                        bean.setWeight((Float) aValue);
                        break;
                }
                MyBeanService.getInstance().update(bean);
                fireTableDataChanged();
        }
        
        private ICriteria getCriteriaAt(int rowIndex)
        {
                return (CriteriaService.getAllCriterias().get(rowIndex));
        }
        
        private MyBean getMyBean(ICriteria criterion)
        {
                for (Object o: entities)
                {
                        MyBean bean = (MyBean) o;
                        if (ObjectUtils.areEqual(criterion, bean.getCriterion()))
                        {
                                return bean;
                        }
                }
                return null;
        }
        
        @Override
        public Class<?> getColumnClass(int colIndex)
        {
                if (colIndex==0)
                {
                        return String.class;
                }
                return Float.class;
        }
}

Create the results view

Creating the results view is very similar to creating the parameters one... Create a new class, named WsResultPanel and extending MultiDeciderTabPanel.

package org.decisiondeck.mytest.view;

import org.decisiondeck.computation.view.MultiDeciderTabPanel;
import org.decisiondeck.model.User;
import org.decisiondeck.view.common.table.AbstractEvalTable;

import org.decisiondeck.model.UserType;
import org.decisiondeck.service.CriteriaService;
import org.decisiondeck.service.AlternativeService;
import org.decisiondeck.service.EvaluatorService;
import org.decisiondeck.mytest.view.WsResultTable;
import java.beans.PropertyChangeEvent;

public class WsResultPanel extends MultiDeciderTabPanel {

        private static final long serialVersionUID = 1L;
        
        public WsResultPanel(UserType userType)
        {
                super(userType);
                CriteriaService.getAllCriterias().addCriteriaListChangedListener(this);
                AlternativeService.getAllMultiCritObjects().addMCObjectListChangedListener(this);
        }
        
        @Override
        protected String canShowPanel() {
                if (CriteriaService.getAllCriterias().size()==0)
                {
                        return "No criterion defined yet";
                }
                if (EvaluatorService.getEvaluators().size()==0)
                {
                        return "No evaluator defined yet";
                }
                if (AlternativeService.getAlternatives().size()==0)
                {
                        return "No alternative defined yet";
                }
                return null;
        }

        @Override
        protected AbstractEvalTable getTabComponent(User u) {
                final AbstractEvalTable table = new WsResultTable(u);
                return table;
        }

        @Override
        protected String getTableTitle() {
                return "Weighted Sum Results";
        }
        
        public void propertyChange(PropertyChangeEvent evt)
        {
                super.propertyChange(evt);
                reloadCurrentComponent();
        }
        
}

Create the result table

Create a new class, named WsResultTable, extending AbstractEvalTable and implementing IReloadable.

package org.decisiondeck.mytest.view;

import org.decisiondeck.computation.view.IReloadable;
import org.decisiondeck.view.common.table.AbstractEvalTable;

import org.decisiondeck.model.User;
import org.decisiondeck.mytest.view.WsResultTableModel;

public class WsResultTable extends AbstractEvalTable implements IReloadable {

        private static final long serialVersionUID = 1L;
        
        public WsResultTable(User user)
        {
                super (new WsResultTableModel(user));
                setSortingCapability();
                setBlankLineVisible(false);
                setColumnHeader();
        }
        
        @Override
        protected void createPopupMenu()
        {
                
        }
        
        public void reloadData() 
        {
                ((WsResultTableModel) getModel()).recomputeAll();
        }
}

Create the result table model

Create a new class, named WsResultTableModel and extending AbstractEvalTableModel.

package org.decisiondeck.mytest.view;

import org.decisiondeck.view.common.table.AbstractEvalTableModel;

import org.decisiondeck.model.User;
import org.decisiondeck.model.AbstractEntity;
import org.decisiondeck.service.CriteriaService;
import org.decisiondeck.service.AlternativeService;
import org.decisiondeck.mytest.service.MyBeanService;
import org.decisiondeck.mytest.computation.WeightedSum;
import org.decisiondeck.computation.service.IEntityChangeListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

public class WsResultTableModel extends AbstractEvalTableModel {

        private static final long serialVersionUID = 1L;
        
        private User user;
        
        public WsResultTableModel(User user)
        {
                this.user = user;
                CriteriaService.loadIfNecessary();
                recomputeAll();
                CriteriaService.getAllCriterias().addCriteriaListChangedListener(new PropertyChangeListener()
                                {
                                        public void propertyChange(PropertyChangeEvent evt)
                                        {
                                                recomputeAll();
                                        }
                                });
                AlternativeService.getAllMultiCritObjects().addMCObjectListChangedListener(new PropertyChangeListener()
                                {
                                        public void propertyChange(PropertyChangeEvent evt)
                                        {
                                                recomputeAll();
                                        }
                                });
                MyBeanService.getInstance().addEntityListener(new IEntityChangeListener()
                                {
                                        public void entityChanged(AbstractEntity cat)
                                        {
                                                fireTableDataChanged();
                                        }
                                });
        }
        
        @Override
        public void addRow(int col, Object value) {
                // nothing
        }

        @Override
        public void copyRow(int row) {
                // nothing
        }

        @Override
        public void deleteRow(int row) {
                // nothing
        }

        @Override
        public String getColumnName(int col) {
                switch (col)
                {
                case 0:
                        return "";
                case 1:
                        return "Score";
                }
                return null;
        }

        @Override
        public boolean isCellEditable(int row, int col) {
                return false;
        }

        public int getColumnCount() {
                return 2;
        }

        public int getRowCount() {
                return AlternativeService.getAlternatives().size();
        }

        public Object getValueAt(int row, int col) {
                switch(col)
                {
                case 0:
                        return AlternativeService.getAlternatives().get(row).getAbbreviation();
                case 1:
                        WeightedSum computer = new WeightedSum();
                        computer.setAlternative1(AlternativeService.getAlternatives().get(row));
                        return computer.compute(user);
                }
                return null;
        }
        
        public void recomputeAll()
        {
                for (int row=0; row<getRowCount(); row++)
                {
                        for (int col=0; col<getColumnCount(); col++)
                        {
                                setValueAt(getValueAt(row,col),row,col);
                        }
                }
        }
        
        @Override
        public Class<?> getColumnClass(int col)
        {
                if (col==0)
                {
                        return String.class;
                }
                return Float.class;
        }
}

Next Step

Congratulations !!! Now, you may run your new plug-in !