ongoign vamsas session debugging
[jalview.git] / src / jalview / io / VamsasDatastore.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer
3  * Copyright (C) 2005 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
20 package jalview.io;
21
22 import org.vamsas.client.Vobject;
23 import org.vamsas.client.VorbaId;
24 import org.vamsas.objects.core.*;
25 import org.vamsas.objects.utils.DocumentStuff;
26 import org.vamsas.test.simpleclient.ClientDoc;
27
28 import jalview.bin.Cache;
29 import jalview.datamodel.AlignmentAnnotation;
30 import jalview.datamodel.AlignmentI;
31 import jalview.datamodel.AlignmentView;
32 import jalview.datamodel.DBRefEntry;
33 import jalview.datamodel.SequenceFeature;
34 import jalview.datamodel.SequenceI;
35 import jalview.gui.*;
36
37 import java.io.*;
38 import java.util.HashMap;
39 import java.util.HashSet;
40 import java.util.Hashtable;
41 import java.util.IdentityHashMap;
42 import java.util.Vector;
43 import java.util.jar.*;
44 import org.exolab.castor.xml.*;
45 import org.exolab.castor.mapping.Mapping;
46
47 /*
48  * 
49  * static {
50  * org.exolab.castor.util.LocalConfiguration.getInstance().getProperties().setProperty(
51  * "org.exolab.castor.serializer", "org.apache.xml.serialize.XMLSerilazizer"); }
52  * 
53  */
54
55 public class VamsasDatastore {
56   Entry provEntry = null;
57
58   // AlignViewport av;
59
60   org.exolab.castor.types.Date date = new org.exolab.castor.types.Date(
61       new java.util.Date());
62
63   ClientDoc cdoc;
64
65   Hashtable vobj2jv;
66
67   IdentityHashMap jv2vobj;
68
69   public VamsasDatastore(ClientDoc cdoc, Hashtable vobj2jv,
70       IdentityHashMap jv2vobj, Entry provEntry) {
71       this.cdoc = cdoc;
72     this.vobj2jv = vobj2jv;
73     this.jv2vobj = jv2vobj;
74     this.provEntry = provEntry;
75   }
76
77   /*
78    * public void storeJalview(String file, AlignFrame af) { try { // 1. Load the
79    * mapping information from the file Mapping map = new
80    * Mapping(getClass().getClassLoader()); java.net.URL url =
81    * getClass().getResource("/jalview_mapping.xml"); map.loadMapping(url); // 2.
82    * Unmarshal the data // Unmarshaller unmar = new Unmarshaller();
83    * //unmar.setIgnoreExtraElements(true); //unmar.setMapping(map); // uni =
84    * (UniprotFile) unmar.unmarshal(new FileReader(file)); // 3. marshal the data
85    * with the total price back and print the XML in the console Marshaller
86    * marshaller = new Marshaller( new FileWriter(file) );
87    * 
88    * marshaller.setMapping(map); marshaller.marshal(af); } catch (Exception e) {
89    * e.printStackTrace(); } }
90    * 
91    * 
92    */
93   /**
94    * @return the Vobject bound to Jalview datamodel object
95    */
96   protected Vobject getjv2vObj(Object jvobj) {
97     if (jv2vobj.containsKey(jvobj))
98       return cdoc.getObject((VorbaId) jv2vobj.get(jvobj));
99     return null;
100   }
101
102   /**
103    * 
104    * @param vobj
105    * @return Jalview datamodel object bound to the vamsas document object
106    */
107   protected Object getvObj2jv(org.vamsas.client.Vobject vobj) {
108     VorbaId id = vobj.getVorbaId();
109     if (id == null)
110     {
111       id = cdoc.registerObject(vobj);
112       Cache.log
113       .debug("Registering new object and returning null for getvObj2jv");
114       return null;
115     }
116     if (vobj2jv.containsKey(vobj.getVorbaId()))
117       return vobj2jv.get(vobj.getVorbaId());
118     return null;
119   }
120
121   protected void bindjvvobj(Object jvobj, org.vamsas.client.Vobject vobj) {
122     VorbaId id = vobj.getVorbaId();
123     if (id == null)
124     {
125       id = cdoc.registerObject(vobj);
126       if (id==null || vobj.getVorbaId()==null)
127         Cache.log.error("Failed to get id for "+(vobj.isRegisterable() ? "registerable" : "unregisterable") +" object "+vobj);
128     }
129     if (vobj2jv.containsKey(vobj.getVorbaId()) || jv2vobj.containsKey(jvobj))
130     {
131       Cache.log.error("Duplicate object binding! "+vobj+" id " +vobj.getVorbaId().getId()+" to "+jvobj);
132     }
133     else
134     {
135       vobj2jv.put(vobj.getVorbaId(), jvobj);// JBPNote - better implementing a
136       // hybrid invertible hash.
137       jv2vobj.put(jvobj, vobj.getVorbaId());
138     }
139   }
140
141   /**
142    * put the alignment viewed by AlignViewport into cdoc.
143    * 
144    * @param av
145    */
146   public void storeVAMSAS(AlignViewport av) {
147     try
148     {
149       jalview.datamodel.AlignmentI jal = av.getAlignment();
150       boolean nw = false;
151       VAMSAS root = null; // will be resolved based on Dataset Parent.
152       // /////////////////////////////////////////
153       // SAVE THE DATASET
154       if (jal.getDataset() == null)
155       {
156         Cache.log.warn("Creating new dataset for an alignment.");
157         jal.setDataset(null);
158       }
159       DataSet dataset = (DataSet) getjv2vObj(jal.getDataset());
160       if (dataset == null)
161       {
162         root = cdoc.getVamsasRoots()[0]; // default vamsas root for modifying.
163         dataset = new DataSet();
164         root.addDataSet(dataset);
165         bindjvvobj(jal.getDataset(), dataset);
166         dataset.setProvenance(dummyProvenance());
167         dataset.getProvenance().addEntry(provEntry);
168         nw = true;
169       }
170       else
171       {
172         root = (VAMSAS) dataset.getV_parent();
173       }
174       // update dataset
175       Sequence sequence;
176       DbRef dbref;
177       // set new dataset and alignment sequences based on alignment Nucleotide
178       // flag.
179       // this *will* break when alignment contains both nucleotide and amino
180       // acid sequences.
181       String dict = jal.isNucleotide() ? org.vamsas.objects.utils.SymbolDictionary.STANDARD_NA
182           : org.vamsas.objects.utils.SymbolDictionary.STANDARD_AA;
183       for (int i = 0; i < jal.getHeight(); i++)
184       {
185         SequenceI sq = jal.getSequenceAt(i).getDatasetSequence(); // only insert
186         // referenced
187         // sequences
188         // to dataset.
189         sequence = (Sequence) getjv2vObj(sq);
190         if (sequence == null)
191         {
192           sequence = new Sequence();
193           bindjvvobj(sq, sequence);
194           sq.setVamsasId(sequence.getVorbaId().getId());
195           sequence.setSequence(sq.getSequence());
196           sequence.setDictionary(dict);
197           sequence.setName(jal.getDataset().getSequenceAt(i).getName());
198           sequence.setStart(jal.getDataset().getSequenceAt(i).getStart());
199           sequence.setEnd(jal.getDataset().getSequenceAt(i).getEnd());
200           dataset.addSequence(sequence);
201         }
202         else
203         {
204           // verify principal attributes. and update any new
205           // features/references.
206           System.out.println("update dataset sequence object.");
207         }
208         if (sq.getSequenceFeatures() != null)
209         {
210           int sfSize = sq.getSequenceFeatures().length;
211
212           for (int sf = 0; sf < sfSize; sf++)
213           {
214             jalview.datamodel.SequenceFeature feature = (jalview.datamodel.SequenceFeature) sq
215             .getSequenceFeatures()[sf];
216
217             DataSetAnnotations dsa = (DataSetAnnotations) getjv2vObj(feature);
218             if (dsa == null)
219             {
220               dsa = (DataSetAnnotations) getDSAnnotationFromJalview(
221                   new DataSetAnnotations(), feature);
222               if (dsa.getProvenance() == null)
223               {
224                 dsa.setProvenance(new Provenance());
225               }
226               addProvenance(dsa.getProvenance(), "created"); // JBPNote - need
227               // to update
228               dsa.setSeqRef(sequence);
229               bindjvvobj(feature, dsa);
230               dataset.addDataSetAnnotations(dsa);
231             }
232             else
233             {
234               // todo: verify and update dataset annotations for sequence
235               System.out.println("update dataset sequence annotations.");
236             }
237           }
238         }
239
240         if (sq.getDBRef() != null)
241         {
242           DBRefEntry[] entries = sq.getDBRef();
243           jalview.datamodel.DBRefEntry dbentry;
244           for (int db = 0; db < entries.length; db++)
245           {
246             dbentry = entries[db];
247             dbref = (DbRef) getjv2vObj(dbentry);
248             if (dbref == null)
249             {
250               dbref = new DbRef();
251               bindjvvobj(dbentry, dbref);
252               dbref.setAccessionId(dbentry.getAccessionId());
253               dbref.setSource(dbentry.getSource());
254               dbref.setVersion(dbentry.getVersion());
255               /*
256                * TODO: Maps are not yet supported by Jalview. Map vMap = new
257                * Map(); vMap.set dbref.addMap(vMap);
258                */
259               sequence.addDbRef(dbref);
260             }
261             else
262             {
263               // TODO: verify and update dbrefs in vamsas document
264               // there will be trouble when a dataset sequence is modified to
265               // contain more residues than were originally referenced - we must
266               // then make a number of dataset sequence entries
267               System.out
268               .println("update dataset sequence database references.");
269             }
270           }
271
272         }
273       }
274       // dataset.setProvenance(getVamsasProvenance(jal.getDataset().getProvenance()));
275       // ////////////////////////////////////////////
276
277       // ////////////////////////////////////////////
278       // Save the Alignments
279
280       Alignment alignment = (Alignment) getjv2vObj(av); // this is so we can get the alignviewport back 
281       if (alignment == null)
282       {
283         alignment = new Alignment();
284         bindjvvobj(av, alignment);
285         if (alignment.getProvenance() == null)
286           alignment.setProvenance(new Provenance());
287         addProvenance(alignment.getProvenance(), "added"); // TODO: insert some
288         // sensible source
289         // here
290         dataset.addAlignment(alignment);
291         alignment.setGapChar(String.valueOf(av.getGapCharacter()));
292         AlignmentSequence alseq = null;
293         for (int i = 0; i < jal.getHeight(); i++)
294         {
295           alseq = new AlignmentSequence();
296           // TODO: VAMSAS: translate lowercase symbols to annotation ?
297           alseq.setSequence(jal.getSequenceAt(i).getSequence());
298           alseq.setName(jal.getSequenceAt(i).getName());
299           alseq.setStart(jal.getSequenceAt(i).getStart());
300           alseq.setEnd(jal.getSequenceAt(i).getEnd());
301           alseq.setRefid(getjv2vObj(jal.getSequenceAt(i).getDatasetSequence()));
302           alignment.addAlignmentSequence(alseq);
303           bindjvvobj(jal.getSequenceAt(i), alseq);
304         }
305       }
306       else
307       {
308         // todo: verify and update mutable alignment props.
309         if (alignment.getModifiable())
310         {
311           System.out.println("update alignment in document.");
312         }
313         else
314         {
315           System.out
316           .println("update edited alignment to new alignment in document.");
317         }
318       }
319       // ////////////////////////////////////////////
320       // SAVE Alignment Sequence Features
321       for (int i = 0, iSize = alignment.getAlignmentSequenceCount(); i < iSize; i++)
322       {
323         AlignmentSequence valseq;
324         SequenceI alseq = (SequenceI) getvObj2jv(valseq = alignment
325             .getAlignmentSequence(i));
326         if (alseq != null && alseq.getSequenceFeatures() != null)
327         {
328           jalview.datamodel.SequenceFeature[] features = alseq
329           .getSequenceFeatures();
330           for (int f = 0; f < features.length; f++)
331           {
332             if (features[f] != null)
333             {
334               AlignmentSequenceAnnotation valseqf = (AlignmentSequenceAnnotation) getjv2vObj(features[i]);
335               if (valseqf == null)
336               {
337
338                 valseqf = (AlignmentSequenceAnnotation) getDSAnnotationFromJalview(
339                     new AlignmentSequenceAnnotation(), features[i]);
340                 if (valseqf.getProvenance() == null)
341                 {
342                   valseqf.setProvenance(new Provenance());
343                 }
344                 addProvenance(valseqf.getProvenance(), "created"); // JBPNote -
345                 // need to
346                 // update
347                 bindjvvobj(features[i], valseqf);
348                 valseq.addAlignmentSequenceAnnotation(valseqf);
349               }
350             }
351
352           }
353         }
354       }
355
356       // ////////////////////////////////////////////
357       // SAVE ANNOTATIONS
358       if (jal.getAlignmentAnnotation() != null)
359       {
360         jalview.datamodel.AlignmentAnnotation[] aa = jal
361         .getAlignmentAnnotation();
362         java.util.HashMap AlSeqMaps = new HashMap(); // stores int maps from
363         // alignment columns to
364         // sequence positions.
365         for (int i = 0; i < aa.length; i++)
366         {
367           if (aa[i] == null || isJalviewOnly(aa[i]))
368           {
369             continue;
370           }
371           if (aa[i].sequenceRef != null)
372           {
373             org.vamsas.objects.core.AlignmentSequence alsref = (org.vamsas.objects.core.AlignmentSequence) getjv2vObj(aa[i].sequenceRef);
374             org.vamsas.objects.core.AlignmentSequenceAnnotation an = (org.vamsas.objects.core.AlignmentSequenceAnnotation) getjv2vObj(aa[i]);
375             int[] gapMap = null;
376             if (AlSeqMaps.containsKey(aa[i].sequenceRef))
377             {
378               gapMap = (int[]) AlSeqMaps.get(aa[i].sequenceRef);
379             }
380             else
381             {
382               gapMap = new int[aa[i].sequenceRef.getLength()];
383               // map from alignment position to sequence position.
384               int[] sgapMap = aa[i].sequenceRef.gapMap();
385               for (int a = 0; a < sgapMap.length; a++)
386                 gapMap[sgapMap[a]] = a;
387             }
388             if (an == null)
389             {
390               an = new org.vamsas.objects.core.AlignmentSequenceAnnotation();
391               Seg vSeg = new Seg();
392               vSeg.setStart(1);
393               vSeg.setInclusive(true);
394               vSeg.setEnd(gapMap.length);
395               an.addSeg(vSeg);
396               an.setType("jalview:SecondaryStructurePrediction");// TODO: better fix this rough guess ;)
397               alsref.addAlignmentSequenceAnnotation(an);
398               bindjvvobj(aa[i],an);
399               // LATER: much of this is verbatim from the alignmentAnnotation
400               // method below. suggests refactoring to make rangeAnnotation the
401               // base class
402               an.setDescription(aa[i].description);
403               if (aa[i].graph > 0)
404                 an.setGraph(true); // aa[i].graph);
405               else
406                 an.setGraph(false);
407               an.setLabel(aa[i].label);
408               an.setProvenance(dummyProvenance()); // get provenance as user
409               // created, or jnet, or
410               // something else.
411               an.setGroup(Integer.toString(aa[i].graphGroup)); // // JBPNote -
412               // originally we
413               // were going to
414               // store
415               // graphGroup in
416               // the Jalview
417               // specific
418               // bits.
419               AnnotationElement ae;
420               for (int a = 0; a < aa[i].annotations.length; a++)
421               {
422                 if (aa[i].annotations[a] == null)
423                 {
424                   continue;
425                 }
426
427                 ae = new AnnotationElement();
428                 ae.setDescription(aa[i].annotations[a].description);
429                 ae.addGlyph(new Glyph());
430                 ae.getGlyph(0)
431                 .setContent(aa[i].annotations[a].displayCharacter); // assume
432                 // jax-b
433                 // takes
434                 // care
435                 // of
436                 // utf8
437                 // translation
438                 ae.addValue(aa[i].annotations[a].value);
439                 ae.setPosition(gapMap[a]+1); // position w.r.t. AlignmentSequence
440                 // symbols
441                 if (aa[i].annotations[a].secondaryStructure != ' ')
442                 {
443                   // we only write an annotation where it really exists.
444                   Glyph ss = new Glyph();
445                   ss
446                   .setDict(org.vamsas.objects.utils.GlyphDictionary.PROTEIN_SS_3STATE);
447                   ss.setContent(String
448                       .valueOf(aa[i].annotations[a].secondaryStructure));
449                   ae.addGlyph(ss);
450                 }
451                 an.addAnnotationElement(ae);
452               }
453             }
454             else
455             {
456               // update reference sequence Annotation
457               if (an.getModifiable())
458               {
459                 // verify existing alignment sequence annotation is up to date
460                 System.out.println("update alignment sequence annotation.");
461               }
462               else
463               {
464                 // verify existing alignment sequence annotation is up to date
465                 System.out
466                 .println("make new alignment sequence annotation if modification has happened.");
467               }
468             }
469           }
470           else
471           {
472             // add Alignment Annotation
473             org.vamsas.objects.core.AlignmentAnnotation an = (org.vamsas.objects.core.AlignmentAnnotation) getjv2vObj(aa[i]);
474             if (an == null)
475             {
476               an = new org.vamsas.objects.core.AlignmentAnnotation();
477               an.setType("jalview:AnnotationRow");
478               an.setDescription(aa[i].description);
479               alignment.addAlignmentAnnotation(an);
480               Seg vSeg = new Seg();
481               vSeg.setStart(1);
482               vSeg.setInclusive(true);
483               vSeg.setEnd(jal.getWidth());
484               an.addSeg(vSeg);
485               if (aa[i].graph > 0)
486                 an.setGraph(true); // aa[i].graph);
487               an.setLabel(aa[i].label);
488               an.setProvenance(dummyProvenance());
489               if (aa[i].graph!=aa[i].NO_GRAPH) {
490                 an.setGroup(Integer.toString(aa[i].graphGroup)); // // JBPNote -
491                 // originally we
492               // were going to
493               // store
494               // graphGroup in
495               // the Jalview
496               // specific
497               // bits.
498                 an.setGraph(true);
499               } else {
500                 an.setGraph(false);
501               }
502               AnnotationElement ae;
503
504               for (int a = 0; a < aa[i].annotations.length; a++)
505               {
506                 if ((aa[i] == null) || (aa[i].annotations[a] == null))
507                 {
508                   continue;
509                 }
510
511                 ae = new AnnotationElement();
512                 ae.setDescription(aa[i].annotations[a].description);
513                 ae.addGlyph(new Glyph());
514                 ae.getGlyph(0)
515                 .setContent(aa[i].annotations[a].displayCharacter); // assume
516                 // jax-b
517                 // takes
518                 // care
519                 // of
520                 // utf8
521                 // translation
522                 ae.addValue(aa[i].annotations[a].value);
523                 ae.setPosition(a+1);
524                 if (aa[i].annotations[a].secondaryStructure != ' ')
525                 {
526                   Glyph ss = new Glyph();
527                   ss
528                   .setDict(org.vamsas.objects.utils.GlyphDictionary.PROTEIN_SS_3STATE);
529                   ss.setContent(String
530                       .valueOf(aa[i].annotations[a].secondaryStructure));
531                   ae.addGlyph(ss);
532                 }
533                 an.addAnnotationElement(ae);
534               }
535               if (aa[i].editable) {
536                 //an.addProperty(newProperty("jalview:editable", null, "true"));
537                 an.setModifiable(true);
538               }
539               if (aa[i].graph!=jalview.datamodel.AlignmentAnnotation.NO_GRAPH) {
540                 an.setGraph(true);
541                 an.setGroup(Integer.toString(aa[i].graphGroup));
542                 an.addProperty(newProperty("jalview:graphType",null,
543                     ((aa[i].graph==jalview.datamodel.AlignmentAnnotation.BAR_GRAPH) ? "BAR_GRAPH" : "LINE_GRAPH")));
544
545                 /** and on and on.. 
546                  vProperty=new Property();
547                   vProperty.setName("jalview:graphThreshhold");
548                   vProperty.setContent(aa[i].threshold);
549                  */
550
551               }
552             }
553             else
554             {
555               if (an.getModifiable())
556               {
557                 // verify annotation - update (perhaps)
558                 Cache.log.info("update alignment sequence annotation. not yet implemented.");
559               }
560               else
561               {
562                 // verify annotation - update (perhaps)
563                 Cache.log.info("updated alignment sequence annotation added.");
564               }
565             }
566           }
567         }
568       }
569       // /////////////////////////////////////////////////////
570
571       // //////////////////////////////////////////////
572       // /SAVE THE TREES
573       // /////////////////////////////////
574       // FIND ANY ASSOCIATED TREES
575       if (Desktop.desktop != null)
576       {
577         javax.swing.JInternalFrame[] frames = Desktop.desktop.getAllFrames();
578
579         for (int t = 0; t < frames.length; t++)
580         {
581           if (frames[t] instanceof TreePanel)
582           {
583             TreePanel tp = (TreePanel) frames[t];
584
585             if (tp.getAlignment() == jal)
586             {
587               Tree tree = (Tree) getjv2vObj(tp);
588               if (tree == null)
589               {
590                 tree = new Tree();
591                 bindjvvobj(tp, tree);
592                 tree.setTitle(tp.getTitle());
593                 Newick newick = new Newick();
594                 // TODO: translate sequenceI to leaf mappings to vamsas
595                 // references - see tree specification in schema.
596                 newick.setContent(tp.getTree().toString());
597                 newick.setTitle(tp.getTitle());
598                 tree.addNewick(newick);
599                 tree.setProvenance(makeTreeProvenance(jal, tp));
600                 alignment.addTree(tree);
601               }
602               else
603               {
604                 if (tree.getModifiable())
605                 {
606                   // verify any changes.
607                   System.out.println("Update tree in document.");
608                 }
609                 else
610                 {
611                   System.out
612                   .println("Add modified tree as new tree in document.");
613                 }
614               }
615             }
616           }
617         }
618       }
619       // Store Jalview specific stuff in the Jalview appData
620       // not implemented in the SimpleDoc interface.
621     }
622
623     catch (Exception ex)
624     {
625       ex.printStackTrace();
626     }
627
628   }
629
630   private Property newProperty(String name, String type, String content) {
631     Property vProperty=new Property();
632     vProperty.setName(name);
633     if (type!=null)
634       vProperty.setType(type);
635     vProperty.setContent(content);
636     return vProperty;
637   }
638
639   /**
640    * correctly create a RangeAnnotation from a jalview sequence feature
641    * 
642    * @param dsa
643    *          (typically DataSetAnnotations or AlignmentSequenceAnnotation)
644    * @param feature
645    *          (the feature to be mapped from)
646    * @return
647    */
648   private RangeAnnotation getDSAnnotationFromJalview(RangeAnnotation dsa,
649       SequenceFeature feature) {
650     dsa.setType(feature.getType());
651     Seg vSeg = new Seg();
652     vSeg.setStart(feature.getBegin());
653     vSeg.setEnd(feature.getEnd());
654     vSeg.setInclusive(true);
655     dsa.addSeg(vSeg);
656     dsa.setDescription(feature.getDescription());
657     dsa.setStatus(feature.getStatus());
658     if (feature.links != null && feature.links.size() > 0)
659     {
660       for (int i = 0, iSize = feature.links.size(); i < iSize; i++)
661       {
662         String link = (String) feature.links.elementAt(i);
663         int sep = link.indexOf('|');
664         if (sep > -1)
665         {
666           Link vLink = new Link();
667           if (sep > 0)
668             vLink.setContent(link.substring(0, sep - 1));
669           else
670             vLink.setContent("");
671           vLink.setHref(link.substring(sep + 1)); // TODO: validate href.
672           dsa.addLink(vLink);
673         }
674       }
675     }
676     dsa.setGroup(feature.getFeatureGroup());
677     return dsa;
678   }
679
680   /**
681    * correctly creates provenance for trees calculated on an alignment by
682    * jalview.
683    * 
684    * @param jal
685    * @param tp
686    * @return
687    */
688   private Provenance makeTreeProvenance(AlignmentI jal, TreePanel tp) {
689     Provenance prov = new Provenance();
690     prov.addEntry(new Entry());
691     prov.getEntry(0).setAction("imported "+tp.getTitle());
692     prov.getEntry(0).setUser(provEntry.getUser());
693     prov.getEntry(0).setApp(provEntry.getApp());
694     prov.getEntry(0).setDate(provEntry.getDate());
695     if (tp.getTree().hasOriginalSequenceData())
696     {
697       Input vInput = new Input();
698       // LATER: check to see if tree input data is contained in this alignment -
699       // or just correctly resolve the tree's seqData to the correct alignment in
700       // the document.
701       vInput.setObjRef(getjv2vObj(jal));
702       prov.getEntry(0).setAction("created "+tp.getTitle());
703       prov.getEntry(0).addInput(vInput);
704       vInput.setName("jalview:seqdist");
705       prov.getEntry(0).addParam(new Param());
706       prov.getEntry(0).getParam(0).setName("treeType");
707       prov.getEntry(0).getParam(0).setType("utf8");
708       prov.getEntry(0).getParam(0).setContent("NJ");
709       
710       int ranges[] = tp.getTree().seqData.getVisibleContigs();
711       for (int r = 0; r < ranges.length; r += 2)
712       {
713         Seg visSeg = new Seg();
714         visSeg.setStart(ranges[r]);
715         visSeg.setEnd(ranges[r + 1]);
716         visSeg.setInclusive(true);
717         vInput.addSeg(visSeg);
718       }
719     }
720     return prov;
721   }
722
723   /**
724    * 
725    * @param tp
726    * @return Object[] { AlignmentView, AlignmentI - reference alignment for
727    *         input }
728    */
729   private Object[] recoverInputData(Provenance tp) {
730     for (int pe = 0; pe < tp.getEntryCount(); pe++)
731     {
732       if (tp.getEntry(pe).getInputCount() > 0)
733       {
734         if (tp.getEntry(pe).getInputCount() > 1)
735           Cache.log.warn("Ignoring additional input spec in provenance entry "
736               + tp.getEntry(pe).toString());
737         // LATER: deal sensibly with multiple inputs.
738         Input vInput = tp.getEntry(pe).getInput(0);
739         if (vInput.getObjRef() instanceof org.vamsas.objects.core.Alignment)
740         {
741           // recover an AlignmentView for the input data
742           AlignmentI jal = (AlignmentI) getvObj2jv((org.vamsas.client.Vobject) vInput
743               .getObjRef());
744           jalview.datamodel.CigarArray view = jal.getCompactAlignment();
745           int from = 0, to = jal.getWidth();
746           for (int r = 0, s = vInput.getSegCount(); r < s; r++)
747           {
748             Seg visSeg = vInput.getSeg(r);
749             int se[] = getSegRange(visSeg,true); // jalview doesn't do bidirection alignments yet.
750             if (to > se[1])
751               Cache.log.warn("Ignoring invalid segment in InputData spec.");
752             else
753             {
754               if (se[0] > from)
755               {
756                 view.deleteRange(from, se[0] - 1);
757               }
758               from = se[1] + 1;
759             }
760           }
761           if (from < to)
762           {
763             view.deleteRange(from, to); // final deletion - TODO: check off by
764             // one for to
765           }
766           return new Object[] { new AlignmentView(view), jal };
767         }
768       }
769     }
770     Cache.log.debug("Returning null for input data recovery from provenance.");
771     return null;
772   }
773
774   /**
775    * get start<end range of segment, adjusting for inclusivity flag and
776    * polarity.
777    *  
778    * @param visSeg
779    * @param ensureDirection when true - always ensure start is less than end.
780    * @return int[] { start, end, direction} where direction==1 for range running from end to start.
781    */
782   private int[] getSegRange(Seg visSeg, boolean ensureDirection) {
783     boolean incl = visSeg.getInclusive();
784     // adjust for inclusive flag.
785     int pol = (visSeg.getStart() <= visSeg.getEnd()) ? 1 : -1; // polarity of
786     // region.
787     int start = visSeg.getStart() + (incl ? 0 : pol);
788     int end = visSeg.getEnd() + (incl ? 0 : -pol);
789     if (ensureDirection && pol==-1)
790     {
791       // jalview doesn't deal with inverted ranges, yet.
792       int t = end;
793       end = start;
794       start = t;
795     }
796     return new int[] { start, end, pol<0 ? 1 : 0 };
797   }
798
799   /**
800    * 
801    * @param annotation
802    * @return true if annotation is not to be stored in document
803    */
804   private boolean isJalviewOnly(AlignmentAnnotation annotation) {
805     return annotation.label.equals("Quality")
806     || annotation.label.equals("Conservation")
807     || annotation.label.equals("Consensus");
808   }
809   /**
810    * This will return the first AlignFrame viewing AlignViewport av.
811    * It will break if there are more than one AlignFrames viewing a particular av.
812    * This also shouldn't be in the io package.
813    * @param av
814    * @return alignFrame for av
815    */
816   public AlignFrame getAlignFrameFor(AlignViewport av) {
817     if (Desktop.desktop != null)
818     {
819       javax.swing.JInternalFrame[] frames = Desktop.desktop.getAllFrames();
820
821       for (int t = 0; t < frames.length; t++)
822       {
823         if (frames[t] instanceof AlignFrame) {
824           if (((AlignFrame) frames[t]).getViewport()==av)
825             return (AlignFrame) frames[t];
826         }
827       }
828     }
829     return null;
830   }
831   public void updateToJalview() {
832     VAMSAS _roots[] = cdoc.getVamsasRoots();
833
834     for (int _root = 0; _root<_roots.length; _root++) {
835       VAMSAS root = _roots[_root];
836       boolean newds=false;
837       for (int _ds=0,_nds=root.getDataSetCount(); _ds<_nds; _ds++) {
838         // ///////////////////////////////////
839         // ///LOAD DATASET
840         DataSet dataset = root.getDataSet(_ds);
841         int i, iSize = dataset.getSequenceCount();
842         Vector dsseqs;
843         jalview.datamodel.Alignment jdataset = (jalview.datamodel.Alignment) getvObj2jv(dataset);
844         int jremain=0;
845         if (jdataset==null) {
846           Cache.log.debug("Initialising new jalview dataset fields");
847           newds=true;
848           dsseqs=new Vector();
849         } else {
850           Cache.log.debug("Update jalview dataset from vamsas.");
851           jremain=jdataset.getHeight();
852           dsseqs=jdataset.getSequences();
853         }
854
855         // TODO: test sequence merging - we preserve existing non vamsas
856         // sequences but add in any new vamsas ones, and don't yet update any
857         // sequence attributes
858         for (i = 0; i < iSize ; i++)
859         {
860           Sequence vdseq = dataset.getSequence(i);
861           jalview.datamodel.SequenceI dsseq = (SequenceI) getvObj2jv(vdseq);
862           if (dsseq!=null) {
863             if (!dsseq.getSequence().equals(vdseq.getSequence()))
864               throw new Error("Broken! - mismatch of dataset sequence and jalview internal dataset sequence.");
865             jremain--;
866           } else {
867             dsseq = new jalview.datamodel.Sequence(
868                 dataset.getSequence(i).getName(),
869                 dataset.getSequence(i).getSequence(),
870                 dataset.getSequence(i).getStart(),
871                 dataset.getSequence(i).getEnd()  );
872             bindjvvobj(dsseq, dataset.getSequence(i));
873             dsseq.setVamsasId(dataset.getSequence(i).getVorbaId().getId());
874             dsseqs.add(dsseq);
875           }
876           if (vdseq.getDbRefCount()>0) {
877             DbRef [] dbref = vdseq.getDbRef();
878             for(int db=0; db<dbref.length; db++)
879             {
880               jalview.datamodel.DBRefEntry dbr=(jalview.datamodel.DBRefEntry) getvObj2jv(dbref[db]);
881               if (dbr==null) {
882                 // add new dbref
883                 dsseq.addDBRef(dbr= new jalview.datamodel.DBRefEntry
884                     (
885                         dbref[db].getSource().toString(),
886                         dbref[db].getVersion().toString(),
887                         dbref[db].getAccessionId().toString()));
888                 bindjvvobj(dbr, dbref[db]);
889               }
890             }
891           }
892         }
893
894         if (newds) {
895           SequenceI[] seqs = new SequenceI[dsseqs.size()];
896           for (i=0,iSize=dsseqs.size(); i<iSize; i++) {
897             seqs[i]=(SequenceI) dsseqs.elementAt(i);
898             dsseqs.setElementAt(null, i);
899           }
900           jdataset = new jalview.datamodel.Alignment(seqs);
901           Cache.log.debug("New vamsas dataset imported into jalview.");
902           bindjvvobj(jdataset, dataset);
903         }
904         // ////////
905         // add any new dataset sequence feature annotations
906         if (dataset.getDataSetAnnotations() != null) {
907           for (int dsa = 0; dsa < dataset.getDataSetAnnotationsCount(); dsa++) {
908             DataSetAnnotations dseta=dataset.getDataSetAnnotations(dsa);
909             SequenceI dsSeq=(SequenceI) getvObj2jv((Vobject) dseta.getSeqRef());
910             if (dsSeq==null) {
911               jalview.bin.Cache.log.warn("Couldn't resolve jalview sequenceI for dataset object reference "+((Vobject)dataset.getDataSetAnnotations(dsa).getSeqRef()).getVorbaId().getId());
912             } else {
913               if (dseta.getAnnotationElementCount()==0) {
914                 jalview.datamodel.SequenceFeature sf=(jalview.datamodel.SequenceFeature) getvObj2jv(dseta);
915                 if (sf==null) {
916                   dsSeq.addSequenceFeature(sf=getJalviewSeqFeature(dseta));
917                   bindjvvobj(sf, dseta);
918                 }
919               } else {
920                 // TODO: deal with alignmentAnnotation style annotation
921                 // appearing on dataset sequences.
922                 // JBPNote: we could just add them to all alignments but
923                 // that may complicate cross references in the jalview
924                 // datamodel
925                 Cache.log.warn("Ignoring dataset annotation with annotationElements. Not yet supported in jalview.");
926               }
927             }
928           }
929         }
930
931         if (dataset.getAlignmentCount()>0) {
932           // LOAD ALIGNMENTS from DATASET 
933
934           for (int al=0,nal=dataset.getAlignmentCount(); al<nal; al++) {
935             org.vamsas.objects.core.Alignment alignment = dataset.getAlignment(al);
936             AlignViewport av = (AlignViewport) getvObj2jv(alignment);
937             jalview.datamodel.AlignmentI jal=null;
938             if (av!=null)
939               jal = av.getAlignment(); 
940             iSize = alignment.getAlignmentSequenceCount();
941             boolean newal=(jal==null) ? true : false;
942             Vector newasAnnots=new Vector();
943             char gapChar=' '; // default for new alignments read in from the document
944             if (jal!=null) {
945               dsseqs=jal.getSequences(); // for merge/update
946               gapChar=jal.getGapCharacter();
947             } else {
948               dsseqs=new Vector();
949             }
950             char valGapchar=alignment.getGapChar().charAt(0);
951             for (i = 0; i < iSize; i++)
952             {
953               AlignmentSequence valseq = alignment.getAlignmentSequence(i);
954               jalview.datamodel.SequenceI alseq = (SequenceI) getvObj2jv(valseq);
955               if (alseq!=null) {
956                 //TODO: upperCase/LowerCase situation here ? do we allow it ?
957                 //if (!alseq.getSequence().equals(valseq.getSequence())) {
958                 // throw new Error("Broken! - mismatch of dataset sequence and jalview internal dataset sequence.");
959                 if (Cache.log.isDebugEnabled())
960                   Cache.log.debug("Updating apparently edited sequence "+alseq.getName());
961                 // this might go *horribly* wrong
962                 alseq.setSequence(new String(valseq.getSequence()).replace(valGapchar, gapChar));
963                 jremain--;
964               } else {
965                 alseq = new jalview.datamodel.Sequence(
966                     valseq.getName(),
967                     valseq.getSequence().replace(valGapchar, gapChar),
968                     valseq.getStart(),
969                     valseq.getEnd()  );
970                 alseq.setDatasetSequence((SequenceI)getvObj2jv((Vobject)valseq.getRefid())); // exceptions if AlignemntSequence reference isn't a simple SequenceI
971                 bindjvvobj(alseq, valseq);
972                 alseq.setVamsasId(valseq.getVorbaId().getId());
973                 dsseqs.add(alseq);
974               }
975               if (valseq.getAlignmentSequenceAnnotationCount()>0) {
976                 AlignmentSequenceAnnotation[] vasannot=valseq.getAlignmentSequenceAnnotation();
977                 for (int a=0; a<vasannot.length; a++) {
978                   jalview.datamodel.AlignmentAnnotation asa = (jalview.datamodel.AlignmentAnnotation) getvObj2jv(vasannot[a]); // TODO: 1:many jalview alignment sequence annotations
979                   if (asa==null) {
980                     int se[] = getBounds(vasannot[a]);
981                     asa = getjAlignmentAnnotation(jal, vasannot[a]);
982                     asa.sequenceRef=alseq;
983                     asa.createSequenceMapping(alseq, alseq.getStart()+se[0], false); // TODO: verify that positions in alseqAnnotation correspond to ungapped residue positions.
984                     bindjvvobj(asa, vasannot[a]);
985                     newasAnnots.add(asa);
986                   } else {
987                     // update existing annotation - can do this in place
988                     if (vasannot[a].getModifiable()) {
989                       Cache.log.info("UNIMPLEMENTED: not recovering user modifiable sequence alignment annotation");
990                       // TODO: should at least replace with new one - otherwise things will break
991                       // basically do this:
992                       // int se[] = getBounds(vasannot[a]);
993                       // asa.update(getjAlignmentAnnotation(jal, vasannot[a])); //  update from another annotation object in place.
994                       // asa.createSequenceMapping(alseq, se[0], false); 
995
996                     }
997                   }
998                 }
999               }
1000             }
1001             if (jal==null) {
1002               SequenceI[] seqs = new SequenceI[dsseqs.size()];
1003               for (i=0,iSize=dsseqs.size(); i<iSize; i++) {
1004                 seqs[i]=(SequenceI) dsseqs.elementAt(i);
1005                 dsseqs.setElementAt(null, i);
1006               }
1007               jal = new jalview.datamodel.Alignment(seqs);
1008               Cache.log.debug("New vamsas alignment imported into jalview "+alignment.getVorbaId().getId());
1009               jal.setDataset(jdataset);
1010             }
1011             if (newasAnnots!=null && newasAnnots.size()>0) {
1012               // Add the new sequence annotations in to the alignment.
1013               for (int an=0,anSize=newasAnnots.size(); an<anSize; an++) {
1014                 jal.addAnnotation((AlignmentAnnotation) newasAnnots.elementAt(an));
1015                 // TODO: check if anything has to be done - like calling adjustForAlignment or something.
1016                 newasAnnots.setElementAt(null, an);
1017               }
1018               newasAnnots=null;
1019             }
1020             // //////////////////////////////////////////
1021             // //LOAD ANNOTATIONS FOR THE ALIGNMENT
1022             // ////////////////////////////////////
1023             if (alignment.getAlignmentAnnotationCount()>0)
1024             {
1025               org.vamsas.objects.core.AlignmentAnnotation[] an = alignment.getAlignmentAnnotation();
1026
1027               for (int j = 0; j < an.length; j++)
1028               {
1029                 jalview.datamodel.AlignmentAnnotation jan=(jalview.datamodel.AlignmentAnnotation) getvObj2jv(an[j]);
1030                 if (jan!=null) {
1031                   // update or stay the same.
1032                   // TODO: should at least replace with a new one - otherwise things will break
1033                   // basically do this:
1034                   // jan.update(getjAlignmentAnnotation(jal, an[a])); //  update from another annotation object in place.
1035
1036                   Cache.log.debug("update from vamsas alignment annotation to existing jalview alignment annotation.");
1037                   if (an[i].getModifiable()) {
1038                     // TODO: user defined annotation is totally mutable... - so load it up or throw away if locally edited.
1039                     Cache.log.info("NOT IMPLEMENTED - Recovering user-modifiable annotation - yet...");
1040                   }
1041                   // TODO: compare annotation element rows
1042                   // TODO: compare props.
1043                 } else {
1044                   jan = getjAlignmentAnnotation(jal, an[j]);
1045                   jal.addAnnotation(jan);
1046                   bindjvvobj(jan, an[j]);
1047                 }
1048               }
1049             }
1050             AlignFrame alignFrame;
1051             if (av==null) {
1052               Cache.log.debug("New alignframe for alignment "+alignment.getVorbaId());
1053               // ///////////////////////////////
1054               // construct alignment view
1055               alignFrame = new AlignFrame(jal);
1056               av=alignFrame.getViewport();
1057               // TODO: automatically create meaningful title for a vamsas alignment using its provenance.
1058               jalview.gui.Desktop.addInternalFrame(alignFrame, alignment.getProvenance().getEntry(alignment.getProvenance().getEntryCount()-1).getAction(),
1059                   AlignFrame.NEW_WINDOW_WIDTH,
1060                   AlignFrame.NEW_WINDOW_HEIGHT);
1061               bindjvvobj(av, alignment);
1062             } else {
1063               // find the alignFrame for jal.
1064               // TODO: fix this so we retrieve the alignFrame handing av *directly*
1065               alignFrame=getAlignFrameFor(av);
1066             }
1067             // LOAD TREES
1068             // /////////////////////////////////////
1069             if (alignment.getTreeCount() > 0)
1070             {
1071
1072               for (int t = 0; t < alignment.getTreeCount(); t++)
1073               {
1074                 Tree tree = alignment.getTree(t);
1075                 TreePanel tp=(TreePanel) getvObj2jv(tree);
1076                 if (tp!=null) {
1077                   Cache.log.info("Update from vamsas document to alignment associated tree not implemented yet.");
1078                 } else {
1079                   // make a new tree
1080                   Object[] idata = this.recoverInputData(tree.getProvenance());
1081                   try {
1082                     AlignmentView inputData=null;
1083                     if (idata!=null && idata[0]!=null)
1084                       inputData = (AlignmentView) idata[0];
1085                     tp = alignFrame.ShowNewickTree(
1086                         new jalview.io.NewickFile(tree.getNewick(0).getContent()),
1087                         tree.getNewick(0).getTitle(),inputData,
1088                         600, 500,
1089                         t * 20 + 50, t * 20 + 50);
1090                     bindjvvobj(tp, tree);
1091                   } catch (Exception e) {
1092                     Cache.log.warn("Problems parsing treefile '"+tree.getNewick(0).getContent()+"'",e);
1093                   }
1094                 }
1095               }
1096             }
1097
1098           }
1099         }
1100       }
1101     }
1102   }
1103   // bitfields - should be a template in j1.5
1104   private static int HASSECSTR=0;
1105   private static int HASVALS=1;
1106   private static int HASHPHOB=2;
1107   private static int HASDC=3;
1108   private static int HASDESCSTR=4;
1109   private static int HASTWOSTATE=5; // not used yet.
1110   /**
1111    * parses the AnnotationElements - if they exist - into jalview.datamodel.Annotation[] rows
1112    * Two annotation rows are made if there are distinct annotation for both at 'pos' and 'after pos' at any particular site.
1113    * @param annotation
1114    * @return { boolean[static int constants ], int[ae.length] - map to annotated object frame, jalview.datamodel.Annotation[], jalview.datamodel.Annotation[] (after)}
1115    */
1116   private Object[] parseRangeAnnotation(org.vamsas.objects.core.RangeAnnotation annotation) {
1117     // set these attributes by looking in the annotation to decide what kind of alignment annotation rows will be made
1118     // TODO: potentially we might make several annotation rows from one vamsas alignment annotation. the jv2Vobj binding mechanism
1119     // may not quite cope with this (without binding an array of annotations to a vamsas alignment annotation)
1120     // summary flags saying what we found over the set of annotation rows.
1121     boolean[] AeContent = new boolean[] { false, false, false, false, false};
1122     int[] rangeMap = getMapping(annotation);
1123     jalview.datamodel.Annotation[][] anot=new jalview.datamodel.Annotation[][] { 
1124         new jalview.datamodel.Annotation[rangeMap.length],
1125         new jalview.datamodel.Annotation[rangeMap.length]
1126     };
1127     boolean mergeable=true; //false  if 'after positions cant be placed on same annotation row as positions. 
1128     
1129     if (annotation.getAnnotationElementCount()>0) {
1130       AnnotationElement ae[] = annotation.getAnnotationElement();
1131       for (int aa = 0; aa < ae.length; aa++)
1132       {
1133         int pos = ae[aa].getPosition()-1;// pos counts from 1 to (|seg.start-seg.end|+1)
1134         if (pos>=0 && pos<rangeMap.length) { 
1135           int row=ae[aa].getAfter()?1:0;
1136           if (anot[row][pos]!=null) {
1137             // only time this should happen is if the After flag is set.
1138             Cache.log.debug("Ignoring duplicate annotation site at "+pos);
1139             continue;
1140           }
1141           if (anot[1-row][pos]!=null)
1142             mergeable=false;
1143           String desc = "";
1144           if (ae[aa].getDescription()!=null) {
1145             desc = ae[aa].getDescription();
1146             if (desc.length()>0) {
1147               // have imported valid description string
1148               AeContent[HASDESCSTR]=true;
1149             }
1150           }
1151           String dc = null;//ae[aa].getDisplayCharacter()==null ? "dc" : ae[aa].getDisplayCharacter();
1152           String ss = null;//ae[aa].getSecondaryStructure()==null ? "ss" : ae[aa].getSecondaryStructure();
1153           java.awt.Color colour = null;
1154           if (ae[aa].getGlyphCount()>0) {
1155             Glyph[] glyphs = ae[aa].getGlyph();
1156             for (int g=0; g<glyphs.length; g++) {
1157               if (glyphs[g].getDict().equals(org.vamsas.objects.utils.GlyphDictionary.PROTEIN_SS_3STATE)) {
1158                 ss=glyphs[g].getContent();
1159                 AeContent[HASSECSTR]=true;
1160               } else if (glyphs[g].getDict().equals(org.vamsas.objects.utils.GlyphDictionary.PROTEIN_HD_HYDRO)) {
1161                 Cache.log.debug("ignoring hydrophobicity glyph marker.");
1162                 AeContent[HASHPHOB]=true;
1163                 char c=(dc=glyphs[g].getContent()).charAt(0);
1164                 // dc may get overwritten - but we still set the colour.
1165                 colour = new java.awt.Color(c=='+'?255:0,c=='.'?255:0,c=='-'?255:0);
1166
1167               } else if (glyphs[g].getDict().equals(org.vamsas.objects.utils.GlyphDictionary.DEFAULT)) {
1168                 dc = glyphs[g].getContent();
1169                 AeContent[HASDC]=true;
1170               } else {
1171                 Cache.log.debug("Ignoring unknown glyph type "+glyphs[g].getDict());
1172               }
1173             }
1174           }
1175           float val=0;
1176           if (ae[aa].getValueCount()>0) {
1177             AeContent[HASVALS]=true;
1178             if (ae[aa].getValueCount()>1) {
1179               Cache.log.warn("ignoring additional "+(ae[aa].getValueCount()-1)+"values in annotation element.");
1180             }
1181             val = ae[aa].getValue(0);
1182           }
1183           if (colour==null) {
1184             anot[row][pos]=new jalview.datamodel.Annotation((dc!=null) ? dc : "", desc, (ss!=null)?ss.charAt(0):' ', val);
1185           } else {
1186             anot[row][pos]=new jalview.datamodel.Annotation((dc!=null) ? dc : "", desc, (ss!=null)?ss.charAt(0):' ', val, colour);
1187           }
1188         } else {
1189           Cache.log.warn("Ignoring out of bound annotation element "+aa+" in "+annotation.getVorbaId().getId());
1190         }
1191       }
1192       // decide on how many annotation rows are needed.
1193       if (mergeable) {
1194         for (int i=0; i<anot[0].length;i++) {
1195           if (anot[1][i]!=null) {
1196             anot[0][i] = anot[1][i];
1197             anot[0][i].description = anot[0][i].description+" (after)";
1198             AeContent[HASDESCSTR]=true; // we have valid description string data
1199             anot[1][i] = null;
1200           }
1201         }
1202         anot[1] = null;
1203       } else {
1204         for (int i=0; i<anot[0].length;i++) {
1205           anot[1][i].description = anot[1][i].description+" (after)";
1206         }
1207       }
1208       return new Object[] { AeContent, rangeMap, anot[0], anot[1] };
1209     } else {
1210       // no annotations to parse.
1211     }
1212     return null;
1213   }
1214   /**
1215    * @param jal the jalview alignment to which the annotation will be attached (ideally - freshly updated from corresponding vamsas alignment)
1216    * @param annotation
1217    * @return unbound jalview alignment annotation object.
1218    */
1219   private jalview.datamodel.AlignmentAnnotation getjAlignmentAnnotation(jalview.datamodel.AlignmentI jal, org.vamsas.objects.core.RangeAnnotation annotation) {
1220     jalview.datamodel.AlignmentAnnotation jan =null;
1221     if (annotation==null)
1222       return null;
1223     // boolean hasSequenceRef=annotation.getClass().equals(org.vamsas.objects.core.AlignmentSequenceAnnotation.class);
1224     //boolean hasProvenance=hasSequenceRef || (annotation.getClass().equals(org.vamsas.objects.core.AlignmentAnnotation.class));
1225     /*int se[] = getBounds(annotation);
1226     if (se==null)
1227       se=new int[] {0,jal.getWidth()-1};
1228      */
1229     Object[] parsedRangeAnnotation = parseRangeAnnotation(annotation);
1230     String a_label=annotation.getLabel();
1231     String a_descr=annotation.getDescription();
1232     if (a_label==null || a_label.length()==0) {
1233       a_label = annotation.getType();
1234       if (a_label.length()==0)
1235         a_label = "Unamed annotation";
1236     }
1237     if (a_descr==null || a_descr.length()==0) {
1238       a_descr = "Annotation of type '"+annotation.getType()+"'";
1239     }
1240     if (parsedRangeAnnotation!=null) {
1241       if (parsedRangeAnnotation[3]!=null) {
1242         Cache.log.warn("Ignoring 'After' annotation row in "+annotation.getVorbaId());
1243       }
1244       jalview.datamodel.Annotation[] arow = (jalview.datamodel.Annotation[]) parsedRangeAnnotation[2];
1245       boolean[] has=(boolean[])parsedRangeAnnotation[0];
1246       // VAMSAS: getGraph is only on derived annotation for alignments - in this way its 'odd' - there is already an existing TODO about removing this flag as being redundant
1247       /*if ((annotation.getClass().equals(org.vamsas.objects.core.AlignmentAnnotation.class) && ((org.vamsas.objects.core.AlignmentAnnotation)annotation).getGraph())
1248           || (hasSequenceRef=true && ((org.vamsas.objects.core.AlignmentSequenceAnnotation)annotation).getGraph())) {
1249       */
1250       if (has[HASVALS]) {
1251         // make bounds and automatic description strings for jalview user's benefit (these shouldn't be written back to vamsas document)
1252         boolean first=true;
1253         float min=0,max=1;
1254         for (int i=0;i<arow.length; i++) {
1255           if (arow[i]!=null) {
1256             // check range - shouldn't we have a min and max property in the annotation object ?
1257             if (first) { min=max=arow[i].value; first=false;}
1258             else { if (arow[i].value<min) { min=arow[i].value; }
1259             else if (arow[i].value>max) { max=arow[i].value; }
1260             }
1261             // make tooltip and display char value
1262             if (!has[HASDESCSTR]) arow[i].description = arow[i].value + "";
1263             if (!has[HASDC]) arow[i].displayCharacter=arow[i].value+"";
1264           }
1265         }
1266         int type=jalview.datamodel.AlignmentAnnotation.LINE_GRAPH;
1267         if (has[HASHPHOB]) {
1268           type = jalview.datamodel.AlignmentAnnotation.BAR_GRAPH;
1269         }
1270         jan = new jalview.datamodel.AlignmentAnnotation(a_label, a_descr, arow, min, max, type);
1271       } else {
1272         jan = new jalview.datamodel.AlignmentAnnotation(a_label, a_descr, arow);
1273       }
1274       if (annotation.getLinkCount()>0) {
1275         Cache.log.warn("Ignoring "+annotation.getLinkCount()+"links added to AlignmentAnnotation.");
1276       }
1277       if (annotation.getModifiable()) {
1278         jan.editable=true;
1279       }
1280
1281       if (annotation.getPropertyCount()>0) {
1282         // look for special jalview properties
1283         org.vamsas.objects.core.Property[] props=annotation.getProperty();
1284         for (int p=0;p<props.length; p++) {
1285           if (props[p].getName().equalsIgnoreCase("jalview:graphType")) {
1286             try { 
1287               // probably a jalview annotation graph so recover the visualization hints.
1288               jan.graph = jalview.datamodel.AlignmentAnnotation.getGraphValueFromString(props[p].getContent());
1289             } catch (Exception e) {
1290               Cache.log.debug("Invalid graph type value in jalview:graphType property.");
1291             }
1292             try {
1293               if (annotation.getGroup()!=null && annotation.getGroup().length()>0)
1294                 jan.graphGroup = Integer.parseInt(annotation.getGroup());
1295             } catch (Exception e) {
1296               Cache.log.info("UNIMPLEMENTED : Couldn't parse non-integer group value for setting graphGroup correctly.");
1297             }
1298           }
1299         }
1300       }
1301
1302       return jan;
1303
1304     } else {
1305       Cache.log.debug("(Ignoring so ... not) Inserting empty annotation row for whole-alignment annotation.");
1306     }
1307
1308     return null;
1309   }
1310
1311   private SequenceFeature getJalviewSeqFeature(RangeAnnotation dseta) {
1312     int[] se = getBounds(dseta);
1313     SequenceFeature sf = new jalview.datamodel.SequenceFeature(dseta.getType(),
1314         dseta.getDescription(), dseta.getStatus(), se[0], se[1], dseta
1315         .getGroup());
1316     if (dseta.getLinkCount() > 0)
1317     {
1318       Link[] links = dseta.getLink();
1319       for (int i = 0; i < links.length; i++)
1320       {
1321         sf.addLink(links[i].getContent() + "|" + links[i].getHref());
1322       }
1323     }
1324     return sf;
1325   }
1326
1327   /**
1328    * get real bounds of a RangeType's specification. start and end are an
1329    * inclusive range within which all segments and positions lie.
1330    * TODO: refactor to vamsas utils
1331    * @param dseta
1332    * @return int[] { start, end}
1333    */
1334   private int[] getBounds(RangeType dseta) {
1335     if (dseta != null)
1336     {
1337       int[] se = null;
1338       if (dseta.getSegCount()>0 && dseta.getPosCount()>0)
1339         throw new Error("Invalid vamsas RangeType - cannot resolve both lists of Pos and Seg from choice!");
1340       if (dseta.getSegCount() > 0)
1341       {
1342         se = getSegRange(dseta.getSeg(0),true);
1343         for (int s = 1, sSize = dseta.getSegCount(); s < sSize; s++)
1344         {
1345           int nse[] = getSegRange(dseta.getSeg(s), true);
1346           if (se[0] > nse[0])
1347             se[0] = nse[0];
1348           if (se[1] < nse[1])
1349             se[1] = nse[1];
1350         }
1351       }
1352       if (dseta.getPosCount() > 0)
1353       {
1354         // could do a polarity for pos range too. and pass back indication of discontinuities.
1355         int pos = dseta.getPos(0).getI();
1356         se = new int[] { pos, pos };
1357         for (int p = 0, pSize = dseta.getPosCount(); p < pSize; p++)
1358         {
1359           pos = dseta.getPos(p).getI();
1360           if (se[0] > pos)
1361             se[0] = pos;
1362           if (se[1] < pos)
1363             se[1] = pos;
1364         }
1365       }
1366       return se;
1367     }
1368     return null;
1369   }
1370   /**
1371    * map from a rangeType's internal frame to the referenced object's coordinate frame.
1372    * @param dseta
1373    * @return int [] { ref(pos)...} for all pos in rangeType's frame.
1374    */
1375   private int[] getMapping(RangeType dseta) {
1376     Vector posList=new Vector();
1377     if (dseta != null)
1378     {
1379       int[] se = null;
1380       if (dseta.getSegCount()>0 && dseta.getPosCount()>0)
1381         throw new Error("Invalid vamsas RangeType - cannot resolve both lists of Pos and Seg from choice!");
1382       if (dseta.getSegCount() > 0)
1383       {
1384         for (int s = 0, sSize = dseta.getSegCount(); s < sSize; s++)
1385         {
1386           se = getSegRange(dseta.getSeg(s), false);
1387           int se_end=se[1-se[2]]+(se[2]==0 ? 1 : -1);
1388           for (int p=se[se[2]]; p!=se_end; p+=se[2]==0 ? 1 : -1 ) {
1389             posList.add(Integer.valueOf(p));
1390           }
1391         }
1392       } 
1393       else if (dseta.getPosCount() > 0)
1394       {
1395         int pos = dseta.getPos(0).getI();
1396
1397         for (int p = 0, pSize = dseta.getPosCount(); p < pSize; p++)
1398         {
1399           pos = dseta.getPos(p).getI();
1400           posList.add(Integer.valueOf(pos));
1401         }
1402       }
1403     }
1404     if (posList!=null && posList.size()>0) {
1405       int[] range=new int[posList.size()];
1406       for (int i=0; i<range.length; i++)
1407         range[i] = ((Integer)posList.elementAt(i)).intValue();
1408       posList.clear();
1409       return range;
1410     }
1411     return null;
1412   }
1413   /* not needed now. 
1414    * Provenance getVamsasProvenance(jalview.datamodel.Provenance jprov) {
1415     jalview.datamodel.ProvenanceEntry[] entries = null;
1416     // TODO: fix App and Action here.
1417     Provenance prov = new Provenance();
1418     org.exolab.castor.types.Date date = new org.exolab.castor.types.Date(
1419         new java.util.Date());
1420     Entry provEntry;
1421
1422     if (jprov != null)
1423     {
1424       entries = jprov.getEntries();
1425       for (int i = 0; i < entries.length; i++)
1426       {
1427         provEntry = new Entry();
1428         try
1429         {
1430           date = new org.exolab.castor.types.Date(entries[i].getDate());
1431         } catch (Exception ex)
1432         {
1433           ex.printStackTrace();
1434
1435           date = new org.exolab.castor.types.Date(entries[i].getDate());
1436         }
1437         provEntry.setDate(date);
1438         provEntry.setUser(entries[i].getUser());
1439         provEntry.setAction(entries[i].getAction());
1440         prov.addEntry(provEntry);
1441       }
1442     }
1443     else
1444     {
1445       provEntry = new Entry();
1446       provEntry.setDate(date);
1447       provEntry.setUser(System.getProperty("user.name")); // TODO: ext string
1448       provEntry.setApp("JVAPP"); // TODO: ext string
1449       provEntry.setAction(action);
1450       prov.addEntry(provEntry);
1451     }
1452
1453     return prov;
1454   }
1455    */
1456   jalview.datamodel.Provenance getJalviewProvenance(Provenance prov) {
1457     // TODO: fix App and Action entries and check use of provenance in jalview.
1458     jalview.datamodel.Provenance jprov = new jalview.datamodel.Provenance();
1459     for (int i = 0; i < prov.getEntryCount(); i++)
1460     {
1461       jprov.addEntry(prov.getEntry(i).getUser(), prov.getEntry(i).getAction(),
1462           prov.getEntry(i).getDate().toDate(), prov.getEntry(i).getId());
1463     }
1464
1465     return jprov;
1466   }
1467
1468   /**
1469    * 
1470    * @return default initial provenance list for a Jalview created vamsas
1471    *         object.
1472    */
1473   Provenance dummyProvenance() {
1474     return dummyProvenance(null);
1475   }
1476
1477   Entry dummyPEntry(String action) {
1478     Entry entry = new Entry();
1479     entry.setApp(this.provEntry.getApp());
1480     if (action != null)
1481       entry.setAction(action);
1482     else
1483       entry.setAction("created.");
1484     entry.setDate(new org.exolab.castor.types.Date(new java.util.Date()));
1485     entry.setUser(this.provEntry.getUser());
1486     return entry;
1487   }
1488
1489   Provenance dummyProvenance(String action) {
1490     Provenance prov = new Provenance();
1491     prov.addEntry(dummyPEntry(action));
1492     return prov;
1493   }
1494
1495   void addProvenance(Provenance p, String action) {
1496     p.addEntry(dummyPEntry(action));
1497   }
1498
1499 }