cccf34d069407975161f36813275ade50d4cd8c4
[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 = fileName + " Dataset for " + _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       // recover dataset - passing on flag indicating if this a 'viewless'
2317       // sequence set (a.k.a. a stored dataset for the project)
2318       recoverDatasetFor(vamsasSet, al, object.getJalviewModelSequence()
2319               .getViewportCount() == 0);
2320     }
2321     // ///////////////////////////////
2322
2323     Hashtable pdbloaded = new Hashtable();
2324     if (!multipleView)
2325     {
2326       // load sequence features, database references and any associated PDB
2327       // structures for the alignment
2328       for (int i = 0; i < vamsasSeq.length; i++)
2329       {
2330         if (JSEQ[i].getFeaturesCount() > 0)
2331         {
2332           Features[] features = JSEQ[i].getFeatures();
2333           for (int f = 0; f < features.length; f++)
2334           {
2335             jalview.datamodel.SequenceFeature sf = new jalview.datamodel.SequenceFeature(
2336                     features[f].getType(), features[f].getDescription(),
2337                     features[f].getStatus(), features[f].getBegin(),
2338                     features[f].getEnd(), features[f].getFeatureGroup());
2339
2340             sf.setScore(features[f].getScore());
2341             for (int od = 0; od < features[f].getOtherDataCount(); od++)
2342             {
2343               OtherData keyValue = features[f].getOtherData(od);
2344               if (keyValue.getKey().startsWith("LINK"))
2345               {
2346                 sf.addLink(keyValue.getValue());
2347               }
2348               else
2349               {
2350                 sf.setValue(keyValue.getKey(), keyValue.getValue());
2351               }
2352
2353             }
2354
2355             al.getSequenceAt(i).getDatasetSequence().addSequenceFeature(sf);
2356           }
2357         }
2358         if (vamsasSeq[i].getDBRefCount() > 0)
2359         {
2360           addDBRefs(al.getSequenceAt(i).getDatasetSequence(), vamsasSeq[i]);
2361         }
2362         if (JSEQ[i].getPdbidsCount() > 0)
2363         {
2364           Pdbids[] ids = JSEQ[i].getPdbids();
2365           for (int p = 0; p < ids.length; p++)
2366           {
2367             jalview.datamodel.PDBEntry entry = new jalview.datamodel.PDBEntry();
2368             entry.setId(ids[p].getId());
2369             entry.setType(ids[p].getType());
2370             if (ids[p].getFile() != null)
2371             {
2372               if (!pdbloaded.containsKey(ids[p].getFile()))
2373               {
2374                 entry.setFile(loadPDBFile(jprovider, ids[p].getId()));
2375               }
2376               else
2377               {
2378                 entry.setFile(pdbloaded.get(ids[p].getId()).toString());
2379               }
2380             }
2381             StructureSelectionManager.getStructureSelectionManager(
2382                     Desktop.instance)
2383                     .registerPDBEntry(entry);
2384             al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
2385           }
2386         }
2387       }
2388     } // end !multipleview
2389
2390     // ///////////////////////////////
2391     // LOAD SEQUENCE MAPPINGS
2392
2393     if (vamsasSet.getAlcodonFrameCount() > 0)
2394     {
2395       // TODO Potentially this should only be done once for all views of an
2396       // alignment
2397       AlcodonFrame[] alc = vamsasSet.getAlcodonFrame();
2398       for (int i = 0; i < alc.length; i++)
2399       {
2400         jalview.datamodel.AlignedCodonFrame cf = new jalview.datamodel.AlignedCodonFrame(
2401                 alc[i].getAlcodonCount());
2402         if (alc[i].getAlcodonCount() > 0)
2403         {
2404           Alcodon[] alcods = alc[i].getAlcodon();
2405           for (int p = 0; p < cf.codons.length; p++)
2406           {
2407             if (alcods[p].hasPos1() && alcods[p].hasPos2()
2408                     && alcods[p].hasPos3())
2409             {
2410               // translated codons require three valid positions
2411               cf.codons[p] = new int[3];
2412               cf.codons[p][0] = (int) alcods[p].getPos1();
2413               cf.codons[p][1] = (int) alcods[p].getPos2();
2414               cf.codons[p][2] = (int) alcods[p].getPos3();
2415             }
2416             else
2417             {
2418               cf.codons[p] = null;
2419             }
2420           }
2421         }
2422         if (alc[i].getAlcodMapCount() > 0)
2423         {
2424           AlcodMap[] maps = alc[i].getAlcodMap();
2425           for (int m = 0; m < maps.length; m++)
2426           {
2427             SequenceI dnaseq = (SequenceI) seqRefIds
2428                     .get(maps[m].getDnasq());
2429             // Load Mapping
2430             jalview.datamodel.Mapping mapping = null;
2431             // attach to dna sequence reference.
2432             if (maps[m].getMapping() != null)
2433             {
2434               mapping = addMapping(maps[m].getMapping());
2435             }
2436             if (dnaseq != null)
2437             {
2438               cf.addMap(dnaseq, mapping.getTo(), mapping.getMap());
2439             }
2440             else
2441             {
2442               // defer to later
2443               frefedSequence.add(new Object[]
2444               { maps[m].getDnasq(), cf, mapping });
2445             }
2446           }
2447         }
2448         al.addCodonFrame(cf);
2449       }
2450
2451     }
2452
2453     // ////////////////////////////////
2454     // LOAD ANNOTATIONS
2455     ArrayList<JvAnnotRow> autoAlan = new ArrayList<JvAnnotRow>();
2456     /**
2457      * store any annotations which forward reference a group's ID
2458      */
2459     Hashtable<String, ArrayList<jalview.datamodel.AlignmentAnnotation>> groupAnnotRefs = new Hashtable<String, ArrayList<jalview.datamodel.AlignmentAnnotation>>();
2460
2461     if (vamsasSet.getAnnotationCount() > 0)
2462     {
2463       Annotation[] an = vamsasSet.getAnnotation();
2464
2465       for (int i = 0; i < an.length; i++)
2466       {
2467         /**
2468          * test if annotation is automatically calculated for this view only
2469          */
2470         boolean autoForView = false;
2471         if (an[i].getLabel().equals("Quality")
2472                 || an[i].getLabel().equals("Conservation")
2473                 || an[i].getLabel().equals("Consensus"))
2474         {
2475           // Kludge for pre 2.5 projects which lacked the autocalculated flag
2476           autoForView = true;
2477           if (!an[i].hasAutoCalculated())
2478           {
2479             an[i].setAutoCalculated(true);
2480           }
2481         }
2482         if (autoForView
2483                 || (an[i].hasAutoCalculated() && an[i].isAutoCalculated()))
2484         {
2485           // remove ID - we don't recover annotation from other views for
2486           // view-specific annotation
2487           an[i].setId(null);
2488         }
2489
2490         // set visiblity for other annotation in this view
2491         if (an[i].getId() != null
2492                 && annotationIds.containsKey(an[i].getId()))
2493         {
2494           jalview.datamodel.AlignmentAnnotation jda = (jalview.datamodel.AlignmentAnnotation) annotationIds
2495                   .get(an[i].getId());
2496           // in principle Visible should always be true for annotation displayed
2497           // in multiple views
2498           if (an[i].hasVisible())
2499           {
2500             jda.visible = an[i].getVisible();
2501           }
2502
2503           al.addAnnotation(jda);
2504
2505           continue;
2506         }
2507         // Construct new annotation from model.
2508         AnnotationElement[] ae = an[i].getAnnotationElement();
2509         jalview.datamodel.Annotation[] anot = null;
2510         java.awt.Color firstColour = null;
2511         int anpos;
2512         if (!an[i].getScoreOnly())
2513         {
2514           anot = new jalview.datamodel.Annotation[al.getWidth()];
2515           for (int aa = 0; aa < ae.length && aa < anot.length; aa++)
2516           {
2517             anpos = ae[aa].getPosition();
2518
2519             if (anpos >= anot.length)
2520             {
2521               continue;
2522             }
2523
2524             anot[anpos] = new jalview.datamodel.Annotation(
2525
2526             ae[aa].getDisplayCharacter(), ae[aa].getDescription(),
2527                     (ae[aa].getSecondaryStructure() == null || ae[aa]
2528                             .getSecondaryStructure().length() == 0) ? ' '
2529                             : ae[aa].getSecondaryStructure().charAt(0),
2530                     ae[aa].getValue()
2531
2532             );
2533             // JBPNote: Consider verifying dataflow for IO of secondary
2534             // structure annotation read from Stockholm files
2535             // this was added to try to ensure that
2536             // if (anot[ae[aa].getPosition()].secondaryStructure>' ')
2537             // {
2538             // anot[ae[aa].getPosition()].displayCharacter = "";
2539             // }
2540             anot[anpos].colour = new java.awt.Color(ae[aa].getColour());
2541             if (firstColour == null)
2542             {
2543               firstColour = anot[anpos].colour;
2544             }
2545           }
2546         }
2547         jalview.datamodel.AlignmentAnnotation jaa = null;
2548
2549         if (an[i].getGraph())
2550         {
2551           float llim = 0, hlim = 0;
2552           // if (autoForView || an[i].isAutoCalculated()) {
2553           // hlim=11f;
2554           // }
2555           jaa = new jalview.datamodel.AlignmentAnnotation(an[i].getLabel(),
2556                   an[i].getDescription(), anot, llim, hlim,
2557                   an[i].getGraphType());
2558
2559           jaa.graphGroup = an[i].getGraphGroup();
2560           jaa._linecolour = firstColour;
2561           if (an[i].getThresholdLine() != null)
2562           {
2563             jaa.setThreshold(new jalview.datamodel.GraphLine(an[i]
2564                     .getThresholdLine().getValue(), an[i]
2565                     .getThresholdLine().getLabel(), new java.awt.Color(
2566                     an[i].getThresholdLine().getColour())));
2567
2568           }
2569           if (autoForView || an[i].isAutoCalculated())
2570           {
2571             // Hardwire the symbol display line to ensure that labels for
2572             // histograms are displayed
2573             jaa.hasText = true;
2574           }
2575         }
2576         else
2577         {
2578           jaa = new jalview.datamodel.AlignmentAnnotation(an[i].getLabel(),
2579                   an[i].getDescription(), anot);
2580           jaa._linecolour = firstColour;
2581         }
2582         // register new annotation
2583         if (an[i].getId() != null)
2584         {
2585           annotationIds.put(an[i].getId(), jaa);
2586           jaa.annotationId = an[i].getId();
2587         }
2588         // recover sequence association
2589         if (an[i].getSequenceRef() != null)
2590         {
2591           if (al.findName(an[i].getSequenceRef()) != null)
2592           {
2593             jaa.createSequenceMapping(al.findName(an[i].getSequenceRef()),
2594                     1, true);
2595             al.findName(an[i].getSequenceRef()).addAlignmentAnnotation(jaa);
2596           }
2597         }
2598         // and make a note of any group association
2599         if (an[i].getGroupRef() != null && an[i].getGroupRef().length() > 0)
2600         {
2601           ArrayList<jalview.datamodel.AlignmentAnnotation> aal = groupAnnotRefs
2602                   .get(an[i].getGroupRef());
2603           if (aal == null)
2604           {
2605             aal = new ArrayList<jalview.datamodel.AlignmentAnnotation>();
2606             groupAnnotRefs.put(an[i].getGroupRef(), aal);
2607           }
2608           aal.add(jaa);
2609         }
2610
2611         if (an[i].hasScore())
2612         {
2613           jaa.setScore(an[i].getScore());
2614         }
2615         if (an[i].hasVisible())
2616         {
2617           jaa.visible = an[i].getVisible();
2618         }
2619
2620         if (an[i].hasCentreColLabels())
2621         {
2622           jaa.centreColLabels = an[i].getCentreColLabels();
2623         }
2624
2625         if (an[i].hasScaleColLabels())
2626         {
2627           jaa.scaleColLabel = an[i].getScaleColLabels();
2628         }
2629         if (an[i].hasAutoCalculated() && an[i].isAutoCalculated())
2630         {
2631           // newer files have an 'autoCalculated' flag and store calculation
2632           // state in viewport properties
2633           jaa.autoCalculated = true; // means annotation will be marked for
2634           // update at end of load.
2635         }
2636         if (an[i].hasGraphHeight())
2637         {
2638           jaa.graphHeight = an[i].getGraphHeight();
2639         }
2640         if (an[i].hasBelowAlignment())
2641         {
2642           jaa.belowAlignment = an[i].isBelowAlignment();
2643         }
2644         jaa.setCalcId(an[i].getCalcId());
2645         if (an[i].getPropertyCount() > 0)
2646         {
2647           for (jalview.schemabinding.version2.Property prop : an[i]
2648                   .getProperty())
2649           {
2650             jaa.setProperty(prop.getName(), prop.getValue());
2651           }
2652         }
2653         if (jaa.autoCalculated)
2654         {
2655           autoAlan.add(new JvAnnotRow(i, jaa));
2656         }
2657         else
2658         // if (!autoForView)
2659         {
2660           // add autocalculated group annotation and any user created annotation
2661           // for the view
2662           al.addAnnotation(jaa);
2663         }
2664       }
2665     }
2666     // ///////////////////////
2667     // LOAD GROUPS
2668     // Create alignment markup and styles for this view
2669     if (jms.getJGroupCount() > 0)
2670     {
2671       JGroup[] groups = jms.getJGroup();
2672       boolean addAnnotSchemeGroup = false;
2673       for (int i = 0; i < groups.length; i++)
2674       {
2675         ColourSchemeI cs = null;
2676
2677         if (groups[i].getColour() != null)
2678         {
2679           if (groups[i].getColour().startsWith("ucs"))
2680           {
2681             cs = GetUserColourScheme(jms, groups[i].getColour());
2682           }
2683           else if (groups[i].getColour().equals("AnnotationColourGradient")
2684                   && groups[i].getAnnotationColours() != null)
2685           {
2686             addAnnotSchemeGroup = true;
2687             cs = null;
2688           }
2689           else
2690           {
2691             cs = ColourSchemeProperty.getColour(al, groups[i].getColour());
2692           }
2693
2694           if (cs != null)
2695           {
2696             cs.setThreshold(groups[i].getPidThreshold(), true);
2697           }
2698         }
2699
2700         Vector seqs = new Vector();
2701
2702         for (int s = 0; s < groups[i].getSeqCount(); s++)
2703         {
2704           String seqId = groups[i].getSeq(s) + "";
2705           jalview.datamodel.SequenceI ts = (jalview.datamodel.SequenceI) seqRefIds
2706                   .get(seqId);
2707
2708           if (ts != null)
2709           {
2710             seqs.addElement(ts);
2711           }
2712         }
2713
2714         if (seqs.size() < 1)
2715         {
2716           continue;
2717         }
2718
2719         jalview.datamodel.SequenceGroup sg = new jalview.datamodel.SequenceGroup(
2720                 seqs, groups[i].getName(), cs, groups[i].getDisplayBoxes(),
2721                 groups[i].getDisplayText(), groups[i].getColourText(),
2722                 groups[i].getStart(), groups[i].getEnd());
2723
2724         sg.setOutlineColour(new java.awt.Color(groups[i].getOutlineColour()));
2725
2726         sg.textColour = new java.awt.Color(groups[i].getTextCol1());
2727         sg.textColour2 = new java.awt.Color(groups[i].getTextCol2());
2728         sg.setShowNonconserved(groups[i].hasShowUnconserved() ? groups[i]
2729                 .isShowUnconserved() : false);
2730         sg.thresholdTextColour = groups[i].getTextColThreshold();
2731         if (groups[i].hasShowConsensusHistogram())
2732         {
2733           sg.setShowConsensusHistogram(groups[i].isShowConsensusHistogram());
2734         }
2735         ;
2736         if (groups[i].hasShowSequenceLogo())
2737         {
2738           sg.setshowSequenceLogo(groups[i].isShowSequenceLogo());
2739         }
2740         if (groups[i].hasNormaliseSequenceLogo())
2741         {
2742           sg.setNormaliseSequenceLogo(groups[i].isNormaliseSequenceLogo());
2743         }
2744         if (groups[i].hasIgnoreGapsinConsensus())
2745         {
2746           sg.setIgnoreGapsConsensus(groups[i].getIgnoreGapsinConsensus());
2747         }
2748         if (groups[i].getConsThreshold() != 0)
2749         {
2750           jalview.analysis.Conservation c = new jalview.analysis.Conservation(
2751                   "All", ResidueProperties.propHash, 3,
2752                   sg.getSequences(null), 0, sg.getWidth() - 1);
2753           c.calculate();
2754           c.verdict(false, 25);
2755           sg.cs.setConservation(c);
2756         }
2757
2758         if (groups[i].getId() != null && groupAnnotRefs.size() > 0)
2759         {
2760           // re-instate unique group/annotation row reference
2761           ArrayList<jalview.datamodel.AlignmentAnnotation> jaal = groupAnnotRefs
2762                   .get(groups[i].getId());
2763           if (jaal != null)
2764           {
2765             for (jalview.datamodel.AlignmentAnnotation jaa : jaal)
2766             {
2767               jaa.groupRef = sg;
2768               if (jaa.autoCalculated)
2769               {
2770                 // match up and try to set group autocalc alignment row for this
2771                 // annotation
2772                 if (jaa.label.startsWith("Consensus for "))
2773                 {
2774                   sg.setConsensus(jaa);
2775                 }
2776                 // match up and try to set group autocalc alignment row for this
2777                 // annotation
2778                 if (jaa.label.startsWith("Conservation for "))
2779                 {
2780                   sg.setConservationRow(jaa);
2781                 }
2782               }
2783             }
2784           }
2785         }
2786         al.addGroup(sg);
2787         if (addAnnotSchemeGroup)
2788         {
2789           // reconstruct the annotation colourscheme
2790           sg.cs = constructAnnotationColour(
2791                   groups[i].getAnnotationColours(), null, al, jms, false);
2792         }
2793       }
2794     }
2795     if (view == null)
2796     {
2797       // only dataset in this model, so just return.
2798       return null;
2799     }
2800     // ///////////////////////////////
2801     // LOAD VIEWPORT
2802
2803     // If we just load in the same jar file again, the sequenceSetId
2804     // will be the same, and we end up with multiple references
2805     // to the same sequenceSet. We must modify this id on load
2806     // so that each load of the file gives a unique id
2807     String uniqueSeqSetId = view.getSequenceSetId() + uniqueSetSuffix;
2808     String viewId = (view.getId() == null ? null : view.getId()
2809             + uniqueSetSuffix);
2810     AlignFrame af = null;
2811     AlignViewport av = null;
2812     // now check to see if we really need to create a new viewport.
2813     if (multipleView && viewportsAdded.size() == 0)
2814     {
2815       // We recovered an alignment for which a viewport already exists.
2816       // TODO: fix up any settings necessary for overlaying stored state onto
2817       // state recovered from another document. (may not be necessary).
2818       // we may need a binding from a viewport in memory to one recovered from
2819       // XML.
2820       // and then recover its containing af to allow the settings to be applied.
2821       // TODO: fix for vamsas demo
2822       System.err
2823               .println("About to recover a viewport for existing alignment: Sequence set ID is "
2824                       + uniqueSeqSetId);
2825       Object seqsetobj = retrieveExistingObj(uniqueSeqSetId);
2826       if (seqsetobj != null)
2827       {
2828         if (seqsetobj instanceof String)
2829         {
2830           uniqueSeqSetId = (String) seqsetobj;
2831           System.err
2832                   .println("Recovered extant sequence set ID mapping for ID : New Sequence set ID is "
2833                           + uniqueSeqSetId);
2834         }
2835         else
2836         {
2837           System.err
2838                   .println("Warning : Collision between sequence set ID string and existing jalview object mapping.");
2839         }
2840
2841       }
2842     }
2843     /**
2844      * indicate that annotation colours are applied across all groups (pre
2845      * Jalview 2.8.1 behaviour)
2846      */
2847     boolean doGroupAnnColour = isVersionStringLaterThan("2.8.1",
2848             object.getVersion());
2849
2850     AlignmentPanel ap = null;
2851     boolean isnewview = true;
2852     if (viewId != null)
2853     {
2854       // Check to see if this alignment already has a view id == viewId
2855       jalview.gui.AlignmentPanel views[] = Desktop
2856               .getAlignmentPanels(uniqueSeqSetId);
2857       if (views != null && views.length > 0)
2858       {
2859         for (int v = 0; v < views.length; v++)
2860         {
2861           if (views[v].av.getViewId().equalsIgnoreCase(viewId))
2862           {
2863             // recover the existing alignpanel, alignframe, viewport
2864             af = views[v].alignFrame;
2865             av = views[v].av;
2866             ap = views[v];
2867             // TODO: could even skip resetting view settings if we don't want to
2868             // change the local settings from other jalview processes
2869             isnewview = false;
2870           }
2871         }
2872       }
2873     }
2874
2875     if (isnewview)
2876     {
2877       af = loadViewport(file, JSEQ, hiddenSeqs, al, jms, view,
2878               uniqueSeqSetId, viewId, autoAlan);
2879       av = af.viewport;
2880       ap = af.alignPanel;
2881     }
2882     // LOAD TREES
2883     // /////////////////////////////////////
2884     if (loadTreesAndStructures && jms.getTreeCount() > 0)
2885     {
2886       try
2887       {
2888         for (int t = 0; t < jms.getTreeCount(); t++)
2889         {
2890
2891           Tree tree = jms.getTree(t);
2892
2893           TreePanel tp = (TreePanel) retrieveExistingObj(tree.getId());
2894           if (tp == null)
2895           {
2896             tp = af.ShowNewickTree(
2897                     new jalview.io.NewickFile(tree.getNewick()),
2898                     tree.getTitle(), tree.getWidth(), tree.getHeight(),
2899                     tree.getXpos(), tree.getYpos());
2900             if (tree.getId() != null)
2901             {
2902               // perhaps bind the tree id to something ?
2903             }
2904           }
2905           else
2906           {
2907             // update local tree attributes ?
2908             // TODO: should check if tp has been manipulated by user - if so its
2909             // settings shouldn't be modified
2910             tp.setTitle(tree.getTitle());
2911             tp.setBounds(new Rectangle(tree.getXpos(), tree.getYpos(), tree
2912                     .getWidth(), tree.getHeight()));
2913             tp.av = av; // af.viewport; // TODO: verify 'associate with all
2914             // views'
2915             // works still
2916             tp.treeCanvas.av = av; // af.viewport;
2917             tp.treeCanvas.ap = ap; // af.alignPanel;
2918
2919           }
2920           if (tp == null)
2921           {
2922             warn("There was a problem recovering stored Newick tree: \n"
2923                     + tree.getNewick());
2924             continue;
2925           }
2926
2927           tp.fitToWindow.setState(tree.getFitToWindow());
2928           tp.fitToWindow_actionPerformed(null);
2929
2930           if (tree.getFontName() != null)
2931           {
2932             tp.setTreeFont(new java.awt.Font(tree.getFontName(), tree
2933                     .getFontStyle(), tree.getFontSize()));
2934           }
2935           else
2936           {
2937             tp.setTreeFont(new java.awt.Font(view.getFontName(), view
2938                     .getFontStyle(), tree.getFontSize()));
2939           }
2940
2941           tp.showPlaceholders(tree.getMarkUnlinked());
2942           tp.showBootstrap(tree.getShowBootstrap());
2943           tp.showDistances(tree.getShowDistances());
2944
2945           tp.treeCanvas.threshold = tree.getThreshold();
2946
2947           if (tree.getCurrentTree())
2948           {
2949             af.viewport.setCurrentTree(tp.getTree());
2950           }
2951         }
2952
2953       } catch (Exception ex)
2954       {
2955         ex.printStackTrace();
2956       }
2957     }
2958
2959     // //LOAD STRUCTURES
2960     if (loadTreesAndStructures)
2961     {
2962       // run through all PDB ids on the alignment, and collect mappings between
2963       // jmol view ids and all sequences referring to it
2964       Hashtable<String, Object[]> jmolViewIds = new Hashtable();
2965
2966       for (int i = 0; i < JSEQ.length; i++)
2967       {
2968         if (JSEQ[i].getPdbidsCount() > 0)
2969         {
2970           Pdbids[] ids = JSEQ[i].getPdbids();
2971           for (int p = 0; p < ids.length; p++)
2972           {
2973             for (int s = 0; s < ids[p].getStructureStateCount(); s++)
2974             {
2975               // check to see if we haven't already created this structure view
2976               String sviewid = (ids[p].getStructureState(s).getViewId() == null) ? null
2977                       : ids[p].getStructureState(s).getViewId()
2978                               + uniqueSetSuffix;
2979               jalview.datamodel.PDBEntry jpdb = new jalview.datamodel.PDBEntry();
2980               // Originally : ids[p].getFile()
2981               // : TODO: verify external PDB file recovery still works in normal
2982               // jalview project load
2983               jpdb.setFile(loadPDBFile(jprovider, ids[p].getId()));
2984               jpdb.setId(ids[p].getId());
2985
2986               int x = ids[p].getStructureState(s).getXpos();
2987               int y = ids[p].getStructureState(s).getYpos();
2988               int width = ids[p].getStructureState(s).getWidth();
2989               int height = ids[p].getStructureState(s).getHeight();
2990
2991               // Probably don't need to do this anymore...
2992               // Desktop.desktop.getComponentAt(x, y);
2993               // TODO: NOW: check that this recovers the PDB file correctly.
2994               String pdbFile = loadPDBFile(jprovider, ids[p].getId());
2995               jalview.datamodel.SequenceI seq = (jalview.datamodel.SequenceI) seqRefIds
2996                       .get(JSEQ[i].getId() + "");
2997               if (sviewid == null)
2998               {
2999                 sviewid = "_jalview_pre2_4_" + x + "," + y + "," + width
3000                         + "," + height;
3001               }
3002               if (!jmolViewIds.containsKey(sviewid))
3003               {
3004                 jmolViewIds.put(sviewid, new Object[]
3005                 { new int[]
3006                 { x, y, width, height }, "",
3007                     new Hashtable<String, Object[]>(), new boolean[]
3008                     { false, false, true } });
3009                 // Legacy pre-2.7 conversion JAL-823 :
3010                 // do not assume any view has to be linked for colour by
3011                 // sequence
3012               }
3013
3014               // assemble String[] { pdb files }, String[] { id for each
3015               // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
3016               // seqs_file 2}, boolean[] {
3017               // linkAlignPanel,superposeWithAlignpanel}} from hash
3018               Object[] jmoldat = jmolViewIds.get(sviewid);
3019               ((boolean[]) jmoldat[3])[0] |= ids[p].getStructureState(s)
3020                       .hasAlignwithAlignPanel() ? ids[p].getStructureState(
3021                       s).getAlignwithAlignPanel() : false;
3022               // never colour by linked panel if not specified
3023               ((boolean[]) jmoldat[3])[1] |= ids[p].getStructureState(s)
3024                       .hasColourwithAlignPanel() ? ids[p]
3025                       .getStructureState(s).getColourwithAlignPanel()
3026                       : false;
3027               // default for pre-2.7 projects is that Jmol colouring is enabled
3028               ((boolean[]) jmoldat[3])[2] &= ids[p].getStructureState(s)
3029                       .hasColourByJmol() ? ids[p].getStructureState(s)
3030                       .getColourByJmol() : true;
3031
3032               if (((String) jmoldat[1]).length() < ids[p]
3033                       .getStructureState(s).getContent().length())
3034               {
3035                 {
3036                   jmoldat[1] = ids[p].getStructureState(s).getContent();
3037                 }
3038               }
3039               if (ids[p].getFile() != null)
3040               {
3041                 File mapkey = new File(ids[p].getFile());
3042                 Object[] seqstrmaps = (Object[]) ((Hashtable) jmoldat[2])
3043                         .get(mapkey);
3044                 if (seqstrmaps == null)
3045                 {
3046                   ((Hashtable) jmoldat[2]).put(mapkey,
3047                           seqstrmaps = new Object[]
3048                           { pdbFile, ids[p].getId(), new Vector(),
3049                               new Vector() });
3050                 }
3051                 if (!((Vector) seqstrmaps[2]).contains(seq))
3052                 {
3053                   ((Vector) seqstrmaps[2]).addElement(seq);
3054                   // ((Vector)seqstrmaps[3]).addElement(n) :
3055                   // in principle, chains
3056                   // should be stored here : do we need to
3057                   // TODO: store and recover seq/pdb_id :
3058                   // chain mappings
3059                 }
3060               }
3061               else
3062               {
3063                 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");
3064                 warn(errorMessage);
3065               }
3066             }
3067           }
3068         }
3069       }
3070       {
3071
3072         // Instantiate the associated Jmol views
3073         for (Entry<String, Object[]> entry : jmolViewIds.entrySet())
3074         {
3075           String sviewid = entry.getKey();
3076           Object[] svattrib = entry.getValue();
3077           int[] geom = (int[]) svattrib[0];
3078           String state = (String) svattrib[1];
3079           Hashtable<File, Object[]> oldFiles = (Hashtable<File, Object[]>) svattrib[2];
3080           final boolean useinJmolsuperpos = ((boolean[]) svattrib[3])[0], usetoColourbyseq = ((boolean[]) svattrib[3])[1], jmolColouring = ((boolean[]) svattrib[3])[2];
3081           int x = geom[0], y = geom[1], width = geom[2], height = geom[3];
3082           // collate the pdbfile -> sequence mappings from this view
3083           Vector<String> pdbfilenames = new Vector<String>();
3084           Vector<SequenceI[]> seqmaps = new Vector<SequenceI[]>();
3085           Vector<String> pdbids = new Vector<String>();
3086
3087           // Search to see if we've already created this Jmol view
3088           AppJmol comp = null;
3089           JInternalFrame[] frames = null;
3090           do
3091           {
3092             try
3093             {
3094               frames = Desktop.desktop.getAllFrames();
3095             } catch (ArrayIndexOutOfBoundsException e)
3096             {
3097               // occasional No such child exceptions are thrown here...
3098               frames = null;
3099               try
3100               {
3101                 Thread.sleep(10);
3102               } catch (Exception f)
3103               {
3104               }
3105               ;
3106             }
3107           } while (frames == null);
3108           // search for any Jmol windows already open from other
3109           // alignment views that exactly match the stored structure state
3110           for (int f = 0; comp == null && f < frames.length; f++)
3111           {
3112             if (frames[f] instanceof AppJmol)
3113             {
3114               if (sviewid != null
3115                       && ((AppJmol) frames[f]).getViewId().equals(sviewid))
3116               {
3117                 // post jalview 2.4 schema includes structure view id
3118                 comp = (AppJmol) frames[f];
3119               }
3120               else if (frames[f].getX() == x && frames[f].getY() == y
3121                       && frames[f].getHeight() == height
3122                       && frames[f].getWidth() == width)
3123               {
3124                 comp = (AppJmol) frames[f];
3125               }
3126             }
3127           }
3128
3129           if (comp == null)
3130           {
3131             // create a new Jmol window.
3132             // First parse the Jmol state to translate filenames loaded into the
3133             // view, and record the order in which files are shown in the Jmol
3134             // view, so we can add the sequence mappings in same order.
3135             StringBuffer newFileLoc = null;
3136             int cp = 0, ncp, ecp;
3137             while ((ncp = state.indexOf("load ", cp)) > -1)
3138             {
3139               if (newFileLoc == null)
3140               {
3141                 newFileLoc = new StringBuffer();
3142               }
3143               do
3144               {
3145                 // look for next filename in load statement
3146                 newFileLoc.append(state.substring(cp,
3147                         ncp = (state.indexOf("\"", ncp + 1) + 1)));
3148                 String oldfilenam = state.substring(ncp,
3149                         ecp = state.indexOf("\"", ncp));
3150                 // recover the new mapping data for this old filename
3151                 // have to normalize filename - since Jmol and jalview do
3152                 // filename
3153                 // translation differently.
3154                 Object[] filedat = oldFiles.get(new File(oldfilenam));
3155                 newFileLoc.append(Platform
3156                         .escapeString((String) filedat[0]));
3157                 pdbfilenames.addElement((String) filedat[0]);
3158                 pdbids.addElement((String) filedat[1]);
3159                 seqmaps.addElement(((Vector<SequenceI>) filedat[2])
3160                         .toArray(new SequenceI[0]));
3161                 newFileLoc.append("\"");
3162                 cp = ecp + 1; // advance beyond last \" and set cursor so we can
3163                               // look for next file statement.
3164               } while ((ncp = state.indexOf("/*file*/", cp)) > -1);
3165             }
3166             if (cp > 0)
3167             {
3168               // just append rest of state
3169               newFileLoc.append(state.substring(cp));
3170             }
3171             else
3172             {
3173               System.err
3174                       .print("Ignoring incomplete Jmol state for PDB ids: ");
3175               newFileLoc = new StringBuffer(state);
3176               newFileLoc.append("; load append ");
3177               for (File id : oldFiles.keySet())
3178               {
3179                 // add this and any other pdb files that should be present in
3180                 // the viewer
3181                 Object[] filedat = oldFiles.get(id);
3182                 String nfilename;
3183                 newFileLoc.append(((String) filedat[0]));
3184                 pdbfilenames.addElement((String) filedat[0]);
3185                 pdbids.addElement((String) filedat[1]);
3186                 seqmaps.addElement(((Vector<SequenceI>) filedat[2])
3187                         .toArray(new SequenceI[0]));
3188                 newFileLoc.append(" \"");
3189                 newFileLoc.append((String) filedat[0]);
3190                 newFileLoc.append("\"");
3191
3192               }
3193               newFileLoc.append(";");
3194             }
3195
3196             if (newFileLoc != null)
3197             {
3198               int histbug = newFileLoc.indexOf("history = ");
3199               histbug += 10;
3200               int diff = histbug == -1 ? -1 : newFileLoc.indexOf(";",
3201                       histbug);
3202               String val = (diff == -1) ? null : newFileLoc.substring(
3203                       histbug, diff);
3204               if (val != null && val.length() >= 4)
3205               {
3206                 if (val.contains("e"))
3207                 {
3208                   if (val.trim().equals("true"))
3209                   {
3210                     val = "1";
3211                   }
3212                   else
3213                   {
3214                     val = "0";
3215                   }
3216                   newFileLoc.replace(histbug, diff, val);
3217                 }
3218               }
3219               // TODO: assemble String[] { pdb files }, String[] { id for each
3220               // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
3221               // seqs_file 2}} from hash
3222               final String[] pdbf = pdbfilenames
3223                       .toArray(new String[pdbfilenames.size()]), id = pdbids
3224                       .toArray(new String[pdbids.size()]);
3225               final SequenceI[][] sq = seqmaps
3226                       .toArray(new SequenceI[seqmaps.size()][]);
3227               final String fileloc = newFileLoc.toString(), vid = sviewid;
3228               final AlignFrame alf = af;
3229               final java.awt.Rectangle rect = new java.awt.Rectangle(x, y,
3230                       width, height);
3231               try
3232               {
3233                 javax.swing.SwingUtilities.invokeAndWait(new Runnable()
3234                 {
3235                   @Override
3236                   public void run()
3237                   {
3238                     JalviewStructureDisplayI sview = null;
3239                     try
3240                     {
3241                       // JAL-1333 note - we probably can't migrate Jmol views to UCSF Chimera!
3242                       sview = new StructureViewer(alf.alignPanel.getStructureSelectionManager()).createView(StructureViewer.Viewer.JMOL, pdbf, id, sq, alf.alignPanel,
3243                               useinJmolsuperpos, usetoColourbyseq,
3244                               jmolColouring, fileloc, rect, vid);
3245                       addNewStructureViewer(sview);
3246                     } catch (OutOfMemoryError ex)
3247                     {
3248                       new OOMWarning("restoring structure view for PDB id "
3249                               + id, (OutOfMemoryError) ex.getCause());
3250                       if (sview != null && sview.isVisible())
3251                       {
3252                         sview.closeViewer();
3253                         sview.setVisible(false);
3254                         sview.dispose();
3255                       }
3256                     }
3257                   }
3258                 });
3259               } catch (InvocationTargetException ex)
3260               {
3261                 warn("Unexpected error when opening Jmol view.", ex);
3262
3263               } catch (InterruptedException e)
3264               {
3265                 // e.printStackTrace();
3266               }
3267             }
3268
3269           }
3270           else
3271           // if (comp != null)
3272           {
3273             // NOTE: if the jalview project is part of a shared session then
3274             // view synchronization should/could be done here.
3275
3276             // add mapping for sequences in this view to an already open Jmol
3277             // instance
3278             for (File id : oldFiles.keySet())
3279             {
3280               // add this and any other pdb files that should be present in the
3281               // viewer
3282               Object[] filedat = oldFiles.get(id);
3283               String pdbFile = (String) filedat[0];
3284               SequenceI[] seq = ((Vector<SequenceI>) filedat[2])
3285                       .toArray(new SequenceI[0]);
3286               comp.jmb.ssm.setMapping(seq, null, pdbFile,
3287                       jalview.io.AppletFormatAdapter.FILE);
3288               comp.jmb.addSequenceForStructFile(pdbFile, seq);
3289             }
3290             // and add the AlignmentPanel's reference to the Jmol view
3291             comp.addAlignmentPanel(ap);
3292             if (useinJmolsuperpos)
3293             {
3294               comp.useAlignmentPanelForSuperposition(ap);
3295             }
3296             else
3297             {
3298               comp.excludeAlignmentPanelForSuperposition(ap);
3299             }
3300             if (usetoColourbyseq)
3301             {
3302               comp.useAlignmentPanelForColourbyseq(ap, !jmolColouring);
3303             }
3304             else
3305             {
3306               comp.excludeAlignmentPanelForColourbyseq(ap);
3307             }
3308           }
3309         }
3310       }
3311     }
3312     // and finally return.
3313     return af;
3314   }
3315
3316   /**
3317    * 
3318    * @param supported
3319    *          - minimum version we are comparing against
3320    * @param version
3321    *          - version of data being processsed.
3322    * @return true if version is development/null or evaluates to the same or
3323    *         later X.Y.Z (where X,Y,Z are like [0-9]+b?[0-9]*)
3324    */
3325   private boolean isVersionStringLaterThan(String supported, String version)
3326   {
3327     if (version == null || version.equalsIgnoreCase("DEVELOPMENT BUILD")
3328             || version.equalsIgnoreCase("Test")
3329             || version.equalsIgnoreCase("AUTOMATED BUILD"))
3330     {
3331       System.err.println("Assuming project file with "
3332               + (version == null ? "null" : version)
3333               + " is compatible with Jalview version " + supported);
3334       return true;
3335     }
3336     else
3337     {
3338       StringTokenizer currentV = new StringTokenizer(supported, "."), fileV = new StringTokenizer(
3339               version, ".");
3340       while (currentV.hasMoreTokens() && fileV.hasMoreTokens())
3341       {
3342         // convert b to decimal to catch bugfix releases within a series
3343         String curT = currentV.nextToken().toLowerCase().replace('b', '.');
3344         String fileT = fileV.nextToken().toLowerCase().replace('b', '.');
3345         try
3346         {
3347           if (Float.valueOf(curT) > Float.valueOf(fileT))
3348           {
3349             // current version is newer than the version that wrote the file
3350             return false;
3351           }
3352         } catch (NumberFormatException nfe)
3353         {
3354           System.err
3355                   .println("** WARNING: Version comparison failed for tokens ("
3356                           + curT
3357                           + ") and ("
3358                           + fileT
3359                           + ")\n** Current: '"
3360                           + supported + "' and Version: '" + version + "'");
3361         }
3362       }
3363       if (currentV.hasMoreElements())
3364       {
3365         // fileV has no minor version but identical series to current
3366         return false;
3367       }
3368     }
3369     return true;
3370   }
3371
3372   Vector<JalviewStructureDisplayI> newStructureViewers = null;
3373
3374   protected void addNewStructureViewer(JalviewStructureDisplayI sview)
3375   {
3376     if (newStructureViewers != null)
3377     {
3378       sview.getBinding().setFinishedLoadingFromArchive(false);
3379       newStructureViewers.add(sview);
3380     }
3381   }
3382
3383   protected void setLoadingFinishedForNewStructureViewers()
3384   {
3385     if (newStructureViewers != null)
3386     {
3387       for (JalviewStructureDisplayI sview : newStructureViewers)
3388       {
3389         sview.getBinding().setFinishedLoadingFromArchive(true);
3390       }
3391       newStructureViewers.clear();
3392       newStructureViewers = null;
3393     }
3394   }
3395
3396   AlignFrame loadViewport(String file, JSeq[] JSEQ, Vector hiddenSeqs,
3397           Alignment al, JalviewModelSequence jms, Viewport view,
3398           String uniqueSeqSetId, String viewId,
3399           ArrayList<JvAnnotRow> autoAlan)
3400   {
3401     AlignFrame af = null;
3402     af = new AlignFrame(al, view.getWidth(), view.getHeight(),
3403             uniqueSeqSetId, viewId);
3404
3405     af.setFileName(file, "Jalview");
3406
3407     for (int i = 0; i < JSEQ.length; i++)
3408     {
3409       af.viewport.setSequenceColour(af.viewport.getAlignment()
3410               .getSequenceAt(i), new java.awt.Color(JSEQ[i].getColour()));
3411     }
3412
3413     af.viewport.gatherViewsHere = view.getGatheredViews();
3414
3415     if (view.getSequenceSetId() != null)
3416     {
3417       jalview.gui.AlignViewport av = (jalview.gui.AlignViewport) viewportsAdded
3418               .get(uniqueSeqSetId);
3419
3420       af.viewport.setSequenceSetId(uniqueSeqSetId);
3421       if (av != null)
3422       {
3423         // propagate shared settings to this new view
3424         af.viewport.historyList = av.historyList;
3425         af.viewport.redoList = av.redoList;
3426       }
3427       else
3428       {
3429         viewportsAdded.put(uniqueSeqSetId, af.viewport);
3430       }
3431       // TODO: check if this method can be called repeatedly without
3432       // side-effects if alignpanel already registered.
3433       PaintRefresher.Register(af.alignPanel, uniqueSeqSetId);
3434     }
3435     // apply Hidden regions to view.
3436     if (hiddenSeqs != null)
3437     {
3438       for (int s = 0; s < JSEQ.length; s++)
3439       {
3440         jalview.datamodel.SequenceGroup hidden = new jalview.datamodel.SequenceGroup();
3441
3442         for (int r = 0; r < JSEQ[s].getHiddenSequencesCount(); r++)
3443         {
3444           hidden.addSequence(
3445                   al.getSequenceAt(JSEQ[s].getHiddenSequences(r)), false);
3446         }
3447         af.viewport.hideRepSequences(al.getSequenceAt(s), hidden);
3448       }
3449
3450       jalview.datamodel.SequenceI[] hseqs = new jalview.datamodel.SequenceI[hiddenSeqs
3451               .size()];
3452
3453       for (int s = 0; s < hiddenSeqs.size(); s++)
3454       {
3455         hseqs[s] = (jalview.datamodel.SequenceI) hiddenSeqs.elementAt(s);
3456       }
3457
3458       af.viewport.hideSequence(hseqs);
3459
3460     }
3461     // recover view properties and display parameters
3462     if (view.getViewName() != null)
3463     {
3464       af.viewport.viewName = view.getViewName();
3465       af.setInitialTabVisible();
3466     }
3467     af.setBounds(view.getXpos(), view.getYpos(), view.getWidth(),
3468             view.getHeight());
3469
3470     af.viewport.setShowAnnotation(view.getShowAnnotation());
3471     af.viewport.setAbovePIDThreshold(view.getPidSelected());
3472
3473     af.viewport.setColourText(view.getShowColourText());
3474
3475     af.viewport.setConservationSelected(view.getConservationSelected());
3476     af.viewport.setShowJVSuffix(view.getShowFullId());
3477     af.viewport.rightAlignIds = view.getRightAlignIds();
3478     af.viewport.setFont(new java.awt.Font(view.getFontName(), view
3479             .getFontStyle(), view.getFontSize()));
3480     af.alignPanel.fontChanged();
3481     af.viewport.setRenderGaps(view.getRenderGaps());
3482     af.viewport.setWrapAlignment(view.getWrapAlignment());
3483     af.alignPanel.setWrapAlignment(view.getWrapAlignment());
3484     af.viewport.setShowAnnotation(view.getShowAnnotation());
3485     af.alignPanel.setAnnotationVisible(view.getShowAnnotation());
3486
3487     af.viewport.setShowBoxes(view.getShowBoxes());
3488
3489     af.viewport.setShowText(view.getShowText());
3490
3491     af.viewport.textColour = new java.awt.Color(view.getTextCol1());
3492     af.viewport.textColour2 = new java.awt.Color(view.getTextCol2());
3493     af.viewport.thresholdTextColour = view.getTextColThreshold();
3494     af.viewport.setShowUnconserved(view.hasShowUnconserved() ? view
3495             .isShowUnconserved() : false);
3496     af.viewport.setStartRes(view.getStartRes());
3497     af.viewport.setStartSeq(view.getStartSeq());
3498
3499     ColourSchemeI cs = null;
3500     // apply colourschemes
3501     if (view.getBgColour() != null)
3502     {
3503       if (view.getBgColour().startsWith("ucs"))
3504       {
3505         cs = GetUserColourScheme(jms, view.getBgColour());
3506       }
3507       else if (view.getBgColour().startsWith("Annotation"))
3508       {
3509         AnnotationColours viewAnnColour = view.getAnnotationColours();
3510         cs = constructAnnotationColour(viewAnnColour, af, al, jms, true);
3511
3512         // annpos
3513
3514       }
3515       else
3516       {
3517         cs = ColourSchemeProperty.getColour(al, view.getBgColour());
3518       }
3519
3520       if (cs != null)
3521       {
3522         cs.setThreshold(view.getPidThreshold(), true);
3523         cs.setConsensus(af.viewport.getSequenceConsensusHash());
3524       }
3525     }
3526
3527     af.viewport.setGlobalColourScheme(cs);
3528     af.viewport.setColourAppliesToAllGroups(false);
3529
3530     if (view.getConservationSelected() && cs != null)
3531     {
3532       cs.setConservationInc(view.getConsThreshold());
3533     }
3534
3535     af.changeColour(cs);
3536
3537     af.viewport.setColourAppliesToAllGroups(true);
3538
3539     if (view.getShowSequenceFeatures())
3540     {
3541       af.viewport.showSequenceFeatures = true;
3542     }
3543     if (view.hasCentreColumnLabels())
3544     {
3545       af.viewport.setCentreColumnLabels(view.getCentreColumnLabels());
3546     }
3547     if (view.hasIgnoreGapsinConsensus())
3548     {
3549       af.viewport.setIgnoreGapsConsensus(view.getIgnoreGapsinConsensus(),
3550               null);
3551     }
3552     if (view.hasFollowHighlight())
3553     {
3554       af.viewport.followHighlight = view.getFollowHighlight();
3555     }
3556     if (view.hasFollowSelection())
3557     {
3558       af.viewport.followSelection = view.getFollowSelection();
3559     }
3560     if (view.hasShowConsensusHistogram())
3561     {
3562       af.viewport.setShowConsensusHistogram(view
3563               .getShowConsensusHistogram());
3564     }
3565     else
3566     {
3567       af.viewport.setShowConsensusHistogram(true);
3568     }
3569     if (view.hasShowSequenceLogo())
3570     {
3571       af.viewport.setShowSequenceLogo(view.getShowSequenceLogo());
3572     }
3573     else
3574     {
3575       af.viewport.setShowSequenceLogo(false);
3576     }
3577     if (view.hasNormaliseSequenceLogo())
3578     {
3579       af.viewport.setNormaliseSequenceLogo(view.getNormaliseSequenceLogo());
3580     }
3581     if (view.hasShowDbRefTooltip())
3582     {
3583       af.viewport.setShowDbRefs(view.getShowDbRefTooltip());
3584     }
3585     if (view.hasShowNPfeatureTooltip())
3586     {
3587       af.viewport.setShowNpFeats(view.hasShowNPfeatureTooltip());
3588     }
3589     if (view.hasShowGroupConsensus())
3590     {
3591       af.viewport.setShowGroupConsensus(view.getShowGroupConsensus());
3592     }
3593     else
3594     {
3595       af.viewport.setShowGroupConsensus(false);
3596     }
3597     if (view.hasShowGroupConservation())
3598     {
3599       af.viewport.setShowGroupConservation(view.getShowGroupConservation());
3600     }
3601     else
3602     {
3603       af.viewport.setShowGroupConservation(false);
3604     }
3605
3606     // recover featre settings
3607     if (jms.getFeatureSettings() != null)
3608     {
3609       af.viewport.featuresDisplayed = new Hashtable();
3610       String[] renderOrder = new String[jms.getFeatureSettings()
3611               .getSettingCount()];
3612       for (int fs = 0; fs < jms.getFeatureSettings().getSettingCount(); fs++)
3613       {
3614         Setting setting = jms.getFeatureSettings().getSetting(fs);
3615         if (setting.hasMincolour())
3616         {
3617           GraduatedColor gc = setting.hasMin() ? new GraduatedColor(
3618                   new java.awt.Color(setting.getMincolour()),
3619                   new java.awt.Color(setting.getColour()),
3620                   setting.getMin(), setting.getMax()) : new GraduatedColor(
3621                   new java.awt.Color(setting.getMincolour()),
3622                   new java.awt.Color(setting.getColour()), 0, 1);
3623           if (setting.hasThreshold())
3624           {
3625             gc.setThresh(setting.getThreshold());
3626             gc.setThreshType(setting.getThreshstate());
3627           }
3628           gc.setAutoScaled(true); // default
3629           if (setting.hasAutoScale())
3630           {
3631             gc.setAutoScaled(setting.getAutoScale());
3632           }
3633           if (setting.hasColourByLabel())
3634           {
3635             gc.setColourByLabel(setting.getColourByLabel());
3636           }
3637           // and put in the feature colour table.
3638           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setColour(
3639                   setting.getType(), gc);
3640         }
3641         else
3642         {
3643           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setColour(
3644                   setting.getType(),
3645                   new java.awt.Color(setting.getColour()));
3646         }
3647         renderOrder[fs] = setting.getType();
3648         if (setting.hasOrder())
3649         {
3650           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setOrder(
3651                   setting.getType(), setting.getOrder());
3652         }
3653         else
3654         {
3655           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setOrder(
3656                   setting.getType(),
3657                   fs / jms.getFeatureSettings().getSettingCount());
3658         }
3659         if (setting.getDisplay())
3660         {
3661           af.viewport.featuresDisplayed.put(setting.getType(), new Integer(
3662                   setting.getColour()));
3663         }
3664       }
3665       af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().renderOrder = renderOrder;
3666       Hashtable fgtable;
3667       af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().featureGroups = fgtable = new Hashtable();
3668       for (int gs = 0; gs < jms.getFeatureSettings().getGroupCount(); gs++)
3669       {
3670         Group grp = jms.getFeatureSettings().getGroup(gs);
3671         fgtable.put(grp.getName(), new Boolean(grp.getDisplay()));
3672       }
3673     }
3674
3675     if (view.getHiddenColumnsCount() > 0)
3676     {
3677       for (int c = 0; c < view.getHiddenColumnsCount(); c++)
3678       {
3679         af.viewport.hideColumns(view.getHiddenColumns(c).getStart(), view
3680                 .getHiddenColumns(c).getEnd() // +1
3681                 );
3682       }
3683     }
3684     if (view.getCalcIdParam() != null)
3685     {
3686       for (CalcIdParam calcIdParam : view.getCalcIdParam())
3687       {
3688         if (calcIdParam != null)
3689         {
3690           if (recoverCalcIdParam(calcIdParam, af.viewport))
3691           {
3692           }
3693           else
3694           {
3695             warn("Couldn't recover parameters for "
3696                     + calcIdParam.getCalcId());
3697           }
3698         }
3699       }
3700     }
3701     af.setMenusFromViewport(af.viewport);
3702     // TODO: we don't need to do this if the viewport is aready visible.
3703     Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(),
3704             view.getHeight());
3705     af.alignPanel.updateAnnotation(false, true); // recompute any autoannotation
3706     reorderAutoannotation(af, al, autoAlan);
3707     af.alignPanel.alignmentChanged();
3708     return af;
3709   }
3710
3711   private ColourSchemeI constructAnnotationColour(
3712           AnnotationColours viewAnnColour, AlignFrame af, Alignment al,
3713           JalviewModelSequence jms, boolean checkGroupAnnColour)
3714   {
3715     boolean propagateAnnColour = false;
3716     ColourSchemeI cs = null;
3717     AlignmentI annAlignment = af != null ? af.viewport.getAlignment() : al;
3718     if (checkGroupAnnColour && al.getGroups() != null
3719             && al.getGroups().size() > 0)
3720     {
3721       // pre 2.8.1 behaviour
3722       // check to see if we should transfer annotation colours
3723       propagateAnnColour = true;
3724       for (jalview.datamodel.SequenceGroup sg : al.getGroups())
3725       {
3726         if (sg.cs instanceof AnnotationColourGradient)
3727         {
3728           propagateAnnColour = false;
3729         }
3730       }
3731     }
3732     // int find annotation
3733     if (annAlignment.getAlignmentAnnotation() != null)
3734     {
3735       for (int i = 0; i < annAlignment.getAlignmentAnnotation().length; i++)
3736       {
3737         if (annAlignment.getAlignmentAnnotation()[i].label
3738                 .equals(viewAnnColour.getAnnotation()))
3739         {
3740           if (annAlignment.getAlignmentAnnotation()[i].getThreshold() == null)
3741           {
3742             annAlignment.getAlignmentAnnotation()[i]
3743                     .setThreshold(new jalview.datamodel.GraphLine(
3744                             viewAnnColour.getThreshold(), "Threshold",
3745                             java.awt.Color.black)
3746
3747                     );
3748           }
3749
3750           if (viewAnnColour.getColourScheme().equals("None"))
3751           {
3752             cs = new AnnotationColourGradient(
3753                     annAlignment.getAlignmentAnnotation()[i],
3754                     new java.awt.Color(viewAnnColour.getMinColour()),
3755                     new java.awt.Color(viewAnnColour.getMaxColour()),
3756                     viewAnnColour.getAboveThreshold());
3757           }
3758           else if (viewAnnColour.getColourScheme().startsWith("ucs"))
3759           {
3760             cs = new AnnotationColourGradient(
3761                     annAlignment.getAlignmentAnnotation()[i],
3762                     GetUserColourScheme(jms,
3763                             viewAnnColour.getColourScheme()),
3764                     viewAnnColour.getAboveThreshold());
3765           }
3766           else
3767           {
3768             cs = new AnnotationColourGradient(
3769                     annAlignment.getAlignmentAnnotation()[i],
3770                     ColourSchemeProperty.getColour(al,
3771                             viewAnnColour.getColourScheme()),
3772                     viewAnnColour.getAboveThreshold());
3773           }
3774           if (viewAnnColour.hasPerSequence())
3775           {
3776             ((AnnotationColourGradient) cs).setSeqAssociated(viewAnnColour
3777                     .isPerSequence());
3778           }
3779           if (viewAnnColour.hasPredefinedColours())
3780           {
3781             ((AnnotationColourGradient) cs)
3782                     .setPredefinedColours(viewAnnColour
3783                             .isPredefinedColours());
3784           }
3785           if (propagateAnnColour && al.getGroups() != null)
3786           {
3787             // Also use these settings for all the groups
3788             for (int g = 0; g < al.getGroups().size(); g++)
3789             {
3790               jalview.datamodel.SequenceGroup sg = al.getGroups().get(g);
3791
3792               if (sg.cs == null)
3793               {
3794                 continue;
3795               }
3796
3797               /*
3798                * if (viewAnnColour.getColourScheme().equals("None" )) { sg.cs =
3799                * new AnnotationColourGradient(
3800                * annAlignment.getAlignmentAnnotation()[i], new
3801                * java.awt.Color(viewAnnColour. getMinColour()), new
3802                * java.awt.Color(viewAnnColour. getMaxColour()),
3803                * viewAnnColour.getAboveThreshold()); } else
3804                */
3805               {
3806                 sg.cs = new AnnotationColourGradient(
3807                         annAlignment.getAlignmentAnnotation()[i], sg.cs,
3808                         viewAnnColour.getAboveThreshold());
3809                 if (cs instanceof AnnotationColourGradient)
3810                 {
3811                   if (viewAnnColour.hasPerSequence())
3812                   {
3813                     ((AnnotationColourGradient) cs)
3814                             .setSeqAssociated(viewAnnColour.isPerSequence());
3815                   }
3816                   if (viewAnnColour.hasPredefinedColours())
3817                   {
3818                     ((AnnotationColourGradient) cs)
3819                             .setPredefinedColours(viewAnnColour
3820                                     .isPredefinedColours());
3821                   }
3822                 }
3823               }
3824
3825             }
3826           }
3827
3828           break;
3829         }
3830
3831       }
3832     }
3833     return cs;
3834   }
3835
3836   private void reorderAutoannotation(AlignFrame af, Alignment al,
3837           ArrayList<JvAnnotRow> autoAlan)
3838   {
3839     // copy over visualization settings for autocalculated annotation in the
3840     // view
3841     if (al.getAlignmentAnnotation() != null)
3842     {
3843       /**
3844        * Kludge for magic autoannotation names (see JAL-811)
3845        */
3846       String[] magicNames = new String[]
3847       { "Consensus", "Quality", "Conservation" };
3848       JvAnnotRow nullAnnot = new JvAnnotRow(-1, null);
3849       Hashtable<String, JvAnnotRow> visan = new Hashtable<String, JvAnnotRow>();
3850       for (String nm : magicNames)
3851       {
3852         visan.put(nm, nullAnnot);
3853       }
3854       for (JvAnnotRow auan : autoAlan)
3855       {
3856         visan.put(auan.template.label
3857                 + (auan.template.getCalcId() == null ? "" : "\t"
3858                         + auan.template.getCalcId()), auan);
3859       }
3860       int hSize = al.getAlignmentAnnotation().length;
3861       ArrayList<JvAnnotRow> reorder = new ArrayList<JvAnnotRow>();
3862       // work through any autoCalculated annotation already on the view
3863       // removing it if it should be placed in a different location on the
3864       // annotation panel.
3865       List<String> remains = new ArrayList(visan.keySet());
3866       for (int h = 0; h < hSize; h++)
3867       {
3868         jalview.datamodel.AlignmentAnnotation jalan = al
3869                 .getAlignmentAnnotation()[h];
3870         if (jalan.autoCalculated)
3871         {
3872           String k;
3873           JvAnnotRow valan = visan.get(k = jalan.label);
3874           if (jalan.getCalcId() != null)
3875           {
3876             valan = visan.get(k = jalan.label + "\t" + jalan.getCalcId());
3877           }
3878
3879           if (valan != null)
3880           {
3881             // delete the auto calculated row from the alignment
3882             al.deleteAnnotation(jalan, false);
3883             remains.remove(k);
3884             hSize--;
3885             h--;
3886             if (valan != nullAnnot)
3887             {
3888               if (jalan != valan.template)
3889               {
3890                 // newly created autoannotation row instance
3891                 // so keep a reference to the visible annotation row
3892                 // and copy over all relevant attributes
3893                 if (valan.template.graphHeight >= 0)
3894
3895                 {
3896                   jalan.graphHeight = valan.template.graphHeight;
3897                 }
3898                 jalan.visible = valan.template.visible;
3899               }
3900               reorder.add(new JvAnnotRow(valan.order, jalan));
3901             }
3902           }
3903         }
3904       }
3905       // Add any (possibly stale) autocalculated rows that were not appended to
3906       // the view during construction
3907       for (String other : remains)
3908       {
3909         JvAnnotRow othera = visan.get(other);
3910         if (othera != nullAnnot && othera.template.getCalcId() != null
3911                 && othera.template.getCalcId().length() > 0)
3912         {
3913           reorder.add(othera);
3914         }
3915       }
3916       // now put the automatic annotation in its correct place
3917       int s = 0, srt[] = new int[reorder.size()];
3918       JvAnnotRow[] rws = new JvAnnotRow[reorder.size()];
3919       for (JvAnnotRow jvar : reorder)
3920       {
3921         rws[s] = jvar;
3922         srt[s++] = jvar.order;
3923       }
3924       reorder.clear();
3925       jalview.util.QuickSort.sort(srt, rws);
3926       // and re-insert the annotation at its correct position
3927       for (JvAnnotRow jvar : rws)
3928       {
3929         al.addAnnotation(jvar.template, jvar.order);
3930       }
3931       af.alignPanel.adjustAnnotationHeight();
3932     }
3933   }
3934
3935   Hashtable skipList = null;
3936
3937   /**
3938    * TODO remove this method
3939    * 
3940    * @param view
3941    * @return AlignFrame bound to sequenceSetId from view, if one exists. private
3942    *         AlignFrame getSkippedFrame(Viewport view) { if (skipList==null) {
3943    *         throw new Error("Implementation Error. No skipList defined for this
3944    *         Jalview2XML instance."); } return (AlignFrame)
3945    *         skipList.get(view.getSequenceSetId()); }
3946    */
3947
3948   /**
3949    * Check if the Jalview view contained in object should be skipped or not.
3950    * 
3951    * @param object
3952    * @return true if view's sequenceSetId is a key in skipList
3953    */
3954   private boolean skipViewport(JalviewModel object)
3955   {
3956     if (skipList == null)
3957     {
3958       return false;
3959     }
3960     String id;
3961     if (skipList.containsKey(id = object.getJalviewModelSequence()
3962             .getViewport()[0].getSequenceSetId()))
3963     {
3964       if (Cache.log != null && Cache.log.isDebugEnabled())
3965       {
3966         Cache.log.debug("Skipping seuqence set id " + id);
3967       }
3968       return true;
3969     }
3970     return false;
3971   }
3972
3973   public void AddToSkipList(AlignFrame af)
3974   {
3975     if (skipList == null)
3976     {
3977       skipList = new Hashtable();
3978     }
3979     skipList.put(af.getViewport().getSequenceSetId(), af);
3980   }
3981
3982   public void clearSkipList()
3983   {
3984     if (skipList != null)
3985     {
3986       skipList.clear();
3987       skipList = null;
3988     }
3989   }
3990
3991   private void recoverDatasetFor(SequenceSet vamsasSet, Alignment al,
3992           boolean ignoreUnrefed)
3993   {
3994     jalview.datamodel.Alignment ds = getDatasetFor(vamsasSet.getDatasetId());
3995     Vector dseqs = null;
3996     if (ds == null)
3997     {
3998       // create a list of new dataset sequences
3999       dseqs = new Vector();
4000     }
4001     for (int i = 0, iSize = vamsasSet.getSequenceCount(); i < iSize; i++)
4002     {
4003       Sequence vamsasSeq = vamsasSet.getSequence(i);
4004       ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs, ignoreUnrefed);
4005     }
4006     // create a new dataset
4007     if (ds == null)
4008     {
4009       SequenceI[] dsseqs = new SequenceI[dseqs.size()];
4010       dseqs.copyInto(dsseqs);
4011       ds = new jalview.datamodel.Alignment(dsseqs);
4012       debug("Created new dataset " + vamsasSet.getDatasetId()
4013               + " for alignment " + System.identityHashCode(al));
4014       addDatasetRef(vamsasSet.getDatasetId(), ds);
4015     }
4016     // set the dataset for the newly imported alignment.
4017     if (al.getDataset() == null && !ignoreUnrefed)
4018     {
4019       al.setDataset(ds);
4020     }
4021   }
4022
4023   /**
4024    * 
4025    * @param vamsasSeq
4026    *          sequence definition to create/merge dataset sequence for
4027    * @param ds
4028    *          dataset alignment
4029    * @param dseqs
4030    *          vector to add new dataset sequence to
4031    */
4032   private void ensureJalviewDatasetSequence(Sequence vamsasSeq,
4033           AlignmentI ds, Vector dseqs, boolean ignoreUnrefed)
4034   {
4035     // JBP TODO: Check this is called for AlCodonFrames to support recovery of
4036     // xRef Codon Maps
4037     jalview.datamodel.Sequence sq = (jalview.datamodel.Sequence) seqRefIds
4038             .get(vamsasSeq.getId());
4039     jalview.datamodel.SequenceI dsq = null;
4040     if (sq != null && sq.getDatasetSequence() != null)
4041     {
4042       dsq = sq.getDatasetSequence();
4043     }
4044     if (sq == null && ignoreUnrefed)
4045     {
4046       return;
4047     }
4048     String sqid = vamsasSeq.getDsseqid();
4049     if (dsq == null)
4050     {
4051       // need to create or add a new dataset sequence reference to this sequence
4052       if (sqid != null)
4053       {
4054         dsq = (jalview.datamodel.SequenceI) seqRefIds.get(sqid);
4055       }
4056       // check again
4057       if (dsq == null)
4058       {
4059         // make a new dataset sequence
4060         dsq = sq.createDatasetSequence();
4061         if (sqid == null)
4062         {
4063           // make up a new dataset reference for this sequence
4064           sqid = seqHash(dsq);
4065         }
4066         dsq.setVamsasId(uniqueSetSuffix + sqid);
4067         seqRefIds.put(sqid, dsq);
4068         if (ds == null)
4069         {
4070           if (dseqs != null)
4071           {
4072             dseqs.addElement(dsq);
4073           }
4074         }
4075         else
4076         {
4077           ds.addSequence(dsq);
4078         }
4079       }
4080       else
4081       {
4082         if (sq != dsq)
4083         { // make this dataset sequence sq's dataset sequence
4084           sq.setDatasetSequence(dsq);
4085           // and update the current dataset alignment
4086           if (ds == null)
4087           {
4088             if (dseqs != null)
4089             {
4090               if (!dseqs.contains(dsq))
4091               {
4092                 dseqs.add(dsq);
4093               }
4094             }
4095             else
4096             {
4097               if (ds.findIndex(dsq) < 0)
4098               {
4099                 ds.addSequence(dsq);
4100               }
4101             }
4102           }
4103         }
4104       }
4105     }
4106     // TODO: refactor this as a merge dataset sequence function
4107     // now check that sq (the dataset sequence) sequence really is the union of
4108     // all references to it
4109     // boolean pre = sq.getStart() < dsq.getStart();
4110     // boolean post = sq.getEnd() > dsq.getEnd();
4111     // if (pre || post)
4112     if (sq != dsq)
4113     {
4114       StringBuffer sb = new StringBuffer();
4115       String newres = jalview.analysis.AlignSeq.extractGaps(
4116               jalview.util.Comparison.GapChars, sq.getSequenceAsString());
4117       if (!newres.equalsIgnoreCase(dsq.getSequenceAsString())
4118               && newres.length() > dsq.getLength())
4119       {
4120         // Update with the longer sequence.
4121         synchronized (dsq)
4122         {
4123           /*
4124            * if (pre) { sb.insert(0, newres .substring(0, dsq.getStart() -
4125            * sq.getStart())); dsq.setStart(sq.getStart()); } if (post) {
4126            * sb.append(newres.substring(newres.length() - sq.getEnd() -
4127            * dsq.getEnd())); dsq.setEnd(sq.getEnd()); }
4128            */
4129           dsq.setSequence(newres);
4130         }
4131         // TODO: merges will never happen if we 'know' we have the real dataset
4132         // sequence - this should be detected when id==dssid
4133         System.err
4134                 .println("DEBUG Notice:  Merged dataset sequence (if you see this often, post at http://issues.jalview.org/browse/JAL-1474)"); // ("
4135         // + (pre ? "prepended" : "") + " "
4136         // + (post ? "appended" : ""));
4137       }
4138     }
4139   }
4140
4141   java.util.Hashtable datasetIds = null;
4142
4143   java.util.IdentityHashMap dataset2Ids = null;
4144
4145   private Alignment getDatasetFor(String datasetId)
4146   {
4147     if (datasetIds == null)
4148     {
4149       datasetIds = new Hashtable();
4150       return null;
4151     }
4152     if (datasetIds.containsKey(datasetId))
4153     {
4154       return (Alignment) datasetIds.get(datasetId);
4155     }
4156     return null;
4157   }
4158
4159   private void addDatasetRef(String datasetId, Alignment dataset)
4160   {
4161     if (datasetIds == null)
4162     {
4163       datasetIds = new Hashtable();
4164     }
4165     datasetIds.put(datasetId, dataset);
4166   }
4167
4168   /**
4169    * make a new dataset ID for this jalview dataset alignment
4170    * 
4171    * @param dataset
4172    * @return
4173    */
4174   private String getDatasetIdRef(jalview.datamodel.Alignment dataset)
4175   {
4176     if (dataset.getDataset() != null)
4177     {
4178       warn("Serious issue!  Dataset Object passed to getDatasetIdRef is not a Jalview DATASET alignment...");
4179     }
4180     String datasetId = makeHashCode(dataset, null);
4181     if (datasetId == null)
4182     {
4183       // make a new datasetId and record it
4184       if (dataset2Ids == null)
4185       {
4186         dataset2Ids = new IdentityHashMap();
4187       }
4188       else
4189       {
4190         datasetId = (String) dataset2Ids.get(dataset);
4191       }
4192       if (datasetId == null)
4193       {
4194         datasetId = "ds" + dataset2Ids.size() + 1;
4195         dataset2Ids.put(dataset, datasetId);
4196       }
4197     }
4198     return datasetId;
4199   }
4200
4201   private void addDBRefs(SequenceI datasetSequence, Sequence sequence)
4202   {
4203     for (int d = 0; d < sequence.getDBRefCount(); d++)
4204     {
4205       DBRef dr = sequence.getDBRef(d);
4206       jalview.datamodel.DBRefEntry entry = new jalview.datamodel.DBRefEntry(
4207               sequence.getDBRef(d).getSource(), sequence.getDBRef(d)
4208                       .getVersion(), sequence.getDBRef(d).getAccessionId());
4209       if (dr.getMapping() != null)
4210       {
4211         entry.setMap(addMapping(dr.getMapping()));
4212       }
4213       datasetSequence.addDBRef(entry);
4214     }
4215   }
4216
4217   private jalview.datamodel.Mapping addMapping(Mapping m)
4218   {
4219     SequenceI dsto = null;
4220     // Mapping m = dr.getMapping();
4221     int fr[] = new int[m.getMapListFromCount() * 2];
4222     Enumeration f = m.enumerateMapListFrom();
4223     for (int _i = 0; f.hasMoreElements(); _i += 2)
4224     {
4225       MapListFrom mf = (MapListFrom) f.nextElement();
4226       fr[_i] = mf.getStart();
4227       fr[_i + 1] = mf.getEnd();
4228     }
4229     int fto[] = new int[m.getMapListToCount() * 2];
4230     f = m.enumerateMapListTo();
4231     for (int _i = 0; f.hasMoreElements(); _i += 2)
4232     {
4233       MapListTo mf = (MapListTo) f.nextElement();
4234       fto[_i] = mf.getStart();
4235       fto[_i + 1] = mf.getEnd();
4236     }
4237     jalview.datamodel.Mapping jmap = new jalview.datamodel.Mapping(dsto,
4238             fr, fto, (int) m.getMapFromUnit(), (int) m.getMapToUnit());
4239     if (m.getMappingChoice() != null)
4240     {
4241       MappingChoice mc = m.getMappingChoice();
4242       if (mc.getDseqFor() != null)
4243       {
4244         String dsfor = "" + mc.getDseqFor();
4245         if (seqRefIds.containsKey(dsfor))
4246         {
4247           /**
4248            * recover from hash
4249            */
4250           jmap.setTo((SequenceI) seqRefIds.get(dsfor));
4251         }
4252         else
4253         {
4254           frefedSequence.add(new Object[]
4255           { dsfor, jmap });
4256         }
4257       }
4258       else
4259       {
4260         /**
4261          * local sequence definition
4262          */
4263         Sequence ms = mc.getSequence();
4264         jalview.datamodel.Sequence djs = null;
4265         String sqid = ms.getDsseqid();
4266         if (sqid != null && sqid.length() > 0)
4267         {
4268           /*
4269            * recover dataset sequence
4270            */
4271           djs = (jalview.datamodel.Sequence) seqRefIds.get(sqid);
4272         }
4273         else
4274         {
4275           System.err
4276                   .println("Warning - making up dataset sequence id for DbRef sequence map reference");
4277           sqid = ((Object) ms).toString(); // make up a new hascode for
4278           // undefined dataset sequence hash
4279           // (unlikely to happen)
4280         }
4281
4282         if (djs == null)
4283         {
4284           /**
4285            * make a new dataset sequence and add it to refIds hash
4286            */
4287           djs = new jalview.datamodel.Sequence(ms.getName(),
4288                   ms.getSequence());
4289           djs.setStart(jmap.getMap().getToLowest());
4290           djs.setEnd(jmap.getMap().getToHighest());
4291           djs.setVamsasId(uniqueSetSuffix + sqid);
4292           jmap.setTo(djs);
4293           seqRefIds.put(sqid, djs);
4294
4295         }
4296         jalview.bin.Cache.log.debug("about to recurse on addDBRefs.");
4297         addDBRefs(djs, ms);
4298
4299       }
4300     }
4301     return (jmap);
4302
4303   }
4304
4305   public jalview.gui.AlignmentPanel copyAlignPanel(AlignmentPanel ap,
4306           boolean keepSeqRefs)
4307   {
4308     initSeqRefs();
4309     jalview.schemabinding.version2.JalviewModel jm = SaveState(ap, null,
4310             null);
4311
4312     if (!keepSeqRefs)
4313     {
4314       clearSeqRefs();
4315       jm.getJalviewModelSequence().getViewport(0).setSequenceSetId(null);
4316     }
4317     else
4318     {
4319       uniqueSetSuffix = "";
4320       jm.getJalviewModelSequence().getViewport(0).setId(null); // we don't
4321       // overwrite the
4322       // view we just
4323       // copied
4324     }
4325     if (this.frefedSequence == null)
4326     {
4327       frefedSequence = new Vector();
4328     }
4329
4330     viewportsAdded = new Hashtable();
4331
4332     AlignFrame af = LoadFromObject(jm, null, false, null);
4333     af.alignPanels.clear();
4334     af.closeMenuItem_actionPerformed(true);
4335
4336     /*
4337      * if(ap.av.getAlignment().getAlignmentAnnotation()!=null) { for(int i=0;
4338      * i<ap.av.getAlignment().getAlignmentAnnotation().length; i++) {
4339      * if(!ap.av.getAlignment().getAlignmentAnnotation()[i].autoCalculated) {
4340      * af.alignPanel.av.getAlignment().getAlignmentAnnotation()[i] =
4341      * ap.av.getAlignment().getAlignmentAnnotation()[i]; } } }
4342      */
4343
4344     return af.alignPanel;
4345   }
4346
4347   /**
4348    * flag indicating if hashtables should be cleared on finalization TODO this
4349    * flag may not be necessary
4350    */
4351   private final boolean _cleartables = true;
4352
4353   private Hashtable jvids2vobj;
4354
4355   /*
4356    * (non-Javadoc)
4357    * 
4358    * @see java.lang.Object#finalize()
4359    */
4360   @Override
4361   protected void finalize() throws Throwable
4362   {
4363     // really make sure we have no buried refs left.
4364     if (_cleartables)
4365     {
4366       clearSeqRefs();
4367     }
4368     this.seqRefIds = null;
4369     this.seqsToIds = null;
4370     super.finalize();
4371   }
4372
4373   private void warn(String msg)
4374   {
4375     warn(msg, null);
4376   }
4377
4378   private void warn(String msg, Exception e)
4379   {
4380     if (Cache.log != null)
4381     {
4382       if (e != null)
4383       {
4384         Cache.log.warn(msg, e);
4385       }
4386       else
4387       {
4388         Cache.log.warn(msg);
4389       }
4390     }
4391     else
4392     {
4393       System.err.println("Warning: " + msg);
4394       if (e != null)
4395       {
4396         e.printStackTrace();
4397       }
4398     }
4399   }
4400
4401   private void debug(String string)
4402   {
4403     debug(string, null);
4404   }
4405
4406   private void debug(String msg, Exception e)
4407   {
4408     if (Cache.log != null)
4409     {
4410       if (e != null)
4411       {
4412         Cache.log.debug(msg, e);
4413       }
4414       else
4415       {
4416         Cache.log.debug(msg);
4417       }
4418     }
4419     else
4420     {
4421       System.err.println("Warning: " + msg);
4422       if (e != null)
4423       {
4424         e.printStackTrace();
4425       }
4426     }
4427   }
4428
4429   /**
4430    * set the object to ID mapping tables used to write/recover objects and XML
4431    * ID strings for the jalview project. If external tables are provided then
4432    * finalize and clearSeqRefs will not clear the tables when the Jalview2XML
4433    * object goes out of scope. - also populates the datasetIds hashtable with
4434    * alignment objects containing dataset sequences
4435    * 
4436    * @param vobj2jv
4437    *          Map from ID strings to jalview datamodel
4438    * @param jv2vobj
4439    *          Map from jalview datamodel to ID strings
4440    * 
4441    * 
4442    */
4443   public void setObjectMappingTables(Hashtable vobj2jv,
4444           IdentityHashMap jv2vobj)
4445   {
4446     this.jv2vobj = jv2vobj;
4447     this.vobj2jv = vobj2jv;
4448     Iterator ds = jv2vobj.keySet().iterator();
4449     String id;
4450     while (ds.hasNext())
4451     {
4452       Object jvobj = ds.next();
4453       id = jv2vobj.get(jvobj).toString();
4454       if (jvobj instanceof jalview.datamodel.Alignment)
4455       {
4456         if (((jalview.datamodel.Alignment) jvobj).getDataset() == null)
4457         {
4458           addDatasetRef(id, (jalview.datamodel.Alignment) jvobj);
4459         }
4460       }
4461       else if (jvobj instanceof jalview.datamodel.Sequence)
4462       {
4463         // register sequence object so the XML parser can recover it.
4464         if (seqRefIds == null)
4465         {
4466           seqRefIds = new Hashtable();
4467         }
4468         if (seqsToIds == null)
4469         {
4470           seqsToIds = new IdentityHashMap();
4471         }
4472         seqRefIds.put(jv2vobj.get(jvobj).toString(), jvobj);
4473         seqsToIds.put(jvobj, id);
4474       }
4475       else if (jvobj instanceof jalview.datamodel.AlignmentAnnotation)
4476       {
4477         if (annotationIds == null)
4478         {
4479           annotationIds = new Hashtable();
4480         }
4481         String anid;
4482         annotationIds.put(anid = jv2vobj.get(jvobj).toString(), jvobj);
4483         jalview.datamodel.AlignmentAnnotation jvann = (jalview.datamodel.AlignmentAnnotation) jvobj;
4484         if (jvann.annotationId == null)
4485         {
4486           jvann.annotationId = anid;
4487         }
4488         if (!jvann.annotationId.equals(anid))
4489         {
4490           // TODO verify that this is the correct behaviour
4491           this.warn("Overriding Annotation ID for " + anid
4492                   + " from different id : " + jvann.annotationId);
4493           jvann.annotationId = anid;
4494         }
4495       }
4496       else if (jvobj instanceof String)
4497       {
4498         if (jvids2vobj == null)
4499         {
4500           jvids2vobj = new Hashtable();
4501           jvids2vobj.put(jvobj, jv2vobj.get(jvobj).toString());
4502         }
4503       }
4504       else
4505       {
4506         Cache.log.debug("Ignoring " + jvobj.getClass() + " (ID = " + id);
4507       }
4508     }
4509   }
4510
4511   /**
4512    * set the uniqueSetSuffix used to prefix/suffix object IDs for jalview
4513    * objects created from the project archive. If string is null (default for
4514    * construction) then suffix will be set automatically.
4515    * 
4516    * @param string
4517    */
4518   public void setUniqueSetSuffix(String string)
4519   {
4520     uniqueSetSuffix = string;
4521
4522   }
4523
4524   /**
4525    * uses skipList2 as the skipList for skipping views on sequence sets
4526    * associated with keys in the skipList
4527    * 
4528    * @param skipList2
4529    */
4530   public void setSkipList(Hashtable skipList2)
4531   {
4532     skipList = skipList2;
4533   }
4534
4535 }