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
030// LibraryIndex.item()
031// LibraryIndexComponentItem.getLSID()
032// CacheManager.getInstance().getObject(lsid)
033// ActorCacheObject.getMetadata().getActorAsNamedObject()
034// ActorCacheObject aco = (ActorCacheObject)cacheMan.getObject(new KeplerLSID(lsidString));
035// ActorMetadata am = aco.getMetadata();
036// NamedObj no = am.getActorAsNamedObj(null);
037
038package org.kepler.sms.gui;
039
040import java.awt.Dimension;
041import java.awt.Frame;
042import java.awt.event.ActionEvent;
043import java.awt.event.ActionListener;
044import java.util.Iterator;
045import java.util.Vector;
046
047import javax.swing.BorderFactory;
048import javax.swing.Box;
049import javax.swing.BoxLayout;
050import javax.swing.JButton;
051import javax.swing.JDialog;
052import javax.swing.JLabel;
053import javax.swing.JOptionPane;
054import javax.swing.JPanel;
055import javax.swing.JScrollPane;
056
057import org.kepler.gui.GraphicalActorMetadata;
058import org.kepler.objectmanager.ActorMetadata;
059import org.kepler.objectmanager.cache.ActorCacheObject;
060import org.kepler.objectmanager.cache.CacheManager;
061import org.kepler.objectmanager.lsid.KeplerLSID;
062import org.kepler.sms.SMSServices;
063import org.kepler.sms.SemanticTypeManager;
064
065import ptolemy.kernel.ComponentEntity;
066import ptolemy.kernel.util.NamedObj;
067import ptolemy.kernel.util.Workspace;
068import ptolemy.moml.EntityLibrary;
069import ptolemy.vergil.tree.EntityTreeModel;
070import ptolemy.vergil.tree.PTree;
071import ptolemy.vergil.tree.VisibleTreeModel;
072
073/**
074 */
075public class SuggestCompatibleOperation {
076
077        // //////////////////////////////////////////////////////////////////////
078        // PUBLIC MEMBERS
079
080        public static int OUTPUT = 1;
081        public static int INPUT = 2;
082        public static int COMPONENTS = 3;
083
084        // //////////////////////////////////////////////////////////////////////
085        // PUBLIC CONSTRUCTORS
086
087        /**
088         * Constructor
089         */
090        public SuggestCompatibleOperation(Frame owner, NamedObj entity, int op) {
091                _owner = owner;
092                _entity = entity;
093                // check for op type
094                if (op == OUTPUT)
095                        _suggestOutput();
096                else if (op == INPUT)
097                        _suggestInput();
098                else if (op == COMPONENTS)
099                        _suggestComponents();
100        }
101
102        // //////////////////////////////////////////////////////////////////////
103        // PRIVATE METHODS
104
105        /**
106         * Private method for performing output operation
107         */
108        private void _suggestOutput() {
109                // make sure output types exist
110                if (SMSServices.getAllOutputSemanticTypes(_entity).size() == 0) {
111                        String msg = "No output semantic type annotations defined"
112                                        + " for this component.";
113                        JOptionPane.showMessageDialog(_owner, msg,
114                                        "Suggest Compatible Outputs", JOptionPane.ERROR_MESSAGE);
115                        return;
116                }
117                // do the input search
118                _searchForInput();
119                if (_components.size() == 0) {
120                        String msg = "No compatible components found in actor library"
121                                        + " for output types.";
122                        JOptionPane.showMessageDialog(_owner, msg,
123                                        "Suggest Compatible Outputs", JOptionPane.ERROR_MESSAGE);
124                        return;
125                }
126                // display results (if any)
127                _displayResults();
128        }
129
130        /**
131         * Private method for performing input operation
132         */
133        private void _suggestInput() {
134                // make sure output types exist
135                if (SMSServices.getAllInputSemanticTypes(_entity).size() == 0) {
136                        String msg = "No input semantic type annotations defined for"
137                                        + " this component.";
138                        JOptionPane.showMessageDialog(_owner, msg,
139                                        "Suggest Compatible Outputs", JOptionPane.ERROR_MESSAGE);
140                        return;
141                }
142                // do the input search
143                _searchForOutput();
144                if (_components.size() == 0) {
145                        String msg = "No compatible components found in actor library"
146                                        + " for output types.";
147                        JOptionPane.showMessageDialog(_owner, msg,
148                                        "Suggest Compatible Outputs", JOptionPane.ERROR_MESSAGE);
149                        return;
150                }
151                // display results (if any)
152                _displayResults();
153        }
154
155        /**
156         * Private method for performing component operation
157         */
158        private void _suggestComponents() {
159                // make sure the actor has at least a semantic type (category or port)
160                if (SMSServices.getAllInputSemanticTypes(_entity).size() == 0
161                                && SMSServices.getAllOutputSemanticTypes(_entity).size() == 0
162                                && SMSServices.getActorSemanticTypes(_entity).size() == 0) {
163                        String msg = "No semantic type annotations are defined"
164                                        + " for this component.";
165                        JOptionPane.showMessageDialog(_owner, msg,
166                                        "Suggest Compatible Outputs", JOptionPane.ERROR_MESSAGE);
167                        return;
168                }
169                // do the input search
170                _searchForComponents();
171                if (_components.size() == 0) {
172                        String msg = "No compatible components found in actor library.";
173                        JOptionPane.showMessageDialog(_owner, msg,
174                                        "Suggest Compatible Outputs", JOptionPane.ERROR_MESSAGE);
175                        return;
176                }
177                // display results (if any)
178                _displayResults();
179        }
180
181        /**
182         * Private method for performing search for matching input
183         * 
184         * @param semTypes
185         *            a set of semantic types to search for
186         * @return a set of matching components
187         */
188        private void _searchForInput() {
189                // build hashtable of port types
190                _buildOutputPortMgr();
191                // get the list of components
192                _buildInputComponents();
193                // match component types to those in port manager
194                _filterInputComponents();
195        }
196
197        /**
198         * Private method for performing search for matching input
199         * 
200         * @param semTypes
201         *            a set of semantic types to search for
202         * @return a set of matching components
203         */
204        private void _searchForOutput() {
205                // build hashtable of port types
206                _buildInputPortMgr();
207                // get the list of components
208                _buildOutputComponents();
209                // match component types to those in port manager
210                _filterOutputComponents();
211        }
212
213        private void _searchForComponents() {
214                // find components with compatible semantic type categorizations
215                _buildSimilarComponents();
216                _filterSimilarComponents();
217        }
218
219        /**
220         * Private method to build a hash table of ports (keys) and their semantic
221         * types (as named ont classes)
222         */
223        private void _buildOutputPortMgr() {
224                Iterator portIter = SMSServices.getAllOutputPorts(_entity).iterator();
225                while (portIter.hasNext()) {
226                        Object port = portIter.next();
227                        _portMgr.addObject(port);
228                        Iterator typeIter = SMSServices.getPortSemanticTypes(port)
229                                        .iterator();
230                        while (typeIter.hasNext())
231                                _portMgr.addType(port, typeIter.next());
232                }
233                _portMgr.pruneUntypedObjects();
234        }
235
236        /**
237         * Private method to build a hash table of ports (keys) and their semantic
238         * types (as named ont classes)
239         */
240        private void _buildInputPortMgr() {
241                Iterator portIter = SMSServices.getAllInputPorts(_entity).iterator();
242                while (portIter.hasNext()) {
243                        Object port = portIter.next();
244                        _portMgr.addObject(port);
245                        Iterator typeIter = SMSServices.getPortSemanticTypes(port)
246                                        .iterator();
247                        while (typeIter.hasNext())
248                                _portMgr.addType(port, typeIter.next());
249                }
250                _portMgr.pruneUntypedObjects();
251        }
252
253        /**
254         * Private method to build a list of components to search over. Components
255         * are those within the actor library that are not entity libraries and that
256         * have at least one output semantic type. The result is stored in
257         * _components
258         */
259        private void _buildOutputComponents() {
260                Iterator<NamedObj> nodes = _getComponents().iterator();
261                while (nodes.hasNext()) {
262                        NamedObj node = nodes.next();
263                        // check if it has any input semantic types
264                        if (SMSServices.getAllOutputSemanticTypes(node).size() != 0) {
265                                if (!_componentExists(node))
266                                        _components.add(node);
267                        }
268                }
269        }
270
271        /**
272         * Private method to build a list of components to search over. Components
273         * are those within the actor library that are not entity libraries and that
274         * have at least one input semantic type. The result is stored in
275         * _components
276         */
277        private void _buildInputComponents() {
278                Iterator<NamedObj> nodes = _getComponents().iterator();
279                while (nodes.hasNext()) {
280                        NamedObj node = nodes.next();
281                        // check if it has any input semantic types
282                        if (SMSServices.getAllInputSemanticTypes(node).size() != 0) {
283                                if (!_componentExists(node))
284                                        _components.add(node);
285                        }
286                }
287        }
288
289        /**
290         * Private method to build a list of components to search over. Components
291         * are those within the actor library that are not entity libraries and that
292         * have at least one input, output, or category semantic type. The result is
293         * stored in _components
294         */
295        private void _buildSimilarComponents() {
296                try {
297                        Iterator<NamedObj> nodes = _getComponents().iterator();
298                        while (nodes.hasNext()) {
299                                NamedObj node = nodes.next();
300                                // check if it has any input semantic types
301                                if (SMSServices.getActorSemanticTypes(node).size() != 0
302                                                || SMSServices.getAllInputSemanticTypes(node).size() != 0
303                                                || SMSServices.getAllOutputSemanticTypes(node).size() != 0) {
304                                        if (!_componentExists(node))
305                                                _components.add(node);
306                                }
307                        }
308                } catch (Exception e) {
309                        e.printStackTrace();
310                }
311
312        }
313
314        /**
315         * Private method that retrieves the components in the library as
316         * NamedObj's, i.e., actual, instantiated actors.
317         */
318        private Vector<NamedObj> _getComponents() {
319                Vector<NamedObj> result = new Vector<NamedObj>();
320                try {
321
322                        CacheManager manager = CacheManager.getInstance();
323                        Vector<KeplerLSID> cachedLsids = manager.getCachedLsids();
324                        for (KeplerLSID lsid : cachedLsids) {
325                                try{
326                                        ActorCacheObject aco = (ActorCacheObject) CacheManager
327                                                .getInstance().getObject(lsid);
328                                        ActorMetadata am = aco.getMetadata();
329                                        GraphicalActorMetadata gam = new GraphicalActorMetadata(am);
330                                        NamedObj node = gam.getActorAsNamedObj(null);
331                                        result.add(node);
332                                }
333                                catch(ClassCastException cce){
334                                        // can no longer assume all items in library can be cast
335                                        // to ActorCacheObject. just skip these.
336                                }
337                        }
338                } catch (Exception e) {
339                        e.printStackTrace();
340                }
341                return result;
342        }
343
344        /**
345         * Checks for like named components
346         */
347        private boolean _componentExists(NamedObj obj) {
348                String str = obj.getName();
349                Iterator<NamedObj> compIter = _components.iterator();
350                while (compIter.hasNext()) {
351                        NamedObj comp = compIter.next();
352                        if (str.equals(comp.getName()))
353                                return true;
354                }
355                return false;
356        }
357
358        /**
359         * This is the main algorithm for checking. Looks at output ports
360         * individually, and checks if any input ports are supertypes. Note this
361         * could be further generalized to support additional types of checking,
362         * e.g., across output/input ports.
363         */
364        private void _filterOutputComponents() {
365                Vector<NamedObj> results = new Vector<NamedObj>();
366
367                // nothing to filter
368                if (_components.size() == 0)
369                        return;
370
371                Iterator<NamedObj> compIter = _components.iterator();
372                while (compIter.hasNext()) {
373                        boolean comp_is_match = false;
374                        NamedObj comp = compIter.next();
375                        // for each input port, check comp output ports
376                        Iterator inPortIter = _portMgr.getObjects().iterator();
377                        while (!comp_is_match && inPortIter.hasNext()) {
378                                // get the input sem types
379                                Vector inSemTypes = _portMgr.getTypes(inPortIter.next());
380                                // get the output ports
381                                Iterator outPortIter = SMSServices.getAllOutputPorts(
382                                                comp).iterator();
383                                while (!comp_is_match && outPortIter.hasNext()) {
384                                        // get the input sem types
385                                        Vector outSemTypes = SMSServices
386                                                        .getPortSemanticTypes(outPortIter.next());
387                                        if (SMSServices.compare(outSemTypes, inSemTypes) == SMSServices.COMPATIBLE) {
388                                                comp_is_match = true;
389                                                results.add(comp);
390                                        }
391                                }
392                        }
393                }
394                _components = results;
395        }
396
397        /**
398         * This is the main algorithm for checking. Looks at output ports
399         * individually, and checks if any input ports are supertypes. Note this
400         * could be further generalized to support additional types of checking,
401         * e.g., across output/input ports.
402         */
403        private void _filterInputComponents() {
404                Vector<NamedObj> results = new Vector<NamedObj>();
405
406                // nothing to filter
407                if (_components.size() == 0)
408                        return;
409
410                Iterator<NamedObj> compIter = _components.iterator();
411                while (compIter.hasNext()) {
412                        boolean comp_is_match = false;
413                        NamedObj comp = compIter.next();
414                        // for each output port, check comp input ports
415                        Iterator outPortIter = _portMgr.getObjects().iterator();
416                        while (!comp_is_match && outPortIter.hasNext()) {
417                                // get the output sem types
418                                Vector outSemTypes = _portMgr.getTypes(outPortIter.next());
419                                // get the input ports
420                                Iterator inPortIter = SMSServices.getAllInputPorts(
421                                                comp).iterator();
422                                while (!comp_is_match && inPortIter.hasNext()) {
423                                        // get the input sem types
424                                        Vector inSemTypes = SMSServices
425                                                        .getPortSemanticTypes(inPortIter.next());
426                                        if (SMSServices.compare(outSemTypes, inSemTypes) == SMSServices.COMPATIBLE) {
427                                                comp_is_match = true;
428                                                results.add(comp);
429                                        }
430                                }
431                        }
432                }
433                _components = results;
434        }
435
436        private void _filterSimilarComponents() {
437                if (SMSServices.getActorSemanticTypes(_entity).size() != 0)
438                        _filterSimilarComponentType();
439                if (SMSServices.getAllInputSemanticTypes(_entity).size() != 0)
440                        _filterSimilarComponentInput();
441                if (SMSServices.getAllOutputSemanticTypes(_entity).size() != 0)
442                        _filterSimilarComponentOutput();
443        }
444
445        private void _filterSimilarComponentType() {
446                Vector<NamedObj> results = new Vector<NamedObj>();
447                // nothing to filter
448                if (_components.size() == 0)
449                        return;
450                Iterator<NamedObj> compIter = _components.iterator();
451                while (compIter.hasNext()) {
452                        boolean comp_is_match = false;
453                        NamedObj comp = compIter.next();
454                        Vector subTypes = SMSServices.getActorSemanticTypes(comp);
455                        Iterator typeIter = SMSServices.getActorSemanticTypes(_entity)
456                                        .iterator();
457                        while (!comp_is_match && typeIter.hasNext()) {
458                                Vector superTypes = new Vector();
459                                superTypes.add(typeIter.next());
460                                if (SMSServices.compare(subTypes, superTypes) == SMSServices.COMPATIBLE) {
461                                        comp_is_match = true;
462                                        results.add(comp);
463                                }
464                        }
465                }
466                _components = results;
467        }
468
469        private void _filterSimilarComponentInput() {
470                Vector<NamedObj> results = new Vector<NamedObj>();
471                // nothing to filter
472                if (_components.size() == 0)
473                        return;
474                SemanticTypeManager manager = _inputPortManager();
475                Iterator<NamedObj> compIter = _components.iterator();
476                while (compIter.hasNext()) {
477                        boolean comp_is_match = false;
478                        NamedObj comp = compIter.next();
479                        // for each output port, check comp input ports
480                        Iterator portIter = manager.getObjects().iterator();
481                        while (!comp_is_match && portIter.hasNext()) {
482                                // get the search item input sem types
483                                Vector superTypes = manager.getTypes(portIter.next());
484                                // get the component input ports
485                                Iterator inPortIter = SMSServices.getAllInputPorts(
486                                                (NamedObj) comp).iterator();
487                                while (!comp_is_match && inPortIter.hasNext()) {
488                                        // get the input sem types
489                                        Vector subTypes = SMSServices
490                                                        .getPortSemanticTypes(inPortIter.next());
491                                        if (SMSServices.compare(subTypes, superTypes) == SMSServices.COMPATIBLE) {
492                                                comp_is_match = true;
493                                                results.add(comp);
494                                        }
495                                }
496                        }
497                }
498                _components = results;
499        }
500
501        private void _filterSimilarComponentOutput() {
502                Vector<NamedObj> results = new Vector<NamedObj>();
503                // nothing to filter
504                if (_components.size() == 0)
505                        return;
506                SemanticTypeManager manager = _outputPortManager();
507                Iterator<NamedObj> compIter = _components.iterator();
508                while (compIter.hasNext()) {
509                        boolean comp_is_match = false;
510                        NamedObj comp = compIter.next();
511                        // for each output port, check comp input ports
512                        Iterator portIter = manager.getObjects().iterator();
513                        while (!comp_is_match && portIter.hasNext()) {
514                                // get the search item input sem types
515                                Vector subTypes = manager.getTypes(portIter.next());
516                                // get the component output ports
517                                Iterator outPortIter = SMSServices.getAllOutputPorts(
518                                                (NamedObj) comp).iterator();
519                                while (!comp_is_match && outPortIter.hasNext()) {
520                                        // get the output sem types
521                                        Vector superTypes = SMSServices
522                                                        .getPortSemanticTypes(outPortIter.next());
523                                        if (SMSServices.compare(subTypes, superTypes) == SMSServices.COMPATIBLE) {
524                                                comp_is_match = true;
525                                                results.add(comp);
526                                        }
527                                }
528                        }
529                }
530                _components = results;
531        }
532
533        private SemanticTypeManager _inputPortManager() {
534                SemanticTypeManager manager = new SemanticTypeManager();
535                Iterator portIter = SMSServices.getAllInputPorts(_entity).iterator();
536                while (portIter.hasNext()) {
537                        Object port = portIter.next();
538                        manager.addObject(port);
539                        Iterator typeIter = SMSServices.getPortSemanticTypes(port)
540                                        .iterator();
541                        while (typeIter.hasNext())
542                                manager.addType(port, typeIter.next());
543                }
544                manager.pruneUntypedObjects();
545                return manager;
546        }
547
548        private SemanticTypeManager _outputPortManager() {
549                SemanticTypeManager manager = new SemanticTypeManager();
550                Iterator portIter = SMSServices.getAllOutputPorts(_entity).iterator();
551                while (portIter.hasNext()) {
552                        Object port = portIter.next();
553                        manager.addObject(port);
554                        Iterator typeIter = SMSServices.getPortSemanticTypes(port)
555                                        .iterator();
556                        while (typeIter.hasNext())
557                                manager.addType(port, typeIter.next());
558                }
559                manager.pruneUntypedObjects();
560                return manager;
561        }
562
563        /**
564         * Private method to draw a simple dialog of search matches.
565         */
566        private void _displayResults() {
567                // the dialog for viewing results
568                final JDialog dialog = new JDialog();
569                dialog.setTitle("Search Results");
570
571                // overall panel for the dialog
572                JPanel panel = new JPanel();
573                panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
574                panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
575
576                // set up the button
577                JButton closeBtn = new JButton("Close");
578                closeBtn.setActionCommand("close");
579                closeBtn.addActionListener(new ActionListener() {
580                        public void actionPerformed(ActionEvent e) {
581                                if (e.getActionCommand().equals("close"))
582                                        dialog.dispose();
583                        }
584                });
585
586                // label
587                JPanel panel1 = new JPanel();
588                panel1.setLayout(new BoxLayout(panel1, BoxLayout.X_AXIS));
589                panel1.add(new JLabel("Suggeted Components (drag to canvas):"));
590                panel1.add(Box.createHorizontalGlue());
591
592                // close button
593                JPanel panel2 = new JPanel();
594                panel2.setLayout(new BoxLayout(panel2, BoxLayout.X_AXIS));
595                panel2.add(Box.createHorizontalGlue());
596                panel2.add(closeBtn);
597
598                // add everything to the main panel
599                panel.add(panel1);
600                panel.add(Box.createRigidArea(new Dimension(0, 5)));
601                panel.add(new JScrollPane(_buildResultTree()));
602                panel.add(Box.createRigidArea(new Dimension(0, 10)));
603                panel.add(panel2);
604
605                // add main panel to the dialog and show it
606                dialog.add(panel);
607                dialog.pack();
608                dialog.show();
609        }
610
611        /**
612         * Construct the visual tree widget showing a list of results.
613         */
614        private PTree _buildResultTree() {
615                EntityLibrary root = new EntityLibrary();
616                Workspace workspace = root.workspace();
617                EntityTreeModel model = new VisibleTreeModel(root);
618
619                Iterator<NamedObj> compIter = _components.iterator();
620                while (compIter.hasNext()) {
621                        try {
622                                NamedObj entity = compIter.next();
623                                // add to the tree
624                                NamedObj obj = (NamedObj) entity.clone(workspace);
625                                if (obj instanceof ComponentEntity)
626                                        ((ComponentEntity) obj).setContainer(root);
627                        } catch (Exception e) {
628                                e.printStackTrace();
629                        }
630                }
631
632                PTree resultTree = new PTree(model);
633                resultTree.setRootVisible(false);
634                resultTree.setMinimumSize(new Dimension(120, 100));
635                resultTree.setMaximumSize(new Dimension(120, 100));
636
637                return resultTree;
638        }
639
640        // //////////////////////////////////////////////////////////////////////////////
641        // PRIVATE MEMBERS
642
643        private Frame _owner;
644        private NamedObj _entity;
645        private int _op;
646
647        private Vector<NamedObj> _components = new Vector<NamedObj>();
648        private SemanticTypeManager _portMgr = new SemanticTypeManager();
649
650}