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