JAL-1588 part work save/restore of Chimera session
[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 svattrib = viewerData.getValue();
3235     ChimeraViewFrame cvf = new ChimeraViewFrame(svattrib, af);
3236   }
3237
3238   /**
3239    * Create a new Jmol window. First parse the Jmol state to translate filenames
3240    * loaded into the view, and record the order in which files are shown in the
3241    * Jmol view, so we can add the sequence mappings in same order.
3242    * 
3243    * @param viewerData
3244    * @param af
3245    */
3246   protected void createJmolViewer(
3247           final Entry<String, ViewerData> viewerData, AlignFrame af)
3248   {
3249     final ViewerData svattrib = viewerData.getValue();
3250     String state = svattrib.stateData;
3251     List<String> pdbfilenames = new ArrayList<String>();
3252     List<SequenceI[]> seqmaps = new ArrayList<SequenceI[]>();
3253     List<String> pdbids = new ArrayList<String>();
3254     StringBuilder newFileLoc = new StringBuilder(64);
3255     int cp = 0, ncp, ecp;
3256     Map<File, Object[]> oldFiles = svattrib.fileData;
3257     while ((ncp = state.indexOf("load ", cp)) > -1)
3258     {
3259       do
3260       {
3261         // look for next filename in load statement
3262         newFileLoc.append(state.substring(cp,
3263                 ncp = (state.indexOf("\"", ncp + 1) + 1)));
3264         String oldfilenam = state.substring(ncp,
3265                 ecp = state.indexOf("\"", ncp));
3266         // recover the new mapping data for this old filename
3267         // have to normalize filename - since Jmol and jalview do
3268         // filename
3269         // translation differently.
3270         Object[] filedat = oldFiles.get(new File(oldfilenam));
3271         newFileLoc.append(Platform.escapeString((String) filedat[0]));
3272         pdbfilenames.add((String) filedat[0]);
3273         pdbids.add((String) filedat[1]);
3274         seqmaps.add(((Vector<SequenceI>) filedat[2])
3275                 .toArray(new SequenceI[0]));
3276         newFileLoc.append("\"");
3277         cp = ecp + 1; // advance beyond last \" and set cursor so we can
3278                       // look for next file statement.
3279       } while ((ncp = state.indexOf("/*file*/", cp)) > -1);
3280     }
3281     if (cp > 0)
3282     {
3283       // just append rest of state
3284       newFileLoc.append(state.substring(cp));
3285     }
3286     else
3287     {
3288       System.err.print("Ignoring incomplete Jmol state for PDB ids: ");
3289       newFileLoc = new StringBuilder(state);
3290       newFileLoc.append("; load append ");
3291       for (File id : oldFiles.keySet())
3292       {
3293         // add this and any other pdb files that should be present in
3294         // the viewer
3295         Object[] filedat = oldFiles.get(id);
3296         String nfilename;
3297         newFileLoc.append(((String) filedat[0]));
3298         pdbfilenames.add((String) filedat[0]);
3299         pdbids.add((String) filedat[1]);
3300         seqmaps.add(((Vector<SequenceI>) filedat[2])
3301                 .toArray(new SequenceI[0]));
3302         newFileLoc.append(" \"");
3303         newFileLoc.append((String) filedat[0]);
3304         newFileLoc.append("\"");
3305
3306       }
3307       newFileLoc.append(";");
3308     }
3309
3310     if (newFileLoc.length() > 0)
3311     {
3312       int histbug = newFileLoc.indexOf("history = ");
3313       histbug += 10;
3314       int diff = histbug == -1 ? -1 : newFileLoc.indexOf(";", histbug);
3315       String val = (diff == -1) ? null : newFileLoc
3316               .substring(histbug, diff);
3317       if (val != null && val.length() >= 4)
3318       {
3319         if (val.contains("e"))
3320         {
3321           if (val.trim().equals("true"))
3322           {
3323             val = "1";
3324           }
3325           else
3326           {
3327             val = "0";
3328           }
3329           newFileLoc.replace(histbug, diff, val);
3330         }
3331       }
3332
3333       final String[] pdbf = pdbfilenames.toArray(new String[pdbfilenames
3334               .size()]);
3335       final String[] id = pdbids.toArray(new String[pdbids.size()]);
3336       final SequenceI[][] sq = seqmaps
3337               .toArray(new SequenceI[seqmaps.size()][]);
3338       final String fileloc = newFileLoc.toString();
3339       final String sviewid = viewerData.getKey();
3340       final AlignFrame alf = af;
3341       final java.awt.Rectangle rect = new java.awt.Rectangle(svattrib.x,
3342               svattrib.y, svattrib.width, svattrib.height);
3343       try
3344       {
3345         javax.swing.SwingUtilities.invokeAndWait(new Runnable()
3346         {
3347           @Override
3348           public void run()
3349           {
3350             JalviewStructureDisplayI sview = null;
3351             try
3352             {
3353               // JAL-1333 note - we probably can't migrate Jmol views to UCSF
3354               // Chimera!
3355               sview = new StructureViewer(alf.alignPanel
3356                       .getStructureSelectionManager()).createView(
3357                       StructureViewer.ViewerType.JMOL, pdbf, id, sq,
3358                       alf.alignPanel, svattrib, fileloc, rect, sviewid);
3359               addNewStructureViewer(sview);
3360             } catch (OutOfMemoryError ex)
3361             {
3362               new OOMWarning("restoring structure view for PDB id " + id,
3363                       (OutOfMemoryError) ex.getCause());
3364               if (sview != null && sview.isVisible())
3365               {
3366                 sview.closeViewer();
3367                 sview.setVisible(false);
3368                 sview.dispose();
3369               }
3370             }
3371           }
3372         });
3373       } catch (InvocationTargetException ex)
3374       {
3375         warn("Unexpected error when opening Jmol view.", ex);
3376
3377       } catch (InterruptedException e)
3378       {
3379         // e.printStackTrace();
3380       }
3381     }
3382   }
3383
3384   /**
3385    * Returns any open frame that matches given structure viewer data. The match
3386    * is based on the unique viewId, or (for older project versions) the frame's
3387    * geometry.
3388    * 
3389    * @param viewerData
3390    * @return
3391    */
3392   protected StructureViewerBase findMatchingViewer(
3393           Entry<String, ViewerData> viewerData)
3394   {
3395     final String sviewid = viewerData.getKey();
3396     final ViewerData svattrib = viewerData.getValue();
3397     StructureViewerBase comp = null;
3398     JInternalFrame[] frames = getAllFrames();
3399     for (JInternalFrame frame : frames)
3400     {
3401       if (frame instanceof StructureViewerBase)
3402       {
3403         /*
3404          * Post jalview 2.4 schema includes structure view id
3405          */
3406         if (sviewid != null
3407                 && ((StructureViewerBase) frame).getViewId().equals(
3408                         sviewid))
3409         {
3410           comp = (AppJmol) frame;
3411           // todo: break?
3412         }
3413         /*
3414          * Otherwise test for matching position and size of viewer frame
3415          */
3416         else if (frame.getX() == svattrib.x && frame.getY() == svattrib.y
3417                 && frame.getHeight() == svattrib.height
3418                 && frame.getWidth() == svattrib.width)
3419         {
3420           comp = (AppJmol) frame;
3421           // todo: break?
3422         }
3423       }
3424     }
3425     return comp;
3426   }
3427
3428   /**
3429    * Link an AlignmentPanel to an existing structure viewer.
3430    * 
3431    * @param ap
3432    * @param viewer
3433    * @param oldFiles
3434    * @param useinViewerSuperpos
3435    * @param usetoColourbyseq
3436    * @param viewerColouring
3437    */
3438   protected void linkStructureViewer(AlignmentPanel ap,
3439           StructureViewerBase viewer, ViewerData svattrib)
3440   {
3441     // NOTE: if the jalview project is part of a shared session then
3442     // view synchronization should/could be done here.
3443
3444     final boolean useinViewerSuperpos = svattrib.alignWithPanel;
3445     final boolean usetoColourbyseq = svattrib.colourWithAlignPanel;
3446     final boolean viewerColouring = svattrib.colourByViewer;
3447     Map<File, Object[]> oldFiles = svattrib.fileData;
3448
3449     /*
3450      * Add mapping for sequences in this view to an already open viewer
3451      */
3452     final AAStructureBindingModel binding = viewer.getBinding();
3453     for (File id : oldFiles.keySet())
3454     {
3455       // add this and any other pdb files that should be present in the
3456       // viewer
3457       Object[] filedat = oldFiles.get(id);
3458       String pdbFile = (String) filedat[0];
3459       SequenceI[] seq = ((Vector<SequenceI>) filedat[2])
3460               .toArray(new SequenceI[0]);
3461       binding
3462               .getSsm()
3463               .setMapping(seq, null, pdbFile,
3464               jalview.io.AppletFormatAdapter.FILE);
3465       binding.addSequenceForStructFile(pdbFile, seq);
3466     }
3467     // and add the AlignmentPanel's reference to the view panel
3468     viewer.addAlignmentPanel(ap);
3469     if (useinViewerSuperpos)
3470     {
3471       viewer.useAlignmentPanelForSuperposition(ap);
3472     }
3473     else
3474     {
3475       viewer.excludeAlignmentPanelForSuperposition(ap);
3476     }
3477     if (usetoColourbyseq)
3478     {
3479       viewer.useAlignmentPanelForColourbyseq(ap, !viewerColouring);
3480     }
3481     else
3482     {
3483       viewer.excludeAlignmentPanelForColourbyseq(ap);
3484     }
3485   }
3486
3487   /**
3488    * Get all frames within the Desktop.
3489    * 
3490    * @return
3491    */
3492   protected JInternalFrame[] getAllFrames()
3493   {
3494     JInternalFrame[] frames = null;
3495     // TODO is this necessary - is it safe - risk of hanging?
3496     do
3497     {
3498       try
3499       {
3500         frames = Desktop.desktop.getAllFrames();
3501       } catch (ArrayIndexOutOfBoundsException e)
3502       {
3503         // occasional No such child exceptions are thrown here...
3504         try
3505         {
3506           Thread.sleep(10);
3507         } catch (InterruptedException f)
3508         {
3509         }
3510       }
3511     } while (frames == null);
3512     return frames;
3513   }
3514
3515   /**
3516    * 
3517    * @param supported
3518    *          - minimum version we are comparing against
3519    * @param version
3520    *          - version of data being processsed.
3521    * @return true if version is development/null or evaluates to the same or
3522    *         later X.Y.Z (where X,Y,Z are like [0-9]+b?[0-9]*)
3523    */
3524   private boolean isVersionStringLaterThan(String supported, String version)
3525   {
3526     if (version == null || version.equalsIgnoreCase("DEVELOPMENT BUILD")
3527             || version.equalsIgnoreCase("Test")
3528             || version.equalsIgnoreCase("AUTOMATED BUILD"))
3529     {
3530       System.err.println("Assuming project file with "
3531               + (version == null ? "null" : version)
3532               + " is compatible with Jalview version " + supported);
3533       return true;
3534     }
3535     else
3536     {
3537       StringTokenizer currentV = new StringTokenizer(supported, "."), fileV = new StringTokenizer(
3538               version, ".");
3539       while (currentV.hasMoreTokens() && fileV.hasMoreTokens())
3540       {
3541         // convert b to decimal to catch bugfix releases within a series
3542         String curT = currentV.nextToken().toLowerCase().replace('b', '.');
3543         String fileT = fileV.nextToken().toLowerCase().replace('b', '.');
3544         try
3545         {
3546           if (Float.valueOf(curT) > Float.valueOf(fileT))
3547           {
3548             // current version is newer than the version that wrote the file
3549             return false;
3550           }
3551         } catch (NumberFormatException nfe)
3552         {
3553           System.err
3554                   .println("** WARNING: Version comparison failed for tokens ("
3555                           + curT
3556                           + ") and ("
3557                           + fileT
3558                           + ")\n** Current: '"
3559                           + supported + "' and Version: '" + version + "'");
3560         }
3561       }
3562       if (currentV.hasMoreElements())
3563       {
3564         // fileV has no minor version but identical series to current
3565         return false;
3566       }
3567     }
3568     return true;
3569   }
3570
3571   Vector<JalviewStructureDisplayI> newStructureViewers = null;
3572
3573   protected void addNewStructureViewer(JalviewStructureDisplayI sview)
3574   {
3575     if (newStructureViewers != null)
3576     {
3577       sview.getBinding().setFinishedLoadingFromArchive(false);
3578       newStructureViewers.add(sview);
3579     }
3580   }
3581
3582   protected void setLoadingFinishedForNewStructureViewers()
3583   {
3584     if (newStructureViewers != null)
3585     {
3586       for (JalviewStructureDisplayI sview : newStructureViewers)
3587       {
3588         sview.getBinding().setFinishedLoadingFromArchive(true);
3589       }
3590       newStructureViewers.clear();
3591       newStructureViewers = null;
3592     }
3593   }
3594
3595   AlignFrame loadViewport(String file, JSeq[] JSEQ, Vector hiddenSeqs,
3596           Alignment al, JalviewModelSequence jms, Viewport view,
3597           String uniqueSeqSetId, String viewId,
3598           ArrayList<JvAnnotRow> autoAlan)
3599   {
3600     AlignFrame af = null;
3601     af = new AlignFrame(al, view.getWidth(), view.getHeight(),
3602             uniqueSeqSetId, viewId);
3603
3604     af.setFileName(file, "Jalview");
3605
3606     for (int i = 0; i < JSEQ.length; i++)
3607     {
3608       af.viewport.setSequenceColour(af.viewport.getAlignment()
3609               .getSequenceAt(i), new java.awt.Color(JSEQ[i].getColour()));
3610     }
3611
3612     af.viewport.gatherViewsHere = view.getGatheredViews();
3613
3614     if (view.getSequenceSetId() != null)
3615     {
3616       jalview.gui.AlignViewport av = (jalview.gui.AlignViewport) viewportsAdded
3617               .get(uniqueSeqSetId);
3618
3619       af.viewport.setSequenceSetId(uniqueSeqSetId);
3620       if (av != null)
3621       {
3622         // propagate shared settings to this new view
3623         af.viewport.historyList = av.historyList;
3624         af.viewport.redoList = av.redoList;
3625       }
3626       else
3627       {
3628         viewportsAdded.put(uniqueSeqSetId, af.viewport);
3629       }
3630       // TODO: check if this method can be called repeatedly without
3631       // side-effects if alignpanel already registered.
3632       PaintRefresher.Register(af.alignPanel, uniqueSeqSetId);
3633     }
3634     // apply Hidden regions to view.
3635     if (hiddenSeqs != null)
3636     {
3637       for (int s = 0; s < JSEQ.length; s++)
3638       {
3639         jalview.datamodel.SequenceGroup hidden = new jalview.datamodel.SequenceGroup();
3640
3641         for (int r = 0; r < JSEQ[s].getHiddenSequencesCount(); r++)
3642         {
3643           hidden.addSequence(
3644                   al.getSequenceAt(JSEQ[s].getHiddenSequences(r)), false);
3645         }
3646         af.viewport.hideRepSequences(al.getSequenceAt(s), hidden);
3647       }
3648
3649       jalview.datamodel.SequenceI[] hseqs = new jalview.datamodel.SequenceI[hiddenSeqs
3650               .size()];
3651
3652       for (int s = 0; s < hiddenSeqs.size(); s++)
3653       {
3654         hseqs[s] = (jalview.datamodel.SequenceI) hiddenSeqs.elementAt(s);
3655       }
3656
3657       af.viewport.hideSequence(hseqs);
3658
3659     }
3660     // recover view properties and display parameters
3661     if (view.getViewName() != null)
3662     {
3663       af.viewport.viewName = view.getViewName();
3664       af.setInitialTabVisible();
3665     }
3666     af.setBounds(view.getXpos(), view.getYpos(), view.getWidth(),
3667             view.getHeight());
3668
3669     af.viewport.setShowAnnotation(view.getShowAnnotation());
3670     af.viewport.setAbovePIDThreshold(view.getPidSelected());
3671
3672     af.viewport.setColourText(view.getShowColourText());
3673
3674     af.viewport.setConservationSelected(view.getConservationSelected());
3675     af.viewport.setShowJVSuffix(view.getShowFullId());
3676     af.viewport.setRightAlignIds(view.getRightAlignIds());
3677     af.viewport.setFont(new java.awt.Font(view.getFontName(), view
3678             .getFontStyle(), view.getFontSize()));
3679     af.alignPanel.fontChanged();
3680     af.viewport.setRenderGaps(view.getRenderGaps());
3681     af.viewport.setWrapAlignment(view.getWrapAlignment());
3682     af.alignPanel.setWrapAlignment(view.getWrapAlignment());
3683     af.viewport.setShowAnnotation(view.getShowAnnotation());
3684     af.alignPanel.setAnnotationVisible(view.getShowAnnotation());
3685
3686     af.viewport.setShowBoxes(view.getShowBoxes());
3687
3688     af.viewport.setShowText(view.getShowText());
3689
3690     af.viewport.textColour = new java.awt.Color(view.getTextCol1());
3691     af.viewport.textColour2 = new java.awt.Color(view.getTextCol2());
3692     af.viewport.thresholdTextColour = view.getTextColThreshold();
3693     af.viewport.setShowUnconserved(view.hasShowUnconserved() ? view
3694             .isShowUnconserved() : false);
3695     af.viewport.setStartRes(view.getStartRes());
3696     af.viewport.setStartSeq(view.getStartSeq());
3697
3698     ColourSchemeI cs = null;
3699     // apply colourschemes
3700     if (view.getBgColour() != null)
3701     {
3702       if (view.getBgColour().startsWith("ucs"))
3703       {
3704         cs = getUserColourScheme(jms, view.getBgColour());
3705       }
3706       else if (view.getBgColour().startsWith("Annotation"))
3707       {
3708         AnnotationColours viewAnnColour = view.getAnnotationColours();
3709         cs = constructAnnotationColour(viewAnnColour, af, al, jms, true);
3710
3711         // annpos
3712
3713       }
3714       else
3715       {
3716         cs = ColourSchemeProperty.getColour(al, view.getBgColour());
3717       }
3718
3719       if (cs != null)
3720       {
3721         cs.setThreshold(view.getPidThreshold(), true);
3722         cs.setConsensus(af.viewport.getSequenceConsensusHash());
3723       }
3724     }
3725
3726     af.viewport.setGlobalColourScheme(cs);
3727     af.viewport.setColourAppliesToAllGroups(false);
3728
3729     if (view.getConservationSelected() && cs != null)
3730     {
3731       cs.setConservationInc(view.getConsThreshold());
3732     }
3733
3734     af.changeColour(cs);
3735
3736     af.viewport.setColourAppliesToAllGroups(true);
3737
3738     if (view.getShowSequenceFeatures())
3739     {
3740       af.viewport.showSequenceFeatures = true;
3741     }
3742     if (view.hasCentreColumnLabels())
3743     {
3744       af.viewport.setCentreColumnLabels(view.getCentreColumnLabels());
3745     }
3746     if (view.hasIgnoreGapsinConsensus())
3747     {
3748       af.viewport.setIgnoreGapsConsensus(view.getIgnoreGapsinConsensus(),
3749               null);
3750     }
3751     if (view.hasFollowHighlight())
3752     {
3753       af.viewport.followHighlight = view.getFollowHighlight();
3754     }
3755     if (view.hasFollowSelection())
3756     {
3757       af.viewport.followSelection = view.getFollowSelection();
3758     }
3759     if (view.hasShowConsensusHistogram())
3760     {
3761       af.viewport.setShowConsensusHistogram(view
3762               .getShowConsensusHistogram());
3763     }
3764     else
3765     {
3766       af.viewport.setShowConsensusHistogram(true);
3767     }
3768     if (view.hasShowSequenceLogo())
3769     {
3770       af.viewport.setShowSequenceLogo(view.getShowSequenceLogo());
3771     }
3772     else
3773     {
3774       af.viewport.setShowSequenceLogo(false);
3775     }
3776     if (view.hasNormaliseSequenceLogo())
3777     {
3778       af.viewport.setNormaliseSequenceLogo(view.getNormaliseSequenceLogo());
3779     }
3780     if (view.hasShowDbRefTooltip())
3781     {
3782       af.viewport.setShowDbRefs(view.getShowDbRefTooltip());
3783     }
3784     if (view.hasShowNPfeatureTooltip())
3785     {
3786       af.viewport.setShowNpFeats(view.hasShowNPfeatureTooltip());
3787     }
3788     if (view.hasShowGroupConsensus())
3789     {
3790       af.viewport.setShowGroupConsensus(view.getShowGroupConsensus());
3791     }
3792     else
3793     {
3794       af.viewport.setShowGroupConsensus(false);
3795     }
3796     if (view.hasShowGroupConservation())
3797     {
3798       af.viewport.setShowGroupConservation(view.getShowGroupConservation());
3799     }
3800     else
3801     {
3802       af.viewport.setShowGroupConservation(false);
3803     }
3804
3805     // recover featre settings
3806     if (jms.getFeatureSettings() != null)
3807     {
3808       af.viewport.setFeaturesDisplayed(new Hashtable());
3809       String[] renderOrder = new String[jms.getFeatureSettings()
3810               .getSettingCount()];
3811       for (int fs = 0; fs < jms.getFeatureSettings().getSettingCount(); fs++)
3812       {
3813         Setting setting = jms.getFeatureSettings().getSetting(fs);
3814         if (setting.hasMincolour())
3815         {
3816           GraduatedColor gc = setting.hasMin() ? new GraduatedColor(
3817                   new java.awt.Color(setting.getMincolour()),
3818                   new java.awt.Color(setting.getColour()),
3819                   setting.getMin(), setting.getMax()) : new GraduatedColor(
3820                   new java.awt.Color(setting.getMincolour()),
3821                   new java.awt.Color(setting.getColour()), 0, 1);
3822           if (setting.hasThreshold())
3823           {
3824             gc.setThresh(setting.getThreshold());
3825             gc.setThreshType(setting.getThreshstate());
3826           }
3827           gc.setAutoScaled(true); // default
3828           if (setting.hasAutoScale())
3829           {
3830             gc.setAutoScaled(setting.getAutoScale());
3831           }
3832           if (setting.hasColourByLabel())
3833           {
3834             gc.setColourByLabel(setting.getColourByLabel());
3835           }
3836           // and put in the feature colour table.
3837           af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer().setColour(
3838                   setting.getType(), gc);
3839         }
3840         else
3841         {
3842           af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer().setColour(
3843                   setting.getType(),
3844                   new java.awt.Color(setting.getColour()));
3845         }
3846         renderOrder[fs] = setting.getType();
3847         if (setting.hasOrder())
3848         {
3849           af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer().setOrder(
3850                   setting.getType(), setting.getOrder());
3851         }
3852         else
3853         {
3854           af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer().setOrder(
3855                   setting.getType(),
3856                   fs / jms.getFeatureSettings().getSettingCount());
3857         }
3858         if (setting.getDisplay())
3859         {
3860           af.viewport.getFeaturesDisplayed().put(setting.getType(), new Integer(
3861                   setting.getColour()));
3862         }
3863       }
3864       af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer().renderOrder = renderOrder;
3865       Hashtable fgtable;
3866       af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer().featureGroups = fgtable = new Hashtable();
3867       for (int gs = 0; gs < jms.getFeatureSettings().getGroupCount(); gs++)
3868       {
3869         Group grp = jms.getFeatureSettings().getGroup(gs);
3870         fgtable.put(grp.getName(), new Boolean(grp.getDisplay()));
3871       }
3872     }
3873
3874     if (view.getHiddenColumnsCount() > 0)
3875     {
3876       for (int c = 0; c < view.getHiddenColumnsCount(); c++)
3877       {
3878         af.viewport.hideColumns(view.getHiddenColumns(c).getStart(), view
3879                 .getHiddenColumns(c).getEnd() // +1
3880                 );
3881       }
3882     }
3883     if (view.getCalcIdParam() != null)
3884     {
3885       for (CalcIdParam calcIdParam : view.getCalcIdParam())
3886       {
3887         if (calcIdParam != null)
3888         {
3889           if (recoverCalcIdParam(calcIdParam, af.viewport))
3890           {
3891           }
3892           else
3893           {
3894             warn("Couldn't recover parameters for "
3895                     + calcIdParam.getCalcId());
3896           }
3897         }
3898       }
3899     }
3900     af.setMenusFromViewport(af.viewport);
3901     // TODO: we don't need to do this if the viewport is aready visible.
3902     Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(),
3903             view.getHeight());
3904     af.alignPanel.updateAnnotation(false, true); // recompute any autoannotation
3905     reorderAutoannotation(af, al, autoAlan);
3906     af.alignPanel.alignmentChanged();
3907     return af;
3908   }
3909
3910   private ColourSchemeI constructAnnotationColour(
3911           AnnotationColours viewAnnColour, AlignFrame af, Alignment al,
3912           JalviewModelSequence jms, boolean checkGroupAnnColour)
3913   {
3914     boolean propagateAnnColour = false;
3915     ColourSchemeI cs = null;
3916     AlignmentI annAlignment = af != null ? af.viewport.getAlignment() : al;
3917     if (checkGroupAnnColour && al.getGroups() != null
3918             && al.getGroups().size() > 0)
3919     {
3920       // pre 2.8.1 behaviour
3921       // check to see if we should transfer annotation colours
3922       propagateAnnColour = true;
3923       for (jalview.datamodel.SequenceGroup sg : al.getGroups())
3924       {
3925         if (sg.cs instanceof AnnotationColourGradient)
3926         {
3927           propagateAnnColour = false;
3928         }
3929       }
3930     }
3931     // int find annotation
3932     if (annAlignment.getAlignmentAnnotation() != null)
3933     {
3934       for (int i = 0; i < annAlignment.getAlignmentAnnotation().length; i++)
3935       {
3936         if (annAlignment.getAlignmentAnnotation()[i].label
3937                 .equals(viewAnnColour.getAnnotation()))
3938         {
3939           if (annAlignment.getAlignmentAnnotation()[i].getThreshold() == null)
3940           {
3941             annAlignment.getAlignmentAnnotation()[i]
3942                     .setThreshold(new jalview.datamodel.GraphLine(
3943                             viewAnnColour.getThreshold(), "Threshold",
3944                             java.awt.Color.black)
3945
3946                     );
3947           }
3948
3949           if (viewAnnColour.getColourScheme().equals("None"))
3950           {
3951             cs = new AnnotationColourGradient(
3952                     annAlignment.getAlignmentAnnotation()[i],
3953                     new java.awt.Color(viewAnnColour.getMinColour()),
3954                     new java.awt.Color(viewAnnColour.getMaxColour()),
3955                     viewAnnColour.getAboveThreshold());
3956           }
3957           else if (viewAnnColour.getColourScheme().startsWith("ucs"))
3958           {
3959             cs = new AnnotationColourGradient(
3960                     annAlignment.getAlignmentAnnotation()[i],
3961                     getUserColourScheme(jms,
3962                             viewAnnColour.getColourScheme()),
3963                     viewAnnColour.getAboveThreshold());
3964           }
3965           else
3966           {
3967             cs = new AnnotationColourGradient(
3968                     annAlignment.getAlignmentAnnotation()[i],
3969                     ColourSchemeProperty.getColour(al,
3970                             viewAnnColour.getColourScheme()),
3971                     viewAnnColour.getAboveThreshold());
3972           }
3973           if (viewAnnColour.hasPerSequence())
3974           {
3975             ((AnnotationColourGradient) cs).setSeqAssociated(viewAnnColour
3976                     .isPerSequence());
3977           }
3978           if (viewAnnColour.hasPredefinedColours())
3979           {
3980             ((AnnotationColourGradient) cs)
3981                     .setPredefinedColours(viewAnnColour
3982                             .isPredefinedColours());
3983           }
3984           if (propagateAnnColour && al.getGroups() != null)
3985           {
3986             // Also use these settings for all the groups
3987             for (int g = 0; g < al.getGroups().size(); g++)
3988             {
3989               jalview.datamodel.SequenceGroup sg = al.getGroups().get(g);
3990
3991               if (sg.cs == null)
3992               {
3993                 continue;
3994               }
3995
3996               /*
3997                * if (viewAnnColour.getColourScheme().equals("None" )) { sg.cs =
3998                * new AnnotationColourGradient(
3999                * annAlignment.getAlignmentAnnotation()[i], new
4000                * java.awt.Color(viewAnnColour. getMinColour()), new
4001                * java.awt.Color(viewAnnColour. getMaxColour()),
4002                * viewAnnColour.getAboveThreshold()); } else
4003                */
4004               {
4005                 sg.cs = new AnnotationColourGradient(
4006                         annAlignment.getAlignmentAnnotation()[i], sg.cs,
4007                         viewAnnColour.getAboveThreshold());
4008                 if (cs instanceof AnnotationColourGradient)
4009                 {
4010                   if (viewAnnColour.hasPerSequence())
4011                   {
4012                     ((AnnotationColourGradient) cs)
4013                             .setSeqAssociated(viewAnnColour.isPerSequence());
4014                   }
4015                   if (viewAnnColour.hasPredefinedColours())
4016                   {
4017                     ((AnnotationColourGradient) cs)
4018                             .setPredefinedColours(viewAnnColour
4019                                     .isPredefinedColours());
4020                   }
4021                 }
4022               }
4023
4024             }
4025           }
4026
4027           break;
4028         }
4029
4030       }
4031     }
4032     return cs;
4033   }
4034
4035   private void reorderAutoannotation(AlignFrame af, Alignment al,
4036           ArrayList<JvAnnotRow> autoAlan)
4037   {
4038     // copy over visualization settings for autocalculated annotation in the
4039     // view
4040     if (al.getAlignmentAnnotation() != null)
4041     {
4042       /**
4043        * Kludge for magic autoannotation names (see JAL-811)
4044        */
4045       String[] magicNames = new String[]
4046       { "Consensus", "Quality", "Conservation" };
4047       JvAnnotRow nullAnnot = new JvAnnotRow(-1, null);
4048       Hashtable<String, JvAnnotRow> visan = new Hashtable<String, JvAnnotRow>();
4049       for (String nm : magicNames)
4050       {
4051         visan.put(nm, nullAnnot);
4052       }
4053       for (JvAnnotRow auan : autoAlan)
4054       {
4055         visan.put(auan.template.label
4056                 + (auan.template.getCalcId() == null ? "" : "\t"
4057                         + auan.template.getCalcId()), auan);
4058       }
4059       int hSize = al.getAlignmentAnnotation().length;
4060       ArrayList<JvAnnotRow> reorder = new ArrayList<JvAnnotRow>();
4061       // work through any autoCalculated annotation already on the view
4062       // removing it if it should be placed in a different location on the
4063       // annotation panel.
4064       List<String> remains = new ArrayList(visan.keySet());
4065       for (int h = 0; h < hSize; h++)
4066       {
4067         jalview.datamodel.AlignmentAnnotation jalan = al
4068                 .getAlignmentAnnotation()[h];
4069         if (jalan.autoCalculated)
4070         {
4071           String k;
4072           JvAnnotRow valan = visan.get(k = jalan.label);
4073           if (jalan.getCalcId() != null)
4074           {
4075             valan = visan.get(k = jalan.label + "\t" + jalan.getCalcId());
4076           }
4077
4078           if (valan != null)
4079           {
4080             // delete the auto calculated row from the alignment
4081             al.deleteAnnotation(jalan, false);
4082             remains.remove(k);
4083             hSize--;
4084             h--;
4085             if (valan != nullAnnot)
4086             {
4087               if (jalan != valan.template)
4088               {
4089                 // newly created autoannotation row instance
4090                 // so keep a reference to the visible annotation row
4091                 // and copy over all relevant attributes
4092                 if (valan.template.graphHeight >= 0)
4093
4094                 {
4095                   jalan.graphHeight = valan.template.graphHeight;
4096                 }
4097                 jalan.visible = valan.template.visible;
4098               }
4099               reorder.add(new JvAnnotRow(valan.order, jalan));
4100             }
4101           }
4102         }
4103       }
4104       // Add any (possibly stale) autocalculated rows that were not appended to
4105       // the view during construction
4106       for (String other : remains)
4107       {
4108         JvAnnotRow othera = visan.get(other);
4109         if (othera != nullAnnot && othera.template.getCalcId() != null
4110                 && othera.template.getCalcId().length() > 0)
4111         {
4112           reorder.add(othera);
4113         }
4114       }
4115       // now put the automatic annotation in its correct place
4116       int s = 0, srt[] = new int[reorder.size()];
4117       JvAnnotRow[] rws = new JvAnnotRow[reorder.size()];
4118       for (JvAnnotRow jvar : reorder)
4119       {
4120         rws[s] = jvar;
4121         srt[s++] = jvar.order;
4122       }
4123       reorder.clear();
4124       jalview.util.QuickSort.sort(srt, rws);
4125       // and re-insert the annotation at its correct position
4126       for (JvAnnotRow jvar : rws)
4127       {
4128         al.addAnnotation(jvar.template, jvar.order);
4129       }
4130       af.alignPanel.adjustAnnotationHeight();
4131     }
4132   }
4133
4134   Hashtable skipList = null;
4135
4136   /**
4137    * TODO remove this method
4138    * 
4139    * @param view
4140    * @return AlignFrame bound to sequenceSetId from view, if one exists. private
4141    *         AlignFrame getSkippedFrame(Viewport view) { if (skipList==null) {
4142    *         throw new Error("Implementation Error. No skipList defined for this
4143    *         Jalview2XML instance."); } return (AlignFrame)
4144    *         skipList.get(view.getSequenceSetId()); }
4145    */
4146
4147   /**
4148    * Check if the Jalview view contained in object should be skipped or not.
4149    * 
4150    * @param object
4151    * @return true if view's sequenceSetId is a key in skipList
4152    */
4153   private boolean skipViewport(JalviewModel object)
4154   {
4155     if (skipList == null)
4156     {
4157       return false;
4158     }
4159     String id;
4160     if (skipList.containsKey(id = object.getJalviewModelSequence()
4161             .getViewport()[0].getSequenceSetId()))
4162     {
4163       if (Cache.log != null && Cache.log.isDebugEnabled())
4164       {
4165         Cache.log.debug("Skipping seuqence set id " + id);
4166       }
4167       return true;
4168     }
4169     return false;
4170   }
4171
4172   public void addToSkipList(AlignFrame af)
4173   {
4174     if (skipList == null)
4175     {
4176       skipList = new Hashtable();
4177     }
4178     skipList.put(af.getViewport().getSequenceSetId(), af);
4179   }
4180
4181   public void clearSkipList()
4182   {
4183     if (skipList != null)
4184     {
4185       skipList.clear();
4186       skipList = null;
4187     }
4188   }
4189
4190   private void recoverDatasetFor(SequenceSet vamsasSet, Alignment al,
4191           boolean ignoreUnrefed)
4192   {
4193     jalview.datamodel.Alignment ds = getDatasetFor(vamsasSet.getDatasetId());
4194     Vector dseqs = null;
4195     if (ds == null)
4196     {
4197       // create a list of new dataset sequences
4198       dseqs = new Vector();
4199     }
4200     for (int i = 0, iSize = vamsasSet.getSequenceCount(); i < iSize; i++)
4201     {
4202       Sequence vamsasSeq = vamsasSet.getSequence(i);
4203       ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs, ignoreUnrefed);
4204     }
4205     // create a new dataset
4206     if (ds == null)
4207     {
4208       SequenceI[] dsseqs = new SequenceI[dseqs.size()];
4209       dseqs.copyInto(dsseqs);
4210       ds = new jalview.datamodel.Alignment(dsseqs);
4211       debug("Created new dataset " + vamsasSet.getDatasetId()
4212               + " for alignment " + System.identityHashCode(al));
4213       addDatasetRef(vamsasSet.getDatasetId(), ds);
4214     }
4215     // set the dataset for the newly imported alignment.
4216     if (al.getDataset() == null && !ignoreUnrefed)
4217     {
4218       al.setDataset(ds);
4219     }
4220   }
4221
4222   /**
4223    * 
4224    * @param vamsasSeq
4225    *          sequence definition to create/merge dataset sequence for
4226    * @param ds
4227    *          dataset alignment
4228    * @param dseqs
4229    *          vector to add new dataset sequence to
4230    */
4231   private void ensureJalviewDatasetSequence(Sequence vamsasSeq,
4232           AlignmentI ds, Vector dseqs, boolean ignoreUnrefed)
4233   {
4234     // JBP TODO: Check this is called for AlCodonFrames to support recovery of
4235     // xRef Codon Maps
4236     jalview.datamodel.Sequence sq = (jalview.datamodel.Sequence) seqRefIds
4237             .get(vamsasSeq.getId());
4238     jalview.datamodel.SequenceI dsq = null;
4239     if (sq != null && sq.getDatasetSequence() != null)
4240     {
4241       dsq = sq.getDatasetSequence();
4242     }
4243     if (sq == null && ignoreUnrefed)
4244     {
4245       return;
4246     }
4247     String sqid = vamsasSeq.getDsseqid();
4248     if (dsq == null)
4249     {
4250       // need to create or add a new dataset sequence reference to this sequence
4251       if (sqid != null)
4252       {
4253         dsq = seqRefIds.get(sqid);
4254       }
4255       // check again
4256       if (dsq == null)
4257       {
4258         // make a new dataset sequence
4259         dsq = sq.createDatasetSequence();
4260         if (sqid == null)
4261         {
4262           // make up a new dataset reference for this sequence
4263           sqid = seqHash(dsq);
4264         }
4265         dsq.setVamsasId(uniqueSetSuffix + sqid);
4266         seqRefIds.put(sqid, dsq);
4267         if (ds == null)
4268         {
4269           if (dseqs != null)
4270           {
4271             dseqs.addElement(dsq);
4272           }
4273         }
4274         else
4275         {
4276           ds.addSequence(dsq);
4277         }
4278       }
4279       else
4280       {
4281         if (sq != dsq)
4282         { // make this dataset sequence sq's dataset sequence
4283           sq.setDatasetSequence(dsq);
4284           // and update the current dataset alignment
4285           if (ds == null)
4286           {
4287             if (dseqs != null)
4288             {
4289               if (!dseqs.contains(dsq))
4290               {
4291                 dseqs.add(dsq);
4292               }
4293             }
4294             else
4295             {
4296               if (ds.findIndex(dsq) < 0)
4297               {
4298                 ds.addSequence(dsq);
4299               }
4300             }
4301           }
4302         }
4303       }
4304     }
4305     // TODO: refactor this as a merge dataset sequence function
4306     // now check that sq (the dataset sequence) sequence really is the union of
4307     // all references to it
4308     // boolean pre = sq.getStart() < dsq.getStart();
4309     // boolean post = sq.getEnd() > dsq.getEnd();
4310     // if (pre || post)
4311     if (sq != dsq)
4312     {
4313       StringBuffer sb = new StringBuffer();
4314       String newres = jalview.analysis.AlignSeq.extractGaps(
4315               jalview.util.Comparison.GapChars, sq.getSequenceAsString());
4316       if (!newres.equalsIgnoreCase(dsq.getSequenceAsString())
4317               && newres.length() > dsq.getLength())
4318       {
4319         // Update with the longer sequence.
4320         synchronized (dsq)
4321         {
4322           /*
4323            * if (pre) { sb.insert(0, newres .substring(0, dsq.getStart() -
4324            * sq.getStart())); dsq.setStart(sq.getStart()); } if (post) {
4325            * sb.append(newres.substring(newres.length() - sq.getEnd() -
4326            * dsq.getEnd())); dsq.setEnd(sq.getEnd()); }
4327            */
4328           dsq.setSequence(newres);
4329         }
4330         // TODO: merges will never happen if we 'know' we have the real dataset
4331         // sequence - this should be detected when id==dssid
4332         System.err
4333                 .println("DEBUG Notice:  Merged dataset sequence (if you see this often, post at http://issues.jalview.org/browse/JAL-1474)"); // ("
4334         // + (pre ? "prepended" : "") + " "
4335         // + (post ? "appended" : ""));
4336       }
4337     }
4338   }
4339
4340   java.util.Hashtable datasetIds = null;
4341
4342   java.util.IdentityHashMap dataset2Ids = null;
4343
4344   private Alignment getDatasetFor(String datasetId)
4345   {
4346     if (datasetIds == null)
4347     {
4348       datasetIds = new Hashtable();
4349       return null;
4350     }
4351     if (datasetIds.containsKey(datasetId))
4352     {
4353       return (Alignment) datasetIds.get(datasetId);
4354     }
4355     return null;
4356   }
4357
4358   private void addDatasetRef(String datasetId, Alignment dataset)
4359   {
4360     if (datasetIds == null)
4361     {
4362       datasetIds = new Hashtable();
4363     }
4364     datasetIds.put(datasetId, dataset);
4365   }
4366
4367   /**
4368    * make a new dataset ID for this jalview dataset alignment
4369    * 
4370    * @param dataset
4371    * @return
4372    */
4373   private String getDatasetIdRef(jalview.datamodel.Alignment dataset)
4374   {
4375     if (dataset.getDataset() != null)
4376     {
4377       warn("Serious issue!  Dataset Object passed to getDatasetIdRef is not a Jalview DATASET alignment...");
4378     }
4379     String datasetId = makeHashCode(dataset, null);
4380     if (datasetId == null)
4381     {
4382       // make a new datasetId and record it
4383       if (dataset2Ids == null)
4384       {
4385         dataset2Ids = new IdentityHashMap();
4386       }
4387       else
4388       {
4389         datasetId = (String) dataset2Ids.get(dataset);
4390       }
4391       if (datasetId == null)
4392       {
4393         datasetId = "ds" + dataset2Ids.size() + 1;
4394         dataset2Ids.put(dataset, datasetId);
4395       }
4396     }
4397     return datasetId;
4398   }
4399
4400   private void addDBRefs(SequenceI datasetSequence, Sequence sequence)
4401   {
4402     for (int d = 0; d < sequence.getDBRefCount(); d++)
4403     {
4404       DBRef dr = sequence.getDBRef(d);
4405       jalview.datamodel.DBRefEntry entry = new jalview.datamodel.DBRefEntry(
4406               sequence.getDBRef(d).getSource(), sequence.getDBRef(d)
4407                       .getVersion(), sequence.getDBRef(d).getAccessionId());
4408       if (dr.getMapping() != null)
4409       {
4410         entry.setMap(addMapping(dr.getMapping()));
4411       }
4412       datasetSequence.addDBRef(entry);
4413     }
4414   }
4415
4416   private jalview.datamodel.Mapping addMapping(Mapping m)
4417   {
4418     SequenceI dsto = null;
4419     // Mapping m = dr.getMapping();
4420     int fr[] = new int[m.getMapListFromCount() * 2];
4421     Enumeration f = m.enumerateMapListFrom();
4422     for (int _i = 0; f.hasMoreElements(); _i += 2)
4423     {
4424       MapListFrom mf = (MapListFrom) f.nextElement();
4425       fr[_i] = mf.getStart();
4426       fr[_i + 1] = mf.getEnd();
4427     }
4428     int fto[] = new int[m.getMapListToCount() * 2];
4429     f = m.enumerateMapListTo();
4430     for (int _i = 0; f.hasMoreElements(); _i += 2)
4431     {
4432       MapListTo mf = (MapListTo) f.nextElement();
4433       fto[_i] = mf.getStart();
4434       fto[_i + 1] = mf.getEnd();
4435     }
4436     jalview.datamodel.Mapping jmap = new jalview.datamodel.Mapping(dsto,
4437             fr, fto, (int) m.getMapFromUnit(), (int) m.getMapToUnit());
4438     if (m.getMappingChoice() != null)
4439     {
4440       MappingChoice mc = m.getMappingChoice();
4441       if (mc.getDseqFor() != null)
4442       {
4443         String dsfor = "" + mc.getDseqFor();
4444         if (seqRefIds.containsKey(dsfor))
4445         {
4446           /**
4447            * recover from hash
4448            */
4449           jmap.setTo(seqRefIds.get(dsfor));
4450         }
4451         else
4452         {
4453           frefedSequence.add(new Object[]
4454           { dsfor, jmap });
4455         }
4456       }
4457       else
4458       {
4459         /**
4460          * local sequence definition
4461          */
4462         Sequence ms = mc.getSequence();
4463         jalview.datamodel.Sequence djs = null;
4464         String sqid = ms.getDsseqid();
4465         if (sqid != null && sqid.length() > 0)
4466         {
4467           /*
4468            * recover dataset sequence
4469            */
4470           djs = (jalview.datamodel.Sequence) seqRefIds.get(sqid);
4471         }
4472         else
4473         {
4474           System.err
4475                   .println("Warning - making up dataset sequence id for DbRef sequence map reference");
4476           sqid = ((Object) ms).toString(); // make up a new hascode for
4477           // undefined dataset sequence hash
4478           // (unlikely to happen)
4479         }
4480
4481         if (djs == null)
4482         {
4483           /**
4484            * make a new dataset sequence and add it to refIds hash
4485            */
4486           djs = new jalview.datamodel.Sequence(ms.getName(),
4487                   ms.getSequence());
4488           djs.setStart(jmap.getMap().getToLowest());
4489           djs.setEnd(jmap.getMap().getToHighest());
4490           djs.setVamsasId(uniqueSetSuffix + sqid);
4491           jmap.setTo(djs);
4492           seqRefIds.put(sqid, djs);
4493
4494         }
4495         jalview.bin.Cache.log.debug("about to recurse on addDBRefs.");
4496         addDBRefs(djs, ms);
4497
4498       }
4499     }
4500     return (jmap);
4501
4502   }
4503
4504   public jalview.gui.AlignmentPanel copyAlignPanel(AlignmentPanel ap,
4505           boolean keepSeqRefs)
4506   {
4507     initSeqRefs();
4508     jalview.schemabinding.version2.JalviewModel jm = saveState(ap, null,
4509             null);
4510
4511     if (!keepSeqRefs)
4512     {
4513       clearSeqRefs();
4514       jm.getJalviewModelSequence().getViewport(0).setSequenceSetId(null);
4515     }
4516     else
4517     {
4518       uniqueSetSuffix = "";
4519       jm.getJalviewModelSequence().getViewport(0).setId(null); // we don't
4520       // overwrite the
4521       // view we just
4522       // copied
4523     }
4524     if (this.frefedSequence == null)
4525     {
4526       frefedSequence = new Vector();
4527     }
4528
4529     viewportsAdded = new Hashtable();
4530
4531     AlignFrame af = loadFromObject(jm, null, false, null);
4532     af.alignPanels.clear();
4533     af.closeMenuItem_actionPerformed(true);
4534
4535     /*
4536      * if(ap.av.getAlignment().getAlignmentAnnotation()!=null) { for(int i=0;
4537      * i<ap.av.getAlignment().getAlignmentAnnotation().length; i++) {
4538      * if(!ap.av.getAlignment().getAlignmentAnnotation()[i].autoCalculated) {
4539      * af.alignPanel.av.getAlignment().getAlignmentAnnotation()[i] =
4540      * ap.av.getAlignment().getAlignmentAnnotation()[i]; } } }
4541      */
4542
4543     return af.alignPanel;
4544   }
4545
4546   /**
4547    * flag indicating if hashtables should be cleared on finalization TODO this
4548    * flag may not be necessary
4549    */
4550   private final boolean _cleartables = true;
4551
4552   private Hashtable jvids2vobj;
4553
4554   /*
4555    * (non-Javadoc)
4556    * 
4557    * @see java.lang.Object#finalize()
4558    */
4559   @Override
4560   protected void finalize() throws Throwable
4561   {
4562     // really make sure we have no buried refs left.
4563     if (_cleartables)
4564     {
4565       clearSeqRefs();
4566     }
4567     this.seqRefIds = null;
4568     this.seqsToIds = null;
4569     super.finalize();
4570   }
4571
4572   private void warn(String msg)
4573   {
4574     warn(msg, null);
4575   }
4576
4577   private void warn(String msg, Exception e)
4578   {
4579     if (Cache.log != null)
4580     {
4581       if (e != null)
4582       {
4583         Cache.log.warn(msg, e);
4584       }
4585       else
4586       {
4587         Cache.log.warn(msg);
4588       }
4589     }
4590     else
4591     {
4592       System.err.println("Warning: " + msg);
4593       if (e != null)
4594       {
4595         e.printStackTrace();
4596       }
4597     }
4598   }
4599
4600   private void debug(String string)
4601   {
4602     debug(string, null);
4603   }
4604
4605   private void debug(String msg, Exception e)
4606   {
4607     if (Cache.log != null)
4608     {
4609       if (e != null)
4610       {
4611         Cache.log.debug(msg, e);
4612       }
4613       else
4614       {
4615         Cache.log.debug(msg);
4616       }
4617     }
4618     else
4619     {
4620       System.err.println("Warning: " + msg);
4621       if (e != null)
4622       {
4623         e.printStackTrace();
4624       }
4625     }
4626   }
4627
4628   /**
4629    * set the object to ID mapping tables used to write/recover objects and XML
4630    * ID strings for the jalview project. If external tables are provided then
4631    * finalize and clearSeqRefs will not clear the tables when the Jalview2XML
4632    * object goes out of scope. - also populates the datasetIds hashtable with
4633    * alignment objects containing dataset sequences
4634    * 
4635    * @param vobj2jv
4636    *          Map from ID strings to jalview datamodel
4637    * @param jv2vobj
4638    *          Map from jalview datamodel to ID strings
4639    * 
4640    * 
4641    */
4642   public void setObjectMappingTables(Hashtable vobj2jv,
4643           IdentityHashMap jv2vobj)
4644   {
4645     this.jv2vobj = jv2vobj;
4646     this.vobj2jv = vobj2jv;
4647     Iterator ds = jv2vobj.keySet().iterator();
4648     String id;
4649     while (ds.hasNext())
4650     {
4651       Object jvobj = ds.next();
4652       id = jv2vobj.get(jvobj).toString();
4653       if (jvobj instanceof jalview.datamodel.Alignment)
4654       {
4655         if (((jalview.datamodel.Alignment) jvobj).getDataset() == null)
4656         {
4657           addDatasetRef(id, (jalview.datamodel.Alignment) jvobj);
4658         }
4659       }
4660       else if (jvobj instanceof jalview.datamodel.Sequence)
4661       {
4662         // register sequence object so the XML parser can recover it.
4663         if (seqRefIds == null)
4664         {
4665           seqRefIds = new HashMap<String, SequenceI>();
4666         }
4667         if (seqsToIds == null)
4668         {
4669           seqsToIds = new IdentityHashMap<SequenceI, String>();
4670         }
4671         seqRefIds.put(jv2vobj.get(jvobj).toString(), (SequenceI) jvobj);
4672         seqsToIds.put((SequenceI) jvobj, id);
4673       }
4674       else if (jvobj instanceof jalview.datamodel.AlignmentAnnotation)
4675       {
4676         if (annotationIds == null)
4677         {
4678           annotationIds = new Hashtable();
4679         }
4680         String anid;
4681         annotationIds.put(anid = jv2vobj.get(jvobj).toString(), jvobj);
4682         jalview.datamodel.AlignmentAnnotation jvann = (jalview.datamodel.AlignmentAnnotation) jvobj;
4683         if (jvann.annotationId == null)
4684         {
4685           jvann.annotationId = anid;
4686         }
4687         if (!jvann.annotationId.equals(anid))
4688         {
4689           // TODO verify that this is the correct behaviour
4690           this.warn("Overriding Annotation ID for " + anid
4691                   + " from different id : " + jvann.annotationId);
4692           jvann.annotationId = anid;
4693         }
4694       }
4695       else if (jvobj instanceof String)
4696       {
4697         if (jvids2vobj == null)
4698         {
4699           jvids2vobj = new Hashtable();
4700           jvids2vobj.put(jvobj, jv2vobj.get(jvobj).toString());
4701         }
4702       }
4703       else
4704       {
4705         Cache.log.debug("Ignoring " + jvobj.getClass() + " (ID = " + id);
4706       }
4707     }
4708   }
4709
4710   /**
4711    * set the uniqueSetSuffix used to prefix/suffix object IDs for jalview
4712    * objects created from the project archive. If string is null (default for
4713    * construction) then suffix will be set automatically.
4714    * 
4715    * @param string
4716    */
4717   public void setUniqueSetSuffix(String string)
4718   {
4719     uniqueSetSuffix = string;
4720
4721   }
4722
4723   /**
4724    * uses skipList2 as the skipList for skipping views on sequence sets
4725    * associated with keys in the skipList
4726    * 
4727    * @param skipList2
4728    */
4729   public void setSkipList(Hashtable skipList2)
4730   {
4731     skipList = skipList2;
4732   }
4733
4734 }