refactored to propery separate addToDocument,addFromDocument, updateToDocument, updat...
[jalview.git] / src / jalview / io / vamsas / DatastoreItem.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.4)
3  * Copyright (C) 2008 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             && !((VorbaId) 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   }
208   /**
209    * construct and initialise datastore object and retrieve document object bound to Jalview object jvobj2 and validate it against boundType
210    * @param datastore2 the datastore
211    * @param jvobj2 the jalview object
212    * @param boundToType - the document object class that the bound object should be assignable from
213    */
214   public DatastoreItem(VamsasAppDatastore datastore2, Object jvobj2, Class boundToType)
215   {
216     this(datastore2);
217     jvobj = jvobj2;
218     tojalview=false;
219     vobj = getjv2vObj(jvobj);
220     if (vobj!=null && !(boundToType.isAssignableFrom(vobj.getClass())))
221     {
222       throw new Error("Implementation Error: Jalview Class "+jvobj2.getClass()+" should bind to a "+boundToType+" (found a "+vobj.getClass()+")");
223     }
224   }
225   /**
226    * create a new vobj to be added to the document 
227    * for the jalview object jvobj
228    * (jvobj!=null, vobj==null)
229    */
230   public abstract void addToDocument();
231   /**
232    * handle a conflict where both an existing vobj has been updated
233    *  and a local jalview object has been updated. This method
234    *  is only called from doSync, when an incoming update from the vamsas
235    *  session conflicts with local modifications made by the Jalview user. 
236    *  (jvobj!=null, vobj!=null)
237    */
238   public abstract void conflict();
239   /**
240    * update an existing vobj in the document with the data and settings from jvobj
241    * (jvobj!=null, vobj!=null) 
242    */
243   public abstract void updateToDoc();
244   /**
245    * update the local jalview object with the data from an existing vobj in the document 
246    * (jvobj!=null, vobj!=null) 
247    */
248   public abstract void updateFromDoc();
249   /**
250    * create a new local jvobj bound to the vobj in the document.
251    * (jvobj==null, vobj!=null)
252    */
253   public abstract void addFromDocument();
254   boolean addtodoc=false, conflicted=false,updated=false,addfromdoc=false,success=false;
255
256   private boolean updatedtodoc;
257
258   private boolean updatedfromdoc;
259   /**
260    * Sync jalview to document. Enact addToDocument, conflict or update dependent on
261    * existence of a vobj bound to the local jvobj. 
262    */
263   protected void doSync()
264   {
265     
266     if (vobj == null)
267     {
268       log.debug("adding new vobject to document.");
269       addtodoc=true;
270       addToDocument();
271     }
272     else
273     {
274       if (vobj.isUpdated())
275       {
276         log.debug("Handling update conflict for existing bound vobject.");
277         conflicted=true;
278         conflict();
279       }
280       else
281       {
282         log.debug("updating existing vobject in document.");
283         updatedtodoc=true;
284         updateToDoc();
285       }
286     }
287     // no exceptions were encountered...
288     success=true;
289   }
290   /**
291    * Update jalview from document. enact addFromDocument if no local jvobj exists, or update iff jvobj
292    * exists and the vobj.isUpdated() flag is set. 
293    */
294   protected void doJvUpdate()
295   {
296     if (jvobj == null)
297     {
298       log.debug("adding new vobject to Jalview from Document");
299       addfromdoc=true;
300       addFromDocument();
301     }
302     else
303     {
304       if (vobj.isUpdated())
305       {
306         log.debug("updating Jalview from existing bound vObject");
307         updatedfromdoc=true;
308         updateFromDoc();
309       }
310     }
311   }
312
313   VamsasAppDatastore datastore = null;
314   /**
315    * object in vamsas document
316    */
317   protected Vobject vobj = null;
318   /**
319    * local jalview object
320    */
321   protected Object jvobj = null;
322
323   public void initDatastoreItem(VamsasAppDatastore ds)
324   {
325     datastore = ds;
326     initDatastoreItem(ds.getProvEntry(), ds.getClientDocument(), ds
327             .getVamsasObjectBinding(), ds.getJvObjectBinding());
328   }
329
330   public void initDatastoreItem(Entry provEntry, IClientDocument cdoc,
331           Hashtable vobj2jv, IdentityHashMap jv2vobj)
332   {
333     this.provEntry = provEntry;
334     this.cdoc = cdoc;
335     this.vobj2jv = vobj2jv;
336     this.jv2vobj = jv2vobj;
337   }
338
339   protected boolean isModifiable(String modifiable)
340   {
341     return modifiable == null; // TODO: USE VAMSAS LIBRARY OBJECT LOCK METHODS)
342   }
343
344   protected Vector getjv2vObjs(Vector alsq)
345   {
346     Vector vObjs = new Vector();
347     Enumeration elm = alsq.elements();
348     while (elm.hasMoreElements())
349     {
350       vObjs.addElement(getjv2vObj(elm.nextElement()));
351     }
352     return vObjs;
353   }
354
355   // utility functions
356   /**
357    * get start<end range of segment, adjusting for inclusivity flag and
358    * polarity.
359    * 
360    * @param visSeg
361    * @param ensureDirection
362    *                when true - always ensure start is less than end.
363    * @return int[] { start, end, direction} where direction==1 for range running
364    *         from end to start.
365    */
366   public int[] getSegRange(Seg visSeg, boolean ensureDirection)
367   {
368     boolean incl = visSeg.getInclusive();
369     // adjust for inclusive flag.
370     int pol = (visSeg.getStart() <= visSeg.getEnd()) ? 1 : -1; // polarity of
371     // region.
372     int start = visSeg.getStart() + (incl ? 0 : pol);
373     int end = visSeg.getEnd() + (incl ? 0 : -pol);
374     if (ensureDirection && pol == -1)
375     {
376       // jalview doesn't deal with inverted ranges, yet.
377       int t = end;
378       end = start;
379       start = t;
380     }
381     return new int[]
382     { start, end, pol < 0 ? 1 : 0 };
383   }
384
385   /**
386    * provenance bits
387    */
388   protected jalview.datamodel.Provenance getJalviewProvenance(
389           Provenance prov)
390   {
391     // TODO: fix App and Action entries and check use of provenance in jalview.
392     jalview.datamodel.Provenance jprov = new jalview.datamodel.Provenance();
393     for (int i = 0; i < prov.getEntryCount(); i++)
394     {
395       jprov.addEntry(prov.getEntry(i).getUser(), prov.getEntry(i)
396               .getAction(), prov.getEntry(i).getDate(), prov.getEntry(i)
397               .getId());
398     }
399
400     return jprov;
401   }
402
403   /**
404    * 
405    * @return default initial provenance list for a Jalview created vamsas
406    *         object.
407    */
408   Provenance dummyProvenance()
409   {
410     return dummyProvenance(null);
411   }
412
413   protected Entry dummyPEntry(String action)
414   {
415     Entry entry = new Entry();
416     entry.setApp(this.provEntry.getApp());
417     if (action != null)
418     {
419       entry.setAction(action);
420     }
421     else
422     {
423       entry.setAction("created.");
424     }
425     entry.setDate(new java.util.Date());
426     entry.setUser(this.provEntry.getUser());
427     return entry;
428   }
429
430   protected Provenance dummyProvenance(String action)
431   {
432     Provenance prov = new Provenance();
433     prov.addEntry(dummyPEntry(action));
434     return prov;
435   }
436
437   protected void addProvenance(Provenance p, String action)
438   {
439     p.addEntry(dummyPEntry(action));
440   }
441
442
443   /**
444    * @return true if jalview was being updated from the vamsas document
445    */
446   public boolean isTojalview()
447   {
448     return tojalview;
449   }
450
451   /**
452    * @return true if addToDocument() was called.
453    */
454   public boolean isAddtodoc()
455   {
456     return addtodoc;
457   }
458
459   /**
460    * @return true if conflict() was called
461    */
462   public boolean isConflicted()
463   {
464     return conflicted;
465   }
466
467   /**
468    * @return true if updateFromDoc() was called
469    */
470   public boolean isUpdatedFromDoc()
471   {
472     return updatedfromdoc;
473   }
474   /**
475    * @return true if updateToDoc() was called
476    */
477   public boolean isUpdatedToDoc()
478   {
479     return updatedtodoc;
480   }
481
482   /**
483    * @return true if addFromDocument() was called.
484    */
485   public boolean isAddfromdoc()
486   {
487     return addfromdoc;
488   }
489
490   /**
491    * @return true if object sync logic completed normally.
492    */
493   public boolean isSuccess()
494   {
495     return success;
496   }
497
498   /**
499    * @return the vobj
500    */
501   public Vobject getVobj()
502   {
503     return vobj;
504   }
505
506   /**
507    * @return the jvobj
508    */
509   public Object getJvobj()
510   {
511     return jvobj;
512   }
513
514 }