/*
* Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
* Copyright (C) $$Year-Rel$$ The Jalview Authors
*
* This file is part of Jalview.
*
* Jalview is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* Jalview is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Jalview. If not, see .
* The Jalview Authors are detailed in the 'AUTHORS' file.
*/
package jalview.io.vamsas;
import jalview.bin.Cache;
import jalview.io.VamsasAppDatastore;
import jalview.util.MessageManager;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.IdentityHashMap;
import java.util.Vector;
import uk.ac.vamsas.client.IClientDocument;
import uk.ac.vamsas.client.Vobject;
import uk.ac.vamsas.client.VorbaId;
import uk.ac.vamsas.objects.core.Entry;
import uk.ac.vamsas.objects.core.Provenance;
import uk.ac.vamsas.objects.core.Seg;
/**
* Holds all the common machinery for binding objects to vamsas objects
*
* @author JimP
*
*/
public abstract class DatastoreItem
{
/**
*
*/
Entry provEntry = null;
IClientDocument cdoc;
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)
{
if (jv2vobj.containsKey(jvobj))
{
return cdoc.getObject((VorbaId) jv2vobj.get(jvobj));
}
if (Cache.log.isDebugEnabled())
{
Cache.log.debug("Returning null VorbaID binding for jalview object "
+ jvobj);
}
return null;
}
/**
*
* @param vobj
* @return Jalview datamodel object bound to the vamsas document object
*/
protected Object getvObj2jv(uk.ac.vamsas.client.Vobject vobj)
{
if (vobj2jv == null)
return null;
VorbaId id = vobj.getVorbaId();
if (id == null)
{
id = cdoc.registerObject(vobj);
Cache.log
.debug("Registering new object and returning null for getvObj2jv");
return null;
}
if (vobj2jv.containsKey(vobj.getVorbaId()))
{
return vobj2jv.get(vobj.getVorbaId());
}
return null;
}
/**
* note: this is taken verbatim from jalview.io.VamsasAppDatastore with added
* call to updateRegistryEntry
*
* @param jvobj
* @param vobj
*/
protected void bindjvvobj(Object jvobj, uk.ac.vamsas.client.Vobject vobj)
{
VorbaId id = vobj.getVorbaId();
if (id == null)
{
id = cdoc.registerObject(vobj);
if (id == null || vobj.getVorbaId() == null
|| cdoc.getObject(id) != vobj)
{
Cache.log.error("Failed to get id for "
+ (vobj.isRegisterable() ? "registerable"
: "unregisterable") + " object " + vobj);
}
}
if (vobj2jv.containsKey(vobj.getVorbaId())
&& !(vobj2jv.get(vobj.getVorbaId())).equals(jvobj))
{
Cache.log
.debug("Warning? Overwriting existing vamsas id binding for "
+ vobj.getVorbaId(),
new Exception(
MessageManager
.getString("exception.overwriting_vamsas_id_binding")));
}
else if (jv2vobj.containsKey(jvobj)
&& !((VorbaId) jv2vobj.get(jvobj)).equals(vobj.getVorbaId()))
{
Cache.log
.debug("Warning? Overwriting existing jalview object binding for "
+ jvobj,
new Exception(
MessageManager
.getString("exception.overwriting_jalview_id_binding")));
}
/*
* Cache.log.error("Attempt to make conflicting object binding! "+vobj+" id "
* +vobj.getVorbaId()+" already bound to "+getvObj2jv(vobj)+" and "+jvobj+"
* already bound to "+getjv2vObj(jvobj),new Exception("Excessive call to
* bindjvvobj")); }
*/
// we just update the hash's regardless!
Cache.log.debug("Binding " + vobj.getVorbaId() + " to " + jvobj);
vobj2jv.put(vobj.getVorbaId(), jvobj);
// JBPNote - better implementing a hybrid invertible hash.
jv2vobj.put(jvobj, vobj.getVorbaId());
if (jvobj == this.jvobj || vobj == this.vobj)
{
updateRegistryEntry(jvobj, vobj);
}
}
/**
* update the vobj and jvobj references and the registry entry for this
* datastore object called by bindjvvobj and replacejvobjmapping
*/
private void updateRegistryEntry(Object jvobj, Vobject vobj)
{
if (this.jvobj != null && this.vobj != null)
{
Cache.log.debug("updating dsobj registry. ("
+ this.getClass().getName() + ")");
}
this.jvobj = jvobj;
this.vobj = vobj;
dsReg.registerDsObj(this);
}
/**
* replaces oldjvobject with newjvobject in the Jalview Object <> VorbaID
* binding tables note: originally taken verbatim from
* jalview.io.VamsasAppDatastore with added call to updateRegistryEntry
*
* @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(MessageManager.formatMessage(
"error.implementation_error_old_jalview_object_not_bound",
new String[] { oldjvobject.toString() }));
}
if (newjvobject != null)
{
jv2vobj.put(newjvobject, vobject);
vobj2jv.put(vobject, newjvobject);
updateRegistryEntry(newjvobject, vobj);
}
}
public DatastoreItem()
{
super();
}
public DatastoreItem(VamsasAppDatastore datastore)
{
this();
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(
MessageManager
.formatMessage(
"error.implementation_error_vamsas_doc_class_should_bind_to_type",
new String[] { vobj.getClass().toString(),
boundType.toString(),
jvobj.getClass().toString() }));
}
dsReg.registerDsObj(this);
}
/**
* 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(
MessageManager
.formatMessage(
"error.implementation_error_vamsas_doc_class_should_bind_to_type",
new String[] { jvobj2.getClass().toString(),
boundToType.toString(),
vobj.getClass().toString() }));
}
dsReg.registerDsObj(this);
}
/**
* 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()
{
dsReg.registerDsObj(this);
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()
{
dsReg.registerDsObj(this);
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;
protected DatastoreRegistry dsReg;
public void initDatastoreItem(VamsasAppDatastore ds)
{
datastore = ds;
dsReg = ds.getDatastoreRegisty();
initDatastoreItem(ds.getProvEntry(), ds.getClientDocument(),
ds.getVamsasObjectBinding(), ds.getJvObjectBinding());
}
private void initDatastoreItem(Entry provEntry, IClientDocument cdoc,
Hashtable vobj2jv, IdentityHashMap jv2vobj)
{
this.provEntry = provEntry;
this.cdoc = cdoc;
this.vobj2jv = vobj2jv;
this.jv2vobj = jv2vobj;
}
protected boolean isModifiable(String modifiable)
{
return modifiable == null; // TODO: USE VAMSAS LIBRARY OBJECT LOCK METHODS)
}
protected Vector getjv2vObjs(Vector alsq)
{
Vector vObjs = new Vector();
Enumeration elm = alsq.elements();
while (elm.hasMoreElements())
{
vObjs.addElement(getjv2vObj(elm.nextElement()));
}
return vObjs;
}
// utility functions
/**
* get start