2a05ffd1bb4804bc16723b20602a27c625e89580
[jalview.git] / src / jalview / io / VamsasAppDatastore.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.io;
22
23 import jalview.bin.Console;
24 import jalview.datamodel.AlignedCodonFrame;
25 import jalview.datamodel.AlignmentAnnotation;
26 import jalview.datamodel.GraphLine;
27 import jalview.datamodel.SequenceI;
28 import jalview.gui.AlignFrame;
29 import jalview.gui.AlignViewport;
30 import jalview.gui.Desktop;
31 import jalview.gui.TreePanel;
32 import jalview.io.vamsas.Datasetsequence;
33 import jalview.io.vamsas.DatastoreItem;
34 import jalview.io.vamsas.DatastoreRegistry;
35 import jalview.io.vamsas.Rangetype;
36 import jalview.project.Jalview2XML;
37 import jalview.util.MessageManager;
38 import jalview.util.jarInputStreamProvider;
39 import jalview.viewmodel.AlignmentViewport;
40
41 import java.io.File;
42 import java.io.IOException;
43 import java.util.Enumeration;
44 import java.util.HashMap;
45 import java.util.Hashtable;
46 import java.util.IdentityHashMap;
47 import java.util.Iterator;
48 import java.util.List;
49 import java.util.Vector;
50 import java.util.jar.JarInputStream;
51 import java.util.jar.JarOutputStream;
52
53 import uk.ac.vamsas.client.IClientAppdata;
54 import uk.ac.vamsas.client.IClientDocument;
55 import uk.ac.vamsas.client.Vobject;
56 import uk.ac.vamsas.client.VorbaId;
57 import uk.ac.vamsas.objects.core.Alignment;
58 import uk.ac.vamsas.objects.core.AlignmentSequence;
59 import uk.ac.vamsas.objects.core.AlignmentSequenceAnnotation;
60 import uk.ac.vamsas.objects.core.AnnotationElement;
61 import uk.ac.vamsas.objects.core.DataSet;
62 import uk.ac.vamsas.objects.core.DataSetAnnotations;
63 import uk.ac.vamsas.objects.core.DbRef;
64 import uk.ac.vamsas.objects.core.Entry;
65 import uk.ac.vamsas.objects.core.Glyph;
66 import uk.ac.vamsas.objects.core.Local;
67 import uk.ac.vamsas.objects.core.MapType;
68 import uk.ac.vamsas.objects.core.Mapped;
69 import uk.ac.vamsas.objects.core.Property;
70 import uk.ac.vamsas.objects.core.Provenance;
71 import uk.ac.vamsas.objects.core.RangeAnnotation;
72 import uk.ac.vamsas.objects.core.RangeType;
73 import uk.ac.vamsas.objects.core.Seg;
74 import uk.ac.vamsas.objects.core.Sequence;
75 import uk.ac.vamsas.objects.core.SequenceType;
76 import uk.ac.vamsas.objects.core.VAMSAS;
77 import uk.ac.vamsas.objects.utils.Properties;
78
79 /*
80  *
81  * static {
82  * org.exolab.castor.util.LocalConfiguration.getInstance().getProperties().setProperty(
83  * "org.exolab.castor.serializer", "org.apache.xml.serialize.XMLSerilazizer"); }
84  *
85  */
86 /*
87  * TODO: check/verify consistency for vamsas sync with group associated alignment annotation
88  */
89 public class VamsasAppDatastore
90 {
91   /**
92    * Type used for general jalview generated annotation added to vamsas document
93    */
94   public static final String JALVIEW_ANNOTATION_ROW = "JalviewAnnotation";
95
96   /**
97    * AlignmentAnnotation property to indicate that values should not be
98    * interpolated
99    */
100   public static final String DISCRETE_ANNOTATION = "discrete";
101
102   /**
103    * continuous property - optional to specify that annotation should be
104    * represented as a continous graph line
105    */
106   private static final String CONTINUOUS_ANNOTATION = "continuous";
107
108   private static final String THRESHOLD = "threshold";
109
110   /**
111    * template for provenance entries written to vamsas session document
112    */
113   Entry provEntry = null;
114
115   /**
116    * Instance of the session document being synchronized with
117    */
118   IClientDocument cdoc;
119
120   /**
121    * map Vorba (vamsas object xml ref) IDs to live jalview object references
122    */
123   Hashtable vobj2jv;
124
125   /**
126    * map live jalview object references to Vorba IDs
127    */
128   IdentityHashMap jv2vobj;
129
130   /**
131    * map jalview sequence set ID (which is vorba ID for alignment) to last
132    * recorded hash value for the alignment viewport (the undo/redo hash value)
133    */
134   Hashtable alignRDHash;
135
136   public VamsasAppDatastore(IClientDocument cdoc, Hashtable vobj2jv,
137           IdentityHashMap jv2vobj, Entry provEntry, Hashtable alignRDHash)
138   {
139     this.cdoc = cdoc;
140     this.vobj2jv = vobj2jv;
141     this.jv2vobj = jv2vobj;
142     this.provEntry = provEntry;
143     this.alignRDHash = alignRDHash;
144     buildSkipList();
145   }
146
147   /**
148    * the skipList used to skip over views from Jalview Appdata's that we've
149    * already syncrhonized
150    */
151   Hashtable skipList;
152
153   private void buildSkipList()
154   {
155     skipList = new Hashtable();
156     AlignFrame[] al = Desktop.getAlignFrames();
157     for (int f = 0; al != null && f < al.length; f++)
158     {
159       skipList.put(al[f].getViewport().getSequenceSetId(), al[f]);
160     }
161   }
162
163   /**
164    * @return the Vobject bound to Jalview datamodel object
165    */
166   protected Vobject getjv2vObj(Object jvobj)
167   {
168     if (jv2vobj.containsKey(jvobj))
169     {
170       return cdoc.getObject((VorbaId) jv2vobj.get(jvobj));
171     }
172     // check if we're working with a string - then workaround
173     // the use of IdentityHashTable because different strings
174     // have different object IDs.
175     if (jvobj instanceof String)
176     {
177       Object seqsetidobj = null;
178       seqsetidobj = getVamsasObjectBinding().get(jvobj);
179       if (seqsetidobj != null)
180       {
181         if (seqsetidobj instanceof String)
182         {
183           // what is expected. object returned by av.getSequenceSetId() -
184           // reverse lookup to get the 'registered' instance of this string
185           Vobject obj = getjv2vObj(seqsetidobj);
186           if (obj != null && !(obj instanceof Alignment))
187           {
188             Console.warn(
189                     "IMPLEMENTATION ERROR?: Unexpected mapping for unmapped jalview string object content:"
190                             + seqsetidobj + " to object " + obj);
191           }
192           return obj;
193         }
194         else
195         {
196           Console.warn("Unexpected mapping for Jalview String Object ID "
197                   + seqsetidobj + " to another jalview dataset object "
198                   + seqsetidobj);
199         }
200       }
201     }
202
203     if (Console.isDebugEnabled())
204     {
205       Console.debug(
206               "Returning null VorbaID binding for jalview object " + jvobj);
207     }
208     return null;
209   }
210
211   /**
212    * 
213    * @param vobj
214    * @return Jalview datamodel object bound to the vamsas document object
215    */
216   protected Object getvObj2jv(uk.ac.vamsas.client.Vobject vobj)
217   {
218     VorbaId id = vobj.getVorbaId();
219     if (id == null)
220     {
221       id = cdoc.registerObject(vobj);
222       Console.debug(
223               "Registering new object and returning null for getvObj2jv");
224       return null;
225     }
226     if (vobj2jv.containsKey(vobj.getVorbaId()))
227     {
228       return vobj2jv.get(vobj.getVorbaId());
229     }
230     return null;
231   }
232
233   protected void bindjvvobj(Object jvobj, uk.ac.vamsas.client.Vobject vobj)
234   {
235     VorbaId id = vobj.getVorbaId();
236     if (id == null)
237     {
238       id = cdoc.registerObject(vobj);
239       if (id == null || vobj.getVorbaId() == null
240               || cdoc.getObject(id) != vobj)
241       {
242         Console.error("Failed to get id for "
243                 + (vobj.isRegisterable() ? "registerable"
244                         : "unregisterable")
245                 + " object " + vobj);
246       }
247     }
248
249     if (vobj2jv.containsKey(vobj.getVorbaId())
250             && !((VorbaId) vobj2jv.get(vobj.getVorbaId())).equals(jvobj))
251     {
252       Console.debug(
253               "Warning? Overwriting existing vamsas id binding for "
254                       + vobj.getVorbaId(),
255               new Exception(MessageManager.getString(
256                       "exception.overwriting_vamsas_id_binding")));
257     }
258     else if (jv2vobj.containsKey(jvobj)
259             && !((VorbaId) jv2vobj.get(jvobj)).equals(vobj.getVorbaId()))
260     {
261       Console.debug(
262               "Warning? Overwriting existing jalview object binding for "
263                       + jvobj,
264               new Exception("Overwriting jalview object binding."));
265     }
266     /*
267      * Cache.error("Attempt to make conflicting object binding! "+vobj+" id "
268      * +vobj.getVorbaId()+" already bound to "+getvObj2jv(vobj)+" and "+jvobj+"
269      * already bound to "+getjv2vObj(jvobj),new Exception("Excessive call to
270      * bindjvvobj")); }
271      */
272     // we just update the hash's regardless!
273     Console.debug("Binding " + vobj.getVorbaId() + " to " + jvobj);
274     vobj2jv.put(vobj.getVorbaId(), jvobj);
275     // JBPNote - better implementing a hybrid invertible hash.
276     jv2vobj.put(jvobj, vobj.getVorbaId());
277   }
278
279   /**
280    * put the alignment viewed by AlignViewport into cdoc.
281    * 
282    * @param av
283    *          alignViewport to be stored
284    * @param aFtitle
285    *          title for alignment
286    * @return true if alignment associated with viewport was stored/synchronized
287    *         to document
288    */
289   public boolean storeVAMSAS(AlignViewport av, String aFtitle)
290   {
291     try
292     {
293       jalview.datamodel.AlignmentI jal = av.getAlignment();
294       jalview.datamodel.AlignmentI jds = jal.getDataset();
295       boolean nw = false;
296       VAMSAS root = null; // will be resolved based on Dataset Parent.
297       // /////////////////////////////////////////
298       // SAVE THE DATASET
299       DataSet dataset = null;
300       if (jds == null)
301       {
302         Console.warn("Creating new dataset for an alignment.");
303         jal.setDataset(null);
304         jds = jal.getDataset();
305       }
306
307       // try and get alignment and association for sequence set id
308
309       Alignment alignment = (Alignment) getjv2vObj(av.getSequenceSetId());
310       if (alignment != null)
311       {
312         dataset = (DataSet) alignment.getV_parent();
313       }
314       else
315       {
316         // is the dataset already registered
317         dataset = (DataSet) getjv2vObj(jds);
318       }
319
320       if (dataset == null)
321       {
322         // it might be that one of the dataset sequences does actually have a
323         // binding, so search for it indirectly. If it does, then the local
324         // jalview dataset
325         // must be merged with the existing vamsas dataset.
326         jalview.datamodel.SequenceI[] jdatset = jds.getSequencesArray();
327         for (int i = 0; i < jdatset.length; i++)
328         {
329           Vobject vbound = getjv2vObj(jdatset[i]);
330           if (vbound != null)
331           {
332             if (vbound instanceof uk.ac.vamsas.objects.core.Sequence)
333             {
334               if (dataset == null)
335               {
336                 dataset = (DataSet) vbound.getV_parent();
337               }
338               else
339               {
340                 if (vbound.getV_parent() != null
341                         && dataset != vbound.getV_parent())
342                 {
343                   throw new Error(
344                           "IMPLEMENTATION ERROR: Cannot map an alignment of sequences from different datasets into a single alignment in the vamsas document.");
345                 }
346               }
347             }
348           }
349         }
350       }
351
352       if (dataset == null)
353       {
354         Console.warn("Creating new vamsas dataset for alignment view "
355                 + av.getSequenceSetId());
356         // we create a new dataset on the default vamsas root.
357         root = cdoc.getVamsasRoots()[0]; // default vamsas root for modifying.
358         dataset = new DataSet();
359         root.addDataSet(dataset);
360         bindjvvobj(jds, dataset);
361         dataset.setProvenance(dummyProvenance());
362         // dataset.getProvenance().addEntry(provEntry);
363         nw = true;
364       }
365       else
366       {
367         root = (VAMSAS) dataset.getV_parent();
368       }
369       // update dataset
370       Sequence sequence;
371       // set new dataset and alignment sequences based on alignment Nucleotide
372       // flag.
373       // this *will* break when alignment contains both nucleotide and amino
374       // acid sequences.
375       String dict = jal.isNucleotide()
376               ? uk.ac.vamsas.objects.utils.SymbolDictionary.STANDARD_NA
377               : uk.ac.vamsas.objects.utils.SymbolDictionary.STANDARD_AA;
378       Vector dssmods = new Vector();
379       for (int i = 0; i < jal.getHeight(); i++)
380       {
381         SequenceI sq = jal.getSequenceAt(i).getDatasetSequence(); // only insert
382         // referenced
383         // sequences
384         // to dataset.
385         Datasetsequence dssync = new jalview.io.vamsas.Datasetsequence(this,
386                 sq, dict, dataset);
387         sequence = (Sequence) dssync.getVobj();
388         if (dssync.getModified())
389         {
390           dssmods.addElement(sequence);
391         }
392         ;
393       }
394       if (dssmods.size() > 0)
395       {
396         if (!nw)
397         {
398           Entry pentry = this.addProvenance(dataset.getProvenance(),
399                   "updated sequences");
400           // pentry.addInput(vInput); could write in which sequences were
401           // modified.
402           dssmods.removeAllElements();
403         }
404       }
405       // dataset.setProvenance(getVamsasProvenance(jal.getDataset().getProvenance()));
406       // ////////////////////////////////////////////
407       if (alignmentWillBeSkipped(av))
408       {
409         // TODO: trees could be written - but for the moment we just
410         addToSkipList(av);
411         // add to the JalviewXML skipList and ..
412         return false;
413       }
414
415       if (alignment == null)
416       {
417         alignment = new Alignment();
418         bindjvvobj(av.getSequenceSetId(), alignment);
419         if (alignment.getProvenance() == null)
420         {
421           alignment.setProvenance(new Provenance());
422         }
423         addProvenance(alignment.getProvenance(), "added"); // TODO: insert some
424         // sensible source
425         // here
426         dataset.addAlignment(alignment);
427         {
428           Property title = new Property();
429           title.setName("title");
430           title.setType("string");
431           title.setContent(aFtitle);
432           alignment.addProperty(title);
433         }
434         alignment.setGapChar(String.valueOf(av.getGapCharacter()));
435         for (int i = 0; i < jal.getHeight(); i++)
436         {
437           syncToAlignmentSequence(jal.getSequenceAt(i), alignment, null);
438         }
439         alignRDHash.put(av.getSequenceSetId(), av.getUndoRedoHash());
440       }
441       else
442       {
443         // always prepare to clone the alignment
444         boolean alismod = av.isUndoRedoHashModified(
445                 (long[]) alignRDHash.get(av.getSequenceSetId()));
446         // todo: verify and update mutable alignment props.
447         // TODO: Use isLocked methods
448         if (alignment.getModifiable() == null
449                 || alignment.getModifiable().length() == 0)
450         // && !alignment.isDependedOn())
451         {
452           boolean modified = false;
453           // check existing sequences in local and in document.
454           Vector docseqs = new Vector(
455                   alignment.getAlignmentSequenceAsReference());
456           for (int i = 0; i < jal.getHeight(); i++)
457           {
458             modified |= syncToAlignmentSequence(jal.getSequenceAt(i),
459                     alignment, docseqs);
460           }
461           if (docseqs.size() > 0)
462           {
463             // removeValignmentSequences(alignment, docseqs);
464             docseqs.removeAllElements();
465             System.out.println(
466                     "Sequence deletion from alignment is not implemented.");
467
468           }
469           if (modified)
470           {
471             if (alismod)
472             {
473               // info in the undo
474               addProvenance(alignment.getProvenance(), "Edited"); // TODO:
475               // insert
476               // something
477               // sensible
478               // here again
479             }
480             else
481             {
482               // info in the undo
483               addProvenance(alignment.getProvenance(), "Attributes Edited"); // TODO:
484               // insert
485               // something
486               // sensible
487               // here
488               // again
489             }
490           }
491           if (alismod)
492           {
493             System.out.println("update alignment in document.");
494           }
495           else
496           {
497             System.out.println("alignment in document left unchanged.");
498           }
499         }
500         else
501         {
502           // unbind alignment from view.
503           // create new binding and new alignment.
504           // mark trail on new alignment as being derived from old ?
505           System.out.println(
506                   "update edited alignment to new alignment in document.");
507         }
508       }
509       // ////////////////////////////////////////////
510       // SAVE Alignment Sequence Features
511       for (int i = 0, iSize = alignment
512               .getAlignmentSequenceCount(); i < iSize; i++)
513       {
514         AlignmentSequence valseq;
515         SequenceI alseq = (SequenceI) getvObj2jv(
516                 valseq = alignment.getAlignmentSequence(i));
517         if (alseq != null && alseq.getSequenceFeatures() != null)
518         {
519           /*
520            * We do not put local Alignment Sequence Features into the vamsas
521            * document yet.
522            * 
523            * 
524            * jalview.datamodel.SequenceFeature[] features = alseq
525            * .getSequenceFeatures(); for (int f = 0; f < features.length; f++) {
526            * if (features[f] != null) { AlignmentSequenceAnnotation valseqf = (
527            * AlignmentSequenceAnnotation) getjv2vObj(features[i]); if (valseqf
528            * == null) {
529            * 
530            * valseqf = (AlignmentSequenceAnnotation) getDSAnnotationFromJalview(
531            * new AlignmentSequenceAnnotation(), features[i]);
532            * valseqf.setGraph(false);
533            * valseqf.addProperty(newProperty("jalview:feature"
534            * ,"boolean","true")); if (valseqf.getProvenance() == null) {
535            * valseqf.setProvenance(new Provenance()); }
536            * addProvenance(valseqf.getProvenance(), "created"); // JBPNote - //
537            * need to // update bindjvvobj(features[i], valseqf);
538            * valseq.addAlignmentSequenceAnnotation(valseqf); } } }
539            */
540         }
541       }
542
543       // ////////////////////////////////////////////
544       // SAVE ANNOTATIONS
545       if (jal.getAlignmentAnnotation() != null)
546       {
547         jalview.datamodel.AlignmentAnnotation[] aa = jal
548                 .getAlignmentAnnotation();
549         java.util.HashMap AlSeqMaps = new HashMap(); // stores int maps from
550         // alignment columns to
551         // sequence positions.
552         for (int i = 0; i < aa.length; i++)
553         {
554           if (aa[i] == null || isJalviewOnly(aa[i]))
555           {
556             continue;
557           }
558           if (aa[i].groupRef != null)
559           {
560             // TODO: store any group associated annotation references
561             Console.warn(
562                     "Group associated sequence annotation is not stored in VAMSAS document.");
563             continue;
564           }
565           if (aa[i].sequenceRef != null)
566           {
567             // Deal with sequence associated annotation
568             Vobject sref = getjv2vObj(aa[i].sequenceRef);
569             if (sref instanceof uk.ac.vamsas.objects.core.AlignmentSequence)
570             {
571               saveAlignmentSequenceAnnotation(AlSeqMaps,
572                       (AlignmentSequence) sref, aa[i]);
573             }
574             else
575             {
576               // first find the alignment sequence to associate this with.
577               for (SequenceI jvalsq : av.getAlignment().getSequences())
578               {
579                 // saveDatasetSequenceAnnotation(AlSeqMaps,(uk.ac.vamsas.objects.core.Sequence)
580                 // sref, aa[i]);
581                 if (jvalsq.getDatasetSequence() == aa[i].sequenceRef)
582                 {
583                   Vobject alsref = getjv2vObj(jvalsq);
584                   saveAlignmentSequenceAnnotation(AlSeqMaps,
585                           (AlignmentSequence) alsref, aa[i]);
586                   break;
587                 }
588                 ;
589               }
590             }
591           }
592           else
593           {
594             // add Alignment Annotation
595             uk.ac.vamsas.objects.core.AlignmentAnnotation an = (uk.ac.vamsas.objects.core.AlignmentAnnotation) getjv2vObj(
596                     aa[i]);
597             if (an == null)
598             {
599               an = new uk.ac.vamsas.objects.core.AlignmentAnnotation();
600               an.setType(JALVIEW_ANNOTATION_ROW);
601               an.setDescription(aa[i].description);
602               alignment.addAlignmentAnnotation(an);
603               Seg vSeg = new Seg(); // TODO: refactor to have a default
604               // rangeAnnotationType initer/updater that
605               // takes a set of int ranges.
606               vSeg.setStart(1);
607               vSeg.setInclusive(true);
608               vSeg.setEnd(jal.getWidth());
609               an.addSeg(vSeg);
610               if (aa[i].graph > 0)
611               {
612                 an.setGraph(true); // aa[i].graph);
613               }
614               an.setLabel(aa[i].label);
615               an.setProvenance(dummyProvenance());
616               if (aa[i].graph != AlignmentAnnotation.NO_GRAPH)
617               {
618                 an.setGroup(Integer.toString(aa[i].graphGroup)); // // JBPNote
619                 // -
620                 // originally we
621                 // were going to
622                 // store
623                 // graphGroup in
624                 // the Jalview
625                 // specific
626                 // bits.
627                 an.setGraph(true);
628               }
629               else
630               {
631                 an.setGraph(false);
632               }
633               AnnotationElement ae;
634
635               for (int a = 0; a < aa[i].annotations.length; a++)
636               {
637                 if ((aa[i] == null) || (aa[i].annotations[a] == null))
638                 {
639                   continue;
640                 }
641
642                 ae = new AnnotationElement();
643                 ae.setDescription(aa[i].annotations[a].description);
644                 ae.addGlyph(new Glyph());
645                 ae.getGlyph(0)
646                         .setContent(aa[i].annotations[a].displayCharacter); // assume
647                 // jax-b
648                 // takes
649                 // care
650                 // of
651                 // utf8
652                 // translation
653                 if (an.isGraph())
654                 {
655                   ae.addValue(aa[i].annotations[a].value);
656                 }
657                 ae.setPosition(a + 1);
658                 if (aa[i].annotations[a].secondaryStructure != ' ')
659                 {
660                   Glyph ss = new Glyph();
661                   ss.setDict(
662                           uk.ac.vamsas.objects.utils.GlyphDictionary.PROTEIN_SS_3STATE);
663                   ss.setContent(String.valueOf(
664                           aa[i].annotations[a].secondaryStructure));
665                   ae.addGlyph(ss);
666                 }
667                 an.addAnnotationElement(ae);
668               }
669               if (aa[i].editable)
670               {
671                 // an.addProperty(newProperty("jalview:editable", null,
672                 // "true"));
673                 // an.setModifiable(""); // TODO: This is not the way the
674                 // modifiable flag is supposed to be used.
675               }
676               setAnnotationType(an, aa[i]);
677
678               if (aa[i].graph != jalview.datamodel.AlignmentAnnotation.NO_GRAPH)
679               {
680                 an.setGraph(true);
681                 an.setGroup(Integer.toString(aa[i].graphGroup));
682                 if (aa[i].threshold != null && aa[i].threshold.displayed)
683                 {
684                   an.addProperty(Properties.newProperty(THRESHOLD,
685                           Properties.FLOATTYPE,
686                           "" + aa[i].threshold.value));
687                   if (aa[i].threshold.label != null)
688                   {
689                     an.addProperty(Properties.newProperty(
690                             THRESHOLD + "Name", Properties.STRINGTYPE,
691                             "" + aa[i].threshold.label));
692                   }
693                 }
694               }
695
696             }
697
698             else
699             {
700               if (an.getModifiable() == null) // TODO: USE VAMSAS LIBRARY OBJECT
701               // LOCK METHODS)
702               {
703                 // verify annotation - update (perhaps)
704                 Console.info(
705                         "update alignment sequence annotation. not yet implemented.");
706               }
707               else
708               {
709                 // verify annotation - update (perhaps)
710                 Console.info(
711                         "updated alignment sequence annotation added.");
712               }
713             }
714           }
715         }
716       }
717       // /////////////////////////////////////////////////////
718
719       // //////////////////////////////////////////////
720       // /SAVE THE TREES
721       // /////////////////////////////////
722       // FIND ANY ASSOCIATED TREES
723       if (Desktop.getDesktopPane() != null)
724       {
725         javax.swing.JInternalFrame[] frames = Desktop.getInstance()
726                 .getAllFrames();
727
728         for (int t = 0; t < frames.length; t++)
729         {
730           if (frames[t] instanceof TreePanel)
731           {
732             TreePanel tp = (TreePanel) frames[t];
733
734             if (tp.getViewPort().getSequenceSetId()
735                     .equals(av.getSequenceSetId()))
736             {
737               DatastoreItem vtree = new jalview.io.vamsas.Tree(this, tp,
738                       jal, alignment);
739             }
740           }
741         }
742       }
743       // Store Jalview specific stuff in the Jalview appData
744       // not implemented in the SimpleDoc interface.
745     }
746
747     catch (Exception ex)
748     {
749       ex.printStackTrace();
750       return false;
751     }
752     return true;
753   }
754
755   /**
756    * very quick test to see if the viewport would be stored in the vamsas
757    * document. Reasons for not storing include the unaligned flag being false
758    * (for all sequences, including the hidden ones!)
759    * 
760    * @param av
761    * @return true if alignment associated with this view will be stored in
762    *         document.
763    */
764   public boolean alignmentWillBeSkipped(AlignmentViewport av)
765   {
766     return (!av.getAlignment().isAligned());
767   }
768
769   private void addToSkipList(AlignmentViewport av)
770   {
771     if (skipList == null)
772     {
773       skipList = new Hashtable();
774     }
775     skipList.put(av.getSequenceSetId(), av);
776   }
777
778   /**
779    * remove docseqs from the given alignment marking provenance appropriately
780    * and removing any references to the sequences.
781    * 
782    * @param alignment
783    * @param docseqs
784    */
785   private void removeValignmentSequences(Alignment alignment,
786           Vector docseqs)
787   {
788     // delete these from document. This really needs to be a generic document
789     // API function derived by CASTOR.
790     Enumeration en = docseqs.elements();
791     while (en.hasMoreElements())
792     {
793       alignment.removeAlignmentSequence(
794               (AlignmentSequence) en.nextElement());
795     }
796     Entry pe = addProvenance(alignment.getProvenance(),
797             "Removed " + docseqs.size() + " sequences");
798     en = alignment.enumerateAlignmentAnnotation();
799     Vector toremove = new Vector();
800     while (en.hasMoreElements())
801     {
802       uk.ac.vamsas.objects.core.AlignmentAnnotation alan = (uk.ac.vamsas.objects.core.AlignmentAnnotation) en
803               .nextElement();
804       if (alan.getSeqrefsCount() > 0)
805       {
806         int p = 0;
807         Vector storem = new Vector();
808         Enumeration sr = alan.enumerateSeqrefs();
809         while (sr.hasMoreElements())
810         {
811           Object alsr = sr.nextElement();
812           if (docseqs.contains(alsr))
813           {
814             storem.addElement(alsr);
815           }
816         }
817         // remove references to the deleted sequences
818         sr = storem.elements();
819         while (sr.hasMoreElements())
820         {
821           alan.removeSeqrefs(sr.nextElement());
822         }
823
824         if (alan.getSeqrefsCount() == 0)
825         {
826           // should then delete alan from dataset
827           toremove.addElement(alan);
828         }
829       }
830     }
831     // remove any annotation that used to be associated to a specific bunch of
832     // sequences
833     en = toremove.elements();
834     while (en.hasMoreElements())
835     {
836       alignment.removeAlignmentAnnotation(
837               (uk.ac.vamsas.objects.core.AlignmentAnnotation) en
838                       .nextElement());
839     }
840     // TODO: search through alignment annotations to remove any references to
841     // this alignment sequence
842   }
843
844   /**
845    * sync a jalview alignment seuqence into a vamsas alignment assumes all lock
846    * transformation/bindings have been sorted out before hand. creates/syncs the
847    * vamsas alignment sequence for jvalsq and adds it to the alignment if
848    * necessary. unbounddocseq is a duplicate of the vamsas alignment sequences
849    * and these are removed after being processed w.r.t a bound jvalsq
850    * 
851    */
852   private boolean syncToAlignmentSequence(SequenceI jvalsq,
853           Alignment alignment, Vector unbounddocseq)
854   {
855     boolean modal = false;
856     // todo: islocked method here
857     boolean up2doc = false;
858     AlignmentSequence alseq = (AlignmentSequence) getjv2vObj(jvalsq);
859     if (alseq == null)
860     {
861       alseq = new AlignmentSequence();
862       up2doc = true;
863     }
864     else
865     {
866       if (unbounddocseq != null)
867       {
868         unbounddocseq.removeElement(alseq);
869       }
870     }
871     // boolean locked = (alignment.getModifiable()==null ||
872     // alignment.getModifiable().length()>0);
873     // TODO: VAMSAS: translate lowercase symbols to annotation ?
874     if (up2doc || !alseq.getSequence().equals(jvalsq.getSequenceAsString()))
875     {
876       alseq.setSequence(jvalsq.getSequenceAsString());
877       alseq.setStart(jvalsq.getStart());
878       alseq.setEnd(jvalsq.getEnd());
879       modal = true;
880     }
881     if (up2doc || !alseq.getName().equals(jvalsq.getName()))
882     {
883       modal = true;
884       alseq.setName(jvalsq.getName());
885     }
886     if (jvalsq.getDescription() != null && (alseq.getDescription() == null
887             || !jvalsq.getDescription().equals(alseq.getDescription())))
888     {
889       modal = true;
890       alseq.setDescription(jvalsq.getDescription());
891     }
892     if (getjv2vObj(jvalsq.getDatasetSequence()) == null)
893     {
894       Console.warn(
895               "Serious Implementation error - Unbound dataset sequence in alignment: "
896                       + jvalsq.getDatasetSequence());
897     }
898     alseq.setRefid(getjv2vObj(jvalsq.getDatasetSequence()));
899     if (up2doc)
900     {
901
902       alignment.addAlignmentSequence(alseq);
903       bindjvvobj(jvalsq, alseq);
904     }
905     return up2doc || modal;
906   }
907
908   /**
909    * locally sync a jalview alignment seuqence from a vamsas alignment assumes
910    * all lock transformation/bindings have been sorted out before hand.
911    * creates/syncs the jvalsq from the alignment sequence
912    */
913   private boolean syncFromAlignmentSequence(AlignmentSequence valseq,
914           char valGapchar, char gapChar, List<SequenceI> dsseqs)
915
916   {
917     boolean modal = false;
918     // todo: islocked method here
919     boolean upFromdoc = false;
920     jalview.datamodel.SequenceI alseq = (SequenceI) getvObj2jv(valseq);
921     if (alseq == null)
922     {
923       upFromdoc = true;
924     }
925     if (alseq != null)
926     {
927
928       // boolean locked = (alignment.getModifiable()==null ||
929       // alignment.getModifiable().length()>0);
930       // TODO: VAMSAS: translate lowercase symbols to annotation ?
931       if (upFromdoc
932               || !valseq.getSequence().equals(alseq.getSequenceAsString()))
933       {
934         // this might go *horribly* wrong
935         alseq.setSequence(new String(valseq.getSequence())
936                 .replace(valGapchar, gapChar));
937         alseq.setStart((int) valseq.getStart());
938         alseq.setEnd((int) valseq.getEnd());
939         modal = true;
940       }
941       if (!valseq.getName().equals(alseq.getName()))
942       {
943         modal = true;
944         alseq.setName(valseq.getName());
945       }
946       if (alseq.getDescription() == null || (valseq.getDescription() != null
947               && !alseq.getDescription().equals(valseq.getDescription())))
948       {
949         alseq.setDescription(valseq.getDescription());
950         modal = true;
951       }
952       if (modal && Console.isDebugEnabled())
953       {
954         Console.debug(
955                 "Updating apparently edited sequence " + alseq.getName());
956       }
957     }
958     else
959     {
960       alseq = new jalview.datamodel.Sequence(valseq.getName(),
961               valseq.getSequence().replace(valGapchar, gapChar),
962               (int) valseq.getStart(), (int) valseq.getEnd());
963
964       Vobject datsetseq = (Vobject) valseq.getRefid();
965       if (datsetseq != null)
966       {
967         alseq.setDatasetSequence((SequenceI) getvObj2jv(datsetseq)); // exceptions
968         if (valseq.getDescription() != null)
969         {
970           alseq.setDescription(valseq.getDescription());
971         }
972         else
973         {
974           // inherit description line from dataset.
975           if (alseq.getDatasetSequence().getDescription() != null)
976           {
977             alseq.setDescription(
978                     alseq.getDatasetSequence().getDescription());
979           }
980         }
981         // if
982         // AlignemntSequence
983         // reference
984         // isn't
985         // a
986         // simple
987         // SequenceI
988       }
989       else
990       {
991         Console.error(
992                 "Invalid dataset sequence id (null) for alignment sequence "
993                         + valseq.getVorbaId());
994       }
995       bindjvvobj(alseq, valseq);
996       alseq.setVamsasId(valseq.getVorbaId().getId());
997       dsseqs.add(alseq);
998     }
999     Vobject datsetseq = (Vobject) valseq.getRefid();
1000     if (datsetseq != null)
1001     {
1002       if (datsetseq != alseq.getDatasetSequence())
1003       {
1004         modal = true;
1005       }
1006       alseq.setDatasetSequence((SequenceI) getvObj2jv(datsetseq)); // exceptions
1007     }
1008     return upFromdoc || modal;
1009   }
1010
1011   private void initRangeAnnotationType(RangeAnnotation an,
1012           AlignmentAnnotation alan, int[] gapMap)
1013   {
1014     Seg vSeg = new Seg();
1015     vSeg.setStart(1);
1016     vSeg.setInclusive(true);
1017     vSeg.setEnd(gapMap.length);
1018     an.addSeg(vSeg);
1019
1020     // LATER: much of this is verbatim from the alignmentAnnotation
1021     // method below. suggests refactoring to make rangeAnnotation the
1022     // base class
1023     an.setDescription(alan.description);
1024     an.setLabel(alan.label);
1025     an.setGroup(Integer.toString(alan.graphGroup));
1026     // // JBPNote -
1027     // originally we
1028     // were going to
1029     // store
1030     // graphGroup in
1031     // the Jalview
1032     // specific
1033     // bits.
1034     AnnotationElement ae;
1035     for (int a = 0; a < alan.annotations.length; a++)
1036     {
1037       if (alan.annotations[a] == null)
1038       {
1039         continue;
1040       }
1041
1042       ae = new AnnotationElement();
1043       ae.setDescription(alan.annotations[a].description);
1044       ae.addGlyph(new Glyph());
1045       ae.getGlyph(0).setContent(alan.annotations[a].displayCharacter); // assume
1046       // jax-b
1047       // takes
1048       // care
1049       // of
1050       // utf8
1051       // translation
1052       if (alan.graph != jalview.datamodel.AlignmentAnnotation.NO_GRAPH)
1053       {
1054         ae.addValue(alan.annotations[a].value);
1055       }
1056       ae.setPosition(gapMap[a] + 1); // position w.r.t. AlignmentSequence
1057       // symbols
1058       if (alan.annotations[a].secondaryStructure != ' ')
1059       {
1060         // we only write an annotation where it really exists.
1061         Glyph ss = new Glyph();
1062         ss.setDict(
1063                 uk.ac.vamsas.objects.utils.GlyphDictionary.PROTEIN_SS_3STATE);
1064         ss.setContent(
1065                 String.valueOf(alan.annotations[a].secondaryStructure));
1066         ae.addGlyph(ss);
1067       }
1068       an.addAnnotationElement(ae);
1069     }
1070
1071   }
1072
1073   private void saveDatasetSequenceAnnotation(HashMap AlSeqMaps,
1074           uk.ac.vamsas.objects.core.Sequence sref, AlignmentAnnotation alan)
1075   {
1076     // {
1077     // uk.ac.vamsas.
1078     // objects.core.AlignmentSequence alsref = (uk.ac.vamsas.
1079     // objects.core.AlignmentSequence) sref;
1080     uk.ac.vamsas.objects.core.DataSetAnnotations an = (uk.ac.vamsas.objects.core.DataSetAnnotations) getjv2vObj(
1081             alan);
1082     int[] gapMap = getGapMap(AlSeqMaps, alan);
1083     if (an == null)
1084     {
1085       an = new uk.ac.vamsas.objects.core.DataSetAnnotations();
1086       initRangeAnnotationType(an, alan, gapMap);
1087
1088       an.setProvenance(dummyProvenance()); // get provenance as user
1089       // created, or jnet, or
1090       // something else.
1091       setAnnotationType(an, alan);
1092       an.setGroup(Integer.toString(alan.graphGroup)); // // JBPNote -
1093       // originally we
1094       // were going to
1095       // store
1096       // graphGroup in
1097       // the Jalview
1098       // specific
1099       // bits.
1100       if (alan.getThreshold() != null && alan.getThreshold().displayed)
1101       {
1102         an.addProperty(Properties.newProperty(THRESHOLD,
1103                 Properties.FLOATTYPE, "" + alan.getThreshold().value));
1104         if (alan.getThreshold().label != null)
1105         {
1106           an.addProperty(Properties.newProperty(THRESHOLD + "Name",
1107                   Properties.STRINGTYPE, "" + alan.getThreshold().label));
1108         }
1109       }
1110       ((DataSet) sref.getV_parent()).addDataSetAnnotations(an);
1111       bindjvvobj(alan, an);
1112     }
1113     else
1114     {
1115       // update reference sequence Annotation
1116       if (an.getModifiable() == null) // TODO: USE VAMSAS LIBRARY OBJECT LOCK
1117       // METHODS)
1118       {
1119         // verify existing alignment sequence annotation is up to date
1120         System.out.println("update dataset sequence annotation.");
1121       }
1122       else
1123       {
1124         // verify existing alignment sequence annotation is up to date
1125         System.out.println(
1126                 "make new alignment dataset sequence annotation if modification has happened.");
1127       }
1128     }
1129
1130   }
1131
1132   private int[] getGapMap(HashMap AlSeqMaps, AlignmentAnnotation alan)
1133   {
1134     int[] gapMap;
1135     if (AlSeqMaps.containsKey(alan.sequenceRef))
1136     {
1137       gapMap = (int[]) AlSeqMaps.get(alan.sequenceRef);
1138     }
1139     else
1140     {
1141       gapMap = new int[alan.sequenceRef.getLength()];
1142       // map from alignment position to sequence position.
1143       int[] sgapMap = alan.sequenceRef.gapMap();
1144       for (int a = 0; a < sgapMap.length; a++)
1145       {
1146         gapMap[sgapMap[a]] = a;
1147       }
1148     }
1149     return gapMap;
1150   }
1151
1152   private void saveAlignmentSequenceAnnotation(HashMap AlSeqMaps,
1153           AlignmentSequence alsref, AlignmentAnnotation alan)
1154   {
1155     // {
1156     // uk.ac.vamsas.
1157     // objects.core.AlignmentSequence alsref = (uk.ac.vamsas.
1158     // objects.core.AlignmentSequence) sref;
1159     uk.ac.vamsas.objects.core.AlignmentSequenceAnnotation an = (uk.ac.vamsas.objects.core.AlignmentSequenceAnnotation) getjv2vObj(
1160             alan);
1161     int[] gapMap = getGapMap(AlSeqMaps, alan);
1162     if (an == null)
1163     {
1164       an = new uk.ac.vamsas.objects.core.AlignmentSequenceAnnotation();
1165       initRangeAnnotationType(an, alan, gapMap);
1166       /**
1167        * I mean here that we don't actually have a semantic 'type' for the
1168        * annotation (this might be - score, intrinsic property, measurement,
1169        * something extracted from another program, etc)
1170        */
1171       an.setType(JALVIEW_ANNOTATION_ROW); // TODO: better fix
1172       // this rough guess ;)
1173       alsref.addAlignmentSequenceAnnotation(an);
1174       bindjvvobj(alan, an);
1175       // These properties are directly supported by the
1176       // AlignmentSequenceAnnotation type.
1177       setAnnotationType(an, alan);
1178       an.setProvenance(dummyProvenance()); // get provenance as user
1179       // created, or jnet, or
1180       // something else.
1181     }
1182     else
1183     {
1184       // update reference sequence Annotation
1185       if (an.getModifiable() == null) // TODO: USE VAMSAS LIBRARY OBJECT LOCK
1186       // METHODS)
1187       {
1188         // verify existing alignment sequence annotation is up to date
1189         System.out.println("update alignment sequence annotation.");
1190       }
1191       else
1192       {
1193         // verify existing alignment sequence annotation is up to date
1194         System.out.println(
1195                 "make new alignment sequence annotation if modification has happened.");
1196       }
1197     }
1198   }
1199
1200   /**
1201    * set vamsas annotation object type from jalview annotation
1202    * 
1203    * @param an
1204    * @param alan
1205    */
1206   private void setAnnotationType(RangeAnnotation an,
1207           AlignmentAnnotation alan)
1208   {
1209     if (an instanceof AlignmentSequenceAnnotation)
1210     {
1211       if (alan.graph != AlignmentAnnotation.NO_GRAPH)
1212       {
1213         ((AlignmentSequenceAnnotation) an).setGraph(true);
1214       }
1215       else
1216       {
1217         ((AlignmentSequenceAnnotation) an).setGraph(false);
1218       }
1219     }
1220     if (an instanceof uk.ac.vamsas.objects.core.AlignmentAnnotation)
1221     {
1222       if (alan.graph != AlignmentAnnotation.NO_GRAPH)
1223       {
1224         ((uk.ac.vamsas.objects.core.AlignmentAnnotation) an).setGraph(true);
1225       }
1226       else
1227       {
1228         ((uk.ac.vamsas.objects.core.AlignmentAnnotation) an)
1229                 .setGraph(false);
1230       }
1231     }
1232     switch (alan.graph)
1233     {
1234     case AlignmentAnnotation.BAR_GRAPH:
1235       an.addProperty(Properties.newProperty(DISCRETE_ANNOTATION,
1236               Properties.BOOLEANTYPE, "true"));
1237       break;
1238     case AlignmentAnnotation.LINE_GRAPH:
1239       an.addProperty(Properties.newProperty(CONTINUOUS_ANNOTATION,
1240               Properties.BOOLEANTYPE, "true"));
1241       break;
1242     default:
1243       // don't add any kind of discrete or continous property info.
1244     }
1245   }
1246
1247   /**
1248    * get start<end range of segment, adjusting for inclusivity flag and
1249    * polarity.
1250    * 
1251    * @param visSeg
1252    * @param ensureDirection
1253    *          when true - always ensure start is less than end.
1254    * @return int[] { start, end, direction} where direction==1 for range running
1255    *         from end to start.
1256    */
1257   private int[] getSegRange(Seg visSeg, boolean ensureDirection)
1258   {
1259     boolean incl = visSeg.getInclusive();
1260     // adjust for inclusive flag.
1261     int pol = (visSeg.getStart() <= visSeg.getEnd()) ? 1 : -1; // polarity of
1262     // region.
1263     int start = visSeg.getStart() + (incl ? 0 : pol);
1264     int end = visSeg.getEnd() + (incl ? 0 : -pol);
1265     if (ensureDirection && pol == -1)
1266     {
1267       // jalview doesn't deal with inverted ranges, yet.
1268       int t = end;
1269       end = start;
1270       start = t;
1271     }
1272     return new int[] { start, end, pol < 0 ? 1 : 0 };
1273   }
1274
1275   /**
1276    * 
1277    * @param annotation
1278    * @return true if annotation is not to be stored in document
1279    */
1280   private boolean isJalviewOnly(AlignmentAnnotation annotation)
1281   {
1282     return annotation.autoCalculated || annotation.label.equals("Quality")
1283             || annotation.label.equals("Conservation")
1284             || annotation.label.equals("Consensus");
1285   }
1286
1287   boolean dojvsync = true;
1288
1289   // boolean dojvsync = false; // disables Jalview AppData IO
1290   /**
1291    * list of alignment views created when updating Jalview from document.
1292    */
1293   private final Vector newAlignmentViews = new Vector();
1294
1295   /**
1296    * update local jalview view settings from the stored appdata (if any)
1297    */
1298   public void updateJalviewFromAppdata()
1299   {
1300     // recover any existing Jalview data from appdata
1301     // TODO: recover any PDB files stored as attachments in the vamsas session
1302     // and initialise the Jalview2XML.alreadyLoadedPDB hashtable with mappings
1303     // to temp files.
1304     {
1305       final IClientAppdata cappdata = cdoc.getClientAppdata();
1306       if (cappdata != null)
1307       {
1308         if (cappdata.hasClientAppdata())
1309         {
1310           // TODO: how to check version of Jalview client app data and whether
1311           // it has been modified
1312           // client data is shared over all app clients
1313           try
1314           {
1315             // jalview.gui.Jalview2XML fromxml = new jalview.gui.Jalview2XML();
1316             Jalview2XML fromxml = new Jalview2XML();
1317             fromxml.attemptversion1parse = false;
1318             fromxml.setUniqueSetSuffix("");
1319             fromxml.setObjectMappingTables(vobj2jv, jv2vobj); // mapKeysToString
1320             // and
1321             // mapValuesToString
1322             fromxml.setSkipList(skipList);
1323             jarInputStreamProvider jprovider = new jarInputStreamProvider()
1324             {
1325
1326               @Override
1327               public String getFilename()
1328               {
1329
1330                 // TODO Get the vamsas session ID here
1331                 return "Jalview Vamsas Document Client Data";
1332               }
1333
1334               @Override
1335               public JarInputStream getJarInputStream() throws IOException
1336               {
1337                 Console.debug(
1338                         "Returning client input stream for Jalview from Vamsas Document.");
1339                 return new JarInputStream(cappdata.getClientInputStream());
1340               }
1341
1342               @Override
1343               public File getFile()
1344               {
1345                 return null;
1346               }
1347             };
1348             if (dojvsync)
1349             {
1350               fromxml.loadJalviewAlign(jprovider);
1351             }
1352           } catch (Exception e)
1353           {
1354
1355           } catch (OutOfMemoryError e)
1356           {
1357
1358           } catch (Error e)
1359           {
1360
1361           }
1362         }
1363       }
1364       if (cappdata.hasUserAppdata())
1365       {
1366         // TODO: how to check version of Jalview user app data and whether it
1367         // has been modified
1368         // user data overrides data shared over all app clients ?
1369         try
1370         {
1371           Jalview2XML fromxml = new Jalview2XML();
1372           fromxml.attemptversion1parse = false;
1373           fromxml.setUniqueSetSuffix("");
1374           fromxml.setSkipList(skipList);
1375           fromxml.setObjectMappingTables(mapKeysToString(vobj2jv),
1376                   mapValuesToString(jv2vobj));
1377           jarInputStreamProvider jarstream = new jarInputStreamProvider()
1378           {
1379
1380             @Override
1381             public String getFilename()
1382             {
1383
1384               // TODO Get the vamsas session ID here
1385               return "Jalview Vamsas Document User Data";
1386             }
1387
1388             @Override
1389             public JarInputStream getJarInputStream() throws IOException
1390             {
1391               Console.debug(
1392                       "Returning user input stream for Jalview from Vamsas Document.");
1393               return new JarInputStream(cappdata.getUserInputStream());
1394             }
1395
1396             @Override
1397             public File getFile()
1398             {
1399               return null;
1400             }
1401           };
1402           if (dojvsync)
1403           {
1404             fromxml.loadJalviewAlign(jarstream);
1405           }
1406         } catch (Exception e)
1407         {
1408
1409         } catch (OutOfMemoryError e)
1410         {
1411
1412         } catch (Error e)
1413         {
1414
1415         }
1416       }
1417
1418     }
1419     flushAlignViewports();
1420   }
1421
1422   /**
1423    * remove any spurious views generated by document synchronization
1424    */
1425   private void flushAlignViewports()
1426   {
1427     // remove any additional viewports originally recovered from the vamsas
1428     // document.
1429     // search for all alignframes containing viewports generated from document
1430     // sync,
1431     // and if any contain more than one view, then remove the one generated by
1432     // document update.
1433     AlignmentViewport views[], av = null;
1434     AlignFrame af = null;
1435     Iterator newviews = newAlignmentViews.iterator();
1436     while (newviews.hasNext())
1437     {
1438       av = (AlignmentViewport) newviews.next();
1439       af = Desktop.getAlignFrameFor(av);
1440       // TODO implement this : af.getNumberOfViews
1441       String seqsetidobj = av.getSequenceSetId();
1442       views = Desktop.getViewports(seqsetidobj);
1443       Console.debug("Found " + (views == null ? " no " : "" + views.length)
1444               + " views for '" + av.getSequenceSetId() + "'");
1445       if (views.length > 1)
1446       {
1447         // we need to close the original document view.
1448
1449         // work out how to do this by seeing if the views are gathered.
1450         // pretty clunky but the only way to do this without adding more flags
1451         // to the align frames.
1452         boolean gathered = false;
1453         String newviewid = null;
1454         List<AlignedCodonFrame> mappings = av.getAlignment()
1455                 .getCodonFrames();
1456         for (int i = 0; i < views.length; i++)
1457         {
1458           if (views[i] != av)
1459           {
1460             AlignFrame viewframe = Desktop.getAlignFrameFor(views[i]);
1461             if (viewframe == af)
1462             {
1463               gathered = true;
1464             }
1465             newviewid = views[i].getSequenceSetId();
1466           }
1467           else
1468           {
1469             // lose the reference to the vamsas document created view
1470             views[i] = null;
1471           }
1472         }
1473         // close the view generated by the vamsas document synchronization
1474         if (gathered)
1475         {
1476           af.closeView(av);
1477         }
1478         else
1479         {
1480           af.closeMenuItem_actionPerformed(false);
1481         }
1482         replaceJvObjMapping(seqsetidobj, newviewid);
1483         seqsetidobj = newviewid;
1484         // not sure if we need to do this:
1485
1486         if (false) // mappings != null)
1487         {
1488           // ensure sequence mappings from vamsas document view still
1489           // active
1490           if (mappings != null)
1491           {
1492             jalview.structure.StructureSelectionManager
1493                     .getStructureSelectionManager(Desktop.getInstance())
1494                     .registerMappings(mappings);
1495           }
1496         }
1497       }
1498       // ensure vamsas object binds to the stored views retrieved from
1499       // Jalview appdata
1500       // jalview.structure.StructureSelectionManager
1501       // .getStructureSelectionManager()
1502       // .addStructureViewerListener(viewframe.alignPanel);
1503
1504     }
1505
1506     newviews = null;
1507     newAlignmentViews.clear();
1508   }
1509
1510   /**
1511    * replaces oldjvobject with newjvobject in the Jalview Object <> VorbaID
1512    * binding tables
1513    * 
1514    * @param oldjvobject
1515    * @param newjvobject
1516    *          (may be null)
1517    */
1518   private void replaceJvObjMapping(Object oldjvobject, Object newjvobject)
1519   {
1520     Object vobject = jv2vobj.remove(oldjvobject);
1521     if (vobject == null)
1522     {
1523       // NOTE: this happens if user deletes object in one session then updates
1524       // from another client
1525       throw new Error(MessageManager.formatMessage(
1526               "error.implementation_error_old_jalview_object_not_bound",
1527               new String[]
1528               { oldjvobject.toString() }));
1529     }
1530     if (newjvobject != null)
1531     {
1532       jv2vobj.put(newjvobject, vobject);
1533       vobj2jv.put(vobject, newjvobject);
1534     }
1535   }
1536
1537   /**
1538    * Update the jalview client and user appdata from the local jalview settings
1539    */
1540   public void updateJalviewClientAppdata()
1541   {
1542     final IClientAppdata cappdata = cdoc.getClientAppdata();
1543     if (cappdata != null)
1544     {
1545       try
1546       {
1547         Jalview2XML jxml = new Jalview2XML();
1548         jxml.setObjectMappingTables(mapKeysToString(vobj2jv),
1549                 mapValuesToString(jv2vobj));
1550         jxml.setSkipList(skipList);
1551         if (dojvsync)
1552         {
1553           jxml.saveState(
1554                   new JarOutputStream(cappdata.getClientOutputStream()));
1555         }
1556
1557       } catch (Exception e)
1558       {
1559         // TODO raise GUI warning if user requests it.
1560         Console.error(
1561                 "Couldn't update jalview client application data. Giving up - local settings probably lost.",
1562                 e);
1563       }
1564     }
1565     else
1566     {
1567       Console.error(
1568               "Couldn't access client application data for vamsas session. This is probably a vamsas client bug.");
1569     }
1570   }
1571
1572   /**
1573    * translate the Vobject keys to strings for use in Jalview2XML
1574    * 
1575    * @param jv2vobj2
1576    * @return
1577    */
1578   private IdentityHashMap mapValuesToString(IdentityHashMap jv2vobj2)
1579   {
1580     IdentityHashMap mapped = new IdentityHashMap();
1581     Iterator keys = jv2vobj2.keySet().iterator();
1582     while (keys.hasNext())
1583     {
1584       Object key = keys.next();
1585       mapped.put(key, jv2vobj2.get(key).toString());
1586     }
1587     return mapped;
1588   }
1589
1590   /**
1591    * translate the Vobject values to strings for use in Jalview2XML
1592    * 
1593    * @param vobj2jv2
1594    * @return hashtable with string values
1595    */
1596   private Hashtable mapKeysToString(Hashtable vobj2jv2)
1597   {
1598     Hashtable mapped = new Hashtable();
1599     Iterator keys = vobj2jv2.keySet().iterator();
1600     while (keys.hasNext())
1601     {
1602       Object key = keys.next();
1603       mapped.put(key.toString(), vobj2jv2.get(key));
1604     }
1605     return mapped;
1606   }
1607
1608   /**
1609    * synchronize Jalview from the vamsas document
1610    * 
1611    * @return number of new views from document
1612    */
1613   public int updateToJalview()
1614   {
1615     VAMSAS _roots[] = cdoc.getVamsasRoots();
1616
1617     for (int _root = 0; _root < _roots.length; _root++)
1618     {
1619       VAMSAS root = _roots[_root];
1620       boolean newds = false;
1621       for (int _ds = 0, _nds = root.getDataSetCount(); _ds < _nds; _ds++)
1622       {
1623         // ///////////////////////////////////
1624         // ///LOAD DATASET
1625         DataSet dataset = root.getDataSet(_ds);
1626         int i, iSize = dataset.getSequenceCount();
1627         List<SequenceI> dsseqs;
1628         jalview.datamodel.Alignment jdataset = (jalview.datamodel.Alignment) getvObj2jv(
1629                 dataset);
1630         int jremain = 0;
1631         if (jdataset == null)
1632         {
1633           Console.debug("Initialising new jalview dataset fields");
1634           newds = true;
1635           dsseqs = new Vector();
1636         }
1637         else
1638         {
1639           Console.debug("Update jalview dataset from vamsas.");
1640           jremain = jdataset.getHeight();
1641           dsseqs = jdataset.getSequences();
1642         }
1643
1644         // TODO: test sequence merging - we preserve existing non vamsas
1645         // sequences but add in any new vamsas ones, and don't yet update any
1646         // sequence attributes
1647         for (i = 0; i < iSize; i++)
1648         {
1649           Sequence vdseq = dataset.getSequence(i);
1650           jalview.io.vamsas.Datasetsequence dssync = new Datasetsequence(
1651                   this, vdseq);
1652
1653           jalview.datamodel.SequenceI dsseq = (SequenceI) dssync.getJvobj();
1654           if (dssync.isAddfromdoc())
1655           {
1656             dsseqs.add(dsseq);
1657           }
1658           if (vdseq.getDbRefCount() > 0)
1659           {
1660             DbRef[] dbref = vdseq.getDbRef();
1661             for (int db = 0; db < dbref.length; db++)
1662             {
1663               new jalview.io.vamsas.Dbref(this, dbref[db], vdseq, dsseq);
1664
1665             }
1666             dsseq.updatePDBIds();
1667           }
1668         }
1669
1670         if (newds)
1671         {
1672           SequenceI[] seqs = new SequenceI[dsseqs.size()];
1673           for (i = 0, iSize = dsseqs.size(); i < iSize; i++)
1674           {
1675             seqs[i] = dsseqs.get(i);
1676             dsseqs.set(i, null);
1677           }
1678           jdataset = new jalview.datamodel.Alignment(seqs);
1679           Console.debug("New vamsas dataset imported into jalview.");
1680           bindjvvobj(jdataset, dataset);
1681         }
1682         // ////////
1683         // add any new dataset sequence feature annotations
1684         if (dataset.getDataSetAnnotations() != null)
1685         {
1686           for (int dsa = 0; dsa < dataset
1687                   .getDataSetAnnotationsCount(); dsa++)
1688           {
1689             DataSetAnnotations dseta = dataset.getDataSetAnnotations(dsa);
1690             // TODO: deal with group annotation on datset sequences.
1691             if (dseta.getSeqRefCount() == 1)
1692             {
1693               SequenceI dsSeq = (SequenceI) getvObj2jv(
1694                       (Vobject) dseta.getSeqRef(0)); // TODO: deal with group
1695                                                      // dataset
1696               // annotations
1697               if (dsSeq == null)
1698               {
1699                 Console.warn(
1700                         "Couldn't resolve jalview sequenceI for dataset object reference "
1701                                 + ((Vobject) dataset
1702                                         .getDataSetAnnotations(dsa)
1703                                         .getSeqRef(0)).getVorbaId()
1704                                                 .getId());
1705               }
1706               else
1707               {
1708                 if (dseta.getAnnotationElementCount() == 0)
1709                 {
1710                   new jalview.io.vamsas.Sequencefeature(this, dseta, dsSeq);
1711
1712                 }
1713                 else
1714                 {
1715                   // TODO: deal with alignmentAnnotation style annotation
1716                   // appearing on dataset sequences.
1717                   // JBPNote: we could just add them to all alignments but
1718                   // that may complicate cross references in the jalview
1719                   // datamodel
1720                   Console.warn(
1721                           "Ignoring dataset annotation with annotationElements. Not yet supported in jalview.");
1722                 }
1723               }
1724             }
1725             else
1726             {
1727               Console.warn(
1728                       "Ignoring multiply referenced dataset sequence annotation for binding to datsaet sequence features.");
1729             }
1730           }
1731         }
1732         if (dataset.getAlignmentCount() > 0)
1733         {
1734           // LOAD ALIGNMENTS from DATASET
1735
1736           for (int al = 0, nal = dataset
1737                   .getAlignmentCount(); al < nal; al++)
1738           {
1739             uk.ac.vamsas.objects.core.Alignment alignment = dataset
1740                     .getAlignment(al);
1741             // TODO check this handles multiple views properly
1742             AlignmentViewport av = findViewport(alignment);
1743
1744             jalview.datamodel.AlignmentI jal = null;
1745             if (av != null)
1746             {
1747               // TODO check that correct alignment object is retrieved when
1748               // hidden seqs exist.
1749               jal = (av.hasHiddenRows()) ? av.getAlignment()
1750                       .getHiddenSequences().getFullAlignment()
1751                       : av.getAlignment();
1752             }
1753             iSize = alignment.getAlignmentSequenceCount();
1754             boolean refreshal = false;
1755             Vector newasAnnots = new Vector();
1756             char gapChar = ' '; // default for new alignments read in from the
1757             // document
1758             if (jal != null)
1759             {
1760               dsseqs = jal.getSequences(); // for merge/update
1761               gapChar = jal.getGapCharacter();
1762             }
1763             else
1764             {
1765               dsseqs = new Vector();
1766             }
1767             char valGapchar = alignment.getGapChar().charAt(0);
1768             for (i = 0; i < iSize; i++)
1769             {
1770               AlignmentSequence valseq = alignment.getAlignmentSequence(i);
1771               jalview.datamodel.Sequence alseq = (jalview.datamodel.Sequence) getvObj2jv(
1772                       valseq);
1773               if (syncFromAlignmentSequence(valseq, valGapchar, gapChar,
1774                       dsseqs) && alseq != null)
1775               {
1776
1777                 // updated to sequence from the document
1778                 jremain--;
1779                 refreshal = true;
1780               }
1781               if (valseq.getAlignmentSequenceAnnotationCount() > 0)
1782               {
1783                 AlignmentSequenceAnnotation[] vasannot = valseq
1784                         .getAlignmentSequenceAnnotation();
1785                 for (int a = 0; a < vasannot.length; a++)
1786                 {
1787                   jalview.datamodel.AlignmentAnnotation asa = (jalview.datamodel.AlignmentAnnotation) getvObj2jv(
1788                           vasannot[a]); // TODO:
1789                   // 1:many
1790                   // jalview
1791                   // alignment
1792                   // sequence
1793                   // annotations
1794                   if (asa == null)
1795                   {
1796                     int se[] = getBounds(vasannot[a]);
1797                     asa = getjAlignmentAnnotation(jal, vasannot[a]);
1798                     asa.setSequenceRef(alseq);
1799                     asa.createSequenceMapping(alseq, se[0], false); // TODO:
1800                     // verify
1801                     // that
1802                     // positions
1803                     // in
1804                     // alseqAnnotation
1805                     // correspond
1806                     // to
1807                     // ungapped
1808                     // residue
1809                     // positions.
1810                     alseq.addAlignmentAnnotation(asa);
1811                     bindjvvobj(asa, vasannot[a]);
1812                     refreshal = true;
1813                     newasAnnots.add(asa);
1814                   }
1815                   else
1816                   {
1817                     // update existing annotation - can do this in place
1818                     if (vasannot[a].getModifiable() == null) // TODO: USE
1819                     // VAMSAS LIBRARY
1820                     // OBJECT LOCK
1821                     // METHODS)
1822                     {
1823                       Console.info(
1824                               "UNIMPLEMENTED: not recovering user modifiable sequence alignment annotation");
1825                       // TODO: should at least replace with new one - otherwise
1826                       // things will break
1827                       // basically do this:
1828                       // int se[] = getBounds(vasannot[a]);
1829                       // asa.update(getjAlignmentAnnotation(jal, vasannot[a]));
1830                       // // update from another annotation object in place.
1831                       // asa.createSequenceMapping(alseq, se[0], false);
1832
1833                     }
1834                   }
1835                 }
1836               }
1837             }
1838             if (jal == null)
1839             {
1840               SequenceI[] seqs = new SequenceI[dsseqs.size()];
1841               for (i = 0, iSize = dsseqs.size(); i < iSize; i++)
1842               {
1843                 seqs[i] = dsseqs.get(i);
1844                 dsseqs.set(i, null);
1845               }
1846               jal = new jalview.datamodel.Alignment(seqs);
1847               Console.debug("New vamsas alignment imported into jalview "
1848                       + alignment.getVorbaId().getId());
1849               jal.setDataset(jdataset);
1850             }
1851             if (newasAnnots != null && newasAnnots.size() > 0)
1852             {
1853               // Add the new sequence annotations in to the alignment.
1854               for (int an = 0, anSize = newasAnnots
1855                       .size(); an < anSize; an++)
1856               {
1857                 jal.addAnnotation(
1858                         (AlignmentAnnotation) newasAnnots.elementAt(an));
1859                 // TODO: check if anything has to be done - like calling
1860                 // adjustForAlignment or something.
1861                 newasAnnots.setElementAt(null, an);
1862               }
1863               newasAnnots = null;
1864             }
1865             // //////////////////////////////////////////
1866             // //LOAD ANNOTATIONS FOR THE ALIGNMENT
1867             // ////////////////////////////////////
1868             if (alignment.getAlignmentAnnotationCount() > 0)
1869             {
1870               uk.ac.vamsas.objects.core.AlignmentAnnotation[] an = alignment
1871                       .getAlignmentAnnotation();
1872
1873               for (int j = 0; j < an.length; j++)
1874               {
1875                 jalview.datamodel.AlignmentAnnotation jan = (jalview.datamodel.AlignmentAnnotation) getvObj2jv(
1876                         an[j]);
1877                 if (jan != null)
1878                 {
1879                   // update or stay the same.
1880                   // TODO: should at least replace with a new one - otherwise
1881                   // things will break
1882                   // basically do this:
1883                   // jan.update(getjAlignmentAnnotation(jal, an[a])); // update
1884                   // from another annotation object in place.
1885
1886                   Console.debug(
1887                           "update from vamsas alignment annotation to existing jalview alignment annotation.");
1888                   if (an[j].getModifiable() == null) // TODO: USE VAMSAS
1889                   // LIBRARY OBJECT LOCK
1890                   // METHODS)
1891                   {
1892                     // TODO: user defined annotation is totally mutable... - so
1893                     // load it up or throw away if locally edited.
1894                     Console.info(
1895                             "NOT IMPLEMENTED - Recovering user-modifiable annotation - yet...");
1896                   }
1897                   // TODO: compare annotation element rows
1898                   // TODO: compare props.
1899                 }
1900                 else
1901                 {
1902                   jan = getjAlignmentAnnotation(jal, an[j]);
1903                   // TODO: ensure we add the alignment annotation before the
1904                   // automatic annotation rows
1905                   jal.addAnnotation(jan);
1906                   bindjvvobj(jan, an[j]);
1907                   refreshal = true;
1908                 }
1909               }
1910             }
1911             AlignFrame alignFrame;
1912             if (av == null)
1913             {
1914               Console.debug("New alignframe for alignment "
1915                       + alignment.getVorbaId());
1916               // ///////////////////////////////
1917               // construct alignment view
1918               alignFrame = new AlignFrame(jal, AlignFrame.DEFAULT_WIDTH,
1919                       AlignFrame.DEFAULT_HEIGHT,
1920                       alignment.getVorbaId().toString());
1921               av = alignFrame.getViewport();
1922               newAlignmentViews.addElement(av);
1923               String title = alignment.getProvenance()
1924                       .getEntry(
1925                               alignment.getProvenance().getEntryCount() - 1)
1926                       .getAction();
1927               if (alignment.getPropertyCount() > 0)
1928               {
1929                 for (int p = 0, pe = alignment
1930                         .getPropertyCount(); p < pe; p++)
1931                 {
1932                   if (alignment.getProperty(p).getName().equals("title"))
1933                   {
1934                     title = alignment.getProperty(p).getContent();
1935                   }
1936                 }
1937               }
1938               // TODO: automatically create meaningful title for a vamsas
1939               // alignment using its provenance.
1940               if (Console.isDebugEnabled())
1941               {
1942                 title = title + "(" + alignment.getVorbaId() + ")";
1943
1944               }
1945               jalview.gui.Desktop.addInternalFrame(alignFrame, title,
1946                       AlignFrame.DEFAULT_WIDTH, AlignFrame.DEFAULT_HEIGHT);
1947               bindjvvobj(av.getSequenceSetId(), alignment);
1948             }
1949             else
1950             {
1951               // find the alignFrame for jal.
1952               // TODO: fix this so we retrieve the alignFrame handing av
1953               // *directly* (JBPNote - don't understand this now)
1954               // TODO: make sure all associated views are refreshed
1955               alignFrame = Desktop.getAlignFrameFor(av);
1956               if (refreshal)
1957               {
1958                 av.alignmentChanged(alignFrame.alignPanel);
1959                 alignFrame.alignPanel.adjustAnnotationHeight();
1960               }
1961             }
1962             // LOAD TREES
1963             // /////////////////////////////////////
1964             if (alignment.getTreeCount() > 0)
1965             {
1966
1967               for (int t = 0; t < alignment.getTreeCount(); t++)
1968               {
1969                 jalview.io.vamsas.Tree vstree = new jalview.io.vamsas.Tree(
1970                         this, alignFrame, alignment.getTree(t));
1971                 TreePanel tp = null;
1972                 if (vstree.isValidTree())
1973                 {
1974                   tp = alignFrame.showNewickTree(vstree.getNewickTree(),
1975                           vstree.getTitle(), vstree.getInputData(), 600,
1976                           500, t * 20 + 50, t * 20 + 50);
1977
1978                 }
1979                 if (tp != null)
1980                 {
1981                   bindjvvobj(tp, alignment.getTree(t));
1982                   try
1983                   {
1984                     vstree.UpdateSequenceTreeMap(tp);
1985                   } catch (RuntimeException e)
1986                   {
1987                     Console.warn("update of labels failed.", e);
1988                   }
1989                 }
1990                 else
1991                 {
1992                   Console.warn("Cannot create tree for tree " + t
1993                           + " in document ("
1994                           + alignment.getTree(t).getVorbaId());
1995                 }
1996
1997               }
1998             }
1999           }
2000         }
2001       }
2002       // we do sequenceMappings last because they span all datasets in a vamsas
2003       // root
2004       for (int _ds = 0, _nds = root.getDataSetCount(); _ds < _nds; _ds++)
2005       {
2006         DataSet dataset = root.getDataSet(_ds);
2007         if (dataset.getSequenceMappingCount() > 0)
2008         {
2009           for (int sm = 0, smCount = dataset
2010                   .getSequenceMappingCount(); sm < smCount; sm++)
2011           {
2012             Rangetype seqmap = new jalview.io.vamsas.Sequencemapping(this,
2013                     dataset.getSequenceMapping(sm));
2014           }
2015         }
2016       }
2017     }
2018     return newAlignmentViews.size();
2019   }
2020
2021   public AlignmentViewport findViewport(Alignment alignment)
2022   {
2023     AlignmentViewport av = null;
2024     AlignmentViewport[] avs = Desktop
2025             .getViewports((String) getvObj2jv(alignment));
2026     if (avs != null)
2027     {
2028       av = avs[0];
2029     }
2030     return av;
2031   }
2032
2033   // bitfields - should be a template in j1.5
2034   private static int HASSECSTR = 0;
2035
2036   private static int HASVALS = 1;
2037
2038   private static int HASHPHOB = 2;
2039
2040   private static int HASDC = 3;
2041
2042   private static int HASDESCSTR = 4;
2043
2044   private static int HASTWOSTATE = 5; // not used yet.
2045
2046   /**
2047    * parses the AnnotationElements - if they exist - into
2048    * jalview.datamodel.Annotation[] rows Two annotation rows are made if there
2049    * are distinct annotation for both at 'pos' and 'after pos' at any particular
2050    * site.
2051    * 
2052    * @param annotation
2053    * @return { boolean[static int constants ], int[ae.length] - map to annotated
2054    *         object frame, jalview.datamodel.Annotation[],
2055    *         jalview.datamodel.Annotation[] (after)}
2056    */
2057   private Object[] parseRangeAnnotation(
2058           uk.ac.vamsas.objects.core.RangeAnnotation annotation)
2059   {
2060     // set these attributes by looking in the annotation to decide what kind of
2061     // alignment annotation rows will be made
2062     // TODO: potentially we might make several annotation rows from one vamsas
2063     // alignment annotation. the jv2Vobj binding mechanism
2064     // may not quite cope with this (without binding an array of annotations to
2065     // a vamsas alignment annotation)
2066     // summary flags saying what we found over the set of annotation rows.
2067     boolean[] AeContent = new boolean[] { false, false, false, false,
2068         false };
2069     int[] rangeMap = getMapping(annotation);
2070     jalview.datamodel.Annotation[][] anot = new jalview.datamodel.Annotation[][] {
2071         new jalview.datamodel.Annotation[rangeMap.length],
2072         new jalview.datamodel.Annotation[rangeMap.length] };
2073     boolean mergeable = true; // false if 'after positions cant be placed on
2074     // same annotation row as positions.
2075
2076     if (annotation.getAnnotationElementCount() > 0)
2077     {
2078       AnnotationElement ae[] = annotation.getAnnotationElement();
2079       for (int aa = 0; aa < ae.length; aa++)
2080       {
2081         int pos = (int) ae[aa].getPosition() - 1; // pos counts from 1 to
2082         // (|seg.start-seg.end|+1)
2083         if (pos >= 0 && pos < rangeMap.length)
2084         {
2085           int row = ae[aa].getAfter() ? 1 : 0;
2086           if (anot[row][pos] != null)
2087           {
2088             // only time this should happen is if the After flag is set.
2089             Console.debug("Ignoring duplicate annotation site at " + pos);
2090             continue;
2091           }
2092           if (anot[1 - row][pos] != null)
2093           {
2094             mergeable = false;
2095           }
2096           String desc = "";
2097           if (ae[aa].getDescription() != null)
2098           {
2099             desc = ae[aa].getDescription();
2100             if (desc.length() > 0)
2101             {
2102               // have imported valid description string
2103               AeContent[HASDESCSTR] = true;
2104             }
2105           }
2106           String dc = null; // ae[aa].getDisplayCharacter()==null ? "dc" :
2107           // ae[aa].getDisplayCharacter();
2108           String ss = null; // ae[aa].getSecondaryStructure()==null ? "ss" :
2109           // ae[aa].getSecondaryStructure();
2110           java.awt.Color colour = null;
2111           if (ae[aa].getGlyphCount() > 0)
2112           {
2113             Glyph[] glyphs = ae[aa].getGlyph();
2114             for (int g = 0; g < glyphs.length; g++)
2115             {
2116               if (glyphs[g].getDict().equals(
2117                       uk.ac.vamsas.objects.utils.GlyphDictionary.PROTEIN_SS_3STATE))
2118               {
2119                 ss = glyphs[g].getContent();
2120                 AeContent[HASSECSTR] = true;
2121               }
2122               else if (glyphs[g].getDict().equals(
2123                       uk.ac.vamsas.objects.utils.GlyphDictionary.PROTEIN_HD_HYDRO))
2124               {
2125                 Console.debug("ignoring hydrophobicity glyph marker.");
2126                 AeContent[HASHPHOB] = true;
2127                 char c = (dc = glyphs[g].getContent()).charAt(0);
2128                 // dc may get overwritten - but we still set the colour.
2129                 colour = new java.awt.Color(c == '+' ? 255 : 0,
2130                         c == '.' ? 255 : 0, c == '-' ? 255 : 0);
2131
2132               }
2133               else if (glyphs[g].getDict().equals(
2134                       uk.ac.vamsas.objects.utils.GlyphDictionary.DEFAULT))
2135               {
2136                 dc = glyphs[g].getContent();
2137                 AeContent[HASDC] = true;
2138               }
2139               else
2140               {
2141                 Console.debug(
2142                         "IMPLEMENTATION TODO: Ignoring unknown glyph type "
2143                                 + glyphs[g].getDict());
2144               }
2145             }
2146           }
2147           float val = 0;
2148           if (ae[aa].getValueCount() > 0)
2149           {
2150             AeContent[HASVALS] = true;
2151             if (ae[aa].getValueCount() > 1)
2152             {
2153               Console.warn(
2154                       "ignoring additional " + (ae[aa].getValueCount() - 1)
2155                               + " values in annotation element.");
2156             }
2157             val = ae[aa].getValue(0);
2158           }
2159           if (colour == null)
2160           {
2161             anot[row][pos] = new jalview.datamodel.Annotation(
2162                     (dc != null) ? dc : "", desc,
2163                     (ss != null) ? ss.charAt(0) : ' ', val);
2164           }
2165           else
2166           {
2167             anot[row][pos] = new jalview.datamodel.Annotation(
2168                     (dc != null) ? dc : "", desc,
2169                     (ss != null) ? ss.charAt(0) : ' ', val, colour);
2170           }
2171         }
2172         else
2173         {
2174           Console.warn("Ignoring out of bound annotation element " + aa
2175                   + " in " + annotation.getVorbaId().getId());
2176         }
2177       }
2178       // decide on how many annotation rows are needed.
2179       if (mergeable)
2180       {
2181         for (int i = 0; i < anot[0].length; i++)
2182         {
2183           if (anot[1][i] != null)
2184           {
2185             anot[0][i] = anot[1][i];
2186             anot[0][i].description = anot[0][i].description + " (after)";
2187             AeContent[HASDESCSTR] = true; // we have valid description string
2188             // data
2189             anot[1][i] = null;
2190           }
2191         }
2192         anot[1] = null;
2193       }
2194       else
2195       {
2196         for (int i = 0; i < anot[0].length; i++)
2197         {
2198           anot[1][i].description = anot[1][i].description + " (after)";
2199         }
2200       }
2201       return new Object[] { AeContent, rangeMap, anot[0], anot[1] };
2202     }
2203     else
2204     {
2205       // no annotations to parse. Just return an empty annotationElement[]
2206       // array.
2207       return new Object[] { AeContent, rangeMap, anot[0], anot[1] };
2208     }
2209     // return null;
2210   }
2211
2212   /**
2213    * @param jal
2214    *          the jalview alignment to which the annotation will be attached
2215    *          (ideally - freshly updated from corresponding vamsas alignment)
2216    * @param annotation
2217    * @return unbound jalview alignment annotation object.
2218    */
2219   private jalview.datamodel.AlignmentAnnotation getjAlignmentAnnotation(
2220           jalview.datamodel.AlignmentI jal,
2221           uk.ac.vamsas.objects.core.RangeAnnotation annotation)
2222   {
2223     if (annotation == null)
2224     {
2225       return null;
2226     }
2227     // boolean
2228     // hasSequenceRef=annotation.getClass().equals(uk.ac.vamsas.objects.core.AlignmentSequenceAnnotation.class);
2229     // boolean hasProvenance=hasSequenceRef ||
2230     // (annotation.getClass().equals(uk.ac.vamsas.objects.core.AlignmentAnnotation.class));
2231     /*
2232      * int se[] = getBounds(annotation); if (se==null) se=new int[]
2233      * {0,jal.getWidth()-1};
2234      */
2235     Object[] parsedRangeAnnotation = parseRangeAnnotation(annotation);
2236     String a_label = annotation.getLabel();
2237     String a_descr = annotation.getDescription();
2238     GraphLine gl = null;
2239     int type = 0;
2240     boolean interp = true; // cleared if annotation is DISCRETE
2241     // set type and other attributes from properties
2242     if (annotation.getPropertyCount() > 0)
2243     {
2244       // look for special jalview properties
2245       uk.ac.vamsas.objects.core.Property[] props = annotation.getProperty();
2246       for (int p = 0; p < props.length; p++)
2247       {
2248         if (props[p].getName().equalsIgnoreCase(DISCRETE_ANNOTATION))
2249         {
2250           type = AlignmentAnnotation.BAR_GRAPH;
2251           interp = false;
2252         }
2253         else if (props[p].getName().equalsIgnoreCase(CONTINUOUS_ANNOTATION))
2254         {
2255           type = AlignmentAnnotation.LINE_GRAPH;
2256         }
2257         else if (props[p].getName().equalsIgnoreCase(THRESHOLD))
2258         {
2259           Float val = null;
2260           try
2261           {
2262             val = Float.valueOf(props[p].getContent());
2263           } catch (Exception e)
2264           {
2265             Console.warn("Failed to parse threshold property");
2266           }
2267           if (val != null)
2268           {
2269             if (gl == null)
2270             {
2271               gl = new GraphLine(val.floatValue(), "",
2272                       java.awt.Color.black);
2273             }
2274             else
2275             {
2276               gl.value = val.floatValue();
2277             }
2278           }
2279         }
2280         else if (props[p].getName().equalsIgnoreCase(THRESHOLD + "Name"))
2281         {
2282           if (gl == null)
2283           {
2284             gl = new GraphLine(0, "", java.awt.Color.black);
2285           }
2286           gl.label = props[p].getContent();
2287         }
2288       }
2289     }
2290     jalview.datamodel.AlignmentAnnotation jan = null;
2291     if (a_label == null || a_label.length() == 0)
2292     {
2293       a_label = annotation.getType();
2294       if (a_label.length() == 0)
2295       {
2296         a_label = "Unamed annotation";
2297       }
2298     }
2299     if (a_descr == null || a_descr.length() == 0)
2300     {
2301       a_descr = "Annotation of type '" + annotation.getType() + "'";
2302     }
2303     if (parsedRangeAnnotation == null)
2304     {
2305       Console.debug(
2306               "Inserting empty annotation row elements for a whole-alignment annotation.");
2307     }
2308     else
2309     {
2310       if (parsedRangeAnnotation[3] != null)
2311       {
2312         Console.warn("Ignoring 'After' annotation row in "
2313                 + annotation.getVorbaId());
2314       }
2315       jalview.datamodel.Annotation[] arow = (jalview.datamodel.Annotation[]) parsedRangeAnnotation[2];
2316       boolean[] has = (boolean[]) parsedRangeAnnotation[0];
2317       // VAMSAS: getGraph is only on derived annotation for alignments - in this
2318       // way its 'odd' - there is already an existing TODO about removing this
2319       // flag as being redundant
2320       /*
2321        * if((annotation.getClass().equals(uk.ac.vamsas.objects.core.
2322        * AlignmentAnnotation.class) &&
2323        * ((uk.ac.vamsas.objects.core.AlignmentAnnotation)annotation).getGraph())
2324        * || (hasSequenceRef=true &&
2325        * ((uk.ac.vamsas.objects.core.AlignmentSequenceAnnotation
2326        * )annotation).getGraph())) {
2327        */
2328       if (has[HASVALS])
2329       {
2330         if (type == 0)
2331         {
2332           type = jalview.datamodel.AlignmentAnnotation.BAR_GRAPH; // default
2333           // type of
2334           // value
2335           // annotation
2336           if (has[HASHPHOB])
2337           {
2338             // no hints - so we ensure HPHOB display is like this.
2339             type = jalview.datamodel.AlignmentAnnotation.BAR_GRAPH;
2340           }
2341         }
2342         // make bounds and automatic description strings for jalview user's
2343         // benefit (these shouldn't be written back to vamsas document)
2344         boolean first = true;
2345         float min = 0, max = 1;
2346         int lastval = 0;
2347         for (int i = 0; i < arow.length; i++)
2348         {
2349           if (arow[i] != null)
2350           {
2351             if (i - lastval > 1 && interp)
2352             {
2353               // do some interpolation *between* points
2354               if (arow[lastval] != null)
2355               {
2356                 float interval = arow[i].value - arow[lastval].value;
2357                 interval /= i - lastval;
2358                 float base = arow[lastval].value;
2359                 for (int ip = lastval + 1, np = 0; ip < i; np++, ip++)
2360                 {
2361                   arow[ip] = new jalview.datamodel.Annotation("", "", ' ',
2362                           interval * np + base);
2363                   // NB - Interpolated points don't get a tooltip and
2364                   // description.
2365                 }
2366               }
2367             }
2368             lastval = i;
2369             // check range - shouldn't we have a min and max property in the
2370             // annotation object ?
2371             if (first)
2372             {
2373               min = max = arow[i].value;
2374               first = false;
2375             }
2376             else
2377             {
2378               if (arow[i].value < min)
2379               {
2380                 min = arow[i].value;
2381               }
2382               else if (arow[i].value > max)
2383               {
2384                 max = arow[i].value;
2385               }
2386             }
2387             // make tooltip and display char value
2388             if (!has[HASDESCSTR])
2389             {
2390               arow[i].description = arow[i].value + "";
2391             }
2392             if (!has[HASDC])
2393             {
2394               if (!interp)
2395               {
2396                 if (arow[i].description != null
2397                         && arow[i].description.length() < 3)
2398                 {
2399                   // copy over the description as the display char.
2400                   arow[i].displayCharacter = new String(
2401                           arow[i].description);
2402                 }
2403               }
2404               else
2405               {
2406                 // mark the position as a point used for the interpolation.
2407                 arow[i].displayCharacter = arow[i].value + "";
2408               }
2409             }
2410           }
2411         }
2412         jan = new jalview.datamodel.AlignmentAnnotation(a_label, a_descr,
2413                 arow, min, max, type);
2414       }
2415       else
2416       {
2417         if (annotation.getAnnotationElementCount() == 0)
2418         {
2419           // empty annotation array
2420           // TODO: alignment 'features' compare rangeType spec to alignment
2421           // width - if it is not complete, then mark regions on the annotation
2422           // row.
2423         }
2424         jan = new jalview.datamodel.AlignmentAnnotation(a_label, a_descr,
2425                 arow);
2426         jan.setThreshold(null);
2427         jan.annotationId = annotation.getVorbaId().toString(); // keep all the
2428         // ids together.
2429       }
2430       if (annotation.getLinkCount() > 0)
2431       {
2432         Console.warn("Ignoring " + annotation.getLinkCount()
2433                 + "links added to AlignmentAnnotation.");
2434       }
2435       if (annotation.getModifiable() == null
2436               || annotation.getModifiable().length() == 0) // TODO: USE VAMSAS
2437       // LIBRARY OBJECT
2438       // LOCK METHODS)
2439       {
2440         jan.editable = true;
2441       }
2442       try
2443       {
2444         if (annotation.getGroup() != null
2445                 && annotation.getGroup().length() > 0)
2446         {
2447           jan.graphGroup = Integer.parseInt(annotation.getGroup()); // TODO:
2448           // group
2449           // similarly
2450           // named
2451           // annotation
2452           // together
2453           // ?
2454         }
2455       } catch (Exception e)
2456       {
2457         Console.info(
2458                 "UNIMPLEMENTED : Couldn't parse non-integer group value for setting graphGroup correctly.");
2459       }
2460       return jan;
2461
2462     }
2463
2464     return null;
2465   }
2466
2467   /**
2468    * get real bounds of a RangeType's specification. start and end are an
2469    * inclusive range within which all segments and positions lie. TODO: refactor
2470    * to vamsas utils
2471    * 
2472    * @param dseta
2473    * @return int[] { start, end}
2474    */
2475   private int[] getBounds(RangeType dseta)
2476   {
2477     if (dseta != null)
2478     {
2479       int[] se = null;
2480       if (dseta.getSegCount() > 0 && dseta.getPosCount() > 0)
2481       {
2482         throw new Error(MessageManager.getString(
2483                 "error.invalid_vamsas_rangetype_cannot_resolve_lists"));
2484       }
2485       if (dseta.getSegCount() > 0)
2486       {
2487         se = getSegRange(dseta.getSeg(0), true);
2488         for (int s = 1, sSize = dseta.getSegCount(); s < sSize; s++)
2489         {
2490           int nse[] = getSegRange(dseta.getSeg(s), true);
2491           if (se[0] > nse[0])
2492           {
2493             se[0] = nse[0];
2494           }
2495           if (se[1] < nse[1])
2496           {
2497             se[1] = nse[1];
2498           }
2499         }
2500       }
2501       if (dseta.getPosCount() > 0)
2502       {
2503         // could do a polarity for pos range too. and pass back indication of
2504         // discontinuities.
2505         int pos = dseta.getPos(0).getI();
2506         se = new int[] { pos, pos };
2507         for (int p = 0, pSize = dseta.getPosCount(); p < pSize; p++)
2508         {
2509           pos = dseta.getPos(p).getI();
2510           if (se[0] > pos)
2511           {
2512             se[0] = pos;
2513           }
2514           if (se[1] < pos)
2515           {
2516             se[1] = pos;
2517           }
2518         }
2519       }
2520       return se;
2521     }
2522     return null;
2523   }
2524
2525   /**
2526    * map from a rangeType's internal frame to the referenced object's coordinate
2527    * frame.
2528    * 
2529    * @param dseta
2530    * @return int [] { ref(pos)...} for all pos in rangeType's frame.
2531    */
2532   private int[] getMapping(RangeType dseta)
2533   {
2534     Vector posList = new Vector();
2535     if (dseta != null)
2536     {
2537       int[] se = null;
2538       if (dseta.getSegCount() > 0 && dseta.getPosCount() > 0)
2539       {
2540         throw new Error(MessageManager.getString(
2541                 "error.invalid_vamsas_rangetype_cannot_resolve_lists"));
2542       }
2543       if (dseta.getSegCount() > 0)
2544       {
2545         for (int s = 0, sSize = dseta.getSegCount(); s < sSize; s++)
2546         {
2547           se = getSegRange(dseta.getSeg(s), false);
2548           int se_end = se[1 - se[2]] + (se[2] == 0 ? 1 : -1);
2549           for (int p = se[se[2]]; p != se_end; p += se[2] == 0 ? 1 : -1)
2550           {
2551             posList.add(Integer.valueOf(p));
2552           }
2553         }
2554       }
2555       else if (dseta.getPosCount() > 0)
2556       {
2557         int pos = dseta.getPos(0).getI();
2558
2559         for (int p = 0, pSize = dseta.getPosCount(); p < pSize; p++)
2560         {
2561           pos = dseta.getPos(p).getI();
2562           posList.add(Integer.valueOf(pos));
2563         }
2564       }
2565     }
2566     if (posList != null && posList.size() > 0)
2567     {
2568       int[] range = new int[posList.size()];
2569       for (int i = 0; i < range.length; i++)
2570       {
2571         range[i] = ((Integer) posList.elementAt(i)).intValue();
2572       }
2573       posList.clear();
2574       return range;
2575     }
2576     return null;
2577   }
2578
2579   /**
2580    * 
2581    * @param maprange
2582    *          where the from range is the local mapped range, and the to range
2583    *          is the 'mapped' range in the MapRangeType
2584    * @param default
2585    *          unit for local
2586    * @param default
2587    *          unit for mapped
2588    * @return MapList
2589    */
2590   private jalview.util.MapList parsemapType(MapType maprange, int localu,
2591           int mappedu)
2592   {
2593     jalview.util.MapList ml = null;
2594     int[] localRange = getMapping(maprange.getLocal());
2595     int[] mappedRange = getMapping(maprange.getMapped());
2596     long lu = maprange.getLocal().hasUnit() ? maprange.getLocal().getUnit()
2597             : localu;
2598     long mu = maprange.getMapped().hasUnit()
2599             ? maprange.getMapped().getUnit()
2600             : mappedu;
2601     ml = new jalview.util.MapList(localRange, mappedRange, (int) lu,
2602             (int) mu);
2603     return ml;
2604   }
2605
2606   /**
2607    * initialise a range type object from a set of start/end inclusive intervals
2608    * 
2609    * @param mrt
2610    * @param ranges
2611    */
2612   private void initRangeType(RangeType mrt, List<int[]> ranges)
2613   {
2614     for (int[] range : ranges)
2615     {
2616       Seg vSeg = new Seg();
2617       vSeg.setStart(range[0]);
2618       vSeg.setEnd(range[1]);
2619       mrt.addSeg(vSeg);
2620     }
2621   }
2622
2623   /**
2624    * initialise a MapType object from a MapList object.
2625    * 
2626    * @param maprange
2627    * @param ml
2628    * @param setUnits
2629    */
2630   private void initMapType(MapType maprange, jalview.util.MapList ml,
2631           boolean setUnits)
2632   {
2633     maprange.setLocal(new Local());
2634     maprange.setMapped(new Mapped());
2635     initRangeType(maprange.getLocal(), ml.getFromRanges());
2636     initRangeType(maprange.getMapped(), ml.getToRanges());
2637     if (setUnits)
2638     {
2639       maprange.getLocal().setUnit(ml.getFromRatio());
2640       maprange.getLocal().setUnit(ml.getToRatio());
2641     }
2642   }
2643
2644   /*
2645    * not needed now. Provenance getVamsasProvenance(jalview.datamodel.Provenance
2646    * jprov) { jalview.datamodel.ProvenanceEntry[] entries = null; // TODO: fix
2647    * App and Action here. Provenance prov = new Provenance();
2648    * org.exolab.castor.types.Date date = new org.exolab.castor.types.Date( new
2649    * java.util.Date()); Entry provEntry;
2650    * 
2651    * if (jprov != null) { entries = jprov.getEntries(); for (int i = 0; i <
2652    * entries.length; i++) { provEntry = new Entry(); try { date = new
2653    * org.exolab.castor.types.Date(entries[i].getDate()); } catch (Exception ex)
2654    * { ex.printStackTrace();
2655    * 
2656    * date = new org.exolab.castor.types.Date(entries[i].getDate()); }
2657    * provEntry.setDate(date); provEntry.setUser(entries[i].getUser());
2658    * provEntry.setAction(entries[i].getAction()); prov.addEntry(provEntry); } }
2659    * else { provEntry = new Entry(); provEntry.setDate(date);
2660    * provEntry.setUser(System.getProperty("user.name")); // TODO: ext string
2661    * provEntry.setApp("JVAPP"); // TODO: ext string provEntry.setAction(action);
2662    * prov.addEntry(provEntry); }
2663    * 
2664    * return prov; }
2665    */
2666   jalview.datamodel.Provenance getJalviewProvenance(Provenance prov)
2667   {
2668     // TODO: fix App and Action entries and check use of provenance in jalview.
2669     jalview.datamodel.Provenance jprov = new jalview.datamodel.Provenance();
2670     for (int i = 0; i < prov.getEntryCount(); i++)
2671     {
2672       jprov.addEntry(prov.getEntry(i).getUser(),
2673               prov.getEntry(i).getAction(), prov.getEntry(i).getDate(),
2674               prov.getEntry(i).getId());
2675     }
2676
2677     return jprov;
2678   }
2679
2680   /**
2681    * 
2682    * @return default initial provenance list for a Jalview created vamsas
2683    *         object.
2684    */
2685   Provenance dummyProvenance()
2686   {
2687     return dummyProvenance(null);
2688   }
2689
2690   Entry dummyPEntry(String action)
2691   {
2692     Entry entry = new Entry();
2693     entry.setApp(this.provEntry.getApp());
2694     if (action != null)
2695     {
2696       entry.setAction(action);
2697     }
2698     else
2699     {
2700       entry.setAction("created.");
2701     }
2702     entry.setDate(new java.util.Date());
2703     entry.setUser(this.provEntry.getUser());
2704     return entry;
2705   }
2706
2707   Provenance dummyProvenance(String action)
2708   {
2709     Provenance prov = new Provenance();
2710     prov.addEntry(dummyPEntry(action));
2711     return prov;
2712   }
2713
2714   Entry addProvenance(Provenance p, String action)
2715   {
2716     Entry dentry = dummyPEntry(action);
2717     p.addEntry(dentry);
2718     return dentry;
2719   }
2720
2721   public Entry getProvEntry()
2722   {
2723     return provEntry;
2724   }
2725
2726   public IClientDocument getClientDocument()
2727   {
2728     return cdoc;
2729   }
2730
2731   public IdentityHashMap getJvObjectBinding()
2732   {
2733     return jv2vobj;
2734   }
2735
2736   public Hashtable getVamsasObjectBinding()
2737   {
2738     return vobj2jv;
2739   }
2740
2741   public void storeSequenceMappings(AlignmentViewport viewport,
2742           String title) throws Exception
2743   {
2744     AlignmentViewport av = viewport;
2745     try
2746     {
2747       jalview.datamodel.AlignmentI jal = av.getAlignment();
2748       // /////////////////////////////////////////
2749       // SAVE THE DATASET
2750       DataSet dataset = null;
2751       if (jal.getDataset() == null)
2752       {
2753         Console.warn("Creating new dataset for an alignment.");
2754         jal.setDataset(null);
2755       }
2756       dataset = (DataSet) ((Alignment) getjv2vObj(
2757               viewport.getSequenceSetId())).getV_parent(); // jal.getDataset());
2758       if (dataset == null)
2759       {
2760         dataset = (DataSet) getjv2vObj(jal.getDataset());
2761         Console.error(
2762                 "Can't find the correct dataset for the alignment in this view. Creating new one.");
2763
2764       }
2765       // Store any sequence mappings.
2766       List<AlignedCodonFrame> cframes = av.getAlignment().getCodonFrames();
2767       if (cframes != null)
2768       {
2769         for (AlignedCodonFrame acf : cframes)
2770         {
2771           if (acf.getdnaSeqs() != null && acf.getdnaSeqs().length > 0)
2772           {
2773             jalview.datamodel.SequenceI[] dmps = acf.getdnaSeqs();
2774             jalview.datamodel.Mapping[] mps = acf.getProtMappings();
2775             for (int smp = 0; smp < mps.length; smp++)
2776             {
2777               uk.ac.vamsas.objects.core.SequenceType mfrom = (SequenceType) getjv2vObj(
2778                       dmps[smp]);
2779               if (mfrom != null)
2780               {
2781                 new jalview.io.vamsas.Sequencemapping(this, mps[smp], mfrom,
2782                         dataset);
2783               }
2784               else
2785               {
2786                 Console.warn(
2787                         "NO Vamsas Binding for local sequence! NOT CREATING MAPPING FOR "
2788                                 + dmps[smp].getDisplayId(true) + " to "
2789                                 + mps[smp].getTo().getName());
2790               }
2791             }
2792           }
2793         }
2794       }
2795     } catch (Exception e)
2796     {
2797       throw new Exception(MessageManager.formatMessage(
2798               "exception.couldnt_store_sequence_mappings", new String[]
2799               { title }), e);
2800     }
2801   }
2802
2803   public void clearSkipList()
2804   {
2805     if (skipList != null)
2806     {
2807       skipList.clear();
2808     }
2809   }
2810
2811   /**
2812    * @return the skipList
2813    */
2814   public Hashtable getSkipList()
2815   {
2816     return skipList;
2817   }
2818
2819   /**
2820    * @param skipList
2821    *          the skipList to set
2822    */
2823   public void setSkipList(Hashtable skipList)
2824   {
2825     this.skipList = skipList;
2826   }
2827
2828   /**
2829    * registry for datastoreItems
2830    */
2831   DatastoreRegistry dsReg = new DatastoreRegistry();
2832
2833   public DatastoreRegistry getDatastoreRegisty()
2834   {
2835     if (dsReg == null)
2836     {
2837       dsReg = new DatastoreRegistry();
2838     }
2839     return dsReg;
2840   }
2841 }