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