7272c1fd4f07738372d6b47bcfe6994ffec37b45
[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       Field fd = null;
88       try {
89         if (nobj.isRegisterable()) {
90           // look for the id field (should be an NCName string)
91           nobj.__vorba = vorbafactory;
92           fd = nobj.getClass().getDeclaredField("_id");
93           String idstring;
94           if (fd.get(nobj) != null) {
95             idstring = (String) fd.get(nobj);
96             if (idstring.length() > 0) {
97               if (!objrefs.containsKey(idstring)) {
98                 objrefs.put(idstring, nobj);
99                 nobj.setVorbaId(VorbaId.newId(idstring));
100               } else {
101                 System.err.println("Serious problem : duplicate id '"+idstring+"' found! expect badness.");
102                 // TODO: HANDLE duplicate XML ids correctly
103               }
104             } else {
105               // add to list of objects without a valid vorbaId
106               obj.add(nobj);
107             }
108           } else {
109             // add to list of objects without a valid vorbaId
110             obj.add(nobj);
111           }
112           
113         nobj.doHash();
114       }
115       } catch (Exception e) {
116         return;
117       };
118       
119     }
120   }
121
122   /**
123    * writes the VamsasDocument to the given stream.
124    * TODO: ensure that (at least) default provenance entries are written for objects.
125    * @param outstream
126    * @param vorba valid VorbaIdFactory to construct any missing IDs 
127    * @param doc
128    * @throws IOException
129    * @throws MarshalException
130    * @throws ValidationException
131    */
132   public static void putVamsasDocument(PrintWriter outstream, VorbaIdFactory vorba, VamsasDocument doc)
133       throws IOException, MarshalException, ValidationException {
134     // Ensure references
135     if (vorba==null)
136       throw new Error("Null VorbaIdVactory Parameter");
137     if (doc.__vorba==null)
138       doc.__vorba = vorba;
139     doc.__ensure_instance_ids(); // this may take a while. Do we allow for cyclic references ? 
140     doc.marshal(outstream);
141   }
142
143   private static boolean ensure_references(Vector unrefed, Hashtable objrefs) {
144     boolean sync=true;
145     if (unrefed.size()>0) {
146       sync=false; // document is out of sync - ids have been created.
147       java.util.Iterator newobj = unrefed.listIterator();
148       while (newobj.hasNext()) {
149         Vobject o = (Vobject) newobj.next();
150         // forces registration and id field update.
151         VorbaId id = o.getVorbaId();
152         if (!objrefs.containsKey(id)) {
153           objrefs.put(id.id, o);
154         } else {
155           throw new Error("Serious! Duplicate reference made by vorbaIdFactory!");
156         }
157       }
158     }
159     return sync;
160   }
161   /**
162      * Unmarshals a vamsasDocument Vobject from a stream, registers
163      * unregistered objects, records existing VorbaIds, and completes 
164      * the org.vamsas.client.Vobject housekeeping fields.
165      * For a valid unmarshalling, the array of returned objects also includes
166      * a <return>sync</return> parameter which is true if new VorbaIds
167      * were created. If sync is false, then the caller should ensure that the
168      * vamsasDocument is written back to disk to propagate the new VorbaIds.
169      *  TODO: ensure that provenance is correct for newly registered objects
170      * @param instream - the XML input stream 
171    * @param factory - the SimpleClient's properly configured VorbaId factory to make new references.
172    * @param root the root element's org.vamsas.objects.core Vobject.
173      * @return null or {(Object) VamsasDocument Vobject, (Object) Hashtable of Vobject references, (Object) Boolean(sync) }
174      */
175   public static Object[] getVamsasObjects(Reader instream,
176         IVorbaIdFactory factory, Vobject root) {
177       Unmarshaller unmarshaller = new Unmarshaller(root);
178       unmarshaller.setIDResolver(new IDResolver() {
179         public Object resolve(String id) {
180           System.err.println("Warning - id " + id
181               + " is not found in the VamsasDocument!");
182           return null;
183         }
184       });
185       Hashtable refbase = new Hashtable();
186       Vector unrefed = new Vector();
187       final Hashtable objrefs = refbase;
188       final IVorbaIdFactory vorbafactory = factory;
189       final Vector unrefedObj =  unrefed;
190       unmarshaller.setUnmarshalListener(new VorbaXmlBinder(vorbafactory, unrefedObj, objrefs));
191       // Call the unmarshaller.
192       try {
193         while (instream.ready()) {
194           Object obj = unmarshaller.unmarshal(instream);
195           boolean sync=ensure_references(unrefed, objrefs);
196           if (!(obj instanceof Vobject))
197             return null;
198           return new Object[] { obj, objrefs, new Boolean(sync)};
199           }
200       } catch (MarshalException e) {
201         // TODO Auto-generated catch block
202         e.printStackTrace();
203       } catch (ValidationException e) {
204         // TODO Auto-generated catch block
205         e.printStackTrace();
206       } catch (IOException e) {
207         // TODO Auto-generated catch block
208         e.printStackTrace();
209       }
210       return null;
211     }
212 }