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