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