001/*
002 * Copyright (c) 2009-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2013-01-23 22:17:38 +0000 (Wed, 23 Jan 2013) $' 
007 * '$Revision: 31362 $'
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.gui;
031
032import java.awt.BorderLayout;
033import java.awt.Dimension;
034import java.awt.event.MouseListener;
035import java.util.List;
036
037import javax.swing.JLabel;
038import javax.swing.JPanel;
039import javax.swing.JScrollPane;
040import javax.swing.JTree;
041import javax.swing.event.TreeModelListener;
042import javax.swing.event.TreeSelectionListener;
043import javax.swing.tree.TreeModel;
044import javax.swing.tree.TreePath;
045import javax.swing.tree.TreeSelectionModel;
046
047import org.apache.commons.logging.Log;
048import org.apache.commons.logging.LogFactory;
049import org.kepler.gui.popups.LibraryPopupListener;
050import org.kepler.moml.DownloadableKAREntityLibrary;
051import org.kepler.moml.FolderEntityLibrary;
052import org.kepler.moml.KAREntityLibrary;
053import org.kepler.moml.KARErrorEntityLibrary;
054import org.kepler.moml.OntologyEntityLibrary;
055import org.kepler.moml.RemoteKARErrorEntityLibrary;
056import org.kepler.moml.RemoteRepositoryEntityLibrary;
057import org.kepler.moml.SearchEntityLibrary;
058import org.kepler.objectmanager.library.LibraryManager;
059import org.kepler.util.StaticResources;
060
061import ptolemy.kernel.ComponentEntity;
062import ptolemy.kernel.CompositeEntity;
063import ptolemy.kernel.util.Attribute;
064import ptolemy.kernel.util.IllegalActionException;
065import ptolemy.kernel.util.NameDuplicationException;
066import ptolemy.kernel.util.NamedObj;
067import ptolemy.kernel.util.StringAttribute;
068import ptolemy.kernel.util.Workspace;
069import ptolemy.moml.EntityLibrary;
070import ptolemy.vergil.tree.PTree;
071import ptolemy.vergil.tree.VisibleTreeModel;
072
073/**
074 * This class builds the search results by traversing the tree and trimming any
075 * sub-nodes that do not have a result in them. This leaves a minimum tree with
076 * only the search results present.
077 * 
078 *@author Chad Berkley
079 *@author Shawn Bowers 
080 *@author Aaron Schultz (... last editor ...)
081 */
082public class ResultTreeRebuilder extends LibrarySearchResultPane {
083        private static final Log log = LogFactory.getLog(ResultTreeRebuilder.class
084                        .getName());
085        private static final boolean isDebugging = log.isDebugEnabled();
086
087        private VisibleTreeModel trimmedLibrary;
088        private PTree resultsTree;
089        private Workspace workspace;
090        private JPanel resultCounterPane;
091        private JLabel resultCounterLabel;
092        private TreeSelectionListener treeSingleSelectionlistener = null;
093
094        /**
095         * the constructor passes in the library to highlight the results in and the
096         * results to highlight. if results is null, the tree is built fully
097         * collapsed with no highlights.
098         * 
099         *@param library
100         *            the library to highlight the results in
101         *@param results
102         *            the results to highlight
103         *@exception IllegalActionException
104         *                Description of the Exception
105         */
106        public ResultTreeRebuilder(PTree library, LibrarySearchResults results)
107                        throws IllegalActionException {
108                super(library, results);
109                this.workspace = ((CompositeEntity) library.getModel().getRoot())
110                                .workspace();
111        }
112
113        /** Update the tree to display only a specific root. */
114        public void update(EntityLibrary root) throws IllegalActionException {
115                if (isDebugging) {
116                        log.debug("update(" + root.getName() + " "
117                                        + root.getClass().getName() + ")");
118                }
119                this.removeAll();
120
121                trimmedLibrary = new VisibleTreeModel(root);
122                
123                AnnotatedPTree rTree = new AnnotatedPTree(trimmedLibrary, this);
124                MouseListener mListener = new LibraryPopupListener(rTree);
125                rTree.setMouseListener(mListener);
126                if(treeSingleSelectionlistener != null)
127                {
128                  rTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
129                  rTree.addTreeSelectionListener(treeSingleSelectionlistener);
130                }
131                rTree.initAnotatedPTree();
132                resultsTree = rTree;
133
134                JScrollPane newpane = new JScrollPane(resultsTree);
135                newpane.setPreferredSize(new Dimension(200, 200));
136                this.add(newpane, BorderLayout.CENTER);
137
138                // add the search results counter stuff
139                resultCounterPane = new JPanel();
140                resultCounterPane.setBackground(TabManager.BGCOLOR);
141                resultCounterPane.setLayout(new BorderLayout());
142                resultCounterLabel = new JLabel("");
143                resultCounterPane.removeAll();
144                resultCounterPane.add(resultCounterLabel, BorderLayout.CENTER);
145                this.add(resultCounterPane, BorderLayout.SOUTH);
146
147                this.repaint();
148                this.validate();
149
150        }
151        
152        
153        /**
154         * Set the tree selection listener (single selection mode will be set as well)
155         * @param listener
156         *              the listener will be set
157         */
158        public void setSingleTreeSelectionListener(TreeSelectionListener listener)
159        {
160          this.treeSingleSelectionlistener = listener;
161        }
162        
163        /**
164         * Get the result tree
165         * @return
166         */
167        public JTree getResultTree()
168        {
169          return resultsTree;
170        }
171
172        /**
173         * this method allows the search results to be updated in the panel
174         * 
175         *@param results
176         *            the results to update to
177         *@exception IllegalActionException
178         *                Description of the Exception
179         */
180        public void update(LibrarySearchResults results)
181                        throws IllegalActionException {
182                if (isDebugging) {
183                        log.debug("update(" + results + ")");
184                }
185                this.results = results;
186                int resultcount;
187                if (results == null || results.size() == 0) {
188                        resultcount = 0;
189                } else {
190                        resultcount = results.size();
191                }
192
193                this.removeAll();
194
195                // add the results if there are any
196                if (resultcount > 0) {
197                        // add the results tree.
198                        EntityLibrary newRoot = new SearchEntityLibrary(workspace);
199
200                        try {
201                                newRoot.setName(
202                                                StaticResources.getDisplayString("components.searchResults", "Search Results"));
203                        } catch (IllegalActionException iae) {
204                                throw iae;
205                        } catch (NameDuplicationException nde) {
206                                throw new IllegalActionException("name duplication exception: "
207                                                + nde);
208                        }
209                        buildResultTree(newRoot);
210                        trimmedLibrary = new VisibleTreeModel(newRoot);
211                        
212                        AnnotatedPTree rTree = new AnnotatedPTree(trimmedLibrary, this);
213                        MouseListener mListener = new LibraryPopupListener(rTree);
214                        rTree.setMouseListener(mListener);
215                        if(treeSingleSelectionlistener != null)
216                        {
217                            rTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
218                            rTree.addTreeSelectionListener(treeSingleSelectionlistener);
219                        }
220                        rTree.initAnotatedPTree();
221                        resultsTree = rTree;
222                        rTree.setShowsRootHandles(false);
223                        
224                        JScrollPane newpane = new JScrollPane(resultsTree);
225                        newpane.setPreferredSize(new Dimension(200, 200));
226                        this.add(newpane, BorderLayout.CENTER);
227                        expandAll(resultsTree);
228                } else {
229                        // if there are no results, just add the library back
230                        if (library.getModel() != null) {
231                                library.setModel(new NoRemoteComponentsTreeModel(library.getModel()));
232                        }
233                        this.add(new JScrollPane(library), BorderLayout.CENTER);
234                }
235
236                // add the search results counter stuff
237                resultCounterPane = new JPanel();
238                resultCounterPane.setBackground(TabManager.BGCOLOR);
239                resultCounterPane.setLayout(new BorderLayout());
240                resultCounterLabel = new JLabel(
241                                resultcount 
242                                + " " 
243                                + StaticResources.getDisplayString("components.resultsFound", "results found."));
244                resultCounterPane.removeAll();
245                resultCounterPane.add(resultCounterLabel, BorderLayout.CENTER);
246                this.add(resultCounterPane, BorderLayout.SOUTH);
247
248                this.repaint();
249                this.validate();
250        }
251
252        /**
253         * clears the tree of any items contained by newRoot
254         * 
255         *@exception IllegalActionException
256         *                Description of the Exception
257         *
258        public void clearTree() throws IllegalActionException {
259                if (newRoot != null && newRoot.entityList() != null) {
260                        List children = newRoot.entityList();
261                        for (Object child : children ) {
262                                try {
263                                        if (child instanceof ComponentEntity) {
264                                                ComponentEntity childCE = (ComponentEntity) child;
265                                                childCE.setContainer(null);
266                                        } else if (child instanceof Attribute) {
267                                                Attribute childAtt = (Attribute) child;
268                                                childAtt.setContainer(null);
269                                        } else {
270                                                log.error("Unhandled library type: " + child.getClass());
271                                        }
272                                } catch (Exception e) {
273                                        // do nothing...just go onto the next node
274                                }
275                        }
276                }
277        }*/
278
279        /*
280         * build the result tree
281         */
282        /**
283         * Description of the Method
284         * 
285         *@exception IllegalActionException
286         *                Description of the Exception
287         */
288        private void buildResultTree(EntityLibrary newRoot) throws IllegalActionException {
289                if (isDebugging)
290                        log.debug("buildResultTree()");
291
292                // iterate over each treepath in results
293                for (int i = 0; i < results.size(); i++) {
294                        
295                        try {
296                                
297                                TreePath currentPath = results.getTreePath(i);
298                                if (isDebugging)
299                                        log.debug(currentPath);
300                                
301                                EntityLibrary treeCurrent = newRoot;
302                                for (int j = 1; j < currentPath.getPathCount(); j++) {
303
304                                        NamedObj pathCurrent = (NamedObj) currentPath.getPathComponent(j);
305                                        
306                                        if (pathCurrent instanceof EntityLibrary) {
307                                        
308                                                List<EntityLibrary> children = treeCurrent.entityList(EntityLibrary.class);
309                                                
310                                                boolean alreadyThere = false;
311                                                for (EntityLibrary child : children) {
312                                                        if (isDebugging) log.debug(child.getName());
313                                                        if (child.getName().equals(pathCurrent.getName())) {
314                                                                // this EntityLibrary is already there
315                                                                treeCurrent = child;
316                                                                alreadyThere = true;
317                                                                break;
318                                                        }
319                                                }
320                                                if (!alreadyThere) {
321                                                        // create it
322                                                        EntityLibrary newEntity = copyEntityLibrary((EntityLibrary) pathCurrent);
323                                                        setContainer(treeCurrent,newEntity);
324                                                        treeCurrent = newEntity;
325                                                }
326                                                
327                                        } else {
328                                                List<NamedObj> children = treeCurrent.entityList(NamedObj.class);
329                                                
330                                                boolean alreadyThere = false;
331                                                for (NamedObj child : children) {
332                                                        if (child.getName().equals(pathCurrent.getName())) {
333                                                                // this NamedObj is already there
334                                                                alreadyThere = true;
335                                                                break;
336                                                        }
337                                                }
338                                                if (!alreadyThere) {
339                                                        // create it
340                                                        NamedObj newEntity = cloneEntity((NamedObj)pathCurrent);
341                                                        setContainer(treeCurrent,newEntity);
342                                                        // Leaf node, all done
343                                                        break;
344                                                }
345                                        }
346                                }
347                                
348                        } catch (IllegalActionException iae) {
349                                throw new IllegalActionException(
350                                                "cannot build search result tree: " + iae);
351                        } catch (NameDuplicationException nde) {
352                                log.error("EXCEPTION CAUGHT: " + nde.getMessage());
353                        } catch (Exception e) {
354                                log.error("EXCEPTION CAUGHT: " + e.getMessage());
355                        }
356                } // end for loop
357        }
358
359        /**
360         * puts entity in container
361         * 
362         *@param container
363         *            The new container value
364         *@param entity
365         *            The new container value
366         *@exception NameDuplicationException
367         *                Description of the Exception
368         *@exception IllegalActionException
369         *                Description of the Exception
370         */
371        private static void setContainer(CompositeEntity container, NamedObj entity)
372                        throws NameDuplicationException, IllegalActionException {
373                if (entity instanceof Attribute) {
374                        ((Attribute) entity).setContainer(container);
375                } else if (entity instanceof ComponentEntity) {
376                        ((ComponentEntity) entity).setContainer(container);
377                }
378        }
379
380        /**
381         * 
382         * 
383         *@param entity
384         *            Description of the Parameter
385         *@return Description of the Return Value
386         *@exception IllegalActionException
387         *                Description of the Exception
388         */
389        private EntityLibrary copyEntityLibrary(EntityLibrary entity)
390                        throws IllegalActionException {
391                if (entity == null)
392                        return null;
393                try {
394                        if (isDebugging)
395                                log.debug("copyCompositeEntity(" + entity.getName() + ")");
396                        if (isDebugging)
397                                log.debug(entity.getClass().getName());
398                        EntityLibrary el = null;
399
400                        if (entity instanceof OntologyEntityLibrary) {
401                                el = new OntologyEntityLibrary(new CompositeEntity(
402                                                this.workspace), entity.getName());
403                        } else if (entity instanceof KAREntityLibrary) {
404                                el = new KAREntityLibrary(new CompositeEntity(
405                                                this.workspace), entity.getName());
406                        } else if (entity instanceof FolderEntityLibrary) {
407                                el = new FolderEntityLibrary(new CompositeEntity(
408                                                this.workspace), entity.getName());
409                        } else if (entity instanceof DownloadableKAREntityLibrary) {
410                                el = new DownloadableKAREntityLibrary(new CompositeEntity(
411                                                this.workspace), (DownloadableKAREntityLibrary) entity);
412                        } else if (entity instanceof RemoteRepositoryEntityLibrary) {
413                                el = new RemoteRepositoryEntityLibrary(new CompositeEntity(
414                                                this.workspace), entity.getName());
415                        } else if (entity instanceof KARErrorEntityLibrary) {
416                                el = new KARErrorEntityLibrary(new CompositeEntity(
417                                                this.workspace), entity.getName());
418                        } else if (entity instanceof RemoteKARErrorEntityLibrary) {
419                                el = new RemoteKARErrorEntityLibrary(new CompositeEntity(
420                                                this.workspace), entity.getName());
421                                ((RemoteKARErrorEntityLibrary) el).setKarXml(((RemoteKARErrorEntityLibrary) entity).getKarXml());
422                        } else {
423                                el = new EntityLibrary(new CompositeEntity(this.workspace),
424                                                entity.getName());
425                        }
426                        if (el == null) {
427                                log.warn("Unrecognized Composite Entity");
428                        } else {
429                            
430                            // set the display name to be the source's display name.
431                            // the display name may be different than the name returned
432                            // by getName(), e.g., the name may end in ",kar" but the
433                            // display name ends in ".kar".
434                            el.setDisplayName(entity.getDisplayName());
435                            
436                                int liid = LibraryManager.getLiidFor(entity);
437                                StringAttribute liidSA = new StringAttribute(el,
438                                                LibraryManager.LIID_LABEL);
439                                liidSA.setExpression("" + liid);
440                                if (isDebugging)
441                                        log.debug(el.getClass().getName());
442                        }
443                        return el;
444                } catch (IllegalActionException iae) {
445                        throw new IllegalActionException("cannot copy composite entity: "
446                                        + iae);
447                } catch (NameDuplicationException nde) {
448                        throw new IllegalActionException(
449                                        "cannot set container because the name "
450                                                        + "already exists: " + nde);
451                }
452        }
453
454        /**
455         * clone the entity into the new workspace.
456         * 
457         *@param entity
458         *            Description of the Parameter
459         *@return Description of the Return Value
460         *@exception IllegalActionException
461         *                Description of the Exception
462         */
463        // private ComponentEntity cloneEntity(ComponentEntity entity) throws
464        // IllegalActionException {
465        private NamedObj cloneEntity(NamedObj entity) throws IllegalActionException {
466                try {
467                        return (NamedObj) entity.clone(this.workspace);
468                } catch (java.lang.CloneNotSupportedException cnse) {
469                        throw new IllegalActionException("clone not supported: " + cnse);
470                }
471        }
472
473        /**
474         * A factory that creates the searcher to search the library
475         * 
476         *@author berkley
477         *@since February 17, 2005
478         */
479        public static class Factory extends LibrarySearchResultPaneFactory {
480                /**
481                 * Create an factory with the given name and container.
482                 * 
483                 *@param container
484                 *            The container.
485                 *@param name
486                 *            The name of the entity.
487                 *@exception IllegalActionException
488                 *                If the container is incompatible with this attribute.
489                 *@exception NameDuplicationException
490                 *                If the name coincides with an attribute already in the
491                 *                container.
492                 */
493                public Factory(NamedObj container, String name)
494                                throws IllegalActionException, NameDuplicationException {
495                        super(container, name);
496                }
497
498                /**
499                 * creates a ResultTreeBuilder and returns it.
500                 * 
501                 *@param library
502                 *            Description of the Parameter
503                 *@param results
504                 *            Description of the Parameter
505                 *@return A new LibraryPane that displays the library
506                 *@exception IllegalActionException
507                 *                Description of the Exception
508                 */
509                public LibrarySearchResultPane createLibrarySearchResultPane(
510                                PTree library, LibrarySearchResults results)
511                                throws IllegalActionException {
512                        return new ResultTreeRebuilder(library, results);
513                }
514        }
515
516        private class NoRemoteComponentsTreeModel implements TreeModel {
517                
518                private boolean isRemoteComponentsNode(Object node) {
519                        // TODO: A more robust test would be good here. Probably just make
520                        // a subclass of EntityLibrary used only for the
521                        // "Remote Components" node and then test with instanceof.
522                        return "ptolemy.moml.EntityLibrary {.kepler actor library.Remote Components}".equals(node.toString());
523                }
524                
525                public NoRemoteComponentsTreeModel(TreeModel model) {
526                        this.model = model;
527                        for (int i = 0; i < model.getChildCount(model.getRoot()); i++) {
528                                Object node = model.getChild(model.getRoot(), i);
529                                if (isRemoteComponentsNode(node)) {
530                                        this.remoteComponentsNode = node;
531                                        this.remoteComponentsNodeIndex = i;
532                                        
533                                }
534                        }
535                }
536                
537                public Object getRoot() {
538                        return model.getRoot();
539                }
540
541                public Object getChild(Object parent, int index) {
542                        return model.getChild(parent, index);
543                }
544
545                public int getChildCount(Object parent) {
546                        if (parent == getRoot() && remoteComponentsNode != null) {
547                                // Don't include the Remote Components
548                                return model.getChildCount(parent) - 1;
549                        }
550                        else {
551                                return model.getChildCount(parent);
552                        }
553                }
554
555                public boolean isLeaf(Object node) {
556                        return model.isLeaf(node);
557                }
558
559                public void valueForPathChanged(TreePath path, Object newValue) {
560                        model.valueForPathChanged(path, newValue);
561                }
562
563                public int getIndexOfChild(Object parent, Object child) {
564                        if (parent != getRoot() || remoteComponentsNode == null) {
565                                return model.getIndexOfChild(parent, child);
566                        }
567                        // No index translation required if index < remote component index
568                        int index = model.getIndexOfChild(parent, child);
569                        if (index > this.remoteComponentsNodeIndex) {
570                                return index - 1;
571                        }
572                        else if (index == this.remoteComponentsNodeIndex) {
573                                System.out.println("Shouldn't happen!");
574                                return 0;
575                        }
576                        else {
577                                return index;
578                        }
579                }
580
581                public void addTreeModelListener(TreeModelListener l) {
582                        model.addTreeModelListener(l);
583                }
584
585                public void removeTreeModelListener(TreeModelListener l) {
586                        model.removeTreeModelListener(l);
587                }
588
589                private TreeModel model;
590                private Object remoteComponentsNode = null;
591                private int remoteComponentsNodeIndex = -1;
592        }
593}