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