Project: Clotho-Core
/*
Copyright (c) 2009 The Regents of the University of California. 
All rights reserved. 
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.clothocad.hibernate; 
 
import java.io.File; 
import java.net.URL; 
import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.Collection; 
import java.util.Date; 
import java.util.Iterator; 
import java.util.List; 
import java.util.Set; 
import java.util.UUID; 
import javax.swing.filechooser.FileFilter; 
import org.clothocore.api.data.ObjBase.ObjBaseDatum; 
import org.clothocad.hibernate.data.*; 
import org.clothocore.api.plugin.ClothoConnection; 
import org.clothocore.api.data.*; 
import org.hibernate.Criteria; 
import org.hibernate.HibernateException; 
import org.hibernate.Query; 
import org.hibernate.Session; 
import org.hibernate.SessionFactory; 
import org.hibernate.Transaction; 
import org.hibernate.cfg.Configuration; 
import org.hibernate.criterion.Conjunction; 
import org.hibernate.criterion.Criterion; 
import org.hibernate.criterion.Disjunction; 
import org.hibernate.criterion.Restrictions; 
import org.openide.filesystems.FileObject; 
import org.openide.filesystems.FileUtil; 
 
/**
 * 
 * @author Bing Xia 
 */
 
public class HibernateConnection implements ClothoConnection { 
 
    public HibernateConnection() { 
    } 
 
    @Override 
    public boolean connect() { 
        return false
    } 
 
    @Override 
    public boolean isAClothoDatabase() { 
        return true
    } 
 
    public static List<FileObject> getDefaultMappings() { 
        FileObject mappingDir = FileUtil.getConfigFile( "data/private/org.clothocad.hibernate" ); 
        return Arrays.asList( mappingDir.getChildren() ); 
    } 
 
    public boolean connect( URL hibernateXML, List<URL> mappings ) { 
        // Create a new configuration and configures it. 
        Configuration config = new Configuration(); 
        for ( URL map : mappings ) { 
            config.addURL( map ); 
        } 
 
        try { 
            // Attempt to create a session factory from the configuration. 
            // As long as this session factory is active, the connection is 
            // defined as active. 
            config.configure( hibernateXML ); 
            fac = config.buildSessionFactory(); 
            HibernateConnection.connection = this
            return true
        } catch ( Throwable e ) { 
            System.err.println( e ); 
            e.printStackTrace(); 
            fac = null
            return false
        } 
    } 
 
    @Override 
    public boolean disconnect() { 
        try { 
            fac.close(); 
            return true
        } catch ( Throwable e ) { 
            e.printStackTrace(); 
            System.err.println( e ); 
            return false
        } finally { 
            fac = null
        } 
    } 
 
    @Override 
    public boolean isConnected() { 
        return fac != null
    } 
 
    @Override 
    public boolean save( ObjBase obj ) { 
        SearchtagTable.updateSearchTags( obj );  //JCA:  Relays to save search tags for object 
        if ( obj.getType() == ObjType.PART ) { 
            //TODO: figure out a better way...if there is one 
            return savePart( (Part) obj ); 
        } 
        Session session = null
        Transaction transaction = null
        try { 
            session = fac.openSession(); 
            Object toSave = getHibernateDatum( obj, session );  //This will instantiate the objbaseTable constructor 
            if ( toSave == null ) { 
                return false
            } 
 
            //Do the save from the hibernateDatum constructor 
            transaction = session.beginTransaction(); 
            hibernateDatum d = (hibernateDatum) toSave; 
            System.out.println( "hibernateconnectionplugin will save" + d.toString() + "  " + d.getName() + " " + d.getUUID() ); // FIXME: remove in final version 
            session.saveOrUpdate( toSave ); 
            transaction.commit(); 
 
            //Do any secondary processing saves for new objBase's xrefs 
            if(d.needsSecondaryProcessing()) { 
                transaction = session.beginTransaction(); 
                d.runSecondaryProcessing(obj); 
                session.saveOrUpdate( toSave ); 
                transaction.commit(); 
            } 
             
            session.close(); 
            activeSession = null
            return true
        } catch ( RuntimeException e ) { 
            e.printStackTrace(); 
            System.err.println( "Commit failed!" ); 
            if ( transaction != null && transaction.isActive() ) { 
                try { 
                    transaction.rollback(); 
                } catch ( HibernateException e1 ) { 
                    System.err.println( "Rollback failed" ); 
                } 
            } 
        } 
        return false
    } 
 
    /**
     * For saving a Part, since the order of operations for this save 
     * doesn't fit well into the normal save method. 
     * @param pt 
     * @return 
     */
 
    boolean savePart( Part pt ) { 
        Session s = null
        Transaction t = null
        try { 
            List<String> newXrefs = pt.getComposition(); 
            s = fac.openSession(); 
 
            if ( pt.getPartType() == Part.partType.Composite ) { 
 
                PartTable existingPart = (PartTable) getHibernateDatum( ObjType.PART, pt.getUUID(), s ); 
 
                if ( existingPart != null ) { 
                    // Part already exists 
                    // Check if all the xrefs match perfectly 
                    boolean changed = false
                    Set<CompositeXref> curXrefs = existingPart.getCompositeXrefsForChildPart(); 
 
                    System.out.println( curXrefs.size() + " " + newXrefs.size() ); 
                    if ( curXrefs.size() == newXrefs.size() ) { 
                        String[] curXrefUUIDs = new String[ curXrefs.size() ]; 
                        for ( CompositeXref xr : curXrefs ) { 
                            String parentUUID = xr.getPartTableByParentPart().getUUID(); 
                            curXrefUUIDs[xr.getPosition()] = parentUUID; 
                        } 
 
                        for ( int i = 0; i < curXrefUUIDs.length; i++ ) { 
                            if ( !newXrefs.get( i ).equals( curXrefUUIDs[i] ) ) { 
                                changed = true
                            } 
                        } 
                    } else { 
                        changed = true
                    } 
 
                    if ( changed ) { 
                        System.out.println( "changed" ); 
                        for ( CompositeXref xr : curXrefs ) { 
                            System.out.println( "deleting " + xr ); 
                            t = s.beginTransaction(); 
                            s.delete( xr ); 
                            t.commit(); 
                        } 
 
                        for ( int i = 0; i < newXrefs.size(); i++ ) { 
                            String uuid = newXrefs.get( i ); 
                            CompositeXref cxr = new CompositeXref( UUID.randomUUID().toString(), new PartTable( uuid ), existingPart, i ); 
                            t = s.beginTransaction(); 
                            s.save( cxr ); 
                            t.commit(); 
                        } 
                    } 
                } else { 
                    // Part doesn't exist yet...have to save it, then save the xrefs 
                    t = s.beginTransaction(); 
                    PartTable toSave = new PartTable( pt ); 
                    s.save( toSave ); 
                    t.commit(); 
 
                    // Save the xrefs 
 
                    for ( int i = 0; i < newXrefs.size(); i++ ) { 
                        CompositeXref xr = new CompositeXref( UUID.randomUUID().toString(), new PartTable( newXrefs.get( i ) ), toSave, i ); 
                        t = s.beginTransaction(); 
                        s.save( xr ); 
                        t.commit(); 
                    } 
                } 
 
            } else { 
                // Not a composite, can save much more simply 
                PartTable toSave = new PartTable( pt ); 
                t = s.beginTransaction(); 
                s.saveOrUpdate( toSave ); 
                t.commit(); 
            } 
            s.close(); 
 
            return true
        } catch ( RuntimeException e ) { 
            e.printStackTrace(); 
            System.err.println( "Commit failed!" ); 
            if ( t != null && t.isActive() ) { 
                try { 
                    t.rollback(); 
                } catch ( HibernateException e1 ) { 
                    System.err.println( "Rollback failed" ); 
                } 
            } 
        } 
 
        return false
    } 
 
    public boolean saveDatum( Object toSave ) { 
        Session s = null
        Transaction t = null
        try { 
            s = fac.openSession(); 
            t = s.beginTransaction(); 
            if ( toSave != null ) { 
                s.saveOrUpdate( toSave ); 
            } else { 
                return false
            } 
            t.commit(); 
            s.close(); 
            return true
        } catch ( RuntimeException e ) { 
            e.printStackTrace(); 
            System.err.println( "Commit failed!" ); 
            if ( t != null && t.isActive() ) { 
                try { 
                    t.rollback(); 
                } catch ( HibernateException e1 ) { 
                    System.err.println( "Rollback failed" ); 
                } 
            } 
        } 
        return false
    } 
 
    public boolean deleteDatum( Object toDelete ) { 
        Session s = null
        Transaction t = null
        try { 
            boolean startedsession = false;; 
            if(activeSession!=null) { 
                s = activeSession; 
            } else { 
                s = fac.openSession(); 
                startedsession = true
            } 
            t = s.beginTransaction(); 
            if ( toDelete != null ) { 
                s.delete( toDelete ); 
            } else { 
                return false
            } 
            t.commit(); 
            if(startedsession) { 
                s.close(); 
            } 
            return true
        } catch ( RuntimeException e ) { 
            e.printStackTrace(); 
            System.err.println( "Commit failed!" ); 
            if ( t != null && t.isActive() ) { 
                try { 
                    t.rollback(); 
                } catch ( HibernateException e1 ) { 
                    System.err.println( "Rollback failed" ); 
                } 
            } 
        } 
        return false
    } 
 
    @Override 
    public int save( Collection<ObjBase> objs ) { 
        Session session = null
        Transaction transaction = null
        int succeeded = 0
        try { 
            session = fac.openSession(); 
            for ( ObjBase obj : objs ) { 
                hibernateDatum toSave = getHibernateDatum( obj, session ); 
                if ( toSave != null ) { 
                    transaction = session.beginTransaction(); 
                    System.out.println( "hibernateconnectionplugin will save" + toSave.toString() + "  " + toSave.getName() + " " + toSave.getUUID() ); 
                    session.saveOrUpdate( toSave ); 
                    succeeded++; 
                    transaction.commit(); 
                } 
            } 
            session.close(); 
        } catch ( RuntimeException e ) { 
            e.printStackTrace(); 
            System.err.println( "Commit failed!" ); 
            if ( transaction != null && transaction.isActive() ) { 
                try { 
                    transaction.rollback(); 
                    return 0
                } catch ( HibernateException e1 ) { 
                    session.close(); 
                    System.err.println( "Rollback failed" ); 
                } 
            } 
        } 
        return succeeded; 
    } 
 
    @Override 
    public ObjBaseDatum getDatum(ObjType type, String uuid) { 
        Session s = fac.openSession(); 
        hibernateDatum data = getHibernateDatum( type, uuid, s ); 
        ObjBaseDatum out = null
        if ( data != null ) { 
            out = data.getObjBaseDatum(); 
        } else { 
            out = null
        } 
        s.close(); 
        return out; 
    } 
 
    @Override 
    public boolean delete( ObjBase obj ) { 
        System.out.println( "Delete got called" ); 
        if ( obj == null ) { 
            System.out.println( "Delete but ObjBase is null" ); 
            return false
        } 
 
        //Query for any CollectionXref links and delete them 
        String query = "from Collectionxref where objectId='" + obj.getType() + "'"
        Iterator xrefs = query( query ); 
        while ( xrefs.hasNext() ) { 
            Object xr = xrefs.next(); 
            Collectionxref csr = (Collectionxref) xr; 
            System.out.println( "Delete found an xref, and its deleting it" ); 
            HibernateConnection.connection.deleteDatum( csr ); 
        } 
 
        Session s = null
        Transaction t = null
        try { 
            System.out.println( "Delete doing a transaction" ); 
            s = fac.openSession(); 
            t = s.beginTransaction(); 
            if ( obj != null ) { 
                s.delete( getEmptyDatum( obj ) ); 
            } else { 
                return false
            } 
            t.commit(); 
            s.close(); 
            System.out.println( "Delete returning true" ); 
            return true
        } catch ( RuntimeException e ) { 
            e.printStackTrace(); 
            System.err.println( "Commit failed!" ); 
            if ( t != null && t.isActive() ) { 
                try { 
                    t.rollback(); 
                } catch ( HibernateException e1 ) { 
                    System.err.println( "Rollback failed" ); 
                } 
            } 
        } 
        System.out.println( "Delete returning false" ); 
        return false
    } 
 
    @Override 
    public int delete( Collection<ObjBase> objs ) { 
        Session session = null
        Transaction transaction = null
        int succeeded = 0
        try { 
            session = fac.openSession(); 
            transaction = session.beginTransaction(); 
            for ( ObjBase obj : objs ) { 
                Object toDelete = getEmptyDatum( obj ); 
                if ( toDelete != null ) { 
                    session.delete( toDelete ); 
                    succeeded++; 
                } 
            } 
            transaction.commit(); 
            session.close(); 
        } catch ( RuntimeException e ) { 
            e.printStackTrace(); 
            System.err.println( "Commit failed!" ); 
            if ( transaction != null && transaction.isActive() ) { 
                try { 
                    transaction.rollback(); 
                    return 0
                } catch ( HibernateException e1 ) { 
                    session.close(); 
                    System.err.println( "Rollback failed" ); 
                } 
            } 
        } 
        return succeeded; 
    } 
 
    /**
     * Retrieves an ObjBase object from the database using a uuid 
     * @param type 
     * @param uuid 
     * @return 
     */
 
    @Override 
    public ObjBase get( ObjType type, String uuid ) { 
        Session s = fac.openSession(); 
        hibernateDatum d = getHibernateDatum( type, uuid, s ); 
        ObjBase result = null
        if ( d != null ) { 
            result = d.getObject(); 
            SearchtagTable.pullSearchTags( result ); 
        } 
        s.close(); 
        return result; 
    } 
 
    @Override 
    public Collection<ObjBase> get( String hibQuery ) { 
        Collection<ObjBase> objs = new ArrayList<ObjBase>(); 
        Session s = fac.openSession(); 
 
        Transaction transaction = s.beginTransaction(); 
        Query q = s.createQuery( hibQuery ); 
        Iterator<hibernateDatum> results = q.iterate(); 
        while ( results.hasNext() ) { 
            hibernateDatum d = results.next(); 
            ObjBase obj = d.getObject(); 
            SearchtagTable.pullSearchTags( obj ); 
            objs.add( obj ); 
        } 
        transaction.commit(); 
        return objs; 
    } 
 
    @Override 
    public ArrayList<ObjLink> getAllLinks( ObjType type ) { 
        ArrayList<ObjLink> out = new ArrayList<ObjLink>(); 
        System.out.println( "********* Getting all " + type.toString() + ":" ); 
 
        Iterator collIT = findAll( type ); 
        while ( collIT.hasNext() ) { 
            hibernateDatum datum = (hibernateDatum) collIT.next(); 
 
            String name = datum.getName(); 
            String uuid = datum.getUUID(); 
 
            out.add( new ObjLink( uuid, type, name ) ); 
        } 
        return out; 
    } 
 
    @Override 
    public String[][] getTableAsArray( ObjType type ) { 
        ArrayList<String[]> templist = new ArrayList<String[]>(); 
 
        Iterator collIT = findAll( type ); 
        while ( collIT.hasNext() ) { 
            hibernateDatum datum = (hibernateDatum) collIT.next(); 
 
            String[] aline = new String[ 3 ]; 
            aline[0] = datum.getUUID(); 
            aline[1] = datum.getName(); 
 
            switch ( type ) { 
                case FEATURE: 
                    FeatureTable ft = (FeatureTable) datum; 
                    aline[2] = ft.getNucseqTable().getSequence(); 
                    break
                default
                    aline[2] = ""
            } 
 
            templist.add( aline ); 
        } 
 
        String[][] out = new String[ templist.size() ][ 3 ]; 
        for ( int i = 0; i < templist.size(); i++ ) { 
            out[i] = templist.get( i ); 
        } 
 
        return out; 
    } 
 
    /**
     * Returns all the objects of the given type. 
     * @param type - Object keyword 
     * @return - Iterator giving results 
     */
 
    private Iterator<hibernateDatum> findAll( ObjType type ) { 
        Session session = null
        Transaction transaction = null
        session = fac.openSession(); 
        Transaction t = session.beginTransaction(); 
        String tabletoprobe = getObjectTranslation( type ); 
        System.out.println( "Probing " + tabletoprobe ); 
 
        Query q = session.createQuery( "from " + tabletoprobe ); 
        Iterator<hibernateDatum> it = q.iterate(); 
 
        t.commit(); 
        return it; 
    } 
 
    /**
     * JCA added this.  Used for queries within the connection. 
     * @param hibQuery 
     * @return 
     */
 
    public Iterator query( String hibQuery ) { 
        Session session = null
        Transaction transaction = null
        session = fac.openSession(); 
        activeSession = session; 
        Transaction t = session.beginTransaction(); 
        Query q = session.createQuery( hibQuery ); 
        Iterator it = q.iterate(); 
        t.commit(); 
        return it; 
    } 
 
    @Override 
    public void init() { 
        // In the future, add preference loading and such 
    } 
 
    public void configure() { 
//        _configurationViewer = new ConfigurationView( this ); 
//        _configurationViewer.setVisible( true ); 
    } 
 
    @Override 
    public Date getTimeModified( ObjBase obj ) { 
        String query = ""
 
        //JCA:  I have this if here since there is a typo in the field name of nucseq.  Worth fixing eventually. 
        if ( obj.getType().equals( ObjType.NUCSEQ ) ) { 
            query = "select lastmodified from " + getObjectTranslation( obj.getType() ) + " where id='" + obj.getUUID() + "'"
        } else { 
            query = "select lastModified from " + getObjectTranslation( obj.getType() ) + " where id='" + obj.getUUID() + "'"
        } 
 
        Session session = null
        Transaction transaction = null
        try { 
            session = fac.openSession(); 
 
            transaction = session.beginTransaction(); 
            Date lastMod = (Date) session.createQuery( query ).uniqueResult(); 
            transaction.commit(); 
            session.close(); 
            return lastMod; 
        } catch ( RuntimeException e ) { 
            e.printStackTrace(); 
            return null
        } 
    } 
 
    @Override 
    public ClothoConnection.ClothoQuery createQuery( ObjType type ) { 
        return new HibernateQuery( type ); 
    } 
 
    /**
     * Use this to get a datum object built from a given ObjBase object. The 
     * resulting hibernateDatum object can be saved using hibernate. There should be one 
     * case here for each API type that is valid. 
     * @param obj 
     * @return 
     */
 
    hibernateDatum getHibernateDatum( ObjBase obj, Session s ) { 
        if ( obj == null ) { 
            return null
        } 
        switch ( obj.getType() ) { 
            case ANNOTATION: 
                return new NucseqAnnotation( (Annotation) obj ); 
            case ATTACHMENT: 
                return new AttachmentTable( (Attachment) obj ); 
            case COLLECTION: 
                return new CollectionTable( (org.clothocore.api.data.Collection) obj ); 
            case CONTAINER: 
                return new ContainerTable( (Container) obj ); 
            case FACTOID: 
                return new FactoidTable( (Factoid) obj ); 
            case FAMILY: 
                return new FamilyTable( (Family) obj ); 
            case FEATURE: 
                return new FeatureTable( (Feature) obj ); 
            case FLEX_FIELD: 
                return new FlexfieldTable( (FlexField) obj ); 
            case FORMAT: 
                return new FormatTable( (Format) obj ); 
            case GRAMMAR: 
                return new GrammarTable( (Grammar) obj ); 
            case INSTITUTION: 
                return new InstitutionTable( (Institution) obj ); 
            case LAB: 
                return new LabTable( (Lab) obj ); 
            case NUCSEQ: 
                return new NucseqTable( (NucSeq) obj ); 
            case NOTE: 
                return new NoteTable( (Note) obj ); 
            case OLIGO: 
                return new OligoTable( (Oligo) obj ); 
            case PART: 
                // ASSERT: never reached, because parts are handled differently 
                return new PartTable( (Part) obj ); 
            case PERSON: 
                return new PersonTable( (Person) obj ); 
            case PLASMID: 
                return new PlasmidTable( (Plasmid) obj ); 
            case PLATE: 
                return new PlateTable( (Plate) obj ); 
            case PLATE_TYPE: 
                return new PlateTypeTable( (PlateType) obj ); 
            case SAMPLE: 
                return new SampleTable( (Sample) obj ); 
            case SAMPLE_DATA: 
                return new SampleDataTable( (SampleData) obj ); 
            case STRAIN: 
                return new StrainTable( (Strain) obj ); 
            case WIKITEXT: 
                return new WikitextTable( (WikiText) obj ); 
            case VECTOR: 
                return new VectorTable( (Vector) obj ); 
            default
                return null
        } 
    } 
 
    /**
     * Use this to create an empty hibernateDatum object for the purpose of deleting or 
     * saving a link. The returned hibernateDatum will be of the right type and only 
     * have its UUID field filled in. 
     * @param obj 
     * @return 
     */
 
    hibernateDatum getEmptyDatum( ObjBase obj ) { 
        if ( obj == null ) { 
            return null
        } 
        switch ( obj.getType() ) { 
            case ANNOTATION: 
                return new NucseqAnnotation( obj.getUUID() ); 
            case ATTACHMENT: 
                return new AttachmentTable( obj.getUUID() ); 
            case COLLECTION: 
                return new CollectionTable( obj.getUUID() ); 
            case CONTAINER: 
                return new ContainerTable( obj.getUUID() ); 
            case FACTOID: 
                return new FactoidTable( obj.getUUID() ); 
            case FAMILY: 
                return new FamilyTable( obj.getUUID() ); 
            case FEATURE: 
                return new FeatureTable( obj.getUUID() ); 
            case FLEX_FIELD: 
                return new FlexfieldTable( obj.getUUID() ); 
            case FORMAT: 
                return new FormatTable( obj.getUUID() ); 
            case GRAMMAR: 
                return new GrammarTable( obj.getUUID() ); 
            case INSTITUTION: 
                return new InstitutionTable( obj.getUUID() ); 
            case LAB: 
                return new LabTable( obj.getUUID() ); 
            case NUCSEQ: 
                return new NucseqTable( obj.getUUID() ); 
            case NOTE: 
                return new NoteTable( obj.getUUID() ); 
            case OLIGO: 
                return new OligoTable( obj.getUUID() ); 
            case PART: 
                return new PartTable( obj.getUUID() ); 
            case PERSON: 
                return new PersonTable( obj.getUUID() ); 
            case PLASMID: 
                return new PlasmidTable( obj.getUUID() ); 
            case PLATE: 
                return new PlateTable( obj.getUUID() ); 
            case PLATE_TYPE: 
                return new PlateTypeTable( obj.getUUID() ); 
            case SAMPLE: 
                return new SampleTable( obj.getUUID() ); 
            case SAMPLE_DATA: 
                return new SampleDataTable( obj.getUUID() ); 
            case STRAIN: 
                return new StrainTable( obj.getUUID() ); 
            case WIKITEXT: 
                return new WikitextTable( obj.getUUID() ); 
            case VECTOR: 
                return new VectorTable( obj.getUUID() ); 
            default
                return null
        } 
    } 
 
    /**
     * Use this to get the datum object of the given type from the database. The 
     * resulting object can be used to create an ObjBase object to return, or to 
     * update an existing ObjBase object. 
     * 
     * openSession specifies whether this method needs to open a session, or whether 
     * a session has already been opened outside this method. 
     * @param type 
     * @param uuid 
     * @param openSession 
     * @return 
     */
 
    hibernateDatum getHibernateDatum( ObjType type, String uuid, Session session ) { 
        try { 
            boolean openedSession = false
            if ( session == null ) { 
                session = fac.openSession(); 
                openedSession = true
            } 
            Transaction transaction = session.beginTransaction(); 
            Query q = session.createQuery( "from " + getObjectTranslation( type ) + " where id='" + uuid + "'" ); 
            hibernateDatum d = (hibernateDatum) q.uniqueResult(); 
            transaction.commit(); 
            if ( openedSession ) { 
                session.close(); 
            } 
            return d; 
        } catch ( Throwable e ) { 
            System.err.println( e ); // TODO: do some error output 
            e.printStackTrace(); 
            return null
        } 
    } 
 
    /**
     * Returns a select query for the given ObjBase, assuming its in the database. 
     * Uses the uuid of the given object along with its type to build the query. 
     * @param obj 
     * @return 
     */
 
    String getSelectQuery( ObjBase obj ) { 
        String uuid = obj.getUUID(); 
        if ( uuid == null ) { 
            return null
        } 
        String typeTranslation = getObjectTranslation( obj.getType() ); 
        if ( typeTranslation == null ) { 
            return null
        } 
        return "from " + typeTranslation + " where id='" + uuid + "'"
 
    } 
 
    /**
     * Returns the database translation for the given type 
     * @param type 
     * @return 
     */
 
    @Override 
    public String getObjectTranslation( ObjType type ) { 
        switch ( type ) { 
            case ANNOTATION: 
                return "NucseqAnnotation"
            case ATTACHMENT: 
                return "AttachmentTable"
            case COLLECTION: 
                return "CollectionTable"
            case CONTAINER: 
                return "ContainerTable"
            case FACTOID: 
                return "FactoidTable"
            case FAMILY: 
                return "FamilyTable"
            case FEATURE: 
                return "FeatureTable"
            case FLEX_FIELD: 
                return "FlexFieldTable"
            case FORMAT: 
                return "FormatTable"
            case GRAMMAR: 
                return "GrammarTable"
            case INSTITUTION: 
                return "InstitutionTable"
            case LAB: 
                return "LabTable"
            case NUCSEQ: 
                return "NucseqTable"
            case NOTE: 
                return "NoteTable"
            case OLIGO: 
                return "OligoTable"
            case PART: 
                return "PartTable"
            case PERSON: 
                return "PersonTable"
            case PLASMID: 
                return "PlasmidTable"
            case PLATE: 
                return "PlateTable"
            case PLATE_TYPE: 
                return "PlateTypeTable"
            case SAMPLE: 
                return "SampleTable"
            case SAMPLE_DATA: 
                return "SampleDataTable"
            case STRAIN: 
                return "StrainTable"
            case VECTOR: 
                return "VectorTable"
            case WIKITEXT: 
                return "WikitextTable"
            default
                return null
        } 
    } 
 
    /**
     * Returns the field name of the given field type of the given object type. 
     * This isn'transaction needed except for the HibernateQuery type searching of the database. 
     * @param type 
     * @param field 
     * @return 
     */
 
    @Override 
    public String getFieldTranslation( ObjType type, Enum field ) { 
        String translation = null
        System.out.println("getfieldTranslation of " + type + field); 
        switch ( type ) { 
            case ANNOTATION: 
                translation = NucseqAnnotation.translate( field ); 
                break
            case ATTACHMENT: 
                translation = AttachmentTable.translate( field ); 
                break
            case COLLECTION: 
                translation = CollectionTable.translate( field ); 
                break
            case CONTAINER: 
                translation = ContainerTable.translate( field ); 
                break
            case FACTOID: 
                translation = FactoidTable.translate( field ); 
                break
            case FAMILY: 
                translation = FamilyTable.translate( field ); 
                break
            case FEATURE: 
                translation = FeatureTable.translate( field ); 
                break
            case FLEX_FIELD: 
                translation = FlexfieldTable.translate( field ); 
                break
            case FORMAT: 
                translation = FormatTable.translate( field ); 
                break
            case GRAMMAR: 
                translation = GrammarTable.translate( field ); 
                break
            case INSTITUTION: 
                translation = InstitutionTable.translate( field ); 
                break
            case LAB: 
                translation = LabTable.translate( field ); 
                break
            case NOTE: 
                translation = NoteTable.translate( field ); 
                break
            case NUCSEQ: 
                translation = NucseqTable.translate( field ); 
                break
            case OLIGO: 
                translation = OligoTable.translate( field ); 
                break
            case PART: 
                translation = PartTable.translate( field ); 
                break
            case PERSON: 
                translation = PersonTable.translate( field ); 
                break
            case PLASMID: 
                translation = PlasmidTable.translate( field ); 
                break
            case PLATE: 
                translation = PlateTable.translate( field ); 
                break
            case PLATE_TYPE: 
                translation = PlateTypeTable.translate( field ); 
                break
            case SAMPLE: 
                translation = SampleTable.translate( field ); 
                break
            case SAMPLE_DATA: 
                translation = SampleDataTable.translate( field ); 
                break
            case STRAIN: 
                translation = StrainTable.translate( field ); 
                break
            case VECTOR: 
                translation = VectorTable.translate( field ); 
                break
            case WIKITEXT: 
                translation = WikitextTable.translate( field ); 
                break
            default
                translation = null
        } 
 
        if ( translation == null ) { 
            // TODO: signal an error somehow, but this should never happen 
        } 
 
        return translation; 
    } 
 
    public ObjType getFieldType( ObjType type, Enum field ) { 
        switch ( type ) { 
            case ANNOTATION: 
                return NucseqAnnotation.getType( field ); 
            case ATTACHMENT: 
                return AttachmentTable.getType( field ); 
            case COLLECTION: 
                return CollectionTable.getType( field ); 
            case CONTAINER: 
                return ContainerTable.getType( field ); 
            case FACTOID: 
                return FactoidTable.getType( field ); 
            case FAMILY: 
                return FamilyTable.getType( field ); 
            case FEATURE: 
                return FeatureTable.getType( field ); 
            case FLEX_FIELD: 
                return FlexfieldTable.getType( field ); 
            case FORMAT: 
                return FormatTable.getType( field ); 
            case GRAMMAR: 
                return GrammarTable.getType( field ); 
            case INSTITUTION: 
                return InstitutionTable.getType( field ); 
            case LAB: 
                return LabTable.getType( field ); 
            case NOTE: 
                return NoteTable.getType( field ); 
            case NUCSEQ: 
                return NucseqTable.getType( field ); 
            case OLIGO: 
                return OligoTable.getType( field ); 
            case PART: 
                return PartTable.getType( field ); 
            case PERSON: 
                return PersonTable.getType( field ); 
            case PLASMID: 
                return PlasmidTable.getType( field ); 
            case PLATE: 
                return PlateTable.getType( field ); 
            case PLATE_TYPE: 
                return PlateTypeTable.getType( field ); 
            case SAMPLE: 
                return SampleTable.getType( field ); 
            case SAMPLE_DATA: 
                return SampleDataTable.getType( field ); 
            case STRAIN: 
                return StrainTable.getType( field ); 
            case VECTOR: 
                return VectorTable.getType( field ); 
            case WIKITEXT: 
                return WikitextTable.getType( field ); 
            default
                return null
        } 
    } 
    /**************** PRIVATE VARIABLES ****************/ 
    private SessionFactory fac; 
    private Session activeSession = null
    private static FileFilter configFileFilter = new FileFilter() { 
 
        public boolean accept( File pathname ) { 
            return pathname.getName().endsWith( ".cfg.xml" ); 
        } 
 
        @Override 
        public String getDescription() { 
            return "Filters for Hibernate Configuration Files"; 
        } 
    }; 
    public static HibernateConnection connection; 
 
    /* Preference enums */ 
    public enum PreferenceKeys { 
 
        CONFIG_DIRECTORY, 
    } 
 
    /***************************************************************************
     ***************************** Inner Class ********************************** 
     ***************************************************************************/
 
    class HibernateQuery implements ClothoConnection.ClothoQuery { 
 
        private Criteria criteria; 
        private ObjType type; 
        private Session session; 
 
        HibernateQuery( ObjType type ) { 
            session = fac.openSession(); 
            this.type = type; 
            switch ( type ) { 
                case ATTACHMENT: 
                    criteria = session.createCriteria( AttachmentTable.class ); 
                    break
                case COLLECTION: 
                    criteria = session.createCriteria( CollectionTable.class ); 
                    break
                case CONTAINER: 
                    criteria = session.createCriteria( ContainerTable.class ); 
                    break
                case FACTOID: 
                    criteria = session.createCriteria( FactoidTable.class ); 
                    break
                case FAMILY: 
                    criteria = session.createCriteria( FamilyTable.class ); 
                    break
                case FEATURE: 
                    criteria = session.createCriteria( FeatureTable.class ); 
                    break
                case FLEX_FIELD: 
                    criteria = session.createCriteria( FlexfieldTable.class ); 
                    break
                case FORMAT: 
                    criteria = session.createCriteria( FormatTable.class ); 
                    break
                case GRAMMAR: 
                    criteria = session.createCriteria( GrammarTable.class ); 
                    break
                case INSTITUTION: 
                    criteria = session.createCriteria( InstitutionTable.class ); 
                    break
                case LAB: 
                    criteria = session.createCriteria( LabTable.class ); 
                    break
                case NOTE: 
                    criteria = session.createCriteria( NoteTable.class ); 
                    break
                case NUCSEQ: 
                    criteria = session.createCriteria( NucseqTable.class ); 
                    break
                case OLIGO: 
                    criteria = session.createCriteria( OligoTable.class ); 
                    break
                case PART: 
                    criteria = session.createCriteria( PartTable.class ); 
                    break
                case PERSON: 
                    criteria = session.createCriteria( PersonTable.class ); 
                    break
                case PLASMID: 
                    criteria = session.createCriteria( PlasmidTable.class ); 
                    break
                case PLATE: 
                    criteria = session.createCriteria( PlateTable.class ); 
                    break
                case PLATE_TYPE: 
                    criteria = session.createCriteria( PlateTypeTable.class ); 
                    break
                case SAMPLE: 
                    criteria = session.createCriteria( SampleTable.class ); 
                    break
                case SAMPLE_DATA: 
                    criteria = session.createCriteria( SampleDataTable.class ); 
                    break
                case STRAIN: 
                    criteria = session.createCriteria( StrainTable.class ); 
                    break
                case VECTOR: 
                    criteria = session.createCriteria( VectorTable.class ); 
                    break
                case WIKITEXT: 
                    criteria = session.createCriteria( WikitextTable.class ); 
                    break
                default
                    System.err.println( "Unrecognized type" ); // TODO: Change this to a warning dialog 
            } 
        } 
 
        private HibernateQuery( ObjType t, Criteria crit ) { 
            type = t; 
            criteria = crit; 
        } 
 
        @Override 
        public ClothoConnection.ClothoQuery setMaxResultsint max ) { 
            criteria = criteria.setMaxResults( max ); 
            return this
        } 
 
        @Override 
        public ClothoConnection.ClothoQuery between( Enum field, Object hi, Object lo ) { 
            criteria = criteria.add( Restrictions.between( getFieldTranslation( type, field ), lo, hi ) ); 
            return this
        } 
 
        @Override 
        public ClothoConnection.ClothoQuery lessThan( Enum field, Object hi ) { 
            criteria = criteria.add( Restrictions.lt( getFieldTranslation( type, field ), hi ) ); 
            return this
        } 
 
        @Override 
        public ClothoConnection.ClothoQuery greaterThan( Enum field, Object lo ) { 
            criteria = criteria.add( Restrictions.gt( getFieldTranslation( type, field ), lo ) ); 
            return this
        } 
 
        @Override 
        public ClothoConnection.ClothoQuery equal( Enum field, Object value ) { 
            criteria = criteria.add( Restrictions.eq( getFieldTranslation( type, field ), value ) ); 
            return this
        } 
 
        @Override 
        public ClothoConnection.ClothoQuery eq( Enum field, Object value ) { 
            return equal( field, value ); 
        } 
 
        @Override 
        public ClothoConnection.ClothoQuery notEqual( Enum field, Object value ) { 
            criteria = criteria.add( Restrictions.ne( getFieldTranslation( type, field ), value ) ); 
            return this
        } 
 
        @Override 
        public ClothoConnection.ClothoQuery neq( Enum field, Object value ) { 
            return notEqual( field, value ); 
        } 
 
        @Override 
        public ClothoConnection.ClothoQuery isNull( Enum field ) { 
            criteria = criteria.add( Restrictions.isNull( getFieldTranslation( type, field ) ) ); 
            return this
        } 
 
        @Override 
        public ClothoConnection.ClothoQuery isNotNull( Enum field ) { 
            criteria = criteria.add( Restrictions.isNotNull( getFieldTranslation( type, field ) ) ); 
            return this
        } 
 
        @Override 
        public ClothoConnection.ClothoQuery matches( Enum field, String value, boolean caseSensitive ) { 
            if ( caseSensitive ) { 
                criteria = criteria.add( Restrictions.like( getFieldTranslation( type, field ), value ) ); 
            } else { 
                criteria = criteria.add( Restrictions.ilike( getFieldTranslation( type, field ), value ) ); 
            } 
            return this
        } 
 
        @Override 
        public ClothoConnection.ClothoQuery contains( Enum field, String value, boolean caseSensitive ) { 
            if ( value.charAt( 0 ) != '%' ) { 
                value = "%" + value; 
            } 
            if ( value.charAt( value.length() - 1 ) != '%' ) { 
                value = value + "%"
            } 
            if ( caseSensitive ) { 
                criteria = criteria.add( Restrictions.like( getFieldTranslation( type, field ), value ) ); 
            } else { 
                criteria = criteria.add( Restrictions.ilike( getFieldTranslation( type, field ), value ) ); 
            } 
            return this
        } 
 
        @Override 
        public List<ObjBase> getResults() { 
            if ( !session.isOpen() ) { 
                System.out.println( "HibernateConnectionPlugin returning null since session isn't open" ); 
                return null// This method should only be called once! 
            } 
            List<ObjBase> results = new ArrayList<ObjBase>(); 
            List<hibernateDatum> datums = criteria.list(); 
 
            for ( hibernateDatum d : datums ) { 
                ObjBase obj = d.getObject(); 
                SearchtagTable.pullSearchTags( obj ); 
                results.add( obj ); 
            } 
            session.close(); 
 
            return results; 
        } 
 
        @Override 
        public ClothoConnection.ClothoQuery add( ClothoConnection.ClothoCriterion crit ) { 
            if ( crit != null && crit.getClass().equals( HibernateCriterion.class ) ) { 
                criteria = criteria.add( ((HibernateCriterion) crit)._myCriterion ); 
            } 
            return this
        } 
 
        @Override 
        public ClothoConnection.ClothoCriterion or( Collection<ClothoConnection.ClothoCriterion> criteria ) { 
            Iterator<ClothoConnection.ClothoCriterion> it = criteria.iterator(); 
 
            Disjunction d = Restrictions.disjunction(); 
            while ( it.hasNext() ) { 
                ClothoConnection.ClothoCriterion cc = it.next(); 
                if ( it.getClass().equals( HibernateCriterion.class ) ) { 
                    d.add( ((HibernateCriterion) cc)._myCriterion ); 
                } 
            } 
            return new HibernateCriterion( d ); 
        } 
 
        @Override 
        public ClothoConnection.ClothoCriterion or( ClothoConnection.ClothoCriterion a, ClothoConnection.ClothoCriterion b ) { 
            if ( !a.getClass().equals( HibernateCriterion.class ) 
                    || !b.getClass().equals( HibernateCriterion.class ) ) { 
                return null
            } 
 
            HibernateCriterion ha = (HibernateCriterion) a, 
                    hb = (HibernateCriterion) b; 
 
            Disjunction d = Restrictions.disjunction(); 
            d.add( ha._myCriterion ).add( hb._myCriterion ); 
            return new HibernateCriterion( d ); 
        } 
 
        @Override 
        public ClothoConnection.ClothoCriterion and( Collection<ClothoConnection.ClothoCriterion> criteria ) { 
            Iterator<ClothoConnection.ClothoCriterion> it = criteria.iterator(); 
 
            Conjunction d = Restrictions.conjunction(); 
            while ( it.hasNext() ) { 
                ClothoConnection.ClothoCriterion cc = it.next(); 
                if ( it.getClass().equals( HibernateCriterion.class ) ) { 
                    d.add( ((HibernateCriterion) cc)._myCriterion ); 
                } 
            } 
            return new HibernateCriterion( d ); 
        } 
 
        @Override 
        public ClothoConnection.ClothoCriterion and( ClothoConnection.ClothoCriterion a, ClothoConnection.ClothoCriterion b ) { 
            if ( a == null || b == null 
                    || !a.getClass().equals( HibernateCriterion.class ) 
                    || !b.getClass().equals( HibernateCriterion.class ) ) { 
                return null
            } 
 
            HibernateCriterion ha = (HibernateCriterion) a, 
                    hb = (HibernateCriterion) b; 
 
            Conjunction d = Restrictions.conjunction(); 
            d.add( ha._myCriterion ).add( hb._myCriterion ); 
            return new HibernateCriterion( d ); 
        } 
 
        @Override 
        public ClothoConnection.ClothoCriterion not( ClothoConnection.ClothoCriterion c ) { 
            if ( c != null && c.getClass().equals( HibernateCriterion.class ) ) { 
                return new HibernateCriterion( 
                        Restrictions.not( ((HibernateCriterion) c)._myCriterion ) ); 
            } 
            return null
        } 
 
        @Override 
        public ClothoConnection.ClothoCriterion getBetweenCrit( Enum field, Object hi, Object lo ) { 
            String f = getFieldTranslation( type, field ); 
 
            Criterion c = Restrictions.between( f, lo, hi ); 
 
            return new HibernateCriterion( c ); 
        } 
 
        @Override 
        public ClothoConnection.ClothoCriterion getLessThanCrit( Enum field, Object hi ) { 
            String f = getFieldTranslation( type, field ); 
 
            Criterion c = Restrictions.lt( f, hi ); 
 
            return new HibernateCriterion( c ); 
        } 
 
        @Override 
        public ClothoConnection.ClothoCriterion getGreaterThanCrit( Enum field, Object lo ) { 
            String f = getFieldTranslation( type, field ); 
 
            Criterion c = Restrictions.gt( f, lo ); 
 
            return new HibernateCriterion( c ); 
        } 
 
        @Override 
        public ClothoConnection.ClothoCriterion getEqualCrit( Enum field, Object value ) { 
            String f = getFieldTranslation( type, field ); 
 
            Criterion c = Restrictions.eq( f, value ); 
 
            return new HibernateCriterion( c ); 
        } 
 
        @Override 
        public ClothoConnection.ClothoCriterion getNotEqualCrit( Enum field, Object value ) { 
            String f = getFieldTranslation( type, field ); 
 
            Criterion c = Restrictions.ne( f, value ); 
 
            return new HibernateCriterion( c ); 
        } 
 
        @Override 
        public ClothoConnection.ClothoCriterion getIsNullCrit( Enum field ) { 
            String f = getFieldTranslation( type, field ); 
 
            Criterion c = Restrictions.isNull( f ); 
 
            return new HibernateCriterion( c ); 
        } 
 
        @Override 
        public ClothoConnection.ClothoCriterion getIsNotNullCrit( Enum field ) { 
            String f = getFieldTranslation( type, field ); 
 
            Criterion c = Restrictions.isNotNull( f ); 
 
            return new HibernateCriterion( c ); 
        } 
 
        @Override 
        public ClothoConnection.ClothoCriterion getMatchesCrit( Enum field, Object value ) { 
            String f = getFieldTranslation( type, field ); 
 
            Criterion c = Restrictions.ilike( f, value ); 
 
            return new HibernateCriterion( c ); 
        } 
 
        @Override 
        public ObjType getType() { 
            return type; 
        } 
 
        @Override 
        public ClothoQuery createAssociationQuery( Enum field ) { 
            return new HibernateQuery( getFieldType( type, field ), criteria.createCriteria( getFieldTranslation( type, field ) ) ); 
        } 
    } 
 
    class HibernateCriterion extends ClothoConnection.ClothoCriterion { 
 
        Criterion _myCriterion; 
 
        protected HibernateCriterion( Criterion c ) { 
            _myCriterion = c; 
        } 
    } 
}