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