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