ensured that hash binding vorbaIds is passed to client document object for vorba...
[vamsas.git] / src / org / vamsas / client / VorbaXmlBinder.java
1 /**
2  * 
3  */
4 package org.vamsas.client;
5
6 import java.io.IOException;
7 import java.io.PrintWriter;
8 import java.io.Reader;
9 import java.io.Writer;
10 import java.lang.reflect.Field;
11 import java.util.Hashtable;
12 import java.util.Iterator;
13 import java.util.Vector;
14
15 import org.exolab.castor.mapping.FieldHandler;
16 import org.exolab.castor.mapping.GeneralizedFieldHandler;
17 import org.exolab.castor.mapping.ValidityException;
18 import org.exolab.castor.xml.IDResolver;
19 import org.exolab.castor.xml.MarshalException;
20 import org.exolab.castor.xml.MarshalListener;
21 import org.exolab.castor.xml.Marshaller;
22 import org.exolab.castor.xml.UnmarshalListener;
23 import org.exolab.castor.xml.Unmarshaller;
24 import org.exolab.castor.xml.ValidationException;
25 import org.vamsas.objects.core.VamsasDocument;
26 /**
27  * Implements the Vamsas Vobject ID machinery for translating 
28  * between non-volatile XML IDs and Vobject references. Use the
29  * marshalling and unmarshalling methods in this class in order
30  * to avoid validation exceptions when marshalling new objects
31  * into the vamsas document.
32  */
33 public class VorbaXmlBinder implements UnmarshalListener {
34   private final IVorbaIdFactory vorbafactory;
35
36   private final Vector obj;
37
38   private final Hashtable objrefs;
39
40   public VorbaXmlBinder(IVorbaIdFactory vorbafactory, Vector obj, Hashtable objrefs) {
41     this.vorbafactory = vorbafactory;
42     this.obj = obj;
43     this.objrefs = objrefs;
44   }
45
46   /*
47    * (non-Javadoc)
48    * 
49    * @see org.exolab.castor.xml.UnmarshalListener#attributesProcessed(java.lang.Object)
50    */
51   public void attributesProcessed(Object object) {
52   }
53
54   /*
55    * (non-Javadoc)
56    * 
57    * @see org.exolab.castor.xml.UnmarshalListener#fieldAdded(java.lang.String,
58    *      java.lang.Object, java.lang.Object)
59    */
60   public void fieldAdded(String fieldName, Object parent, Object child) {
61     if (parent instanceof Vobject && child instanceof Vobject) {
62       if (((Vobject) child).V_parent==null) {
63         // System.err.println("Setting parent of "+fieldName);
64         ((Vobject) child).setV_parent((Vobject) parent);
65       }
66     }
67   }
68
69   /*
70    * (non-Javadoc)
71    * 
72    * @see org.exolab.castor.xml.UnmarshalListener#initialized(java.lang.Object)
73    */
74   public void initialized(Object object) {
75   }
76   
77   /*
78    * Check if the object has an 'id' field - if it does, copy the value into
79    * the VorbaId field of Vobject, and add the Vobject to the VorbaId hash.
80    * 
81    * @see org.exolab.castor.xml.UnmarshalListener#unmarshalled(java.lang.Object)
82    */
83   public void unmarshalled(Object newobj) {
84     if (newobj instanceof Vobject) {
85       Vobject nobj = (Vobject) newobj;
86       nobj.set__stored_in_document(true);
87       try {
88         if (nobj.isRegisterable() && nobj.___id_field!=null) {
89           // look for the id field (should be an NCName string)
90           nobj.__vorba = vorbafactory;
91           // use the Vobject accessor method to avoid unpleasant security exceptions.
92           String idstring = nobj.__getInstanceIdField();
93           if (idstring!=null) { 
94             if (idstring.length() > 0) {
95               nobj.setVorbaId(VorbaId.newId(idstring));
96               if (objrefs.containsKey(nobj.getVorbaId()) && !objrefs.get(nobj.getVorbaId()).equals(nobj)) {
97                 System.err.println("Serious problem : duplicate id '"+idstring+"' found! expect badness.");
98                 // TODO: HANDLE duplicate XML ids correctly
99               }
100               objrefs.put(nobj.getVorbaId(), nobj);
101             } else {
102               // add to list of objects without a valid vorbaId
103               obj.add(nobj);
104             }
105           } else {
106             // TODO: add to list of objects without a valid vorbaId
107             obj.add(nobj);
108           }
109           
110         nobj.doHash();
111       }
112       } catch (Exception e) {
113         return;
114       };
115       
116     }
117   }
118
119   /**
120    * writes the VamsasDocument to the given stream.
121    * TODO: ensure that (at least) default provenance entries are written for objects.
122    * @param outstream
123    * @param vorba valid VorbaIdFactory to construct any missing IDs 
124    * @param doc
125    * @throws IOException
126    * @throws MarshalException
127    * @throws ValidationException
128    */
129   public static void putVamsasDocument(PrintWriter outstream, VorbaIdFactory vorba, VamsasDocument doc)
130       throws IOException, MarshalException, ValidationException {
131     // Ensure references
132     if (vorba==null)
133       throw new Error("Null VorbaIdVactory Parameter");
134     if (doc.__vorba==null)
135       doc.__vorba = vorba;
136     doc.__ensure_instance_ids(); // this may take a while. Do we allow for cyclic references ? 
137     doc.marshal(outstream);
138   }
139
140   private static boolean ensure_references(Vector unrefed, Hashtable objrefs) {
141     boolean sync=true;
142     if (unrefed.size()>0) {
143       sync=false; // document is out of sync - ids have been created.
144       java.util.Iterator newobj = unrefed.listIterator();
145       while (newobj.hasNext()) {
146         Vobject o = (Vobject) newobj.next();
147         // forces registration and id field update.
148         VorbaId id = o.getVorbaId();
149         if (!objrefs.containsKey(id)) {
150           objrefs.put(id, o);
151         } else {
152           if (!objrefs.get(id).equals(o))
153             throw new Error("Serious! Duplicate reference made by vorbaIdFactory!");
154         }
155       }
156     }
157     return sync;
158   }
159   /**
160      * Unmarshals a vamsasDocument Vobject from a stream, registers
161      * unregistered objects, records existing VorbaIds, and completes 
162      * the org.vamsas.client.Vobject housekeeping fields.
163      * For a valid unmarshalling, the array of returned objects also includes
164      * a <return>sync</return> parameter which is true if new VorbaIds
165      * were created. If sync is false, then the caller should ensure that the
166      * vamsasDocument is written back to disk to propagate the new VorbaIds.
167      *  TODO: ensure that provenance is correct for newly registered objects
168      * @param instream - the XML input stream 
169    * @param factory - the SimpleClient's properly configured VorbaId factory to make new references.
170    * @param root the root element's org.vamsas.objects.core Vobject.
171      * @return null or {(Object) VamsasDocument Vobject, (Object) Hashtable of Vobject references, (Object) Boolean(sync) }
172      */
173   public static Object[] getVamsasObjects(Reader instream,
174         VorbaIdFactory factory, Vobject root) {
175       Unmarshaller unmarshaller = new Unmarshaller(root);
176       unmarshaller.setIDResolver(new IDResolver() {
177         public Object resolve(String id) {
178           System.err.println("Warning - id " + id
179               + " is not found in the VamsasDocument!");
180           return null;
181         }
182       });
183       Hashtable refbase = new Hashtable();
184       Vector unrefed = new Vector();
185       final Hashtable objrefs = refbase;
186       final VorbaIdFactory vorbafactory = factory;
187       final Vector unrefedObj =  unrefed;
188       unmarshaller.setUnmarshalListener(new VorbaXmlBinder(vorbafactory, unrefedObj, objrefs));
189       // Call the unmarshaller.
190       try {
191         while (instream.ready()) {
192           Object obj = unmarshaller.unmarshal(instream);
193           boolean sync=ensure_references(unrefed, objrefs);
194           if (!(obj instanceof Vobject))
195             return null;
196           vorbafactory.setNewIdHash(objrefs); // update the Document IO Handler's set of vorbaId<>Object bindings.
197           return new Object[] { obj, objrefs, new Boolean(sync)};
198           }
199       } catch (MarshalException e) {
200         // TODO Auto-generated catch block
201         e.printStackTrace();
202       } catch (ValidationException e) {
203         // TODO Auto-generated catch block
204         e.printStackTrace();
205       } catch (IOException e) {
206         // TODO Auto-generated catch block
207         e.printStackTrace();
208       }
209       return null;
210     }
211 }