001/*
002 * Copyright (c) 2005-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: welker $'
006 * '$Date: 2010-05-06 05:21:26 +0000 (Thu, 06 May 2010) $' 
007 * '$Revision: 24234 $'
008 * 
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029
030package org.kepler.sms.actors;
031
032import java.awt.Component;
033import java.awt.Dimension;
034import java.awt.Frame;
035import java.awt.event.ActionEvent;
036import java.awt.event.ActionListener;
037import java.util.Iterator;
038import java.util.Vector;
039
040import javax.swing.BorderFactory;
041import javax.swing.Box;
042import javax.swing.BoxLayout;
043import javax.swing.JButton;
044import javax.swing.JComboBox;
045import javax.swing.JDialog;
046import javax.swing.JLabel;
047import javax.swing.JOptionPane;
048import javax.swing.JPanel;
049import javax.swing.JScrollPane;
050import javax.swing.JTable;
051import javax.swing.ScrollPaneConstants;
052import javax.swing.SwingConstants;
053import javax.swing.table.AbstractTableModel;
054
055import ptolemy.actor.IOPort;
056import ptolemy.kernel.util.NamedObj;
057
058public class MappingRefinementDialog extends JDialog {
059
060        /**
061         * Note: Only entities may have ports
062         */
063        public static void showDialog(Frame aFrame, MergeActor mergeActor,
064                        MergeEditorDialog parent) {
065                MappingRefinementDialog d = new MappingRefinementDialog(aFrame,
066                                mergeActor, parent);
067        }
068
069        /**
070     *
071     */
072        protected MappingRefinementDialog(Frame aFrame, MergeActor mergeActor,
073                        MergeEditorDialog parent) {
074                super(aFrame, true);
075                setTitle("Refine Input-Output Mappings");
076
077                _mergeActor = mergeActor;
078                _parent = parent;
079
080                // load all the given mappings for the merge actor
081                _loadMappings();
082
083                JPanel pane = new JPanel();
084                pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));
085                pane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
086
087                pane.add(_createInputPortList());
088                pane.add(Box.createRigidArea(new Dimension(0, 10)));
089                pane.add(_createOutputPortList());
090                pane.add(Box.createRigidArea(new Dimension(0, 10)));
091                pane.add(_createButtons());
092
093                // set up the dialog
094                this.setContentPane(pane);
095                this.setResizable(false);
096                this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
097                this.pack();
098                this.show();
099        }
100
101        /**
102     *
103     */
104        private JPanel _createInputPortList() {
105                JPanel pane = new JPanel();
106                pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));
107
108                // get the list of actors
109                Vector actors = new Vector();
110                Iterator iter = _mergeActor.getActors().iterator();
111                while (iter.hasNext()) {
112                        NamedObj obj = (NamedObj) iter.next();
113                        actors.add(new _ActorWrapper(obj));
114                }
115
116                _cbActors = new JComboBox(actors);
117                _cbActors.setEditable(false);
118                _cbActors.setSelectedIndex(-1);
119                _cbActors.setPreferredSize(new Dimension(250, 20));
120                _cbActors.setMaximumSize(new Dimension(250, 20));
121                _cbActors.addActionListener(new ActionListener() {
122                        public void actionPerformed(ActionEvent e) {
123                                _actorChoiceChanged();
124                        }
125                });
126                pane.add(_labelComponent("Source Actor", _cbActors, 250, 50));
127                pane.add(Box.createRigidArea(new Dimension(0, 5)));
128
129                _cbPorts = new JComboBox();
130                _cbPorts.setEditable(false);
131                _cbPorts.setSelectedIndex(-1);
132                _cbPorts.setPreferredSize(new Dimension(250, 20));
133                _cbPorts.setMaximumSize(new Dimension(250, 20));
134                _cbPorts.addActionListener(new ActionListener() {
135                        public void actionPerformed(ActionEvent e) {
136                                _portChoiceChanged();
137                        }
138                });
139                pane.add(_labelComponent("Source Actor Port", _cbPorts, 250, 50));
140
141                return pane;
142        }
143
144        /**
145     *
146     */
147        private JPanel _createOutputPortList() {
148                JPanel pane = new JPanel();
149                pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));
150
151                _tblPortList = new JTable(_model);
152                _tblPortList.setCellSelectionEnabled(false);
153                _tblPortList.setColumnSelectionAllowed(false);
154                _tblPortList.setRowSelectionAllowed(false);
155                _tblPortList.setShowGrid(false);
156                Iterator<IOPort> iter = _parent.getTargetPorts().iterator();
157                while (iter.hasNext()) {
158                        IOPort p = iter.next();
159                        _model.insertRow(new _IOPortWrapper(p), new Boolean(false));
160                }
161                JScrollPane view = new JScrollPane(_tblPortList,
162                                ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
163                                ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
164                view.setPreferredSize(new Dimension(260, 150));
165                view.setMaximumSize(new Dimension(260, 150));
166                pane.add(view);
167
168                return pane;
169        }
170
171        /**
172     *
173     */
174        private JPanel _createButtons() {
175                JPanel pane = new JPanel();
176                pane.setLayout(new BoxLayout(pane, BoxLayout.X_AXIS));
177                pane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
178
179                _btnOK = new JButton("OK");
180                _btnCancel = new JButton("Cancel");
181
182                pane.add(Box.createHorizontalGlue());
183                pane.add(_btnOK);
184                pane.add(Box.createRigidArea(new Dimension(15, 0)));
185                pane.add(_btnCancel);
186
187                // add listeners to the buttons
188                _btnOK.addActionListener(new ActionListener() {
189                        public void actionPerformed(ActionEvent ev) {
190                                _doOK();
191                        }
192                });
193
194                _btnCancel.addActionListener(new ActionListener() {
195                        public void actionPerformed(ActionEvent ev) {
196                                dispose();
197                        }
198                });
199
200                return pane;
201        }
202
203        public void _actorChoiceChanged() {
204                _ActorWrapper w = (_ActorWrapper) _cbActors.getSelectedItem();
205                if (w == null)
206                        return;
207                NamedObj actor = w.getActor();
208                // clear the old ports
209                _cbPorts.removeAllItems();
210                // populate the port combo box
211                Iterator<IOPort> ports = _mergeActor.getActorPorts(actor).iterator();
212                while (ports.hasNext()) {
213                        IOPort p = ports.next();
214                        _cbPorts.addItem(new _IOPortWrapper(p));
215                }// end while
216        }
217
218        public void _portChoiceChanged() {
219                // get the actor
220                _ActorWrapper aw = (_ActorWrapper) _cbActors.getSelectedItem();
221                // get the new port
222                _IOPortWrapper pw = (_IOPortWrapper) _cbPorts.getSelectedItem();
223                if (aw == null || pw == null)
224                        return;
225                //NamedObj actor = aw.getActor();
226                IOPort port = pw.getPort();
227                Vector<IOPort> targets = _getCurrentTargetPorts(port);
228                for (int i = 0; i < _model.getRowCount(); i++) {
229                        _IOPortWrapper tw = (_IOPortWrapper) _model.getValueAt(i, 0);
230                        IOPort target = tw.getPort();
231                        if (targets.contains(target))
232                                _model.setValueAt(new Boolean(true), i, 1);
233                        else
234                                _model.setValueAt(new Boolean(false), i, 1);
235                }// end for
236        }
237
238        private Vector<IOPort> _getCurrentTargetPorts(IOPort port) {
239                Vector<IOPort> results = new Vector<IOPort>();
240                String actorName = port.getContainer().getName();
241                String portName = port.getName();
242                Iterator<SimpleMergeMapping> maps = _mappings.iterator();
243                while (maps.hasNext()) {
244                        SimpleMergeMapping m = maps.next();
245                        if (actorName.equals(m.getSourceActor())
246                                        && portName.equals(m.getSourceActorPort())) {
247                                String outName = m.getTargetPort();
248                                Iterator<IOPort> ports = _parent.getTargetPorts().iterator();
249                                while (ports.hasNext()) {
250                                        IOPort p = ports.next();
251                                        if (outName.equals(p.getName()))
252                                                results.add(p);
253                                }// end while
254                        }
255                }// end while
256                return results;
257        }
258
259        /**
260         * Given a label string, component, height, and width, creates a new panel
261         * with a label and the component of size height and width. The label is
262         * positioned above the component and is left justified.
263         */
264        private JPanel _labelComponent(String str, Component component, int width,
265                        int height) {
266                // output pane
267                JPanel pane = new JPanel();
268                pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));
269                // pane for label only
270                JPanel labelPane = new JPanel();
271                labelPane.setLayout(new BoxLayout(labelPane, BoxLayout.X_AXIS));
272                labelPane.add(new JLabel(str, SwingConstants.LEFT));
273                labelPane.add(Box.createHorizontalGlue());
274                // add label
275                pane.add(labelPane);
276                // add space
277                pane.add(Box.createRigidArea(new Dimension(0, 5)));
278                // add component
279                pane.add(component);
280                // set sizes
281                pane.setMaximumSize(new Dimension(width, height));
282                pane.setMinimumSize(new Dimension(width, height));
283                pane.setPreferredSize(new Dimension(width, height));
284                // return outer pane
285                return pane;
286        }
287
288        private void _loadMappings() {
289                Iterator<SimpleMergeMapping> iter = _parent.getMappings().iterator();
290                while (iter.hasNext()) {
291                        SimpleMergeMapping mapping = iter.next();
292                        String actor = mapping.getSourceActor();
293                        String actorPort = mapping.getSourceActorPort();
294                        String target = mapping.getTargetPort();
295                        try {
296                                SimpleMergeMapping m = new SimpleMergeMapping(actor, actorPort,
297                                                target);
298                                _mappings.add(m);
299                        } catch (Exception e) {
300                                e.printStackTrace();
301                        }
302                }// end while
303        }
304
305        private void _addMapping(IOPort target) {
306                _ActorWrapper aw = (_ActorWrapper) _cbActors.getSelectedItem();
307                _IOPortWrapper pw = (_IOPortWrapper) _cbPorts.getSelectedItem();
308                if (aw == null || pw == null)
309                        return;
310                NamedObj actor = aw.getActor();
311                IOPort port = pw.getPort();
312                try {
313                        SimpleMergeMapping m = new SimpleMergeMapping(actor.getName(), port
314                                        .getName(), target.getName());
315                        if (!_mappings.contains(m))
316                                _mappings.add(m);
317                } catch (Exception e) {
318                        e.printStackTrace();
319                }
320        }
321
322        private void _removeMapping(IOPort target) {
323                _ActorWrapper aw = (_ActorWrapper) _cbActors.getSelectedItem();
324                _IOPortWrapper pw = (_IOPortWrapper) _cbPorts.getSelectedItem();
325                if (aw == null || pw == null)
326                        return;
327                NamedObj actor = aw.getActor();
328                IOPort port = pw.getPort();
329                try {
330                        SimpleMergeMapping m = new SimpleMergeMapping(actor.getName(), port
331                                        .getName(), target.getName());
332                        _mappings.remove(m);
333                } catch (Exception e) {
334                        e.printStackTrace();
335                }
336        }
337
338        /**
339     *
340     */
341        private void _doOK() {
342                // we don't care about the current one ...
343                // we want to look at all and check that they each have targets
344                Iterator actors = _mergeActor.getActors().iterator();
345                while (actors.hasNext()) {
346                        NamedObj actor = (NamedObj) actors.next();
347                        String actorName = actor.getName();
348                        Iterator<IOPort> ports = _mergeActor.getActorPorts(actor).iterator();
349                        while (ports.hasNext()) {
350                                IOPort port = ports.next();
351                                String portName = port.getName();
352                                // check if well formed
353                                if (!_hasTarget(actorName, portName)) {
354                                        String msg = "Actor '" + actorName
355                                                        + "' is missing an output port target for "
356                                                        + "port '" + portName + "'";
357                                        JOptionPane.showMessageDialog(this, msg, "Message",
358                                                        JOptionPane.ERROR_MESSAGE);
359                                        return;
360                                }
361                        }// end while
362                        if (!_hasAllowableMappings(actor)) {
363                                String msg = "Multiple ports for Actor '" + actorName
364                                                + "' are mapped " + "to the same output port target";
365                                JOptionPane.showMessageDialog(this, msg, "Message",
366                                                JOptionPane.ERROR_MESSAGE);
367                                return;
368                        }
369                }
370                // commit the mappings (prune unused ports, etc.)
371                _doCommit();
372        }
373
374        private void _doCommit() {
375                // copy the parent mappings
376                Vector parentMappings = new Vector();
377                Iterator iter = _parent.getMappings().iterator();
378                while (iter.hasNext())
379                        parentMappings.add(iter.next());
380
381                // keep the same mappings in mergeActor, delete the "old ones"
382                iter = parentMappings.iterator();
383                while (iter.hasNext()) {
384                        try {
385                                SimpleMergeMapping m = (SimpleMergeMapping) iter.next();
386                                SimpleMergeMapping mcurr = _getMapping(m.getSourceActor(), m
387                                                .getSourceActorPort(), m.getTargetPort());
388                                if (mcurr == null)
389                                        _parent.removeMapping(m); // this mapping was removed in the
390                                                                                                // refinement
391                                else
392                                        _mappings.remove(mcurr); // this mapping existed, so remove
393                                                                                                // from current
394                        } catch (Exception e) {
395                                e.printStackTrace();
396                        }
397                }// end while
398
399                // add the rest of the current mappings to mergeActor
400                iter = _mappings.iterator();
401                while (iter.hasNext()) {
402                        SimpleMergeMapping m = (SimpleMergeMapping) iter.next();
403                        _parent.addMapping(m);
404                }
405                dispose();
406        }
407
408        /**
409         * @return A mapping from the local mappings that matches the input
410         */
411        private SimpleMergeMapping _getMapping(String actor, String port,
412                        String target) {
413                Iterator iter = _mappings.iterator();
414                while (iter.hasNext()) {
415                        SimpleMergeMapping m = (SimpleMergeMapping) iter.next();
416                        if (actor.equals(m.getSourceActor())
417                                        && port.equals(m.getSourceActorPort())
418                                        && target.equals(m.getTargetPort()))
419                                return m;
420                }// end while
421                return null;
422        }
423
424        private boolean _hasTarget(String actorName, String portName) {
425                Iterator mappings = _mappings.iterator();
426                boolean found = false;
427                while (mappings.hasNext()) {
428                        SimpleMergeMapping m = (SimpleMergeMapping) mappings.next();
429                        if (actorName.equals(m.getSourceActor())
430                                        && portName.equals(m.getSourceActorPort())) {
431                                found = true;
432                                break;
433                        }
434                }// end while
435                return found;
436        }
437
438        /**
439         * Given one input and one output port, determines whether they can be an
440         * input-output mapping according to the existing mappings. Currently, the
441         * only mapping not allowed is when the an actor has multiple output ports
442         * mapped to the same target port. Note that a mapping is different than a
443         * "match", which is between input ports (e.g., of different source actors).
444         */
445        private boolean _hasAllowableMappings(NamedObj actor) {
446                // get all the output port names for the actor
447                Vector<String> ports = new Vector<String>();
448                Iterator<IOPort> iter = _mergeActor.getActorPorts(actor).iterator();
449                while (iter.hasNext()) {
450                        IOPort p = iter.next();
451                        ports.add(p.getName());
452                }// end while
453
454                // iterator through the target ports, obtaining their mappings
455                iter = _parent.getTargetPorts().iterator();
456                while (iter.hasNext()) {
457                        IOPort target = iter.next();
458                        // store the actor input ports for the target
459                        Vector<String> targetInputs = new Vector<String>();
460                        Iterator<SimpleMergeMapping> mappings = _mappings.iterator();
461                        while (mappings.hasNext()) {
462                                SimpleMergeMapping m = mappings.next();
463                                if (m.getTargetPort().equals(target.getName())
464                                                && m.getSourceActor().equals(actor.getName()))
465                                        targetInputs.add(m.getSourceActorPort());
466                        }
467                        // check if multiple target inputs
468                        if (targetInputs.size() > 1)
469                                return false;
470                }// end while
471
472                return true;
473        }
474
475        private void _printMappings() {
476                Iterator iter = _mappings.iterator();
477                while (iter.hasNext()) {
478                        SimpleMergeMapping m = (SimpleMergeMapping) iter.next();
479                        String actor = m.getSourceActor();
480                        String actorPort = m.getSourceActorPort();
481                        String target = m.getTargetPort();
482                        System.out.println("... mapping: " + actor + ", " + actorPort
483                                        + ", " + target);
484                }
485        }
486
487        /**
488     *
489     */
490        private class _IOPortWrapper {
491                private IOPort _port;
492
493                public _IOPortWrapper(IOPort port) {
494                        _port = port;
495                }
496
497                public IOPort getPort() {
498                        return _port;
499                }
500
501                public String toString() {
502                        if (_port != null)
503                                return _port.getName();
504                        else
505                                return "";
506                }
507        };
508
509        /**
510     *
511     */
512        private class _ActorWrapper {
513                private NamedObj _actor;
514
515                public _ActorWrapper(NamedObj actor) {
516                        _actor = actor;
517                }
518
519                public NamedObj getActor() {
520                        return _actor;
521                }
522
523                public String toString() {
524                        if (_actor != null)
525                                return _actor.getName();
526                        else
527                                return "";
528                }
529        };
530
531        private class _PortListTableModel extends AbstractTableModel {
532                private String[] columnNames = { "Output Port", "Target" };
533                private Vector data = new Vector();
534
535                public int getColumnCount() {
536                        return columnNames.length;
537                }
538
539                public int getRowCount() {
540                        return data.size();
541                }
542
543                public String getColumnName(int col) {
544                        return columnNames[col];
545                }
546
547                public void insertRow(_IOPortWrapper p, Boolean checked) {
548                        Object[] row = new Object[2];
549                        row[0] = p;
550                        row[1] = checked;
551                        addRow(row);
552                }
553
554                public void addRow(Object[] row) {
555                        data.add(row);
556                        fireTableRowsInserted(getRowCount(), getRowCount());
557                }
558
559                public void clearRows() {
560                        data = new Vector();
561                        fireTableDataChanged();
562                }
563
564                public Object getValueAt(int row, int col) {
565                        Object[] obj = (Object[]) data.elementAt(row);
566                        return obj[col];
567                }
568
569                /*
570                 * JTable uses this method to determine the default renderer/ editor for
571                 * each cell. If we didn't implement this method, then the last column
572                 * would contain text ("true"/"false"), rather than a check box.
573                 */
574                public Class getColumnClass(int c) {
575                        if (getColumnName(c).equals(columnNames[0]))
576                                return _IOPortWrapper.class;
577                        else if (getColumnName(c).equals(columnNames[1]))
578                                return Boolean.class;
579                        else
580                                return Object.class;
581                }
582
583                public void setValueAt(Object value, int row, int col) {
584                        Object[] obj = (Object[]) data.elementAt(row);
585                        // update the mappings
586                        if (col == 1) {
587                                _IOPortWrapper w = (_IOPortWrapper) obj[0];
588                                IOPort p = (IOPort) w.getPort();
589                                if (((Boolean) value).booleanValue() == true)
590                                        _addMapping(p);
591                                else if (((Boolean) value).booleanValue() == false)
592                                        _removeMapping(p);
593                        }
594                        // change the value
595                        obj[col] = value;
596                        fireTableCellUpdated(row, col);
597                }
598
599                public boolean rowSelected(int row) {
600                        Object[] obj = (Object[]) data.elementAt(row);
601                        Boolean sel = (Boolean) obj[1];
602                        if (sel.equals(Boolean.TRUE))
603                                return true;
604                        return false;
605                }
606
607                public boolean isCellEditable(int row, int col) {
608                        if (getColumnName(col).equals(columnNames[1]))
609                                return true;
610                        return false;
611                }
612
613        };
614
615        /** Private members */
616        private MergeActor _mergeActor;
617        private JButton _btnOK;
618        private JButton _btnCancel;
619        private JComboBox _cbActors;
620        private JComboBox _cbPorts;
621        private JTable _tblPortList;
622        private _PortListTableModel _model = new _PortListTableModel();
623        private Vector<SimpleMergeMapping> _mappings = new Vector<SimpleMergeMapping>();
624        private MergeEditorDialog _parent;
625
626}