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