71365ee7220bd686da33b98c2c0272edb35e8d15
[vamsas.git] / src / org / vamsas / client / simpleclient / ClientDocument.java
1 /*
2  *
3  */
4 package org.vamsas.client.simpleclient;
5
6 import java.util.Hashtable;
7 import java.util.Vector;
8
9 import org.apache.commons.logging.Log;
10 import org.apache.commons.logging.LogFactory;
11 import org.vamsas.client.IClient;
12 import org.vamsas.client.IClientAppdata;
13 import org.vamsas.client.IClientDocument;
14 import org.vamsas.client.VorbaId;
15 import org.vamsas.client.Vobject;
16 import org.vamsas.objects.core.AppData;
17 import org.vamsas.objects.core.ApplicationData;
18 import org.vamsas.objects.core.User;
19 import org.vamsas.objects.core.VAMSAS;
20 import org.vamsas.objects.core.VamsasDocument;
21 import org.vamsas.objects.utils.AppDataReference;
22
23 /**
24  * Maintains a collection of vamsas objects, appdatas and states, and provides api for a SimpleClient's client.
25  * @author jimp 
26  */
27 public class ClientDocument extends org.vamsas.client.ClientDocument implements IClientDocument {
28   private static Log log = LogFactory.getLog(ClientDocument.class);
29   private VamsasDocument doc;
30   protected SimpleClient sclient;
31   protected VamsasArchive archive = null;
32   /**
33    * indicate if new data has been incorporated
34    */
35   private boolean isModified = false;
36   /**
37    * Public method for internal use by SimpleClient.
38    * @return true if document has been modified.
39    */
40   public boolean isModified() {
41     return isModified;
42   }
43   /**
44    *
45    *  prepare Application-side dataset from the vamsas Document archive
46    * @param doc - the dataset
47    * @param docHandler - the sessionFile IO handler
48    * @param Factory - the source of current and new vorbaIds
49    * @param sclient - the simpleclient instance
50    */
51   protected ClientDocument(VamsasDocument doc, VamsasArchive docHandler, IdFactory Factory, SimpleClient sclient) {
52     super(Factory.getVorbaIdHash(), Factory);
53     
54     /**
55      * prepare Application-side dataset from the vamsas Document archive
56      */
57     this.sclient = sclient;
58     archive = docHandler;
59     this.doc = doc;
60   }
61   
62   /*
63    * (non-Javadoc)
64    * 
65    * @see org.vamsas.client.IClientDocument#getObject(org.vamsas.client.VorbaId)
66    */
67   public Vobject getObject(VorbaId id) {
68     // TODO: look up id in document Vobject
69     // retrieve Vobject and return
70     return null;
71   }
72   
73   /*
74    * (non-Javadoc)
75    * 
76    * @see org.vamsas.client.IClientDocument#getObjects(org.vamsas.client.VorbaId[])
77    */
78   public Vobject[] getObjects(VorbaId[] ids) {
79     // TODO: getObject in bulk
80     return null;
81   }
82   /**
83    * internal reference to single copy of Document Roots array
84    */
85   private VAMSAS[] _VamsasRoots=null;
86   /*
87    * (non-Javadoc)
88    * LATER: currently there is only one Vector of roots ever passed to client - decide if this is correct (means this is not thread safe and may behave unexpectedly)
89    * @see org.vamsas.client.IClientDocument#getVamsasRoots()
90    */
91   public VAMSAS[] getVamsasRoots() {
92     if (_VamsasRoots!=null)
93       return _VamsasRoots;
94     VAMSAS[] roots = doc.getVAMSAS();
95     if (roots == null) {
96       // Make a new one to return to client : TODO: Decide if this is correct
97       _VamsasRoots = new VAMSAS[] { new VAMSAS() };
98     } else {
99       _VamsasRoots = new VAMSAS[roots.length];
100       for (int r=0;r<roots.length; r++)
101         _VamsasRoots[r] = roots[r];
102     }
103     return _VamsasRoots;
104   }
105   
106   private int _contains(VAMSAS root, VAMSAS[] docRoots) {
107     if (root==null)
108       return -1;
109     if (docRoots==null || docRoots.length==0)
110       return -1;
111     String r_id = root.getId();
112     for (int i=0,j=docRoots.length; i<j; i++)
113       if (docRoots[i]==root || (docRoots[i]!=null && docRoots[i].getId().equals(r_id)))
114         return i;
115     return -1;
116   }
117 /**
118  * verify that newr version is really an intact version of the 
119  * @param newVersion (may be modified)
120  * @param oldVersion 
121  * @return true if newVersion is a valid root that preserves original references
122  */
123   private boolean isValidUpdate(VAMSAS newVersion, final VAMSAS oldVersion) {
124     // LATER: IMPLEMENT
125     // ideal - this cascades down the two structures, ensuring that all ID'd objects in one are present in the other.
126     if (oldVersion==newVersion) {
127       // may be a virgin root element.
128       if (!newVersion.isRegistered())
129         _registerObject(newVersion);
130       // Should retrieve original version and compare - unless local hashes can be used to determine if resultSet has been truncated.
131       // just do internal validation for moment.
132       if (newVersion.isValid())
133         return true;
134       return false;
135     } else {
136       // redundant ? if (oldVersion.is__stored_in_document())
137       if (!newVersion.isRegistered())
138         _registerObject(newVersion);
139       if (newVersion.isValid())
140         return true;
141     }
142     return false;
143   }
144   /**
145    * merge old and new root vectors
146    * @param newr This array may be written to
147    * @param original
148    * @param the client document (usually this) which this root set belongs to.
149    * @return merged vector of vamsas roots
150    */
151   private VAMSAS[] _combineRoots(VAMSAS[] newr, final VAMSAS[] original, ClientDocument modflag) {
152     Vector rts = new Vector();
153     boolean modified=false;
154     for (int i=0,j=original.length; i<j; i++) {
155       int k = _contains(original[i], newr);
156       if (k>-1) {
157         if (isValidUpdate(newr[k], original[i])) {
158           modified=true;
159           rts.add(newr[k]);
160           newr[k]=null;
161         } else {
162           // LATER: try harder to merge ducument roots.
163           log.warn("Couldn't merge new VAMSAS root "+newr[k].getId());
164           newr[k] = null; // LATER: this means we ignore mangled roots. NOT GOOD
165         }
166       } else {
167         // add in order.
168         rts.add(original[i]);
169       }
170     }
171     // add remaining (new) roots
172     for (int i=0,j=newr.length; i<j; i++) {
173       if (newr[i]!=null) {
174         rts.add(newr[i]);
175         modified=true;
176       }
177     }
178     newr = new VAMSAS[rts.size()];
179     for (int i=0,j=rts.size(); i<j; i++)
180       newr[i] = (VAMSAS) rts.get(i);
181     if (modflag!=null)
182       modflag.isModified = modified;
183     return newr;
184   }
185   
186   /**
187    * update the document with new roots.
188    * LATER: decide: this affects the next call to getVamsasRoots()
189    */
190   public void setVamsasRoots(VAMSAS[] newroots) {
191     
192     // are we dealing with same array ?
193     if (_VamsasRoots!=newroots) {
194       // merge roots into 
195     }
196     // extract root objects
197     if (newroots != null) {
198       // check newroots for objects that were present in the old document
199       // check to see if the 'old' objects have been modified
200       // if they have ? we overwrite them with their new version, ensuring that
201       // provenance is updated.
202       // if they haven't ? do nothing ?
203       
204       for (int i = 0, k = newroots.length; i < k; i++) {
205         if (newroots[i].isRegistered()) {
206           // easy - just check if anything has changed and do provenance
207           Vobject oldversion = getObject(newroots[i].getVorbaId());
208           if (oldversion instanceof VAMSAS) {
209             // LATER: appropriate merging behaviour when two clients have improperly modified the same Vobject independently.
210             if (newroots[i].get__last_hash() != newroots[i].hashCode()) {
211               // client has modified this Vobject since last retrieval.
212               if (newroots[i].get__last_hash() != oldversion.get__last_hash()) {
213                 // Vobject has been modified by another client since this
214                 // client's
215                 // last access to document.
216               }
217             }
218           } else {
219             throw new Error(
220                 "SimpleClient error when using setVamsasRoots : The vorbaId for Vobject "
221                 + i
222                 + " does not refer to an Vobject of type VAMSAS in the current document!");
223           }
224         } else {
225           if (!newroots[i].is__stored_in_document()) {
226             // check if Vobject is modified
227             if (newroots[i].get__last_hash() != newroots[i].hashCode()) {
228               // it is - so we add newroots[i] as a new Vobject, with updated
229               // provenance.
230             } else {
231               // do nothing
232               newroots[i] = null;
233             }
234           } else {
235             // just add newroots[i] as a new Vobject in the document
236             // - with appropriate provenance.
237           }
238         }
239       }
240     }
241   }
242   
243   /* (non-Javadoc)
244    * LATER: decide: this affects the next call to getVamsasRoots()
245    * @see org.vamsas.client.IClientDocument#addVamsasRoot(org.vamsas.objects.core.VAMSAS)
246    */
247   public void addVamsasRoot(VAMSAS newroot) {
248     VAMSAS[] newroots = _combineRoots(new VAMSAS[] {newroot}, _VamsasRoots, this);
249     _VamsasRoots = newroots;  
250   }
251   
252   /*
253    * (non-Javadoc)
254    * 
255    * @see org.vamsas.client.IClientDocument#registerObjects(org.vamsas.client.Vobject[])
256    */
257   public VorbaId[] registerObjects(Vobject[] unregistered) {
258     if (unregistered!=null) {
259       VorbaId ids[] = new VorbaId[unregistered.length];
260       for (int i=0,k=unregistered.length; i<k; i++)
261         if (unregistered[i]!=null) {
262           log.warn("Null Vobject passed to registerObject[] at position "+i);
263           return null;
264         } else {
265           ids[i]=registerObject(unregistered[i]);
266         }
267       return ids;
268     }
269     return null;
270   }
271   /* (non-Javadoc)
272    * @see org.vamsas.client.IClientDocument#registerObject(org.vamsas.client.Vobject)
273    */
274   public VorbaId registerObject(Vobject unregistered) {
275     if (unregistered!=null)
276       return _registerObject(unregistered);
277     log.warn("Null Vobject passed to registerObject.");
278     return null;
279   }
280   /**
281    * IClientAppdata instance - if it exists.
282    */
283   SimpleClientAppdata scappd = null;
284   /* (non-Javadoc)
285    * @see org.vamsas.client.IClientDocument#getClientAppdata()
286    */
287   public IClientAppdata getClientAppdata() {
288     if (scappd==null) {
289       log.debug("Creating new SimpleClientAppdata instance for "+sclient.getSessionHandle());
290       scappd = new SimpleClientAppdata(this);
291       if (scappd==null) {
292         // LATER: may not need this as a warning message.
293         log.warn("Null appdata object for "+sclient.getSessionHandle());
294       } else {
295         log.debug("Created SimpleClientAppdata successfully.");
296       }
297     } else {
298       log.debug("Returning existing SimpleClientAppdata reference.");
299     }
300     return scappd;
301   }
302   /* (non-Javadoc)
303    * @see java.lang.Object#finalize()
304    */
305   protected void finalize() throws Throwable {
306     log.debug("Garbage collecting on ClientDocument instance.");
307     if (scappd!=null) {
308       scappd.finalize();
309       scappd = null;
310     }
311     
312     super.finalize();
313   }
314   /**
315    * access the vamsas document
316    * @return the session's vamsas document
317    */
318   protected VamsasDocument getVamsasDocument() {
319     return doc;
320   }
321   /**
322    * returns the read-only IO interface for the vamsas document Jar file
323    * @return
324    */
325   protected VamsasArchiveReader getVamsasArchiveReader() {
326     try {
327       return archive.getOriginalArchiveReader();
328     } catch (Exception e) {
329       log.warn("Unable to create OriginalArchiveReader!", e);
330     }
331     return null;
332   }
333 }