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