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