From 12e7f97678ce85b0238f987d5f4eaf18512a0b94 Mon Sep 17 00:00:00 2001 From: jprocter Date: Wed, 12 Nov 2008 17:05:58 +0000 Subject: [PATCH] refactored to propery separate addToDocument,addFromDocument, updateToDocument, updateToJalview and conflict processes --- src/jalview/io/vamsas/Datasetsequence.java | 185 ++++++++++++++ src/jalview/io/vamsas/DatastoreItem.java | 245 ++++++++++++++++++- src/jalview/io/vamsas/Dbref.java | 182 +++++++++----- src/jalview/io/vamsas/LocalDocSyncObject.java | 9 +- src/jalview/io/vamsas/Rangetype.java | 19 +- src/jalview/io/vamsas/Sequencefeature.java | 326 +++++++++++++++++++++++++ src/jalview/io/vamsas/Sequencemapping.java | 150 ++++++++---- src/jalview/io/vamsas/Tree.java | 143 +++++++---- 8 files changed, 1087 insertions(+), 172 deletions(-) create mode 100644 src/jalview/io/vamsas/Datasetsequence.java create mode 100644 src/jalview/io/vamsas/Sequencefeature.java diff --git a/src/jalview/io/vamsas/Datasetsequence.java b/src/jalview/io/vamsas/Datasetsequence.java new file mode 100644 index 0000000..85a228e --- /dev/null +++ b/src/jalview/io/vamsas/Datasetsequence.java @@ -0,0 +1,185 @@ +package jalview.io.vamsas; + +import jalview.datamodel.AlignmentI; +import jalview.datamodel.DBRefEntry; +import jalview.datamodel.SequenceI; +import jalview.io.VamsasAppDatastore; +import uk.ac.vamsas.objects.core.DataSet; +import uk.ac.vamsas.objects.core.Sequence; + +/** + * synchronize a vamsas dataset sequence with a jalview dataset sequence. + * This class deals with all sequence features and database references associated with + * the jalview sequence. + * @author JimP + * + */ +public class Datasetsequence extends DatastoreItem +{ + String dict; + private DataSet dataset; + // private AlignmentI jvdset; + + public Datasetsequence(VamsasAppDatastore vamsasAppDatastore, + SequenceI sq, String dict, DataSet dataset) + { + super(vamsasAppDatastore, sq, uk.ac.vamsas.objects.core.Sequence.class); + this.dataset = dataset; + // this.jvdset = jvdset; + this.dict = dict; + doSync(); + } + + public Datasetsequence(VamsasAppDatastore vamsasAppDatastore, + Sequence vdseq) + { + super(vamsasAppDatastore, vdseq, SequenceI.class); + doJvUpdate(); + } + + public void addFromDocument() + { + Sequence vseq = (Sequence) vobj; + SequenceI dsseq = new jalview.datamodel.Sequence(vseq.getName(), + vseq.getSequence(), + (int) vseq.getStart(), (int) vseq.getEnd()); + dsseq.setDescription(vseq.getDescription()); + bindjvvobj(dsseq, vseq); + dsseq.setVamsasId(vseq.getVorbaId().getId()); + jvobj = dsseq; + modified=true; + } + public void updateFromDoc() + { + Sequence sq = (Sequence) vobj; + SequenceI sequence = (SequenceI) jvobj; + if (!sequence.getSequenceAsString().equals(sq.getSequence())) + { + log.warn("Potential Client Error ! - mismatch of dataset sequence: and jalview internal dataset sequence."); + } else { + // verify and update principal attributes. + if (sequence.getDescription() != null + && (sequence.getDescription() == null || !sequence + .getDescription().equals(sq.getDescription()))) + { + sequence.setDescription(sq.getDescription()); + modified = true; + } + if (sequence.getSequence() == null + || !sequence.getSequenceAsString().equals( + sq.getSequence())) + { + if (sequence.getStart() != sq.getStart() + || sequence.getEnd() != sq.getEnd()) + { + // update modified sequence. + sequence.setSequence(sq.getSequence()); + sequence.setStart((int)sq.getStart()); + sequence.setEnd((int)sq.getEnd()); + modified = true; + } + } + if (!sequence.getName().equals(sq.getName())) + { + sequence.setName(sq.getName()); + modified = true; + } + } + } + + + public void addToDocument() + { + SequenceI sq = (SequenceI) jvobj; + Sequence sequence = new Sequence(); + bindjvvobj(sq, sequence); + sq.setVamsasId(sequence.getVorbaId().getId()); + sequence.setSequence(sq.getSequenceAsString()); + sequence.setDictionary(dict); + sequence.setName(sq.getName()); + sequence.setStart(sq.getStart()); + sequence.setEnd(sq.getEnd()); + sequence.setDescription(sq.getDescription()); + dataset.addSequence(sequence); + vobj = sequence; + // add or update any new features/references on dataset sequence + if (sq.getSequenceFeatures() != null) + { + int sfSize = sq.getSequenceFeatures().length; + + for (int sf = 0; sf < sfSize; sf++) + { + new jalview.io.vamsas.Sequencefeature(datastore, (jalview.datamodel.SequenceFeature) sq + .getSequenceFeatures()[sf], dataset, (Sequence) vobj); + } + } + if (sq.getDatasetSequence() == null && sq.getDBRef() != null) + { + // only sync database references for dataset sequences + DBRefEntry[] entries = sq.getDBRef(); + jalview.datamodel.DBRefEntry dbentry; + for (int db = 0; db < entries.length; db++) + { + Rangetype dbr = new jalview.io.vamsas.Dbref(datastore, + dbentry = entries[db], sq, (Sequence) vobj, dataset); + } + + } + + } + + public void conflict() + { + log.warn("Conflict in dataset sequence update to document. Overwriting document"); + // TODO: could try to import from document data to jalview first. and then + updateToDoc(); + } + boolean modified = false; + public void updateToDoc() + { + SequenceI sq = (SequenceI) jvobj; + Sequence sequence = (Sequence) vobj; + // verify and update principal attributes. + if (sequence.getDescription() != null + && (sequence.getDescription() == null || !sequence + .getDescription().equals(sq.getDescription()))) + { + sequence.setDescription(sq.getDescription()); + modified = true; + } + if (sequence.getSequence() == null + || !sequence.getSequence().equals( + sq.getSequenceAsString())) + { + if (sequence.getStart() != sq.getStart() + || sequence.getEnd() != sq.getEnd()) + { + // update modified sequence. + sequence.setSequence(sq.getSequenceAsString()); + sequence.setStart(sq.getStart()); + sequence.setEnd(sq.getEnd()); + modified = true; + } + } + if (!dict.equals(sequence.getDictionary())) + { + sequence.setDictionary(dict); + modified = true; + } + if (!sequence.getName().equals(sq.getName())) + { + sequence.setName(sq.getName()); + modified = true; + } + } + + /** + * (probably could just do vobj.isModified(), but..) + * @return true if document's dataset sequence was modified + */ + public boolean getModified() + { + return modified; + } + +} diff --git a/src/jalview/io/vamsas/DatastoreItem.java b/src/jalview/io/vamsas/DatastoreItem.java index 42e494f..ce5acd3 100644 --- a/src/jalview/io/vamsas/DatastoreItem.java +++ b/src/jalview/io/vamsas/DatastoreItem.java @@ -19,6 +19,7 @@ package jalview.io.vamsas; import jalview.bin.Cache; +import jalview.datamodel.DBRefEntry; import jalview.gui.TreePanel; import jalview.io.VamsasAppDatastore; @@ -27,9 +28,12 @@ import java.util.Hashtable; import java.util.IdentityHashMap; import java.util.Vector; +import org.apache.commons.logging.Log; + import uk.ac.vamsas.client.IClientDocument; import uk.ac.vamsas.client.Vobject; import uk.ac.vamsas.client.VorbaId; +import uk.ac.vamsas.objects.core.DbRef; import uk.ac.vamsas.objects.core.Entry; import uk.ac.vamsas.objects.core.Provenance; import uk.ac.vamsas.objects.core.Seg; @@ -40,7 +44,7 @@ import uk.ac.vamsas.objects.core.Seg; * @author JimP * */ -public class DatastoreItem +public abstract class DatastoreItem { /** * @@ -52,8 +56,14 @@ public class DatastoreItem Hashtable vobj2jv; IdentityHashMap jv2vobj; - + + boolean tojalview=false; + /** + * shared log instance + */ + protected static org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(DatastoreItem.class); /** + * note: this is taken verbatim from jalview.io.VamsasAppDatastore * @return the Vobject bound to Jalview datamodel object */ protected Vobject getjv2vObj(Object jvobj) @@ -94,6 +104,12 @@ public class DatastoreItem return null; } + /** + * note: this is taken verbatim from jalview.io.VamsasAppDatastore + + * @param jvobj + * @param vobj + */ protected void bindjvvobj(Object jvobj, uk.ac.vamsas.client.Vobject vobj) { VorbaId id = vobj.getVorbaId(); @@ -137,6 +153,29 @@ public class DatastoreItem // JBPNote - better implementing a hybrid invertible hash. jv2vobj.put(jvobj, vobj.getVorbaId()); } + /** + * replaces oldjvobject with newjvobject in the Jalview Object <> VorbaID + * binding tables + * note: this is taken verbatim from jalview.io.VamsasAppDatastore + * @param oldjvobject + * @param newjvobject (may be null to forget the oldjvobject's document mapping) + * + */ + protected void replaceJvObjMapping(Object oldjvobject, Object newjvobject) + { + Object vobject = jv2vobj.remove(oldjvobject); + if (vobject == null) + { + throw new Error( + "IMPLEMENTATION ERROR: old jalview object is not bound ! (" + + oldjvobject + ")"); + } + if (newjvobject!=null) + { + jv2vobj.put(newjvobject, vobject); + vobj2jv.put(vobject, newjvobject); + } + } public DatastoreItem() { @@ -149,8 +188,137 @@ public class DatastoreItem initDatastoreItem(datastore); // TODO Auto-generated constructor stub } + /** + * construct and initialise datastore object and retrieve object bound to vobj2 and validate it against boundType + * @param datastore2 + * @param vobj2 + * @param boundType + */ + public DatastoreItem(VamsasAppDatastore datastore2, Vobject vobj2, Class boundType) + { + this(datastore2); + vobj = vobj2; + jvobj = getvObj2jv(vobj2); + tojalview=true; + if (jvobj!=null && !(boundType.isAssignableFrom(jvobj.getClass()))) + { + throw new Error("Implementation Error: Vamsas Document Class "+vobj.getClass()+" should bind to a "+boundType+" (found a "+jvobj.getClass()+")"); + } + } + /** + * construct and initialise datastore object and retrieve document object bound to Jalview object jvobj2 and validate it against boundType + * @param datastore2 the datastore + * @param jvobj2 the jalview object + * @param boundToType - the document object class that the bound object should be assignable from + */ + public DatastoreItem(VamsasAppDatastore datastore2, Object jvobj2, Class boundToType) + { + this(datastore2); + jvobj = jvobj2; + tojalview=false; + vobj = getjv2vObj(jvobj); + if (vobj!=null && !(boundToType.isAssignableFrom(vobj.getClass()))) + { + throw new Error("Implementation Error: Jalview Class "+jvobj2.getClass()+" should bind to a "+boundToType+" (found a "+vobj.getClass()+")"); + } + } + /** + * create a new vobj to be added to the document + * for the jalview object jvobj + * (jvobj!=null, vobj==null) + */ + public abstract void addToDocument(); + /** + * handle a conflict where both an existing vobj has been updated + * and a local jalview object has been updated. This method + * is only called from doSync, when an incoming update from the vamsas + * session conflicts with local modifications made by the Jalview user. + * (jvobj!=null, vobj!=null) + */ + public abstract void conflict(); + /** + * update an existing vobj in the document with the data and settings from jvobj + * (jvobj!=null, vobj!=null) + */ + public abstract void updateToDoc(); + /** + * update the local jalview object with the data from an existing vobj in the document + * (jvobj!=null, vobj!=null) + */ + public abstract void updateFromDoc(); + /** + * create a new local jvobj bound to the vobj in the document. + * (jvobj==null, vobj!=null) + */ + public abstract void addFromDocument(); + boolean addtodoc=false, conflicted=false,updated=false,addfromdoc=false,success=false; + + private boolean updatedtodoc; + + private boolean updatedfromdoc; + /** + * Sync jalview to document. Enact addToDocument, conflict or update dependent on + * existence of a vobj bound to the local jvobj. + */ + protected void doSync() + { + + if (vobj == null) + { + log.debug("adding new vobject to document."); + addtodoc=true; + addToDocument(); + } + else + { + if (vobj.isUpdated()) + { + log.debug("Handling update conflict for existing bound vobject."); + conflicted=true; + conflict(); + } + else + { + log.debug("updating existing vobject in document."); + updatedtodoc=true; + updateToDoc(); + } + } + // no exceptions were encountered... + success=true; + } + /** + * Update jalview from document. enact addFromDocument if no local jvobj exists, or update iff jvobj + * exists and the vobj.isUpdated() flag is set. + */ + protected void doJvUpdate() + { + if (jvobj == null) + { + log.debug("adding new vobject to Jalview from Document"); + addfromdoc=true; + addFromDocument(); + } + else + { + if (vobj.isUpdated()) + { + log.debug("updating Jalview from existing bound vObject"); + updatedfromdoc=true; + updateFromDoc(); + } + } + } VamsasAppDatastore datastore = null; + /** + * object in vamsas document + */ + protected Vobject vobj = null; + /** + * local jalview object + */ + protected Object jvobj = null; public void initDatastoreItem(VamsasAppDatastore ds) { @@ -270,4 +438,77 @@ public class DatastoreItem { p.addEntry(dummyPEntry(action)); } + + + /** + * @return true if jalview was being updated from the vamsas document + */ + public boolean isTojalview() + { + return tojalview; + } + + /** + * @return true if addToDocument() was called. + */ + public boolean isAddtodoc() + { + return addtodoc; + } + + /** + * @return true if conflict() was called + */ + public boolean isConflicted() + { + return conflicted; + } + + /** + * @return true if updateFromDoc() was called + */ + public boolean isUpdatedFromDoc() + { + return updatedfromdoc; + } + /** + * @return true if updateToDoc() was called + */ + public boolean isUpdatedToDoc() + { + return updatedtodoc; + } + + /** + * @return true if addFromDocument() was called. + */ + public boolean isAddfromdoc() + { + return addfromdoc; + } + + /** + * @return true if object sync logic completed normally. + */ + public boolean isSuccess() + { + return success; + } + + /** + * @return the vobj + */ + public Vobject getVobj() + { + return vobj; + } + + /** + * @return the jvobj + */ + public Object getJvobj() + { + return jvobj; + } + } diff --git a/src/jalview/io/vamsas/Dbref.java b/src/jalview/io/vamsas/Dbref.java index e8fedac..c27bc9a 100644 --- a/src/jalview/io/vamsas/Dbref.java +++ b/src/jalview/io/vamsas/Dbref.java @@ -20,6 +20,7 @@ package jalview.io.vamsas; import jalview.datamodel.DBRefEntry; import jalview.datamodel.SequenceI; +import uk.ac.vamsas.objects.core.DataSet; import uk.ac.vamsas.objects.core.DbRef; import uk.ac.vamsas.objects.core.Map; import uk.ac.vamsas.objects.core.Sequence; @@ -31,115 +32,170 @@ public class Dbref extends Rangetype uk.ac.vamsas.objects.core.Sequence sequence = null; - DbRef dbref = null; - - DBRefEntry dbentry = null; + DataSet ds; public Dbref(VamsasAppDatastore datastore, DBRefEntry dbentry, jalview.datamodel.SequenceI sq2, - uk.ac.vamsas.objects.core.Sequence sequence2) + uk.ac.vamsas.objects.core.Sequence sequence2, DataSet dataset) { - super(datastore); - dbref = (DbRef) getjv2vObj(dbentry); + super(datastore, dbentry, DbRef.class); + // initialise object specific attributes sq = sq2; sequence = sequence2; - this.dbentry = dbentry; - if (dbref == null) - { - add(); - } - else - { - if (dbref.isUpdated()) - { - conflict(); - } - else - { - update(); - } - - } - + this.jvobj = dbentry; + ds = dataset; + // call the accessors + doSync(); } public Dbref(VamsasAppDatastore datastore, DbRef ref, Sequence vdseq, SequenceI dsseq) { - super(datastore); - dbref = ref; + super(datastore, ref, jalview.datamodel.DBRefEntry.class); sequence = vdseq; sq = dsseq; - dbentry = (jalview.datamodel.DBRefEntry) getvObj2jv(dbref); - if (dbentry == null) + ds = (DataSet) vdseq.getV_parent(); + doJvUpdate(); + } + + public void updateToDoc() + { + DbRef dbref = (DbRef) this.vobj; + DBRefEntry jvobj = (DBRefEntry) this.jvobj; + dbref.setAccessionId(jvobj.getAccessionId()); + dbref.setSource(jvobj.getSource()); + dbref.setVersion(jvobj.getVersion()); + if (jvobj.getMap() != null) { - addFromDocument(); + // Record mapping to external database coordinate system. + jalview.datamodel.Mapping mp = jvobj.getMap(); + if (mp.getMap() != null) + { + Map vMap = null; + if (dbref.getMapCount() == 0) + { + vMap = new Map(); + initMapType(vMap, mp.getMap(), true); + dbref.addMap(vMap); + } + else + { + // we just update the data anyway. + vMap = dbref.getMap(0); + initMapType(vMap, mp.getMap(), true); + } + updateMapTo(mp); + } } - else + else + { + jalview.bin.Cache.log.debug("Ignoring mapless DbRef.Map " + + jvobj.getSrcAccString()); + } + + } + /** + * ugly hack to try to get the embedded sequences within a database reference to be stored in the document's dataset. + * @param mp + */ + private void updateMapTo(jalview.datamodel.Mapping mp) + { + log.info("Performing updateMapTo remove this message when we know what we're doing."); + // TODO determine how sequences associated with database mappings are stored in the document + if (mp!=null && mp.getTo() != null) { - if (dbref.isUpdated()) + if (mp.getTo().getDatasetSequence() == null) { - update(); + // sync the dataset sequence, if it hasn't been done already. + Datasetsequence dssync = new Datasetsequence( + datastore, mp.getTo(), + (mp.getMappedWidth() == mp.getWidth()) ? sequence + .getDictionary() + : ((mp.getMappedWidth() == 3) ? uk.ac.vamsas.objects.utils.SymbolDictionary.STANDARD_AA + : uk.ac.vamsas.objects.utils.SymbolDictionary.STANDARD_NA), + ds); + // add a mapping between new dataset sequence and sequence associated with the database reference + + } else { + log.debug("Ignoring non-dataset sequence mapping."); } } } - - private void update() + public void updateFromDoc() { - // TODO: verify and update dbrefs in vamsas document - // there will be trouble when a dataset sequence is modified to - // contain more residues than were originally referenced - we must - // then make a number of dataset sequence entries - this info is already - // stored - jalview.bin.Cache.log - .debug("TODO verify update of dataset sequence database references."); + DbRef vobj = (DbRef) this.vobj; + DBRefEntry jvobj = (DBRefEntry) this.jvobj; + jvobj.setAccessionId(vobj.getAccessionId()); + jvobj.setSource(vobj.getSource()); + jvobj.setVersion(vobj.getVersion()); + // add new dbref + if (vobj.getMapCount() > 0) + { + // TODO: Jalview ignores all the other maps + if (vobj.getMapCount() > 1) + { + jalview.bin.Cache.log + .debug("Ignoring additional mappings on DbRef: " + + jvobj.getSource() + ":" + jvobj.getAccessionId()); + } + jalview.datamodel.Mapping mp = new jalview.datamodel.Mapping( + parsemapType(vobj.getMap(0))); + if (jvobj.getMap()==null || !mp.equals(jvobj.getMap())) + { + jvobj.setMap(mp); + } + } } - - private void conflict() + + public void conflict() { + DbRef vobj = (DbRef) this.vobj; + DBRefEntry jvobj = (DBRefEntry) this.jvobj; jalview.bin.Cache.log.debug("Conflict in dbentry update for " - + dbref.getAccessionId() + dbref.getSource() + " " - + dbref.getVorbaId()); + + vobj.getAccessionId() + vobj.getSource() + " " + + vobj.getVorbaId()); // TODO Auto-generated method stub } - private void addFromDocument() + public void addFromDocument() { + DbRef vobj = (DbRef) this.vobj; + DBRefEntry jvobj = (DBRefEntry) this.jvobj; // add new dbref - sq.addDBRef(dbentry = new jalview.datamodel.DBRefEntry(dbref - .getSource().toString(), dbref.getVersion().toString(), dbref + sq.addDBRef(jvobj = new jalview.datamodel.DBRefEntry(vobj.getSource() + .toString(), vobj.getVersion().toString(), vobj .getAccessionId().toString())); - if (dbref.getMapCount() > 0) + if (vobj.getMapCount() > 0) { // TODO: Jalview ignores all the other maps - if (dbref.getMapCount() > 1) + if (vobj.getMapCount() > 1) { jalview.bin.Cache.log .debug("Ignoring additional mappings on DbRef: " - + dbentry.getSource() + ":" - + dbentry.getAccessionId()); + + jvobj.getSource() + ":" + jvobj.getAccessionId()); } jalview.datamodel.Mapping mp = new jalview.datamodel.Mapping( - parsemapType(dbref.getMap(0))); - dbentry.setMap(mp); + parsemapType(vobj.getMap(0))); + jvobj.setMap(mp); } // TODO: jalview ignores links and properties because it doesn't know what // to do with them. - bindjvvobj(dbentry, dbref); + bindjvvobj(jvobj, vobj); } - private void add() + public void addToDocument() { + DBRefEntry jvobj = (DBRefEntry) this.jvobj; DbRef dbref = new DbRef(); - bindjvvobj(dbentry, dbref); - dbref.setAccessionId(dbentry.getAccessionId()); - dbref.setSource(dbentry.getSource()); - dbref.setVersion(dbentry.getVersion()); - if (dbentry.getMap() != null) + bindjvvobj(jvobj, dbref); + dbref.setAccessionId(jvobj.getAccessionId()); + dbref.setSource(jvobj.getSource()); + dbref.setVersion(jvobj.getVersion()); + if (jvobj.getMap() != null) { - jalview.datamodel.Mapping mp = dbentry.getMap(); + jalview.datamodel.Mapping mp = jvobj.getMap(); if (mp.getMap() != null) { Map vMap = new Map(); @@ -149,7 +205,7 @@ public class Dbref extends Rangetype else { jalview.bin.Cache.log.debug("Ignoring mapless DbRef.Map " - + dbentry.getSrcAccString()); + + jvobj.getSrcAccString()); } } sequence.addDbRef(dbref); diff --git a/src/jalview/io/vamsas/LocalDocSyncObject.java b/src/jalview/io/vamsas/LocalDocSyncObject.java index 2c9eddc..50b0cdb 100644 --- a/src/jalview/io/vamsas/LocalDocSyncObject.java +++ b/src/jalview/io/vamsas/LocalDocSyncObject.java @@ -22,8 +22,9 @@ import uk.ac.vamsas.client.Vobject; /** * Implement the basic logic for synchronising changes to or from the Vamsas - * Document - * + * Document. This is a more generic and normalised framework than the one implemented in DatastoreItem, + * but probably more tedious to implement. + * .. abandoned. Nov 2008 * @author JimP */ public abstract class LocalDocSyncObject extends DatastoreItem @@ -48,14 +49,14 @@ public abstract class LocalDocSyncObject extends DatastoreItem /** * called if the local object can be safely updated from the bound document * object. + public abstract void updateToDoc(); */ - public abstract void updateFromDoc(); /** * called if the associated document object can be safely updated with the * local changes - */ public abstract void updateToDoc(); + */ /** * @return true if the local object is modified diff --git a/src/jalview/io/vamsas/Rangetype.java b/src/jalview/io/vamsas/Rangetype.java index 2686e85..8c8b7d7 100644 --- a/src/jalview/io/vamsas/Rangetype.java +++ b/src/jalview/io/vamsas/Rangetype.java @@ -20,16 +20,22 @@ package jalview.io.vamsas; import java.util.Vector; +import uk.ac.vamsas.client.Vobject; import uk.ac.vamsas.objects.core.Local; import uk.ac.vamsas.objects.core.Map; import uk.ac.vamsas.objects.core.MapType; import uk.ac.vamsas.objects.core.Mapped; import uk.ac.vamsas.objects.core.RangeType; import uk.ac.vamsas.objects.core.Seg; +import jalview.datamodel.DBRefEntry; import jalview.datamodel.Mapping; import jalview.io.VamsasAppDatastore; - -public class Rangetype extends DatastoreItem +/** + * Enhances DatastoreItem objects with additional functions to do with RangeType objects + * @author JimP + * + */ +public abstract class Rangetype extends DatastoreItem { public Rangetype() @@ -41,6 +47,15 @@ public class Rangetype extends DatastoreItem { super(datastore); } + + public Rangetype(VamsasAppDatastore datastore, Vobject vobj, Class jvClass) + { + super(datastore, vobj, jvClass); + } + public Rangetype(VamsasAppDatastore datastore, Object jvobj, Class vClass) + { + super(datastore, jvobj, vClass); + } /** * get real bounds of a RangeType's specification. start and end are an diff --git a/src/jalview/io/vamsas/Sequencefeature.java b/src/jalview/io/vamsas/Sequencefeature.java new file mode 100644 index 0000000..0ddfc6f --- /dev/null +++ b/src/jalview/io/vamsas/Sequencefeature.java @@ -0,0 +1,326 @@ +/** + * + */ +package jalview.io.vamsas; + +import java.util.Enumeration; +import java.util.Vector; + +import uk.ac.vamsas.objects.core.DataSet; +import uk.ac.vamsas.objects.core.DataSetAnnotations; +import uk.ac.vamsas.objects.core.Link; +import uk.ac.vamsas.objects.core.Property; +import uk.ac.vamsas.objects.core.Provenance; +import uk.ac.vamsas.objects.core.RangeAnnotation; +import uk.ac.vamsas.objects.core.Score; +import uk.ac.vamsas.objects.core.Seg; +import uk.ac.vamsas.objects.core.Sequence; +import uk.ac.vamsas.objects.utils.Properties; +import jalview.bin.Cache; +import jalview.datamodel.SequenceFeature; +import jalview.datamodel.SequenceI; +import jalview.io.VamsasAppDatastore; +import jalview.util.UrlLink; + +/** + * @author JimP + * + */ +public class Sequencefeature extends Rangetype +{ + + uk.ac.vamsas.objects.core.DataSet dataset; + + uk.ac.vamsas.objects.core.Sequence sequence; + + private SequenceI dsSeq; + + public Sequencefeature(VamsasAppDatastore vamsasAppDatastore, + SequenceFeature sequenceFeature, + uk.ac.vamsas.objects.core.DataSet dataset, + uk.ac.vamsas.objects.core.Sequence sequence) + { + super(vamsasAppDatastore, sequenceFeature, DataSetAnnotations.class); + this.dataset = dataset; + this.sequence = sequence; + doSync(); + } + + public Sequencefeature(VamsasAppDatastore vamsasAppDatastore, + DataSetAnnotations dseta, SequenceI dsSeq) + { + super(vamsasAppDatastore, dseta, jalview.datamodel.SequenceFeature.class); + this.dsSeq = dsSeq; + doJvUpdate(); + } + + public void addToDocument() + { + DataSetAnnotations dsa = (DataSetAnnotations) vobj; + jalview.datamodel.SequenceFeature feature = (jalview.datamodel.SequenceFeature) jvobj; + dsa = (DataSetAnnotations) getDSAnnotationFromJalview( + new DataSetAnnotations(), feature); + if (dsa.getProvenance() == null) + { + dsa.setProvenance(new Provenance()); + } + addProvenance(dsa.getProvenance(), "created"); // JBPNote - need + // to update + dsa.addSeqRef(sequence); // we have just created this annotation + // - so safe to use this + bindjvvobj(feature, dsa); + dataset.addDataSetAnnotations(dsa); + } + + public void addFromDocument() + { + DataSetAnnotations dsa = (DataSetAnnotations) vobj; + if (dsa.getSeqRefCount()!=1) + { + Cache.log.warn("Not binding "+dsa.getVorbaId()+" to Sequence Feature - has multiple dataset sequence references."); + return; + } + jalview.datamodel.SequenceFeature sf = (jalview.datamodel.SequenceFeature) jvobj; + dsSeq.addSequenceFeature(sf = getJalviewSeqFeature(dsa)); + jvobj = sf; + bindjvvobj(sf, dsa); + } + + public void conflict() + { + log.warn("Untested sequencefeature conflict code"); + DataSetAnnotations dsa = (DataSetAnnotations) vobj; + jalview.datamodel.SequenceFeature feature = (jalview.datamodel.SequenceFeature) jvobj; + jalview.datamodel.SequenceFeature sf = getJalviewSeqFeature(dsa); + replaceJvObjMapping(feature, sf); // switch binding of dsa from old feature to newly created feature + dsSeq.addSequenceFeature(sf); // add new imported feature + addToDocument(); // and create a new feature in the document + } + + public void updateToDoc() + { + DataSetAnnotations dsa = (DataSetAnnotations) vobj; + jalview.datamodel.SequenceFeature feature = (jalview.datamodel.SequenceFeature) jvobj; + if (dsa.getSeqRefCount() != 1) + { + replaceJvObjMapping(feature, null); + Cache.log + .warn("Binding of annotation to jalview feature has changed. Removing binding and recreating."); + doSync(); // re-verify bindings. + } + else + { + // Sync the features from Jalview + long oldref = dsa.get__last_hash(); + getDSAnnotationFromJalview(dsa, feature); + if (oldref != dsa.hashCode()) + { + Cache.log + .debug("Updated dataset sequence annotation from feature."); + addProvenance(dsa.getProvenance(), "modified"); + } + } + + } + public void updateFromDoc() + { + DataSetAnnotations dsa = (DataSetAnnotations) vobj; + jalview.datamodel.SequenceFeature feature = (jalview.datamodel.SequenceFeature) jvobj; + if (dsa.getSeqRefCount() != 1) + { + // conflicting update from document - we cannot map this feature anymore. + replaceJvObjMapping(feature, null); + Cache.log + .warn("annotation ("+dsa.getVorbaId()+" bound to jalview feature cannot be mapped. Removing binding, deleting feature, and deleting feature."); + // - consider deleting the feature ? + dsSeq.deleteFeature(feature); + // doSync(); + } + else + { + // Sync the features to Jalview - easiest to delete and add the feature again + jalview.datamodel.SequenceFeature newsf = getJalviewSeqFeature(dsa); + dsSeq.deleteFeature(feature); + replaceJvObjMapping(feature, newsf); + dsSeq.addSequenceFeature(newsf); + feature.otherDetails.clear(); + } + } + + /** + * correctly create/update a RangeAnnotation from a jalview sequence feature + * TODO: refactor to a method in jalview.io.vamsas.RangeAnnotation class + * + * @param dsa + * (typically DataSetAnnotations or + * AlignmentSequenceAnnotation) + * @param feature + * (the feature to be mapped from) + * @return + */ + private RangeAnnotation getDSAnnotationFromJalview(RangeAnnotation dsa, + jalview.datamodel.SequenceFeature feature) + { + dsa.setType(feature.getType()); + Seg vSeg = new Seg(); + vSeg.setStart(feature.getBegin()); + vSeg.setEnd(feature.getEnd()); + vSeg.setInclusive(true); + if (dsa.getSegCount() > 1) + { + Cache.log + .debug("About to destroy complex annotation in vamsas document mapped to sequence feature (" + + dsa.getVorbaId() + ")"); + } + dsa.setSeg(new Seg[] + { vSeg }); + dsa.setDescription(feature.getDescription()); + dsa.setStatus(feature.getStatus()); + if (feature.links != null && feature.links.size() > 0) + { + for (int i = 0, iSize = feature.links.size(); i < iSize; i++) + { + String link = (String) feature.links.elementAt(i); + UrlLink ulink = new UrlLink(link); + /* + * int sep = link.indexOf('|'); if (sep > -1) { Link vLink = new Link(); + * if (sep > 0) { vLink.setContent(link.substring(0, sep - 1)); } else { + * vLink.setContent(""); } vLink.setHref(link.substring(sep + 1)); // + * TODO: validate href. dsa.addLink(vLink); } + */ + Link vLink = new Link(); + vLink.setContent(ulink.getLabel()); + vLink.setHref(ulink.getUrl_prefix()); + } + } + dsa.setGroup(feature.getFeatureGroup()); + if (feature.getScore() != Float.NaN) + { + Score fscore = new Score(); + dsa.setScore(new Score[] + { fscore }); + fscore.setContent(feature.getScore()); + fscore.setName(feature.getType()); + } + if (feature.otherDetails != null) + { + Enumeration iter = feature.otherDetails.keys(); + Vector props = dsa.getPropertyAsReference(); + while (iter.hasMoreElements()) + { + String key = (String) iter.nextElement(); + if (!key.equalsIgnoreCase("score") + && !key.equalsIgnoreCase("status")) + { + Property nprop = new Property(); + nprop.setName(key); + Object vlu = feature.getValue(key); + nprop.setContent(feature.getValue(key).toString()); + boolean valid = false; + if (vlu instanceof String) + { + nprop.setType(uk.ac.vamsas.objects.utils.Properties.STRINGTYPE); + valid = true; + } + else if (vlu instanceof Integer) + { + valid = true; + nprop + .setType(uk.ac.vamsas.objects.utils.Properties.INTEGERTYPE); + } + else if (vlu instanceof Float) + { + nprop.setType(uk.ac.vamsas.objects.utils.Properties.FLOATTYPE); + valid = true; + } + if (valid) + { + if (props != null) + { + uk.ac.vamsas.objects.utils.Properties.addOrReplace(props, + nprop); + } + else + { + dsa.addProperty(nprop); + } + } + } + } + } + return dsa; + } + private SequenceFeature getJalviewSeqFeature(RangeAnnotation dseta) + { + int[] se = getBounds(dseta); + SequenceFeature sf = new jalview.datamodel.SequenceFeature(dseta + .getType(), dseta.getDescription(), dseta.getStatus(), se[0], + se[1], dseta.getGroup()); + if (dseta.getLinkCount() > 0) + { + Link[] links = dseta.getLink(); + for (int i = 0; i < links.length; i++) + { + sf.addLink(links[i].getContent() + "|" + links[i].getHref()); + } + } + if (dseta.getScoreCount()>0) + { + Enumeration scr = dseta.enumerateScore(); + while (scr.hasMoreElements()) + { + Score score = (Score) scr.nextElement(); + if (score.getName().equals(sf.getType())) + { + sf.setScore(score.getContent()); + } else { + sf.setValue(score.getName(), ""+score.getContent()); + } + } + } + // other details + Enumeration props = dseta.enumerateProperty(); + while (props.hasMoreElements()) + { + Property p = (Property) props.nextElement(); + Object val = null; + if (Properties.isValid(p)) + { + if (Properties.isString(p)) + { + val = p.getContent(); + } + if (Properties.isBoolean(p)) + { + try { + val = new Boolean(p.getContent()); + } catch (Exception e) {} + } + if (Properties.isFloat(p)) + { + try { + val = new Float(p.getContent()); + + } catch (Exception e) + { + } + } + if(Properties.isInteger(p)) + { + try { + val = new Integer(p.getContent()); + } catch (Exception e) + { + } + } + if (val!=null) + { + sf.setValue(p.getName(), val); + } + } + } + + return sf; + } + +} diff --git a/src/jalview/io/vamsas/Sequencemapping.java b/src/jalview/io/vamsas/Sequencemapping.java index de4a01e..2e8d31b 100644 --- a/src/jalview/io/vamsas/Sequencemapping.java +++ b/src/jalview/io/vamsas/Sequencemapping.java @@ -34,26 +34,29 @@ import uk.ac.vamsas.objects.core.Sequence; import uk.ac.vamsas.objects.core.SequenceMapping; import uk.ac.vamsas.objects.core.SequenceType; +/** + * binds a vamsas sequence mapping object from the vamsas document to + * a maplist object associated with a mapping in the Jalview model. + * We use the maplist object because these are referred to both in + * the Mapping object associated with a jalview.datamodel.DBRefEntry + * and in the array of jalview.datamodel.AlCodonFrame objects that + * Jalview uses to propagate sequence mapping position highlighting + * across the views. + * @author JimP + * + */ public class Sequencemapping extends Rangetype { public Sequencemapping(VamsasAppDatastore datastore, SequenceMapping sequenceMapping) { - super(datastore); - Object mjvmapping = getvObj2jv(sequenceMapping); - if (mjvmapping == null) - { - add(sequenceMapping); - } - else - { - if (sequenceMapping.isUpdated()) - { - update((jalview.util.MapList) mjvmapping, sequenceMapping); - } - } + super(datastore, sequenceMapping, jalview.util.MapList.class); + doJvUpdate(); } - + private SequenceType from; + private DataSet ds; + private Mapping mjvmapping; + /** * create or update a vamsas sequence mapping corresponding to a jalview * Mapping between two dataset sequences @@ -68,38 +71,64 @@ public class Sequencemapping extends Rangetype uk.ac.vamsas.objects.core.SequenceType from, uk.ac.vamsas.objects.core.DataSet ds) { - super(datastore); - SequenceMapping sequenceMapping = (SequenceMapping) getjv2vObj(mjvmapping); - if (sequenceMapping == null) + super(datastore, mjvmapping.getMap(), SequenceMapping.class); + this.from = from; + this.ds = ds; + this.mjvmapping = mjvmapping; + validate(); + doSync(); + } + /** + * local check that extant mapping context is valid + */ + public void validate() + { + + SequenceMapping sequenceMapping = (SequenceMapping) vobj; + if (sequenceMapping==null) { - add(mjvmapping, from, ds); + return; } - else + if (from != null && sequenceMapping.getLoc() != from) { - if (from != null && sequenceMapping.getLoc() != from) - { - jalview.bin.Cache.log.warn("Probable IMPLEMENTATION ERROR: " + from - + " doesn't match the local mapping sequence."); - } - if (ds != null && sequenceMapping.is__stored_in_document() - && sequenceMapping.getV_parent() != ds) - { - jalview.bin.Cache.log - .warn("Probable IMPLEMENTATION ERROR: " - + ds - + " doesn't match the parent of the bound sequence mapping object."); - } - if (sequenceMapping.isUpdated()) - { - conflict(mjvmapping, sequenceMapping); - } - else - { - update(mjvmapping, sequenceMapping); - } + jalview.bin.Cache.log.warn("Probable IMPLEMENTATION ERROR: " + from + + " doesn't match the local mapping sequence."); } + if (ds != null && sequenceMapping.is__stored_in_document() + && sequenceMapping.getV_parent() != ds) + { + jalview.bin.Cache.log + .warn("Probable IMPLEMENTATION ERROR: " + + ds + + " doesn't match the parent of the bound sequence mapping object."); + } + } + + public void addToDocument() + { + add(mjvmapping, from, ds); } + public void addFromDocument() + { + add((SequenceMapping) vobj); + } + + public void conflict() + { + conflict(mjvmapping, (SequenceMapping) vobj); + + } + + public void updateToDoc() + { + update(mjvmapping, (SequenceMapping) vobj); + } + public void updateFromDoc() + { + update((SequenceMapping) vobj, (jalview.datamodel.Mapping) jvobj); + } + private void conflict(Mapping mjvmapping, SequenceMapping sequenceMapping) { System.err.println("Conflict in update of sequenceMapping " @@ -118,7 +147,7 @@ public class Sequencemapping extends Rangetype if (to == null) { jalview.bin.Cache.log - .warn("NONFATAL - do a second update: Ignoring Forward Reference to seuqence not yet bound to vamsas seuqence object"); + .warn("FIXME NONFATAL - do a second update: Ignoring Forward Reference to seuqence not yet bound to vamsas seuqence object"); return; } SequenceMapping sequenceMapping = new SequenceMapping(); @@ -186,19 +215,25 @@ public class Sequencemapping extends Rangetype // source // of // mapping - bindjvvobj(mjvmapping, sequenceMapping); + bindjvvobj(mjvmapping.getMap(), sequenceMapping); jalview.bin.Cache.log.debug("Successfully created mapping " + sequenceMapping.getVorbaId()); } - private void update(jalview.util.MapList mjvmapping, - SequenceMapping sequenceMapping) + // private void update(jalview.util.MapList mjvmapping, + // SequenceMapping sequenceMapping) { jalview.bin.Cache.log .error("Not implemented: Jalview Update Alcodon Mapping:TODO!"); } + private void update(SequenceMapping sequenceMapping, + jalview.datamodel.Mapping mjvmapping) + { + jalview.bin.Cache.log + .error("Not implemented: Update DBRef Mapping from Jalview"); + } private void update(jalview.datamodel.Mapping mjvmapping, SequenceMapping sequenceMapping) { @@ -207,6 +242,7 @@ public class Sequencemapping extends Rangetype } /** + * bind a SequenceMapping to a live AlCodonFrame element * limitations: Currently, jalview only deals with mappings between dataset * sequences, and even then, only between those that map from DNA to Protein. * @@ -300,17 +336,23 @@ public class Sequencemapping extends Rangetype // create and add the new mapping to (each) dataset's codonFrame jalview.util.MapList mapping = null; - if (!sense) - { - mapping = this.parsemapType(sequenceMapping, 1, 3); // invert sense - mapping = new jalview.util.MapList(mapping.getToRanges(), mapping - .getFromRanges(), mapping.getToRatio(), mapping - .getFromRatio()); - afc.addMap(to, from, mapping); - } - else + if (dnaToProt) { - mapping = this.parsemapType(sequenceMapping, 3, 1); // correct sense + if (!sense) + { + mapping = this.parsemapType(sequenceMapping, 1, 3); // invert sense + mapping = new jalview.util.MapList(mapping.getToRanges(), mapping + .getFromRanges(), mapping.getToRatio(), mapping + .getFromRatio()); + afc.addMap(to, from, mapping); + } + else + { + mapping = this.parsemapType(sequenceMapping, 3, 1); // correct sense + afc.addMap(from, to, mapping); + } + } else { + mapping = this.parsemapType(sequenceMapping, 1, 1); // correct sense afc.addMap(from, to, mapping); } bindjvvobj(mapping, sequenceMapping); diff --git a/src/jalview/io/vamsas/Tree.java b/src/jalview/io/vamsas/Tree.java index fd9294b..1866336 100644 --- a/src/jalview/io/vamsas/Tree.java +++ b/src/jalview/io/vamsas/Tree.java @@ -71,46 +71,14 @@ public class Tree extends DatastoreItem uk.ac.vamsas.objects.core.Tree vtree) { Tree toTree = new Tree(datastore, alignFrame, vtree); - } public Tree(VamsasAppDatastore datastore, jalview.gui.AlignFrame alignFrame, uk.ac.vamsas.objects.core.Tree vtree) { - super(datastore); - tree = vtree; - TreePanel tp = (TreePanel) getvObj2jv(tree); - if (tp != null) - { - if (tree.isUpdated()) - { - Cache.log - .info("Update from vamsas document to alignment associated tree not implemented yet."); - } - } - else - { - // make a new tree - Object[] idata = recoverInputData(tree.getProvenance()); - try - { - if (idata != null && idata[0] != null) - { - inputData = (AlignmentView) idata[0]; - } - ntree = getNtree(); - title = tree.getNewick(0).getTitle(); - if (title == null || title.length() == 0) - { - title = tree.getTitle(); // hack!!!! - } - } catch (Exception e) - { - Cache.log.warn("Problems parsing treefile '" - + tree.getNewick(0).getContent() + "'", e); - } - } + super(datastore, vtree, TreePanel.class); + doJvUpdate(); } private NewickFile getNtree() throws IOException @@ -121,30 +89,111 @@ public class Tree extends DatastoreItem public Tree(VamsasAppDatastore datastore, TreePanel tp2, AlignmentI jal2, uk.ac.vamsas.objects.core.Alignment alignment2) { - super(datastore); + super(datastore, tp2, uk.ac.vamsas.objects.core.Tree.class); jal = jal2; - tp = tp2; + tp = (TreePanel) jvobj; alignment = alignment2; - tree = (uk.ac.vamsas.objects.core.Tree) getjv2vObj(tp); - if (tree == null) + tree = (uk.ac.vamsas.objects.core.Tree) vobj; + doSync(); + } + + /* (non-Javadoc) + * @see jalview.io.vamsas.DatastoreItem#addFromDocument() + */ + public void addFromDocument() + { + tree = (uk.ac.vamsas.objects.core.Tree) vobj; // vtree; + TreePanel tp = (TreePanel) jvobj; // getvObj2jv(tree); + // make a new tree + Object[] idata = recoverInputData(tree.getProvenance()); + try + { + if (idata != null && idata[0] != null) + { + inputData = (AlignmentView) idata[0]; + } + ntree = getNtree(); + title = tree.getNewick(0).getTitle(); + if (title == null || title.length() == 0) + { + title = tree.getTitle(); // hack!!!! + } + } catch (Exception e) { - add(); + Cache.log.warn("Problems parsing treefile '" + + tree.getNewick(0).getContent() + "'", e); + } + } + + /* (non-Javadoc) + * @see jalview.io.vamsas.DatastoreItem#conflict() + */ + public void conflict() + { + Cache.log + .info("Update (with conflict) from vamsas document to alignment associated tree not implemented yet."); + } + + /* (non-Javadoc) + * @see jalview.io.vamsas.DatastoreItem#update() + */ + public void updateToDoc() + { + if (isModifiable(tree.getModifiable())) + { + // synchronize(); // update(); + // verify any changes. + log.info("TODO: Update tree in document from jalview."); } else { - if (isModifiable(tree.getModifiable())) + // handle conflict + log.info("TODO: Add the locally modified tree in Jalview as a new tree in document, leaving locked tree unchanged."); + } + } + /* (non-Javadoc) + * @see jalview.io.vamsas.DatastoreItem#updateFromDoc() + */ + public void updateFromDoc() + { + // should probably just open a new tree panel in the same place as the old one + // TODO: Tree.updateFromDoc + /* + TreePanel tp = (TreePanel) jvobj; // getvObj2jv(tree); + + // make a new tree + Object[] idata = recoverInputData(tree.getProvenance()); + try + { + if (idata != null && idata[0] != null) { - // synchronize(); // update(); - // verify any changes. - System.out.println("Update tree in document."); + inputData = (AlignmentView) idata[0]; } - else + ntree = getNtree(); + title = tree.getNewick(0).getTitle(); + if (title == null || title.length() == 0) { - // handle conflict - System.out.println("Add modified tree as new tree in document."); + title = tree.getTitle(); // hack!!!! } + } catch (Exception e) + { + Cache.log.warn("Problems parsing treefile '" + + tree.getNewick(0).getContent() + "'", e); + }*/ + log.debug("Update the local tree in jalview from the document."); + + if (isModifiable(tree.getModifiable())) + { + // synchronize(); // update(); + // verify any changes. + log.debug("Update tree in document from jalview."); + } + else + { + // handle conflict + log.debug("Add modified jalview tree as new tree in document."); } } @@ -430,7 +479,7 @@ public class Tree extends DatastoreItem * add jalview object to vamsas document * */ - public void add() + public void addToDocument() { tree = new uk.ac.vamsas.objects.core.Tree(); bindjvvobj(tp, tree); -- 1.7.10.2