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