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