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.gui;
031
032import java.awt.Color;
033import java.awt.Dimension;
034import java.awt.Frame;
035import java.awt.event.ActionEvent;
036import java.awt.event.ActionListener;
037import java.awt.event.ItemEvent;
038import java.awt.event.ItemListener;
039import java.util.Iterator;
040import java.util.StringTokenizer;
041import java.util.Vector;
042
043import javax.swing.BorderFactory;
044import javax.swing.Box;
045import javax.swing.BoxLayout;
046import javax.swing.JButton;
047import javax.swing.JCheckBox;
048import javax.swing.JDialog;
049import javax.swing.JLabel;
050import javax.swing.JOptionPane;
051import javax.swing.JPanel;
052import javax.swing.JScrollPane;
053import javax.swing.UIManager;
054import javax.swing.border.TitledBorder;
055import javax.swing.event.TableModelEvent;
056import javax.swing.table.AbstractTableModel;
057
058import org.kepler.gui.GraphicalActorMetadata;
059import org.kepler.objectmanager.ActorMetadata;
060import org.kepler.objectmanager.cache.ActorCacheObject;
061import org.kepler.objectmanager.cache.CacheManager;
062import org.kepler.objectmanager.lsid.KeplerLSID;
063import org.kepler.sms.SMSServices;
064import org.kepler.sms.SemanticType;
065
066import ptolemy.actor.IOPort;
067import ptolemy.actor.TypedAtomicActor;
068import ptolemy.actor.TypedCompositeActor;
069import ptolemy.actor.TypedIOPort;
070import ptolemy.kernel.ComponentEntity;
071import ptolemy.kernel.Entity;
072import ptolemy.kernel.util.NamedObj;
073import ptolemy.kernel.util.Workspace;
074import ptolemy.moml.EntityLibrary;
075import ptolemy.vergil.tree.EntityTreeModel;
076import ptolemy.vergil.tree.PTree;
077import ptolemy.vergil.tree.VisibleTreeModel;
078
079
080
081/**
082 * 
083 * @author Shawn Bowers
084 */
085public class SemanticSearchDialog extends JDialog {
086    /**
087     * This is the default constructor
088     */
089    public SemanticSearchDialog(Frame owner, NamedObj namedObj) {
090        super(owner);
091
092        _namedObj = namedObj;
093        _owner = owner;
094
095        // set title and close behavior
096        this.setTitle("Semantic Search");
097        this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
098
099        // create the frame's pane
100        JPanel pane = new JPanel();
101        pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));
102        pane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
103
104        // create the ssearch and result pane
105        JPanel bodyPane = new JPanel();
106        bodyPane.setLayout(new BoxLayout(bodyPane, BoxLayout.X_AXIS));
107        bodyPane.add(_createSearchPane());
108        bodyPane.add(Box.createRigidArea(new Dimension(10, 0)));
109        bodyPane.add(_createResultPane());
110
111        // add components to pane
112        pane.add(bodyPane);
113        pane.add(Box.createRigidArea(new Dimension(0, 5)));
114        pane.add(_createButtons());
115
116        // pane.add(Box.createRigidArea(new Dimension(0, 5)));
117        pane.add(_createStatus());
118
119        // add the pane
120        setContentPane(pane);
121        // set size
122        this.setSize(675, 575);
123        // setResizable(false);
124
125    }
126
127    /**
128     * Initialize the bottom buttons (close)
129     */
130    private JPanel _createSearchPane() {
131        // initialize "dummy" search objects
132        try {
133            _searchActor = new TypedAtomicActor();
134            _inSearchPort = new TypedIOPort(_searchActor, "input", true, false);
135            _outSearchPort = new TypedIOPort(_searchActor, "output", false,
136                                             true);
137            _actorTable = new SemanticTypeTable(_getFrame(), false);
138            _actorTable.addAnnotationObject(_searchActor);
139            _actorTable.setAnnotationObjectVisible(_searchActor);
140            _actorTable.setEnabled(false);
141            _inPortTable = new SemanticTypeTable(_getFrame(), false);
142            _inPortTable.addAnnotationObject(_inSearchPort);
143            _inPortTable.setAnnotationObjectVisible(_inSearchPort);
144            _inPortTable.setEnabled(false);
145            _outPortTable = new SemanticTypeTable(_getFrame(), false);
146            _outPortTable.addAnnotationObject(_outSearchPort);
147            _outPortTable.setAnnotationObjectVisible(_outSearchPort);
148            _outPortTable.setEnabled(false);
149        } catch (Exception e) {
150            e.printStackTrace();
151        }
152
153        // initialize the checkboxes
154        _chkActorSearch = new JCheckBox("Actor Semantic Types");
155        _chkActorSearch.setSelected(false);
156        _chkActorSearch.addItemListener(_checkBoxListener);
157        _chkInPortSearch = new JCheckBox("Input Semantic Types");
158        _chkInPortSearch.setSelected(false);
159        _chkInPortSearch.addItemListener(_checkBoxListener);
160        _chkOutPortSearch = new JCheckBox("Output Semantic Types");
161        _chkOutPortSearch.setSelected(false);
162        _chkOutPortSearch.addItemListener(_checkBoxListener);
163
164        // initialize the panel
165        JPanel pane = new JPanel();
166        pane.setLayout(new BoxLayout(pane, BoxLayout.PAGE_AXIS));
167        pane.setBorder(_createTitledBorder("Search Criteria"));
168
169        pane.add(_createSearchComponent(_chkActorSearch, _actorTable));
170        pane.add(Box.createRigidArea(new Dimension(0, 5)));
171        pane.add(_createSearchComponent(_chkInPortSearch, _inPortTable));
172        pane.add(Box.createRigidArea(new Dimension(0, 5)));
173        pane.add(_createSearchComponent(_chkOutPortSearch, _outPortTable));
174
175        return pane;
176    }
177
178    /**
179     * 
180     */
181    private JPanel _createResultPane() {
182        JPanel pane = new JPanel();
183        pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));
184        pane.setBorder(_createTitledBorder("Search Results"));
185
186        _resultTree = new PTree(new EntityTreeModel(null));
187        _resultTree.setPreferredSize(new Dimension(280, 100));
188        JPanel treePane = new JPanel();
189        treePane.setLayout(new BoxLayout(treePane, BoxLayout.X_AXIS));
190        treePane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
191        treePane.add(new JScrollPane(_resultTree));
192
193        pane.add(treePane);
194
195        // return
196        return pane;
197    }
198
199    /**
200     * Combines a check box and semantic type table into a single pane.
201     */
202    private JPanel _createSearchComponent(JCheckBox chk, SemanticTypeTable tbl) {
203        // set up the main panel
204        JPanel pane = new JPanel();
205        pane.setLayout(new BoxLayout(pane, BoxLayout.Y_AXIS));
206        // construct the check box
207        JPanel chkPane = new JPanel();
208        chkPane.add(Box.createRigidArea(new Dimension(10, 0)));
209        chkPane.setLayout(new BoxLayout(chkPane, BoxLayout.X_AXIS));
210        chkPane.add(chk);
211        chkPane.add(Box.createHorizontalGlue());
212        // add the check box
213        pane.add(chkPane);
214        // add the table
215        pane.add(tbl);
216        // return the pane
217        return pane;
218    }
219
220    /**
221     * Initialize the bottom buttons (close)
222     */
223    private JPanel _createButtons() {
224        // init buttons
225        _btnSearch = new JButton("Search");
226        _btnSearch.setActionCommand("search");
227        _btnSearch.setToolTipText("Search for matching components");
228        _btnSearch.addActionListener(_buttonListener);
229        _btnClose = new JButton("Close");
230        _btnClose.setActionCommand("close");
231        _btnClose.setToolTipText("Close dialog");
232        _btnClose.addActionListener(_buttonListener);
233        // create the pane
234        JPanel pane = new JPanel();
235        pane.setLayout(new BoxLayout(pane, BoxLayout.LINE_AXIS));
236        pane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
237        pane.add(Box.createHorizontalGlue());
238        pane.add(_btnSearch);
239        pane.add(Box.createRigidArea(new Dimension(10, 0)));
240        pane.add(_btnClose);
241
242        return pane;
243    }
244
245    /**
246     *
247     */
248    private JPanel _createStatus() {
249        JPanel pane = new JPanel();
250        pane.setLayout(new BoxLayout(pane, BoxLayout.X_AXIS));
251
252        _lblQueryStatus = new JLabel("status: ");
253
254        pane.add(Box.createRigidArea(new Dimension(5, 0)));
255        pane.add(_lblQueryStatus);
256        // statusPane.setPreferredSize(new Dimension(100, 25));
257        pane.add(Box.createHorizontalGlue());
258
259        return pane;
260    }
261
262    /**
263     * Create a titled border with a blue font
264     */
265    private TitledBorder _createTitledBorder(String title) {
266        TitledBorder border = BorderFactory.createTitledBorder(" " + title
267                                                               + " ");
268        border.setTitleColor(new Color(0, 10, 230));
269        return border;
270    }
271
272    /**
273     * @return The frame of the dialog
274     */
275    private Frame _getFrame() {
276        java.awt.Container c = getParent();
277        while (!(c instanceof Frame) && c != null)
278            c = c.getParent();
279        if (c != null)
280            return (Frame) c;
281        return null;
282    }
283
284    /**
285     * Anonymous class to handle check box state changes
286     */
287    private ItemListener _checkBoxListener = new ItemListener() {
288            public void itemStateChanged(ItemEvent e) {
289                Object source = e.getItemSelectable();
290                if (source == _chkActorSearch)
291                    _actorSearchStateChanged();
292                else if (source == _chkInPortSearch)
293                    _inPortSearchStateChanged();
294                else if (source == _chkOutPortSearch)
295                    _outPortSearchStateChanged();
296            }
297        };
298
299    /**
300     * anonymous class to handle button events
301     */
302    private ActionListener _buttonListener = new ActionListener() {
303            public void actionPerformed(ActionEvent e) {
304                if (e.getActionCommand().equals("search"))
305                    _doSearch();
306                else if (e.getActionCommand().equals("close"))
307                    _doClose();
308            }
309        };
310
311    /**
312     * Toggles the actor search pane
313     */
314    private void _actorSearchStateChanged() {
315        if (_chkActorSearch.isSelected())
316            _actorTable.setEnabled(true);
317        else
318            _actorTable.setEnabled(false);
319    }
320
321    /**
322     * Toggles the input port search pane
323     */
324    private void _inPortSearchStateChanged() {
325        if (_chkInPortSearch.isSelected())
326            _inPortTable.setEnabled(true);
327        else
328            _inPortTable.setEnabled(false);
329    }
330
331    /**
332     * Toggles the out port search pane
333     */
334    private void _outPortSearchStateChanged() {
335        if (_chkOutPortSearch.isSelected())
336            _outPortTable.setEnabled(true);
337        else
338            _outPortTable.setEnabled(false);
339    }
340
341    /**
342     * Performs the search button operation.
343     */
344    private void _doSearch() {
345
346        Vector<SemanticType> actorTypes = new Vector<SemanticType>();
347        Vector<SemanticType> inTypes = new Vector<SemanticType>();
348        Vector<SemanticType> outTypes = new Vector<SemanticType>();
349
350        String msg;
351        // check for some search terms; if none then warn
352        // if(_chkOutPortSearch.isSelected())
353        outTypes = _outPortTable.getSemanticTypes(_outSearchPort);
354        // if(_chkInPortSearch.isSelected())
355        inTypes = _inPortTable.getSemanticTypes(_inSearchPort);
356        // if(_chkActorSearch.isSelected())
357        actorTypes = _actorTable.getSemanticTypes(_searchActor);
358        if (outTypes.size() == 0 && inTypes.size() == 0
359            && actorTypes.size() == 0) {
360            msg = "No search criteria specified.";
361            JOptionPane.showMessageDialog(this, msg, "Message",
362                                          JOptionPane.ERROR_MESSAGE);
363            return;
364        }
365
366        // make sure the sem types are well formed first!
367        if ((msg = _outPortTable.wellFormedSemTypes()) != null) {
368            JOptionPane.showMessageDialog(this, msg, "Message",
369                                          JOptionPane.ERROR_MESSAGE);
370            return;
371        } else if ((msg = _inPortTable.wellFormedSemTypes()) != null) {
372            JOptionPane.showMessageDialog(this, msg, "Message",
373                                          JOptionPane.ERROR_MESSAGE);
374            return;
375        } else if ((msg = _actorTable.wellFormedSemTypes()) != null) {
376            JOptionPane.showMessageDialog(this, msg, "Message",
377                                          JOptionPane.ERROR_MESSAGE);
378            return;
379        }
380
381        // warn about unknown semantic types
382        if (_outPortTable.hasUnknownSemTypes()
383            || _inPortTable.hasUnknownSemTypes()
384            || _actorTable.hasUnknownSemTypes()) {
385            msg = "Unable to find a matching ontology class for at least one semantic type. \n"
386                + "Do you wish to search anyway?";
387            int n = JOptionPane.showConfirmDialog(this, msg, "Message",
388                                                  JOptionPane.YES_NO_OPTION);
389            if (n == 1)
390                return;
391        }
392
393        // give notice of executing search, and disable search button
394        _lblQueryStatus.setText("status: executing search ");
395        _btnSearch.setEnabled(false);
396
397        // create a search task, add listener and run it
398        _SearchTask task = new _SearchTask(actorTypes, inTypes, outTypes);
399        task.addListener(this);
400        Thread thread = new Thread(task);
401        thread.start();
402    }
403
404    /**
405     * Callback for thread
406     */
407    private void _searchCompletedAction(_SearchTask task) {
408        _btnSearch.setEnabled(true);
409
410        Vector<Entity> result = task.getResults();
411        _lblQueryStatus.setText("status: found " + result.size() + " results");
412
413        EntityLibrary root = new EntityLibrary();
414        Workspace workspace = root.workspace();
415        EntityTreeModel model = new VisibleTreeModel(root);
416
417        for (Iterator<Entity> iter = result.iterator(); iter.hasNext();) {
418            try {
419                NamedObj entity = (NamedObj) iter.next();
420                // add to the tree
421                NamedObj obj = _clone(entity, workspace);
422                if (obj instanceof ComponentEntity) {
423                    ((ComponentEntity) obj).setContainer(root);
424                    // ChangeRequest request = new MoMLChangeRequest(obj,
425                    // "adding object to search result");
426                    // obj.requestChange(request);
427                    // obj.executeChangeRequests();
428                }
429            } catch (Exception e) {
430                e.printStackTrace();
431            }
432        }
433        _resultTree.setModel(model);
434        _resultTree.setRootVisible(false);
435
436    }
437
438    private NamedObj _clone(NamedObj obj, Workspace workspace) {
439        NamedObj result = null;
440        try {
441            result = (NamedObj) obj.clone(workspace);
442        } catch (Exception e) {
443            e.printStackTrace();
444        }
445        return result;
446    }
447
448    /**
449     * A private inner class to execute query as a separate thread.
450     */
451    private class _SearchTask implements Runnable {
452        private Vector<SemanticType> _actorTypes;
453        private Vector<SemanticType> _inputTypes;
454        private Vector<SemanticType> _outputTypes;
455        private Vector<Entity> _results;
456        private Vector<SemanticSearchDialog> _listeners = new Vector<SemanticSearchDialog>();
457
458        /** constructor */
459        public _SearchTask(Vector<SemanticType> actorTypes, Vector<SemanticType> inputTypes,
460                           Vector<SemanticType> outputTypes) {
461            _actorTypes = actorTypes;
462            _inputTypes = inputTypes;
463            _outputTypes = outputTypes;
464        }
465
466        /** to notify dialog when results are obtained */
467        public void addListener(SemanticSearchDialog obj) {
468            _listeners.add(obj);
469        }
470
471        /** executes the query */
472        public void run() {
473            _results = _doSearch(_actorTypes, _inputTypes, _outputTypes);
474            for (Iterator<SemanticSearchDialog> iter = _listeners.iterator(); iter.hasNext();) {
475                SemanticSearchDialog dialog = (SemanticSearchDialog) iter
476                    .next();
477                dialog._searchCompletedAction(this);
478            }
479        }
480
481        /** retrieve the results */
482        public Vector<Entity> getResults() {
483            return _results;
484        }
485
486    }; // _SearchTask
487
488    /**
489     * 
490     */
491    private synchronized Vector<Entity> _doSearch(Vector<SemanticType> searchActorTypes,
492                                          Vector<SemanticType> searchInTypes, 
493                                          Vector<SemanticType> searchOutTypes) {
494        Vector<Entity> result = new Vector<Entity>();
495
496        Vector<Entity> objects = _getObjectsToSearch();
497
498        // check if there is a match; we know there is at least one
499        // semantic type, which is checked in _doSearch() above
500        for (Iterator<Entity> iter = objects.iterator(); iter.hasNext();) {
501            boolean compatible = true;
502            Entity entity = iter.next();
503
504            if (searchActorTypes.size() > 0 && compatible) {
505                Vector entityActorTypes = SMSServices
506                    .getActorSemanticTypes(entity);
507                if (SMSServices.compare(entityActorTypes, searchActorTypes) != SMSServices.COMPATIBLE)
508                    compatible = false;
509            }
510
511            if (searchInTypes.size() > 0 && compatible) {
512                // iterate through the semantic types while still compatible
513                for (Iterator<SemanticType> types = searchInTypes.iterator(); types.hasNext()
514                         && compatible;) {
515                    boolean found = false;
516                    Vector<SemanticType> searchInType = new Vector<SemanticType>();
517                    searchInType.add(types.next());
518                    // iterator through ports until we find a match
519                    for (Iterator ports = entity.portList().iterator(); ports
520                             .hasNext()
521                             && !found;) {
522                        IOPort port = (IOPort) ports.next();
523                        Vector entityInTypes = SMSServices
524                            .getPortSemanticTypes(port);
525                        if (SMSServices.compare(searchInType, entityInTypes) == SMSServices.COMPATIBLE)
526                            found = true;
527                    }
528                    // if we didn't find a match for a semtype then we're not
529                    // compatible
530                    if (!found)
531                        compatible = false;
532                }
533            }
534
535            if (searchOutTypes.size() > 0 && compatible) {
536                // iterate through the semantic types while still compatible
537                for (Iterator<SemanticType> types = searchOutTypes.iterator(); types
538                         .hasNext()
539                         && compatible;) {
540                    boolean found = false;
541                    Vector<SemanticType> searchOutType = new Vector<SemanticType>();
542                    searchOutType.add(types.next());
543                    // iterator through ports until we find a match
544                    for (Iterator ports = entity.portList().iterator(); ports
545                             .hasNext()
546                             && !found;) {
547                        IOPort port = (IOPort) ports.next();
548                        Vector entityOutTypes = SMSServices
549                            .getPortSemanticTypes(port);
550                        if (SMSServices.compare(entityOutTypes, searchOutType) == SMSServices.COMPATIBLE)
551                            found = true;
552                    }
553                    // if we didn't find a match for a semtype then we're not
554                    // compatible
555                    if (!found)
556                        compatible = false;
557                }
558            }
559
560            if (compatible)
561                result.add(entity);
562        }
563
564        return result;
565    }
566
567        /**
568     * 
569     */
570        private Vector<Entity> _getObjectsToSearch() {
571                Vector<Entity> result = new Vector<Entity>();
572                try {
573                        CacheManager manager = CacheManager.getInstance();
574                        Vector<KeplerLSID> cachedLsids = manager.getCachedLsids();
575                        for (KeplerLSID lsid : cachedLsids) {
576                                if (manager.getObject(lsid) instanceof ActorCacheObject){
577                                        ActorCacheObject aco = (ActorCacheObject) manager
578                                                .getObject(lsid);
579                                        ActorMetadata am = aco.getMetadata();
580                                        GraphicalActorMetadata gam = new GraphicalActorMetadata(am);
581                                        NamedObj obj = gam.getActorAsNamedObj(null);
582                                        // relax to just named obj here?
583                                        if (obj instanceof Entity){
584                                                result.add((Entity)obj);
585                                        }
586                                }
587                        }
588                } catch (Exception e) {
589                        e.printStackTrace();
590                }
591                return result;
592        }
593
594    /**
595     * Performs the cancel button operation.
596     */
597    private void _doClose() {
598        _actorTable.dispose();
599        _inPortTable.dispose();
600        _outPortTable.dispose();
601        dispose();
602    }
603
604    /**
605     * Really shut the thing down.
606     */
607    private void _close() {
608        dispose();
609    }
610
611    private class _ResultTableModel extends AbstractTableModel {
612        // private members
613        private String[] tableHeader = { "Object Name", "Object Type" }; // two
614        // columns
615        private Vector tableData = new Vector(); // vector of NamedObj
616
617        /**
618         * get the column count
619         */
620        public int getColumnCount() {
621            return tableHeader.length;
622        }
623
624        /**
625         * get the row cound
626         */
627        public int getRowCount() {
628            return tableData.size();
629        }
630
631        /**
632         * get the column name
633         */
634        public String getColumnName(int columnIndex) {
635            return tableHeader[columnIndex];
636        }
637
638        /**
639         * get the value of a cell
640         */
641        public Object getValueAt(int rowIndex, int columnIndex) {
642            if (!validRowIndex(rowIndex))
643                return null;
644            NamedObj obj = (NamedObj) tableData.elementAt(rowIndex);
645            if (rowIndex == 0)
646                return obj.getName();
647            if (rowIndex == 1)
648                return _getSimpleName(obj.getClass());
649            return null;
650        }
651
652        /**
653         * results not editable
654         */
655        public boolean isCellEditable(int rowIndex, int columnIndex) {
656            return false;
657        }
658
659        /**
660         * sets the value in the column. checks that the term is valid?
661         */
662        public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
663            // if(!validRowIndex(rowIndex))
664            // return;
665            // Object[] row = (Object []) tableData.elementAt(rowIndex);
666            // row[columnIndex] = aValue;
667            // fireTableChanged(new TableModelEvent(this)); // change event
668        }
669
670        /**
671         * Insert a new named ontology class into the model
672         */
673        public void insertRow(NamedObj obj) {
674            if (rowExists(obj))
675                return;
676            tableData.add(obj);
677        }
678
679        /**
680         * @return True if the table contains the given class, and false
681         *         otherwise.
682         */
683        public boolean containsRow(NamedObj obj) {
684            return rowExists(obj);
685        }
686
687        /**
688         * @return a list of rows of non-empty Object arrays of arity 2
689         *         (Object[2])
690         */
691        public Iterator getRows() {
692            return tableData.iterator();
693        }
694
695        /**
696         * @return the first empty row or -1 if no empties
697         */
698        public int firstEmptyRow() {
699            int index = 0;
700            for (Iterator iter = tableData.iterator(); iter.hasNext(); index++) {
701                Object[] row = (Object[]) iter.next();
702                if (row[0] == null && row[1] == null)
703                    return index;
704            }
705            return -1;
706        }
707
708        /**
709         * @return true if the row already exists
710         */
711        private boolean rowExists(NamedObj obj) {
712            return tableData.contains(obj);
713        }
714
715        /**
716         * remove selected row
717         */
718        public void removeRow(int rowIndex) {
719            if (!validRowIndex(rowIndex))
720                return;
721            tableData.removeElementAt(rowIndex);
722            fireTableChanged(new TableModelEvent(this)); // change event
723        }
724
725        /**
726         * check that we are at a valid row
727         */
728        private boolean validRowIndex(int rowIndex) {
729            if (rowIndex >= 0 && rowIndex < getRowCount())
730                return true;
731            return false;
732        }
733
734        /**
735         * returns the "simple" name of the class
736         */
737        private String _getSimpleName(Class c) {
738            StringTokenizer st = new StringTokenizer(c.getName(), ".", false);
739            while (st.hasMoreTokens()) {
740                String str = st.nextToken();
741                if (!st.hasMoreTokens())
742                    return str;
743            }
744            return null;
745        }
746
747    };
748
749    /**
750     * Main method for testing the dialog.
751     * 
752     * @param args
753     *            the arguments to the program
754     */
755    public static void main(String[] args) {
756        try {
757            // a composite "wrapper"
758            TypedCompositeActor swf = new TypedCompositeActor();
759
760            // windows look and feel
761            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
762            SemanticSearchDialog dialog = new SemanticSearchDialog(null, swf);
763            dialog.setVisible(true);
764        } catch (Exception e) {
765            e.printStackTrace();
766        }
767    }
768
769    /** Private members */
770
771    private SemanticTypeTable _actorTable; // the sem types for actor search
772    // criteria
773    private SemanticTypeTable _inPortTable; // the sem types for input
774    private SemanticTypeTable _outPortTable; // the sem types for output
775    private TypedAtomicActor _searchActor; // dummy actor for attaching search
776    // criteria
777    private TypedIOPort _inSearchPort; // dummy port for attaching search
778    // criteria
779    private TypedIOPort _outSearchPort; // dummy port for attaching search
780    // criteria
781    private JCheckBox _chkActorSearch; // check box for using actor search
782    private JCheckBox _chkInPortSearch; // check box for using input search
783    private JCheckBox _chkOutPortSearch; // check box for using output search
784    private PTree _resultTree; // stores results in the tree
785    private JButton _btnSearch; // search btton
786    private JButton _btnClose; // close dialog button
787    private JLabel _lblQueryStatus; // status of query
788    private Frame _owner; // the owner of this dialog
789    private NamedObj _namedObj; // the canvas
790}