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