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