/* 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 org.clothocore.api.core.Collector; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import javax.swing.ButtonGroup; import javax.swing.JOptionPane; import javax.swing.JRadioButton; 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 J. Christopher Anderson */ public class Plasmid extends ObjBase { public Plasmid( PlasmidDatum d ) { super( d ); _plasDatum = d; if ( _plasDatum._riskGroup == -1 ) { final Plasmid item = this; Thread bslThread = new Thread() { @Override public void run() { changeRiskGroup(); } }; bslThread.start(); addSaveHold( bslThread ); } } /**This constructor can only be called with a check to the Format * * @param p the Part that's been through a Format check * @param v the Vector that's been through a Format check * @param author */ private Plasmid( Part p, Vector v, Person author, Format f ) { super(); _plasDatum = new PlasmidDatum(); _datum = _plasDatum; _datum.name = v.getName() + "-" + p.getName(); _datum.dateCreated = new Date(); _datum.lastModified = new Date(); _datum.uuid = _uuid; _plasDatum._partUUID = p.getUUID(); _plasDatum._vectorUUID = v.getUUID(); _plasDatum._formatUUID = f.getUUID(); _plasDatum._authorUUID = author.getUUID(); } @Override public ObjType getType() { return ObjType.PLASMID; } protected static ObjBase importFromHashMap( String uuid, HashMap<String, Object> objHash ) { String name = (String) objHash.get( "name" ); Date dateCreated = getDateFromString( (String) objHash.get( "_dateCreated" ) ); Date lastModified = getDateFromString( (String) objHash.get( "_lastModified" ) ); String idPerson = (String) objHash.get( "_authorUUID" ); String sriskGroup = (String) objHash.get( "_riskGroup" ); short riskGroup = Short.parseShort( sriskGroup ); String idPart = (String) objHash.get( "_partUUID" ); String idVector = (String) objHash.get( "_vectorUUID" ); String idformat = (String) objHash.get( "_formatUUID" ); String constructionFile = (String) objHash.get( "_constructionFile" ); PlasmidDatum d = new PlasmidDatum(); d.uuid = uuid; d.name = name; d.dateCreated = dateCreated; d.lastModified = lastModified; d._authorUUID = idPerson; d._partUUID = idPart; d._vectorUUID = idVector; d._formatUUID = idformat; d._constructionFile = constructionFile; d._riskGroup = riskGroup; return new Plasmid( 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", _plasDatum.uuid ); datahash.put( "name", _plasDatum.name ); datahash.put( "_dateCreated", getDateCreatedAsString() ); datahash.put( "_lastModified", getLastModifiedAsString() ); datahash.put( "_partUUID", _plasDatum._partUUID ); datahash.put( "_vectorUUID", _plasDatum._vectorUUID ); datahash.put( "_formatUUID", _plasDatum._formatUUID ); datahash.put( "_authorUUID", _plasDatum._authorUUID ); datahash.put( "_constructionFile", _plasDatum._constructionFile ); //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 = getPart().generateXml( allObjects ); allObjects = getVector().generateXml( allObjects ); //Return the datahash return allObjects; } /** * In essence, this is the "real" constructor for a new Plasmid, can make other ones if need to * use additional options, but regardless, generatePlasmid methods will check that the Part and Vector * are compatible with the provided Format, and then the Plasmid gets generated from Part and Vector * and finally this method fills in the rest of the information and returns the Plasmid * * @param p the Part being composed * @param v the Vector being composed * @param author the author of the Plasmid * @param f the Format being used to compose the Plasmid * @return */ public static Plasmid generatePlasmid( Part p, Vector v, Person author, Format f ) { if(p==null || v==null || f == null || author==null) { return null; } //Try to see if the Plasmid is already in the database String name = v.getName() + "-" + p.getName(); Plasmid prexistingSeq = retrieveByName(name); if ( prexistingSeq != null ) { if(prexistingSeq.getPart().getUUID().equals(p.getUUID())) { if(prexistingSeq.getVector().getUUID().equals(v.getUUID())) { int n = JOptionPane.showConfirmDialog( null, "A plasmid with this composition already exists. You should try to use that plasmid. Do you want to cancel this new Plasmid?", "Plasmid " + "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 composition? It isn't recommended, can I please abort this?", "Plasmid " + "already exists", JOptionPane.YES_NO_OPTION ); if ( m == 0 ) { return prexistingSeq; } } } } //At least force them to give it a different name while ( prexistingSeq != null ) { name = JOptionPane.showInputDialog( "A Plasmid named " + name + " already exists, please give me a new name." ); if(name==null) { return null; } prexistingSeq = retrieveByName( name ); } try { if ( f.checkPlasmid( p, v, null ) ) { Plasmid newplasmid = new Plasmid( p, v, author, f ); if ( v.isGenomic() ) { newplasmid.changeName( v.getName() + p.getName() ); } if ( v.getSeq().getSeq().length() == 0 ) { newplasmid.changeName( v.getName() + p.getName() ); } //FOR A VERSION OF GENERATEPLASMID THAT INVOLVES A LIST OF OLIGOS, THIS IS WHERE YOU'D POPULATE THAT newplasmid.changeName(name); return newplasmid; } else { JOptionPane.showMessageDialog( null, "I couldn't generate the plasmid", "Error", JOptionPane.ERROR_MESSAGE ); return null; } }catch(Exception e) { return null; } } public static Plasmid retrieveByName( String name ) { if ( name.length() == 0 ) { return null; } ClothoQuery cq = Hub.defaultConnection.createQuery( ObjType.PLASMID ); cq.eq( Plasmid.Fields.NAME, name ); List l = cq.getResults(); if ( l.isEmpty() ) { return null; } Plasmid p = (Plasmid) l.get( 0 ); return p; } @Override public boolean addObject( ObjBase dropObject ) { switch ( dropObject.getType() ) { case PART: return true; default: return false; } } /* SETTERS * */ /** * Recursively save all child elements and then call ObjBase to save itself. */ @Override public synchronized boolean save( ClothoConnection conn ) { System.out.println( "============ Starting plasmid save" ); if ( !isChanged() ) { System.out.println( "plasmid didn't require saving" ); return true; } if ( Collector.isLocal( _plasDatum._authorUUID ) ) { Person aut = getAuthor(); if ( !aut.isInDatabase() ) { if ( !aut.save( conn ) ) { return false; } } } if ( Collector.isLocal( _plasDatum._partUUID ) ) { Part seq = getPart(); if ( !seq.isInDatabase() ) { if ( !seq.save( conn ) ) { return false; } } } if ( Collector.isLocal( _plasDatum._vectorUUID ) ) { Vector seq = getVector(); if ( !seq.isInDatabase() ) { if ( !seq.save( conn ) ) { return false; } } } return super.save( conn ); } public void changeAuthor(Person newauthor) { if(newauthor==null) { fireData(new RefreshEvent(this, RefreshEvent.Condition.AUTHOR_CHANGED)); return; } addUndo( "_authorUUID", _plasDatum._authorUUID, newauthor.getUUID() ); _plasDatum._authorUUID = newauthor.getUUID(); fireData(new RefreshEvent(this, RefreshEvent.Condition.AUTHOR_CHANGED)); } public void putConstructionFileAsString( String s ) { _plasDatum._constructionFile = s; this.setChanged(org.clothocore.api.dnd.RefreshEvent.Condition.NAME_CHANGED); } private void changeRiskGroup() { Part mypart = getPart(); Vector myvect = getVector(); //If either portion's riskgroup is undetermined, keep this undetermined if ( mypart.getRiskGroup() == -1 || myvect.getRiskGroup() == -1 ) { relayRiskGroup((short) -1); return; } //If either portion's riskgroup is 5, it's 5 if ( mypart.getRiskGroup() == 5 || myvect.getRiskGroup() == 5 ) { relayRiskGroup((short) 5); return; } //If both components are RG2+, ask user what to do if ( mypart.getRiskGroup() > 1 && myvect.getRiskGroup() > 1 ) { short currentHighest = (short) Math.max( mypart.getRiskGroup(), myvect.getRiskGroup() ); //Throw a dialog asking for user to put in the new risk group ButtonGroup group = new javax.swing.ButtonGroup(); String msgString = "This plasmid joins a part and vector both with risk groups of 2 or higher. What should the new risk group be?"; int numelements = 5 - currentHighest; Object[] array = new Object[ numelements + 1 ]; JRadioButton[] buttons = new JRadioButton[ numelements ]; for ( short i = 0; i < numelements; i++ ) { buttons[i] = new javax.swing.JRadioButton( "Risk Group " + (i + currentHighest) ); group.add( buttons[i] ); array[i + 1] = buttons[i]; } array[0] = msgString; int sel = -1; while ( sel != 0 ) { sel = JOptionPane.showConfirmDialog( null, array, "", JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE ); } scanButtons: for ( short i = 1; i < numelements; i++ ) { if ( buttons[i].isSelected() ) { relayRiskGroup((short) (currentHighest + i)); break scanButtons; } } currentHighest = _plasDatum._riskGroup; } } private void relayRiskGroup(short value) { if(value == _plasDatum._riskGroup) { fireData(new RefreshEvent(this, RefreshEvent.Condition.RISK_GROUP_CHANGED)); return; } _plasDatum._riskGroup = value; setChanged(RefreshEvent.Condition.RISK_GROUP_CHANGED); } /* GETTERS * */ public Part getPart() { return Collector.getPart( _plasDatum._partUUID ); } public Vector getVector() { return Collector.getVector( _plasDatum._vectorUUID ); } public Format getFormat() { return Collector.getFormat( _plasDatum._formatUUID ); } public NucSeq getSeq() { if ( _plasDatum.tempSeq != null ) { return Collector.getNucSeq( _plasDatum.tempSeq ); } NucSeq aseq = getFormat().generatePlasmidSequence( this ); _plasDatum.tempSeq = aseq.getUUID(); return aseq; } public Person getAuthor() { return Collector.getPerson( _plasDatum._authorUUID ); } public String getAuthorUUID() { return _plasDatum._authorUUID; } public short getRiskGroup() { return _plasDatum._riskGroup; } public String getConstructionFileAsString() { return _plasDatum._constructionFile; } public ArrayList<Sample> getSamples() { System.out.println( "getSamples not implemented, need to do a database query" ); return null; } /*----------------- variables -----------------*/ private PlasmidDatum _plasDatum; public static class PlasmidDatum extends ObjBaseDatum { public ArrayList<String> _mySamples = new ArrayList<String>(); public String _partUUID; public String _vectorUUID; public String _formatUUID; public String _authorUUID; public String _constructionFile; public String tempSeq; public short _riskGroup = -1; @Override public ObjType getType() { return ObjType.PLASMID; } } /******* FIELDS *******/ public static enum Fields { NAME, DATE_CREATED, LAST_MODIFIED, CONSTRUCTION_FILE, PART, VECTOR, AUTHOR, FORMAT, RISK_GROUP } }