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