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