refactored org to uk
[vamsas.git] / src / org / vamsas / client / VorbaXmlBinder.java
index ec6c7f4..556386d 100644 (file)
@@ -12,6 +12,8 @@ import java.util.Hashtable;
 import java.util.Iterator;
 import java.util.Vector;
 
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.exolab.castor.mapping.FieldHandler;
 import org.exolab.castor.mapping.GeneralizedFieldHandler;
 import org.exolab.castor.mapping.ValidityException;
@@ -24,23 +26,28 @@ import org.exolab.castor.xml.Unmarshaller;
 import org.exolab.castor.xml.ValidationException;
 import org.vamsas.objects.core.VamsasDocument;
 /**
- * Implements the Vamsas object ID machinery for translating 
- * between non-volatile XML IDs and object references. Use the
+ * Implements the Vamsas Vobject ID machinery for translating 
+ * between non-volatile XML IDs and Vobject references. Use the
  * marshalling and unmarshalling methods in this class in order
- * to avoid validation exceptions when marshalling new objects
+ * to add automatically computed values for required fields in objects,
+ * so as to avoid validation exceptions when marshalling new objects
  * into the vamsas document.
  */
-public class VorbaXmlBinder extends GeneralizedFieldHandler implements UnmarshalListener, MarshalListener {
+public class VorbaXmlBinder implements UnmarshalListener {
+  private static Log log = LogFactory.getLog(VorbaXmlBinder.class);
   private final IVorbaIdFactory vorbafactory;
 
   private final Vector obj;
-
+  private final Hashtable oldobjhashes;
   private final Hashtable objrefs;
-
-  public VorbaXmlBinder(IVorbaIdFactory vorbafactory, Vector obj, Hashtable objrefs) {
+  private final Vector updatedobjs;
+  
+  public VorbaXmlBinder(IVorbaIdFactory vorbafactory, Vector obj, Hashtable objrefs, Hashtable oldobjhashes, Vector updatedobjs) {
     this.vorbafactory = vorbafactory;
     this.obj = obj;
     this.objrefs = objrefs;
+    this.oldobjhashes = oldobjhashes;
+    this.updatedobjs = updatedobjs;
   }
 
   /*
@@ -58,6 +65,12 @@ public class VorbaXmlBinder extends GeneralizedFieldHandler implements Unmarshal
    *      java.lang.Object, java.lang.Object)
    */
   public void fieldAdded(String fieldName, Object parent, Object child) {
+    if (parent instanceof Vobject && child instanceof Vobject) {
+      if (((Vobject) child).V_parent==null) {
+        // System.err.println("Setting parent of "+fieldName);
+        ((Vobject) child).setV_parent((Vobject) parent);
+      }
+    }
   }
 
   /*
@@ -70,41 +83,49 @@ public class VorbaXmlBinder extends GeneralizedFieldHandler implements Unmarshal
   
   /*
    * Check if the object has an 'id' field - if it does, copy the value into
-   * the VorbaId field of object, and add the object to the VorbaId hash.
+   * the VorbaId field of Vobject, and add the Vobject to the VorbaId hash.
    * 
    * @see org.exolab.castor.xml.UnmarshalListener#unmarshalled(java.lang.Object)
    */
   public void unmarshalled(Object newobj) {
-    if (newobj instanceof object) {
-      object nobj = (object) newobj;
+    if (newobj instanceof Vobject) {
+      Vobject nobj = (Vobject) newobj;
       nobj.set__stored_in_document(true);
-      Field fd = null;
       try {
-        if (nobj.isRegisterable()) {
+        if (nobj.isRegisterable() && nobj.___id_field!=null) {
+          VorbaId nobj_id=null;
           // look for the id field (should be an NCName string)
           nobj.__vorba = vorbafactory;
-          fd = nobj.getClass().getField("id");
-          String idstring;
-          if (fd.get(nobj) != null) {
-            idstring = (String) fd.get(nobj);
+          // use the Vobject accessor method to avoid unpleasant security exceptions.
+          String idstring = nobj.__getInstanceIdField();
+          if (idstring!=null) { 
             if (idstring.length() > 0) {
-              if (!objrefs.containsKey(idstring)) {
-                objrefs.put(idstring, nobj);
-                nobj.setVorbaId(VorbaId.newId(idstring));
-              } else {
+              nobj.setVorbaId(VorbaId.newId(idstring));
+              if (objrefs.containsKey(nobj_id=nobj.getVorbaId()) && !objrefs.get(nobj.getVorbaId()).equals(nobj)) {
                 System.err.println("Serious problem : duplicate id '"+idstring+"' found! expect badness.");
                 // TODO: HANDLE duplicate XML ids correctly
               }
+              objrefs.put(nobj_id, nobj);
             } else {
               // add to list of objects without a valid vorbaId
               obj.add(nobj);
             }
           } else {
-            // add to list of objects without a valid vorbaId
+            // TODO: add to list of objects without a valid vorbaId
             obj.add(nobj);
           }
           
         nobj.doHash();
+        // check to see if new object was present in old object hash
+        if (oldobjhashes.containsKey(nobj.getVorbaId())) {
+          Vobjhash oldhash = (Vobjhash) oldobjhashes.get(nobj.getVorbaId());
+          if (oldhash.isUpdated(nobj)) {
+            // mark the object as updated in this document read.
+            nobj.set__updated_since_last_read(true);
+            oldobjhashes.put(nobj_id, new Vobjhash(nobj));
+            updatedobjs.addElement(nobj);
+          }
+        }
       }
       } catch (Exception e) {
         return;
@@ -127,74 +148,83 @@ public class VorbaXmlBinder extends GeneralizedFieldHandler implements Unmarshal
       throws IOException, MarshalException, ValidationException {
     // Ensure references
     if (vorba==null)
-      throw new Error("Null VorbaIdVactory Parameter");
+      throw new Error("Null VorbaIdFactory Parameter");
     if (doc.__vorba==null)
       doc.__vorba = vorba;
     doc.__ensure_instance_ids(); // this may take a while. Do we allow for cyclic references ? 
-    final Vector refstomake = new Vector();
-    VorbaXmlBinder binder = new VorbaXmlBinder(vorba, refstomake, vorba.extantids);
-    Marshaller marshaller = new Marshaller(outstream);
-    marshaller.setMarshalAsDocument(true);
-    marshaller.setMarshalListener(binder);
-    marshaller.marshal(doc);
+    doc.marshal(outstream);
+    
   }
-
+  /**
+   * creates new VorbaId references where necessary for newly unmarshalled objects
+   * @param unrefed
+   * @param objrefs
+   * @return false if any new object references were made
+   */
   private static boolean ensure_references(Vector unrefed, Hashtable objrefs) {
     boolean sync=true;
     if (unrefed.size()>0) {
       sync=false; // document is out of sync - ids have been created.
       java.util.Iterator newobj = unrefed.listIterator();
       while (newobj.hasNext()) {
-        object o = (object) newobj.next();
+        Vobject o = (Vobject) newobj.next();
         // forces registration and id field update.
         VorbaId id = o.getVorbaId();
         if (!objrefs.containsKey(id)) {
-          objrefs.put(id.id, o);
+          objrefs.put(id, o);
         } else {
-          throw new Error("Serious! Duplicate reference made by vorbaIdFactory!");
+          if (!objrefs.get(id).equals(o))
+            throw new Error("Serious! Duplicate reference made by vorbaIdFactory!");
         }
       }
     }
     return sync;
   }
   /**
-     * Unmarshals a vamsasDocument object from a stream, registers
+     * Unmarshals a vamsasDocument Vobject from a stream, registers
      * unregistered objects, records existing VorbaIds, and completes 
-     * the org.vamsas.client.object housekeeping fields.
+     * the org.vamsas.client.Vobject housekeeping fields.
      * For a valid unmarshalling, the array of returned objects also includes
      * a <return>sync</return> parameter which is true if new VorbaIds
      * were created. If sync is false, then the caller should ensure that the
      * vamsasDocument is written back to disk to propagate the new VorbaIds.
      *  TODO: ensure that provenance is correct for newly registered objects
+     * as getVamsasObjects but will detect updated objects based on differing hash values
+     * obtained from the VorbaIdFactory's VorbaId, Vobject.get__last_Hash() pairs (if any) 
      * @param instream - the XML input stream 
    * @param factory - the SimpleClient's properly configured VorbaId factory to make new references.
-   * @param root the root element's org.vamsas.objects.core object.
-     * @return null or {(Object) VamsasDocument object, (Object) Hashtable of object references, (Object) Boolean(sync) }
+   * @param root the root element's org.vamsas.objects.core Vobject.
+     * @return null or {(Object) VamsasDocument Vobject, (Object) Hashtable of Vobject references, (Object) Boolean(sync), (Object) Vector of updated objects in document }
      */
   public static Object[] getVamsasObjects(Reader instream,
-        IVorbaIdFactory factory, object root) {
-      Unmarshaller unmarshaller = new Unmarshaller(root);
+        VorbaIdFactory factory, Vobject root) {  
+    Unmarshaller unmarshaller = new Unmarshaller(root);
       unmarshaller.setIDResolver(new IDResolver() {
         public Object resolve(String id) {
-          System.err.println("Warning - id " + id
-              + " is not found in the VamsasDocument!");
+          VorbaXmlBinder.log.warn("Warning - id " + id
+              + " is not found in the Vamsas XML!");
           return null;
         }
       });
-      Hashtable refbase = new Hashtable();
-      Vector unrefed = new Vector();
-      final Hashtable objrefs = refbase;
-      final IVorbaIdFactory vorbafactory = factory;
-      final Vector unrefedObj =  unrefed;
-      unmarshaller.setUnmarshalListener(new VorbaXmlBinder(vorbafactory, unrefedObj, objrefs));
+      final Hashtable objrefs = new Hashtable();
+      if (factory.extanthashv==null)
+        factory.extanthashv=new Hashtable();
+      final Hashtable oobjhashes=factory.extanthashv;
+      final VorbaIdFactory vorbafactory = factory;
+      final Vector unrefedObj =  new Vector();
+      final Vector updatedObj = new Vector();
+      unmarshaller.setUnmarshalListener(new VorbaXmlBinder(vorbafactory, unrefedObj, objrefs, oobjhashes, updatedObj));
       // Call the unmarshaller.
       try {
         while (instream.ready()) {
+          // TODO: mark objects in oobjhash prior to unmarshalling, to detect when objects have been lost through an update.
+          //tohere
           Object obj = unmarshaller.unmarshal(instream);
-          boolean sync=ensure_references(unrefed, objrefs);
-          if (!(obj instanceof object))
+          boolean sync=ensure_references(unrefedObj, objrefs);
+          if (!(obj instanceof Vobject))
             return null;
-          return new Object[] { obj, objrefs, new Boolean(sync)};
+          vorbafactory.setNewIdHash(objrefs); // update the Document IO Handler's set of vorbaId<>Object bindings.
+          return new Object[] { obj, objrefs, new Boolean(sync),updatedObj};
           }
       } catch (MarshalException e) {
         // TODO Auto-generated catch block
@@ -208,103 +238,4 @@ public class VorbaXmlBinder extends GeneralizedFieldHandler implements Unmarshal
       }
       return null;
     }
-
-  /* (non-Javadoc)
-   * @see org.exolab.castor.xml.MarshalListener#postMarshal(java.lang.Object)
-   */
-  public void postMarshal(Object object) {
-    // TODO Auto-generated method stub
-    
-  }
-
-  /* (non-Javadoc)
-   * @see org.exolab.castor.xml.MarshalListener#preMarshal(java.lang.Object)
-   */
-  public boolean preMarshal(Object newobj) {
-    if (newobj instanceof object) {
-      object nobj = (object) newobj;
-      nobj.set__stored_in_document(true);
-      Field fd = null;
-      try {
-        if (nobj.isRegisterable()) {
-          // make sure the id field is set
-          nobj.__vorba = vorbafactory;
-          fd = nobj.getClass().getField("_id");
-          if (fd.get(nobj) != null) {
-            fd.set(nobj, nobj.getVorbaId().getId());
-            /* all thats needed perhaps
-             * 
-             *if (idstring.length() > 0) {
-              if (!objrefs.containsKey(idstring)) {
-                objrefs.put(idstring, nobj);
-                nobj.setVorbaId(VorbaId.newId(idstring));
-              } else {
-                System.err.println("Serious problem : duplicate id '"+idstring+"' found! expect badness.");
-                return false; // TODO: HANDLE duplicate XML ids correctly
-              }*/
-          }
-        }
-      } catch (Exception e) {
-        return false;
-      };
-      
-    }
-    return false;
-  }
-
-  /* (non-Javadoc)
-   * @see org.exolab.castor.mapping.GeneralizedFieldHandler#convertUponGet(java.lang.Object)
-   */
-  public Object convertUponGet(Object value) {
-    // TODO Auto-generated method stub
-    return null;
-  }
-
-  /* (non-Javadoc)
-   * @see org.exolab.castor.mapping.GeneralizedFieldHandler#convertUponSet(java.lang.Object)
-   */
-  public Object convertUponSet(Object value) {
-    // TODO Auto-generated method stub
-    return null;
-  }
-
-  /* (non-Javadoc)
-   * @see org.exolab.castor.mapping.GeneralizedFieldHandler#getFieldType()
-   */
-  public Class getFieldType() {
-    // TODO Auto-generated method stub
-    return null;
-  }
-
-  /* (non-Javadoc)
-   * @see org.exolab.castor.mapping.GeneralizedFieldHandler#newInstance(java.lang.Object, java.lang.Object[])
-   */
-  public Object newInstance(Object parent, Object[] args) throws IllegalStateException {
-    // TODO Auto-generated method stub
-    return super.newInstance(parent, args);
-  }
-
-  /* (non-Javadoc)
-   * @see org.exolab.castor.mapping.GeneralizedFieldHandler#newInstance(java.lang.Object)
-   */
-  public Object newInstance(Object parent) throws IllegalStateException {
-    // TODO Auto-generated method stub
-    return super.newInstance(parent);
-  }
-
-  /* (non-Javadoc)
-   * @see org.exolab.castor.mapping.GeneralizedFieldHandler#setCollectionIteration(boolean)
-   */
-  public void setCollectionIteration(boolean autoCollectionIteration) {
-    // TODO Auto-generated method stub
-    super.setCollectionIteration(autoCollectionIteration);
-  }
-
-  /* (non-Javadoc)
-   * @see org.exolab.castor.mapping.AbstractFieldHandler#hasValue(java.lang.Object)
-   */
-  public boolean hasValue(Object object) {
-    // TODO Auto-generated method stub
-    return super.hasValue(object);
-  }
 }
\ No newline at end of file