JAL-1588 check Chimera session file still exists
[jalview.git] / src / jalview / gui / Jalview2XML.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2)
3  * Copyright (C) 2014 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.gui;
22
23 import jalview.api.structures.JalviewStructureDisplayI;
24 import jalview.bin.Cache;
25 import jalview.datamodel.Alignment;
26 import jalview.datamodel.AlignmentAnnotation;
27 import jalview.datamodel.AlignmentI;
28 import jalview.datamodel.PDBEntry;
29 import jalview.datamodel.SequenceI;
30 import jalview.schemabinding.version2.AlcodMap;
31 import jalview.schemabinding.version2.Alcodon;
32 import jalview.schemabinding.version2.AlcodonFrame;
33 import jalview.schemabinding.version2.Annotation;
34 import jalview.schemabinding.version2.AnnotationColours;
35 import jalview.schemabinding.version2.AnnotationElement;
36 import jalview.schemabinding.version2.CalcIdParam;
37 import jalview.schemabinding.version2.DBRef;
38 import jalview.schemabinding.version2.Features;
39 import jalview.schemabinding.version2.Group;
40 import jalview.schemabinding.version2.HiddenColumns;
41 import jalview.schemabinding.version2.JGroup;
42 import jalview.schemabinding.version2.JSeq;
43 import jalview.schemabinding.version2.JalviewModel;
44 import jalview.schemabinding.version2.JalviewModelSequence;
45 import jalview.schemabinding.version2.MapListFrom;
46 import jalview.schemabinding.version2.MapListTo;
47 import jalview.schemabinding.version2.Mapping;
48 import jalview.schemabinding.version2.MappingChoice;
49 import jalview.schemabinding.version2.OtherData;
50 import jalview.schemabinding.version2.PdbentryItem;
51 import jalview.schemabinding.version2.Pdbids;
52 import jalview.schemabinding.version2.Property;
53 import jalview.schemabinding.version2.Sequence;
54 import jalview.schemabinding.version2.SequenceSet;
55 import jalview.schemabinding.version2.SequenceSetProperties;
56 import jalview.schemabinding.version2.Setting;
57 import jalview.schemabinding.version2.StructureState;
58 import jalview.schemabinding.version2.ThresholdLine;
59 import jalview.schemabinding.version2.Tree;
60 import jalview.schemabinding.version2.UserColours;
61 import jalview.schemabinding.version2.Viewport;
62 import jalview.schemes.AnnotationColourGradient;
63 import jalview.schemes.ColourSchemeI;
64 import jalview.schemes.ColourSchemeProperty;
65 import jalview.schemes.GraduatedColor;
66 import jalview.schemes.ResidueColourScheme;
67 import jalview.schemes.ResidueProperties;
68 import jalview.schemes.UserColourScheme;
69 import jalview.structure.StructureSelectionManager;
70 import jalview.structures.models.AAStructureBindingModel;
71 import jalview.util.MessageManager;
72 import jalview.util.Platform;
73 import jalview.util.jarInputStreamProvider;
74 import jalview.viewmodel.AlignmentViewport;
75 import jalview.ws.jws2.Jws2Discoverer;
76 import jalview.ws.jws2.dm.AAConSettings;
77 import jalview.ws.jws2.jabaws2.Jws2Instance;
78 import jalview.ws.params.ArgumentI;
79 import jalview.ws.params.AutoCalcSetting;
80 import jalview.ws.params.WsParamSetI;
81
82 import java.awt.Rectangle;
83 import java.io.BufferedReader;
84 import java.io.DataInputStream;
85 import java.io.DataOutputStream;
86 import java.io.File;
87 import java.io.FileInputStream;
88 import java.io.FileOutputStream;
89 import java.io.IOException;
90 import java.io.InputStreamReader;
91 import java.io.OutputStreamWriter;
92 import java.io.PrintWriter;
93 import java.lang.reflect.InvocationTargetException;
94 import java.net.MalformedURLException;
95 import java.net.URL;
96 import java.util.ArrayList;
97 import java.util.Enumeration;
98 import java.util.HashMap;
99 import java.util.HashSet;
100 import java.util.Hashtable;
101 import java.util.IdentityHashMap;
102 import java.util.Iterator;
103 import java.util.List;
104 import java.util.Map;
105 import java.util.Map.Entry;
106 import java.util.Set;
107 import java.util.StringTokenizer;
108 import java.util.Vector;
109 import java.util.jar.JarEntry;
110 import java.util.jar.JarInputStream;
111 import java.util.jar.JarOutputStream;
112
113 import javax.swing.JInternalFrame;
114 import javax.swing.JOptionPane;
115 import javax.swing.SwingUtilities;
116
117 import org.exolab.castor.xml.Unmarshaller;
118
119 /**
120  * Write out the current jalview desktop state as a Jalview XML stream.
121  * 
122  * Note: the vamsas objects referred to here are primitive versions of the
123  * VAMSAS project schema elements - they are not the same and most likely never
124  * will be :)
125  * 
126  * @author $author$
127  * @version $Revision: 1.134 $
128  */
129 public class Jalview2XML
130 {
131
132   /**
133    * A data bean to hold stored data about a structure viewer.
134    */
135   public class ViewerData
136   {
137
138     private int x;
139
140     private int y;
141
142     private int width;
143
144     private int height;
145
146     public boolean alignWithPanel;
147
148     public boolean colourWithAlignPanel;
149
150     public boolean colourByViewer;
151
152     String stateData = "";
153
154     // todo: java bean in place of Object []
155     private Map<File, Object[]> fileData = new HashMap<File, Object[]>();
156
157     public ViewerData(int x, int y, int width, int height,
158             boolean alignWithPanel, boolean colourWithAlignPanel,
159             boolean colourByViewer)
160     {
161       this.x = x;
162       this.y = y;
163       this.width = width;
164       this.height = height;
165       this.alignWithPanel = alignWithPanel;
166       this.colourWithAlignPanel = colourWithAlignPanel;
167       this.colourByViewer = colourByViewer;
168     }
169
170   }
171
172   /*
173    * SequenceI reference -> XML ID string in jalview XML. Populated as XML reps
174    * of sequence objects are created.
175    */
176   IdentityHashMap<SequenceI, String> seqsToIds = null;
177
178   /**
179    * jalview XML Sequence ID to jalview sequence object reference (both dataset
180    * and alignment sequences. Populated as XML reps of sequence objects are
181    * created.)
182    */
183   Map<String, SequenceI> seqRefIds = null;
184
185   Vector frefedSequence = null;
186
187   boolean raiseGUI = true; // whether errors are raised in dialog boxes or not
188
189   /**
190    * create/return unique hash string for sq
191    * 
192    * @param sq
193    * @return new or existing unique string for sq
194    */
195   String seqHash(SequenceI sq)
196   {
197     if (seqsToIds == null)
198     {
199       initSeqRefs();
200     }
201     if (seqsToIds.containsKey(sq))
202     {
203       return seqsToIds.get(sq);
204     }
205     else
206     {
207       // create sequential key
208       String key = "sq" + (seqsToIds.size() + 1);
209       key = makeHashCode(sq, key); // check we don't have an external reference
210       // for it already.
211       seqsToIds.put(sq, key);
212       return key;
213     }
214   }
215
216   void clearSeqRefs()
217   {
218     if (_cleartables)
219     {
220       if (seqRefIds != null)
221       {
222         seqRefIds.clear();
223       }
224       if (seqsToIds != null)
225       {
226         seqsToIds.clear();
227       }
228       // seqRefIds = null;
229       // seqsToIds = null;
230     }
231     else
232     {
233       // do nothing
234       warn("clearSeqRefs called when _cleartables was not set. Doing nothing.");
235       // seqRefIds = new Hashtable();
236       // seqsToIds = new IdentityHashMap();
237     }
238   }
239
240   void initSeqRefs()
241   {
242     if (seqsToIds == null)
243     {
244       seqsToIds = new IdentityHashMap<SequenceI, String>();
245     }
246     if (seqRefIds == null)
247     {
248       seqRefIds = new HashMap<String, SequenceI>();
249     }
250   }
251
252   public Jalview2XML()
253   {
254   }
255
256   public Jalview2XML(boolean raiseGUI)
257   {
258     this.raiseGUI = raiseGUI;
259   }
260
261   public void resolveFrefedSequences()
262   {
263     if (frefedSequence.size() > 0)
264     {
265       int r = 0, rSize = frefedSequence.size();
266       while (r < rSize)
267       {
268         Object[] ref = (Object[]) frefedSequence.elementAt(r);
269         if (ref != null)
270         {
271           String sref = (String) ref[0];
272           if (seqRefIds.containsKey(sref))
273           {
274             if (ref[1] instanceof jalview.datamodel.Mapping)
275             {
276               SequenceI seq = seqRefIds.get(sref);
277               while (seq.getDatasetSequence() != null)
278               {
279                 seq = seq.getDatasetSequence();
280               }
281               ((jalview.datamodel.Mapping) ref[1]).setTo(seq);
282             }
283             else
284             {
285               if (ref[1] instanceof jalview.datamodel.AlignedCodonFrame)
286               {
287                 SequenceI seq = seqRefIds.get(sref);
288                 while (seq.getDatasetSequence() != null)
289                 {
290                   seq = seq.getDatasetSequence();
291                 }
292                 if (ref[2] != null
293                         && ref[2] instanceof jalview.datamodel.Mapping)
294                 {
295                   jalview.datamodel.Mapping mp = (jalview.datamodel.Mapping) ref[2];
296                   ((jalview.datamodel.AlignedCodonFrame) ref[1]).addMap(
297                           seq, mp.getTo(), mp.getMap());
298                 }
299                 else
300                 {
301                   System.err
302                           .println("IMPLEMENTATION ERROR: Unimplemented forward sequence references for AlcodonFrames involving "
303                                   + ref[2].getClass() + " type objects.");
304                 }
305               }
306               else
307               {
308                 System.err
309                         .println("IMPLEMENTATION ERROR: Unimplemented forward sequence references for "
310                                 + ref[1].getClass() + " type objects.");
311               }
312             }
313             frefedSequence.remove(r);
314             rSize--;
315           }
316           else
317           {
318             System.err
319                     .println("IMPLEMENTATION WARNING: Unresolved forward reference for hash string "
320                             + ref[0]
321                             + " with objecttype "
322                             + ref[1].getClass());
323             r++;
324           }
325         }
326         else
327         {
328           // empty reference
329           frefedSequence.remove(r);
330           rSize--;
331         }
332       }
333     }
334   }
335
336   /**
337    * This maintains a list of viewports, the key being the seqSetId. Important
338    * to set historyItem and redoList for multiple views
339    */
340   Hashtable viewportsAdded;
341
342   Hashtable annotationIds = new Hashtable();
343
344   String uniqueSetSuffix = "";
345
346   /**
347    * List of pdbfiles added to Jar
348    */
349   List<String> pdbfiles = null;
350
351   // SAVES SEVERAL ALIGNMENT WINDOWS TO SAME JARFILE
352   public void saveState(File statefile)
353   {
354     FileOutputStream fos = null;
355     try
356     {
357       fos = new FileOutputStream(statefile);
358       JarOutputStream jout = new JarOutputStream(fos);
359       saveState(jout);
360
361     } catch (Exception e)
362     {
363       // TODO: inform user of the problem - they need to know if their data was
364       // not saved !
365       if (errorMessage == null)
366       {
367         errorMessage = "Couldn't write Jalview Archive to output file '"
368                 + statefile + "' - See console error log for details";
369       }
370       else
371       {
372         errorMessage += "(output file was '" + statefile + "')";
373       }
374       e.printStackTrace();
375     } finally
376     {
377       if (fos != null)
378       {
379         try
380         {
381           fos.close();
382         } catch (IOException e)
383         {
384           // ignore
385         }
386       }
387     }
388     reportErrors();
389   }
390
391   /**
392    * Writes a jalview project archive to the given Jar output stream.
393    * 
394    * @param jout
395    */
396   public void saveState(JarOutputStream jout)
397   {
398     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
399
400     if (frames == null)
401     {
402       return;
403     }
404
405     Hashtable<String, AlignFrame> dsses = new Hashtable<String, AlignFrame>();
406
407     try
408     {
409
410       // NOTE UTF-8 MUST BE USED FOR WRITING UNICODE CHARS
411       // //////////////////////////////////////////////////
412
413       Vector shortNames = new Vector();
414
415       // REVERSE ORDER
416       for (int i = frames.length - 1; i > -1; i--)
417       {
418         if (frames[i] instanceof AlignFrame)
419         {
420           AlignFrame af = (AlignFrame) frames[i];
421           // skip ?
422           if (skipList != null
423                   && skipList.containsKey(af.getViewport()
424                           .getSequenceSetId()))
425           {
426             continue;
427           }
428
429           String shortName = af.getTitle();
430
431           if (shortName.indexOf(File.separatorChar) > -1)
432           {
433             shortName = shortName.substring(shortName
434                     .lastIndexOf(File.separatorChar) + 1);
435           }
436
437           int count = 1;
438
439           while (shortNames.contains(shortName))
440           {
441             if (shortName.endsWith("_" + (count - 1)))
442             {
443               shortName = shortName
444                       .substring(0, shortName.lastIndexOf("_"));
445             }
446
447             shortName = shortName.concat("_" + count);
448             count++;
449           }
450
451           shortNames.addElement(shortName);
452
453           if (!shortName.endsWith(".xml"))
454           {
455             shortName = shortName + ".xml";
456           }
457
458           int ap, apSize = af.alignPanels.size();
459
460           for (ap = 0; ap < apSize; ap++)
461           {
462             AlignmentPanel apanel = (AlignmentPanel) af.alignPanels
463                     .elementAt(ap);
464             String fileName = apSize == 1 ? shortName : ap + shortName;
465             if (!fileName.endsWith(".xml"))
466             {
467               fileName = fileName + ".xml";
468             }
469
470             saveState(apanel, fileName, jout);
471
472             String dssid = getDatasetIdRef(af.getViewport().getAlignment()
473                     .getDataset());
474             if (!dsses.containsKey(dssid))
475             {
476               dsses.put(dssid, af);
477             }
478
479           }
480         }
481       }
482
483       writeDatasetFor(dsses, "" + jout.hashCode() + " " + uniqueSetSuffix,
484               jout);
485
486       try
487       {
488         jout.flush();
489       } catch (Exception foo)
490       {
491       }
492       ;
493       jout.close();
494     } catch (Exception ex)
495     {
496       // TODO: inform user of the problem - they need to know if their data was
497       // not saved !
498       if (errorMessage == null)
499       {
500         errorMessage = "Couldn't write Jalview Archive - see error output for details";
501       }
502       ex.printStackTrace();
503     }
504   }
505
506   // USE THIS METHOD TO SAVE A SINGLE ALIGNMENT WINDOW
507   public boolean saveAlignment(AlignFrame af, String jarFile,
508           String fileName)
509   {
510     try
511     {
512       int ap, apSize = af.alignPanels.size();
513       FileOutputStream fos = new FileOutputStream(jarFile);
514       JarOutputStream jout = new JarOutputStream(fos);
515       Hashtable<String, AlignFrame> dsses = new Hashtable<String, AlignFrame>();
516       for (ap = 0; ap < apSize; ap++)
517       {
518         AlignmentPanel apanel = (AlignmentPanel) af.alignPanels
519                 .elementAt(ap);
520         String jfileName = apSize == 1 ? fileName : fileName + ap;
521         if (!jfileName.endsWith(".xml"))
522         {
523           jfileName = jfileName + ".xml";
524         }
525         saveState(apanel, jfileName, jout);
526         String dssid = getDatasetIdRef(af.getViewport().getAlignment()
527                 .getDataset());
528         if (!dsses.containsKey(dssid))
529         {
530           dsses.put(dssid, af);
531         }
532       }
533       writeDatasetFor(dsses, fileName, jout);
534       try
535       {
536         jout.flush();
537       } catch (Exception foo)
538       {
539       }
540       ;
541       jout.close();
542       return true;
543     } catch (Exception ex)
544     {
545       errorMessage = "Couldn't Write alignment view to Jalview Archive - see error output for details";
546       ex.printStackTrace();
547       return false;
548     }
549   }
550
551   private void writeDatasetFor(Hashtable<String, AlignFrame> dsses,
552           String fileName, JarOutputStream jout)
553   {
554
555     for (String dssids : dsses.keySet())
556     {
557       AlignFrame _af = dsses.get(dssids);
558       String jfileName = fileName + " Dataset for " + _af.getTitle();
559       if (!jfileName.endsWith(".xml"))
560       {
561         jfileName = jfileName + ".xml";
562       }
563       saveState(_af.alignPanel, jfileName, true, jout);
564     }
565   }
566
567   /**
568    * create a JalviewModel from an alignment view and marshall it to a
569    * JarOutputStream
570    * 
571    * @param ap
572    *          panel to create jalview model for
573    * @param fileName
574    *          name of alignment panel written to output stream
575    * @param jout
576    *          jar output stream
577    * @param out
578    *          jar entry name
579    */
580   public JalviewModel saveState(AlignmentPanel ap, String fileName,
581           JarOutputStream jout)
582   {
583     return saveState(ap, fileName, false, jout);
584   }
585
586   /**
587    * create a JalviewModel from an alignment view and marshall it to a
588    * JarOutputStream
589    * 
590    * @param ap
591    *          panel to create jalview model for
592    * @param fileName
593    *          name of alignment panel written to output stream
594    * @param storeDS
595    *          when true, only write the dataset for the alignment, not the data
596    *          associated with the view.
597    * @param jout
598    *          jar output stream
599    * @param out
600    *          jar entry name
601    */
602   public JalviewModel saveState(AlignmentPanel ap, String fileName,
603           boolean storeDS, JarOutputStream jout)
604   {
605     initSeqRefs();
606     List<String> viewIds = new ArrayList<String>();
607     List<UserColourScheme> userColours = new ArrayList<UserColourScheme>();
608
609     AlignViewport av = ap.av;
610
611     JalviewModel object = new JalviewModel();
612     object.setVamsasModel(new jalview.schemabinding.version2.VamsasModel());
613
614     object.setCreationDate(new java.util.Date(System.currentTimeMillis()));
615     object.setVersion(jalview.bin.Cache.getDefault("VERSION",
616             "Development Build"));
617
618     jalview.datamodel.AlignmentI jal = av.getAlignment();
619
620     if (av.hasHiddenRows())
621     {
622       jal = jal.getHiddenSequences().getFullAlignment();
623     }
624
625     SequenceSet vamsasSet = new SequenceSet();
626     Sequence vamsasSeq;
627     JalviewModelSequence jms = new JalviewModelSequence();
628
629     vamsasSet.setGapChar(jal.getGapCharacter() + "");
630
631     if (jal.getDataset() != null)
632     {
633       // dataset id is the dataset's hashcode
634       vamsasSet.setDatasetId(getDatasetIdRef(jal.getDataset()));
635       if (storeDS)
636       {
637         // switch jal and the dataset
638         jal = jal.getDataset();
639       }
640     }
641     if (jal.getProperties() != null)
642     {
643       Enumeration en = jal.getProperties().keys();
644       while (en.hasMoreElements())
645       {
646         String key = en.nextElement().toString();
647         SequenceSetProperties ssp = new SequenceSetProperties();
648         ssp.setKey(key);
649         ssp.setValue(jal.getProperties().get(key).toString());
650         vamsasSet.addSequenceSetProperties(ssp);
651       }
652     }
653
654     JSeq jseq;
655     Set<String> calcIdSet = new HashSet<String>();
656
657     // SAVE SEQUENCES
658     String id = "";
659     jalview.datamodel.SequenceI jds, jdatasq;
660     for (int i = 0; i < jal.getHeight(); i++)
661     {
662       jds = jal.getSequenceAt(i);
663       jdatasq = jds.getDatasetSequence() == null ? jds : jds
664               .getDatasetSequence();
665       id = seqHash(jds);
666
667       if (seqRefIds.get(id) != null)
668       {
669         // This happens for two reasons: 1. multiple views are being serialised.
670         // 2. the hashCode has collided with another sequence's code. This DOES
671         // HAPPEN! (PF00072.15.stk does this)
672         // JBPNote: Uncomment to debug writing out of files that do not read
673         // back in due to ArrayOutOfBoundExceptions.
674         // System.err.println("vamsasSeq backref: "+id+"");
675         // System.err.println(jds.getName()+"
676         // "+jds.getStart()+"-"+jds.getEnd()+" "+jds.getSequenceAsString());
677         // System.err.println("Hashcode: "+seqHash(jds));
678         // SequenceI rsq = (SequenceI) seqRefIds.get(id + "");
679         // System.err.println(rsq.getName()+"
680         // "+rsq.getStart()+"-"+rsq.getEnd()+" "+rsq.getSequenceAsString());
681         // System.err.println("Hashcode: "+seqHash(rsq));
682       }
683       else
684       {
685         vamsasSeq = createVamsasSequence(id, jds);
686         vamsasSet.addSequence(vamsasSeq);
687         seqRefIds.put(id, jds);
688       }
689
690       jseq = new JSeq();
691       jseq.setStart(jds.getStart());
692       jseq.setEnd(jds.getEnd());
693       jseq.setColour(av.getSequenceColour(jds).getRGB());
694
695       jseq.setId(id); // jseq id should be a string not a number
696       if (!storeDS)
697       {
698         // Store any sequences this sequence represents
699         if (av.hasHiddenRows())
700         {
701           jseq.setHidden(av.getAlignment().getHiddenSequences()
702                   .isHidden(jds));
703
704           if (av.isHiddenRepSequence(jal.getSequenceAt(i)))
705           {
706             jalview.datamodel.SequenceI[] reps = av
707                     .getRepresentedSequences(jal.getSequenceAt(i))
708                     .getSequencesInOrder(jal);
709
710             for (int h = 0; h < reps.length; h++)
711             {
712               if (reps[h] != jal.getSequenceAt(i))
713               {
714                 jseq.addHiddenSequences(jal.findIndex(reps[h]));
715               }
716             }
717           }
718         }
719       }
720
721       if (jdatasq.getSequenceFeatures() != null)
722       {
723         jalview.datamodel.SequenceFeature[] sf = jdatasq
724                 .getSequenceFeatures();
725         int index = 0;
726         while (index < sf.length)
727         {
728           Features features = new Features();
729
730           features.setBegin(sf[index].getBegin());
731           features.setEnd(sf[index].getEnd());
732           features.setDescription(sf[index].getDescription());
733           features.setType(sf[index].getType());
734           features.setFeatureGroup(sf[index].getFeatureGroup());
735           features.setScore(sf[index].getScore());
736           if (sf[index].links != null)
737           {
738             for (int l = 0; l < sf[index].links.size(); l++)
739             {
740               OtherData keyValue = new OtherData();
741               keyValue.setKey("LINK_" + l);
742               keyValue.setValue(sf[index].links.elementAt(l).toString());
743               features.addOtherData(keyValue);
744             }
745           }
746           if (sf[index].otherDetails != null)
747           {
748             String key;
749             Enumeration keys = sf[index].otherDetails.keys();
750             while (keys.hasMoreElements())
751             {
752               key = keys.nextElement().toString();
753               OtherData keyValue = new OtherData();
754               keyValue.setKey(key);
755               keyValue.setValue(sf[index].otherDetails.get(key).toString());
756               features.addOtherData(keyValue);
757             }
758           }
759
760           jseq.addFeatures(features);
761           index++;
762         }
763       }
764
765       if (jdatasq.getPDBId() != null)
766       {
767         Enumeration en = jdatasq.getPDBId().elements();
768         while (en.hasMoreElements())
769         {
770           Pdbids pdb = new Pdbids();
771           jalview.datamodel.PDBEntry entry = (jalview.datamodel.PDBEntry) en
772                   .nextElement();
773
774           pdb.setId(entry.getId());
775           pdb.setType(entry.getType());
776
777           /*
778            * Store any structure views associated with this sequence. This
779            * section copes with duplicate entries in the project, so a dataset
780            * only view *should* be coped with sensibly.
781            */
782           // This must have been loaded, is it still visible?
783           JInternalFrame[] frames = Desktop.desktop.getAllFrames();
784           String matchedFile = null;
785           for (int f = frames.length - 1; f > -1; f--)
786           {
787             if (frames[f] instanceof StructureViewerBase)
788             {
789               StructureViewerBase viewFrame = (StructureViewerBase) frames[f];
790               matchedFile = saveStructureState(ap, jds, pdb, entry,
791                       viewIds, matchedFile, viewFrame);
792             }
793           }
794
795           if (matchedFile != null || entry.getFile() != null)
796           {
797             if (entry.getFile() != null)
798             {
799               // use entry's file
800               matchedFile = entry.getFile();
801             }
802             pdb.setFile(matchedFile); // entry.getFile());
803             if (pdbfiles == null)
804             {
805               pdbfiles = new ArrayList<String>();
806             }
807
808             if (!pdbfiles.contains(entry.getId()))
809             {
810               pdbfiles.add(entry.getId());
811               DataInputStream dis = null;
812               try
813               {
814                 File file = new File(matchedFile);
815                 if (file.exists() && jout != null)
816                 {
817                   byte[] data = new byte[(int) file.length()];
818                   jout.putNextEntry(new JarEntry(entry.getId()));
819                   dis = new DataInputStream(
820                           new FileInputStream(file));
821                   dis.readFully(data);
822
823                   DataOutputStream dout = new DataOutputStream(jout);
824                   dout.write(data, 0, data.length);
825                   dout.flush();
826                   jout.closeEntry();
827                 }
828               } catch (Exception ex)
829               {
830                 ex.printStackTrace();
831               } finally
832               {
833                 if (dis != null)
834                 {
835                   try
836                   {
837                     dis.close();
838                   } catch (IOException e)
839                   {
840                     // ignore
841                   }
842                 }
843               }
844
845             }
846           }
847
848           if (entry.getProperty() != null)
849           {
850             PdbentryItem item = new PdbentryItem();
851             Hashtable properties = entry.getProperty();
852             Enumeration en2 = properties.keys();
853             while (en2.hasMoreElements())
854             {
855               Property prop = new Property();
856               String key = en2.nextElement().toString();
857               prop.setName(key);
858               prop.setValue(properties.get(key).toString());
859               item.addProperty(prop);
860             }
861             pdb.addPdbentryItem(item);
862           }
863
864           jseq.addPdbids(pdb);
865         }
866       }
867
868       jms.addJSeq(jseq);
869     }
870
871     if (!storeDS && av.hasHiddenRows())
872     {
873       jal = av.getAlignment();
874     }
875     // SAVE MAPPINGS
876     if (jal.getCodonFrames() != null && jal.getCodonFrames().length > 0)
877     {
878       jalview.datamodel.AlignedCodonFrame[] jac = jal.getCodonFrames();
879       for (int i = 0; i < jac.length; i++)
880       {
881         AlcodonFrame alc = new AlcodonFrame();
882         vamsasSet.addAlcodonFrame(alc);
883         for (int p = 0; p < jac[i].aaWidth; p++)
884         {
885           Alcodon cmap = new Alcodon();
886           if (jac[i].codons[p] != null)
887           {
888             // Null codons indicate a gapped column in the translated peptide
889             // alignment.
890             cmap.setPos1(jac[i].codons[p][0]);
891             cmap.setPos2(jac[i].codons[p][1]);
892             cmap.setPos3(jac[i].codons[p][2]);
893           }
894           alc.addAlcodon(cmap);
895         }
896         if (jac[i].getProtMappings() != null
897                 && jac[i].getProtMappings().length > 0)
898         {
899           SequenceI[] dnas = jac[i].getdnaSeqs();
900           jalview.datamodel.Mapping[] pmaps = jac[i].getProtMappings();
901           for (int m = 0; m < pmaps.length; m++)
902           {
903             AlcodMap alcmap = new AlcodMap();
904             alcmap.setDnasq(seqHash(dnas[m]));
905             alcmap.setMapping(createVamsasMapping(pmaps[m], dnas[m], null,
906                     false));
907             alc.addAlcodMap(alcmap);
908           }
909         }
910       }
911     }
912
913     // SAVE TREES
914     // /////////////////////////////////
915     if (!storeDS && av.currentTree != null)
916     {
917       // FIND ANY ASSOCIATED TREES
918       // NOT IMPLEMENTED FOR HEADLESS STATE AT PRESENT
919       if (Desktop.desktop != null)
920       {
921         JInternalFrame[] frames = Desktop.desktop.getAllFrames();
922
923         for (int t = 0; t < frames.length; t++)
924         {
925           if (frames[t] instanceof TreePanel)
926           {
927             TreePanel tp = (TreePanel) frames[t];
928
929             if (tp.treeCanvas.av.getAlignment() == jal)
930             {
931               Tree tree = new Tree();
932               tree.setTitle(tp.getTitle());
933               tree.setCurrentTree((av.currentTree == tp.getTree()));
934               tree.setNewick(tp.getTree().toString());
935               tree.setThreshold(tp.treeCanvas.threshold);
936
937               tree.setFitToWindow(tp.fitToWindow.getState());
938               tree.setFontName(tp.getTreeFont().getName());
939               tree.setFontSize(tp.getTreeFont().getSize());
940               tree.setFontStyle(tp.getTreeFont().getStyle());
941               tree.setMarkUnlinked(tp.placeholdersMenu.getState());
942
943               tree.setShowBootstrap(tp.bootstrapMenu.getState());
944               tree.setShowDistances(tp.distanceMenu.getState());
945
946               tree.setHeight(tp.getHeight());
947               tree.setWidth(tp.getWidth());
948               tree.setXpos(tp.getX());
949               tree.setYpos(tp.getY());
950               tree.setId(makeHashCode(tp, null));
951               jms.addTree(tree);
952             }
953           }
954         }
955       }
956     }
957     // SAVE ANNOTATIONS
958     /**
959      * store forward refs from an annotationRow to any groups
960      */
961     IdentityHashMap groupRefs = new IdentityHashMap();
962     if (storeDS)
963     {
964       for (SequenceI sq : jal.getSequences())
965       {
966         // Store annotation on dataset sequences only
967         jalview.datamodel.AlignmentAnnotation[] aa = sq.getAnnotation();
968         if (aa != null && aa.length > 0)
969         {
970           storeAlignmentAnnotation(aa, groupRefs, av, calcIdSet, storeDS,
971                   vamsasSet);
972         }
973       }
974     }
975     else
976     {
977       if (jal.getAlignmentAnnotation() != null)
978       {
979         // Store the annotation shown on the alignment.
980         jalview.datamodel.AlignmentAnnotation[] aa = jal
981                 .getAlignmentAnnotation();
982         storeAlignmentAnnotation(aa, groupRefs, av, calcIdSet, storeDS,
983                 vamsasSet);
984       }
985     }
986     // SAVE GROUPS
987     if (jal.getGroups() != null)
988     {
989       JGroup[] groups = new JGroup[jal.getGroups().size()];
990       int i = -1;
991       for (jalview.datamodel.SequenceGroup sg : jal.getGroups())
992       {
993         groups[++i] = new JGroup();
994
995         groups[i].setStart(sg.getStartRes());
996         groups[i].setEnd(sg.getEndRes());
997         groups[i].setName(sg.getName());
998         if (groupRefs.containsKey(sg))
999         {
1000           // group has references so set it's ID field
1001           groups[i].setId(groupRefs.get(sg).toString());
1002         }
1003         if (sg.cs != null)
1004         {
1005           if (sg.cs.conservationApplied())
1006           {
1007             groups[i].setConsThreshold(sg.cs.getConservationInc());
1008
1009             if (sg.cs instanceof jalview.schemes.UserColourScheme)
1010             {
1011               groups[i].setColour(setUserColourScheme(sg.cs, userColours,
1012                       jms));
1013             }
1014             else
1015             {
1016               groups[i]
1017                       .setColour(ColourSchemeProperty.getColourName(sg.cs));
1018             }
1019           }
1020           else if (sg.cs instanceof jalview.schemes.AnnotationColourGradient)
1021           {
1022             groups[i].setColour("AnnotationColourGradient");
1023             groups[i].setAnnotationColours(constructAnnotationColours(
1024                     (jalview.schemes.AnnotationColourGradient) sg.cs,
1025                     userColours, jms));
1026           }
1027           else if (sg.cs instanceof jalview.schemes.UserColourScheme)
1028           {
1029             groups[i]
1030                     .setColour(setUserColourScheme(sg.cs, userColours, jms));
1031           }
1032           else
1033           {
1034             groups[i].setColour(ColourSchemeProperty.getColourName(sg.cs));
1035           }
1036
1037           groups[i].setPidThreshold(sg.cs.getThreshold());
1038         }
1039
1040         groups[i].setOutlineColour(sg.getOutlineColour().getRGB());
1041         groups[i].setDisplayBoxes(sg.getDisplayBoxes());
1042         groups[i].setDisplayText(sg.getDisplayText());
1043         groups[i].setColourText(sg.getColourText());
1044         groups[i].setTextCol1(sg.textColour.getRGB());
1045         groups[i].setTextCol2(sg.textColour2.getRGB());
1046         groups[i].setTextColThreshold(sg.thresholdTextColour);
1047         groups[i].setShowUnconserved(sg.getShowNonconserved());
1048         groups[i].setIgnoreGapsinConsensus(sg.getIgnoreGapsConsensus());
1049         groups[i].setShowConsensusHistogram(sg.isShowConsensusHistogram());
1050         groups[i].setShowSequenceLogo(sg.isShowSequenceLogo());
1051         groups[i].setNormaliseSequenceLogo(sg.isNormaliseSequenceLogo());
1052         for (int s = 0; s < sg.getSize(); s++)
1053         {
1054           jalview.datamodel.Sequence seq = (jalview.datamodel.Sequence) sg
1055                   .getSequenceAt(s);
1056           groups[i].addSeq(seqHash(seq));
1057         }
1058       }
1059
1060       jms.setJGroup(groups);
1061     }
1062     if (!storeDS)
1063     {
1064       // /////////SAVE VIEWPORT
1065       Viewport view = new Viewport();
1066       view.setTitle(ap.alignFrame.getTitle());
1067       view.setSequenceSetId(makeHashCode(av.getSequenceSetId(),
1068               av.getSequenceSetId()));
1069       view.setId(av.getViewId());
1070       view.setViewName(av.viewName);
1071       view.setGatheredViews(av.gatherViewsHere);
1072
1073       if (ap.av.explodedPosition != null)
1074       {
1075         view.setXpos(av.explodedPosition.x);
1076         view.setYpos(av.explodedPosition.y);
1077         view.setWidth(av.explodedPosition.width);
1078         view.setHeight(av.explodedPosition.height);
1079       }
1080       else
1081       {
1082         view.setXpos(ap.alignFrame.getBounds().x);
1083         view.setYpos(ap.alignFrame.getBounds().y);
1084         view.setWidth(ap.alignFrame.getBounds().width);
1085         view.setHeight(ap.alignFrame.getBounds().height);
1086       }
1087
1088       view.setStartRes(av.startRes);
1089       view.setStartSeq(av.startSeq);
1090
1091       if (av.getGlobalColourScheme() instanceof jalview.schemes.UserColourScheme)
1092       {
1093         view.setBgColour(setUserColourScheme(av.getGlobalColourScheme(),
1094                 userColours, jms));
1095       }
1096       else if (av.getGlobalColourScheme() instanceof jalview.schemes.AnnotationColourGradient)
1097       {
1098         AnnotationColours ac = constructAnnotationColours(
1099                 (jalview.schemes.AnnotationColourGradient) av
1100                         .getGlobalColourScheme(),
1101                 userColours, jms);
1102
1103         view.setAnnotationColours(ac);
1104         view.setBgColour("AnnotationColourGradient");
1105       }
1106       else
1107       {
1108         view.setBgColour(ColourSchemeProperty.getColourName(av
1109                 .getGlobalColourScheme()));
1110       }
1111
1112       ColourSchemeI cs = av.getGlobalColourScheme();
1113
1114       if (cs != null)
1115       {
1116         if (cs.conservationApplied())
1117         {
1118           view.setConsThreshold(cs.getConservationInc());
1119           if (cs instanceof jalview.schemes.UserColourScheme)
1120           {
1121             view.setBgColour(setUserColourScheme(cs, userColours, jms));
1122           }
1123         }
1124
1125         if (cs instanceof ResidueColourScheme)
1126         {
1127           view.setPidThreshold(cs.getThreshold());
1128         }
1129       }
1130
1131       view.setConservationSelected(av.getConservationSelected());
1132       view.setPidSelected(av.getAbovePIDThreshold());
1133       view.setFontName(av.font.getName());
1134       view.setFontSize(av.font.getSize());
1135       view.setFontStyle(av.font.getStyle());
1136       view.setRenderGaps(av.renderGaps);
1137       view.setShowAnnotation(av.getShowAnnotation());
1138       view.setShowBoxes(av.getShowBoxes());
1139       view.setShowColourText(av.getColourText());
1140       view.setShowFullId(av.getShowJVSuffix());
1141       view.setRightAlignIds(av.isRightAlignIds());
1142       view.setShowSequenceFeatures(av.showSequenceFeatures);
1143       view.setShowText(av.getShowText());
1144       view.setShowUnconserved(av.getShowUnconserved());
1145       view.setWrapAlignment(av.getWrapAlignment());
1146       view.setTextCol1(av.textColour.getRGB());
1147       view.setTextCol2(av.textColour2.getRGB());
1148       view.setTextColThreshold(av.thresholdTextColour);
1149       view.setShowConsensusHistogram(av.isShowConsensusHistogram());
1150       view.setShowSequenceLogo(av.isShowSequenceLogo());
1151       view.setNormaliseSequenceLogo(av.isNormaliseSequenceLogo());
1152       view.setShowGroupConsensus(av.isShowGroupConsensus());
1153       view.setShowGroupConservation(av.isShowGroupConservation());
1154       view.setShowNPfeatureTooltip(av.isShowNpFeats());
1155       view.setShowDbRefTooltip(av.isShowDbRefs());
1156       view.setFollowHighlight(av.followHighlight);
1157       view.setFollowSelection(av.followSelection);
1158       view.setIgnoreGapsinConsensus(av.getIgnoreGapsConsensus());
1159       if (av.getFeaturesDisplayed() != null)
1160       {
1161         jalview.schemabinding.version2.FeatureSettings fs = new jalview.schemabinding.version2.FeatureSettings();
1162
1163         String[] renderOrder = ap.getSeqPanel().seqCanvas.getFeatureRenderer().renderOrder;
1164
1165         Vector settingsAdded = new Vector();
1166         Object gstyle = null;
1167         GraduatedColor gcol = null;
1168         if (renderOrder != null)
1169         {
1170           for (int ro = 0; ro < renderOrder.length; ro++)
1171           {
1172             gstyle = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
1173                     .getFeatureStyle(renderOrder[ro]);
1174             Setting setting = new Setting();
1175             setting.setType(renderOrder[ro]);
1176             if (gstyle instanceof GraduatedColor)
1177             {
1178               gcol = (GraduatedColor) gstyle;
1179               setting.setColour(gcol.getMaxColor().getRGB());
1180               setting.setMincolour(gcol.getMinColor().getRGB());
1181               setting.setMin(gcol.getMin());
1182               setting.setMax(gcol.getMax());
1183               setting.setColourByLabel(gcol.isColourByLabel());
1184               setting.setAutoScale(gcol.isAutoScale());
1185               setting.setThreshold(gcol.getThresh());
1186               setting.setThreshstate(gcol.getThreshType());
1187             }
1188             else
1189             {
1190               setting.setColour(ap.getSeqPanel().seqCanvas.getFeatureRenderer()
1191                       .getColour(renderOrder[ro]).getRGB());
1192             }
1193
1194             setting.setDisplay(av.getFeaturesDisplayed()
1195                     .containsKey(renderOrder[ro]));
1196             float rorder = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
1197                     .getOrder(renderOrder[ro]);
1198             if (rorder > -1)
1199             {
1200               setting.setOrder(rorder);
1201             }
1202             fs.addSetting(setting);
1203             settingsAdded.addElement(renderOrder[ro]);
1204           }
1205         }
1206
1207         // Make sure we save none displayed feature settings
1208         Iterator en = ap.getSeqPanel().seqCanvas.getFeatureRenderer().featureColours
1209                 .keySet().iterator();
1210         while (en.hasNext())
1211         {
1212           String key = en.next().toString();
1213           if (settingsAdded.contains(key))
1214           {
1215             continue;
1216           }
1217
1218           Setting setting = new Setting();
1219           setting.setType(key);
1220           setting.setColour(ap.getSeqPanel().seqCanvas.getFeatureRenderer()
1221                   .getColour(key).getRGB());
1222
1223           setting.setDisplay(false);
1224           float rorder = ap.getSeqPanel().seqCanvas.getFeatureRenderer()
1225                   .getOrder(key);
1226           if (rorder > -1)
1227           {
1228             setting.setOrder(rorder);
1229           }
1230           fs.addSetting(setting);
1231           settingsAdded.addElement(key);
1232         }
1233         en = ap.getSeqPanel().seqCanvas.getFeatureRenderer().featureGroups
1234                 .keySet().iterator();
1235         Vector groupsAdded = new Vector();
1236         while (en.hasNext())
1237         {
1238           String grp = en.next().toString();
1239           if (groupsAdded.contains(grp))
1240           {
1241             continue;
1242           }
1243           Group g = new Group();
1244           g.setName(grp);
1245           g.setDisplay(((Boolean) ap.getSeqPanel().seqCanvas
1246                   .getFeatureRenderer().featureGroups.get(grp))
1247                   .booleanValue());
1248           fs.addGroup(g);
1249           groupsAdded.addElement(grp);
1250         }
1251         jms.setFeatureSettings(fs);
1252
1253       }
1254
1255       if (av.hasHiddenColumns())
1256       {
1257         if (av.getColumnSelection() == null
1258                 || av.getColumnSelection().getHiddenColumns() == null)
1259         {
1260           warn("REPORT BUG: avoided null columnselection bug (DMAM reported). Please contact Jim about this.");
1261         }
1262         else
1263         {
1264           for (int c = 0; c < av.getColumnSelection().getHiddenColumns()
1265                   .size(); c++)
1266           {
1267             int[] region = (int[]) av.getColumnSelection()
1268                     .getHiddenColumns().elementAt(c);
1269             HiddenColumns hc = new HiddenColumns();
1270             hc.setStart(region[0]);
1271             hc.setEnd(region[1]);
1272             view.addHiddenColumns(hc);
1273           }
1274         }
1275       }
1276       if (calcIdSet.size() > 0)
1277       {
1278         for (String calcId : calcIdSet)
1279         {
1280           if (calcId.trim().length() > 0)
1281           {
1282             CalcIdParam cidp = createCalcIdParam(calcId, av);
1283             // Some calcIds have no parameters.
1284             if (cidp != null)
1285             {
1286               view.addCalcIdParam(cidp);
1287             }
1288           }
1289         }
1290       }
1291
1292       jms.addViewport(view);
1293     }
1294     object.setJalviewModelSequence(jms);
1295     object.getVamsasModel().addSequenceSet(vamsasSet);
1296
1297     if (jout != null && fileName != null)
1298     {
1299       // We may not want to write the object to disk,
1300       // eg we can copy the alignViewport to a new view object
1301       // using save and then load
1302       try
1303       {
1304         JarEntry entry = new JarEntry(fileName);
1305         jout.putNextEntry(entry);
1306         PrintWriter pout = new PrintWriter(new OutputStreamWriter(jout,
1307                 "UTF-8"));
1308         org.exolab.castor.xml.Marshaller marshaller = new org.exolab.castor.xml.Marshaller(
1309                 pout);
1310         marshaller.marshal(object);
1311         pout.flush();
1312         jout.closeEntry();
1313       } catch (Exception ex)
1314       {
1315         // TODO: raise error in GUI if marshalling failed.
1316         ex.printStackTrace();
1317       }
1318     }
1319     return object;
1320   }
1321
1322   /**
1323    * Save the state of a structure viewer
1324    * 
1325    * @param ap
1326    * @param jds
1327    * @param pdb
1328    *          the archive XML element under which to save the state
1329    * @param entry
1330    * @param viewIds
1331    * @param matchedFile
1332    * @param viewFrame
1333    * @return
1334    */
1335   protected String saveStructureState(AlignmentPanel ap, SequenceI jds,
1336           Pdbids pdb, PDBEntry entry, List<String> viewIds,
1337           String matchedFile, StructureViewerBase viewFrame)
1338   {
1339     final AAStructureBindingModel bindingModel = viewFrame
1340             .getBinding();
1341     for (int peid = 0; peid < bindingModel
1342             .getPdbCount(); peid++)
1343     {
1344       final PDBEntry pdbentry = bindingModel.getPdbEntry(peid);
1345       final String pdbId = pdbentry.getId();
1346       if (!pdbId.equals(entry.getId())
1347               && !(entry.getId().length() > 4 && entry.getId()
1348                       .toLowerCase()
1349                       .startsWith(pdbId.toLowerCase())))
1350       {
1351         continue;
1352       }
1353       if (matchedFile == null)
1354       {
1355         matchedFile = pdbentry.getFile();
1356       }
1357       else if (!matchedFile.equals(pdbentry
1358               .getFile()))
1359       {
1360         Cache.log
1361                 .warn("Probably lost some PDB-Sequence mappings for this structure file (which apparently has same PDB Entry code): "
1362                         + pdbentry.getFile());
1363       }
1364       // record the
1365       // file so we
1366       // can get at it if the ID
1367       // match is ambiguous (e.g.
1368       // 1QIP==1qipA)
1369       String statestring = viewFrame.getStateInfo();
1370
1371       for (int smap = 0; smap < viewFrame.getBinding()
1372               .getSequence()[peid].length; smap++)
1373       {
1374         // if (jal.findIndex(jmol.jmb.sequence[peid][smap]) > -1)
1375         if (jds == viewFrame.getBinding().getSequence()[peid][smap])
1376         {
1377           StructureState state = new StructureState();
1378           state.setVisible(true);
1379           state.setXpos(viewFrame.getX());
1380           state.setYpos(viewFrame.getY());
1381           state.setWidth(viewFrame.getWidth());
1382           state.setHeight(viewFrame.getHeight());
1383           final String viewId = viewFrame.getViewId();
1384           state.setViewId(viewId);
1385           state.setAlignwithAlignPanel(viewFrame.isUsedforaligment(ap));
1386           state.setColourwithAlignPanel(viewFrame
1387                   .isUsedforcolourby(ap));
1388           state.setColourByJmol(viewFrame.isColouredByViewer());
1389           /*
1390            * Only store each structure viewer's state once in each XML document.
1391            */
1392           if (!viewIds.contains(viewId))
1393           {
1394             viewIds.add(viewId);
1395             state.setContent(statestring.replaceAll("\n", ""));
1396           }
1397           else
1398           {
1399             state.setContent("# duplicate state");
1400           }
1401           pdb.addStructureState(state);
1402         }
1403       }
1404     }
1405     return matchedFile;
1406   }
1407
1408   private AnnotationColours constructAnnotationColours(
1409           AnnotationColourGradient acg, List<UserColourScheme> userColours,
1410           JalviewModelSequence jms)
1411   {
1412     AnnotationColours ac = new AnnotationColours();
1413     ac.setAboveThreshold(acg.getAboveThreshold());
1414     ac.setThreshold(acg.getAnnotationThreshold());
1415     ac.setAnnotation(acg.getAnnotation());
1416     if (acg.getBaseColour() instanceof jalview.schemes.UserColourScheme)
1417     {
1418       ac.setColourScheme(setUserColourScheme(acg.getBaseColour(),
1419               userColours, jms));
1420     }
1421     else
1422     {
1423       ac.setColourScheme(ColourSchemeProperty.getColourName(acg
1424               .getBaseColour()));
1425     }
1426
1427     ac.setMaxColour(acg.getMaxColour().getRGB());
1428     ac.setMinColour(acg.getMinColour().getRGB());
1429     ac.setPerSequence(acg.isSeqAssociated());
1430     ac.setPredefinedColours(acg.isPredefinedColours());
1431     return ac;
1432   }
1433
1434   private void storeAlignmentAnnotation(AlignmentAnnotation[] aa,
1435           IdentityHashMap groupRefs, AlignmentViewport av,
1436           Set<String> calcIdSet, boolean storeDS, SequenceSet vamsasSet)
1437   {
1438
1439     for (int i = 0; i < aa.length; i++)
1440     {
1441       Annotation an = new Annotation();
1442
1443       if (aa[i].annotationId != null)
1444       {
1445         annotationIds.put(aa[i].annotationId, aa[i]);
1446       }
1447
1448       an.setId(aa[i].annotationId);
1449
1450       an.setVisible(aa[i].visible);
1451
1452       an.setDescription(aa[i].description);
1453
1454       if (aa[i].sequenceRef != null)
1455       {
1456         // TODO later annotation sequenceRef should be the XML ID of the
1457         // sequence rather than its display name
1458         an.setSequenceRef(aa[i].sequenceRef.getName());
1459       }
1460       if (aa[i].groupRef != null)
1461       {
1462         Object groupIdr = groupRefs.get(aa[i].groupRef);
1463         if (groupIdr == null)
1464         {
1465           // make a locally unique String
1466           groupRefs.put(aa[i].groupRef,
1467                   groupIdr = ("" + System.currentTimeMillis()
1468                           + aa[i].groupRef.getName() + groupRefs.size()));
1469         }
1470         an.setGroupRef(groupIdr.toString());
1471       }
1472
1473       // store all visualization attributes for annotation
1474       an.setGraphHeight(aa[i].graphHeight);
1475       an.setCentreColLabels(aa[i].centreColLabels);
1476       an.setScaleColLabels(aa[i].scaleColLabel);
1477       an.setShowAllColLabels(aa[i].showAllColLabels);
1478       an.setBelowAlignment(aa[i].belowAlignment);
1479
1480       if (aa[i].graph > 0)
1481       {
1482         an.setGraph(true);
1483         an.setGraphType(aa[i].graph);
1484         an.setGraphGroup(aa[i].graphGroup);
1485         if (aa[i].getThreshold() != null)
1486         {
1487           ThresholdLine line = new ThresholdLine();
1488           line.setLabel(aa[i].getThreshold().label);
1489           line.setValue(aa[i].getThreshold().value);
1490           line.setColour(aa[i].getThreshold().colour.getRGB());
1491           an.setThresholdLine(line);
1492         }
1493       }
1494       else
1495       {
1496         an.setGraph(false);
1497       }
1498
1499       an.setLabel(aa[i].label);
1500
1501       if (aa[i] == av.getAlignmentQualityAnnot()
1502               || aa[i] == av.getAlignmentConservationAnnotation()
1503               || aa[i] == av.getAlignmentConsensusAnnotation()
1504               || aa[i].autoCalculated)
1505       {
1506         // new way of indicating autocalculated annotation -
1507         an.setAutoCalculated(aa[i].autoCalculated);
1508       }
1509       if (aa[i].hasScore())
1510       {
1511         an.setScore(aa[i].getScore());
1512       }
1513
1514       if (aa[i].getCalcId() != null)
1515       {
1516         calcIdSet.add(aa[i].getCalcId());
1517         an.setCalcId(aa[i].getCalcId());
1518       }
1519       if (aa[i].hasProperties())
1520       {
1521         for (String pr : aa[i].getProperties())
1522         {
1523           Property prop = new Property();
1524           prop.setName(pr);
1525           prop.setValue(aa[i].getProperty(pr));
1526           an.addProperty(prop);
1527         }
1528       }
1529       AnnotationElement ae;
1530       if (aa[i].annotations != null)
1531       {
1532         an.setScoreOnly(false);
1533         for (int a = 0; a < aa[i].annotations.length; a++)
1534         {
1535           if ((aa[i] == null) || (aa[i].annotations[a] == null))
1536           {
1537             continue;
1538           }
1539
1540           ae = new AnnotationElement();
1541           if (aa[i].annotations[a].description != null)
1542           {
1543             ae.setDescription(aa[i].annotations[a].description);
1544           }
1545           if (aa[i].annotations[a].displayCharacter != null)
1546           {
1547             ae.setDisplayCharacter(aa[i].annotations[a].displayCharacter);
1548           }
1549
1550           if (!Float.isNaN(aa[i].annotations[a].value))
1551           {
1552             ae.setValue(aa[i].annotations[a].value);
1553           }
1554
1555           ae.setPosition(a);
1556           if (aa[i].annotations[a].secondaryStructure > ' ')
1557           {
1558             ae.setSecondaryStructure(aa[i].annotations[a].secondaryStructure
1559                     + "");
1560           }
1561
1562           if (aa[i].annotations[a].colour != null
1563                   && aa[i].annotations[a].colour != java.awt.Color.black)
1564           {
1565             ae.setColour(aa[i].annotations[a].colour.getRGB());
1566           }
1567
1568           an.addAnnotationElement(ae);
1569           if (aa[i].autoCalculated)
1570           {
1571             // only write one non-null entry into the annotation row -
1572             // sufficient to get the visualization attributes necessary to
1573             // display data
1574             continue;
1575           }
1576         }
1577       }
1578       else
1579       {
1580         an.setScoreOnly(true);
1581       }
1582       if (!storeDS || (storeDS && !aa[i].autoCalculated))
1583       {
1584         // skip autocalculated annotation - these are only provided for
1585         // alignments
1586         vamsasSet.addAnnotation(an);
1587       }
1588     }
1589
1590   }
1591
1592   private CalcIdParam createCalcIdParam(String calcId, AlignViewport av)
1593   {
1594     AutoCalcSetting settings = av.getCalcIdSettingsFor(calcId);
1595     if (settings != null)
1596     {
1597       CalcIdParam vCalcIdParam = new CalcIdParam();
1598       vCalcIdParam.setCalcId(calcId);
1599       vCalcIdParam.addServiceURL(settings.getServiceURI());
1600       // generic URI allowing a third party to resolve another instance of the
1601       // service used for this calculation
1602       for (String urls : settings.getServiceURLs())
1603       {
1604         vCalcIdParam.addServiceURL(urls);
1605       }
1606       vCalcIdParam.setVersion("1.0");
1607       if (settings.getPreset() != null)
1608       {
1609         WsParamSetI setting = settings.getPreset();
1610         vCalcIdParam.setName(setting.getName());
1611         vCalcIdParam.setDescription(setting.getDescription());
1612       }
1613       else
1614       {
1615         vCalcIdParam.setName("");
1616         vCalcIdParam.setDescription("Last used parameters");
1617       }
1618       // need to be able to recover 1) settings 2) user-defined presets or
1619       // recreate settings from preset 3) predefined settings provided by
1620       // service - or settings that can be transferred (or discarded)
1621       vCalcIdParam.setParameters(settings.getWsParamFile().replace("\n",
1622               "|\\n|"));
1623       vCalcIdParam.setAutoUpdate(settings.isAutoUpdate());
1624       // todo - decide if updateImmediately is needed for any projects.
1625
1626       return vCalcIdParam;
1627     }
1628     return null;
1629   }
1630
1631   private boolean recoverCalcIdParam(CalcIdParam calcIdParam,
1632           AlignViewport av)
1633   {
1634     if (calcIdParam.getVersion().equals("1.0"))
1635     {
1636       Jws2Instance service = Jws2Discoverer.getDiscoverer()
1637               .getPreferredServiceFor(calcIdParam.getServiceURL());
1638       if (service != null)
1639       {
1640         WsParamSetI parmSet = null;
1641         try
1642         {
1643           parmSet = service.getParamStore().parseServiceParameterFile(
1644                   calcIdParam.getName(), calcIdParam.getDescription(),
1645                   calcIdParam.getServiceURL(),
1646                   calcIdParam.getParameters().replace("|\\n|", "\n"));
1647         } catch (IOException x)
1648         {
1649           warn("Couldn't parse parameter data for "
1650                   + calcIdParam.getCalcId(), x);
1651           return false;
1652         }
1653         List<ArgumentI> argList = null;
1654         if (calcIdParam.getName().length() > 0)
1655         {
1656           parmSet = service.getParamStore()
1657                   .getPreset(calcIdParam.getName());
1658           if (parmSet != null)
1659           {
1660             // TODO : check we have a good match with settings in AACon -
1661             // otherwise we'll need to create a new preset
1662           }
1663         }
1664         else
1665         {
1666           argList = parmSet.getArguments();
1667           parmSet = null;
1668         }
1669         AAConSettings settings = new AAConSettings(
1670                 calcIdParam.isAutoUpdate(), service, parmSet, argList);
1671         av.setCalcIdSettingsFor(calcIdParam.getCalcId(), settings,
1672                 calcIdParam.isNeedsUpdate());
1673         return true;
1674       }
1675       else
1676       {
1677         warn("Cannot resolve a service for the parameters used in this project. Try configuring a JABAWS server.");
1678         return false;
1679       }
1680     }
1681     throw new Error(MessageManager.formatMessage("error.unsupported_version_calcIdparam", new String[]{calcIdParam.toString()}));
1682   }
1683
1684   /**
1685    * External mapping between jalview objects and objects yielding a valid and
1686    * unique object ID string. This is null for normal Jalview project IO, but
1687    * non-null when a jalview project is being read or written as part of a
1688    * vamsas session.
1689    */
1690   IdentityHashMap jv2vobj = null;
1691
1692   /**
1693    * Construct a unique ID for jvobj using either existing bindings or if none
1694    * exist, the result of the hashcode call for the object.
1695    * 
1696    * @param jvobj
1697    *          jalview data object
1698    * @return unique ID for referring to jvobj
1699    */
1700   private String makeHashCode(Object jvobj, String altCode)
1701   {
1702     if (jv2vobj != null)
1703     {
1704       Object id = jv2vobj.get(jvobj);
1705       if (id != null)
1706       {
1707         return id.toString();
1708       }
1709       // check string ID mappings
1710       if (jvids2vobj != null && jvobj instanceof String)
1711       {
1712         id = jvids2vobj.get(jvobj);
1713       }
1714       if (id != null)
1715       {
1716         return id.toString();
1717       }
1718       // give up and warn that something has gone wrong
1719       warn("Cannot find ID for object in external mapping : " + jvobj);
1720     }
1721     return altCode;
1722   }
1723
1724   /**
1725    * return local jalview object mapped to ID, if it exists
1726    * 
1727    * @param idcode
1728    *          (may be null)
1729    * @return null or object bound to idcode
1730    */
1731   private Object retrieveExistingObj(String idcode)
1732   {
1733     if (idcode != null && vobj2jv != null)
1734     {
1735       return vobj2jv.get(idcode);
1736     }
1737     return null;
1738   }
1739
1740   /**
1741    * binding from ID strings from external mapping table to jalview data model
1742    * objects.
1743    */
1744   private Hashtable vobj2jv;
1745
1746   private Sequence createVamsasSequence(String id, SequenceI jds)
1747   {
1748     return createVamsasSequence(true, id, jds, null);
1749   }
1750
1751   private Sequence createVamsasSequence(boolean recurse, String id,
1752           SequenceI jds, SequenceI parentseq)
1753   {
1754     Sequence vamsasSeq = new Sequence();
1755     vamsasSeq.setId(id);
1756     vamsasSeq.setName(jds.getName());
1757     vamsasSeq.setSequence(jds.getSequenceAsString());
1758     vamsasSeq.setDescription(jds.getDescription());
1759     jalview.datamodel.DBRefEntry[] dbrefs = null;
1760     if (jds.getDatasetSequence() != null)
1761     {
1762       vamsasSeq.setDsseqid(seqHash(jds.getDatasetSequence()));
1763       if (jds.getDatasetSequence().getDBRef() != null)
1764       {
1765         dbrefs = jds.getDatasetSequence().getDBRef();
1766       }
1767     }
1768     else
1769     {
1770       vamsasSeq.setDsseqid(id); // so we can tell which sequences really are
1771       // dataset sequences only
1772       dbrefs = jds.getDBRef();
1773     }
1774     if (dbrefs != null)
1775     {
1776       for (int d = 0; d < dbrefs.length; d++)
1777       {
1778         DBRef dbref = new DBRef();
1779         dbref.setSource(dbrefs[d].getSource());
1780         dbref.setVersion(dbrefs[d].getVersion());
1781         dbref.setAccessionId(dbrefs[d].getAccessionId());
1782         if (dbrefs[d].hasMap())
1783         {
1784           Mapping mp = createVamsasMapping(dbrefs[d].getMap(), parentseq,
1785                   jds, recurse);
1786           dbref.setMapping(mp);
1787         }
1788         vamsasSeq.addDBRef(dbref);
1789       }
1790     }
1791     return vamsasSeq;
1792   }
1793
1794   private Mapping createVamsasMapping(jalview.datamodel.Mapping jmp,
1795           SequenceI parentseq, SequenceI jds, boolean recurse)
1796   {
1797     Mapping mp = null;
1798     if (jmp.getMap() != null)
1799     {
1800       mp = new Mapping();
1801
1802       jalview.util.MapList mlst = jmp.getMap();
1803       int r[] = mlst.getFromRanges();
1804       for (int s = 0; s < r.length; s += 2)
1805       {
1806         MapListFrom mfrom = new MapListFrom();
1807         mfrom.setStart(r[s]);
1808         mfrom.setEnd(r[s + 1]);
1809         mp.addMapListFrom(mfrom);
1810       }
1811       r = mlst.getToRanges();
1812       for (int s = 0; s < r.length; s += 2)
1813       {
1814         MapListTo mto = new MapListTo();
1815         mto.setStart(r[s]);
1816         mto.setEnd(r[s + 1]);
1817         mp.addMapListTo(mto);
1818       }
1819       mp.setMapFromUnit(mlst.getFromRatio());
1820       mp.setMapToUnit(mlst.getToRatio());
1821       if (jmp.getTo() != null)
1822       {
1823         MappingChoice mpc = new MappingChoice();
1824         if (recurse
1825                 && (parentseq != jmp.getTo() || parentseq
1826                         .getDatasetSequence() != jmp.getTo()))
1827         {
1828           mpc.setSequence(createVamsasSequence(false, seqHash(jmp.getTo()),
1829                   jmp.getTo(), jds));
1830         }
1831         else
1832         {
1833           String jmpid = "";
1834           SequenceI ps = null;
1835           if (parentseq != jmp.getTo()
1836                   && parentseq.getDatasetSequence() != jmp.getTo())
1837           {
1838             // chaining dbref rather than a handshaking one
1839             jmpid = seqHash(ps = jmp.getTo());
1840           }
1841           else
1842           {
1843             jmpid = seqHash(ps = parentseq);
1844           }
1845           mpc.setDseqFor(jmpid);
1846           if (!seqRefIds.containsKey(mpc.getDseqFor()))
1847           {
1848             jalview.bin.Cache.log.debug("creatign new DseqFor ID");
1849             seqRefIds.put(mpc.getDseqFor(), ps);
1850           }
1851           else
1852           {
1853             jalview.bin.Cache.log.debug("reusing DseqFor ID");
1854           }
1855         }
1856         mp.setMappingChoice(mpc);
1857       }
1858     }
1859     return mp;
1860   }
1861
1862   String setUserColourScheme(jalview.schemes.ColourSchemeI cs,
1863           List<UserColourScheme> userColours, JalviewModelSequence jms)
1864   {
1865     String id = null;
1866     jalview.schemes.UserColourScheme ucs = (jalview.schemes.UserColourScheme) cs;
1867     boolean newucs = false;
1868     if (!userColours.contains(ucs))
1869     {
1870       userColours.add(ucs);
1871       newucs = true;
1872     }
1873     id = "ucs" + userColours.indexOf(ucs);
1874     if (newucs)
1875     {
1876       // actually create the scheme's entry in the XML model
1877       java.awt.Color[] colours = ucs.getColours();
1878       jalview.schemabinding.version2.UserColours uc = new jalview.schemabinding.version2.UserColours();
1879       jalview.schemabinding.version2.UserColourScheme jbucs = new jalview.schemabinding.version2.UserColourScheme();
1880
1881       for (int i = 0; i < colours.length; i++)
1882       {
1883         jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
1884         col.setName(ResidueProperties.aa[i]);
1885         col.setRGB(jalview.util.Format.getHexString(colours[i]));
1886         jbucs.addColour(col);
1887       }
1888       if (ucs.getLowerCaseColours() != null)
1889       {
1890         colours = ucs.getLowerCaseColours();
1891         for (int i = 0; i < colours.length; i++)
1892         {
1893           jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
1894           col.setName(ResidueProperties.aa[i].toLowerCase());
1895           col.setRGB(jalview.util.Format.getHexString(colours[i]));
1896           jbucs.addColour(col);
1897         }
1898       }
1899
1900       uc.setId(id);
1901       uc.setUserColourScheme(jbucs);
1902       jms.addUserColours(uc);
1903     }
1904
1905     return id;
1906   }
1907
1908   jalview.schemes.UserColourScheme getUserColourScheme(
1909           JalviewModelSequence jms, String id)
1910   {
1911     UserColours[] uc = jms.getUserColours();
1912     UserColours colours = null;
1913
1914     for (int i = 0; i < uc.length; i++)
1915     {
1916       if (uc[i].getId().equals(id))
1917       {
1918         colours = uc[i];
1919
1920         break;
1921       }
1922     }
1923
1924     java.awt.Color[] newColours = new java.awt.Color[24];
1925
1926     for (int i = 0; i < 24; i++)
1927     {
1928       newColours[i] = new java.awt.Color(Integer.parseInt(colours
1929               .getUserColourScheme().getColour(i).getRGB(), 16));
1930     }
1931
1932     jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme(
1933             newColours);
1934
1935     if (colours.getUserColourScheme().getColourCount() > 24)
1936     {
1937       newColours = new java.awt.Color[23];
1938       for (int i = 0; i < 23; i++)
1939       {
1940         newColours[i] = new java.awt.Color(Integer.parseInt(colours
1941                 .getUserColourScheme().getColour(i + 24).getRGB(), 16));
1942       }
1943       ucs.setLowerCaseColours(newColours);
1944     }
1945
1946     return ucs;
1947   }
1948
1949   /**
1950    * contains last error message (if any) encountered by XML loader.
1951    */
1952   String errorMessage = null;
1953
1954   /**
1955    * flag to control whether the Jalview2XML_V1 parser should be deferred to if
1956    * exceptions are raised during project XML parsing
1957    */
1958   public boolean attemptversion1parse = true;
1959
1960   /**
1961    * Load a jalview project archive from a jar file
1962    * 
1963    * @param file
1964    *          - HTTP URL or filename
1965    */
1966   public AlignFrame loadJalviewAlign(final String file)
1967   {
1968
1969     jalview.gui.AlignFrame af = null;
1970
1971     try
1972     {
1973       // create list to store references for any new Jmol viewers created
1974       newStructureViewers = new Vector<JalviewStructureDisplayI>();
1975       // UNMARSHALLER SEEMS TO CLOSE JARINPUTSTREAM, MOST ANNOYING
1976       // Workaround is to make sure caller implements the JarInputStreamProvider
1977       // interface
1978       // so we can re-open the jar input stream for each entry.
1979
1980       jarInputStreamProvider jprovider = createjarInputStreamProvider(file);
1981       af = loadJalviewAlign(jprovider);
1982
1983     } catch (MalformedURLException e)
1984     {
1985       errorMessage = "Invalid URL format for '" + file + "'";
1986       reportErrors();
1987     } finally
1988     {
1989       try
1990       {
1991         SwingUtilities.invokeAndWait(new Runnable()
1992         {
1993           public void run()
1994           {
1995             setLoadingFinishedForNewStructureViewers();
1996           };
1997         });
1998       } catch (Exception x)
1999       {
2000
2001       }
2002     }
2003     return af;
2004   }
2005
2006   private jarInputStreamProvider createjarInputStreamProvider(
2007           final String file) throws MalformedURLException
2008   {
2009     URL url = null;
2010     errorMessage = null;
2011     uniqueSetSuffix = null;
2012     seqRefIds = null;
2013     viewportsAdded = null;
2014     frefedSequence = null;
2015
2016     if (file.startsWith("http://"))
2017     {
2018       url = new URL(file);
2019     }
2020     final URL _url = url;
2021     return new jarInputStreamProvider()
2022     {
2023
2024       @Override
2025       public JarInputStream getJarInputStream() throws IOException
2026       {
2027         if (_url != null)
2028         {
2029           return new JarInputStream(_url.openStream());
2030         }
2031         else
2032         {
2033           return new JarInputStream(new FileInputStream(file));
2034         }
2035       }
2036
2037       @Override
2038       public String getFilename()
2039       {
2040         return file;
2041       }
2042     };
2043   }
2044
2045   /**
2046    * Recover jalview session from a jalview project archive. Caller may
2047    * initialise uniqueSetSuffix, seqRefIds, viewportsAdded and frefedSequence
2048    * themselves. Any null fields will be initialised with default values,
2049    * non-null fields are left alone.
2050    * 
2051    * @param jprovider
2052    * @return
2053    */
2054   public AlignFrame loadJalviewAlign(final jarInputStreamProvider jprovider)
2055   {
2056     errorMessage = null;
2057     if (uniqueSetSuffix == null)
2058     {
2059       uniqueSetSuffix = System.currentTimeMillis() % 100000 + "";
2060     }
2061     if (seqRefIds == null)
2062     {
2063       seqRefIds = new HashMap<String, SequenceI>();
2064     }
2065     if (viewportsAdded == null)
2066     {
2067       viewportsAdded = new Hashtable();
2068     }
2069     if (frefedSequence == null)
2070     {
2071       frefedSequence = new Vector();
2072     }
2073
2074     jalview.gui.AlignFrame af = null, _af = null;
2075     Hashtable gatherToThisFrame = new Hashtable();
2076     final String file = jprovider.getFilename();
2077     try
2078     {
2079       JarInputStream jin = null;
2080       JarEntry jarentry = null;
2081       int entryCount = 1;
2082
2083       do
2084       {
2085         jin = jprovider.getJarInputStream();
2086         for (int i = 0; i < entryCount; i++)
2087         {
2088           jarentry = jin.getNextJarEntry();
2089         }
2090
2091         if (jarentry != null && jarentry.getName().endsWith(".xml"))
2092         {
2093           InputStreamReader in = new InputStreamReader(jin, "UTF-8");
2094           JalviewModel object = new JalviewModel();
2095
2096           Unmarshaller unmar = new Unmarshaller(object);
2097           unmar.setValidation(false);
2098           object = (JalviewModel) unmar.unmarshal(in);
2099           if (true) // !skipViewport(object))
2100           {
2101             _af = loadFromObject(object, file, true, jprovider);
2102             if (object.getJalviewModelSequence().getViewportCount() > 0)
2103             {
2104               af = _af;
2105               if (af.viewport.gatherViewsHere)
2106               {
2107                 gatherToThisFrame.put(af.viewport.getSequenceSetId(), af);
2108               }
2109             }
2110           }
2111           entryCount++;
2112         }
2113         else if (jarentry != null)
2114         {
2115           // Some other file here.
2116           entryCount++;
2117         }
2118       } while (jarentry != null);
2119       resolveFrefedSequences();
2120     } catch (java.io.FileNotFoundException ex)
2121     {
2122       ex.printStackTrace();
2123       errorMessage = "Couldn't locate Jalview XML file : " + file;
2124       System.err.println("Exception whilst loading jalview XML file : "
2125               + ex + "\n");
2126     } catch (java.net.UnknownHostException ex)
2127     {
2128       ex.printStackTrace();
2129       errorMessage = "Couldn't locate Jalview XML file : " + file;
2130       System.err.println("Exception whilst loading jalview XML file : "
2131               + ex + "\n");
2132     } catch (Exception ex)
2133     {
2134       System.err.println("Parsing as Jalview Version 2 file failed.");
2135       ex.printStackTrace(System.err);
2136       if (attemptversion1parse)
2137       {
2138         // Is Version 1 Jar file?
2139         try
2140         {
2141           af = new Jalview2XML_V1(raiseGUI).LoadJalviewAlign(jprovider);
2142         } catch (Exception ex2)
2143         {
2144           System.err.println("Exception whilst loading as jalviewXMLV1:");
2145           ex2.printStackTrace();
2146           af = null;
2147         }
2148       }
2149       if (Desktop.instance != null)
2150       {
2151         Desktop.instance.stopLoading();
2152       }
2153       if (af != null)
2154       {
2155         System.out.println("Successfully loaded archive file");
2156         return af;
2157       }
2158       ex.printStackTrace();
2159
2160       System.err.println("Exception whilst loading jalview XML file : "
2161               + ex + "\n");
2162     } catch (OutOfMemoryError e)
2163     {
2164       // Don't use the OOM Window here
2165       errorMessage = "Out of memory loading jalview XML file";
2166       System.err.println("Out of memory whilst loading jalview XML file");
2167       e.printStackTrace();
2168     }
2169
2170     if (Desktop.instance != null)
2171     {
2172       Desktop.instance.stopLoading();
2173     }
2174
2175     Enumeration en = gatherToThisFrame.elements();
2176     while (en.hasMoreElements())
2177     {
2178       Desktop.instance.gatherViews((AlignFrame) en.nextElement());
2179     }
2180     if (errorMessage != null)
2181     {
2182       reportErrors();
2183     }
2184     return af;
2185   }
2186
2187   /**
2188    * check errorMessage for a valid error message and raise an error box in the
2189    * GUI or write the current errorMessage to stderr and then clear the error
2190    * state.
2191    */
2192   protected void reportErrors()
2193   {
2194     reportErrors(false);
2195   }
2196
2197   protected void reportErrors(final boolean saving)
2198   {
2199     if (errorMessage != null)
2200     {
2201       final String finalErrorMessage = errorMessage;
2202       if (raiseGUI)
2203       {
2204         javax.swing.SwingUtilities.invokeLater(new Runnable()
2205         {
2206           @Override
2207           public void run()
2208           {
2209             JOptionPane.showInternalMessageDialog(Desktop.desktop,
2210                     finalErrorMessage, "Error "
2211                             + (saving ? "saving" : "loading")
2212                             + " Jalview file", JOptionPane.WARNING_MESSAGE);
2213           }
2214         });
2215       }
2216       else
2217       {
2218         System.err.println("Problem loading Jalview file: " + errorMessage);
2219       }
2220     }
2221     errorMessage = null;
2222   }
2223
2224   Hashtable<String, String> alreadyLoadedPDB;
2225
2226   /**
2227    * when set, local views will be updated from view stored in JalviewXML
2228    * Currently (28th Sep 2008) things will go horribly wrong in vamsas document
2229    * sync if this is set to true.
2230    */
2231   private final boolean updateLocalViews = false;
2232
2233   String loadPDBFile(jarInputStreamProvider jprovider, String pdbId)
2234   {
2235     if (alreadyLoadedPDB == null)
2236     {
2237       alreadyLoadedPDB = new Hashtable();
2238     }
2239
2240     if (alreadyLoadedPDB.containsKey(pdbId))
2241     {
2242       return alreadyLoadedPDB.get(pdbId).toString();
2243     }
2244
2245     try
2246     {
2247       JarInputStream jin = jprovider.getJarInputStream();
2248       /*
2249        * if (jprovider.startsWith("http://")) { jin = new JarInputStream(new
2250        * URL(jprovider).openStream()); } else { jin = new JarInputStream(new
2251        * FileInputStream(jprovider)); }
2252        */
2253
2254       JarEntry entry = null;
2255       do
2256       {
2257         entry = jin.getNextJarEntry();
2258       } while (entry != null && !entry.getName().equals(pdbId));
2259       if (entry != null)
2260       {
2261         BufferedReader in = new BufferedReader(new InputStreamReader(jin));
2262         File outFile = File.createTempFile("jalview_pdb", ".txt");
2263         outFile.deleteOnExit();
2264         PrintWriter out = new PrintWriter(new FileOutputStream(outFile));
2265         String data;
2266
2267         while ((data = in.readLine()) != null)
2268         {
2269           out.println(data);
2270         }
2271         try
2272         {
2273           out.flush();
2274         } catch (Exception foo)
2275         {
2276         }
2277         ;
2278         out.close();
2279         String t = outFile.getAbsolutePath();
2280         alreadyLoadedPDB.put(pdbId, t);
2281         return t;
2282       }
2283       else
2284       {
2285         warn("Couldn't find PDB file entry in Jalview Jar for " + pdbId);
2286       }
2287     } catch (Exception ex)
2288     {
2289       ex.printStackTrace();
2290     }
2291
2292     return null;
2293   }
2294
2295   private class JvAnnotRow
2296   {
2297     public JvAnnotRow(int i, AlignmentAnnotation jaa)
2298     {
2299       order = i;
2300       template = jaa;
2301     }
2302
2303     /**
2304      * persisted version of annotation row from which to take vis properties
2305      */
2306     public jalview.datamodel.AlignmentAnnotation template;
2307
2308     /**
2309      * original position of the annotation row in the alignment
2310      */
2311     public int order;
2312   }
2313
2314   /**
2315    * Load alignment frame from jalview XML DOM object
2316    * 
2317    * @param object
2318    *          DOM
2319    * @param file
2320    *          filename source string
2321    * @param loadTreesAndStructures
2322    *          when false only create Viewport
2323    * @param jprovider
2324    *          data source provider
2325    * @return alignment frame created from view stored in DOM
2326    */
2327   AlignFrame loadFromObject(JalviewModel object, String file,
2328           boolean loadTreesAndStructures, jarInputStreamProvider jprovider)
2329   {
2330     SequenceSet vamsasSet = object.getVamsasModel().getSequenceSet(0);
2331     Sequence[] vamsasSeq = vamsasSet.getSequence();
2332
2333     JalviewModelSequence jms = object.getJalviewModelSequence();
2334
2335     Viewport view = (jms.getViewportCount() > 0) ? jms.getViewport(0)
2336             : null;
2337
2338     // ////////////////////////////////
2339     // LOAD SEQUENCES
2340
2341     Vector hiddenSeqs = null;
2342     jalview.datamodel.Sequence jseq;
2343
2344     ArrayList tmpseqs = new ArrayList();
2345
2346     boolean multipleView = false;
2347
2348     JSeq[] jseqs = object.getJalviewModelSequence().getJSeq();
2349     int vi = 0; // counter in vamsasSeq array
2350     for (int i = 0; i < jseqs.length; i++)
2351     {
2352       String seqId = jseqs[i].getId();
2353
2354       if (seqRefIds.get(seqId) != null)
2355       {
2356         tmpseqs.add(seqRefIds.get(seqId));
2357         multipleView = true;
2358       }
2359       else
2360       {
2361         jseq = new jalview.datamodel.Sequence(vamsasSeq[vi].getName(),
2362                 vamsasSeq[vi].getSequence());
2363         jseq.setDescription(vamsasSeq[vi].getDescription());
2364         jseq.setStart(jseqs[i].getStart());
2365         jseq.setEnd(jseqs[i].getEnd());
2366         jseq.setVamsasId(uniqueSetSuffix + seqId);
2367         seqRefIds.put(vamsasSeq[vi].getId(), jseq);
2368         tmpseqs.add(jseq);
2369         vi++;
2370       }
2371
2372       if (jseqs[i].getHidden())
2373       {
2374         if (hiddenSeqs == null)
2375         {
2376           hiddenSeqs = new Vector();
2377         }
2378
2379         hiddenSeqs.addElement(seqRefIds.get(seqId));
2380       }
2381
2382     }
2383
2384     // /
2385     // Create the alignment object from the sequence set
2386     // ///////////////////////////////
2387     jalview.datamodel.Sequence[] orderedSeqs = new jalview.datamodel.Sequence[tmpseqs
2388             .size()];
2389
2390     tmpseqs.toArray(orderedSeqs);
2391
2392     jalview.datamodel.Alignment al = new jalview.datamodel.Alignment(
2393             orderedSeqs);
2394
2395     // / Add the alignment properties
2396     for (int i = 0; i < vamsasSet.getSequenceSetPropertiesCount(); i++)
2397     {
2398       SequenceSetProperties ssp = vamsasSet.getSequenceSetProperties(i);
2399       al.setProperty(ssp.getKey(), ssp.getValue());
2400     }
2401
2402     // /
2403     // SequenceFeatures are added to the DatasetSequence,
2404     // so we must create or recover the dataset before loading features
2405     // ///////////////////////////////
2406     if (vamsasSet.getDatasetId() == null || vamsasSet.getDatasetId() == "")
2407     {
2408       // older jalview projects do not have a dataset id.
2409       al.setDataset(null);
2410     }
2411     else
2412     {
2413       // recover dataset - passing on flag indicating if this a 'viewless'
2414       // sequence set (a.k.a. a stored dataset for the project)
2415       recoverDatasetFor(vamsasSet, al, object.getJalviewModelSequence()
2416               .getViewportCount() == 0);
2417     }
2418     // ///////////////////////////////
2419
2420     Hashtable pdbloaded = new Hashtable();
2421     if (!multipleView)
2422     {
2423       // load sequence features, database references and any associated PDB
2424       // structures for the alignment
2425       for (int i = 0; i < vamsasSeq.length; i++)
2426       {
2427         if (jseqs[i].getFeaturesCount() > 0)
2428         {
2429           Features[] features = jseqs[i].getFeatures();
2430           for (int f = 0; f < features.length; f++)
2431           {
2432             jalview.datamodel.SequenceFeature sf = new jalview.datamodel.SequenceFeature(
2433                     features[f].getType(), features[f].getDescription(),
2434                     features[f].getStatus(), features[f].getBegin(),
2435                     features[f].getEnd(), features[f].getFeatureGroup());
2436
2437             sf.setScore(features[f].getScore());
2438             for (int od = 0; od < features[f].getOtherDataCount(); od++)
2439             {
2440               OtherData keyValue = features[f].getOtherData(od);
2441               if (keyValue.getKey().startsWith("LINK"))
2442               {
2443                 sf.addLink(keyValue.getValue());
2444               }
2445               else
2446               {
2447                 sf.setValue(keyValue.getKey(), keyValue.getValue());
2448               }
2449
2450             }
2451
2452             al.getSequenceAt(i).getDatasetSequence().addSequenceFeature(sf);
2453           }
2454         }
2455         if (vamsasSeq[i].getDBRefCount() > 0)
2456         {
2457           addDBRefs(al.getSequenceAt(i).getDatasetSequence(), vamsasSeq[i]);
2458         }
2459         if (jseqs[i].getPdbidsCount() > 0)
2460         {
2461           Pdbids[] ids = jseqs[i].getPdbids();
2462           for (int p = 0; p < ids.length; p++)
2463           {
2464             jalview.datamodel.PDBEntry entry = new jalview.datamodel.PDBEntry();
2465             entry.setId(ids[p].getId());
2466             entry.setType(ids[p].getType());
2467             if (ids[p].getFile() != null)
2468             {
2469               if (!pdbloaded.containsKey(ids[p].getFile()))
2470               {
2471                 entry.setFile(loadPDBFile(jprovider, ids[p].getId()));
2472               }
2473               else
2474               {
2475                 entry.setFile(pdbloaded.get(ids[p].getId()).toString());
2476               }
2477             }
2478             StructureSelectionManager.getStructureSelectionManager(
2479                     Desktop.instance)
2480                     .registerPDBEntry(entry);
2481             al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
2482           }
2483         }
2484       }
2485     } // end !multipleview
2486
2487     // ///////////////////////////////
2488     // LOAD SEQUENCE MAPPINGS
2489
2490     if (vamsasSet.getAlcodonFrameCount() > 0)
2491     {
2492       // TODO Potentially this should only be done once for all views of an
2493       // alignment
2494       AlcodonFrame[] alc = vamsasSet.getAlcodonFrame();
2495       for (int i = 0; i < alc.length; i++)
2496       {
2497         jalview.datamodel.AlignedCodonFrame cf = new jalview.datamodel.AlignedCodonFrame(
2498                 alc[i].getAlcodonCount());
2499         if (alc[i].getAlcodonCount() > 0)
2500         {
2501           Alcodon[] alcods = alc[i].getAlcodon();
2502           for (int p = 0; p < cf.codons.length; p++)
2503           {
2504             if (alcods[p].hasPos1() && alcods[p].hasPos2()
2505                     && alcods[p].hasPos3())
2506             {
2507               // translated codons require three valid positions
2508               cf.codons[p] = new int[3];
2509               cf.codons[p][0] = (int) alcods[p].getPos1();
2510               cf.codons[p][1] = (int) alcods[p].getPos2();
2511               cf.codons[p][2] = (int) alcods[p].getPos3();
2512             }
2513             else
2514             {
2515               cf.codons[p] = null;
2516             }
2517           }
2518         }
2519         if (alc[i].getAlcodMapCount() > 0)
2520         {
2521           AlcodMap[] maps = alc[i].getAlcodMap();
2522           for (int m = 0; m < maps.length; m++)
2523           {
2524             SequenceI dnaseq = seqRefIds
2525                     .get(maps[m].getDnasq());
2526             // Load Mapping
2527             jalview.datamodel.Mapping mapping = null;
2528             // attach to dna sequence reference.
2529             if (maps[m].getMapping() != null)
2530             {
2531               mapping = addMapping(maps[m].getMapping());
2532             }
2533             if (dnaseq != null)
2534             {
2535               cf.addMap(dnaseq, mapping.getTo(), mapping.getMap());
2536             }
2537             else
2538             {
2539               // defer to later
2540               frefedSequence.add(new Object[]
2541               { maps[m].getDnasq(), cf, mapping });
2542             }
2543           }
2544         }
2545         al.addCodonFrame(cf);
2546       }
2547
2548     }
2549
2550     // ////////////////////////////////
2551     // LOAD ANNOTATIONS
2552     ArrayList<JvAnnotRow> autoAlan = new ArrayList<JvAnnotRow>();
2553     /**
2554      * store any annotations which forward reference a group's ID
2555      */
2556     Hashtable<String, ArrayList<jalview.datamodel.AlignmentAnnotation>> groupAnnotRefs = new Hashtable<String, ArrayList<jalview.datamodel.AlignmentAnnotation>>();
2557
2558     if (vamsasSet.getAnnotationCount() > 0)
2559     {
2560       Annotation[] an = vamsasSet.getAnnotation();
2561
2562       for (int i = 0; i < an.length; i++)
2563       {
2564         /**
2565          * test if annotation is automatically calculated for this view only
2566          */
2567         boolean autoForView = false;
2568         if (an[i].getLabel().equals("Quality")
2569                 || an[i].getLabel().equals("Conservation")
2570                 || an[i].getLabel().equals("Consensus"))
2571         {
2572           // Kludge for pre 2.5 projects which lacked the autocalculated flag
2573           autoForView = true;
2574           if (!an[i].hasAutoCalculated())
2575           {
2576             an[i].setAutoCalculated(true);
2577           }
2578         }
2579         if (autoForView
2580                 || (an[i].hasAutoCalculated() && an[i].isAutoCalculated()))
2581         {
2582           // remove ID - we don't recover annotation from other views for
2583           // view-specific annotation
2584           an[i].setId(null);
2585         }
2586
2587         // set visiblity for other annotation in this view
2588         if (an[i].getId() != null
2589                 && annotationIds.containsKey(an[i].getId()))
2590         {
2591           jalview.datamodel.AlignmentAnnotation jda = (jalview.datamodel.AlignmentAnnotation) annotationIds
2592                   .get(an[i].getId());
2593           // in principle Visible should always be true for annotation displayed
2594           // in multiple views
2595           if (an[i].hasVisible())
2596           {
2597             jda.visible = an[i].getVisible();
2598           }
2599
2600           al.addAnnotation(jda);
2601
2602           continue;
2603         }
2604         // Construct new annotation from model.
2605         AnnotationElement[] ae = an[i].getAnnotationElement();
2606         jalview.datamodel.Annotation[] anot = null;
2607         java.awt.Color firstColour = null;
2608         int anpos;
2609         if (!an[i].getScoreOnly())
2610         {
2611           anot = new jalview.datamodel.Annotation[al.getWidth()];
2612           for (int aa = 0; aa < ae.length && aa < anot.length; aa++)
2613           {
2614             anpos = ae[aa].getPosition();
2615
2616             if (anpos >= anot.length)
2617             {
2618               continue;
2619             }
2620
2621             anot[anpos] = new jalview.datamodel.Annotation(
2622
2623             ae[aa].getDisplayCharacter(), ae[aa].getDescription(),
2624                     (ae[aa].getSecondaryStructure() == null || ae[aa]
2625                             .getSecondaryStructure().length() == 0) ? ' '
2626                             : ae[aa].getSecondaryStructure().charAt(0),
2627                     ae[aa].getValue()
2628
2629             );
2630             // JBPNote: Consider verifying dataflow for IO of secondary
2631             // structure annotation read from Stockholm files
2632             // this was added to try to ensure that
2633             // if (anot[ae[aa].getPosition()].secondaryStructure>' ')
2634             // {
2635             // anot[ae[aa].getPosition()].displayCharacter = "";
2636             // }
2637             anot[anpos].colour = new java.awt.Color(ae[aa].getColour());
2638             if (firstColour == null)
2639             {
2640               firstColour = anot[anpos].colour;
2641             }
2642           }
2643         }
2644         jalview.datamodel.AlignmentAnnotation jaa = null;
2645
2646         if (an[i].getGraph())
2647         {
2648           float llim = 0, hlim = 0;
2649           // if (autoForView || an[i].isAutoCalculated()) {
2650           // hlim=11f;
2651           // }
2652           jaa = new jalview.datamodel.AlignmentAnnotation(an[i].getLabel(),
2653                   an[i].getDescription(), anot, llim, hlim,
2654                   an[i].getGraphType());
2655
2656           jaa.graphGroup = an[i].getGraphGroup();
2657           jaa._linecolour = firstColour;
2658           if (an[i].getThresholdLine() != null)
2659           {
2660             jaa.setThreshold(new jalview.datamodel.GraphLine(an[i]
2661                     .getThresholdLine().getValue(), an[i]
2662                     .getThresholdLine().getLabel(), new java.awt.Color(
2663                     an[i].getThresholdLine().getColour())));
2664
2665           }
2666           if (autoForView || an[i].isAutoCalculated())
2667           {
2668             // Hardwire the symbol display line to ensure that labels for
2669             // histograms are displayed
2670             jaa.hasText = true;
2671           }
2672         }
2673         else
2674         {
2675           jaa = new jalview.datamodel.AlignmentAnnotation(an[i].getLabel(),
2676                   an[i].getDescription(), anot);
2677           jaa._linecolour = firstColour;
2678         }
2679         // register new annotation
2680         if (an[i].getId() != null)
2681         {
2682           annotationIds.put(an[i].getId(), jaa);
2683           jaa.annotationId = an[i].getId();
2684         }
2685         // recover sequence association
2686         if (an[i].getSequenceRef() != null)
2687         {
2688           if (al.findName(an[i].getSequenceRef()) != null)
2689           {
2690             jaa.createSequenceMapping(al.findName(an[i].getSequenceRef()),
2691                     1, true);
2692             al.findName(an[i].getSequenceRef()).addAlignmentAnnotation(jaa);
2693           }
2694         }
2695         // and make a note of any group association
2696         if (an[i].getGroupRef() != null && an[i].getGroupRef().length() > 0)
2697         {
2698           ArrayList<jalview.datamodel.AlignmentAnnotation> aal = groupAnnotRefs
2699                   .get(an[i].getGroupRef());
2700           if (aal == null)
2701           {
2702             aal = new ArrayList<jalview.datamodel.AlignmentAnnotation>();
2703             groupAnnotRefs.put(an[i].getGroupRef(), aal);
2704           }
2705           aal.add(jaa);
2706         }
2707
2708         if (an[i].hasScore())
2709         {
2710           jaa.setScore(an[i].getScore());
2711         }
2712         if (an[i].hasVisible())
2713         {
2714           jaa.visible = an[i].getVisible();
2715         }
2716
2717         if (an[i].hasCentreColLabels())
2718         {
2719           jaa.centreColLabels = an[i].getCentreColLabels();
2720         }
2721
2722         if (an[i].hasScaleColLabels())
2723         {
2724           jaa.scaleColLabel = an[i].getScaleColLabels();
2725         }
2726         if (an[i].hasAutoCalculated() && an[i].isAutoCalculated())
2727         {
2728           // newer files have an 'autoCalculated' flag and store calculation
2729           // state in viewport properties
2730           jaa.autoCalculated = true; // means annotation will be marked for
2731           // update at end of load.
2732         }
2733         if (an[i].hasGraphHeight())
2734         {
2735           jaa.graphHeight = an[i].getGraphHeight();
2736         }
2737         if (an[i].hasBelowAlignment())
2738         {
2739           jaa.belowAlignment = an[i].isBelowAlignment();
2740         }
2741         jaa.setCalcId(an[i].getCalcId());
2742         if (an[i].getPropertyCount() > 0)
2743         {
2744           for (jalview.schemabinding.version2.Property prop : an[i]
2745                   .getProperty())
2746           {
2747             jaa.setProperty(prop.getName(), prop.getValue());
2748           }
2749         }
2750         if (jaa.autoCalculated)
2751         {
2752           autoAlan.add(new JvAnnotRow(i, jaa));
2753         }
2754         else
2755         // if (!autoForView)
2756         {
2757           // add autocalculated group annotation and any user created annotation
2758           // for the view
2759           al.addAnnotation(jaa);
2760         }
2761       }
2762     }
2763     // ///////////////////////
2764     // LOAD GROUPS
2765     // Create alignment markup and styles for this view
2766     if (jms.getJGroupCount() > 0)
2767     {
2768       JGroup[] groups = jms.getJGroup();
2769       boolean addAnnotSchemeGroup = false;
2770       for (int i = 0; i < groups.length; i++)
2771       {
2772         ColourSchemeI cs = null;
2773
2774         if (groups[i].getColour() != null)
2775         {
2776           if (groups[i].getColour().startsWith("ucs"))
2777           {
2778             cs = getUserColourScheme(jms, groups[i].getColour());
2779           }
2780           else if (groups[i].getColour().equals("AnnotationColourGradient")
2781                   && groups[i].getAnnotationColours() != null)
2782           {
2783             addAnnotSchemeGroup = true;
2784             cs = null;
2785           }
2786           else
2787           {
2788             cs = ColourSchemeProperty.getColour(al, groups[i].getColour());
2789           }
2790
2791           if (cs != null)
2792           {
2793             cs.setThreshold(groups[i].getPidThreshold(), true);
2794           }
2795         }
2796
2797         Vector seqs = new Vector();
2798
2799         for (int s = 0; s < groups[i].getSeqCount(); s++)
2800         {
2801           String seqId = groups[i].getSeq(s) + "";
2802           jalview.datamodel.SequenceI ts = seqRefIds
2803                   .get(seqId);
2804
2805           if (ts != null)
2806           {
2807             seqs.addElement(ts);
2808           }
2809         }
2810
2811         if (seqs.size() < 1)
2812         {
2813           continue;
2814         }
2815
2816         jalview.datamodel.SequenceGroup sg = new jalview.datamodel.SequenceGroup(
2817                 seqs, groups[i].getName(), cs, groups[i].getDisplayBoxes(),
2818                 groups[i].getDisplayText(), groups[i].getColourText(),
2819                 groups[i].getStart(), groups[i].getEnd());
2820
2821         sg.setOutlineColour(new java.awt.Color(groups[i].getOutlineColour()));
2822
2823         sg.textColour = new java.awt.Color(groups[i].getTextCol1());
2824         sg.textColour2 = new java.awt.Color(groups[i].getTextCol2());
2825         sg.setShowNonconserved(groups[i].hasShowUnconserved() ? groups[i]
2826                 .isShowUnconserved() : false);
2827         sg.thresholdTextColour = groups[i].getTextColThreshold();
2828         if (groups[i].hasShowConsensusHistogram())
2829         {
2830           sg.setShowConsensusHistogram(groups[i].isShowConsensusHistogram());
2831         }
2832         ;
2833         if (groups[i].hasShowSequenceLogo())
2834         {
2835           sg.setshowSequenceLogo(groups[i].isShowSequenceLogo());
2836         }
2837         if (groups[i].hasNormaliseSequenceLogo())
2838         {
2839           sg.setNormaliseSequenceLogo(groups[i].isNormaliseSequenceLogo());
2840         }
2841         if (groups[i].hasIgnoreGapsinConsensus())
2842         {
2843           sg.setIgnoreGapsConsensus(groups[i].getIgnoreGapsinConsensus());
2844         }
2845         if (groups[i].getConsThreshold() != 0)
2846         {
2847           jalview.analysis.Conservation c = new jalview.analysis.Conservation(
2848                   "All", ResidueProperties.propHash, 3,
2849                   sg.getSequences(null), 0, sg.getWidth() - 1);
2850           c.calculate();
2851           c.verdict(false, 25);
2852           sg.cs.setConservation(c);
2853         }
2854
2855         if (groups[i].getId() != null && groupAnnotRefs.size() > 0)
2856         {
2857           // re-instate unique group/annotation row reference
2858           ArrayList<jalview.datamodel.AlignmentAnnotation> jaal = groupAnnotRefs
2859                   .get(groups[i].getId());
2860           if (jaal != null)
2861           {
2862             for (jalview.datamodel.AlignmentAnnotation jaa : jaal)
2863             {
2864               jaa.groupRef = sg;
2865               if (jaa.autoCalculated)
2866               {
2867                 // match up and try to set group autocalc alignment row for this
2868                 // annotation
2869                 if (jaa.label.startsWith("Consensus for "))
2870                 {
2871                   sg.setConsensus(jaa);
2872                 }
2873                 // match up and try to set group autocalc alignment row for this
2874                 // annotation
2875                 if (jaa.label.startsWith("Conservation for "))
2876                 {
2877                   sg.setConservationRow(jaa);
2878                 }
2879               }
2880             }
2881           }
2882         }
2883         al.addGroup(sg);
2884         if (addAnnotSchemeGroup)
2885         {
2886           // reconstruct the annotation colourscheme
2887           sg.cs = constructAnnotationColour(
2888                   groups[i].getAnnotationColours(), null, al, jms, false);
2889         }
2890       }
2891     }
2892     if (view == null)
2893     {
2894       // only dataset in this model, so just return.
2895       return null;
2896     }
2897     // ///////////////////////////////
2898     // LOAD VIEWPORT
2899
2900     // If we just load in the same jar file again, the sequenceSetId
2901     // will be the same, and we end up with multiple references
2902     // to the same sequenceSet. We must modify this id on load
2903     // so that each load of the file gives a unique id
2904     String uniqueSeqSetId = view.getSequenceSetId() + uniqueSetSuffix;
2905     String viewId = (view.getId() == null ? null : view.getId()
2906             + uniqueSetSuffix);
2907     AlignFrame af = null;
2908     AlignViewport av = null;
2909     // now check to see if we really need to create a new viewport.
2910     if (multipleView && viewportsAdded.size() == 0)
2911     {
2912       // We recovered an alignment for which a viewport already exists.
2913       // TODO: fix up any settings necessary for overlaying stored state onto
2914       // state recovered from another document. (may not be necessary).
2915       // we may need a binding from a viewport in memory to one recovered from
2916       // XML.
2917       // and then recover its containing af to allow the settings to be applied.
2918       // TODO: fix for vamsas demo
2919       System.err
2920               .println("About to recover a viewport for existing alignment: Sequence set ID is "
2921                       + uniqueSeqSetId);
2922       Object seqsetobj = retrieveExistingObj(uniqueSeqSetId);
2923       if (seqsetobj != null)
2924       {
2925         if (seqsetobj instanceof String)
2926         {
2927           uniqueSeqSetId = (String) seqsetobj;
2928           System.err
2929                   .println("Recovered extant sequence set ID mapping for ID : New Sequence set ID is "
2930                           + uniqueSeqSetId);
2931         }
2932         else
2933         {
2934           System.err
2935                   .println("Warning : Collision between sequence set ID string and existing jalview object mapping.");
2936         }
2937
2938       }
2939     }
2940     /**
2941      * indicate that annotation colours are applied across all groups (pre
2942      * Jalview 2.8.1 behaviour)
2943      */
2944     boolean doGroupAnnColour = isVersionStringLaterThan("2.8.1",
2945             object.getVersion());
2946
2947     AlignmentPanel ap = null;
2948     boolean isnewview = true;
2949     if (viewId != null)
2950     {
2951       // Check to see if this alignment already has a view id == viewId
2952       jalview.gui.AlignmentPanel views[] = Desktop
2953               .getAlignmentPanels(uniqueSeqSetId);
2954       if (views != null && views.length > 0)
2955       {
2956         for (int v = 0; v < views.length; v++)
2957         {
2958           if (views[v].av.getViewId().equalsIgnoreCase(viewId))
2959           {
2960             // recover the existing alignpanel, alignframe, viewport
2961             af = views[v].alignFrame;
2962             av = views[v].av;
2963             ap = views[v];
2964             // TODO: could even skip resetting view settings if we don't want to
2965             // change the local settings from other jalview processes
2966             isnewview = false;
2967           }
2968         }
2969       }
2970     }
2971
2972     if (isnewview)
2973     {
2974       af = loadViewport(file, jseqs, hiddenSeqs, al, jms, view,
2975               uniqueSeqSetId, viewId, autoAlan);
2976       av = af.viewport;
2977       ap = af.alignPanel;
2978     }
2979     // LOAD TREES
2980     // /////////////////////////////////////
2981     if (loadTreesAndStructures && jms.getTreeCount() > 0)
2982     {
2983       try
2984       {
2985         for (int t = 0; t < jms.getTreeCount(); t++)
2986         {
2987
2988           Tree tree = jms.getTree(t);
2989
2990           TreePanel tp = (TreePanel) retrieveExistingObj(tree.getId());
2991           if (tp == null)
2992           {
2993             tp = af.ShowNewickTree(
2994                     new jalview.io.NewickFile(tree.getNewick()),
2995                     tree.getTitle(), tree.getWidth(), tree.getHeight(),
2996                     tree.getXpos(), tree.getYpos());
2997             if (tree.getId() != null)
2998             {
2999               // perhaps bind the tree id to something ?
3000             }
3001           }
3002           else
3003           {
3004             // update local tree attributes ?
3005             // TODO: should check if tp has been manipulated by user - if so its
3006             // settings shouldn't be modified
3007             tp.setTitle(tree.getTitle());
3008             tp.setBounds(new Rectangle(tree.getXpos(), tree.getYpos(), tree
3009                     .getWidth(), tree.getHeight()));
3010             tp.av = av; // af.viewport; // TODO: verify 'associate with all
3011             // views'
3012             // works still
3013             tp.treeCanvas.av = av; // af.viewport;
3014             tp.treeCanvas.ap = ap; // af.alignPanel;
3015
3016           }
3017           if (tp == null)
3018           {
3019             warn("There was a problem recovering stored Newick tree: \n"
3020                     + tree.getNewick());
3021             continue;
3022           }
3023
3024           tp.fitToWindow.setState(tree.getFitToWindow());
3025           tp.fitToWindow_actionPerformed(null);
3026
3027           if (tree.getFontName() != null)
3028           {
3029             tp.setTreeFont(new java.awt.Font(tree.getFontName(), tree
3030                     .getFontStyle(), tree.getFontSize()));
3031           }
3032           else
3033           {
3034             tp.setTreeFont(new java.awt.Font(view.getFontName(), view
3035                     .getFontStyle(), tree.getFontSize()));
3036           }
3037
3038           tp.showPlaceholders(tree.getMarkUnlinked());
3039           tp.showBootstrap(tree.getShowBootstrap());
3040           tp.showDistances(tree.getShowDistances());
3041
3042           tp.treeCanvas.threshold = tree.getThreshold();
3043
3044           if (tree.getCurrentTree())
3045           {
3046             af.viewport.setCurrentTree(tp.getTree());
3047           }
3048         }
3049
3050       } catch (Exception ex)
3051       {
3052         ex.printStackTrace();
3053       }
3054     }
3055
3056     // //LOAD STRUCTURES
3057     if (loadTreesAndStructures)
3058     {
3059       loadStructures(jprovider, jseqs, af, ap);
3060     }
3061     // and finally return.
3062     return af;
3063   }
3064
3065   /**
3066    * Load and link any saved structure viewers.
3067    * 
3068    * @param jprovider
3069    * @param jseqs
3070    * @param af
3071    * @param ap
3072    */
3073   protected void loadStructures(jarInputStreamProvider jprovider,
3074           JSeq[] jseqs, AlignFrame af, AlignmentPanel ap)
3075   {
3076     // run through all PDB ids on the alignment, and collect mappings between
3077     // jmol view ids and all sequences referring to it
3078     Map<String, ViewerData> jmolViewIds = new HashMap<String, ViewerData>();
3079
3080     for (int i = 0; i < jseqs.length; i++)
3081     {
3082       if (jseqs[i].getPdbidsCount() > 0)
3083       {
3084         Pdbids[] ids = jseqs[i].getPdbids();
3085         for (int p = 0; p < ids.length; p++)
3086         {
3087           final int structureStateCount = ids[p].getStructureStateCount();
3088           for (int s = 0; s < structureStateCount; s++)
3089           {
3090             // check to see if we haven't already created this structure view
3091             final StructureState structureState = ids[p].getStructureState(s);
3092             String sviewid = (structureState.getViewId() == null) ? null
3093                     : structureState.getViewId()
3094                             + uniqueSetSuffix;
3095             jalview.datamodel.PDBEntry jpdb = new jalview.datamodel.PDBEntry();
3096             // Originally : ids[p].getFile()
3097             // : TODO: verify external PDB file recovery still works in normal
3098             // jalview project load
3099             jpdb.setFile(loadPDBFile(jprovider, ids[p].getId()));
3100             jpdb.setId(ids[p].getId());
3101
3102             int x = structureState.getXpos();
3103             int y = structureState.getYpos();
3104             int width = structureState.getWidth();
3105             int height = structureState.getHeight();
3106
3107             // Probably don't need to do this anymore...
3108             // Desktop.desktop.getComponentAt(x, y);
3109             // TODO: NOW: check that this recovers the PDB file correctly.
3110             String pdbFile = loadPDBFile(jprovider, ids[p].getId());
3111             jalview.datamodel.SequenceI seq = seqRefIds
3112                     .get(jseqs[i].getId() + "");
3113             if (sviewid == null)
3114             {
3115               sviewid = "_jalview_pre2_4_" + x + "," + y + "," + width
3116                       + "," + height;
3117             }
3118             if (!jmolViewIds.containsKey(sviewid))
3119             {
3120               jmolViewIds.put(sviewid, new ViewerData(x, y, width, height,
3121                       false, false, true));
3122               // Legacy pre-2.7 conversion JAL-823 :
3123               // do not assume any view has to be linked for colour by
3124               // sequence
3125             }
3126
3127             // assemble String[] { pdb files }, String[] { id for each
3128             // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
3129             // seqs_file 2}, boolean[] {
3130             // linkAlignPanel,superposeWithAlignpanel}} from hash
3131             ViewerData jmoldat = jmolViewIds.get(sviewid);
3132             jmoldat.alignWithPanel |= structureState
3133                     .hasAlignwithAlignPanel() ? structureState.getAlignwithAlignPanel() : false;
3134             // never colour by linked panel if not specified
3135             jmoldat.colourWithAlignPanel |= structureState
3136                     .hasColourwithAlignPanel() ? structureState.getColourwithAlignPanel()
3137                     : false;
3138             // default for pre-2.7 projects is that Jmol colouring is enabled
3139             jmoldat.colourByViewer &= structureState
3140                     .hasColourByJmol() ? structureState
3141                     .getColourByJmol() : true;
3142
3143             if (jmoldat.stateData.length() < structureState.getContent()
3144                     .length())
3145             {
3146               {
3147                 jmoldat.stateData = structureState.getContent();
3148               }
3149             }
3150             if (ids[p].getFile() != null)
3151             {
3152               File mapkey = new File(ids[p].getFile());
3153               Object[] seqstrmaps = jmoldat.fileData.get(mapkey);
3154               if (seqstrmaps == null)
3155               {
3156                 jmoldat.fileData.put(mapkey,
3157                         seqstrmaps = new Object[]
3158                         { pdbFile, ids[p].getId(), new Vector(),
3159                             new Vector() });
3160               }
3161               if (!((Vector) seqstrmaps[2]).contains(seq))
3162               {
3163                 ((Vector) seqstrmaps[2]).addElement(seq);
3164                 // ((Vector)seqstrmaps[3]).addElement(n) :
3165                 // in principle, chains
3166                 // should be stored here : do we need to
3167                 // TODO: store and recover seq/pdb_id :
3168                 // chain mappings
3169               }
3170             }
3171             else
3172             {
3173               errorMessage = ("The Jmol views in this project were imported\nfrom an older version of Jalview.\nPlease review the sequence colour associations\nin the Colour by section of the Jmol View menu.\n\nIn the case of problems, see note at\nhttp://issues.jalview.org/browse/JAL-747");
3174               warn(errorMessage);
3175             }
3176           }
3177         }
3178       }
3179     }
3180       // Instantiate the associated structure views
3181       for (Entry<String, ViewerData> entry : jmolViewIds.entrySet())
3182       {
3183         createOrLinkStructureViewer(entry, af, ap);
3184       }
3185   }
3186
3187   /**
3188    * 
3189    * @param viewerData
3190    * @param af
3191    * @param ap
3192    */
3193   protected void createOrLinkStructureViewer(
3194           Entry<String, ViewerData> viewerData, AlignFrame af,
3195           AlignmentPanel ap)
3196   {
3197     final ViewerData svattrib = viewerData.getValue();
3198
3199     /*
3200      * Search for any viewer windows already open from other alignment views
3201      * that exactly match the stored structure state
3202      */
3203     StructureViewerBase comp = findMatchingViewer(viewerData);
3204
3205     if (comp != null)
3206     {
3207       linkStructureViewer(ap, comp, svattrib);
3208       return;
3209     }
3210
3211     /*
3212      * Pending an XML element for ViewerType, just check if stateData contains
3213      * "chimera" (part of the chimera session filename).
3214      */
3215     if (svattrib.stateData.indexOf("chimera") > -1)
3216     {
3217       createChimeraViewer(viewerData, af);
3218     }
3219     else
3220     {
3221       createJmolViewer(viewerData, af);
3222     }
3223   }
3224
3225   /**
3226    * Create a new Chimera viewer.
3227    * 
3228    * @param viewerData
3229    * @param af
3230    */
3231   protected void createChimeraViewer(Entry<String, ViewerData> viewerData,
3232           AlignFrame af)
3233   {
3234     final ViewerData data = viewerData.getValue();
3235     String chimeraSession = data.stateData;
3236     if (new File(chimeraSession).exists())
3237     {
3238       // TODO can/should this be done via StructureViewer (like Jmol)?
3239       ChimeraViewFrame cvf = new ChimeraViewFrame(data, af);
3240     }
3241     else
3242     {
3243       Cache.log.error("Chimera session file " + chimeraSession
3244               + " not found");
3245     }
3246   }
3247
3248   /**
3249    * Create a new Jmol window. First parse the Jmol state to translate filenames
3250    * loaded into the view, and record the order in which files are shown in the
3251    * Jmol view, so we can add the sequence mappings in same order.
3252    * 
3253    * @param viewerData
3254    * @param af
3255    */
3256   protected void createJmolViewer(
3257           final Entry<String, ViewerData> viewerData, AlignFrame af)
3258   {
3259     final ViewerData svattrib = viewerData.getValue();
3260     String state = svattrib.stateData;
3261     List<String> pdbfilenames = new ArrayList<String>();
3262     List<SequenceI[]> seqmaps = new ArrayList<SequenceI[]>();
3263     List<String> pdbids = new ArrayList<String>();
3264     StringBuilder newFileLoc = new StringBuilder(64);
3265     int cp = 0, ncp, ecp;
3266     Map<File, Object[]> oldFiles = svattrib.fileData;
3267     while ((ncp = state.indexOf("load ", cp)) > -1)
3268     {
3269       do
3270       {
3271         // look for next filename in load statement
3272         newFileLoc.append(state.substring(cp,
3273                 ncp = (state.indexOf("\"", ncp + 1) + 1)));
3274         String oldfilenam = state.substring(ncp,
3275                 ecp = state.indexOf("\"", ncp));
3276         // recover the new mapping data for this old filename
3277         // have to normalize filename - since Jmol and jalview do
3278         // filename
3279         // translation differently.
3280         Object[] filedat = oldFiles.get(new File(oldfilenam));
3281         newFileLoc.append(Platform.escapeString((String) filedat[0]));
3282         pdbfilenames.add((String) filedat[0]);
3283         pdbids.add((String) filedat[1]);
3284         seqmaps.add(((Vector<SequenceI>) filedat[2])
3285                 .toArray(new SequenceI[0]));
3286         newFileLoc.append("\"");
3287         cp = ecp + 1; // advance beyond last \" and set cursor so we can
3288                       // look for next file statement.
3289       } while ((ncp = state.indexOf("/*file*/", cp)) > -1);
3290     }
3291     if (cp > 0)
3292     {
3293       // just append rest of state
3294       newFileLoc.append(state.substring(cp));
3295     }
3296     else
3297     {
3298       System.err.print("Ignoring incomplete Jmol state for PDB ids: ");
3299       newFileLoc = new StringBuilder(state);
3300       newFileLoc.append("; load append ");
3301       for (File id : oldFiles.keySet())
3302       {
3303         // add this and any other pdb files that should be present in
3304         // the viewer
3305         Object[] filedat = oldFiles.get(id);
3306         String nfilename;
3307         newFileLoc.append(((String) filedat[0]));
3308         pdbfilenames.add((String) filedat[0]);
3309         pdbids.add((String) filedat[1]);
3310         seqmaps.add(((Vector<SequenceI>) filedat[2])
3311                 .toArray(new SequenceI[0]));
3312         newFileLoc.append(" \"");
3313         newFileLoc.append((String) filedat[0]);
3314         newFileLoc.append("\"");
3315
3316       }
3317       newFileLoc.append(";");
3318     }
3319
3320     if (newFileLoc.length() > 0)
3321     {
3322       int histbug = newFileLoc.indexOf("history = ");
3323       histbug += 10;
3324       int diff = histbug == -1 ? -1 : newFileLoc.indexOf(";", histbug);
3325       String val = (diff == -1) ? null : newFileLoc
3326               .substring(histbug, diff);
3327       if (val != null && val.length() >= 4)
3328       {
3329         if (val.contains("e"))
3330         {
3331           if (val.trim().equals("true"))
3332           {
3333             val = "1";
3334           }
3335           else
3336           {
3337             val = "0";
3338           }
3339           newFileLoc.replace(histbug, diff, val);
3340         }
3341       }
3342
3343       final String[] pdbf = pdbfilenames.toArray(new String[pdbfilenames
3344               .size()]);
3345       final String[] id = pdbids.toArray(new String[pdbids.size()]);
3346       final SequenceI[][] sq = seqmaps
3347               .toArray(new SequenceI[seqmaps.size()][]);
3348       final String fileloc = newFileLoc.toString();
3349       final String sviewid = viewerData.getKey();
3350       final AlignFrame alf = af;
3351       final java.awt.Rectangle rect = new java.awt.Rectangle(svattrib.x,
3352               svattrib.y, svattrib.width, svattrib.height);
3353       try
3354       {
3355         javax.swing.SwingUtilities.invokeAndWait(new Runnable()
3356         {
3357           @Override
3358           public void run()
3359           {
3360             JalviewStructureDisplayI sview = null;
3361             try
3362             {
3363               // JAL-1333 note - we probably can't migrate Jmol views to UCSF
3364               // Chimera!
3365               sview = new StructureViewer(alf.alignPanel
3366                       .getStructureSelectionManager()).createView(
3367                       StructureViewer.ViewerType.JMOL, pdbf, id, sq,
3368                       alf.alignPanel, svattrib, fileloc, rect, sviewid);
3369               addNewStructureViewer(sview);
3370             } catch (OutOfMemoryError ex)
3371             {
3372               new OOMWarning("restoring structure view for PDB id " + id,
3373                       (OutOfMemoryError) ex.getCause());
3374               if (sview != null && sview.isVisible())
3375               {
3376                 sview.closeViewer();
3377                 sview.setVisible(false);
3378                 sview.dispose();
3379               }
3380             }
3381           }
3382         });
3383       } catch (InvocationTargetException ex)
3384       {
3385         warn("Unexpected error when opening Jmol view.", ex);
3386
3387       } catch (InterruptedException e)
3388       {
3389         // e.printStackTrace();
3390       }
3391     }
3392   }
3393
3394   /**
3395    * Returns any open frame that matches given structure viewer data. The match
3396    * is based on the unique viewId, or (for older project versions) the frame's
3397    * geometry.
3398    * 
3399    * @param viewerData
3400    * @return
3401    */
3402   protected StructureViewerBase findMatchingViewer(
3403           Entry<String, ViewerData> viewerData)
3404   {
3405     final String sviewid = viewerData.getKey();
3406     final ViewerData svattrib = viewerData.getValue();
3407     StructureViewerBase comp = null;
3408     JInternalFrame[] frames = getAllFrames();
3409     for (JInternalFrame frame : frames)
3410     {
3411       if (frame instanceof StructureViewerBase)
3412       {
3413         /*
3414          * Post jalview 2.4 schema includes structure view id
3415          */
3416         if (sviewid != null
3417                 && ((StructureViewerBase) frame).getViewId().equals(
3418                         sviewid))
3419         {
3420           comp = (AppJmol) frame;
3421           // todo: break?
3422         }
3423         /*
3424          * Otherwise test for matching position and size of viewer frame
3425          */
3426         else if (frame.getX() == svattrib.x && frame.getY() == svattrib.y
3427                 && frame.getHeight() == svattrib.height
3428                 && frame.getWidth() == svattrib.width)
3429         {
3430           comp = (AppJmol) frame;
3431           // todo: break?
3432         }
3433       }
3434     }
3435     return comp;
3436   }
3437
3438   /**
3439    * Link an AlignmentPanel to an existing structure viewer.
3440    * 
3441    * @param ap
3442    * @param viewer
3443    * @param oldFiles
3444    * @param useinViewerSuperpos
3445    * @param usetoColourbyseq
3446    * @param viewerColouring
3447    */
3448   protected void linkStructureViewer(AlignmentPanel ap,
3449           StructureViewerBase viewer, ViewerData svattrib)
3450   {
3451     // NOTE: if the jalview project is part of a shared session then
3452     // view synchronization should/could be done here.
3453
3454     final boolean useinViewerSuperpos = svattrib.alignWithPanel;
3455     final boolean usetoColourbyseq = svattrib.colourWithAlignPanel;
3456     final boolean viewerColouring = svattrib.colourByViewer;
3457     Map<File, Object[]> oldFiles = svattrib.fileData;
3458
3459     /*
3460      * Add mapping for sequences in this view to an already open viewer
3461      */
3462     final AAStructureBindingModel binding = viewer.getBinding();
3463     for (File id : oldFiles.keySet())
3464     {
3465       // add this and any other pdb files that should be present in the
3466       // viewer
3467       Object[] filedat = oldFiles.get(id);
3468       String pdbFile = (String) filedat[0];
3469       SequenceI[] seq = ((Vector<SequenceI>) filedat[2])
3470               .toArray(new SequenceI[0]);
3471       binding
3472               .getSsm()
3473               .setMapping(seq, null, pdbFile,
3474               jalview.io.AppletFormatAdapter.FILE);
3475       binding.addSequenceForStructFile(pdbFile, seq);
3476     }
3477     // and add the AlignmentPanel's reference to the view panel
3478     viewer.addAlignmentPanel(ap);
3479     if (useinViewerSuperpos)
3480     {
3481       viewer.useAlignmentPanelForSuperposition(ap);
3482     }
3483     else
3484     {
3485       viewer.excludeAlignmentPanelForSuperposition(ap);
3486     }
3487     if (usetoColourbyseq)
3488     {
3489       viewer.useAlignmentPanelForColourbyseq(ap, !viewerColouring);
3490     }
3491     else
3492     {
3493       viewer.excludeAlignmentPanelForColourbyseq(ap);
3494     }
3495   }
3496
3497   /**
3498    * Get all frames within the Desktop.
3499    * 
3500    * @return
3501    */
3502   protected JInternalFrame[] getAllFrames()
3503   {
3504     JInternalFrame[] frames = null;
3505     // TODO is this necessary - is it safe - risk of hanging?
3506     do
3507     {
3508       try
3509       {
3510         frames = Desktop.desktop.getAllFrames();
3511       } catch (ArrayIndexOutOfBoundsException e)
3512       {
3513         // occasional No such child exceptions are thrown here...
3514         try
3515         {
3516           Thread.sleep(10);
3517         } catch (InterruptedException f)
3518         {
3519         }
3520       }
3521     } while (frames == null);
3522     return frames;
3523   }
3524
3525   /**
3526    * 
3527    * @param supported
3528    *          - minimum version we are comparing against
3529    * @param version
3530    *          - version of data being processsed.
3531    * @return true if version is development/null or evaluates to the same or
3532    *         later X.Y.Z (where X,Y,Z are like [0-9]+b?[0-9]*)
3533    */
3534   private boolean isVersionStringLaterThan(String supported, String version)
3535   {
3536     if (version == null || version.equalsIgnoreCase("DEVELOPMENT BUILD")
3537             || version.equalsIgnoreCase("Test")
3538             || version.equalsIgnoreCase("AUTOMATED BUILD"))
3539     {
3540       System.err.println("Assuming project file with "
3541               + (version == null ? "null" : version)
3542               + " is compatible with Jalview version " + supported);
3543       return true;
3544     }
3545     else
3546     {
3547       StringTokenizer currentV = new StringTokenizer(supported, "."), fileV = new StringTokenizer(
3548               version, ".");
3549       while (currentV.hasMoreTokens() && fileV.hasMoreTokens())
3550       {
3551         // convert b to decimal to catch bugfix releases within a series
3552         String curT = currentV.nextToken().toLowerCase().replace('b', '.');
3553         String fileT = fileV.nextToken().toLowerCase().replace('b', '.');
3554         try
3555         {
3556           if (Float.valueOf(curT) > Float.valueOf(fileT))
3557           {
3558             // current version is newer than the version that wrote the file
3559             return false;
3560           }
3561         } catch (NumberFormatException nfe)
3562         {
3563           System.err
3564                   .println("** WARNING: Version comparison failed for tokens ("
3565                           + curT
3566                           + ") and ("
3567                           + fileT
3568                           + ")\n** Current: '"
3569                           + supported + "' and Version: '" + version + "'");
3570         }
3571       }
3572       if (currentV.hasMoreElements())
3573       {
3574         // fileV has no minor version but identical series to current
3575         return false;
3576       }
3577     }
3578     return true;
3579   }
3580
3581   Vector<JalviewStructureDisplayI> newStructureViewers = null;
3582
3583   protected void addNewStructureViewer(JalviewStructureDisplayI sview)
3584   {
3585     if (newStructureViewers != null)
3586     {
3587       sview.getBinding().setFinishedLoadingFromArchive(false);
3588       newStructureViewers.add(sview);
3589     }
3590   }
3591
3592   protected void setLoadingFinishedForNewStructureViewers()
3593   {
3594     if (newStructureViewers != null)
3595     {
3596       for (JalviewStructureDisplayI sview : newStructureViewers)
3597       {
3598         sview.getBinding().setFinishedLoadingFromArchive(true);
3599       }
3600       newStructureViewers.clear();
3601       newStructureViewers = null;
3602     }
3603   }
3604
3605   AlignFrame loadViewport(String file, JSeq[] JSEQ, Vector hiddenSeqs,
3606           Alignment al, JalviewModelSequence jms, Viewport view,
3607           String uniqueSeqSetId, String viewId,
3608           ArrayList<JvAnnotRow> autoAlan)
3609   {
3610     AlignFrame af = null;
3611     af = new AlignFrame(al, view.getWidth(), view.getHeight(),
3612             uniqueSeqSetId, viewId);
3613
3614     af.setFileName(file, "Jalview");
3615
3616     for (int i = 0; i < JSEQ.length; i++)
3617     {
3618       af.viewport.setSequenceColour(af.viewport.getAlignment()
3619               .getSequenceAt(i), new java.awt.Color(JSEQ[i].getColour()));
3620     }
3621
3622     af.viewport.gatherViewsHere = view.getGatheredViews();
3623
3624     if (view.getSequenceSetId() != null)
3625     {
3626       jalview.gui.AlignViewport av = (jalview.gui.AlignViewport) viewportsAdded
3627               .get(uniqueSeqSetId);
3628
3629       af.viewport.setSequenceSetId(uniqueSeqSetId);
3630       if (av != null)
3631       {
3632         // propagate shared settings to this new view
3633         af.viewport.historyList = av.historyList;
3634         af.viewport.redoList = av.redoList;
3635       }
3636       else
3637       {
3638         viewportsAdded.put(uniqueSeqSetId, af.viewport);
3639       }
3640       // TODO: check if this method can be called repeatedly without
3641       // side-effects if alignpanel already registered.
3642       PaintRefresher.Register(af.alignPanel, uniqueSeqSetId);
3643     }
3644     // apply Hidden regions to view.
3645     if (hiddenSeqs != null)
3646     {
3647       for (int s = 0; s < JSEQ.length; s++)
3648       {
3649         jalview.datamodel.SequenceGroup hidden = new jalview.datamodel.SequenceGroup();
3650
3651         for (int r = 0; r < JSEQ[s].getHiddenSequencesCount(); r++)
3652         {
3653           hidden.addSequence(
3654                   al.getSequenceAt(JSEQ[s].getHiddenSequences(r)), false);
3655         }
3656         af.viewport.hideRepSequences(al.getSequenceAt(s), hidden);
3657       }
3658
3659       jalview.datamodel.SequenceI[] hseqs = new jalview.datamodel.SequenceI[hiddenSeqs
3660               .size()];
3661
3662       for (int s = 0; s < hiddenSeqs.size(); s++)
3663       {
3664         hseqs[s] = (jalview.datamodel.SequenceI) hiddenSeqs.elementAt(s);
3665       }
3666
3667       af.viewport.hideSequence(hseqs);
3668
3669     }
3670     // recover view properties and display parameters
3671     if (view.getViewName() != null)
3672     {
3673       af.viewport.viewName = view.getViewName();
3674       af.setInitialTabVisible();
3675     }
3676     af.setBounds(view.getXpos(), view.getYpos(), view.getWidth(),
3677             view.getHeight());
3678
3679     af.viewport.setShowAnnotation(view.getShowAnnotation());
3680     af.viewport.setAbovePIDThreshold(view.getPidSelected());
3681
3682     af.viewport.setColourText(view.getShowColourText());
3683
3684     af.viewport.setConservationSelected(view.getConservationSelected());
3685     af.viewport.setShowJVSuffix(view.getShowFullId());
3686     af.viewport.setRightAlignIds(view.getRightAlignIds());
3687     af.viewport.setFont(new java.awt.Font(view.getFontName(), view
3688             .getFontStyle(), view.getFontSize()));
3689     af.alignPanel.fontChanged();
3690     af.viewport.setRenderGaps(view.getRenderGaps());
3691     af.viewport.setWrapAlignment(view.getWrapAlignment());
3692     af.alignPanel.setWrapAlignment(view.getWrapAlignment());
3693     af.viewport.setShowAnnotation(view.getShowAnnotation());
3694     af.alignPanel.setAnnotationVisible(view.getShowAnnotation());
3695
3696     af.viewport.setShowBoxes(view.getShowBoxes());
3697
3698     af.viewport.setShowText(view.getShowText());
3699
3700     af.viewport.textColour = new java.awt.Color(view.getTextCol1());
3701     af.viewport.textColour2 = new java.awt.Color(view.getTextCol2());
3702     af.viewport.thresholdTextColour = view.getTextColThreshold();
3703     af.viewport.setShowUnconserved(view.hasShowUnconserved() ? view
3704             .isShowUnconserved() : false);
3705     af.viewport.setStartRes(view.getStartRes());
3706     af.viewport.setStartSeq(view.getStartSeq());
3707
3708     ColourSchemeI cs = null;
3709     // apply colourschemes
3710     if (view.getBgColour() != null)
3711     {
3712       if (view.getBgColour().startsWith("ucs"))
3713       {
3714         cs = getUserColourScheme(jms, view.getBgColour());
3715       }
3716       else if (view.getBgColour().startsWith("Annotation"))
3717       {
3718         AnnotationColours viewAnnColour = view.getAnnotationColours();
3719         cs = constructAnnotationColour(viewAnnColour, af, al, jms, true);
3720
3721         // annpos
3722
3723       }
3724       else
3725       {
3726         cs = ColourSchemeProperty.getColour(al, view.getBgColour());
3727       }
3728
3729       if (cs != null)
3730       {
3731         cs.setThreshold(view.getPidThreshold(), true);
3732         cs.setConsensus(af.viewport.getSequenceConsensusHash());
3733       }
3734     }
3735
3736     af.viewport.setGlobalColourScheme(cs);
3737     af.viewport.setColourAppliesToAllGroups(false);
3738
3739     if (view.getConservationSelected() && cs != null)
3740     {
3741       cs.setConservationInc(view.getConsThreshold());
3742     }
3743
3744     af.changeColour(cs);
3745
3746     af.viewport.setColourAppliesToAllGroups(true);
3747
3748     if (view.getShowSequenceFeatures())
3749     {
3750       af.viewport.showSequenceFeatures = true;
3751     }
3752     if (view.hasCentreColumnLabels())
3753     {
3754       af.viewport.setCentreColumnLabels(view.getCentreColumnLabels());
3755     }
3756     if (view.hasIgnoreGapsinConsensus())
3757     {
3758       af.viewport.setIgnoreGapsConsensus(view.getIgnoreGapsinConsensus(),
3759               null);
3760     }
3761     if (view.hasFollowHighlight())
3762     {
3763       af.viewport.followHighlight = view.getFollowHighlight();
3764     }
3765     if (view.hasFollowSelection())
3766     {
3767       af.viewport.followSelection = view.getFollowSelection();
3768     }
3769     if (view.hasShowConsensusHistogram())
3770     {
3771       af.viewport.setShowConsensusHistogram(view
3772               .getShowConsensusHistogram());
3773     }
3774     else
3775     {
3776       af.viewport.setShowConsensusHistogram(true);
3777     }
3778     if (view.hasShowSequenceLogo())
3779     {
3780       af.viewport.setShowSequenceLogo(view.getShowSequenceLogo());
3781     }
3782     else
3783     {
3784       af.viewport.setShowSequenceLogo(false);
3785     }
3786     if (view.hasNormaliseSequenceLogo())
3787     {
3788       af.viewport.setNormaliseSequenceLogo(view.getNormaliseSequenceLogo());
3789     }
3790     if (view.hasShowDbRefTooltip())
3791     {
3792       af.viewport.setShowDbRefs(view.getShowDbRefTooltip());
3793     }
3794     if (view.hasShowNPfeatureTooltip())
3795     {
3796       af.viewport.setShowNpFeats(view.hasShowNPfeatureTooltip());
3797     }
3798     if (view.hasShowGroupConsensus())
3799     {
3800       af.viewport.setShowGroupConsensus(view.getShowGroupConsensus());
3801     }
3802     else
3803     {
3804       af.viewport.setShowGroupConsensus(false);
3805     }
3806     if (view.hasShowGroupConservation())
3807     {
3808       af.viewport.setShowGroupConservation(view.getShowGroupConservation());
3809     }
3810     else
3811     {
3812       af.viewport.setShowGroupConservation(false);
3813     }
3814
3815     // recover featre settings
3816     if (jms.getFeatureSettings() != null)
3817     {
3818       af.viewport.setFeaturesDisplayed(new Hashtable());
3819       String[] renderOrder = new String[jms.getFeatureSettings()
3820               .getSettingCount()];
3821       for (int fs = 0; fs < jms.getFeatureSettings().getSettingCount(); fs++)
3822       {
3823         Setting setting = jms.getFeatureSettings().getSetting(fs);
3824         if (setting.hasMincolour())
3825         {
3826           GraduatedColor gc = setting.hasMin() ? new GraduatedColor(
3827                   new java.awt.Color(setting.getMincolour()),
3828                   new java.awt.Color(setting.getColour()),
3829                   setting.getMin(), setting.getMax()) : new GraduatedColor(
3830                   new java.awt.Color(setting.getMincolour()),
3831                   new java.awt.Color(setting.getColour()), 0, 1);
3832           if (setting.hasThreshold())
3833           {
3834             gc.setThresh(setting.getThreshold());
3835             gc.setThreshType(setting.getThreshstate());
3836           }
3837           gc.setAutoScaled(true); // default
3838           if (setting.hasAutoScale())
3839           {
3840             gc.setAutoScaled(setting.getAutoScale());
3841           }
3842           if (setting.hasColourByLabel())
3843           {
3844             gc.setColourByLabel(setting.getColourByLabel());
3845           }
3846           // and put in the feature colour table.
3847           af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer().setColour(
3848                   setting.getType(), gc);
3849         }
3850         else
3851         {
3852           af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer().setColour(
3853                   setting.getType(),
3854                   new java.awt.Color(setting.getColour()));
3855         }
3856         renderOrder[fs] = setting.getType();
3857         if (setting.hasOrder())
3858         {
3859           af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer().setOrder(
3860                   setting.getType(), setting.getOrder());
3861         }
3862         else
3863         {
3864           af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer().setOrder(
3865                   setting.getType(),
3866                   fs / jms.getFeatureSettings().getSettingCount());
3867         }
3868         if (setting.getDisplay())
3869         {
3870           af.viewport.getFeaturesDisplayed().put(setting.getType(), new Integer(
3871                   setting.getColour()));
3872         }
3873       }
3874       af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer().renderOrder = renderOrder;
3875       Hashtable fgtable;
3876       af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer().featureGroups = fgtable = new Hashtable();
3877       for (int gs = 0; gs < jms.getFeatureSettings().getGroupCount(); gs++)
3878       {
3879         Group grp = jms.getFeatureSettings().getGroup(gs);
3880         fgtable.put(grp.getName(), new Boolean(grp.getDisplay()));
3881       }
3882     }
3883
3884     if (view.getHiddenColumnsCount() > 0)
3885     {
3886       for (int c = 0; c < view.getHiddenColumnsCount(); c++)
3887       {
3888         af.viewport.hideColumns(view.getHiddenColumns(c).getStart(), view
3889                 .getHiddenColumns(c).getEnd() // +1
3890                 );
3891       }
3892     }
3893     if (view.getCalcIdParam() != null)
3894     {
3895       for (CalcIdParam calcIdParam : view.getCalcIdParam())
3896       {
3897         if (calcIdParam != null)
3898         {
3899           if (recoverCalcIdParam(calcIdParam, af.viewport))
3900           {
3901           }
3902           else
3903           {
3904             warn("Couldn't recover parameters for "
3905                     + calcIdParam.getCalcId());
3906           }
3907         }
3908       }
3909     }
3910     af.setMenusFromViewport(af.viewport);
3911     // TODO: we don't need to do this if the viewport is aready visible.
3912     Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(),
3913             view.getHeight());
3914     af.alignPanel.updateAnnotation(false, true); // recompute any autoannotation
3915     reorderAutoannotation(af, al, autoAlan);
3916     af.alignPanel.alignmentChanged();
3917     return af;
3918   }
3919
3920   private ColourSchemeI constructAnnotationColour(
3921           AnnotationColours viewAnnColour, AlignFrame af, Alignment al,
3922           JalviewModelSequence jms, boolean checkGroupAnnColour)
3923   {
3924     boolean propagateAnnColour = false;
3925     ColourSchemeI cs = null;
3926     AlignmentI annAlignment = af != null ? af.viewport.getAlignment() : al;
3927     if (checkGroupAnnColour && al.getGroups() != null
3928             && al.getGroups().size() > 0)
3929     {
3930       // pre 2.8.1 behaviour
3931       // check to see if we should transfer annotation colours
3932       propagateAnnColour = true;
3933       for (jalview.datamodel.SequenceGroup sg : al.getGroups())
3934       {
3935         if (sg.cs instanceof AnnotationColourGradient)
3936         {
3937           propagateAnnColour = false;
3938         }
3939       }
3940     }
3941     // int find annotation
3942     if (annAlignment.getAlignmentAnnotation() != null)
3943     {
3944       for (int i = 0; i < annAlignment.getAlignmentAnnotation().length; i++)
3945       {
3946         if (annAlignment.getAlignmentAnnotation()[i].label
3947                 .equals(viewAnnColour.getAnnotation()))
3948         {
3949           if (annAlignment.getAlignmentAnnotation()[i].getThreshold() == null)
3950           {
3951             annAlignment.getAlignmentAnnotation()[i]
3952                     .setThreshold(new jalview.datamodel.GraphLine(
3953                             viewAnnColour.getThreshold(), "Threshold",
3954                             java.awt.Color.black)
3955
3956                     );
3957           }
3958
3959           if (viewAnnColour.getColourScheme().equals("None"))
3960           {
3961             cs = new AnnotationColourGradient(
3962                     annAlignment.getAlignmentAnnotation()[i],
3963                     new java.awt.Color(viewAnnColour.getMinColour()),
3964                     new java.awt.Color(viewAnnColour.getMaxColour()),
3965                     viewAnnColour.getAboveThreshold());
3966           }
3967           else if (viewAnnColour.getColourScheme().startsWith("ucs"))
3968           {
3969             cs = new AnnotationColourGradient(
3970                     annAlignment.getAlignmentAnnotation()[i],
3971                     getUserColourScheme(jms,
3972                             viewAnnColour.getColourScheme()),
3973                     viewAnnColour.getAboveThreshold());
3974           }
3975           else
3976           {
3977             cs = new AnnotationColourGradient(
3978                     annAlignment.getAlignmentAnnotation()[i],
3979                     ColourSchemeProperty.getColour(al,
3980                             viewAnnColour.getColourScheme()),
3981                     viewAnnColour.getAboveThreshold());
3982           }
3983           if (viewAnnColour.hasPerSequence())
3984           {
3985             ((AnnotationColourGradient) cs).setSeqAssociated(viewAnnColour
3986                     .isPerSequence());
3987           }
3988           if (viewAnnColour.hasPredefinedColours())
3989           {
3990             ((AnnotationColourGradient) cs)
3991                     .setPredefinedColours(viewAnnColour
3992                             .isPredefinedColours());
3993           }
3994           if (propagateAnnColour && al.getGroups() != null)
3995           {
3996             // Also use these settings for all the groups
3997             for (int g = 0; g < al.getGroups().size(); g++)
3998             {
3999               jalview.datamodel.SequenceGroup sg = al.getGroups().get(g);
4000
4001               if (sg.cs == null)
4002               {
4003                 continue;
4004               }
4005
4006               /*
4007                * if (viewAnnColour.getColourScheme().equals("None" )) { sg.cs =
4008                * new AnnotationColourGradient(
4009                * annAlignment.getAlignmentAnnotation()[i], new
4010                * java.awt.Color(viewAnnColour. getMinColour()), new
4011                * java.awt.Color(viewAnnColour. getMaxColour()),
4012                * viewAnnColour.getAboveThreshold()); } else
4013                */
4014               {
4015                 sg.cs = new AnnotationColourGradient(
4016                         annAlignment.getAlignmentAnnotation()[i], sg.cs,
4017                         viewAnnColour.getAboveThreshold());
4018                 if (cs instanceof AnnotationColourGradient)
4019                 {
4020                   if (viewAnnColour.hasPerSequence())
4021                   {
4022                     ((AnnotationColourGradient) cs)
4023                             .setSeqAssociated(viewAnnColour.isPerSequence());
4024                   }
4025                   if (viewAnnColour.hasPredefinedColours())
4026                   {
4027                     ((AnnotationColourGradient) cs)
4028                             .setPredefinedColours(viewAnnColour
4029                                     .isPredefinedColours());
4030                   }
4031                 }
4032               }
4033
4034             }
4035           }
4036
4037           break;
4038         }
4039
4040       }
4041     }
4042     return cs;
4043   }
4044
4045   private void reorderAutoannotation(AlignFrame af, Alignment al,
4046           ArrayList<JvAnnotRow> autoAlan)
4047   {
4048     // copy over visualization settings for autocalculated annotation in the
4049     // view
4050     if (al.getAlignmentAnnotation() != null)
4051     {
4052       /**
4053        * Kludge for magic autoannotation names (see JAL-811)
4054        */
4055       String[] magicNames = new String[]
4056       { "Consensus", "Quality", "Conservation" };
4057       JvAnnotRow nullAnnot = new JvAnnotRow(-1, null);
4058       Hashtable<String, JvAnnotRow> visan = new Hashtable<String, JvAnnotRow>();
4059       for (String nm : magicNames)
4060       {
4061         visan.put(nm, nullAnnot);
4062       }
4063       for (JvAnnotRow auan : autoAlan)
4064       {
4065         visan.put(auan.template.label
4066                 + (auan.template.getCalcId() == null ? "" : "\t"
4067                         + auan.template.getCalcId()), auan);
4068       }
4069       int hSize = al.getAlignmentAnnotation().length;
4070       ArrayList<JvAnnotRow> reorder = new ArrayList<JvAnnotRow>();
4071       // work through any autoCalculated annotation already on the view
4072       // removing it if it should be placed in a different location on the
4073       // annotation panel.
4074       List<String> remains = new ArrayList(visan.keySet());
4075       for (int h = 0; h < hSize; h++)
4076       {
4077         jalview.datamodel.AlignmentAnnotation jalan = al
4078                 .getAlignmentAnnotation()[h];
4079         if (jalan.autoCalculated)
4080         {
4081           String k;
4082           JvAnnotRow valan = visan.get(k = jalan.label);
4083           if (jalan.getCalcId() != null)
4084           {
4085             valan = visan.get(k = jalan.label + "\t" + jalan.getCalcId());
4086           }
4087
4088           if (valan != null)
4089           {
4090             // delete the auto calculated row from the alignment
4091             al.deleteAnnotation(jalan, false);
4092             remains.remove(k);
4093             hSize--;
4094             h--;
4095             if (valan != nullAnnot)
4096             {
4097               if (jalan != valan.template)
4098               {
4099                 // newly created autoannotation row instance
4100                 // so keep a reference to the visible annotation row
4101                 // and copy over all relevant attributes
4102                 if (valan.template.graphHeight >= 0)
4103
4104                 {
4105                   jalan.graphHeight = valan.template.graphHeight;
4106                 }
4107                 jalan.visible = valan.template.visible;
4108               }
4109               reorder.add(new JvAnnotRow(valan.order, jalan));
4110             }
4111           }
4112         }
4113       }
4114       // Add any (possibly stale) autocalculated rows that were not appended to
4115       // the view during construction
4116       for (String other : remains)
4117       {
4118         JvAnnotRow othera = visan.get(other);
4119         if (othera != nullAnnot && othera.template.getCalcId() != null
4120                 && othera.template.getCalcId().length() > 0)
4121         {
4122           reorder.add(othera);
4123         }
4124       }
4125       // now put the automatic annotation in its correct place
4126       int s = 0, srt[] = new int[reorder.size()];
4127       JvAnnotRow[] rws = new JvAnnotRow[reorder.size()];
4128       for (JvAnnotRow jvar : reorder)
4129       {
4130         rws[s] = jvar;
4131         srt[s++] = jvar.order;
4132       }
4133       reorder.clear();
4134       jalview.util.QuickSort.sort(srt, rws);
4135       // and re-insert the annotation at its correct position
4136       for (JvAnnotRow jvar : rws)
4137       {
4138         al.addAnnotation(jvar.template, jvar.order);
4139       }
4140       af.alignPanel.adjustAnnotationHeight();
4141     }
4142   }
4143
4144   Hashtable skipList = null;
4145
4146   /**
4147    * TODO remove this method
4148    * 
4149    * @param view
4150    * @return AlignFrame bound to sequenceSetId from view, if one exists. private
4151    *         AlignFrame getSkippedFrame(Viewport view) { if (skipList==null) {
4152    *         throw new Error("Implementation Error. No skipList defined for this
4153    *         Jalview2XML instance."); } return (AlignFrame)
4154    *         skipList.get(view.getSequenceSetId()); }
4155    */
4156
4157   /**
4158    * Check if the Jalview view contained in object should be skipped or not.
4159    * 
4160    * @param object
4161    * @return true if view's sequenceSetId is a key in skipList
4162    */
4163   private boolean skipViewport(JalviewModel object)
4164   {
4165     if (skipList == null)
4166     {
4167       return false;
4168     }
4169     String id;
4170     if (skipList.containsKey(id = object.getJalviewModelSequence()
4171             .getViewport()[0].getSequenceSetId()))
4172     {
4173       if (Cache.log != null && Cache.log.isDebugEnabled())
4174       {
4175         Cache.log.debug("Skipping seuqence set id " + id);
4176       }
4177       return true;
4178     }
4179     return false;
4180   }
4181
4182   public void addToSkipList(AlignFrame af)
4183   {
4184     if (skipList == null)
4185     {
4186       skipList = new Hashtable();
4187     }
4188     skipList.put(af.getViewport().getSequenceSetId(), af);
4189   }
4190
4191   public void clearSkipList()
4192   {
4193     if (skipList != null)
4194     {
4195       skipList.clear();
4196       skipList = null;
4197     }
4198   }
4199
4200   private void recoverDatasetFor(SequenceSet vamsasSet, Alignment al,
4201           boolean ignoreUnrefed)
4202   {
4203     jalview.datamodel.Alignment ds = getDatasetFor(vamsasSet.getDatasetId());
4204     Vector dseqs = null;
4205     if (ds == null)
4206     {
4207       // create a list of new dataset sequences
4208       dseqs = new Vector();
4209     }
4210     for (int i = 0, iSize = vamsasSet.getSequenceCount(); i < iSize; i++)
4211     {
4212       Sequence vamsasSeq = vamsasSet.getSequence(i);
4213       ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs, ignoreUnrefed);
4214     }
4215     // create a new dataset
4216     if (ds == null)
4217     {
4218       SequenceI[] dsseqs = new SequenceI[dseqs.size()];
4219       dseqs.copyInto(dsseqs);
4220       ds = new jalview.datamodel.Alignment(dsseqs);
4221       debug("Created new dataset " + vamsasSet.getDatasetId()
4222               + " for alignment " + System.identityHashCode(al));
4223       addDatasetRef(vamsasSet.getDatasetId(), ds);
4224     }
4225     // set the dataset for the newly imported alignment.
4226     if (al.getDataset() == null && !ignoreUnrefed)
4227     {
4228       al.setDataset(ds);
4229     }
4230   }
4231
4232   /**
4233    * 
4234    * @param vamsasSeq
4235    *          sequence definition to create/merge dataset sequence for
4236    * @param ds
4237    *          dataset alignment
4238    * @param dseqs
4239    *          vector to add new dataset sequence to
4240    */
4241   private void ensureJalviewDatasetSequence(Sequence vamsasSeq,
4242           AlignmentI ds, Vector dseqs, boolean ignoreUnrefed)
4243   {
4244     // JBP TODO: Check this is called for AlCodonFrames to support recovery of
4245     // xRef Codon Maps
4246     jalview.datamodel.Sequence sq = (jalview.datamodel.Sequence) seqRefIds
4247             .get(vamsasSeq.getId());
4248     jalview.datamodel.SequenceI dsq = null;
4249     if (sq != null && sq.getDatasetSequence() != null)
4250     {
4251       dsq = sq.getDatasetSequence();
4252     }
4253     if (sq == null && ignoreUnrefed)
4254     {
4255       return;
4256     }
4257     String sqid = vamsasSeq.getDsseqid();
4258     if (dsq == null)
4259     {
4260       // need to create or add a new dataset sequence reference to this sequence
4261       if (sqid != null)
4262       {
4263         dsq = seqRefIds.get(sqid);
4264       }
4265       // check again
4266       if (dsq == null)
4267       {
4268         // make a new dataset sequence
4269         dsq = sq.createDatasetSequence();
4270         if (sqid == null)
4271         {
4272           // make up a new dataset reference for this sequence
4273           sqid = seqHash(dsq);
4274         }
4275         dsq.setVamsasId(uniqueSetSuffix + sqid);
4276         seqRefIds.put(sqid, dsq);
4277         if (ds == null)
4278         {
4279           if (dseqs != null)
4280           {
4281             dseqs.addElement(dsq);
4282           }
4283         }
4284         else
4285         {
4286           ds.addSequence(dsq);
4287         }
4288       }
4289       else
4290       {
4291         if (sq != dsq)
4292         { // make this dataset sequence sq's dataset sequence
4293           sq.setDatasetSequence(dsq);
4294           // and update the current dataset alignment
4295           if (ds == null)
4296           {
4297             if (dseqs != null)
4298             {
4299               if (!dseqs.contains(dsq))
4300               {
4301                 dseqs.add(dsq);
4302               }
4303             }
4304             else
4305             {
4306               if (ds.findIndex(dsq) < 0)
4307               {
4308                 ds.addSequence(dsq);
4309               }
4310             }
4311           }
4312         }
4313       }
4314     }
4315     // TODO: refactor this as a merge dataset sequence function
4316     // now check that sq (the dataset sequence) sequence really is the union of
4317     // all references to it
4318     // boolean pre = sq.getStart() < dsq.getStart();
4319     // boolean post = sq.getEnd() > dsq.getEnd();
4320     // if (pre || post)
4321     if (sq != dsq)
4322     {
4323       StringBuffer sb = new StringBuffer();
4324       String newres = jalview.analysis.AlignSeq.extractGaps(
4325               jalview.util.Comparison.GapChars, sq.getSequenceAsString());
4326       if (!newres.equalsIgnoreCase(dsq.getSequenceAsString())
4327               && newres.length() > dsq.getLength())
4328       {
4329         // Update with the longer sequence.
4330         synchronized (dsq)
4331         {
4332           /*
4333            * if (pre) { sb.insert(0, newres .substring(0, dsq.getStart() -
4334            * sq.getStart())); dsq.setStart(sq.getStart()); } if (post) {
4335            * sb.append(newres.substring(newres.length() - sq.getEnd() -
4336            * dsq.getEnd())); dsq.setEnd(sq.getEnd()); }
4337            */
4338           dsq.setSequence(newres);
4339         }
4340         // TODO: merges will never happen if we 'know' we have the real dataset
4341         // sequence - this should be detected when id==dssid
4342         System.err
4343                 .println("DEBUG Notice:  Merged dataset sequence (if you see this often, post at http://issues.jalview.org/browse/JAL-1474)"); // ("
4344         // + (pre ? "prepended" : "") + " "
4345         // + (post ? "appended" : ""));
4346       }
4347     }
4348   }
4349
4350   java.util.Hashtable datasetIds = null;
4351
4352   java.util.IdentityHashMap dataset2Ids = null;
4353
4354   private Alignment getDatasetFor(String datasetId)
4355   {
4356     if (datasetIds == null)
4357     {
4358       datasetIds = new Hashtable();
4359       return null;
4360     }
4361     if (datasetIds.containsKey(datasetId))
4362     {
4363       return (Alignment) datasetIds.get(datasetId);
4364     }
4365     return null;
4366   }
4367
4368   private void addDatasetRef(String datasetId, Alignment dataset)
4369   {
4370     if (datasetIds == null)
4371     {
4372       datasetIds = new Hashtable();
4373     }
4374     datasetIds.put(datasetId, dataset);
4375   }
4376
4377   /**
4378    * make a new dataset ID for this jalview dataset alignment
4379    * 
4380    * @param dataset
4381    * @return
4382    */
4383   private String getDatasetIdRef(jalview.datamodel.Alignment dataset)
4384   {
4385     if (dataset.getDataset() != null)
4386     {
4387       warn("Serious issue!  Dataset Object passed to getDatasetIdRef is not a Jalview DATASET alignment...");
4388     }
4389     String datasetId = makeHashCode(dataset, null);
4390     if (datasetId == null)
4391     {
4392       // make a new datasetId and record it
4393       if (dataset2Ids == null)
4394       {
4395         dataset2Ids = new IdentityHashMap();
4396       }
4397       else
4398       {
4399         datasetId = (String) dataset2Ids.get(dataset);
4400       }
4401       if (datasetId == null)
4402       {
4403         datasetId = "ds" + dataset2Ids.size() + 1;
4404         dataset2Ids.put(dataset, datasetId);
4405       }
4406     }
4407     return datasetId;
4408   }
4409
4410   private void addDBRefs(SequenceI datasetSequence, Sequence sequence)
4411   {
4412     for (int d = 0; d < sequence.getDBRefCount(); d++)
4413     {
4414       DBRef dr = sequence.getDBRef(d);
4415       jalview.datamodel.DBRefEntry entry = new jalview.datamodel.DBRefEntry(
4416               sequence.getDBRef(d).getSource(), sequence.getDBRef(d)
4417                       .getVersion(), sequence.getDBRef(d).getAccessionId());
4418       if (dr.getMapping() != null)
4419       {
4420         entry.setMap(addMapping(dr.getMapping()));
4421       }
4422       datasetSequence.addDBRef(entry);
4423     }
4424   }
4425
4426   private jalview.datamodel.Mapping addMapping(Mapping m)
4427   {
4428     SequenceI dsto = null;
4429     // Mapping m = dr.getMapping();
4430     int fr[] = new int[m.getMapListFromCount() * 2];
4431     Enumeration f = m.enumerateMapListFrom();
4432     for (int _i = 0; f.hasMoreElements(); _i += 2)
4433     {
4434       MapListFrom mf = (MapListFrom) f.nextElement();
4435       fr[_i] = mf.getStart();
4436       fr[_i + 1] = mf.getEnd();
4437     }
4438     int fto[] = new int[m.getMapListToCount() * 2];
4439     f = m.enumerateMapListTo();
4440     for (int _i = 0; f.hasMoreElements(); _i += 2)
4441     {
4442       MapListTo mf = (MapListTo) f.nextElement();
4443       fto[_i] = mf.getStart();
4444       fto[_i + 1] = mf.getEnd();
4445     }
4446     jalview.datamodel.Mapping jmap = new jalview.datamodel.Mapping(dsto,
4447             fr, fto, (int) m.getMapFromUnit(), (int) m.getMapToUnit());
4448     if (m.getMappingChoice() != null)
4449     {
4450       MappingChoice mc = m.getMappingChoice();
4451       if (mc.getDseqFor() != null)
4452       {
4453         String dsfor = "" + mc.getDseqFor();
4454         if (seqRefIds.containsKey(dsfor))
4455         {
4456           /**
4457            * recover from hash
4458            */
4459           jmap.setTo(seqRefIds.get(dsfor));
4460         }
4461         else
4462         {
4463           frefedSequence.add(new Object[]
4464           { dsfor, jmap });
4465         }
4466       }
4467       else
4468       {
4469         /**
4470          * local sequence definition
4471          */
4472         Sequence ms = mc.getSequence();
4473         jalview.datamodel.Sequence djs = null;
4474         String sqid = ms.getDsseqid();
4475         if (sqid != null && sqid.length() > 0)
4476         {
4477           /*
4478            * recover dataset sequence
4479            */
4480           djs = (jalview.datamodel.Sequence) seqRefIds.get(sqid);
4481         }
4482         else
4483         {
4484           System.err
4485                   .println("Warning - making up dataset sequence id for DbRef sequence map reference");
4486           sqid = ((Object) ms).toString(); // make up a new hascode for
4487           // undefined dataset sequence hash
4488           // (unlikely to happen)
4489         }
4490
4491         if (djs == null)
4492         {
4493           /**
4494            * make a new dataset sequence and add it to refIds hash
4495            */
4496           djs = new jalview.datamodel.Sequence(ms.getName(),
4497                   ms.getSequence());
4498           djs.setStart(jmap.getMap().getToLowest());
4499           djs.setEnd(jmap.getMap().getToHighest());
4500           djs.setVamsasId(uniqueSetSuffix + sqid);
4501           jmap.setTo(djs);
4502           seqRefIds.put(sqid, djs);
4503
4504         }
4505         jalview.bin.Cache.log.debug("about to recurse on addDBRefs.");
4506         addDBRefs(djs, ms);
4507
4508       }
4509     }
4510     return (jmap);
4511
4512   }
4513
4514   public jalview.gui.AlignmentPanel copyAlignPanel(AlignmentPanel ap,
4515           boolean keepSeqRefs)
4516   {
4517     initSeqRefs();
4518     jalview.schemabinding.version2.JalviewModel jm = saveState(ap, null,
4519             null);
4520
4521     if (!keepSeqRefs)
4522     {
4523       clearSeqRefs();
4524       jm.getJalviewModelSequence().getViewport(0).setSequenceSetId(null);
4525     }
4526     else
4527     {
4528       uniqueSetSuffix = "";
4529       jm.getJalviewModelSequence().getViewport(0).setId(null); // we don't
4530       // overwrite the
4531       // view we just
4532       // copied
4533     }
4534     if (this.frefedSequence == null)
4535     {
4536       frefedSequence = new Vector();
4537     }
4538
4539     viewportsAdded = new Hashtable();
4540
4541     AlignFrame af = loadFromObject(jm, null, false, null);
4542     af.alignPanels.clear();
4543     af.closeMenuItem_actionPerformed(true);
4544
4545     /*
4546      * if(ap.av.getAlignment().getAlignmentAnnotation()!=null) { for(int i=0;
4547      * i<ap.av.getAlignment().getAlignmentAnnotation().length; i++) {
4548      * if(!ap.av.getAlignment().getAlignmentAnnotation()[i].autoCalculated) {
4549      * af.alignPanel.av.getAlignment().getAlignmentAnnotation()[i] =
4550      * ap.av.getAlignment().getAlignmentAnnotation()[i]; } } }
4551      */
4552
4553     return af.alignPanel;
4554   }
4555
4556   /**
4557    * flag indicating if hashtables should be cleared on finalization TODO this
4558    * flag may not be necessary
4559    */
4560   private final boolean _cleartables = true;
4561
4562   private Hashtable jvids2vobj;
4563
4564   /*
4565    * (non-Javadoc)
4566    * 
4567    * @see java.lang.Object#finalize()
4568    */
4569   @Override
4570   protected void finalize() throws Throwable
4571   {
4572     // really make sure we have no buried refs left.
4573     if (_cleartables)
4574     {
4575       clearSeqRefs();
4576     }
4577     this.seqRefIds = null;
4578     this.seqsToIds = null;
4579     super.finalize();
4580   }
4581
4582   private void warn(String msg)
4583   {
4584     warn(msg, null);
4585   }
4586
4587   private void warn(String msg, Exception e)
4588   {
4589     if (Cache.log != null)
4590     {
4591       if (e != null)
4592       {
4593         Cache.log.warn(msg, e);
4594       }
4595       else
4596       {
4597         Cache.log.warn(msg);
4598       }
4599     }
4600     else
4601     {
4602       System.err.println("Warning: " + msg);
4603       if (e != null)
4604       {
4605         e.printStackTrace();
4606       }
4607     }
4608   }
4609
4610   private void debug(String string)
4611   {
4612     debug(string, null);
4613   }
4614
4615   private void debug(String msg, Exception e)
4616   {
4617     if (Cache.log != null)
4618     {
4619       if (e != null)
4620       {
4621         Cache.log.debug(msg, e);
4622       }
4623       else
4624       {
4625         Cache.log.debug(msg);
4626       }
4627     }
4628     else
4629     {
4630       System.err.println("Warning: " + msg);
4631       if (e != null)
4632       {
4633         e.printStackTrace();
4634       }
4635     }
4636   }
4637
4638   /**
4639    * set the object to ID mapping tables used to write/recover objects and XML
4640    * ID strings for the jalview project. If external tables are provided then
4641    * finalize and clearSeqRefs will not clear the tables when the Jalview2XML
4642    * object goes out of scope. - also populates the datasetIds hashtable with
4643    * alignment objects containing dataset sequences
4644    * 
4645    * @param vobj2jv
4646    *          Map from ID strings to jalview datamodel
4647    * @param jv2vobj
4648    *          Map from jalview datamodel to ID strings
4649    * 
4650    * 
4651    */
4652   public void setObjectMappingTables(Hashtable vobj2jv,
4653           IdentityHashMap jv2vobj)
4654   {
4655     this.jv2vobj = jv2vobj;
4656     this.vobj2jv = vobj2jv;
4657     Iterator ds = jv2vobj.keySet().iterator();
4658     String id;
4659     while (ds.hasNext())
4660     {
4661       Object jvobj = ds.next();
4662       id = jv2vobj.get(jvobj).toString();
4663       if (jvobj instanceof jalview.datamodel.Alignment)
4664       {
4665         if (((jalview.datamodel.Alignment) jvobj).getDataset() == null)
4666         {
4667           addDatasetRef(id, (jalview.datamodel.Alignment) jvobj);
4668         }
4669       }
4670       else if (jvobj instanceof jalview.datamodel.Sequence)
4671       {
4672         // register sequence object so the XML parser can recover it.
4673         if (seqRefIds == null)
4674         {
4675           seqRefIds = new HashMap<String, SequenceI>();
4676         }
4677         if (seqsToIds == null)
4678         {
4679           seqsToIds = new IdentityHashMap<SequenceI, String>();
4680         }
4681         seqRefIds.put(jv2vobj.get(jvobj).toString(), (SequenceI) jvobj);
4682         seqsToIds.put((SequenceI) jvobj, id);
4683       }
4684       else if (jvobj instanceof jalview.datamodel.AlignmentAnnotation)
4685       {
4686         if (annotationIds == null)
4687         {
4688           annotationIds = new Hashtable();
4689         }
4690         String anid;
4691         annotationIds.put(anid = jv2vobj.get(jvobj).toString(), jvobj);
4692         jalview.datamodel.AlignmentAnnotation jvann = (jalview.datamodel.AlignmentAnnotation) jvobj;
4693         if (jvann.annotationId == null)
4694         {
4695           jvann.annotationId = anid;
4696         }
4697         if (!jvann.annotationId.equals(anid))
4698         {
4699           // TODO verify that this is the correct behaviour
4700           this.warn("Overriding Annotation ID for " + anid
4701                   + " from different id : " + jvann.annotationId);
4702           jvann.annotationId = anid;
4703         }
4704       }
4705       else if (jvobj instanceof String)
4706       {
4707         if (jvids2vobj == null)
4708         {
4709           jvids2vobj = new Hashtable();
4710           jvids2vobj.put(jvobj, jv2vobj.get(jvobj).toString());
4711         }
4712       }
4713       else
4714       {
4715         Cache.log.debug("Ignoring " + jvobj.getClass() + " (ID = " + id);
4716       }
4717     }
4718   }
4719
4720   /**
4721    * set the uniqueSetSuffix used to prefix/suffix object IDs for jalview
4722    * objects created from the project archive. If string is null (default for
4723    * construction) then suffix will be set automatically.
4724    * 
4725    * @param string
4726    */
4727   public void setUniqueSetSuffix(String string)
4728   {
4729     uniqueSetSuffix = string;
4730
4731   }
4732
4733   /**
4734    * uses skipList2 as the skipList for skipping views on sequence sets
4735    * associated with keys in the skipList
4736    * 
4737    * @param skipList2
4738    */
4739   public void setSkipList(Hashtable skipList2)
4740   {
4741     skipList = skipList2;
4742   }
4743
4744 }