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