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