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