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