/** * */ 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 extends GeneralizedFieldHandler implements UnmarshalListener, MarshalListener { 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 ? 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); } 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; } /* (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); } }