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