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