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