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