001/*
002 * Copyright (c) 2004-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2013-01-18 03:25:48 +0000 (Fri, 18 Jan 2013) $' 
007 * '$Revision: 31348 $'
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.objectmanager.repository;
031
032import java.util.ArrayList;
033import java.util.Arrays;
034import java.util.Collections;
035import java.util.HashMap;
036import java.util.Iterator;
037import java.util.List;
038import java.util.Map;
039import java.util.Vector;
040
041import javax.swing.JTree;
042import javax.swing.tree.TreePath;
043
044import org.apache.commons.logging.Log;
045import org.apache.commons.logging.LogFactory;
046import org.kepler.authentication.AuthenticationException;
047import org.kepler.gui.LibrarySearchPane;
048import org.kepler.gui.LibrarySearchResults;
049import org.kepler.gui.LibrarySearcher;
050import org.kepler.gui.RepositorySearcher;
051import org.kepler.kar.ModuleDependencyUtil;
052import org.kepler.kar.karxml.KarXml;
053import org.kepler.moml.DownloadableKAREntityLibrary;
054import org.kepler.moml.RemoteKARErrorEntityLibrary;
055import org.kepler.moml.RemoteRepositoryEntityLibrary;
056import org.kepler.objectmanager.cache.ActorCacheObject;
057import org.kepler.objectmanager.cache.CacheNamedObj;
058import org.kepler.objectmanager.cache.CacheObject;
059import org.kepler.sms.NamedOntClass;
060import org.kepler.sms.OntologyCatalog;
061
062import ptolemy.kernel.ComponentEntity;
063import ptolemy.kernel.CompositeEntity;
064import ptolemy.kernel.Entity;
065import ptolemy.kernel.util.ConfigurableAttribute;
066import ptolemy.kernel.util.IllegalActionException;
067import ptolemy.kernel.util.NameDuplicationException;
068import ptolemy.kernel.util.StringAttribute;
069import ptolemy.kernel.util.Workspace;
070import ptolemy.moml.EntityLibrary;
071
072/**
073 * this abstract class is should be extended by all classes that provide a
074 * search engine for the actory library. Any local variables in the extending
075 * classes should be initialized in the init method because it is called from
076 * the constructor of this class.
077 * 
078 *@author berkley
079 *@since November the ninth one thousand times two plus six
080 */
081public class EcogridRepositoryLibrarySearcher extends LibrarySearcher implements RepositorySearcher {
082        private static final Log log = LogFactory
083        .getLog(EcogridRepositoryLibrarySearcher.class.getName());
084        private static final boolean isDebugging = log.isDebugEnabled();
085
086        // the name of the repository
087        private String repositoryName = "defaultRepository"; // the default name.
088        // this can get reset in the constructor
089
090        // the repository
091        private Repository repository;
092        private boolean skipOntology = false;
093
094        /**
095         * constructor
096         * 
097         *@param library
098         *            Description of the Parameter
099         *@param searchPane
100         *            Description of the Parameter
101         */
102        public EcogridRepositoryLibrarySearcher(JTree library,
103                        LibrarySearchPane searchPane, String reposName)
104        throws IllegalActionException {
105                super(library, searchPane);
106                try {
107                        if (reposName != null) {
108                                repositoryName = reposName;
109                        }
110                        if (isDebugging) {
111                                log.debug("repositoryName: " + repositoryName);
112                        }
113                        repository = RepositoryManager.getInstance().getRepository(
114                                        repositoryName);
115                        if (isDebugging) {
116                                log.debug(repository.getClass());
117                        }
118                } catch (Exception e) {
119                        e.printStackTrace();
120                        throw new IllegalActionException(
121                                        "Could not get an instance of the "
122                                        + "repository manager: " + e.getMessage());
123                }
124
125        }
126
127        /**
128         * search a component hierarchy for a specific component in a specific
129         * component returns null if the component is not found
130         */
131        private CompositeEntity findEntity(String name, CompositeEntity entity) {
132                if (entity.getName().trim().equals(name.trim())) {
133                        return entity;
134                } else {
135                        Iterator pathItt = entity.entityList().iterator();
136                        while (pathItt.hasNext()) {
137                                Entity nextEntity = (Entity) pathItt.next();
138                                if (nextEntity instanceof CompositeEntity) {
139                                        CompositeEntity newEntity = findEntity(name,
140                                                        (CompositeEntity) nextEntity);
141                                        if (newEntity != null) {
142                                                return newEntity;
143                                        }
144                                }
145                        }
146                }
147                return null;
148        }
149
150        /**
151         * build a path from the ontology tree from a given NamedOntClass
152         */
153        private Object[] getTreePathObjectArray(NamedOntClass ontClass,
154                        Vector<String> v) {
155                Iterator<NamedOntClass> itt = ontClass.getNamedSuperClasses(false);
156                Object[] o = null;
157                if (!itt.hasNext()) {
158                        v.addElement(ontClass.getName());
159                        o = new Object[v.size()];
160                        for (int i = 0; i < v.size(); i++) {
161                                o[i] = v.elementAt(i);
162                        }
163                } else {
164                        while (itt.hasNext()) {
165                                NamedOntClass supClass = itt.next();
166                                v.addElement(ontClass.getName());
167                                return getTreePathObjectArray(supClass, v);
168                        }
169                }
170
171                // the array is backwards so it needs to be flipped
172                Object[] result = new Object[o.length];
173                for (int i = 0; i < o.length; i++) {
174                        result[(result.length - 1) - i] = o[i];
175                }
176
177                return result;
178        }
179
180        /**
181         * provides any initialization needed prior to searching. It is called when
182         * the class is constructed. Note that any local variables of extending
183         * classes should be initialized in init since it is called be the
184         * constructor of the super class (LibrarySearcher).
185         */
186        @Override
187        protected void init() {
188
189        }
190
191        /**
192         * If we need to skip ontology display
193         */
194        public void setSkipOntology(boolean skipOntology)
195        {
196                this.skipOntology = skipOntology;
197        }
198
199        /**
200         * search for value in the library
201         * 
202         *@param value
203         *            the value to search for
204         *@return Description of the Return Value
205         * @throws RepositoryException 
206         * @throws AuthenticationException 
207         */
208        @Override
209        public LibrarySearchResults search(String value, boolean authenticate)
210        throws IllegalActionException, RepositoryException, AuthenticationException {
211
212                try {
213                        _results = new LibrarySearchResults();
214
215                        TreePath path = _library.getPathForRow(0); // get the container
216                        Object[] pathComps = path.getPath();
217                        EntityLibrary topLevel = (EntityLibrary) pathComps[0];
218                        EntityLibrary remoteLevel = null;
219                        try {
220                                remoteLevel = new EntityLibrary(topLevel, "Remote Components");
221                        } catch (NameDuplicationException nde) {
222                                Iterator itt = topLevel.entityList().iterator();
223                                while (itt.hasNext()) {
224                                        CompositeEntity entity = (CompositeEntity) itt.next();
225                                        if (entity.getName().equals("Remote Components")) {
226                                                remoteLevel = (EntityLibrary) entity;
227                                                remoteLevel.setContainer(null);
228                                                remoteLevel = new EntityLibrary(topLevel,
229                                                "Remote Components");
230                                        }
231                                }
232                        }
233                        // setup the remote components part of the tree
234                        Object[] o = new Object[2];
235                        o[0] = topLevel;
236                        o[1] = remoteLevel;
237
238                        _results.add(new TreePath(o));
239
240                        // get the results from the repository
241                        OntologyCatalog ontCatalog = OntologyCatalog.instance();
242                        // search the repository
243                        //System.out.println("EcogridRepositoryLibrarySearcher search('" + value + "')");
244                        Iterator repoResults = repository.search(value, authenticate);
245                        Iterator<EcogridRepositoryResults> castRepoResultsIterator = (Iterator<EcogridRepositoryResults>) repoResults;
246                        List<EcogridRepositoryResults> repoResultsList = iteratorToList(castRepoResultsIterator);
247                        //System.out.println("EcogridRepositoryLibrarySearcher found " + repoResultsList.size() + " results");
248                        List<List<EcogridRepositoryResults>> groupedResults = groupResultsByIndex(repoResultsList);
249                        //System.out.println("having " +  groupedResults.size() + " groups");
250                        //                      System.out.println("Matched KARs:");
251                        //                      for (List<EcogridRepositoryResults> resultGroup : groupedResults) {
252                        //                              System.out.println("* " + resultGroup.get(0).getKarEntry().getParent().getId());
253                        //                      }
254                        if (repoResults != null) {
255                                EntityLibrary karLevel = null;
256                                EntityLibrary repositoryLevel = null;
257                                for (List<EcogridRepositoryResults> repoResultsGroup : groupedResults) {
258                                        //System.out.println("A group has "+repoResultsGroup.size()+" EcogridResult");
259                                        boolean isDone = false;
260                                        for (EcogridRepositoryResults repoResult : repoResultsGroup) {                                    
261                                                if(!isDone){
262                                                        karLevel = createKarLevel(remoteLevel, repoResult);
263                                                        // Get the remote repository level
264                                                        repositoryLevel= getRepositoryLevel(remoteLevel, repoResult.getKarEntry().getParent());
265                                                        isDone = true;
266                                                }
267
268                                                // get each result from the repository
269                                                CacheObject co = repoResult.getCacheObject();
270                                                ActorCacheObject aco = (ActorCacheObject) co;
271                                                KarXml.KarEntry karEntry = repoResult.getKarEntry();
272
273                                                Object leafObject;
274
275                                                if (co == null) {
276                                                        // Not an actor
277                                                        String name = repoResult.getName();
278                                                        if (name == null) {
279                                                                name = "unnamed";
280                                                        }
281
282                                                        NondraggableTreeItem nti = new NondraggableTreeItem(name);
283                                                        if(karEntry.isWorkflow())
284                                                        {
285                                                                nti.setWorkflowLSID(karEntry.getLsid());
286                                                                nti.setWorkflowName(karEntry.getWorkflowName());
287                                                        }
288                                                        StringAttribute alternateGetPopupActionAttribute = new StringAttribute(nti, "_alternateGetPopupAction");
289                                                        alternateGetPopupActionAttribute.setExpression(RepositoryPopup.class.getName());
290                                                        StringAttribute notDraggableAttribute = new StringAttribute(nti, "_notDraggable");
291                                                        notDraggableAttribute.setExpression("true"); // Not strictly needed, but makes reading the MOML a little nicer.
292
293                                                        //FIXME hardcode to add-on module
294                                                        if ("org.kepler.reporting.roml.ReportLayout".equals(repoResult.getKarEntry().getType())) {
295                                                                ConfigurableAttribute thumbnailAttribute = new ConfigurableAttribute(nti, "_thumbnailRasterIcon");
296                                                                thumbnailAttribute.setExpression("/actorthumbs/basic-report-sm.gif");
297                                                        }
298                                                        leafObject = nti;
299                                                } else {
300                                                        // An actor
301                                                        CacheNamedObj cno = new CacheNamedObj(new CompositeEntity(new Workspace()), co);
302                                                        if(karEntry.isWorkflow())
303                                                        {
304                                                                cno.setWorkflowLSID(karEntry.getLsid());
305                                                                cno.setWorkflowName(karEntry.getWorkflowName());
306                                                        }
307                                                        StringAttribute entityIdAttribute = new StringAttribute(cno, "entityId");
308                                                        entityIdAttribute.setExpression(aco.getLSID().toString());
309                                                        StringAttribute alternateGetMomlActionAttribute = new StringAttribute(cno, "_alternateGetMomlAction");
310                                                        alternateGetMomlActionAttribute.setExpression(AlternateGetMoml.class.getName());
311                                                        StringAttribute alternateGetPopupActionAttribute = new StringAttribute(cno, "_alternateGetPopupAction");
312                                                        alternateGetPopupActionAttribute.setExpression(RepositoryPopup.class.getName());
313
314                                                        ConfigurableAttribute thumbnailAttribute = new ConfigurableAttribute(cno, "_thumbnailRasterIcon");
315                                                        thumbnailAttribute.setExpression("/actorthumbs/basic-actor-sm.gif");
316
317                                                        leafObject = cno;
318                                                }
319
320                                                // find the semantic types and loop through them to place
321                                                // the results
322                                                if(!skipOntology){
323                                                        Vector<String> semTypes = repoResult.getSemanticTypes();
324                                                        for (int k = 0; k < semTypes.size(); k++) {
325                                                                String semType = (String) semTypes.elementAt(k);
326                                                                // find the class, search only the library ontologies
327                                                                NamedOntClass ontClass = ontCatalog.getNamedOntClass(
328                                                                                semType, true);
329
330                                                                if (ontClass == null) { // skip this component if it
331                                                                        // doesn't have a known class
332                                                                        continue;
333                                                                }
334
335                                                                // get the tree path that goes with this semantic type
336                                                                Object[] treePathArray = getTreePathObjectArray(
337                                                                                ontClass, new Vector());
338
339                                                                // add the tree path to the topLevel and remoteLevel
340                                                                // objects to make the fullTreePath
341                                                                Object[] fullTreePath = new Object[treePathArray.length + 3];
342                                                                fullTreePath[0] = topLevel;
343                                                                fullTreePath[1] = remoteLevel;
344                                                                for (int i = 2; i < fullTreePath.length - 1; i++) {
345                                                                        // put the treepath together to be added to the
346                                                                        // results vector this just adds EntityLibraries
347                                                                        try {
348                                                                                // adding a space to the treePathArray content
349                                                                                // is a hack to keep
350                                                                                // the tree from stealing results from the
351                                                                                // remote part and putting
352                                                                                // them back into the local part. I'm not sure
353                                                                                // why it does this
354                                                                                // but keeping the names unique seems to be the
355                                                                                // only way to
356                                                                                // prevent results seepage.
357                                                                                treePathArray[i - 2] = (String) treePathArray[i - 2]
358                                                                                                                              + " ";
359                                                                                fullTreePath[i] = new EntityLibrary(
360                                                                                                (CompositeEntity) fullTreePath[i - 1],
361                                                                                                (String) treePathArray[i - 2]);
362                                                                        } catch(NameDuplicationException nde) {
363                                                                                // if we get a NDE, we need to search the
364                                                                                // existing tree for the
365                                                                                // correct parent
366                                                                                fullTreePath[i] = findEntity(
367                                                                                                (String) treePathArray[i - 2],
368                                                                                                (CompositeEntity) fullTreePath[i - 1]);
369                                                                                if (fullTreePath[i] == null) {
370                                                                                        System.out
371                                                                                        .println("ERROR: no path found for fullTreePath["
372                                                                                                        + i + "]");
373                                                                                }
374                                                                        }
375                                                                }
376
377                                                                fullTreePath[fullTreePath.length - 1] = leafObject;
378
379                                                                _results.add(new TreePath(fullTreePath));
380                                                        }
381                                                }
382
383
384                                                Object[] containmentRepresentation = getContainmentRepresentation(topLevel, remoteLevel, 
385                                                                repositoryLevel, karLevel, leafObject);
386                                                if (containmentRepresentation == null) {
387                                                        log.warn("Could not generate containment representation: " + repoResult.getName());                                                     
388                                                }
389                                                else {
390                                                        _results.add(new TreePath(containmentRepresentation));
391                                                }
392                                        }
393                                }
394                        }
395
396                        if (_results.size() == 1) {// no results were added, so remove the
397                                // Remote Components result tree
398                                _results = new LibrarySearchResults();
399                        }
400
401                        return _results;
402                } catch (NameDuplicationException nde) {
403                        nde.printStackTrace();
404                        throw new IllegalActionException(
405                                        "Error building remote repository " + "search results: "
406                                        + nde.getMessage());
407                }
408        }
409
410
411
412        private Object[] getContainmentRepresentation(EntityLibrary topLevel, EntityLibrary remoteLevel, EntityLibrary repositoryLevel,
413                        EntityLibrary karLevel, Object leafObject) {
414
415                if (repositoryLevel != null) {
416                        Object[] newPath = new Object[5];
417                        newPath[0] = topLevel;  // Copy over "Search Results" top-level
418                        newPath[1] = remoteLevel;       // Copy over "Remote Components"
419                        newPath[2] = repositoryLevel;
420                        newPath[3] = karLevel;
421                        newPath[4] = leafObject;        // Copy over the component itself
422                        return newPath;
423                }
424                else {
425                        return null;
426                }               
427        }
428
429        private EntityLibrary getRepositoryLevel(EntityLibrary parent, KarXml karXml) {
430                EntityLibrary repositoryLevel = null;
431                try {
432                        repositoryLevel = new RemoteRepositoryEntityLibrary(parent, karXml.getRepositoryName());
433                }
434                catch(IllegalActionException ex) {
435                        ex.printStackTrace();
436                }
437                catch(NameDuplicationException ex) {
438                        // The containment level is already there. Find it.
439                        repositoryLevel = (EntityLibrary) findEntity(karXml.getRepositoryName(), (CompositeEntity) parent);
440                }
441                return repositoryLevel;
442        }
443
444        private EntityLibrary createKarLevel(EntityLibrary parentLibrary, EcogridRepositoryResults repoResult) {
445                KarXml kx = repoResult.getKarEntry().getParent();
446                List<String> prefixes = Arrays.asList("urn:lsid:gamma.msi.ucsb.edu/OpenAuth/:", "urn:lsid:kepler-project.org/ns/:");
447                String rawId = kx.getName();
448                // Remove the prefix from the rawId before sanitizing. All that
449                // boilerplate just gets in the way.
450                for (String prefix : prefixes) {
451                        if (rawId.startsWith(prefix)) {
452                                rawId = rawId.substring(prefix.length());
453                                break;
454                        }
455                }
456                String karLevelName = rawId;
457                if (!karLevelName.endsWith(".kar")) {
458                    karLevelName += ".kar";
459                }
460                EntityLibrary karLevel = null;
461
462                Vector<String> modDeps = new Vector<String>(kx.getModuleDependencies());
463                boolean dependenciesSatisfied = ModuleDependencyUtil.checkIfModuleDependenciesSatisfied(modDeps);
464                try {
465                    // replace periods in the name since they are not allowed in NamedObj
466                    String sanitizedName = karLevelName.replace(".", ",");
467                        if (dependenciesSatisfied) {
468                                karLevel = new DownloadableKAREntityLibrary(parentLibrary, sanitizedName, kx);
469                        }
470                        else {
471                                karLevel = new RemoteKARErrorEntityLibrary(parentLibrary, sanitizedName);
472                                ((RemoteKARErrorEntityLibrary) karLevel).setKarXml(kx);
473                        }
474                        //                      karLevel = new EntityLibrary(parentLibrary, karLevelName);
475            
476                        // set the display name, which can have periods
477                        karLevel.setDisplayName(karLevelName);
478                }
479                catch (NameDuplicationException nde) {
480                        //System.out.println("name duplication exception "+nde.getMessage());
481                        try {
482                                // add an appendix for the name
483                                if(kx != null && kx.getLsid() != null)
484                                {
485                                        //add docid as an appendix
486                                        karLevelName = karLevelName+"-(karId-"+transformLSIDtoDocid(kx.getLsid())+")"+
487                                        "-(karXmlId-"+repoResult.getDocid().replace('.', ':')+")";
488                                }
489                                else
490                                {
491                                        //add random number
492                                        double number = Math.random();
493                                        long appendix = Math.round(number);
494                                        karLevelName = karLevelName+"-("+appendix+")";
495                                }
496
497                        // replace periods in the name since they are not allowed in NamedObj
498                                String sanitizedName = karLevelName.replace(".", ",");
499                                if (dependenciesSatisfied) {
500                                        karLevel = new DownloadableKAREntityLibrary(parentLibrary, sanitizedName, kx);
501                                }
502                                else {
503                                        karLevel = new RemoteKARErrorEntityLibrary(parentLibrary, sanitizedName);
504                                        ((RemoteKARErrorEntityLibrary) karLevel).setKarXml(kx);
505                                }
506                                // set the display name, which can have periods
507                                karLevel.setDisplayName(karLevelName);
508                        }
509                        catch(NameDuplicationException ex) {
510                                log.warn("This shouldn't happen", ex);
511                        }
512                        catch(IllegalActionException ex) {
513                                log.error("Illegal action exception", ex);
514                        }
515                        /*Iterator<CompositeEntity> iterator = (Iterator<CompositeEntity>) parentLibrary.entityList().iterator();
516                        while (iterator.hasNext()) {
517                                CompositeEntity entity = iterator.next();
518                                if (entity.getName().equals(karLevelName)) {
519                                        try {
520                                                karLevel = (EntityLibrary) entity;
521                                                karLevel.setContainer(null);
522                                                if (dependenciesSatisfied) {
523                                                        karLevel = new DownloadableKAREntityLibrary(parentLibrary, karLevelName, karEntry.getParent());
524                                                }
525                                                else {
526                                                        karLevel = new RemoteKARErrorEntityLibrary(parentLibrary, karLevelName);
527                                                        ((RemoteKARErrorEntityLibrary) karLevel).setKarXml(kx);
528                                                }
529                                        }
530                                        catch(NameDuplicationException ex) {
531                                                log.warn("This shouldn't happen", ex);
532                                        }
533                                        catch(IllegalActionException ex) {
534                                                log.error("Illegal action exception", ex);
535                                        }
536                                }
537                        }*/
538                }
539                catch(IllegalActionException ex) {
540                        log.error("Illegal action exception", ex);
541                }
542                return karLevel;
543        }
544
545        /*
546         *Transform a urn:lsid:kepler-project.org/ns/:2099:21:2 to 2099.21.2
547         */
548        private String transformLSIDtoDocid(String lsid) {
549                String docid = null;
550                char colon = ':';
551                int targetNumber =3;
552                if(lsid != null){
553                        int index =0;
554                        for(int i=0; i<lsid.length(); i++){
555                                char character = lsid.charAt(lsid.length()-1-i);
556                                if(character == colon){
557                                        index = index+1;
558                                        if(index == targetNumber){
559                                                docid = lsid.substring(lsid.length()-i);
560                                        }
561                                }
562                        }
563                }
564                return docid;
565        }
566
567        private List<List<EcogridRepositoryResults>> groupResultsByIndex(List<EcogridRepositoryResults> repoResults) {
568                Map<Integer, List<EcogridRepositoryResults>> resultsByIndex = new HashMap<Integer, List<EcogridRepositoryResults>>();
569                for (EcogridRepositoryResults repoResult : repoResults) {
570                        Integer indexValue = repoResult.getIndexValue();
571                        if (!resultsByIndex.containsKey(indexValue)) {
572                                resultsByIndex.put(indexValue, new ArrayList<EcogridRepositoryResults>());
573                        }
574                        resultsByIndex.get(indexValue).add(repoResult);
575                }
576
577                List<List<EcogridRepositoryResults>> results = new ArrayList<List<EcogridRepositoryResults>>();
578                for (Integer indexValue : resultsByIndex.keySet()) {
579                        if (indexValue == null) {
580                                // These aren't in a single group of 'not a group'. Add them
581                                // as separate lists.
582                                for (EcogridRepositoryResults repoResult : resultsByIndex.get(null)) {
583                                        results.add(Collections.singletonList(repoResult));
584                                }
585                        }
586                        else {
587                                results.add(resultsByIndex.get(indexValue));
588                        }
589                }
590                return results;
591        }
592
593        private <T> List<T> iteratorToList(Iterator<T> iterator) {
594                if (iterator == null) {
595                        return Collections.emptyList();
596                }
597                List<T> list = new ArrayList<T>();
598                while (iterator.hasNext()) {
599                        list.add(iterator.next());
600                }
601                return list;
602        }
603
604        public class NondraggableTreeItem extends ComponentEntity {
605                
606                private String workflowName = null;
607                private String workflowLSID = null;
608                
609                public NondraggableTreeItem(String name) throws IllegalActionException, NameDuplicationException {
610                        super(new CompositeEntity(new Workspace()), name.replace('.', ','));
611                        setDisplayName(name);
612                }
613
614                public String getWorkflowName(){
615                        return workflowName;
616                }
617                public void setWorkflowName(String workflowName){
618                        this.workflowName = workflowName;
619                }
620
621                public String getWorkflowLSID(){
622                        return workflowLSID;
623                }
624
625                public void setWorkflowLSID(String workflowLSID){
626                        this.workflowLSID = workflowLSID;
627                }
628        }
629}