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