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