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