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