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