comments re JAL-823 legacy issue
[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 pre-2.7 conversion JAL-823 : 
2578                 // do not assume any view has to be linked for colour by sequence
2579               }
2580               
2581               // assemble String[] { pdb files }, String[] { id for each
2582               // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
2583               // seqs_file 2}, boolean[] {
2584               // linkAlignPanel,superposeWithAlignpanel}} from hash
2585               Object[] jmoldat = (Object[]) jmolViewIds.get(sviewid);
2586               ((boolean[]) jmoldat[3])[0] |= ids[p].getStructureState(s)
2587                       .hasAlignwithAlignPanel() ? ids[p].getStructureState(
2588                       s).getAlignwithAlignPanel() : false;
2589               // never colour by linked panel if not specified
2590               ((boolean[]) jmoldat[3])[1] |= ids[p].getStructureState(s)
2591                       .hasColourwithAlignPanel() ? ids[p]
2592                       .getStructureState(s).getColourwithAlignPanel()
2593                       : false;
2594
2595               if (((String) jmoldat[1]).length() < ids[p]
2596                       .getStructureState(s).getContent().length())
2597               {
2598                 {
2599                   jmoldat[1] = ids[p].getStructureState(s).getContent();
2600                 }
2601               }
2602               Object[] seqstrmaps = (Object[]) ((Hashtable) jmoldat[2])
2603                       .get(ids[p].getFile());
2604               if (seqstrmaps == null)
2605               {
2606                 ((Hashtable) jmoldat[2]).put(
2607                         new File(ids[p].getFile()).toString(),
2608                         seqstrmaps = new Object[]
2609                         { pdbFile, ids[p].getId(), new Vector(),
2610                             new Vector() });
2611               }
2612               if (!((Vector) seqstrmaps[2]).contains(seq))
2613               {
2614                 ((Vector) seqstrmaps[2]).addElement(seq);
2615                 // ((Vector)seqstrmaps[3]).addElement(n) : in principle, chains
2616                 // should be stored here : do we need to
2617                 // TODO: store and recover seq/pdb_id : chain mappings
2618               }
2619             }
2620           }
2621         }
2622       }
2623       {
2624
2625         // Instantiate the associated Jmol views
2626         for (Entry<String, Object[]> entry : jmolViewIds.entrySet())
2627         {
2628           String sviewid = entry.getKey();
2629           Object[] svattrib = entry.getValue();
2630           int[] geom = (int[]) svattrib[0];
2631           String state = (String) svattrib[1];
2632           Hashtable<String, Object[]> oldFiles = (Hashtable<String, Object[]>) svattrib[2];
2633           final boolean useinJmolsuperpos = ((boolean[]) svattrib[3])[0], usetoColourbyseq = ((boolean[]) svattrib[3])[1];
2634           int x = geom[0], y = geom[1], width = geom[2], height = geom[3];
2635           // collate the pdbfile -> sequence mappings from this view
2636           Vector<String> pdbfilenames = new Vector<String>();
2637           Vector<SequenceI[]> seqmaps = new Vector<SequenceI[]>();
2638           Vector<String> pdbids = new Vector<String>();
2639
2640           // Search to see if we've already created this Jmol view
2641           AppJmol comp = null;
2642           JInternalFrame[] frames = null;
2643           do
2644           {
2645             try
2646             {
2647               frames = Desktop.desktop.getAllFrames();
2648             } catch (ArrayIndexOutOfBoundsException e)
2649             {
2650               // occasional No such child exceptions are thrown here...
2651               frames = null;
2652               try
2653               {
2654                 Thread.sleep(10);
2655               } catch (Exception f)
2656               {
2657               }
2658               ;
2659             }
2660           } while (frames == null);
2661           // search for any Jmol windows already open from other
2662           // alignment views that exactly match the stored structure state
2663           for (int f = 0; comp == null && f < frames.length; f++)
2664           {
2665             if (frames[f] instanceof AppJmol)
2666             {
2667               if (sviewid != null
2668                       && ((AppJmol) frames[f]).getViewId().equals(sviewid))
2669               {
2670                 // post jalview 2.4 schema includes structure view id
2671                 comp = (AppJmol) frames[f];
2672               }
2673               else if (frames[f].getX() == x && frames[f].getY() == y
2674                       && frames[f].getHeight() == height
2675                       && frames[f].getWidth() == width)
2676               {
2677                 comp = (AppJmol) frames[f];
2678               }
2679             }
2680           }
2681
2682           if (comp == null)
2683           {
2684             // create a new Jmol window.
2685             // First parse the Jmol state to translate filenames loaded into the
2686             // view, and record the order in which files are shown in the Jmol
2687             // view, so we can add the sequence mappings in same order.
2688             StringBuffer newFileLoc = null;
2689             int cp = 0, ncp, ecp;
2690             while ((ncp = state.indexOf("load ", cp)) > -1)
2691             {
2692               if (newFileLoc == null)
2693               {
2694                 newFileLoc = new StringBuffer();
2695               }
2696               newFileLoc.append(state.substring(cp,
2697                       ncp = (state.indexOf("\"", ncp + 1) + 1)));
2698               String oldfilenam = state.substring(ncp,
2699                       ecp = state.indexOf("\"", ncp));
2700               // recover the new mapping data for this old filename
2701               // have to normalize filename - since Jmol and jalview do filename
2702               // translation differently.
2703               Object[] filedat = oldFiles.get(new File(oldfilenam)
2704                       .toString());
2705               newFileLoc.append(((String) filedat[0]));
2706               pdbfilenames.addElement((String) filedat[0]);
2707               pdbids.addElement((String) filedat[1]);
2708               seqmaps.addElement((SequenceI[]) ((Vector<SequenceI>) filedat[2])
2709                       .toArray(new SequenceI[0]));
2710               newFileLoc.append("\"");
2711               cp = ecp + 1; // advance beyond last \" and set cursor so we can
2712                             // look for next file statement.
2713             }
2714             if (cp > 0)
2715             {
2716               // just append rest of state
2717               newFileLoc.append(state.substring(cp));
2718             }
2719             else
2720             {
2721               System.err
2722                       .print("Ignoring incomplete Jmol state for PDB ids: ");
2723               newFileLoc = new StringBuffer(state);
2724               newFileLoc.append("; load append ");
2725               for (String id : oldFiles.keySet())
2726               {
2727                 // add this and any other pdb files that should be present in
2728                 // the viewer
2729                 Object[] filedat = oldFiles.get(id);
2730                 String nfilename;
2731                 newFileLoc.append(((String) filedat[0]));
2732                 pdbfilenames.addElement((String) filedat[0]);
2733                 pdbids.addElement((String) filedat[1]);
2734                 seqmaps.addElement((SequenceI[]) ((Vector<SequenceI>) filedat[2])
2735                         .toArray(new SequenceI[0]));
2736                 newFileLoc.append(" \"");
2737                 newFileLoc.append((String) filedat[0]);
2738                 newFileLoc.append("\"");
2739
2740               }
2741               newFileLoc.append(";");
2742             }
2743
2744             if (newFileLoc != null)
2745             {
2746               int histbug = newFileLoc.indexOf("history = ");
2747               histbug += 10;
2748               int diff = histbug == -1 ? -1 : newFileLoc.indexOf(";",
2749                       histbug);
2750               String val = (diff == -1) ? null : newFileLoc.substring(
2751                       histbug, diff);
2752               if (val != null && val.length() >= 4)
2753               {
2754                 if (val.contains("e"))
2755                 {
2756                   if (val.trim().equals("true"))
2757                   {
2758                     val = "1";
2759                   }
2760                   else
2761                   {
2762                     val = "0";
2763                   }
2764                   newFileLoc.replace(histbug, diff, val);
2765                 }
2766               }
2767               // TODO: assemble String[] { pdb files }, String[] { id for each
2768               // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
2769               // seqs_file 2}} from hash
2770               final String[] pdbf = (String[]) pdbfilenames
2771                       .toArray(new String[pdbfilenames.size()]), id = (String[]) pdbids
2772                       .toArray(new String[pdbids.size()]);
2773               final SequenceI[][] sq = (SequenceI[][]) seqmaps
2774                       .toArray(new SequenceI[seqmaps.size()][]);
2775               final String fileloc = newFileLoc.toString(), vid = sviewid;
2776               final AlignFrame alf = af;
2777               final java.awt.Rectangle rect = new java.awt.Rectangle(x, y,
2778                       width, height);
2779               try
2780               {
2781                 javax.swing.SwingUtilities.invokeAndWait(new Runnable()
2782                 {
2783                   public void run()
2784                   {
2785                     AppJmol sview = null;
2786                     try
2787                     {
2788                       sview = new AppJmol(pdbf, id, sq, alf.alignPanel,
2789                               useinJmolsuperpos, usetoColourbyseq, fileloc,
2790                               rect, vid);
2791                     } catch (OutOfMemoryError ex)
2792                     {
2793                       new OOMWarning("restoring structure view for PDB id "
2794                               + id, (OutOfMemoryError) ex.getCause());
2795                       if (sview != null && sview.isVisible())
2796                       {
2797                         sview.closeViewer();
2798                         sview.setVisible(false);
2799                         sview.dispose();
2800                       }
2801                     }
2802                   }
2803                 });
2804               } catch (InvocationTargetException ex)
2805               {
2806                 warn("Unexpected error when opening Jmol view.", ex);
2807
2808               } catch (InterruptedException e)
2809               {
2810                 // e.printStackTrace();
2811               }
2812             }
2813
2814           }
2815           else
2816           // if (comp != null)
2817           {
2818             // NOTE: if the jalview project is part of a shared session then
2819             // view synchronization should/could be done here.
2820
2821             // add mapping for sequences in this view to an already open Jmol
2822             // instance
2823             for (String id : oldFiles.keySet())
2824             {
2825               // add this and any other pdb files that should be present in the
2826               // viewer
2827               Object[] filedat = oldFiles.get(id);
2828               String pdbFile = (String) filedat[0];
2829               SequenceI[] seq = (SequenceI[]) ((Vector<SequenceI>) filedat[2])
2830                       .toArray(new SequenceI[0]);
2831               StructureSelectionManager.getStructureSelectionManager()
2832                       .setMapping(seq, null, pdbFile,
2833                               jalview.io.AppletFormatAdapter.FILE);
2834               ((AppJmol) comp).jmb.addSequenceForStructFile(pdbFile, seq);
2835             }
2836             // and add the AlignmentPanel's reference to the Jmol view
2837             ((AppJmol) comp).addAlignmentPanel(ap);
2838             if (useinJmolsuperpos)
2839             {
2840               ((AppJmol) comp).useAlignmentPanelForSuperposition(ap);
2841             }
2842             else
2843             {
2844               ((AppJmol) comp).excludeAlignmentPanelForSuperposition(ap);
2845             }
2846             if (usetoColourbyseq)
2847             {
2848               ((AppJmol) comp).useAlignmentPanelForColourbyseq(ap);
2849             }
2850             else
2851             {
2852               ((AppJmol) comp).excludeAlignmentPanelForColourbyseq(ap);
2853             }
2854           }
2855         }
2856       }
2857     }
2858     // and finally return.
2859     return af;
2860   }
2861
2862   AlignFrame loadViewport(String file, JSeq[] JSEQ, Vector hiddenSeqs,
2863           Alignment al, JalviewModelSequence jms, Viewport view,
2864           String uniqueSeqSetId, String viewId,
2865           ArrayList<JvAnnotRow> autoAlan)
2866   {
2867     AlignFrame af = null;
2868     af = new AlignFrame(al, view.getWidth(), view.getHeight(),
2869             uniqueSeqSetId, viewId);
2870
2871     af.setFileName(file, "Jalview");
2872
2873     for (int i = 0; i < JSEQ.length; i++)
2874     {
2875       af.viewport.setSequenceColour(af.viewport.alignment.getSequenceAt(i),
2876               new java.awt.Color(JSEQ[i].getColour()));
2877     }
2878
2879     af.viewport.gatherViewsHere = view.getGatheredViews();
2880
2881     if (view.getSequenceSetId() != null)
2882     {
2883       jalview.gui.AlignViewport av = (jalview.gui.AlignViewport) viewportsAdded
2884               .get(uniqueSeqSetId);
2885
2886       af.viewport.sequenceSetID = uniqueSeqSetId;
2887       if (av != null)
2888       {
2889         // propagate shared settings to this new view
2890         af.viewport.historyList = av.historyList;
2891         af.viewport.redoList = av.redoList;
2892       }
2893       else
2894       {
2895         viewportsAdded.put(uniqueSeqSetId, af.viewport);
2896       }
2897       // TODO: check if this method can be called repeatedly without
2898       // side-effects if alignpanel already registered.
2899       PaintRefresher.Register(af.alignPanel, uniqueSeqSetId);
2900     }
2901     // apply Hidden regions to view.
2902     if (hiddenSeqs != null)
2903     {
2904       for (int s = 0; s < JSEQ.length; s++)
2905       {
2906         jalview.datamodel.SequenceGroup hidden = new jalview.datamodel.SequenceGroup();
2907
2908         for (int r = 0; r < JSEQ[s].getHiddenSequencesCount(); r++)
2909         {
2910           hidden.addSequence(
2911                   al.getSequenceAt(JSEQ[s].getHiddenSequences(r)), false);
2912         }
2913         af.viewport.hideRepSequences(al.getSequenceAt(s), hidden);
2914       }
2915
2916       jalview.datamodel.SequenceI[] hseqs = new jalview.datamodel.SequenceI[hiddenSeqs
2917               .size()];
2918
2919       for (int s = 0; s < hiddenSeqs.size(); s++)
2920       {
2921         hseqs[s] = (jalview.datamodel.SequenceI) hiddenSeqs.elementAt(s);
2922       }
2923
2924       af.viewport.hideSequence(hseqs);
2925
2926     }
2927     // recover view properties and display parameters
2928     if (view.getViewName() != null)
2929     {
2930       af.viewport.viewName = view.getViewName();
2931       af.setInitialTabVisible();
2932     }
2933     af.setBounds(view.getXpos(), view.getYpos(), view.getWidth(),
2934             view.getHeight());
2935
2936     af.viewport.setShowAnnotation(view.getShowAnnotation());
2937     af.viewport.setAbovePIDThreshold(view.getPidSelected());
2938
2939     af.viewport.setColourText(view.getShowColourText());
2940
2941     af.viewport.setConservationSelected(view.getConservationSelected());
2942     af.viewport.setShowJVSuffix(view.getShowFullId());
2943     af.viewport.rightAlignIds = view.getRightAlignIds();
2944     af.viewport.setFont(new java.awt.Font(view.getFontName(), view
2945             .getFontStyle(), view.getFontSize()));
2946     af.alignPanel.fontChanged();
2947     af.viewport.setRenderGaps(view.getRenderGaps());
2948     af.viewport.setWrapAlignment(view.getWrapAlignment());
2949     af.alignPanel.setWrapAlignment(view.getWrapAlignment());
2950     af.viewport.setShowAnnotation(view.getShowAnnotation());
2951     af.alignPanel.setAnnotationVisible(view.getShowAnnotation());
2952
2953     af.viewport.setShowBoxes(view.getShowBoxes());
2954
2955     af.viewport.setShowText(view.getShowText());
2956
2957     af.viewport.textColour = new java.awt.Color(view.getTextCol1());
2958     af.viewport.textColour2 = new java.awt.Color(view.getTextCol2());
2959     af.viewport.thresholdTextColour = view.getTextColThreshold();
2960     af.viewport.setShowUnconserved(view.hasShowUnconserved() ? view
2961             .isShowUnconserved() : false);
2962     af.viewport.setStartRes(view.getStartRes());
2963     af.viewport.setStartSeq(view.getStartSeq());
2964
2965     ColourSchemeI cs = null;
2966     // apply colourschemes
2967     if (view.getBgColour() != null)
2968     {
2969       if (view.getBgColour().startsWith("ucs"))
2970       {
2971         cs = GetUserColourScheme(jms, view.getBgColour());
2972       }
2973       else if (view.getBgColour().startsWith("Annotation"))
2974       {
2975         // int find annotation
2976         if (af.viewport.alignment.getAlignmentAnnotation() != null)
2977         {
2978           for (int i = 0; i < af.viewport.alignment
2979                   .getAlignmentAnnotation().length; i++)
2980           {
2981             if (af.viewport.alignment.getAlignmentAnnotation()[i].label
2982                     .equals(view.getAnnotationColours().getAnnotation()))
2983             {
2984               if (af.viewport.alignment.getAlignmentAnnotation()[i]
2985                       .getThreshold() == null)
2986               {
2987                 af.viewport.alignment.getAlignmentAnnotation()[i]
2988                         .setThreshold(new jalview.datamodel.GraphLine(view
2989                                 .getAnnotationColours().getThreshold(),
2990                                 "Threshold", java.awt.Color.black)
2991
2992                         );
2993               }
2994
2995               if (view.getAnnotationColours().getColourScheme()
2996                       .equals("None"))
2997               {
2998                 cs = new AnnotationColourGradient(
2999                         af.viewport.alignment.getAlignmentAnnotation()[i],
3000                         new java.awt.Color(view.getAnnotationColours()
3001                                 .getMinColour()), new java.awt.Color(view
3002                                 .getAnnotationColours().getMaxColour()),
3003                         view.getAnnotationColours().getAboveThreshold());
3004               }
3005               else if (view.getAnnotationColours().getColourScheme()
3006                       .startsWith("ucs"))
3007               {
3008                 cs = new AnnotationColourGradient(
3009                         af.viewport.alignment.getAlignmentAnnotation()[i],
3010                         GetUserColourScheme(jms, view
3011                                 .getAnnotationColours().getColourScheme()),
3012                         view.getAnnotationColours().getAboveThreshold());
3013               }
3014               else
3015               {
3016                 cs = new AnnotationColourGradient(
3017                         af.viewport.alignment.getAlignmentAnnotation()[i],
3018                         ColourSchemeProperty.getColour(al, view
3019                                 .getAnnotationColours().getColourScheme()),
3020                         view.getAnnotationColours().getAboveThreshold());
3021               }
3022
3023               // Also use these settings for all the groups
3024               if (al.getGroups() != null)
3025               {
3026                 for (int g = 0; g < al.getGroups().size(); g++)
3027                 {
3028                   jalview.datamodel.SequenceGroup sg = (jalview.datamodel.SequenceGroup) al
3029                           .getGroups().elementAt(g);
3030
3031                   if (sg.cs == null)
3032                   {
3033                     continue;
3034                   }
3035
3036                   /*
3037                    * if
3038                    * (view.getAnnotationColours().getColourScheme().equals("None"
3039                    * )) { sg.cs = new AnnotationColourGradient(
3040                    * af.viewport.alignment.getAlignmentAnnotation()[i], new
3041                    * java.awt.Color(view.getAnnotationColours().
3042                    * getMinColour()), new
3043                    * java.awt.Color(view.getAnnotationColours().
3044                    * getMaxColour()),
3045                    * view.getAnnotationColours().getAboveThreshold()); } else
3046                    */
3047                   {
3048                     sg.cs = new AnnotationColourGradient(
3049                             af.viewport.alignment.getAlignmentAnnotation()[i],
3050                             sg.cs, view.getAnnotationColours()
3051                                     .getAboveThreshold());
3052                   }
3053
3054                 }
3055               }
3056
3057               break;
3058             }
3059
3060           }
3061         }
3062       }
3063       else
3064       {
3065         cs = ColourSchemeProperty.getColour(al, view.getBgColour());
3066       }
3067
3068       if (cs != null)
3069       {
3070         cs.setThreshold(view.getPidThreshold(), true);
3071         cs.setConsensus(af.viewport.hconsensus);
3072       }
3073     }
3074
3075     af.viewport.setGlobalColourScheme(cs);
3076     af.viewport.setColourAppliesToAllGroups(false);
3077
3078     if (view.getConservationSelected() && cs != null)
3079     {
3080       cs.setConservationInc(view.getConsThreshold());
3081     }
3082
3083     af.changeColour(cs);
3084
3085     af.viewport.setColourAppliesToAllGroups(true);
3086
3087     if (view.getShowSequenceFeatures())
3088     {
3089       af.viewport.showSequenceFeatures = true;
3090     }
3091     if (view.hasCentreColumnLabels())
3092     {
3093       af.viewport.setCentreColumnLabels(view.getCentreColumnLabels());
3094     }
3095     if (view.hasIgnoreGapsinConsensus())
3096     {
3097       af.viewport.ignoreGapsInConsensusCalculation = view
3098               .getIgnoreGapsinConsensus();
3099     }
3100     if (view.hasFollowHighlight())
3101     {
3102       af.viewport.followHighlight = view.getFollowHighlight();
3103     }
3104     if (view.hasFollowSelection())
3105     {
3106       af.viewport.followSelection = view.getFollowSelection();
3107     }
3108     if (view.hasShowConsensusHistogram())
3109     {
3110       af.viewport.setShowConsensusHistogram(view
3111               .getShowConsensusHistogram());
3112     }
3113     else
3114     {
3115       af.viewport.setShowConsensusHistogram(true);
3116     }
3117     if (view.hasShowSequenceLogo())
3118     {
3119       af.viewport.showSequenceLogo = view.getShowSequenceLogo();
3120     }
3121     else
3122     {
3123       af.viewport.showSequenceLogo = false;
3124     }
3125     if (view.hasShowDbRefTooltip())
3126     {
3127       af.viewport.setShowDbRefs(view.getShowDbRefTooltip());
3128     }
3129     if (view.hasShowNPfeatureTooltip())
3130     {
3131       af.viewport.setShowNpFeats(view.hasShowNPfeatureTooltip());
3132     }
3133     if (view.hasShowGroupConsensus())
3134     {
3135       af.viewport.setShowGroupConsensus(view.getShowGroupConsensus());
3136     }
3137     else
3138     {
3139       af.viewport.setShowGroupConsensus(false);
3140     }
3141     if (view.hasShowGroupConservation())
3142     {
3143       af.viewport.setShowGroupConservation(view.getShowGroupConservation());
3144     }
3145     else
3146     {
3147       af.viewport.setShowGroupConservation(false);
3148     }
3149
3150     // recover featre settings
3151     if (jms.getFeatureSettings() != null)
3152     {
3153       af.viewport.featuresDisplayed = new Hashtable();
3154       String[] renderOrder = new String[jms.getFeatureSettings()
3155               .getSettingCount()];
3156       for (int fs = 0; fs < jms.getFeatureSettings().getSettingCount(); fs++)
3157       {
3158         Setting setting = jms.getFeatureSettings().getSetting(fs);
3159         if (setting.hasMincolour())
3160         {
3161           GraduatedColor gc = setting.hasMin() ? new GraduatedColor(
3162                   new java.awt.Color(setting.getMincolour()),
3163                   new java.awt.Color(setting.getColour()),
3164                   setting.getMin(), setting.getMax()) : new GraduatedColor(
3165                   new java.awt.Color(setting.getMincolour()),
3166                   new java.awt.Color(setting.getColour()), 0, 1);
3167           if (setting.hasThreshold())
3168           {
3169             gc.setThresh(setting.getThreshold());
3170             gc.setThreshType(setting.getThreshstate());
3171           }
3172           gc.setAutoScaled(true); // default
3173           if (setting.hasAutoScale())
3174           {
3175             gc.setAutoScaled(setting.getAutoScale());
3176           }
3177           if (setting.hasColourByLabel())
3178           {
3179             gc.setColourByLabel(setting.getColourByLabel());
3180           }
3181           // and put in the feature colour table.
3182           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setColour(
3183                   setting.getType(), gc);
3184         }
3185         else
3186         {
3187           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setColour(
3188                   setting.getType(),
3189                   new java.awt.Color(setting.getColour()));
3190         }
3191         renderOrder[fs] = setting.getType();
3192         if (setting.hasOrder())
3193           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setOrder(
3194                   setting.getType(), setting.getOrder());
3195         else
3196           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setOrder(
3197                   setting.getType(),
3198                   fs / jms.getFeatureSettings().getSettingCount());
3199         if (setting.getDisplay())
3200         {
3201           af.viewport.featuresDisplayed.put(setting.getType(), new Integer(
3202                   setting.getColour()));
3203         }
3204       }
3205       af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().renderOrder = renderOrder;
3206       Hashtable fgtable;
3207       af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().featureGroups = fgtable = new Hashtable();
3208       for (int gs = 0; gs < jms.getFeatureSettings().getGroupCount(); gs++)
3209       {
3210         Group grp = jms.getFeatureSettings().getGroup(gs);
3211         fgtable.put(grp.getName(), new Boolean(grp.getDisplay()));
3212       }
3213     }
3214
3215     if (view.getHiddenColumnsCount() > 0)
3216     {
3217       for (int c = 0; c < view.getHiddenColumnsCount(); c++)
3218       {
3219         af.viewport.hideColumns(view.getHiddenColumns(c).getStart(), view
3220                 .getHiddenColumns(c).getEnd() // +1
3221                 );
3222       }
3223     }
3224
3225     af.setMenusFromViewport(af.viewport);
3226     // TODO: we don't need to do this if the viewport is aready visible.
3227     Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(),
3228             view.getHeight());
3229     af.alignPanel.updateAnnotation(false); // recompute any autoannotation
3230     reorderAutoannotation(af,al,autoAlan);
3231     return af;
3232   }
3233
3234   private void reorderAutoannotation(AlignFrame af, Alignment al,
3235           ArrayList<JvAnnotRow> autoAlan)
3236   {
3237  // copy over visualization settings for autocalculated annotation in the
3238     // view
3239     if (al.getAlignmentAnnotation() != null)
3240     {
3241       /**
3242        * Kludge for magic autoannotation names (see JAL-811)
3243        */
3244       String[] magicNames = new String[]
3245       { "Consensus", "Quality", "Conservation" };
3246       JvAnnotRow nullAnnot = new JvAnnotRow(-1, null);
3247       Hashtable<String, JvAnnotRow> visan = new Hashtable<String, JvAnnotRow>();
3248       for (String nm : magicNames)
3249       {
3250         visan.put(nm, nullAnnot);
3251       }
3252       for (JvAnnotRow auan : autoAlan)
3253       {
3254         visan.put(auan.template.label, auan);
3255       }
3256       int hSize = al.getAlignmentAnnotation().length;
3257       ArrayList<JvAnnotRow> reorder = new ArrayList<JvAnnotRow>();
3258       for (int h = 0; h < hSize; h++)
3259       {
3260         jalview.datamodel.AlignmentAnnotation jalan = al
3261                 .getAlignmentAnnotation()[h];
3262         if (jalan.autoCalculated)
3263         {
3264           JvAnnotRow valan = visan.get(jalan.label);
3265           if (valan != null)
3266           {
3267             // delete the auto calculated row from the alignment
3268             al.deleteAnnotation(al.getAlignmentAnnotation()[h],false);
3269             hSize--;
3270             h--;
3271             if (valan != nullAnnot)
3272             {
3273               if (jalan!=valan.template) { 
3274                 // newly created autoannotation row instance
3275                 // so keep a reference to the visible annotation row
3276                 // and copy over all relevant attributes
3277                   if (valan.template.graphHeight >= 0)
3278               
3279               {
3280                 jalan.graphHeight = valan.template.graphHeight;
3281               }
3282               jalan.visible = valan.template.visible;
3283               }
3284               reorder.add(new JvAnnotRow(valan.order, jalan));
3285             }
3286           }
3287         }
3288       }
3289       int s=0,srt[] = new int[reorder.size()];
3290       JvAnnotRow[] rws = new JvAnnotRow[reorder.size()];
3291       for (JvAnnotRow jvar:reorder) {
3292         rws[s] = jvar;
3293         srt[s++]=jvar.order;
3294       }
3295       reorder.clear();
3296       jalview.util.QuickSort.sort(srt, rws);
3297       // and re-insert the annotation at its correct position
3298       for (JvAnnotRow jvar : rws)
3299       {
3300         al.addAnnotation(jvar.template, jvar.order);
3301       }
3302       af.alignPanel.adjustAnnotationHeight();
3303     }
3304   }
3305
3306   Hashtable skipList = null;
3307
3308   /**
3309    * TODO remove this method
3310    * 
3311    * @param view
3312    * @return AlignFrame bound to sequenceSetId from view, if one exists. private
3313    *         AlignFrame getSkippedFrame(Viewport view) { if (skipList==null) {
3314    *         throw new Error("Implementation Error. No skipList defined for this
3315    *         Jalview2XML instance."); } return (AlignFrame)
3316    *         skipList.get(view.getSequenceSetId()); }
3317    */
3318
3319   /**
3320    * Check if the Jalview view contained in object should be skipped or not.
3321    * 
3322    * @param object
3323    * @return true if view's sequenceSetId is a key in skipList
3324    */
3325   private boolean skipViewport(JalviewModel object)
3326   {
3327     if (skipList == null)
3328     {
3329       return false;
3330     }
3331     String id;
3332     if (skipList.containsKey(id = object.getJalviewModelSequence()
3333             .getViewport()[0].getSequenceSetId()))
3334     {
3335       if (Cache.log != null && Cache.log.isDebugEnabled())
3336       {
3337         Cache.log.debug("Skipping seuqence set id " + id);
3338       }
3339       return true;
3340     }
3341     return false;
3342   }
3343
3344   public void AddToSkipList(AlignFrame af)
3345   {
3346     if (skipList == null)
3347     {
3348       skipList = new Hashtable();
3349     }
3350     skipList.put(af.getViewport().getSequenceSetId(), af);
3351   }
3352
3353   public void clearSkipList()
3354   {
3355     if (skipList != null)
3356     {
3357       skipList.clear();
3358       skipList = null;
3359     }
3360   }
3361
3362   private void recoverDatasetFor(SequenceSet vamsasSet, Alignment al)
3363   {
3364     jalview.datamodel.Alignment ds = getDatasetFor(vamsasSet.getDatasetId());
3365     Vector dseqs = null;
3366     if (ds == null)
3367     {
3368       // create a list of new dataset sequences
3369       dseqs = new Vector();
3370     }
3371     for (int i = 0, iSize = vamsasSet.getSequenceCount(); i < iSize; i++)
3372     {
3373       Sequence vamsasSeq = vamsasSet.getSequence(i);
3374       ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs);
3375     }
3376     // create a new dataset
3377     if (ds == null)
3378     {
3379       SequenceI[] dsseqs = new SequenceI[dseqs.size()];
3380       dseqs.copyInto(dsseqs);
3381       ds = new jalview.datamodel.Alignment(dsseqs);
3382       debug("Created new dataset " + vamsasSet.getDatasetId()
3383               + " for alignment " + System.identityHashCode(al));
3384       addDatasetRef(vamsasSet.getDatasetId(), ds);
3385     }
3386     // set the dataset for the newly imported alignment.
3387     if (al.getDataset() == null)
3388     {
3389       al.setDataset(ds);
3390     }
3391   }
3392
3393   /**
3394    * 
3395    * @param vamsasSeq
3396    *          sequence definition to create/merge dataset sequence for
3397    * @param ds
3398    *          dataset alignment
3399    * @param dseqs
3400    *          vector to add new dataset sequence to
3401    */
3402   private void ensureJalviewDatasetSequence(Sequence vamsasSeq,
3403           AlignmentI ds, Vector dseqs)
3404   {
3405     // JBP TODO: Check this is called for AlCodonFrames to support recovery of
3406     // xRef Codon Maps
3407     jalview.datamodel.Sequence sq = (jalview.datamodel.Sequence) seqRefIds
3408             .get(vamsasSeq.getId());
3409     jalview.datamodel.SequenceI dsq = null;
3410     if (sq != null && sq.getDatasetSequence() != null)
3411     {
3412       dsq = (jalview.datamodel.SequenceI) sq.getDatasetSequence();
3413     }
3414
3415     String sqid = vamsasSeq.getDsseqid();
3416     if (dsq == null)
3417     {
3418       // need to create or add a new dataset sequence reference to this sequence
3419       if (sqid != null)
3420       {
3421         dsq = (jalview.datamodel.SequenceI) seqRefIds.get(sqid);
3422       }
3423       // check again
3424       if (dsq == null)
3425       {
3426         // make a new dataset sequence
3427         dsq = sq.createDatasetSequence();
3428         if (sqid == null)
3429         {
3430           // make up a new dataset reference for this sequence
3431           sqid = seqHash(dsq);
3432         }
3433         dsq.setVamsasId(uniqueSetSuffix + sqid);
3434         seqRefIds.put(sqid, dsq);
3435         if (ds == null)
3436         {
3437           if (dseqs != null)
3438           {
3439             dseqs.addElement(dsq);
3440           }
3441         }
3442         else
3443         {
3444           ds.addSequence(dsq);
3445         }
3446       }
3447       else
3448       {
3449         if (sq != dsq)
3450         { // make this dataset sequence sq's dataset sequence
3451           sq.setDatasetSequence(dsq);
3452         }
3453       }
3454     }
3455     // TODO: refactor this as a merge dataset sequence function
3456     // now check that sq (the dataset sequence) sequence really is the union of
3457     // all references to it
3458     // boolean pre = sq.getStart() < dsq.getStart();
3459     // boolean post = sq.getEnd() > dsq.getEnd();
3460     // if (pre || post)
3461     if (sq != dsq)
3462     {
3463       StringBuffer sb = new StringBuffer();
3464       String newres = jalview.analysis.AlignSeq.extractGaps(
3465               jalview.util.Comparison.GapChars, sq.getSequenceAsString());
3466       if (!newres.equalsIgnoreCase(dsq.getSequenceAsString())
3467               && newres.length() > dsq.getLength())
3468       {
3469         // Update with the longer sequence.
3470         synchronized (dsq)
3471         {
3472           /*
3473            * if (pre) { sb.insert(0, newres .substring(0, dsq.getStart() -
3474            * sq.getStart())); dsq.setStart(sq.getStart()); } if (post) {
3475            * sb.append(newres.substring(newres.length() - sq.getEnd() -
3476            * dsq.getEnd())); dsq.setEnd(sq.getEnd()); }
3477            */
3478           dsq.setSequence(sb.toString());
3479         }
3480         // TODO: merges will never happen if we 'know' we have the real dataset
3481         // sequence - this should be detected when id==dssid
3482         System.err.println("DEBUG Notice:  Merged dataset sequence"); // ("
3483         // + (pre ? "prepended" : "") + " "
3484         // + (post ? "appended" : ""));
3485       }
3486     }
3487   }
3488
3489   java.util.Hashtable datasetIds = null;
3490
3491   java.util.IdentityHashMap dataset2Ids = null;
3492
3493   private Alignment getDatasetFor(String datasetId)
3494   {
3495     if (datasetIds == null)
3496     {
3497       datasetIds = new Hashtable();
3498       return null;
3499     }
3500     if (datasetIds.containsKey(datasetId))
3501     {
3502       return (Alignment) datasetIds.get(datasetId);
3503     }
3504     return null;
3505   }
3506
3507   private void addDatasetRef(String datasetId, Alignment dataset)
3508   {
3509     if (datasetIds == null)
3510     {
3511       datasetIds = new Hashtable();
3512     }
3513     datasetIds.put(datasetId, dataset);
3514   }
3515
3516   /**
3517    * make a new dataset ID for this jalview dataset alignment
3518    * 
3519    * @param dataset
3520    * @return
3521    */
3522   private String getDatasetIdRef(jalview.datamodel.Alignment dataset)
3523   {
3524     if (dataset.getDataset() != null)
3525     {
3526       warn("Serious issue!  Dataset Object passed to getDatasetIdRef is not a Jalview DATASET alignment...");
3527     }
3528     String datasetId = makeHashCode(dataset, null);
3529     if (datasetId == null)
3530     {
3531       // make a new datasetId and record it
3532       if (dataset2Ids == null)
3533       {
3534         dataset2Ids = new IdentityHashMap();
3535       }
3536       else
3537       {
3538         datasetId = (String) dataset2Ids.get(dataset);
3539       }
3540       if (datasetId == null)
3541       {
3542         datasetId = "ds" + dataset2Ids.size() + 1;
3543         dataset2Ids.put(dataset, datasetId);
3544       }
3545     }
3546     return datasetId;
3547   }
3548
3549   private void addDBRefs(SequenceI datasetSequence, Sequence sequence)
3550   {
3551     for (int d = 0; d < sequence.getDBRefCount(); d++)
3552     {
3553       DBRef dr = sequence.getDBRef(d);
3554       jalview.datamodel.DBRefEntry entry = new jalview.datamodel.DBRefEntry(
3555               sequence.getDBRef(d).getSource(), sequence.getDBRef(d)
3556                       .getVersion(), sequence.getDBRef(d).getAccessionId());
3557       if (dr.getMapping() != null)
3558       {
3559         entry.setMap(addMapping(dr.getMapping()));
3560       }
3561       datasetSequence.addDBRef(entry);
3562     }
3563   }
3564
3565   private jalview.datamodel.Mapping addMapping(Mapping m)
3566   {
3567     SequenceI dsto = null;
3568     // Mapping m = dr.getMapping();
3569     int fr[] = new int[m.getMapListFromCount() * 2];
3570     Enumeration f = m.enumerateMapListFrom();
3571     for (int _i = 0; f.hasMoreElements(); _i += 2)
3572     {
3573       MapListFrom mf = (MapListFrom) f.nextElement();
3574       fr[_i] = mf.getStart();
3575       fr[_i + 1] = mf.getEnd();
3576     }
3577     int fto[] = new int[m.getMapListToCount() * 2];
3578     f = m.enumerateMapListTo();
3579     for (int _i = 0; f.hasMoreElements(); _i += 2)
3580     {
3581       MapListTo mf = (MapListTo) f.nextElement();
3582       fto[_i] = mf.getStart();
3583       fto[_i + 1] = mf.getEnd();
3584     }
3585     jalview.datamodel.Mapping jmap = new jalview.datamodel.Mapping(dsto,
3586             fr, fto, (int) m.getMapFromUnit(), (int) m.getMapToUnit());
3587     if (m.getMappingChoice() != null)
3588     {
3589       MappingChoice mc = m.getMappingChoice();
3590       if (mc.getDseqFor() != null)
3591       {
3592         String dsfor = "" + mc.getDseqFor();
3593         if (seqRefIds.containsKey(dsfor))
3594         {
3595           /**
3596            * recover from hash
3597            */
3598           jmap.setTo((SequenceI) seqRefIds.get(dsfor));
3599         }
3600         else
3601         {
3602           frefedSequence.add(new Object[]
3603           { dsfor, jmap });
3604         }
3605       }
3606       else
3607       {
3608         /**
3609          * local sequence definition
3610          */
3611         Sequence ms = mc.getSequence();
3612         jalview.datamodel.Sequence djs = null;
3613         String sqid = ms.getDsseqid();
3614         if (sqid != null && sqid.length() > 0)
3615         {
3616           /*
3617            * recover dataset sequence
3618            */
3619           djs = (jalview.datamodel.Sequence) seqRefIds.get(sqid);
3620         }
3621         else
3622         {
3623           System.err
3624                   .println("Warning - making up dataset sequence id for DbRef sequence map reference");
3625           sqid = ((Object) ms).toString(); // make up a new hascode for
3626           // undefined dataset sequence hash
3627           // (unlikely to happen)
3628         }
3629
3630         if (djs == null)
3631         {
3632           /**
3633            * make a new dataset sequence and add it to refIds hash
3634            */
3635           djs = new jalview.datamodel.Sequence(ms.getName(),
3636                   ms.getSequence());
3637           djs.setStart(jmap.getMap().getToLowest());
3638           djs.setEnd(jmap.getMap().getToHighest());
3639           djs.setVamsasId(uniqueSetSuffix + sqid);
3640           jmap.setTo(djs);
3641           seqRefIds.put(sqid, djs);
3642
3643         }
3644         jalview.bin.Cache.log.debug("about to recurse on addDBRefs.");
3645         addDBRefs(djs, ms);
3646
3647       }
3648     }
3649     return (jmap);
3650
3651   }
3652
3653   public jalview.gui.AlignmentPanel copyAlignPanel(AlignmentPanel ap,
3654           boolean keepSeqRefs)
3655   {
3656     initSeqRefs();
3657     jalview.schemabinding.version2.JalviewModel jm = SaveState(ap, null,
3658             null);
3659
3660     if (!keepSeqRefs)
3661     {
3662       clearSeqRefs();
3663       jm.getJalviewModelSequence().getViewport(0).setSequenceSetId(null);
3664     }
3665     else
3666     {
3667       uniqueSetSuffix = "";
3668       jm.getJalviewModelSequence().getViewport(0).setId(null); // we don't
3669       // overwrite the
3670       // view we just
3671       // copied
3672     }
3673     if (this.frefedSequence == null)
3674     {
3675       frefedSequence = new Vector();
3676     }
3677
3678     viewportsAdded = new Hashtable();
3679
3680     AlignFrame af = LoadFromObject(jm, null, false, null);
3681     af.alignPanels.clear();
3682     af.closeMenuItem_actionPerformed(true);
3683
3684     /*
3685      * if(ap.av.alignment.getAlignmentAnnotation()!=null) { for(int i=0;
3686      * i<ap.av.alignment.getAlignmentAnnotation().length; i++) {
3687      * if(!ap.av.alignment.getAlignmentAnnotation()[i].autoCalculated) {
3688      * af.alignPanel.av.alignment.getAlignmentAnnotation()[i] =
3689      * ap.av.alignment.getAlignmentAnnotation()[i]; } } }
3690      */
3691
3692     return af.alignPanel;
3693   }
3694
3695   /**
3696    * flag indicating if hashtables should be cleared on finalization TODO this
3697    * flag may not be necessary
3698    */
3699   private boolean _cleartables = true;
3700
3701   private Hashtable jvids2vobj;
3702
3703   /*
3704    * (non-Javadoc)
3705    * 
3706    * @see java.lang.Object#finalize()
3707    */
3708   protected void finalize() throws Throwable
3709   {
3710     // really make sure we have no buried refs left.
3711     if (_cleartables)
3712     {
3713       clearSeqRefs();
3714     }
3715     this.seqRefIds = null;
3716     this.seqsToIds = null;
3717     super.finalize();
3718   }
3719
3720   private void warn(String msg)
3721   {
3722     warn(msg, null);
3723   }
3724
3725   private void warn(String msg, Exception e)
3726   {
3727     if (Cache.log != null)
3728     {
3729       if (e != null)
3730       {
3731         Cache.log.warn(msg, e);
3732       }
3733       else
3734       {
3735         Cache.log.warn(msg);
3736       }
3737     }
3738     else
3739     {
3740       System.err.println("Warning: " + msg);
3741       if (e != null)
3742       {
3743         e.printStackTrace();
3744       }
3745     }
3746   }
3747
3748   private void debug(String string)
3749   {
3750     debug(string, null);
3751   }
3752
3753   private void debug(String msg, Exception e)
3754   {
3755     if (Cache.log != null)
3756     {
3757       if (e != null)
3758       {
3759         Cache.log.debug(msg, e);
3760       }
3761       else
3762       {
3763         Cache.log.debug(msg);
3764       }
3765     }
3766     else
3767     {
3768       System.err.println("Warning: " + msg);
3769       if (e != null)
3770       {
3771         e.printStackTrace();
3772       }
3773     }
3774   }
3775
3776   /**
3777    * set the object to ID mapping tables used to write/recover objects and XML
3778    * ID strings for the jalview project. If external tables are provided then
3779    * finalize and clearSeqRefs will not clear the tables when the Jalview2XML
3780    * object goes out of scope. - also populates the datasetIds hashtable with
3781    * alignment objects containing dataset sequences
3782    * 
3783    * @param vobj2jv
3784    *          Map from ID strings to jalview datamodel
3785    * @param jv2vobj
3786    *          Map from jalview datamodel to ID strings
3787    * 
3788    * 
3789    */
3790   public void setObjectMappingTables(Hashtable vobj2jv,
3791           IdentityHashMap jv2vobj)
3792   {
3793     this.jv2vobj = jv2vobj;
3794     this.vobj2jv = vobj2jv;
3795     Iterator ds = jv2vobj.keySet().iterator();
3796     String id;
3797     while (ds.hasNext())
3798     {
3799       Object jvobj = ds.next();
3800       id = jv2vobj.get(jvobj).toString();
3801       if (jvobj instanceof jalview.datamodel.Alignment)
3802       {
3803         if (((jalview.datamodel.Alignment) jvobj).getDataset() == null)
3804         {
3805           addDatasetRef(id, (jalview.datamodel.Alignment) jvobj);
3806         }
3807       }
3808       else if (jvobj instanceof jalview.datamodel.Sequence)
3809       {
3810         // register sequence object so the XML parser can recover it.
3811         if (seqRefIds == null)
3812         {
3813           seqRefIds = new Hashtable();
3814         }
3815         if (seqsToIds == null)
3816         {
3817           seqsToIds = new IdentityHashMap();
3818         }
3819         seqRefIds.put(jv2vobj.get(jvobj).toString(), jvobj);
3820         seqsToIds.put(jvobj, id);
3821       }
3822       else if (jvobj instanceof jalview.datamodel.AlignmentAnnotation)
3823       {
3824         if (annotationIds == null)
3825         {
3826           annotationIds = new Hashtable();
3827         }
3828         String anid;
3829         annotationIds.put(anid = jv2vobj.get(jvobj).toString(), jvobj);
3830         jalview.datamodel.AlignmentAnnotation jvann = (jalview.datamodel.AlignmentAnnotation) jvobj;
3831         if (jvann.annotationId == null)
3832         {
3833           jvann.annotationId = anid;
3834         }
3835         if (!jvann.annotationId.equals(anid))
3836         {
3837           // TODO verify that this is the correct behaviour
3838           this.warn("Overriding Annotation ID for " + anid
3839                   + " from different id : " + jvann.annotationId);
3840           jvann.annotationId = anid;
3841         }
3842       }
3843       else if (jvobj instanceof String)
3844       {
3845         if (jvids2vobj == null)
3846         {
3847           jvids2vobj = new Hashtable();
3848           jvids2vobj.put(jvobj, jv2vobj.get(jvobj).toString());
3849         }
3850       }
3851       else
3852         Cache.log.debug("Ignoring " + jvobj.getClass() + " (ID = " + id);
3853     }
3854   }
3855
3856   /**
3857    * set the uniqueSetSuffix used to prefix/suffix object IDs for jalview
3858    * objects created from the project archive. If string is null (default for
3859    * construction) then suffix will be set automatically.
3860    * 
3861    * @param string
3862    */
3863   public void setUniqueSetSuffix(String string)
3864   {
3865     uniqueSetSuffix = string;
3866
3867   }
3868
3869   /**
3870    * uses skipList2 as the skipList for skipping views on sequence sets
3871    * associated with keys in the skipList
3872    * 
3873    * @param skipList2
3874    */
3875   public void setSkipList(Hashtable skipList2)
3876   {
3877     skipList = skipList2;
3878   }
3879
3880 }