X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Forg%2Fvamsas%2Fclient%2Fobject.java;h=18054b7dbc3ea85ddecbe7d914f18688508ad6ad;hb=65a8158d49f3c25e78deba6ddd2f4a83fc695821;hp=c1a13da8d7435a0867eec2af0105984050e9a9e4;hpb=c8fa5246fcdb1fb5544a8116a48eda973ed2f222;p=vamsas.git diff --git a/src/org/vamsas/client/object.java b/src/org/vamsas/client/object.java index c1a13da..18054b7 100644 --- a/src/org/vamsas/client/object.java +++ b/src/org/vamsas/client/object.java @@ -2,70 +2,336 @@ * */ package org.vamsas.client; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Iterator; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.exolab.castor.mapping.FieldDescriptor; +import org.exolab.castor.mapping.FieldHandler; +import org.exolab.castor.xml.util.XMLClassDescriptorImpl; +import org.vamsas.test.simpleclient.VamsasArchive; + /** - * Base class for all Vamsas objects extracted - * from an IClientDocument. - * An object maybe registered or unregistered. + * Base class for all Vamsas objects extracted from an IClientDocument. An + * object maybe registered or unregistered. * * @author jimp - * + * */ public abstract class object { + static Log log = LogFactory.getLog(object.class); + + /** + * true if object was stored in a vamsas Document or has been retrieved from it + */ + protected boolean __stored_in_document = false; + /** + * memory of the last doHash() value computed for the object + * @see doHash() + */ + protected long __last_hash = 0; + /** + * set by testInstanceForIdField() if object should have a VorbaId + */ + protected boolean registerable = false; - /** - * unique id for all vamsas objects - * allows unambiguous referencing - * to any object in the vamsas document - */ - protected boolean __stored_in_document=false; - protected VorbaId __vorba__id=null; - protected VorbaIdFactory __vorba=null; - /** - * - * @return true if object is registered - */ - public boolean isRegistered() { - return (__vorba__id!=null); - } - /** - * Method to get fixed reference for - * the object in the vamsas document. - * @returns null if object is neither registered - * or not associated with a properly instantiated - * VorbaIdFactory. - */ - public String getId() { - if (__vorba__id==null) { - // Try to use the associated factory. - if (__vorba!=null) - if ((__vorba__id = __vorba.makeVorbaId())==null) - return null; // Factory not valid. - else - return null; + + /** + * unique id for all vamsas objects allows unambiguous referencing to any + * object in the vamsas document + */ + protected VorbaId vorbaId = null; + + /** + * the source of unique VorbaIds. + */ + protected IVorbaIdFactory __vorba = null; + + /** + * + */ + protected object() { + super(); + testInstanceForIdField(); + } + + /** + * set the isRegisterable flag based on the presence of a 'private String _id' field in + * the reflected class instance. + */ + private void testInstanceForIdField() { + // look for the id field (should be an NCName string) + // TODO: decide if 'id' is an appropriate reserved attribute name for the VorbaId + try { + java.lang.reflect.Field fd = this.getClass().getDeclaredField("_id"); + if (String.class.isAssignableFrom(fd.getType())) { + this.setRegisterable(true); } - return __vorba__id.getId(); + } catch (SecurityException e) { + e.printStackTrace(); + } catch (NoSuchFieldException e) { + this.setRegisterable(false); } - /** - * used by the IClient implementation - * to generate unique Id based on - * client applications current namespace. - */ - protected void setVorbaId(VorbaId newid) { - __vorba__id = newid; + } + + /** + * update the object instance's _id field, based on the contents of the + * VorbaId. Only call this if you mean to do it! + */ + protected void setInstanceIdField() { + if (registerable) { + if (__vorba != null) + try { + Method fd = this.getClass().getMethod("setId", new Class[] { String.class }); + fd.invoke((Object) this, new Object[] {new String(this.getVorbaId().id)}); + log.debug(this.getClass().getName()+" called setInstanceIdField!"); + } catch (InvocationTargetException e) { + log.error("SourceGeneration of " + + this.getClass().toString() + + "\n has resulted in an inaccessible 'setId' method!\nCannot set ID from the vorbaId object.", e); + } + catch (IllegalAccessException e) { + log.error("SourceGeneration of " + + this.getClass().toString() + + "\n has resulted in an inaccessible 'setId' method!\nCannot set ID from the vorbaId object.", e); + } catch (SecurityException e) { + log.error("Security access violation for "+this.getClass().toString(),e); + } catch (NoSuchMethodException e) { + log.warn(this.getClass().toString()+" was erroneously marked as a Vorba object class (Implementation error?)"); + this.setRegisterable(false); + } + } else { + System.err.println("Client error. Trying to setInstanceIdField on a " + + this.getClass().toString() + " (which cannot be given a vorbaId)"); } - - /** - * @return true if object is present in Vamsas Document. - */ - public boolean is__stored_in_document() { - return __stored_in_document; + } + + /** + * calculate a hash for the object with all housekeeping fields at standard + * values. (isRegisterable is an immutable attribute property) + * TODO: decide if __stored_in_document should be included in the hash or not. + * @return true if new hash different to last hash + */ + synchronized protected boolean doHash() { + long __old_hash = __last_hash; + __last_hash = 0; + VorbaId thisid = vorbaId; + IVorbaIdFactory factory = __vorba; + boolean stored = __stored_in_document; + vorbaId = null; + __vorba = null; + __last_hash = this.hashCode(); + vorbaId = thisid; + __vorba = factory; + __stored_in_document = stored; + return (__old_hash==0) || (__old_hash == __last_hash); + } + + /** + * TODO: combine two versions of the same collection object to resolve + * asynchronous updates to the same vamsas object Merges two vamsas objects, + * one of which is a later version of the earlier (ie they have the same + * vorbaId but one is a later version recently read from the vamsasDocument + * collection. + * + * @return + */ + protected boolean merge(object laterCopy) { + log.warn(this.getClass().getName()+".merge() not implemented."); + return true; + } + + /** + * + * @return true if object is registered + */ + public boolean isRegistered() { + return (registerable) ? (vorbaId != null) : false; + } + + /** + * Method to get fixed reference for the object in the vamsas document. + * + * @returns null if object is neither registered or not associated with a + * properly instantiated VorbaIdFactory. + */ + public VorbaId getVorbaId() { + if (registerable && vorbaId == null) { + // Try to use the associated factory. + if (__vorba != null) + if ((vorbaId = __vorba.makeVorbaId(this)) == null) + return null; // Factory not valid. + else { + this.setInstanceIdField(); + return vorbaId; + } } - /** - * for use by Vorba agent to reflect state of - * vamsas object to client application. - * @param __stored_in_document The __stored_in_document to set. - */ - protected void set__stored_in_document(boolean __stored_in_document) { - this.__stored_in_document = __stored_in_document; + return vorbaId; + } + + /** + * used by the IClient implementation to generate unique Id based on client + * applications current namespace. + */ + protected void setVorbaId(VorbaId newid) { + vorbaId = newid; + } + + /** + * @return true if object is present in Vamsas Document. + */ + public boolean is__stored_in_document() { + return __stored_in_document; + } + + /** + * for use by Vorba agent to reflect state of vamsas object to client + * application. + * Setting stored_in_document on a registerable object without a + * vorbaId will mean is will *never* get a vorbaId and + * horrible things will happen. + * @param __stored_in_document true if object has been marshalled into current document. + */ + protected void set__stored_in_document(boolean __stored_in_document) { + this.__stored_in_document = __stored_in_document; + } + + /** + * __last_hash is the hash value computed when the object was last checked + * against a IClientDocument generated by the object's parent IClient + * instance. + * + * @return Returns the __last_hash. + */ + public long get__last_hash() { + return __last_hash; + } + + /** + * @return true if object can have a vorbaId + */ + public boolean isRegisterable() { + return registerable; + } + + /** + * Called by __testInstanceForidField and the post-unmarshalling handler + * to indicate if object will have a vorbaId. + * @param registerable + */ + protected void setRegisterable(boolean registerable) { + this.registerable = registerable; + } + /** + * ensure's internal id field corresponds to vorbaId and + * cascade through all fields referring to an instance of object + * calling the same method on them. + * TODO: LATER: properly apply castors own field mechanisms to get at accessors + * TODO: FIX CYCLIC __ensure+instance_ids + * Implementation note for the todo: + * this works like a depth-first search over all vamsas objects in an vamsasDocument. + * The doHash() function is used as the 'visited' flag - + * this *is not* a valid heuristic, although it will work "most of the time". + * TODO: LATER? Add another method for setDefaultProvenanceField (in the spirit of setInstanceIdField) using the info from the __vorba.getClient/User/Session methods + */ + protected void __ensure_instance_ids() { + if (__vorba==null) + throw new Error("Improperly intialised org.vamsas.client.object - no VorbaFactory given."); + log.debug("doing "+this.getClass()+".__ensure_instance_ids()"); + if (!__stored_in_document && registerable) + setInstanceIdField(); + if (!doHash()) + return; // nothing has changed in this object - probably visited it before. + Class descriptor = null; + XMLClassDescriptorImpl descimpl = null; + try { + // castor descriptor resolver magic + descriptor = this.getClass().getClassLoader().loadClass(this.getClass().getName()+"Descriptor"); + descimpl = (XMLClassDescriptorImpl) descriptor.getConstructor(null).newInstance(null); + } catch (Exception e) { + log.fatal("Source Generation Error!: Couldn't resolve descriptor for " + +this.getClass().getName() + +" was 'generate descriptors' set for castorbuilder.properties?"); + return; } + FieldDescriptor fields[] = descimpl.getFields(); + for (int i=0,j=fields.length; i