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