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