001/*
002 * Copyright (c) 2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2015-10-28 20:48:37 +0000 (Wed, 28 Oct 2015) $' 
007 * '$Revision: 34128 $'
008 * 
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029
030/**
031 * 
032 */
033package org.kepler.objectmanager.library;
034
035import java.io.File;
036import java.io.IOException;
037import java.sql.Connection;
038import java.sql.PreparedStatement;
039import java.sql.ResultSet;
040import java.sql.SQLException;
041import java.sql.Statement;
042import java.util.HashMap;
043import java.util.Hashtable;
044import java.util.Iterator;
045import java.util.Map;
046import java.util.Map.Entry;
047import java.util.Vector;
048import java.util.regex.Matcher;
049import java.util.regex.Pattern;
050
051import org.apache.commons.logging.Log;
052import org.apache.commons.logging.LogFactory;
053import org.kepler.build.modules.Module;
054import org.kepler.build.modules.ModuleTree;
055import org.kepler.build.util.Version;
056import org.kepler.kar.KARCacheContent;
057import org.kepler.kar.KARCacheError;
058import org.kepler.kar.KARCacheManager;
059import org.kepler.kar.KARCached;
060import org.kepler.kar.KAREntry;
061import org.kepler.kar.KARFile;
062import org.kepler.kar.handlers.ActorMetadataKAREntryHandler;
063import org.kepler.moml.KeplerActorMetadata;
064import org.kepler.moml.KeplerMetadataExtractor;
065import org.kepler.objectmanager.cache.ActorCacheObject;
066import org.kepler.objectmanager.cache.CacheContent;
067import org.kepler.objectmanager.cache.LocalRepositoryManager;
068import org.kepler.objectmanager.cache.LocalRepositoryManager.LocalRepository;
069import org.kepler.objectmanager.lsid.KeplerLSID;
070import org.kepler.sms.NamedOntClass;
071import org.kepler.sms.NamedOntModel;
072import org.kepler.sms.OntologyCatalog;
073import org.kepler.util.DotKeplerManager;
074import org.kepler.util.StatusNotifier;
075
076
077/**
078 * A library index for keeping track of the tree structure in the component
079 * library. This class uses preorder tree traversal method in an SQL table to
080 * maintain the indexed structure of the component library instead of using XML
081 * (as was previously done). This allows for easier control over the ordering of
082 * the contents of the tree, faster and more targeted access to the contents of
083 * the library using SQL queries (and some simple math), and it also introduces
084 * a very useful new Identifier for the contents of the tree, the LIID.
085 * 
086 * Two companion tables, LIBRARY_LSIDS and LIBRARY_ATTRIBUTES, are used to store
087 * additional metadata about the library contents. The LIBRARY_LSIDS table
088 * serves to track the existence of multiple LSIDs for one single Component in
089 * the library. This is necessary for handling revision management and the user
090 * will be allowed to toggle which LSID the component is currently associated
091 * with, without destroying the look of the component library tree. The
092 * LIBRARY_ATTRIBUTES table is used for storing single string values that are
093 * associated with component library items. For example, a KAR item can have the
094 * location of that KAR on disk stored as an attribute of the library index
095 * item.
096 * 
097 * @author Aaron Schultz
098 */
099public class LibIndex {
100        private static final Log log = LogFactory.getLog(LibIndex.class.getName());
101        private static final boolean isDebugging = log.isDebugEnabled();
102
103        /**
104         * the name of the table in the database
105         */
106        public static final String LIBRARY_INDEX_TABLE_NAME = "LIBRARY_INDEX";
107
108        /**
109     * the name of the table in the database used to store attributes
110     */
111    public static final String LIBRARY_LSIDS_TABLE_NAME = "LIBRARY_LSIDS";
112
113    /**
114     * the name of the table in the database used to store attributes
115     */
116    public static final String LIBRARY_ATTRIBUTES_TABLE_NAME = "LIBRARY_ATTRIBUTES";
117    
118    /**
119         * Map of integers for the different types of objects that are stored in the
120         * LIBRARY_INDEX table. To add a new type to the tree you must do a few
121         * things. Add a new static integer here. Modify LibraryManager.getTreeItem
122         * to handle the new type. Add methods to this class for recognizing the
123         * type in KAR files.
124         */
125        public static final int TYPE_COMPONENT = 1;
126        public static final int TYPE_NAMED_OBJ = 2;
127        public static final int TYPE_ONTOLOGY = 3;
128        public static final int TYPE_CONCEPT = 4;
129        public static final int TYPE_FOLDER = 5;
130        public static final int TYPE_LOCALREPO = 6;
131        public static final int TYPE_KAR = 7;
132        public static final int TYPE_KAR_ERROR = 8;
133        public static final int TYPE_KARFOLDER = 9;
134        public static final int TYPE_OBJECT = 10;
135
136        /**
137         * Attribute names that are used to keep information about Library nodes.
138         */
139        public static final String ATT_REPOPATH = "REPOPATH";
140        public static final String ATT_FOLDER = "FOLDER";
141        public static final String ATT_KARENTRYPATH = "KARENTRYPATH";
142        public static final String ATT_KARFILE = "KARFILE";
143        public static final String ATT_XMLFILE = "XMLFILE";
144        public static final String ATT_CLASSNAME = "CLASSNAME";
145
146        private LibSearch _searcher;
147        private Hashtable<Integer, Integer> _searchTypeMap;
148
149        // Convenience references
150        private Connection _conn;
151        private Statement _stmt;
152
153        // Prepared Statements
154        private PreparedStatement _updateOrderPrepStmt;
155        private PreparedStatement _updateLsidPrepStmt;
156        private PreparedStatement _getLIIDForKarPrepStmt;
157        private PreparedStatement _getLIIDForXMLPrepStmt;
158        private PreparedStatement _getLIIDForFolderPrepStmt;
159        private PreparedStatement _getLIIDForKarEntryPrepStmt;
160        private PreparedStatement _getLIIDForKarErrorPrepStmt;
161        private PreparedStatement _getLIIDForRepositoryPrepStmt;
162        private PreparedStatement _getLIIDForOntologyNamePrepStmt;
163        private PreparedStatement _getLIIDForOntologyClassPrepStmt;
164        private PreparedStatement _getNumRowsInLibraryIndexPrepStmt;
165        private PreparedStatement _getLIIDOfParentsPrepStmt;
166        private PreparedStatement _getLIIDForParentAndNamePrepStmt;
167        private PreparedStatement _getLIIDForNullParentAndNamePrepStmt;
168        private PreparedStatement _getRangeForLIIDPrepStmt;
169        private PreparedStatement _getLIIDForParentPrepStmt;
170        private PreparedStatement _getLftRgtSumPrepStmt;
171        private PreparedStatement _getLIIDRootsPrepStmt;
172        private PreparedStatement _deleteLIIDFromLibraryAttributesPrepStmt;
173        private PreparedStatement _insertIntoLibraryAttributesPrepStmt;
174        private PreparedStatement _getNumLIIDForLIIDAndLSIDPrepStmt;
175        private PreparedStatement _insertIntoLibraryLSIDsPrepStmt;
176
177        private LibraryManager _libraryManager;
178        
179        /** Level for root objects in the tree. */
180        private static final int ROOT_LEVEL = 1;
181
182        /** Root item for Demos */
183        private LibItem _demosFolderItem;
184        
185        /** Pattern for the modules' persistent workflow directory. */
186    private Pattern _demosFolderPattern = Pattern.compile(Pattern
187            .quote(DotKeplerManager.getInstance()
188                    .getPersistentModuleWorkflowsDirString())
189            + "([^" + Pattern.quote(File.separator) + "]+)");
190
191        /**
192         * There are two ways to insert rows into the table. One was is to have the
193         * insert statement handle the ordering of the hierarchy at insert time.
194         * This is very convenient for inserting just one row. But very time
195         * consuming for inserting many rows. For many rows, one may toggle the
196         * _orderedInsert to Off and once the rows have been inserted call the
197         * refreshPreorderValues method to order the rows based on parent
198         * relationships.
199         */
200        private boolean _orderedInsert;
201
202        /**
203         * A constructor that is given a connection to the database.
204         * 
205         * @param conn
206         */
207        public LibIndex(Connection conn) {
208                initialize(conn);
209        }
210
211        /**
212         * Return the current value of orderedInsert. A value of false means that
213         * items inserted will not be automatically ordered during insert.
214         * 
215         * @return
216         */
217        public boolean isOrderedInsert() {
218                return _orderedInsert;
219        }
220
221        /**
222         * In general orderedInsert should always be true. When inserting many rows
223         * however it is faster to not order the entire table dynamically on every
224         * insert but to wait until all the rows have been inserted and then update
225         * the ordering based on parent relationships using the
226         * refreshPreorderValues method. This method allows you to toggle ordering
227         * during insert to on (true) or off (false).
228         * 
229         * @param orderedInsert
230         */
231        public void setOrderedInsert(boolean orderedInsert) {
232                _orderedInsert = orderedInsert;
233        }
234
235        /**
236         * Initialize the instance.
237         * 
238         * @param conn
239         */
240        private void initialize(Connection conn) {
241                if (isDebugging)
242                        log.debug("initialize(" + conn.toString() + ")");
243                _conn = conn;
244
245                _libraryManager = LibraryManager.getInstance();
246                
247                try {
248                        // By creating the statement and keeping it around
249                        // make sure to close your resultsets to save memory
250                        _stmt = _conn.createStatement();
251
252                        _updateOrderPrepStmt = _conn.prepareStatement("update "
253                                        + LIBRARY_INDEX_TABLE_NAME
254                                        + " SET LFT=?, RGT=? WHERE LIID=?");
255                        _updateLsidPrepStmt = _conn.prepareStatement("update "
256                                        + LIBRARY_INDEX_TABLE_NAME + " SET LSID=? WHERE LIID=?");
257                        
258                        _getLIIDForKarPrepStmt = _conn.prepareStatement("SELECT LIID FROM "
259                                + LIBRARY_ATTRIBUTES_TABLE_NAME + " WHERE NAME = '"
260                                + ATT_KARFILE + "' AND VALUE = ?");
261
262                        _getLIIDForXMLPrepStmt = _conn.prepareStatement("SELECT LIID FROM "
263                    + LIBRARY_ATTRIBUTES_TABLE_NAME + " WHERE NAME = '"
264                    + ATT_XMLFILE + "' AND VALUE = ?");
265                        
266                        _getLIIDForFolderPrepStmt = _conn.prepareStatement("SELECT LIID FROM "
267                                + LIBRARY_ATTRIBUTES_TABLE_NAME + " WHERE NAME = '"
268                                + ATT_FOLDER + "' AND VALUE = ?");
269
270                        
271                    _getLIIDForKarEntryPrepStmt = _conn.prepareStatement("SELECT LIID FROM "
272                                + LIBRARY_ATTRIBUTES_TABLE_NAME + " WHERE NAME = '"
273                                + ATT_KARENTRYPATH + "' AND VALUE = ?");
274
275                    _getLIIDForKarErrorPrepStmt = _conn.prepareStatement("SELECT LIID FROM "
276                                + LIBRARY_ATTRIBUTES_TABLE_NAME + " WHERE NAME = '"
277                                + ATT_KARFILE + "' AND VALUE = ?");
278
279                    _getLIIDForRepositoryPrepStmt = _conn.prepareStatement("SELECT LIID FROM "
280                        + LIBRARY_ATTRIBUTES_TABLE_NAME + " WHERE NAME = '"
281                        + ATT_REPOPATH + "' AND VALUE = ?");
282
283                    _getLIIDForOntologyNamePrepStmt = _conn.prepareStatement("SELECT LIID FROM "
284                            + LIBRARY_INDEX_TABLE_NAME + " WHERE NAME = ? AND LEVEL = 1");
285
286                    _getLIIDForOntologyClassPrepStmt = _conn.prepareStatement("SELECT LIID FROM "
287                            + LIBRARY_INDEX_TABLE_NAME + " WHERE LSID = ?");
288
289                    _getNumRowsInLibraryIndexPrepStmt = _conn.prepareStatement("SELECT count(LIID) from "
290                        + LIBRARY_INDEX_TABLE_NAME);
291
292                    _getLIIDOfParentsPrepStmt = _conn.prepareStatement("SELECT LIID from "
293                            + LIBRARY_INDEX_TABLE_NAME + " WHERE LFT < ? AND RGT > ? "
294                            + "ORDER BY LEVEL");
295
296                    _getLIIDForParentAndNamePrepStmt = _conn.prepareStatement("select liid from "
297                            + LIBRARY_INDEX_TABLE_NAME + " where parent = ? and name = ?");
298
299                    _getLIIDForNullParentAndNamePrepStmt = _conn.prepareStatement("select LIID from "
300                            + LIBRARY_INDEX_TABLE_NAME + " where PARENT IS NULL AND NAME = ?");
301
302                    _getRangeForLIIDPrepStmt = _conn.prepareStatement("SELECT LFT,RGT from "
303                            + LIBRARY_INDEX_TABLE_NAME + " WHERE LIID = ?");
304                        
305                    _getLIIDForParentPrepStmt = _conn.prepareStatement("SELECT liid FROM "
306                            + LIBRARY_INDEX_TABLE_NAME + " WHERE parent = ? ORDER BY TYPE,NAME");
307
308                    _getLftRgtSumPrepStmt = _conn.prepareStatement("select (sum(LFT) + sum(RGT)) FROM "
309                            + LIBRARY_INDEX_TABLE_NAME);
310                    
311                    _getLIIDRootsPrepStmt = _conn.prepareStatement("SELECT LIID FROM "
312                            + LIBRARY_INDEX_TABLE_NAME + " WHERE LEVEL = 1 ORDER BY TYPE,NAME");
313                    
314                    _deleteLIIDFromLibraryAttributesPrepStmt = _conn.prepareStatement("DELETE FROM "
315                            + LIBRARY_ATTRIBUTES_TABLE_NAME + " WHERE LIID = ?");
316
317            _insertIntoLibraryAttributesPrepStmt = _conn.prepareStatement("INSERT INTO "
318                    + LIBRARY_ATTRIBUTES_TABLE_NAME + " (LIID,NAME,VALUE) values (?, ?, ?)"); 
319                        
320            _getNumLIIDForLIIDAndLSIDPrepStmt = _conn.prepareStatement("SELECT count(LIID) FROM "
321                    + LIBRARY_LSIDS_TABLE_NAME + " WHERE LIID = ? AND LSID = ?");
322            
323            _insertIntoLibraryLSIDsPrepStmt = _conn.prepareStatement("insert into "
324                    + LIBRARY_LSIDS_TABLE_NAME + " (LIID,LSID) values (?,?)");
325            
326        } catch (SQLException e) {
327            e.printStackTrace();
328        }
329                
330                // Share the connection with the LibSearch
331                _searcher = new LibSearch(_conn);
332                initSearchMap();
333
334                setOrderedInsert(true);
335        }
336
337        /**
338         * Delete all rows in the LIBRARY_INDEX table. This will also delete all
339         * rows in the LIBRARY_ATTRIBUTES table by cascading foreign key deletes.
340         */
341        public void clear() {
342                String clear = "delete from " + LIBRARY_INDEX_TABLE_NAME;
343                if (isDebugging)
344                        log.debug(clear);
345                String resetAutoInc = "ALTER TABLE " + LIBRARY_INDEX_TABLE_NAME
346                                + " ALTER COLUMN LIID RESTART WITH 1";
347                if (isDebugging)
348                        log.debug(resetAutoInc);
349                try {
350                        _stmt.executeUpdate(clear);
351                        _stmt.execute(resetAutoInc);
352
353                        getSearcher().clear();
354                        _stmt.getConnection().commit();
355                } catch (SQLException e) {
356                        e.printStackTrace();
357                }
358                
359                _demosFolderItem = null;
360        }
361
362        /**
363         * Completely delete the library index and recreate it from the contents of
364         * cached KARs.
365         */
366        public void rebuild() {
367
368                StatusNotifier.log("Building Library Index (this can take several minutes).");
369                
370                clear();
371
372                setOrderedInsert(false);
373                try {
374
375                        KARCacheManager kcm = KARCacheManager.getInstance();
376
377                        // Add all Kar Contents to the Library Index table
378                        Vector<KARCacheContent> contents = kcm.getKARCacheContents();
379                        for (KARCacheContent content : contents) {
380
381                                assureOntologyComponent(content);
382                                assureKarEntry(content);
383
384                        }
385
386                        // Add all Kar Errors to the Library
387                        Vector<KARCacheError> errors = kcm.getKARCacheErrors();
388                        for (KARCacheError error : errors) {
389                                assureKarError(error);
390                        }
391                        
392                        for(File xmlFile : LocalRepositoryManager.getInstance().getXMLFiles()) {
393                            assureXML(xmlFile);
394                        }
395
396                        // Refresh the ordering of the library (this is much faster than
397                        // updating the order every time we insert)
398                        refreshPreorderValues();
399
400                        // because we insert with no order here, we can't
401                        // easily determine the path to any given LibItem
402                        // so we wait until all individual LibItems have been
403                        // created and ordered to finish up indexing the items
404                        // for the search
405                        finishSearchIndexing();
406
407                } catch (SQLException e) {
408                        e.printStackTrace();
409                }
410                setOrderedInsert(true);
411        }
412
413        /**
414         * Select all of the existing LibItems from the Library Index and insert all
415         * of their parent items into the Search Index.
416         * 
417         * @throws SQLException
418         */
419        private void finishSearchIndexing() throws SQLException {
420                Vector<LibItem> items = getItems();
421                for (LibItem item : items) {
422                        addAllParentsToSearchIndex(item);
423                }
424        }
425
426        /**
427         * Update the default LSID for the given LIID to the given LSID.
428         * 
429         * @param liid
430         * @param lsid
431         * @throws SQLException
432         */
433        public void updateDefaultLsid(int liid, KeplerLSID lsid)
434                        throws SQLException {
435                if (isDebugging)
436                        log.debug("updateDefaultLsid(" + liid + "," + lsid + ")");
437
438                try {
439                        _updateLsidPrepStmt.setString(1, lsid.toString());
440                        _updateLsidPrepStmt.setInt(2, liid);
441
442                        _updateLsidPrepStmt.executeUpdate();
443                        _updateLsidPrepStmt.clearParameters();
444                        _conn.commit();
445                } catch (SQLException sqle) {
446                        throw sqle;
447                }
448        }
449
450        /**
451         * Remove all data from the index database that is associated with the given
452         * Library Index ID and all of the data of it's children.
453         * 
454         * @param liid
455         * @return
456         */
457        public boolean removeItem(int liid) {
458                boolean success = false;
459                LibItem li = null;
460        try {
461            li = _libraryManager.getPopulatedLibItem(liid);
462        } catch (SQLException e) {
463            // TODO Auto-generated catch block
464            e.printStackTrace();
465        }
466                if (li != null) {
467                        success = removeItem(li);
468                }
469                return success;
470        }
471
472        /**
473         * Remove all data from the index database that is associated with the given
474         * Library Item (using the Library Index ID) and all of the data of it's
475         * children. This method is the same as removeItem(int liid). It is here for
476         * convenience.
477         * 
478         * @param li
479         * @return
480         */
481        public boolean removeItem(LibItem li) {
482                String delete = "DELETE FROM " + LibIndex.LIBRARY_INDEX_TABLE_NAME
483                                + " WHERE LIID = " + li.getLiid();
484                // NOTE: the Foreign Key to the parent LIID prevents us from doing this:
485                // + " WHERE LFT >= " + li.getLeft() + " AND RGT <= " + li.getRight();
486                // which is faster performance wise. Instead we'll trust the ON DELETE
487                // CASCADE for the parent foreign key to properly remove all the
488                // children of the LIID we're removing which is a little slower
489                // performance wise but still should work fine
490
491                String updateLFT = "UPDATE " + LibIndex.LIBRARY_INDEX_TABLE_NAME
492                                + " SET LFT = LFT - " + (li.getRight() - li.getLeft() + 1)
493                                + " WHERE LFT > " + li.getLeft();
494                String updateRGT = "UPDATE " + LibIndex.LIBRARY_INDEX_TABLE_NAME
495                                + " SET RGT = RGT - " + (li.getRight() - li.getLeft() + 1)
496                                + " WHERE RGT > " + li.getLeft();
497                try {
498                        _stmt.executeUpdate(delete);
499                        _stmt.executeUpdate(updateLFT);
500                        _stmt.executeUpdate(updateRGT);
501                        _stmt.getConnection().commit();
502                } catch (Exception e) {
503                        try {
504                                _stmt.getConnection().rollback();
505                        } catch (SQLException e1) {
506                                e1.printStackTrace();
507                        }
508                        return false;
509                }
510                return true;
511        }
512
513        /**
514         * This method will remove the given LSID from being associated with any of
515         * the Library Items. If there is another LSID associated with the given
516         * item then it will be assigned as the default. If the Library Item is only
517         * associated with the given LSID then the Item itself will be removed.
518         * 
519         * @param lsid
520         * @return Vector<Integer> all LIIDs that got removed
521         * @throws SQLException
522         */
523        public Vector<Integer> removeItemsByLsid(KeplerLSID lsidToRemove)
524                        throws SQLException {
525                Vector<Integer> liidsThatGotRemoved = new Vector<Integer>();
526
527                // First keep the history of all LIIDs this LSID was associated with
528                Vector<KeplerLSID> kludge = new Vector<KeplerLSID>(1);
529                kludge.add(lsidToRemove);
530                Vector<Integer> liidAssociations = LibraryManager.getInstance()
531                                .getLiidsFor(kludge);
532
533                // Now remove all the LIID -> LSID associations for this LSID
534                String remove = "DELETE FROM " + LIBRARY_LSIDS_TABLE_NAME
535                                + " WHERE lsid = '" + lsidToRemove + "'";
536                _stmt.executeUpdate(remove);
537
538                // Now go through all the liids
539                for (Integer liid : liidAssociations) {
540                        // check if this LSID is the default LSID
541                        String defaultQuery = "SELECT LSID FROM "
542                                        + LibIndex.LIBRARY_INDEX_TABLE_NAME + " WHERE LIID = "
543                                        + liid.intValue();
544                        if (isDebugging)
545                                log.debug(defaultQuery);
546                        ResultSet rs1 = null;
547                        ResultSet rs2 = null;
548                        try {
549                                rs1 = _stmt.executeQuery(defaultQuery);
550                                if (rs1 == null)
551                                        throw new SQLException("Query Failed: " + defaultQuery);
552                                if (rs1.next()) {
553                                        String currentDefaultLsidStr = rs1.getString(1);
554                                        KeplerLSID currentDefaultLsid = null;
555                                        try {
556                                                currentDefaultLsid = new KeplerLSID(currentDefaultLsidStr);
557                                        } catch (Exception e) {
558                                                e.printStackTrace();
559                                        }
560                                        if (currentDefaultLsid.equals(lsidToRemove)) {
561                                                // This is the default LSID
562                                                // check if there are any LSIDs that can become the new
563                                                // default
564                                                String newDefaultQuery = "SELECT LSID FROM "
565                                                                + LIBRARY_LSIDS_TABLE_NAME
566                                                                + " WHERE LIID = " + liid.intValue()
567                                                                + " order by LSID DESC";
568                                                if (isDebugging)
569                                                        log.debug(newDefaultQuery);
570                                                rs2 = _stmt.executeQuery(newDefaultQuery);
571                                                if (rs2 == null)
572                                                        throw new SQLException("Query Failed: "
573                                                                        + newDefaultQuery);
574                                                if (rs2.next()) {
575                                                        // Yes there are other LSIDs associated with this LIID,
576                                                        // set the first one we get back as the new default
577                                                        // (hopefully the "order by DESC" will give us the
578                                                        // highest revision)
579                                                        String updateDefaultLSID = "UPDATE "
580                                                                        + LibIndex.LIBRARY_INDEX_TABLE_NAME
581                                                                        + " SET LSID = '" + rs2.getString(1)
582                                                                        + " WHERE LIID = " + liid.intValue();
583                                                        if (isDebugging)
584                                                                log.debug(updateDefaultLSID);
585                                                        _stmt.executeUpdate(updateDefaultLSID);
586                                                } else {
587                                                        // No there are no more LSIDs associated with this LIID
588                                                        // so let's remove this LIID if it is a leaf
589                                                        LibItem li = _libraryManager.getPopulatedLibItem(liid);
590                                                        // double check that this is a leaf node
591                                                        if ((li.getRight() - li.getLeft()) == 1) {
592                                                                li.delete(_stmt);
593                                                                liidsThatGotRemoved.add(li.getLiid());
594                                                        } else {
595                                                                // theoretically we never run into this BUT
596                                                                // if we do let's go ahead and null out the
597                                                                // default lsid
598                                                                String updateDefaultLSID = "UPDATE "
599                                                                                + LibIndex.LIBRARY_INDEX_TABLE_NAME
600                                                                                + " SET LSID = NULL" + " WHERE LIID = "
601                                                                                + liid.intValue();
602                                                                _stmt.executeUpdate(updateDefaultLSID);
603                                                                log
604                                                                                .warn("Nulled default LSID because the node is not a leaf");
605                                                        }
606                                                }
607                                        } else {
608                                                // This is not the default LSID so we're done with this one
609                                        }
610                                }
611                        } finally {
612                                if(rs1 != null) {
613                                        rs1.close();
614                                }
615                                if(rs2 != null) {
616                                        rs2.close();
617                                }
618                        }
619                }
620                _stmt.getConnection().commit();
621                return liidsThatGotRemoved;
622        }
623
624        /**
625         * Assures that the LibIndex row under the folder hierarchy exists for the
626         * given KARCacheContent. This method shouldn't really be used. It is only
627         * public so the LibraryManager.addKar() method can use it.
628         * 
629         * @param content
630         * @throws SQLException
631         */
632        public LibItem assureKarEntry(KARCacheContent content) throws SQLException {
633                if (isDebugging)
634                        log.debug("assureKarEntry(" + content.getName() + ")");
635
636                // add the object to the folders hierarchy
637                File karFile = content.getKarFile();
638                String entry = content.getName();
639
640                LibItem li = findKarEntry(karFile, entry);
641                if (li == null) {
642                        LibItem liParent = null;
643                        String[] entryPath = entry.split("/");
644                        if (entryPath.length > 1) {
645                                // entry is in a subfolder
646                                String folderPath = new String();
647                                for (int i = 0; i < (entryPath.length - 1); i++) {
648                                        folderPath = entryPath[i] + "/";
649                                }
650                                folderPath = folderPath.substring(0, folderPath.length() - 1);
651                                liParent = assureKarFolder(content.getKarCached(), folderPath);
652                        } else {
653                                liParent = assureKar(content.getKarCached());
654                        }
655                        
656                        CacheContent cc = content.getCacheContent();
657                        String actorName = cc.getName();
658
659                        li = new LibItem();
660                        li.setName(actorName);
661
662                        int t = determineLibIndexType(content);
663                        if (t > 0) {
664                                li.setType(t);
665                        } else {
666                                log
667                                                .error("Could not determine the LibIndex type for KARCacheContent");
668                                return null;
669                        }
670
671                        li.setParent(liParent.getLiid());
672                        li.setLevel(liParent.getLevel() + 1);
673                        li.setLsid(content.getLsid());
674                        li.addAttribute(ATT_KARENTRYPATH, getKarEntryPath(karFile, entry));
675                        String className = content.getCacheContent().getClassName();
676                        if (className != null) {
677                                li.addAttribute(ATT_CLASSNAME, className);
678                                transferAttributes(li, content);
679                        }
680                        insertNoOrder(li);
681
682                } else {
683                        // it is already there
684                        // this is probably an unnecessary check
685                }
686                return li;
687        }
688        
689        private static Map<KeplerLSID, Map<String, String>> cachedItemAttributes = new HashMap<KeplerLSID, Map<String, String>>();
690
691        private void transferAttributes(LibItem li, KARCacheContent content) {
692
693                if (cachedItemAttributes.containsKey(li.getLsid())) {
694                        Map<String, String> attributes = cachedItemAttributes.get(li.getLsid());
695                        for (String name : attributes.keySet()) {
696                                li.addAttribute(name, attributes.get(name));
697                        }
698                        return;
699                }
700
701                KARFile karFile = null;
702                try {
703                        karFile = new KARFile(content.getKarFile());
704                }
705                catch(IOException ex) {
706                        ex.printStackTrace();
707                }
708                
709                KAREntry karEntry = new KAREntry(content.getName());
710                ActorMetadataKAREntryHandler keh = new ActorMetadataKAREntryHandler();
711                ActorCacheObject aco;
712                try {
713                        aco = (ActorCacheObject) keh.cache(karFile, karEntry);
714                }
715                catch(Exception ex) {
716                        ex.printStackTrace();
717                        return;
718                }
719
720                Map<String, String> attributes = new HashMap<String, String>();
721                for (String attributeName : aco.getAttributeNames()) {
722                        String attributeValue = aco.getAttribute(attributeName);
723                        attributes.put(attributeName, attributeValue);
724                        li.addAttribute(attributeName, attributeValue);
725                }
726
727                cachedItemAttributes.put(li.getLsid(), attributes);
728        }
729
730        /**
731         * Determine the appropriate LibIndex type of the given KARCacheContent
732         * 
733         * @param kcc
734         * @return
735         */
736        public int determineLibIndexType(KARCacheContent content) {
737                int theType = -1;
738                String cacheObjectType = content.getCacheContent().getType();
739                boolean isActorCacheObject = false;
740                try {
741                        String actorCacheObjectType = "org.kepler.objectmanager.cache.ActorCacheObject";
742                        if (cacheObjectType.equals(actorCacheObjectType)) {
743                                isActorCacheObject = true;
744                        } else if(cacheObjectType.equals("org.kepler.objectmanager.cache.TextFileCacheObject")) {
745                          isActorCacheObject = false;
746                          return LibIndex.TYPE_OBJECT;
747                        } else {
748                                isActorCacheObject = KARFile.isSubclass(actorCacheObjectType,
749                                                cacheObjectType);
750                        }
751                } catch (ClassNotFoundException e) {
752                        e.printStackTrace();
753                }
754                if (isActorCacheObject) {
755                        theType = LibIndex.TYPE_COMPONENT;
756                } else {
757                        String entryObjectType = content.getType();
758                        boolean isGenericNamedObjType = false;
759                        try {
760                                String namedObjectType = "ptolemy.kernel.util.NamedObj";
761                                if (entryObjectType.equals(namedObjectType)) {
762                                        isGenericNamedObjType = true;
763                                } else {
764                                        isGenericNamedObjType = KARFile.isSubclass(namedObjectType,
765                                                        entryObjectType);
766                                }
767                        } catch (ClassNotFoundException e) {
768                                e.printStackTrace();
769                        }
770                        if (isGenericNamedObjType) {
771                                theType = LibIndex.TYPE_NAMED_OBJ;
772                        } else {
773                                theType = LibIndex.TYPE_OBJECT;
774                        }
775                }
776                return theType;
777        }
778
779        /**
780         * Assures that the LibIndex rows for the KARCacheContent object exist under
781         * the Ontology tree. This method shouldn't really be used. It is only
782         * public so the LibraryManager.addKar() method can use it.
783         * 
784         * @param content
785         */
786        public Vector<LibItem> assureOntologyComponent(KARCacheContent content) {
787                Vector<LibItem> items = new Vector<LibItem>();
788
789                OntologyCatalog oc = OntologyCatalog.instance();
790
791                // add the object in the ontology class hierarchy
792                for (KeplerLSID st : content.getSemanticTypes()) {
793                                            
794                        NamedOntClass noc = oc.getNamedOntClass(st.toString());
795                        if (noc != null) {
796
797                                // First determine if the NamedOntClass belongs to
798                                // an Ontology that is supposed to be showing up
799                                // in the library.
800                                boolean includeInOntologies = false;
801                                Iterator<NamedOntModel> libraryModels = oc
802                                                .getLibraryNamedOntModels();
803                                while (libraryModels.hasNext()) {
804                                        if (libraryModels.next().equals(noc.getModel())) {
805                                                includeInOntologies = true;
806                                                break;
807                                        }
808                                }
809                                // If this NamedOntClass belongs to an Ontology Model
810                                // that is configured to be included in the library
811                                // then add it, otherwise just skip it
812                                if (includeInOntologies) {
813
814                                        // Recurse up the tree
815                                        Vector<LibItem> parents = assureOntClass(noc);
816                                        for (LibItem parent : parents) {
817
818                                                CacheContent cc = content.getCacheContent();
819                                                String actorName = cc.getName();
820                                                
821                                                LibItem li = new LibItem();
822                                                li.setName(actorName);
823                                                li.setParent(parent.getLiid());
824                                                KeplerLSID lsid = content.getLsid();
825                                                if (lsid != null) {
826                                                        li.setLsid(lsid);
827                                                }
828
829                                                // Figure out what LibIndex type this KARCacheContent is
830                                                int t = determineLibIndexType(content);
831                                                if (t > 0) {
832                                                        li.setType(t);
833                                                } else {
834                                                        log
835                                                                        .error("Could not determine the LibIndex type for KARCacheContent");
836                                                        continue;
837                                                }
838
839                                                li.setLevel(parent.getLevel() + 1);
840
841                                                String className = content.getCacheContent()
842                                                                .getClassName();
843                                                if (className != null) {
844                                                        li.addAttribute(ATT_CLASSNAME, className);
845                                                        transferAttributes(li, content);
846                                                }
847                                                try {
848                                                        
849                                                        // See if this LibItem will collide with an existing LibItem
850                                                        int liidOfExistingChild = childExists(li.getParent(),li.getName());
851                                                        if (liidOfExistingChild > -1) {
852                                                                // A LIID with this name and parent already exists
853                                                                // just add the LSID to the existing LIID item
854                                                                insertLiidLsid(liidOfExistingChild, li.getLsid());
855                                                        } else {
856                                                                // create a new item in the library
857                                                                insertNoOrder(li);
858                                                                items.add(li);
859                                                        }
860                                                } catch (SQLException e) {
861                                                        e.printStackTrace();
862                                                }
863                                        }
864                                }
865                        }
866                }
867                return items;
868        }
869
870        /**
871         * Count how many rows there are in the LIBRARY_INDEX table.
872         * 
873         * @return
874         */
875        public int countItems() {
876                int count = 0;
877                try {
878                        ResultSet rs = null;
879                        try {
880                                rs = _getNumRowsInLibraryIndexPrepStmt.executeQuery();
881                                if (rs == null)
882                                        return count;
883                                if (rs.next()) {
884                                        count = rs.getInt(1);
885                                        if (rs.next()) {
886                                                // should never happen
887                                                throw new SQLException(
888                                                                "Multiple rows found in countItems()");
889                                        }
890                                }
891                        } finally {
892                                if(rs != null) {
893                                        rs.close();
894                                }
895                        }
896                } catch (SQLException sqle) {
897                        sqle.printStackTrace();
898                }
899                return count;
900
901        }
902
903        /**
904         * Returns a Vector of LibItems that are the parents of the given LibItem.
905         * 
906         * @param li
907         * @return Vector<LibItem> representing all parents of the given LibItem
908         * @throws SQLException
909         */
910        public Vector<LibItem> getPath(LibItem li) throws SQLException {
911                Vector<LibItem> pathItems = new Vector<LibItem>();
912
913                _getLIIDOfParentsPrepStmt.setInt(1, li.getLeft());
914                _getLIIDOfParentsPrepStmt.setInt(2, li.getRight());
915                if (isDebugging)
916                        log.debug(_getLIIDOfParentsPrepStmt);
917                ResultSet rs = null;
918                try {
919                        rs = _getLIIDOfParentsPrepStmt.executeQuery();
920                        if (rs == null)
921                                throw new SQLException("Query Failed: " + _getLIIDOfParentsPrepStmt);
922                        while (rs.next()) {
923                                int liid = rs.getInt(1);
924                                LibItem parentItem = _libraryManager.getPopulatedLibItem(liid);
925                                pathItems.add(parentItem);
926                        }
927                } finally {
928                        if(rs != null) {
929                                rs.close();
930                        }
931                }
932                pathItems.add(li);
933                return pathItems;
934        }
935
936        /**
937         * Get all of the items.
938         * 
939         * @return
940         */
941        public Vector<LibItem> getItems() {
942                return getItemsOfType(-1, -1);
943        }
944
945        /**
946         * Get all items of the given type.
947         * 
948         * @param type
949         * @return
950         */
951        public Vector<LibItem> getItemsOfType(int type) {
952                return getItemsOfType(type, -1);
953        }
954
955        /**
956         * Return items of the given type that are somewhere under the given root.
957         * 
958         * @param type
959         * @param root
960         * @return
961         */
962        public Vector<LibItem> getItemsOfType(int type, int root) {
963                Vector<LibItem> items = new Vector<LibItem>(countItems());
964
965                // TODO: actually select all the values instead of just the liid
966                String query = "SELECT LIID " + " FROM " + LIBRARY_INDEX_TABLE_NAME;
967
968                if (type <= 0 && root <= 0) {
969                        // get everything
970                } else {
971                        if (root > 0) {
972                                // get items under the specified root node
973                                try {
974                                        query += " WHERE " + getRange(root);
975                                } catch (SQLException sqle) {
976                                        log.warn(root + " library item not found");
977                                        return items;
978                                }
979                                if (type > 0) {
980                                        query += " AND TYPE = " + type;
981                                }
982                        } else {
983                                // root <= 0
984                                if (type > 0) {
985                                        // get everything with the specified type
986                                        query += " WHERE TYPE = " + type;
987                                }
988                        }
989                }
990
991                query += " ORDER BY LFT";
992
993                try {
994                        if (isDebugging)
995                                log.debug(query);
996                        ResultSet rs = null;
997                        try {
998                                rs = _stmt.executeQuery(query);
999                                if (rs == null)
1000                                        throw new SQLException("Query Failed: " + query);
1001                                while (rs.next()) {
1002                                        int liid = rs.getInt(1);
1003                                        // Doing it this way is kinda slow, but works for now
1004                        LibItem li = _libraryManager.getPopulatedLibItem(liid);
1005                                        items.add(li);
1006                                }
1007                        } finally {
1008                                if(rs != null) {
1009                                        rs.close();
1010                                }
1011                        }
1012                } catch (SQLException sqle) {
1013                        log.warn(root + " library item not found");
1014                        return items;
1015                }
1016                if (isDebugging)
1017                        log.debug(items.size() + " library items found");
1018                return items;
1019        }
1020
1021        /**
1022         * Convenience method for building the SQL string for queries needing the
1023         * range between an LIIDs LFT and RGT preorder values.
1024         * 
1025         * @param liid
1026         * @return
1027         * @throws SQLException
1028         */
1029        private String getRange(int liid) throws SQLException {
1030                String rangeString = new String();
1031                _getRangeForLIIDPrepStmt.setInt(1, liid);
1032                ResultSet rs = null;
1033                try {
1034                        rs = _getRangeForLIIDPrepStmt.executeQuery();
1035                        if (rs == null)
1036                                throw new SQLException("Query Failed: " + _getRangeForLIIDPrepStmt);
1037                        if (rs.next()) {
1038                                int l = rs.getInt(1);
1039                                int r = rs.getInt(2);
1040                                rangeString += " LFT >= " + l + " AND ";
1041                                rangeString += " RGT <= " + r;
1042                        }
1043                } finally {
1044                        if(rs != null) {
1045                                rs.close();
1046                        }
1047                }
1048                return rangeString;
1049
1050        }
1051        
1052        /**
1053         * Take a LibItem and check to see if there is already an entry
1054         * in the sql table for it depending on the type,name,and parent.
1055         * If there is already an item in the table, return the LIID for
1056         * the existing item.
1057         * 
1058         * @param li
1059         * @return long LIID of existing item or -1 if no duplicate is found
1060         */
1061        public int checkIfDuplicate(LibItem li) throws SQLException {
1062                int liid = -1;
1063
1064                _getLIIDForParentAndNamePrepStmt.setInt(1, li.getParent());
1065                _getLIIDForParentAndNamePrepStmt.setString(2, li.getName());
1066                ResultSet rs = null;
1067                try {
1068                        rs = _getLIIDForParentAndNamePrepStmt.executeQuery();
1069                        if (rs == null)
1070                                throw new SQLException("Query failed: " + _getLIIDForParentAndNamePrepStmt);
1071                        if (rs.next()) {
1072                                liid = rs.getInt(1);
1073                                if (rs.wasNull()) {
1074                                        // bad news.
1075                                        liid = -1;
1076                                }
1077                                if (rs.next()) {
1078                                        // also bad news
1079                                }
1080                        }
1081                } finally {
1082                        if(rs != null) {
1083                                rs.close();
1084                        }
1085                }
1086                return liid;
1087        }
1088        
1089        /**
1090         * Insert this LibItem into the table, make sure it doesn't already
1091         * exist.  If it does try to add the LSID to the LIBRARY_LSIDS table.
1092         * 
1093         * @param li
1094         * @throws SQLException
1095         */
1096        private void insert(LibItem li) throws SQLException {
1097                if (isDebugging)
1098                        log.debug("insert(" + li.getName() + ")");
1099                
1100                // Make sure there isn't an existing entry for this item
1101                int liidOfExistingChild = childExists(li.getParent(), li.getName());
1102                if (liidOfExistingChild > -1) {
1103                        try {
1104                                // Add the lsid to the Library_lsids table if the item already exists
1105                                insertLiidLsid(liidOfExistingChild, li.getLsid());
1106                        } catch (SQLException sqle) {
1107                                throw new SQLException("Unable to insert LIID for existing child.");
1108                        }
1109                } else {
1110                    /**
1111                     * Insert a new row for this LibItem under the parent liid or at level 1 if
1112                     * parent is null.
1113                     *
1114                     */
1115                String insert = "INSERT INTO " + LibIndex.LIBRARY_INDEX_TABLE_NAME
1116                        + " (PARENT,LFT,RGT,LEVEL,LSID,TYPE,NAME) values (";
1117
1118                int index = getAlphabeticInsertIndex(li);
1119                if (index < 0)
1120                    return;
1121
1122                li.setLeft(index);
1123                li.setRight(index + 1);
1124
1125                if (li.getParent() == null) {
1126                    insert += "NULL"; // parent
1127                } else {
1128                    insert += "" + li.getParent(); // parent
1129                }
1130
1131                insert += "," + li.getLeft(); // left
1132                insert += "," + li.getRight(); // right
1133                insert += "," + li.getLevel(); // level
1134
1135                if (li.getLsid() != null) {
1136                    insert += ",'" + li.getLsid().toString() + "'"; // lsid
1137                } else {
1138                    insert += ",NULL";
1139                }
1140                insert += "," + li.getType(); // type
1141                insert += ",'" + li.getName() + "'"; // name
1142                insert += ")";
1143
1144                String updateLeft = "UPDATE " + LibIndex.LIBRARY_INDEX_TABLE_NAME
1145                        + " SET LFT = LFT + 2 WHERE LFT >= " + li.getLeft();
1146                String updateRight = "UPDATE " + LibIndex.LIBRARY_INDEX_TABLE_NAME
1147                        + " SET RGT = RGT + 2 WHERE RGT >= " + li.getLeft();
1148
1149                if (isDebugging) {
1150                    log.debug("\n" + updateLeft + "\n" + updateRight + "\n" + insert);
1151                }
1152
1153                _stmt.executeUpdate(updateLeft);
1154                _stmt.executeUpdate(updateRight);
1155                _stmt.executeUpdate(insert);
1156
1157                // find out the auto assigned liid
1158                String queryNewLiid = "CALL IDENTITY();";
1159                ResultSet rs = null;
1160                try {
1161                        rs = _stmt.executeQuery(queryNewLiid);
1162                        if (rs != null && rs.next()) {
1163                            int newLiid = rs.getInt(1);
1164                            if (!rs.wasNull()) {
1165                                li.setLiid(newLiid);
1166                                if (isDebugging)
1167                                    log.debug("IDENTITY: " + li.getLiid());
1168                            } else {
1169                                log.error("Failed to retrieve auto assigned identity");
1170                            }
1171                        }
1172                } finally {
1173                        if(rs != null) {
1174                                rs.close();
1175                        }
1176                }
1177
1178                // add the lsid to the LiidLsid table
1179                insertLiidLsid(li.getLiid(), li.getLsid());
1180                insertAttributes(li);
1181                
1182                LibraryManager.getInstance().getIndex().addToSearchIndex(li);
1183
1184                _stmt.getConnection().commit();
1185                }
1186        }
1187        
1188        private void insertAttributes(LibItem li) throws SQLException {
1189        
1190            final int liid = li.getLiid();
1191            
1192            _deleteLIIDFromLibraryAttributesPrepStmt.setInt(1, liid);
1193            
1194        if (isDebugging) log.debug(_deleteLIIDFromLibraryAttributesPrepStmt);
1195        _deleteLIIDFromLibraryAttributesPrepStmt.executeUpdate();
1196        _stmt.getConnection().commit();
1197
1198        for(Entry<String, String> entry : li.getAttributes().entrySet()) {
1199            final String attName = entry.getKey();
1200            final String attValue = entry.getValue();
1201            _insertIntoLibraryAttributesPrepStmt.setInt(1,liid);
1202            _insertIntoLibraryAttributesPrepStmt.setString(2, attName);
1203            _insertIntoLibraryAttributesPrepStmt.setString(3, attValue);
1204            if (isDebugging) log.debug(_insertIntoLibraryAttributesPrepStmt);
1205            _insertIntoLibraryAttributesPrepStmt.executeUpdate();
1206        }
1207        _stmt.getConnection().commit();
1208    }
1209        
1210        /**
1211     * Figure out what the LEFT integer should be for a new row that is to be
1212     * inserted in alphabetical order for the current getParent() value. Or for
1213     * Level 1 if getParent() == null
1214     * 
1215     * @param stmt
1216     * @return
1217     * @throws SQLException
1218     */
1219    private int getAlphabeticInsertIndex(LibItem li) throws SQLException {
1220        int insertIndex = -1;
1221
1222        int parentLevel = 0;
1223        String query = "select LIID,RGT,LEVEL,LSID,NAME from "
1224                + LibIndex.LIBRARY_INDEX_TABLE_NAME;
1225        if (li.getParent() == null) {
1226            query += " where LEVEL = 1";
1227        } else {
1228            query += " where PARENT = " + li.getParent();
1229        }
1230        query += " order by NAME ";
1231        if (isDebugging) {
1232            log.debug(query);
1233        }
1234        ResultSet rs = null;
1235        int cnt = 0;
1236        try {
1237                rs = _stmt.executeQuery(query);
1238                if (rs == null)
1239                    log.error("Query Failed: " + query);
1240                int prevRight = -1;
1241                while (rs.next()) {
1242                    int liid = rs.getInt(1); // LIID
1243                    int r = rs.getInt(2); // RGT
1244                    parentLevel = rs.getInt(3) - 1; // LEVEL
1245                    String lsid = rs.getString(4); // LSID
1246                    String n = rs.getString(5); // NAME
1247        
1248                    int comparison = n.compareToIgnoreCase(li.getName());
1249                    if (comparison == 0) {
1250                        log.debug(lsid);
1251                        log.debug(li.getLsid());
1252                        if (lsid.equals(li.getLsid())) {
1253                            log.debug("LSID matches");
1254                            throw new SQLException(li.getName()
1255                                    + " already exists as child of parent "
1256                                    + li.getParent());
1257                        } else {
1258                            log.debug("LSID does not match");
1259                            try {
1260                                // Add the lsid to the Library_lsids table
1261                                insertLiidLsid(liid, li.getLsid());
1262                                return -1;
1263                            } catch (SQLException sqle) {
1264                                throw new SQLException("bummer");
1265                            }
1266                        }
1267                    }
1268                    if (comparison > 0) {
1269                        // we want to insert before this item
1270                        if (cnt == 0) {
1271                            // this is the first child
1272                            // use the parents' left index
1273                            break;
1274                        } else {
1275                            // use the right index of the previous row plus 1
1276                            insertIndex = prevRight + 1;
1277                        }
1278                    }
1279                    if (comparison < 0) {
1280                        // Go on to the next child, unless there is no next child
1281                        // for that case we'll set this to be the current right plus 1
1282                        // every pass through the loop
1283                        insertIndex = r + 1;
1284                    }
1285                    prevRight = r;
1286                    cnt++;
1287                }
1288        } finally {
1289                if(rs != null) {
1290                rs.close();
1291                }
1292        }
1293        
1294        if (cnt == 0) {
1295            if (isDebugging)
1296                log.debug("No children found for parent " + li.getParent());
1297            // Or we're inserting at the top of the list
1298            if (li.getParent() == null) {
1299                // we're inserting at the very beginning
1300                insertIndex = 0;
1301            } else {
1302                String parentQuery = "SELECT LFT,LEVEL from "
1303                        + LibIndex.LIBRARY_INDEX_TABLE_NAME + " WHERE LIID = "
1304                        + li.getParent();
1305                if (isDebugging) {
1306                    log.debug(parentQuery);
1307                }
1308                ResultSet parentResult = null;
1309                try {
1310                        parentResult = _stmt.executeQuery(parentQuery);
1311                        if (parentResult == null)
1312                            log.error("Query Failed: " + parentQuery);
1313                        if (parentResult.next()) {
1314                            insertIndex = parentResult.getInt(1) + 1;
1315                            parentLevel = parentResult.getInt(2);
1316                        }
1317                } finally {
1318                        if(parentResult != null) {
1319                                parentResult.close();
1320                        }
1321                }
1322            }
1323        }
1324        if (isDebugging)
1325            log.debug("return: " + insertIndex);
1326        li.setLevel(parentLevel + 1);
1327        return insertIndex;
1328    }
1329
1330
1331        /**
1332         * Insert a LibItem row but do not update the lft and rgt ordering columns.
1333         * This method is useful only for doing bulk inserts of many rows at a time.
1334         * Then after a bunch of inserts the ordering must be updated by calling the
1335         * refreshPreorderValues() method. For only inserting one row and having the
1336         * ordering updated use the LibItem.insert() method.
1337         * 
1338         * You can setOrderedInsert(true) to force insert by insert ordering in this
1339         * method.
1340         * 
1341         * @param li
1342         */
1343        private void insertNoOrder(LibItem li) throws SQLException {
1344                if (isDebugging)
1345                        log.debug("insertNoOrder(" + li.getName() + ")");
1346
1347                if (isOrderedInsert()) {
1348                        if (isDebugging)
1349                                log.debug("Do Ordered Insert");
1350                        insert(li);
1351                        return;
1352                }
1353
1354                // else we insert without updating the preorder values
1355                if (isDebugging)
1356                        log.debug("Do Unordered Insert");
1357
1358                String insert = "INSERT INTO " + LibIndex.LIBRARY_INDEX_TABLE_NAME
1359                                + " (PARENT,LFT,RGT,LEVEL,LSID,TYPE,NAME) values (";
1360
1361                if (li.getParent() == null) {
1362                        insert += "NULL"; // parent
1363                } else {
1364                        insert += "" + li.getParent(); // parent
1365                }
1366
1367                insert += "," + li.getLeft(); // left
1368                insert += "," + li.getRight(); // right
1369                insert += "," + li.getLevel(); // level
1370
1371                if (li.getLsid() != null) {
1372                        insert += ",'" + li.getLsid().toString() + "'"; // lsid
1373                } else {
1374                        insert += ",NULL";
1375                }
1376                insert += "," + li.getType(); // type
1377                insert += ",'" + li.getName() + "'"; // name
1378                insert += ")";
1379
1380                if (isDebugging)
1381                        log.debug(insert);
1382
1383                boolean itemInserted = false;
1384                try {
1385                        int rows = _stmt.executeUpdate(insert);
1386                        if (rows == 1) {
1387                                itemInserted = true;
1388                        } else {
1389                                log.error("item was not inserted");
1390                        }
1391                } catch (SQLException sqle) {
1392                        if (isDebugging) {
1393                                log.debug("ERROR CODE: " + sqle.getErrorCode());
1394                                log.debug("ERROR MESSAGE: " + sqle.getMessage());
1395                        }
1396                        if (sqle.getErrorCode() == -104) {
1397                                // This Name already exists under this parent
1398                                if (li.getType() == LibIndex.TYPE_COMPONENT
1399                                                || li.getType() == LibIndex.TYPE_NAMED_OBJ) {
1400
1401                                        // Add it to the Library_LSIDS table
1402                                        KeplerLSID lsid = li.getLsid();
1403                                        if (lsid != null) {
1404                                                
1405                                                int liid = checkIfDuplicate(li);
1406                                                if (liid < 0) {
1407                                                        // not a duplicate
1408                                                } else {
1409                                                        insertLiidLsid(liid, li.getLsid());
1410                                                }
1411
1412                                                return;
1413
1414                                        } else {
1415                                                if (isDebugging)
1416                                                        log.debug("lsid is null");
1417                                                throw sqle;
1418                                        }
1419                                } else {
1420                                        if (isDebugging)
1421                                                log.debug("type is " + li.getType());
1422                                        throw sqle;
1423                                }
1424                        } else {
1425                                if (isDebugging)
1426                                        log.debug("different error code");
1427                                throw sqle;
1428                        }
1429                }
1430
1431                if (itemInserted) {
1432                        // find out the auto assigned liid
1433                        String queryNewLiid = "CALL IDENTITY();";
1434                        ResultSet rs = null;
1435                        try {
1436                                rs = _stmt.executeQuery(queryNewLiid);
1437                                if (rs == null)
1438                                        throw new SQLException("Query Failed: " + queryNewLiid);
1439                                if (rs.next()) {
1440                                        int newLiid = rs.getInt(1);
1441                                        if (!rs.wasNull()) {
1442                                                li.setLiid(newLiid);
1443                                                if (isDebugging)
1444                                                        log.debug("IDENTITY: " + li.getLiid());
1445                                        } else {
1446                                                log.error("Failed to retrieve auto assigned identity");
1447                                        }
1448                                }
1449                        } finally {
1450                                if(rs != null) {
1451                                        rs.close();
1452                                }
1453                        }
1454
1455                        insertLiidLsid(li.getLiid(), li.getLsid());
1456                        insertAttributes(li);
1457
1458                        addToSearchIndex(li);
1459                }
1460
1461                _stmt.getConnection().commit();
1462        }
1463
1464           /**
1465     * Add a new LSID to the LIBRARY_LSIDS table for the specified LIID.
1466     * 
1467     * @param liid
1468     * @param lsid
1469     * @throws SQLException
1470     */
1471    private void insertLiidLsid(int liid, KeplerLSID lsid)
1472            throws SQLException {
1473        if (lsid == null) {
1474            if (isDebugging)
1475                log.debug("lsid is null, skip insert into LIBRARY_LSIDS");
1476            return;
1477        }
1478
1479        _getNumLIIDForLIIDAndLSIDPrepStmt.setInt(1, liid);
1480        _getNumLIIDForLIIDAndLSIDPrepStmt.setString(2, lsid.toString());        
1481        if (isDebugging) log.debug(_getNumLIIDForLIIDAndLSIDPrepStmt);
1482        ResultSet rs = null;
1483        try {
1484                rs = _getNumLIIDForLIIDAndLSIDPrepStmt.executeQuery();
1485                if (rs.next()) {
1486                    int cnt = rs.getInt(1);
1487                    if (cnt <= 0) {
1488                        _insertIntoLibraryLSIDsPrepStmt.setInt(1, liid);
1489                        _insertIntoLibraryLSIDsPrepStmt.setString(2, lsid.toString());
1490                        if (isDebugging) log.debug(_insertIntoLibraryLSIDsPrepStmt);
1491                        _insertIntoLibraryLSIDsPrepStmt.executeUpdate();
1492                    } else {
1493                        // Already in there. ignore
1494                    }
1495                }
1496        } finally {
1497                if(rs != null) {
1498                        rs.close();
1499                }
1500        }
1501        _stmt.getConnection().commit();
1502    }
1503
1504        /**
1505         * Add the given LibItem to the search index. If the OrderedInsert flag is
1506         * set to true then all of the parents of this LibItem will be add to the
1507         * search index for this item.
1508         * 
1509         * @param li
1510         * @throws SQLException
1511         */
1512        public void addToSearchIndex(LibItem li) throws SQLException {
1513
1514                // Add a row for this LibItem
1515                Integer searchType = _searchTypeMap.get(li.getType());
1516                if (searchType != null) {
1517                        _searcher.insertRow(searchType.intValue(), li.getLiid(), li
1518                                        .getName());
1519                }
1520
1521                // We can only get the path to this LibItem if it has been inserted AND
1522                // ordered
1523                if (isOrderedInsert()) {
1524                        addAllParentsToSearchIndex(li);
1525                }
1526        }
1527
1528        /**
1529         * Add all of the parent LibItems to the search index for the given LibItem.
1530         * 
1531         * @param li
1532         * @throws SQLException
1533         */
1534        private void addAllParentsToSearchIndex(LibItem li) throws SQLException {
1535                // Add a row for every LibItem in the path to this LibItem
1536                Vector<LibItem> parents = getPath(li);
1537                for (LibItem parent : parents) {
1538                        Integer searchType = _searchTypeMap.get(parent.getType());
1539                        if (searchType != null) {
1540                                _searcher.insertRow(searchType.intValue(), li.getLiid(), parent
1541                                                .getName());
1542                        }
1543                }
1544
1545        }
1546
1547        /**
1548         * Here we map the LibItem types to the different Search types.
1549         */
1550        private void initSearchMap() {
1551                _searchTypeMap = new Hashtable<Integer, Integer>();
1552                _searchTypeMap.put(TYPE_COMPONENT, LibSearch.TYPE_NAME);
1553                _searchTypeMap.put(TYPE_CONCEPT, LibSearch.TYPE_ONTCLASSNAME);
1554                _searchTypeMap.put(TYPE_NAMED_OBJ, LibSearch.TYPE_NAME);
1555                _searchTypeMap.put(TYPE_ONTOLOGY, LibSearch.TYPE_ONTOLOGY);
1556                _searchTypeMap.put(TYPE_FOLDER, LibSearch.TYPE_FOLDERNAME);
1557                _searchTypeMap.put(TYPE_KARFOLDER, LibSearch.TYPE_FOLDERNAME);
1558                _searchTypeMap.put(TYPE_KAR, LibSearch.TYPE_KARNAME);
1559                _searchTypeMap.put(TYPE_LOCALREPO, LibSearch.TYPE_LOCALREPO);
1560        }
1561
1562        public LibSearch getSearcher() {
1563                return _searcher;
1564        }
1565
1566        /**
1567         * Sum the LFT and RGT columns and verify they are continuous integers from
1568         * 1 to (countItems()*2). This function should always return true after
1569         * finishing sql transactions for insert, update, or delete.
1570         * 
1571         * @return true if the LFT and RGT columns of the table add up
1572         * @throws SQLException
1573         */
1574        public boolean verifyPreorderValues() throws SQLException {
1575                if (isDebugging)
1576                        log.debug(_getLftRgtSumPrepStmt);
1577                ResultSet rs = null;
1578                try {
1579                        rs = _getLftRgtSumPrepStmt.executeQuery();
1580                        if (rs == null)
1581                                throw new SQLException("Query Failed: " + _getLftRgtSumPrepStmt);
1582                        if (rs.next()) {
1583                                int sumLR = rs.getInt(1);
1584                                int cnt = countItems() * 2;
1585                                int sumCnt = (cnt * (cnt + 1)) / 2;
1586                                if (isDebugging)
1587                                        log.debug(sumLR + " " + sumCnt);
1588                                if (sumLR == sumCnt) {
1589                                        return true;
1590                                }
1591                        }
1592                } finally {
1593                        if(rs != null) {
1594                                rs.close();
1595                        }
1596                }
1597                return false;
1598        }
1599
1600        /**
1601         * Refresh the lft and rgt PTT values based on the parent information and
1602         * ordering by name.
1603         */
1604        private void refreshPreorderValues() {
1605                try {
1606                        int left = 1;
1607                        ResultSet rs = null;
1608                        try {
1609                                        rs = _getLIIDRootsPrepStmt.executeQuery();
1610                                if (rs == null)
1611                                        throw new SQLException("Query Failed: " + _getLIIDRootsPrepStmt);
1612                                while (rs.next()) {
1613                                        int liid = rs.getInt(1);
1614                                        left = refreshPreorderValues(liid, left);
1615                                }
1616                        } finally {
1617                                if(rs != null) {
1618                                        rs.close();
1619                                }
1620                        }
1621                } catch (SQLException sqle) {
1622                        sqle.printStackTrace();
1623                }
1624        }
1625
1626        /**
1627         * Recursive function for refreshing the lft and rgt PTT values of a given
1628         * parent LIID starting at the given LEFT value.
1629         * 
1630         * @param parent
1631         * @param left
1632         * @return
1633         * @throws SQLException
1634         */
1635        private int refreshPreorderValues(int parent, int left) throws SQLException {
1636                int right = left + 1;
1637
1638                // get all children of this node
1639                _getLIIDForParentPrepStmt.setInt(1, parent);
1640                ResultSet rs = null;
1641                try {
1642                        rs = _getLIIDForParentPrepStmt.executeQuery();
1643                        while (rs.next()) {
1644                                int p = rs.getInt(1);
1645                                right = refreshPreorderValues(p, right);
1646                        }
1647                } finally {
1648                        if(rs != null) {
1649                                rs.close();
1650                        }
1651                }
1652
1653                // UPDATE LIBRARY_INDEX SET LFT=?, RGT=? WHERE LIID=?
1654                _updateOrderPrepStmt.clearParameters();
1655                _updateOrderPrepStmt.setInt(1, left);
1656                _updateOrderPrepStmt.setInt(2, right);
1657                _updateOrderPrepStmt.setInt(3, parent);
1658                _updateOrderPrepStmt.executeUpdate();
1659
1660                // return the right value of this node + 1
1661                return right + 1;
1662        }
1663
1664        /**
1665         * Returns a string that uniquely identifies a KAR entry using the full path
1666         * to the KAR file and the name of the entry as it appears in the KAR
1667         * manifest.
1668         * 
1669         * @param karFile
1670         * @param entry
1671         * @return
1672         */
1673        private String getKarEntryPath(File karFile, String entry) {
1674                String fullPath = karFile.toString();
1675                if (!fullPath.endsWith(File.separator)
1676                                && !entry.startsWith(File.separator)) {
1677                        fullPath += File.separator + entry;
1678                } else {
1679                        fullPath += entry;
1680                }
1681                return fullPath;
1682        }
1683
1684        /**
1685         * This will find any folder or file kar entry.
1686         * 
1687         * @param karFile
1688         * @param path
1689         * @return
1690         */
1691        private LibItem findKarEntry(File karFile, String path) throws SQLException {
1692                LibItem li = null;
1693
1694                _getLIIDForKarEntryPrepStmt.setString(1, getKarEntryPath(karFile, path));
1695                if (isDebugging)
1696                        log.debug(_getLIIDForKarEntryPrepStmt);
1697                ResultSet rs = null;
1698                try {
1699                        rs = _getLIIDForKarEntryPrepStmt.executeQuery();
1700                        if (rs == null)
1701                                throw new SQLException("Query Failed: " + _getLIIDForKarEntryPrepStmt);
1702                        if (rs.next()) {
1703                                int liid = rs.getInt(1);
1704                                if (!rs.wasNull()) {
1705                        li = _libraryManager.getPopulatedLibItem(liid);
1706                                }
1707                                if (rs.next()) {
1708                                        log.error("LIBRARY_INDEX table is corrupt: "
1709                                                        + "Multiples of KAREntry " + path + " found");
1710                                }
1711                        }
1712                } finally {
1713                        if(rs != null) {
1714                                rs.close();
1715                        }
1716                }
1717                return li;
1718        }
1719
1720        /**
1721         * Given the KARCached object and the
1722         * 
1723         * @param kc
1724         * @param path
1725         * @return
1726         */
1727        private LibItem assureKarFolder(KARCached kc, String path) {
1728                File karFile = kc.getFile();
1729                LibItem li = null;
1730                try {
1731                        li = findKarEntry(karFile, path);
1732                        if (li == null) {
1733                                LibItem parent = null;
1734                                String[] pathRep = path.split("/");
1735                                if (pathRep.length > 1) {
1736                                        String parentPath = new String();
1737                                        for (int i = 0; i < pathRep.length - 1; i++) {
1738                                                parentPath += pathRep[i] + "/";
1739                                        }
1740                                        parent = assureKarFolder(kc, parentPath);
1741                                        if (parent == null) {
1742                                                throw new Exception("Unable to find or create "
1743                                                                + getKarEntryPath(karFile, parentPath));
1744                                        }
1745                                } else {
1746                                        parent = assureKar(kc);
1747                                        if (parent == null) {
1748                                                throw new Exception("Unable to find or create "
1749                                                                + karFile);
1750                                        }
1751                                }
1752                                li = new LibItem();
1753                                li.setName(pathRep[pathRep.length - 1]);
1754                                li.setType(LibIndex.TYPE_KARFOLDER);
1755                                li.setParent(parent.getLiid());
1756                                li.setLevel(parent.getLevel() + 1);
1757                                li.setLsid(null); // folders don't have LSIDs!
1758                                li.addAttribute(ATT_KARENTRYPATH,
1759                                                getKarEntryPath(karFile, path));
1760                                insertNoOrder(li);
1761                        }
1762                } catch (Exception e) {
1763                        log.error("Unable to assureKarFolder(" + karFile.toString() + ","
1764                                        + path + ")");
1765                        e.printStackTrace();
1766                }
1767                return li;
1768        }
1769
1770        /**
1771         * 
1772         * @param kce
1773         * @return
1774         * @throws SQLException
1775         */
1776        private LibItem findKarError(KARCacheError kce) throws SQLException {
1777                LibItem li = null;
1778                
1779                File karFile = kce.getFile();
1780                _getLIIDForKarErrorPrepStmt.setString(1, karFile.toString());
1781                if (isDebugging)
1782                        log.debug(_getLIIDForKarErrorPrepStmt);
1783                ResultSet rs = null;
1784                try {
1785                        rs = _getLIIDForKarErrorPrepStmt.executeQuery();
1786                        if (rs == null)
1787                                throw new SQLException("Query Failed: " + _getLIIDForKarErrorPrepStmt);
1788                        if (rs.next()) {
1789                                int liid = rs.getInt(1);
1790                                if (rs.wasNull()) {
1791                        li = _libraryManager.getPopulatedLibItem(liid);
1792                                }
1793                                if (rs.next()) {
1794                                        log.error("LIBRARY_INDEX table is corrupt: "
1795                                                        + "multiples of KAR Error " + karFile + " found");
1796                                }
1797                                return li;
1798                        }
1799                } finally {
1800                        if(rs != null) {
1801                                rs.close();
1802                        }
1803                }
1804                
1805                return li;
1806        }
1807
1808        /**
1809         * 
1810         * @param kce
1811         * @return
1812         */
1813        private LibItem assureKarError(KARCacheError kce) {
1814                File karFile = kce.getFile();
1815                LibItem li = null;
1816                try {
1817                        li = findKarError(kce);
1818                        
1819                        if (li == null) {
1820                                LibItem parent = null;
1821                                File parentFile = karFile.getParentFile();
1822                                LocalRepositoryManager lrm = LocalRepositoryManager.getInstance();
1823                                LocalRepository repo = lrm.getRepositoryForFile(parentFile);
1824                                if (repo != null) {
1825                                        parent = assureLocalRepository(repo);
1826                                } else {
1827                                        parent = assureFolder(parentFile);
1828                                }
1829                                if (parent == null)
1830                                        throw new Exception();
1831        
1832                                li = new LibItem();
1833                                li.setName(kce.getName());
1834                                li.setType(LibIndex.TYPE_KAR_ERROR);
1835                                li.setParent(parent.getLiid());
1836                                li.setLevel(parent.getLevel() + 1);
1837                                li.setLsid(kce.getLsid());
1838                                li.addAttribute(ATT_KARFILE, karFile.toString());
1839                                insertNoOrder(li);
1840                        }
1841
1842                } catch (Exception e) {
1843                        log.error("Unable to assureKarError(" + kce.getFile().toString()
1844                                        + ")");
1845                        e.printStackTrace();
1846                }
1847                return li;
1848        }
1849
1850        /**
1851         * @param karFile
1852         * @return LibItem that represents the given KAR File or null if the given
1853         *         KAR File does not correspond to an entry in the Library Index.
1854         * @throws SQLException
1855         */
1856        public LibItem findKar(File karFile) throws SQLException {
1857                LibItem li = null;
1858            _getLIIDForKarPrepStmt.setString(1, karFile.toString());
1859                if (isDebugging)
1860                        log.debug(_getLIIDForKarPrepStmt);
1861                ResultSet rs = null;
1862                try {
1863                        rs = _getLIIDForKarPrepStmt.executeQuery();
1864                        if (rs == null)
1865                                throw new SQLException("Query Failed: " + _getLIIDForKarPrepStmt);
1866                        if (rs.next()) {
1867                                int liid = rs.getInt(1);
1868                                if (!rs.wasNull()) {
1869                        li = _libraryManager.getPopulatedLibItem(liid);
1870                                }
1871                                if (rs.next()) {
1872                                        log.error("LIBRARY_INDEX table is corrupt: "
1873                                                        + " multiples of KAR " + karFile + " found");
1874                                }
1875                        }
1876                } finally {
1877                        if(rs != null) {
1878                                rs.close();
1879                        }
1880                }
1881                return li;
1882        }
1883
1884        /**
1885         * 
1886         * @param kc
1887         * @return
1888         */
1889        private LibItem assureKar(KARCached kc) {
1890                File karFile = kc.getFile();
1891                LibItem li = null;              
1892                try {
1893                        li = findKar(karFile);
1894                        if (li != null) {
1895                                return li;
1896                        }
1897
1898                        LibItem parent = null;
1899                        File parentFile = karFile.getParentFile();
1900                        LocalRepositoryManager lrm = LocalRepositoryManager.getInstance();
1901                        LocalRepository repo = lrm.getRepositoryForFile(parentFile);
1902                        if (repo != null) {
1903                                parent = assureLocalRepository(repo);
1904                        } else {
1905                                parent = assureFolder(parentFile);
1906                        }
1907                        if (parent == null)
1908                                throw new Exception();
1909
1910                        li = new LibItem();
1911                        li.setName(kc.getName());
1912                        li.setType(LibIndex.TYPE_KAR);
1913                        li.setParent(parent.getLiid());
1914                        li.setLevel(parent.getLevel() + 1);
1915                        li.setLsid(kc.getLsid());
1916                        li.addAttribute(ATT_KARFILE, karFile.toString());
1917                        insertNoOrder(li);
1918                        
1919                        // see if this kar belongs to a demo
1920                        ModuleTree tree = ModuleTree.instance();
1921                        DotKeplerManager dkm = DotKeplerManager.getInstance();
1922                        for(Module module : tree.getModuleList()) {
1923                            String moduleName = module.getName();
1924                            String moduleWorkflowsDir = dkm.getPersistentModuleWorkflowsDir(moduleName).getAbsolutePath();
1925                            String moduleDemosDir = moduleWorkflowsDir + File.separator + "demos";
1926                            
1927                            if(karFile.getAbsolutePath().startsWith(moduleDemosDir)) {
1928                                                                
1929                            parent = assureDemoFolder(parentFile);                          
1930                            li = new LibItem();
1931                            li.setName(kc.getName());
1932                            li.setType(TYPE_KAR);
1933                            li.setParent(parent.getLiid());
1934                            li.setLevel(parent.getLevel() + 1);
1935                            li.setLsid(kc.getLsid());
1936                            li.addAttribute(ATT_KARFILE, karFile.toString());
1937                            insertNoOrder(li);
1938                            }
1939                        }
1940
1941                } catch (Exception e) {
1942                        log.error("Unable to assureKar(" + kc.getFile().toString() + ")");
1943                        e.printStackTrace();
1944                }
1945                return li;
1946        }
1947        
1948        /**
1949     * @param file
1950     * @return LibItem that represents the given XML File or null if the given
1951     *         XML File does not correspond to an entry in the Library Index.
1952     * @throws SQLException
1953     */
1954    public LibItem findXML(File file) throws SQLException {
1955        LibItem li = null;
1956        _getLIIDForXMLPrepStmt.setString(1, file.toString());
1957        if (isDebugging)
1958            log.debug(_getLIIDForXMLPrepStmt);
1959        ResultSet rs = null;
1960        try {
1961                rs = _getLIIDForXMLPrepStmt.executeQuery();
1962                if (rs == null)
1963                    throw new SQLException("Query Failed: " + _getLIIDForXMLPrepStmt);
1964                if (rs.next()) {
1965                    int liid = rs.getInt(1);
1966                    if (!rs.wasNull()) {
1967                        li = _libraryManager.getPopulatedLibItem(liid);
1968                    }
1969                    if (rs.next()) {
1970                        log.error("LIBRARY_INDEX table is corrupt: "
1971                                + " multiples of XML " + file + " found");
1972                    }
1973                }
1974        } finally {
1975                if(rs != null) {
1976                        rs.close();
1977                }
1978        }
1979        return li;
1980    }
1981
1982        /**
1983     * 
1984     * @param kc
1985     * @return
1986     */
1987    public LibItem assureXML(File file) {
1988        LibItem li = null;      
1989        try {
1990            li = findXML(file);
1991            if (li != null) {
1992                return li;
1993            }
1994
1995            LibItem parent = null;
1996            File parentFile = file.getParentFile();
1997            LocalRepositoryManager lrm = LocalRepositoryManager.getInstance();
1998            LocalRepository repo = lrm.getRepositoryForFile(parentFile);
1999            if (repo != null) {
2000                parent = assureLocalRepository(repo);
2001            } else {
2002                parent = assureFolder(parentFile);
2003            }
2004            if (parent == null)
2005                throw new Exception();
2006            
2007            KeplerActorMetadata metadata = null;
2008            KeplerLSID lsid = null;
2009            try {
2010                 metadata = KeplerMetadataExtractor.extractActorMetadata(file, false);
2011            } catch(Exception e) {
2012                System.err.println("Error parsing " + file + ": " + e.getMessage());
2013                return null;                
2014            }
2015            if(metadata == null) {
2016                return null;
2017            }
2018            
2019            String className = metadata.getClassName();
2020            
2021            li = new LibItem();
2022            li.setName(file.getName());
2023            li.setType(TYPE_COMPONENT);
2024            li.setParent(parent.getLiid());
2025            li.setLevel(parent.getLevel() + 1);
2026            li.setLsid(lsid);
2027            li.addAttribute(ATT_XMLFILE, file.toString());
2028            if(className != null) {
2029                li.addAttribute(ATT_CLASSNAME, className);
2030            }
2031            insertNoOrder(li);
2032            
2033            // see if this kar belongs to a demo
2034            ModuleTree tree = ModuleTree.instance();
2035            DotKeplerManager dkm = DotKeplerManager.getInstance();
2036            for(Module module : tree.getModuleList()) {
2037                String moduleName = module.getName();
2038                String moduleWorkflowsDir = dkm.getPersistentModuleWorkflowsDir(moduleName).getAbsolutePath();
2039                String moduleDemosDir = moduleWorkflowsDir + File.separator + "demos";
2040                
2041                if(file.getAbsolutePath().startsWith(moduleDemosDir)) {
2042                                        
2043                    parent = assureDemoFolder(parentFile);                  
2044                    li = new LibItem();
2045                    li.setName(file.getName());
2046                    li.setType(TYPE_COMPONENT);
2047                    li.setParent(parent.getLiid());
2048                    li.setLevel(parent.getLevel() + 1);
2049                    li.setLsid(lsid);
2050                    li.addAttribute(ATT_XMLFILE, file.toString());
2051                    if(className != null) {
2052                        li.addAttribute(ATT_CLASSNAME, className);
2053                    }
2054                    insertNoOrder(li);
2055                }
2056            }
2057
2058        } catch (Exception e) {
2059            log.error("Unable to assureXML(" + file.toString() + ")");
2060            e.printStackTrace();
2061        }
2062        return li;
2063    }
2064    
2065        private LibItem findFolder(String folder) throws SQLException {
2066                LibItem li = null;
2067                _getLIIDForFolderPrepStmt.setString(1, folder);
2068                if (isDebugging)
2069                        log.debug(_getLIIDForFolderPrepStmt);
2070                ResultSet rs = null;
2071                try {
2072                        rs = _getLIIDForFolderPrepStmt.executeQuery();
2073                        if (rs == null)
2074                                throw new SQLException("Query Failed: " + _getLIIDForFolderPrepStmt);
2075                        if (rs.next()) {
2076                                int liid = rs.getInt(1);
2077                                if (!rs.wasNull()) {
2078                                        li = _libraryManager.getPopulatedLibItem(liid);
2079                                }
2080                                if (rs.next()) {
2081                                        log.error("LIBRARY_INDEX table is corrupt: "
2082                                                        + "multiples of folder " + folder + " found");
2083                                }
2084                                return li;
2085                        }
2086                } finally {
2087                        if(rs != null) {
2088                                rs.close();
2089                        }
2090                }
2091                return li;
2092        }
2093        
2094        private LibItem assureDemoFolder(File folder) {
2095            LibItem li = null;
2096            try {
2097                
2098                li = findFolder(_demosFolderItem + ":" + folder.toString());
2099                if(li == null) {
2100                    
2101                if(_demosFolderItem == null) {
2102                    _demosFolderItem = new LibItem();
2103                    _demosFolderItem.setName("Demos");
2104                    _demosFolderItem.setType(TYPE_FOLDER);
2105                    _demosFolderItem.setParent(null);
2106                    _demosFolderItem.setLevel(ROOT_LEVEL);
2107                    _demosFolderItem.setLsid(null);
2108                    insertNoOrder(_demosFolderItem);                                    
2109                }
2110
2111                LibItem parent = null;
2112                
2113                String path = folder.getAbsolutePath();
2114                Matcher matcher = _demosFolderPattern.matcher(path);
2115                
2116                if(path.equals(DotKeplerManager.getInstance()
2117                            .getPersistentModuleWorkflowsDir().getPath())) {
2118                    return _demosFolderItem;
2119                } else if (matcher.find() && path.endsWith("demos")) {
2120                    if(matcher.group(1).startsWith("outreach")) {
2121                        return _demosFolderItem;
2122                    } else {
2123                        return assureDemoFolder(folder.getParentFile());
2124                    }
2125                } else {
2126                    parent = assureDemoFolder(folder.getParentFile());
2127                }
2128                
2129                // remove the version from the name, if present.
2130                final String unversionedName = Version.stem(folder.getName());
2131                final String name = LocalRepositoryManager.getLocalRepositoryName(unversionedName);
2132                
2133                li = new LibItem();
2134                li.setName(name);
2135                li.setType(LibIndex.TYPE_FOLDER);
2136                li.setParent(parent.getLiid());
2137                li.setLevel(parent.getLevel() + 1);
2138                li.setLsid(null); // folders don't have LSIDs!
2139                li.addAttribute(ATT_FOLDER, _demosFolderItem + ":" + folder.toString());
2140                insertNoOrder(li);
2141                }               
2142            } catch (Exception e) {
2143            log.error("Unable to assureFolder(" + folder.toString() + ") rooted at " + _demosFolderItem.getName());
2144            e.printStackTrace();
2145            }
2146            return li;
2147        }
2148        
2149        /**
2150         * 
2151         * @param folder
2152         * @return
2153         */
2154        private LibItem assureFolder(File folder) {
2155                LibItem li = null;
2156                try {
2157                        li = findFolder(folder.toString());
2158
2159                        if (li == null) {
2160                                // determine the parent LibItem
2161                                LibItem parent = null;
2162
2163                                LocalRepositoryManager lrm = LocalRepositoryManager
2164                                                .getInstance();
2165                                LocalRepository repo = lrm.getRepositoryForFile(folder);
2166                                if (repo != null) {
2167                                        if (isDebugging)
2168                                                log.debug(folder + " is a repository");
2169                                        parent = assureLocalRepository(repo);
2170                                } else {
2171                                    repo = lrm.getContainingLocalRepository(folder);
2172                                        if (repo == null) {
2173                                                throw new Exception(folder
2174                                                                + " is not in a local repository");
2175                                        }
2176                                        File parentFile = folder.getParentFile();
2177                                        if (isDebugging)
2178                                                log.debug(repo + " - " + parentFile);
2179                                        if (repo.isFileRepoDirectory(parentFile)) {
2180                                                parent = assureLocalRepository(repo);
2181                                        } else {
2182                                                parent = assureFolder(parentFile);
2183                                        }
2184
2185                                        // insert a new LibItem for this folder
2186                                        li = new LibItem();
2187                                        li.setName(folder.getName());
2188                                        li.setType(LibIndex.TYPE_FOLDER);
2189                                        li.setParent(parent.getLiid());
2190                                        li.setLevel(parent.getLevel() + 1);
2191                                        li.setLsid(null); // folders don't have LSIDs!
2192                                        li.addAttribute(ATT_FOLDER, folder.toString());
2193                                        insertNoOrder(li);
2194                                }
2195                        }
2196                } catch (Exception e) {
2197                        log.error("Unable to assureFolder(" + folder.toString() + ")");
2198                        e.printStackTrace();
2199                }
2200                return li;
2201        }
2202
2203        private LibItem findLocalRepository(LocalRepository repo) throws SQLException {
2204                LibItem li = null;
2205                _getLIIDForRepositoryPrepStmt.setString(1, repo.toString());
2206                if (isDebugging)
2207                        log.debug(_getLIIDForRepositoryPrepStmt);
2208                ResultSet rs = null;
2209                try {
2210                        rs = _getLIIDForRepositoryPrepStmt.executeQuery();
2211                        if (rs == null)
2212                                throw new SQLException("Query Failed: " + _getLIIDForRepositoryPrepStmt);
2213                        if (rs.next()) {
2214                                int liid = rs.getInt(1);
2215                                if (!rs.wasNull()) {
2216                        li = _libraryManager.getPopulatedLibItem(liid);
2217                                }
2218                                if (rs.next()) {
2219                                        log.error("LIBRARY_INDEX table is corrupt: "
2220                                                        + " multiples of local repository " + repo + " found");
2221                                }
2222                                return li;
2223                        }
2224                } finally {
2225                        if(rs != null) {
2226                                rs.close();
2227                        }
2228                }
2229                return li;
2230        }
2231
2232        /**
2233         * 
2234         * @param repo
2235         * @return
2236         */
2237        private LibItem assureLocalRepository(LocalRepository repo) {
2238                LibItem li = null;
2239
2240                try {
2241                        li = findLocalRepository(repo);
2242
2243                        if (li == null) {
2244                                // insert a new LibItem row for this Local Repository
2245                                LocalRepositoryManager lrm = LocalRepositoryManager
2246                                                .getInstance();
2247                                String repoName = lrm.getLocalRepositories().get(repo);
2248
2249                                li = new LibItem();
2250                                li.setName(repoName);
2251                                li.setType(LibIndex.TYPE_LOCALREPO);
2252                                li.setParent(null);
2253                                li.setLevel(ROOT_LEVEL);
2254                                li.setLsid(null); // repos don't have LSIDs!
2255                                li.addAttribute(ATT_REPOPATH, repo.toString());
2256                                insertNoOrder(li);
2257                        }
2258                } catch (Exception e) {
2259                        log.error("Unable to assureLocalRepository(" + repo.toString()
2260                                        + ")");
2261                        e.printStackTrace();
2262                }
2263                return li;
2264        }
2265
2266        /**
2267         * 
2268         * @param ontologyName
2269         * @return
2270         * @throws SQLException
2271         */
2272        private LibItem findOntology(String ontologyName) throws SQLException {
2273                LibItem li = null;
2274                _getLIIDForOntologyNamePrepStmt.setString(1, ontologyName);
2275                if (isDebugging)
2276                        log.debug(_getLIIDForOntologyNamePrepStmt);
2277                ResultSet rs = null;
2278                try {
2279                        rs = _getLIIDForOntologyNamePrepStmt.executeQuery();
2280                        if (rs == null)
2281                                throw new SQLException("Query Failed: " + _getLIIDForOntologyNamePrepStmt);
2282                        if (rs.next()) {
2283                                int liid = rs.getInt(1);
2284                                if (isDebugging)
2285                                        log.debug(liid + " is already in the index table");
2286                                if (!rs.wasNull()) {
2287                        li = _libraryManager.getPopulatedLibItem(liid);
2288                                }
2289                                if (rs.next()) {
2290                                        log.error("LIBRARY_INDEX table is corrupt: "
2291                                                        + "multiples of ontology " + ontologyName + " found");
2292                                }
2293                        }
2294                } finally {
2295                        if(rs != null) {
2296                                rs.close();
2297                        }
2298                }
2299                return li;
2300        }
2301
2302        /**
2303         * Return the LibItem for the given Ontology Name. If no LibItem exists in
2304         * the LIBRARY_INDEX then insert a new row for it and return a LibItem
2305         * representation of the row.
2306         * 
2307         * @param ontologyName
2308         * @return
2309         */
2310        private LibItem assureOntology(String ontologyName) {
2311                LibItem li = null;
2312
2313                try {
2314                        li = findOntology(ontologyName);
2315
2316                        if (li == null) {
2317                                if (isDebugging)
2318                                        log.debug(ontologyName + " is not in the index table");
2319                                li = new LibItem();
2320                                li.setName(ontologyName);
2321                                li.setType(LibIndex.TYPE_ONTOLOGY);
2322                                li.setParent(null);
2323                                li.setLevel(1);
2324                                insertNoOrder(li);
2325                        }
2326                } catch (Exception e) {
2327                        log.error("Unable to assureOntology(" + ontologyName + ")");
2328                        e.printStackTrace();
2329                }
2330                return li;
2331        }
2332
2333        /**
2334         * 
2335         * @param noc
2336         * @return
2337         * @throws SQLException
2338         */
2339        private Vector<LibItem> findOntClass(NamedOntClass noc) throws SQLException {
2340                Vector<LibItem> items = new Vector<LibItem>();
2341                _getLIIDForOntologyClassPrepStmt.setString(1, noc.getConceptId());
2342                if (isDebugging)
2343                        log.debug(_getLIIDForOntologyClassPrepStmt);
2344                ResultSet rs = null;
2345                try { 
2346                        rs = _getLIIDForOntologyClassPrepStmt.executeQuery();
2347                        if (rs == null)
2348                                log.error("Query Failed: " + _getLIIDForOntologyClassPrepStmt);
2349                        while (rs.next()) {
2350                                int liid = rs.getInt(1);
2351                                if (isDebugging)
2352                                        log.debug(liid + " is already in the index table");
2353                                if (!rs.wasNull()) {
2354                        LibItem li = _libraryManager.getPopulatedLibItem(liid);
2355                                        items.add(li);
2356                                }
2357                        }
2358                } finally {
2359                        if(rs != null) {
2360                                rs.close();
2361                        }
2362                }
2363                return items;
2364        }
2365
2366        /**
2367         * Return a Vector of LibItem objects from the Library_Index that correlate
2368         * to the given NamedOntClass. If none exist then insert.
2369         * 
2370         * TODO: there is likely a serious flaw here, check into it...
2371         * 
2372         * @param noc
2373         * @return
2374         */
2375        private Vector<LibItem> assureOntClass(NamedOntClass noc) {
2376                if (isDebugging)
2377                        log.debug(noc.getName());
2378                Vector<LibItem> items = null;
2379                try {
2380
2381                        items = findOntClass(noc);
2382
2383                        if (items.size() <= 0) {
2384                                if (isDebugging)
2385                                        log.debug("needs to be added to the index table");
2386                                Iterator<NamedOntClass> parents = noc
2387                                                .getNamedSuperClasses(false);
2388                                while (parents.hasNext()) {
2389                                        NamedOntClass parent = parents.next();
2390                                        Vector<LibItem> parentItems = assureOntClass(parent);
2391                                        for (LibItem parentItem : parentItems) {
2392                                                LibItem newli = new LibItem();
2393                                                newli.setName(noc.getName());
2394                                                newli.setLsid(new KeplerLSID(noc.getConceptId()));
2395                                                newli.setParent(parentItem.getLiid());
2396                                                newli.setType(LibIndex.TYPE_CONCEPT);
2397                                                newli.setLevel(parentItem.getLevel() + 1);
2398                                                insertNoOrder(newli);
2399                                                items.add(newli);
2400                                        }
2401                                }
2402                                if (items.size() <= 0) {
2403                                        if (isDebugging)
2404                                                log.debug("no super classes, add the ontology");
2405                                        LibItem parentOnt = assureOntology(noc.getOntologyName());
2406
2407                                        LibItem newli = new LibItem();
2408                                        newli.setName(noc.getName());
2409                                        newli.setLsid(new KeplerLSID(noc.getConceptId()));
2410                                        newli.setParent(parentOnt.getLiid());
2411                                        newli.setType(LibIndex.TYPE_CONCEPT);
2412                                        newli.setLevel(parentOnt.getLevel() + 1);
2413                                        insertNoOrder(newli);
2414                                        items.add(newli);
2415                                }
2416                        }
2417                } catch (Exception e) {
2418                        log.error("Unable to assureOntClass(" + noc.toString() + ")");
2419                        e.printStackTrace();
2420                }
2421                return items;
2422        }
2423        
2424        /**
2425         * Return true if there is a child of the parent in the index with the given
2426         * name.
2427         * 
2428         * @param parentLiid
2429         * @param childName
2430         * @return
2431         */
2432        public int childExists(Integer parentLiid, String childName) {
2433                int liidOfExistingChild = -1;
2434                try {
2435                    PreparedStatement query;
2436                        if (parentLiid == null) {
2437                                query = _getLIIDForNullParentAndNamePrepStmt;
2438                                query.setString(1, childName);
2439                        } else {
2440                            query = _getLIIDForParentAndNamePrepStmt;
2441                            query.setInt(1, parentLiid.intValue());
2442                            query.setString(2, childName);
2443                        }
2444                        
2445                        if (isDebugging)
2446                                log.debug(query);
2447                        ResultSet rs = null;
2448                        try {
2449                                rs = query.executeQuery();
2450                                if (rs == null)
2451                                        throw new SQLException("Query Failed: " + query);
2452                                if (rs.next()) {
2453                                        liidOfExistingChild = rs.getInt(1);
2454                                }
2455                        } finally {
2456                                if(rs != null) {
2457                                        rs.close();
2458                                }
2459                        }
2460                } catch (SQLException e) {
2461                        e.printStackTrace();
2462                }
2463                return liidOfExistingChild;
2464        }
2465
2466}