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