/* 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.clothocore.api.data; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import org.clothocore.api.core.Collector; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import javax.swing.JOptionPane; import org.clothocore.api.dnd.RefreshEvent; import org.clothocore.api.plugin.ClothoConnection; import org.clothocore.api.plugin.ClothoConnection.ClothoQuery; import org.clothocore.core.Hub; /** * * @author jcanderson */ public class Vector extends ObjBase { /** * Constructor from database * @param uuid * @param hash * @param rg * @param dateCreated * @param lastModified * @param description * @param circular * @param genomic * @param idformat * @param idPerson * @param idNucseq */ public Vector( VectorDatum d ) { super( d ); _vecDatum = d; if ( _vecDatum._riskGroup == -1 ) { final Vector item = this; Thread bslThread = new Thread() { @Override public void run() { short newrg = item.getSeq().performBiosafetyCheck(); if(item._vecDatum._riskGroup!=newrg) { _vecDatum._riskGroup = newrg; item.setChanged(org.clothocore.api.dnd.RefreshEvent.Condition.NAME_CHANGED); } } }; bslThread.start(); addSaveHold( bslThread ); } } private Vector( String name, String desc, String seq, Format form, Person author ) { super(); _vecDatum = new VectorDatum(); _datum = _vecDatum; _datum.name = name; _datum.dateCreated = new Date(); _datum.lastModified = new Date(); _datum.uuid = _uuid; _vecDatum._authorUUID = author.getUUID(); _vecDatum._formatUUID = form.getUUID(); _vecDatum._description = desc; _vecDatum._seqID = new NucSeq( seq ).getUUID(); } /** * Factory method for creating a new vector. * @param name * @param desc * @param seq * @param form * @param author * @return */ public static Vector generateVector( String name, String desc, String seq, Format form, Person author ) { //Generate a hash of the sequence and Format NucSeq aseq = new NucSeq( seq ); aseq.setTransient(); String key = aseq.getSeq() + form.getUUID(); String uuidKey = generateUUIDAsHash( key ); //THIS NEEDS TO BE CHANGED--IT NEEDS TO QUERY THE HASH COLUMN OF THE TABLE, NOT PRIIMARY KEY //DO A QUERY FOR A PART WHOSE HASH IS THE KEY AND RETURN IT TO prexistingSeq Vector prexistingSeq = retrieveByHash( uuidKey ); //If this sequence/Format is already in the database, return that if ( prexistingSeq != null ) { int n = JOptionPane.showConfirmDialog( null, "A vector with this sequence already exists. You should try to use that vector. Do you want to cancel this new vector?", "Vector" + "already exists", JOptionPane.YES_NO_OPTION ); if ( n == 0 ) { return prexistingSeq; } //Do a second chance to cancel int m = JOptionPane.showConfirmDialog( null, "Are you sure you really want two copies of this sequence? It isn't recommended, can I please abort this?", "Vector " + "already exists", JOptionPane.YES_NO_OPTION ); if ( m == 0 ) { return prexistingSeq; } } //Check to see if a Part by this hash already exists in the database prexistingSeq = retrieveByName( name ); while ( prexistingSeq != null ) { name = JOptionPane.showInputDialog( "A vector named " + name + " already exists, please give me a new name." ); prexistingSeq = retrieveByName( name ); } Vector v = null; try { v = new Vector( name, desc, seq, form, author ); } catch ( java.lang.NullPointerException e ) { return null; } if ( v == null ) { return null; } v.setTransient(); if ( !form.checkVector( v ) ) { return null; } //If it's gotten this far it's a good Vector, so finish it up and return it if ( v.getSeq().getSeq().indexOf( "." ) > -1 ) { v._vecDatum._isCircular = false; } v._vecDatum._hash = uuidKey; v._isTransient = false; v.getSeq().setLocked( true ); //Do risk group setting on a new thread final Vector vectorout = v; Thread bslThread = new Thread() { @Override public void run() { vectorout.setChanged(org.clothocore.api.dnd.RefreshEvent.Condition.NAME_CHANGED); Short rg = vectorout.getSeq().performBiosafetyCheck(); vectorout._vecDatum._riskGroup = rg; } }; bslThread.start(); vectorout.addSaveHold( bslThread ); return v; } @Override public ObjType getType() { return ObjType.VECTOR; } protected static ObjBase importFromHashMap( String uuid, HashMap<String, Object> objHash ) { String name = (String) objHash.get( "name" ); String description = (String) objHash.get( "_description" ); String hash = (String) objHash.get( "_hash" ); String sriskGroup = (String) objHash.get( "_riskGroup" ); short riskGroup = Short.parseShort( sriskGroup ); String idformat = (String) objHash.get( "_formatUUID" ); String idPerson = (String) objHash.get( "_authorUUID" ); String idNucseq = (String) objHash.get( "_seqID" ); String sccirular = (String) objHash.get( "_iscircular" ); boolean circular = Boolean.parseBoolean( sccirular ); String sgenomic = (String) objHash.get( "_isGenomic" ); boolean genomic = Boolean.parseBoolean( sgenomic ); Date dateCreated = getDateFromString( (String) objHash.get( "_dateCreated" ) ); Date lastModified = getDateFromString( (String) objHash.get( "_lastModified" ) ); VectorDatum d = new VectorDatum(); d.uuid = uuid; d.name = name; d._riskGroup = riskGroup; d.dateCreated = dateCreated; d.lastModified = lastModified; d._description = description; d._isCircular = circular; d._isGenomic = genomic; d._formatUUID = idformat; d._authorUUID = idPerson; d._seqID = idNucseq; d._hash = hash; return new Vector( d ); } @Override protected HashMap<String, HashMap<String, Object>> generateXml( HashMap<String, HashMap<String, Object>> allObjects ) { //If the hash already has the object, skip adding anything if ( allObjects.containsKey( getUUID() ) ) { return allObjects; } //Fill in the individual fields HashMap<String, Object> datahash = new HashMap<String, Object>(); datahash.put( "objType", getType().toString() ); datahash.put( "uuid", _vecDatum.uuid ); datahash.put( "name", _vecDatum.name ); datahash.put( "_dateCreated", getDateCreatedAsString() ); datahash.put( "_lastModified", getLastModifiedAsString() ); datahash.put( "_description", _vecDatum._description ); datahash.put( "_riskGroup", Integer.toString( _vecDatum._riskGroup ) ); datahash.put( "_isGenomic", Boolean.toString( _vecDatum._isGenomic ) ); datahash.put( "_iscircular", Boolean.toString( _vecDatum._isCircular ) ); datahash.put( "_formatUUID", _vecDatum._formatUUID ); datahash.put( "_authorUUID", _vecDatum._authorUUID ); datahash.put( "_seqID", _vecDatum._seqID ); datahash.put( "_hash", _vecDatum._hash ); //Add the HashMap to the list allObjects.put( getUUID(), datahash ); //Recursively gather the objects linked to this object allObjects = getAuthor().generateXml( allObjects ); allObjects = getFormat().generateXml( allObjects ); allObjects = getSeq().generateXml( allObjects ); //Return the datahash return allObjects; } /** * Recursively save all child elements and then call ObjBase to save itself. */ @Override public synchronized boolean save( ClothoConnection conn ) { System.out.println( "============ Starting vector save" ); if ( !isChanged() ) { System.out.println( "vector didn't require saving" ); return true; } if ( Collector.isLocal( _vecDatum._authorUUID ) ) { Person aut = getAuthor(); if ( !aut.isInDatabase() ) { if ( !aut.save( conn ) ) { return false; } } } if ( Collector.isLocal( _vecDatum._seqID ) ) { NucSeq seq = getSeq(); if ( !seq.save( conn ) ) { return false; } } return super.save( conn ); } public void printOutInformation() { } public String stringOutInformation() { return ""; } public void uploadPartToClotho() { } /* public container clone_PartIntoContainer(Vector myvector, strain mycell, double myvolume, trajectory traj) { return null; } * */ /* public void EVT_add(plasmid aplasmid) { _myPlasmids.add(aplasmid.getUUID().toString()); } * */ public boolean checkFormat() { //SEE IF THE NUCSEQ OBEYS THE RULES OF THE FORMAT, IF SO RETURN TRUE return true; } /* public ArrayList getPlasmids() { ArrayList<plasmid> out = new ArrayList<plasmid>(); for(String str: _myPlasmids) { plasmid aplasmid = (plasmid) line.lineBase.get(str); out.add(aplasmid); } return out; } * * */ /* SETTERS * */ /* PUTTERS * */ @Override public void changeName( final String newname ) { Vector existing = Vector.retrieveByName(newname); if(existing!=null) { if(!existing.getUUID().equals(this.getUUID())) { setChanged(RefreshEvent.Condition.NAME_CHANGED); return; } } super.changeName( newname ); } public void changeShortDescription( String text ) { addUndo( "_description", _vecDatum._description, text ); _vecDatum._description = text; setChanged(org.clothocore.api.dnd.RefreshEvent.Condition.NAME_CHANGED); } public void changeSequence( final String newseq ) { if ( newseq == null ) { return; } final String oldseq = Collector.getNucSeq( _vecDatum._seqID ).toString(); Collector.getNucSeq( _vecDatum._seqID ).APIchangeSeq( newseq ); boolean isok = getFormat().checkVector( this ); if ( !isok ) { Collector.getNucSeq( _vecDatum._seqID ).APIchangeSeq( oldseq ); return; } ActionListener undo = new ActionListener() { @Override public void actionPerformed( ActionEvent e ) { Collector.getNucSeq( _vecDatum._seqID ).APIchangeSeq( oldseq ); } }; ActionListener redo = new ActionListener() { @Override public void actionPerformed( ActionEvent e ) { Collector.getNucSeq( _vecDatum._seqID ).APIchangeSeq( newseq ); } }; //Change the risk group _vecDatum._riskGroup = -1; final Vector item = this; Thread bslThread = new Thread() { @Override public void run() { // setChanged(org.clothocore.api.dnd.RefreshEvent.Condition.NAME_CHANGED); setRiskGroup( item.getSeq().performBiosafetyCheck() ); } }; bslThread.start(); addSaveHold( bslThread ); addUndo( undo, redo ); } public void changeFormat( Format f ) { if ( f == null ) { return; } boolean ok = f.checkVector( this ); if ( !ok ) { return; } addUndo( "_formatUUID", _vecDatum._formatUUID, f.getUUID() ); _vecDatum._formatUUID = f.getUUID(); setChanged(org.clothocore.api.dnd.RefreshEvent.Condition.NAME_CHANGED); } public void changeAuthor( Person newauthor ) { if(newauthor==null) { fireData(new RefreshEvent(this, RefreshEvent.Condition.AUTHOR_CHANGED)); return; } addUndo( "_authorUUID", _vecDatum._authorUUID, newauthor.getUUID() ); _vecDatum._authorUUID = newauthor.getUUID(); fireData(new RefreshEvent(this, RefreshEvent.Condition.AUTHOR_CHANGED)); } @Override public boolean addObject( ObjBase dropObject ) { switch ( dropObject.getType() ) { default: return false; } } public void setGenomic( boolean b ) { addUndo( "_isGenomic", _vecDatum._isGenomic, b ); _vecDatum._isGenomic = b; setChanged(org.clothocore.api.dnd.RefreshEvent.Condition.NAME_CHANGED); } /* GETTERS * */ public boolean isCircular() { return _vecDatum._isCircular; } public boolean isGenomic() { return _vecDatum._isGenomic; } public NucSeq getSeq() { return Collector.getNucSeq( _vecDatum._seqID ); } public Short getRiskGroup() { return _vecDatum._riskGroup; } public String getShortDescription() { return _vecDatum._description; } public Person getAuthor() { return Collector.getPerson( _vecDatum._authorUUID ); } public Format getFormat() { return Collector.getFormat( _vecDatum._formatUUID ); } /** * Method for increasing the risk group of the Vector. The * risk group cannot be decreased from public methods. * @param newrg the new risk group (2, 3, 4, or 5) */ public void changeRiskGroup( Short newrg ) { changeRiskGroupRelay( newrg ); } private void changeRiskGroupRelay( Short newrg ) { if ( newrg > _vecDatum._riskGroup ) { addUndo( "_riskGroup", _vecDatum._riskGroup, newrg ); _vecDatum._riskGroup = newrg; setChanged(RefreshEvent.Condition.RISK_GROUP_CHANGED); return; } fireData(new RefreshEvent(this, RefreshEvent.Condition.RISK_GROUP_CHANGED)); } /** * Determines the risk group of the Part, * relayed from the initial call to NucSeq's * call to foreign server * @param rg */ private void setRiskGroup( short rg ) { if ( rg == 5 ) { _vecDatum._riskGroup = 5; return; } _vecDatum._riskGroup = rg; //Check it's features to see if any are higher RG for ( Annotation an : this.getSeq().getAnnotations() ) { Feature afeat = an.getFeature(); if ( afeat == null ) { continue; } if ( afeat.getRiskGroup() > _vecDatum._riskGroup ) { _vecDatum._riskGroup = afeat.getRiskGroup(); } } } public static Vector retrieveByName( String name ) { if ( name.length() == 0 ) { return null; } ClothoQuery cq = Hub.defaultConnection.createQuery( ObjType.VECTOR ); cq.eq( Vector.Fields.NAME, name ); List l = cq.getResults(); if ( l.isEmpty() ) { return null; } Vector p = (Vector) l.get( 0 ); return p; } public static Vector retrieveByHash( String hash ) { if ( hash.length() == 0 ) { return null; } ClothoQuery cq = Hub.defaultConnection.createQuery( ObjType.VECTOR ); cq.eq( Vector.Fields.HASH, hash ); List l = cq.getResults(); if ( l.isEmpty() ) { return null; } Vector p = (Vector) l.get( 0 ); return p; } public String getHash() { return _vecDatum._hash; } /**----------------- variables -----------------*/ private VectorDatum _vecDatum; public static class VectorDatum extends ObjBaseDatum { public ArrayList<String> _myPlasmids; public String _seqID; public String _description; public String _authorUUID; public String _hash; public String _formatUUID; public Short _riskGroup = 1; public ArrayList<Part> _composition; //NEED TO FLATTEN THAT //For handling genomic stuff public boolean _isGenomic; public boolean _isCircular = false; @Override public ObjType getType() { return ObjType.VECTOR; } } /******* FIELDS *******/ public static enum Fields { NAME, DATE_CREATED, LAST_MODIFIED, IS_GENOMIC, RISK_GROUP, DESCRIPTION, TYPE, HASH, AUTHOR, FORMAT, SEQUENCE, IS_CIRCULAR, PLASMIDS } }