X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fuk%2Fac%2Fvamsas%2Fclient%2FVorbaXmlBinder.java;h=b237dcfa0fb7541bec5d8209d6362cab8ea9c7fd;hb=844ccad5a3fcbedec17b2af66d460f31abc7cff1;hp=330372a4c9621fbc4cb8382cd36aa72f6b3b2a1a;hpb=ea8b10ee787a2b1e2b35f2acb4dccaf12fd2a6fd;p=vamsas.git diff --git a/src/uk/ac/vamsas/client/VorbaXmlBinder.java b/src/uk/ac/vamsas/client/VorbaXmlBinder.java index 330372a..b237dcf 100644 --- a/src/uk/ac/vamsas/client/VorbaXmlBinder.java +++ b/src/uk/ac/vamsas/client/VorbaXmlBinder.java @@ -1,241 +1,310 @@ -/** - * - */ -package uk.ac.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.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -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 Vobject ID machinery for translating - * between non-volatile XML IDs and Vobject references. Use the - * marshalling and unmarshalling methods in this class in order - * to add automatically computed values for required fields in objects, - * so as to avoid validation exceptions when marshalling new objects - * into the vamsas document. - */ -public class VorbaXmlBinder implements UnmarshalListener { - private static Log log = LogFactory.getLog(VorbaXmlBinder.class); - private final IVorbaIdFactory vorbafactory; - - private final Vector obj; - private final Hashtable oldobjhashes; - private final Hashtable objrefs; - private final Vector updatedobjs; - - public VorbaXmlBinder(IVorbaIdFactory vorbafactory, Vector obj, Hashtable objrefs, Hashtable oldobjhashes, Vector updatedobjs) { - this.vorbafactory = vorbafactory; - this.obj = obj; - this.objrefs = objrefs; - this.oldobjhashes = oldobjhashes; - this.updatedobjs = updatedobjs; - } - - /* - * (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) { - if (parent instanceof Vobject && child instanceof Vobject) { - if (((Vobject) child).V_parent==null) { - // System.err.println("Setting parent of "+fieldName); - ((Vobject) child).setV_parent((Vobject) parent); - } - } - } - - /* - * (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 Vobject, and add the Vobject to the VorbaId hash. - * - * @see org.exolab.castor.xml.UnmarshalListener#unmarshalled(java.lang.Object) - */ - public void unmarshalled(Object newobj) { - if (newobj instanceof Vobject) { - Vobject nobj = (Vobject) newobj; - nobj.set__stored_in_document(true); - try { - if (nobj.isRegisterable() && nobj.___id_field!=null) { - VorbaId nobj_id=null; - // look for the id field (should be an NCName string) - nobj.__vorba = vorbafactory; - // use the Vobject accessor method to avoid unpleasant security exceptions. - String idstring = nobj.__getInstanceIdField(); - if (idstring!=null) { - if (idstring.length() > 0) { - nobj.setVorbaId(VorbaId.newId(idstring)); - if (objrefs.containsKey(nobj_id=nobj.getVorbaId()) && !objrefs.get(nobj.getVorbaId()).equals(nobj)) { - System.err.println("Serious problem : duplicate id '"+idstring+"' found! expect badness."); - // TODO: HANDLE duplicate XML ids correctly - } - objrefs.put(nobj_id, nobj); - } else { - // add to list of objects without a valid vorbaId - obj.add(nobj); - } - } else { - // TODO: add to list of objects without a valid vorbaId - obj.add(nobj); - } - - nobj.doHash(); - // check to see if new object was present in old object hash - if (oldobjhashes.containsKey(nobj.getVorbaId())) { - Vobjhash oldhash = (Vobjhash) oldobjhashes.get(nobj.getVorbaId()); - if (oldhash.isUpdated(nobj)) { - // mark the object as updated in this document read. - nobj.set__updated_since_last_read(true); - oldobjhashes.put(nobj_id, new Vobjhash(nobj)); - updatedobjs.addElement(nobj); - } - } - } - } 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 VorbaIdFactory 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); - - } - /** - * creates new VorbaId references where necessary for newly unmarshalled objects - * @param unrefed - * @param objrefs - * @return false if any new object references were made - */ - 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()) { - Vobject o = (Vobject) newobj.next(); - // forces registration and id field update. - VorbaId id = o.getVorbaId(); - if (!objrefs.containsKey(id)) { - objrefs.put(id, o); - } else { - if (!objrefs.get(id).equals(o)) - throw new Error("Serious! Duplicate reference made by vorbaIdFactory!"); - } - } - } - return sync; - } - /** - * Unmarshals a vamsasDocument Vobject from a stream, registers - * unregistered objects, records existing VorbaIds, and completes - * the uk.ac.vamsas.client.Vobject 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 - * as getVamsasObjects but will detect updated objects based on differing hash values - * obtained from the VorbaIdFactory's VorbaId, Vobject.get__last_Hash() pairs (if any) - * @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 Vobject. - * @return null or {(Object) VamsasDocument Vobject, (Object) Hashtable of Vobject references, (Object) Boolean(sync), (Object) Vector of updated objects in document } - */ - public static Object[] getVamsasObjects(Reader instream, - VorbaIdFactory factory, Vobject root) { - Unmarshaller unmarshaller = new Unmarshaller(root); - unmarshaller.setIDResolver(new IDResolver() { - public Object resolve(String id) { - VorbaXmlBinder.log.warn("Warning - id " + id - + " is not found in the Vamsas XML!"); - return null; - } - }); - final Hashtable objrefs = new Hashtable(); - if (factory.extanthashv==null) - factory.extanthashv=new Hashtable(); - final Hashtable oobjhashes=factory.extanthashv; - final VorbaIdFactory vorbafactory = factory; - final Vector unrefedObj = new Vector(); - final Vector updatedObj = new Vector(); - unmarshaller.setUnmarshalListener(new VorbaXmlBinder(vorbafactory, unrefedObj, objrefs, oobjhashes, updatedObj)); - // Call the unmarshaller. - try { - while (instream.ready()) { - // TODO: mark objects in oobjhash prior to unmarshalling, to detect when objects have been lost through an update. - //tohere - Object obj = unmarshaller.unmarshal(instream); - boolean sync=ensure_references(unrefedObj, objrefs); - if (!(obj instanceof Vobject)) - return null; - vorbafactory.setNewIdHash(objrefs); // update the Document IO Handler's set of vorbaId<>Object bindings. - return new Object[] { obj, objrefs, new Boolean(sync),updatedObj}; - } - } 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; - } -} \ No newline at end of file +/* + * This file is part of the Vamsas Client version 0.1. + * Copyright 2009 by Jim Procter, Iain Milne, Pierre Marguerite, + * Andrew Waterhouse and Dominik Lindner. + * + * Earlier versions have also been incorporated into Jalview version 2.4 + * since 2008, and TOPALi version 2 since 2007. + * + * The Vamsas Client is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The Vamsas Client is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the Vamsas Client. If not, see . + */ +package uk.ac.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.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +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 uk.ac.vamsas.objects.core.VamsasDocument; + +/** + * Implements the Vamsas Vobject ID machinery for translating between + * non-volatile XML IDs and Vobject references. Use the marshalling and + * unmarshalling methods in this class in order to add automatically computed + * values for required fields in objects, so as to avoid validation exceptions + * when marshalling new objects into the vamsas document. + */ +public class VorbaXmlBinder implements UnmarshalListener { + private static Log log = LogFactory.getLog(VorbaXmlBinder.class); + + private final IVorbaIdFactory vorbafactory; + + private final Vector obj; + + private final Hashtable oldobjhashes; + + private final Hashtable objrefs; + + private final Vector updatedobjs; // not yet used elswhere ? + + public VorbaXmlBinder(IVorbaIdFactory vorbafactory2, Vector unrefedObj, + Hashtable objrefs2, Hashtable oldobjhashes, Vector updatedObj) { + this.vorbafactory = vorbafactory2; + this.obj = unrefedObj; + this.objrefs = objrefs2; + this.oldobjhashes = oldobjhashes; + this.updatedobjs = updatedObj; + } + + /* + * (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) { + if (parent instanceof Vobject && child instanceof Vobject) { + if (((Vobject) child).V_parent == null) { + // System.err.println("Setting parent of "+fieldName); + ((Vobject) child).setV_parent((Vobject) parent); + } + } + } + + /* + * (non-Javadoc) + * + * @see org.exolab.castor.xml.UnmarshalListener#initialized(java.lang.Object) + */ + public void initialized(Object object) { + if (object instanceof Vobject) { + Vobject nobj = (Vobject) object; + } + } + + /* + * Check if the object has an 'id' field - if it does, copy the value into the + * VorbaId field of Vobject, and add the Vobject to the VorbaId hash. + * + * @see org.exolab.castor.xml.UnmarshalListener#unmarshalled(java.lang.Object) + */ + public void unmarshalled(Object newobj) { + if (newobj instanceof Vobject) { + Vobject nobj = (Vobject) newobj; + nobj.set__stored_in_document(true); + try { + if (nobj.isRegisterable() && nobj.___id_field != null) { + VorbaId nobj_id = null; + // look for the id field (should be an NCName string) + nobj.__vorba = vorbafactory; + // use the Vobject accessor method to avoid unpleasant security + // exceptions. + String idstring = nobj.__getInstanceIdField(); + if (idstring != null) { + if (idstring.length() > 0) { + nobj.setVorbaId(VorbaId.newId(idstring)); + nobj_id = nobj.getVorbaId(); + if (objrefs.containsKey(nobj_id) + && !objrefs.get(nobj_id).equals(nobj)) { + System.err.println("Serious problem : duplicate id '" + + idstring + "' found! expect badness."); + // TODO: HANDLE duplicate XML ids correctly + } + objrefs.put(nobj_id, nobj); + } else { + // add to list of objects without a valid vorbaId + obj.add(nobj); + } + } else { + // TODO: add to list of objects without a valid vorbaId + obj.add(nobj); + } + nobj.doHash(); // updates detected by comparing with last hash when + // marshalling + // check to see if new object was present in old object hash + if (nobj_id != null) { + if (oldobjhashes.containsKey(nobj_id)) { + Vobjhash oldhash = (Vobjhash) oldobjhashes.get(nobj_id); + if (oldhash.isUpdated(nobj)) { + // mark the object as updated in this document read. + nobj.set__updated_since_last_read(true); + updatedobjs.addElement(nobj); + } + } else { + // object has no entry in the oldhashvalue list but + // there is a valid vorbaId so + nobj.set__added_since_last_read(true); + } + // and record the just-unmarshalled hash value + oldobjhashes.put(nobj_id, new Vobjhash(nobj)); + } else { + // for objects yet to get a valid vorba_id + nobj.set__added_since_last_read(true); + } + } + + } 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 VorbaIdFactory Parameter"); + if (doc.__vorba == null) + doc.__vorba = vorba; + doc.__ensure_instance_ids(); // this may take a while. Do we allow for + // cyclic references ? + Marshaller mshl = new Marshaller(outstream); + mshl.marshal(doc); + + } + + /** + * creates new VorbaId references where necessary for newly unmarshalled + * objects + * + * @param unrefed + * @param objrefs + * @return false if any new object references were made + */ + 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()) { + Vobject o = (Vobject) newobj.next(); + // forces registration and id field update. + VorbaId id = o.getVorbaId(); + if (!objrefs.containsKey(id)) { + objrefs.put(id, o); + } else { + if (!objrefs.get(id).equals(o)) + throw new Error( + "Serious! Duplicate reference made by vorbaIdFactory!"); + } + } + } + return sync; + } + + /** + * Unmarshals a vamsasDocument Vobject from a stream, registers unregistered + * objects, records existing VorbaIds, and completes the + * uk.ac.vamsas.client.Vobject 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 as getVamsasObjects but will detect updated + * objects based on differing hash values obtained from the VorbaIdFactory's + * VorbaId, Vobject.get__last_Hash() pairs (if any) + * + * @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 uk.ac.vamsas.objects.core Vobject. + * @return null or {(Object) VamsasDocument Vobject, (Object) Hashtable of + * Vobject references, (Object) Boolean(sync), (Object) Vector of + * updated objects in document } + */ + public static Object[] getVamsasObjects(Reader instream, + VorbaIdFactory factory, Vobject root) { + Unmarshaller unmarshaller = new Unmarshaller(root); + unmarshaller.setIDResolver(new IDResolver() { + public Object resolve(String id) { + // TODO: allow for external ID resolution + VorbaXmlBinder.log + .warn("Warning - id " + + id + + " is not found in the Vamsas XML! (TODO: Ignore if this is a forward reference!)"); + return null; + } + }); + final Hashtable objrefs = new Hashtable(); + if (factory.extanthashv == null) + factory.extanthashv = new Hashtable(); + final Hashtable oobjhashes = factory.extanthashv; + final VorbaIdFactory vorbafactory = factory; + final Vector unrefedObj = new Vector(); + final Vector updatedObj = new Vector(); + unmarshaller.setUnmarshalListener(new VorbaXmlBinder(vorbafactory, + unrefedObj, objrefs, oobjhashes, updatedObj)); + // Call the unmarshaller. + try { + while (instream.ready()) { + // TODO: mark objects in oobjhash prior to unmarshalling, to detect when + // objects have been lost through an update. + // tohere + Object obj = unmarshaller.unmarshal(instream); + boolean sync = ensure_references(unrefedObj, objrefs); + if (!(obj instanceof Vobject)) + return null; + vorbafactory.setNewIdHash(objrefs); // update the Document IO Handler's + // set of vorbaId<>Object bindings. + return new Object[] { obj, objrefs, new Boolean(sync), updatedObj }; + } + } 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; + } +}