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.Color;
033import java.awt.Component;
034import java.awt.Dimension;
035import java.awt.Frame;
036import java.awt.event.ActionEvent;
037import java.awt.event.ActionListener;
038import java.awt.event.KeyEvent;
039import java.awt.event.MouseAdapter;
040import java.awt.event.MouseEvent;
041import java.util.Enumeration;
042import java.util.Iterator;
043import java.util.Vector;
044
045import javax.swing.BorderFactory;
046import javax.swing.Box;
047import javax.swing.BoxLayout;
048import javax.swing.DefaultCellEditor;
049import javax.swing.JButton;
050import javax.swing.JComboBox;
051import javax.swing.JDialog;
052import javax.swing.JLabel;
053import javax.swing.JOptionPane;
054import javax.swing.JPanel;
055import javax.swing.JScrollPane;
056import javax.swing.JTable;
057import javax.swing.JTree;
058import javax.swing.ListSelectionModel;
059import javax.swing.SwingConstants;
060import javax.swing.border.TitledBorder;
061import javax.swing.event.TableModelEvent;
062import javax.swing.table.AbstractTableModel;
063import javax.swing.table.TableColumn;
064import javax.swing.tree.DefaultTreeModel;
065import javax.swing.tree.TreeModel;
066import javax.swing.tree.TreeNode;
067import javax.swing.tree.TreePath;
068
069import org.kepler.sms.KeplerVirtualIOPort;
070import org.kepler.sms.SemanticType;
071import org.kepler.sms.gui.SemanticTypeTable;
072
073import ptolemy.actor.IOPort;
074import ptolemy.actor.TypedIOPort;
075import ptolemy.actor.TypedIORelation;
076import ptolemy.kernel.util.NamedObj;
077
078/*
079 FIXME: 
080
081 - the conversion table: 
082 1). read in while loading mappings (both MergeEditor and MappingRefinement)
083 2). store when changed in editor
084 3). make sure stored as output
085
086 - go back to _computeMerge ... 
087 */
088
089/**
090 * 
091 * @author Shawn Bowers
092 */
093public class MergeEditorDialog extends JDialog {
094
095        /**
096         * Constructor
097         */
098        public MergeEditorDialog(Frame owner, MergeActor mergeActor) {
099                super(owner);
100                _owner = owner;
101                _mergeActor = mergeActor;
102
103                _loadTargetPorts();
104                _loadMappings();
105
106                this.setTitle("Compute Merge Results");
107                this.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
108
109                JPanel pane = new JPanel();
110                pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));
111                pane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
112
113                // add the tree part
114                pane.add(_createPortTrees());
115                pane.add(Box.createRigidArea(new Dimension(0, 10)));
116
117                // add the conversion part
118                pane.add(_createConversionTable());
119                pane.add(Box.createRigidArea(new Dimension(0, 10)));
120
121                // add the semantic type table
122                pane.add(_createSemTypeTable(owner));
123                // pane.setBorder(_createTitledBorder("Output Port Semantic Type"));
124                pane.add(Box.createRigidArea(new Dimension(0, 10)));
125
126                pane.add(_createButtonPane());
127
128                // add the pane
129                setContentPane(pane);
130                // set size
131                this.setSize(500, 725);
132                this.setResizable(false);
133        }
134
135        /**
136         * Create and initialize the main buttons
137         */
138        private JPanel _createButtonPane() {
139                JPanel pane = new JPanel();
140                pane.setLayout(new BoxLayout(pane, BoxLayout.LINE_AXIS));
141                pane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
142                pane.add(Box.createHorizontalGlue());
143                pane.add(_commitBtn);
144                pane.add(Box.createRigidArea(new Dimension(10, 0)));
145                pane.add(_closeBtn);
146
147                // init buttons
148                _commitBtn.setMnemonic(KeyEvent.VK_C);
149                _commitBtn.setActionCommand("commit");
150                _commitBtn
151                                .setToolTipText("Save changes to annotations and close editor");
152                _commitBtn.addActionListener(_buttonListener);
153                _closeBtn.setActionCommand("cancel");
154                _closeBtn.setToolTipText("Close editor without saving");
155                _closeBtn.addActionListener(_buttonListener);
156
157                return pane;
158        }
159
160        /**
161         * Create and initialize the input and output port trees.
162         */
163        private JPanel _createPortTrees() {
164                // outer pane
165                JPanel pane = new JPanel();
166                pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));
167                pane.setBorder(_createTitledBorder("Input-Output Mapping"));
168
169                // tree pane
170                JPanel treePane = new JPanel();
171                treePane.setLayout(new BoxLayout(treePane, BoxLayout.X_AXIS));
172
173                // create the input tree
174                _inputPortTree = new JTree(_createInputModel());
175                _inputPortTree.setRootVisible(false);
176                _inputPortTree.setEditable(false);
177                _inputPortTree.setExpandsSelectedPaths(true);
178                // listen for mouse clicks
179                _inputPortTree.addMouseListener(new MouseAdapter() {
180                        public void mousePressed(MouseEvent e) {
181                                int selRow = _inputPortTree.getRowForLocation(e.getX(), e
182                                                .getY());
183                                if (selRow == -1)
184                                        return;
185                                TreePath selPath = _inputPortTree.getPathForLocation(e.getX(),
186                                                e.getY());
187                                TreeNode node = (TreeNode) selPath.getLastPathComponent();
188                                if (node == null || e.getClickCount() != 1)
189                                        return;
190                                TreePath[] paths = { selPath };
191                                _inputPortTree.setSelectionPaths(paths);
192                                _outputPortTree.setSelectionPaths(null);
193                                _conversionTbl.setModel(_emptyConversionModel);
194                                // set the model to an empty model instance
195                                if (node instanceof _PortTreeNode) {
196                                        _inputSingleClick((_PortTreeNode) node);
197                                        _semTypeTable.setEnabled(false);
198                                        _semTypeTable
199                                                        .setAnnotationObjectVisible(_emptyAnnotationPort);
200                                        _loadNewConversionTable((IOPort) ((_PortTreeNode) node)
201                                                        .getPort());
202                                }
203
204                        }
205                });
206                JScrollPane inputTreeView = new JScrollPane(_inputPortTree);
207                JPanel inputTreePane = _labelComponent("merge input ports ",
208                                inputTreeView, 225, 175);
209
210                // create the output tree
211                _outputPortTree = new JTree(_createOutputModel());
212                _outputPortTree.setRootVisible(false);
213                _outputPortTree.setEditable(false);
214                _inputPortTree.setExpandsSelectedPaths(true);
215                // listen for mouse clicks
216                _outputPortTree.addMouseListener(new MouseAdapter() {
217                        public void mousePressed(MouseEvent e) {
218                                int selRow = _outputPortTree.getRowForLocation(e.getX(), e
219                                                .getY());
220                                if (selRow == -1)
221                                        return;
222                                TreePath selPath = _outputPortTree.getPathForLocation(e.getX(),
223                                                e.getY());
224                                TreeNode node = (TreeNode) selPath.getLastPathComponent();
225                                if (node == null || e.getClickCount() != 1)
226                                        return;
227                                TreePath[] paths = { selPath };
228                                _outputPortTree.setSelectionPaths(paths);
229                                _inputPortTree.setSelectionPaths(null);
230                                _conversionTbl.setModel(_emptyConversionModel);
231                                if (node instanceof _PortTreeNode) {
232                                        _outputSingleClick((_PortTreeNode) node);
233                                        _semTypeTable.setEnabled(true);
234                                        _semTypeTable
235                                                        .setAnnotationObjectVisible((IOPort) ((_PortTreeNode) node)
236                                                                        .getPort());
237                                }
238                        }
239                });
240                JScrollPane outputTreeView = new JScrollPane(_outputPortTree);
241                JPanel outputTreePane = _labelComponent("merge output ports ",
242                                outputTreeView, 225, 175);
243
244                // add input and output tree to treepane
245                treePane.add(inputTreePane);
246                treePane.add(Box.createRigidArea(new Dimension(10, 0)));
247                treePane.add(outputTreePane);
248
249                // button pane
250                JPanel buttonPane = new JPanel();
251                buttonPane.setLayout(new BoxLayout(buttonPane, BoxLayout.X_AXIS));
252                buttonPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
253
254                // create the buttons
255                JButton btnEdit = new JButton("Refine Mappings");
256                btnEdit.setActionCommand("refine");
257                btnEdit.addActionListener(_buttonListener);
258                btnEdit.setToolTipText("Add or remove input-output port mappings");
259                JButton btnPrune = new JButton("Prune Output Ports");
260                btnPrune.setActionCommand("prune");
261                btnPrune.addActionListener(_buttonListener);
262                btnPrune.setToolTipText("Remove unused output ports");
263                JButton btnAdd = new JButton("Add Output Port");
264                btnAdd.setActionCommand("add");
265                btnAdd.addActionListener(_buttonListener);
266                btnAdd.setToolTipText("Add a new output port");
267
268                // add buttons
269                buttonPane.add(Box.createHorizontalGlue());
270                buttonPane.add(btnEdit);
271                buttonPane.add(Box.createRigidArea(new Dimension(5, 0)));
272                buttonPane.add(btnPrune);
273                buttonPane.add(Box.createRigidArea(new Dimension(5, 0)));
274                buttonPane.add(btnAdd);
275
276                // add intermediate panes to outer pane
277                pane.add(treePane);
278                pane.add(Box.createRigidArea(new Dimension(0, 5)));
279                pane.add(buttonPane);
280                // return it
281                return pane;
282        }
283
284        /*
285         * Create and initialize the conversion function combo box
286         */
287        private JPanel _createConversionTable() {
288                // outer pane
289                JPanel pane = new JPanel();
290                pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));
291                pane.setBorder(_createTitledBorder("Conversion Functions"));
292
293                // set up the table
294                _conversionTbl = new JTable(_emptyConversionModel);
295                _conversionTbl
296                                .setPreferredScrollableViewportSize(new Dimension(200, 80));
297                // init
298                _conversionTbl.setColumnSelectionAllowed(false);
299                _conversionTbl.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
300
301                // init the combo box view pane
302                JPanel view = new JPanel();
303                view.setLayout(new BoxLayout(view, BoxLayout.Y_AXIS));
304                view.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); // top,left,bottom,right
305                view.add(new JScrollPane(_conversionTbl));
306                // add to the pane
307                pane.add(view);
308                // return
309                return pane;
310        }
311
312        /**
313         * Create and initialize the output semantic type table
314         */
315        private JPanel _createSemTypeTable(Frame owner) {
316                // outer pane
317                JPanel pane = new JPanel();
318                pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));
319                pane.setBorder(_createTitledBorder("Output Port Semantic Type"));
320                // init the table
321                _semTypeTable = new SemanticTypeTable(owner, false); // don't show the
322                                                                                                                                // label
323                _semTypeTable.setEnabled(false);
324                _initSemTypeTable();
325
326                // add to the pane
327                pane.add(_semTypeTable);
328                // return
329                return pane;
330        }
331
332        /**
333     *
334     */
335        private void _setFunctionCombo() {
336                TableColumn functionColumn = _conversionTbl.getColumnModel().getColumn(
337                                0);
338                JComboBox comboBox = new JComboBox();
339                comboBox.setEditable(false);
340                comboBox.addItem("");
341                comboBox.addItem("gramsToKilograms");
342                comboBox.addItem("inchesToMeters");
343                functionColumn.setCellEditor(new DefaultCellEditor(comboBox));
344        }
345
346        /**
347     * 
348     */
349        private void _loadNewConversionTable(IOPort port) {
350                _ConversionFunctionTableModel model = new _ConversionFunctionTableModel();
351                _conversionTbl.setModel(model);
352
353                // set up where the column draws functions from
354                _setFunctionCombo();
355
356                // iteration over mapping targets ...
357                Iterator<SimpleMergeMapping> iter = _mergeActor.getInputPortMappings(port);
358                while (iter.hasNext()) {
359                        SimpleMergeMapping m = iter.next();
360                        String function = m.getConversion();
361                        if (function == null)
362                                function = "";
363                        model.insertRow(function, m.getTargetPort());
364                }
365        }
366
367        /**
368     *
369     */
370        private void _initSemTypeTable() {
371                Vector oldports = _semTypeTable.getAnnotationObjects();
372                Vector<IOPort> currports = new Vector<IOPort>();
373                Iterator<IOPort> iter = getTargetPorts().iterator();
374                
375                while (iter.hasNext()) {
376                        IOPort p = iter.next();
377                        currports.add(p);
378                        if (!oldports.contains(p))
379                                _semTypeTable.addAnnotationObject(p);
380                }
381                
382                Iterator oldPortsIter = oldports.iterator();
383                while (iter.hasNext()) {
384                        IOPort p = (IOPort) oldPortsIter.next();
385                        if (!currports.contains(p))
386                                _semTypeTable.removeAnnotationObject(p);
387                }
388                _semTypeTable.addAnnotationObject(_emptyAnnotationPort);
389        }
390
391        /**
392         * Create a titled border with a blue font
393         */
394        private TitledBorder _createTitledBorder(String title) {
395                TitledBorder border = BorderFactory.createTitledBorder(" " + title
396                                + " ");
397                border.setTitleColor(new Color(0, 10, 230));
398                return border;
399        }
400
401        /**
402         * Call back for input port tree
403         */
404        private void _inputSingleClick(_PortTreeNode portNode) {
405                // get the output mappings for the port
406                IOPort p = (IOPort) portNode.getPort();
407                _selectPortNodes(_outputPortTree, getTargetPorts(p));
408        }
409
410        /**
411         * Call back for output port tree
412         */
413        private void _outputSingleClick(_PortTreeNode portNode) {
414                IOPort outputPort = (IOPort) portNode.getPort();
415                Vector<IOPort> inputs = new Vector<IOPort>();
416                Iterator iter = _mergeActor.connectedPortList().iterator();
417                while (iter.hasNext()) {
418                        IOPort inPort = (IOPort) iter.next();
419                        Iterator<IOPort> targets = getTargetPorts(inPort).iterator();
420                        while (targets.hasNext()) {
421                                IOPort target = targets.next();
422                                if (target.equals(outputPort))
423                                        inputs.add(inPort);
424                        }// end while
425                }// end while
426                _selectPortNodes(_inputPortTree, inputs);
427        }
428
429        /**
430         * Selects the given set of ports in the tree.
431         */
432        private void _selectPortNodes(JTree tree, Vector<IOPort> ports) {
433                Vector<TreePath> treePaths = new Vector<TreePath>();
434                Iterator<IOPort> iter = ports.iterator();
435                while (iter.hasNext()) {
436                        TreePath path = _getTreePath(iter.next(), tree);
437                        if (path != null && !treePaths.contains(path))
438                                treePaths.add(path);
439                }
440                TreePath[] t = new TreePath[treePaths.size()];
441                for (int i = 0; i < treePaths.size(); i++)
442                        t[i] = treePaths.elementAt(i);
443                tree.setSelectionPaths(t);
444        }
445
446        /**
447         * Gets the tree path for the given port in the given tree
448         */
449        private TreePath _getTreePath(IOPort port, JTree tree) {
450                TreeModel m = tree.getModel();
451                TreeNode root = (TreeNode) m.getRoot();
452                TreePath startpath = new TreePath(root);
453                return _getTreePath(port, root, startpath);
454        }
455
456        /**
457         * Gets the tree path for the given port, a parent node, and a current path.
458         * The parent is the leaf node of the current path.
459         */
460        private TreePath _getTreePath(IOPort port, TreeNode parent,
461                        TreePath currpath) {
462                Enumeration children = parent.children();
463                while (children.hasMoreElements()) {
464                        TreeNode t = (TreeNode) children.nextElement();
465                        TreePath path = currpath.pathByAddingChild(t);
466                        if (t instanceof _PortTreeNode
467                                        && port.equals(((_PortTreeNode) t).getPort()))
468                                return path;
469                        TreePath portpath = _getTreePath(port, t, path);
470                        if (portpath != null)
471                                return portpath;
472                }// end while
473                return null;
474        }
475
476        /**
477         * Creates and initializes the default tree model of the input tree
478         */
479        protected DefaultTreeModel _createInputModel() {
480                _ActorTreeNode root = new _ActorTreeNode(null);
481                _inputTreeModel = new DefaultTreeModel(root);
482
483                Iterator iter = _mergeActor.getActors().iterator();
484                while (iter.hasNext()) {
485                        NamedObj actor = (NamedObj) iter.next();
486                        _ActorTreeNode anode = new _ActorTreeNode(actor);
487                        root.addChild(anode);
488                        Iterator ports = _mergeActor.getActorPorts(actor).iterator();
489                        while (ports.hasNext()) {
490                                IOPort port = (IOPort) ports.next();
491                                _PortTreeNode pnode = new _PortTreeNode(port);
492                                anode.addChild(pnode);
493                        }// end while
494                }// end while
495
496                return _inputTreeModel;
497        }
498
499        /**
500         * Creates and initializes the default tree model of the output tree
501         */
502        protected DefaultTreeModel _createOutputModel() {
503                _ActorTreeNode root = new _ActorTreeNode(null);
504                _outputTreeModel = new DefaultTreeModel(root);
505
506                Iterator<IOPort> iter = getTargetPorts().iterator();
507                while (iter.hasNext()) {
508                        IOPort port = iter.next();
509                        _PortTreeNode pnode = new _PortTreeNode(port);
510                        root.addChild(pnode);
511                }// end while
512                return _outputTreeModel;
513        }
514
515        private void _resetGUI() {
516                _outputPortTree.setSelectionPaths(null);
517                _inputPortTree.setSelectionPaths(null);
518                _semTypeTable.setAnnotationObjectVisible(_emptyAnnotationPort);
519                _conversionTbl.setModel(_emptyConversionModel);
520        }
521
522        // //////////////////////////////////////////////////////////////////////////
523        // LOCAL TARGET PORTS
524
525        /**
526         * Load the target ports from the merge actor. This method should be called
527         * only once when the dialog is created.
528         */
529        private void _loadTargetPorts() {
530                Iterator iter = _mergeActor.outputPortList().iterator();
531                while (iter.hasNext()) {
532                        try {
533                                IOPort p = (IOPort) ((IOPort) iter.next()).clone();
534                                p.setContainer(null);
535                                _targetPorts.add(p);
536                        } catch (Exception e) {
537                                e.printStackTrace();
538                        }
539                }
540        }
541
542        /**
543         * @return the set of local target ports
544         */
545        public Vector<IOPort> getTargetPorts() {
546                return _targetPorts;
547        }
548
549        /**
550         * @return the set of local target ports for the given input port
551         */
552        public Vector<IOPort> getTargetPorts(IOPort inputPort) {
553                Vector<IOPort> results = new Vector<IOPort>();
554                NamedObj actorObj = inputPort.getContainer();
555                String actor = actorObj.getName();
556                String port = inputPort.getName();
557                Vector<String> targetNames = new Vector<String>();
558                Iterator<SimpleMergeMapping> iter = getMappings().iterator();
559                while (iter.hasNext()) {
560                        SimpleMergeMapping m = iter.next();
561                        if (actor.equals(m.getSourceActor())
562                                        && port.equals(m.getSourceActorPort()))
563                                targetNames.add(m.getTargetPort());
564                }// end while
565                
566                Iterator<IOPort> ioPortIter = getTargetPorts().iterator();
567                while (ioPortIter.hasNext()) {
568                        IOPort p = ioPortIter.next();
569                        if (targetNames.contains(p.getName()))
570                                results.add(p);
571                }// end while
572                return results;
573        }
574
575        /**
576         * removes the given target port
577         */
578        public void removeTargetPort(IOPort port) {
579                _targetPorts.remove(port);
580        }
581
582        /**
583         * adds the given target port
584         */
585        public void addTargetPort(IOPort port) {
586                if (!_targetPorts.contains(port))
587                        _targetPorts.add(port);
588        }
589
590        // //////////////////////////////////////////////////////////////////////////
591        // LOCAL MAPPINGS
592
593        private void _loadMappings() {
594                Iterator iter = _mergeActor.getMappings().iterator();
595                while (iter.hasNext()) {
596                        try {
597                                SimpleMergeMapping m = (SimpleMergeMapping) iter.next();
598                                SimpleMergeMapping mcopy = new SimpleMergeMapping(m
599                                                .getSourceActor(), m.getSourceActorPort(), m
600                                                .getTargetPort());
601                                if (m.getConversion() != null)
602                                        mcopy.setConversion(m.getConversion());
603                                _mappings.add(mcopy);
604                        } catch (Exception e) {
605                                e.printStackTrace();
606                        }
607                }// end while
608        }
609
610        private void _printMappings() {
611                Iterator<SimpleMergeMapping> iter = _mappings.iterator();
612                while (iter.hasNext()) {
613                        SimpleMergeMapping m = iter.next();
614                        String actor = m.getSourceActor();
615                        String actorPort = m.getSourceActorPort();
616                        String target = m.getTargetPort();
617                        System.out.println("... mapping: " + actor + ", " + actorPort
618                                        + ", " + target);
619                }
620        }
621
622        // for commit ...
623        // m.setName(_mergeActor.uniqueName("_merge"));
624
625        public Vector<SimpleMergeMapping> getMappings() {
626                return _mappings;
627        }
628
629        public void removeMapping(SimpleMergeMapping m) {
630                _mappings.remove(m);
631        }
632
633        /**
634         * replace current mappings with the new set
635         */
636        public void addMapping(SimpleMergeMapping m) {
637                if (!_mappings.contains(m))
638                        _mappings.add(m);
639        }
640
641        private void _doAdd() {
642                String msg = "Please enter a port name";
643                String portName = null;
644                boolean success = true;
645
646                _resetGUI();
647
648                try {
649                        String title = "Create Merge Output Port";
650                        int type = JOptionPane.PLAIN_MESSAGE;
651                        portName = (String) JOptionPane.showInputDialog(this, msg, title,
652                                        type);
653                } catch (Exception e) {
654                        e.printStackTrace();
655                }
656                if (portName == null)
657                        return;
658                if (portName.trim().length() < 1) {
659                        msg = "A port name must be provided";
660                        success = false;
661                }
662                if (success) {
663                        try {
664                                NamedObj o = new NamedObj();
665                                o.setName(portName);
666                        } catch (Exception e) {
667                                success = false;
668                                msg = e.getMessage();
669                        }
670                }
671                Iterator<IOPort> iter = getTargetPorts().iterator();
672                while (success && iter.hasNext()) {
673                        IOPort p = iter.next();
674                        if (portName.equals(p.getName())) {
675                                success = false;
676                                msg = "A port named '" + portName + "' already exists.";
677                                break;
678                        }
679                }// end while
680
681                if (!success) {
682                        JOptionPane.showMessageDialog(this, msg, "Error",
683                                        JOptionPane.ERROR_MESSAGE);
684                        return;
685                }
686
687                // add the port
688                TypedIOPort out = new TypedIOPort();
689                try {
690                        out.setName(portName);
691                        out.setOutput(true);
692                        out.setInput(false);
693                        addTargetPort(out);
694                } catch (Exception e) {
695                        e.printStackTrace();
696                        return;
697                }
698                // update the display
699                _outputPortTree.setModel(_createOutputModel());
700                // add to the sem port dialog
701                _semTypeTable.addAnnotationObject(out);
702        }
703
704        private void _doPrune() {
705                _resetGUI();
706
707                Iterator iter = _getDanglingOutputPorts().iterator();
708                Vector dangling = new Vector();
709                while (iter.hasNext()) {
710                        IOPort p = (IOPort) iter.next();
711                        dangling.add(p);
712                        removeTargetPort(p);
713                }
714                // refresh the dialog
715                _outputPortTree.setModel(_createOutputModel());
716                // remove from semantic type dialog
717                iter = dangling.iterator();
718                while (iter.hasNext())
719                        _semTypeTable.removeAnnotationObject((IOPort) iter.next());
720        }
721
722        private Vector<IOPort> _getDanglingOutputPorts() {
723                Vector<IOPort> results = new Vector<IOPort>();
724                Iterator<SimpleMergeMapping> iter = getMappings().iterator();
725                Vector<String> targets = new Vector<String>();
726                while (iter.hasNext()) {
727                        SimpleMergeMapping m = iter.next();
728                        targets.add(m.getTargetPort());
729                }
730
731                Iterator<IOPort> ioIter = getTargetPorts().iterator();
732                while (iter.hasNext()) {
733                        IOPort p = ioIter.next();
734                        if (!targets.contains(p.getName()))
735                                results.add(p);
736                }// end while
737                return results;
738        }
739
740        /**
741         * Call back for the commit button
742         */
743        private void _doCommit() {
744                _resetGUI();
745
746                String msg = null;
747                int opt = JOptionPane.YES_NO_OPTION;
748                int type = JOptionPane.WARNING_MESSAGE;
749                // if any "dangling" ports output warning message
750                if (_getDanglingOutputPorts().size() > 0) {
751                        msg = "Dangling output ports: Some output ports are not mapped to input ports \n"
752                                        + "Commit anyway?";
753                        if (1 == JOptionPane.showConfirmDialog(this, msg, "Warning", opt,
754                                        type))
755                                return; // abort
756                }
757                // commit the semantic type changes
758                if ((msg = _semTypeTable.wellFormedSemTypes()) != null) {
759                        JOptionPane.showMessageDialog(this, msg, "Message",
760                                        JOptionPane.ERROR_MESSAGE);
761                        return; // abort
762                }
763                if (_semTypeTable.hasUnknownSemTypes()) {
764                        msg = "Unable to find a matching ontology class for at least one semantic type. \n"
765                                        + "Commit anyway?";
766                        if (1 == JOptionPane.showConfirmDialog(this, msg, "Message", opt,
767                                        type))
768                                return; // abort
769                }
770                _semTypeTable.commitAnnotationObjects();
771
772                // update the mergeActor mappings and output ports
773                _saveChanges();
774
775                // System.out.println(_mergeActor.exportMoML());
776
777                _mergeActor.update();
778
779                // close the dialog
780                _doClose();
781        }
782
783        private void _saveChanges() {
784                // remove existing output ports
785                Iterator iter = _mergeActor.outputPortList().iterator();
786                while (iter.hasNext()) {
787                        IOPort p = (IOPort) iter.next();
788                        try {
789                                p.setContainer(null);
790                        } catch (Exception e) {
791                                e.printStackTrace();
792                        }
793                }// end while
794
795                // add the new ports
796                iter = getTargetPorts().iterator();
797                while (iter.hasNext()) {
798                        TypedIOPort p = (TypedIOPort) iter.next();
799                        try {
800                                TypedIOPort port = (TypedIOPort) p.clone(_mergeActor
801                                                .workspace());
802                                port.setContainer(_mergeActor);
803                        } catch (Exception e) {
804                                e.printStackTrace();
805                        }
806                }// end while
807                _mergeActor.setProductionRate();
808
809                // remove existing mappings
810                iter = _mergeActor.getMappings().iterator();
811                while (iter.hasNext()) {
812                        try {
813                                SimpleMergeMapping m = (SimpleMergeMapping) iter.next();
814                                m.setContainer(null);
815                        } catch (Exception e) {
816                                e.printStackTrace();
817                        }
818                }
819
820                // for each mapping, set name and clone into workspace
821                iter = getMappings().iterator();
822                while (iter.hasNext()) {
823                        SimpleMergeMapping m = (SimpleMergeMapping) iter.next();
824                        try {
825                                m.setName(_mergeActor.uniqueName("_merge"));
826                                SimpleMergeMapping map = (SimpleMergeMapping) m
827                                                .clone(_mergeActor.workspace());
828                                map.setContainer(_mergeActor);
829                        } catch (Exception e) {
830                                e.printStackTrace();
831                        }
832                }
833        }
834
835        /**
836         * Call back for the close button
837         */
838        private void _doClose() {
839                dispose();
840        }
841
842        /**
843     * 
844     */
845        private void _doEdit() {
846                _resetGUI();
847                MappingRefinementDialog.showDialog(_owner, _mergeActor, this);
848                _outputPortTree.setSelectionPaths(null);
849                _inputPortTree.setSelectionPaths(null);
850        }
851
852        /**
853         * Given a label string, component, height, and width, creates a new panel
854         * with a label and the component of size height and width. The label is
855         * positioned above the component and is left justified.
856         */
857        private JPanel _labelComponent(String str, Component component, int width,
858                        int height) {
859                // output pane
860                JPanel pane = new JPanel();
861                pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));
862                // pane for label only
863                JPanel labelPane = new JPanel();
864                labelPane.setLayout(new BoxLayout(labelPane, BoxLayout.X_AXIS));
865                labelPane.add(new JLabel(str, SwingConstants.LEFT));
866                labelPane.add(Box.createHorizontalGlue());
867                // add label
868                pane.add(labelPane);
869                // add space
870                pane.add(Box.createRigidArea(new Dimension(0, 5)));
871                // add component
872                pane.add(component);
873                // set sizes
874                pane.setMaximumSize(new Dimension(width, height));
875                pane.setMinimumSize(new Dimension(width, height));
876                pane.setPreferredSize(new Dimension(width, height));
877                // return outer pane
878                return pane;
879        }
880
881        /**
882         * Inner class for tree nodes
883         */
884        private class _ActorTreeNode implements TreeNode {
885                public _ActorTreeNode(NamedObj actor) {
886                        _actor = actor;
887                }
888
889                public NamedObj getActor() {
890                        return _actor;
891                }
892
893                public void addChild(_ActorTreeNode child) {
894                        if (!_children.contains(child)) {
895                                _children.add(child);
896                                child.removeFromParent();
897                                child.setParent(this);
898                        }
899                }
900
901                public void addChild(_PortTreeNode child) {
902                        if (!_children.contains(child)) {
903                                _children.add(child);
904                                child.removeFromParent();
905                                child.setParent(this);
906                        }
907                }
908
909                public void setParent(TreeNode parent) {
910                        _parent = parent;
911                }
912
913                public void removeFromParent() {
914                        _parent = null;
915                }
916
917                public Enumeration children() {
918                        return _children.elements();
919                }
920
921                public boolean getAllowsChildren() {
922                        return true;
923                }
924
925                public TreeNode getChildAt(int index) {
926                        if (index < _children.size() && index >= 0)
927                                return (TreeNode) _children.elementAt(index);
928                        return null;
929                }
930
931                public int getIndex(TreeNode node) {
932                        return _children.indexOf(node);
933                }
934
935                public int getChildCount() {
936                        return _children.size();
937                }
938
939                public TreeNode getParent() {
940                        return _parent;
941                }
942
943                public boolean isLeaf() {
944                        return _children.size() == 0;
945                }
946
947                public String toString() {
948                        if (_actor != null)
949                                return _actor.getName();
950                        return "";
951                }
952
953                private NamedObj _actor;
954                private Vector _children = new Vector();
955                private TreeNode _parent;
956        };
957
958        /**
959         * Inner class for port tree nodes
960         */
961        private class _PortTreeNode implements TreeNode {
962                public _PortTreeNode(Object port) {
963                        _port = port;
964                }
965
966                public Object getPort() {
967                        return _port;
968                }
969
970                public void addChild(_PortTreeNode child) {
971                        if (!_children.contains(child)) {
972                                _children.add(child);
973                                child.removeFromParent();
974                                child.setParent(this);
975                        }
976                }
977
978                public void setParent(TreeNode parent) {
979                        _parent = parent;
980                }
981
982                public void removeFromParent() {
983                        _parent = null;
984                }
985
986                public Enumeration children() {
987                        return _children.elements();
988                }
989
990                public boolean getAllowsChildren() {
991                        return true;
992                }
993
994                public TreeNode getChildAt(int index) {
995                        if (index < _children.size() && index >= 0)
996                                return (TreeNode) _children.elementAt(index);
997                        return null;
998                }
999
1000                public int getIndex(TreeNode node) {
1001                        return _children.indexOf(node);
1002                }
1003
1004                public int getChildCount() {
1005                        return _children.size();
1006                }
1007
1008                public TreeNode getParent() {
1009                        return _parent;
1010                }
1011
1012                public boolean isLeaf() {
1013                        return _children.size() == 0;
1014                }
1015
1016                public String toString() {
1017                        String str = "";
1018                        if (_port instanceof IOPort)
1019                                str += ((IOPort) _port).getName();
1020                        else if (_port instanceof KeplerVirtualIOPort)
1021                                str += ((KeplerVirtualIOPort) _port).getName();
1022                        return str;
1023                }
1024
1025                private Object _port;
1026                private Vector _children = new Vector();
1027                private TreeNode _parent;
1028
1029        };
1030
1031        /**
1032         * anonymous class to handle button events
1033         */
1034        private ActionListener _buttonListener = new ActionListener() {
1035                public void actionPerformed(ActionEvent e) {
1036                        if (e.getActionCommand().equals("commit"))
1037                                _doCommit();
1038                        else if (e.getActionCommand().equals("cancel"))
1039                                _doClose();
1040                        else if (e.getActionCommand().equals("refine")) {
1041                                _doEdit(); // new dialog
1042                        } else if (e.getActionCommand().equals("prune")) {
1043                                _doPrune();
1044                        } else if (e.getActionCommand().equals("add")) {
1045                                _doAdd();
1046                        }
1047                }
1048        };
1049
1050        public static void main(String[] args) {
1051                try {
1052                        javax.swing.UIManager.setLookAndFeel(javax.swing.UIManager
1053                                        .getSystemLookAndFeelClassName());
1054
1055                        ptolemy.actor.TypedCompositeActor cont = new ptolemy.actor.TypedCompositeActor();
1056
1057                        MergeActor m = new MergeActor(cont, "merge");
1058
1059                        ptolemy.actor.TypedCompositeActor a = new ptolemy.actor.TypedCompositeActor(
1060                                        cont, "A");
1061                        ptolemy.actor.lib.Const a1_inner = new ptolemy.actor.lib.Const(a,
1062                                        "A1_inner");
1063                        a1_inner.value.setToken(new ptolemy.data.IntToken(1));
1064                        ptolemy.actor.lib.Const a2_inner = new ptolemy.actor.lib.Const(a,
1065                                        "A2_inner");
1066                        a2_inner.value.setToken(new ptolemy.data.IntToken(4));
1067                        TypedIOPort a_output1 = new TypedIOPort(a, "output1", false, true);
1068                        TypedIOPort a_output2 = new TypedIOPort(a, "output2", false, true);
1069                        TypedIORelation a1rel = new TypedIORelation(a, "a1rel");
1070                        a1_inner.output.link(a1rel);
1071                        a_output1.link(a1rel);
1072                        TypedIORelation a2rel = new TypedIORelation(a, "a2rel");
1073                        a2_inner.output.link(a2rel);
1074                        a_output2.link(a1rel);
1075
1076                        ptolemy.actor.lib.Const b = new ptolemy.actor.lib.Const(cont, "B");
1077                        b.value.setToken(new ptolemy.data.IntToken(2));
1078
1079                        ptolemy.actor.lib.Const c = new ptolemy.actor.lib.Const(cont, "C");
1080                        c.value.setToken(new ptolemy.data.IntToken(3));
1081
1082                        TypedIORelation arel11 = new TypedIORelation(cont, "rel11");
1083                        a_output1.link(arel11);
1084                        m.mergeInputPort.link(arel11);
1085
1086                        TypedIORelation arel12 = new TypedIORelation(cont, "rel12");
1087                        a_output2.link(arel12);
1088                        m.mergeInputPort.link(arel12);
1089
1090                        TypedIORelation brel = new TypedIORelation(cont, "rel2");
1091                        b.output.link(brel);
1092                        m.mergeInputPort.link(brel);
1093
1094                        TypedIORelation crel = new TypedIORelation(cont, "rel3");
1095                        c.output.link(crel);
1096                        m.mergeInputPort.link(crel);
1097
1098                        // semantic port annotations
1099                        SemanticType t1 = new SemanticType(b.output, b.output
1100                                        .uniqueName("_semanticType"));
1101                        t1
1102                                        .setConceptId("urn:lsid:lsid.ecoinformatics.org:onto:4:1#BiomassMeasurement");
1103                        SemanticType t2 = new SemanticType(c.output, c.output
1104                                        .uniqueName("_semanticType"));
1105                        t2
1106                                        .setConceptId("urn:lsid:lsid.ecoinformatics.org:onto:4:1#BiomassMeasurement");
1107
1108                        m.computeMerge();
1109                        m.editMerge();
1110
1111                } catch (Exception e) {
1112                        e.printStackTrace();
1113                }
1114        }
1115
1116        // a table for the conversion functions: t(function, output port)
1117        /**
1118         * inner class for managing the table data
1119         */
1120        private class _ConversionFunctionTableModel extends AbstractTableModel {
1121                // private members
1122                private String[] tableHeader = { "Function", "Output Port" }; // two
1123                                                                                                                                                // columns
1124                private Vector tableData = new Vector(); // vector of arrays
1125
1126                public int getColumnCount() {
1127                        return 2;
1128                }
1129
1130                public int getRowCount() {
1131                        return tableData.size();
1132                }
1133
1134                public String getColumnName(int columnIndex) {
1135                        return tableHeader[columnIndex];
1136                }
1137
1138                public Object getValueAt(int rowIndex, int columnIndex) {
1139                        Object[] row = (Object[]) tableData.elementAt(rowIndex);
1140                        return row[columnIndex];
1141                }
1142
1143                public boolean isCellEditable(int rowIndex, int columnIndex) {
1144                        if (columnIndex == 0)
1145                                return true;
1146                        return false;
1147                }
1148
1149                public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
1150                        if (!validRowIndex(rowIndex))
1151                                return;
1152                        Object[] row = (Object[]) tableData.elementAt(rowIndex);
1153                        row[columnIndex] = aValue;
1154                        fireTableChanged(new TableModelEvent(this)); // change event
1155                }
1156
1157                public void insertRow(String function, String portName) {
1158                        Object[] row = new Object[2];
1159                        row[0] = function;
1160                        row[1] = portName;
1161                        if (rowExists(row)) // if exists, return
1162                                return;
1163                        // make sure there are no empty rows (if so, to first one)
1164                        int index = firstEmptyRow();
1165                        if (index != -1)
1166                                tableData.setElementAt(row, index);
1167                        else
1168                                tableData.add(row);
1169                        fireTableChanged(new TableModelEvent(this));
1170                }
1171
1172                public boolean containsRow(String function, String portName) {
1173                        Object[] tmpRow = new Object[2];
1174                        tmpRow[0] = function;
1175                        tmpRow[1] = portName;
1176                        return rowExists(tmpRow);
1177                }
1178
1179                /**
1180                 * @return a list of rows of non-empty Object arrays of arity 2
1181                 *         (Object[2])
1182                 */
1183                public Iterator getRows() {
1184                        Vector results = new Vector();
1185                        for (Iterator iter = tableData.iterator(); iter.hasNext();) {
1186                                Object[] row = (Object[]) iter.next();
1187                                if (row[0] != null || row[1] != null)
1188                                        results.add(row);
1189                        }
1190                        return results.iterator();
1191                }
1192
1193                public void insertEmptyRow() {
1194                        tableData.addElement(new Object[2]);
1195                        fireTableChanged(new TableModelEvent(this)); // change event
1196                }
1197
1198                public int firstEmptyRow() {
1199                        int index = 0;
1200                        for (Iterator iter = tableData.iterator(); iter.hasNext(); index++) {
1201                                Object[] row = (Object[]) iter.next();
1202                                if (row[0] == null && row[1] == null)
1203                                        return index;
1204                        }
1205                        return -1;
1206                }
1207
1208                private boolean rowExists(Object[] newrow) {
1209                        for (Iterator iter = tableData.iterator(); iter.hasNext();) {
1210                                Object[] row = (Object[]) iter.next();
1211                                if (newrow[0].equals(row[0]) && newrow[1].equals(row[1]))
1212                                        return true;
1213                        }
1214                        return false;
1215                }
1216
1217                public void removeRow(int rowIndex) {
1218                        if (!validRowIndex(rowIndex))
1219                                return;
1220                        tableData.removeElementAt(rowIndex);
1221                        fireTableChanged(new TableModelEvent(this)); // change event
1222                }
1223
1224                private boolean validRowIndex(int rowIndex) {
1225                        if (rowIndex >= 0 && rowIndex < getRowCount())
1226                                return true;
1227                        return false;
1228                }
1229        };
1230
1231        private Frame _owner;
1232        private MergeActor _mergeActor;
1233        private JButton _commitBtn = new JButton("Commit");
1234        private JButton _closeBtn = new JButton("Cancel");
1235        private DefaultTreeModel _outputTreeModel;
1236        private DefaultTreeModel _inputTreeModel;
1237        private JTree _outputPortTree;
1238        private JTree _inputPortTree;
1239        private SemanticTypeTable _semTypeTable;
1240        private IOPort _emptyAnnotationPort = new IOPort();
1241        private JTable _conversionTbl;
1242        private _ConversionFunctionTableModel _emptyConversionModel = new _ConversionFunctionTableModel();
1243         // a local copy of the merge actor mappings
1244        private Vector<SimpleMergeMapping> _mappings = new Vector<SimpleMergeMapping>();
1245        // a local copy of the target output ports
1246        private Vector<IOPort> _targetPorts = new Vector<IOPort>(); 
1247
1248}// MergeEditorDialog