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