/** * */ package org.vamsas.client; import java.io.IOException; import java.io.PrintWriter; import java.io.Reader; import java.io.Writer; import java.lang.reflect.Field; import java.util.Hashtable; import java.util.Iterator; import java.util.Vector; import org.exolab.castor.mapping.FieldHandler; import org.exolab.castor.mapping.GeneralizedFieldHandler; import org.exolab.castor.mapping.ValidityException; import org.exolab.castor.xml.IDResolver; import org.exolab.castor.xml.MarshalException; import org.exolab.castor.xml.MarshalListener; import org.exolab.castor.xml.Marshaller; import org.exolab.castor.xml.UnmarshalListener; 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 * marshalling and unmarshalling methods in this class in order * to avoid validation exceptions when marshalling new objects * into the vamsas document. */ public class VorbaXmlBinder implements UnmarshalListener { private final IVorbaIdFactory vorbafactory; private final Vector obj; private final Hashtable objrefs; public VorbaXmlBinder(IVorbaIdFactory vorbafactory, Vector obj, Hashtable objrefs) { this.vorbafactory = vorbafactory; this.obj = obj; this.objrefs = objrefs; } /* * (non-Javadoc) * * @see org.exolab.castor.xml.UnmarshalListener#attributesProcessed(java.lang.Object) */ public void attributesProcessed(Object object) { } /* * (non-Javadoc) * * @see org.exolab.castor.xml.UnmarshalListener#fieldAdded(java.lang.String, * java.lang.Object, java.lang.Object) */ public void fieldAdded(String fieldName, Object parent, Object child) { } /* * (non-Javadoc) * * @see org.exolab.castor.xml.UnmarshalListener#initialized(java.lang.Object) */ public void initialized(Object object) { } /* * 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. * * @see org.exolab.castor.xml.UnmarshalListener#unmarshalled(java.lang.Object) */ public void unmarshalled(Object newobj) { if (newobj instanceof object) { object nobj = (object) newobj; nobj.set__stored_in_document(true); Field fd = null; try { if (nobj.isRegisterable()) { // 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); 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."); // TODO: HANDLE duplicate XML ids correctly } } else { // add to list of objects without a valid vorbaId obj.add(nobj); } } else { // add to list of objects without a valid vorbaId obj.add(nobj); } nobj.doHash(); } } catch (Exception e) { return; }; } } /** * writes the VamsasDocument to the given stream. * TODO: ensure that (at least) default provenance entries are written for objects. * @param outstream * @param vorba valid VorbaIdFactory to construct any missing IDs * @param doc * @throws IOException * @throws MarshalException * @throws ValidationException */ public static void putVamsasDocument(PrintWriter outstream, VorbaIdFactory vorba, VamsasDocument doc) throws IOException, MarshalException, ValidationException { // Ensure references if (vorba==null) throw new Error("Null VorbaIdVactory Parameter"); if (doc.__vorba==null) doc.__vorba = vorba; doc.__ensure_instance_ids(); // this may take a while. Do we allow for cyclic references ? doc.marshal(outstream); } 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(); // forces registration and id field update. VorbaId id = o.getVorbaId(); if (!objrefs.containsKey(id)) { objrefs.put(id.id, o); } else { throw new Error("Serious! Duplicate reference made by vorbaIdFactory!"); } } } return sync; } /** * Unmarshals a vamsasDocument object from a stream, registers * unregistered objects, records existing VorbaIds, and completes * the org.vamsas.client.object housekeeping fields. * For a valid unmarshalling, the array of returned objects also includes * a sync 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 * @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) } */ public static Object[] getVamsasObjects(Reader instream, IVorbaIdFactory factory, object 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!"); 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)); // Call the unmarshaller. try { while (instream.ready()) { Object obj = unmarshaller.unmarshal(instream); boolean sync=ensure_references(unrefed, objrefs); if (!(obj instanceof object)) return null; return new Object[] { obj, objrefs, new Boolean(sync)}; } } catch (MarshalException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ValidationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return null; } }