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