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