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