4 package uk.ac.vamsas.client;
6 import java.lang.reflect.Field;
7 import java.lang.reflect.InvocationTargetException;
8 import java.lang.reflect.Method;
9 import java.util.Iterator;
11 import org.apache.commons.logging.Log;
12 import org.apache.commons.logging.LogFactory;
13 import org.exolab.castor.mapping.FieldDescriptor;
14 import org.exolab.castor.mapping.FieldHandler;
15 import org.exolab.castor.xml.util.XMLClassDescriptorImpl;
17 import uk.ac.vamsas.test.simpleclient.VamsasArchive;
20 * Base class for all Vamsas objects extracted from an IClientDocument. An
21 * Vobject maybe registered or unregistered.
26 public abstract class Vobject {
27 static Log log = LogFactory.getLog(Vobject.class);
30 * true if Vobject was stored in a vamsas Document or has been retrieved from it
32 protected boolean __stored_in_document = false;
34 * true if Vobject was updated since the vamsas library last read a Vobj with the same VorbaId from a document.
36 protected boolean __updated_since_last_read = false;
38 * memory of the last doHash() value computed for the Vobject
41 protected int __last_hash = 0;
43 * set by testInstanceForIdField() if Vobject should have a VorbaId
45 protected boolean registerable = false;
46 protected boolean __visited = false;
48 * reference to containing object for this Vobject.
50 protected Vobject V_parent=null;
52 * unique id for all vamsas objects allows unambiguous referencing to any
53 * Vobject in the vamsas document
55 protected VorbaId vorbaId = null;
58 * the source of unique VorbaIds.
60 protected IVorbaIdFactory __vorba = null;
63 * @see java.lang.Object#finalize()
65 protected void finalize() throws Throwable {
77 testInstanceForIdField();
79 java.lang.reflect.Field ___id_field=null; // set to ease pain of reflection
81 * set the isRegisterable flag based on the presence of a 'private String _id' field in
82 * the reflected class instance.
84 private void testInstanceForIdField() {
85 // TODO: decide if 'id' is an appropriate reserved attribute name for the VorbaId
86 // look for the id field in all castor classes (should be an NCName string)
88 Class thisclass=this.getClass();
89 setRegisterable(false);
90 while (!thisclass.equals(Vobject.class)) {
92 java.lang.reflect.Field fd = thisclass.getDeclaredField("_id");
93 if (String.class.isAssignableFrom(fd.getType())) {
95 this.setRegisterable(true);
98 } catch (SecurityException e) {
99 log.error("Unexpected Security Exception whilst finding id fields to set!",e);
100 } catch (NoSuchFieldException e) {
101 thisclass=thisclass.getSuperclass();
105 // boolean __testedInstance=false;
107 * update the Vobject instance's _id field, based on the contents of the
108 * VorbaId. Only call this if you mean to do it!
110 protected void setInstanceIdField() {
111 /*if (!registerable && !__testedInstance) {
112 testInstanceForIdField();
113 __testedInstance=true;
118 Method fd = this.getClass().getMethod("setId", new Class[] { String.class });
119 fd.invoke((Object) this, new Object[] {new String(this.getVorbaId().id)});
120 log.debug(this.getClass().getName()+" called setInstanceIdField!");
121 } catch (InvocationTargetException e) {
122 log.error("SourceGeneration of "
123 + this.getClass().toString()
124 + "\n has resulted in an inaccessible 'setId' method!\nCannot set ID from the vorbaId Vobject.", e);
126 catch (IllegalAccessException e) {
127 log.error("SourceGeneration of "
128 + this.getClass().toString()
129 + "\n has resulted in an inaccessible 'setId' method!\nCannot set ID from the vorbaId Vobject.", e);
130 } catch (SecurityException e) {
131 log.error("Security access violation for "+this.getClass().toString(),e);
132 } catch (NoSuchMethodException e) {
133 log.warn(this.getClass().toString()+" was erroneously marked as a Vorba Vobject class (Implementation error?)");
134 this.setRegisterable(false);
137 System.err.println("Client error. Trying to setInstanceIdField on a "
138 + this.getClass().toString() + " (which cannot be given a vorbaId)");
142 protected String __getInstanceIdField() {
143 /*if (!registerable && !__testedInstance) {
144 testInstanceForIdField();
145 __testedInstance=true;
150 Method fd = this.getClass().getMethod("getId", (Class[]) null);
151 Object idstring = fd.invoke((Object) this, (Object[]) null);
152 log.debug(this.getClass().getName()+" called getInstanceIdField!");
153 if (idstring!=null && idstring instanceof String) {
154 if (((String) idstring).length()>0)
155 return (String) idstring;
157 } catch (InvocationTargetException e) {
158 log.error("SourceGeneration of "
159 + this.getClass().toString()
160 + "\n has resulted in an inaccessible 'getId' method!\nCannot set ID from the vorbaId Vobject.", e);
162 catch (IllegalAccessException e) {
163 log.error("SourceGeneration of "
164 + this.getClass().toString()
165 + "\n has resulted in an inaccessible 'getId' method!\nCannot set ID from the vorbaId Vobject.", e);
166 } catch (SecurityException e) {
167 log.error("Security access violation for "+this.getClass().toString(),e);
168 } catch (NoSuchMethodException e) {
169 log.warn(this.getClass().toString()+" was erroneously marked as a Vorba Vobject class (Implementation error?)");
170 this.setRegisterable(false);
173 System.err.println("Client error. Trying to getInstanceIdField on a "
174 + this.getClass().toString() + " (which cannot be given a vorbaId)");
180 * calculate a hash for the Vobject with all housekeeping fields at standard
181 * values. (isRegisterable is an immutable attribute property)
182 * TODO: decide if __stored_in_document should be included in the hash or not.
183 * @return true if new hash different to last hash
185 synchronized protected boolean doHash() {
186 long __old_hash = __last_hash;
188 Vobject _V_parent=V_parent;
190 VorbaId thisid = vorbaId;
191 IVorbaIdFactory factory = __vorba;
192 boolean stored = __stored_in_document;
193 boolean updated = __updated_since_last_read;
194 boolean visited = __visited;
195 java.lang.reflect.Field idfield = ___id_field;
197 __updated_since_last_read=false;
202 __last_hash = this.hashCode();
203 // reset houseskeeping variables
207 __stored_in_document = stored;
208 __updated_since_last_read=updated;
211 return (__old_hash==0) || (__old_hash == __last_hash);
215 * TODO: combine two versions of the same collection Vobject to resolve
216 * asynchronous updates to the same vamsas Vobject Merges two vamsas objects,
217 * one of which is a later version of the earlier (ie they have the same
218 * vorbaId but one is a later version recently read from the vamsasDocument
223 protected boolean merge(Vobject laterCopy) {
224 log.warn(this.getClass().getName()+".merge() not implemented.");
230 * @return true if Vobject is registered
232 public boolean isRegistered() {
233 return (registerable) ? (vorbaId != null) : false;
237 * Method to get fixed reference for the Vobject in the vamsas document.
239 * @returns null if Vobject is neither registered or not associated with a
240 * properly instantiated VorbaIdFactory.
242 public VorbaId getVorbaId() {
243 if (registerable && vorbaId == null) {
244 if (this.__stored_in_document) {
246 vorbaId=uk.ac.vamsas.client.VorbaId.newId(this.__getInstanceIdField());
248 // Try to use the associated factory.
250 if ((vorbaId = __vorba.makeVorbaId(this)) == null)
251 return null; // Factory not valid.
253 this.setInstanceIdField();
261 * used by the IClient implementation to generate unique Id based on client
262 * applications current namespace.
264 protected void setVorbaId(VorbaId newid) {
269 * @return true if Vobject is present in Vamsas Document.
271 public boolean is__stored_in_document() {
272 return __stored_in_document;
276 * @return true if this object has been updated in the currently stored document since the last time a Vobject with the same ID was read from a Vamsas Document
278 public boolean is__updated_since_last_read() {
279 return __updated_since_last_read;
283 * Set internal flag to indicate this object was updated since the last document read
284 * @param __updated_since_last_read the __updated_since_last_read to set
286 protected void set__updated_since_last_read(boolean __updated_since_last_read) {
287 this.__updated_since_last_read = __updated_since_last_read;
291 * for use by Vorba agent to reflect state of vamsas Vobject to client
293 * Setting stored_in_document on a registerable Vobject without a
294 * vorbaId will mean is will *never* get a vorbaId and
295 * horrible things will happen.
296 * @param __stored_in_document true if Vobject has been marshalled into current document.
298 protected void set__stored_in_document(boolean __stored_in_document) {
299 this.__stored_in_document = __stored_in_document;
303 * __last_hash is the hash value computed when the Vobject was last checked
304 * against a IClientDocument generated by the Vobject's parent IClient
307 * @return Returns the __last_hash.
309 public int get__last_hash() {
314 * @return true if Vobject can have a vorbaId
316 public boolean isRegisterable() {
321 * Called by __testInstanceForidField and the post-unmarshalling handler
322 * to indicate if Vobject will have a vorbaId.
323 * @param registerable
325 protected void setRegisterable(boolean registerable) {
326 this.registerable = registerable;
329 * ensure's internal id field corresponds to vorbaId and
330 * cascade through all fields referring to an instance of Vobject
331 * calling the same method on them.
332 * TODO: LATER: properly apply castors own field mechanisms to get at accessors
333 * TODO: FIX CYCLIC __ensure+instance_ids
334 * Implementation note for the todo:
335 * this works like a depth-first search over all vamsas objects in an vamsasDocument.
336 * __visited is the visited flag, any Vobj who's flag is of a different parity
337 * to the visited argument will be recursed on.
338 * note - the doHash() function used to be used as the 'visited' flag -
339 * this *is not* a valid heuristic, although it will work "most of the time".
340 * TODO: LATER? Add another method for setDefaultProvenanceField (in the spirit of setInstanceIdField) using the info from the __vorba.getClient/User/Session methods
342 protected void __ensure_instance_ids() {
343 __ensure_instance_ids(!__visited);
345 protected void __ensure_instance_ids(boolean visited) {
347 throw new Error("Improperly intialised uk.ac.vamsas.client.Vobject - no VorbaFactory given.");
348 log.debug("doing "+this.getClass()+".__ensure_instance_ids()");
349 if (!__stored_in_document && registerable)
350 setInstanceIdField();
351 if (__visited==visited)
354 __vorba.updateHashValue(this);
356 Class descriptor = null;
357 XMLClassDescriptorImpl descimpl = null;
359 // castor descriptor resolver magic
360 descriptor = this.getClass().getClassLoader().loadClass(this.getClass().getName()+"Descriptor");
361 descimpl = (XMLClassDescriptorImpl) descriptor.getConstructor((Class[])null).newInstance((Object[])null);
362 } catch (Exception e) {
363 log.fatal("Source Generation Error!: Couldn't resolve descriptor for "
364 +this.getClass().getName()
365 +" was 'generate descriptors' set for castorbuilder.properties?");
368 FieldDescriptor fields[] = descimpl.getFields();
369 for (int i=0,j=fields.length; i<j; i++) {
370 Class type= fields[i].getFieldType();
371 if (type.isArray()) {
372 if (Vobject[].class.isAssignableFrom(type)) {
374 Object val = fields[i].getHandler().getValue(this);
376 Vobject vals[] = (Vobject[]) val;
377 for (int k=0, l=vals.length; k<l; k++) {
378 if (vals[k].__vorba==null)
379 vals[k].__vorba = __vorba; // propagate IVorbaIdFactory
380 if (vals[k].V_parent==null)
381 vals[k].V_parent=this; // propagate parent reference to this element.
382 vals[k].__ensure_instance_ids(visited);
386 catch (Exception e) {
387 log.error("Client error - could not access array "+type.getName()+" in "+this.getClass().getName(), e);
391 if (Vobject.class.isAssignableFrom(type)) {
393 FieldHandler fh = fields[i].getHandler();
396 Object fval = fh.getValue(this);
398 if (fval.getClass().isArray()) {
399 //if (Vobject[].class.isAssignableFrom(type)) {
401 Vobject vals[] = (Vobject[]) fval;
402 for (int k=0, l=vals.length; k<l; k++) {
403 if (vals[k].__vorba==null)
404 vals[k].__vorba = __vorba; // propagate IVorbaIdFactory
405 if (vals[k].V_parent==null)
406 vals[k].V_parent=this; // propagate parent reference to this field object
407 vals[k].__ensure_instance_ids(visited);
410 catch (Exception e) {
411 log.error("Client error - could not access (fhval)array "+type.getName()+" in "+this.getClass().getName(), e);
416 log.debug("Got value for "+fields[i].getFieldName());
420 // castor's mechanism doesn't work for this object... so...*/
421 // fuck around, fuck around, jump up jump up and get down! */
422 Object o = fields[i].getClassDescriptor();
424 // XMLClassDescriptorImpl fclasdes = (XMLClassDescriptorImpl) o;
425 String methname = "get"+fields[i].getFieldName();
426 Method fgetmeth = this.getClass().getMethod(methname,(Class[])null);
427 if (fgetmeth!=null) {
428 Object fval = fgetmeth.invoke(this,(Object[])null);
432 log.warn("Couldn't find "+this.getClass().getName()+"."+methname);
437 if (rf.__vorba==null)
438 rf.__vorba = __vorba; // propagate IVorbaIdFactory
439 if (rf.V_parent==null)
440 rf.V_parent=this; // propagate parent reference
441 rf.__ensure_instance_ids(visited);
444 catch (Exception e) {
445 log.error("Client error - could not access "+type.getName()+" in "+this.getClass().getName(), e);
453 * @return the __parent
455 public Vobject getV_parent() {
460 * @param __parent the __parent to set
462 protected void setV_parent(Vobject V_parent) {
463 this.V_parent = V_parent;