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