001/*
002 * Copyright (c) 2004-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2014-08-13 22:56:02 +0000 (Wed, 13 Aug 2014) $' 
007 * '$Revision: 32876 $'
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.library;
031
032import java.io.File;
033import java.io.IOException;
034import java.lang.ref.WeakReference;
035import java.sql.Connection;
036import java.sql.PreparedStatement;
037import java.sql.ResultSet;
038import java.sql.SQLException;
039import java.sql.Statement;
040import java.util.Hashtable;
041import java.util.LinkedList;
042import java.util.List;
043import java.util.Map;
044import java.util.Vector;
045import java.util.WeakHashMap;
046
047import javax.swing.JTree;
048import javax.swing.event.TreeExpansionEvent;
049import javax.swing.event.TreeExpansionListener;
050import javax.swing.tree.TreePath;
051
052import org.apache.commons.logging.Log;
053import org.apache.commons.logging.LogFactory;
054import org.kepler.gui.FilteredVisibleTreeModel;
055import org.kepler.icon.ComponentEntityConfig;
056import org.kepler.kar.KARCacheContent;
057import org.kepler.kar.KARCacheManager;
058import org.kepler.kar.KARFile;
059import org.kepler.moml.FolderEntityLibrary;
060import org.kepler.moml.KAREntityLibrary;
061import org.kepler.moml.KARErrorEntityLibrary;
062import org.kepler.moml.NamedObjId;
063import org.kepler.moml.OntologyEntityLibrary;
064import org.kepler.objectmanager.cache.CacheManager;
065import org.kepler.objectmanager.lsid.KeplerLSID;
066import org.kepler.util.StatusNotifier;
067import org.kepler.util.sql.DatabaseFactory;
068
069import ptolemy.actor.gui.TableauFrame;
070import ptolemy.data.expr.Parameter;
071import ptolemy.kernel.ComponentEntity;
072import ptolemy.kernel.CompositeEntity;
073import ptolemy.kernel.util.Attribute;
074import ptolemy.kernel.util.IllegalActionException;
075import ptolemy.kernel.util.NameDuplicationException;
076import ptolemy.kernel.util.StringAttribute;
077import ptolemy.kernel.util.Workspace;
078import ptolemy.moml.EntityLibrary;
079import ptolemy.vergil.tree.EntityTreeModel;
080import ptolemy.vergil.tree.VisibleTreeModel;
081
082/**
083 * The LibraryManager is used for managing the Actor Library. It maintains an
084 * index for increased performance. Any code that adds, updates, or removes
085 * items in the library should use the LibraryManager class directly.
086 * 
087 *@author Aaron Schultz
088 */
089public class LibraryManager implements TreeExpansionListener {
090        private static class LibraryManagerHolder {
091                private static final LibraryManager INSTANCE = new LibraryManager();
092        }
093
094        private static final Log log = LogFactory.getLog(LibraryManager.class
095                        .getName());
096        private static final boolean isDebugging = log.isDebugEnabled();
097
098        /**
099         * The LIID_LABEL is used as the name of the String Attribute that is
100         * attached to Tree Items to keep track of their Library Index ID.
101         */
102        public static final String LIID_LABEL = "_LIID";
103
104        /** A mapping of tree to a list of tree paths that are expanded in it. */
105        private static final Map<JTree,List<TreePath>> _treeExpansionMap = new WeakHashMap<JTree,List<TreePath>>();
106        
107        /**
108         * Method for getting an instance of this singleton class.
109         */
110        public static LibraryManager getInstance() {
111                return LibraryManagerHolder.INSTANCE;
112        }
113
114        /**
115         * Return the Library Index ID for the given NamedObj or -1 if an ID cannot
116         * be found.
117         * 
118         * @param obj
119         *            a NamedObj from the Library tree.
120         * @return int Library Index ID or
121         */
122        public static int getLiidFor(ComponentEntity obj) {
123                if (isDebugging)
124                        log.debug("getLiidFor(" + obj.getName() + ")");
125                int liid = -1;
126                try {
127                        Attribute liidAttribute = obj
128                                        .getAttribute(LibraryManager.LIID_LABEL);
129                        if (liidAttribute == null) {
130                                return -1;
131                        }
132                        if (liidAttribute instanceof StringAttribute) {
133                                StringAttribute liidSA = (StringAttribute) liidAttribute;
134                                liid = Integer.parseInt(liidSA.getExpression());
135                        } else {
136                                liid = -1;
137                        }
138                } catch (Exception e) {
139                        liid = -1;
140                }
141                return liid;
142        }
143
144        /**
145         * This is the composite entity version of the library.
146         */
147        private CompositeEntity _actorLibrary;
148
149        /**
150         * This is the workspace passed to us from
151         * ptolemy.actor.gui.UserActorLibrary It is the top level workspace.
152         */
153        private Workspace _actorLibraryWorkspace = null;
154
155        /**
156         * The LibIndex is used to build the Actor Library.
157         */
158        private LibIndex _libIndex;
159
160        /**
161         * This is the EntityTreeModel version of the library, used in
162         * AnnotatedPTree.
163         */
164        private VisibleTreeModel _libraryModel;
165
166        /**
167         * Convenience reference
168         */
169        private Connection _conn;
170
171        /**
172         * Convenience reference. Make sure to close your ResultSets since we're
173         * reusing the Statement.
174         */
175        private Statement _stmt;
176
177        /**
178         * JTrees that contain the library can be added to the library manager so
179         * they can be refreshed whenever the library changes.
180         */
181//      private Vector<JTree> _trees = new Vector<JTree>();     
182        private Vector<WeakReference<JTree>> _trees = new Vector<WeakReference<JTree>>();
183
184        // PreparedStatements
185    private PreparedStatement _getPopulateInfoForLIIDPrepStmt;
186    private PreparedStatement _getLSIDForLIIDPrepStmt;
187    private PreparedStatement _getNameValueForLIIDPrepStmt;
188
189        /**
190         * constructor called from getInstance()
191         */
192        public LibraryManager() {
193                if (isDebugging)
194                        log.debug("Instantiate LibraryManager");
195
196                try {
197                        _conn = DatabaseFactory.getDBConnection();
198                        _stmt = _conn.createStatement();
199                        
200            _getPopulateInfoForLIIDPrepStmt = _conn.prepareStatement(
201                    "SELECT PARENT,LFT,RGT,LEVEL,LSID,TYPE,NAME FROM "
202                    + LibIndex.LIBRARY_INDEX_TABLE_NAME + " WHERE LIID = ?");
203
204            _getLSIDForLIIDPrepStmt = _conn.prepareStatement("SELECT LSID FROM "
205                    + LibIndex.LIBRARY_LSIDS_TABLE_NAME + " WHERE LIID = ?");
206
207            _getNameValueForLIIDPrepStmt = _conn.prepareStatement("SELECT NAME,VALUE FROM "
208                    + LibIndex.LIBRARY_ATTRIBUTES_TABLE_NAME + " WHERE LIID = ?");
209
210                } catch (Exception e) {
211                        e.printStackTrace();
212                }
213
214        }
215
216        /**
217         * Given the LSID of a KAR that has been successfully cached this method
218         * will add all of the contents of that KAR into the Library by first
219         * updating the LibIndex and then updating the tree model.
220         * 
221         * @param f
222         * @throws SQLException
223         */
224        public void addKAR(File karFile) throws SQLException {
225
226                KARCacheManager kcm = KARCacheManager.getInstance();
227                Vector<KARCacheContent> contents = kcm.getKARCacheContents(karFile);
228                getIndex().setOrderedInsert(true);
229                for (KARCacheContent content : contents) {
230
231                        // Update the ontologies in the library index
232                        Vector<LibItem> ontItems = getIndex().assureOntologyComponent(
233                                        content);
234                        // update the ontologies in the library tree model
235                        for (LibItem ontLI : ontItems) {
236                                ComponentEntity ce = assureTreeItem(ontLI);
237                                if (ce == null) {
238                                        // Problem adding item to the tree
239                                        // Here we could rebuild the library completely...
240                                        log.error("Component not added to the library properly");
241                                }
242                        }
243
244                        // Update the folders in the library index
245                        LibItem folderLI = getIndex().assureKarEntry(content);
246                        // Update the folders in the library tree model
247                        assureTreeItem(folderLI);
248                }
249
250                getIndex().setOrderedInsert(false);
251        }
252
253        /**
254         * Update the LibIndex and tree model for a XML file.
255     * 
256     * @param f
257     * @throws SQLException
258     */
259    public void addXML(File xmlFile) throws SQLException {
260
261        LibIndex index = getIndex();
262        index.setOrderedInsert(true);
263        
264        LibItem xmlLI = index.assureXML(xmlFile);
265        assureTreeItem(xmlLI);
266        
267        index.setOrderedInsert(false);
268    }
269
270        /**
271         * Add a reference to a JTree that is using the Library Tree Model so that
272         * it can be updated when the tree model changes. Whenever getTreeModel() is
273         * used in a gui component, the JTree reference should be added using this
274         * method so that it can get refreshed when the library model changes using
275         * the refreshJTrees() method.
276         * 
277         *@param ptree
278         *            The feature to be added to the LibraryComponent attribute
279         */
280        public void addLibraryJTree(JTree ptree) {
281                if (isDebugging) {
282                        log.debug("addLibraryComponent(" + ptree.getName() + ")");
283                }
284                if (isDebugging)
285                        log.debug("addLibraryComponent(" + ptree + ")");
286                WeakReference<JTree> ptreeWR =  new WeakReference<JTree>(ptree);
287                _trees.addElement(ptreeWR);
288                if (isDebugging)
289                        log.debug("_trees size:" + _trees.size());
290                
291                // add this tree to the map and register for expansion events
292                _treeExpansionMap.put(ptree, new LinkedList<TreePath>());
293                ptree.addTreeExpansionListener(this);
294        }
295
296        /**
297         * Passing a LibItem that has been populated from the database will assure
298         * that the appropriate object is represented in the Library Tree. If the
299         * item already exists it will be returned. If it does not exist then it
300         * will be created along with any parent path components that do not already
301         * exist. The newly created ComponentEntity will then be returned.
302         * 
303         * This method is private because API users should only be using addKAR and
304         * deleteKAR methods to modify the Library.
305         * 
306         * @param li
307         * @return
308         */
309        private ComponentEntity assureTreeItem(LibItem li) {
310                if (isDebugging)
311                        log.debug("assureTreeItem(" + li.getName() + ")");
312                ComponentEntity ce = null;
313                try {
314                        CompositeEntity current = (CompositeEntity) getTreeModel()
315                                        .getRoot();
316                        Vector<LibItem> pathItems = _libIndex.getPath(li);
317                        for (int i = 0; i < pathItems.size(); i++) {
318                                LibItem pathItem = pathItems.elementAt(i);
319
320                                int pathLiid = pathItem.getLiid();
321                                if (isDebugging) {
322                                        log.debug(pathItem.getName());
323                                        log.debug(current.getName());
324                                }
325
326                                ComponentEntity existingCE = null;
327                                List children = current.entityList(ComponentEntity.class);
328                                for (Object child : children) {
329                                        if (child instanceof ComponentEntity) {
330                                                ComponentEntity childCE = (ComponentEntity) child;
331                                                int childLiid = LibraryManager.getLiidFor(childCE);
332                                                if (childLiid == pathLiid) {
333                                                        existingCE = childCE;
334                                                        break;
335                                                }
336                                        } else {
337                                                log.error("Child was not a ComponentEntity!");
338                                        }
339                                }
340                                if (existingCE == null) {
341                                        // add this puppy to the tree
342                                        ComponentEntity newCE = createAndAddTreeItem(current,
343                                                        pathItem);
344                                        if (newCE != null) {
345                                                if (newCE instanceof CompositeEntity) {
346                                                        current = (CompositeEntity) newCE;
347                                                        ce = current;
348                                                } else {
349                                                        // ComponentEntity leaf node
350                                                        ce = newCE;
351                                                        break;
352                                                }
353                                        } else {
354                                                log.debug("fail");
355                                                ce = null;
356                                        }
357                                } else if (existingCE instanceof CompositeEntity) {
358                                        current = (CompositeEntity) existingCE;
359                                        ce = current;
360                                }
361                        } // end for loop
362                } catch (Exception e) {
363                        e.printStackTrace();
364                }
365                return ce;
366        }
367
368    /** Create an empty library. When KeplerGraphFrame starts, it initializes
369     * ComponentLibraryTab, which in turn requires a library. Building the
370     * library with buildLibrary() can take a long time, so this method can
371     * be used to open a KeplerGraphFrame quickly.
372     */
373    public void buildEmptyLibrary() {
374        
375        EntityLibrary el = new EntityLibrary(_actorLibraryWorkspace);
376        
377        LibItem li = new LibItem();
378        li.setName("Empty Library");
379        li.setType(LibIndex.TYPE_CONCEPT);
380
381        try {
382            createAndAddTreeItem(el, li);
383        } catch (Exception e) {
384            // TODO Auto-generated catch block
385            e.printStackTrace();
386        }
387        
388        this.setActorLibrary(el);
389
390    }
391
392        /**
393         * This method will synchronize the cache with local repositories. Then it
394         * will rebuild the LibIndex if needed. Then it will generate the Actor
395         * Library from the LibIndex. To force the rebuilding of the LibIndex before
396         * calling this method call getIndex().clear()
397         */
398        public void buildLibrary() {
399                if (isDebugging)
400                        log.debug("buildLibrary()");
401
402                StatusNotifier.log("Building Library.");
403                
404                try {
405
406                        KARCacheManager kcm = KARCacheManager.getInstance();
407
408                        // Synchronize the Cache with Local Repositories
409                        boolean changed = kcm.synchronizeKARCacheWithLocalRepositories();
410
411                        // Rebuild the Library Index if needed
412                        _libIndex = new LibIndex(_conn);
413
414                        long start = System.currentTimeMillis();
415                        if (_libIndex.countItems() <= 0) {
416                                // rebuild the index if it is empty
417                                _libIndex.rebuild();
418                        } else if (!_libIndex.verifyPreorderValues()) {
419                                // rebuild the index if the preorder values don't add up
420                                // correctly
421                                log
422                                                .error("\n\nPreorder values are corrupt! Rebuilding index.\n\n");
423                                _libIndex.rebuild();
424                        } else if (changed) {
425                                // rebuild the index if the KARs in the local repositories have
426                                // changed
427                                _libIndex.rebuild();
428                        }
429                        if (isDebugging)
430                                log.debug("\n\nTIME " + (System.currentTimeMillis() - start)
431                                                + "\n\n");
432
433                        // Generate the actor library using the library index
434                        LibraryGenerator lg = new LibraryGenerator();
435                        
436                        // we're using the same workspace so we need to remove the entities
437                        clearActorLibrary();
438                        
439                        CompositeEntity newLibrary = lg.generate(_actorLibraryWorkspace,
440                                        _libIndex);
441                        this.setActorLibrary(newLibrary);
442
443                } catch (Exception e) {
444                        e.printStackTrace();
445                }
446
447        }
448        
449        public void removeAllFrameTabs(TableauFrame parent) {
450                if (isDebugging)
451                {
452                        log.debug("parent in _trees is:" + parent);
453                        for (int i = 0; i < _trees.size(); i++) {
454                                JTree treeEle = _trees.elementAt(i).get();
455                                log.debug("treeEle in _trees element at " + i + " is:" + treeEle);
456                        }
457                        log.debug("_libraryModel:" + _libraryModel);
458                }
459        }
460
461        /**
462         * Create and add the appropriate type of placeholder object to the parent
463         * CompositeEntity that is in the tree model. Return the object when
464         * finished.
465         * 
466         * Only subtypes of ComponentEntity may be returned. However, the tree item
467         * can represent any type of object, depending on what the type is in the
468         * LibIndex.
469         * 
470         * @param li
471         * @return
472         * @throws NameDuplicationException
473         * @throws IllegalActionException
474         * @throws IOException
475         * @throws IllegalArgumentException
476         */
477        public ComponentEntity createAndAddTreeItem(CompositeEntity parent,
478                        LibItem li) throws IllegalActionException,
479                        NameDuplicationException, IllegalArgumentException, IOException {
480                ComponentEntity ce = null;
481
482                // EditorDropTarget in ptolemy requires an entityId so we set one here
483                // even though we don't ever use it for anything, the LSID should
484                // always be gotten from the LibItem
485                NamedObjId lsidSA = null;
486
487                int type = li.getType();
488                String displayName = li.getName();
489                
490                String name = displayName;
491                // remove periods from the name since they are not allowed
492                if(name.contains(".")) {
493                        name = name.replaceAll("\\.", ",");
494                }
495                li.setName(name);
496
497                switch (type) {
498                case LibIndex.TYPE_COMPONENT:
499                        ce = new ComponentEntity(parent, li.getName());
500                        ce.setDisplayName(displayName);
501                        // This is needed for ptolemy at the moment
502                        KeplerLSID lsid = li.getLsid();
503                        if(lsid != null) {
504                        lsidSA = new NamedObjId(ce, NamedObjId.NAME);
505                        lsidSA.setExpression(li.getLsid());
506                        }
507                        // disable drag and drop for MoML files
508                        if(li.getAttributeValue(LibIndex.ATT_XMLFILE) != null) {
509                            Parameter parameter = new Parameter(ce, "_notDraggable");
510                            parameter.setExpression("true");
511                        }
512                        ce.setClassName(li.getAttributeValue(LibIndex.ATT_CLASSNAME));
513                        ComponentEntityConfig.addSVGIconTo(ce);
514                        copyAttributes(li, ce);
515                        break;
516                case LibIndex.TYPE_NAMED_OBJ:
517                        ce = new ComponentEntity(parent, li.getName());
518                        ce.setDisplayName(displayName);
519                        lsidSA = new NamedObjId(ce, NamedObjId.NAME);
520                        lsidSA.setExpression(li.getLsid());
521                        // ce.setClassName(li.getAttributeValue(LibIndex.ATT_CLASSNAME));
522                        ComponentEntityConfig.addSVGIconTo(ce);
523                        // To catch WorkflowRuns
524                        StringAttribute notDraggableAttribute = new StringAttribute(ce, "_notDraggable");
525                        notDraggableAttribute.setExpression("true");                    
526                        copyAttributes(li, ce);
527                        break;
528                case LibIndex.TYPE_ONTOLOGY:
529                        ce = new OntologyEntityLibrary(parent, li.getName());
530                        ce.setDisplayName(displayName);
531                        break;
532                case LibIndex.TYPE_CONCEPT:
533                        ce = new EntityLibrary(parent, li.getName());
534                        ce.setDisplayName(displayName);
535                        break;
536                case LibIndex.TYPE_FOLDER:
537                        ce = new FolderEntityLibrary(parent, li.getName());
538                        ce.setDisplayName(displayName);
539                        break;
540                case LibIndex.TYPE_LOCALREPO:
541                        ce = new FolderEntityLibrary(parent, li.getName());
542                        ce.setDisplayName(displayName);
543                        break;
544                case LibIndex.TYPE_KAR:
545                        ce = new KAREntityLibrary(parent, li.getName());
546                        ce.setDisplayName(displayName);
547                        lsidSA = new NamedObjId(ce, NamedObjId.NAME);
548                        lsidSA.setExpression(li.getLsid());
549                        break;
550                case LibIndex.TYPE_KAR_ERROR:
551                        ce = new KARErrorEntityLibrary(parent, li.getName());
552                        ce.setDisplayName(displayName);
553                        lsidSA = new NamedObjId(ce, NamedObjId.NAME);
554                        lsidSA.setExpression(li.getLsid());
555                        break;
556                case LibIndex.TYPE_KARFOLDER:
557                        ce = new FolderEntityLibrary(parent, li.getName());
558                        ce.setDisplayName(displayName);
559                        break;
560                case LibIndex.TYPE_OBJECT:
561                        ce = new ComponentEntity(parent, li.getName());
562                        ce.setDisplayName(displayName);
563                        lsidSA = new NamedObjId(ce, NamedObjId.NAME);
564                        lsidSA.setExpression(li.getLsid());
565                        // ce.setClassName(li.getAttributeValue(LibIndex.ATT_CLASSNAME));
566                        ComponentEntityConfig.addSVGIconTo(ce);
567                        notDraggableAttribute = new StringAttribute(ce, "_notDraggable");
568                        notDraggableAttribute.setExpression("true");
569                }
570                if (ce != null) {
571                        StringAttribute liidSA = new StringAttribute(ce,
572                                        LibraryManager.LIID_LABEL);
573                        liidSA.setExpression("" + li.getLiid());
574                }
575                return ce;
576        }
577
578        private void copyAttributes(LibItem li, ComponentEntity ce) {
579                Hashtable<String,String> attributes = li.getAttributes();
580                for (String key : attributes.keySet()) {
581                        String value = attributes.get(key);
582                        try {
583                                StringAttribute sa = new StringAttribute(ce, key);
584                                sa.setExpression(value);
585                        }
586                        catch(IllegalActionException e) {
587                                e.printStackTrace();
588                        }
589                        catch(NameDuplicationException e) {
590                                e.printStackTrace();
591                        }
592                }
593        }
594
595        /**
596         * Remove a KAR file from the Component Library and from the user's Disk.
597         * 
598         * @param File
599         *            aKarFile
600         */
601        public void deleteKAR(File aKarFile) {
602
603                KARCacheManager kcm = KARCacheManager.getInstance();
604                if (!kcm.isCached(aKarFile)) {
605                        // this file is not in the cache.
606                        return;
607                }
608
609                KARFile karf;
610                try {
611
612                        karf = new KARFile(aKarFile);
613                        File karFileLocation = karf.getFileLocation();
614                        karf.close(); // free up the file pointer
615
616                        // remember what was in the kar file before we remove it all
617                        Vector<KARCacheContent> entries = kcm
618                                        .getKARCacheContents(karFileLocation);
619
620                        // Remove it from the Index
621                        LibItem karItem = getIndex().findKar(karFileLocation);
622                        // make sure KAR is in library
623                        if(karItem != null) {
624                        getIndex().removeItem(karItem.getLiid());
625    
626                        // Remove it from the Library
627                        ComponentEntity item = findTreeItem(karItem.getLiid());
628                        if (item == null)
629                                return;
630                        item.setContainer(null);
631                        }
632
633                        // Remove it from the cache
634                        kcm.removeKARFromCache(karFileLocation);
635
636                        // verify that it is gone before doing the rest
637                        if (!kcm.isCached(karFileLocation)) {
638
639                                // Remove the kar file from the file system
640                                boolean success = karFileLocation.delete();
641                                if (success) {
642                                        log.info(karFileLocation.toString() + " was deleted.");
643                                } else {
644                                        System.out.println("Unable to delete "
645                                                        + karFileLocation.toString());
646                                }
647
648                                // Update the Ontologies if no other KARs contain the entries
649                                Vector<KeplerLSID> lsidsToRemoveFromOntologies = new Vector<KeplerLSID>();
650                                CacheManager cache = CacheManager.getInstance();
651                                for (KARCacheContent entry : entries) {
652                                        if (isDebugging)
653                                                log.debug(entry.getLsid());
654                                        if (cache.isContained(entry.getLsid())) {
655                                                // Some other KAR has this object in it too
656                                                // Don't remove it from the ontologies
657                                        } else {
658                                                // This LSID is no longer in the cache
659                                                lsidsToRemoveFromOntologies.add(entry.getLsid());
660                                        }
661                                }
662                                // remove ontology ComponentEntities that are no longer in the
663                                // cache
664                                for (KeplerLSID lsid : lsidsToRemoveFromOntologies) {
665                                        // now we check each Liid to make sure there are no other
666                                        // LSIDs
667                                        // that exist for it
668
669                                        // Remove it from the Index
670                                        Vector<Integer> liidsRemovedFromIndex = getIndex()
671                                                        .removeItemsByLsid(lsid);
672
673                                        // Remove them from the Library too
674                                        for (Integer removedLiid : liidsRemovedFromIndex) {
675                                                ComponentEntity ontItem = findTreeItem(removedLiid
676                                                                .intValue());
677                                                if (ontItem != null) {
678                                                        ontItem.setContainer(null);
679                                                } else {
680                                                        log.warn("ontItem not found for " + removedLiid);
681                                                }
682                                        }
683                                }
684                        }
685
686                        // update the JTrees
687                        refreshJTrees();
688
689                } catch (Exception e) {
690                        e.printStackTrace();
691                }
692        }
693
694        /**
695         * Look only at the children of the given parent to match the given LIID. Do
696         * not recurse the children here, use findTreeItemDeep() for that.
697         * 
698         * @param parent
699         * @param liid
700         * @return
701         */
702        public ComponentEntity findTreeItem(CompositeEntity parent, int liid) {
703                if (isDebugging)
704                        log.debug("findTreeItem(" + parent.getName() + "," + liid + ")");
705                ComponentEntity ce = null;
706                List<ComponentEntity> children = parent
707                                .entityList(ComponentEntity.class);
708                for (ComponentEntity child : children) {
709                        int childLiid = LibraryManager.getLiidFor(child);
710                        if (isDebugging)
711                                log.debug("   " + childLiid + " " + child.getName());
712                        if (childLiid == liid) {
713                                ce = child;
714                                // break;
715                        }
716                }
717                return ce;
718        }
719
720        /**
721         * Look in the entire tree and return the ComponentEntity that matches the
722         * given LIID.
723         * 
724         * @param liid
725         * @return
726         */
727        public ComponentEntity findTreeItem(int liid) {
728                if (isDebugging)
729                        log.debug("findTreeItem(" + liid + ")");
730                CompositeEntity root = (CompositeEntity) getTreeModel().getRoot();
731                ComponentEntity ce = findTreeItemDeep(root, liid);
732                if (ce == null) {
733                        log.debug("Unable to find item " + liid + " in Library Tree");
734                }
735                return ce;
736        }
737
738        /**
739         * Recurse all children under parent to match the given LIID.
740         * 
741         * @param parent
742         * @param liid
743         * @return
744         */
745        public ComponentEntity findTreeItemDeep(CompositeEntity parent, int liid) {
746                if (isDebugging)
747                        log
748                                        .debug("findTreeItemDeep(" + parent.getName() + "," + liid
749                                                        + ")");
750                // check the children of this parent to see if any of them match the
751                // liid
752                ComponentEntity ce = findTreeItem(parent, liid);
753                if (ce == null) {
754                        // recurse the children if we don't find it
755                        List<CompositeEntity> children = parent
756                                        .entityList(CompositeEntity.class);
757                        for (CompositeEntity child : children) {
758                                ce = findTreeItemDeep(child, liid);
759                                if (ce != null) {
760                                        break;
761                                }
762                        }
763                }
764                return ce;
765        }
766
767        /**
768         * generate the EntityTreeModel version of the library
769         * 
770         *@return Description of the Return Value
771         *@exception IllegalActionException
772         *                Description of the Exception
773         */
774        private void generateTreeModel() {
775                if (isDebugging) {
776                        log.debug("generateTreeModel()");
777                }
778
779                _libraryModel = new VisibleTreeModel(_actorLibrary);
780        }
781        
782        private FilteredVisibleTreeModel generateTreeModel(File filterFile) {
783                FilteredVisibleTreeModel treeModel = new FilteredVisibleTreeModel(_libraryModel, filterFile);
784                return treeModel;
785        }
786
787        /**
788         * Get the CompositeEntity version of the library.
789         * 
790         * */
791        public CompositeEntity getActorLibrary() {
792
793                return _actorLibrary;
794        }
795
796        private void setActorLibrary(CompositeEntity ce) {
797                _actorLibrary = ce;
798                generateTreeModel();
799        }
800
801        /**
802         * @return LibIndex object that the manager is using
803         */
804        public LibIndex getIndex() {
805                return _libIndex;
806        }
807
808        /**
809         * Return the LIIDs for the given set of LSIDs.
810         * 
811         * @param lsids
812         * @return
813         */
814        public Vector<Integer> getLiidsFor(Vector<KeplerLSID> lsids) {
815                Vector<Integer> liids = new Vector<Integer>();
816
817                for (KeplerLSID lsid : lsids) {
818                        try {
819                                String query = "SELECT LIID FROM "
820                                                + LibIndex.LIBRARY_LSIDS_TABLE_NAME + " WHERE LSID = '"
821                                                + lsid + "'";
822                                ResultSet rs = _stmt.executeQuery(query);
823                                if (rs == null)
824                                        throw new SQLException("Query Failed: " + query);
825                                while (rs.next()) {
826                                        int liid = rs.getInt(1);
827                                        liids.add(new Integer(liid));
828                                }
829                                rs.close();
830                        } catch (SQLException sqle) {
831                                sqle.printStackTrace();
832                        }
833                }
834                return liids;
835        }
836
837        /**
838         * Return a populated LibItem for the given liid.
839         * 
840         * @param liid
841         * @return LibItem
842         */
843        public LibItem getTreeItemIndexInformation(int liid) {
844                LibItem li = null;
845                try {
846                        li = getPopulatedLibItem(liid);
847                } catch (Exception e) {
848                        e.printStackTrace();
849                }
850                return li;
851        }
852
853        private void clearActorLibrary() {
854                CompositeEntity root = getActorLibrary();
855                if (root != null) {
856                        List childen = root.entityList();
857                        for (Object child : childen) {
858                                if (child instanceof ComponentEntity) {
859                                        try {
860                                                ((ComponentEntity) child).setContainer(null);
861                                        } catch (IllegalActionException e) {
862                                                e.printStackTrace();
863                                        } catch (NameDuplicationException e) {
864                                                e.printStackTrace();
865                                        }
866                                }
867                        }
868                }
869        }
870        
871        
872        /**
873         * @return the TreeModel version of the library.
874         */
875        public EntityTreeModel getTreeModel() {
876                if (_libraryModel == null) {
877                        generateTreeModel();
878                }
879                return _libraryModel;
880        }
881        
882        public EntityTreeModel getTreeModel(File filterFile) {
883                return generateTreeModel(filterFile);
884        }
885
886        /**
887         * Refresh and redraw any JTrees that the LibraryManager is keeping track
888         * of.
889         * 
890         *@exception IllegalActionException
891         *                Description of the Exception
892         */
893        public void refreshJTrees() throws IllegalActionException {
894                if (isDebugging) {
895                        log.debug("refresh");
896                }
897
898                // Iterate over all the registered JTrees and reset their models.
899                //Iterator<WeakReference> treeItt = _trees.iterator();
900                
901                int size = _trees.size();
902                int i = 0;
903                while (i < size){
904                        WeakReference<JTree> wf = _trees.elementAt(i);
905                        JTree ptree = (JTree)wf.get();
906                        if (ptree == null)
907                        {
908                                _trees.remove(wf);
909                                size--;
910                        }
911                        else {
912                                ptree.setModel(getTreeModel());
913
914                                // stop listening for expansion events since we are going to
915                                // expand the paths and do not want to modify the list
916                                ptree.removeTreeExpansionListener(this);
917                                
918                                // expand all the paths that were previously expanded
919                                final List<TreePath> expansions = _treeExpansionMap.get(ptree);
920                                for(TreePath path : expansions) {
921                                    //System.out.println("expanding " + path);
922                                    ptree.expandPath(path);
923                                }
924                                
925                                // start listening again for expansion events
926                                ptree.addTreeExpansionListener(this);
927                                
928                                /*
929                                EntityTreeModel etm = (EntityTreeModel) ptree.getModel();
930
931                                // Of course this would be easier if
932                                // ptolemy.vergil.tree.EntityTreeModel
933                                // had a reload method similar to DefaultTreeModel
934                                // that did this for us
935                                // TODO etm.reload()
936
937                                ArrayList<NamedObj> path = new ArrayList<NamedObj>();
938                                path.add(0, (NamedObj) etm.getRoot());
939                                TreePath tp = new TreePath(path);
940                                etm.valueForPathChanged(tp, etm.getRoot());
941                                */
942                                i++;
943                        }
944                }
945        }
946
947        /**
948         * Set the workspace for the actor library and regenerate
949         * 
950         * @param ws
951         */
952        public void setActorLibraryWorkspace(Workspace ws) {
953                if (isDebugging)
954                        log.debug("setActorLibraryWorkspace(" + ws.getName() + ")");
955                _actorLibraryWorkspace = ws;
956        }
957        
958           /**
959     * Populate this LibItem Object from the database using the given Library
960     * Index ID.
961     * 
962     * @param liid
963     * @throws SQLException
964     */
965    public LibItem getPopulatedLibItem(int liid) throws SQLException {
966        
967        LibItem li = new LibItem();
968        _getPopulateInfoForLIIDPrepStmt.setInt(1, liid);
969        ResultSet rs = _getPopulateInfoForLIIDPrepStmt.executeQuery();
970        if (rs == null) throw new SQLException("Query Failed: " + _getPopulateInfoForLIIDPrepStmt);
971        if (rs.next()) {
972
973            li.setLiid(liid);
974
975            int parent = rs.getInt(1);
976            if (rs.wasNull()) {
977                li.setParent(null);
978            } else {
979                li.setParent(new Integer(parent));
980            }
981
982            li.setLeft(rs.getInt(2));
983            li.setRight(rs.getInt(3));
984            li.setLevel(rs.getInt(4));
985
986            String lsid = rs.getString(5);
987            if (rs.wasNull()) {
988                li.setLsid(null);
989            } else {
990                try {
991                    li.setLsid(new KeplerLSID(lsid));
992                } catch (Exception e) {
993                    e.printStackTrace();
994                }
995            }
996            li.setType(rs.getInt(6));
997            li.setName(rs.getString(7));
998
999            // double check there is only one row returned for this liid
1000            if (rs.next()) {
1001                throw new SQLException(LibIndex.LIBRARY_INDEX_TABLE_NAME
1002                        + " is corrupt"
1003                        + " more than one row returned for primary key "
1004                        + li.getLiid());
1005            }
1006        }
1007        rs.close();
1008
1009        populateAttributes(li, liid);
1010        populateLsids(li, liid);
1011        
1012        return li;
1013
1014    }
1015
1016    /**
1017     * Populate this LibItem with the corresponding Attributes stored in the
1018     * LIBRARY_ATTRIBUTES table for the given LIID.
1019     * 
1020     * @param liid
1021     * @param stmt
1022     * @throws SQLException
1023     */
1024    private void populateAttributes(LibItem li, int liid)
1025            throws SQLException {
1026
1027        _getNameValueForLIIDPrepStmt.setInt(1, liid);
1028        ResultSet rs = _getNameValueForLIIDPrepStmt.executeQuery();
1029        if (rs != null) {
1030            while (rs.next()) {
1031                String name = rs.getString(1);
1032                String value = rs.getString(2);
1033                if (value == null) {
1034                    value = new String();
1035                }
1036                li.addAttribute(name, value);
1037            }
1038        }
1039        rs.close();
1040
1041    }
1042
1043    /**
1044     * Populate this LibItem with the corresponding LSID values stored in the
1045     * LIBRARY_LSIDS table for the given LIID.
1046     * 
1047     * @param liid
1048     * @param stmt
1049     * @throws SQLException
1050     */
1051    private void populateLsids(LibItem li, int liid) throws SQLException {
1052
1053        _getLSIDForLIIDPrepStmt.setInt(1, liid);
1054        ResultSet rs = _getLSIDForLIIDPrepStmt.executeQuery();
1055        if (rs != null) {
1056            while (rs.next()) {
1057                String lsidStr = rs.getString(1);
1058                KeplerLSID lsid;
1059                try {
1060                    lsid = new KeplerLSID(lsidStr);
1061                    li.addLsid(lsid);
1062                } catch (Exception e) {
1063                    e.printStackTrace();
1064                }
1065            }
1066        }
1067        rs.close();
1068
1069    }
1070
1071    /** Called whenever an item in the tree is expanded. */
1072    @Override
1073    public void treeExpanded(TreeExpansionEvent event) {
1074        final Object tree = event.getSource();
1075        final List<TreePath> expansions = _treeExpansionMap.get(tree);
1076        if(expansions != null) {
1077            //System.out.println("expanded " + event.getPath());
1078            expansions.add(event.getPath());
1079        }
1080    }
1081
1082
1083    /** Called whenever an item in the tree is collapsed. */
1084    @Override
1085    public void treeCollapsed(TreeExpansionEvent event) {
1086        final Object tree = event.getSource();
1087        final List<TreePath> expansions = _treeExpansionMap.get(tree);
1088        if(expansions != null) {
1089            //System.out.println("collapsed " + event.getPath());
1090            expansions.remove(event.getPath());
1091        }
1092    }
1093
1094
1095}