7375d438c12c979247fabdf26a8e60f571f17582
[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.util.jarInputStreamProvider;
39 import jalview.viewmodel.AlignmentViewport;
40
41 import java.io.File;
42 import java.io.IOException;
43 import java.util.Enumeration;
44 import java.util.HashMap;
45 import java.util.Hashtable;
46 import java.util.IdentityHashMap;
47 import java.util.Iterator;
48 import java.util.List;
49 import java.util.Vector;
50 import java.util.jar.JarInputStream;
51 import java.util.jar.JarOutputStream;
52
53 import uk.ac.vamsas.client.IClientAppdata;
54 import uk.ac.vamsas.client.IClientDocument;
55 import uk.ac.vamsas.client.Vobject;
56 import uk.ac.vamsas.client.VorbaId;
57 import uk.ac.vamsas.objects.core.Alignment;
58 import uk.ac.vamsas.objects.core.AlignmentSequence;
59 import uk.ac.vamsas.objects.core.AlignmentSequenceAnnotation;
60 import uk.ac.vamsas.objects.core.AnnotationElement;
61 import uk.ac.vamsas.objects.core.DataSet;
62 import uk.ac.vamsas.objects.core.DataSetAnnotations;
63 import uk.ac.vamsas.objects.core.DbRef;
64 import uk.ac.vamsas.objects.core.Entry;
65 import uk.ac.vamsas.objects.core.Glyph;
66 import uk.ac.vamsas.objects.core.Local;
67 import uk.ac.vamsas.objects.core.MapType;
68 import uk.ac.vamsas.objects.core.Mapped;
69 import uk.ac.vamsas.objects.core.Property;
70 import uk.ac.vamsas.objects.core.Provenance;
71 import uk.ac.vamsas.objects.core.RangeAnnotation;
72 import uk.ac.vamsas.objects.core.RangeType;
73 import uk.ac.vamsas.objects.core.Seg;
74 import uk.ac.vamsas.objects.core.Sequence;
75 import uk.ac.vamsas.objects.core.SequenceType;
76 import uk.ac.vamsas.objects.core.VAMSAS;
77 import uk.ac.vamsas.objects.utils.Properties;
78
79 /*
80  *
81  * static {
82  * org.exolab.castor.util.LocalConfiguration.getInstance().getProperties().setProperty(
83  * "org.exolab.castor.serializer", "org.apache.xml.serialize.XMLSerilazizer"); }
84  *
85  */
86 /*
87  * TODO: check/verify consistency for vamsas sync with group associated alignment annotation
88  */
89 public class VamsasAppDatastore
90 {
91   /**
92    * Type used for general jalview generated annotation added to vamsas document
93    */
94   public static final String JALVIEW_ANNOTATION_ROW = "JalviewAnnotation";
95
96   /**
97    * AlignmentAnnotation property to indicate that values should not be
98    * interpolated
99    */
100   public static final String DISCRETE_ANNOTATION = "discrete";
101
102   /**
103    * continuous property - optional to specify that annotation should be
104    * represented as a continous graph line
105    */
106   private static final String CONTINUOUS_ANNOTATION = "continuous";
107
108   private static final String THRESHOLD = "threshold";
109
110   /**
111    * template for provenance entries written to vamsas session document
112    */
113   Entry provEntry = null;
114
115   /**
116    * Instance of the session document being synchronized with
117    */
118   IClientDocument cdoc;
119
120   /**
121    * map Vorba (vamsas object xml ref) IDs to live jalview object references
122    */
123   Hashtable vobj2jv;
124
125   /**
126    * map live jalview object references to Vorba IDs
127    */
128   IdentityHashMap jv2vobj;
129
130   /**
131    * map jalview sequence set ID (which is vorba ID for alignment) to last
132    * recorded hash value for the alignment viewport (the undo/redo hash value)
133    */
134   Hashtable alignRDHash;
135
136   public VamsasAppDatastore(IClientDocument cdoc, Hashtable vobj2jv,
137           IdentityHashMap jv2vobj, Entry provEntry, Hashtable alignRDHash)
138   {
139     this.cdoc = cdoc;
140     this.vobj2jv = vobj2jv;
141     this.jv2vobj = jv2vobj;
142     this.provEntry = provEntry;
143     this.alignRDHash = alignRDHash;
144     buildSkipList();
145   }
146
147   /**
148    * the skipList used to skip over views from Jalview Appdata's that we've
149    * already syncrhonized
150    */
151   Hashtable skipList;
152
153   private void buildSkipList()
154   {
155     skipList = new Hashtable();
156     AlignFrame[] al = Desktop.getAlignFrames();
157     for (int f = 0; al != null && f < al.length; f++)
158     {
159       skipList.put(al[f].getViewport().getSequenceSetId(), al[f]);
160     }
161   }
162
163   /**
164    * @return the Vobject bound to Jalview datamodel object
165    */
166   protected Vobject getjv2vObj(Object jvobj)
167   {
168     if (jv2vobj.containsKey(jvobj))
169     {
170       return cdoc.getObject((VorbaId) jv2vobj.get(jvobj));
171     }
172     // check if we're working with a string - then workaround
173     // the use of IdentityHashTable because different strings
174     // have different object IDs.
175     if (jvobj instanceof String)
176     {
177       Object seqsetidobj = null;
178       seqsetidobj = getVamsasObjectBinding().get(jvobj);
179       if (seqsetidobj != null)
180       {
181         if (seqsetidobj instanceof String)
182         {
183           // what is expected. object returned by av.getSequenceSetId() -
184           // reverse lookup to get the 'registered' instance of this string
185           Vobject obj = getjv2vObj(seqsetidobj);
186           if (obj != null && !(obj instanceof Alignment))
187           {
188             Cache.log.warn(
189                     "IMPLEMENTATION ERROR?: Unexpected mapping for unmapped jalview string object content:"
190                             + seqsetidobj + " to object " + obj);
191           }
192           return obj;
193         }
194         else
195         {
196           Cache.log.warn("Unexpected mapping for Jalview String Object ID "
197                   + seqsetidobj + " to another jalview dataset object "
198                   + seqsetidobj);
199         }
200       }
201     }
202
203     if (Cache.log.isDebugEnabled())
204     {
205       Cache.log.debug(
206               "Returning null VorbaID binding for jalview object " + jvobj);
207     }
208     return null;
209   }
210
211   /**
212    * 
213    * @param vobj
214    * @return Jalview datamodel object bound to the vamsas document object
215    */
216   protected Object getvObj2jv(uk.ac.vamsas.client.Vobject vobj)
217   {
218     VorbaId id = vobj.getVorbaId();
219     if (id == null)
220     {
221       id = cdoc.registerObject(vobj);
222       Cache.log.debug(
223               "Registering new object and returning null for getvObj2jv");
224       return null;
225     }
226     if (vobj2jv.containsKey(vobj.getVorbaId()))
227     {
228       return vobj2jv.get(vobj.getVorbaId());
229     }
230     return null;
231   }
232
233   protected void bindjvvobj(Object jvobj, uk.ac.vamsas.client.Vobject vobj)
234   {
235     VorbaId id = vobj.getVorbaId();
236     if (id == null)
237     {
238       id = cdoc.registerObject(vobj);
239       if (id == null || vobj.getVorbaId() == null
240               || cdoc.getObject(id) != vobj)
241       {
242         Cache.log.error("Failed to get id for "
243                 + (vobj.isRegisterable() ? "registerable"
244                         : "unregisterable")
245                 + " object " + vobj);
246       }
247     }
248
249     if (vobj2jv.containsKey(vobj.getVorbaId())
250             && !((VorbaId) vobj2jv.get(vobj.getVorbaId())).equals(jvobj))
251     {
252       Cache.log.debug(
253               "Warning? Overwriting existing vamsas id binding for "
254                       + vobj.getVorbaId(),
255               new Exception(MessageManager.getString(
256                       "exception.overwriting_vamsas_id_binding")));
257     }
258     else if (jv2vobj.containsKey(jvobj)
259             && !((VorbaId) jv2vobj.get(jvobj)).equals(vobj.getVorbaId()))
260     {
261       Cache.log.debug(
262               "Warning? Overwriting existing jalview object binding for "
263                       + jvobj,
264               new Exception("Overwriting jalview object binding."));
265     }
266     /*
267      * Cache.log.error("Attempt to make conflicting object binding! "+vobj+" id "
268      * +vobj.getVorbaId()+" already bound to "+getvObj2jv(vobj)+" and "+jvobj+"
269      * already bound to "+getjv2vObj(jvobj),new Exception("Excessive call to
270      * bindjvvobj")); }
271      */
272     // we just update the hash's regardless!
273     Cache.log.debug("Binding " + vobj.getVorbaId() + " to " + jvobj);
274     vobj2jv.put(vobj.getVorbaId(), jvobj);
275     // JBPNote - better implementing a hybrid invertible hash.
276     jv2vobj.put(jvobj, vobj.getVorbaId());
277   }
278
279   /**
280    * put the alignment viewed by AlignViewport into cdoc.
281    * 
282    * @param av
283    *          alignViewport to be stored
284    * @param aFtitle
285    *          title for alignment
286    * @return true if alignment associated with viewport was stored/synchronized
287    *         to document
288    */
289   public boolean storeVAMSAS(AlignViewport av, String aFtitle)
290   {
291     try
292     {
293       jalview.datamodel.AlignmentI jal = av.getAlignment();
294       jalview.datamodel.AlignmentI jds = jal.getDataset();
295       boolean nw = false;
296       VAMSAS root = null; // will be resolved based on Dataset Parent.
297       // /////////////////////////////////////////
298       // SAVE THE DATASET
299       DataSet dataset = null;
300       if (jds == null)
301       {
302         Cache.log.warn("Creating new dataset for an alignment.");
303         jal.setDataset(null);
304         jds = jal.getDataset();
305       }
306
307       // try and get alignment and association for sequence set id
308
309       Alignment alignment = (Alignment) getjv2vObj(av.getSequenceSetId());
310       if (alignment != null)
311       {
312         dataset = (DataSet) alignment.getV_parent();
313       }
314       else
315       {
316         // is the dataset already registered
317         dataset = (DataSet) getjv2vObj(jds);
318       }
319
320       if (dataset == null)
321       {
322         // it might be that one of the dataset sequences does actually have a
323         // binding, so search for it indirectly. If it does, then the local
324         // jalview dataset
325         // must be merged with the existing vamsas dataset.
326         jalview.datamodel.SequenceI[] jdatset = jds.getSequencesArray();
327         for (int i = 0; i < jdatset.length; i++)
328         {
329           Vobject vbound = getjv2vObj(jdatset[i]);
330           if (vbound != null)
331           {
332             if (vbound instanceof uk.ac.vamsas.objects.core.Sequence)
333             {
334               if (dataset == null)
335               {
336                 dataset = (DataSet) vbound.getV_parent();
337               }
338               else
339               {
340                 if (vbound.getV_parent() != null
341                         && dataset != vbound.getV_parent())
342                 {
343                   throw new Error(
344                           "IMPLEMENTATION ERROR: Cannot map an alignment of sequences from different datasets into a single alignment in the vamsas document.");
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             jarInputStreamProvider jprovider = new 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               @Override
1343               public File getFile()
1344               {
1345                 return null;
1346               }
1347             };
1348             if (dojvsync)
1349             {
1350               fromxml.loadJalviewAlign(jprovider);
1351             }
1352           } catch (Exception e)
1353           {
1354
1355           } catch (OutOfMemoryError e)
1356           {
1357
1358           } catch (Error e)
1359           {
1360
1361           }
1362         }
1363       }
1364       if (cappdata.hasUserAppdata())
1365       {
1366         // TODO: how to check version of Jalview user app data and whether it
1367         // has been modified
1368         // user data overrides data shared over all app clients ?
1369         try
1370         {
1371           Jalview2XML fromxml = new Jalview2XML();
1372           fromxml.attemptversion1parse = false;
1373           fromxml.setUniqueSetSuffix("");
1374           fromxml.setSkipList(skipList);
1375           fromxml.setObjectMappingTables(mapKeysToString(vobj2jv),
1376                   mapValuesToString(jv2vobj));
1377           jarInputStreamProvider jarstream = new jarInputStreamProvider()
1378           {
1379
1380             @Override
1381             public String getFilename()
1382             {
1383
1384               // TODO Get the vamsas session ID here
1385               return "Jalview Vamsas Document User Data";
1386             }
1387
1388             @Override
1389             public JarInputStream getJarInputStream() throws IOException
1390             {
1391               jalview.bin.Cache.log.debug(
1392                       "Returning user input stream for Jalview from Vamsas Document.");
1393               return new JarInputStream(cappdata.getUserInputStream());
1394             }
1395
1396             @Override
1397             public File getFile()
1398             {
1399               return null;
1400             }
1401           };
1402           if (dojvsync)
1403           {
1404             fromxml.loadJalviewAlign(jarstream);
1405           }
1406         } catch (Exception e)
1407         {
1408
1409         } catch (OutOfMemoryError e)
1410         {
1411
1412         } catch (Error e)
1413         {
1414
1415         }
1416       }
1417
1418     }
1419     flushAlignViewports();
1420   }
1421
1422   /**
1423    * remove any spurious views generated by document synchronization
1424    */
1425   private void flushAlignViewports()
1426   {
1427     // remove any additional viewports originally recovered from the vamsas
1428     // document.
1429     // search for all alignframes containing viewports generated from document
1430     // sync,
1431     // and if any contain more than one view, then remove the one generated by
1432     // document update.
1433     AlignmentViewport views[], av = null;
1434     AlignFrame af = null;
1435     Iterator newviews = newAlignmentViews.iterator();
1436     while (newviews.hasNext())
1437     {
1438       av = (AlignmentViewport) newviews.next();
1439       af = Desktop.getAlignFrameFor(av);
1440       // TODO implement this : af.getNumberOfViews
1441       String seqsetidobj = av.getSequenceSetId();
1442       views = Desktop.getViewports(seqsetidobj);
1443       Cache.log
1444               .debug("Found " + (views == null ? " no " : "" + views.length)
1445                       + " views for '" + av.getSequenceSetId() + "'");
1446       if (views.length > 1)
1447       {
1448         // we need to close the original document view.
1449
1450         // work out how to do this by seeing if the views are gathered.
1451         // pretty clunky but the only way to do this without adding more flags
1452         // to the align frames.
1453         boolean gathered = false;
1454         String newviewid = null;
1455         List<AlignedCodonFrame> mappings = av.getAlignment()
1456                 .getCodonFrames();
1457         for (int i = 0; i < views.length; i++)
1458         {
1459           if (views[i] != av)
1460           {
1461             AlignFrame viewframe = Desktop.getAlignFrameFor(views[i]);
1462             if (viewframe == af)
1463             {
1464               gathered = true;
1465             }
1466             newviewid = views[i].getSequenceSetId();
1467           }
1468           else
1469           {
1470             // lose the reference to the vamsas document created view
1471             views[i] = null;
1472           }
1473         }
1474         // close the view generated by the vamsas document synchronization
1475         if (gathered)
1476         {
1477           af.closeView(av);
1478         }
1479         else
1480         {
1481           af.closeMenuItem_actionPerformed(false);
1482         }
1483         replaceJvObjMapping(seqsetidobj, newviewid);
1484         seqsetidobj = newviewid;
1485         // not sure if we need to do this:
1486
1487         if (false) // mappings != null)
1488         {
1489           // ensure sequence mappings from vamsas document view still
1490           // active
1491           if (mappings != null)
1492           {
1493             jalview.structure.StructureSelectionManager
1494                     .getStructureSelectionManager(Desktop.getInstance())
1495                     .registerMappings(mappings);
1496           }
1497         }
1498       }
1499       // ensure vamsas object binds to the stored views retrieved from
1500       // Jalview appdata
1501       // jalview.structure.StructureSelectionManager
1502       // .getStructureSelectionManager()
1503       // .addStructureViewerListener(viewframe.alignPanel);
1504
1505     }
1506
1507     newviews = null;
1508     newAlignmentViews.clear();
1509   }
1510
1511   /**
1512    * replaces oldjvobject with newjvobject in the Jalview Object <> VorbaID
1513    * binding tables
1514    * 
1515    * @param oldjvobject
1516    * @param newjvobject
1517    *          (may be null)
1518    */
1519   private void replaceJvObjMapping(Object oldjvobject, Object newjvobject)
1520   {
1521     Object vobject = jv2vobj.remove(oldjvobject);
1522     if (vobject == null)
1523     {
1524       // NOTE: this happens if user deletes object in one session then updates
1525       // from another client
1526       throw new Error(MessageManager.formatMessage(
1527               "error.implementation_error_old_jalview_object_not_bound",
1528               new String[]
1529               { oldjvobject.toString() }));
1530     }
1531     if (newjvobject != null)
1532     {
1533       jv2vobj.put(newjvobject, vobject);
1534       vobj2jv.put(vobject, newjvobject);
1535     }
1536   }
1537
1538   /**
1539    * Update the jalview client and user appdata from the local jalview settings
1540    */
1541   public void updateJalviewClientAppdata()
1542   {
1543     final IClientAppdata cappdata = cdoc.getClientAppdata();
1544     if (cappdata != null)
1545     {
1546       try
1547       {
1548         Jalview2XML jxml = new Jalview2XML();
1549         jxml.setObjectMappingTables(mapKeysToString(vobj2jv),
1550                 mapValuesToString(jv2vobj));
1551         jxml.setSkipList(skipList);
1552         if (dojvsync)
1553         {
1554           jxml.saveState(
1555                   new JarOutputStream(cappdata.getClientOutputStream()));
1556         }
1557
1558       } catch (Exception e)
1559       {
1560         // TODO raise GUI warning if user requests it.
1561         jalview.bin.Cache.log.error(
1562                 "Couldn't update jalview client application data. Giving up - local settings probably lost.",
1563                 e);
1564       }
1565     }
1566     else
1567     {
1568       jalview.bin.Cache.log.error(
1569               "Couldn't access client application data for vamsas session. This is probably a vamsas client bug.");
1570     }
1571   }
1572
1573   /**
1574    * translate the Vobject keys to strings for use in Jalview2XML
1575    * 
1576    * @param jv2vobj2
1577    * @return
1578    */
1579   private IdentityHashMap mapValuesToString(IdentityHashMap jv2vobj2)
1580   {
1581     IdentityHashMap mapped = new IdentityHashMap();
1582     Iterator keys = jv2vobj2.keySet().iterator();
1583     while (keys.hasNext())
1584     {
1585       Object key = keys.next();
1586       mapped.put(key, jv2vobj2.get(key).toString());
1587     }
1588     return mapped;
1589   }
1590
1591   /**
1592    * translate the Vobject values to strings for use in Jalview2XML
1593    * 
1594    * @param vobj2jv2
1595    * @return hashtable with string values
1596    */
1597   private Hashtable mapKeysToString(Hashtable vobj2jv2)
1598   {
1599     Hashtable mapped = new Hashtable();
1600     Iterator keys = vobj2jv2.keySet().iterator();
1601     while (keys.hasNext())
1602     {
1603       Object key = keys.next();
1604       mapped.put(key.toString(), vobj2jv2.get(key));
1605     }
1606     return mapped;
1607   }
1608
1609   /**
1610    * synchronize Jalview from the vamsas document
1611    * 
1612    * @return number of new views from document
1613    */
1614   public int updateToJalview()
1615   {
1616     VAMSAS _roots[] = cdoc.getVamsasRoots();
1617
1618     for (int _root = 0; _root < _roots.length; _root++)
1619     {
1620       VAMSAS root = _roots[_root];
1621       boolean newds = false;
1622       for (int _ds = 0, _nds = root.getDataSetCount(); _ds < _nds; _ds++)
1623       {
1624         // ///////////////////////////////////
1625         // ///LOAD DATASET
1626         DataSet dataset = root.getDataSet(_ds);
1627         int i, iSize = dataset.getSequenceCount();
1628         List<SequenceI> dsseqs;
1629         jalview.datamodel.Alignment jdataset = (jalview.datamodel.Alignment) getvObj2jv(
1630                 dataset);
1631         int jremain = 0;
1632         if (jdataset == null)
1633         {
1634           Cache.log.debug("Initialising new jalview dataset fields");
1635           newds = true;
1636           dsseqs = new Vector();
1637         }
1638         else
1639         {
1640           Cache.log.debug("Update jalview dataset from vamsas.");
1641           jremain = jdataset.getHeight();
1642           dsseqs = jdataset.getSequences();
1643         }
1644
1645         // TODO: test sequence merging - we preserve existing non vamsas
1646         // sequences but add in any new vamsas ones, and don't yet update any
1647         // sequence attributes
1648         for (i = 0; i < iSize; i++)
1649         {
1650           Sequence vdseq = dataset.getSequence(i);
1651           jalview.io.vamsas.Datasetsequence dssync = new Datasetsequence(
1652                   this, vdseq);
1653
1654           jalview.datamodel.SequenceI dsseq = (SequenceI) dssync.getJvobj();
1655           if (dssync.isAddfromdoc())
1656           {
1657             dsseqs.add(dsseq);
1658           }
1659           if (vdseq.getDbRefCount() > 0)
1660           {
1661             DbRef[] dbref = vdseq.getDbRef();
1662             for (int db = 0; db < dbref.length; db++)
1663             {
1664               new jalview.io.vamsas.Dbref(this, dbref[db], vdseq, dsseq);
1665
1666             }
1667             dsseq.updatePDBIds();
1668           }
1669         }
1670
1671         if (newds)
1672         {
1673           SequenceI[] seqs = new SequenceI[dsseqs.size()];
1674           for (i = 0, iSize = dsseqs.size(); i < iSize; i++)
1675           {
1676             seqs[i] = dsseqs.get(i);
1677             dsseqs.set(i, null);
1678           }
1679           jdataset = new jalview.datamodel.Alignment(seqs);
1680           Cache.log.debug("New vamsas dataset imported into jalview.");
1681           bindjvvobj(jdataset, dataset);
1682         }
1683         // ////////
1684         // add any new dataset sequence feature annotations
1685         if (dataset.getDataSetAnnotations() != null)
1686         {
1687           for (int dsa = 0; dsa < dataset
1688                   .getDataSetAnnotationsCount(); dsa++)
1689           {
1690             DataSetAnnotations dseta = dataset.getDataSetAnnotations(dsa);
1691             // TODO: deal with group annotation on datset sequences.
1692             if (dseta.getSeqRefCount() == 1)
1693             {
1694               SequenceI dsSeq = (SequenceI) getvObj2jv(
1695                       (Vobject) dseta.getSeqRef(0)); // TODO: deal with group
1696                                                      // dataset
1697               // annotations
1698               if (dsSeq == null)
1699               {
1700                 jalview.bin.Cache.log.warn(
1701                         "Couldn't resolve jalview sequenceI for dataset object reference "
1702                                 + ((Vobject) dataset
1703                                         .getDataSetAnnotations(dsa)
1704                                         .getSeqRef(0)).getVorbaId()
1705                                                 .getId());
1706               }
1707               else
1708               {
1709                 if (dseta.getAnnotationElementCount() == 0)
1710                 {
1711                   new jalview.io.vamsas.Sequencefeature(this, dseta, dsSeq);
1712
1713                 }
1714                 else
1715                 {
1716                   // TODO: deal with alignmentAnnotation style annotation
1717                   // appearing on dataset sequences.
1718                   // JBPNote: we could just add them to all alignments but
1719                   // that may complicate cross references in the jalview
1720                   // datamodel
1721                   Cache.log.warn(
1722                           "Ignoring dataset annotation with annotationElements. Not yet supported in jalview.");
1723                 }
1724               }
1725             }
1726             else
1727             {
1728               Cache.log.warn(
1729                       "Ignoring multiply referenced dataset sequence annotation for binding to datsaet sequence features.");
1730             }
1731           }
1732         }
1733         if (dataset.getAlignmentCount() > 0)
1734         {
1735           // LOAD ALIGNMENTS from DATASET
1736
1737           for (int al = 0, nal = dataset
1738                   .getAlignmentCount(); al < nal; al++)
1739           {
1740             uk.ac.vamsas.objects.core.Alignment alignment = dataset
1741                     .getAlignment(al);
1742             // TODO check this handles multiple views properly
1743             AlignmentViewport av = findViewport(alignment);
1744
1745             jalview.datamodel.AlignmentI jal = null;
1746             if (av != null)
1747             {
1748               // TODO check that correct alignment object is retrieved when
1749               // hidden seqs exist.
1750               jal = (av.hasHiddenRows()) ? av.getAlignment()
1751                       .getHiddenSequences().getFullAlignment()
1752                       : av.getAlignment();
1753             }
1754             iSize = alignment.getAlignmentSequenceCount();
1755             boolean refreshal = false;
1756             Vector newasAnnots = new Vector();
1757             char gapChar = ' '; // default for new alignments read in from the
1758             // document
1759             if (jal != null)
1760             {
1761               dsseqs = jal.getSequences(); // for merge/update
1762               gapChar = jal.getGapCharacter();
1763             }
1764             else
1765             {
1766               dsseqs = new Vector();
1767             }
1768             char valGapchar = alignment.getGapChar().charAt(0);
1769             for (i = 0; i < iSize; i++)
1770             {
1771               AlignmentSequence valseq = alignment.getAlignmentSequence(i);
1772               jalview.datamodel.Sequence alseq = (jalview.datamodel.Sequence) getvObj2jv(
1773                       valseq);
1774               if (syncFromAlignmentSequence(valseq, valGapchar, gapChar,
1775                       dsseqs) && alseq != null)
1776               {
1777
1778                 // updated to sequence from the document
1779                 jremain--;
1780                 refreshal = true;
1781               }
1782               if (valseq.getAlignmentSequenceAnnotationCount() > 0)
1783               {
1784                 AlignmentSequenceAnnotation[] vasannot = valseq
1785                         .getAlignmentSequenceAnnotation();
1786                 for (int a = 0; a < vasannot.length; a++)
1787                 {
1788                   jalview.datamodel.AlignmentAnnotation asa = (jalview.datamodel.AlignmentAnnotation) getvObj2jv(
1789                           vasannot[a]); // TODO:
1790                   // 1:many
1791                   // jalview
1792                   // alignment
1793                   // sequence
1794                   // annotations
1795                   if (asa == null)
1796                   {
1797                     int se[] = getBounds(vasannot[a]);
1798                     asa = getjAlignmentAnnotation(jal, vasannot[a]);
1799                     asa.setSequenceRef(alseq);
1800                     asa.createSequenceMapping(alseq, se[0], false); // TODO:
1801                     // verify
1802                     // that
1803                     // positions
1804                     // in
1805                     // alseqAnnotation
1806                     // correspond
1807                     // to
1808                     // ungapped
1809                     // residue
1810                     // positions.
1811                     alseq.addAlignmentAnnotation(asa);
1812                     bindjvvobj(asa, vasannot[a]);
1813                     refreshal = true;
1814                     newasAnnots.add(asa);
1815                   }
1816                   else
1817                   {
1818                     // update existing annotation - can do this in place
1819                     if (vasannot[a].getModifiable() == null) // TODO: USE
1820                     // VAMSAS LIBRARY
1821                     // OBJECT LOCK
1822                     // METHODS)
1823                     {
1824                       Cache.log.info(
1825                               "UNIMPLEMENTED: not recovering user modifiable sequence alignment annotation");
1826                       // TODO: should at least replace with new one - otherwise
1827                       // things will break
1828                       // basically do this:
1829                       // int se[] = getBounds(vasannot[a]);
1830                       // asa.update(getjAlignmentAnnotation(jal, vasannot[a]));
1831                       // // update from another annotation object in place.
1832                       // asa.createSequenceMapping(alseq, se[0], false);
1833
1834                     }
1835                   }
1836                 }
1837               }
1838             }
1839             if (jal == null)
1840             {
1841               SequenceI[] seqs = new SequenceI[dsseqs.size()];
1842               for (i = 0, iSize = dsseqs.size(); i < iSize; i++)
1843               {
1844                 seqs[i] = dsseqs.get(i);
1845                 dsseqs.set(i, null);
1846               }
1847               jal = new jalview.datamodel.Alignment(seqs);
1848               Cache.log.debug("New vamsas alignment imported into jalview "
1849                       + alignment.getVorbaId().getId());
1850               jal.setDataset(jdataset);
1851             }
1852             if (newasAnnots != null && newasAnnots.size() > 0)
1853             {
1854               // Add the new sequence annotations in to the alignment.
1855               for (int an = 0, anSize = newasAnnots
1856                       .size(); an < anSize; an++)
1857               {
1858                 jal.addAnnotation(
1859                         (AlignmentAnnotation) newasAnnots.elementAt(an));
1860                 // TODO: check if anything has to be done - like calling
1861                 // adjustForAlignment or something.
1862                 newasAnnots.setElementAt(null, an);
1863               }
1864               newasAnnots = null;
1865             }
1866             // //////////////////////////////////////////
1867             // //LOAD ANNOTATIONS FOR THE ALIGNMENT
1868             // ////////////////////////////////////
1869             if (alignment.getAlignmentAnnotationCount() > 0)
1870             {
1871               uk.ac.vamsas.objects.core.AlignmentAnnotation[] an = alignment
1872                       .getAlignmentAnnotation();
1873
1874               for (int j = 0; j < an.length; j++)
1875               {
1876                 jalview.datamodel.AlignmentAnnotation jan = (jalview.datamodel.AlignmentAnnotation) getvObj2jv(
1877                         an[j]);
1878                 if (jan != null)
1879                 {
1880                   // update or stay the same.
1881                   // TODO: should at least replace with a new one - otherwise
1882                   // things will break
1883                   // basically do this:
1884                   // jan.update(getjAlignmentAnnotation(jal, an[a])); // update
1885                   // from another annotation object in place.
1886
1887                   Cache.log.debug(
1888                           "update from vamsas alignment annotation to existing jalview alignment annotation.");
1889                   if (an[j].getModifiable() == null) // TODO: USE VAMSAS
1890                   // LIBRARY OBJECT LOCK
1891                   // METHODS)
1892                   {
1893                     // TODO: user defined annotation is totally mutable... - so
1894                     // load it up or throw away if locally edited.
1895                     Cache.log.info(
1896                             "NOT IMPLEMENTED - Recovering user-modifiable annotation - yet...");
1897                   }
1898                   // TODO: compare annotation element rows
1899                   // TODO: compare props.
1900                 }
1901                 else
1902                 {
1903                   jan = getjAlignmentAnnotation(jal, an[j]);
1904                   // TODO: ensure we add the alignment annotation before the
1905                   // automatic annotation rows
1906                   jal.addAnnotation(jan);
1907                   bindjvvobj(jan, an[j]);
1908                   refreshal = true;
1909                 }
1910               }
1911             }
1912             AlignFrame alignFrame;
1913             if (av == null)
1914             {
1915               Cache.log.debug("New alignframe for alignment "
1916                       + alignment.getVorbaId());
1917               // ///////////////////////////////
1918               // construct alignment view
1919               alignFrame = new AlignFrame(jal, AlignFrame.DEFAULT_WIDTH,
1920                       AlignFrame.DEFAULT_HEIGHT,
1921                       alignment.getVorbaId().toString());
1922               av = alignFrame.getViewport();
1923               newAlignmentViews.addElement(av);
1924               String title = alignment.getProvenance()
1925                       .getEntry(
1926                               alignment.getProvenance().getEntryCount() - 1)
1927                       .getAction();
1928               if (alignment.getPropertyCount() > 0)
1929               {
1930                 for (int p = 0, pe = alignment
1931                         .getPropertyCount(); p < pe; p++)
1932                 {
1933                   if (alignment.getProperty(p).getName().equals("title"))
1934                   {
1935                     title = alignment.getProperty(p).getContent();
1936                   }
1937                 }
1938               }
1939               // TODO: automatically create meaningful title for a vamsas
1940               // alignment using its provenance.
1941               if (Cache.log.isDebugEnabled())
1942               {
1943                 title = title + "(" + alignment.getVorbaId() + ")";
1944
1945               }
1946               jalview.gui.Desktop.addInternalFrame(alignFrame, title,
1947                       AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
1948               bindjvvobj(av.getSequenceSetId(), alignment);
1949             }
1950             else
1951             {
1952               // find the alignFrame for jal.
1953               // TODO: fix this so we retrieve the alignFrame handing av
1954               // *directly* (JBPNote - don't understand this now)
1955               // TODO: make sure all associated views are refreshed
1956               alignFrame = Desktop.getAlignFrameFor(av);
1957               if (refreshal)
1958               {
1959                 av.alignmentChanged(alignFrame.alignPanel);
1960                 alignFrame.alignPanel.adjustAnnotationHeight();
1961               }
1962             }
1963             // LOAD TREES
1964             // /////////////////////////////////////
1965             if (alignment.getTreeCount() > 0)
1966             {
1967
1968               for (int t = 0; t < alignment.getTreeCount(); t++)
1969               {
1970                 jalview.io.vamsas.Tree vstree = new jalview.io.vamsas.Tree(
1971                         this, alignFrame, alignment.getTree(t));
1972                 TreePanel tp = null;
1973                 if (vstree.isValidTree())
1974                 {
1975                   tp = alignFrame.showNewickTree(vstree.getNewickTree(),
1976                           vstree.getTitle(), vstree.getInputData(), 600,
1977                           500, t * 20 + 50, t * 20 + 50);
1978
1979                 }
1980                 if (tp != null)
1981                 {
1982                   bindjvvobj(tp, alignment.getTree(t));
1983                   try
1984                   {
1985                     vstree.UpdateSequenceTreeMap(tp);
1986                   } catch (RuntimeException e)
1987                   {
1988                     Cache.log.warn("update of labels failed.", e);
1989                   }
1990                 }
1991                 else
1992                 {
1993                   Cache.log.warn("Cannot create tree for tree " + t
1994                           + " in document ("
1995                           + alignment.getTree(t).getVorbaId());
1996                 }
1997
1998               }
1999             }
2000           }
2001         }
2002       }
2003       // we do sequenceMappings last because they span all datasets in a vamsas
2004       // root
2005       for (int _ds = 0, _nds = root.getDataSetCount(); _ds < _nds; _ds++)
2006       {
2007         DataSet dataset = root.getDataSet(_ds);
2008         if (dataset.getSequenceMappingCount() > 0)
2009         {
2010           for (int sm = 0, smCount = dataset
2011                   .getSequenceMappingCount(); sm < smCount; sm++)
2012           {
2013             Rangetype seqmap = new jalview.io.vamsas.Sequencemapping(this,
2014                     dataset.getSequenceMapping(sm));
2015           }
2016         }
2017       }
2018     }
2019     return newAlignmentViews.size();
2020   }
2021
2022   public AlignmentViewport findViewport(Alignment alignment)
2023   {
2024     AlignmentViewport av = null;
2025     AlignmentViewport[] avs = Desktop
2026             .getViewports((String) getvObj2jv(alignment));
2027     if (avs != null)
2028     {
2029       av = avs[0];
2030     }
2031     return av;
2032   }
2033
2034   // bitfields - should be a template in j1.5
2035   private static int HASSECSTR = 0;
2036
2037   private static int HASVALS = 1;
2038
2039   private static int HASHPHOB = 2;
2040
2041   private static int HASDC = 3;
2042
2043   private static int HASDESCSTR = 4;
2044
2045   private static int HASTWOSTATE = 5; // not used yet.
2046
2047   /**
2048    * parses the AnnotationElements - if they exist - into
2049    * jalview.datamodel.Annotation[] rows Two annotation rows are made if there
2050    * are distinct annotation for both at 'pos' and 'after pos' at any particular
2051    * site.
2052    * 
2053    * @param annotation
2054    * @return { boolean[static int constants ], int[ae.length] - map to annotated
2055    *         object frame, jalview.datamodel.Annotation[],
2056    *         jalview.datamodel.Annotation[] (after)}
2057    */
2058   private Object[] parseRangeAnnotation(
2059           uk.ac.vamsas.objects.core.RangeAnnotation annotation)
2060   {
2061     // set these attributes by looking in the annotation to decide what kind of
2062     // alignment annotation rows will be made
2063     // TODO: potentially we might make several annotation rows from one vamsas
2064     // alignment annotation. the jv2Vobj binding mechanism
2065     // may not quite cope with this (without binding an array of annotations to
2066     // a vamsas alignment annotation)
2067     // summary flags saying what we found over the set of annotation rows.
2068     boolean[] AeContent = new boolean[] { false, false, false, false,
2069         false };
2070     int[] rangeMap = getMapping(annotation);
2071     jalview.datamodel.Annotation[][] anot = new jalview.datamodel.Annotation[][] {
2072         new jalview.datamodel.Annotation[rangeMap.length],
2073         new jalview.datamodel.Annotation[rangeMap.length] };
2074     boolean mergeable = true; // false if 'after positions cant be placed on
2075     // same annotation row as positions.
2076
2077     if (annotation.getAnnotationElementCount() > 0)
2078     {
2079       AnnotationElement ae[] = annotation.getAnnotationElement();
2080       for (int aa = 0; aa < ae.length; aa++)
2081       {
2082         int pos = (int) ae[aa].getPosition() - 1; // pos counts from 1 to
2083         // (|seg.start-seg.end|+1)
2084         if (pos >= 0 && pos < rangeMap.length)
2085         {
2086           int row = ae[aa].getAfter() ? 1 : 0;
2087           if (anot[row][pos] != null)
2088           {
2089             // only time this should happen is if the After flag is set.
2090             Cache.log.debug("Ignoring duplicate annotation site at " + pos);
2091             continue;
2092           }
2093           if (anot[1 - row][pos] != null)
2094           {
2095             mergeable = false;
2096           }
2097           String desc = "";
2098           if (ae[aa].getDescription() != null)
2099           {
2100             desc = ae[aa].getDescription();
2101             if (desc.length() > 0)
2102             {
2103               // have imported valid description string
2104               AeContent[HASDESCSTR] = true;
2105             }
2106           }
2107           String dc = null; // ae[aa].getDisplayCharacter()==null ? "dc" :
2108           // ae[aa].getDisplayCharacter();
2109           String ss = null; // ae[aa].getSecondaryStructure()==null ? "ss" :
2110           // ae[aa].getSecondaryStructure();
2111           java.awt.Color colour = null;
2112           if (ae[aa].getGlyphCount() > 0)
2113           {
2114             Glyph[] glyphs = ae[aa].getGlyph();
2115             for (int g = 0; g < glyphs.length; g++)
2116             {
2117               if (glyphs[g].getDict().equals(
2118                       uk.ac.vamsas.objects.utils.GlyphDictionary.PROTEIN_SS_3STATE))
2119               {
2120                 ss = glyphs[g].getContent();
2121                 AeContent[HASSECSTR] = true;
2122               }
2123               else if (glyphs[g].getDict().equals(
2124                       uk.ac.vamsas.objects.utils.GlyphDictionary.PROTEIN_HD_HYDRO))
2125               {
2126                 Cache.log.debug("ignoring hydrophobicity glyph marker.");
2127                 AeContent[HASHPHOB] = true;
2128                 char c = (dc = glyphs[g].getContent()).charAt(0);
2129                 // dc may get overwritten - but we still set the colour.
2130                 colour = new java.awt.Color(c == '+' ? 255 : 0,
2131                         c == '.' ? 255 : 0, c == '-' ? 255 : 0);
2132
2133               }
2134               else if (glyphs[g].getDict().equals(
2135                       uk.ac.vamsas.objects.utils.GlyphDictionary.DEFAULT))
2136               {
2137                 dc = glyphs[g].getContent();
2138                 AeContent[HASDC] = true;
2139               }
2140               else
2141               {
2142                 Cache.log.debug(
2143                         "IMPLEMENTATION TODO: Ignoring unknown glyph type "
2144                                 + glyphs[g].getDict());
2145               }
2146             }
2147           }
2148           float val = 0;
2149           if (ae[aa].getValueCount() > 0)
2150           {
2151             AeContent[HASVALS] = true;
2152             if (ae[aa].getValueCount() > 1)
2153             {
2154               Cache.log.warn(
2155                       "ignoring additional " + (ae[aa].getValueCount() - 1)
2156                               + " values in annotation element.");
2157             }
2158             val = ae[aa].getValue(0);
2159           }
2160           if (colour == null)
2161           {
2162             anot[row][pos] = new jalview.datamodel.Annotation(
2163                     (dc != null) ? dc : "", desc,
2164                     (ss != null) ? ss.charAt(0) : ' ', val);
2165           }
2166           else
2167           {
2168             anot[row][pos] = new jalview.datamodel.Annotation(
2169                     (dc != null) ? dc : "", desc,
2170                     (ss != null) ? ss.charAt(0) : ' ', val, colour);
2171           }
2172         }
2173         else
2174         {
2175           Cache.log.warn("Ignoring out of bound annotation element " + aa
2176                   + " in " + annotation.getVorbaId().getId());
2177         }
2178       }
2179       // decide on how many annotation rows are needed.
2180       if (mergeable)
2181       {
2182         for (int i = 0; i < anot[0].length; i++)
2183         {
2184           if (anot[1][i] != null)
2185           {
2186             anot[0][i] = anot[1][i];
2187             anot[0][i].description = anot[0][i].description + " (after)";
2188             AeContent[HASDESCSTR] = true; // we have valid description string
2189             // data
2190             anot[1][i] = null;
2191           }
2192         }
2193         anot[1] = null;
2194       }
2195       else
2196       {
2197         for (int i = 0; i < anot[0].length; i++)
2198         {
2199           anot[1][i].description = anot[1][i].description + " (after)";
2200         }
2201       }
2202       return new Object[] { AeContent, rangeMap, anot[0], anot[1] };
2203     }
2204     else
2205     {
2206       // no annotations to parse. Just return an empty annotationElement[]
2207       // array.
2208       return new Object[] { AeContent, rangeMap, anot[0], anot[1] };
2209     }
2210     // return null;
2211   }
2212
2213   /**
2214    * @param jal
2215    *          the jalview alignment to which the annotation will be attached
2216    *          (ideally - freshly updated from corresponding vamsas alignment)
2217    * @param annotation
2218    * @return unbound jalview alignment annotation object.
2219    */
2220   private jalview.datamodel.AlignmentAnnotation getjAlignmentAnnotation(
2221           jalview.datamodel.AlignmentI jal,
2222           uk.ac.vamsas.objects.core.RangeAnnotation annotation)
2223   {
2224     if (annotation == null)
2225     {
2226       return null;
2227     }
2228     // boolean
2229     // hasSequenceRef=annotation.getClass().equals(uk.ac.vamsas.objects.core.AlignmentSequenceAnnotation.class);
2230     // boolean hasProvenance=hasSequenceRef ||
2231     // (annotation.getClass().equals(uk.ac.vamsas.objects.core.AlignmentAnnotation.class));
2232     /*
2233      * int se[] = getBounds(annotation); if (se==null) se=new int[]
2234      * {0,jal.getWidth()-1};
2235      */
2236     Object[] parsedRangeAnnotation = parseRangeAnnotation(annotation);
2237     String a_label = annotation.getLabel();
2238     String a_descr = annotation.getDescription();
2239     GraphLine gl = null;
2240     int type = 0;
2241     boolean interp = true; // cleared if annotation is DISCRETE
2242     // set type and other attributes from properties
2243     if (annotation.getPropertyCount() > 0)
2244     {
2245       // look for special jalview properties
2246       uk.ac.vamsas.objects.core.Property[] props = annotation.getProperty();
2247       for (int p = 0; p < props.length; p++)
2248       {
2249         if (props[p].getName().equalsIgnoreCase(DISCRETE_ANNOTATION))
2250         {
2251           type = AlignmentAnnotation.BAR_GRAPH;
2252           interp = false;
2253         }
2254         else if (props[p].getName().equalsIgnoreCase(CONTINUOUS_ANNOTATION))
2255         {
2256           type = AlignmentAnnotation.LINE_GRAPH;
2257         }
2258         else if (props[p].getName().equalsIgnoreCase(THRESHOLD))
2259         {
2260           Float val = null;
2261           try
2262           {
2263             val = Float.valueOf(props[p].getContent());
2264           } catch (Exception e)
2265           {
2266             Cache.log.warn("Failed to parse threshold property");
2267           }
2268           if (val != null)
2269           {
2270             if (gl == null)
2271             {
2272               gl = new GraphLine(val.floatValue(), "",
2273                       java.awt.Color.black);
2274             }
2275             else
2276             {
2277               gl.value = val.floatValue();
2278             }
2279           }
2280         }
2281         else if (props[p].getName().equalsIgnoreCase(THRESHOLD + "Name"))
2282         {
2283           if (gl == null)
2284           {
2285             gl = new GraphLine(0, "", java.awt.Color.black);
2286           }
2287           gl.label = props[p].getContent();
2288         }
2289       }
2290     }
2291     jalview.datamodel.AlignmentAnnotation jan = null;
2292     if (a_label == null || a_label.length() == 0)
2293     {
2294       a_label = annotation.getType();
2295       if (a_label.length() == 0)
2296       {
2297         a_label = "Unamed annotation";
2298       }
2299     }
2300     if (a_descr == null || a_descr.length() == 0)
2301     {
2302       a_descr = "Annotation of type '" + annotation.getType() + "'";
2303     }
2304     if (parsedRangeAnnotation == null)
2305     {
2306       Cache.log.debug(
2307               "Inserting empty annotation row elements for a whole-alignment annotation.");
2308     }
2309     else
2310     {
2311       if (parsedRangeAnnotation[3] != null)
2312       {
2313         Cache.log.warn("Ignoring 'After' annotation row in "
2314                 + annotation.getVorbaId());
2315       }
2316       jalview.datamodel.Annotation[] arow = (jalview.datamodel.Annotation[]) parsedRangeAnnotation[2];
2317       boolean[] has = (boolean[]) parsedRangeAnnotation[0];
2318       // VAMSAS: getGraph is only on derived annotation for alignments - in this
2319       // way its 'odd' - there is already an existing TODO about removing this
2320       // flag as being redundant
2321       /*
2322        * if((annotation.getClass().equals(uk.ac.vamsas.objects.core.
2323        * AlignmentAnnotation.class) &&
2324        * ((uk.ac.vamsas.objects.core.AlignmentAnnotation)annotation).getGraph())
2325        * || (hasSequenceRef=true &&
2326        * ((uk.ac.vamsas.objects.core.AlignmentSequenceAnnotation
2327        * )annotation).getGraph())) {
2328        */
2329       if (has[HASVALS])
2330       {
2331         if (type == 0)
2332         {
2333           type = jalview.datamodel.AlignmentAnnotation.BAR_GRAPH; // default
2334           // type of
2335           // value
2336           // annotation
2337           if (has[HASHPHOB])
2338           {
2339             // no hints - so we ensure HPHOB display is like this.
2340             type = jalview.datamodel.AlignmentAnnotation.BAR_GRAPH;
2341           }
2342         }
2343         // make bounds and automatic description strings for jalview user's
2344         // benefit (these shouldn't be written back to vamsas document)
2345         boolean first = true;
2346         float min = 0, max = 1;
2347         int lastval = 0;
2348         for (int i = 0; i < arow.length; i++)
2349         {
2350           if (arow[i] != null)
2351           {
2352             if (i - lastval > 1 && interp)
2353             {
2354               // do some interpolation *between* points
2355               if (arow[lastval] != null)
2356               {
2357                 float interval = arow[i].value - arow[lastval].value;
2358                 interval /= i - lastval;
2359                 float base = arow[lastval].value;
2360                 for (int ip = lastval + 1, np = 0; ip < i; np++, ip++)
2361                 {
2362                   arow[ip] = new jalview.datamodel.Annotation("", "", ' ',
2363                           interval * np + base);
2364                   // NB - Interpolated points don't get a tooltip and
2365                   // description.
2366                 }
2367               }
2368             }
2369             lastval = i;
2370             // check range - shouldn't we have a min and max property in the
2371             // annotation object ?
2372             if (first)
2373             {
2374               min = max = arow[i].value;
2375               first = false;
2376             }
2377             else
2378             {
2379               if (arow[i].value < min)
2380               {
2381                 min = arow[i].value;
2382               }
2383               else if (arow[i].value > max)
2384               {
2385                 max = arow[i].value;
2386               }
2387             }
2388             // make tooltip and display char value
2389             if (!has[HASDESCSTR])
2390             {
2391               arow[i].description = arow[i].value + "";
2392             }
2393             if (!has[HASDC])
2394             {
2395               if (!interp)
2396               {
2397                 if (arow[i].description != null
2398                         && arow[i].description.length() < 3)
2399                 {
2400                   // copy over the description as the display char.
2401                   arow[i].displayCharacter = new String(
2402                           arow[i].description);
2403                 }
2404               }
2405               else
2406               {
2407                 // mark the position as a point used for the interpolation.
2408                 arow[i].displayCharacter = arow[i].value + "";
2409               }
2410             }
2411           }
2412         }
2413         jan = new jalview.datamodel.AlignmentAnnotation(a_label, a_descr,
2414                 arow, min, max, type);
2415       }
2416       else
2417       {
2418         if (annotation.getAnnotationElementCount() == 0)
2419         {
2420           // empty annotation array
2421           // TODO: alignment 'features' compare rangeType spec to alignment
2422           // width - if it is not complete, then mark regions on the annotation
2423           // row.
2424         }
2425         jan = new jalview.datamodel.AlignmentAnnotation(a_label, a_descr,
2426                 arow);
2427         jan.setThreshold(null);
2428         jan.annotationId = annotation.getVorbaId().toString(); // keep all the
2429         // ids together.
2430       }
2431       if (annotation.getLinkCount() > 0)
2432       {
2433         Cache.log.warn("Ignoring " + annotation.getLinkCount()
2434                 + "links added to AlignmentAnnotation.");
2435       }
2436       if (annotation.getModifiable() == null
2437               || annotation.getModifiable().length() == 0) // TODO: USE VAMSAS
2438       // LIBRARY OBJECT
2439       // LOCK METHODS)
2440       {
2441         jan.editable = true;
2442       }
2443       try
2444       {
2445         if (annotation.getGroup() != null
2446                 && annotation.getGroup().length() > 0)
2447         {
2448           jan.graphGroup = Integer.parseInt(annotation.getGroup()); // TODO:
2449           // group
2450           // similarly
2451           // named
2452           // annotation
2453           // together
2454           // ?
2455         }
2456       } catch (Exception e)
2457       {
2458         Cache.log.info(
2459                 "UNIMPLEMENTED : Couldn't parse non-integer group value for setting graphGroup correctly.");
2460       }
2461       return jan;
2462
2463     }
2464
2465     return null;
2466   }
2467
2468   /**
2469    * get real bounds of a RangeType's specification. start and end are an
2470    * inclusive range within which all segments and positions lie. TODO: refactor
2471    * to vamsas utils
2472    * 
2473    * @param dseta
2474    * @return int[] { start, end}
2475    */
2476   private int[] getBounds(RangeType dseta)
2477   {
2478     if (dseta != null)
2479     {
2480       int[] se = null;
2481       if (dseta.getSegCount() > 0 && dseta.getPosCount() > 0)
2482       {
2483         throw new Error(MessageManager.getString(
2484                 "error.invalid_vamsas_rangetype_cannot_resolve_lists"));
2485       }
2486       if (dseta.getSegCount() > 0)
2487       {
2488         se = getSegRange(dseta.getSeg(0), true);
2489         for (int s = 1, sSize = dseta.getSegCount(); s < sSize; s++)
2490         {
2491           int nse[] = getSegRange(dseta.getSeg(s), true);
2492           if (se[0] > nse[0])
2493           {
2494             se[0] = nse[0];
2495           }
2496           if (se[1] < nse[1])
2497           {
2498             se[1] = nse[1];
2499           }
2500         }
2501       }
2502       if (dseta.getPosCount() > 0)
2503       {
2504         // could do a polarity for pos range too. and pass back indication of
2505         // discontinuities.
2506         int pos = dseta.getPos(0).getI();
2507         se = new int[] { pos, pos };
2508         for (int p = 0, pSize = dseta.getPosCount(); p < pSize; p++)
2509         {
2510           pos = dseta.getPos(p).getI();
2511           if (se[0] > pos)
2512           {
2513             se[0] = pos;
2514           }
2515           if (se[1] < pos)
2516           {
2517             se[1] = pos;
2518           }
2519         }
2520       }
2521       return se;
2522     }
2523     return null;
2524   }
2525
2526   /**
2527    * map from a rangeType's internal frame to the referenced object's coordinate
2528    * frame.
2529    * 
2530    * @param dseta
2531    * @return int [] { ref(pos)...} for all pos in rangeType's frame.
2532    */
2533   private int[] getMapping(RangeType dseta)
2534   {
2535     Vector posList = new Vector();
2536     if (dseta != null)
2537     {
2538       int[] se = null;
2539       if (dseta.getSegCount() > 0 && dseta.getPosCount() > 0)
2540       {
2541         throw new Error(MessageManager.getString(
2542                 "error.invalid_vamsas_rangetype_cannot_resolve_lists"));
2543       }
2544       if (dseta.getSegCount() > 0)
2545       {
2546         for (int s = 0, sSize = dseta.getSegCount(); s < sSize; s++)
2547         {
2548           se = getSegRange(dseta.getSeg(s), false);
2549           int se_end = se[1 - se[2]] + (se[2] == 0 ? 1 : -1);
2550           for (int p = se[se[2]]; p != se_end; p += se[2] == 0 ? 1 : -1)
2551           {
2552             posList.add(Integer.valueOf(p));
2553           }
2554         }
2555       }
2556       else if (dseta.getPosCount() > 0)
2557       {
2558         int pos = dseta.getPos(0).getI();
2559
2560         for (int p = 0, pSize = dseta.getPosCount(); p < pSize; p++)
2561         {
2562           pos = dseta.getPos(p).getI();
2563           posList.add(Integer.valueOf(pos));
2564         }
2565       }
2566     }
2567     if (posList != null && posList.size() > 0)
2568     {
2569       int[] range = new int[posList.size()];
2570       for (int i = 0; i < range.length; i++)
2571       {
2572         range[i] = ((Integer) posList.elementAt(i)).intValue();
2573       }
2574       posList.clear();
2575       return range;
2576     }
2577     return null;
2578   }
2579
2580   /**
2581    * 
2582    * @param maprange
2583    *          where the from range is the local mapped range, and the to range
2584    *          is the 'mapped' range in the MapRangeType
2585    * @param default
2586    *          unit for local
2587    * @param default
2588    *          unit for mapped
2589    * @return MapList
2590    */
2591   private jalview.util.MapList parsemapType(MapType maprange, int localu,
2592           int mappedu)
2593   {
2594     jalview.util.MapList ml = null;
2595     int[] localRange = getMapping(maprange.getLocal());
2596     int[] mappedRange = getMapping(maprange.getMapped());
2597     long lu = maprange.getLocal().hasUnit() ? maprange.getLocal().getUnit()
2598             : localu;
2599     long mu = maprange.getMapped().hasUnit()
2600             ? maprange.getMapped().getUnit()
2601             : mappedu;
2602     ml = new jalview.util.MapList(localRange, mappedRange, (int) lu,
2603             (int) mu);
2604     return ml;
2605   }
2606
2607   /**
2608    * initialise a range type object from a set of start/end inclusive intervals
2609    * 
2610    * @param mrt
2611    * @param ranges
2612    */
2613   private void initRangeType(RangeType mrt, List<int[]> ranges)
2614   {
2615     for (int[] range : ranges)
2616     {
2617       Seg vSeg = new Seg();
2618       vSeg.setStart(range[0]);
2619       vSeg.setEnd(range[1]);
2620       mrt.addSeg(vSeg);
2621     }
2622   }
2623
2624   /**
2625    * initialise a MapType object from a MapList object.
2626    * 
2627    * @param maprange
2628    * @param ml
2629    * @param setUnits
2630    */
2631   private void initMapType(MapType maprange, jalview.util.MapList ml,
2632           boolean setUnits)
2633   {
2634     maprange.setLocal(new Local());
2635     maprange.setMapped(new Mapped());
2636     initRangeType(maprange.getLocal(), ml.getFromRanges());
2637     initRangeType(maprange.getMapped(), ml.getToRanges());
2638     if (setUnits)
2639     {
2640       maprange.getLocal().setUnit(ml.getFromRatio());
2641       maprange.getLocal().setUnit(ml.getToRatio());
2642     }
2643   }
2644
2645   /*
2646    * not needed now. Provenance getVamsasProvenance(jalview.datamodel.Provenance
2647    * jprov) { jalview.datamodel.ProvenanceEntry[] entries = null; // TODO: fix
2648    * App and Action here. Provenance prov = new Provenance();
2649    * org.exolab.castor.types.Date date = new org.exolab.castor.types.Date( new
2650    * java.util.Date()); Entry provEntry;
2651    * 
2652    * if (jprov != null) { entries = jprov.getEntries(); for (int i = 0; i <
2653    * entries.length; i++) { provEntry = new Entry(); try { date = new
2654    * org.exolab.castor.types.Date(entries[i].getDate()); } catch (Exception ex)
2655    * { ex.printStackTrace();
2656    * 
2657    * date = new org.exolab.castor.types.Date(entries[i].getDate()); }
2658    * provEntry.setDate(date); provEntry.setUser(entries[i].getUser());
2659    * provEntry.setAction(entries[i].getAction()); prov.addEntry(provEntry); } }
2660    * else { provEntry = new Entry(); provEntry.setDate(date);
2661    * provEntry.setUser(System.getProperty("user.name")); // TODO: ext string
2662    * provEntry.setApp("JVAPP"); // TODO: ext string provEntry.setAction(action);
2663    * prov.addEntry(provEntry); }
2664    * 
2665    * return prov; }
2666    */
2667   jalview.datamodel.Provenance getJalviewProvenance(Provenance prov)
2668   {
2669     // TODO: fix App and Action entries and check use of provenance in jalview.
2670     jalview.datamodel.Provenance jprov = new jalview.datamodel.Provenance();
2671     for (int i = 0; i < prov.getEntryCount(); i++)
2672     {
2673       jprov.addEntry(prov.getEntry(i).getUser(),
2674               prov.getEntry(i).getAction(), prov.getEntry(i).getDate(),
2675               prov.getEntry(i).getId());
2676     }
2677
2678     return jprov;
2679   }
2680
2681   /**
2682    * 
2683    * @return default initial provenance list for a Jalview created vamsas
2684    *         object.
2685    */
2686   Provenance dummyProvenance()
2687   {
2688     return dummyProvenance(null);
2689   }
2690
2691   Entry dummyPEntry(String action)
2692   {
2693     Entry entry = new Entry();
2694     entry.setApp(this.provEntry.getApp());
2695     if (action != null)
2696     {
2697       entry.setAction(action);
2698     }
2699     else
2700     {
2701       entry.setAction("created.");
2702     }
2703     entry.setDate(new java.util.Date());
2704     entry.setUser(this.provEntry.getUser());
2705     return entry;
2706   }
2707
2708   Provenance dummyProvenance(String action)
2709   {
2710     Provenance prov = new Provenance();
2711     prov.addEntry(dummyPEntry(action));
2712     return prov;
2713   }
2714
2715   Entry addProvenance(Provenance p, String action)
2716   {
2717     Entry dentry = dummyPEntry(action);
2718     p.addEntry(dentry);
2719     return dentry;
2720   }
2721
2722   public Entry getProvEntry()
2723   {
2724     return provEntry;
2725   }
2726
2727   public IClientDocument getClientDocument()
2728   {
2729     return cdoc;
2730   }
2731
2732   public IdentityHashMap getJvObjectBinding()
2733   {
2734     return jv2vobj;
2735   }
2736
2737   public Hashtable getVamsasObjectBinding()
2738   {
2739     return vobj2jv;
2740   }
2741
2742   public void storeSequenceMappings(AlignmentViewport viewport,
2743           String title) throws Exception
2744   {
2745     AlignmentViewport av = viewport;
2746     try
2747     {
2748       jalview.datamodel.AlignmentI jal = av.getAlignment();
2749       // /////////////////////////////////////////
2750       // SAVE THE DATASET
2751       DataSet dataset = null;
2752       if (jal.getDataset() == null)
2753       {
2754         Cache.log.warn("Creating new dataset for an alignment.");
2755         jal.setDataset(null);
2756       }
2757       dataset = (DataSet) ((Alignment) getjv2vObj(
2758               viewport.getSequenceSetId())).getV_parent(); // jal.getDataset());
2759       if (dataset == null)
2760       {
2761         dataset = (DataSet) getjv2vObj(jal.getDataset());
2762         Cache.log.error(
2763                 "Can't find the correct dataset for the alignment in this view. Creating new one.");
2764
2765       }
2766       // Store any sequence mappings.
2767       List<AlignedCodonFrame> cframes = av.getAlignment().getCodonFrames();
2768       if (cframes != null)
2769       {
2770         for (AlignedCodonFrame acf : cframes)
2771         {
2772           if (acf.getdnaSeqs() != null && acf.getdnaSeqs().length > 0)
2773           {
2774             jalview.datamodel.SequenceI[] dmps = acf.getdnaSeqs();
2775             jalview.datamodel.Mapping[] mps = acf.getProtMappings();
2776             for (int smp = 0; smp < mps.length; smp++)
2777             {
2778               uk.ac.vamsas.objects.core.SequenceType mfrom = (SequenceType) getjv2vObj(
2779                       dmps[smp]);
2780               if (mfrom != null)
2781               {
2782                 new jalview.io.vamsas.Sequencemapping(this, mps[smp], mfrom,
2783                         dataset);
2784               }
2785               else
2786               {
2787                 Cache.log.warn(
2788                         "NO Vamsas Binding for local sequence! NOT CREATING MAPPING FOR "
2789                                 + dmps[smp].getDisplayId(true) + " to "
2790                                 + mps[smp].getTo().getName());
2791               }
2792             }
2793           }
2794         }
2795       }
2796     } catch (Exception e)
2797     {
2798       throw new Exception(MessageManager.formatMessage(
2799               "exception.couldnt_store_sequence_mappings", new String[]
2800               { title }), e);
2801     }
2802   }
2803
2804   public void clearSkipList()
2805   {
2806     if (skipList != null)
2807     {
2808       skipList.clear();
2809     }
2810   }
2811
2812   /**
2813    * @return the skipList
2814    */
2815   public Hashtable getSkipList()
2816   {
2817     return skipList;
2818   }
2819
2820   /**
2821    * @param skipList
2822    *          the skipList to set
2823    */
2824   public void setSkipList(Hashtable skipList)
2825   {
2826     this.skipList = skipList;
2827   }
2828
2829   /**
2830    * registry for datastoreItems
2831    */
2832   DatastoreRegistry dsReg = new DatastoreRegistry();
2833
2834   public DatastoreRegistry getDatastoreRegisty()
2835   {
2836     if (dsReg == null)
2837     {
2838       dsReg = new DatastoreRegistry();
2839     }
2840     return dsReg;
2841   }
2842 }