debugged VamsasArchive and rationalised debugging messages.
[vamsas.git] / src / org / vamsas / client / object.java
1 /**
2  * 
3  */
4 package org.vamsas.client;
5
6 import java.lang.reflect.Field;
7 import java.lang.reflect.InvocationTargetException;
8 import java.lang.reflect.Method;
9 import java.util.Iterator;
10
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;
16 import org.vamsas.test.simpleclient.VamsasArchive;
17
18 /**
19  * Base class for all Vamsas objects extracted from an IClientDocument. An
20  * object maybe registered or unregistered.
21  * 
22  * @author jimp
23  * 
24  */
25 public abstract class object {
26   static Log log = LogFactory.getLog(object.class);
27   
28   /**
29    * unique id for all vamsas objects allows unambiguous referencing to any
30    * object in the vamsas document
31    */
32   protected boolean __stored_in_document = false;
33
34   protected long __last_hash = 0; // default is different so doHash always returns true initially.
35
36   protected boolean registerable = false;
37
38   protected VorbaId vorbaId = null;
39
40   protected IVorbaIdFactory __vorba = null;
41
42   /**
43    * 
44    */
45   protected object() {
46     super();
47     testInstanceForIdField();
48   }
49
50   /**
51    * set the isRegisterable flag based on the presence of a 'String id' field in
52    * the reflected class instance.
53    */
54   private void testInstanceForIdField() {
55     // look for the id field (should be an NCName string)
56     // TODO: decide if 'id' is an appropriate reserved attribute name for the VorbaId
57     try {
58       java.lang.reflect.Field fd = this.getClass().getDeclaredField("_id");
59       if (String.class.isAssignableFrom(fd.getType())) {
60         this.setRegisterable(true);
61       }
62     } catch (SecurityException e) {
63       e.printStackTrace();
64     } catch (NoSuchFieldException e) {
65       this.setRegisterable(false);
66     }
67   }
68
69   /**
70    * update the object instance's String id field, based on the contents of the
71    * VorbaId. Only call this if you mean to do it!
72    */
73   protected void setInstanceIdField() {
74     if (registerable) {
75       if (__vorba != null)
76         try {
77           Method fd = this.getClass().getMethod("setId", new Class[] { String.class });
78           fd.invoke((Object) this, new Object[] {new String(this.getVorbaId().id)});
79           log.debug(this.getClass().getName()+" called setInstanceIdField!");
80         } catch (InvocationTargetException e) { 
81           log.error("SourceGeneration of "
82               + this.getClass().toString()
83               + "\n has resulted in an inaccessible 'setId' method!\nCannot set ID from the vorbaId object.", e);
84         }
85         catch (IllegalAccessException e) {
86           log.error("SourceGeneration of "
87                   + this.getClass().toString()
88                   + "\n has resulted in an inaccessible 'setId' method!\nCannot set ID from the vorbaId object.", e);
89         } catch (SecurityException e) {
90           log.error("Security access violation for "+this.getClass().toString(),e);
91         } catch (NoSuchMethodException e) {
92           log.warn(this.getClass().toString()+" was erroneously marked as a Vorba object class (Implementation error?)");
93           this.setRegisterable(false);
94         }
95     } else {
96       System.err.println("Client error. Trying to setInstanceIdField on a "
97           + this.getClass().toString() + " (which cannot be given a vorbaId)");
98     }
99   }
100
101   /**
102    * calculate a hash for the object with all housekeeping fields at standard
103    * values. (isRegisterable is an immutable attribute property)
104    * @return true if new hash different to last hash
105    */
106   synchronized protected boolean doHash() {
107     long __old_hash = __last_hash;
108     __last_hash = 0;
109     VorbaId thisid = vorbaId;
110     IVorbaIdFactory factory = __vorba;
111     boolean stored = __stored_in_document;
112     vorbaId = null;
113     __vorba = null;
114     __last_hash = this.hashCode();
115     vorbaId = thisid;
116     __vorba = factory;
117     __stored_in_document = stored;
118     return (__old_hash==0) || (__old_hash == __last_hash);
119   }
120
121   /**
122    * TODO: combine two versions of the same collection object to resolve
123    * asynchronous updates to the same vamsas object Merges two vamsas objects,
124    * one of which is a later version of the earlier (ie they have the same
125    * vorbaId but one is a later version recently read from the vamsasDocument
126    * collection.
127    * 
128    * @return
129    */
130   protected boolean merge(object laterCopy) {
131     log.warn(this.getClass().getName()+".merge() not implemented.");
132     return true;
133   }
134
135   /**
136    * 
137    * @return true if object is registered
138    */
139   public boolean isRegistered() {
140     return (registerable) ? (vorbaId != null) : false;
141   }
142
143   /**
144    * Method to get fixed reference for the object in the vamsas document.
145    * 
146    * @returns null if object is neither registered or not associated with a
147    *          properly instantiated VorbaIdFactory.
148    */
149   public VorbaId getVorbaId() {
150     if (registerable && vorbaId == null) {
151       // Try to use the associated factory.
152       if (__vorba != null)
153         if ((vorbaId = __vorba.makeVorbaId()) == null)
154           return null; // Factory not valid.
155         else {
156           this.setInstanceIdField();
157           return vorbaId;
158         }
159     }
160     return vorbaId;
161   }
162
163   /**
164    * used by the IClient implementation to generate unique Id based on client
165    * applications current namespace.
166    */
167   protected void setVorbaId(VorbaId newid) {
168     vorbaId = newid;
169   }
170
171   /**
172    * @return true if object is present in Vamsas Document.
173    */
174   public boolean is__stored_in_document() {
175     return __stored_in_document;
176   }
177
178   /**
179    * for use by Vorba agent to reflect state of vamsas object to client
180    * application.
181    * 
182    * @param __stored_in_document
183    *          The __stored_in_document to set.
184    */
185   protected void set__stored_in_document(boolean __stored_in_document) {
186     this.__stored_in_document = __stored_in_document;
187   }
188
189   /**
190    * __last_hash is the hash value computed when the object was last checked
191    * against a IClientDocument generated by the object's parent IClient
192    * instance.
193    * 
194    * @return Returns the __last_hash.
195    */
196   public long get__last_hash() {
197     return __last_hash;
198   }
199
200   /**
201    * @return Returns the registerable.
202    */
203   public boolean isRegisterable() {
204     return registerable;
205   }
206
207   /**
208    * Called during unmarshalling - if object has an id string, it is
209    * registerable.
210    * 
211    * @param registerable
212    *          The registerable to set.
213    */
214   protected void setRegisterable(boolean registerable) {
215     this.registerable = registerable;
216   }
217   /**
218    * ensure's internal id field corresponds to vorbaId and
219    * cascade through all fields referring to an instance of object
220    * calling the same method on them.
221    * TODO: LATER: properly apply castors own field mechanisms to get at accessors
222    * TODO: FIX CYCLIC __ensure+instance_ids
223    */
224   protected void __ensure_instance_ids() {
225     if (__vorba==null)
226       throw new Error("Improperly intialised org.vamsas.client.object - no VorbaFactory given.");
227     log.debug("doing "+this.getClass()+".__ensure_instance_ids()");
228     if (!__stored_in_document && registerable)
229       setInstanceIdField();
230     if (!doHash())
231       return; // nothing has changed in this object - probably visited it before.
232     Class descriptor = null;
233     XMLClassDescriptorImpl descimpl = null;
234     try {
235       // castor descriptor resolver magic
236       descriptor = this.getClass().getClassLoader().loadClass(this.getClass().getName()+"Descriptor");
237       descimpl = (XMLClassDescriptorImpl) descriptor.getConstructor(null).newInstance(null);
238     } catch (Exception e) {
239       log.fatal("Source Generation Error!: Couldn't resolve descriptor for "
240           +this.getClass().getName()
241           +" was 'generate descriptors' set for castorbuilder.properties?");
242       return;
243     }
244     FieldDescriptor fields[] = descimpl.getFields();
245     for (int i=0,j=fields.length; i<j; i++) {
246       Class type= fields[i].getFieldType();
247       if (type.isArray()) {
248         if (object[].class.isAssignableFrom(type)) {
249           try {
250             Object val = fields[i].getHandler().getValue(this);
251             if (val!=null) {
252               object vals[] = (object[]) val; 
253               for (int k=0, l=vals.length; k<l; k++) {
254                 if (vals[k].__vorba==null)
255                   vals[k].__vorba = __vorba; // propagate IVorbaIdFactory
256                 vals[k].__ensure_instance_ids();
257               }
258             }
259           }
260           catch (Exception e) {
261             log.error("Client error - could not access array "+type.getName()+" in "+this.getClass().getName(), e);
262           }
263         }
264       } else
265         if (object.class.isAssignableFrom(type)) {
266           try {
267             FieldHandler fh = fields[i].getHandler();
268             object rf = null;
269             if (fh != null) {
270               Object fval = fh.getValue(this);
271               if (fval!=null) {
272                 if (fval.getClass().isArray()) {
273                   //if (object[].class.isAssignableFrom(type)) {
274                     try {
275                       object vals[] = (object[]) fval; 
276                       for (int k=0, l=vals.length; k<l; k++) {
277                         if (vals[k].__vorba==null)
278                           vals[k].__vorba = __vorba; // propagate IVorbaIdFactory
279                         vals[k].__ensure_instance_ids();
280                       }
281                     }
282                     catch (Exception e) {
283                       log.error("Client error - could not access (fhval)array "+type.getName()+" in "+this.getClass().getName(), e);
284                     }
285                   //}
286                 } else {
287                   rf = (object) fval;
288                   log.debug("Got value for "+fields[i].getFieldName());
289                 }
290               }
291             } else {
292               // fuck around, fuck around, jump up jump up and get down! */
293               Object o = fields[i].getClassDescriptor();
294               if (o!=null) {
295                 // XMLClassDescriptorImpl fclasdes = (XMLClassDescriptorImpl) o;
296                 String methname = "get"+fields[i].getFieldName();
297                 Method fgetmeth = this.getClass().getMethod(methname,null);
298                 if (fgetmeth!=null) {
299                   Object fval = fgetmeth.invoke(this,null);
300                   if (fval!=null)
301                     rf = (object) fval;
302                 } else {
303                   log.warn("Couldn't find "+this.getClass().getName()+"."+methname);
304                 }
305               }
306             }
307             if (rf!=null) {
308               if (rf.__vorba==null)
309                 rf.__vorba = __vorba; // propagate IVorbaIdFactory
310               rf.__ensure_instance_ids();
311             }
312           }
313           catch (Exception e) {
314             log.error("Client error - could not access "+type.getName()+" in "+this.getClass().getName(), e);
315           }
316         }
317     }
318     
319   }
320 }