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