da9c6a9ad46a69b6afd2b27bcf43ef4c4eafd459
[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, getViewerJarEntryName(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           state.setType(viewFrame.getViewerType().toString());
1459           pdb.addStructureState(state);
1460         }
1461       }
1462     }
1463     return matchedFile;
1464   }
1465
1466   private AnnotationColours constructAnnotationColours(
1467           AnnotationColourGradient acg, List<UserColourScheme> userColours,
1468           JalviewModelSequence jms)
1469   {
1470     AnnotationColours ac = new AnnotationColours();
1471     ac.setAboveThreshold(acg.getAboveThreshold());
1472     ac.setThreshold(acg.getAnnotationThreshold());
1473     ac.setAnnotation(acg.getAnnotation());
1474     if (acg.getBaseColour() instanceof jalview.schemes.UserColourScheme)
1475     {
1476       ac.setColourScheme(setUserColourScheme(acg.getBaseColour(),
1477               userColours, jms));
1478     }
1479     else
1480     {
1481       ac.setColourScheme(ColourSchemeProperty.getColourName(acg
1482               .getBaseColour()));
1483     }
1484
1485     ac.setMaxColour(acg.getMaxColour().getRGB());
1486     ac.setMinColour(acg.getMinColour().getRGB());
1487     ac.setPerSequence(acg.isSeqAssociated());
1488     ac.setPredefinedColours(acg.isPredefinedColours());
1489     return ac;
1490   }
1491
1492   private void storeAlignmentAnnotation(AlignmentAnnotation[] aa,
1493           IdentityHashMap groupRefs, AlignmentViewport av,
1494           Set<String> calcIdSet, boolean storeDS, SequenceSet vamsasSet)
1495   {
1496
1497     for (int i = 0; i < aa.length; i++)
1498     {
1499       Annotation an = new Annotation();
1500
1501       if (aa[i].annotationId != null)
1502       {
1503         annotationIds.put(aa[i].annotationId, aa[i]);
1504       }
1505
1506       an.setId(aa[i].annotationId);
1507
1508       an.setVisible(aa[i].visible);
1509
1510       an.setDescription(aa[i].description);
1511
1512       if (aa[i].sequenceRef != null)
1513       {
1514         // 2.9 JAL-1781 xref on sequence id rather than name
1515         an.setSequenceRef(seqsToIds.get(aa[i].sequenceRef));
1516       }
1517       if (aa[i].groupRef != null)
1518       {
1519         Object groupIdr = groupRefs.get(aa[i].groupRef);
1520         if (groupIdr == null)
1521         {
1522           // make a locally unique String
1523           groupRefs.put(aa[i].groupRef,
1524                   groupIdr = ("" + System.currentTimeMillis()
1525                           + aa[i].groupRef.getName() + groupRefs.size()));
1526         }
1527         an.setGroupRef(groupIdr.toString());
1528       }
1529
1530       // store all visualization attributes for annotation
1531       an.setGraphHeight(aa[i].graphHeight);
1532       an.setCentreColLabels(aa[i].centreColLabels);
1533       an.setScaleColLabels(aa[i].scaleColLabel);
1534       an.setShowAllColLabels(aa[i].showAllColLabels);
1535       an.setBelowAlignment(aa[i].belowAlignment);
1536
1537       if (aa[i].graph > 0)
1538       {
1539         an.setGraph(true);
1540         an.setGraphType(aa[i].graph);
1541         an.setGraphGroup(aa[i].graphGroup);
1542         if (aa[i].getThreshold() != null)
1543         {
1544           ThresholdLine line = new ThresholdLine();
1545           line.setLabel(aa[i].getThreshold().label);
1546           line.setValue(aa[i].getThreshold().value);
1547           line.setColour(aa[i].getThreshold().colour.getRGB());
1548           an.setThresholdLine(line);
1549         }
1550       }
1551       else
1552       {
1553         an.setGraph(false);
1554       }
1555
1556       an.setLabel(aa[i].label);
1557
1558       if (aa[i] == av.getAlignmentQualityAnnot()
1559               || aa[i] == av.getAlignmentConservationAnnotation()
1560               || aa[i] == av.getAlignmentConsensusAnnotation()
1561               || aa[i].autoCalculated)
1562       {
1563         // new way of indicating autocalculated annotation -
1564         an.setAutoCalculated(aa[i].autoCalculated);
1565       }
1566       if (aa[i].hasScore())
1567       {
1568         an.setScore(aa[i].getScore());
1569       }
1570
1571       if (aa[i].getCalcId() != null)
1572       {
1573         calcIdSet.add(aa[i].getCalcId());
1574         an.setCalcId(aa[i].getCalcId());
1575       }
1576       if (aa[i].hasProperties())
1577       {
1578         for (String pr : aa[i].getProperties())
1579         {
1580           Property prop = new Property();
1581           prop.setName(pr);
1582           prop.setValue(aa[i].getProperty(pr));
1583           an.addProperty(prop);
1584         }
1585       }
1586
1587       AnnotationElement ae;
1588       if (aa[i].annotations != null)
1589       {
1590         an.setScoreOnly(false);
1591         for (int a = 0; a < aa[i].annotations.length; a++)
1592         {
1593           if ((aa[i] == null) || (aa[i].annotations[a] == null))
1594           {
1595             continue;
1596           }
1597
1598           ae = new AnnotationElement();
1599           if (aa[i].annotations[a].description != null)
1600           {
1601             ae.setDescription(aa[i].annotations[a].description);
1602           }
1603           if (aa[i].annotations[a].displayCharacter != null)
1604           {
1605             ae.setDisplayCharacter(aa[i].annotations[a].displayCharacter);
1606           }
1607
1608           if (!Float.isNaN(aa[i].annotations[a].value))
1609           {
1610             ae.setValue(aa[i].annotations[a].value);
1611           }
1612
1613           ae.setPosition(a);
1614           if (aa[i].annotations[a].secondaryStructure > ' ')
1615           {
1616             ae.setSecondaryStructure(aa[i].annotations[a].secondaryStructure
1617                     + "");
1618           }
1619
1620           if (aa[i].annotations[a].colour != null
1621                   && aa[i].annotations[a].colour != java.awt.Color.black)
1622           {
1623             ae.setColour(aa[i].annotations[a].colour.getRGB());
1624           }
1625
1626           an.addAnnotationElement(ae);
1627           if (aa[i].autoCalculated)
1628           {
1629             // only write one non-null entry into the annotation row -
1630             // sufficient to get the visualization attributes necessary to
1631             // display data
1632             continue;
1633           }
1634         }
1635       }
1636       else
1637       {
1638         an.setScoreOnly(true);
1639       }
1640       if (!storeDS || (storeDS && !aa[i].autoCalculated))
1641       {
1642         // skip autocalculated annotation - these are only provided for
1643         // alignments
1644         vamsasSet.addAnnotation(an);
1645       }
1646     }
1647
1648   }
1649
1650   private CalcIdParam createCalcIdParam(String calcId, AlignViewport av)
1651   {
1652     AutoCalcSetting settings = av.getCalcIdSettingsFor(calcId);
1653     if (settings != null)
1654     {
1655       CalcIdParam vCalcIdParam = new CalcIdParam();
1656       vCalcIdParam.setCalcId(calcId);
1657       vCalcIdParam.addServiceURL(settings.getServiceURI());
1658       // generic URI allowing a third party to resolve another instance of the
1659       // service used for this calculation
1660       for (String urls : settings.getServiceURLs())
1661       {
1662         vCalcIdParam.addServiceURL(urls);
1663       }
1664       vCalcIdParam.setVersion("1.0");
1665       if (settings.getPreset() != null)
1666       {
1667         WsParamSetI setting = settings.getPreset();
1668         vCalcIdParam.setName(setting.getName());
1669         vCalcIdParam.setDescription(setting.getDescription());
1670       }
1671       else
1672       {
1673         vCalcIdParam.setName("");
1674         vCalcIdParam.setDescription("Last used parameters");
1675       }
1676       // need to be able to recover 1) settings 2) user-defined presets or
1677       // recreate settings from preset 3) predefined settings provided by
1678       // service - or settings that can be transferred (or discarded)
1679       vCalcIdParam.setParameters(settings.getWsParamFile().replace("\n",
1680               "|\\n|"));
1681       vCalcIdParam.setAutoUpdate(settings.isAutoUpdate());
1682       // todo - decide if updateImmediately is needed for any projects.
1683
1684       return vCalcIdParam;
1685     }
1686     return null;
1687   }
1688
1689   private boolean recoverCalcIdParam(CalcIdParam calcIdParam,
1690           AlignViewport av)
1691   {
1692     if (calcIdParam.getVersion().equals("1.0"))
1693     {
1694       Jws2Instance service = Jws2Discoverer.getDiscoverer()
1695               .getPreferredServiceFor(calcIdParam.getServiceURL());
1696       if (service != null)
1697       {
1698         WsParamSetI parmSet = null;
1699         try
1700         {
1701           parmSet = service.getParamStore().parseServiceParameterFile(
1702                   calcIdParam.getName(), calcIdParam.getDescription(),
1703                   calcIdParam.getServiceURL(),
1704                   calcIdParam.getParameters().replace("|\\n|", "\n"));
1705         } catch (IOException x)
1706         {
1707           warn("Couldn't parse parameter data for "
1708                   + calcIdParam.getCalcId(), x);
1709           return false;
1710         }
1711         List<ArgumentI> argList = null;
1712         if (calcIdParam.getName().length() > 0)
1713         {
1714           parmSet = service.getParamStore()
1715                   .getPreset(calcIdParam.getName());
1716           if (parmSet != null)
1717           {
1718             // TODO : check we have a good match with settings in AACon -
1719             // otherwise we'll need to create a new preset
1720           }
1721         }
1722         else
1723         {
1724           argList = parmSet.getArguments();
1725           parmSet = null;
1726         }
1727         AAConSettings settings = new AAConSettings(
1728                 calcIdParam.isAutoUpdate(), service, parmSet, argList);
1729         av.setCalcIdSettingsFor(calcIdParam.getCalcId(), settings,
1730                 calcIdParam.isNeedsUpdate());
1731         return true;
1732       }
1733       else
1734       {
1735         warn("Cannot resolve a service for the parameters used in this project. Try configuring a JABAWS server.");
1736         return false;
1737       }
1738     }
1739     throw new Error(MessageManager.formatMessage(
1740             "error.unsupported_version_calcIdparam", new Object[]
1741             { calcIdParam.toString() }));
1742   }
1743
1744   /**
1745    * External mapping between jalview objects and objects yielding a valid and
1746    * unique object ID string. This is null for normal Jalview project IO, but
1747    * non-null when a jalview project is being read or written as part of a
1748    * vamsas session.
1749    */
1750   IdentityHashMap jv2vobj = null;
1751
1752   /**
1753    * Construct a unique ID for jvobj using either existing bindings or if none
1754    * exist, the result of the hashcode call for the object.
1755    * 
1756    * @param jvobj
1757    *          jalview data object
1758    * @return unique ID for referring to jvobj
1759    */
1760   private String makeHashCode(Object jvobj, String altCode)
1761   {
1762     if (jv2vobj != null)
1763     {
1764       Object id = jv2vobj.get(jvobj);
1765       if (id != null)
1766       {
1767         return id.toString();
1768       }
1769       // check string ID mappings
1770       if (jvids2vobj != null && jvobj instanceof String)
1771       {
1772         id = jvids2vobj.get(jvobj);
1773       }
1774       if (id != null)
1775       {
1776         return id.toString();
1777       }
1778       // give up and warn that something has gone wrong
1779       warn("Cannot find ID for object in external mapping : " + jvobj);
1780     }
1781     return altCode;
1782   }
1783
1784   /**
1785    * return local jalview object mapped to ID, if it exists
1786    * 
1787    * @param idcode
1788    *          (may be null)
1789    * @return null or object bound to idcode
1790    */
1791   private Object retrieveExistingObj(String idcode)
1792   {
1793     if (idcode != null && vobj2jv != null)
1794     {
1795       return vobj2jv.get(idcode);
1796     }
1797     return null;
1798   }
1799
1800   /**
1801    * binding from ID strings from external mapping table to jalview data model
1802    * objects.
1803    */
1804   private Hashtable vobj2jv;
1805
1806   private Sequence createVamsasSequence(String id, SequenceI jds)
1807   {
1808     return createVamsasSequence(true, id, jds, null);
1809   }
1810
1811   private Sequence createVamsasSequence(boolean recurse, String id,
1812           SequenceI jds, SequenceI parentseq)
1813   {
1814     Sequence vamsasSeq = new Sequence();
1815     vamsasSeq.setId(id);
1816     vamsasSeq.setName(jds.getName());
1817     vamsasSeq.setSequence(jds.getSequenceAsString());
1818     vamsasSeq.setDescription(jds.getDescription());
1819     jalview.datamodel.DBRefEntry[] dbrefs = null;
1820     if (jds.getDatasetSequence() != null)
1821     {
1822       vamsasSeq.setDsseqid(seqHash(jds.getDatasetSequence()));
1823       if (jds.getDatasetSequence().getDBRef() != null)
1824       {
1825         dbrefs = jds.getDatasetSequence().getDBRef();
1826       }
1827     }
1828     else
1829     {
1830       vamsasSeq.setDsseqid(id); // so we can tell which sequences really are
1831       // dataset sequences only
1832       dbrefs = jds.getDBRef();
1833     }
1834     if (dbrefs != null)
1835     {
1836       for (int d = 0; d < dbrefs.length; d++)
1837       {
1838         DBRef dbref = new DBRef();
1839         dbref.setSource(dbrefs[d].getSource());
1840         dbref.setVersion(dbrefs[d].getVersion());
1841         dbref.setAccessionId(dbrefs[d].getAccessionId());
1842         if (dbrefs[d].hasMap())
1843         {
1844           Mapping mp = createVamsasMapping(dbrefs[d].getMap(), parentseq,
1845                   jds, recurse);
1846           dbref.setMapping(mp);
1847         }
1848         vamsasSeq.addDBRef(dbref);
1849       }
1850     }
1851     return vamsasSeq;
1852   }
1853
1854   private Mapping createVamsasMapping(jalview.datamodel.Mapping jmp,
1855           SequenceI parentseq, SequenceI jds, boolean recurse)
1856   {
1857     Mapping mp = null;
1858     if (jmp.getMap() != null)
1859     {
1860       mp = new Mapping();
1861
1862       jalview.util.MapList mlst = jmp.getMap();
1863       List<int[]> r = mlst.getFromRanges();
1864       for (int[] range : r)
1865       {
1866         MapListFrom mfrom = new MapListFrom();
1867         mfrom.setStart(range[0]);
1868         mfrom.setEnd(range[1]);
1869         mp.addMapListFrom(mfrom);
1870       }
1871       r = mlst.getToRanges();
1872       for (int[] range : r)
1873       {
1874         MapListTo mto = new MapListTo();
1875         mto.setStart(range[0]);
1876         mto.setEnd(range[1]);
1877         mp.addMapListTo(mto);
1878       }
1879       mp.setMapFromUnit(mlst.getFromRatio());
1880       mp.setMapToUnit(mlst.getToRatio());
1881       if (jmp.getTo() != null)
1882       {
1883         MappingChoice mpc = new MappingChoice();
1884         if (recurse
1885                 && (parentseq != jmp.getTo() || parentseq
1886                         .getDatasetSequence() != jmp.getTo()))
1887         {
1888           mpc.setSequence(createVamsasSequence(false, seqHash(jmp.getTo()),
1889                   jmp.getTo(), jds));
1890         }
1891         else
1892         {
1893           String jmpid = "";
1894           SequenceI ps = null;
1895           if (parentseq != jmp.getTo()
1896                   && parentseq.getDatasetSequence() != jmp.getTo())
1897           {
1898             // chaining dbref rather than a handshaking one
1899             jmpid = seqHash(ps = jmp.getTo());
1900           }
1901           else
1902           {
1903             jmpid = seqHash(ps = parentseq);
1904           }
1905           mpc.setDseqFor(jmpid);
1906           if (!seqRefIds.containsKey(mpc.getDseqFor()))
1907           {
1908             jalview.bin.Cache.log.debug("creatign new DseqFor ID");
1909             seqRefIds.put(mpc.getDseqFor(), ps);
1910           }
1911           else
1912           {
1913             jalview.bin.Cache.log.debug("reusing DseqFor ID");
1914           }
1915         }
1916         mp.setMappingChoice(mpc);
1917       }
1918     }
1919     return mp;
1920   }
1921
1922   String setUserColourScheme(jalview.schemes.ColourSchemeI cs,
1923           List<UserColourScheme> userColours, JalviewModelSequence jms)
1924   {
1925     String id = null;
1926     jalview.schemes.UserColourScheme ucs = (jalview.schemes.UserColourScheme) cs;
1927     boolean newucs = false;
1928     if (!userColours.contains(ucs))
1929     {
1930       userColours.add(ucs);
1931       newucs = true;
1932     }
1933     id = "ucs" + userColours.indexOf(ucs);
1934     if (newucs)
1935     {
1936       // actually create the scheme's entry in the XML model
1937       java.awt.Color[] colours = ucs.getColours();
1938       jalview.schemabinding.version2.UserColours uc = new jalview.schemabinding.version2.UserColours();
1939       jalview.schemabinding.version2.UserColourScheme jbucs = new jalview.schemabinding.version2.UserColourScheme();
1940
1941       for (int i = 0; i < colours.length; i++)
1942       {
1943         jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
1944         col.setName(ResidueProperties.aa[i]);
1945         col.setRGB(jalview.util.Format.getHexString(colours[i]));
1946         jbucs.addColour(col);
1947       }
1948       if (ucs.getLowerCaseColours() != null)
1949       {
1950         colours = ucs.getLowerCaseColours();
1951         for (int i = 0; i < colours.length; i++)
1952         {
1953           jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
1954           col.setName(ResidueProperties.aa[i].toLowerCase());
1955           col.setRGB(jalview.util.Format.getHexString(colours[i]));
1956           jbucs.addColour(col);
1957         }
1958       }
1959
1960       uc.setId(id);
1961       uc.setUserColourScheme(jbucs);
1962       jms.addUserColours(uc);
1963     }
1964
1965     return id;
1966   }
1967
1968   jalview.schemes.UserColourScheme getUserColourScheme(
1969           JalviewModelSequence jms, String id)
1970   {
1971     UserColours[] uc = jms.getUserColours();
1972     UserColours colours = null;
1973
1974     for (int i = 0; i < uc.length; i++)
1975     {
1976       if (uc[i].getId().equals(id))
1977       {
1978         colours = uc[i];
1979
1980         break;
1981       }
1982     }
1983
1984     java.awt.Color[] newColours = new java.awt.Color[24];
1985
1986     for (int i = 0; i < 24; i++)
1987     {
1988       newColours[i] = new java.awt.Color(Integer.parseInt(colours
1989               .getUserColourScheme().getColour(i).getRGB(), 16));
1990     }
1991
1992     jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme(
1993             newColours);
1994
1995     if (colours.getUserColourScheme().getColourCount() > 24)
1996     {
1997       newColours = new java.awt.Color[23];
1998       for (int i = 0; i < 23; i++)
1999       {
2000         newColours[i] = new java.awt.Color(Integer.parseInt(colours
2001                 .getUserColourScheme().getColour(i + 24).getRGB(), 16));
2002       }
2003       ucs.setLowerCaseColours(newColours);
2004     }
2005
2006     return ucs;
2007   }
2008
2009   /**
2010    * contains last error message (if any) encountered by XML loader.
2011    */
2012   String errorMessage = null;
2013
2014   /**
2015    * flag to control whether the Jalview2XML_V1 parser should be deferred to if
2016    * exceptions are raised during project XML parsing
2017    */
2018   public boolean attemptversion1parse = true;
2019
2020   /**
2021    * Load a jalview project archive from a jar file
2022    * 
2023    * @param file
2024    *          - HTTP URL or filename
2025    */
2026   public AlignFrame loadJalviewAlign(final String file)
2027   {
2028
2029     jalview.gui.AlignFrame af = null;
2030
2031     try
2032     {
2033       // create list to store references for any new Jmol viewers created
2034       newStructureViewers = new Vector<JalviewStructureDisplayI>();
2035       // UNMARSHALLER SEEMS TO CLOSE JARINPUTSTREAM, MOST ANNOYING
2036       // Workaround is to make sure caller implements the JarInputStreamProvider
2037       // interface
2038       // so we can re-open the jar input stream for each entry.
2039
2040       jarInputStreamProvider jprovider = createjarInputStreamProvider(file);
2041       af = loadJalviewAlign(jprovider);
2042
2043     } catch (MalformedURLException e)
2044     {
2045       errorMessage = "Invalid URL format for '" + file + "'";
2046       reportErrors();
2047     } finally
2048     {
2049       try
2050       {
2051         SwingUtilities.invokeAndWait(new Runnable()
2052         {
2053           public void run()
2054           {
2055             setLoadingFinishedForNewStructureViewers();
2056           };
2057         });
2058       } catch (Exception x)
2059       {
2060
2061       }
2062     }
2063     return af;
2064   }
2065
2066   private jarInputStreamProvider createjarInputStreamProvider(
2067           final String file) throws MalformedURLException
2068   {
2069     URL url = null;
2070     errorMessage = null;
2071     uniqueSetSuffix = null;
2072     seqRefIds = null;
2073     viewportsAdded.clear();
2074     frefedSequence = null;
2075
2076     if (file.startsWith("http://"))
2077     {
2078       url = new URL(file);
2079     }
2080     final URL _url = url;
2081     return new jarInputStreamProvider()
2082     {
2083
2084       @Override
2085       public JarInputStream getJarInputStream() throws IOException
2086       {
2087         if (_url != null)
2088         {
2089           return new JarInputStream(_url.openStream());
2090         }
2091         else
2092         {
2093           return new JarInputStream(new FileInputStream(file));
2094         }
2095       }
2096
2097       @Override
2098       public String getFilename()
2099       {
2100         return file;
2101       }
2102     };
2103   }
2104
2105   /**
2106    * Recover jalview session from a jalview project archive. Caller may
2107    * initialise uniqueSetSuffix, seqRefIds, viewportsAdded and frefedSequence
2108    * themselves. Any null fields will be initialised with default values,
2109    * non-null fields are left alone.
2110    * 
2111    * @param jprovider
2112    * @return
2113    */
2114   public AlignFrame loadJalviewAlign(final jarInputStreamProvider jprovider)
2115   {
2116     errorMessage = null;
2117     if (uniqueSetSuffix == null)
2118     {
2119       uniqueSetSuffix = System.currentTimeMillis() % 100000 + "";
2120     }
2121     if (seqRefIds == null)
2122     {
2123       seqRefIds = new HashMap<String, SequenceI>();
2124     }
2125     if (frefedSequence == null)
2126     {
2127       frefedSequence = new Vector();
2128     }
2129
2130     AlignFrame af = null, _af = null;
2131     Map<String, AlignFrame> gatherToThisFrame = new HashMap<String, AlignFrame>();
2132     final String file = jprovider.getFilename();
2133     try
2134     {
2135       JarInputStream jin = null;
2136       JarEntry jarentry = null;
2137       int entryCount = 1;
2138
2139       do
2140       {
2141         jin = jprovider.getJarInputStream();
2142         for (int i = 0; i < entryCount; i++)
2143         {
2144           jarentry = jin.getNextJarEntry();
2145         }
2146
2147         if (jarentry != null && jarentry.getName().endsWith(".xml"))
2148         {
2149           InputStreamReader in = new InputStreamReader(jin, UTF_8);
2150           JalviewModel object = new JalviewModel();
2151
2152           Unmarshaller unmar = new Unmarshaller(object);
2153           unmar.setValidation(false);
2154           object = (JalviewModel) unmar.unmarshal(in);
2155           if (true) // !skipViewport(object))
2156           {
2157             _af = loadFromObject(object, file, true, jprovider);
2158             if (object.getJalviewModelSequence().getViewportCount() > 0)
2159             {
2160               af = _af;
2161               if (af.viewport.isGatherViewsHere())
2162               {
2163                 gatherToThisFrame.put(af.viewport.getSequenceSetId(), af);
2164               }
2165             }
2166           }
2167           entryCount++;
2168         }
2169         else if (jarentry != null)
2170         {
2171           // Some other file here.
2172           entryCount++;
2173         }
2174       } while (jarentry != null);
2175       resolveFrefedSequences();
2176     } catch (java.io.FileNotFoundException ex)
2177     {
2178       ex.printStackTrace();
2179       errorMessage = "Couldn't locate Jalview XML file : " + file;
2180       System.err.println("Exception whilst loading jalview XML file : "
2181               + ex + "\n");
2182     } catch (java.net.UnknownHostException ex)
2183     {
2184       ex.printStackTrace();
2185       errorMessage = "Couldn't locate Jalview XML file : " + file;
2186       System.err.println("Exception whilst loading jalview XML file : "
2187               + ex + "\n");
2188     } catch (Exception ex)
2189     {
2190       System.err.println("Parsing as Jalview Version 2 file failed.");
2191       ex.printStackTrace(System.err);
2192       if (attemptversion1parse)
2193       {
2194         // Is Version 1 Jar file?
2195         try
2196         {
2197           af = new Jalview2XML_V1(raiseGUI).LoadJalviewAlign(jprovider);
2198         } catch (Exception ex2)
2199         {
2200           System.err.println("Exception whilst loading as jalviewXMLV1:");
2201           ex2.printStackTrace();
2202           af = null;
2203         }
2204       }
2205       if (Desktop.instance != null)
2206       {
2207         Desktop.instance.stopLoading();
2208       }
2209       if (af != null)
2210       {
2211         System.out.println("Successfully loaded archive file");
2212         return af;
2213       }
2214       ex.printStackTrace();
2215
2216       System.err.println("Exception whilst loading jalview XML file : "
2217               + ex + "\n");
2218     } catch (OutOfMemoryError e)
2219     {
2220       // Don't use the OOM Window here
2221       errorMessage = "Out of memory loading jalview XML file";
2222       System.err.println("Out of memory whilst loading jalview XML file");
2223       e.printStackTrace();
2224     }
2225
2226     if (Desktop.instance != null)
2227     {
2228       Desktop.instance.stopLoading();
2229     }
2230
2231     /*
2232      * Regather multiple views (with the same sequence set id) to the frame (if
2233      * any) that is flagged as the one to gather to, i.e. convert them to tabbed
2234      * views instead of separate frames. Note this doesn't restore a state where
2235      * some expanded views in turn have tabbed views - the last "first tab" read
2236      * in will play the role of gatherer for all.
2237      */
2238     for (AlignFrame fr : gatherToThisFrame.values())
2239     {
2240       Desktop.instance.gatherViews(fr);
2241     }
2242
2243     restoreSplitFrames();
2244
2245     if (errorMessage != null)
2246     {
2247       reportErrors();
2248     }
2249     return af;
2250   }
2251
2252   /**
2253    * Try to reconstruct and display SplitFrame windows, where each contains
2254    * complementary dna and protein alignments. Done by pairing up AlignFrame
2255    * objects (created earlier) which have complementary viewport ids associated.
2256    */
2257   protected void restoreSplitFrames()
2258   {
2259     List<SplitFrame> gatherTo = new ArrayList<SplitFrame>();
2260     List<AlignFrame> addedToSplitFrames = new ArrayList<AlignFrame>();
2261     Map<String, AlignFrame> dna = new HashMap<String, AlignFrame>();
2262
2263     /*
2264      * Identify the DNA alignments
2265      */
2266     for (Entry<Viewport, AlignFrame> candidate : splitFrameCandidates
2267             .entrySet())
2268     {
2269       AlignFrame af = candidate.getValue();
2270       if (af.getViewport().getAlignment().isNucleotide())
2271       {
2272         dna.put(candidate.getKey().getId(), af);
2273       }
2274     }
2275
2276     /*
2277      * Try to match up the protein complements
2278      */
2279     for (Entry<Viewport, AlignFrame> candidate : splitFrameCandidates
2280             .entrySet())
2281     {
2282       AlignFrame af = candidate.getValue();
2283       if (!af.getViewport().getAlignment().isNucleotide())
2284       {
2285         String complementId = candidate.getKey().getComplementId();
2286         // only non-null complements should be in the Map
2287         if (complementId != null && dna.containsKey(complementId))
2288         {
2289           final AlignFrame dnaFrame = dna.get(complementId);
2290           SplitFrame sf = createSplitFrame(dnaFrame, af);
2291           addedToSplitFrames.add(dnaFrame);
2292           addedToSplitFrames.add(af);
2293           if (af.viewport.isGatherViewsHere())
2294           {
2295             gatherTo.add(sf);
2296           }
2297         }
2298       }
2299     }
2300
2301     /*
2302      * Open any that we failed to pair up (which shouldn't happen!) as
2303      * standalone AlignFrame's.
2304      */
2305     for (Entry<Viewport, AlignFrame> candidate : splitFrameCandidates
2306             .entrySet())
2307     {
2308       AlignFrame af = candidate.getValue();
2309       if (!addedToSplitFrames.contains(af)) {
2310         Viewport view = candidate.getKey();
2311         Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(),
2312                 view.getHeight());
2313         System.err.println("Failed to restore view " + view.getTitle()
2314                 + " to split frame");
2315       }
2316     }
2317
2318     /*
2319      * Gather back into tabbed views as flagged.
2320      */
2321     for (SplitFrame sf : gatherTo)
2322     {
2323       Desktop.instance.gatherViews(sf);
2324     }
2325
2326     splitFrameCandidates.clear();
2327   }
2328
2329   /**
2330    * Construct and display one SplitFrame holding DNA and protein alignments.
2331    * 
2332    * @param dnaFrame
2333    * @param proteinFrame
2334    * @return
2335    */
2336   protected SplitFrame createSplitFrame(AlignFrame dnaFrame,
2337           AlignFrame proteinFrame)
2338   {
2339     SplitFrame splitFrame = new SplitFrame(dnaFrame, proteinFrame);
2340     String title = MessageManager.getString("label.linked_view_title");
2341     int width = (int) dnaFrame.getBounds().getWidth();
2342     int height = (int) (dnaFrame.getBounds().getHeight()
2343             + proteinFrame.getBounds().getHeight() + 50);
2344     Desktop.addInternalFrame(splitFrame, title, width, height);
2345
2346     /*
2347      * And compute cDNA consensus (couldn't do earlier with consensus as
2348      * mappings were not yet present)
2349      */
2350     proteinFrame.viewport.alignmentChanged(proteinFrame.alignPanel);
2351
2352     return splitFrame;
2353   }
2354
2355   /**
2356    * check errorMessage for a valid error message and raise an error box in the
2357    * GUI or write the current errorMessage to stderr and then clear the error
2358    * state.
2359    */
2360   protected void reportErrors()
2361   {
2362     reportErrors(false);
2363   }
2364
2365   protected void reportErrors(final boolean saving)
2366   {
2367     if (errorMessage != null)
2368     {
2369       final String finalErrorMessage = errorMessage;
2370       if (raiseGUI)
2371       {
2372         javax.swing.SwingUtilities.invokeLater(new Runnable()
2373         {
2374           @Override
2375           public void run()
2376           {
2377             JOptionPane.showInternalMessageDialog(Desktop.desktop,
2378                     finalErrorMessage, "Error "
2379                             + (saving ? "saving" : "loading")
2380                             + " Jalview file", JOptionPane.WARNING_MESSAGE);
2381           }
2382         });
2383       }
2384       else
2385       {
2386         System.err.println("Problem loading Jalview file: " + errorMessage);
2387       }
2388     }
2389     errorMessage = null;
2390   }
2391
2392   Map<String, String> alreadyLoadedPDB = new HashMap<String, String>();
2393
2394   /**
2395    * when set, local views will be updated from view stored in JalviewXML
2396    * Currently (28th Sep 2008) things will go horribly wrong in vamsas document
2397    * sync if this is set to true.
2398    */
2399   private final boolean updateLocalViews = false;
2400
2401   /**
2402    * Returns the path to a temporary file holding the PDB file for the given PDB
2403    * id. The first time of asking, searches for a file of that name in the
2404    * Jalview project jar, and copies it to a new temporary file. Any repeat
2405    * requests just return the path to the file previously created.
2406    * 
2407    * @param jprovider
2408    * @param pdbId
2409    * @return
2410    */
2411   String loadPDBFile(jarInputStreamProvider jprovider, String pdbId)
2412   {
2413     if (alreadyLoadedPDB.containsKey(pdbId))
2414     {
2415       return alreadyLoadedPDB.get(pdbId).toString();
2416     }
2417
2418     String tempFile = copyJarEntry(jprovider, pdbId, "jalview_pdb");
2419     if (tempFile != null)
2420     {
2421       alreadyLoadedPDB.put(pdbId, tempFile);
2422     }
2423     return tempFile;
2424   }
2425
2426   /**
2427    * Copies the jar entry of given name to a new temporary file and returns the
2428    * path to the file, or null if the entry is not found.
2429    * 
2430    * @param jprovider
2431    * @param jarEntryName
2432    * @param prefix
2433    *          a prefix for the temporary file name, must be at least three
2434    *          characters long
2435    * @return
2436    */
2437   protected String copyJarEntry(jarInputStreamProvider jprovider,
2438           String jarEntryName, String prefix)
2439   {
2440     BufferedReader in = null;
2441     PrintWriter out = null;
2442
2443     try
2444     {
2445       JarInputStream jin = jprovider.getJarInputStream();
2446       /*
2447        * if (jprovider.startsWith("http://")) { jin = new JarInputStream(new
2448        * URL(jprovider).openStream()); } else { jin = new JarInputStream(new
2449        * FileInputStream(jprovider)); }
2450        */
2451
2452       JarEntry entry = null;
2453       do
2454       {
2455         entry = jin.getNextJarEntry();
2456       } while (entry != null && !entry.getName().equals(jarEntryName));
2457       if (entry != null)
2458       {
2459         in = new BufferedReader(new InputStreamReader(jin, UTF_8));
2460         File outFile = File.createTempFile(prefix, ".tmp");
2461         outFile.deleteOnExit();
2462         out = new PrintWriter(new FileOutputStream(outFile));
2463         String data;
2464
2465         while ((data = in.readLine()) != null)
2466         {
2467           out.println(data);
2468         }
2469         out.flush();
2470         String t = outFile.getAbsolutePath();
2471         return t;
2472       }
2473       else
2474       {
2475         warn("Couldn't find entry in Jalview Jar for " + jarEntryName);
2476       }
2477     } catch (Exception ex)
2478     {
2479       ex.printStackTrace();
2480     } finally
2481     {
2482       if (in != null)
2483       {
2484         try
2485         {
2486           in.close();
2487         } catch (IOException e)
2488         {
2489           // ignore
2490         }
2491       }
2492       if (out != null)
2493       {
2494         out.close();
2495       }
2496     }
2497
2498     return null;
2499   }
2500
2501   private class JvAnnotRow
2502   {
2503     public JvAnnotRow(int i, AlignmentAnnotation jaa)
2504     {
2505       order = i;
2506       template = jaa;
2507     }
2508
2509     /**
2510      * persisted version of annotation row from which to take vis properties
2511      */
2512     public jalview.datamodel.AlignmentAnnotation template;
2513
2514     /**
2515      * original position of the annotation row in the alignment
2516      */
2517     public int order;
2518   }
2519
2520   /**
2521    * Load alignment frame from jalview XML DOM object
2522    * 
2523    * @param object
2524    *          DOM
2525    * @param file
2526    *          filename source string
2527    * @param loadTreesAndStructures
2528    *          when false only create Viewport
2529    * @param jprovider
2530    *          data source provider
2531    * @return alignment frame created from view stored in DOM
2532    */
2533   AlignFrame loadFromObject(JalviewModel object, String file,
2534           boolean loadTreesAndStructures, jarInputStreamProvider jprovider)
2535   {
2536     SequenceSet vamsasSet = object.getVamsasModel().getSequenceSet(0);
2537     Sequence[] vamsasSeq = vamsasSet.getSequence();
2538
2539     JalviewModelSequence jms = object.getJalviewModelSequence();
2540
2541     Viewport view = (jms.getViewportCount() > 0) ? jms.getViewport(0)
2542             : null;
2543
2544     // ////////////////////////////////
2545     // LOAD SEQUENCES
2546
2547     List<SequenceI> hiddenSeqs = null;
2548     jalview.datamodel.Sequence jseq;
2549
2550     List<SequenceI> tmpseqs = new ArrayList<SequenceI>();
2551
2552     boolean multipleView = false;
2553
2554     JSeq[] jseqs = object.getJalviewModelSequence().getJSeq();
2555     int vi = 0; // counter in vamsasSeq array
2556     for (int i = 0; i < jseqs.length; i++)
2557     {
2558       String seqId = jseqs[i].getId();
2559
2560       if (seqRefIds.get(seqId) != null)
2561       {
2562         tmpseqs.add(seqRefIds.get(seqId));
2563         multipleView = true;
2564       }
2565       else
2566       {
2567         jseq = new jalview.datamodel.Sequence(vamsasSeq[vi].getName(),
2568                 vamsasSeq[vi].getSequence());
2569         jseq.setDescription(vamsasSeq[vi].getDescription());
2570         jseq.setStart(jseqs[i].getStart());
2571         jseq.setEnd(jseqs[i].getEnd());
2572         jseq.setVamsasId(uniqueSetSuffix + seqId);
2573         seqRefIds.put(vamsasSeq[vi].getId(), jseq);
2574         tmpseqs.add(jseq);
2575         vi++;
2576       }
2577
2578       if (jseqs[i].getHidden())
2579       {
2580         if (hiddenSeqs == null)
2581         {
2582           hiddenSeqs = new ArrayList<SequenceI>();
2583         }
2584
2585         hiddenSeqs.add(seqRefIds.get(seqId));
2586       }
2587
2588     }
2589
2590     // /
2591     // Create the alignment object from the sequence set
2592     // ///////////////////////////////
2593     SequenceI[] orderedSeqs = tmpseqs
2594             .toArray(new SequenceI[tmpseqs.size()]);
2595
2596     Alignment al = new Alignment(orderedSeqs);
2597
2598     // / Add the alignment properties
2599     for (int i = 0; i < vamsasSet.getSequenceSetPropertiesCount(); i++)
2600     {
2601       SequenceSetProperties ssp = vamsasSet.getSequenceSetProperties(i);
2602       al.setProperty(ssp.getKey(), ssp.getValue());
2603     }
2604
2605     // /
2606     // SequenceFeatures are added to the DatasetSequence,
2607     // so we must create or recover the dataset before loading features
2608     // ///////////////////////////////
2609     if (vamsasSet.getDatasetId() == null || vamsasSet.getDatasetId() == "")
2610     {
2611       // older jalview projects do not have a dataset id.
2612       al.setDataset(null);
2613     }
2614     else
2615     {
2616       // recover dataset - passing on flag indicating if this a 'viewless'
2617       // sequence set (a.k.a. a stored dataset for the project)
2618       recoverDatasetFor(vamsasSet, al, object.getJalviewModelSequence()
2619               .getViewportCount() == 0);
2620     }
2621     // ///////////////////////////////
2622
2623     Hashtable pdbloaded = new Hashtable(); // TODO nothing writes to this??
2624     if (!multipleView)
2625     {
2626       // load sequence features, database references and any associated PDB
2627       // structures for the alignment
2628       for (int i = 0; i < vamsasSeq.length; i++)
2629       {
2630         if (jseqs[i].getFeaturesCount() > 0)
2631         {
2632           Features[] features = jseqs[i].getFeatures();
2633           for (int f = 0; f < features.length; f++)
2634           {
2635             jalview.datamodel.SequenceFeature sf = new jalview.datamodel.SequenceFeature(
2636                     features[f].getType(), features[f].getDescription(),
2637                     features[f].getStatus(), features[f].getBegin(),
2638                     features[f].getEnd(), features[f].getFeatureGroup());
2639
2640             sf.setScore(features[f].getScore());
2641             for (int od = 0; od < features[f].getOtherDataCount(); od++)
2642             {
2643               OtherData keyValue = features[f].getOtherData(od);
2644               if (keyValue.getKey().startsWith("LINK"))
2645               {
2646                 sf.addLink(keyValue.getValue());
2647               }
2648               else
2649               {
2650                 sf.setValue(keyValue.getKey(), keyValue.getValue());
2651               }
2652
2653             }
2654
2655             al.getSequenceAt(i).getDatasetSequence().addSequenceFeature(sf);
2656           }
2657         }
2658         if (vamsasSeq[i].getDBRefCount() > 0)
2659         {
2660           addDBRefs(al.getSequenceAt(i).getDatasetSequence(), vamsasSeq[i]);
2661         }
2662         if (jseqs[i].getPdbidsCount() > 0)
2663         {
2664           Pdbids[] ids = jseqs[i].getPdbids();
2665           for (int p = 0; p < ids.length; p++)
2666           {
2667             jalview.datamodel.PDBEntry entry = new jalview.datamodel.PDBEntry();
2668             entry.setId(ids[p].getId());
2669             if (ids[p].getType() != null)
2670             {
2671               if (ids[p].getType().equalsIgnoreCase("PDB"))
2672               {
2673                 entry.setType(PDBEntry.Type.PDB);
2674               }
2675               else
2676               {
2677                 entry.setType(PDBEntry.Type.FILE);
2678               }
2679             }
2680             if (ids[p].getFile() != null)
2681             {
2682               if (!pdbloaded.containsKey(ids[p].getFile()))
2683               {
2684                 entry.setFile(loadPDBFile(jprovider, ids[p].getId()));
2685               }
2686               else
2687               {
2688                 entry.setFile(pdbloaded.get(ids[p].getId()).toString());
2689               }
2690             }
2691             StructureSelectionManager.getStructureSelectionManager(
2692                     Desktop.instance).registerPDBEntry(entry);
2693             al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
2694           }
2695         }
2696       }
2697     } // end !multipleview
2698
2699     // ///////////////////////////////
2700     // LOAD SEQUENCE MAPPINGS
2701
2702     if (vamsasSet.getAlcodonFrameCount() > 0)
2703     {
2704       // TODO Potentially this should only be done once for all views of an
2705       // alignment
2706       AlcodonFrame[] alc = vamsasSet.getAlcodonFrame();
2707       for (int i = 0; i < alc.length; i++)
2708       {
2709         AlignedCodonFrame cf = new AlignedCodonFrame();
2710         if (alc[i].getAlcodMapCount() > 0)
2711         {
2712           AlcodMap[] maps = alc[i].getAlcodMap();
2713           for (int m = 0; m < maps.length; m++)
2714           {
2715             SequenceI dnaseq = seqRefIds.get(maps[m].getDnasq());
2716             // Load Mapping
2717             jalview.datamodel.Mapping mapping = null;
2718             // attach to dna sequence reference.
2719             if (maps[m].getMapping() != null)
2720             {
2721               mapping = addMapping(maps[m].getMapping());
2722             }
2723             if (dnaseq != null)
2724             {
2725               cf.addMap(dnaseq, mapping.getTo(), mapping.getMap());
2726             }
2727             else
2728             {
2729               // defer to later
2730               frefedSequence.add(new Object[]
2731               { maps[m].getDnasq(), cf, mapping });
2732             }
2733           }
2734         }
2735         al.addCodonFrame(cf);
2736       }
2737     }
2738
2739     // ////////////////////////////////
2740     // LOAD ANNOTATIONS
2741     List<JvAnnotRow> autoAlan = new ArrayList<JvAnnotRow>();
2742     /**
2743      * store any annotations which forward reference a group's ID
2744      */
2745     Hashtable<String, ArrayList<jalview.datamodel.AlignmentAnnotation>> groupAnnotRefs = new Hashtable<String, ArrayList<jalview.datamodel.AlignmentAnnotation>>();
2746
2747     if (vamsasSet.getAnnotationCount() > 0)
2748     {
2749       Annotation[] an = vamsasSet.getAnnotation();
2750
2751       for (int i = 0; i < an.length; i++)
2752       {
2753         /**
2754          * test if annotation is automatically calculated for this view only
2755          */
2756         boolean autoForView = false;
2757         if (an[i].getLabel().equals("Quality")
2758                 || an[i].getLabel().equals("Conservation")
2759                 || an[i].getLabel().equals("Consensus"))
2760         {
2761           // Kludge for pre 2.5 projects which lacked the autocalculated flag
2762           autoForView = true;
2763           if (!an[i].hasAutoCalculated())
2764           {
2765             an[i].setAutoCalculated(true);
2766           }
2767         }
2768         if (autoForView
2769                 || (an[i].hasAutoCalculated() && an[i].isAutoCalculated()))
2770         {
2771           // remove ID - we don't recover annotation from other views for
2772           // view-specific annotation
2773           an[i].setId(null);
2774         }
2775
2776         // set visiblity for other annotation in this view
2777         if (an[i].getId() != null
2778                 && annotationIds.containsKey(an[i].getId()))
2779         {
2780           AlignmentAnnotation jda = annotationIds.get(an[i].getId());
2781           // in principle Visible should always be true for annotation displayed
2782           // in multiple views
2783           if (an[i].hasVisible())
2784           {
2785             jda.visible = an[i].getVisible();
2786           }
2787
2788           al.addAnnotation(jda);
2789
2790           continue;
2791         }
2792         // Construct new annotation from model.
2793         AnnotationElement[] ae = an[i].getAnnotationElement();
2794         jalview.datamodel.Annotation[] anot = null;
2795         java.awt.Color firstColour = null;
2796         int anpos;
2797         if (!an[i].getScoreOnly())
2798         {
2799           anot = new jalview.datamodel.Annotation[al.getWidth()];
2800           for (int aa = 0; aa < ae.length && aa < anot.length; aa++)
2801           {
2802             anpos = ae[aa].getPosition();
2803
2804             if (anpos >= anot.length)
2805             {
2806               continue;
2807             }
2808
2809             anot[anpos] = new jalview.datamodel.Annotation(
2810
2811             ae[aa].getDisplayCharacter(), ae[aa].getDescription(),
2812                     (ae[aa].getSecondaryStructure() == null || ae[aa]
2813                             .getSecondaryStructure().length() == 0) ? ' '
2814                             : ae[aa].getSecondaryStructure().charAt(0),
2815                     ae[aa].getValue()
2816
2817             );
2818             // JBPNote: Consider verifying dataflow for IO of secondary
2819             // structure annotation read from Stockholm files
2820             // this was added to try to ensure that
2821             // if (anot[ae[aa].getPosition()].secondaryStructure>' ')
2822             // {
2823             // anot[ae[aa].getPosition()].displayCharacter = "";
2824             // }
2825             anot[anpos].colour = new java.awt.Color(ae[aa].getColour());
2826             if (firstColour == null)
2827             {
2828               firstColour = anot[anpos].colour;
2829             }
2830           }
2831         }
2832         jalview.datamodel.AlignmentAnnotation jaa = null;
2833
2834         if (an[i].getGraph())
2835         {
2836           float llim = 0, hlim = 0;
2837           // if (autoForView || an[i].isAutoCalculated()) {
2838           // hlim=11f;
2839           // }
2840           jaa = new jalview.datamodel.AlignmentAnnotation(an[i].getLabel(),
2841                   an[i].getDescription(), anot, llim, hlim,
2842                   an[i].getGraphType());
2843
2844           jaa.graphGroup = an[i].getGraphGroup();
2845           jaa._linecolour = firstColour;
2846           if (an[i].getThresholdLine() != null)
2847           {
2848             jaa.setThreshold(new jalview.datamodel.GraphLine(an[i]
2849                     .getThresholdLine().getValue(), an[i]
2850                     .getThresholdLine().getLabel(), new java.awt.Color(
2851                     an[i].getThresholdLine().getColour())));
2852
2853           }
2854           if (autoForView || an[i].isAutoCalculated())
2855           {
2856             // Hardwire the symbol display line to ensure that labels for
2857             // histograms are displayed
2858             jaa.hasText = true;
2859           }
2860         }
2861         else
2862         {
2863           jaa = new jalview.datamodel.AlignmentAnnotation(an[i].getLabel(),
2864                   an[i].getDescription(), anot);
2865           jaa._linecolour = firstColour;
2866         }
2867         // register new annotation
2868         if (an[i].getId() != null)
2869         {
2870           annotationIds.put(an[i].getId(), jaa);
2871           jaa.annotationId = an[i].getId();
2872         }
2873         // recover sequence association
2874         String sequenceRef = an[i].getSequenceRef();
2875         if (sequenceRef != null)
2876         {
2877           // from 2.9 sequenceRef is to sequence id (JAL-1781)
2878           SequenceI sequence = seqRefIds.get(sequenceRef);
2879           if (sequence == null)
2880           {
2881             // in pre-2.9 projects sequence ref is to sequence name
2882             sequence = al.findName(sequenceRef);
2883           }
2884           if (sequence != null)
2885           {
2886             jaa.createSequenceMapping(sequence, 1, true);
2887             sequence.addAlignmentAnnotation(jaa);
2888           }
2889         }
2890         // and make a note of any group association
2891         if (an[i].getGroupRef() != null && an[i].getGroupRef().length() > 0)
2892         {
2893           ArrayList<jalview.datamodel.AlignmentAnnotation> aal = groupAnnotRefs
2894                   .get(an[i].getGroupRef());
2895           if (aal == null)
2896           {
2897             aal = new ArrayList<jalview.datamodel.AlignmentAnnotation>();
2898             groupAnnotRefs.put(an[i].getGroupRef(), aal);
2899           }
2900           aal.add(jaa);
2901         }
2902
2903         if (an[i].hasScore())
2904         {
2905           jaa.setScore(an[i].getScore());
2906         }
2907         if (an[i].hasVisible())
2908         {
2909           jaa.visible = an[i].getVisible();
2910         }
2911
2912         if (an[i].hasCentreColLabels())
2913         {
2914           jaa.centreColLabels = an[i].getCentreColLabels();
2915         }
2916
2917         if (an[i].hasScaleColLabels())
2918         {
2919           jaa.scaleColLabel = an[i].getScaleColLabels();
2920         }
2921         if (an[i].hasAutoCalculated() && an[i].isAutoCalculated())
2922         {
2923           // newer files have an 'autoCalculated' flag and store calculation
2924           // state in viewport properties
2925           jaa.autoCalculated = true; // means annotation will be marked for
2926           // update at end of load.
2927         }
2928         if (an[i].hasGraphHeight())
2929         {
2930           jaa.graphHeight = an[i].getGraphHeight();
2931         }
2932         if (an[i].hasBelowAlignment())
2933         {
2934           jaa.belowAlignment = an[i].isBelowAlignment();
2935         }
2936         jaa.setCalcId(an[i].getCalcId());
2937         if (an[i].getPropertyCount() > 0)
2938         {
2939           for (jalview.schemabinding.version2.Property prop : an[i]
2940                   .getProperty())
2941           {
2942             jaa.setProperty(prop.getName(), prop.getValue());
2943           }
2944         }
2945         if (jaa.autoCalculated)
2946         {
2947           autoAlan.add(new JvAnnotRow(i, jaa));
2948         }
2949         else
2950         // if (!autoForView)
2951         {
2952           // add autocalculated group annotation and any user created annotation
2953           // for the view
2954           al.addAnnotation(jaa);
2955         }
2956       }
2957     }
2958     // ///////////////////////
2959     // LOAD GROUPS
2960     // Create alignment markup and styles for this view
2961     if (jms.getJGroupCount() > 0)
2962     {
2963       JGroup[] groups = jms.getJGroup();
2964       boolean addAnnotSchemeGroup = false;
2965       for (int i = 0; i < groups.length; i++)
2966       {
2967         ColourSchemeI cs = null;
2968
2969         if (groups[i].getColour() != null)
2970         {
2971           if (groups[i].getColour().startsWith("ucs"))
2972           {
2973             cs = getUserColourScheme(jms, groups[i].getColour());
2974           }
2975           else if (groups[i].getColour().equals("AnnotationColourGradient")
2976                   && groups[i].getAnnotationColours() != null)
2977           {
2978             addAnnotSchemeGroup = true;
2979             cs = null;
2980           }
2981           else
2982           {
2983             cs = ColourSchemeProperty.getColour(al, groups[i].getColour());
2984           }
2985
2986           if (cs != null)
2987           {
2988             cs.setThreshold(groups[i].getPidThreshold(), true);
2989           }
2990         }
2991
2992         Vector seqs = new Vector();
2993
2994         for (int s = 0; s < groups[i].getSeqCount(); s++)
2995         {
2996           String seqId = groups[i].getSeq(s) + "";
2997           jalview.datamodel.SequenceI ts = seqRefIds.get(seqId);
2998
2999           if (ts != null)
3000           {
3001             seqs.addElement(ts);
3002           }
3003         }
3004
3005         if (seqs.size() < 1)
3006         {
3007           continue;
3008         }
3009
3010         jalview.datamodel.SequenceGroup sg = new jalview.datamodel.SequenceGroup(
3011                 seqs, groups[i].getName(), cs, groups[i].getDisplayBoxes(),
3012                 groups[i].getDisplayText(), groups[i].getColourText(),
3013                 groups[i].getStart(), groups[i].getEnd());
3014
3015         sg.setOutlineColour(new java.awt.Color(groups[i].getOutlineColour()));
3016
3017         sg.textColour = new java.awt.Color(groups[i].getTextCol1());
3018         sg.textColour2 = new java.awt.Color(groups[i].getTextCol2());
3019         sg.setShowNonconserved(groups[i].hasShowUnconserved() ? groups[i]
3020                 .isShowUnconserved() : false);
3021         sg.thresholdTextColour = groups[i].getTextColThreshold();
3022         if (groups[i].hasShowConsensusHistogram())
3023         {
3024           sg.setShowConsensusHistogram(groups[i].isShowConsensusHistogram());
3025         }
3026         ;
3027         if (groups[i].hasShowSequenceLogo())
3028         {
3029           sg.setshowSequenceLogo(groups[i].isShowSequenceLogo());
3030         }
3031         if (groups[i].hasNormaliseSequenceLogo())
3032         {
3033           sg.setNormaliseSequenceLogo(groups[i].isNormaliseSequenceLogo());
3034         }
3035         if (groups[i].hasIgnoreGapsinConsensus())
3036         {
3037           sg.setIgnoreGapsConsensus(groups[i].getIgnoreGapsinConsensus());
3038         }
3039         if (groups[i].getConsThreshold() != 0)
3040         {
3041           jalview.analysis.Conservation c = new jalview.analysis.Conservation(
3042                   "All", ResidueProperties.propHash, 3,
3043                   sg.getSequences(null), 0, sg.getWidth() - 1);
3044           c.calculate();
3045           c.verdict(false, 25);
3046           sg.cs.setConservation(c);
3047         }
3048
3049         if (groups[i].getId() != null && groupAnnotRefs.size() > 0)
3050         {
3051           // re-instate unique group/annotation row reference
3052           ArrayList<jalview.datamodel.AlignmentAnnotation> jaal = groupAnnotRefs
3053                   .get(groups[i].getId());
3054           if (jaal != null)
3055           {
3056             for (jalview.datamodel.AlignmentAnnotation jaa : jaal)
3057             {
3058               jaa.groupRef = sg;
3059               if (jaa.autoCalculated)
3060               {
3061                 // match up and try to set group autocalc alignment row for this
3062                 // annotation
3063                 if (jaa.label.startsWith("Consensus for "))
3064                 {
3065                   sg.setConsensus(jaa);
3066                 }
3067                 // match up and try to set group autocalc alignment row for this
3068                 // annotation
3069                 if (jaa.label.startsWith("Conservation for "))
3070                 {
3071                   sg.setConservationRow(jaa);
3072                 }
3073               }
3074             }
3075           }
3076         }
3077         al.addGroup(sg);
3078         if (addAnnotSchemeGroup)
3079         {
3080           // reconstruct the annotation colourscheme
3081           sg.cs = constructAnnotationColour(
3082                   groups[i].getAnnotationColours(), null, al, jms, false);
3083         }
3084       }
3085     }
3086     if (view == null)
3087     {
3088       // only dataset in this model, so just return.
3089       return null;
3090     }
3091     // ///////////////////////////////
3092     // LOAD VIEWPORT
3093
3094     // If we just load in the same jar file again, the sequenceSetId
3095     // will be the same, and we end up with multiple references
3096     // to the same sequenceSet. We must modify this id on load
3097     // so that each load of the file gives a unique id
3098     String uniqueSeqSetId = view.getSequenceSetId() + uniqueSetSuffix;
3099     String viewId = (view.getId() == null ? null : view.getId()
3100             + uniqueSetSuffix);
3101     AlignFrame af = null;
3102     AlignViewport av = null;
3103     // now check to see if we really need to create a new viewport.
3104     if (multipleView && viewportsAdded.size() == 0)
3105     {
3106       // We recovered an alignment for which a viewport already exists.
3107       // TODO: fix up any settings necessary for overlaying stored state onto
3108       // state recovered from another document. (may not be necessary).
3109       // we may need a binding from a viewport in memory to one recovered from
3110       // XML.
3111       // and then recover its containing af to allow the settings to be applied.
3112       // TODO: fix for vamsas demo
3113       System.err
3114               .println("About to recover a viewport for existing alignment: Sequence set ID is "
3115                       + uniqueSeqSetId);
3116       Object seqsetobj = retrieveExistingObj(uniqueSeqSetId);
3117       if (seqsetobj != null)
3118       {
3119         if (seqsetobj instanceof String)
3120         {
3121           uniqueSeqSetId = (String) seqsetobj;
3122           System.err
3123                   .println("Recovered extant sequence set ID mapping for ID : New Sequence set ID is "
3124                           + uniqueSeqSetId);
3125         }
3126         else
3127         {
3128           System.err
3129                   .println("Warning : Collision between sequence set ID string and existing jalview object mapping.");
3130         }
3131
3132       }
3133     }
3134     /**
3135      * indicate that annotation colours are applied across all groups (pre
3136      * Jalview 2.8.1 behaviour)
3137      */
3138     boolean doGroupAnnColour = isVersionStringLaterThan("2.8.1",
3139             object.getVersion());
3140
3141     AlignmentPanel ap = null;
3142     boolean isnewview = true;
3143     if (viewId != null)
3144     {
3145       // Check to see if this alignment already has a view id == viewId
3146       jalview.gui.AlignmentPanel views[] = Desktop
3147               .getAlignmentPanels(uniqueSeqSetId);
3148       if (views != null && views.length > 0)
3149       {
3150         for (int v = 0; v < views.length; v++)
3151         {
3152           if (views[v].av.getViewId().equalsIgnoreCase(viewId))
3153           {
3154             // recover the existing alignpanel, alignframe, viewport
3155             af = views[v].alignFrame;
3156             av = views[v].av;
3157             ap = views[v];
3158             // TODO: could even skip resetting view settings if we don't want to
3159             // change the local settings from other jalview processes
3160             isnewview = false;
3161           }
3162         }
3163       }
3164     }
3165
3166     if (isnewview)
3167     {
3168       af = loadViewport(file, jseqs, hiddenSeqs, al, jms, view,
3169               uniqueSeqSetId, viewId, autoAlan);
3170       av = af.viewport;
3171       ap = af.alignPanel;
3172     }
3173     // LOAD TREES
3174     // /////////////////////////////////////
3175     if (loadTreesAndStructures && jms.getTreeCount() > 0)
3176     {
3177       try
3178       {
3179         for (int t = 0; t < jms.getTreeCount(); t++)
3180         {
3181
3182           Tree tree = jms.getTree(t);
3183
3184           TreePanel tp = (TreePanel) retrieveExistingObj(tree.getId());
3185           if (tp == null)
3186           {
3187             tp = af.ShowNewickTree(
3188                     new jalview.io.NewickFile(tree.getNewick()),
3189                     tree.getTitle(), tree.getWidth(), tree.getHeight(),
3190                     tree.getXpos(), tree.getYpos());
3191             if (tree.getId() != null)
3192             {
3193               // perhaps bind the tree id to something ?
3194             }
3195           }
3196           else
3197           {
3198             // update local tree attributes ?
3199             // TODO: should check if tp has been manipulated by user - if so its
3200             // settings shouldn't be modified
3201             tp.setTitle(tree.getTitle());
3202             tp.setBounds(new Rectangle(tree.getXpos(), tree.getYpos(), tree
3203                     .getWidth(), tree.getHeight()));
3204             tp.av = av; // af.viewport; // TODO: verify 'associate with all
3205             // views'
3206             // works still
3207             tp.treeCanvas.av = av; // af.viewport;
3208             tp.treeCanvas.ap = ap; // af.alignPanel;
3209
3210           }
3211           if (tp == null)
3212           {
3213             warn("There was a problem recovering stored Newick tree: \n"
3214                     + tree.getNewick());
3215             continue;
3216           }
3217
3218           tp.fitToWindow.setState(tree.getFitToWindow());
3219           tp.fitToWindow_actionPerformed(null);
3220
3221           if (tree.getFontName() != null)
3222           {
3223             tp.setTreeFont(new java.awt.Font(tree.getFontName(), tree
3224                     .getFontStyle(), tree.getFontSize()));
3225           }
3226           else
3227           {
3228             tp.setTreeFont(new java.awt.Font(view.getFontName(), view
3229                     .getFontStyle(), tree.getFontSize()));
3230           }
3231
3232           tp.showPlaceholders(tree.getMarkUnlinked());
3233           tp.showBootstrap(tree.getShowBootstrap());
3234           tp.showDistances(tree.getShowDistances());
3235
3236           tp.treeCanvas.threshold = tree.getThreshold();
3237
3238           if (tree.getCurrentTree())
3239           {
3240             af.viewport.setCurrentTree(tp.getTree());
3241           }
3242         }
3243
3244       } catch (Exception ex)
3245       {
3246         ex.printStackTrace();
3247       }
3248     }
3249
3250     // //LOAD STRUCTURES
3251     if (loadTreesAndStructures)
3252     {
3253       loadStructures(jprovider, jseqs, af, ap);
3254     }
3255     // and finally return.
3256     return af;
3257   }
3258
3259   /**
3260    * Load and link any saved structure viewers.
3261    * 
3262    * @param jprovider
3263    * @param jseqs
3264    * @param af
3265    * @param ap
3266    */
3267   protected void loadStructures(jarInputStreamProvider jprovider,
3268           JSeq[] jseqs, AlignFrame af, AlignmentPanel ap)
3269   {
3270     /*
3271      * Run through all PDB ids on the alignment, and collect mappings between
3272      * distinct view ids and all sequences referring to that view.
3273      */
3274     Map<String, StructureViewerModel> structureViewers = new LinkedHashMap<String, StructureViewerModel>();
3275
3276     for (int i = 0; i < jseqs.length; i++)
3277     {
3278       if (jseqs[i].getPdbidsCount() > 0)
3279       {
3280         Pdbids[] ids = jseqs[i].getPdbids();
3281         for (int p = 0; p < ids.length; p++)
3282         {
3283           final int structureStateCount = ids[p].getStructureStateCount();
3284           for (int s = 0; s < structureStateCount; s++)
3285           {
3286             // check to see if we haven't already created this structure view
3287             final StructureState structureState = ids[p]
3288                     .getStructureState(s);
3289             String sviewid = (structureState.getViewId() == null) ? null
3290                     : structureState.getViewId() + uniqueSetSuffix;
3291             jalview.datamodel.PDBEntry jpdb = new jalview.datamodel.PDBEntry();
3292             // Originally : ids[p].getFile()
3293             // : TODO: verify external PDB file recovery still works in normal
3294             // jalview project load
3295             jpdb.setFile(loadPDBFile(jprovider, ids[p].getId()));
3296             jpdb.setId(ids[p].getId());
3297
3298             int x = structureState.getXpos();
3299             int y = structureState.getYpos();
3300             int width = structureState.getWidth();
3301             int height = structureState.getHeight();
3302
3303             // Probably don't need to do this anymore...
3304             // Desktop.desktop.getComponentAt(x, y);
3305             // TODO: NOW: check that this recovers the PDB file correctly.
3306             String pdbFile = loadPDBFile(jprovider, ids[p].getId());
3307             jalview.datamodel.SequenceI seq = seqRefIds.get(jseqs[i]
3308                     .getId() + "");
3309             if (sviewid == null)
3310             {
3311               sviewid = "_jalview_pre2_4_" + x + "," + y + "," + width
3312                       + "," + height;
3313             }
3314             if (!structureViewers.containsKey(sviewid))
3315             {
3316               structureViewers.put(sviewid,
3317                       new StructureViewerModel(x, y, width, height, false,
3318                               false, true, structureState.getViewId(),
3319                               structureState.getType()));
3320               // Legacy pre-2.7 conversion JAL-823 :
3321               // do not assume any view has to be linked for colour by
3322               // sequence
3323             }
3324
3325             // assemble String[] { pdb files }, String[] { id for each
3326             // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
3327             // seqs_file 2}, boolean[] {
3328             // linkAlignPanel,superposeWithAlignpanel}} from hash
3329             StructureViewerModel jmoldat = structureViewers.get(sviewid);
3330             jmoldat.setAlignWithPanel(jmoldat.isAlignWithPanel()
3331                     | (structureState.hasAlignwithAlignPanel() ? structureState
3332                             .getAlignwithAlignPanel() : false));
3333
3334             /*
3335              * Default colour by linked panel to false if not specified (e.g.
3336              * for pre-2.7 projects)
3337              */
3338             boolean colourWithAlignPanel = jmoldat.isColourWithAlignPanel();
3339             colourWithAlignPanel |= (structureState
3340                     .hasColourwithAlignPanel() ? structureState
3341                     .getColourwithAlignPanel() : false);
3342             jmoldat.setColourWithAlignPanel(colourWithAlignPanel);
3343
3344             /*
3345              * Default colour by viewer to true if not specified (e.g. for
3346              * pre-2.7 projects)
3347              */
3348             boolean colourByViewer = jmoldat.isColourByViewer();
3349             colourByViewer &= structureState.hasColourByJmol() ? structureState
3350                     .getColourByJmol() : true;
3351             jmoldat.setColourByViewer(colourByViewer);
3352
3353             if (jmoldat.getStateData().length() < structureState
3354                     .getContent().length())
3355             {
3356               {
3357                 jmoldat.setStateData(structureState.getContent());
3358               }
3359             }
3360             if (ids[p].getFile() != null)
3361             {
3362               File mapkey = new File(ids[p].getFile());
3363               StructureData seqstrmaps = jmoldat.getFileData().get(mapkey);
3364               if (seqstrmaps == null)
3365               {
3366                 jmoldat.getFileData().put(
3367                         mapkey,
3368                         seqstrmaps = jmoldat.new StructureData(pdbFile,
3369                                 ids[p].getId()));
3370               }
3371               if (!seqstrmaps.getSeqList().contains(seq))
3372               {
3373                 seqstrmaps.getSeqList().add(seq);
3374                 // TODO and chains?
3375               }
3376             }
3377             else
3378             {
3379               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");
3380               warn(errorMessage);
3381             }
3382           }
3383         }
3384       }
3385     }
3386     // Instantiate the associated structure views
3387     for (Entry<String, StructureViewerModel> entry : structureViewers
3388             .entrySet())
3389     {
3390       try
3391       {
3392         createOrLinkStructureViewer(entry, af, ap, jprovider);
3393       } catch (Exception e)
3394       {
3395         System.err.println("Error loading structure viewer: "
3396                 + e.getMessage());
3397         // failed - try the next one
3398       }
3399     }
3400   }
3401
3402   /**
3403    * 
3404    * @param viewerData
3405    * @param af
3406    * @param ap
3407    * @param jprovider
3408    */
3409   protected void createOrLinkStructureViewer(
3410           Entry<String, StructureViewerModel> viewerData, AlignFrame af,
3411           AlignmentPanel ap, jarInputStreamProvider jprovider)
3412   {
3413     final StructureViewerModel stateData = viewerData.getValue();
3414
3415     /*
3416      * Search for any viewer windows already open from other alignment views
3417      * that exactly match the stored structure state
3418      */
3419     StructureViewerBase comp = findMatchingViewer(viewerData);
3420
3421     if (comp != null)
3422     {
3423       linkStructureViewer(ap, comp, stateData);
3424       return;
3425     }
3426
3427     /*
3428      * From 2.9: stateData.type contains JMOL or CHIMERA, data is in jar entry
3429      * "viewer_"+stateData.viewId
3430      */
3431     if (ViewerType.CHIMERA.toString().equals(stateData.getType()))
3432     {
3433       createChimeraViewer(viewerData, af, jprovider);
3434     }
3435     else
3436     {
3437       /*
3438        * else Jmol (if pre-2.9, stateData contains JMOL state string)
3439        */
3440       createJmolViewer(viewerData, af, jprovider);
3441     }
3442   }
3443
3444   /**
3445    * Create a new Chimera viewer.
3446    * 
3447    * @param data
3448    * @param af
3449    * @param jprovider
3450    */
3451   protected void createChimeraViewer(Entry<String, StructureViewerModel> viewerData,
3452           AlignFrame af,
3453           jarInputStreamProvider jprovider)
3454   {
3455     StructureViewerModel data = viewerData.getValue();
3456     String chimeraSessionFile =  data.getStateData();
3457
3458     /*
3459      * Copy Chimera session from jar entry "viewer_"+viewId to a temporary file
3460      * 
3461      * Note this is the 'saved' viewId as in the project file XML, _not_ the
3462      * 'uniquified' sviewid used to reconstruct the viewer here
3463      */
3464     chimeraSessionFile = copyJarEntry(jprovider,
3465             getViewerJarEntryName(data.getViewId()), "chimera");
3466
3467     Set<Entry<File, StructureData>> fileData = data.getFileData()
3468             .entrySet();
3469     List<PDBEntry> pdbs = new ArrayList<PDBEntry>();
3470     List<SequenceI[]> allseqs = new ArrayList<SequenceI[]>();
3471     for (Entry<File, StructureData> pdb : fileData)
3472     {
3473       String filePath = pdb.getValue().getFilePath();
3474       String pdbId = pdb.getValue().getPdbId();
3475       // pdbs.add(new PDBEntry(filePath, pdbId));
3476       pdbs.add(new PDBEntry(pdbId, null, PDBEntry.Type.PDB, filePath));
3477       final List<SequenceI> seqList = pdb.getValue().getSeqList();
3478       SequenceI[] seqs = seqList.toArray(new SequenceI[seqList.size()]);
3479       allseqs.add(seqs);
3480     }
3481
3482     boolean colourByChimera = data.isColourByViewer();
3483     boolean colourBySequence = data.isColourWithAlignPanel();
3484
3485     // TODO use StructureViewer as a factory here, see JAL-1761
3486     final PDBEntry[] pdbArray = pdbs.toArray(new PDBEntry[pdbs.size()]);
3487     final SequenceI[][] seqsArray = allseqs.toArray(new SequenceI[allseqs
3488             .size()][]);
3489     String newViewId = viewerData.getKey();
3490     ChimeraViewFrame cvf = new ChimeraViewFrame(chimeraSessionFile,
3491             af.alignPanel, pdbArray,
3492             seqsArray, colourByChimera, colourBySequence, newViewId);
3493     cvf.setSize(data.getWidth(), data.getHeight());
3494     cvf.setLocation(data.getX(), data.getY());
3495   }
3496
3497   /**
3498    * Create a new Jmol window. First parse the Jmol state to translate filenames
3499    * loaded into the view, and record the order in which files are shown in the
3500    * Jmol view, so we can add the sequence mappings in same order.
3501    * 
3502    * @param viewerData
3503    * @param af
3504    * @param jprovider
3505    */
3506   protected void createJmolViewer(
3507           final Entry<String, StructureViewerModel> viewerData,
3508           AlignFrame af, jarInputStreamProvider jprovider)
3509   {
3510     final StructureViewerModel svattrib = viewerData.getValue();
3511     String state = svattrib.getStateData();
3512
3513     /*
3514      * Pre-2.9: state element value is the Jmol state string
3515      * 
3516      * 2.9+: @type is "JMOL", state data is in a Jar file member named "viewer_"
3517      * + viewId
3518      */
3519     if (ViewerType.JMOL.toString().equals(svattrib.getType()))
3520     {
3521       state = readJarEntry(jprovider,
3522               getViewerJarEntryName(svattrib.getViewId()));
3523     }
3524
3525     List<String> pdbfilenames = new ArrayList<String>();
3526     List<SequenceI[]> seqmaps = new ArrayList<SequenceI[]>();
3527     List<String> pdbids = new ArrayList<String>();
3528     StringBuilder newFileLoc = new StringBuilder(64);
3529     int cp = 0, ncp, ecp;
3530     Map<File, StructureData> oldFiles = svattrib.getFileData();
3531     while ((ncp = state.indexOf("load ", cp)) > -1)
3532     {
3533       do
3534       {
3535         // look for next filename in load statement
3536         newFileLoc.append(state.substring(cp,
3537                 ncp = (state.indexOf("\"", ncp + 1) + 1)));
3538         String oldfilenam = state.substring(ncp,
3539                 ecp = state.indexOf("\"", ncp));
3540         // recover the new mapping data for this old filename
3541         // have to normalize filename - since Jmol and jalview do
3542         // filename
3543         // translation differently.
3544         StructureData filedat = oldFiles.get(new File(oldfilenam));
3545         newFileLoc.append(Platform.escapeString(filedat.getFilePath()));
3546         pdbfilenames.add(filedat.getFilePath());
3547         pdbids.add(filedat.getPdbId());
3548         seqmaps.add(filedat.getSeqList().toArray(new SequenceI[0]));
3549         newFileLoc.append("\"");
3550         cp = ecp + 1; // advance beyond last \" and set cursor so we can
3551                       // look for next file statement.
3552       } while ((ncp = state.indexOf("/*file*/", cp)) > -1);
3553     }
3554     if (cp > 0)
3555     {
3556       // just append rest of state
3557       newFileLoc.append(state.substring(cp));
3558     }
3559     else
3560     {
3561       System.err.print("Ignoring incomplete Jmol state for PDB ids: ");
3562       newFileLoc = new StringBuilder(state);
3563       newFileLoc.append("; load append ");
3564       for (File id : oldFiles.keySet())
3565       {
3566         // add this and any other pdb files that should be present in
3567         // the viewer
3568         StructureData filedat = oldFiles.get(id);
3569         newFileLoc.append(filedat.getFilePath());
3570         pdbfilenames.add(filedat.getFilePath());
3571         pdbids.add(filedat.getPdbId());
3572         seqmaps.add(filedat.getSeqList().toArray(new SequenceI[0]));
3573         newFileLoc.append(" \"");
3574         newFileLoc.append(filedat.getFilePath());
3575         newFileLoc.append("\"");
3576
3577       }
3578       newFileLoc.append(";");
3579     }
3580
3581     if (newFileLoc.length() > 0)
3582     {
3583       int histbug = newFileLoc.indexOf("history = ");
3584       histbug += 10;
3585       int diff = histbug == -1 ? -1 : newFileLoc.indexOf(";", histbug);
3586       String val = (diff == -1) ? null : newFileLoc
3587               .substring(histbug, diff);
3588       if (val != null && val.length() >= 4)
3589       {
3590         if (val.contains("e"))
3591         {
3592           if (val.trim().equals("true"))
3593           {
3594             val = "1";
3595           }
3596           else
3597           {
3598             val = "0";
3599           }
3600           newFileLoc.replace(histbug, diff, val);
3601         }
3602       }
3603
3604       final String[] pdbf = pdbfilenames.toArray(new String[pdbfilenames
3605               .size()]);
3606       final String[] id = pdbids.toArray(new String[pdbids.size()]);
3607       final SequenceI[][] sq = seqmaps
3608               .toArray(new SequenceI[seqmaps.size()][]);
3609       final String fileloc = newFileLoc.toString();
3610       final String sviewid = viewerData.getKey();
3611       final AlignFrame alf = af;
3612       final Rectangle rect = new Rectangle(svattrib.getX(),
3613               svattrib.getY(), svattrib.getWidth(), svattrib.getHeight());
3614       try
3615       {
3616         javax.swing.SwingUtilities.invokeAndWait(new Runnable()
3617         {
3618           @Override
3619           public void run()
3620           {
3621             JalviewStructureDisplayI sview = null;
3622             try
3623             {
3624               sview = new StructureViewer(alf.alignPanel
3625                       .getStructureSelectionManager()).createView(
3626                       StructureViewer.ViewerType.JMOL, pdbf, id, sq,
3627                       alf.alignPanel, svattrib, fileloc, rect, sviewid);
3628               addNewStructureViewer(sview);
3629             } catch (OutOfMemoryError ex)
3630             {
3631               new OOMWarning("restoring structure view for PDB id " + id,
3632                       (OutOfMemoryError) ex.getCause());
3633               if (sview != null && sview.isVisible())
3634               {
3635                 sview.closeViewer(false);
3636                 sview.setVisible(false);
3637                 sview.dispose();
3638               }
3639             }
3640           }
3641         });
3642       } catch (InvocationTargetException ex)
3643       {
3644         warn("Unexpected error when opening Jmol view.", ex);
3645
3646       } catch (InterruptedException e)
3647       {
3648         // e.printStackTrace();
3649       }
3650     }
3651   }
3652
3653   /**
3654    * Generates a name for the entry in the project jar file to hold state
3655    * information for a structure viewer
3656    * 
3657    * @param viewId
3658    * @return
3659    */
3660   protected String getViewerJarEntryName(String viewId)
3661   {
3662     return "viewer_" + viewId;
3663   }
3664
3665   /**
3666    * Returns any open frame that matches given structure viewer data. The match
3667    * is based on the unique viewId, or (for older project versions) the frame's
3668    * geometry.
3669    * 
3670    * @param viewerData
3671    * @return
3672    */
3673   protected StructureViewerBase findMatchingViewer(
3674           Entry<String, StructureViewerModel> viewerData)
3675   {
3676     final String sviewid = viewerData.getKey();
3677     final StructureViewerModel svattrib = viewerData.getValue();
3678     StructureViewerBase comp = null;
3679     JInternalFrame[] frames = getAllFrames();
3680     for (JInternalFrame frame : frames)
3681     {
3682       if (frame instanceof StructureViewerBase)
3683       {
3684         /*
3685          * Post jalview 2.4 schema includes structure view id
3686          */
3687         if (sviewid != null
3688                 && ((StructureViewerBase) frame).getViewId()
3689                         .equals(sviewid))
3690         {
3691           comp = (StructureViewerBase) frame;
3692           break; // break added in 2.9
3693         }
3694         /*
3695          * Otherwise test for matching position and size of viewer frame
3696          */
3697         else if (frame.getX() == svattrib.getX()
3698                 && frame.getY() == svattrib.getY()
3699                 && frame.getHeight() == svattrib.getHeight()
3700                 && frame.getWidth() == svattrib.getWidth())
3701         {
3702           comp = (StructureViewerBase) frame;
3703           // no break in faint hope of an exact match on viewId
3704         }
3705       }
3706     }
3707     return comp;
3708   }
3709
3710   /**
3711    * Link an AlignmentPanel to an existing structure viewer.
3712    * 
3713    * @param ap
3714    * @param viewer
3715    * @param oldFiles
3716    * @param useinViewerSuperpos
3717    * @param usetoColourbyseq
3718    * @param viewerColouring
3719    */
3720   protected void linkStructureViewer(AlignmentPanel ap,
3721           StructureViewerBase viewer, StructureViewerModel stateData)
3722   {
3723     // NOTE: if the jalview project is part of a shared session then
3724     // view synchronization should/could be done here.
3725
3726     final boolean useinViewerSuperpos = stateData.isAlignWithPanel();
3727     final boolean usetoColourbyseq = stateData.isColourWithAlignPanel();
3728     final boolean viewerColouring = stateData.isColourByViewer();
3729     Map<File, StructureData> oldFiles = stateData.getFileData();
3730
3731     /*
3732      * Add mapping for sequences in this view to an already open viewer
3733      */
3734     final AAStructureBindingModel binding = viewer.getBinding();
3735     for (File id : oldFiles.keySet())
3736     {
3737       // add this and any other pdb files that should be present in the
3738       // viewer
3739       StructureData filedat = oldFiles.get(id);
3740       String pdbFile = filedat.getFilePath();
3741       SequenceI[] seq = filedat.getSeqList().toArray(new SequenceI[0]);
3742       binding.getSsm().setMapping(seq, null, pdbFile,
3743               jalview.io.AppletFormatAdapter.FILE);
3744       binding.addSequenceForStructFile(pdbFile, seq);
3745     }
3746     // and add the AlignmentPanel's reference to the view panel
3747     viewer.addAlignmentPanel(ap);
3748     if (useinViewerSuperpos)
3749     {
3750       viewer.useAlignmentPanelForSuperposition(ap);
3751     }
3752     else
3753     {
3754       viewer.excludeAlignmentPanelForSuperposition(ap);
3755     }
3756     if (usetoColourbyseq)
3757     {
3758       viewer.useAlignmentPanelForColourbyseq(ap, !viewerColouring);
3759     }
3760     else
3761     {
3762       viewer.excludeAlignmentPanelForColourbyseq(ap);
3763     }
3764   }
3765
3766   /**
3767    * Get all frames within the Desktop.
3768    * 
3769    * @return
3770    */
3771   protected JInternalFrame[] getAllFrames()
3772   {
3773     JInternalFrame[] frames = null;
3774     // TODO is this necessary - is it safe - risk of hanging?
3775     do
3776     {
3777       try
3778       {
3779         frames = Desktop.desktop.getAllFrames();
3780       } catch (ArrayIndexOutOfBoundsException e)
3781       {
3782         // occasional No such child exceptions are thrown here...
3783         try
3784         {
3785           Thread.sleep(10);
3786         } catch (InterruptedException f)
3787         {
3788         }
3789       }
3790     } while (frames == null);
3791     return frames;
3792   }
3793
3794   /**
3795    * 
3796    * @param supported
3797    *          - minimum version we are comparing against
3798    * @param version
3799    *          - version of data being processsed.
3800    * @return true if version is development/null or evaluates to the same or
3801    *         later X.Y.Z (where X,Y,Z are like [0-9]+b?[0-9]*)
3802    */
3803   private boolean isVersionStringLaterThan(String supported, String version)
3804   {
3805     if (version == null || version.equalsIgnoreCase("DEVELOPMENT BUILD")
3806             || version.equalsIgnoreCase("Test")
3807             || version.equalsIgnoreCase("AUTOMATED BUILD"))
3808     {
3809       System.err.println("Assuming project file with "
3810               + (version == null ? "null" : version)
3811               + " is compatible with Jalview version " + supported);
3812       return true;
3813     }
3814     else
3815     {
3816       StringTokenizer currentV = new StringTokenizer(supported, "."), fileV = new StringTokenizer(
3817               version, ".");
3818       while (currentV.hasMoreTokens() && fileV.hasMoreTokens())
3819       {
3820         // convert b to decimal to catch bugfix releases within a series
3821         String curT = currentV.nextToken().toLowerCase().replace('b', '.');
3822         String fileT = fileV.nextToken().toLowerCase().replace('b', '.');
3823         try
3824         {
3825           if (Float.valueOf(curT) > Float.valueOf(fileT))
3826           {
3827             // current version is newer than the version that wrote the file
3828             return false;
3829           }
3830         } catch (NumberFormatException nfe)
3831         {
3832           System.err
3833                   .println("** WARNING: Version comparison failed for tokens ("
3834                           + curT
3835                           + ") and ("
3836                           + fileT
3837                           + ")\n** Current: '"
3838                           + supported + "' and Version: '" + version + "'");
3839         }
3840       }
3841       if (currentV.hasMoreElements())
3842       {
3843         // fileV has no minor version but identical series to current
3844         return false;
3845       }
3846     }
3847     return true;
3848   }
3849
3850   Vector<JalviewStructureDisplayI> newStructureViewers = null;
3851
3852   protected void addNewStructureViewer(JalviewStructureDisplayI sview)
3853   {
3854     if (newStructureViewers != null)
3855     {
3856       sview.getBinding().setFinishedLoadingFromArchive(false);
3857       newStructureViewers.add(sview);
3858     }
3859   }
3860
3861   protected void setLoadingFinishedForNewStructureViewers()
3862   {
3863     if (newStructureViewers != null)
3864     {
3865       for (JalviewStructureDisplayI sview : newStructureViewers)
3866       {
3867         sview.getBinding().setFinishedLoadingFromArchive(true);
3868       }
3869       newStructureViewers.clear();
3870       newStructureViewers = null;
3871     }
3872   }
3873
3874   AlignFrame loadViewport(String file, JSeq[] JSEQ,
3875           List<SequenceI> hiddenSeqs, Alignment al,
3876           JalviewModelSequence jms, Viewport view, String uniqueSeqSetId,
3877           String viewId, List<JvAnnotRow> autoAlan)
3878   {
3879     AlignFrame af = null;
3880     af = new AlignFrame(al, view.getWidth(), view.getHeight(),
3881             uniqueSeqSetId, viewId);
3882
3883     af.setFileName(file, "Jalview");
3884
3885     for (int i = 0; i < JSEQ.length; i++)
3886     {
3887       af.viewport.setSequenceColour(af.viewport.getAlignment()
3888               .getSequenceAt(i), new java.awt.Color(JSEQ[i].getColour()));
3889     }
3890
3891     af.viewport.setGatherViewsHere(view.getGatheredViews());
3892
3893     if (view.getSequenceSetId() != null)
3894     {
3895       AlignmentViewport av = viewportsAdded.get(uniqueSeqSetId);
3896
3897       af.viewport.setSequenceSetId(uniqueSeqSetId);
3898       if (av != null)
3899       {
3900         // propagate shared settings to this new view
3901         af.viewport.setHistoryList(av.getHistoryList());
3902         af.viewport.setRedoList(av.getRedoList());
3903       }
3904       else
3905       {
3906         viewportsAdded.put(uniqueSeqSetId, af.viewport);
3907       }
3908       // TODO: check if this method can be called repeatedly without
3909       // side-effects if alignpanel already registered.
3910       PaintRefresher.Register(af.alignPanel, uniqueSeqSetId);
3911     }
3912     // apply Hidden regions to view.
3913     if (hiddenSeqs != null)
3914     {
3915       for (int s = 0; s < JSEQ.length; s++)
3916       {
3917         jalview.datamodel.SequenceGroup hidden = new jalview.datamodel.SequenceGroup();
3918
3919         for (int r = 0; r < JSEQ[s].getHiddenSequencesCount(); r++)
3920         {
3921           hidden.addSequence(
3922                   al.getSequenceAt(JSEQ[s].getHiddenSequences(r)), false);
3923         }
3924         af.viewport.hideRepSequences(al.getSequenceAt(s), hidden);
3925       }
3926
3927       // jalview.datamodel.SequenceI[] hseqs = new
3928       // jalview.datamodel.SequenceI[hiddenSeqs
3929       // .size()];
3930       //
3931       // for (int s = 0; s < hiddenSeqs.size(); s++)
3932       // {
3933       // hseqs[s] = (jalview.datamodel.SequenceI) hiddenSeqs.elementAt(s);
3934       // }
3935
3936       SequenceI[] hseqs = hiddenSeqs.toArray(new SequenceI[hiddenSeqs
3937               .size()]);
3938       af.viewport.hideSequence(hseqs);
3939
3940     }
3941     // recover view properties and display parameters
3942     if (view.getViewName() != null)
3943     {
3944       af.viewport.viewName = view.getViewName();
3945       af.setInitialTabVisible();
3946     }
3947     af.setBounds(view.getXpos(), view.getYpos(), view.getWidth(),
3948             view.getHeight());
3949
3950     af.viewport.setShowAnnotation(view.getShowAnnotation());
3951     af.viewport.setAbovePIDThreshold(view.getPidSelected());
3952
3953     af.viewport.setColourText(view.getShowColourText());
3954
3955     af.viewport.setConservationSelected(view.getConservationSelected());
3956     af.viewport.setShowJVSuffix(view.getShowFullId());
3957     af.viewport.setRightAlignIds(view.getRightAlignIds());
3958     af.viewport.setFont(
3959             new java.awt.Font(view.getFontName(), view.getFontStyle(), view
3960                     .getFontSize()), true);
3961     // TODO: allow custom charWidth/Heights to be restored by updating them
3962     // after setting font - which means set above to false
3963     af.viewport.setRenderGaps(view.getRenderGaps());
3964     af.viewport.setWrapAlignment(view.getWrapAlignment());
3965     af.viewport.setShowAnnotation(view.getShowAnnotation());
3966
3967     af.viewport.setShowBoxes(view.getShowBoxes());
3968
3969     af.viewport.setShowText(view.getShowText());
3970
3971     af.viewport.setTextColour(new java.awt.Color(view.getTextCol1()));
3972     af.viewport.setTextColour2(new java.awt.Color(view.getTextCol2()));
3973     af.viewport.setThresholdTextColour(view.getTextColThreshold());
3974     af.viewport.setShowUnconserved(view.hasShowUnconserved() ? view
3975             .isShowUnconserved() : false);
3976     af.viewport.setStartRes(view.getStartRes());
3977     af.viewport.setStartSeq(view.getStartSeq());
3978     af.alignPanel.updateLayout();
3979     ColourSchemeI cs = null;
3980     // apply colourschemes
3981     if (view.getBgColour() != null)
3982     {
3983       if (view.getBgColour().startsWith("ucs"))
3984       {
3985         cs = getUserColourScheme(jms, view.getBgColour());
3986       }
3987       else if (view.getBgColour().startsWith("Annotation"))
3988       {
3989         AnnotationColours viewAnnColour = view.getAnnotationColours();
3990         cs = constructAnnotationColour(viewAnnColour, af, al, jms, true);
3991
3992         // annpos
3993
3994       }
3995       else
3996       {
3997         cs = ColourSchemeProperty.getColour(al, view.getBgColour());
3998       }
3999
4000       if (cs != null)
4001       {
4002         cs.setThreshold(view.getPidThreshold(), true);
4003         cs.setConsensus(af.viewport.getSequenceConsensusHash());
4004       }
4005     }
4006
4007     af.viewport.setGlobalColourScheme(cs);
4008     af.viewport.setColourAppliesToAllGroups(false);
4009
4010     if (view.getConservationSelected() && cs != null)
4011     {
4012       cs.setConservationInc(view.getConsThreshold());
4013     }
4014
4015     af.changeColour(cs);
4016
4017     af.viewport.setColourAppliesToAllGroups(true);
4018
4019     af.viewport.setShowSequenceFeatures(view.getShowSequenceFeatures());
4020
4021     if (view.hasCentreColumnLabels())
4022     {
4023       af.viewport.setCentreColumnLabels(view.getCentreColumnLabels());
4024     }
4025     if (view.hasIgnoreGapsinConsensus())
4026     {
4027       af.viewport.setIgnoreGapsConsensus(view.getIgnoreGapsinConsensus(),
4028               null);
4029     }
4030     if (view.hasFollowHighlight())
4031     {
4032       af.viewport.setFollowHighlight(view.getFollowHighlight());
4033     }
4034     if (view.hasFollowSelection())
4035     {
4036       af.viewport.followSelection = view.getFollowSelection();
4037     }
4038     if (view.hasShowConsensusHistogram())
4039     {
4040       af.viewport.setShowConsensusHistogram(view
4041               .getShowConsensusHistogram());
4042     }
4043     else
4044     {
4045       af.viewport.setShowConsensusHistogram(true);
4046     }
4047     if (view.hasShowSequenceLogo())
4048     {
4049       af.viewport.setShowSequenceLogo(view.getShowSequenceLogo());
4050     }
4051     else
4052     {
4053       af.viewport.setShowSequenceLogo(false);
4054     }
4055     if (view.hasNormaliseSequenceLogo())
4056     {
4057       af.viewport.setNormaliseSequenceLogo(view.getNormaliseSequenceLogo());
4058     }
4059     if (view.hasShowDbRefTooltip())
4060     {
4061       af.viewport.setShowDBRefs(view.getShowDbRefTooltip());
4062     }
4063     if (view.hasShowNPfeatureTooltip())
4064     {
4065       af.viewport.setShowNPFeats(view.hasShowNPfeatureTooltip());
4066     }
4067     if (view.hasShowGroupConsensus())
4068     {
4069       af.viewport.setShowGroupConsensus(view.getShowGroupConsensus());
4070     }
4071     else
4072     {
4073       af.viewport.setShowGroupConsensus(false);
4074     }
4075     if (view.hasShowGroupConservation())
4076     {
4077       af.viewport.setShowGroupConservation(view.getShowGroupConservation());
4078     }
4079     else
4080     {
4081       af.viewport.setShowGroupConservation(false);
4082     }
4083
4084     // recover featre settings
4085     if (jms.getFeatureSettings() != null)
4086     {
4087       FeaturesDisplayed fdi;
4088       af.viewport.setFeaturesDisplayed(fdi = new FeaturesDisplayed());
4089       String[] renderOrder = new String[jms.getFeatureSettings()
4090               .getSettingCount()];
4091       Hashtable featureGroups = new Hashtable();
4092       Hashtable featureColours = new Hashtable();
4093       Hashtable featureOrder = new Hashtable();
4094
4095       for (int fs = 0; fs < jms.getFeatureSettings().getSettingCount(); fs++)
4096       {
4097         Setting setting = jms.getFeatureSettings().getSetting(fs);
4098         if (setting.hasMincolour())
4099         {
4100           GraduatedColor gc = setting.hasMin() ? new GraduatedColor(
4101                   new java.awt.Color(setting.getMincolour()),
4102                   new java.awt.Color(setting.getColour()),
4103                   setting.getMin(), setting.getMax()) : new GraduatedColor(
4104                   new java.awt.Color(setting.getMincolour()),
4105                   new java.awt.Color(setting.getColour()), 0, 1);
4106           if (setting.hasThreshold())
4107           {
4108             gc.setThresh(setting.getThreshold());
4109             gc.setThreshType(setting.getThreshstate());
4110           }
4111           gc.setAutoScaled(true); // default
4112           if (setting.hasAutoScale())
4113           {
4114             gc.setAutoScaled(setting.getAutoScale());
4115           }
4116           if (setting.hasColourByLabel())
4117           {
4118             gc.setColourByLabel(setting.getColourByLabel());
4119           }
4120           // and put in the feature colour table.
4121           featureColours.put(setting.getType(), gc);
4122         }
4123         else
4124         {
4125           featureColours.put(setting.getType(),
4126                   new java.awt.Color(setting.getColour()));
4127         }
4128         renderOrder[fs] = setting.getType();
4129         if (setting.hasOrder())
4130         {
4131           featureOrder.put(setting.getType(), setting.getOrder());
4132         }
4133         else
4134         {
4135           featureOrder.put(setting.getType(), new Float(fs
4136                   / jms.getFeatureSettings().getSettingCount()));
4137         }
4138         if (setting.getDisplay())
4139         {
4140           fdi.setVisible(setting.getType());
4141         }
4142       }
4143       Hashtable fgtable = new Hashtable();
4144       for (int gs = 0; gs < jms.getFeatureSettings().getGroupCount(); gs++)
4145       {
4146         Group grp = jms.getFeatureSettings().getGroup(gs);
4147         fgtable.put(grp.getName(), new Boolean(grp.getDisplay()));
4148       }
4149       // FeatureRendererSettings frs = new FeatureRendererSettings(renderOrder,
4150       // fgtable, featureColours, jms.getFeatureSettings().hasTransparency() ?
4151       // jms.getFeatureSettings().getTransparency() : 0.0, featureOrder);
4152       FeatureRendererSettings frs = new FeatureRendererSettings(
4153               renderOrder, fgtable, featureColours, 1.0f, featureOrder);
4154       af.alignPanel.getSeqPanel().seqCanvas.getFeatureRenderer()
4155               .transferSettings(frs);
4156
4157     }
4158
4159     if (view.getHiddenColumnsCount() > 0)
4160     {
4161       for (int c = 0; c < view.getHiddenColumnsCount(); c++)
4162       {
4163         af.viewport.hideColumns(view.getHiddenColumns(c).getStart(), view
4164                 .getHiddenColumns(c).getEnd() // +1
4165                 );
4166       }
4167     }
4168     if (view.getCalcIdParam() != null)
4169     {
4170       for (CalcIdParam calcIdParam : view.getCalcIdParam())
4171       {
4172         if (calcIdParam != null)
4173         {
4174           if (recoverCalcIdParam(calcIdParam, af.viewport))
4175           {
4176           }
4177           else
4178           {
4179             warn("Couldn't recover parameters for "
4180                     + calcIdParam.getCalcId());
4181           }
4182         }
4183       }
4184     }
4185     af.setMenusFromViewport(af.viewport);
4186     
4187     // TODO: we don't need to do this if the viewport is aready visible.
4188     /*
4189      * Add the AlignFrame to the desktop (it may be 'gathered' later), unless it
4190      * has a 'cdna/protein complement' view, in which case save it in order to
4191      * populate a SplitFrame once all views have been read in.
4192      */
4193     String complementaryViewId = view.getComplementId();
4194     if (complementaryViewId == null)
4195     {
4196       Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(),
4197               view.getHeight());
4198       // recompute any autoannotation
4199       af.alignPanel.updateAnnotation(false, true);
4200       reorderAutoannotation(af, al, autoAlan);
4201       af.alignPanel.alignmentChanged();
4202     }
4203     else
4204     {
4205       splitFrameCandidates.put(view, af);
4206     }
4207     return af;
4208   }
4209
4210   private ColourSchemeI constructAnnotationColour(
4211           AnnotationColours viewAnnColour, AlignFrame af, Alignment al,
4212           JalviewModelSequence jms, boolean checkGroupAnnColour)
4213   {
4214     boolean propagateAnnColour = false;
4215     ColourSchemeI cs = null;
4216     AlignmentI annAlignment = af != null ? af.viewport.getAlignment() : al;
4217     if (checkGroupAnnColour && al.getGroups() != null
4218             && al.getGroups().size() > 0)
4219     {
4220       // pre 2.8.1 behaviour
4221       // check to see if we should transfer annotation colours
4222       propagateAnnColour = true;
4223       for (jalview.datamodel.SequenceGroup sg : al.getGroups())
4224       {
4225         if (sg.cs instanceof AnnotationColourGradient)
4226         {
4227           propagateAnnColour = false;
4228         }
4229       }
4230     }
4231     // int find annotation
4232     if (annAlignment.getAlignmentAnnotation() != null)
4233     {
4234       for (int i = 0; i < annAlignment.getAlignmentAnnotation().length; i++)
4235       {
4236         if (annAlignment.getAlignmentAnnotation()[i].label
4237                 .equals(viewAnnColour.getAnnotation()))
4238         {
4239           if (annAlignment.getAlignmentAnnotation()[i].getThreshold() == null)
4240           {
4241             annAlignment.getAlignmentAnnotation()[i]
4242                     .setThreshold(new jalview.datamodel.GraphLine(
4243                             viewAnnColour.getThreshold(), "Threshold",
4244                             java.awt.Color.black)
4245
4246                     );
4247           }
4248
4249           if (viewAnnColour.getColourScheme().equals("None"))
4250           {
4251             cs = new AnnotationColourGradient(
4252                     annAlignment.getAlignmentAnnotation()[i],
4253                     new java.awt.Color(viewAnnColour.getMinColour()),
4254                     new java.awt.Color(viewAnnColour.getMaxColour()),
4255                     viewAnnColour.getAboveThreshold());
4256           }
4257           else if (viewAnnColour.getColourScheme().startsWith("ucs"))
4258           {
4259             cs = new AnnotationColourGradient(
4260                     annAlignment.getAlignmentAnnotation()[i],
4261                     getUserColourScheme(jms,
4262                             viewAnnColour.getColourScheme()),
4263                     viewAnnColour.getAboveThreshold());
4264           }
4265           else
4266           {
4267             cs = new AnnotationColourGradient(
4268                     annAlignment.getAlignmentAnnotation()[i],
4269                     ColourSchemeProperty.getColour(al,
4270                             viewAnnColour.getColourScheme()),
4271                     viewAnnColour.getAboveThreshold());
4272           }
4273           if (viewAnnColour.hasPerSequence())
4274           {
4275             ((AnnotationColourGradient) cs).setSeqAssociated(viewAnnColour
4276                     .isPerSequence());
4277           }
4278           if (viewAnnColour.hasPredefinedColours())
4279           {
4280             ((AnnotationColourGradient) cs)
4281                     .setPredefinedColours(viewAnnColour
4282                             .isPredefinedColours());
4283           }
4284           if (propagateAnnColour && al.getGroups() != null)
4285           {
4286             // Also use these settings for all the groups
4287             for (int g = 0; g < al.getGroups().size(); g++)
4288             {
4289               jalview.datamodel.SequenceGroup sg = al.getGroups().get(g);
4290
4291               if (sg.cs == null)
4292               {
4293                 continue;
4294               }
4295
4296               /*
4297                * if (viewAnnColour.getColourScheme().equals("None" )) { sg.cs =
4298                * new AnnotationColourGradient(
4299                * annAlignment.getAlignmentAnnotation()[i], new
4300                * java.awt.Color(viewAnnColour. getMinColour()), new
4301                * java.awt.Color(viewAnnColour. getMaxColour()),
4302                * viewAnnColour.getAboveThreshold()); } else
4303                */
4304               {
4305                 sg.cs = new AnnotationColourGradient(
4306                         annAlignment.getAlignmentAnnotation()[i], sg.cs,
4307                         viewAnnColour.getAboveThreshold());
4308                 if (cs instanceof AnnotationColourGradient)
4309                 {
4310                   if (viewAnnColour.hasPerSequence())
4311                   {
4312                     ((AnnotationColourGradient) cs)
4313                             .setSeqAssociated(viewAnnColour.isPerSequence());
4314                   }
4315                   if (viewAnnColour.hasPredefinedColours())
4316                   {
4317                     ((AnnotationColourGradient) cs)
4318                             .setPredefinedColours(viewAnnColour
4319                                     .isPredefinedColours());
4320                   }
4321                 }
4322               }
4323
4324             }
4325           }
4326
4327           break;
4328         }
4329
4330       }
4331     }
4332     return cs;
4333   }
4334
4335   private void reorderAutoannotation(AlignFrame af, Alignment al,
4336           List<JvAnnotRow> autoAlan)
4337   {
4338     // copy over visualization settings for autocalculated annotation in the
4339     // view
4340     if (al.getAlignmentAnnotation() != null)
4341     {
4342       /**
4343        * Kludge for magic autoannotation names (see JAL-811)
4344        */
4345       String[] magicNames = new String[]
4346       { "Consensus", "Quality", "Conservation" };
4347       JvAnnotRow nullAnnot = new JvAnnotRow(-1, null);
4348       Hashtable<String, JvAnnotRow> visan = new Hashtable<String, JvAnnotRow>();
4349       for (String nm : magicNames)
4350       {
4351         visan.put(nm, nullAnnot);
4352       }
4353       for (JvAnnotRow auan : autoAlan)
4354       {
4355         visan.put(auan.template.label
4356                 + (auan.template.getCalcId() == null ? "" : "\t"
4357                         + auan.template.getCalcId()), auan);
4358       }
4359       int hSize = al.getAlignmentAnnotation().length;
4360       List<JvAnnotRow> reorder = new ArrayList<JvAnnotRow>();
4361       // work through any autoCalculated annotation already on the view
4362       // removing it if it should be placed in a different location on the
4363       // annotation panel.
4364       List<String> remains = new ArrayList<String>(visan.keySet());
4365       for (int h = 0; h < hSize; h++)
4366       {
4367         jalview.datamodel.AlignmentAnnotation jalan = al
4368                 .getAlignmentAnnotation()[h];
4369         if (jalan.autoCalculated)
4370         {
4371           String k;
4372           JvAnnotRow valan = visan.get(k = jalan.label);
4373           if (jalan.getCalcId() != null)
4374           {
4375             valan = visan.get(k = jalan.label + "\t" + jalan.getCalcId());
4376           }
4377
4378           if (valan != null)
4379           {
4380             // delete the auto calculated row from the alignment
4381             al.deleteAnnotation(jalan, false);
4382             remains.remove(k);
4383             hSize--;
4384             h--;
4385             if (valan != nullAnnot)
4386             {
4387               if (jalan != valan.template)
4388               {
4389                 // newly created autoannotation row instance
4390                 // so keep a reference to the visible annotation row
4391                 // and copy over all relevant attributes
4392                 if (valan.template.graphHeight >= 0)
4393
4394                 {
4395                   jalan.graphHeight = valan.template.graphHeight;
4396                 }
4397                 jalan.visible = valan.template.visible;
4398               }
4399               reorder.add(new JvAnnotRow(valan.order, jalan));
4400             }
4401           }
4402         }
4403       }
4404       // Add any (possibly stale) autocalculated rows that were not appended to
4405       // the view during construction
4406       for (String other : remains)
4407       {
4408         JvAnnotRow othera = visan.get(other);
4409         if (othera != nullAnnot && othera.template.getCalcId() != null
4410                 && othera.template.getCalcId().length() > 0)
4411         {
4412           reorder.add(othera);
4413         }
4414       }
4415       // now put the automatic annotation in its correct place
4416       int s = 0, srt[] = new int[reorder.size()];
4417       JvAnnotRow[] rws = new JvAnnotRow[reorder.size()];
4418       for (JvAnnotRow jvar : reorder)
4419       {
4420         rws[s] = jvar;
4421         srt[s++] = jvar.order;
4422       }
4423       reorder.clear();
4424       jalview.util.QuickSort.sort(srt, rws);
4425       // and re-insert the annotation at its correct position
4426       for (JvAnnotRow jvar : rws)
4427       {
4428         al.addAnnotation(jvar.template, jvar.order);
4429       }
4430       af.alignPanel.adjustAnnotationHeight();
4431     }
4432   }
4433
4434   Hashtable skipList = null;
4435
4436   /**
4437    * TODO remove this method
4438    * 
4439    * @param view
4440    * @return AlignFrame bound to sequenceSetId from view, if one exists. private
4441    *         AlignFrame getSkippedFrame(Viewport view) { if (skipList==null) {
4442    *         throw new Error("Implementation Error. No skipList defined for this
4443    *         Jalview2XML instance."); } return (AlignFrame)
4444    *         skipList.get(view.getSequenceSetId()); }
4445    */
4446
4447   /**
4448    * Check if the Jalview view contained in object should be skipped or not.
4449    * 
4450    * @param object
4451    * @return true if view's sequenceSetId is a key in skipList
4452    */
4453   private boolean skipViewport(JalviewModel object)
4454   {
4455     if (skipList == null)
4456     {
4457       return false;
4458     }
4459     String id;
4460     if (skipList.containsKey(id = object.getJalviewModelSequence()
4461             .getViewport()[0].getSequenceSetId()))
4462     {
4463       if (Cache.log != null && Cache.log.isDebugEnabled())
4464       {
4465         Cache.log.debug("Skipping seuqence set id " + id);
4466       }
4467       return true;
4468     }
4469     return false;
4470   }
4471
4472   public void addToSkipList(AlignFrame af)
4473   {
4474     if (skipList == null)
4475     {
4476       skipList = new Hashtable();
4477     }
4478     skipList.put(af.getViewport().getSequenceSetId(), af);
4479   }
4480
4481   public void clearSkipList()
4482   {
4483     if (skipList != null)
4484     {
4485       skipList.clear();
4486       skipList = null;
4487     }
4488   }
4489
4490   private void recoverDatasetFor(SequenceSet vamsasSet, Alignment al,
4491           boolean ignoreUnrefed)
4492   {
4493     jalview.datamodel.Alignment ds = getDatasetFor(vamsasSet.getDatasetId());
4494     Vector dseqs = null;
4495     if (ds == null)
4496     {
4497       // create a list of new dataset sequences
4498       dseqs = new Vector();
4499     }
4500     for (int i = 0, iSize = vamsasSet.getSequenceCount(); i < iSize; i++)
4501     {
4502       Sequence vamsasSeq = vamsasSet.getSequence(i);
4503       ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs, ignoreUnrefed);
4504     }
4505     // create a new dataset
4506     if (ds == null)
4507     {
4508       SequenceI[] dsseqs = new SequenceI[dseqs.size()];
4509       dseqs.copyInto(dsseqs);
4510       ds = new jalview.datamodel.Alignment(dsseqs);
4511       debug("Created new dataset " + vamsasSet.getDatasetId()
4512               + " for alignment " + System.identityHashCode(al));
4513       addDatasetRef(vamsasSet.getDatasetId(), ds);
4514     }
4515     // set the dataset for the newly imported alignment.
4516     if (al.getDataset() == null && !ignoreUnrefed)
4517     {
4518       al.setDataset(ds);
4519     }
4520   }
4521
4522   /**
4523    * 
4524    * @param vamsasSeq
4525    *          sequence definition to create/merge dataset sequence for
4526    * @param ds
4527    *          dataset alignment
4528    * @param dseqs
4529    *          vector to add new dataset sequence to
4530    */
4531   private void ensureJalviewDatasetSequence(Sequence vamsasSeq,
4532           AlignmentI ds, Vector dseqs, boolean ignoreUnrefed)
4533   {
4534     // JBP TODO: Check this is called for AlCodonFrames to support recovery of
4535     // xRef Codon Maps
4536     SequenceI sq = seqRefIds.get(vamsasSeq.getId());
4537     SequenceI dsq = null;
4538     if (sq != null && sq.getDatasetSequence() != null)
4539     {
4540       dsq = sq.getDatasetSequence();
4541     }
4542     if (sq == null && ignoreUnrefed)
4543     {
4544       return;
4545     }
4546     String sqid = vamsasSeq.getDsseqid();
4547     if (dsq == null)
4548     {
4549       // need to create or add a new dataset sequence reference to this sequence
4550       if (sqid != null)
4551       {
4552         dsq = seqRefIds.get(sqid);
4553       }
4554       // check again
4555       if (dsq == null)
4556       {
4557         // make a new dataset sequence
4558         dsq = sq.createDatasetSequence();
4559         if (sqid == null)
4560         {
4561           // make up a new dataset reference for this sequence
4562           sqid = seqHash(dsq);
4563         }
4564         dsq.setVamsasId(uniqueSetSuffix + sqid);
4565         seqRefIds.put(sqid, dsq);
4566         if (ds == null)
4567         {
4568           if (dseqs != null)
4569           {
4570             dseqs.addElement(dsq);
4571           }
4572         }
4573         else
4574         {
4575           ds.addSequence(dsq);
4576         }
4577       }
4578       else
4579       {
4580         if (sq != dsq)
4581         { // make this dataset sequence sq's dataset sequence
4582           sq.setDatasetSequence(dsq);
4583           // and update the current dataset alignment
4584           if (ds == null)
4585           {
4586             if (dseqs != null)
4587             {
4588               if (!dseqs.contains(dsq))
4589               {
4590                 dseqs.add(dsq);
4591               }
4592             }
4593             else
4594             {
4595               if (ds.findIndex(dsq) < 0)
4596               {
4597                 ds.addSequence(dsq);
4598               }
4599             }
4600           }
4601         }
4602       }
4603     }
4604     // TODO: refactor this as a merge dataset sequence function
4605     // now check that sq (the dataset sequence) sequence really is the union of
4606     // all references to it
4607     // boolean pre = sq.getStart() < dsq.getStart();
4608     // boolean post = sq.getEnd() > dsq.getEnd();
4609     // if (pre || post)
4610     if (sq != dsq)
4611     {
4612       // StringBuffer sb = new StringBuffer();
4613       String newres = jalview.analysis.AlignSeq.extractGaps(
4614               jalview.util.Comparison.GapChars, sq.getSequenceAsString());
4615       if (!newres.equalsIgnoreCase(dsq.getSequenceAsString())
4616               && newres.length() > dsq.getLength())
4617       {
4618         // Update with the longer sequence.
4619         synchronized (dsq)
4620         {
4621           /*
4622            * if (pre) { sb.insert(0, newres .substring(0, dsq.getStart() -
4623            * sq.getStart())); dsq.setStart(sq.getStart()); } if (post) {
4624            * sb.append(newres.substring(newres.length() - sq.getEnd() -
4625            * dsq.getEnd())); dsq.setEnd(sq.getEnd()); }
4626            */
4627           dsq.setSequence(newres);
4628         }
4629         // TODO: merges will never happen if we 'know' we have the real dataset
4630         // sequence - this should be detected when id==dssid
4631         System.err
4632                 .println("DEBUG Notice:  Merged dataset sequence (if you see this often, post at http://issues.jalview.org/browse/JAL-1474)"); // ("
4633         // + (pre ? "prepended" : "") + " "
4634         // + (post ? "appended" : ""));
4635       }
4636     }
4637   }
4638
4639   java.util.Hashtable datasetIds = null;
4640
4641   java.util.IdentityHashMap dataset2Ids = null;
4642
4643   private Alignment getDatasetFor(String datasetId)
4644   {
4645     if (datasetIds == null)
4646     {
4647       datasetIds = new Hashtable();
4648       return null;
4649     }
4650     if (datasetIds.containsKey(datasetId))
4651     {
4652       return (Alignment) datasetIds.get(datasetId);
4653     }
4654     return null;
4655   }
4656
4657   private void addDatasetRef(String datasetId, Alignment dataset)
4658   {
4659     if (datasetIds == null)
4660     {
4661       datasetIds = new Hashtable();
4662     }
4663     datasetIds.put(datasetId, dataset);
4664   }
4665
4666   /**
4667    * make a new dataset ID for this jalview dataset alignment
4668    * 
4669    * @param dataset
4670    * @return
4671    */
4672   private String getDatasetIdRef(jalview.datamodel.Alignment dataset)
4673   {
4674     if (dataset.getDataset() != null)
4675     {
4676       warn("Serious issue!  Dataset Object passed to getDatasetIdRef is not a Jalview DATASET alignment...");
4677     }
4678     String datasetId = makeHashCode(dataset, null);
4679     if (datasetId == null)
4680     {
4681       // make a new datasetId and record it
4682       if (dataset2Ids == null)
4683       {
4684         dataset2Ids = new IdentityHashMap();
4685       }
4686       else
4687       {
4688         datasetId = (String) dataset2Ids.get(dataset);
4689       }
4690       if (datasetId == null)
4691       {
4692         datasetId = "ds" + dataset2Ids.size() + 1;
4693         dataset2Ids.put(dataset, datasetId);
4694       }
4695     }
4696     return datasetId;
4697   }
4698
4699   private void addDBRefs(SequenceI datasetSequence, Sequence sequence)
4700   {
4701     for (int d = 0; d < sequence.getDBRefCount(); d++)
4702     {
4703       DBRef dr = sequence.getDBRef(d);
4704       jalview.datamodel.DBRefEntry entry = new jalview.datamodel.DBRefEntry(
4705               sequence.getDBRef(d).getSource(), sequence.getDBRef(d)
4706                       .getVersion(), sequence.getDBRef(d).getAccessionId());
4707       if (dr.getMapping() != null)
4708       {
4709         entry.setMap(addMapping(dr.getMapping()));
4710       }
4711       datasetSequence.addDBRef(entry);
4712     }
4713   }
4714
4715   private jalview.datamodel.Mapping addMapping(Mapping m)
4716   {
4717     SequenceI dsto = null;
4718     // Mapping m = dr.getMapping();
4719     int fr[] = new int[m.getMapListFromCount() * 2];
4720     Enumeration f = m.enumerateMapListFrom();
4721     for (int _i = 0; f.hasMoreElements(); _i += 2)
4722     {
4723       MapListFrom mf = (MapListFrom) f.nextElement();
4724       fr[_i] = mf.getStart();
4725       fr[_i + 1] = mf.getEnd();
4726     }
4727     int fto[] = new int[m.getMapListToCount() * 2];
4728     f = m.enumerateMapListTo();
4729     for (int _i = 0; f.hasMoreElements(); _i += 2)
4730     {
4731       MapListTo mf = (MapListTo) f.nextElement();
4732       fto[_i] = mf.getStart();
4733       fto[_i + 1] = mf.getEnd();
4734     }
4735     jalview.datamodel.Mapping jmap = new jalview.datamodel.Mapping(dsto,
4736             fr, fto, (int) m.getMapFromUnit(), (int) m.getMapToUnit());
4737     if (m.getMappingChoice() != null)
4738     {
4739       MappingChoice mc = m.getMappingChoice();
4740       if (mc.getDseqFor() != null)
4741       {
4742         String dsfor = "" + mc.getDseqFor();
4743         if (seqRefIds.containsKey(dsfor))
4744         {
4745           /**
4746            * recover from hash
4747            */
4748           jmap.setTo(seqRefIds.get(dsfor));
4749         }
4750         else
4751         {
4752           frefedSequence.add(new Object[]
4753           { dsfor, jmap });
4754         }
4755       }
4756       else
4757       {
4758         /**
4759          * local sequence definition
4760          */
4761         Sequence ms = mc.getSequence();
4762         SequenceI djs = null;
4763         String sqid = ms.getDsseqid();
4764         if (sqid != null && sqid.length() > 0)
4765         {
4766           /*
4767            * recover dataset sequence
4768            */
4769           djs = seqRefIds.get(sqid);
4770         }
4771         else
4772         {
4773           System.err
4774                   .println("Warning - making up dataset sequence id for DbRef sequence map reference");
4775           sqid = ((Object) ms).toString(); // make up a new hascode for
4776           // undefined dataset sequence hash
4777           // (unlikely to happen)
4778         }
4779
4780         if (djs == null)
4781         {
4782           /**
4783            * make a new dataset sequence and add it to refIds hash
4784            */
4785           djs = new jalview.datamodel.Sequence(ms.getName(),
4786                   ms.getSequence());
4787           djs.setStart(jmap.getMap().getToLowest());
4788           djs.setEnd(jmap.getMap().getToHighest());
4789           djs.setVamsasId(uniqueSetSuffix + sqid);
4790           jmap.setTo(djs);
4791           seqRefIds.put(sqid, djs);
4792
4793         }
4794         jalview.bin.Cache.log.debug("about to recurse on addDBRefs.");
4795         addDBRefs(djs, ms);
4796
4797       }
4798     }
4799     return (jmap);
4800
4801   }
4802
4803   public jalview.gui.AlignmentPanel copyAlignPanel(AlignmentPanel ap,
4804           boolean keepSeqRefs)
4805   {
4806     initSeqRefs();
4807     jalview.schemabinding.version2.JalviewModel jm = saveState(ap, null,
4808             null);
4809
4810     if (!keepSeqRefs)
4811     {
4812       clearSeqRefs();
4813       jm.getJalviewModelSequence().getViewport(0).setSequenceSetId(null);
4814     }
4815     else
4816     {
4817       uniqueSetSuffix = "";
4818       jm.getJalviewModelSequence().getViewport(0).setId(null); // we don't
4819       // overwrite the
4820       // view we just
4821       // copied
4822     }
4823     if (this.frefedSequence == null)
4824     {
4825       frefedSequence = new Vector();
4826     }
4827
4828     viewportsAdded.clear();
4829
4830     AlignFrame af = loadFromObject(jm, null, false, null);
4831     af.alignPanels.clear();
4832     af.closeMenuItem_actionPerformed(true);
4833
4834     /*
4835      * if(ap.av.getAlignment().getAlignmentAnnotation()!=null) { for(int i=0;
4836      * i<ap.av.getAlignment().getAlignmentAnnotation().length; i++) {
4837      * if(!ap.av.getAlignment().getAlignmentAnnotation()[i].autoCalculated) {
4838      * af.alignPanel.av.getAlignment().getAlignmentAnnotation()[i] =
4839      * ap.av.getAlignment().getAlignmentAnnotation()[i]; } } }
4840      */
4841
4842     return af.alignPanel;
4843   }
4844
4845   /**
4846    * flag indicating if hashtables should be cleared on finalization TODO this
4847    * flag may not be necessary
4848    */
4849   private final boolean _cleartables = true;
4850
4851   private Hashtable jvids2vobj;
4852
4853   /*
4854    * (non-Javadoc)
4855    * 
4856    * @see java.lang.Object#finalize()
4857    */
4858   @Override
4859   protected void finalize() throws Throwable
4860   {
4861     // really make sure we have no buried refs left.
4862     if (_cleartables)
4863     {
4864       clearSeqRefs();
4865     }
4866     this.seqRefIds = null;
4867     this.seqsToIds = null;
4868     super.finalize();
4869   }
4870
4871   private void warn(String msg)
4872   {
4873     warn(msg, null);
4874   }
4875
4876   private void warn(String msg, Exception e)
4877   {
4878     if (Cache.log != null)
4879     {
4880       if (e != null)
4881       {
4882         Cache.log.warn(msg, e);
4883       }
4884       else
4885       {
4886         Cache.log.warn(msg);
4887       }
4888     }
4889     else
4890     {
4891       System.err.println("Warning: " + msg);
4892       if (e != null)
4893       {
4894         e.printStackTrace();
4895       }
4896     }
4897   }
4898
4899   private void debug(String string)
4900   {
4901     debug(string, null);
4902   }
4903
4904   private void debug(String msg, Exception e)
4905   {
4906     if (Cache.log != null)
4907     {
4908       if (e != null)
4909       {
4910         Cache.log.debug(msg, e);
4911       }
4912       else
4913       {
4914         Cache.log.debug(msg);
4915       }
4916     }
4917     else
4918     {
4919       System.err.println("Warning: " + msg);
4920       if (e != null)
4921       {
4922         e.printStackTrace();
4923       }
4924     }
4925   }
4926
4927   /**
4928    * set the object to ID mapping tables used to write/recover objects and XML
4929    * ID strings for the jalview project. If external tables are provided then
4930    * finalize and clearSeqRefs will not clear the tables when the Jalview2XML
4931    * object goes out of scope. - also populates the datasetIds hashtable with
4932    * alignment objects containing dataset sequences
4933    * 
4934    * @param vobj2jv
4935    *          Map from ID strings to jalview datamodel
4936    * @param jv2vobj
4937    *          Map from jalview datamodel to ID strings
4938    * 
4939    * 
4940    */
4941   public void setObjectMappingTables(Hashtable vobj2jv,
4942           IdentityHashMap jv2vobj)
4943   {
4944     this.jv2vobj = jv2vobj;
4945     this.vobj2jv = vobj2jv;
4946     Iterator ds = jv2vobj.keySet().iterator();
4947     String id;
4948     while (ds.hasNext())
4949     {
4950       Object jvobj = ds.next();
4951       id = jv2vobj.get(jvobj).toString();
4952       if (jvobj instanceof jalview.datamodel.Alignment)
4953       {
4954         if (((jalview.datamodel.Alignment) jvobj).getDataset() == null)
4955         {
4956           addDatasetRef(id, (jalview.datamodel.Alignment) jvobj);
4957         }
4958       }
4959       else if (jvobj instanceof jalview.datamodel.Sequence)
4960       {
4961         // register sequence object so the XML parser can recover it.
4962         if (seqRefIds == null)
4963         {
4964           seqRefIds = new HashMap<String, SequenceI>();
4965         }
4966         if (seqsToIds == null)
4967         {
4968           seqsToIds = new IdentityHashMap<SequenceI, String>();
4969         }
4970         seqRefIds.put(jv2vobj.get(jvobj).toString(), (SequenceI) jvobj);
4971         seqsToIds.put((SequenceI) jvobj, id);
4972       }
4973       else if (jvobj instanceof jalview.datamodel.AlignmentAnnotation)
4974       {
4975         String anid;
4976         AlignmentAnnotation jvann = (AlignmentAnnotation) jvobj;
4977         annotationIds.put(anid = jv2vobj.get(jvobj).toString(), jvann);
4978         if (jvann.annotationId == null)
4979         {
4980           jvann.annotationId = anid;
4981         }
4982         if (!jvann.annotationId.equals(anid))
4983         {
4984           // TODO verify that this is the correct behaviour
4985           this.warn("Overriding Annotation ID for " + anid
4986                   + " from different id : " + jvann.annotationId);
4987           jvann.annotationId = anid;
4988         }
4989       }
4990       else if (jvobj instanceof String)
4991       {
4992         if (jvids2vobj == null)
4993         {
4994           jvids2vobj = new Hashtable();
4995           jvids2vobj.put(jvobj, jv2vobj.get(jvobj).toString());
4996         }
4997       }
4998       else
4999       {
5000         Cache.log.debug("Ignoring " + jvobj.getClass() + " (ID = " + id);
5001       }
5002     }
5003   }
5004
5005   /**
5006    * set the uniqueSetSuffix used to prefix/suffix object IDs for jalview
5007    * objects created from the project archive. If string is null (default for
5008    * construction) then suffix will be set automatically.
5009    * 
5010    * @param string
5011    */
5012   public void setUniqueSetSuffix(String string)
5013   {
5014     uniqueSetSuffix = string;
5015
5016   }
5017
5018   /**
5019    * uses skipList2 as the skipList for skipping views on sequence sets
5020    * associated with keys in the skipList
5021    * 
5022    * @param skipList2
5023    */
5024   public void setSkipList(Hashtable skipList2)
5025   {
5026     skipList = skipList2;
5027   }
5028
5029   /**
5030    * Reads the jar entry of given name and returns its contents, or null if the
5031    * entry is not found.
5032    * 
5033    * @param jprovider
5034    * @param jarEntryName
5035    * @return
5036    */
5037   protected String readJarEntry(jarInputStreamProvider jprovider,
5038           String jarEntryName)
5039   {
5040     String result = null;
5041     BufferedReader in = null;
5042
5043     try
5044     {
5045       /*
5046        * Reopen the jar input stream and traverse its entries to find a matching
5047        * name
5048        */
5049       JarInputStream jin = jprovider.getJarInputStream();
5050       JarEntry entry = null;
5051       do
5052       {
5053         entry = jin.getNextJarEntry();
5054       } while (entry != null && !entry.getName().equals(jarEntryName));
5055
5056       if (entry != null)
5057       {
5058         StringBuilder out = new StringBuilder(256);
5059         in = new BufferedReader(new InputStreamReader(jin, UTF_8));
5060         String data;
5061
5062         while ((data = in.readLine()) != null)
5063         {
5064           out.append(data);
5065         }
5066         result = out.toString();
5067       }
5068       else
5069       {
5070         warn("Couldn't find entry in Jalview Jar for " + jarEntryName);
5071       }
5072     } catch (Exception ex)
5073     {
5074       ex.printStackTrace();
5075     } finally
5076     {
5077       if (in != null)
5078       {
5079         try
5080         {
5081           in.close();
5082         } catch (IOException e)
5083         {
5084           // ignore
5085         }
5086       }
5087     }
5088   
5089     return result;
5090   }
5091 }