new marshaller instance ensures we use marshalling properties and correct validation...
[vamsas.git] / src / uk / ac / vamsas / client / Vobject.java
index a8a32e4..6603f1a 100644 (file)
@@ -13,7 +13,8 @@ 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;
+
+import uk.ac.vamsas.test.simpleclient.VamsasArchive;
 
 /**
  * Base class for all Vamsas objects extracted from an IClientDocument. An
@@ -34,6 +35,10 @@ public abstract class Vobject {
    */
   protected boolean __updated_since_last_read = false;
   /**
+   * true if Vobject appeared in the document after the last access by this vamsas library instance
+   */
+  protected boolean __added_since_last_read = false;
+  /**
    * memory of the last doHash() value computed for the Vobject 
    * @see doHash()
    */
@@ -75,6 +80,12 @@ public abstract class Vobject {
     super();
     testInstanceForIdField();
   }
+  /**
+   * Override Object.hashCode with base value for castor generated object hashcodes.
+   */
+  public int hashCode() {
+    return 17;
+  }
   java.lang.reflect.Field ___id_field=null; // set to ease pain of reflection
   /**
    * set the isRegisterable flag based on the presence of a 'private String _id' field in
@@ -174,31 +185,66 @@ public abstract class Vobject {
     }
     return null;
   }
-
+  /**
+   * calls the castor-generated hashCode() method
+   * @return
+   */
+  protected int __callHash() {
+    try {
+      Method fd = this.getClass().getMethod("hashCode", (Class[]) null);
+      Object hashvalue = fd.invoke((Object) this, (Object[]) null);
+      if (log.isDebugEnabled())
+        log.debug(this.getClass().getName()+" called hashCode()!");
+      if (hashvalue!=null && hashvalue instanceof Integer) {
+        return ((Integer) hashvalue).intValue();
+      }
+    } catch (InvocationTargetException e) { 
+      log.error("SourceGeneration of "
+          + this.getClass().toString()
+          + "\n has resulted in an inaccessible 'hashCode' method!\nHave you set org.exolab.castor.builder.equalsmethod=true in castorbuilder.properties ?.", e);
+    }
+    catch (IllegalAccessException e) {
+      log.error("SourceGeneration of "
+              + this.getClass().toString()
+              + "\n has resulted in an inaccessible 'hashCode' method!\nHave you set org.exolab.castor.builder.equalsmethod=true in castorbuilder.properties ?.", e);
+    } catch (SecurityException e) {
+      log.error("Security access violation for "+this.getClass().toString(),e);
+    } catch (NoSuchMethodException e) {
+      log.warn(this.getClass().toString()+" was erroneously extending from a Vorba Vobject class (Implementation error? no hashCode() method)" +
+          "\nHave you set org.exolab.castor.builder.equalsmethod=true in castorbuilder.properties ?.", e);
+    }
+    return 0;
+  }
   /**
    * calculate a hash for the Vobject 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
+   * TODO: LATER: make this hash function compute a hash that truly reflects changes in Vobject attributes for benefit of update mechanism
+   * @return true if new hash different to last hash (or first time its been computed)
    */
   synchronized protected boolean doHash() {
+    boolean stored = __stored_in_document;
+    __stored_in_document=false;
+    boolean updated = __updated_since_last_read;
+    __updated_since_last_read=false;
+    boolean added_since=__added_since_last_read;
+    __added_since_last_read=false;
     long __old_hash = __last_hash;
     __last_hash = 0;
+    // leave registerable - doesn't change
+    boolean visited = __visited;
+    __visited=false;
     Vobject _V_parent=V_parent;
     V_parent=null;
     VorbaId thisid = vorbaId;
+    vorbaId = null;
     IVorbaIdFactory factory = __vorba;
-    boolean stored = __stored_in_document;
-    boolean updated = __updated_since_last_read;
-    boolean visited = __visited;
+    __vorba = null;
     java.lang.reflect.Field idfield = ___id_field;
     ___id_field=null;
-    __updated_since_last_read=false;
-    vorbaId = null;
-    __vorba = null;
-    __visited=false;
+    long l_hash = __l_hash;
+    __l_hash = 0;
     // compute hash
-    __last_hash = this.hashCode();
+    __last_hash = __callHash();
     // reset houseskeeping variables
     ___id_field=idfield;
     vorbaId = thisid;
@@ -207,7 +253,10 @@ public abstract class Vobject {
     __updated_since_last_read=updated;
     V_parent=_V_parent;
     __visited=visited;
-    return (__old_hash==0) || (__old_hash == __last_hash);
+    __added_since_last_read=added_since;
+    __l_hash = l_hash;
+    // return true if first time hash was computed or if hash has changed
+    return (__old_hash==0) || (__old_hash != __last_hash);
   }
 
   /**
@@ -274,16 +323,24 @@ public abstract class Vobject {
   /**
    * @return true if this object has been updated in the currently stored document since the last time a Vobject with the same ID was read from a Vamsas Document
    */
-  public boolean is__updated_since_last_read() {
+  public boolean isUpdated() {
     return __updated_since_last_read;
   }
-
+  /**
+   * 
+   * @return true if this object was added to the document after the last time the vamsas library acessed the session document
+   */
+  public boolean isNewInDocument() {
+    return __added_since_last_read;
+  }
   /**
    * Set internal flag to indicate this object was updated since the last document read
    * @param __updated_since_last_read the __updated_since_last_read to set
    */
   protected void set__updated_since_last_read(boolean __updated_since_last_read) {
     this.__updated_since_last_read = __updated_since_last_read;
+    if(__updated_since_last_read && log.isDebugEnabled())
+      log.debug("Registered update for "+this.getVorbaId());
   }
 
   /**
@@ -296,6 +353,18 @@ public abstract class Vobject {
    */
   protected void set__stored_in_document(boolean __stored_in_document) {
     this.__stored_in_document = __stored_in_document;
+    if(__stored_in_document && log.isDebugEnabled())
+      log.debug("Retrieved document object: "+this.getVorbaId());
+  }
+
+  /**
+   * @param __added_since_last_read the __added_since_last_read to set
+   */
+  protected void set__added_since_last_read(boolean __added_since_last_read) {
+    this.__added_since_last_read = __added_since_last_read;
+
+    if(__added_since_last_read && log.isDebugEnabled())
+      log.debug("New object in document: "+this.getVorbaId());
   }
 
   /**
@@ -305,7 +374,7 @@ public abstract class Vobject {
    * 
    * @return Returns the __last_hash.
    */
-  public int get__last_hash() {
+  public long get__last_hash() {
     return __last_hash;
   }
 
@@ -350,13 +419,16 @@ public abstract class Vobject {
     if (__visited==visited)
       return;
     __visited=visited;
-    __vorba.updateHashValue(this);
+    //__vorba.updateHashValue(this);
     
     Class descriptor = null;
     XMLClassDescriptorImpl descimpl = null;
     try {
       // castor descriptor resolver magic
-      descriptor = this.getClass().getClassLoader().loadClass(this.getClass().getName()+"Descriptor");
+      StringBuffer desname = new StringBuffer(this.getClass().getName());
+      desname.insert(desname.lastIndexOf("."), ".descriptors");
+      desname.append("Descriptor");
+      descriptor = this.getClass().getClassLoader().loadClass(desname.toString());
       descimpl = (XMLClassDescriptorImpl) descriptor.getConstructor((Class[])null).newInstance((Object[])null);
     } catch (Exception e) {
       log.fatal("Source Generation Error!: Couldn't resolve descriptor for "
@@ -461,4 +533,29 @@ public abstract class Vobject {
   protected void setV_parent(Vobject V_parent) {
     this.V_parent = V_parent;
   }
+  /**
+   * LhashValue - used for change detection between document updates.
+   */
+  private long __l_hash=0;
+  /**
+   * set the base LhashValue for this object
+   * @param checksum
+   */
+  protected void __setInitHash(long checksum) {
+    __l_hash = checksum;
+  }
+  /**
+   * compute the final LhashValue as a difference between checksum and the current base
+   * @param checksum
+   */
+  protected void __setFinalHash(long checksum) {
+    __l_hash = checksum - __l_hash;
+  }
+  /**
+   * get the LhashValue for this object
+   * @return the difference in values passed to __setFinalHash less __setInitHash
+   */
+  protected long __getLHash() {
+    return __l_hash;
+  }
 }