489f56fbeb6b3125c634a286682ed3c3ecab94c8
[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
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
128     if (vobj2jv.containsKey(vobj.getVorbaId())
129             && !(vobj2jv.get(vobj.getVorbaId())).equals(jvobj))
130     {
131       Cache.log.debug(
132               "Warning? Overwriting existing vamsas id binding for "
133                       + vobj.getVorbaId(), new Exception(
134                       "Overwriting vamsas id binding."));
135     }
136     else if (jv2vobj.containsKey(jvobj)
137             && !((VorbaId) jv2vobj.get(jvobj)).equals(vobj.getVorbaId()))
138     {
139       Cache.log.debug(
140               "Warning? Overwriting existing jalview object binding for "
141                       + jvobj, new Exception(
142                       "Overwriting jalview object binding."));
143     }
144     /*
145      * Cache.log.error("Attempt to make conflicting object binding! "+vobj+" id "
146      * +vobj.getVorbaId()+" already bound to "+getvObj2jv(vobj)+" and "+jvobj+"
147      * already bound to "+getjv2vObj(jvobj),new Exception("Excessive call to
148      * bindjvvobj")); }
149      */
150     // we just update the hash's regardless!
151     Cache.log.debug("Binding " + vobj.getVorbaId() + " to " + jvobj);
152     vobj2jv.put(vobj.getVorbaId(), jvobj);
153     // JBPNote - better implementing a hybrid invertible hash.
154     jv2vobj.put(jvobj, vobj.getVorbaId());
155   }
156   /**
157    * replaces oldjvobject with newjvobject in the Jalview Object <> VorbaID
158    * binding tables
159    * note: this is taken verbatim from jalview.io.VamsasAppDatastore
160    * @param oldjvobject
161    * @param newjvobject (may be null to forget the oldjvobject's document mapping)
162    * 
163    */
164   protected void replaceJvObjMapping(Object oldjvobject, Object newjvobject)
165   {
166     Object vobject = jv2vobj.remove(oldjvobject);
167     if (vobject == null)
168     {
169       throw new Error(
170               "IMPLEMENTATION ERROR: old jalview object is not bound ! ("
171                       + oldjvobject + ")");
172     }
173     if (newjvobject!=null)
174     {
175       jv2vobj.put(newjvobject, vobject);
176       vobj2jv.put(vobject, newjvobject);
177     }
178   }
179
180   public DatastoreItem()
181   {
182     super();
183   }
184
185   public DatastoreItem(VamsasAppDatastore datastore)
186   {
187     this();
188     initDatastoreItem(datastore);
189     // TODO Auto-generated constructor stub
190   }
191   /**
192    * construct and initialise datastore object and retrieve object bound to vobj2 and validate it against boundType
193    * @param datastore2
194    * @param vobj2
195    * @param boundType
196    */
197   public DatastoreItem(VamsasAppDatastore datastore2, Vobject vobj2, Class boundType)
198   {
199     this(datastore2);
200     vobj = vobj2;
201     jvobj = getvObj2jv(vobj2);
202     tojalview=true;
203     if (jvobj!=null && !(boundType.isAssignableFrom(jvobj.getClass())))
204     {
205       throw new Error("Implementation Error: Vamsas Document Class "+vobj.getClass()+" should bind to a "+boundType+" (found a "+jvobj.getClass()+")");
206     }
207     dsReg.registerDsObj(this);
208   }
209   /**
210    * construct and initialise datastore object and retrieve document object bound to Jalview object jvobj2 and validate it against boundType
211    * @param datastore2 the datastore
212    * @param jvobj2 the jalview object
213    * @param boundToType - the document object class that the bound object should be assignable from
214    */
215   public DatastoreItem(VamsasAppDatastore datastore2, Object jvobj2, Class boundToType)
216   {
217     this(datastore2);
218     jvobj = jvobj2;
219     tojalview=false;
220     vobj = getjv2vObj(jvobj);
221     if (vobj!=null && !(boundToType.isAssignableFrom(vobj.getClass())))
222     {
223       throw new Error("Implementation Error: Jalview Class "+jvobj2.getClass()+" should bind to a "+boundToType+" (found a "+vobj.getClass()+")");
224     }
225     dsReg.registerDsObj(this);
226   }
227   /**
228    * create a new vobj to be added to the document 
229    * for the jalview object jvobj
230    * (jvobj!=null, vobj==null)
231    */
232   public abstract void addToDocument();
233   /**
234    * handle a conflict where both an existing vobj has been updated
235    *  and a local jalview object has been updated. This method
236    *  is only called from doSync, when an incoming update from the vamsas
237    *  session conflicts with local modifications made by the Jalview user. 
238    *  (jvobj!=null, vobj!=null)
239    */
240   public abstract void conflict();
241   /**
242    * update an existing vobj in the document with the data and settings from jvobj
243    * (jvobj!=null, vobj!=null) 
244    */
245   public abstract void updateToDoc();
246   /**
247    * update the local jalview object with the data from an existing vobj in the document 
248    * (jvobj!=null, vobj!=null) 
249    */
250   public abstract void updateFromDoc();
251   /**
252    * create a new local jvobj bound to the vobj in the document.
253    * (jvobj==null, vobj!=null)
254    */
255   public abstract void addFromDocument();
256   boolean addtodoc=false, conflicted=false,updated=false,addfromdoc=false,success=false;
257
258   private boolean updatedtodoc;
259
260   private boolean updatedfromdoc;
261   /**
262    * Sync jalview to document. Enact addToDocument, conflict or update dependent on
263    * existence of a vobj bound to the local jvobj. 
264    */
265   protected void doSync()
266   {
267     dsReg.registerDsObj(this);
268     if (vobj == null)
269     {
270       log.debug("adding new vobject to document.");
271       addtodoc=true;
272       addToDocument();
273     }
274     else
275     {
276       if (vobj.isUpdated())
277       {
278         log.debug("Handling update conflict for existing bound vobject.");
279         conflicted=true;
280         conflict();
281       }
282       else
283       {
284         log.debug("updating existing vobject in document.");
285         updatedtodoc=true;
286         updateToDoc();
287       }
288     }
289     // no exceptions were encountered...
290     success=true;
291   }
292   /**
293    * Update jalview from document. enact addFromDocument if no local jvobj exists, or update iff jvobj
294    * exists and the vobj.isUpdated() flag is set. 
295    */
296   protected void doJvUpdate()
297   {
298     dsReg.registerDsObj(this);
299     if (jvobj == null)
300     {
301       log.debug("adding new vobject to Jalview from Document");
302       addfromdoc=true;
303       addFromDocument();
304     }
305     else
306     {
307       if (vobj.isUpdated())
308       {
309         log.debug("updating Jalview from existing bound vObject");
310         updatedfromdoc=true;
311         updateFromDoc();
312       }
313     }
314   }
315
316   VamsasAppDatastore datastore = null;
317   /**
318    * object in vamsas document
319    */
320   protected Vobject vobj = null;
321   /**
322    * local jalview object
323    */
324   protected Object jvobj = null;
325
326   protected DatastoreRegistry dsReg;
327
328   public void initDatastoreItem(VamsasAppDatastore ds)
329   {
330     datastore = ds;
331     dsReg = ds.getDatastoreRegisty();
332     initDatastoreItem(ds.getProvEntry(), ds.getClientDocument(), ds
333             .getVamsasObjectBinding(), ds.getJvObjectBinding());
334   }
335
336   private void initDatastoreItem(Entry provEntry, IClientDocument cdoc,
337           Hashtable vobj2jv, IdentityHashMap jv2vobj)
338   {
339     this.provEntry = provEntry;
340     this.cdoc = cdoc;
341     this.vobj2jv = vobj2jv;
342     this.jv2vobj = jv2vobj;
343   }
344
345   protected boolean isModifiable(String modifiable)
346   {
347     return modifiable == null; // TODO: USE VAMSAS LIBRARY OBJECT LOCK METHODS)
348   }
349
350   protected Vector getjv2vObjs(Vector alsq)
351   {
352     Vector vObjs = new Vector();
353     Enumeration elm = alsq.elements();
354     while (elm.hasMoreElements())
355     {
356       vObjs.addElement(getjv2vObj(elm.nextElement()));
357     }
358     return vObjs;
359   }
360
361   // utility functions
362   /**
363    * get start<end range of segment, adjusting for inclusivity flag and
364    * polarity.
365    * 
366    * @param visSeg
367    * @param ensureDirection
368    *                when true - always ensure start is less than end.
369    * @return int[] { start, end, direction} where direction==1 for range running
370    *         from end to start.
371    */
372   public int[] getSegRange(Seg visSeg, boolean ensureDirection)
373   {
374     boolean incl = visSeg.getInclusive();
375     // adjust for inclusive flag.
376     int pol = (visSeg.getStart() <= visSeg.getEnd()) ? 1 : -1; // polarity of
377     // region.
378     int start = visSeg.getStart() + (incl ? 0 : pol);
379     int end = visSeg.getEnd() + (incl ? 0 : -pol);
380     if (ensureDirection && pol == -1)
381     {
382       // jalview doesn't deal with inverted ranges, yet.
383       int t = end;
384       end = start;
385       start = t;
386     }
387     return new int[]
388     { start, end, pol < 0 ? 1 : 0 };
389   }
390
391   /**
392    * provenance bits
393    */
394   protected jalview.datamodel.Provenance getJalviewProvenance(
395           Provenance prov)
396   {
397     // TODO: fix App and Action entries and check use of provenance in jalview.
398     jalview.datamodel.Provenance jprov = new jalview.datamodel.Provenance();
399     for (int i = 0; i < prov.getEntryCount(); i++)
400     {
401       jprov.addEntry(prov.getEntry(i).getUser(), prov.getEntry(i)
402               .getAction(), prov.getEntry(i).getDate(), prov.getEntry(i)
403               .getId());
404     }
405
406     return jprov;
407   }
408
409   /**
410    * 
411    * @return default initial provenance list for a Jalview created vamsas
412    *         object.
413    */
414   Provenance dummyProvenance()
415   {
416     return dummyProvenance(null);
417   }
418
419   protected Entry dummyPEntry(String action)
420   {
421     Entry entry = new Entry();
422     entry.setApp(this.provEntry.getApp());
423     if (action != null)
424     {
425       entry.setAction(action);
426     }
427     else
428     {
429       entry.setAction("created.");
430     }
431     entry.setDate(new java.util.Date());
432     entry.setUser(this.provEntry.getUser());
433     return entry;
434   }
435
436   protected Provenance dummyProvenance(String action)
437   {
438     Provenance prov = new Provenance();
439     prov.addEntry(dummyPEntry(action));
440     return prov;
441   }
442
443   protected void addProvenance(Provenance p, String action)
444   {
445     p.addEntry(dummyPEntry(action));
446   }
447
448
449   /**
450    * @return true if jalview was being updated from the vamsas document
451    */
452   public boolean isTojalview()
453   {
454     return tojalview;
455   }
456
457   /**
458    * @return true if addToDocument() was called.
459    */
460   public boolean isAddtodoc()
461   {
462     return addtodoc;
463   }
464
465   /**
466    * @return true if conflict() was called
467    */
468   public boolean isConflicted()
469   {
470     return conflicted;
471   }
472
473   /**
474    * @return true if updateFromDoc() was called
475    */
476   public boolean isUpdatedFromDoc()
477   {
478     return updatedfromdoc;
479   }
480   /**
481    * @return true if updateToDoc() was called
482    */
483   public boolean isUpdatedToDoc()
484   {
485     return updatedtodoc;
486   }
487
488   /**
489    * @return true if addFromDocument() was called.
490    */
491   public boolean isAddfromdoc()
492   {
493     return addfromdoc;
494   }
495
496   /**
497    * @return true if object sync logic completed normally.
498    */
499   public boolean isSuccess()
500   {
501     return success;
502   }
503
504   /**
505    * @return the vobj
506    */
507   public Vobject getVobj()
508   {
509     return vobj;
510   }
511
512   /**
513    * @return the jvobj
514    */
515   public Object getJvobj()
516   {
517     return jvobj;
518   }
519
520   public boolean docWasUpdated()
521   {
522     return (this.addtodoc || this.updated) && this.success;
523   }
524
525   public boolean jvWasUpdated()
526   {
527     return (success); // TODO : Implement this properly!
528   }
529
530 }