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