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