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