moved IClientDocument implementation into the simpleclient package.
[vamsas.git] / src / org / vamsas / client / object.java
index 6498d70..4757589 100644 (file)
@@ -26,17 +26,29 @@ public abstract class object {
   static Log log = LogFactory.getLog(object.class);
   
   /**
-   * unique id for all vamsas objects allows unambiguous referencing to any
-   * object in the vamsas document
+   * 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; 
 
-  protected long __last_hash = 0;
-
-  protected boolean registerable = false;
 
+  /**
+   * 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;
 
   /**
@@ -48,7 +60,7 @@ public abstract class object {
   }
 
   /**
-   * set the isRegisterable flag based on the presence of a 'String id' field in
+   * set the isRegisterable flag based on the presence of a 'private String _id' field in
    * the reflected class instance.
    */
   private void testInstanceForIdField() {
@@ -67,7 +79,7 @@ public abstract class object {
   }
 
   /**
-   * update the object instance's String id field, based on the contents of the
+   * 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() {
@@ -76,23 +88,20 @@ public abstract class object {
         try {
           Method fd = this.getClass().getMethod("setId", new Class[] { String.class });
           fd.invoke((Object) this, new Object[] {new String(this.getVorbaId().id)});
-          log.info(this.getClass().getName()+" called setInstanceIdField!");
+          log.debug(this.getClass().getName()+" called setInstanceIdField!");
         } catch (InvocationTargetException e) { 
-          System.err
-          .println("SourceGeneration of "
+          log.error("SourceGeneration of "
               + this.getClass().toString()
-              + "\n has resulted in an inaccessible 'setId' method!\nCannot set ID from the vorbaId object.");
-          e.printStackTrace(System.err);
+              + "\n has resulted in an inaccessible 'setId' method!\nCannot set ID from the vorbaId object.", e);
         }
         catch (IllegalAccessException e) {
-          System.err
-              .println("SourceGeneration of "
+          log.error("SourceGeneration of "
                   + this.getClass().toString()
-                  + "\n has resulted in an inaccessible 'setId' method!\nCannot set ID from the vorbaId object.");
-          e.printStackTrace(System.err);
+                  + "\n has resulted in an inaccessible 'setId' method!\nCannot set ID from the vorbaId object.", e);
         } catch (SecurityException e) {
-          e.printStackTrace(System.err);
+          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 {
@@ -104,8 +113,11 @@ public abstract class object {
   /**
    * 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 void doHash() {
+  synchronized protected boolean doHash() {
+    long __old_hash = __last_hash;
     __last_hash = 0;
     VorbaId thisid = vorbaId;
     IVorbaIdFactory factory = __vorba;
@@ -116,6 +128,7 @@ public abstract class object {
     vorbaId = thisid;
     __vorba = factory;
     __stored_in_document = stored;
+    return (__old_hash==0) || (__old_hash == __last_hash);
   }
 
   /**
@@ -128,6 +141,7 @@ public abstract class object {
    * @return
    */
   protected boolean merge(object laterCopy) {
+    log.warn(this.getClass().getName()+".merge() not implemented.");
     return true;
   }
 
@@ -177,9 +191,10 @@ public abstract class object {
   /**
    * for use by Vorba agent to reflect state of vamsas object to client
    * application.
-   * 
-   * @param __stored_in_document
-   *          The __stored_in_document to set.
+   * 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;
@@ -197,18 +212,16 @@ public abstract class object {
   }
 
   /**
-   * @return Returns the registerable.
+   * @return true if object can have a vorbaId
    */
   public boolean isRegisterable() {
     return registerable;
   }
 
   /**
-   * Called during unmarshalling - if object has an id string, it is
-   * registerable.
-   * 
-   * @param registerable
-   *          The registerable to set.
+   * 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;
@@ -218,14 +231,20 @@ public abstract class object {
    * 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". 
    */
   protected void __ensure_instance_ids() {
     if (__vorba==null)
       throw new Error("Improperly intialised org.vamsas.client.object - no VorbaFactory given.");
-    log.info(this.getClass()+".__ensure_instance_ids()");
+    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 {
@@ -233,7 +252,9 @@ public abstract class object {
       descriptor = this.getClass().getClassLoader().loadClass(this.getClass().getName()+"Descriptor");
       descimpl = (XMLClassDescriptorImpl) descriptor.getConstructor(null).newInstance(null);
     } catch (Exception e) {
-      log.error("Couldn't resolve descriptor for "+this.getClass().getName());
+      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();
@@ -280,7 +301,7 @@ public abstract class object {
                   //}
                 } else {
                   rf = (object) fval;
-                  log.info("Got value for "+fields[i].getFieldName());
+                  log.debug("Got value for "+fields[i].getFieldName());
                 }
               }
             } else {