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