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