org.kepler.objectmanager.library.LibIndex.java Source code

Java tutorial

Introduction

Here is the source code for org.kepler.objectmanager.library.LibIndex.java

Source

/*
 * Copyright (c) 2010 The Regents of the University of California.
 * All rights reserved.
 *
 * '$Author: crawl $'
 * '$Date: 2014-02-11 09:34:36 -0800 (Tue, 11 Feb 2014) $' 
 * '$Revision: 32585 $'
 * 
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the above
 * copyright notice and the following two paragraphs appear in all copies
 * of this software.
 *
 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
 * ENHANCEMENTS, OR MODIFICATIONS.
 *
 */

/**
 * 
 */
package org.kepler.objectmanager.library;

import java.io.File;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.kepler.build.modules.Module;
import org.kepler.build.modules.ModuleTree;
import org.kepler.build.util.Version;
import org.kepler.kar.KARCacheContent;
import org.kepler.kar.KARCacheError;
import org.kepler.kar.KARCacheManager;
import org.kepler.kar.KARCached;
import org.kepler.kar.KAREntry;
import org.kepler.kar.KARFile;
import org.kepler.kar.handlers.ActorMetadataKAREntryHandler;
import org.kepler.moml.KeplerActorMetadata;
import org.kepler.moml.KeplerMetadataExtractor;
import org.kepler.objectmanager.cache.ActorCacheObject;
import org.kepler.objectmanager.cache.CacheContent;
import org.kepler.objectmanager.cache.LocalRepositoryManager;
import org.kepler.objectmanager.cache.LocalRepositoryManager.LocalRepository;
import org.kepler.objectmanager.lsid.KeplerLSID;
import org.kepler.sms.NamedOntClass;
import org.kepler.sms.NamedOntModel;
import org.kepler.sms.OntologyCatalog;
import org.kepler.util.DotKeplerManager;

/**
 * A library index for keeping track of the tree structure in the component
 * library. This class uses preorder tree traversal method in an SQL table to
 * maintain the indexed structure of the component library instead of using XML
 * (as was previously done). This allows for easier control over the ordering of
 * the contents of the tree, faster and more targeted access to the contents of
 * the library using SQL queries (and some simple math), and it also introduces
 * a very useful new Identifier for the contents of the tree, the LIID.
 * 
 * Two companion tables, LIBRARY_LSIDS and LIBRARY_ATTRIBUTES, are used to store
 * additional metadata about the library contents. The LIBRARY_LSIDS table
 * serves to track the existence of multiple LSIDs for one single Component in
 * the library. This is necessary for handling revision management and the user
 * will be allowed to toggle which LSID the component is currently associated
 * with, without destroying the look of the component library tree. The
 * LIBRARY_ATTRIBUTES table is used for storing single string values that are
 * associated with component library items. For example, a KAR item can have the
 * location of that KAR on disk stored as an attribute of the library index
 * item.
 * 
 * @author Aaron Schultz
 */
public class LibIndex {
    private static final Log log = LogFactory.getLog(LibIndex.class.getName());
    private static final boolean isDebugging = log.isDebugEnabled();

    /**
     * the name of the table in the database
     */
    public static final String LIBRARY_INDEX_TABLE_NAME = "LIBRARY_INDEX";

    /**
      * the name of the table in the database used to store attributes
      */
    public static final String LIBRARY_LSIDS_TABLE_NAME = "LIBRARY_LSIDS";

    /**
     * the name of the table in the database used to store attributes
     */
    public static final String LIBRARY_ATTRIBUTES_TABLE_NAME = "LIBRARY_ATTRIBUTES";

    /**
    * Map of integers for the different types of objects that are stored in the
    * LIBRARY_INDEX table. To add a new type to the tree you must do a few
    * things. Add a new static integer here. Modify LibraryManager.getTreeItem
    * to handle the new type. Add methods to this class for recognizing the
    * type in KAR files.
    */
    public static final int TYPE_COMPONENT = 1;
    public static final int TYPE_NAMED_OBJ = 2;
    public static final int TYPE_ONTOLOGY = 3;
    public static final int TYPE_CONCEPT = 4;
    public static final int TYPE_FOLDER = 5;
    public static final int TYPE_LOCALREPO = 6;
    public static final int TYPE_KAR = 7;
    public static final int TYPE_KAR_ERROR = 8;
    public static final int TYPE_KARFOLDER = 9;
    public static final int TYPE_OBJECT = 10;

    /**
     * Attribute names that are used to keep information about Library nodes.
     */
    public static final String ATT_REPOPATH = "REPOPATH";
    public static final String ATT_FOLDER = "FOLDER";
    public static final String ATT_KARENTRYPATH = "KARENTRYPATH";
    public static final String ATT_KARFILE = "KARFILE";
    public static final String ATT_XMLFILE = "XMLFILE";
    public static final String ATT_CLASSNAME = "CLASSNAME";

    private LibSearch _searcher;
    private Hashtable<Integer, Integer> _searchTypeMap;

    // Convenience references
    private Connection _conn;
    private Statement _stmt;

    // Prepared Statements
    private PreparedStatement _updateOrderPrepStmt;
    private PreparedStatement _updateLsidPrepStmt;
    private PreparedStatement _getLIIDForKarPrepStmt;
    private PreparedStatement _getLIIDForXMLPrepStmt;
    private PreparedStatement _getLIIDForFolderPrepStmt;
    private PreparedStatement _getLIIDForKarEntryPrepStmt;
    private PreparedStatement _getLIIDForKarErrorPrepStmt;
    private PreparedStatement _getLIIDForRepositoryPrepStmt;
    private PreparedStatement _getLIIDForOntologyNamePrepStmt;
    private PreparedStatement _getLIIDForOntologyClassPrepStmt;
    private PreparedStatement _getNumRowsInLibraryIndexPrepStmt;
    private PreparedStatement _getLIIDOfParentsPrepStmt;
    private PreparedStatement _getLIIDForParentAndNamePrepStmt;
    private PreparedStatement _getLIIDForNullParentAndNamePrepStmt;
    private PreparedStatement _getRangeForLIIDPrepStmt;
    private PreparedStatement _getLIIDForParentPrepStmt;
    private PreparedStatement _getLftRgtSumPrepStmt;
    private PreparedStatement _getLIIDRootsPrepStmt;
    private PreparedStatement _deleteLIIDFromLibraryAttributesPrepStmt;
    private PreparedStatement _insertIntoLibraryAttributesPrepStmt;
    private PreparedStatement _getNumLIIDForLIIDAndLSIDPrepStmt;
    private PreparedStatement _insertIntoLibraryLSIDsPrepStmt;

    private LibraryManager _libraryManager;

    /** Level for root objects in the tree. */
    private static final int ROOT_LEVEL = 1;

    /** Root item for Demos */
    private LibItem _demosFolderItem;

    /** Pattern for the modules' persistent workflow directory. */
    private Pattern _demosFolderPattern = Pattern
            .compile(Pattern.quote(DotKeplerManager.getInstance().getPersistentModuleWorkflowsDirString()) + "([^"
                    + Pattern.quote(File.separator) + "]+)");

    /**
     * There are two ways to insert rows into the table. One was is to have the
     * insert statement handle the ordering of the hierarchy at insert time.
     * This is very convenient for inserting just one row. But very time
     * consuming for inserting many rows. For many rows, one may toggle the
     * _orderedInsert to Off and once the rows have been inserted call the
     * refreshPreorderValues method to order the rows based on parent
     * relationships.
     */
    private boolean _orderedInsert;

    /**
     * A constructor that is given a connection to the database.
     * 
     * @param conn
     */
    public LibIndex(Connection conn) {
        initialize(conn);
    }

    /**
     * Return the current value of orderedInsert. A value of false means that
     * items inserted will not be automatically ordered during insert.
     * 
     * @return
     */
    public boolean isOrderedInsert() {
        return _orderedInsert;
    }

    /**
     * In general orderedInsert should always be true. When inserting many rows
     * however it is faster to not order the entire table dynamically on every
     * insert but to wait until all the rows have been inserted and then update
     * the ordering based on parent relationships using the
     * refreshPreorderValues method. This method allows you to toggle ordering
     * during insert to on (true) or off (false).
     * 
     * @param orderedInsert
     */
    public void setOrderedInsert(boolean orderedInsert) {
        _orderedInsert = orderedInsert;
    }

    /**
     * Initialize the instance.
     * 
     * @param conn
     */
    private void initialize(Connection conn) {
        if (isDebugging)
            log.debug("initialize(" + conn.toString() + ")");
        _conn = conn;

        _libraryManager = LibraryManager.getInstance();

        try {
            // By creating the statement and keeping it around
            // make sure to close your resultsets to save memory
            _stmt = _conn.createStatement();

            _updateOrderPrepStmt = _conn
                    .prepareStatement("update " + LIBRARY_INDEX_TABLE_NAME + " SET LFT=?, RGT=? WHERE LIID=?");
            _updateLsidPrepStmt = _conn
                    .prepareStatement("update " + LIBRARY_INDEX_TABLE_NAME + " SET LSID=? WHERE LIID=?");

            _getLIIDForKarPrepStmt = _conn.prepareStatement("SELECT LIID FROM " + LIBRARY_ATTRIBUTES_TABLE_NAME
                    + " WHERE NAME = '" + ATT_KARFILE + "' AND VALUE = ?");

            _getLIIDForXMLPrepStmt = _conn.prepareStatement("SELECT LIID FROM " + LIBRARY_ATTRIBUTES_TABLE_NAME
                    + " WHERE NAME = '" + ATT_XMLFILE + "' AND VALUE = ?");

            _getLIIDForFolderPrepStmt = _conn.prepareStatement("SELECT LIID FROM " + LIBRARY_ATTRIBUTES_TABLE_NAME
                    + " WHERE NAME = '" + ATT_FOLDER + "' AND VALUE = ?");

            _getLIIDForKarEntryPrepStmt = _conn.prepareStatement("SELECT LIID FROM " + LIBRARY_ATTRIBUTES_TABLE_NAME
                    + " WHERE NAME = '" + ATT_KARENTRYPATH + "' AND VALUE = ?");

            _getLIIDForKarErrorPrepStmt = _conn.prepareStatement("SELECT LIID FROM " + LIBRARY_ATTRIBUTES_TABLE_NAME
                    + " WHERE NAME = '" + ATT_KARFILE + "' AND VALUE = ?");

            _getLIIDForRepositoryPrepStmt = _conn.prepareStatement("SELECT LIID FROM "
                    + LIBRARY_ATTRIBUTES_TABLE_NAME + " WHERE NAME = '" + ATT_REPOPATH + "' AND VALUE = ?");

            _getLIIDForOntologyNamePrepStmt = _conn.prepareStatement(
                    "SELECT LIID FROM " + LIBRARY_INDEX_TABLE_NAME + " WHERE NAME = ? AND LEVEL = 1");

            _getLIIDForOntologyClassPrepStmt = _conn
                    .prepareStatement("SELECT LIID FROM " + LIBRARY_INDEX_TABLE_NAME + " WHERE LSID = ?");

            _getNumRowsInLibraryIndexPrepStmt = _conn
                    .prepareStatement("SELECT count(LIID) from " + LIBRARY_INDEX_TABLE_NAME);

            _getLIIDOfParentsPrepStmt = _conn.prepareStatement("SELECT LIID from " + LIBRARY_INDEX_TABLE_NAME
                    + " WHERE LFT < ? AND RGT > ? " + "ORDER BY LEVEL");

            _getLIIDForParentAndNamePrepStmt = _conn.prepareStatement(
                    "select liid from " + LIBRARY_INDEX_TABLE_NAME + " where parent = ? and name = ?");

            _getLIIDForNullParentAndNamePrepStmt = _conn.prepareStatement(
                    "select LIID from " + LIBRARY_INDEX_TABLE_NAME + " where PARENT IS NULL AND NAME = ?");

            _getRangeForLIIDPrepStmt = _conn
                    .prepareStatement("SELECT LFT,RGT from " + LIBRARY_INDEX_TABLE_NAME + " WHERE LIID = ?");

            _getLIIDForParentPrepStmt = _conn.prepareStatement(
                    "SELECT liid FROM " + LIBRARY_INDEX_TABLE_NAME + " WHERE parent = ? ORDER BY TYPE,NAME");

            _getLftRgtSumPrepStmt = _conn
                    .prepareStatement("select (sum(LFT) + sum(RGT)) FROM " + LIBRARY_INDEX_TABLE_NAME);

            _getLIIDRootsPrepStmt = _conn.prepareStatement(
                    "SELECT LIID FROM " + LIBRARY_INDEX_TABLE_NAME + " WHERE LEVEL = 1 ORDER BY TYPE,NAME");

            _deleteLIIDFromLibraryAttributesPrepStmt = _conn
                    .prepareStatement("DELETE FROM " + LIBRARY_ATTRIBUTES_TABLE_NAME + " WHERE LIID = ?");

            _insertIntoLibraryAttributesPrepStmt = _conn.prepareStatement(
                    "INSERT INTO " + LIBRARY_ATTRIBUTES_TABLE_NAME + " (LIID,NAME,VALUE) values (?, ?, ?)");

            _getNumLIIDForLIIDAndLSIDPrepStmt = _conn.prepareStatement(
                    "SELECT count(LIID) FROM " + LIBRARY_LSIDS_TABLE_NAME + " WHERE LIID = ? AND LSID = ?");

            _insertIntoLibraryLSIDsPrepStmt = _conn
                    .prepareStatement("insert into " + LIBRARY_LSIDS_TABLE_NAME + " (LIID,LSID) values (?,?)");

        } catch (SQLException e) {
            e.printStackTrace();
        }

        // Share the connection with the LibSearch
        _searcher = new LibSearch(_conn);
        initSearchMap();

        setOrderedInsert(true);
    }

    /**
     * Delete all rows in the LIBRARY_INDEX table. This will also delete all
     * rows in the LIBRARY_ATTRIBUTES table by cascading foreign key deletes.
     */
    public void clear() {
        String clear = "delete from " + LIBRARY_INDEX_TABLE_NAME;
        if (isDebugging)
            log.debug(clear);
        String resetAutoInc = "ALTER TABLE " + LIBRARY_INDEX_TABLE_NAME + " ALTER COLUMN LIID RESTART WITH 1";
        if (isDebugging)
            log.debug(resetAutoInc);
        try {
            _stmt.executeUpdate(clear);
            _stmt.execute(resetAutoInc);

            getSearcher().clear();
            _stmt.getConnection().commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }

        _demosFolderItem = null;
    }

    /**
     * Completely delete the library index and recreate it from the contents of
     * cached KARs.
     */
    public void rebuild() {
        log.info("Building LibIndex...");
        clear();

        setOrderedInsert(false);
        try {

            KARCacheManager kcm = KARCacheManager.getInstance();

            // Add all Kar Contents to the Library Index table
            Vector<KARCacheContent> contents = kcm.getKARCacheContents();
            for (KARCacheContent content : contents) {

                assureOntologyComponent(content);
                assureKarEntry(content);

            }

            // Add all Kar Errors to the Library
            Vector<KARCacheError> errors = kcm.getKARCacheErrors();
            for (KARCacheError error : errors) {
                assureKarError(error);
            }

            for (File xmlFile : LocalRepositoryManager.getInstance().getXMLFiles()) {
                assureXML(xmlFile);
            }

            // Refresh the ordering of the library (this is much faster than
            // updating the order every time we insert)
            refreshPreorderValues();

            // because we insert with no order here, we can't
            // easily determine the path to any given LibItem
            // so we wait until all individual LibItems have been
            // created and ordered to finish up indexing the items
            // for the search
            finishSearchIndexing();

        } catch (SQLException e) {
            e.printStackTrace();
        }
        setOrderedInsert(true);
    }

    /**
     * Select all of the existing LibItems from the Library Index and insert all
     * of their parent items into the Search Index.
     * 
     * @throws SQLException
     */
    private void finishSearchIndexing() throws SQLException {
        Vector<LibItem> items = getItems();
        for (LibItem item : items) {
            addAllParentsToSearchIndex(item);
        }
    }

    /**
     * Update the default LSID for the given LIID to the given LSID.
     * 
     * @param liid
     * @param lsid
     * @throws SQLException
     */
    public void updateDefaultLsid(int liid, KeplerLSID lsid) throws SQLException {
        if (isDebugging)
            log.debug("updateDefaultLsid(" + liid + "," + lsid + ")");

        try {
            _updateLsidPrepStmt.setString(1, lsid.toString());
            _updateLsidPrepStmt.setInt(2, liid);

            _updateLsidPrepStmt.executeUpdate();
            _updateLsidPrepStmt.clearParameters();
            _conn.commit();
        } catch (SQLException sqle) {
            throw sqle;
        }
    }

    /**
     * Remove all data from the index database that is associated with the given
     * Library Index ID and all of the data of it's children.
     * 
     * @param liid
     * @return
     */
    public boolean removeItem(int liid) {
        boolean success = false;
        LibItem li = null;
        try {
            li = _libraryManager.getPopulatedLibItem(liid);
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        if (li != null) {
            success = removeItem(li);
        }
        return success;
    }

    /**
     * Remove all data from the index database that is associated with the given
     * Library Item (using the Library Index ID) and all of the data of it's
     * children. This method is the same as removeItem(int liid). It is here for
     * convenience.
     * 
     * @param li
     * @return
     */
    public boolean removeItem(LibItem li) {
        String delete = "DELETE FROM " + LibIndex.LIBRARY_INDEX_TABLE_NAME + " WHERE LIID = " + li.getLiid();
        // NOTE: the Foreign Key to the parent LIID prevents us from doing this:
        // + " WHERE LFT >= " + li.getLeft() + " AND RGT <= " + li.getRight();
        // which is faster performance wise. Instead we'll trust the ON DELETE
        // CASCADE for the parent foreign key to properly remove all the
        // children of the LIID we're removing which is a little slower
        // performance wise but still should work fine

        String updateLFT = "UPDATE " + LibIndex.LIBRARY_INDEX_TABLE_NAME + " SET LFT = LFT - "
                + (li.getRight() - li.getLeft() + 1) + " WHERE LFT > " + li.getLeft();
        String updateRGT = "UPDATE " + LibIndex.LIBRARY_INDEX_TABLE_NAME + " SET RGT = RGT - "
                + (li.getRight() - li.getLeft() + 1) + " WHERE RGT > " + li.getLeft();
        try {
            _stmt.executeUpdate(delete);
            _stmt.executeUpdate(updateLFT);
            _stmt.executeUpdate(updateRGT);
            _stmt.getConnection().commit();
        } catch (Exception e) {
            try {
                _stmt.getConnection().rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
            return false;
        }
        return true;
    }

    /**
     * This method will remove the given LSID from being associated with any of
     * the Library Items. If there is another LSID associated with the given
     * item then it will be assigned as the default. If the Library Item is only
     * associated with the given LSID then the Item itself will be removed.
     * 
     * @param lsid
     * @return Vector<Integer> all LIIDs that got removed
     * @throws SQLException
     */
    public Vector<Integer> removeItemsByLsid(KeplerLSID lsidToRemove) throws SQLException {
        Vector<Integer> liidsThatGotRemoved = new Vector<Integer>();

        // First keep the history of all LIIDs this LSID was associated with
        Vector<KeplerLSID> kludge = new Vector<KeplerLSID>(1);
        kludge.add(lsidToRemove);
        Vector<Integer> liidAssociations = LibraryManager.getInstance().getLiidsFor(kludge);

        // Now remove all the LIID -> LSID associations for this LSID
        String remove = "DELETE FROM " + LIBRARY_LSIDS_TABLE_NAME + " WHERE lsid = '" + lsidToRemove + "'";
        _stmt.executeUpdate(remove);

        // Now go through all the liids
        for (Integer liid : liidAssociations) {
            // check if this LSID is the default LSID
            String defaultQuery = "SELECT LSID FROM " + LibIndex.LIBRARY_INDEX_TABLE_NAME + " WHERE LIID = "
                    + liid.intValue();
            if (isDebugging)
                log.debug(defaultQuery);
            ResultSet rs1 = null;
            ResultSet rs2 = null;
            try {
                rs1 = _stmt.executeQuery(defaultQuery);
                if (rs1 == null)
                    throw new SQLException("Query Failed: " + defaultQuery);
                if (rs1.next()) {
                    String currentDefaultLsidStr = rs1.getString(1);
                    KeplerLSID currentDefaultLsid = null;
                    try {
                        currentDefaultLsid = new KeplerLSID(currentDefaultLsidStr);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    if (currentDefaultLsid.equals(lsidToRemove)) {
                        // This is the default LSID
                        // check if there are any LSIDs that can become the new
                        // default
                        String newDefaultQuery = "SELECT LSID FROM " + LIBRARY_LSIDS_TABLE_NAME + " WHERE LIID = "
                                + liid.intValue() + " order by LSID DESC";
                        if (isDebugging)
                            log.debug(newDefaultQuery);
                        rs2 = _stmt.executeQuery(newDefaultQuery);
                        if (rs2 == null)
                            throw new SQLException("Query Failed: " + newDefaultQuery);
                        if (rs2.next()) {
                            // Yes there are other LSIDs associated with this LIID,
                            // set the first one we get back as the new default
                            // (hopefully the "order by DESC" will give us the
                            // highest revision)
                            String updateDefaultLSID = "UPDATE " + LibIndex.LIBRARY_INDEX_TABLE_NAME
                                    + " SET LSID = '" + rs2.getString(1) + " WHERE LIID = " + liid.intValue();
                            if (isDebugging)
                                log.debug(updateDefaultLSID);
                            _stmt.executeUpdate(updateDefaultLSID);
                        } else {
                            // No there are no more LSIDs associated with this LIID
                            // so let's remove this LIID if it is a leaf
                            LibItem li = _libraryManager.getPopulatedLibItem(liid);
                            // double check that this is a leaf node
                            if ((li.getRight() - li.getLeft()) == 1) {
                                li.delete(_stmt);
                                liidsThatGotRemoved.add(li.getLiid());
                            } else {
                                // theoretically we never run into this BUT
                                // if we do let's go ahead and null out the
                                // default lsid
                                String updateDefaultLSID = "UPDATE " + LibIndex.LIBRARY_INDEX_TABLE_NAME
                                        + " SET LSID = NULL" + " WHERE LIID = " + liid.intValue();
                                _stmt.executeUpdate(updateDefaultLSID);
                                log.warn("Nulled default LSID because the node is not a leaf");
                            }
                        }
                    } else {
                        // This is not the default LSID so we're done with this one
                    }
                }
            } finally {
                if (rs1 != null) {
                    rs1.close();
                }
                if (rs2 != null) {
                    rs2.close();
                }
            }
        }
        _stmt.getConnection().commit();
        return liidsThatGotRemoved;
    }

    /**
     * Assures that the LibIndex row under the folder hierarchy exists for the
     * given KARCacheContent. This method shouldn't really be used. It is only
     * public so the LibraryManager.addKar() method can use it.
     * 
     * @param content
     * @throws SQLException
     */
    public LibItem assureKarEntry(KARCacheContent content) throws SQLException {
        if (isDebugging)
            log.debug("assureKarEntry(" + content.getName() + ")");

        // add the object to the folders hierarchy
        File karFile = content.getKarFile();
        String entry = content.getName();

        LibItem li = findKarEntry(karFile, entry);
        if (li == null) {
            LibItem liParent = null;
            String[] entryPath = entry.split("/");
            if (entryPath.length > 1) {
                // entry is in a subfolder
                String folderPath = new String();
                for (int i = 0; i < (entryPath.length - 1); i++) {
                    folderPath = entryPath[i] + "/";
                }
                folderPath = folderPath.substring(0, folderPath.length() - 1);
                liParent = assureKarFolder(content.getKarCached(), folderPath);
            } else {
                liParent = assureKar(content.getKarCached());
            }

            CacheContent cc = content.getCacheContent();
            String actorName = cc.getName();

            li = new LibItem();
            li.setName(actorName);

            int t = determineLibIndexType(content);
            if (t > 0) {
                li.setType(t);
            } else {
                log.error("Could not determine the LibIndex type for KARCacheContent");
                return null;
            }

            li.setParent(liParent.getLiid());
            li.setLevel(liParent.getLevel() + 1);
            li.setLsid(content.getLsid());
            li.addAttribute(ATT_KARENTRYPATH, getKarEntryPath(karFile, entry));
            String className = content.getCacheContent().getClassName();
            if (className != null) {
                li.addAttribute(ATT_CLASSNAME, className);
                transferAttributes(li, content);
            }
            insertNoOrder(li);

        } else {
            // it is already there
            // this is probably an unnecessary check
        }
        return li;
    }

    private static Map<KeplerLSID, Map<String, String>> cachedItemAttributes = new HashMap<KeplerLSID, Map<String, String>>();

    private void transferAttributes(LibItem li, KARCacheContent content) {

        if (cachedItemAttributes.containsKey(li.getLsid())) {
            Map<String, String> attributes = cachedItemAttributes.get(li.getLsid());
            for (String name : attributes.keySet()) {
                li.addAttribute(name, attributes.get(name));
            }
            return;
        }

        KARFile karFile = null;
        try {
            karFile = new KARFile(content.getKarFile());
        } catch (IOException ex) {
            ex.printStackTrace();
        }

        KAREntry karEntry = new KAREntry(content.getName());
        ActorMetadataKAREntryHandler keh = new ActorMetadataKAREntryHandler();
        ActorCacheObject aco;
        try {
            aco = (ActorCacheObject) keh.cache(karFile, karEntry);
        } catch (Exception ex) {
            ex.printStackTrace();
            return;
        }

        Map<String, String> attributes = new HashMap<String, String>();
        for (String attributeName : aco.getAttributeNames()) {
            String attributeValue = aco.getAttribute(attributeName);
            attributes.put(attributeName, attributeValue);
            li.addAttribute(attributeName, attributeValue);
        }

        cachedItemAttributes.put(li.getLsid(), attributes);
    }

    /**
     * Determine the appropriate LibIndex type of the given KARCacheContent
     * 
     * @param kcc
     * @return
     */
    public int determineLibIndexType(KARCacheContent content) {
        int theType = -1;
        String cacheObjectType = content.getCacheContent().getType();
        boolean isActorCacheObject = false;
        try {
            String actorCacheObjectType = "org.kepler.objectmanager.cache.ActorCacheObject";
            if (cacheObjectType.equals(actorCacheObjectType)) {
                isActorCacheObject = true;
            } else if (cacheObjectType.equals("org.kepler.objectmanager.cache.TextFileCacheObject")) {
                isActorCacheObject = false;
                return LibIndex.TYPE_OBJECT;
            } else {
                isActorCacheObject = KARFile.isSubclass(actorCacheObjectType, cacheObjectType);
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        if (isActorCacheObject) {
            theType = LibIndex.TYPE_COMPONENT;
        } else {
            String entryObjectType = content.getType();
            boolean isGenericNamedObjType = false;
            try {
                String namedObjectType = "ptolemy.kernel.util.NamedObj";
                if (entryObjectType.equals(namedObjectType)) {
                    isGenericNamedObjType = true;
                } else {
                    isGenericNamedObjType = KARFile.isSubclass(namedObjectType, entryObjectType);
                }
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            if (isGenericNamedObjType) {
                theType = LibIndex.TYPE_NAMED_OBJ;
            } else {
                theType = LibIndex.TYPE_OBJECT;
            }
        }
        return theType;
    }

    /**
     * Assures that the LibIndex rows for the KARCacheContent object exist under
     * the Ontology tree. This method shouldn't really be used. It is only
     * public so the LibraryManager.addKar() method can use it.
     * 
     * @param content
     */
    public Vector<LibItem> assureOntologyComponent(KARCacheContent content) {
        Vector<LibItem> items = new Vector<LibItem>();

        OntologyCatalog oc = OntologyCatalog.instance();

        // add the object in the ontology class hierarchy
        for (KeplerLSID st : content.getSemanticTypes()) {

            NamedOntClass noc = oc.getNamedOntClass(st.toString());
            if (noc != null) {

                // First determine if the NamedOntClass belongs to
                // an Ontology that is supposed to be showing up
                // in the library.
                boolean includeInOntologies = false;
                Iterator<NamedOntModel> libraryModels = oc.getLibraryNamedOntModels();
                while (libraryModels.hasNext()) {
                    if (libraryModels.next().equals(noc.getModel())) {
                        includeInOntologies = true;
                        break;
                    }
                }
                // If this NamedOntClass belongs to an Ontology Model
                // that is configured to be included in the library
                // then add it, otherwise just skip it
                if (includeInOntologies) {

                    // Recurse up the tree
                    Vector<LibItem> parents = assureOntClass(noc);
                    for (LibItem parent : parents) {

                        CacheContent cc = content.getCacheContent();
                        String actorName = cc.getName();

                        LibItem li = new LibItem();
                        li.setName(actorName);
                        li.setParent(parent.getLiid());
                        KeplerLSID lsid = content.getLsid();
                        if (lsid != null) {
                            li.setLsid(lsid);
                        }

                        // Figure out what LibIndex type this KARCacheContent is
                        int t = determineLibIndexType(content);
                        if (t > 0) {
                            li.setType(t);
                        } else {
                            log.error("Could not determine the LibIndex type for KARCacheContent");
                            continue;
                        }

                        li.setLevel(parent.getLevel() + 1);

                        String className = content.getCacheContent().getClassName();
                        if (className != null) {
                            li.addAttribute(ATT_CLASSNAME, className);
                            transferAttributes(li, content);
                        }
                        try {

                            // See if this LibItem will collide with an existing LibItem
                            int liidOfExistingChild = childExists(li.getParent(), li.getName());
                            if (liidOfExistingChild > -1) {
                                // A LIID with this name and parent already exists
                                // just add the LSID to the existing LIID item
                                insertLiidLsid(liidOfExistingChild, li.getLsid());
                            } else {
                                // create a new item in the library
                                insertNoOrder(li);
                                items.add(li);
                            }
                        } catch (SQLException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        return items;
    }

    /**
     * Count how many rows there are in the LIBRARY_INDEX table.
     * 
     * @return
     */
    public int countItems() {
        int count = 0;
        try {
            ResultSet rs = null;
            try {
                rs = _getNumRowsInLibraryIndexPrepStmt.executeQuery();
                if (rs == null)
                    return count;
                if (rs.next()) {
                    count = rs.getInt(1);
                    if (rs.next()) {
                        // should never happen
                        throw new SQLException("Multiple rows found in countItems()");
                    }
                }
            } finally {
                if (rs != null) {
                    rs.close();
                }
            }
        } catch (SQLException sqle) {
            sqle.printStackTrace();
        }
        return count;

    }

    /**
     * Returns a Vector of LibItems that are the parents of the given LibItem.
     * 
     * @param li
     * @return Vector<LibItem> representing all parents of the given LibItem
     * @throws SQLException
     */
    public Vector<LibItem> getPath(LibItem li) throws SQLException {
        Vector<LibItem> pathItems = new Vector<LibItem>();

        _getLIIDOfParentsPrepStmt.setInt(1, li.getLeft());
        _getLIIDOfParentsPrepStmt.setInt(2, li.getRight());
        if (isDebugging)
            log.debug(_getLIIDOfParentsPrepStmt);
        ResultSet rs = null;
        try {
            rs = _getLIIDOfParentsPrepStmt.executeQuery();
            if (rs == null)
                throw new SQLException("Query Failed: " + _getLIIDOfParentsPrepStmt);
            while (rs.next()) {
                int liid = rs.getInt(1);
                LibItem parentItem = _libraryManager.getPopulatedLibItem(liid);
                pathItems.add(parentItem);
            }
        } finally {
            if (rs != null) {
                rs.close();
            }
        }
        pathItems.add(li);
        return pathItems;
    }

    /**
     * Get all of the items.
     * 
     * @return
     */
    public Vector<LibItem> getItems() {
        return getItemsOfType(-1, -1);
    }

    /**
     * Get all items of the given type.
     * 
     * @param type
     * @return
     */
    public Vector<LibItem> getItemsOfType(int type) {
        return getItemsOfType(type, -1);
    }

    /**
     * Return items of the given type that are somewhere under the given root.
     * 
     * @param type
     * @param root
     * @return
     */
    public Vector<LibItem> getItemsOfType(int type, int root) {
        Vector<LibItem> items = new Vector<LibItem>(countItems());

        // TODO: actually select all the values instead of just the liid
        String query = "SELECT LIID " + " FROM " + LIBRARY_INDEX_TABLE_NAME;

        if (type <= 0 && root <= 0) {
            // get everything
        } else {
            if (root > 0) {
                // get items under the specified root node
                try {
                    query += " WHERE " + getRange(root);
                } catch (SQLException sqle) {
                    log.warn(root + " library item not found");
                    return items;
                }
                if (type > 0) {
                    query += " AND TYPE = " + type;
                }
            } else {
                // root <= 0
                if (type > 0) {
                    // get everything with the specified type
                    query += " WHERE TYPE = " + type;
                }
            }
        }

        query += " ORDER BY LFT";

        try {
            if (isDebugging)
                log.debug(query);
            ResultSet rs = null;
            try {
                rs = _stmt.executeQuery(query);
                if (rs == null)
                    throw new SQLException("Query Failed: " + query);
                while (rs.next()) {
                    int liid = rs.getInt(1);
                    // Doing it this way is kinda slow, but works for now
                    LibItem li = _libraryManager.getPopulatedLibItem(liid);
                    items.add(li);
                }
            } finally {
                if (rs != null) {
                    rs.close();
                }
            }
        } catch (SQLException sqle) {
            log.warn(root + " library item not found");
            return items;
        }
        if (isDebugging)
            log.debug(items.size() + " library items found");
        return items;
    }

    /**
     * Convenience method for building the SQL string for queries needing the
     * range between an LIIDs LFT and RGT preorder values.
     * 
     * @param liid
     * @return
     * @throws SQLException
     */
    private String getRange(int liid) throws SQLException {
        String rangeString = new String();
        _getRangeForLIIDPrepStmt.setInt(1, liid);
        ResultSet rs = null;
        try {
            rs = _getRangeForLIIDPrepStmt.executeQuery();
            if (rs == null)
                throw new SQLException("Query Failed: " + _getRangeForLIIDPrepStmt);
            if (rs.next()) {
                int l = rs.getInt(1);
                int r = rs.getInt(2);
                rangeString += " LFT >= " + l + " AND ";
                rangeString += " RGT <= " + r;
            }
        } finally {
            if (rs != null) {
                rs.close();
            }
        }
        return rangeString;

    }

    /**
     * Take a LibItem and check to see if there is already an entry
     * in the sql table for it depending on the type,name,and parent.
     * If there is already an item in the table, return the LIID for
     * the existing item.
     * 
     * @param li
     * @return long LIID of existing item or -1 if no duplicate is found
     */
    public int checkIfDuplicate(LibItem li) throws SQLException {
        int liid = -1;

        _getLIIDForParentAndNamePrepStmt.setInt(1, li.getParent());
        _getLIIDForParentAndNamePrepStmt.setString(2, li.getName());
        ResultSet rs = null;
        try {
            rs = _getLIIDForParentAndNamePrepStmt.executeQuery();
            if (rs == null)
                throw new SQLException("Query failed: " + _getLIIDForParentAndNamePrepStmt);
            if (rs.next()) {
                liid = rs.getInt(1);
                if (rs.wasNull()) {
                    // bad news.
                    liid = -1;
                }
                if (rs.next()) {
                    // also bad news
                }
            }
        } finally {
            if (rs != null) {
                rs.close();
            }
        }
        return liid;
    }

    /**
     * Insert this LibItem into the table, make sure it doesn't already
     * exist.  If it does try to add the LSID to the LIBRARY_LSIDS table.
     * 
     * @param li
     * @throws SQLException
     */
    private void insert(LibItem li) throws SQLException {
        if (isDebugging)
            log.debug("insert(" + li.getName() + ")");

        // Make sure there isn't an existing entry for this item
        int liidOfExistingChild = childExists(li.getParent(), li.getName());
        if (liidOfExistingChild > -1) {
            try {
                // Add the lsid to the Library_lsids table if the item already exists
                insertLiidLsid(liidOfExistingChild, li.getLsid());
            } catch (SQLException sqle) {
                throw new SQLException("Unable to insert LIID for existing child.");
            }
        } else {
            /**
             * Insert a new row for this LibItem under the parent liid or at level 1 if
             * parent is null.
             *
             */
            String insert = "INSERT INTO " + LibIndex.LIBRARY_INDEX_TABLE_NAME
                    + " (PARENT,LFT,RGT,LEVEL,LSID,TYPE,NAME) values (";

            int index = getAlphabeticInsertIndex(li);
            if (index < 0)
                return;

            li.setLeft(index);
            li.setRight(index + 1);

            if (li.getParent() == null) {
                insert += "NULL"; // parent
            } else {
                insert += "" + li.getParent(); // parent
            }

            insert += "," + li.getLeft(); // left
            insert += "," + li.getRight(); // right
            insert += "," + li.getLevel(); // level

            if (li.getLsid() != null) {
                insert += ",'" + li.getLsid().toString() + "'"; // lsid
            } else {
                insert += ",NULL";
            }
            insert += "," + li.getType(); // type
            insert += ",'" + li.getName() + "'"; // name
            insert += ")";

            String updateLeft = "UPDATE " + LibIndex.LIBRARY_INDEX_TABLE_NAME + " SET LFT = LFT + 2 WHERE LFT >= "
                    + li.getLeft();
            String updateRight = "UPDATE " + LibIndex.LIBRARY_INDEX_TABLE_NAME + " SET RGT = RGT + 2 WHERE RGT >= "
                    + li.getLeft();

            if (isDebugging) {
                log.debug("\n" + updateLeft + "\n" + updateRight + "\n" + insert);
            }

            _stmt.executeUpdate(updateLeft);
            _stmt.executeUpdate(updateRight);
            _stmt.executeUpdate(insert);

            // find out the auto assigned liid
            String queryNewLiid = "CALL IDENTITY();";
            ResultSet rs = null;
            try {
                rs = _stmt.executeQuery(queryNewLiid);
                if (rs != null && rs.next()) {
                    int newLiid = rs.getInt(1);
                    if (!rs.wasNull()) {
                        li.setLiid(newLiid);
                        if (isDebugging)
                            log.debug("IDENTITY: " + li.getLiid());
                    } else {
                        log.error("Failed to retrieve auto assigned identity");
                    }
                }
            } finally {
                if (rs != null) {
                    rs.close();
                }
            }

            // add the lsid to the LiidLsid table
            insertLiidLsid(li.getLiid(), li.getLsid());
            insertAttributes(li);

            LibraryManager.getInstance().getIndex().addToSearchIndex(li);

            _stmt.getConnection().commit();
        }
    }

    private void insertAttributes(LibItem li) throws SQLException {

        final int liid = li.getLiid();

        _deleteLIIDFromLibraryAttributesPrepStmt.setInt(1, liid);

        if (isDebugging)
            log.debug(_deleteLIIDFromLibraryAttributesPrepStmt);
        _deleteLIIDFromLibraryAttributesPrepStmt.executeUpdate();
        _stmt.getConnection().commit();

        for (Entry<String, String> entry : li.getAttributes().entrySet()) {
            final String attName = entry.getKey();
            final String attValue = entry.getValue();
            _insertIntoLibraryAttributesPrepStmt.setInt(1, liid);
            _insertIntoLibraryAttributesPrepStmt.setString(2, attName);
            _insertIntoLibraryAttributesPrepStmt.setString(3, attValue);
            if (isDebugging)
                log.debug(_insertIntoLibraryAttributesPrepStmt);
            _insertIntoLibraryAttributesPrepStmt.executeUpdate();
        }
        _stmt.getConnection().commit();
    }

    /**
      * Figure out what the LEFT integer should be for a new row that is to be
      * inserted in alphabetical order for the current getParent() value. Or for
      * Level 1 if getParent() == null
      * 
      * @param stmt
      * @return
      * @throws SQLException
      */
    private int getAlphabeticInsertIndex(LibItem li) throws SQLException {
        int insertIndex = -1;

        int parentLevel = 0;
        String query = "select LIID,RGT,LEVEL,LSID,NAME from " + LibIndex.LIBRARY_INDEX_TABLE_NAME;
        if (li.getParent() == null) {
            query += " where LEVEL = 1";
        } else {
            query += " where PARENT = " + li.getParent();
        }
        query += " order by NAME ";
        if (isDebugging) {
            log.debug(query);
        }
        ResultSet rs = null;
        int cnt = 0;
        try {
            rs = _stmt.executeQuery(query);
            if (rs == null)
                log.error("Query Failed: " + query);
            int prevRight = -1;
            while (rs.next()) {
                int liid = rs.getInt(1); // LIID
                int r = rs.getInt(2); // RGT
                parentLevel = rs.getInt(3) - 1; // LEVEL
                String lsid = rs.getString(4); // LSID
                String n = rs.getString(5); // NAME

                int comparison = n.compareToIgnoreCase(li.getName());
                if (comparison == 0) {
                    log.debug(lsid);
                    log.debug(li.getLsid());
                    if (lsid.equals(li.getLsid())) {
                        log.debug("LSID matches");
                        throw new SQLException(
                                li.getName() + " already exists as child of parent " + li.getParent());
                    } else {
                        log.debug("LSID does not match");
                        try {
                            // Add the lsid to the Library_lsids table
                            insertLiidLsid(liid, li.getLsid());
                            return -1;
                        } catch (SQLException sqle) {
                            throw new SQLException("bummer");
                        }
                    }
                }
                if (comparison > 0) {
                    // we want to insert before this item
                    if (cnt == 0) {
                        // this is the first child
                        // use the parents' left index
                        break;
                    } else {
                        // use the right index of the previous row plus 1
                        insertIndex = prevRight + 1;
                    }
                }
                if (comparison < 0) {
                    // Go on to the next child, unless there is no next child
                    // for that case we'll set this to be the current right plus 1
                    // every pass through the loop
                    insertIndex = r + 1;
                }
                prevRight = r;
                cnt++;
            }
        } finally {
            if (rs != null) {
                rs.close();
            }
        }

        if (cnt == 0) {
            if (isDebugging)
                log.debug("No children found for parent " + li.getParent());
            // Or we're inserting at the top of the list
            if (li.getParent() == null) {
                // we're inserting at the very beginning
                insertIndex = 0;
            } else {
                String parentQuery = "SELECT LFT,LEVEL from " + LibIndex.LIBRARY_INDEX_TABLE_NAME + " WHERE LIID = "
                        + li.getParent();
                if (isDebugging) {
                    log.debug(parentQuery);
                }
                ResultSet parentResult = null;
                try {
                    parentResult = _stmt.executeQuery(parentQuery);
                    if (parentResult == null)
                        log.error("Query Failed: " + parentQuery);
                    if (parentResult.next()) {
                        insertIndex = parentResult.getInt(1) + 1;
                        parentLevel = parentResult.getInt(2);
                    }
                } finally {
                    if (parentResult != null) {
                        parentResult.close();
                    }
                }
            }
        }
        if (isDebugging)
            log.debug("return: " + insertIndex);
        li.setLevel(parentLevel + 1);
        return insertIndex;
    }

    /**
     * Insert a LibItem row but do not update the lft and rgt ordering columns.
     * This method is useful only for doing bulk inserts of many rows at a time.
     * Then after a bunch of inserts the ordering must be updated by calling the
     * refreshPreorderValues() method. For only inserting one row and having the
     * ordering updated use the LibItem.insert() method.
     * 
     * You can setOrderedInsert(true) to force insert by insert ordering in this
     * method.
     * 
     * @param li
     */
    private void insertNoOrder(LibItem li) throws SQLException {
        if (isDebugging)
            log.debug("insertNoOrder(" + li.getName() + ")");

        if (isOrderedInsert()) {
            if (isDebugging)
                log.debug("Do Ordered Insert");
            insert(li);
            return;
        }

        // else we insert without updating the preorder values
        if (isDebugging)
            log.debug("Do Unordered Insert");

        String insert = "INSERT INTO " + LibIndex.LIBRARY_INDEX_TABLE_NAME
                + " (PARENT,LFT,RGT,LEVEL,LSID,TYPE,NAME) values (";

        if (li.getParent() == null) {
            insert += "NULL"; // parent
        } else {
            insert += "" + li.getParent(); // parent
        }

        insert += "," + li.getLeft(); // left
        insert += "," + li.getRight(); // right
        insert += "," + li.getLevel(); // level

        if (li.getLsid() != null) {
            insert += ",'" + li.getLsid().toString() + "'"; // lsid
        } else {
            insert += ",NULL";
        }
        insert += "," + li.getType(); // type
        insert += ",'" + li.getName() + "'"; // name
        insert += ")";

        if (isDebugging)
            log.debug(insert);

        boolean itemInserted = false;
        try {
            int rows = _stmt.executeUpdate(insert);
            if (rows == 1) {
                itemInserted = true;
            } else {
                log.error("item was not inserted");
            }
        } catch (SQLException sqle) {
            if (isDebugging) {
                log.debug("ERROR CODE: " + sqle.getErrorCode());
                log.debug("ERROR MESSAGE: " + sqle.getMessage());
            }
            if (sqle.getErrorCode() == -104) {
                // This Name already exists under this parent
                if (li.getType() == LibIndex.TYPE_COMPONENT || li.getType() == LibIndex.TYPE_NAMED_OBJ) {

                    // Add it to the Library_LSIDS table
                    KeplerLSID lsid = li.getLsid();
                    if (lsid != null) {

                        int liid = checkIfDuplicate(li);
                        if (liid < 0) {
                            // not a duplicate
                        } else {
                            insertLiidLsid(liid, li.getLsid());
                        }

                        return;

                    } else {
                        if (isDebugging)
                            log.debug("lsid is null");
                        throw sqle;
                    }
                } else {
                    if (isDebugging)
                        log.debug("type is " + li.getType());
                    throw sqle;
                }
            } else {
                if (isDebugging)
                    log.debug("different error code");
                throw sqle;
            }
        }

        if (itemInserted) {
            // find out the auto assigned liid
            String queryNewLiid = "CALL IDENTITY();";
            ResultSet rs = null;
            try {
                rs = _stmt.executeQuery(queryNewLiid);
                if (rs == null)
                    throw new SQLException("Query Failed: " + queryNewLiid);
                if (rs.next()) {
                    int newLiid = rs.getInt(1);
                    if (!rs.wasNull()) {
                        li.setLiid(newLiid);
                        if (isDebugging)
                            log.debug("IDENTITY: " + li.getLiid());
                    } else {
                        log.error("Failed to retrieve auto assigned identity");
                    }
                }
            } finally {
                if (rs != null) {
                    rs.close();
                }
            }

            insertLiidLsid(li.getLiid(), li.getLsid());
            insertAttributes(li);

            addToSearchIndex(li);
        }

        _stmt.getConnection().commit();
    }

    /**
    * Add a new LSID to the LIBRARY_LSIDS table for the specified LIID.
    * 
    * @param liid
    * @param lsid
    * @throws SQLException
    */
    private void insertLiidLsid(int liid, KeplerLSID lsid) throws SQLException {
        if (lsid == null) {
            if (isDebugging)
                log.debug("lsid is null, skip insert into LIBRARY_LSIDS");
            return;
        }

        _getNumLIIDForLIIDAndLSIDPrepStmt.setInt(1, liid);
        _getNumLIIDForLIIDAndLSIDPrepStmt.setString(2, lsid.toString());
        if (isDebugging)
            log.debug(_getNumLIIDForLIIDAndLSIDPrepStmt);
        ResultSet rs = null;
        try {
            rs = _getNumLIIDForLIIDAndLSIDPrepStmt.executeQuery();
            if (rs.next()) {
                int cnt = rs.getInt(1);
                if (cnt <= 0) {
                    _insertIntoLibraryLSIDsPrepStmt.setInt(1, liid);
                    _insertIntoLibraryLSIDsPrepStmt.setString(2, lsid.toString());
                    if (isDebugging)
                        log.debug(_insertIntoLibraryLSIDsPrepStmt);
                    _insertIntoLibraryLSIDsPrepStmt.executeUpdate();
                } else {
                    // Already in there. ignore
                }
            }
        } finally {
            if (rs != null) {
                rs.close();
            }
        }
        _stmt.getConnection().commit();
    }

    /**
     * Add the given LibItem to the search index. If the OrderedInsert flag is
     * set to true then all of the parents of this LibItem will be add to the
     * search index for this item.
     * 
     * @param li
     * @throws SQLException
     */
    public void addToSearchIndex(LibItem li) throws SQLException {

        // Add a row for this LibItem
        Integer searchType = _searchTypeMap.get(li.getType());
        if (searchType != null) {
            _searcher.insertRow(searchType.intValue(), li.getLiid(), li.getName());
        }

        // We can only get the path to this LibItem if it has been inserted AND
        // ordered
        if (isOrderedInsert()) {
            addAllParentsToSearchIndex(li);
        }
    }

    /**
     * Add all of the parent LibItems to the search index for the given LibItem.
     * 
     * @param li
     * @throws SQLException
     */
    private void addAllParentsToSearchIndex(LibItem li) throws SQLException {
        // Add a row for every LibItem in the path to this LibItem
        Vector<LibItem> parents = getPath(li);
        for (LibItem parent : parents) {
            Integer searchType = _searchTypeMap.get(parent.getType());
            if (searchType != null) {
                _searcher.insertRow(searchType.intValue(), li.getLiid(), parent.getName());
            }
        }

    }

    /**
     * Here we map the LibItem types to the different Search types.
     */
    private void initSearchMap() {
        _searchTypeMap = new Hashtable<Integer, Integer>();
        _searchTypeMap.put(TYPE_COMPONENT, LibSearch.TYPE_NAME);
        _searchTypeMap.put(TYPE_CONCEPT, LibSearch.TYPE_ONTCLASSNAME);
        _searchTypeMap.put(TYPE_NAMED_OBJ, LibSearch.TYPE_NAME);
        _searchTypeMap.put(TYPE_ONTOLOGY, LibSearch.TYPE_ONTOLOGY);
        _searchTypeMap.put(TYPE_FOLDER, LibSearch.TYPE_FOLDERNAME);
        _searchTypeMap.put(TYPE_KARFOLDER, LibSearch.TYPE_FOLDERNAME);
        _searchTypeMap.put(TYPE_KAR, LibSearch.TYPE_KARNAME);
        _searchTypeMap.put(TYPE_LOCALREPO, LibSearch.TYPE_LOCALREPO);
    }

    public LibSearch getSearcher() {
        return _searcher;
    }

    /**
     * Sum the LFT and RGT columns and verify they are continuous integers from
     * 1 to (countItems()*2). This function should always return true after
     * finishing sql transactions for insert, update, or delete.
     * 
     * @return true if the LFT and RGT columns of the table add up
     * @throws SQLException
     */
    public boolean verifyPreorderValues() throws SQLException {
        if (isDebugging)
            log.debug(_getLftRgtSumPrepStmt);
        ResultSet rs = null;
        try {
            rs = _getLftRgtSumPrepStmt.executeQuery();
            if (rs == null)
                throw new SQLException("Query Failed: " + _getLftRgtSumPrepStmt);
            if (rs.next()) {
                int sumLR = rs.getInt(1);
                int cnt = countItems() * 2;
                int sumCnt = (cnt * (cnt + 1)) / 2;
                if (isDebugging)
                    log.debug(sumLR + " " + sumCnt);
                if (sumLR == sumCnt) {
                    return true;
                }
            }
        } finally {
            if (rs != null) {
                rs.close();
            }
        }
        return false;
    }

    /**
     * Refresh the lft and rgt PTT values based on the parent information and
     * ordering by name.
     */
    private void refreshPreorderValues() {
        try {
            int left = 1;
            ResultSet rs = null;
            try {
                rs = _getLIIDRootsPrepStmt.executeQuery();
                if (rs == null)
                    throw new SQLException("Query Failed: " + _getLIIDRootsPrepStmt);
                while (rs.next()) {
                    int liid = rs.getInt(1);
                    left = refreshPreorderValues(liid, left);
                }
            } finally {
                if (rs != null) {
                    rs.close();
                }
            }
        } catch (SQLException sqle) {
            sqle.printStackTrace();
        }
    }

    /**
     * Recursive function for refreshing the lft and rgt PTT values of a given
     * parent LIID starting at the given LEFT value.
     * 
     * @param parent
     * @param left
     * @return
     * @throws SQLException
     */
    private int refreshPreorderValues(int parent, int left) throws SQLException {
        int right = left + 1;

        // get all children of this node
        _getLIIDForParentPrepStmt.setInt(1, parent);
        ResultSet rs = null;
        try {
            rs = _getLIIDForParentPrepStmt.executeQuery();
            while (rs.next()) {
                int p = rs.getInt(1);
                right = refreshPreorderValues(p, right);
            }
        } finally {
            if (rs != null) {
                rs.close();
            }
        }

        // UPDATE LIBRARY_INDEX SET LFT=?, RGT=? WHERE LIID=?
        _updateOrderPrepStmt.clearParameters();
        _updateOrderPrepStmt.setInt(1, left);
        _updateOrderPrepStmt.setInt(2, right);
        _updateOrderPrepStmt.setInt(3, parent);
        _updateOrderPrepStmt.executeUpdate();

        // return the right value of this node + 1
        return right + 1;
    }

    /**
     * Returns a string that uniquely identifies a KAR entry using the full path
     * to the KAR file and the name of the entry as it appears in the KAR
     * manifest.
     * 
     * @param karFile
     * @param entry
     * @return
     */
    private String getKarEntryPath(File karFile, String entry) {
        String fullPath = karFile.toString();
        if (!fullPath.endsWith(File.separator) && !entry.startsWith(File.separator)) {
            fullPath += File.separator + entry;
        } else {
            fullPath += entry;
        }
        return fullPath;
    }

    /**
     * This will find any folder or file kar entry.
     * 
     * @param karFile
     * @param path
     * @return
     */
    private LibItem findKarEntry(File karFile, String path) throws SQLException {
        LibItem li = null;

        _getLIIDForKarEntryPrepStmt.setString(1, getKarEntryPath(karFile, path));
        if (isDebugging)
            log.debug(_getLIIDForKarEntryPrepStmt);
        ResultSet rs = null;
        try {
            rs = _getLIIDForKarEntryPrepStmt.executeQuery();
            if (rs == null)
                throw new SQLException("Query Failed: " + _getLIIDForKarEntryPrepStmt);
            if (rs.next()) {
                int liid = rs.getInt(1);
                if (!rs.wasNull()) {
                    li = _libraryManager.getPopulatedLibItem(liid);
                }
                if (rs.next()) {
                    log.error("LIBRARY_INDEX table is corrupt: " + "Multiples of KAREntry " + path + " found");
                }
            }
        } finally {
            if (rs != null) {
                rs.close();
            }
        }
        return li;
    }

    /**
     * Given the KARCached object and the
     * 
     * @param kc
     * @param path
     * @return
     */
    private LibItem assureKarFolder(KARCached kc, String path) {
        File karFile = kc.getFile();
        LibItem li = null;
        try {
            li = findKarEntry(karFile, path);
            if (li == null) {
                LibItem parent = null;
                String[] pathRep = path.split("/");
                if (pathRep.length > 1) {
                    String parentPath = new String();
                    for (int i = 0; i < pathRep.length - 1; i++) {
                        parentPath += pathRep[i] + "/";
                    }
                    parent = assureKarFolder(kc, parentPath);
                    if (parent == null) {
                        throw new Exception("Unable to find or create " + getKarEntryPath(karFile, parentPath));
                    }
                } else {
                    parent = assureKar(kc);
                    if (parent == null) {
                        throw new Exception("Unable to find or create " + karFile);
                    }
                }
                li = new LibItem();
                li.setName(pathRep[pathRep.length - 1]);
                li.setType(LibIndex.TYPE_KARFOLDER);
                li.setParent(parent.getLiid());
                li.setLevel(parent.getLevel() + 1);
                li.setLsid(null); // folders don't have LSIDs!
                li.addAttribute(ATT_KARENTRYPATH, getKarEntryPath(karFile, path));
                insertNoOrder(li);
            }
        } catch (Exception e) {
            log.error("Unable to assureKarFolder(" + karFile.toString() + "," + path + ")");
            e.printStackTrace();
        }
        return li;
    }

    /**
     * 
     * @param kce
     * @return
     * @throws SQLException
     */
    private LibItem findKarError(KARCacheError kce) throws SQLException {
        LibItem li = null;

        File karFile = kce.getFile();
        _getLIIDForKarErrorPrepStmt.setString(1, karFile.toString());
        if (isDebugging)
            log.debug(_getLIIDForKarErrorPrepStmt);
        ResultSet rs = null;
        try {
            rs = _getLIIDForKarErrorPrepStmt.executeQuery();
            if (rs == null)
                throw new SQLException("Query Failed: " + _getLIIDForKarErrorPrepStmt);
            if (rs.next()) {
                int liid = rs.getInt(1);
                if (rs.wasNull()) {
                    li = _libraryManager.getPopulatedLibItem(liid);
                }
                if (rs.next()) {
                    log.error("LIBRARY_INDEX table is corrupt: " + "multiples of KAR Error " + karFile + " found");
                }
                return li;
            }
        } finally {
            if (rs != null) {
                rs.close();
            }
        }

        return li;
    }

    /**
     * 
     * @param kce
     * @return
     */
    private LibItem assureKarError(KARCacheError kce) {
        File karFile = kce.getFile();
        LibItem li = null;
        try {
            li = findKarError(kce);

            if (li == null) {
                LibItem parent = null;
                File parentFile = karFile.getParentFile();
                LocalRepositoryManager lrm = LocalRepositoryManager.getInstance();
                LocalRepository repo = lrm.getRepositoryForFile(parentFile);
                if (repo != null) {
                    parent = assureLocalRepository(repo);
                } else {
                    parent = assureFolder(parentFile);
                }
                if (parent == null)
                    throw new Exception();

                li = new LibItem();
                li.setName(kce.getName());
                li.setType(LibIndex.TYPE_KAR_ERROR);
                li.setParent(parent.getLiid());
                li.setLevel(parent.getLevel() + 1);
                li.setLsid(kce.getLsid());
                li.addAttribute(ATT_KARFILE, karFile.toString());
                insertNoOrder(li);
            }

        } catch (Exception e) {
            log.error("Unable to assureKarError(" + kce.getFile().toString() + ")");
            e.printStackTrace();
        }
        return li;
    }

    /**
     * @param karFile
     * @return LibItem that represents the given KAR File or null if the given
     *         KAR File does not correspond to an entry in the Library Index.
     * @throws SQLException
     */
    public LibItem findKar(File karFile) throws SQLException {
        LibItem li = null;
        _getLIIDForKarPrepStmt.setString(1, karFile.toString());
        if (isDebugging)
            log.debug(_getLIIDForKarPrepStmt);
        ResultSet rs = null;
        try {
            rs = _getLIIDForKarPrepStmt.executeQuery();
            if (rs == null)
                throw new SQLException("Query Failed: " + _getLIIDForKarPrepStmt);
            if (rs.next()) {
                int liid = rs.getInt(1);
                if (!rs.wasNull()) {
                    li = _libraryManager.getPopulatedLibItem(liid);
                }
                if (rs.next()) {
                    log.error("LIBRARY_INDEX table is corrupt: " + " multiples of KAR " + karFile + " found");
                }
            }
        } finally {
            if (rs != null) {
                rs.close();
            }
        }
        return li;
    }

    /**
     * 
     * @param kc
     * @return
     */
    private LibItem assureKar(KARCached kc) {
        File karFile = kc.getFile();
        LibItem li = null;
        try {
            li = findKar(karFile);
            if (li != null) {
                return li;
            }

            LibItem parent = null;
            File parentFile = karFile.getParentFile();
            LocalRepositoryManager lrm = LocalRepositoryManager.getInstance();
            LocalRepository repo = lrm.getRepositoryForFile(parentFile);
            if (repo != null) {
                parent = assureLocalRepository(repo);
            } else {
                parent = assureFolder(parentFile);
            }
            if (parent == null)
                throw new Exception();

            li = new LibItem();
            li.setName(kc.getName());
            li.setType(LibIndex.TYPE_KAR);
            li.setParent(parent.getLiid());
            li.setLevel(parent.getLevel() + 1);
            li.setLsid(kc.getLsid());
            li.addAttribute(ATT_KARFILE, karFile.toString());
            insertNoOrder(li);

            // see if this kar belongs to a demo
            ModuleTree tree = ModuleTree.instance();
            DotKeplerManager dkm = DotKeplerManager.getInstance();
            for (Module module : tree.getModuleList()) {
                String moduleName = module.getName();
                String moduleWorkflowsDir = dkm.getPersistentModuleWorkflowsDir(moduleName).getAbsolutePath();
                String moduleDemosDir = moduleWorkflowsDir + File.separator + "demos";

                if (karFile.getAbsolutePath().startsWith(moduleDemosDir)) {

                    parent = assureDemoFolder(parentFile);
                    li = new LibItem();
                    li.setName(kc.getName());
                    li.setType(TYPE_KAR);
                    li.setParent(parent.getLiid());
                    li.setLevel(parent.getLevel() + 1);
                    li.setLsid(kc.getLsid());
                    li.addAttribute(ATT_KARFILE, karFile.toString());
                    insertNoOrder(li);
                }
            }

        } catch (Exception e) {
            log.error("Unable to assureKar(" + kc.getFile().toString() + ")");
            e.printStackTrace();
        }
        return li;
    }

    /**
      * @param file
      * @return LibItem that represents the given XML File or null if the given
      *         XML File does not correspond to an entry in the Library Index.
      * @throws SQLException
      */
    public LibItem findXML(File file) throws SQLException {
        LibItem li = null;
        _getLIIDForXMLPrepStmt.setString(1, file.toString());
        if (isDebugging)
            log.debug(_getLIIDForXMLPrepStmt);
        ResultSet rs = null;
        try {
            rs = _getLIIDForXMLPrepStmt.executeQuery();
            if (rs == null)
                throw new SQLException("Query Failed: " + _getLIIDForXMLPrepStmt);
            if (rs.next()) {
                int liid = rs.getInt(1);
                if (!rs.wasNull()) {
                    li = _libraryManager.getPopulatedLibItem(liid);
                }
                if (rs.next()) {
                    log.error("LIBRARY_INDEX table is corrupt: " + " multiples of XML " + file + " found");
                }
            }
        } finally {
            if (rs != null) {
                rs.close();
            }
        }
        return li;
    }

    /**
      * 
      * @param kc
      * @return
      */
    public LibItem assureXML(File file) {
        LibItem li = null;
        try {
            li = findXML(file);
            if (li != null) {
                return li;
            }

            LibItem parent = null;
            File parentFile = file.getParentFile();
            LocalRepositoryManager lrm = LocalRepositoryManager.getInstance();
            LocalRepository repo = lrm.getRepositoryForFile(parentFile);
            if (repo != null) {
                parent = assureLocalRepository(repo);
            } else {
                parent = assureFolder(parentFile);
            }
            if (parent == null)
                throw new Exception();

            KeplerActorMetadata metadata = null;
            KeplerLSID lsid = null;
            try {
                metadata = KeplerMetadataExtractor.extractActorMetadata(file, false);
            } catch (Exception e) {
                System.err.println("Error parsing " + file + ": " + e.getMessage());
                return null;
            }
            if (metadata == null) {
                return null;
            }

            String className = metadata.getClassName();

            li = new LibItem();
            li.setName(file.getName());
            li.setType(TYPE_COMPONENT);
            li.setParent(parent.getLiid());
            li.setLevel(parent.getLevel() + 1);
            li.setLsid(lsid);
            li.addAttribute(ATT_XMLFILE, file.toString());
            if (className != null) {
                li.addAttribute(ATT_CLASSNAME, className);
            }
            insertNoOrder(li);

            // see if this kar belongs to a demo
            ModuleTree tree = ModuleTree.instance();
            DotKeplerManager dkm = DotKeplerManager.getInstance();
            for (Module module : tree.getModuleList()) {
                String moduleName = module.getName();
                String moduleWorkflowsDir = dkm.getPersistentModuleWorkflowsDir(moduleName).getAbsolutePath();
                String moduleDemosDir = moduleWorkflowsDir + File.separator + "demos";

                if (file.getAbsolutePath().startsWith(moduleDemosDir)) {

                    parent = assureDemoFolder(parentFile);
                    li = new LibItem();
                    li.setName(file.getName());
                    li.setType(TYPE_COMPONENT);
                    li.setParent(parent.getLiid());
                    li.setLevel(parent.getLevel() + 1);
                    li.setLsid(lsid);
                    li.addAttribute(ATT_XMLFILE, file.toString());
                    if (className != null) {
                        li.addAttribute(ATT_CLASSNAME, className);
                    }
                    insertNoOrder(li);
                }
            }

        } catch (Exception e) {
            log.error("Unable to assureXML(" + file.toString() + ")");
            e.printStackTrace();
        }
        return li;
    }

    private LibItem findFolder(String folder) throws SQLException {
        LibItem li = null;
        _getLIIDForFolderPrepStmt.setString(1, folder);
        if (isDebugging)
            log.debug(_getLIIDForFolderPrepStmt);
        ResultSet rs = null;
        try {
            rs = _getLIIDForFolderPrepStmt.executeQuery();
            if (rs == null)
                throw new SQLException("Query Failed: " + _getLIIDForFolderPrepStmt);
            if (rs.next()) {
                int liid = rs.getInt(1);
                if (!rs.wasNull()) {
                    li = _libraryManager.getPopulatedLibItem(liid);
                }
                if (rs.next()) {
                    log.error("LIBRARY_INDEX table is corrupt: " + "multiples of folder " + folder + " found");
                }
                return li;
            }
        } finally {
            if (rs != null) {
                rs.close();
            }
        }
        return li;
    }

    private LibItem assureDemoFolder(File folder) {
        LibItem li = null;
        try {

            li = findFolder(_demosFolderItem + ":" + folder.toString());
            if (li == null) {

                if (_demosFolderItem == null) {
                    _demosFolderItem = new LibItem();
                    _demosFolderItem.setName("Demos");
                    _demosFolderItem.setType(TYPE_FOLDER);
                    _demosFolderItem.setParent(null);
                    _demosFolderItem.setLevel(ROOT_LEVEL);
                    _demosFolderItem.setLsid(null);
                    insertNoOrder(_demosFolderItem);
                }

                LibItem parent = null;

                String path = folder.getAbsolutePath();
                Matcher matcher = _demosFolderPattern.matcher(path);

                if (path.equals(DotKeplerManager.getInstance().getPersistentModuleWorkflowsDir().getPath())) {
                    return _demosFolderItem;
                } else if (matcher.find() && path.endsWith("demos")) {
                    if (matcher.group(1).startsWith("outreach")) {
                        return _demosFolderItem;
                    } else {
                        return assureDemoFolder(folder.getParentFile());
                    }
                } else {
                    parent = assureDemoFolder(folder.getParentFile());
                }

                // remove the version from the name, if present.
                final String unversionedName = Version.stem(folder.getName());
                final String name = LocalRepositoryManager.getLocalRepositoryName(unversionedName);

                li = new LibItem();
                li.setName(name);
                li.setType(LibIndex.TYPE_FOLDER);
                li.setParent(parent.getLiid());
                li.setLevel(parent.getLevel() + 1);
                li.setLsid(null); // folders don't have LSIDs!
                li.addAttribute(ATT_FOLDER, _demosFolderItem + ":" + folder.toString());
                insertNoOrder(li);
            }
        } catch (Exception e) {
            log.error("Unable to assureFolder(" + folder.toString() + ") rooted at " + _demosFolderItem.getName());
            e.printStackTrace();
        }
        return li;
    }

    /**
     * 
     * @param folder
     * @return
     */
    private LibItem assureFolder(File folder) {
        LibItem li = null;
        try {
            li = findFolder(folder.toString());

            if (li == null) {
                // determine the parent LibItem
                LibItem parent = null;

                LocalRepositoryManager lrm = LocalRepositoryManager.getInstance();
                LocalRepository repo = lrm.getRepositoryForFile(folder);
                if (repo != null) {
                    if (isDebugging)
                        log.debug(folder + " is a repository");
                    parent = assureLocalRepository(repo);
                } else {
                    repo = lrm.getContainingLocalRepository(folder);
                    if (repo == null) {
                        throw new Exception(folder + " is not in a local repository");
                    }
                    File parentFile = folder.getParentFile();
                    if (isDebugging)
                        log.debug(repo + " - " + parentFile);
                    if (repo.isFileRepoDirectory(parentFile)) {
                        parent = assureLocalRepository(repo);
                    } else {
                        parent = assureFolder(parentFile);
                    }

                    // insert a new LibItem for this folder
                    li = new LibItem();
                    li.setName(folder.getName());
                    li.setType(LibIndex.TYPE_FOLDER);
                    li.setParent(parent.getLiid());
                    li.setLevel(parent.getLevel() + 1);
                    li.setLsid(null); // folders don't have LSIDs!
                    li.addAttribute(ATT_FOLDER, folder.toString());
                    insertNoOrder(li);
                }
            }
        } catch (Exception e) {
            log.error("Unable to assureFolder(" + folder.toString() + ")");
            e.printStackTrace();
        }
        return li;
    }

    private LibItem findLocalRepository(LocalRepository repo) throws SQLException {
        LibItem li = null;
        _getLIIDForRepositoryPrepStmt.setString(1, repo.toString());
        if (isDebugging)
            log.debug(_getLIIDForRepositoryPrepStmt);
        ResultSet rs = null;
        try {
            rs = _getLIIDForRepositoryPrepStmt.executeQuery();
            if (rs == null)
                throw new SQLException("Query Failed: " + _getLIIDForRepositoryPrepStmt);
            if (rs.next()) {
                int liid = rs.getInt(1);
                if (!rs.wasNull()) {
                    li = _libraryManager.getPopulatedLibItem(liid);
                }
                if (rs.next()) {
                    log.error("LIBRARY_INDEX table is corrupt: " + " multiples of local repository " + repo
                            + " found");
                }
                return li;
            }
        } finally {
            if (rs != null) {
                rs.close();
            }
        }
        return li;
    }

    /**
     * 
     * @param repo
     * @return
     */
    private LibItem assureLocalRepository(LocalRepository repo) {
        LibItem li = null;

        try {
            li = findLocalRepository(repo);

            if (li == null) {
                // insert a new LibItem row for this Local Repository
                LocalRepositoryManager lrm = LocalRepositoryManager.getInstance();
                String repoName = lrm.getLocalRepositories().get(repo);

                li = new LibItem();
                li.setName(repoName);
                li.setType(LibIndex.TYPE_LOCALREPO);
                li.setParent(null);
                li.setLevel(ROOT_LEVEL);
                li.setLsid(null); // repos don't have LSIDs!
                li.addAttribute(ATT_REPOPATH, repo.toString());
                insertNoOrder(li);
            }
        } catch (Exception e) {
            log.error("Unable to assureLocalRepository(" + repo.toString() + ")");
            e.printStackTrace();
        }
        return li;
    }

    /**
     * 
     * @param ontologyName
     * @return
     * @throws SQLException
     */
    private LibItem findOntology(String ontologyName) throws SQLException {
        LibItem li = null;
        _getLIIDForOntologyNamePrepStmt.setString(1, ontologyName);
        if (isDebugging)
            log.debug(_getLIIDForOntologyNamePrepStmt);
        ResultSet rs = null;
        try {
            rs = _getLIIDForOntologyNamePrepStmt.executeQuery();
            if (rs == null)
                throw new SQLException("Query Failed: " + _getLIIDForOntologyNamePrepStmt);
            if (rs.next()) {
                int liid = rs.getInt(1);
                if (isDebugging)
                    log.debug(liid + " is already in the index table");
                if (!rs.wasNull()) {
                    li = _libraryManager.getPopulatedLibItem(liid);
                }
                if (rs.next()) {
                    log.error("LIBRARY_INDEX table is corrupt: " + "multiples of ontology " + ontologyName
                            + " found");
                }
            }
        } finally {
            if (rs != null) {
                rs.close();
            }
        }
        return li;
    }

    /**
     * Return the LibItem for the given Ontology Name. If no LibItem exists in
     * the LIBRARY_INDEX then insert a new row for it and return a LibItem
     * representation of the row.
     * 
     * @param ontologyName
     * @return
     */
    private LibItem assureOntology(String ontologyName) {
        LibItem li = null;

        try {
            li = findOntology(ontologyName);

            if (li == null) {
                if (isDebugging)
                    log.debug(ontologyName + " is not in the index table");
                li = new LibItem();
                li.setName(ontologyName);
                li.setType(LibIndex.TYPE_ONTOLOGY);
                li.setParent(null);
                li.setLevel(1);
                insertNoOrder(li);
            }
        } catch (Exception e) {
            log.error("Unable to assureOntology(" + ontologyName + ")");
            e.printStackTrace();
        }
        return li;
    }

    /**
     * 
     * @param noc
     * @return
     * @throws SQLException
     */
    private Vector<LibItem> findOntClass(NamedOntClass noc) throws SQLException {
        Vector<LibItem> items = new Vector<LibItem>();
        _getLIIDForOntologyClassPrepStmt.setString(1, noc.getConceptId());
        if (isDebugging)
            log.debug(_getLIIDForOntologyClassPrepStmt);
        ResultSet rs = null;
        try {
            rs = _getLIIDForOntologyClassPrepStmt.executeQuery();
            if (rs == null)
                log.error("Query Failed: " + _getLIIDForOntologyClassPrepStmt);
            while (rs.next()) {
                int liid = rs.getInt(1);
                if (isDebugging)
                    log.debug(liid + " is already in the index table");
                if (!rs.wasNull()) {
                    LibItem li = _libraryManager.getPopulatedLibItem(liid);
                    items.add(li);
                }
            }
        } finally {
            if (rs != null) {
                rs.close();
            }
        }
        return items;
    }

    /**
     * Return a Vector of LibItem objects from the Library_Index that correlate
     * to the given NamedOntClass. If none exist then insert.
     * 
     * TODO: there is likely a serious flaw here, check into it...
     * 
     * @param noc
     * @return
     */
    private Vector<LibItem> assureOntClass(NamedOntClass noc) {
        if (isDebugging)
            log.debug(noc.getName());
        Vector<LibItem> items = null;
        try {

            items = findOntClass(noc);

            if (items.size() <= 0) {
                if (isDebugging)
                    log.debug("needs to be added to the index table");
                Iterator<NamedOntClass> parents = noc.getNamedSuperClasses(false);
                while (parents.hasNext()) {
                    NamedOntClass parent = parents.next();
                    Vector<LibItem> parentItems = assureOntClass(parent);
                    for (LibItem parentItem : parentItems) {
                        LibItem newli = new LibItem();
                        newli.setName(noc.getName());
                        newli.setLsid(new KeplerLSID(noc.getConceptId()));
                        newli.setParent(parentItem.getLiid());
                        newli.setType(LibIndex.TYPE_CONCEPT);
                        newli.setLevel(parentItem.getLevel() + 1);
                        insertNoOrder(newli);
                        items.add(newli);
                    }
                }
                if (items.size() <= 0) {
                    if (isDebugging)
                        log.debug("no super classes, add the ontology");
                    LibItem parentOnt = assureOntology(noc.getOntologyName());

                    LibItem newli = new LibItem();
                    newli.setName(noc.getName());
                    newli.setLsid(new KeplerLSID(noc.getConceptId()));
                    newli.setParent(parentOnt.getLiid());
                    newli.setType(LibIndex.TYPE_CONCEPT);
                    newli.setLevel(parentOnt.getLevel() + 1);
                    insertNoOrder(newli);
                    items.add(newli);
                }
            }
        } catch (Exception e) {
            log.error("Unable to assureOntClass(" + noc.toString() + ")");
            e.printStackTrace();
        }
        return items;
    }

    /**
     * Return true if there is a child of the parent in the index with the given
     * name.
     * 
     * @param parentLiid
     * @param childName
     * @return
     */
    public int childExists(Integer parentLiid, String childName) {
        int liidOfExistingChild = -1;
        try {
            PreparedStatement query;
            if (parentLiid == null) {
                query = _getLIIDForNullParentAndNamePrepStmt;
                query.setString(1, childName);
            } else {
                query = _getLIIDForParentAndNamePrepStmt;
                query.setInt(1, parentLiid.intValue());
                query.setString(2, childName);
            }

            if (isDebugging)
                log.debug(query);
            ResultSet rs = null;
            try {
                rs = query.executeQuery();
                if (rs == null)
                    throw new SQLException("Query Failed: " + query);
                if (rs.next()) {
                    liidOfExistingChild = rs.getInt(1);
                }
            } finally {
                if (rs != null) {
                    rs.close();
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return liidOfExistingChild;
    }

}