JAL-1432 updated copyright notices
[jalview.git] / src / jalview / io / VamsasAppDatastore.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;
20
21 import jalview.bin.Cache;
22 import jalview.datamodel.AlignedCodonFrame;
23 import jalview.datamodel.AlignmentAnnotation;
24 import jalview.datamodel.GraphLine;
25 import jalview.datamodel.SequenceI;
26 import jalview.gui.AlignFrame;
27 import jalview.gui.AlignViewport;
28 import jalview.gui.Desktop;
29 import jalview.gui.TreePanel;
30 import jalview.io.vamsas.Datasetsequence;
31 import jalview.io.vamsas.DatastoreItem;
32 import jalview.io.vamsas.DatastoreRegistry;
33 import jalview.io.vamsas.Rangetype;
34 import java.io.IOException;
35 import java.util.Enumeration;
36 import java.util.HashMap;
37 import java.util.Hashtable;
38 import java.util.IdentityHashMap;
39 import java.util.Iterator;
40 import java.util.List;
41 import java.util.Vector;
42 import java.util.jar.JarInputStream;
43 import java.util.jar.JarOutputStream;
44
45 import uk.ac.vamsas.client.*;
46 import uk.ac.vamsas.objects.core.*;
47 import uk.ac.vamsas.objects.utils.Properties;
48
49 /*
50  *
51  * static {
52  * org.exolab.castor.util.LocalConfiguration.getInstance().getProperties().setProperty(
53  * "org.exolab.castor.serializer", "org.apache.xml.serialize.XMLSerilazizer"); }
54  *
55  */
56 /*
57  * TODO: check/verify consistency for vamsas sync with group associated alignment annotation
58  */
59 public class VamsasAppDatastore
60 {
61   /**
62    * Type used for general jalview generated annotation added to vamsas document
63    */
64   public static final String JALVIEW_ANNOTATION_ROW = "JalviewAnnotation";
65
66   /**
67    * AlignmentAnnotation property to indicate that values should not be
68    * interpolated
69    */
70   public static final String DISCRETE_ANNOTATION = "discrete";
71
72   /**
73    * continuous property - optional to specify that annotation should be
74    * represented as a continous graph line
75    */
76   private static final String CONTINUOUS_ANNOTATION = "continuous";
77
78   private static final String THRESHOLD = "threshold";
79
80   /**
81    * template for provenance entries written to vamsas session document
82    */
83   Entry provEntry = null;
84
85   /**
86    * Instance of the session document being synchronized with
87    */
88   IClientDocument cdoc;
89
90   /**
91    * map Vorba (vamsas object xml ref) IDs to live jalview object references
92    */
93   Hashtable vobj2jv;
94
95   /**
96    * map live jalview object references to Vorba IDs
97    */
98   IdentityHashMap jv2vobj;
99
100   /**
101    * map jalview sequence set ID (which is vorba ID for alignment) to last
102    * recorded hash value for the alignment viewport (the undo/redo hash value)
103    */
104   Hashtable alignRDHash;
105
106   public VamsasAppDatastore(IClientDocument cdoc, Hashtable vobj2jv,
107           IdentityHashMap jv2vobj, Entry provEntry, Hashtable alignRDHash)
108   {
109     this.cdoc = cdoc;
110     this.vobj2jv = vobj2jv;
111     this.jv2vobj = jv2vobj;
112     this.provEntry = provEntry;
113     this.alignRDHash = alignRDHash;
114     buildSkipList();
115   }
116
117   /**
118    * the skipList used to skip over views from Jalview Appdata's that we've
119    * already syncrhonized
120    */
121   Hashtable skipList;
122
123   private void buildSkipList()
124   {
125     skipList = new Hashtable();
126     AlignFrame[] al = Desktop.getAlignframes();
127     for (int f = 0; al != null && f < al.length; f++)
128     {
129       skipList.put(al[f].getViewport().getSequenceSetId(), al[f]);
130     }
131   }
132
133   /**
134    * @return the Vobject bound to Jalview datamodel object
135    */
136   protected Vobject getjv2vObj(Object jvobj)
137   {
138     if (jv2vobj.containsKey(jvobj))
139     {
140       return cdoc.getObject((VorbaId) jv2vobj.get(jvobj));
141     }
142     // check if we're working with a string - then workaround
143     // the use of IdentityHashTable because different strings
144     // have different object IDs.
145     if (jvobj instanceof String)
146     {
147       Object seqsetidobj = null;
148       seqsetidobj = getVamsasObjectBinding().get(jvobj);
149       if (seqsetidobj != null)
150       {
151         if (seqsetidobj instanceof String)
152         {
153           // what is expected. object returned by av.getSequenceSetId() -
154           // reverse lookup to get the 'registered' instance of this string
155           Vobject obj = getjv2vObj(seqsetidobj);
156           if (obj != null && !(obj instanceof Alignment))
157           {
158             Cache.log
159                     .warn("IMPLEMENTATION ERROR?: Unexpected mapping for unmapped jalview string object content:"
160                             + seqsetidobj + " to object " + obj);
161           }
162           return obj;
163         }
164         else
165         {
166           Cache.log.warn("Unexpected mapping for Jalview String Object ID "
167                   + seqsetidobj + " to another jalview dataset object "
168                   + seqsetidobj);
169         }
170       }
171     }
172
173     if (Cache.log.isDebugEnabled())
174     {
175       Cache.log.debug("Returning null VorbaID binding for jalview object "
176               + jvobj);
177     }
178     return null;
179   }
180
181   /**
182    * 
183    * @param vobj
184    * @return Jalview datamodel object bound to the vamsas document object
185    */
186   protected Object getvObj2jv(uk.ac.vamsas.client.Vobject vobj)
187   {
188     VorbaId id = vobj.getVorbaId();
189     if (id == null)
190     {
191       id = cdoc.registerObject(vobj);
192       Cache.log
193               .debug("Registering new object and returning null for getvObj2jv");
194       return null;
195     }
196     if (vobj2jv.containsKey(vobj.getVorbaId()))
197     {
198       return vobj2jv.get(vobj.getVorbaId());
199     }
200     return null;
201   }
202
203   protected void bindjvvobj(Object jvobj, uk.ac.vamsas.client.Vobject vobj)
204   {
205     VorbaId id = vobj.getVorbaId();
206     if (id == null)
207     {
208       id = cdoc.registerObject(vobj);
209       if (id == null || vobj.getVorbaId() == null
210               || cdoc.getObject(id) != vobj)
211       {
212         Cache.log.error("Failed to get id for "
213                 + (vobj.isRegisterable() ? "registerable"
214                         : "unregisterable") + " object " + vobj);
215       }
216     }
217
218     if (vobj2jv.containsKey(vobj.getVorbaId())
219             && !((VorbaId) vobj2jv.get(vobj.getVorbaId())).equals(jvobj))
220     {
221       Cache.log.debug(
222               "Warning? Overwriting existing vamsas id binding for "
223                       + vobj.getVorbaId(), new Exception(
224                       "Overwriting vamsas id binding."));
225     }
226     else if (jv2vobj.containsKey(jvobj)
227             && !((VorbaId) jv2vobj.get(jvobj)).equals(vobj.getVorbaId()))
228     {
229       Cache.log.debug(
230               "Warning? Overwriting existing jalview object binding for "
231                       + jvobj, new Exception(
232                       "Overwriting jalview object binding."));
233     }
234     /*
235      * Cache.log.error("Attempt to make conflicting object binding! "+vobj+" id "
236      * +vobj.getVorbaId()+" already bound to "+getvObj2jv(vobj)+" and "+jvobj+"
237      * already bound to "+getjv2vObj(jvobj),new Exception("Excessive call to
238      * bindjvvobj")); }
239      */
240     // we just update the hash's regardless!
241     Cache.log.debug("Binding " + vobj.getVorbaId() + " to " + jvobj);
242     vobj2jv.put(vobj.getVorbaId(), jvobj);
243     // JBPNote - better implementing a hybrid invertible hash.
244     jv2vobj.put(jvobj, vobj.getVorbaId());
245   }
246
247   /**
248    * put the alignment viewed by AlignViewport into cdoc.
249    * 
250    * @param av
251    *          alignViewport to be stored
252    * @param aFtitle
253    *          title for alignment
254    * @return true if alignment associated with viewport was stored/synchronized
255    *         to document
256    */
257   public boolean storeVAMSAS(AlignViewport av, String aFtitle)
258   {
259     try
260     {
261       jalview.datamodel.AlignmentI jal = av.getAlignment();
262       jalview.datamodel.AlignmentI jds = jal.getDataset();
263       boolean nw = false;
264       VAMSAS root = null; // will be resolved based on Dataset Parent.
265       // /////////////////////////////////////////
266       // SAVE THE DATASET
267       DataSet dataset = null;
268       if (jds == null)
269       {
270         Cache.log.warn("Creating new dataset for an alignment.");
271         jal.setDataset(null);
272         jds = jal.getDataset();
273       }
274
275       // try and get alignment and association for sequence set id
276
277       Alignment alignment = (Alignment) getjv2vObj(av.getSequenceSetId());
278       if (alignment != null)
279       {
280         dataset = (DataSet) alignment.getV_parent();
281       }
282       else
283       {
284         // is the dataset already registered
285         dataset = (DataSet) getjv2vObj(jds);
286       }
287
288       if (dataset == null)
289       {
290         // it might be that one of the dataset sequences does actually have a
291         // binding, so search for it indirectly. If it does, then the local
292         // jalview dataset
293         // must be merged with the existing vamsas dataset.
294         jalview.datamodel.SequenceI[] jdatset = jds.getSequencesArray();
295         for (int i = 0; i < jdatset.length; i++)
296         {
297           Vobject vbound = getjv2vObj(jdatset[i]);
298           if (vbound != null)
299           {
300             if (vbound instanceof uk.ac.vamsas.objects.core.Sequence)
301             {
302               if (dataset == null)
303               {
304                 dataset = (DataSet) vbound.getV_parent();
305               }
306               else
307               {
308                 if (vbound.getV_parent() != null
309                         && dataset != vbound.getV_parent())
310                 {
311                   throw new Error(
312                           "IMPLEMENTATION ERROR: Cannot map an alignment of sequences from different datasets into a single alignment in the vamsas document.");
313                   // This occurs because the dataset for the alignment we are
314                   // trying to
315                 }
316               }
317             }
318           }
319         }
320       }
321
322       if (dataset == null)
323       {
324         Cache.log.warn("Creating new vamsas dataset for alignment view "
325                 + av.getSequenceSetId());
326         // we create a new dataset on the default vamsas root.
327         root = cdoc.getVamsasRoots()[0]; // default vamsas root for modifying.
328         dataset = new DataSet();
329         root.addDataSet(dataset);
330         bindjvvobj(jds, dataset);
331         dataset.setProvenance(dummyProvenance());
332         // dataset.getProvenance().addEntry(provEntry);
333         nw = true;
334       }
335       else
336       {
337         root = (VAMSAS) dataset.getV_parent();
338       }
339       // update dataset
340       Sequence sequence;
341       // set new dataset and alignment sequences based on alignment Nucleotide
342       // flag.
343       // this *will* break when alignment contains both nucleotide and amino
344       // acid sequences.
345       String dict = jal.isNucleotide() ? uk.ac.vamsas.objects.utils.SymbolDictionary.STANDARD_NA
346               : uk.ac.vamsas.objects.utils.SymbolDictionary.STANDARD_AA;
347       Vector dssmods = new Vector();
348       for (int i = 0; i < jal.getHeight(); i++)
349       {
350         SequenceI sq = jal.getSequenceAt(i).getDatasetSequence(); // only insert
351         // referenced
352         // sequences
353         // to dataset.
354         Datasetsequence dssync = new jalview.io.vamsas.Datasetsequence(
355                 this, sq, dict, dataset);
356         sequence = (Sequence) dssync.getVobj();
357         if (dssync.getModified())
358         {
359           dssmods.addElement(sequence);
360         }
361         ;
362       }
363       if (dssmods.size() > 0)
364       {
365         if (!nw)
366         {
367           Entry pentry = this.addProvenance(dataset.getProvenance(),
368                   "updated sequences");
369           // pentry.addInput(vInput); could write in which sequences were
370           // modified.
371           dssmods.removeAllElements();
372         }
373       }
374       // dataset.setProvenance(getVamsasProvenance(jal.getDataset().getProvenance()));
375       // ////////////////////////////////////////////
376       if (alignmentWillBeSkipped(av))
377       {
378         // TODO: trees could be written - but for the moment we just
379         addToSkipList(av);
380         // add to the JalviewXML skipList and ..
381         return false;
382       }
383
384       if (alignment == null)
385       {
386         alignment = new Alignment();
387         bindjvvobj(av.getSequenceSetId(), alignment);
388         if (alignment.getProvenance() == null)
389         {
390           alignment.setProvenance(new Provenance());
391         }
392         addProvenance(alignment.getProvenance(), "added"); // TODO: insert some
393         // sensible source
394         // here
395         dataset.addAlignment(alignment);
396         {
397           Property title = new Property();
398           title.setName("title");
399           title.setType("string");
400           title.setContent(aFtitle);
401           alignment.addProperty(title);
402         }
403         alignment.setGapChar(String.valueOf(av.getGapCharacter()));
404         for (int i = 0; i < jal.getHeight(); i++)
405         {
406           syncToAlignmentSequence(jal.getSequenceAt(i), alignment, null);
407         }
408         alignRDHash.put(av.getSequenceSetId(), av.getUndoRedoHash());
409       }
410       else
411       {
412         // always prepare to clone the alignment
413         boolean alismod = av.isUndoRedoHashModified((long[]) alignRDHash
414                 .get(av.getSequenceSetId()));
415         // todo: verify and update mutable alignment props.
416         // TODO: Use isLocked methods
417         if (alignment.getModifiable() == null
418                 || alignment.getModifiable().length() == 0)
419         // && !alignment.isDependedOn())
420         {
421           boolean modified = false;
422           // check existing sequences in local and in document.
423           Vector docseqs = new Vector(
424                   alignment.getAlignmentSequenceAsReference());
425           for (int i = 0; i < jal.getHeight(); i++)
426           {
427             modified |= syncToAlignmentSequence(jal.getSequenceAt(i),
428                     alignment, docseqs);
429           }
430           if (docseqs.size() > 0)
431           {
432             // removeValignmentSequences(alignment, docseqs);
433             docseqs.removeAllElements();
434             System.out
435                     .println("Sequence deletion from alignment is not implemented.");
436
437           }
438           if (modified)
439           {
440             if (alismod)
441             {
442               // info in the undo
443               addProvenance(alignment.getProvenance(), "Edited"); // TODO:
444               // insert
445               // something
446               // sensible
447               // here again
448             }
449             else
450             {
451               // info in the undo
452               addProvenance(alignment.getProvenance(), "Attributes Edited"); // TODO:
453               // insert
454               // something
455               // sensible
456               // here
457               // again
458             }
459           }
460           if (alismod)
461           {
462             System.out.println("update alignment in document.");
463           }
464           else
465           {
466             System.out.println("alignment in document left unchanged.");
467           }
468         }
469         else
470         {
471           // unbind alignment from view.
472           // create new binding and new alignment.
473           // mark trail on new alignment as being derived from old ?
474           System.out
475                   .println("update edited alignment to new alignment in document.");
476         }
477       }
478       // ////////////////////////////////////////////
479       // SAVE Alignment Sequence Features
480       for (int i = 0, iSize = alignment.getAlignmentSequenceCount(); i < iSize; i++)
481       {
482         AlignmentSequence valseq;
483         SequenceI alseq = (SequenceI) getvObj2jv(valseq = alignment
484                 .getAlignmentSequence(i));
485         if (alseq != null && alseq.getSequenceFeatures() != null)
486         {
487           /*
488            * We do not put local Alignment Sequence Features into the vamsas
489            * document yet.
490            * 
491            * 
492            * jalview.datamodel.SequenceFeature[] features = alseq
493            * .getSequenceFeatures(); for (int f = 0; f < features.length; f++) {
494            * if (features[f] != null) { AlignmentSequenceAnnotation valseqf = (
495            * AlignmentSequenceAnnotation) getjv2vObj(features[i]); if (valseqf
496            * == null) {
497            * 
498            * valseqf = (AlignmentSequenceAnnotation) getDSAnnotationFromJalview(
499            * new AlignmentSequenceAnnotation(), features[i]);
500            * valseqf.setGraph(false);
501            * valseqf.addProperty(newProperty("jalview:feature"
502            * ,"boolean","true")); if (valseqf.getProvenance() == null) {
503            * valseqf.setProvenance(new Provenance()); }
504            * addProvenance(valseqf.getProvenance(), "created"); // JBPNote - //
505            * need to // update bindjvvobj(features[i], valseqf);
506            * valseq.addAlignmentSequenceAnnotation(valseqf); } } }
507            */
508         }
509       }
510
511       // ////////////////////////////////////////////
512       // SAVE ANNOTATIONS
513       if (jal.getAlignmentAnnotation() != null)
514       {
515         jalview.datamodel.AlignmentAnnotation[] aa = jal
516                 .getAlignmentAnnotation();
517         java.util.HashMap AlSeqMaps = new HashMap(); // stores int maps from
518         // alignment columns to
519         // sequence positions.
520         for (int i = 0; i < aa.length; i++)
521         {
522           if (aa[i] == null || isJalviewOnly(aa[i]))
523           {
524             continue;
525           }
526           if (aa[i].groupRef != null)
527           {
528             // TODO: store any group associated annotation references
529             Cache.log
530                     .warn("Group associated sequence annotation is not stored in VAMSAS document.");
531             continue;
532           }
533           if (aa[i].sequenceRef != null)
534           {
535             // Deal with sequence associated annotation
536             Vobject sref = getjv2vObj(aa[i].sequenceRef);
537             if (sref instanceof uk.ac.vamsas.objects.core.AlignmentSequence)
538             {
539               saveAlignmentSequenceAnnotation(AlSeqMaps,
540                       (AlignmentSequence) sref, aa[i]);
541             }
542             else
543             {
544               // first find the alignment sequence to associate this with.
545               for (SequenceI jvalsq : av.getAlignment().getSequences())
546               {
547                 // saveDatasetSequenceAnnotation(AlSeqMaps,(uk.ac.vamsas.objects.core.Sequence)
548                 // sref, aa[i]);
549                 if (jvalsq.getDatasetSequence() == aa[i].sequenceRef)
550                 {
551                   Vobject alsref = getjv2vObj(jvalsq);
552                   saveAlignmentSequenceAnnotation(AlSeqMaps,
553                           (AlignmentSequence) alsref, aa[i]);
554                   break;
555                 }
556                 ;
557               }
558             }
559           }
560           else
561           {
562             // add Alignment Annotation
563             uk.ac.vamsas.objects.core.AlignmentAnnotation an = (uk.ac.vamsas.objects.core.AlignmentAnnotation) getjv2vObj(aa[i]);
564             if (an == null)
565             {
566               an = new uk.ac.vamsas.objects.core.AlignmentAnnotation();
567               an.setType(JALVIEW_ANNOTATION_ROW);
568               an.setDescription(aa[i].description);
569               alignment.addAlignmentAnnotation(an);
570               Seg vSeg = new Seg(); // TODO: refactor to have a default
571               // rangeAnnotationType initer/updater that
572               // takes a set of int ranges.
573               vSeg.setStart(1);
574               vSeg.setInclusive(true);
575               vSeg.setEnd(jal.getWidth());
576               an.addSeg(vSeg);
577               if (aa[i].graph > 0)
578               {
579                 an.setGraph(true); // aa[i].graph);
580               }
581               an.setLabel(aa[i].label);
582               an.setProvenance(dummyProvenance());
583               if (aa[i].graph != AlignmentAnnotation.NO_GRAPH)
584               {
585                 an.setGroup(Integer.toString(aa[i].graphGroup)); // // JBPNote
586                 // -
587                 // originally we
588                 // were going to
589                 // store
590                 // graphGroup in
591                 // the Jalview
592                 // specific
593                 // bits.
594                 an.setGraph(true);
595               }
596               else
597               {
598                 an.setGraph(false);
599               }
600               AnnotationElement ae;
601
602               for (int a = 0; a < aa[i].annotations.length; a++)
603               {
604                 if ((aa[i] == null) || (aa[i].annotations[a] == null))
605                 {
606                   continue;
607                 }
608
609                 ae = new AnnotationElement();
610                 ae.setDescription(aa[i].annotations[a].description);
611                 ae.addGlyph(new Glyph());
612                 ae.getGlyph(0).setContent(
613                         aa[i].annotations[a].displayCharacter); // assume
614                 // jax-b
615                 // takes
616                 // care
617                 // of
618                 // utf8
619                 // translation
620                 if (an.isGraph())
621                 {
622                   ae.addValue(aa[i].annotations[a].value);
623                 }
624                 ae.setPosition(a + 1);
625                 if (aa[i].annotations[a].secondaryStructure != ' ')
626                 {
627                   Glyph ss = new Glyph();
628                   ss.setDict(uk.ac.vamsas.objects.utils.GlyphDictionary.PROTEIN_SS_3STATE);
629                   ss.setContent(String
630                           .valueOf(aa[i].annotations[a].secondaryStructure));
631                   ae.addGlyph(ss);
632                 }
633                 an.addAnnotationElement(ae);
634               }
635               if (aa[i].editable)
636               {
637                 // an.addProperty(newProperty("jalview:editable", null,
638                 // "true"));
639                 // an.setModifiable(""); // TODO: This is not the way the
640                 // modifiable flag is supposed to be used.
641               }
642               setAnnotationType(an, aa[i]);
643
644               if (aa[i].graph != jalview.datamodel.AlignmentAnnotation.NO_GRAPH)
645               {
646                 an.setGraph(true);
647                 an.setGroup(Integer.toString(aa[i].graphGroup));
648                 if (aa[i].threshold != null && aa[i].threshold.displayed)
649                 {
650                   an.addProperty(Properties.newProperty(THRESHOLD,
651                           Properties.FLOATTYPE, "" + aa[i].threshold.value));
652                   if (aa[i].threshold.label != null)
653                   {
654                     an.addProperty(Properties.newProperty(THRESHOLD
655                             + "Name", Properties.STRINGTYPE, ""
656                             + aa[i].threshold.label));
657                   }
658                 }
659               }
660
661             }
662
663             else
664             {
665               if (an.getModifiable() == null) // TODO: USE VAMSAS LIBRARY OBJECT
666               // LOCK METHODS)
667               {
668                 // verify annotation - update (perhaps)
669                 Cache.log
670                         .info("update alignment sequence annotation. not yet implemented.");
671               }
672               else
673               {
674                 // verify annotation - update (perhaps)
675                 Cache.log
676                         .info("updated alignment sequence annotation added.");
677               }
678             }
679           }
680         }
681       }
682       // /////////////////////////////////////////////////////
683
684       // //////////////////////////////////////////////
685       // /SAVE THE TREES
686       // /////////////////////////////////
687       // FIND ANY ASSOCIATED TREES
688       if (Desktop.desktop != null)
689       {
690         javax.swing.JInternalFrame[] frames = Desktop.instance
691                 .getAllFrames();
692
693         for (int t = 0; t < frames.length; t++)
694         {
695           if (frames[t] instanceof TreePanel)
696           {
697             TreePanel tp = (TreePanel) frames[t];
698
699             if (tp.getViewPort().getSequenceSetId()
700                     .equals(av.getSequenceSetId()))
701             {
702               DatastoreItem vtree = new jalview.io.vamsas.Tree(this, tp,
703                       jal, alignment);
704             }
705           }
706         }
707       }
708       // Store Jalview specific stuff in the Jalview appData
709       // not implemented in the SimpleDoc interface.
710     }
711
712     catch (Exception ex)
713     {
714       ex.printStackTrace();
715       return false;
716     }
717     return true;
718   }
719
720   /**
721    * very quick test to see if the viewport would be stored in the vamsas
722    * document. Reasons for not storing include the unaligned flag being false
723    * (for all sequences, including the hidden ones!)
724    * 
725    * @param av
726    * @return true if alignment associated with this view will be stored in
727    *         document.
728    */
729   public boolean alignmentWillBeSkipped(AlignViewport av)
730   {
731     return (!av.getAlignment().isAligned());
732   }
733
734   private void addToSkipList(AlignViewport av)
735   {
736     if (skipList == null)
737     {
738       skipList = new Hashtable();
739     }
740     skipList.put(av.getSequenceSetId(), av);
741   }
742
743   /**
744    * remove docseqs from the given alignment marking provenance appropriately
745    * and removing any references to the sequences.
746    * 
747    * @param alignment
748    * @param docseqs
749    */
750   private void removeValignmentSequences(Alignment alignment, Vector docseqs)
751   {
752     // delete these from document. This really needs to be a generic document
753     // API function derived by CASTOR.
754     Enumeration en = docseqs.elements();
755     while (en.hasMoreElements())
756     {
757       alignment.removeAlignmentSequence((AlignmentSequence) en
758               .nextElement());
759     }
760     Entry pe = addProvenance(alignment.getProvenance(), "Removed "
761             + docseqs.size() + " sequences");
762     en = alignment.enumerateAlignmentAnnotation();
763     Vector toremove = new Vector();
764     while (en.hasMoreElements())
765     {
766       uk.ac.vamsas.objects.core.AlignmentAnnotation alan = (uk.ac.vamsas.objects.core.AlignmentAnnotation) en
767               .nextElement();
768       if (alan.getSeqrefsCount() > 0)
769       {
770         int p = 0;
771         Vector storem = new Vector();
772         Enumeration sr = alan.enumerateSeqrefs();
773         while (sr.hasMoreElements())
774         {
775           Object alsr = sr.nextElement();
776           if (docseqs.contains(alsr))
777           {
778             storem.addElement(alsr);
779           }
780         }
781         // remove references to the deleted sequences
782         sr = storem.elements();
783         while (sr.hasMoreElements())
784         {
785           alan.removeSeqrefs(sr.nextElement());
786         }
787
788         if (alan.getSeqrefsCount() == 0)
789         {
790           // should then delete alan from dataset
791           toremove.addElement(alan);
792         }
793       }
794     }
795     // remove any annotation that used to be associated to a specific bunch of
796     // sequences
797     en = toremove.elements();
798     while (en.hasMoreElements())
799     {
800       alignment
801               .removeAlignmentAnnotation((uk.ac.vamsas.objects.core.AlignmentAnnotation) en
802                       .nextElement());
803     }
804     // TODO: search through alignment annotations to remove any references to
805     // this alignment sequence
806   }
807
808   /**
809    * sync a jalview alignment seuqence into a vamsas alignment assumes all lock
810    * transformation/bindings have been sorted out before hand. creates/syncs the
811    * vamsas alignment sequence for jvalsq and adds it to the alignment if
812    * necessary. unbounddocseq is a duplicate of the vamsas alignment sequences
813    * and these are removed after being processed w.r.t a bound jvalsq
814    * 
815    */
816   private boolean syncToAlignmentSequence(SequenceI jvalsq,
817           Alignment alignment, Vector unbounddocseq)
818   {
819     boolean modal = false;
820     // todo: islocked method here
821     boolean up2doc = false;
822     AlignmentSequence alseq = (AlignmentSequence) getjv2vObj(jvalsq);
823     if (alseq == null)
824     {
825       alseq = new AlignmentSequence();
826       up2doc = true;
827     }
828     else
829     {
830       if (unbounddocseq != null)
831       {
832         unbounddocseq.removeElement(alseq);
833       }
834     }
835     // boolean locked = (alignment.getModifiable()==null ||
836     // alignment.getModifiable().length()>0);
837     // TODO: VAMSAS: translate lowercase symbols to annotation ?
838     if (up2doc || !alseq.getSequence().equals(jvalsq.getSequenceAsString()))
839     {
840       alseq.setSequence(jvalsq.getSequenceAsString());
841       alseq.setStart(jvalsq.getStart());
842       alseq.setEnd(jvalsq.getEnd());
843       modal = true;
844     }
845     if (up2doc || !alseq.getName().equals(jvalsq.getName()))
846     {
847       modal = true;
848       alseq.setName(jvalsq.getName());
849     }
850     if (jvalsq.getDescription() != null
851             && (alseq.getDescription() == null || !jvalsq.getDescription()
852                     .equals(alseq.getDescription())))
853     {
854       modal = true;
855       alseq.setDescription(jvalsq.getDescription());
856     }
857     if (getjv2vObj(jvalsq.getDatasetSequence()) == null)
858     {
859       Cache.log
860               .warn("Serious Implementation error - Unbound dataset sequence in alignment: "
861                       + jvalsq.getDatasetSequence());
862     }
863     alseq.setRefid(getjv2vObj(jvalsq.getDatasetSequence()));
864     if (up2doc)
865     {
866
867       alignment.addAlignmentSequence(alseq);
868       bindjvvobj(jvalsq, alseq);
869     }
870     return up2doc || modal;
871   }
872
873   /**
874    * locally sync a jalview alignment seuqence from a vamsas alignment assumes
875    * all lock transformation/bindings have been sorted out before hand.
876    * creates/syncs the jvalsq from the alignment sequence
877    */
878   private boolean syncFromAlignmentSequence(AlignmentSequence valseq,
879           char valGapchar, char gapChar, List<SequenceI> dsseqs)
880
881   {
882     boolean modal = false;
883     // todo: islocked method here
884     boolean upFromdoc = false;
885     jalview.datamodel.SequenceI alseq = (SequenceI) getvObj2jv(valseq);
886     if (alseq == null)
887     {
888       upFromdoc = true;
889     }
890     if (alseq != null)
891     {
892
893       // boolean locked = (alignment.getModifiable()==null ||
894       // alignment.getModifiable().length()>0);
895       // TODO: VAMSAS: translate lowercase symbols to annotation ?
896       if (upFromdoc
897               || !valseq.getSequence().equals(alseq.getSequenceAsString()))
898       {
899         // this might go *horribly* wrong
900         alseq.setSequence(new String(valseq.getSequence()).replace(
901                 valGapchar, gapChar));
902         alseq.setStart((int) valseq.getStart());
903         alseq.setEnd((int) valseq.getEnd());
904         modal = true;
905       }
906       if (!valseq.getName().equals(alseq.getName()))
907       {
908         modal = true;
909         alseq.setName(valseq.getName());
910       }
911       if (alseq.getDescription() == null
912               || (valseq.getDescription() != null && !alseq
913                       .getDescription().equals(valseq.getDescription())))
914       {
915         alseq.setDescription(valseq.getDescription());
916         modal = true;
917       }
918       if (modal && Cache.log.isDebugEnabled())
919       {
920         Cache.log.debug("Updating apparently edited sequence "
921                 + alseq.getName());
922       }
923     }
924     else
925     {
926       alseq = new jalview.datamodel.Sequence(valseq.getName(), valseq
927               .getSequence().replace(valGapchar, gapChar),
928               (int) valseq.getStart(), (int) valseq.getEnd());
929
930       Vobject datsetseq = (Vobject) valseq.getRefid();
931       if (datsetseq != null)
932       {
933         alseq.setDatasetSequence((SequenceI) getvObj2jv(datsetseq)); // exceptions
934         if (valseq.getDescription() != null)
935         {
936           alseq.setDescription(valseq.getDescription());
937         }
938         else
939         {
940           // inherit description line from dataset.
941           if (alseq.getDatasetSequence().getDescription() != null)
942           {
943             alseq.setDescription(alseq.getDatasetSequence()
944                     .getDescription());
945           }
946         }
947         // if
948         // AlignemntSequence
949         // reference
950         // isn't
951         // a
952         // simple
953         // SequenceI
954       }
955       else
956       {
957         Cache.log
958                 .error("Invalid dataset sequence id (null) for alignment sequence "
959                         + valseq.getVorbaId());
960       }
961       bindjvvobj(alseq, valseq);
962       alseq.setVamsasId(valseq.getVorbaId().getId());
963       dsseqs.add(alseq);
964     }
965     Vobject datsetseq = (Vobject) valseq.getRefid();
966     if (datsetseq != null)
967     {
968       if (datsetseq != alseq.getDatasetSequence())
969       {
970         modal = true;
971       }
972       alseq.setDatasetSequence((SequenceI) getvObj2jv(datsetseq)); // exceptions
973     }
974     return upFromdoc || modal;
975   }
976
977   private void initRangeAnnotationType(RangeAnnotation an,
978           AlignmentAnnotation alan, int[] gapMap)
979   {
980     Seg vSeg = new Seg();
981     vSeg.setStart(1);
982     vSeg.setInclusive(true);
983     vSeg.setEnd(gapMap.length);
984     an.addSeg(vSeg);
985
986     // LATER: much of this is verbatim from the alignmentAnnotation
987     // method below. suggests refactoring to make rangeAnnotation the
988     // base class
989     an.setDescription(alan.description);
990     an.setLabel(alan.label);
991     an.setGroup(Integer.toString(alan.graphGroup));
992     // // JBPNote -
993     // originally we
994     // were going to
995     // store
996     // graphGroup in
997     // the Jalview
998     // specific
999     // bits.
1000     AnnotationElement ae;
1001     for (int a = 0; a < alan.annotations.length; a++)
1002     {
1003       if (alan.annotations[a] == null)
1004       {
1005         continue;
1006       }
1007
1008       ae = new AnnotationElement();
1009       ae.setDescription(alan.annotations[a].description);
1010       ae.addGlyph(new Glyph());
1011       ae.getGlyph(0).setContent(alan.annotations[a].displayCharacter); // assume
1012       // jax-b
1013       // takes
1014       // care
1015       // of
1016       // utf8
1017       // translation
1018       if (alan.graph != jalview.datamodel.AlignmentAnnotation.NO_GRAPH)
1019       {
1020         ae.addValue(alan.annotations[a].value);
1021       }
1022       ae.setPosition(gapMap[a] + 1); // position w.r.t. AlignmentSequence
1023       // symbols
1024       if (alan.annotations[a].secondaryStructure != ' ')
1025       {
1026         // we only write an annotation where it really exists.
1027         Glyph ss = new Glyph();
1028         ss.setDict(uk.ac.vamsas.objects.utils.GlyphDictionary.PROTEIN_SS_3STATE);
1029         ss.setContent(String
1030                 .valueOf(alan.annotations[a].secondaryStructure));
1031         ae.addGlyph(ss);
1032       }
1033       an.addAnnotationElement(ae);
1034     }
1035
1036   }
1037
1038   private void saveDatasetSequenceAnnotation(HashMap AlSeqMaps,
1039           uk.ac.vamsas.objects.core.Sequence sref, AlignmentAnnotation alan)
1040   {
1041     // {
1042     // uk.ac.vamsas.
1043     // objects.core.AlignmentSequence alsref = (uk.ac.vamsas.
1044     // objects.core.AlignmentSequence) sref;
1045     uk.ac.vamsas.objects.core.DataSetAnnotations an = (uk.ac.vamsas.objects.core.DataSetAnnotations) getjv2vObj(alan);
1046     int[] gapMap = getGapMap(AlSeqMaps, alan);
1047     if (an == null)
1048     {
1049       an = new uk.ac.vamsas.objects.core.DataSetAnnotations();
1050       initRangeAnnotationType(an, alan, gapMap);
1051
1052       an.setProvenance(dummyProvenance()); // get provenance as user
1053       // created, or jnet, or
1054       // something else.
1055       setAnnotationType(an, alan);
1056       an.setGroup(Integer.toString(alan.graphGroup)); // // JBPNote -
1057       // originally we
1058       // were going to
1059       // store
1060       // graphGroup in
1061       // the Jalview
1062       // specific
1063       // bits.
1064       if (alan.getThreshold() != null && alan.getThreshold().displayed)
1065       {
1066         an.addProperty(Properties.newProperty(THRESHOLD,
1067                 Properties.FLOATTYPE, "" + alan.getThreshold().value));
1068         if (alan.getThreshold().label != null)
1069           an.addProperty(Properties.newProperty(THRESHOLD + "Name",
1070                   Properties.STRINGTYPE, "" + alan.getThreshold().label));
1071       }
1072       ((DataSet) sref.getV_parent()).addDataSetAnnotations(an);
1073       bindjvvobj(alan, an);
1074     }
1075     else
1076     {
1077       // update reference sequence Annotation
1078       if (an.getModifiable() == null) // TODO: USE VAMSAS LIBRARY OBJECT LOCK
1079       // METHODS)
1080       {
1081         // verify existing alignment sequence annotation is up to date
1082         System.out.println("update dataset sequence annotation.");
1083       }
1084       else
1085       {
1086         // verify existing alignment sequence annotation is up to date
1087         System.out
1088                 .println("make new alignment dataset sequence annotation if modification has happened.");
1089       }
1090     }
1091
1092   }
1093
1094   private int[] getGapMap(HashMap AlSeqMaps, AlignmentAnnotation alan)
1095   {
1096     int[] gapMap;
1097     if (AlSeqMaps.containsKey(alan.sequenceRef))
1098     {
1099       gapMap = (int[]) AlSeqMaps.get(alan.sequenceRef);
1100     }
1101     else
1102     {
1103       gapMap = new int[alan.sequenceRef.getLength()];
1104       // map from alignment position to sequence position.
1105       int[] sgapMap = alan.sequenceRef.gapMap();
1106       for (int a = 0; a < sgapMap.length; a++)
1107       {
1108         gapMap[sgapMap[a]] = a;
1109       }
1110     }
1111     return gapMap;
1112   }
1113
1114   private void saveAlignmentSequenceAnnotation(HashMap AlSeqMaps,
1115           AlignmentSequence alsref, AlignmentAnnotation alan)
1116   {
1117     // {
1118     // uk.ac.vamsas.
1119     // objects.core.AlignmentSequence alsref = (uk.ac.vamsas.
1120     // objects.core.AlignmentSequence) sref;
1121     uk.ac.vamsas.objects.core.AlignmentSequenceAnnotation an = (uk.ac.vamsas.objects.core.AlignmentSequenceAnnotation) getjv2vObj(alan);
1122     int[] gapMap = getGapMap(AlSeqMaps, alan);
1123     if (an == null)
1124     {
1125       an = new uk.ac.vamsas.objects.core.AlignmentSequenceAnnotation();
1126       initRangeAnnotationType(an, alan, gapMap);
1127       /**
1128        * I mean here that we don't actually have a semantic 'type' for the
1129        * annotation (this might be - score, intrinsic property, measurement,
1130        * something extracted from another program, etc)
1131        */
1132       an.setType(JALVIEW_ANNOTATION_ROW); // TODO: better fix
1133       // this rough guess ;)
1134       alsref.addAlignmentSequenceAnnotation(an);
1135       bindjvvobj(alan, an);
1136       // These properties are directly supported by the
1137       // AlignmentSequenceAnnotation type.
1138       setAnnotationType(an, alan);
1139       an.setProvenance(dummyProvenance()); // get provenance as user
1140       // created, or jnet, or
1141       // something else.
1142     }
1143     else
1144     {
1145       // update reference sequence Annotation
1146       if (an.getModifiable() == null) // TODO: USE VAMSAS LIBRARY OBJECT LOCK
1147       // METHODS)
1148       {
1149         // verify existing alignment sequence annotation is up to date
1150         System.out.println("update alignment sequence annotation.");
1151       }
1152       else
1153       {
1154         // verify existing alignment sequence annotation is up to date
1155         System.out
1156                 .println("make new alignment sequence annotation if modification has happened.");
1157       }
1158     }
1159   }
1160
1161   /**
1162    * set vamsas annotation object type from jalview annotation
1163    * 
1164    * @param an
1165    * @param alan
1166    */
1167   private void setAnnotationType(RangeAnnotation an,
1168           AlignmentAnnotation alan)
1169   {
1170     if (an instanceof AlignmentSequenceAnnotation)
1171     {
1172       if (alan.graph != AlignmentAnnotation.NO_GRAPH)
1173       {
1174         ((AlignmentSequenceAnnotation) an).setGraph(true);
1175       }
1176       else
1177       {
1178         ((AlignmentSequenceAnnotation) an).setGraph(false);
1179       }
1180     }
1181     if (an instanceof uk.ac.vamsas.objects.core.AlignmentAnnotation)
1182     {
1183       if (alan.graph != AlignmentAnnotation.NO_GRAPH)
1184       {
1185         ((uk.ac.vamsas.objects.core.AlignmentAnnotation) an).setGraph(true);
1186       }
1187       else
1188       {
1189         ((uk.ac.vamsas.objects.core.AlignmentAnnotation) an)
1190                 .setGraph(false);
1191       }
1192     }
1193     switch (alan.graph)
1194     {
1195     case AlignmentAnnotation.BAR_GRAPH:
1196       an.addProperty(Properties.newProperty(DISCRETE_ANNOTATION,
1197               Properties.BOOLEANTYPE, "true"));
1198       break;
1199     case AlignmentAnnotation.LINE_GRAPH:
1200       an.addProperty(Properties.newProperty(CONTINUOUS_ANNOTATION,
1201               Properties.BOOLEANTYPE, "true"));
1202       break;
1203     default:
1204       // don't add any kind of discrete or continous property info.
1205     }
1206   }
1207
1208   /**
1209    * get start<end range of segment, adjusting for inclusivity flag and
1210    * polarity.
1211    * 
1212    * @param visSeg
1213    * @param ensureDirection
1214    *          when true - always ensure start is less than end.
1215    * @return int[] { start, end, direction} where direction==1 for range running
1216    *         from end to start.
1217    */
1218   private int[] getSegRange(Seg visSeg, boolean ensureDirection)
1219   {
1220     boolean incl = visSeg.getInclusive();
1221     // adjust for inclusive flag.
1222     int pol = (visSeg.getStart() <= visSeg.getEnd()) ? 1 : -1; // polarity of
1223     // region.
1224     int start = visSeg.getStart() + (incl ? 0 : pol);
1225     int end = visSeg.getEnd() + (incl ? 0 : -pol);
1226     if (ensureDirection && pol == -1)
1227     {
1228       // jalview doesn't deal with inverted ranges, yet.
1229       int t = end;
1230       end = start;
1231       start = t;
1232     }
1233     return new int[]
1234     { start, end, pol < 0 ? 1 : 0 };
1235   }
1236
1237   /**
1238    * 
1239    * @param annotation
1240    * @return true if annotation is not to be stored in document
1241    */
1242   private boolean isJalviewOnly(AlignmentAnnotation annotation)
1243   {
1244     return annotation.autoCalculated || annotation.label.equals("Quality")
1245             || annotation.label.equals("Conservation")
1246             || annotation.label.equals("Consensus");
1247   }
1248
1249   boolean dojvsync = true;
1250
1251   // boolean dojvsync = false; // disables Jalview AppData IO
1252   /**
1253    * list of alignment views created when updating Jalview from document.
1254    */
1255   private final Vector newAlignmentViews = new Vector();
1256
1257   /**
1258    * update local jalview view settings from the stored appdata (if any)
1259    */
1260   public void updateJalviewFromAppdata()
1261   {
1262     // recover any existing Jalview data from appdata
1263     // TODO: recover any PDB files stored as attachments in the vamsas session
1264     // and initialise the Jalview2XML.alreadyLoadedPDB hashtable with mappings
1265     // to temp files.
1266     {
1267       final IClientAppdata cappdata = cdoc.getClientAppdata();
1268       if (cappdata != null)
1269       {
1270         if (cappdata.hasClientAppdata())
1271         {
1272           // TODO: how to check version of Jalview client app data and whether
1273           // it has been modified
1274           // client data is shared over all app clients
1275           try
1276           {
1277             jalview.gui.Jalview2XML fromxml = new jalview.gui.Jalview2XML();
1278             fromxml.attemptversion1parse = false;
1279             fromxml.setUniqueSetSuffix("");
1280             fromxml.setObjectMappingTables(vobj2jv, jv2vobj); // mapKeysToString
1281             // and
1282             // mapValuesToString
1283             fromxml.setSkipList(skipList);
1284             jalview.util.jarInputStreamProvider jprovider = new jalview.util.jarInputStreamProvider()
1285             {
1286
1287               @Override
1288               public String getFilename()
1289               {
1290
1291                 // TODO Get the vamsas session ID here
1292                 return "Jalview Vamsas Document Client Data";
1293               }
1294
1295               @Override
1296               public JarInputStream getJarInputStream() throws IOException
1297               {
1298                 jalview.bin.Cache.log
1299                         .debug("Returning client input stream for Jalview from Vamsas Document.");
1300                 return new JarInputStream(cappdata.getClientInputStream());
1301               }
1302             };
1303             if (dojvsync)
1304             {
1305               fromxml.LoadJalviewAlign(jprovider);
1306             }
1307           } catch (Exception e)
1308           {
1309
1310           } catch (OutOfMemoryError e)
1311           {
1312
1313           } catch (Error e)
1314           {
1315
1316           }
1317         }
1318       }
1319       if (cappdata.hasUserAppdata())
1320       {
1321         // TODO: how to check version of Jalview user app data and whether it
1322         // has been modified
1323         // user data overrides data shared over all app clients ?
1324         try
1325         {
1326           jalview.gui.Jalview2XML fromxml = new jalview.gui.Jalview2XML();
1327           fromxml.attemptversion1parse = false;
1328           fromxml.setUniqueSetSuffix("");
1329           fromxml.setSkipList(skipList);
1330           fromxml.setObjectMappingTables(mapKeysToString(vobj2jv),
1331                   mapValuesToString(jv2vobj));
1332           jalview.util.jarInputStreamProvider jarstream = new jalview.util.jarInputStreamProvider()
1333           {
1334
1335             @Override
1336             public String getFilename()
1337             {
1338
1339               // TODO Get the vamsas session ID here
1340               return "Jalview Vamsas Document User Data";
1341             }
1342
1343             @Override
1344             public JarInputStream getJarInputStream() throws IOException
1345             {
1346               jalview.bin.Cache.log
1347                       .debug("Returning user input stream for Jalview from Vamsas Document.");
1348               return new JarInputStream(cappdata.getUserInputStream());
1349             }
1350           };
1351           if (dojvsync)
1352           {
1353             fromxml.LoadJalviewAlign(jarstream);
1354           }
1355         } catch (Exception e)
1356         {
1357
1358         } catch (OutOfMemoryError e)
1359         {
1360
1361         } catch (Error e)
1362         {
1363
1364         }
1365       }
1366
1367     }
1368     flushAlignViewports();
1369   }
1370
1371   /**
1372    * remove any spurious views generated by document synchronization
1373    */
1374   private void flushAlignViewports()
1375   {
1376     // remove any additional viewports originally recovered from the vamsas
1377     // document.
1378     // search for all alignframes containing viewports generated from document
1379     // sync,
1380     // and if any contain more than one view, then remove the one generated by
1381     // document update.
1382     AlignViewport views[], av = null;
1383     AlignFrame af = null;
1384     Iterator newviews = newAlignmentViews.iterator();
1385     while (newviews.hasNext())
1386     {
1387       av = (AlignViewport) newviews.next();
1388       af = Desktop.getAlignFrameFor(av);
1389       // TODO implement this : af.getNumberOfViews
1390       String seqsetidobj = av.getSequenceSetId();
1391       views = Desktop.getViewports(seqsetidobj);
1392       Cache.log.debug("Found "
1393               + (views == null ? " no " : "" + views.length)
1394               + " views for '" + av.getSequenceSetId() + "'");
1395       if (views.length > 1)
1396       {
1397         // we need to close the original document view.
1398
1399         // work out how to do this by seeing if the views are gathered.
1400         // pretty clunky but the only way to do this without adding more flags
1401         // to the align frames.
1402         boolean gathered = false;
1403         String newviewid = null;
1404         AlignedCodonFrame[] mappings = av.getAlignment().getCodonFrames();
1405         for (int i = 0; i < views.length; i++)
1406         {
1407           if (views[i] != av)
1408           {
1409             AlignFrame viewframe = Desktop.getAlignFrameFor(views[i]);
1410             if (viewframe == af)
1411             {
1412               gathered = true;
1413             }
1414             newviewid = views[i].getSequenceSetId();
1415           }
1416           else
1417           {
1418             // lose the reference to the vamsas document created view
1419             views[i] = null;
1420           }
1421         }
1422         // close the view generated by the vamsas document synchronization
1423         if (gathered)
1424         {
1425           af.closeView(av);
1426         }
1427         else
1428         {
1429           af.closeMenuItem_actionPerformed(false);
1430         }
1431         replaceJvObjMapping(seqsetidobj, newviewid);
1432         seqsetidobj = newviewid;
1433         // not sure if we need to do this:
1434
1435         if (false) // mappings != null)
1436         {
1437           // ensure sequence mappings from vamsas document view still
1438           // active
1439           if (mappings != null && mappings.length > 0)
1440           {
1441             jalview.structure.StructureSelectionManager
1442                     .getStructureSelectionManager(Desktop.instance)
1443                     .addMappings(mappings);
1444           }
1445         }
1446       }
1447       // ensure vamsas object binds to the stored views retrieved from
1448       // Jalview appdata
1449       // jalview.structure.StructureSelectionManager
1450       // .getStructureSelectionManager()
1451       // .addStructureViewerListener(viewframe.alignPanel);
1452
1453     }
1454
1455     newviews = null;
1456     newAlignmentViews.clear();
1457   }
1458
1459   /**
1460    * replaces oldjvobject with newjvobject in the Jalview Object <> VorbaID
1461    * binding tables
1462    * 
1463    * @param oldjvobject
1464    * @param newjvobject
1465    *          (may be null)
1466    */
1467   private void replaceJvObjMapping(Object oldjvobject, Object newjvobject)
1468   {
1469     Object vobject = jv2vobj.remove(oldjvobject);
1470     if (vobject == null)
1471     {
1472       // NOTE: this happens if user deletes object in one session then updates
1473       // from another client
1474       throw new Error(
1475               "IMPLEMENTATION ERROR: old jalview object is not bound ! ("
1476                       + oldjvobject + ")");
1477     }
1478     if (newjvobject != null)
1479     {
1480       jv2vobj.put(newjvobject, vobject);
1481       vobj2jv.put(vobject, newjvobject);
1482     }
1483   }
1484
1485   /**
1486    * Update the jalview client and user appdata from the local jalview settings
1487    */
1488   public void updateJalviewClientAppdata()
1489   {
1490     final IClientAppdata cappdata = cdoc.getClientAppdata();
1491     if (cappdata != null)
1492     {
1493       try
1494       {
1495         jalview.gui.Jalview2XML jxml = new jalview.gui.Jalview2XML();
1496         jxml.setObjectMappingTables(mapKeysToString(vobj2jv),
1497                 mapValuesToString(jv2vobj));
1498         jxml.setSkipList(skipList);
1499         if (dojvsync)
1500         {
1501           jxml.SaveState(new JarOutputStream(cappdata
1502                   .getClientOutputStream()));
1503         }
1504
1505       } catch (Exception e)
1506       {
1507         // TODO raise GUI warning if user requests it.
1508         jalview.bin.Cache.log
1509                 .error("Couldn't update jalview client application data. Giving up - local settings probably lost.",
1510                         e);
1511       }
1512     }
1513     else
1514     {
1515       jalview.bin.Cache.log
1516               .error("Couldn't access client application data for vamsas session. This is probably a vamsas client bug.");
1517     }
1518   }
1519
1520   /**
1521    * translate the Vobject keys to strings for use in Jalview2XML
1522    * 
1523    * @param jv2vobj2
1524    * @return
1525    */
1526   private IdentityHashMap mapValuesToString(IdentityHashMap jv2vobj2)
1527   {
1528     IdentityHashMap mapped = new IdentityHashMap();
1529     Iterator keys = jv2vobj2.keySet().iterator();
1530     while (keys.hasNext())
1531     {
1532       Object key = keys.next();
1533       mapped.put(key, jv2vobj2.get(key).toString());
1534     }
1535     return mapped;
1536   }
1537
1538   /**
1539    * translate the Vobject values to strings for use in Jalview2XML
1540    * 
1541    * @param vobj2jv2
1542    * @return hashtable with string values
1543    */
1544   private Hashtable mapKeysToString(Hashtable vobj2jv2)
1545   {
1546     Hashtable mapped = new Hashtable();
1547     Iterator keys = vobj2jv2.keySet().iterator();
1548     while (keys.hasNext())
1549     {
1550       Object key = keys.next();
1551       mapped.put(key.toString(), vobj2jv2.get(key));
1552     }
1553     return mapped;
1554   }
1555
1556   /**
1557    * synchronize Jalview from the vamsas document
1558    * 
1559    * @return number of new views from document
1560    */
1561   public int updateToJalview()
1562   {
1563     VAMSAS _roots[] = cdoc.getVamsasRoots();
1564
1565     for (int _root = 0; _root < _roots.length; _root++)
1566     {
1567       VAMSAS root = _roots[_root];
1568       boolean newds = false;
1569       for (int _ds = 0, _nds = root.getDataSetCount(); _ds < _nds; _ds++)
1570       {
1571         // ///////////////////////////////////
1572         // ///LOAD DATASET
1573         DataSet dataset = root.getDataSet(_ds);
1574         int i, iSize = dataset.getSequenceCount();
1575         List<SequenceI> dsseqs;
1576         jalview.datamodel.Alignment jdataset = (jalview.datamodel.Alignment) getvObj2jv(dataset);
1577         int jremain = 0;
1578         if (jdataset == null)
1579         {
1580           Cache.log.debug("Initialising new jalview dataset fields");
1581           newds = true;
1582           dsseqs = new Vector();
1583         }
1584         else
1585         {
1586           Cache.log.debug("Update jalview dataset from vamsas.");
1587           jremain = jdataset.getHeight();
1588           dsseqs = jdataset.getSequences();
1589         }
1590
1591         // TODO: test sequence merging - we preserve existing non vamsas
1592         // sequences but add in any new vamsas ones, and don't yet update any
1593         // sequence attributes
1594         for (i = 0; i < iSize; i++)
1595         {
1596           Sequence vdseq = dataset.getSequence(i);
1597           jalview.io.vamsas.Datasetsequence dssync = new Datasetsequence(
1598                   this, vdseq);
1599
1600           jalview.datamodel.SequenceI dsseq = (SequenceI) dssync.getJvobj();
1601           if (dssync.isAddfromdoc())
1602           {
1603             dsseqs.add(dsseq);
1604           }
1605           if (vdseq.getDbRefCount() > 0)
1606           {
1607             DbRef[] dbref = vdseq.getDbRef();
1608             for (int db = 0; db < dbref.length; db++)
1609             {
1610               new jalview.io.vamsas.Dbref(this, dbref[db], vdseq, dsseq);
1611
1612             }
1613             dsseq.updatePDBIds();
1614           }
1615         }
1616
1617         if (newds)
1618         {
1619           SequenceI[] seqs = new SequenceI[dsseqs.size()];
1620           for (i = 0, iSize = dsseqs.size(); i < iSize; i++)
1621           {
1622             seqs[i] = dsseqs.get(i);
1623             dsseqs.set(i, null);
1624           }
1625           jdataset = new jalview.datamodel.Alignment(seqs);
1626           Cache.log.debug("New vamsas dataset imported into jalview.");
1627           bindjvvobj(jdataset, dataset);
1628         }
1629         // ////////
1630         // add any new dataset sequence feature annotations
1631         if (dataset.getDataSetAnnotations() != null)
1632         {
1633           for (int dsa = 0; dsa < dataset.getDataSetAnnotationsCount(); dsa++)
1634           {
1635             DataSetAnnotations dseta = dataset.getDataSetAnnotations(dsa);
1636             // TODO: deal with group annotation on datset sequences.
1637             if (dseta.getSeqRefCount() == 1)
1638             {
1639               SequenceI dsSeq = (SequenceI) getvObj2jv((Vobject) dseta
1640                       .getSeqRef(0)); // TODO: deal with group dataset
1641               // annotations
1642               if (dsSeq == null)
1643               {
1644                 jalview.bin.Cache.log
1645                         .warn("Couldn't resolve jalview sequenceI for dataset object reference "
1646                                 + ((Vobject) dataset.getDataSetAnnotations(
1647                                         dsa).getSeqRef(0)).getVorbaId()
1648                                         .getId());
1649               }
1650               else
1651               {
1652                 if (dseta.getAnnotationElementCount() == 0)
1653                 {
1654                   new jalview.io.vamsas.Sequencefeature(this, dseta, dsSeq);
1655
1656                 }
1657                 else
1658                 {
1659                   // TODO: deal with alignmentAnnotation style annotation
1660                   // appearing on dataset sequences.
1661                   // JBPNote: we could just add them to all alignments but
1662                   // that may complicate cross references in the jalview
1663                   // datamodel
1664                   Cache.log
1665                           .warn("Ignoring dataset annotation with annotationElements. Not yet supported in jalview.");
1666                 }
1667               }
1668             }
1669             else
1670             {
1671               Cache.log
1672                       .warn("Ignoring multiply referenced dataset sequence annotation for binding to datsaet sequence features.");
1673             }
1674           }
1675         }
1676         if (dataset.getAlignmentCount() > 0)
1677         {
1678           // LOAD ALIGNMENTS from DATASET
1679
1680           for (int al = 0, nal = dataset.getAlignmentCount(); al < nal; al++)
1681           {
1682             uk.ac.vamsas.objects.core.Alignment alignment = dataset
1683                     .getAlignment(al);
1684             // TODO check this handles multiple views properly
1685             AlignViewport av = findViewport(alignment);
1686
1687             jalview.datamodel.AlignmentI jal = null;
1688             if (av != null)
1689             {
1690               // TODO check that correct alignment object is retrieved when
1691               // hidden seqs exist.
1692               jal = (av.hasHiddenRows()) ? av.getAlignment()
1693                       .getHiddenSequences().getFullAlignment() : av
1694                       .getAlignment();
1695             }
1696             iSize = alignment.getAlignmentSequenceCount();
1697             boolean refreshal = false;
1698             Vector newasAnnots = new Vector();
1699             char gapChar = ' '; // default for new alignments read in from the
1700             // document
1701             if (jal != null)
1702             {
1703               dsseqs = jal.getSequences(); // for merge/update
1704               gapChar = jal.getGapCharacter();
1705             }
1706             else
1707             {
1708               dsseqs = new Vector();
1709             }
1710             char valGapchar = alignment.getGapChar().charAt(0);
1711             for (i = 0; i < iSize; i++)
1712             {
1713               AlignmentSequence valseq = alignment.getAlignmentSequence(i);
1714               jalview.datamodel.Sequence alseq = (jalview.datamodel.Sequence) getvObj2jv(valseq);
1715               if (syncFromAlignmentSequence(valseq, valGapchar, gapChar,
1716                       dsseqs) && alseq != null)
1717               {
1718
1719                 // updated to sequence from the document
1720                 jremain--;
1721                 refreshal = true;
1722               }
1723               if (valseq.getAlignmentSequenceAnnotationCount() > 0)
1724               {
1725                 AlignmentSequenceAnnotation[] vasannot = valseq
1726                         .getAlignmentSequenceAnnotation();
1727                 for (int a = 0; a < vasannot.length; a++)
1728                 {
1729                   jalview.datamodel.AlignmentAnnotation asa = (jalview.datamodel.AlignmentAnnotation) getvObj2jv(vasannot[a]); // TODO:
1730                   // 1:many
1731                   // jalview
1732                   // alignment
1733                   // sequence
1734                   // annotations
1735                   if (asa == null)
1736                   {
1737                     int se[] = getBounds(vasannot[a]);
1738                     asa = getjAlignmentAnnotation(jal, vasannot[a]);
1739                     asa.setSequenceRef(alseq);
1740                     asa.createSequenceMapping(alseq, se[0], false); // TODO:
1741                     // verify
1742                     // that
1743                     // positions
1744                     // in
1745                     // alseqAnnotation
1746                     // correspond
1747                     // to
1748                     // ungapped
1749                     // residue
1750                     // positions.
1751                     alseq.addAlignmentAnnotation(asa);
1752                     bindjvvobj(asa, vasannot[a]);
1753                     refreshal = true;
1754                     newasAnnots.add(asa);
1755                   }
1756                   else
1757                   {
1758                     // update existing annotation - can do this in place
1759                     if (vasannot[a].getModifiable() == null) // TODO: USE
1760                     // VAMSAS LIBRARY
1761                     // OBJECT LOCK
1762                     // METHODS)
1763                     {
1764                       Cache.log
1765                               .info("UNIMPLEMENTED: not recovering user modifiable sequence alignment annotation");
1766                       // TODO: should at least replace with new one - otherwise
1767                       // things will break
1768                       // basically do this:
1769                       // int se[] = getBounds(vasannot[a]);
1770                       // asa.update(getjAlignmentAnnotation(jal, vasannot[a]));
1771                       // // update from another annotation object in place.
1772                       // asa.createSequenceMapping(alseq, se[0], false);
1773
1774                     }
1775                   }
1776                 }
1777               }
1778             }
1779             if (jal == null)
1780             {
1781               SequenceI[] seqs = new SequenceI[dsseqs.size()];
1782               for (i = 0, iSize = dsseqs.size(); i < iSize; i++)
1783               {
1784                 seqs[i] = dsseqs.get(i);
1785                 dsseqs.set(i, null);
1786               }
1787               jal = new jalview.datamodel.Alignment(seqs);
1788               Cache.log.debug("New vamsas alignment imported into jalview "
1789                       + alignment.getVorbaId().getId());
1790               jal.setDataset(jdataset);
1791             }
1792             if (newasAnnots != null && newasAnnots.size() > 0)
1793             {
1794               // Add the new sequence annotations in to the alignment.
1795               for (int an = 0, anSize = newasAnnots.size(); an < anSize; an++)
1796               {
1797                 jal.addAnnotation((AlignmentAnnotation) newasAnnots
1798                         .elementAt(an));
1799                 // TODO: check if anything has to be done - like calling
1800                 // adjustForAlignment or something.
1801                 newasAnnots.setElementAt(null, an);
1802               }
1803               newasAnnots = null;
1804             }
1805             // //////////////////////////////////////////
1806             // //LOAD ANNOTATIONS FOR THE ALIGNMENT
1807             // ////////////////////////////////////
1808             if (alignment.getAlignmentAnnotationCount() > 0)
1809             {
1810               uk.ac.vamsas.objects.core.AlignmentAnnotation[] an = alignment
1811                       .getAlignmentAnnotation();
1812
1813               for (int j = 0; j < an.length; j++)
1814               {
1815                 jalview.datamodel.AlignmentAnnotation jan = (jalview.datamodel.AlignmentAnnotation) getvObj2jv(an[j]);
1816                 if (jan != null)
1817                 {
1818                   // update or stay the same.
1819                   // TODO: should at least replace with a new one - otherwise
1820                   // things will break
1821                   // basically do this:
1822                   // jan.update(getjAlignmentAnnotation(jal, an[a])); // update
1823                   // from another annotation object in place.
1824
1825                   Cache.log
1826                           .debug("update from vamsas alignment annotation to existing jalview alignment annotation.");
1827                   if (an[j].getModifiable() == null) // TODO: USE VAMSAS
1828                   // LIBRARY OBJECT LOCK
1829                   // METHODS)
1830                   {
1831                     // TODO: user defined annotation is totally mutable... - so
1832                     // load it up or throw away if locally edited.
1833                     Cache.log
1834                             .info("NOT IMPLEMENTED - Recovering user-modifiable annotation - yet...");
1835                   }
1836                   // TODO: compare annotation element rows
1837                   // TODO: compare props.
1838                 }
1839                 else
1840                 {
1841                   jan = getjAlignmentAnnotation(jal, an[j]);
1842                   // TODO: ensure we add the alignment annotation before the
1843                   // automatic annotation rows
1844                   jal.addAnnotation(jan);
1845                   bindjvvobj(jan, an[j]);
1846                   refreshal = true;
1847                 }
1848               }
1849             }
1850             AlignFrame alignFrame;
1851             if (av == null)
1852             {
1853               Cache.log.debug("New alignframe for alignment "
1854                       + alignment.getVorbaId());
1855               // ///////////////////////////////
1856               // construct alignment view
1857               alignFrame = new AlignFrame(jal, AlignFrame.DEFAULT_WIDTH,
1858                       AlignFrame.DEFAULT_HEIGHT, alignment.getVorbaId()
1859                               .toString());
1860               av = alignFrame.getViewport();
1861               newAlignmentViews.addElement(av);
1862               String title = alignment
1863                       .getProvenance()
1864                       .getEntry(
1865                               alignment.getProvenance().getEntryCount() - 1)
1866                       .getAction();
1867               if (alignment.getPropertyCount() > 0)
1868               {
1869                 for (int p = 0, pe = alignment.getPropertyCount(); p < pe; p++)
1870                 {
1871                   if (alignment.getProperty(p).getName().equals("title"))
1872                   {
1873                     title = alignment.getProperty(p).getContent();
1874                   }
1875                 }
1876               }
1877               // TODO: automatically create meaningful title for a vamsas
1878               // alignment using its provenance.
1879               if (Cache.log.isDebugEnabled())
1880               {
1881                 title = title + "(" + alignment.getVorbaId() + ")";
1882
1883               }
1884               jalview.gui.Desktop.addInternalFrame(alignFrame, title,
1885                       AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
1886               bindjvvobj(av.getSequenceSetId(), alignment);
1887             }
1888             else
1889             {
1890               // find the alignFrame for jal.
1891               // TODO: fix this so we retrieve the alignFrame handing av
1892               // *directly* (JBPNote - don't understand this now)
1893               // TODO: make sure all associated views are refreshed
1894               alignFrame = Desktop.getAlignFrameFor(av);
1895               if (refreshal)
1896               {
1897                 av.alignmentChanged(alignFrame.alignPanel);
1898                 alignFrame.alignPanel.adjustAnnotationHeight();
1899               }
1900             }
1901             // LOAD TREES
1902             // /////////////////////////////////////
1903             if (alignment.getTreeCount() > 0)
1904             {
1905
1906               for (int t = 0; t < alignment.getTreeCount(); t++)
1907               {
1908                 jalview.io.vamsas.Tree vstree = new jalview.io.vamsas.Tree(
1909                         this, alignFrame, alignment.getTree(t));
1910                 TreePanel tp = null;
1911                 if (vstree.isValidTree())
1912                 {
1913                   tp = alignFrame.ShowNewickTree(vstree.getNewickTree(),
1914                           vstree.getTitle(), vstree.getInputData(), 600,
1915                           500, t * 20 + 50, t * 20 + 50);
1916
1917                 }
1918                 if (tp != null)
1919                 {
1920                   bindjvvobj(tp, alignment.getTree(t));
1921                   try
1922                   {
1923                     vstree.UpdateSequenceTreeMap(tp);
1924                   } catch (RuntimeException e)
1925                   {
1926                     Cache.log.warn("update of labels failed.", e);
1927                   }
1928                 }
1929                 else
1930                 {
1931                   Cache.log.warn("Cannot create tree for tree " + t
1932                           + " in document ("
1933                           + alignment.getTree(t).getVorbaId());
1934                 }
1935
1936               }
1937             }
1938           }
1939         }
1940       }
1941       // we do sequenceMappings last because they span all datasets in a vamsas
1942       // root
1943       for (int _ds = 0, _nds = root.getDataSetCount(); _ds < _nds; _ds++)
1944       {
1945         DataSet dataset = root.getDataSet(_ds);
1946         if (dataset.getSequenceMappingCount() > 0)
1947         {
1948           for (int sm = 0, smCount = dataset.getSequenceMappingCount(); sm < smCount; sm++)
1949           {
1950             Rangetype seqmap = new jalview.io.vamsas.Sequencemapping(this,
1951                     dataset.getSequenceMapping(sm));
1952           }
1953         }
1954       }
1955     }
1956     return newAlignmentViews.size();
1957   }
1958
1959   public AlignViewport findViewport(Alignment alignment)
1960   {
1961     AlignViewport av = null;
1962     AlignViewport[] avs = Desktop
1963             .getViewports((String) getvObj2jv(alignment));
1964     if (avs != null)
1965     {
1966       av = avs[0];
1967     }
1968     return av;
1969   }
1970
1971   // bitfields - should be a template in j1.5
1972   private static int HASSECSTR = 0;
1973
1974   private static int HASVALS = 1;
1975
1976   private static int HASHPHOB = 2;
1977
1978   private static int HASDC = 3;
1979
1980   private static int HASDESCSTR = 4;
1981
1982   private static int HASTWOSTATE = 5; // not used yet.
1983
1984   /**
1985    * parses the AnnotationElements - if they exist - into
1986    * jalview.datamodel.Annotation[] rows Two annotation rows are made if there
1987    * are distinct annotation for both at 'pos' and 'after pos' at any particular
1988    * site.
1989    * 
1990    * @param annotation
1991    * @return { boolean[static int constants ], int[ae.length] - map to annotated
1992    *         object frame, jalview.datamodel.Annotation[],
1993    *         jalview.datamodel.Annotation[] (after)}
1994    */
1995   private Object[] parseRangeAnnotation(
1996           uk.ac.vamsas.objects.core.RangeAnnotation annotation)
1997   {
1998     // set these attributes by looking in the annotation to decide what kind of
1999     // alignment annotation rows will be made
2000     // TODO: potentially we might make several annotation rows from one vamsas
2001     // alignment annotation. the jv2Vobj binding mechanism
2002     // may not quite cope with this (without binding an array of annotations to
2003     // a vamsas alignment annotation)
2004     // summary flags saying what we found over the set of annotation rows.
2005     boolean[] AeContent = new boolean[]
2006     { false, false, false, false, false };
2007     int[] rangeMap = getMapping(annotation);
2008     jalview.datamodel.Annotation[][] anot = new jalview.datamodel.Annotation[][]
2009     { new jalview.datamodel.Annotation[rangeMap.length],
2010         new jalview.datamodel.Annotation[rangeMap.length] };
2011     boolean mergeable = true; // false if 'after positions cant be placed on
2012     // same annotation row as positions.
2013
2014     if (annotation.getAnnotationElementCount() > 0)
2015     {
2016       AnnotationElement ae[] = annotation.getAnnotationElement();
2017       for (int aa = 0; aa < ae.length; aa++)
2018       {
2019         int pos = (int) ae[aa].getPosition() - 1; // pos counts from 1 to
2020         // (|seg.start-seg.end|+1)
2021         if (pos >= 0 && pos < rangeMap.length)
2022         {
2023           int row = ae[aa].getAfter() ? 1 : 0;
2024           if (anot[row][pos] != null)
2025           {
2026             // only time this should happen is if the After flag is set.
2027             Cache.log.debug("Ignoring duplicate annotation site at " + pos);
2028             continue;
2029           }
2030           if (anot[1 - row][pos] != null)
2031           {
2032             mergeable = false;
2033           }
2034           String desc = "";
2035           if (ae[aa].getDescription() != null)
2036           {
2037             desc = ae[aa].getDescription();
2038             if (desc.length() > 0)
2039             {
2040               // have imported valid description string
2041               AeContent[HASDESCSTR] = true;
2042             }
2043           }
2044           String dc = null; // ae[aa].getDisplayCharacter()==null ? "dc" :
2045           // ae[aa].getDisplayCharacter();
2046           String ss = null; // ae[aa].getSecondaryStructure()==null ? "ss" :
2047           // ae[aa].getSecondaryStructure();
2048           java.awt.Color colour = null;
2049           if (ae[aa].getGlyphCount() > 0)
2050           {
2051             Glyph[] glyphs = ae[aa].getGlyph();
2052             for (int g = 0; g < glyphs.length; g++)
2053             {
2054               if (glyphs[g]
2055                       .getDict()
2056                       .equals(uk.ac.vamsas.objects.utils.GlyphDictionary.PROTEIN_SS_3STATE))
2057               {
2058                 ss = glyphs[g].getContent();
2059                 AeContent[HASSECSTR] = true;
2060               }
2061               else if (glyphs[g]
2062                       .getDict()
2063                       .equals(uk.ac.vamsas.objects.utils.GlyphDictionary.PROTEIN_HD_HYDRO))
2064               {
2065                 Cache.log.debug("ignoring hydrophobicity glyph marker.");
2066                 AeContent[HASHPHOB] = true;
2067                 char c = (dc = glyphs[g].getContent()).charAt(0);
2068                 // dc may get overwritten - but we still set the colour.
2069                 colour = new java.awt.Color(c == '+' ? 255 : 0,
2070                         c == '.' ? 255 : 0, c == '-' ? 255 : 0);
2071
2072               }
2073               else if (glyphs[g].getDict().equals(
2074                       uk.ac.vamsas.objects.utils.GlyphDictionary.DEFAULT))
2075               {
2076                 dc = glyphs[g].getContent();
2077                 AeContent[HASDC] = true;
2078               }
2079               else
2080               {
2081                 Cache.log
2082                         .debug("IMPLEMENTATION TODO: Ignoring unknown glyph type "
2083                                 + glyphs[g].getDict());
2084               }
2085             }
2086           }
2087           float val = 0;
2088           if (ae[aa].getValueCount() > 0)
2089           {
2090             AeContent[HASVALS] = true;
2091             if (ae[aa].getValueCount() > 1)
2092             {
2093               Cache.log.warn("ignoring additional "
2094                       + (ae[aa].getValueCount() - 1)
2095                       + " values in annotation element.");
2096             }
2097             val = ae[aa].getValue(0);
2098           }
2099           if (colour == null)
2100           {
2101             anot[row][pos] = new jalview.datamodel.Annotation(
2102                     (dc != null) ? dc : "", desc,
2103                     (ss != null) ? ss.charAt(0) : ' ', val);
2104           }
2105           else
2106           {
2107             anot[row][pos] = new jalview.datamodel.Annotation(
2108                     (dc != null) ? dc : "", desc,
2109                     (ss != null) ? ss.charAt(0) : ' ', val, colour);
2110           }
2111         }
2112         else
2113         {
2114           Cache.log.warn("Ignoring out of bound annotation element " + aa
2115                   + " in " + annotation.getVorbaId().getId());
2116         }
2117       }
2118       // decide on how many annotation rows are needed.
2119       if (mergeable)
2120       {
2121         for (int i = 0; i < anot[0].length; i++)
2122         {
2123           if (anot[1][i] != null)
2124           {
2125             anot[0][i] = anot[1][i];
2126             anot[0][i].description = anot[0][i].description + " (after)";
2127             AeContent[HASDESCSTR] = true; // we have valid description string
2128             // data
2129             anot[1][i] = null;
2130           }
2131         }
2132         anot[1] = null;
2133       }
2134       else
2135       {
2136         for (int i = 0; i < anot[0].length; i++)
2137         {
2138           anot[1][i].description = anot[1][i].description + " (after)";
2139         }
2140       }
2141       return new Object[]
2142       { AeContent, rangeMap, anot[0], anot[1] };
2143     }
2144     else
2145     {
2146       // no annotations to parse. Just return an empty annotationElement[]
2147       // array.
2148       return new Object[]
2149       { AeContent, rangeMap, anot[0], anot[1] };
2150     }
2151     // return null;
2152   }
2153
2154   /**
2155    * @param jal
2156    *          the jalview alignment to which the annotation will be attached
2157    *          (ideally - freshly updated from corresponding vamsas alignment)
2158    * @param annotation
2159    * @return unbound jalview alignment annotation object.
2160    */
2161   private jalview.datamodel.AlignmentAnnotation getjAlignmentAnnotation(
2162           jalview.datamodel.AlignmentI jal,
2163           uk.ac.vamsas.objects.core.RangeAnnotation annotation)
2164   {
2165     if (annotation == null)
2166     {
2167       return null;
2168     }
2169     // boolean
2170     // hasSequenceRef=annotation.getClass().equals(uk.ac.vamsas.objects.core.AlignmentSequenceAnnotation.class);
2171     // boolean hasProvenance=hasSequenceRef ||
2172     // (annotation.getClass().equals(uk.ac.vamsas.objects.core.AlignmentAnnotation.class));
2173     /*
2174      * int se[] = getBounds(annotation); if (se==null) se=new int[]
2175      * {0,jal.getWidth()-1};
2176      */
2177     Object[] parsedRangeAnnotation = parseRangeAnnotation(annotation);
2178     String a_label = annotation.getLabel();
2179     String a_descr = annotation.getDescription();
2180     GraphLine gl = null;
2181     int type = 0;
2182     boolean interp = true; // cleared if annotation is DISCRETE
2183     // set type and other attributes from properties
2184     if (annotation.getPropertyCount() > 0)
2185     {
2186       // look for special jalview properties
2187       uk.ac.vamsas.objects.core.Property[] props = annotation.getProperty();
2188       for (int p = 0; p < props.length; p++)
2189       {
2190         if (props[p].getName().equalsIgnoreCase(DISCRETE_ANNOTATION))
2191         {
2192           type = AlignmentAnnotation.BAR_GRAPH;
2193           interp = false;
2194         }
2195         else if (props[p].getName().equalsIgnoreCase(CONTINUOUS_ANNOTATION))
2196         {
2197           type = AlignmentAnnotation.LINE_GRAPH;
2198         }
2199         else if (props[p].getName().equalsIgnoreCase(THRESHOLD))
2200         {
2201           Float val = null;
2202           try
2203           {
2204             val = new Float(props[p].getContent());
2205           } catch (Exception e)
2206           {
2207             Cache.log.warn("Failed to parse threshold property");
2208           }
2209           if (val != null)
2210             if (gl == null)
2211             {
2212               gl = new GraphLine(val.floatValue(), "", java.awt.Color.black);
2213             }
2214             else
2215             {
2216               gl.value = val.floatValue();
2217             }
2218         }
2219         else if (props[p].getName().equalsIgnoreCase(THRESHOLD + "Name"))
2220         {
2221           if (gl == null)
2222             gl = new GraphLine(0, "", java.awt.Color.black);
2223           gl.label = props[p].getContent();
2224         }
2225       }
2226     }
2227     jalview.datamodel.AlignmentAnnotation jan = null;
2228     if (a_label == null || a_label.length() == 0)
2229     {
2230       a_label = annotation.getType();
2231       if (a_label.length() == 0)
2232       {
2233         a_label = "Unamed annotation";
2234       }
2235     }
2236     if (a_descr == null || a_descr.length() == 0)
2237     {
2238       a_descr = "Annotation of type '" + annotation.getType() + "'";
2239     }
2240     if (parsedRangeAnnotation == null)
2241     {
2242       Cache.log
2243               .debug("Inserting empty annotation row elements for a whole-alignment annotation.");
2244     }
2245     else
2246     {
2247       if (parsedRangeAnnotation[3] != null)
2248       {
2249         Cache.log.warn("Ignoring 'After' annotation row in "
2250                 + annotation.getVorbaId());
2251       }
2252       jalview.datamodel.Annotation[] arow = (jalview.datamodel.Annotation[]) parsedRangeAnnotation[2];
2253       boolean[] has = (boolean[]) parsedRangeAnnotation[0];
2254       // VAMSAS: getGraph is only on derived annotation for alignments - in this
2255       // way its 'odd' - there is already an existing TODO about removing this
2256       // flag as being redundant
2257       /*
2258        * if((annotation.getClass().equals(uk.ac.vamsas.objects.core.
2259        * AlignmentAnnotation.class) &&
2260        * ((uk.ac.vamsas.objects.core.AlignmentAnnotation)annotation).getGraph())
2261        * || (hasSequenceRef=true &&
2262        * ((uk.ac.vamsas.objects.core.AlignmentSequenceAnnotation
2263        * )annotation).getGraph())) {
2264        */
2265       if (has[HASVALS])
2266       {
2267         if (type == 0)
2268         {
2269           type = jalview.datamodel.AlignmentAnnotation.BAR_GRAPH; // default
2270           // type of
2271           // value
2272           // annotation
2273           if (has[HASHPHOB])
2274           {
2275             // no hints - so we ensure HPHOB display is like this.
2276             type = jalview.datamodel.AlignmentAnnotation.BAR_GRAPH;
2277           }
2278         }
2279         // make bounds and automatic description strings for jalview user's
2280         // benefit (these shouldn't be written back to vamsas document)
2281         boolean first = true;
2282         float min = 0, max = 1;
2283         int lastval = 0;
2284         for (int i = 0; i < arow.length; i++)
2285         {
2286           if (arow[i] != null)
2287           {
2288             if (i - lastval > 1 && interp)
2289             {
2290               // do some interpolation *between* points
2291               if (arow[lastval] != null)
2292               {
2293                 float interval = arow[i].value - arow[lastval].value;
2294                 interval /= i - lastval;
2295                 float base = arow[lastval].value;
2296                 for (int ip = lastval + 1, np = 0; ip < i; np++, ip++)
2297                 {
2298                   arow[ip] = new jalview.datamodel.Annotation("", "", ' ',
2299                           interval * np + base);
2300                   // NB - Interpolated points don't get a tooltip and
2301                   // description.
2302                 }
2303               }
2304             }
2305             lastval = i;
2306             // check range - shouldn't we have a min and max property in the
2307             // annotation object ?
2308             if (first)
2309             {
2310               min = max = arow[i].value;
2311               first = false;
2312             }
2313             else
2314             {
2315               if (arow[i].value < min)
2316               {
2317                 min = arow[i].value;
2318               }
2319               else if (arow[i].value > max)
2320               {
2321                 max = arow[i].value;
2322               }
2323             }
2324             // make tooltip and display char value
2325             if (!has[HASDESCSTR])
2326             {
2327               arow[i].description = arow[i].value + "";
2328             }
2329             if (!has[HASDC])
2330             {
2331               if (!interp)
2332               {
2333                 if (arow[i].description != null
2334                         && arow[i].description.length() < 3)
2335                 {
2336                   // copy over the description as the display char.
2337                   arow[i].displayCharacter = new String(arow[i].description);
2338                 }
2339               }
2340               else
2341               {
2342                 // mark the position as a point used for the interpolation.
2343                 arow[i].displayCharacter = arow[i].value + "";
2344               }
2345             }
2346           }
2347         }
2348         jan = new jalview.datamodel.AlignmentAnnotation(a_label, a_descr,
2349                 arow, min, max, type);
2350       }
2351       else
2352       {
2353         if (annotation.getAnnotationElementCount() == 0)
2354         {
2355           // empty annotation array
2356           // TODO: alignment 'features' compare rangeType spec to alignment
2357           // width - if it is not complete, then mark regions on the annotation
2358           // row.
2359         }
2360         jan = new jalview.datamodel.AlignmentAnnotation(a_label, a_descr,
2361                 arow);
2362         jan.setThreshold(null);
2363         jan.annotationId = annotation.getVorbaId().toString(); // keep all the
2364         // ids together.
2365       }
2366       if (annotation.getLinkCount() > 0)
2367       {
2368         Cache.log.warn("Ignoring " + annotation.getLinkCount()
2369                 + "links added to AlignmentAnnotation.");
2370       }
2371       if (annotation.getModifiable() == null
2372               || annotation.getModifiable().length() == 0) // TODO: USE VAMSAS
2373       // LIBRARY OBJECT
2374       // LOCK METHODS)
2375       {
2376         jan.editable = true;
2377       }
2378       try
2379       {
2380         if (annotation.getGroup() != null
2381                 && annotation.getGroup().length() > 0)
2382         {
2383           jan.graphGroup = Integer.parseInt(annotation.getGroup()); // TODO:
2384           // group
2385           // similarly
2386           // named
2387           // annotation
2388           // together
2389           // ?
2390         }
2391       } catch (Exception e)
2392       {
2393         Cache.log
2394                 .info("UNIMPLEMENTED : Couldn't parse non-integer group value for setting graphGroup correctly.");
2395       }
2396       return jan;
2397
2398     }
2399
2400     return null;
2401   }
2402
2403   /**
2404    * get real bounds of a RangeType's specification. start and end are an
2405    * inclusive range within which all segments and positions lie. TODO: refactor
2406    * to vamsas utils
2407    * 
2408    * @param dseta
2409    * @return int[] { start, end}
2410    */
2411   private int[] getBounds(RangeType dseta)
2412   {
2413     if (dseta != null)
2414     {
2415       int[] se = null;
2416       if (dseta.getSegCount() > 0 && dseta.getPosCount() > 0)
2417       {
2418         throw new Error(
2419                 "Invalid vamsas RangeType - cannot resolve both lists of Pos and Seg from choice!");
2420       }
2421       if (dseta.getSegCount() > 0)
2422       {
2423         se = getSegRange(dseta.getSeg(0), true);
2424         for (int s = 1, sSize = dseta.getSegCount(); s < sSize; s++)
2425         {
2426           int nse[] = getSegRange(dseta.getSeg(s), true);
2427           if (se[0] > nse[0])
2428           {
2429             se[0] = nse[0];
2430           }
2431           if (se[1] < nse[1])
2432           {
2433             se[1] = nse[1];
2434           }
2435         }
2436       }
2437       if (dseta.getPosCount() > 0)
2438       {
2439         // could do a polarity for pos range too. and pass back indication of
2440         // discontinuities.
2441         int pos = dseta.getPos(0).getI();
2442         se = new int[]
2443         { pos, pos };
2444         for (int p = 0, pSize = dseta.getPosCount(); p < pSize; p++)
2445         {
2446           pos = dseta.getPos(p).getI();
2447           if (se[0] > pos)
2448           {
2449             se[0] = pos;
2450           }
2451           if (se[1] < pos)
2452           {
2453             se[1] = pos;
2454           }
2455         }
2456       }
2457       return se;
2458     }
2459     return null;
2460   }
2461
2462   /**
2463    * map from a rangeType's internal frame to the referenced object's coordinate
2464    * frame.
2465    * 
2466    * @param dseta
2467    * @return int [] { ref(pos)...} for all pos in rangeType's frame.
2468    */
2469   private int[] getMapping(RangeType dseta)
2470   {
2471     Vector posList = new Vector();
2472     if (dseta != null)
2473     {
2474       int[] se = null;
2475       if (dseta.getSegCount() > 0 && dseta.getPosCount() > 0)
2476       {
2477         throw new Error(
2478                 "Invalid vamsas RangeType - cannot resolve both lists of Pos and Seg from choice!");
2479       }
2480       if (dseta.getSegCount() > 0)
2481       {
2482         for (int s = 0, sSize = dseta.getSegCount(); s < sSize; s++)
2483         {
2484           se = getSegRange(dseta.getSeg(s), false);
2485           int se_end = se[1 - se[2]] + (se[2] == 0 ? 1 : -1);
2486           for (int p = se[se[2]]; p != se_end; p += se[2] == 0 ? 1 : -1)
2487           {
2488             posList.add(new Integer(p));
2489           }
2490         }
2491       }
2492       else if (dseta.getPosCount() > 0)
2493       {
2494         int pos = dseta.getPos(0).getI();
2495
2496         for (int p = 0, pSize = dseta.getPosCount(); p < pSize; p++)
2497         {
2498           pos = dseta.getPos(p).getI();
2499           posList.add(new Integer(pos));
2500         }
2501       }
2502     }
2503     if (posList != null && posList.size() > 0)
2504     {
2505       int[] range = new int[posList.size()];
2506       for (int i = 0; i < range.length; i++)
2507       {
2508         range[i] = ((Integer) posList.elementAt(i)).intValue();
2509       }
2510       posList.clear();
2511       return range;
2512     }
2513     return null;
2514   }
2515
2516   /**
2517    * 
2518    * @param maprange
2519    *          where the from range is the local mapped range, and the to range
2520    *          is the 'mapped' range in the MapRangeType
2521    * @param default unit for local
2522    * @param default unit for mapped
2523    * @return MapList
2524    */
2525   private jalview.util.MapList parsemapType(MapType maprange, int localu,
2526           int mappedu)
2527   {
2528     jalview.util.MapList ml = null;
2529     int[] localRange = getMapping(maprange.getLocal());
2530     int[] mappedRange = getMapping(maprange.getMapped());
2531     long lu = maprange.getLocal().hasUnit() ? maprange.getLocal().getUnit()
2532             : localu;
2533     long mu = maprange.getMapped().hasUnit() ? maprange.getMapped()
2534             .getUnit() : mappedu;
2535     ml = new jalview.util.MapList(localRange, mappedRange, (int) lu,
2536             (int) mu);
2537     return ml;
2538   }
2539
2540   /**
2541    * initialise a range type object from a set of start/end inclusive intervals
2542    * 
2543    * @param mrt
2544    * @param range
2545    */
2546   private void initRangeType(RangeType mrt, int[] range)
2547   {
2548     for (int i = 0; i < range.length; i += 2)
2549     {
2550       Seg vSeg = new Seg();
2551       vSeg.setStart(range[i]);
2552       vSeg.setEnd(range[i + 1]);
2553       mrt.addSeg(vSeg);
2554     }
2555   }
2556
2557   /**
2558    * initialise a MapType object from a MapList object.
2559    * 
2560    * @param maprange
2561    * @param ml
2562    * @param setUnits
2563    */
2564   private void initMapType(MapType maprange, jalview.util.MapList ml,
2565           boolean setUnits)
2566   {
2567     maprange.setLocal(new Local());
2568     maprange.setMapped(new Mapped());
2569     initRangeType(maprange.getLocal(), ml.getFromRanges());
2570     initRangeType(maprange.getMapped(), ml.getToRanges());
2571     if (setUnits)
2572     {
2573       maprange.getLocal().setUnit(ml.getFromRatio());
2574       maprange.getLocal().setUnit(ml.getToRatio());
2575     }
2576   }
2577
2578   /*
2579    * not needed now. Provenance getVamsasProvenance(jalview.datamodel.Provenance
2580    * jprov) { jalview.datamodel.ProvenanceEntry[] entries = null; // TODO: fix
2581    * App and Action here. Provenance prov = new Provenance();
2582    * org.exolab.castor.types.Date date = new org.exolab.castor.types.Date( new
2583    * java.util.Date()); Entry provEntry;
2584    * 
2585    * if (jprov != null) { entries = jprov.getEntries(); for (int i = 0; i <
2586    * entries.length; i++) { provEntry = new Entry(); try { date = new
2587    * org.exolab.castor.types.Date(entries[i].getDate()); } catch (Exception ex)
2588    * { ex.printStackTrace();
2589    * 
2590    * date = new org.exolab.castor.types.Date(entries[i].getDate()); }
2591    * provEntry.setDate(date); provEntry.setUser(entries[i].getUser());
2592    * provEntry.setAction(entries[i].getAction()); prov.addEntry(provEntry); } }
2593    * else { provEntry = new Entry(); provEntry.setDate(date);
2594    * provEntry.setUser(System.getProperty("user.name")); // TODO: ext string
2595    * provEntry.setApp("JVAPP"); // TODO: ext string provEntry.setAction(action);
2596    * prov.addEntry(provEntry); }
2597    * 
2598    * return prov; }
2599    */
2600   jalview.datamodel.Provenance getJalviewProvenance(Provenance prov)
2601   {
2602     // TODO: fix App and Action entries and check use of provenance in jalview.
2603     jalview.datamodel.Provenance jprov = new jalview.datamodel.Provenance();
2604     for (int i = 0; i < prov.getEntryCount(); i++)
2605     {
2606       jprov.addEntry(prov.getEntry(i).getUser(), prov.getEntry(i)
2607               .getAction(), prov.getEntry(i).getDate(), prov.getEntry(i)
2608               .getId());
2609     }
2610
2611     return jprov;
2612   }
2613
2614   /**
2615    * 
2616    * @return default initial provenance list for a Jalview created vamsas
2617    *         object.
2618    */
2619   Provenance dummyProvenance()
2620   {
2621     return dummyProvenance(null);
2622   }
2623
2624   Entry dummyPEntry(String action)
2625   {
2626     Entry entry = new Entry();
2627     entry.setApp(this.provEntry.getApp());
2628     if (action != null)
2629     {
2630       entry.setAction(action);
2631     }
2632     else
2633     {
2634       entry.setAction("created.");
2635     }
2636     entry.setDate(new java.util.Date());
2637     entry.setUser(this.provEntry.getUser());
2638     return entry;
2639   }
2640
2641   Provenance dummyProvenance(String action)
2642   {
2643     Provenance prov = new Provenance();
2644     prov.addEntry(dummyPEntry(action));
2645     return prov;
2646   }
2647
2648   Entry addProvenance(Provenance p, String action)
2649   {
2650     Entry dentry = dummyPEntry(action);
2651     p.addEntry(dentry);
2652     return dentry;
2653   }
2654
2655   public Entry getProvEntry()
2656   {
2657     return provEntry;
2658   }
2659
2660   public IClientDocument getClientDocument()
2661   {
2662     return cdoc;
2663   }
2664
2665   public IdentityHashMap getJvObjectBinding()
2666   {
2667     return jv2vobj;
2668   }
2669
2670   public Hashtable getVamsasObjectBinding()
2671   {
2672     return vobj2jv;
2673   }
2674
2675   public void storeSequenceMappings(AlignViewport viewport, String title)
2676           throws Exception
2677   {
2678     AlignViewport av = viewport;
2679     try
2680     {
2681       jalview.datamodel.AlignmentI jal = av.getAlignment();
2682       // /////////////////////////////////////////
2683       // SAVE THE DATASET
2684       DataSet dataset = null;
2685       if (jal.getDataset() == null)
2686       {
2687         Cache.log.warn("Creating new dataset for an alignment.");
2688         jal.setDataset(null);
2689       }
2690       dataset = (DataSet) ((Alignment) getjv2vObj(viewport
2691               .getSequenceSetId())).getV_parent(); // jal.getDataset());
2692       if (dataset == null)
2693       {
2694         dataset = (DataSet) getjv2vObj(jal.getDataset());
2695         Cache.log
2696                 .error("Can't find the correct dataset for the alignment in this view. Creating new one.");
2697
2698       }
2699       // Store any sequence mappings.
2700       if (av.getAlignment().getCodonFrames() != null
2701               && av.getAlignment().getCodonFrames().length > 0)
2702       {
2703         jalview.datamodel.AlignedCodonFrame[] cframes = av.getAlignment()
2704                 .getCodonFrames();
2705         for (int cf = 0; cf < cframes.length; cf++)
2706         {
2707           if (cframes[cf].getdnaSeqs() != null
2708                   && cframes[cf].getdnaSeqs().length > 0)
2709           {
2710             jalview.datamodel.SequenceI[] dmps = cframes[cf].getdnaSeqs();
2711             jalview.datamodel.Mapping[] mps = cframes[cf].getProtMappings();
2712             for (int smp = 0; smp < mps.length; smp++)
2713             {
2714               uk.ac.vamsas.objects.core.SequenceType mfrom = (SequenceType) getjv2vObj(dmps[smp]);
2715               if (mfrom != null)
2716               {
2717                 new jalview.io.vamsas.Sequencemapping(this, mps[smp],
2718                         mfrom, dataset);
2719               }
2720               else
2721               {
2722                 Cache.log
2723                         .warn("NO Vamsas Binding for local sequence! NOT CREATING MAPPING FOR "
2724                                 + dmps[smp].getDisplayId(true)
2725                                 + " to "
2726                                 + mps[smp].getTo().getName());
2727               }
2728             }
2729           }
2730         }
2731       }
2732     } catch (Exception e)
2733     {
2734       throw new Exception("Couldn't store sequence mappings for " + title,
2735               e);
2736     }
2737   }
2738
2739   public void clearSkipList()
2740   {
2741     if (skipList != null)
2742     {
2743       skipList.clear();
2744     }
2745   }
2746
2747   /**
2748    * @return the skipList
2749    */
2750   public Hashtable getSkipList()
2751   {
2752     return skipList;
2753   }
2754
2755   /**
2756    * @param skipList
2757    *          the skipList to set
2758    */
2759   public void setSkipList(Hashtable skipList)
2760   {
2761     this.skipList = skipList;
2762   }
2763
2764   /**
2765    * registry for datastoreItems
2766    */
2767   DatastoreRegistry dsReg = new DatastoreRegistry();
2768
2769   public DatastoreRegistry getDatastoreRegisty()
2770   {
2771     if (dsReg == null)
2772     {
2773       dsReg = new DatastoreRegistry();
2774     }
2775     return dsReg;
2776   }
2777 }