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