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