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