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