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