b899ae91286069efd7693efbf31071f4f44d2cd1
[jalview.git] / src / jalview / gui / Jalview2XML.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.6)
3  * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
10  * 
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 package jalview.gui;
19
20 import java.awt.Rectangle;
21 import java.io.*;
22 import java.lang.reflect.InvocationTargetException;
23 import java.net.*;
24 import java.util.*;
25 import java.util.Map.Entry;
26 import java.util.jar.*;
27
28 import javax.swing.*;
29
30 import org.exolab.castor.xml.*;
31
32 import uk.ac.vamsas.objects.utils.MapList;
33 import jalview.bin.Cache;
34 import jalview.datamodel.Alignment;
35 import jalview.datamodel.AlignmentAnnotation;
36 import jalview.datamodel.AlignmentI;
37 import jalview.datamodel.SequenceI;
38 import jalview.schemabinding.version2.*;
39 import jalview.schemes.*;
40 import jalview.structure.StructureSelectionManager;
41 import jalview.util.jarInputStreamProvider;
42
43 /**
44  * Write out the current jalview desktop state as a Jalview XML stream.
45  * 
46  * Note: the vamsas objects referred to here are primitive versions of the
47  * VAMSAS project schema elements - they are not the same and most likely never
48  * will be :)
49  * 
50  * @author $author$
51  * @version $Revision: 1.134 $
52  */
53 public class Jalview2XML
54 {
55   /**
56    * create/return unique hash string for sq
57    * 
58    * @param sq
59    * @return new or existing unique string for sq
60    */
61   String seqHash(SequenceI sq)
62   {
63     if (seqsToIds == null)
64     {
65       initSeqRefs();
66     }
67     if (seqsToIds.containsKey(sq))
68     {
69       return (String) seqsToIds.get(sq);
70     }
71     else
72     {
73       // create sequential key
74       String key = "sq" + (seqsToIds.size() + 1);
75       key = makeHashCode(sq, key); // check we don't have an external reference
76       // for it already.
77       seqsToIds.put(sq, key);
78       return key;
79     }
80   }
81
82   void clearSeqRefs()
83   {
84     if (_cleartables)
85     {
86       if (seqRefIds != null)
87       {
88         seqRefIds.clear();
89       }
90       if (seqsToIds != null)
91       {
92         seqsToIds.clear();
93       }
94       // seqRefIds = null;
95       // seqsToIds = null;
96     }
97     else
98     {
99       // do nothing
100       warn("clearSeqRefs called when _cleartables was not set. Doing nothing.");
101       // seqRefIds = new Hashtable();
102       // seqsToIds = new IdentityHashMap();
103     }
104   }
105
106   void initSeqRefs()
107   {
108     if (seqsToIds == null)
109     {
110       seqsToIds = new IdentityHashMap();
111     }
112     if (seqRefIds == null)
113     {
114       seqRefIds = new Hashtable();
115     }
116   }
117
118   /**
119    * SequenceI reference -> XML ID string in jalview XML. Populated as XML reps
120    * of sequence objects are created.
121    */
122   java.util.IdentityHashMap seqsToIds = null;
123
124   /**
125    * jalview XML Sequence ID to jalview sequence object reference (both dataset
126    * and alignment sequences. Populated as XML reps of sequence objects are
127    * created.)
128    */
129   java.util.Hashtable seqRefIds = null; // key->SequenceI resolution
130
131   Vector frefedSequence = null;
132
133   boolean raiseGUI = true; // whether errors are raised in dialog boxes or not
134
135   public Jalview2XML()
136   {
137   }
138
139   public Jalview2XML(boolean raiseGUI)
140   {
141     this.raiseGUI = raiseGUI;
142   }
143
144   public void resolveFrefedSequences()
145   {
146     if (frefedSequence.size() > 0)
147     {
148       int r = 0, rSize = frefedSequence.size();
149       while (r < rSize)
150       {
151         Object[] ref = (Object[]) frefedSequence.elementAt(r);
152         if (ref != null)
153         {
154           String sref = (String) ref[0];
155           if (seqRefIds.containsKey(sref))
156           {
157             if (ref[1] instanceof jalview.datamodel.Mapping)
158             {
159               SequenceI seq = (SequenceI) seqRefIds.get(sref);
160               while (seq.getDatasetSequence() != null)
161               {
162                 seq = seq.getDatasetSequence();
163               }
164               ((jalview.datamodel.Mapping) ref[1]).setTo(seq);
165             }
166             else
167             {
168               if (ref[1] instanceof jalview.datamodel.AlignedCodonFrame)
169               {
170                 SequenceI seq = (SequenceI) seqRefIds.get(sref);
171                 while (seq.getDatasetSequence() != null)
172                 {
173                   seq = seq.getDatasetSequence();
174                 }
175                 if (ref[2] != null
176                         && ref[2] instanceof jalview.datamodel.Mapping)
177                 {
178                   jalview.datamodel.Mapping mp = (jalview.datamodel.Mapping) ref[2];
179                   ((jalview.datamodel.AlignedCodonFrame) ref[1]).addMap(
180                           seq, mp.getTo(), mp.getMap());
181                 }
182                 else
183                 {
184                   System.err
185                           .println("IMPLEMENTATION ERROR: Unimplemented forward sequence references for AlcodonFrames involving "
186                                   + ref[2].getClass() + " type objects.");
187                 }
188               }
189               else
190               {
191                 System.err
192                         .println("IMPLEMENTATION ERROR: Unimplemented forward sequence references for "
193                                 + ref[1].getClass() + " type objects.");
194               }
195             }
196             frefedSequence.remove(r);
197             rSize--;
198           }
199           else
200           {
201             System.err
202                     .println("IMPLEMENTATION WARNING: Unresolved forward reference for hash string "
203                             + ref[0]
204                             + " with objecttype "
205                             + ref[1].getClass());
206             r++;
207           }
208         }
209         else
210         {
211           // empty reference
212           frefedSequence.remove(r);
213           rSize--;
214         }
215       }
216     }
217   }
218
219   /**
220    * This maintains a list of viewports, the key being the seqSetId. Important
221    * to set historyItem and redoList for multiple views
222    */
223   Hashtable viewportsAdded;
224
225   Hashtable annotationIds = new Hashtable();
226
227   String uniqueSetSuffix = "";
228
229   /**
230    * List of pdbfiles added to Jar
231    */
232   Vector pdbfiles = null;
233
234   // SAVES SEVERAL ALIGNMENT WINDOWS TO SAME JARFILE
235   public void SaveState(File statefile)
236   {
237     try
238     {
239       FileOutputStream fos = new FileOutputStream(statefile);
240       JarOutputStream jout = new JarOutputStream(fos);
241       SaveState(jout);
242
243     } catch (Exception e)
244     {
245       // TODO: inform user of the problem - they need to know if their data was
246       // not saved !
247       if (errorMessage == null)
248       {
249         errorMessage = "Couldn't write Jalview Archive to output file '"
250                 + statefile + "' - See console error log for details";
251       }
252       else
253       {
254         errorMessage += "(output file was '" + statefile + "')";
255       }
256       e.printStackTrace();
257     }
258     reportErrors();
259   }
260
261   /**
262    * Writes a jalview project archive to the given Jar output stream.
263    * 
264    * @param jout
265    */
266   public void SaveState(JarOutputStream jout)
267   {
268     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
269
270     if (frames == null)
271     {
272       return;
273     }
274
275     try
276     {
277
278       // NOTE UTF-8 MUST BE USED FOR WRITING UNICODE CHARS
279       // //////////////////////////////////////////////////
280       // NOTE ALSO new PrintWriter must be used for each new JarEntry
281       PrintWriter out = null;
282
283       Vector shortNames = new Vector();
284
285       // REVERSE ORDER
286       for (int i = frames.length - 1; i > -1; i--)
287       {
288         if (frames[i] instanceof AlignFrame)
289         {
290           AlignFrame af = (AlignFrame) frames[i];
291           // skip ?
292           if (skipList != null
293                   && skipList.containsKey(af.getViewport()
294                           .getSequenceSetId()))
295           {
296             continue;
297           }
298
299           String shortName = af.getTitle();
300
301           if (shortName.indexOf(File.separatorChar) > -1)
302           {
303             shortName = shortName.substring(shortName
304                     .lastIndexOf(File.separatorChar) + 1);
305           }
306
307           int count = 1;
308
309           while (shortNames.contains(shortName))
310           {
311             if (shortName.endsWith("_" + (count - 1)))
312             {
313               shortName = shortName
314                       .substring(0, shortName.lastIndexOf("_"));
315             }
316
317             shortName = shortName.concat("_" + count);
318             count++;
319           }
320
321           shortNames.addElement(shortName);
322
323           if (!shortName.endsWith(".xml"))
324           {
325             shortName = shortName + ".xml";
326           }
327
328           int ap, apSize = af.alignPanels.size();
329           for (ap = 0; ap < apSize; ap++)
330           {
331             AlignmentPanel apanel = (AlignmentPanel) af.alignPanels
332                     .elementAt(ap);
333             String fileName = apSize == 1 ? shortName : ap + shortName;
334             if (!fileName.endsWith(".xml"))
335             {
336               fileName = fileName + ".xml";
337             }
338
339             SaveState(apanel, fileName, jout);
340           }
341         }
342       }
343       try
344       {
345         jout.flush();
346       } catch (Exception foo)
347       {
348       }
349       ;
350       jout.close();
351     } catch (Exception ex)
352     {
353       // TODO: inform user of the problem - they need to know if their data was
354       // not saved !
355       if (errorMessage == null)
356       {
357         errorMessage = "Couldn't write Jalview Archive - see error output for details";
358       }
359       ex.printStackTrace();
360     }
361   }
362
363   // USE THIS METHOD TO SAVE A SINGLE ALIGNMENT WINDOW
364   public boolean SaveAlignment(AlignFrame af, String jarFile,
365           String fileName)
366   {
367     try
368     {
369       int ap, apSize = af.alignPanels.size();
370       FileOutputStream fos = new FileOutputStream(jarFile);
371       JarOutputStream jout = new JarOutputStream(fos);
372       for (ap = 0; ap < apSize; ap++)
373       {
374         AlignmentPanel apanel = (AlignmentPanel) af.alignPanels
375                 .elementAt(ap);
376         String jfileName = apSize == 1 ? fileName : fileName + ap;
377         if (!jfileName.endsWith(".xml"))
378         {
379           jfileName = jfileName + ".xml";
380         }
381         SaveState(apanel, jfileName, jout);
382       }
383
384       try
385       {
386         jout.flush();
387       } catch (Exception foo)
388       {
389       }
390       ;
391       jout.close();
392       return true;
393     } catch (Exception ex)
394     {
395       errorMessage = "Couldn't Write alignment view to Jalview Archive - see error output for details";
396       ex.printStackTrace();
397       return false;
398     }
399   }
400
401   /**
402    * create a JalviewModel from an algnment view and marshall it to a
403    * JarOutputStream
404    * 
405    * @param ap
406    *          panel to create jalview model for
407    * @param fileName
408    *          name of alignment panel written to output stream
409    * @param jout
410    *          jar output stream
411    * @param out
412    *          jar entry name
413    */
414   public JalviewModel SaveState(AlignmentPanel ap, String fileName,
415           JarOutputStream jout)
416   {
417     initSeqRefs();
418     Vector jmolViewIds = new Vector(); //
419     Vector userColours = new Vector();
420
421     AlignViewport av = ap.av;
422
423     JalviewModel object = new JalviewModel();
424     object.setVamsasModel(new jalview.schemabinding.version2.VamsasModel());
425
426     object.setCreationDate(new java.util.Date(System.currentTimeMillis()));
427     object.setVersion(jalview.bin.Cache.getProperty("VERSION"));
428
429     jalview.datamodel.AlignmentI jal = av.alignment;
430
431     if (av.hasHiddenRows)
432     {
433       jal = jal.getHiddenSequences().getFullAlignment();
434     }
435
436     SequenceSet vamsasSet = new SequenceSet();
437     Sequence vamsasSeq;
438     JalviewModelSequence jms = new JalviewModelSequence();
439
440     vamsasSet.setGapChar(jal.getGapCharacter() + "");
441
442     if (jal.getDataset() != null)
443     {
444       // dataset id is the dataset's hashcode
445       vamsasSet.setDatasetId(getDatasetIdRef(jal.getDataset()));
446     }
447     if (jal.getProperties() != null)
448     {
449       Enumeration en = jal.getProperties().keys();
450       while (en.hasMoreElements())
451       {
452         String key = en.nextElement().toString();
453         SequenceSetProperties ssp = new SequenceSetProperties();
454         ssp.setKey(key);
455         ssp.setValue(jal.getProperties().get(key).toString());
456         vamsasSet.addSequenceSetProperties(ssp);
457       }
458     }
459
460     JSeq jseq;
461
462     // SAVE SEQUENCES
463     String id = "";
464     jalview.datamodel.SequenceI jds;
465     for (int i = 0; i < jal.getHeight(); i++)
466     {
467       jds = jal.getSequenceAt(i);
468       id = seqHash(jds);
469
470       if (seqRefIds.get(id) != null)
471       {
472         // This happens for two reasons: 1. multiple views are being serialised.
473         // 2. the hashCode has collided with another sequence's code. This DOES
474         // HAPPEN! (PF00072.15.stk does this)
475         // JBPNote: Uncomment to debug writing out of files that do not read
476         // back in due to ArrayOutOfBoundExceptions.
477         // System.err.println("vamsasSeq backref: "+id+"");
478         // System.err.println(jds.getName()+"
479         // "+jds.getStart()+"-"+jds.getEnd()+" "+jds.getSequenceAsString());
480         // System.err.println("Hashcode: "+seqHash(jds));
481         // SequenceI rsq = (SequenceI) seqRefIds.get(id + "");
482         // System.err.println(rsq.getName()+"
483         // "+rsq.getStart()+"-"+rsq.getEnd()+" "+rsq.getSequenceAsString());
484         // System.err.println("Hashcode: "+seqHash(rsq));
485       }
486       else
487       {
488         vamsasSeq = createVamsasSequence(id, jds);
489         vamsasSet.addSequence(vamsasSeq);
490         seqRefIds.put(id, jds);
491       }
492
493       jseq = new JSeq();
494       jseq.setStart(jds.getStart());
495       jseq.setEnd(jds.getEnd());
496       jseq.setColour(av.getSequenceColour(jds).getRGB());
497
498       jseq.setId(id); // jseq id should be a string not a number
499
500       if (av.hasHiddenRows)
501       {
502         jseq.setHidden(av.alignment.getHiddenSequences().isHidden(jds));
503
504         if (av.hiddenRepSequences != null
505                 && av.hiddenRepSequences.containsKey(jal.getSequenceAt(i)))
506         {
507           jalview.datamodel.SequenceI[] reps = ((jalview.datamodel.SequenceGroup) av.hiddenRepSequences
508                   .get(jal.getSequenceAt(i))).getSequencesInOrder(jal);
509
510           for (int h = 0; h < reps.length; h++)
511           {
512             if (reps[h] != jal.getSequenceAt(i))
513             {
514               jseq.addHiddenSequences(jal.findIndex(reps[h]));
515             }
516           }
517         }
518       }
519
520       if (jds.getDatasetSequence().getSequenceFeatures() != null)
521       {
522         jalview.datamodel.SequenceFeature[] sf = jds.getDatasetSequence()
523                 .getSequenceFeatures();
524         int index = 0;
525         while (index < sf.length)
526         {
527           Features features = new Features();
528
529           features.setBegin(sf[index].getBegin());
530           features.setEnd(sf[index].getEnd());
531           features.setDescription(sf[index].getDescription());
532           features.setType(sf[index].getType());
533           features.setFeatureGroup(sf[index].getFeatureGroup());
534           features.setScore(sf[index].getScore());
535           if (sf[index].links != null)
536           {
537             for (int l = 0; l < sf[index].links.size(); l++)
538             {
539               OtherData keyValue = new OtherData();
540               keyValue.setKey("LINK_" + l);
541               keyValue.setValue(sf[index].links.elementAt(l).toString());
542               features.addOtherData(keyValue);
543             }
544           }
545           if (sf[index].otherDetails != null)
546           {
547             String key;
548             Enumeration keys = sf[index].otherDetails.keys();
549             while (keys.hasMoreElements())
550             {
551               key = keys.nextElement().toString();
552               OtherData keyValue = new OtherData();
553               keyValue.setKey(key);
554               keyValue.setValue(sf[index].otherDetails.get(key).toString());
555               features.addOtherData(keyValue);
556             }
557           }
558
559           jseq.addFeatures(features);
560           index++;
561         }
562       }
563
564       if (jds.getDatasetSequence().getPDBId() != null)
565       {
566         Enumeration en = jds.getDatasetSequence().getPDBId().elements();
567         while (en.hasMoreElements())
568         {
569           Pdbids pdb = new Pdbids();
570           jalview.datamodel.PDBEntry entry = (jalview.datamodel.PDBEntry) en
571                   .nextElement();
572
573           pdb.setId(entry.getId());
574           pdb.setType(entry.getType());
575
576           AppJmol jmol;
577           // This must have been loaded, is it still visible?
578           JInternalFrame[] frames = Desktop.desktop.getAllFrames();
579           String matchedFile = null;
580           for (int f = frames.length - 1; f > -1; f--)
581           {
582             if (frames[f] instanceof AppJmol)
583             {
584               jmol = (AppJmol) frames[f];
585               for (int peid = 0; peid < jmol.jmb.pdbentry.length; peid++)
586               {
587                 if (!jmol.jmb.pdbentry[peid].getId().equals(entry.getId())
588                         && !(entry.getId().length() > 4 && entry
589                                 .getId()
590                                 .toLowerCase()
591                                 .startsWith(
592                                         jmol.jmb.pdbentry[peid].getId()
593                                                 .toLowerCase())))
594                   continue;
595                 if (matchedFile == null)
596                 {
597                   matchedFile = jmol.jmb.pdbentry[peid].getFile();
598                 }
599                 else if (!matchedFile.equals(jmol.jmb.pdbentry[peid]
600                         .getFile()))
601                 {
602                   Cache.log
603                           .warn("Probably lost some PDB-Sequence mappings for this structure file (which apparently has same PDB Entry code): "
604                                   + jmol.jmb.pdbentry[peid].getFile());
605                   ; // record the
606                 }
607                 // file so we
608                 // can get at it if the ID
609                 // match is ambiguous (e.g.
610                 // 1QIP==1qipA)
611                 String statestring = jmol.jmb.viewer.getStateInfo();
612
613                 for (int smap = 0; smap < jmol.jmb.sequence[peid].length; smap++)
614                 {
615                   if (jal.findIndex(jmol.jmb.sequence[peid][smap]) > -1)
616                   {
617                     StructureState state = new StructureState();
618                     state.setVisible(true);
619                     state.setXpos(jmol.getX());
620                     state.setYpos(jmol.getY());
621                     state.setWidth(jmol.getWidth());
622                     state.setHeight(jmol.getHeight());
623                     state.setViewId(jmol.getViewId());
624                     state.setAlignwithAlignPanel(jmol.isUsedforaligment(ap));
625                     state.setColourwithAlignPanel(jmol
626                             .isUsedforcolourby(ap));
627                     state.setColourByJmol(jmol.isColouredByJmol());
628                     if (!jmolViewIds.contains(state.getViewId()))
629                     {
630                       // Make sure we only store a Jmol state once in each XML
631                       // document.
632                       jmolViewIds.addElement(state.getViewId());
633                       state.setContent(statestring.replaceAll("\n", ""));
634                     }
635                     else
636                     {
637                       state.setContent("# duplicate state");
638                     }
639                     pdb.addStructureState(state);
640                   }
641                 }
642               }
643             }
644           }
645
646           if (matchedFile != null || entry.getFile() != null)
647           {
648             if (entry.getFile() != null)
649             {
650               // use entry's file
651               matchedFile = entry.getFile();
652             }
653             pdb.setFile(matchedFile); // entry.getFile());
654             if (pdbfiles == null)
655             {
656               pdbfiles = new Vector();
657             }
658
659             if (!pdbfiles.contains(entry.getId()))
660             {
661               pdbfiles.addElement(entry.getId());
662               try
663               {
664                 File file = new File(matchedFile);
665                 if (file.exists() && jout != null)
666                 {
667                   byte[] data = new byte[(int) file.length()];
668                   jout.putNextEntry(new JarEntry(entry.getId()));
669                   DataInputStream dis = new DataInputStream(
670                           new FileInputStream(file));
671                   dis.readFully(data);
672
673                   DataOutputStream dout = new DataOutputStream(jout);
674                   dout.write(data, 0, data.length);
675                   dout.flush();
676                   jout.closeEntry();
677                 }
678               } catch (Exception ex)
679               {
680                 ex.printStackTrace();
681               }
682
683             }
684           }
685
686           if (entry.getProperty() != null)
687           {
688             PdbentryItem item = new PdbentryItem();
689             Hashtable properties = entry.getProperty();
690             Enumeration en2 = properties.keys();
691             while (en2.hasMoreElements())
692             {
693               Property prop = new Property();
694               String key = en2.nextElement().toString();
695               prop.setName(key);
696               prop.setValue(properties.get(key).toString());
697               item.addProperty(prop);
698             }
699             pdb.addPdbentryItem(item);
700           }
701
702           jseq.addPdbids(pdb);
703         }
704       }
705
706       jms.addJSeq(jseq);
707     }
708
709     if (av.hasHiddenRows)
710     {
711       jal = av.alignment;
712     }
713     // SAVE MAPPINGS
714     if (jal.getCodonFrames() != null && jal.getCodonFrames().length > 0)
715     {
716       jalview.datamodel.AlignedCodonFrame[] jac = jal.getCodonFrames();
717       for (int i = 0; i < jac.length; i++)
718       {
719         AlcodonFrame alc = new AlcodonFrame();
720         vamsasSet.addAlcodonFrame(alc);
721         for (int p = 0; p < jac[i].aaWidth; p++)
722         {
723           Alcodon cmap = new Alcodon();
724           if (jac[i].codons[p] != null)
725           {
726             // Null codons indicate a gapped column in the translated peptide
727             // alignment.
728             cmap.setPos1(jac[i].codons[p][0]);
729             cmap.setPos2(jac[i].codons[p][1]);
730             cmap.setPos3(jac[i].codons[p][2]);
731           }
732           alc.addAlcodon(cmap);
733         }
734         if (jac[i].getProtMappings() != null
735                 && jac[i].getProtMappings().length > 0)
736         {
737           SequenceI[] dnas = jac[i].getdnaSeqs();
738           jalview.datamodel.Mapping[] pmaps = jac[i].getProtMappings();
739           for (int m = 0; m < pmaps.length; m++)
740           {
741             AlcodMap alcmap = new AlcodMap();
742             alcmap.setDnasq(seqHash(dnas[m]));
743             alcmap.setMapping(createVamsasMapping(pmaps[m], dnas[m], null,
744                     false));
745             alc.addAlcodMap(alcmap);
746           }
747         }
748       }
749     }
750
751     // SAVE TREES
752     // /////////////////////////////////
753     if (av.currentTree != null)
754     {
755       // FIND ANY ASSOCIATED TREES
756       // NOT IMPLEMENTED FOR HEADLESS STATE AT PRESENT
757       if (Desktop.desktop != null)
758       {
759         JInternalFrame[] frames = Desktop.desktop.getAllFrames();
760
761         for (int t = 0; t < frames.length; t++)
762         {
763           if (frames[t] instanceof TreePanel)
764           {
765             TreePanel tp = (TreePanel) frames[t];
766
767             if (tp.treeCanvas.av.alignment == jal)
768             {
769               Tree tree = new Tree();
770               tree.setTitle(tp.getTitle());
771               tree.setCurrentTree((av.currentTree == tp.getTree()));
772               tree.setNewick(tp.getTree().toString());
773               tree.setThreshold(tp.treeCanvas.threshold);
774
775               tree.setFitToWindow(tp.fitToWindow.getState());
776               tree.setFontName(tp.getTreeFont().getName());
777               tree.setFontSize(tp.getTreeFont().getSize());
778               tree.setFontStyle(tp.getTreeFont().getStyle());
779               tree.setMarkUnlinked(tp.placeholdersMenu.getState());
780
781               tree.setShowBootstrap(tp.bootstrapMenu.getState());
782               tree.setShowDistances(tp.distanceMenu.getState());
783
784               tree.setHeight(tp.getHeight());
785               tree.setWidth(tp.getWidth());
786               tree.setXpos(tp.getX());
787               tree.setYpos(tp.getY());
788               tree.setId(makeHashCode(tp, null));
789               jms.addTree(tree);
790             }
791           }
792         }
793       }
794     }
795
796     // SAVE ANNOTATIONS
797     /**
798      * store forward refs from an annotationRow to any groups
799      */
800     IdentityHashMap groupRefs = new IdentityHashMap();
801     if (jal.getAlignmentAnnotation() != null)
802     {
803       jalview.datamodel.AlignmentAnnotation[] aa = jal
804               .getAlignmentAnnotation();
805
806       for (int i = 0; i < aa.length; i++)
807       {
808         Annotation an = new Annotation();
809
810         if (aa[i].annotationId != null)
811         {
812           annotationIds.put(aa[i].annotationId, aa[i]);
813         }
814
815         an.setId(aa[i].annotationId);
816
817         an.setVisible(aa[i].visible);
818
819         an.setDescription(aa[i].description);
820
821         if (aa[i].sequenceRef != null)
822         {
823           // TODO later annotation sequenceRef should be the XML ID of the
824           // sequence rather than its display name
825           an.setSequenceRef(aa[i].sequenceRef.getName());
826         }
827         if (aa[i].groupRef != null)
828         {
829           Object groupIdr = groupRefs.get(aa[i].groupRef);
830           if (groupIdr == null)
831           {
832             // make a locally unique String
833             groupRefs.put(aa[i].groupRef,
834                     groupIdr = ("" + System.currentTimeMillis()
835                             + aa[i].groupRef.getName() + groupRefs.size()));
836           }
837           an.setGroupRef(groupIdr.toString());
838         }
839
840         // store all visualization attributes for annotation
841         an.setGraphHeight(aa[i].graphHeight);
842         an.setCentreColLabels(aa[i].centreColLabels);
843         an.setScaleColLabels(aa[i].scaleColLabel);
844         an.setShowAllColLabels(aa[i].showAllColLabels);
845
846         if (aa[i].graph > 0)
847         {
848           an.setGraph(true);
849           an.setGraphType(aa[i].graph);
850           an.setGraphGroup(aa[i].graphGroup);
851           if (aa[i].getThreshold() != null)
852           {
853             ThresholdLine line = new ThresholdLine();
854             line.setLabel(aa[i].getThreshold().label);
855             line.setValue(aa[i].getThreshold().value);
856             line.setColour(aa[i].getThreshold().colour.getRGB());
857             an.setThresholdLine(line);
858           }
859         }
860         else
861         {
862           an.setGraph(false);
863         }
864
865         an.setLabel(aa[i].label);
866
867         if (aa[i] == av.quality || aa[i] == av.conservation
868                 || aa[i] == av.consensus || aa[i].autoCalculated)
869         {
870           // new way of indicating autocalculated annotation -
871           an.setAutoCalculated(aa[i].autoCalculated);
872         }
873         if (aa[i].hasScore())
874         {
875           an.setScore(aa[i].getScore());
876         }
877         AnnotationElement ae;
878         if (aa[i].annotations != null)
879         {
880           an.setScoreOnly(false);
881           for (int a = 0; a < aa[i].annotations.length; a++)
882           {
883             if ((aa[i] == null) || (aa[i].annotations[a] == null))
884             {
885               continue;
886             }
887
888             ae = new AnnotationElement();
889             if (aa[i].annotations[a].description != null)
890               ae.setDescription(aa[i].annotations[a].description);
891             if (aa[i].annotations[a].displayCharacter != null)
892               ae.setDisplayCharacter(aa[i].annotations[a].displayCharacter);
893
894             if (!Float.isNaN(aa[i].annotations[a].value))
895               ae.setValue(aa[i].annotations[a].value);
896
897             ae.setPosition(a);
898             if (aa[i].annotations[a].secondaryStructure != ' '
899                     && aa[i].annotations[a].secondaryStructure != '\0')
900               ae.setSecondaryStructure(aa[i].annotations[a].secondaryStructure
901                       + "");
902
903             if (aa[i].annotations[a].colour != null
904                     && aa[i].annotations[a].colour != java.awt.Color.black)
905             {
906               ae.setColour(aa[i].annotations[a].colour.getRGB());
907             }
908
909             an.addAnnotationElement(ae);
910             if (aa[i].autoCalculated)
911             {
912               // only write one non-null entry into the annotation row -
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 the Jalview 2 project may\nnot be correctly bound to sequences in the alignment.\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 }