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