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