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