javadoc and datastoreitem registry support for binding/rebinding methods
[jalview.git] / src / jalview / io / vamsas / DatastoreItem.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Development Version 2.4.1)
3  * Copyright (C) 2009 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.io.vamsas;
20
21 import jalview.bin.Cache;
22 import jalview.datamodel.DBRefEntry;
23 import jalview.gui.TreePanel;
24 import jalview.io.VamsasAppDatastore;
25
26 import java.util.Enumeration;
27 import java.util.Hashtable;
28 import java.util.IdentityHashMap;
29 import java.util.Vector;
30
31 import org.apache.commons.logging.Log;
32
33 import uk.ac.vamsas.client.IClientDocument;
34 import uk.ac.vamsas.client.Vobject;
35 import uk.ac.vamsas.client.VorbaId;
36 import uk.ac.vamsas.objects.core.DbRef;
37 import uk.ac.vamsas.objects.core.Entry;
38 import uk.ac.vamsas.objects.core.Provenance;
39 import uk.ac.vamsas.objects.core.Seg;
40
41 /**
42  * Holds all the common machinery for binding objects to vamsas objects
43  * 
44  * @author JimP
45  * 
46  */
47 public abstract class DatastoreItem
48 {
49   /**
50    * 
51    */
52   Entry provEntry = null;
53
54   IClientDocument cdoc;
55
56   Hashtable vobj2jv;
57
58   IdentityHashMap jv2vobj;
59   
60   boolean tojalview=false;
61   /**
62    * shared log instance
63    */
64   protected static org.apache.log4j.Logger log = org.apache.log4j.Logger.getLogger(DatastoreItem.class);
65   /**
66    * note: this is taken verbatim from jalview.io.VamsasAppDatastore
67    * @return the Vobject bound to Jalview datamodel object
68    */
69   protected Vobject getjv2vObj(Object jvobj)
70   {
71     if (jv2vobj.containsKey(jvobj))
72     {
73       return cdoc.getObject((VorbaId) jv2vobj.get(jvobj));
74     }
75     if (Cache.log.isDebugEnabled())
76     {
77       Cache.log.debug("Returning null VorbaID binding for jalview object "
78               + jvobj);
79     }
80     return null;
81   }
82
83   /**
84    * 
85    * @param vobj
86    * @return Jalview datamodel object bound to the vamsas document object
87    */
88   protected Object getvObj2jv(uk.ac.vamsas.client.Vobject vobj)
89   {
90     if (vobj2jv == null)
91       return null;
92     VorbaId id = vobj.getVorbaId();
93     if (id == null)
94     {
95       id = cdoc.registerObject(vobj);
96       Cache.log
97               .debug("Registering new object and returning null for getvObj2jv");
98       return null;
99     }
100     if (vobj2jv.containsKey(vobj.getVorbaId()))
101     {
102       return vobj2jv.get(vobj.getVorbaId());
103     }
104     return null;
105   }
106
107   /**
108    * note: this is taken verbatim from jalview.io.VamsasAppDatastore
109    * with added call to updateRegistryEntry
110    * @param jvobj
111    * @param vobj
112    */
113   protected void bindjvvobj(Object jvobj, uk.ac.vamsas.client.Vobject vobj)
114   {
115     VorbaId id = vobj.getVorbaId();
116     if (id == null)
117     {
118       id = cdoc.registerObject(vobj);
119       if (id == null || vobj.getVorbaId() == null
120               || cdoc.getObject(id) != vobj)
121       {
122         Cache.log.error("Failed to get id for "
123                 + (vobj.isRegisterable() ? "registerable"
124                         : "unregisterable") + " object " + vobj);
125       }
126     }
127     if (vobj2jv.containsKey(vobj.getVorbaId())
128             && !(vobj2jv.get(vobj.getVorbaId())).equals(jvobj))
129     {
130       Cache.log.debug(
131               "Warning? Overwriting existing vamsas id binding for "
132                       + vobj.getVorbaId(), new Exception(
133                       "Overwriting vamsas id binding."));
134     }
135     else if (jv2vobj.containsKey(jvobj)
136             && !((VorbaId) jv2vobj.get(jvobj)).equals(vobj.getVorbaId()))
137     {
138       Cache.log.debug(
139               "Warning? Overwriting existing jalview object binding for "
140                       + jvobj, new Exception(
141                       "Overwriting jalview object binding."));
142     }
143     /*
144      * Cache.log.error("Attempt to make conflicting object binding! "+vobj+" id "
145      * +vobj.getVorbaId()+" already bound to "+getvObj2jv(vobj)+" and "+jvobj+"
146      * already bound to "+getjv2vObj(jvobj),new Exception("Excessive call to
147      * bindjvvobj")); }
148      */
149     // we just update the hash's regardless!
150     Cache.log.debug("Binding " + vobj.getVorbaId() + " to " + jvobj);
151     vobj2jv.put(vobj.getVorbaId(), jvobj);
152     // JBPNote - better implementing a hybrid invertible hash.
153     jv2vobj.put(jvobj, vobj.getVorbaId());
154     if (jvobj==this.jvobj || vobj==this.vobj)
155     {
156         updateRegistryEntry(jvobj,vobj);
157     }
158   }
159   /**
160    * update the vobj and jvobj references and the registry entry for this datastore object
161    * called by bindjvvobj and replacejvobjmapping
162    */
163   private void updateRegistryEntry(Object jvobj,Vobject vobj)
164   {
165     if (this.jvobj!=null && this.vobj!=null) {
166       Cache.log.debug("updating dsobj registry. ("+this.getClass().getName()+")");
167     }
168     this.jvobj = jvobj;
169     this.vobj = vobj;
170     dsReg.registerDsObj(this);
171   }
172
173   /**
174    * replaces oldjvobject with newjvobject in the Jalview Object <> VorbaID
175    * binding tables
176    * note: originally taken verbatim from jalview.io.VamsasAppDatastore with added call to updateRegistryEntry
177    * @param oldjvobject
178    * @param newjvobject (may be null to forget the oldjvobject's document mapping)
179    * 
180    */
181   protected void replaceJvObjMapping(Object oldjvobject, Object newjvobject)
182   {
183     Object vobject = jv2vobj.remove(oldjvobject);
184     if (vobject == null)
185     {
186       throw new Error(
187               "IMPLEMENTATION ERROR: old jalview object is not bound ! ("
188                       + oldjvobject + ")");
189     }
190     if (newjvobject!=null)
191     {
192       jv2vobj.put(newjvobject, vobject);
193       vobj2jv.put(vobject, newjvobject);
194       updateRegistryEntry(newjvobject,(Vobject) vobject);
195     }
196   }
197
198   public DatastoreItem()
199   {
200     super();
201   }
202
203   public DatastoreItem(VamsasAppDatastore datastore)
204   {
205     this();
206     initDatastoreItem(datastore);
207     // TODO Auto-generated constructor stub
208   }
209   /**
210    * construct and initialise datastore object and retrieve object bound to vobj2 and validate it against boundType
211    * @param datastore2
212    * @param vobj2
213    * @param boundType
214    */
215   public DatastoreItem(VamsasAppDatastore datastore2, Vobject vobj2, Class boundType)
216   {
217     this(datastore2);
218     vobj = vobj2;
219     jvobj = getvObj2jv(vobj2);
220     tojalview=true;
221     if (jvobj!=null && !(boundType.isAssignableFrom(jvobj.getClass())))
222     {
223       throw new Error("Implementation Error: Vamsas Document Class "+vobj.getClass()+" should bind to a "+boundType+" (found a "+jvobj.getClass()+")");
224     }
225     dsReg.registerDsObj(this);
226   }
227   /**
228    * construct and initialise datastore object and retrieve document object bound to Jalview object jvobj2 and validate it against boundType
229    * @param datastore2 the datastore
230    * @param jvobj2 the jalview object
231    * @param boundToType - the document object class that the bound object should be assignable from
232    */
233   public DatastoreItem(VamsasAppDatastore datastore2, Object jvobj2, Class boundToType)
234   {
235     this(datastore2);
236     jvobj = jvobj2;
237     tojalview=false;
238     vobj = getjv2vObj(jvobj);
239     if (vobj!=null && !(boundToType.isAssignableFrom(vobj.getClass())))
240     {
241       throw new Error("Implementation Error: Jalview Class "+jvobj2.getClass()+" should bind to a "+boundToType+" (found a "+vobj.getClass()+")");
242     }
243     dsReg.registerDsObj(this);
244   }
245   /**
246    * create a new vobj to be added to the document 
247    * for the jalview object jvobj
248    * (jvobj!=null, vobj==null)
249    */
250   public abstract void addToDocument();
251   /**
252    * handle a conflict where both an existing vobj has been updated
253    *  and a local jalview object has been updated. This method
254    *  is only called from doSync, when an incoming update from the vamsas
255    *  session conflicts with local modifications made by the Jalview user. 
256    *  (jvobj!=null, vobj!=null)
257    */
258   public abstract void conflict();
259   /**
260    * update an existing vobj in the document with the data and settings from jvobj
261    * (jvobj!=null, vobj!=null) 
262    */
263   public abstract void updateToDoc();
264   /**
265    * update the local jalview object with the data from an existing vobj in the document 
266    * (jvobj!=null, vobj!=null) 
267    */
268   public abstract void updateFromDoc();
269   /**
270    * create a new local jvobj bound to the vobj in the document.
271    * (jvobj==null, vobj!=null)
272    */
273   public abstract void addFromDocument();
274   boolean addtodoc=false, conflicted=false,updated=false,addfromdoc=false,success=false;
275
276   private boolean updatedtodoc;
277
278   private boolean updatedfromdoc;
279   /**
280    * Sync jalview to document. Enact addToDocument, conflict or update dependent on
281    * existence of a vobj bound to the local jvobj. 
282    */
283   protected void doSync()
284   {
285     dsReg.registerDsObj(this);
286     if (vobj == null)
287     {
288       log.debug("adding new vobject to document.");
289       addtodoc=true;
290       addToDocument();
291     }
292     else
293     {
294       if (vobj.isUpdated())
295       {
296         log.debug("Handling update conflict for existing bound vobject.");
297         conflicted=true;
298         conflict();
299       }
300       else
301       {
302         log.debug("updating existing vobject in document.");
303         updatedtodoc=true;
304         updateToDoc();
305       }
306     }
307     // no exceptions were encountered...
308     success=true;
309   }
310   /**
311    * Update jalview from document. enact addFromDocument if no local jvobj exists, or update iff jvobj
312    * exists and the vobj.isUpdated() flag is set. 
313    */
314   protected void doJvUpdate()
315   {
316     dsReg.registerDsObj(this);
317     if (jvobj == null)
318     {
319       log.debug("adding new vobject to Jalview from Document");
320       addfromdoc=true;
321       addFromDocument();
322     }
323     else
324     {
325       if (vobj.isUpdated())
326       {
327         log.debug("updating Jalview from existing bound vObject");
328         updatedfromdoc=true;
329         updateFromDoc();
330       }
331     }
332   }
333
334   VamsasAppDatastore datastore = null;
335   /**
336    * object in vamsas document
337    */
338   protected Vobject vobj = null;
339   /**
340    * local jalview object
341    */
342   protected Object jvobj = null;
343
344   protected DatastoreRegistry dsReg;
345
346   public void initDatastoreItem(VamsasAppDatastore ds)
347   {
348     datastore = ds;
349     dsReg = ds.getDatastoreRegisty();
350     initDatastoreItem(ds.getProvEntry(), ds.getClientDocument(), ds
351             .getVamsasObjectBinding(), ds.getJvObjectBinding());
352   }
353
354   private void initDatastoreItem(Entry provEntry, IClientDocument cdoc,
355           Hashtable vobj2jv, IdentityHashMap jv2vobj)
356   {
357     this.provEntry = provEntry;
358     this.cdoc = cdoc;
359     this.vobj2jv = vobj2jv;
360     this.jv2vobj = jv2vobj;
361   }
362
363   protected boolean isModifiable(String modifiable)
364   {
365     return modifiable == null; // TODO: USE VAMSAS LIBRARY OBJECT LOCK METHODS)
366   }
367
368   protected Vector getjv2vObjs(Vector alsq)
369   {
370     Vector vObjs = new Vector();
371     Enumeration elm = alsq.elements();
372     while (elm.hasMoreElements())
373     {
374       vObjs.addElement(getjv2vObj(elm.nextElement()));
375     }
376     return vObjs;
377   }
378
379   // utility functions
380   /**
381    * get start<end range of segment, adjusting for inclusivity flag and
382    * polarity.
383    * 
384    * @param visSeg
385    * @param ensureDirection
386    *                when true - always ensure start is less than end.
387    * @return int[] { start, end, direction} where direction==1 for range running
388    *         from end to start.
389    */
390   public int[] getSegRange(Seg visSeg, boolean ensureDirection)
391   {
392     boolean incl = visSeg.getInclusive();
393     // adjust for inclusive flag.
394     int pol = (visSeg.getStart() <= visSeg.getEnd()) ? 1 : -1; // polarity of
395     // region.
396     int start = visSeg.getStart() + (incl ? 0 : pol);
397     int end = visSeg.getEnd() + (incl ? 0 : -pol);
398     if (ensureDirection && pol == -1)
399     {
400       // jalview doesn't deal with inverted ranges, yet.
401       int t = end;
402       end = start;
403       start = t;
404     }
405     return new int[]
406     { start, end, pol < 0 ? 1 : 0 };
407   }
408
409   /**
410    * provenance bits
411    */
412   protected jalview.datamodel.Provenance getJalviewProvenance(
413           Provenance prov)
414   {
415     // TODO: fix App and Action entries and check use of provenance in jalview.
416     jalview.datamodel.Provenance jprov = new jalview.datamodel.Provenance();
417     for (int i = 0; i < prov.getEntryCount(); i++)
418     {
419       jprov.addEntry(prov.getEntry(i).getUser(), prov.getEntry(i)
420               .getAction(), prov.getEntry(i).getDate(), prov.getEntry(i)
421               .getId());
422     }
423
424     return jprov;
425   }
426
427   /**
428    * 
429    * @return default initial provenance list for a Jalview created vamsas
430    *         object.
431    */
432   Provenance dummyProvenance()
433   {
434     return dummyProvenance(null);
435   }
436
437   protected Entry dummyPEntry(String action)
438   {
439     Entry entry = new Entry();
440     entry.setApp(this.provEntry.getApp());
441     if (action != null)
442     {
443       entry.setAction(action);
444     }
445     else
446     {
447       entry.setAction("created.");
448     }
449     entry.setDate(new java.util.Date());
450     entry.setUser(this.provEntry.getUser());
451     return entry;
452   }
453
454   protected Provenance dummyProvenance(String action)
455   {
456     Provenance prov = new Provenance();
457     prov.addEntry(dummyPEntry(action));
458     return prov;
459   }
460
461   protected void addProvenance(Provenance p, String action)
462   {
463     p.addEntry(dummyPEntry(action));
464   }
465
466
467   /**
468    * @return true if jalview was being updated from the vamsas document
469    */
470   public boolean isTojalview()
471   {
472     return tojalview;
473   }
474
475   /**
476    * @return true if addToDocument() was called.
477    */
478   public boolean isAddtodoc()
479   {
480     return addtodoc;
481   }
482
483   /**
484    * @return true if conflict() was called
485    */
486   public boolean isConflicted()
487   {
488     return conflicted;
489   }
490
491   /**
492    * @return true if updateFromDoc() was called
493    */
494   public boolean isUpdatedFromDoc()
495   {
496     return updatedfromdoc;
497   }
498   /**
499    * @return true if updateToDoc() was called
500    */
501   public boolean isUpdatedToDoc()
502   {
503     return updatedtodoc;
504   }
505
506   /**
507    * @return true if addFromDocument() was called.
508    */
509   public boolean isAddfromdoc()
510   {
511     return addfromdoc;
512   }
513
514   /**
515    * @return true if object sync logic completed normally.
516    */
517   public boolean isSuccess()
518   {
519     return success;
520   }
521
522   /**
523    * @return the vobj
524    */
525   public Vobject getVobj()
526   {
527     return vobj;
528   }
529
530   /**
531    * @return the jvobj
532    */
533   public Object getJvobj()
534   {
535     return jvobj;
536   }
537
538   public boolean docWasUpdated()
539   {
540     return (this.addtodoc || this.updated) && this.success;
541   }
542
543   public boolean jvWasUpdated()
544   {
545     return (success); // TODO : Implement this properly!
546   }
547
548 }