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