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