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