JAL-1397 store colour by sequence and originalColour flags for AnnotationColourScheme
[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.getDefault("VERSION","Development Build"));
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       ac.setPerSequence(acg.isSeqAssociated());
1073       ac.setPredefinedColours(acg.isPredefinedColours());
1074       view.setAnnotationColours(ac);
1075       view.setBgColour("AnnotationColourGradient");
1076     }
1077     else
1078     {
1079       view.setBgColour(ColourSchemeProperty.getColourName(av
1080               .getGlobalColourScheme()));
1081     }
1082
1083     ColourSchemeI cs = av.getGlobalColourScheme();
1084
1085     if (cs != null)
1086     {
1087       if (cs.conservationApplied())
1088       {
1089         view.setConsThreshold(cs.getConservationInc());
1090         if (cs instanceof jalview.schemes.UserColourScheme)
1091         {
1092           view.setBgColour(SetUserColourScheme(cs, userColours, jms));
1093         }
1094       }
1095
1096       if (cs instanceof ResidueColourScheme)
1097       {
1098         view.setPidThreshold(cs.getThreshold());
1099       }
1100     }
1101
1102     view.setConservationSelected(av.getConservationSelected());
1103     view.setPidSelected(av.getAbovePIDThreshold());
1104     view.setFontName(av.font.getName());
1105     view.setFontSize(av.font.getSize());
1106     view.setFontStyle(av.font.getStyle());
1107     view.setRenderGaps(av.renderGaps);
1108     view.setShowAnnotation(av.getShowAnnotation());
1109     view.setShowBoxes(av.getShowBoxes());
1110     view.setShowColourText(av.getColourText());
1111     view.setShowFullId(av.getShowJVSuffix());
1112     view.setRightAlignIds(av.rightAlignIds);
1113     view.setShowSequenceFeatures(av.showSequenceFeatures);
1114     view.setShowText(av.getShowText());
1115     view.setShowUnconserved(av.getShowUnconserved());
1116     view.setWrapAlignment(av.getWrapAlignment());
1117     view.setTextCol1(av.textColour.getRGB());
1118     view.setTextCol2(av.textColour2.getRGB());
1119     view.setTextColThreshold(av.thresholdTextColour);
1120     view.setShowConsensusHistogram(av.isShowConsensusHistogram());
1121     view.setShowSequenceLogo(av.isShowSequenceLogo());
1122     view.setNormaliseSequenceLogo(av.isNormaliseSequenceLogo());
1123     view.setShowGroupConsensus(av.isShowGroupConsensus());
1124     view.setShowGroupConservation(av.isShowGroupConservation());
1125     view.setShowNPfeatureTooltip(av.isShowNpFeats());
1126     view.setShowDbRefTooltip(av.isShowDbRefs());
1127     view.setFollowHighlight(av.followHighlight);
1128     view.setFollowSelection(av.followSelection);
1129     view.setIgnoreGapsinConsensus(av.getIgnoreGapsConsensus());
1130     if (av.featuresDisplayed != null)
1131     {
1132       jalview.schemabinding.version2.FeatureSettings fs = new jalview.schemabinding.version2.FeatureSettings();
1133
1134       String[] renderOrder = ap.seqPanel.seqCanvas.getFeatureRenderer().renderOrder;
1135
1136       Vector settingsAdded = new Vector();
1137       Object gstyle = null;
1138       GraduatedColor gcol = null;
1139       if (renderOrder != null)
1140       {
1141         for (int ro = 0; ro < renderOrder.length; ro++)
1142         {
1143           gstyle = ap.seqPanel.seqCanvas.getFeatureRenderer()
1144                   .getFeatureStyle(renderOrder[ro]);
1145           Setting setting = new Setting();
1146           setting.setType(renderOrder[ro]);
1147           if (gstyle instanceof GraduatedColor)
1148           {
1149             gcol = (GraduatedColor) gstyle;
1150             setting.setColour(gcol.getMaxColor().getRGB());
1151             setting.setMincolour(gcol.getMinColor().getRGB());
1152             setting.setMin(gcol.getMin());
1153             setting.setMax(gcol.getMax());
1154             setting.setColourByLabel(gcol.isColourByLabel());
1155             setting.setAutoScale(gcol.isAutoScale());
1156             setting.setThreshold(gcol.getThresh());
1157             setting.setThreshstate(gcol.getThreshType());
1158           }
1159           else
1160           {
1161             setting.setColour(ap.seqPanel.seqCanvas.getFeatureRenderer()
1162                     .getColour(renderOrder[ro]).getRGB());
1163           }
1164
1165           setting.setDisplay(av.featuresDisplayed
1166                   .containsKey(renderOrder[ro]));
1167           float rorder = ap.seqPanel.seqCanvas.getFeatureRenderer()
1168                   .getOrder(renderOrder[ro]);
1169           if (rorder > -1)
1170           {
1171             setting.setOrder(rorder);
1172           }
1173           fs.addSetting(setting);
1174           settingsAdded.addElement(renderOrder[ro]);
1175         }
1176       }
1177
1178       // Make sure we save none displayed feature settings
1179       Iterator en = ap.seqPanel.seqCanvas.getFeatureRenderer().featureColours
1180               .keySet().iterator();
1181       while (en.hasNext())
1182       {
1183         String key = en.next().toString();
1184         if (settingsAdded.contains(key))
1185         {
1186           continue;
1187         }
1188
1189         Setting setting = new Setting();
1190         setting.setType(key);
1191         setting.setColour(ap.seqPanel.seqCanvas.getFeatureRenderer()
1192                 .getColour(key).getRGB());
1193
1194         setting.setDisplay(false);
1195         float rorder = ap.seqPanel.seqCanvas.getFeatureRenderer().getOrder(
1196                 key);
1197         if (rorder > -1)
1198         {
1199           setting.setOrder(rorder);
1200         }
1201         fs.addSetting(setting);
1202         settingsAdded.addElement(key);
1203       }
1204       en = ap.seqPanel.seqCanvas.getFeatureRenderer().featureGroups
1205               .keySet().iterator();
1206       Vector groupsAdded = new Vector();
1207       while (en.hasNext())
1208       {
1209         String grp = en.next().toString();
1210         if (groupsAdded.contains(grp))
1211         {
1212           continue;
1213         }
1214         Group g = new Group();
1215         g.setName(grp);
1216         g.setDisplay(((Boolean) ap.seqPanel.seqCanvas.getFeatureRenderer().featureGroups
1217                 .get(grp)).booleanValue());
1218         fs.addGroup(g);
1219         groupsAdded.addElement(grp);
1220       }
1221       jms.setFeatureSettings(fs);
1222
1223     }
1224
1225     if (av.hasHiddenColumns())
1226     {
1227       if (av.getColumnSelection() == null
1228               || av.getColumnSelection().getHiddenColumns() == null)
1229       {
1230         warn("REPORT BUG: avoided null columnselection bug (DMAM reported). Please contact Jim about this.");
1231       }
1232       else
1233       {
1234         for (int c = 0; c < av.getColumnSelection().getHiddenColumns()
1235                 .size(); c++)
1236         {
1237           int[] region = (int[]) av.getColumnSelection().getHiddenColumns()
1238                   .elementAt(c);
1239           HiddenColumns hc = new HiddenColumns();
1240           hc.setStart(region[0]);
1241           hc.setEnd(region[1]);
1242           view.addHiddenColumns(hc);
1243         }
1244       }
1245     }
1246     if (calcIdSet.size() > 0)
1247     {
1248       for (String calcId : calcIdSet)
1249       {
1250         if (calcId.trim().length() > 0)
1251         {
1252           CalcIdParam cidp = createCalcIdParam(calcId, av);
1253           // Some calcIds have no parameters.
1254           if (cidp != null)
1255           {
1256             view.addCalcIdParam(cidp);
1257           }
1258         }
1259       }
1260     }
1261
1262     jms.addViewport(view);
1263
1264     object.setJalviewModelSequence(jms);
1265     object.getVamsasModel().addSequenceSet(vamsasSet);
1266
1267     if (jout != null && fileName != null)
1268     {
1269       // We may not want to write the object to disk,
1270       // eg we can copy the alignViewport to a new view object
1271       // using save and then load
1272       try
1273       {
1274         JarEntry entry = new JarEntry(fileName);
1275         jout.putNextEntry(entry);
1276         PrintWriter pout = new PrintWriter(new OutputStreamWriter(jout,
1277                 "UTF-8"));
1278         org.exolab.castor.xml.Marshaller marshaller = new org.exolab.castor.xml.Marshaller(
1279                 pout);
1280         marshaller.marshal(object);
1281         pout.flush();
1282         jout.closeEntry();
1283       } catch (Exception ex)
1284       {
1285         // TODO: raise error in GUI if marshalling failed.
1286         ex.printStackTrace();
1287       }
1288     }
1289     return object;
1290   }
1291
1292   private CalcIdParam createCalcIdParam(String calcId, AlignViewport av)
1293   {
1294     AutoCalcSetting settings = av.getCalcIdSettingsFor(calcId);
1295     if (settings != null)
1296     {
1297       CalcIdParam vCalcIdParam = new CalcIdParam();
1298       vCalcIdParam.setCalcId(calcId);
1299       vCalcIdParam.addServiceURL(settings.getServiceURI());
1300       // generic URI allowing a third party to resolve another instance of the
1301       // service used for this calculation
1302       for (String urls : settings.getServiceURLs())
1303       {
1304         vCalcIdParam.addServiceURL(urls);
1305       }
1306       vCalcIdParam.setVersion("1.0");
1307       if (settings.getPreset() != null)
1308       {
1309         WsParamSetI setting = settings.getPreset();
1310         vCalcIdParam.setName(setting.getName());
1311         vCalcIdParam.setDescription(setting.getDescription());
1312       }
1313       else
1314       {
1315         vCalcIdParam.setName("");
1316         vCalcIdParam.setDescription("Last used parameters");
1317       }
1318       // need to be able to recover 1) settings 2) user-defined presets or
1319       // recreate settings from preset 3) predefined settings provided by
1320       // service - or settings that can be transferred (or discarded)
1321       vCalcIdParam.setParameters(settings.getWsParamFile().replace("\n",
1322               "|\\n|"));
1323       vCalcIdParam.setAutoUpdate(settings.isAutoUpdate());
1324       // todo - decide if updateImmediately is needed for any projects.
1325
1326       return vCalcIdParam;
1327     }
1328     return null;
1329   }
1330
1331   private boolean recoverCalcIdParam(CalcIdParam calcIdParam,
1332           AlignViewport av)
1333   {
1334     if (calcIdParam.getVersion().equals("1.0"))
1335     {
1336       Jws2Instance service = Jws2Discoverer.getDiscoverer()
1337               .getPreferredServiceFor(calcIdParam.getServiceURL());
1338       if (service != null)
1339       {
1340         WsParamSetI parmSet = null;
1341         try
1342         {
1343           parmSet = service.getParamStore().parseServiceParameterFile(
1344                   calcIdParam.getName(), calcIdParam.getDescription(),
1345                   calcIdParam.getServiceURL(),
1346                   calcIdParam.getParameters().replace("|\\n|", "\n"));
1347         } catch (IOException x)
1348         {
1349           warn("Couldn't parse parameter data for "
1350                   + calcIdParam.getCalcId(), x);
1351           return false;
1352         }
1353         List<ArgumentI> argList = null;
1354         if (calcIdParam.getName().length() > 0)
1355         {
1356           parmSet = service.getParamStore()
1357                   .getPreset(calcIdParam.getName());
1358           if (parmSet != null)
1359           {
1360             // TODO : check we have a good match with settings in AACon -
1361             // otherwise we'll need to create a new preset
1362           }
1363         }
1364         else
1365         {
1366           argList = parmSet.getArguments();
1367           parmSet = null;
1368         }
1369         AAConSettings settings = new AAConSettings(
1370                 calcIdParam.isAutoUpdate(), service, parmSet, argList);
1371         av.setCalcIdSettingsFor(calcIdParam.getCalcId(), settings,
1372                 calcIdParam.isNeedsUpdate());
1373         return true;
1374       }
1375       else
1376       {
1377         warn("Cannot resolve a service for the parameters used in this project. Try configuring a JABAWS server.");
1378         return false;
1379       }
1380     }
1381     throw new Error("Unsupported Version for calcIdparam "
1382             + calcIdParam.toString());
1383   }
1384
1385   /**
1386    * External mapping between jalview objects and objects yielding a valid and
1387    * unique object ID string. This is null for normal Jalview project IO, but
1388    * non-null when a jalview project is being read or written as part of a
1389    * vamsas session.
1390    */
1391   IdentityHashMap jv2vobj = null;
1392
1393   /**
1394    * Construct a unique ID for jvobj using either existing bindings or if none
1395    * exist, the result of the hashcode call for the object.
1396    * 
1397    * @param jvobj
1398    *          jalview data object
1399    * @return unique ID for referring to jvobj
1400    */
1401   private String makeHashCode(Object jvobj, String altCode)
1402   {
1403     if (jv2vobj != null)
1404     {
1405       Object id = jv2vobj.get(jvobj);
1406       if (id != null)
1407       {
1408         return id.toString();
1409       }
1410       // check string ID mappings
1411       if (jvids2vobj != null && jvobj instanceof String)
1412       {
1413         id = jvids2vobj.get(jvobj);
1414       }
1415       if (id != null)
1416       {
1417         return id.toString();
1418       }
1419       // give up and warn that something has gone wrong
1420       warn("Cannot find ID for object in external mapping : " + jvobj);
1421     }
1422     return altCode;
1423   }
1424
1425   /**
1426    * return local jalview object mapped to ID, if it exists
1427    * 
1428    * @param idcode
1429    *          (may be null)
1430    * @return null or object bound to idcode
1431    */
1432   private Object retrieveExistingObj(String idcode)
1433   {
1434     if (idcode != null && vobj2jv != null)
1435     {
1436       return vobj2jv.get(idcode);
1437     }
1438     return null;
1439   }
1440
1441   /**
1442    * binding from ID strings from external mapping table to jalview data model
1443    * objects.
1444    */
1445   private Hashtable vobj2jv;
1446
1447   private Sequence createVamsasSequence(String id, SequenceI jds)
1448   {
1449     return createVamsasSequence(true, id, jds, null);
1450   }
1451
1452   private Sequence createVamsasSequence(boolean recurse, String id,
1453           SequenceI jds, SequenceI parentseq)
1454   {
1455     Sequence vamsasSeq = new Sequence();
1456     vamsasSeq.setId(id);
1457     vamsasSeq.setName(jds.getName());
1458     vamsasSeq.setSequence(jds.getSequenceAsString());
1459     vamsasSeq.setDescription(jds.getDescription());
1460     jalview.datamodel.DBRefEntry[] dbrefs = null;
1461     if (jds.getDatasetSequence() != null)
1462     {
1463       vamsasSeq.setDsseqid(seqHash(jds.getDatasetSequence()));
1464       if (jds.getDatasetSequence().getDBRef() != null)
1465       {
1466         dbrefs = jds.getDatasetSequence().getDBRef();
1467       }
1468     }
1469     else
1470     {
1471       vamsasSeq.setDsseqid(id); // so we can tell which sequences really are
1472       // dataset sequences only
1473       dbrefs = jds.getDBRef();
1474     }
1475     if (dbrefs != null)
1476     {
1477       for (int d = 0; d < dbrefs.length; d++)
1478       {
1479         DBRef dbref = new DBRef();
1480         dbref.setSource(dbrefs[d].getSource());
1481         dbref.setVersion(dbrefs[d].getVersion());
1482         dbref.setAccessionId(dbrefs[d].getAccessionId());
1483         if (dbrefs[d].hasMap())
1484         {
1485           Mapping mp = createVamsasMapping(dbrefs[d].getMap(), parentseq,
1486                   jds, recurse);
1487           dbref.setMapping(mp);
1488         }
1489         vamsasSeq.addDBRef(dbref);
1490       }
1491     }
1492     return vamsasSeq;
1493   }
1494
1495   private Mapping createVamsasMapping(jalview.datamodel.Mapping jmp,
1496           SequenceI parentseq, SequenceI jds, boolean recurse)
1497   {
1498     Mapping mp = null;
1499     if (jmp.getMap() != null)
1500     {
1501       mp = new Mapping();
1502
1503       jalview.util.MapList mlst = jmp.getMap();
1504       int r[] = mlst.getFromRanges();
1505       for (int s = 0; s < r.length; s += 2)
1506       {
1507         MapListFrom mfrom = new MapListFrom();
1508         mfrom.setStart(r[s]);
1509         mfrom.setEnd(r[s + 1]);
1510         mp.addMapListFrom(mfrom);
1511       }
1512       r = mlst.getToRanges();
1513       for (int s = 0; s < r.length; s += 2)
1514       {
1515         MapListTo mto = new MapListTo();
1516         mto.setStart(r[s]);
1517         mto.setEnd(r[s + 1]);
1518         mp.addMapListTo(mto);
1519       }
1520       mp.setMapFromUnit(mlst.getFromRatio());
1521       mp.setMapToUnit(mlst.getToRatio());
1522       if (jmp.getTo() != null)
1523       {
1524         MappingChoice mpc = new MappingChoice();
1525         if (recurse
1526                 && (parentseq != jmp.getTo() || parentseq
1527                         .getDatasetSequence() != jmp.getTo()))
1528         {
1529           mpc.setSequence(createVamsasSequence(false, seqHash(jmp.getTo()),
1530                   jmp.getTo(), jds));
1531         }
1532         else
1533         {
1534           String jmpid = "";
1535           SequenceI ps = null;
1536           if (parentseq != jmp.getTo()
1537                   && parentseq.getDatasetSequence() != jmp.getTo())
1538           {
1539             // chaining dbref rather than a handshaking one
1540             jmpid = seqHash(ps = jmp.getTo());
1541           }
1542           else
1543           {
1544             jmpid = seqHash(ps = parentseq);
1545           }
1546           mpc.setDseqFor(jmpid);
1547           if (!seqRefIds.containsKey(mpc.getDseqFor()))
1548           {
1549             jalview.bin.Cache.log.debug("creatign new DseqFor ID");
1550             seqRefIds.put(mpc.getDseqFor(), ps);
1551           }
1552           else
1553           {
1554             jalview.bin.Cache.log.debug("reusing DseqFor ID");
1555           }
1556         }
1557         mp.setMappingChoice(mpc);
1558       }
1559     }
1560     return mp;
1561   }
1562
1563   String SetUserColourScheme(jalview.schemes.ColourSchemeI cs,
1564           Vector userColours, JalviewModelSequence jms)
1565   {
1566     String id = null;
1567     jalview.schemes.UserColourScheme ucs = (jalview.schemes.UserColourScheme) cs;
1568     boolean newucs = false;
1569     if (!userColours.contains(ucs))
1570     {
1571       userColours.add(ucs);
1572       newucs = true;
1573     }
1574     id = "ucs" + userColours.indexOf(ucs);
1575     if (newucs)
1576     {
1577       // actually create the scheme's entry in the XML model
1578       java.awt.Color[] colours = ucs.getColours();
1579       jalview.schemabinding.version2.UserColours uc = new jalview.schemabinding.version2.UserColours();
1580       jalview.schemabinding.version2.UserColourScheme jbucs = new jalview.schemabinding.version2.UserColourScheme();
1581
1582       for (int i = 0; i < colours.length; i++)
1583       {
1584         jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
1585         col.setName(ResidueProperties.aa[i]);
1586         col.setRGB(jalview.util.Format.getHexString(colours[i]));
1587         jbucs.addColour(col);
1588       }
1589       if (ucs.getLowerCaseColours() != null)
1590       {
1591         colours = ucs.getLowerCaseColours();
1592         for (int i = 0; i < colours.length; i++)
1593         {
1594           jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
1595           col.setName(ResidueProperties.aa[i].toLowerCase());
1596           col.setRGB(jalview.util.Format.getHexString(colours[i]));
1597           jbucs.addColour(col);
1598         }
1599       }
1600
1601       uc.setId(id);
1602       uc.setUserColourScheme(jbucs);
1603       jms.addUserColours(uc);
1604     }
1605
1606     return id;
1607   }
1608
1609   jalview.schemes.UserColourScheme GetUserColourScheme(
1610           JalviewModelSequence jms, String id)
1611   {
1612     UserColours[] uc = jms.getUserColours();
1613     UserColours colours = null;
1614
1615     for (int i = 0; i < uc.length; i++)
1616     {
1617       if (uc[i].getId().equals(id))
1618       {
1619         colours = uc[i];
1620
1621         break;
1622       }
1623     }
1624
1625     java.awt.Color[] newColours = new java.awt.Color[24];
1626
1627     for (int i = 0; i < 24; i++)
1628     {
1629       newColours[i] = new java.awt.Color(Integer.parseInt(colours
1630               .getUserColourScheme().getColour(i).getRGB(), 16));
1631     }
1632
1633     jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme(
1634             newColours);
1635
1636     if (colours.getUserColourScheme().getColourCount() > 24)
1637     {
1638       newColours = new java.awt.Color[23];
1639       for (int i = 0; i < 23; i++)
1640       {
1641         newColours[i] = new java.awt.Color(Integer.parseInt(colours
1642                 .getUserColourScheme().getColour(i + 24).getRGB(), 16));
1643       }
1644       ucs.setLowerCaseColours(newColours);
1645     }
1646
1647     return ucs;
1648   }
1649
1650   /**
1651    * contains last error message (if any) encountered by XML loader.
1652    */
1653   String errorMessage = null;
1654
1655   /**
1656    * flag to control whether the Jalview2XML_V1 parser should be deferred to if
1657    * exceptions are raised during project XML parsing
1658    */
1659   public boolean attemptversion1parse = true;
1660
1661   /**
1662    * Load a jalview project archive from a jar file
1663    * 
1664    * @param file
1665    *          - HTTP URL or filename
1666    */
1667   public AlignFrame LoadJalviewAlign(final String file)
1668   {
1669
1670     jalview.gui.AlignFrame af = null;
1671
1672     try
1673     {
1674       // create list to store references for any new Jmol viewers created
1675       newStructureViewers=new Vector<AppJmol>();
1676       // UNMARSHALLER SEEMS TO CLOSE JARINPUTSTREAM, MOST ANNOYING
1677       // Workaround is to make sure caller implements the JarInputStreamProvider
1678       // interface
1679       // so we can re-open the jar input stream for each entry.
1680
1681       jarInputStreamProvider jprovider = createjarInputStreamProvider(file);
1682       af = LoadJalviewAlign(jprovider);
1683       
1684     } catch (MalformedURLException e)
1685     {
1686       errorMessage = "Invalid URL format for '" + file + "'";
1687       reportErrors();
1688     }
1689     finally {
1690       try
1691       {
1692         SwingUtilities.invokeAndWait(new Runnable()
1693         {
1694           public void run()
1695           {
1696             setLoadingFinishedForNewStructureViewers();
1697           };
1698         });
1699       } catch (Exception x)
1700       {
1701
1702       }
1703     }
1704     return af;
1705   }
1706
1707   private jarInputStreamProvider createjarInputStreamProvider(
1708           final String file) throws MalformedURLException
1709   {
1710     URL url = null;
1711     errorMessage = null;
1712     uniqueSetSuffix = null;
1713     seqRefIds = null;
1714     viewportsAdded = null;
1715     frefedSequence = null;
1716
1717     if (file.startsWith("http://"))
1718     {
1719       url = new URL(file);
1720     }
1721     final URL _url = url;
1722     return new jarInputStreamProvider()
1723     {
1724
1725       @Override
1726       public JarInputStream getJarInputStream() throws IOException
1727       {
1728         if (_url != null)
1729         {
1730           return new JarInputStream(_url.openStream());
1731         }
1732         else
1733         {
1734           return new JarInputStream(new FileInputStream(file));
1735         }
1736       }
1737
1738       @Override
1739       public String getFilename()
1740       {
1741         return file;
1742       }
1743     };
1744   }
1745
1746   /**
1747    * Recover jalview session from a jalview project archive. Caller may
1748    * initialise uniqueSetSuffix, seqRefIds, viewportsAdded and frefedSequence
1749    * themselves. Any null fields will be initialised with default values,
1750    * non-null fields are left alone.
1751    * 
1752    * @param jprovider
1753    * @return
1754    */
1755   public AlignFrame LoadJalviewAlign(final jarInputStreamProvider jprovider)
1756   {
1757     errorMessage = null;
1758     if (uniqueSetSuffix == null)
1759     {
1760       uniqueSetSuffix = System.currentTimeMillis() % 100000 + "";
1761     }
1762     if (seqRefIds == null)
1763     {
1764       seqRefIds = new Hashtable();
1765     }
1766     if (viewportsAdded == null)
1767     {
1768       viewportsAdded = new Hashtable();
1769     }
1770     if (frefedSequence == null)
1771     {
1772       frefedSequence = new Vector();
1773     }
1774
1775     jalview.gui.AlignFrame af = null;
1776     Hashtable gatherToThisFrame = new Hashtable();
1777     final String file = jprovider.getFilename();
1778     try
1779     {
1780       JarInputStream jin = null;
1781       JarEntry jarentry = null;
1782       int entryCount = 1;
1783
1784       do
1785       {
1786         jin = jprovider.getJarInputStream();
1787         for (int i = 0; i < entryCount; i++)
1788         {
1789           jarentry = jin.getNextJarEntry();
1790         }
1791
1792         if (jarentry != null && jarentry.getName().endsWith(".xml"))
1793         {
1794           InputStreamReader in = new InputStreamReader(jin, "UTF-8");
1795           JalviewModel object = new JalviewModel();
1796
1797           Unmarshaller unmar = new Unmarshaller(object);
1798           unmar.setValidation(false);
1799           object = (JalviewModel) unmar.unmarshal(in);
1800           if (true) // !skipViewport(object))
1801           {
1802             af = LoadFromObject(object, file, true, jprovider);
1803             if (af.viewport.gatherViewsHere)
1804             {
1805               gatherToThisFrame.put(af.viewport.getSequenceSetId(), af);
1806             }
1807           }
1808           entryCount++;
1809         }
1810         else if (jarentry != null)
1811         {
1812           // Some other file here.
1813           entryCount++;
1814         }
1815       } while (jarentry != null);
1816       resolveFrefedSequences();
1817     } catch (java.io.FileNotFoundException ex)
1818     {
1819       ex.printStackTrace();
1820       errorMessage = "Couldn't locate Jalview XML file : " + file;
1821       System.err.println("Exception whilst loading jalview XML file : "
1822               + ex + "\n");
1823     } catch (java.net.UnknownHostException ex)
1824     {
1825       ex.printStackTrace();
1826       errorMessage = "Couldn't locate Jalview XML file : " + file;
1827       System.err.println("Exception whilst loading jalview XML file : "
1828               + ex + "\n");
1829     } catch (Exception ex)
1830     {
1831       System.err.println("Parsing as Jalview Version 2 file failed.");
1832       ex.printStackTrace(System.err);
1833       if (attemptversion1parse)
1834       {
1835         // Is Version 1 Jar file?
1836         try
1837         {
1838           af = new Jalview2XML_V1(raiseGUI).LoadJalviewAlign(jprovider);
1839         } catch (Exception ex2)
1840         {
1841           System.err.println("Exception whilst loading as jalviewXMLV1:");
1842           ex2.printStackTrace();
1843           af = null;
1844         }
1845       }
1846       if (Desktop.instance != null)
1847       {
1848         Desktop.instance.stopLoading();
1849       }
1850       if (af != null)
1851       {
1852         System.out.println("Successfully loaded archive file");
1853         return af;
1854       }
1855       ex.printStackTrace();
1856
1857       System.err.println("Exception whilst loading jalview XML file : "
1858               + ex + "\n");
1859     } catch (OutOfMemoryError e)
1860     {
1861       // Don't use the OOM Window here
1862       errorMessage = "Out of memory loading jalview XML file";
1863       System.err.println("Out of memory whilst loading jalview XML file");
1864       e.printStackTrace();
1865     }
1866
1867     if (Desktop.instance != null)
1868     {
1869       Desktop.instance.stopLoading();
1870     }
1871
1872     Enumeration en = gatherToThisFrame.elements();
1873     while (en.hasMoreElements())
1874     {
1875       Desktop.instance.gatherViews((AlignFrame) en.nextElement());
1876     }
1877     if (errorMessage != null)
1878     {
1879       reportErrors();
1880     }
1881     return af;
1882   }
1883
1884   /**
1885    * check errorMessage for a valid error message and raise an error box in the
1886    * GUI or write the current errorMessage to stderr and then clear the error
1887    * state.
1888    */
1889   protected void reportErrors()
1890   {
1891     reportErrors(false);
1892   }
1893
1894   protected void reportErrors(final boolean saving)
1895   {
1896     if (errorMessage != null)
1897     {
1898       final String finalErrorMessage = errorMessage;
1899       if (raiseGUI)
1900       {
1901         javax.swing.SwingUtilities.invokeLater(new Runnable()
1902         {
1903           @Override
1904           public void run()
1905           {
1906             JOptionPane.showInternalMessageDialog(Desktop.desktop,
1907                     finalErrorMessage, "Error "
1908                             + (saving ? "saving" : "loading")
1909                             + " Jalview file", JOptionPane.WARNING_MESSAGE);
1910           }
1911         });
1912       }
1913       else
1914       {
1915         System.err.println("Problem loading Jalview file: " + errorMessage);
1916       }
1917     }
1918     errorMessage = null;
1919   }
1920
1921   Hashtable alreadyLoadedPDB;
1922
1923   /**
1924    * when set, local views will be updated from view stored in JalviewXML
1925    * Currently (28th Sep 2008) things will go horribly wrong in vamsas document
1926    * sync if this is set to true.
1927    */
1928   private final boolean updateLocalViews = false;
1929
1930   String loadPDBFile(jarInputStreamProvider jprovider, String pdbId)
1931   {
1932     if (alreadyLoadedPDB == null)
1933       alreadyLoadedPDB = new Hashtable();
1934
1935     if (alreadyLoadedPDB.containsKey(pdbId))
1936       return alreadyLoadedPDB.get(pdbId).toString();
1937
1938     try
1939     {
1940       JarInputStream jin = jprovider.getJarInputStream();
1941       /*
1942        * if (jprovider.startsWith("http://")) { jin = new JarInputStream(new
1943        * URL(jprovider).openStream()); } else { jin = new JarInputStream(new
1944        * FileInputStream(jprovider)); }
1945        */
1946
1947       JarEntry entry = null;
1948       do
1949       {
1950         entry = jin.getNextJarEntry();
1951       } while (entry != null && !entry.getName().equals(pdbId));
1952       if (entry != null)
1953       {
1954         BufferedReader in = new BufferedReader(new InputStreamReader(jin));
1955         File outFile = File.createTempFile("jalview_pdb", ".txt");
1956         outFile.deleteOnExit();
1957         PrintWriter out = new PrintWriter(new FileOutputStream(outFile));
1958         String data;
1959
1960         while ((data = in.readLine()) != null)
1961         {
1962           out.println(data);
1963         }
1964         try
1965         {
1966           out.flush();
1967         } catch (Exception foo)
1968         {
1969         }
1970         ;
1971         out.close();
1972         String t = outFile.getAbsolutePath();
1973         alreadyLoadedPDB.put(pdbId, t);
1974         return t;
1975       }
1976       else
1977       {
1978         warn("Couldn't find PDB file entry in Jalview Jar for " + pdbId);
1979       }
1980     } catch (Exception ex)
1981     {
1982       ex.printStackTrace();
1983     }
1984
1985     return null;
1986   }
1987
1988   private class JvAnnotRow
1989   {
1990     public JvAnnotRow(int i, AlignmentAnnotation jaa)
1991     {
1992       order = i;
1993       template = jaa;
1994     }
1995
1996     /**
1997      * persisted version of annotation row from which to take vis properties
1998      */
1999     public jalview.datamodel.AlignmentAnnotation template;
2000
2001     /**
2002      * original position of the annotation row in the alignment
2003      */
2004     public int order;
2005   }
2006
2007   /**
2008    * Load alignment frame from jalview XML DOM object
2009    * 
2010    * @param object
2011    *          DOM
2012    * @param file
2013    *          filename source string
2014    * @param loadTreesAndStructures
2015    *          when false only create Viewport
2016    * @param jprovider
2017    *          data source provider
2018    * @return alignment frame created from view stored in DOM
2019    */
2020   AlignFrame LoadFromObject(JalviewModel object, String file,
2021           boolean loadTreesAndStructures, jarInputStreamProvider jprovider)
2022   {
2023     SequenceSet vamsasSet = object.getVamsasModel().getSequenceSet(0);
2024     Sequence[] vamsasSeq = vamsasSet.getSequence();
2025
2026     JalviewModelSequence jms = object.getJalviewModelSequence();
2027
2028     Viewport view = jms.getViewport(0);
2029     // ////////////////////////////////
2030     // LOAD SEQUENCES
2031
2032     Vector hiddenSeqs = null;
2033     jalview.datamodel.Sequence jseq;
2034
2035     ArrayList tmpseqs = new ArrayList();
2036
2037     boolean multipleView = false;
2038
2039     JSeq[] JSEQ = object.getJalviewModelSequence().getJSeq();
2040     int vi = 0; // counter in vamsasSeq array
2041     for (int i = 0; i < JSEQ.length; i++)
2042     {
2043       String seqId = JSEQ[i].getId();
2044
2045       if (seqRefIds.get(seqId) != null)
2046       {
2047         tmpseqs.add(seqRefIds.get(seqId));
2048         multipleView = true;
2049       }
2050       else
2051       {
2052         jseq = new jalview.datamodel.Sequence(vamsasSeq[vi].getName(),
2053                 vamsasSeq[vi].getSequence());
2054         jseq.setDescription(vamsasSeq[vi].getDescription());
2055         jseq.setStart(JSEQ[i].getStart());
2056         jseq.setEnd(JSEQ[i].getEnd());
2057         jseq.setVamsasId(uniqueSetSuffix + seqId);
2058         seqRefIds.put(vamsasSeq[vi].getId(), jseq);
2059         tmpseqs.add(jseq);
2060         vi++;
2061       }
2062
2063       if (JSEQ[i].getHidden())
2064       {
2065         if (hiddenSeqs == null)
2066         {
2067           hiddenSeqs = new Vector();
2068         }
2069
2070         hiddenSeqs.addElement(seqRefIds.get(seqId));
2071       }
2072
2073     }
2074
2075     // /
2076     // Create the alignment object from the sequence set
2077     // ///////////////////////////////
2078     jalview.datamodel.Sequence[] orderedSeqs = new jalview.datamodel.Sequence[tmpseqs
2079             .size()];
2080
2081     tmpseqs.toArray(orderedSeqs);
2082
2083     jalview.datamodel.Alignment al = new jalview.datamodel.Alignment(
2084             orderedSeqs);
2085
2086     // / Add the alignment properties
2087     for (int i = 0; i < vamsasSet.getSequenceSetPropertiesCount(); i++)
2088     {
2089       SequenceSetProperties ssp = vamsasSet.getSequenceSetProperties(i);
2090       al.setProperty(ssp.getKey(), ssp.getValue());
2091     }
2092
2093     // /
2094     // SequenceFeatures are added to the DatasetSequence,
2095     // so we must create or recover the dataset before loading features
2096     // ///////////////////////////////
2097     if (vamsasSet.getDatasetId() == null || vamsasSet.getDatasetId() == "")
2098     {
2099       // older jalview projects do not have a dataset id.
2100       al.setDataset(null);
2101     }
2102     else
2103     {
2104       recoverDatasetFor(vamsasSet, al);
2105     }
2106     // ///////////////////////////////
2107
2108     Hashtable pdbloaded = new Hashtable();
2109     if (!multipleView)
2110     {
2111       // load sequence features, database references and any associated PDB
2112       // structures for the alignment
2113       for (int i = 0; i < vamsasSeq.length; i++)
2114       {
2115         if (JSEQ[i].getFeaturesCount() > 0)
2116         {
2117           Features[] features = JSEQ[i].getFeatures();
2118           for (int f = 0; f < features.length; f++)
2119           {
2120             jalview.datamodel.SequenceFeature sf = new jalview.datamodel.SequenceFeature(
2121                     features[f].getType(), features[f].getDescription(),
2122                     features[f].getStatus(), features[f].getBegin(),
2123                     features[f].getEnd(), features[f].getFeatureGroup());
2124
2125             sf.setScore(features[f].getScore());
2126             for (int od = 0; od < features[f].getOtherDataCount(); od++)
2127             {
2128               OtherData keyValue = features[f].getOtherData(od);
2129               if (keyValue.getKey().startsWith("LINK"))
2130               {
2131                 sf.addLink(keyValue.getValue());
2132               }
2133               else
2134               {
2135                 sf.setValue(keyValue.getKey(), keyValue.getValue());
2136               }
2137
2138             }
2139
2140             al.getSequenceAt(i).getDatasetSequence().addSequenceFeature(sf);
2141           }
2142         }
2143         if (vamsasSeq[i].getDBRefCount() > 0)
2144         {
2145           addDBRefs(al.getSequenceAt(i).getDatasetSequence(), vamsasSeq[i]);
2146         }
2147         if (JSEQ[i].getPdbidsCount() > 0)
2148         {
2149           Pdbids[] ids = JSEQ[i].getPdbids();
2150           for (int p = 0; p < ids.length; p++)
2151           {
2152             jalview.datamodel.PDBEntry entry = new jalview.datamodel.PDBEntry();
2153             entry.setId(ids[p].getId());
2154             entry.setType(ids[p].getType());
2155             if (ids[p].getFile() != null)
2156             {
2157               if (!pdbloaded.containsKey(ids[p].getFile()))
2158               {
2159                 entry.setFile(loadPDBFile(jprovider, ids[p].getId()));
2160               }
2161               else
2162               {
2163                 entry.setFile(pdbloaded.get(ids[p].getId()).toString());
2164               }
2165             }
2166
2167             al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
2168           }
2169         }
2170       }
2171     } // end !multipleview
2172
2173     // ///////////////////////////////
2174     // LOAD SEQUENCE MAPPINGS
2175
2176     if (vamsasSet.getAlcodonFrameCount() > 0)
2177     {
2178       // TODO Potentially this should only be done once for all views of an
2179       // alignment
2180       AlcodonFrame[] alc = vamsasSet.getAlcodonFrame();
2181       for (int i = 0; i < alc.length; i++)
2182       {
2183         jalview.datamodel.AlignedCodonFrame cf = new jalview.datamodel.AlignedCodonFrame(
2184                 alc[i].getAlcodonCount());
2185         if (alc[i].getAlcodonCount() > 0)
2186         {
2187           Alcodon[] alcods = alc[i].getAlcodon();
2188           for (int p = 0; p < cf.codons.length; p++)
2189           {
2190             if (alcods[p].hasPos1() && alcods[p].hasPos2()
2191                     && alcods[p].hasPos3())
2192             {
2193               // translated codons require three valid positions
2194               cf.codons[p] = new int[3];
2195               cf.codons[p][0] = (int) alcods[p].getPos1();
2196               cf.codons[p][1] = (int) alcods[p].getPos2();
2197               cf.codons[p][2] = (int) alcods[p].getPos3();
2198             }
2199             else
2200             {
2201               cf.codons[p] = null;
2202             }
2203           }
2204         }
2205         if (alc[i].getAlcodMapCount() > 0)
2206         {
2207           AlcodMap[] maps = alc[i].getAlcodMap();
2208           for (int m = 0; m < maps.length; m++)
2209           {
2210             SequenceI dnaseq = (SequenceI) seqRefIds
2211                     .get(maps[m].getDnasq());
2212             // Load Mapping
2213             jalview.datamodel.Mapping mapping = null;
2214             // attach to dna sequence reference.
2215             if (maps[m].getMapping() != null)
2216             {
2217               mapping = addMapping(maps[m].getMapping());
2218             }
2219             if (dnaseq != null)
2220             {
2221               cf.addMap(dnaseq, mapping.getTo(), mapping.getMap());
2222             }
2223             else
2224             {
2225               // defer to later
2226               frefedSequence.add(new Object[]
2227               { maps[m].getDnasq(), cf, mapping });
2228             }
2229           }
2230         }
2231         al.addCodonFrame(cf);
2232       }
2233
2234     }
2235
2236     // ////////////////////////////////
2237     // LOAD ANNOTATIONS
2238     ArrayList<JvAnnotRow> autoAlan = new ArrayList<JvAnnotRow>();
2239     /**
2240      * store any annotations which forward reference a group's ID
2241      */
2242     Hashtable<String, ArrayList<jalview.datamodel.AlignmentAnnotation>> groupAnnotRefs = new Hashtable<String, ArrayList<jalview.datamodel.AlignmentAnnotation>>();
2243
2244     if (vamsasSet.getAnnotationCount() > 0)
2245     {
2246       Annotation[] an = vamsasSet.getAnnotation();
2247
2248       for (int i = 0; i < an.length; i++)
2249       {
2250         /**
2251          * test if annotation is automatically calculated for this view only
2252          */
2253         boolean autoForView = false;
2254         if (an[i].getLabel().equals("Quality")
2255                 || an[i].getLabel().equals("Conservation")
2256                 || an[i].getLabel().equals("Consensus"))
2257         {
2258           // Kludge for pre 2.5 projects which lacked the autocalculated flag
2259           autoForView = true;
2260           if (!an[i].hasAutoCalculated())
2261           {
2262             an[i].setAutoCalculated(true);
2263           }
2264         }
2265         if (autoForView
2266                 || (an[i].hasAutoCalculated() && an[i].isAutoCalculated()))
2267         {
2268           // remove ID - we don't recover annotation from other views for
2269           // view-specific annotation
2270           an[i].setId(null);
2271         }
2272
2273         // set visiblity for other annotation in this view
2274         if (an[i].getId() != null
2275                 && annotationIds.containsKey(an[i].getId()))
2276         {
2277           jalview.datamodel.AlignmentAnnotation jda = (jalview.datamodel.AlignmentAnnotation) annotationIds
2278                   .get(an[i].getId());
2279           // in principle Visible should always be true for annotation displayed
2280           // in multiple views
2281           if (an[i].hasVisible())
2282             jda.visible = an[i].getVisible();
2283
2284           al.addAnnotation(jda);
2285
2286           continue;
2287         }
2288         // Construct new annotation from model.
2289         AnnotationElement[] ae = an[i].getAnnotationElement();
2290         jalview.datamodel.Annotation[] anot = null;
2291         java.awt.Color firstColour = null;
2292         int anpos;
2293         if (!an[i].getScoreOnly())
2294         {
2295           anot = new jalview.datamodel.Annotation[al.getWidth()];
2296           for (int aa = 0; aa < ae.length && aa < anot.length; aa++)
2297           {
2298             anpos = ae[aa].getPosition();
2299
2300             if (anpos >= anot.length)
2301               continue;
2302
2303             anot[anpos] = new jalview.datamodel.Annotation(
2304
2305             ae[aa].getDisplayCharacter(), ae[aa].getDescription(),
2306                     (ae[aa].getSecondaryStructure() == null || ae[aa]
2307                             .getSecondaryStructure().length() == 0) ? ' '
2308                             : ae[aa].getSecondaryStructure().charAt(0),
2309                     ae[aa].getValue()
2310
2311             );
2312             // JBPNote: Consider verifying dataflow for IO of secondary
2313             // structure annotation read from Stockholm files
2314             // this was added to try to ensure that
2315             // if (anot[ae[aa].getPosition()].secondaryStructure>' ')
2316             // {
2317             // anot[ae[aa].getPosition()].displayCharacter = "";
2318             // }
2319             anot[anpos].colour = new java.awt.Color(ae[aa].getColour());
2320             if (firstColour == null)
2321             {
2322               firstColour = anot[anpos].colour;
2323             }
2324           }
2325         }
2326         jalview.datamodel.AlignmentAnnotation jaa = null;
2327
2328         if (an[i].getGraph())
2329         {
2330           float llim = 0, hlim = 0;
2331           // if (autoForView || an[i].isAutoCalculated()) {
2332           // hlim=11f;
2333           // }
2334           jaa = new jalview.datamodel.AlignmentAnnotation(an[i].getLabel(),
2335                   an[i].getDescription(), anot, llim, hlim,
2336                   an[i].getGraphType());
2337
2338           jaa.graphGroup = an[i].getGraphGroup();
2339           jaa._linecolour = firstColour;
2340           if (an[i].getThresholdLine() != null)
2341           {
2342             jaa.setThreshold(new jalview.datamodel.GraphLine(an[i]
2343                     .getThresholdLine().getValue(), an[i]
2344                     .getThresholdLine().getLabel(), new java.awt.Color(
2345                     an[i].getThresholdLine().getColour())));
2346
2347           }
2348           if (autoForView || an[i].isAutoCalculated())
2349           {
2350             // Hardwire the symbol display line to ensure that labels for
2351             // histograms are displayed
2352             jaa.hasText = true;
2353           }
2354         }
2355         else
2356         {
2357           jaa = new jalview.datamodel.AlignmentAnnotation(an[i].getLabel(),
2358                   an[i].getDescription(), anot);
2359           jaa._linecolour = firstColour;
2360         }
2361         // register new annotation
2362         if (an[i].getId() != null)
2363         {
2364           annotationIds.put(an[i].getId(), jaa);
2365           jaa.annotationId = an[i].getId();
2366         }
2367         // recover sequence association
2368         if (an[i].getSequenceRef() != null)
2369         {
2370           if (al.findName(an[i].getSequenceRef()) != null)
2371           {
2372             jaa.createSequenceMapping(al.findName(an[i].getSequenceRef()),
2373                     1, true);
2374             al.findName(an[i].getSequenceRef()).addAlignmentAnnotation(jaa);
2375           }
2376         }
2377         // and make a note of any group association
2378         if (an[i].getGroupRef() != null && an[i].getGroupRef().length() > 0)
2379         {
2380           ArrayList<jalview.datamodel.AlignmentAnnotation> aal = groupAnnotRefs
2381                   .get(an[i].getGroupRef());
2382           if (aal == null)
2383           {
2384             aal = new ArrayList<jalview.datamodel.AlignmentAnnotation>();
2385             groupAnnotRefs.put(an[i].getGroupRef(), aal);
2386           }
2387           aal.add(jaa);
2388         }
2389
2390         if (an[i].hasScore())
2391         {
2392           jaa.setScore(an[i].getScore());
2393         }
2394         if (an[i].hasVisible())
2395           jaa.visible = an[i].getVisible();
2396
2397         if (an[i].hasCentreColLabels())
2398           jaa.centreColLabels = an[i].getCentreColLabels();
2399
2400         if (an[i].hasScaleColLabels())
2401         {
2402           jaa.scaleColLabel = an[i].getScaleColLabels();
2403         }
2404         if (an[i].hasAutoCalculated() && an[i].isAutoCalculated())
2405         {
2406           // newer files have an 'autoCalculated' flag and store calculation
2407           // state in viewport properties
2408           jaa.autoCalculated = true; // means annotation will be marked for
2409           // update at end of load.
2410         }
2411         if (an[i].hasGraphHeight())
2412         {
2413           jaa.graphHeight = an[i].getGraphHeight();
2414         }
2415         if (an[i].hasBelowAlignment())
2416         {
2417           jaa.belowAlignment = an[i].isBelowAlignment();
2418         }
2419         jaa.setCalcId(an[i].getCalcId());
2420
2421         if (jaa.autoCalculated)
2422         {
2423           autoAlan.add(new JvAnnotRow(i, jaa));
2424         }
2425         else
2426         // if (!autoForView)
2427         {
2428           // add autocalculated group annotation and any user created annotation
2429           // for the view
2430           al.addAnnotation(jaa);
2431         }
2432       }
2433     }
2434
2435     // ///////////////////////
2436     // LOAD GROUPS
2437     // Create alignment markup and styles for this view
2438     if (jms.getJGroupCount() > 0)
2439     {
2440       JGroup[] groups = jms.getJGroup();
2441
2442       for (int i = 0; i < groups.length; i++)
2443       {
2444         ColourSchemeI cs = null;
2445
2446         if (groups[i].getColour() != null)
2447         {
2448           if (groups[i].getColour().startsWith("ucs"))
2449           {
2450             cs = GetUserColourScheme(jms, groups[i].getColour());
2451           }
2452           else
2453           {
2454             cs = ColourSchemeProperty.getColour(al, groups[i].getColour());
2455           }
2456
2457           if (cs != null)
2458           {
2459             cs.setThreshold(groups[i].getPidThreshold(), true);
2460           }
2461         }
2462
2463         Vector seqs = new Vector();
2464
2465         for (int s = 0; s < groups[i].getSeqCount(); s++)
2466         {
2467           String seqId = groups[i].getSeq(s) + "";
2468           jalview.datamodel.SequenceI ts = (jalview.datamodel.SequenceI) seqRefIds
2469                   .get(seqId);
2470
2471           if (ts != null)
2472           {
2473             seqs.addElement(ts);
2474           }
2475         }
2476
2477         if (seqs.size() < 1)
2478         {
2479           continue;
2480         }
2481
2482         jalview.datamodel.SequenceGroup sg = new jalview.datamodel.SequenceGroup(
2483                 seqs, groups[i].getName(), cs, groups[i].getDisplayBoxes(),
2484                 groups[i].getDisplayText(), groups[i].getColourText(),
2485                 groups[i].getStart(), groups[i].getEnd());
2486
2487         sg.setOutlineColour(new java.awt.Color(groups[i].getOutlineColour()));
2488
2489         sg.textColour = new java.awt.Color(groups[i].getTextCol1());
2490         sg.textColour2 = new java.awt.Color(groups[i].getTextCol2());
2491         sg.setShowNonconserved(groups[i].hasShowUnconserved() ? groups[i]
2492                 .isShowUnconserved() : false);
2493         sg.thresholdTextColour = groups[i].getTextColThreshold();
2494         if (groups[i].hasShowConsensusHistogram())
2495         {
2496           sg.setShowConsensusHistogram(groups[i].isShowConsensusHistogram());
2497         }
2498         ;
2499         if (groups[i].hasShowSequenceLogo())
2500         {
2501           sg.setshowSequenceLogo(groups[i].isShowSequenceLogo());
2502         }
2503         if (groups[i].hasNormaliseSequenceLogo())
2504         {
2505           sg.setNormaliseSequenceLogo(groups[i].isNormaliseSequenceLogo());
2506         }
2507         if (groups[i].hasIgnoreGapsinConsensus())
2508         {
2509           sg.setIgnoreGapsConsensus(groups[i].getIgnoreGapsinConsensus());
2510         }
2511         if (groups[i].getConsThreshold() != 0)
2512         {
2513           jalview.analysis.Conservation c = new jalview.analysis.Conservation(
2514                   "All", ResidueProperties.propHash, 3,
2515                   sg.getSequences(null), 0, sg.getWidth() - 1);
2516           c.calculate();
2517           c.verdict(false, 25);
2518           sg.cs.setConservation(c);
2519         }
2520
2521         if (groups[i].getId() != null && groupAnnotRefs.size() > 0)
2522         {
2523           // re-instate unique group/annotation row reference
2524           ArrayList<jalview.datamodel.AlignmentAnnotation> jaal = groupAnnotRefs
2525                   .get(groups[i].getId());
2526           if (jaal != null)
2527           {
2528             for (jalview.datamodel.AlignmentAnnotation jaa : jaal)
2529             {
2530               jaa.groupRef = sg;
2531               if (jaa.autoCalculated)
2532               {
2533                 // match up and try to set group autocalc alignment row for this
2534                 // annotation
2535                 if (jaa.label.startsWith("Consensus for "))
2536                 {
2537                   sg.setConsensus(jaa);
2538                 }
2539                 // match up and try to set group autocalc alignment row for this
2540                 // annotation
2541                 if (jaa.label.startsWith("Conservation for "))
2542                 {
2543                   sg.setConservationRow(jaa);
2544                 }
2545               }
2546             }
2547           }
2548         }
2549         al.addGroup(sg);
2550
2551       }
2552     }
2553
2554     // ///////////////////////////////
2555     // LOAD VIEWPORT
2556
2557     // If we just load in the same jar file again, the sequenceSetId
2558     // will be the same, and we end up with multiple references
2559     // to the same sequenceSet. We must modify this id on load
2560     // so that each load of the file gives a unique id
2561     String uniqueSeqSetId = view.getSequenceSetId() + uniqueSetSuffix;
2562     String viewId = (view.getId() == null ? null : view.getId()
2563             + uniqueSetSuffix);
2564     AlignFrame af = null;
2565     AlignViewport av = null;
2566     // now check to see if we really need to create a new viewport.
2567     if (multipleView && viewportsAdded.size() == 0)
2568     {
2569       // We recovered an alignment for which a viewport already exists.
2570       // TODO: fix up any settings necessary for overlaying stored state onto
2571       // state recovered from another document. (may not be necessary).
2572       // we may need a binding from a viewport in memory to one recovered from
2573       // XML.
2574       // and then recover its containing af to allow the settings to be applied.
2575       // TODO: fix for vamsas demo
2576       System.err
2577               .println("About to recover a viewport for existing alignment: Sequence set ID is "
2578                       + uniqueSeqSetId);
2579       Object seqsetobj = retrieveExistingObj(uniqueSeqSetId);
2580       if (seqsetobj != null)
2581       {
2582         if (seqsetobj instanceof String)
2583         {
2584           uniqueSeqSetId = (String) seqsetobj;
2585           System.err
2586                   .println("Recovered extant sequence set ID mapping for ID : New Sequence set ID is "
2587                           + uniqueSeqSetId);
2588         }
2589         else
2590         {
2591           System.err
2592                   .println("Warning : Collision between sequence set ID string and existing jalview object mapping.");
2593         }
2594
2595       }
2596     }
2597     AlignmentPanel ap = null;
2598     boolean isnewview = true;
2599     if (viewId != null)
2600     {
2601       // Check to see if this alignment already has a view id == viewId
2602       jalview.gui.AlignmentPanel views[] = Desktop
2603               .getAlignmentPanels(uniqueSeqSetId);
2604       if (views != null && views.length > 0)
2605       {
2606         for (int v = 0; v < views.length; v++)
2607         {
2608           if (views[v].av.getViewId().equalsIgnoreCase(viewId))
2609           {
2610             // recover the existing alignpanel, alignframe, viewport
2611             af = views[v].alignFrame;
2612             av = views[v].av;
2613             ap = views[v];
2614             // TODO: could even skip resetting view settings if we don't want to
2615             // change the local settings from other jalview processes
2616             isnewview = false;
2617           }
2618         }
2619       }
2620     }
2621
2622     if (isnewview)
2623     {
2624       af = loadViewport(file, JSEQ, hiddenSeqs, al, jms, view,
2625               uniqueSeqSetId, viewId, autoAlan);
2626       av = af.viewport;
2627       ap = af.alignPanel;
2628     }
2629     // LOAD TREES
2630     // /////////////////////////////////////
2631     if (loadTreesAndStructures && jms.getTreeCount() > 0)
2632     {
2633       try
2634       {
2635         for (int t = 0; t < jms.getTreeCount(); t++)
2636         {
2637
2638           Tree tree = jms.getTree(t);
2639
2640           TreePanel tp = (TreePanel) retrieveExistingObj(tree.getId());
2641           if (tp == null)
2642           {
2643             tp = af.ShowNewickTree(
2644                     new jalview.io.NewickFile(tree.getNewick()),
2645                     tree.getTitle(), tree.getWidth(), tree.getHeight(),
2646                     tree.getXpos(), tree.getYpos());
2647             if (tree.getId() != null)
2648             {
2649               // perhaps bind the tree id to something ?
2650             }
2651           }
2652           else
2653           {
2654             // update local tree attributes ?
2655             // TODO: should check if tp has been manipulated by user - if so its
2656             // settings shouldn't be modified
2657             tp.setTitle(tree.getTitle());
2658             tp.setBounds(new Rectangle(tree.getXpos(), tree.getYpos(), tree
2659                     .getWidth(), tree.getHeight()));
2660             tp.av = av; // af.viewport; // TODO: verify 'associate with all
2661             // views'
2662             // works still
2663             tp.treeCanvas.av = av; // af.viewport;
2664             tp.treeCanvas.ap = ap; // af.alignPanel;
2665
2666           }
2667           if (tp == null)
2668           {
2669             warn("There was a problem recovering stored Newick tree: \n"
2670                     + tree.getNewick());
2671             continue;
2672           }
2673
2674           tp.fitToWindow.setState(tree.getFitToWindow());
2675           tp.fitToWindow_actionPerformed(null);
2676
2677           if (tree.getFontName() != null)
2678           {
2679             tp.setTreeFont(new java.awt.Font(tree.getFontName(), tree
2680                     .getFontStyle(), tree.getFontSize()));
2681           }
2682           else
2683           {
2684             tp.setTreeFont(new java.awt.Font(view.getFontName(), view
2685                     .getFontStyle(), tree.getFontSize()));
2686           }
2687
2688           tp.showPlaceholders(tree.getMarkUnlinked());
2689           tp.showBootstrap(tree.getShowBootstrap());
2690           tp.showDistances(tree.getShowDistances());
2691
2692           tp.treeCanvas.threshold = tree.getThreshold();
2693
2694           if (tree.getCurrentTree())
2695           {
2696             af.viewport.setCurrentTree(tp.getTree());
2697           }
2698         }
2699
2700       } catch (Exception ex)
2701       {
2702         ex.printStackTrace();
2703       }
2704     }
2705
2706     // //LOAD STRUCTURES
2707     if (loadTreesAndStructures)
2708     {
2709       // run through all PDB ids on the alignment, and collect mappings between
2710       // jmol view ids and all sequences referring to it
2711       Hashtable<String, Object[]> jmolViewIds = new Hashtable();
2712
2713       for (int i = 0; i < JSEQ.length; i++)
2714       {
2715         if (JSEQ[i].getPdbidsCount() > 0)
2716         {
2717           Pdbids[] ids = JSEQ[i].getPdbids();
2718           for (int p = 0; p < ids.length; p++)
2719           {
2720             for (int s = 0; s < ids[p].getStructureStateCount(); s++)
2721             {
2722               // check to see if we haven't already created this structure view
2723               String sviewid = (ids[p].getStructureState(s).getViewId() == null) ? null
2724                       : ids[p].getStructureState(s).getViewId()
2725                               + uniqueSetSuffix;
2726               jalview.datamodel.PDBEntry jpdb = new jalview.datamodel.PDBEntry();
2727               // Originally : ids[p].getFile()
2728               // : TODO: verify external PDB file recovery still works in normal
2729               // jalview project load
2730               jpdb.setFile(loadPDBFile(jprovider, ids[p].getId()));
2731               jpdb.setId(ids[p].getId());
2732
2733               int x = ids[p].getStructureState(s).getXpos();
2734               int y = ids[p].getStructureState(s).getYpos();
2735               int width = ids[p].getStructureState(s).getWidth();
2736               int height = ids[p].getStructureState(s).getHeight();
2737
2738               // Probably don't need to do this anymore...
2739               // Desktop.desktop.getComponentAt(x, y);
2740               // TODO: NOW: check that this recovers the PDB file correctly.
2741               String pdbFile = loadPDBFile(jprovider, ids[p].getId());
2742               jalview.datamodel.SequenceI seq = (jalview.datamodel.SequenceI) seqRefIds
2743                       .get(JSEQ[i].getId() + "");
2744               if (sviewid == null)
2745               {
2746                 sviewid = "_jalview_pre2_4_" + x + "," + y + "," + width
2747                         + "," + height;
2748               }
2749               if (!jmolViewIds.containsKey(sviewid))
2750               {
2751                 jmolViewIds.put(sviewid, new Object[]
2752                 { new int[]
2753                 { x, y, width, height }, "",
2754                     new Hashtable<String, Object[]>(), new boolean[]
2755                     { false, false, true } });
2756                 // Legacy pre-2.7 conversion JAL-823 :
2757                 // do not assume any view has to be linked for colour by
2758                 // sequence
2759               }
2760
2761               // assemble String[] { pdb files }, String[] { id for each
2762               // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
2763               // seqs_file 2}, boolean[] {
2764               // linkAlignPanel,superposeWithAlignpanel}} from hash
2765               Object[] jmoldat = jmolViewIds.get(sviewid);
2766               ((boolean[]) jmoldat[3])[0] |= ids[p].getStructureState(s)
2767                       .hasAlignwithAlignPanel() ? ids[p].getStructureState(
2768                       s).getAlignwithAlignPanel() : false;
2769               // never colour by linked panel if not specified
2770               ((boolean[]) jmoldat[3])[1] |= ids[p].getStructureState(s)
2771                       .hasColourwithAlignPanel() ? ids[p]
2772                       .getStructureState(s).getColourwithAlignPanel()
2773                       : false;
2774               // default for pre-2.7 projects is that Jmol colouring is enabled
2775               ((boolean[]) jmoldat[3])[2] &= ids[p].getStructureState(s)
2776                       .hasColourByJmol() ? ids[p].getStructureState(s)
2777                       .getColourByJmol() : true;
2778
2779               if (((String) jmoldat[1]).length() < ids[p]
2780                       .getStructureState(s).getContent().length())
2781               {
2782                 {
2783                   jmoldat[1] = ids[p].getStructureState(s).getContent();
2784                 }
2785               }
2786               if (ids[p].getFile() != null)
2787               {
2788                 File mapkey = new File(ids[p].getFile());
2789                 Object[] seqstrmaps = (Object[]) ((Hashtable) jmoldat[2])
2790                         .get(mapkey);
2791                 if (seqstrmaps == null)
2792                 {
2793                   ((Hashtable) jmoldat[2]).put(mapkey,
2794                           seqstrmaps = new Object[]
2795                           { pdbFile, ids[p].getId(), new Vector(),
2796                               new Vector() });
2797                 }
2798                 if (!((Vector) seqstrmaps[2]).contains(seq))
2799                 {
2800                   ((Vector) seqstrmaps[2]).addElement(seq);
2801                   // ((Vector)seqstrmaps[3]).addElement(n) :
2802                   // in principle, chains
2803                   // should be stored here : do we need to
2804                   // TODO: store and recover seq/pdb_id :
2805                   // chain mappings
2806                 }
2807               }
2808               else
2809               {
2810                 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");
2811                 warn(errorMessage);
2812               }
2813             }
2814           }
2815         }
2816       }
2817       {
2818
2819         // Instantiate the associated Jmol views
2820         for (Entry<String, Object[]> entry : jmolViewIds.entrySet())
2821         {
2822           String sviewid = entry.getKey();
2823           Object[] svattrib = entry.getValue();
2824           int[] geom = (int[]) svattrib[0];
2825           String state = (String) svattrib[1];
2826           Hashtable<File, Object[]> oldFiles = (Hashtable<File, Object[]>) svattrib[2];
2827           final boolean useinJmolsuperpos = ((boolean[]) svattrib[3])[0], usetoColourbyseq = ((boolean[]) svattrib[3])[1], jmolColouring = ((boolean[]) svattrib[3])[2];
2828           int x = geom[0], y = geom[1], width = geom[2], height = geom[3];
2829           // collate the pdbfile -> sequence mappings from this view
2830           Vector<String> pdbfilenames = new Vector<String>();
2831           Vector<SequenceI[]> seqmaps = new Vector<SequenceI[]>();
2832           Vector<String> pdbids = new Vector<String>();
2833
2834           // Search to see if we've already created this Jmol view
2835           AppJmol comp = null;
2836           JInternalFrame[] frames = null;
2837           do
2838           {
2839             try
2840             {
2841               frames = Desktop.desktop.getAllFrames();
2842             } catch (ArrayIndexOutOfBoundsException e)
2843             {
2844               // occasional No such child exceptions are thrown here...
2845               frames = null;
2846               try
2847               {
2848                 Thread.sleep(10);
2849               } catch (Exception f)
2850               {
2851               }
2852               ;
2853             }
2854           } while (frames == null);
2855           // search for any Jmol windows already open from other
2856           // alignment views that exactly match the stored structure state
2857           for (int f = 0; comp == null && f < frames.length; f++)
2858           {
2859             if (frames[f] instanceof AppJmol)
2860             {
2861               if (sviewid != null
2862                       && ((AppJmol) frames[f]).getViewId().equals(sviewid))
2863               {
2864                 // post jalview 2.4 schema includes structure view id
2865                 comp = (AppJmol) frames[f];
2866               }
2867               else if (frames[f].getX() == x && frames[f].getY() == y
2868                       && frames[f].getHeight() == height
2869                       && frames[f].getWidth() == width)
2870               {
2871                 comp = (AppJmol) frames[f];
2872               }
2873             }
2874           }
2875
2876           if (comp == null)
2877           {
2878             // create a new Jmol window.
2879             // First parse the Jmol state to translate filenames loaded into the
2880             // view, and record the order in which files are shown in the Jmol
2881             // view, so we can add the sequence mappings in same order.
2882             StringBuffer newFileLoc = null;
2883             int cp = 0, ncp, ecp;
2884             while ((ncp = state.indexOf("load ", cp)) > -1)
2885             {
2886               if (newFileLoc == null)
2887               {
2888                 newFileLoc = new StringBuffer();
2889               }
2890               do
2891               {
2892                 // look for next filename in load statement
2893                 newFileLoc.append(state.substring(cp,
2894                         ncp = (state.indexOf("\"", ncp + 1) + 1)));
2895                 String oldfilenam = state.substring(ncp,
2896                         ecp = state.indexOf("\"", ncp));
2897                 // recover the new mapping data for this old filename
2898                 // have to normalize filename - since Jmol and jalview do
2899                 // filename
2900                 // translation differently.
2901                 Object[] filedat = oldFiles.get(new File(oldfilenam));
2902                 newFileLoc.append(Platform
2903                         .escapeString((String) filedat[0]));
2904                 pdbfilenames.addElement((String) filedat[0]);
2905                 pdbids.addElement((String) filedat[1]);
2906                 seqmaps.addElement(((Vector<SequenceI>) filedat[2])
2907                         .toArray(new SequenceI[0]));
2908                 newFileLoc.append("\"");
2909                 cp = ecp + 1; // advance beyond last \" and set cursor so we can
2910                               // look for next file statement.
2911               } while ((ncp = state.indexOf("/*file*/", cp)) > -1);
2912             }
2913             if (cp > 0)
2914             {
2915               // just append rest of state
2916               newFileLoc.append(state.substring(cp));
2917             }
2918             else
2919             {
2920               System.err
2921                       .print("Ignoring incomplete Jmol state for PDB ids: ");
2922               newFileLoc = new StringBuffer(state);
2923               newFileLoc.append("; load append ");
2924               for (File id : oldFiles.keySet())
2925               {
2926                 // add this and any other pdb files that should be present in
2927                 // the viewer
2928                 Object[] filedat = oldFiles.get(id);
2929                 String nfilename;
2930                 newFileLoc.append(((String) filedat[0]));
2931                 pdbfilenames.addElement((String) filedat[0]);
2932                 pdbids.addElement((String) filedat[1]);
2933                 seqmaps.addElement(((Vector<SequenceI>) filedat[2])
2934                         .toArray(new SequenceI[0]));
2935                 newFileLoc.append(" \"");
2936                 newFileLoc.append((String) filedat[0]);
2937                 newFileLoc.append("\"");
2938
2939               }
2940               newFileLoc.append(";");
2941             }
2942
2943             if (newFileLoc != null)
2944             {
2945               int histbug = newFileLoc.indexOf("history = ");
2946               histbug += 10;
2947               int diff = histbug == -1 ? -1 : newFileLoc.indexOf(";",
2948                       histbug);
2949               String val = (diff == -1) ? null : newFileLoc.substring(
2950                       histbug, diff);
2951               if (val != null && val.length() >= 4)
2952               {
2953                 if (val.contains("e"))
2954                 {
2955                   if (val.trim().equals("true"))
2956                   {
2957                     val = "1";
2958                   }
2959                   else
2960                   {
2961                     val = "0";
2962                   }
2963                   newFileLoc.replace(histbug, diff, val);
2964                 }
2965               }
2966               // TODO: assemble String[] { pdb files }, String[] { id for each
2967               // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
2968               // seqs_file 2}} from hash
2969               final String[] pdbf = pdbfilenames
2970                       .toArray(new String[pdbfilenames.size()]), id = pdbids
2971                       .toArray(new String[pdbids.size()]);
2972               final SequenceI[][] sq = seqmaps
2973                       .toArray(new SequenceI[seqmaps.size()][]);
2974               final String fileloc = newFileLoc.toString(), vid = sviewid;
2975               final AlignFrame alf = af;
2976               final java.awt.Rectangle rect = new java.awt.Rectangle(x, y,
2977                       width, height);
2978               try
2979               {
2980                 javax.swing.SwingUtilities.invokeAndWait(new Runnable()
2981                 {
2982                   @Override
2983                   public void run()
2984                   {
2985                     AppJmol sview = null;
2986                     try
2987                     {
2988                       sview = new AppJmol(pdbf, id, sq, alf.alignPanel,
2989                               useinJmolsuperpos, usetoColourbyseq,
2990                               jmolColouring, fileloc, rect, vid);
2991                       addNewStructureViewer(sview);
2992                     } catch (OutOfMemoryError ex)
2993                     {
2994                       new OOMWarning("restoring structure view for PDB id "
2995                               + id, (OutOfMemoryError) ex.getCause());
2996                       if (sview != null && sview.isVisible())
2997                       {
2998                         sview.closeViewer();
2999                         sview.setVisible(false);
3000                         sview.dispose();
3001                       }
3002                     }
3003                   }
3004                 });
3005               } catch (InvocationTargetException ex)
3006               {
3007                 warn("Unexpected error when opening Jmol view.", ex);
3008
3009               } catch (InterruptedException e)
3010               {
3011                 // e.printStackTrace();
3012               }
3013             }
3014
3015           }
3016           else
3017           // if (comp != null)
3018           {
3019             // NOTE: if the jalview project is part of a shared session then
3020             // view synchronization should/could be done here.
3021
3022             // add mapping for sequences in this view to an already open Jmol
3023             // instance
3024             for (File id : oldFiles.keySet())
3025             {
3026               // add this and any other pdb files that should be present in the
3027               // viewer
3028               Object[] filedat = oldFiles.get(id);
3029               String pdbFile = (String) filedat[0];
3030               SequenceI[] seq = ((Vector<SequenceI>) filedat[2])
3031                       .toArray(new SequenceI[0]);
3032               comp.jmb.ssm.setMapping(seq, null, pdbFile,
3033                       jalview.io.AppletFormatAdapter.FILE);
3034               comp.jmb.addSequenceForStructFile(pdbFile, seq);
3035             }
3036             // and add the AlignmentPanel's reference to the Jmol view
3037             comp.addAlignmentPanel(ap);
3038             if (useinJmolsuperpos)
3039             {
3040               comp.useAlignmentPanelForSuperposition(ap);
3041             }
3042             else
3043             {
3044               comp.excludeAlignmentPanelForSuperposition(ap);
3045             }
3046             if (usetoColourbyseq)
3047             {
3048               comp.useAlignmentPanelForColourbyseq(ap, !jmolColouring);
3049             }
3050             else
3051             {
3052               comp.excludeAlignmentPanelForColourbyseq(ap);
3053             }
3054           }
3055         }
3056       }
3057     }
3058     // and finally return.
3059     return af;
3060   }
3061   Vector<AppJmol> newStructureViewers=null;
3062   protected void addNewStructureViewer(AppJmol sview)
3063   {
3064     if (newStructureViewers!=null)
3065     {
3066       sview.jmb.setFinishedLoadingFromArchive(false);
3067       newStructureViewers.add(sview);
3068     }
3069   }
3070   protected void setLoadingFinishedForNewStructureViewers()
3071   {
3072     if (newStructureViewers!=null)
3073     {
3074       for (AppJmol sview:newStructureViewers)
3075       {
3076         sview.jmb.setFinishedLoadingFromArchive(true);
3077       }
3078       newStructureViewers.clear();
3079       newStructureViewers=null;
3080     }
3081   }
3082
3083   AlignFrame loadViewport(String file, JSeq[] JSEQ, Vector hiddenSeqs,
3084           Alignment al, JalviewModelSequence jms, Viewport view,
3085           String uniqueSeqSetId, String viewId,
3086           ArrayList<JvAnnotRow> autoAlan)
3087   {
3088     AlignFrame af = null;
3089     af = new AlignFrame(al, view.getWidth(), view.getHeight(),
3090             uniqueSeqSetId, viewId);
3091
3092     af.setFileName(file, "Jalview");
3093
3094     for (int i = 0; i < JSEQ.length; i++)
3095     {
3096       af.viewport.setSequenceColour(af.viewport.getAlignment()
3097               .getSequenceAt(i), new java.awt.Color(JSEQ[i].getColour()));
3098     }
3099
3100     af.viewport.gatherViewsHere = view.getGatheredViews();
3101
3102     if (view.getSequenceSetId() != null)
3103     {
3104       jalview.gui.AlignViewport av = (jalview.gui.AlignViewport) viewportsAdded
3105               .get(uniqueSeqSetId);
3106
3107       af.viewport.setSequenceSetId(uniqueSeqSetId);
3108       if (av != null)
3109       {
3110         // propagate shared settings to this new view
3111         af.viewport.historyList = av.historyList;
3112         af.viewport.redoList = av.redoList;
3113       }
3114       else
3115       {
3116         viewportsAdded.put(uniqueSeqSetId, af.viewport);
3117       }
3118       // TODO: check if this method can be called repeatedly without
3119       // side-effects if alignpanel already registered.
3120       PaintRefresher.Register(af.alignPanel, uniqueSeqSetId);
3121     }
3122     // apply Hidden regions to view.
3123     if (hiddenSeqs != null)
3124     {
3125       for (int s = 0; s < JSEQ.length; s++)
3126       {
3127         jalview.datamodel.SequenceGroup hidden = new jalview.datamodel.SequenceGroup();
3128
3129         for (int r = 0; r < JSEQ[s].getHiddenSequencesCount(); r++)
3130         {
3131           hidden.addSequence(
3132                   al.getSequenceAt(JSEQ[s].getHiddenSequences(r)), false);
3133         }
3134         af.viewport.hideRepSequences(al.getSequenceAt(s), hidden);
3135       }
3136
3137       jalview.datamodel.SequenceI[] hseqs = new jalview.datamodel.SequenceI[hiddenSeqs
3138               .size()];
3139
3140       for (int s = 0; s < hiddenSeqs.size(); s++)
3141       {
3142         hseqs[s] = (jalview.datamodel.SequenceI) hiddenSeqs.elementAt(s);
3143       }
3144
3145       af.viewport.hideSequence(hseqs);
3146
3147     }
3148     // recover view properties and display parameters
3149     if (view.getViewName() != null)
3150     {
3151       af.viewport.viewName = view.getViewName();
3152       af.setInitialTabVisible();
3153     }
3154     af.setBounds(view.getXpos(), view.getYpos(), view.getWidth(),
3155             view.getHeight());
3156
3157     af.viewport.setShowAnnotation(view.getShowAnnotation());
3158     af.viewport.setAbovePIDThreshold(view.getPidSelected());
3159
3160     af.viewport.setColourText(view.getShowColourText());
3161
3162     af.viewport.setConservationSelected(view.getConservationSelected());
3163     af.viewport.setShowJVSuffix(view.getShowFullId());
3164     af.viewport.rightAlignIds = view.getRightAlignIds();
3165     af.viewport.setFont(new java.awt.Font(view.getFontName(), view
3166             .getFontStyle(), view.getFontSize()));
3167     af.alignPanel.fontChanged();
3168     af.viewport.setRenderGaps(view.getRenderGaps());
3169     af.viewport.setWrapAlignment(view.getWrapAlignment());
3170     af.alignPanel.setWrapAlignment(view.getWrapAlignment());
3171     af.viewport.setShowAnnotation(view.getShowAnnotation());
3172     af.alignPanel.setAnnotationVisible(view.getShowAnnotation());
3173
3174     af.viewport.setShowBoxes(view.getShowBoxes());
3175
3176     af.viewport.setShowText(view.getShowText());
3177
3178     af.viewport.textColour = new java.awt.Color(view.getTextCol1());
3179     af.viewport.textColour2 = new java.awt.Color(view.getTextCol2());
3180     af.viewport.thresholdTextColour = view.getTextColThreshold();
3181     af.viewport.setShowUnconserved(view.hasShowUnconserved() ? view
3182             .isShowUnconserved() : false);
3183     af.viewport.setStartRes(view.getStartRes());
3184     af.viewport.setStartSeq(view.getStartSeq());
3185
3186     ColourSchemeI cs = null;
3187     // apply colourschemes
3188     if (view.getBgColour() != null)
3189     {
3190       if (view.getBgColour().startsWith("ucs"))
3191       {
3192         cs = GetUserColourScheme(jms, view.getBgColour());
3193       }
3194       else if (view.getBgColour().startsWith("Annotation"))
3195       {
3196         // int find annotation
3197         if (af.viewport.getAlignment().getAlignmentAnnotation() != null)
3198         {
3199           for (int i = 0; i < af.viewport.getAlignment()
3200                   .getAlignmentAnnotation().length; i++)
3201           {
3202             if (af.viewport.getAlignment().getAlignmentAnnotation()[i].label
3203                     .equals(view.getAnnotationColours().getAnnotation()))
3204             {
3205               if (af.viewport.getAlignment().getAlignmentAnnotation()[i]
3206                       .getThreshold() == null)
3207               {
3208                 af.viewport.getAlignment().getAlignmentAnnotation()[i]
3209                         .setThreshold(new jalview.datamodel.GraphLine(view
3210                                 .getAnnotationColours().getThreshold(),
3211                                 "Threshold", java.awt.Color.black)
3212
3213                         );
3214               }
3215
3216               if (view.getAnnotationColours().getColourScheme()
3217                       .equals("None"))
3218               {
3219                 cs = new AnnotationColourGradient(af.viewport
3220                         .getAlignment().getAlignmentAnnotation()[i],
3221                         new java.awt.Color(view.getAnnotationColours()
3222                                 .getMinColour()), new java.awt.Color(view
3223                                 .getAnnotationColours().getMaxColour()),
3224                         view.getAnnotationColours().getAboveThreshold());
3225               }
3226               else if (view.getAnnotationColours().getColourScheme()
3227                       .startsWith("ucs"))
3228               {
3229                 cs = new AnnotationColourGradient(af.viewport
3230                         .getAlignment().getAlignmentAnnotation()[i],
3231                         GetUserColourScheme(jms, view
3232                                 .getAnnotationColours().getColourScheme()),
3233                         view.getAnnotationColours().getAboveThreshold());
3234               }
3235               else
3236               {
3237                 cs = new AnnotationColourGradient(af.viewport
3238                         .getAlignment().getAlignmentAnnotation()[i],
3239                         ColourSchemeProperty.getColour(al, view
3240                                 .getAnnotationColours().getColourScheme()),
3241                         view.getAnnotationColours().getAboveThreshold());
3242               }
3243               if (view.getAnnotationColours().hasPerSequence())
3244               {
3245                 ((AnnotationColourGradient)cs).setSeqAssociated(view.getAnnotationColours().isPerSequence());
3246               }
3247               if (view.getAnnotationColours().hasPredefinedColours())
3248               {
3249                 ((AnnotationColourGradient)cs).setPredefinedColours(view.getAnnotationColours().isPredefinedColours());
3250               }
3251               // Also use these settings for all the groups
3252               if (al.getGroups() != null)
3253               {
3254                 for (int g = 0; g < al.getGroups().size(); g++)
3255                 {
3256                   jalview.datamodel.SequenceGroup sg = al.getGroups()
3257                           .get(g);
3258
3259                   if (sg.cs == null)
3260                   {
3261                     continue;
3262                   }
3263
3264                   /*
3265                    * if
3266                    * (view.getAnnotationColours().getColourScheme().equals("None"
3267                    * )) { sg.cs = new AnnotationColourGradient(
3268                    * af.viewport.getAlignment().getAlignmentAnnotation()[i], new
3269                    * java.awt.Color(view.getAnnotationColours().
3270                    * getMinColour()), new
3271                    * java.awt.Color(view.getAnnotationColours().
3272                    * getMaxColour()),
3273                    * view.getAnnotationColours().getAboveThreshold()); } else
3274                    */
3275                   {
3276                     sg.cs = new AnnotationColourGradient(af.viewport
3277                             .getAlignment().getAlignmentAnnotation()[i],
3278                             sg.cs, view.getAnnotationColours()
3279                                     .getAboveThreshold());
3280                     if (cs instanceof AnnotationColourGradient) 
3281                     {
3282                       if (view.getAnnotationColours().hasPerSequence())
3283                       { 
3284                         ((AnnotationColourGradient)cs).setSeqAssociated(view.getAnnotationColours().isPerSequence());
3285                       }
3286                       if (view.getAnnotationColours().hasPredefinedColours())
3287                       {
3288                         ((AnnotationColourGradient)cs).setPredefinedColours(view.getAnnotationColours().isPredefinedColours());
3289                       }
3290                     }
3291                   }
3292
3293                 }
3294               }
3295
3296               break;
3297             }
3298
3299           }
3300         }
3301       }
3302       else
3303       {
3304         cs = ColourSchemeProperty.getColour(al, view.getBgColour());
3305       }
3306
3307       if (cs != null)
3308       {
3309         cs.setThreshold(view.getPidThreshold(), true);
3310         cs.setConsensus(af.viewport.getSequenceConsensusHash());
3311       }
3312     }
3313
3314     af.viewport.setGlobalColourScheme(cs);
3315     af.viewport.setColourAppliesToAllGroups(false);
3316
3317     if (view.getConservationSelected() && cs != null)
3318     {
3319       cs.setConservationInc(view.getConsThreshold());
3320     }
3321
3322     af.changeColour(cs);
3323
3324     af.viewport.setColourAppliesToAllGroups(true);
3325
3326     if (view.getShowSequenceFeatures())
3327     {
3328       af.viewport.showSequenceFeatures = true;
3329     }
3330     if (view.hasCentreColumnLabels())
3331     {
3332       af.viewport.setCentreColumnLabels(view.getCentreColumnLabels());
3333     }
3334     if (view.hasIgnoreGapsinConsensus())
3335     {
3336       af.viewport.setIgnoreGapsConsensus(view.getIgnoreGapsinConsensus(),
3337               null);
3338     }
3339     if (view.hasFollowHighlight())
3340     {
3341       af.viewport.followHighlight = view.getFollowHighlight();
3342     }
3343     if (view.hasFollowSelection())
3344     {
3345       af.viewport.followSelection = view.getFollowSelection();
3346     }
3347     if (view.hasShowConsensusHistogram())
3348     {
3349       af.viewport.setShowConsensusHistogram(view
3350               .getShowConsensusHistogram());
3351     }
3352     else
3353     {
3354       af.viewport.setShowConsensusHistogram(true);
3355     }
3356     if (view.hasShowSequenceLogo())
3357     {
3358       af.viewport.setShowSequenceLogo(view.getShowSequenceLogo());
3359     }
3360     else
3361     {
3362       af.viewport.setShowSequenceLogo(false);
3363     }
3364     if (view.hasNormaliseSequenceLogo())
3365     {
3366       af.viewport.setNormaliseSequenceLogo(view.getNormaliseSequenceLogo());
3367     }
3368     if (view.hasShowDbRefTooltip())
3369     {
3370       af.viewport.setShowDbRefs(view.getShowDbRefTooltip());
3371     }
3372     if (view.hasShowNPfeatureTooltip())
3373     {
3374       af.viewport.setShowNpFeats(view.hasShowNPfeatureTooltip());
3375     }
3376     if (view.hasShowGroupConsensus())
3377     {
3378       af.viewport.setShowGroupConsensus(view.getShowGroupConsensus());
3379     }
3380     else
3381     {
3382       af.viewport.setShowGroupConsensus(false);
3383     }
3384     if (view.hasShowGroupConservation())
3385     {
3386       af.viewport.setShowGroupConservation(view.getShowGroupConservation());
3387     }
3388     else
3389     {
3390       af.viewport.setShowGroupConservation(false);
3391     }
3392
3393     // recover featre settings
3394     if (jms.getFeatureSettings() != null)
3395     {
3396       af.viewport.featuresDisplayed = new Hashtable();
3397       String[] renderOrder = new String[jms.getFeatureSettings()
3398               .getSettingCount()];
3399       for (int fs = 0; fs < jms.getFeatureSettings().getSettingCount(); fs++)
3400       {
3401         Setting setting = jms.getFeatureSettings().getSetting(fs);
3402         if (setting.hasMincolour())
3403         {
3404           GraduatedColor gc = setting.hasMin() ? new GraduatedColor(
3405                   new java.awt.Color(setting.getMincolour()),
3406                   new java.awt.Color(setting.getColour()),
3407                   setting.getMin(), setting.getMax()) : new GraduatedColor(
3408                   new java.awt.Color(setting.getMincolour()),
3409                   new java.awt.Color(setting.getColour()), 0, 1);
3410           if (setting.hasThreshold())
3411           {
3412             gc.setThresh(setting.getThreshold());
3413             gc.setThreshType(setting.getThreshstate());
3414           }
3415           gc.setAutoScaled(true); // default
3416           if (setting.hasAutoScale())
3417           {
3418             gc.setAutoScaled(setting.getAutoScale());
3419           }
3420           if (setting.hasColourByLabel())
3421           {
3422             gc.setColourByLabel(setting.getColourByLabel());
3423           }
3424           // and put in the feature colour table.
3425           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setColour(
3426                   setting.getType(), gc);
3427         }
3428         else
3429         {
3430           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setColour(
3431                   setting.getType(),
3432                   new java.awt.Color(setting.getColour()));
3433         }
3434         renderOrder[fs] = setting.getType();
3435         if (setting.hasOrder())
3436           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setOrder(
3437                   setting.getType(), setting.getOrder());
3438         else
3439           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setOrder(
3440                   setting.getType(),
3441                   fs / jms.getFeatureSettings().getSettingCount());
3442         if (setting.getDisplay())
3443         {
3444           af.viewport.featuresDisplayed.put(setting.getType(), new Integer(
3445                   setting.getColour()));
3446         }
3447       }
3448       af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().renderOrder = renderOrder;
3449       Hashtable fgtable;
3450       af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().featureGroups = fgtable = new Hashtable();
3451       for (int gs = 0; gs < jms.getFeatureSettings().getGroupCount(); gs++)
3452       {
3453         Group grp = jms.getFeatureSettings().getGroup(gs);
3454         fgtable.put(grp.getName(), new Boolean(grp.getDisplay()));
3455       }
3456     }
3457
3458     if (view.getHiddenColumnsCount() > 0)
3459     {
3460       for (int c = 0; c < view.getHiddenColumnsCount(); c++)
3461       {
3462         af.viewport.hideColumns(view.getHiddenColumns(c).getStart(), view
3463                 .getHiddenColumns(c).getEnd() // +1
3464                 );
3465       }
3466     }
3467     if (view.getCalcIdParam() != null)
3468     {
3469       for (CalcIdParam calcIdParam : view.getCalcIdParam())
3470       {
3471         if (calcIdParam != null)
3472         {
3473           if (recoverCalcIdParam(calcIdParam, af.viewport))
3474           {
3475           }
3476           else
3477           {
3478             warn("Couldn't recover parameters for "
3479                     + calcIdParam.getCalcId());
3480           }
3481         }
3482       }
3483     }
3484     af.setMenusFromViewport(af.viewport);
3485     // TODO: we don't need to do this if the viewport is aready visible.
3486     Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(),
3487             view.getHeight());
3488     af.alignPanel.updateAnnotation(false, true); // recompute any autoannotation
3489     reorderAutoannotation(af, al, autoAlan);
3490     return af;
3491   }
3492
3493   private void reorderAutoannotation(AlignFrame af, Alignment al,
3494           ArrayList<JvAnnotRow> autoAlan)
3495   {
3496     // copy over visualization settings for autocalculated annotation in the
3497     // view
3498     if (al.getAlignmentAnnotation() != null)
3499     {
3500       /**
3501        * Kludge for magic autoannotation names (see JAL-811)
3502        */
3503       String[] magicNames = new String[]
3504       { "Consensus", "Quality", "Conservation" };
3505       JvAnnotRow nullAnnot = new JvAnnotRow(-1, null);
3506       Hashtable<String, JvAnnotRow> visan = new Hashtable<String, JvAnnotRow>();
3507       for (String nm : magicNames)
3508       {
3509         visan.put(nm, nullAnnot);
3510       }
3511       for (JvAnnotRow auan : autoAlan)
3512       {
3513         visan.put(auan.template.label
3514                 + (auan.template.getCalcId() == null ? "" : "\t"
3515                         + auan.template.getCalcId()), auan);
3516       }
3517       int hSize = al.getAlignmentAnnotation().length;
3518       ArrayList<JvAnnotRow> reorder = new ArrayList<JvAnnotRow>();
3519       // work through any autoCalculated annotation already on the view
3520       // removing it if it should be placed in a different location on the
3521       // annotation panel.
3522       List<String> remains = new ArrayList(visan.keySet());
3523       for (int h = 0; h < hSize; h++)
3524       {
3525         jalview.datamodel.AlignmentAnnotation jalan = al
3526                 .getAlignmentAnnotation()[h];
3527         if (jalan.autoCalculated)
3528         {
3529           String k;
3530           JvAnnotRow valan = visan.get(k = jalan.label);
3531           if (jalan.getCalcId() != null)
3532           {
3533             valan = visan.get(k = jalan.label + "\t" + jalan.getCalcId());
3534           }
3535
3536           if (valan != null)
3537           {
3538             // delete the auto calculated row from the alignment
3539             al.deleteAnnotation(jalan, false);
3540             remains.remove(k);
3541             hSize--;
3542             h--;
3543             if (valan != nullAnnot)
3544             {
3545               if (jalan != valan.template)
3546               {
3547                 // newly created autoannotation row instance
3548                 // so keep a reference to the visible annotation row
3549                 // and copy over all relevant attributes
3550                 if (valan.template.graphHeight >= 0)
3551
3552                 {
3553                   jalan.graphHeight = valan.template.graphHeight;
3554                 }
3555                 jalan.visible = valan.template.visible;
3556               }
3557               reorder.add(new JvAnnotRow(valan.order, jalan));
3558             }
3559           }
3560         }
3561       }
3562       // Add any (possibly stale) autocalculated rows that were not appended to
3563       // the view during construction
3564       for (String other : remains)
3565       {
3566         JvAnnotRow othera = visan.get(other);
3567         if (othera != nullAnnot && othera.template.getCalcId() != null
3568                 && othera.template.getCalcId().length() > 0)
3569         {
3570           reorder.add(othera);
3571         }
3572       }
3573       // now put the automatic annotation in its correct place
3574       int s = 0, srt[] = new int[reorder.size()];
3575       JvAnnotRow[] rws = new JvAnnotRow[reorder.size()];
3576       for (JvAnnotRow jvar : reorder)
3577       {
3578         rws[s] = jvar;
3579         srt[s++] = jvar.order;
3580       }
3581       reorder.clear();
3582       jalview.util.QuickSort.sort(srt, rws);
3583       // and re-insert the annotation at its correct position
3584       for (JvAnnotRow jvar : rws)
3585       {
3586         al.addAnnotation(jvar.template, jvar.order);
3587       }
3588       af.alignPanel.adjustAnnotationHeight();
3589     }
3590   }
3591
3592   Hashtable skipList = null;
3593
3594   /**
3595    * TODO remove this method
3596    * 
3597    * @param view
3598    * @return AlignFrame bound to sequenceSetId from view, if one exists. private
3599    *         AlignFrame getSkippedFrame(Viewport view) { if (skipList==null) {
3600    *         throw new Error("Implementation Error. No skipList defined for this
3601    *         Jalview2XML instance."); } return (AlignFrame)
3602    *         skipList.get(view.getSequenceSetId()); }
3603    */
3604
3605   /**
3606    * Check if the Jalview view contained in object should be skipped or not.
3607    * 
3608    * @param object
3609    * @return true if view's sequenceSetId is a key in skipList
3610    */
3611   private boolean skipViewport(JalviewModel object)
3612   {
3613     if (skipList == null)
3614     {
3615       return false;
3616     }
3617     String id;
3618     if (skipList.containsKey(id = object.getJalviewModelSequence()
3619             .getViewport()[0].getSequenceSetId()))
3620     {
3621       if (Cache.log != null && Cache.log.isDebugEnabled())
3622       {
3623         Cache.log.debug("Skipping seuqence set id " + id);
3624       }
3625       return true;
3626     }
3627     return false;
3628   }
3629
3630   public void AddToSkipList(AlignFrame af)
3631   {
3632     if (skipList == null)
3633     {
3634       skipList = new Hashtable();
3635     }
3636     skipList.put(af.getViewport().getSequenceSetId(), af);
3637   }
3638
3639   public void clearSkipList()
3640   {
3641     if (skipList != null)
3642     {
3643       skipList.clear();
3644       skipList = null;
3645     }
3646   }
3647
3648   private void recoverDatasetFor(SequenceSet vamsasSet, Alignment al)
3649   {
3650     jalview.datamodel.Alignment ds = getDatasetFor(vamsasSet.getDatasetId());
3651     Vector dseqs = null;
3652     if (ds == null)
3653     {
3654       // create a list of new dataset sequences
3655       dseqs = new Vector();
3656     }
3657     for (int i = 0, iSize = vamsasSet.getSequenceCount(); i < iSize; i++)
3658     {
3659       Sequence vamsasSeq = vamsasSet.getSequence(i);
3660       ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs);
3661     }
3662     // create a new dataset
3663     if (ds == null)
3664     {
3665       SequenceI[] dsseqs = new SequenceI[dseqs.size()];
3666       dseqs.copyInto(dsseqs);
3667       ds = new jalview.datamodel.Alignment(dsseqs);
3668       debug("Created new dataset " + vamsasSet.getDatasetId()
3669               + " for alignment " + System.identityHashCode(al));
3670       addDatasetRef(vamsasSet.getDatasetId(), ds);
3671     }
3672     // set the dataset for the newly imported alignment.
3673     if (al.getDataset() == null)
3674     {
3675       al.setDataset(ds);
3676     }
3677   }
3678
3679   /**
3680    * 
3681    * @param vamsasSeq
3682    *          sequence definition to create/merge dataset sequence for
3683    * @param ds
3684    *          dataset alignment
3685    * @param dseqs
3686    *          vector to add new dataset sequence to
3687    */
3688   private void ensureJalviewDatasetSequence(Sequence vamsasSeq,
3689           AlignmentI ds, Vector dseqs)
3690   {
3691     // JBP TODO: Check this is called for AlCodonFrames to support recovery of
3692     // xRef Codon Maps
3693     jalview.datamodel.Sequence sq = (jalview.datamodel.Sequence) seqRefIds
3694             .get(vamsasSeq.getId());
3695     jalview.datamodel.SequenceI dsq = null;
3696     if (sq != null && sq.getDatasetSequence() != null)
3697     {
3698       dsq = sq.getDatasetSequence();
3699     }
3700
3701     String sqid = vamsasSeq.getDsseqid();
3702     if (dsq == null)
3703     {
3704       // need to create or add a new dataset sequence reference to this sequence
3705       if (sqid != null)
3706       {
3707         dsq = (jalview.datamodel.SequenceI) seqRefIds.get(sqid);
3708       }
3709       // check again
3710       if (dsq == null)
3711       {
3712         // make a new dataset sequence
3713         dsq = sq.createDatasetSequence();
3714         if (sqid == null)
3715         {
3716           // make up a new dataset reference for this sequence
3717           sqid = seqHash(dsq);
3718         }
3719         dsq.setVamsasId(uniqueSetSuffix + sqid);
3720         seqRefIds.put(sqid, dsq);
3721         if (ds == null)
3722         {
3723           if (dseqs != null)
3724           {
3725             dseqs.addElement(dsq);
3726           }
3727         }
3728         else
3729         {
3730           ds.addSequence(dsq);
3731         }
3732       }
3733       else
3734       {
3735         if (sq != dsq)
3736         { // make this dataset sequence sq's dataset sequence
3737           sq.setDatasetSequence(dsq);
3738         }
3739       }
3740     }
3741     // TODO: refactor this as a merge dataset sequence function
3742     // now check that sq (the dataset sequence) sequence really is the union of
3743     // all references to it
3744     // boolean pre = sq.getStart() < dsq.getStart();
3745     // boolean post = sq.getEnd() > dsq.getEnd();
3746     // if (pre || post)
3747     if (sq != dsq)
3748     {
3749       StringBuffer sb = new StringBuffer();
3750       String newres = jalview.analysis.AlignSeq.extractGaps(
3751               jalview.util.Comparison.GapChars, sq.getSequenceAsString());
3752       if (!newres.equalsIgnoreCase(dsq.getSequenceAsString())
3753               && newres.length() > dsq.getLength())
3754       {
3755         // Update with the longer sequence.
3756         synchronized (dsq)
3757         {
3758           /*
3759            * if (pre) { sb.insert(0, newres .substring(0, dsq.getStart() -
3760            * sq.getStart())); dsq.setStart(sq.getStart()); } if (post) {
3761            * sb.append(newres.substring(newres.length() - sq.getEnd() -
3762            * dsq.getEnd())); dsq.setEnd(sq.getEnd()); }
3763            */
3764           dsq.setSequence(sb.toString());
3765         }
3766         // TODO: merges will never happen if we 'know' we have the real dataset
3767         // sequence - this should be detected when id==dssid
3768         System.err.println("DEBUG Notice:  Merged dataset sequence"); // ("
3769         // + (pre ? "prepended" : "") + " "
3770         // + (post ? "appended" : ""));
3771       }
3772     }
3773   }
3774
3775   java.util.Hashtable datasetIds = null;
3776
3777   java.util.IdentityHashMap dataset2Ids = null;
3778
3779   private Alignment getDatasetFor(String datasetId)
3780   {
3781     if (datasetIds == null)
3782     {
3783       datasetIds = new Hashtable();
3784       return null;
3785     }
3786     if (datasetIds.containsKey(datasetId))
3787     {
3788       return (Alignment) datasetIds.get(datasetId);
3789     }
3790     return null;
3791   }
3792
3793   private void addDatasetRef(String datasetId, Alignment dataset)
3794   {
3795     if (datasetIds == null)
3796     {
3797       datasetIds = new Hashtable();
3798     }
3799     datasetIds.put(datasetId, dataset);
3800   }
3801
3802   /**
3803    * make a new dataset ID for this jalview dataset alignment
3804    * 
3805    * @param dataset
3806    * @return
3807    */
3808   private String getDatasetIdRef(jalview.datamodel.Alignment dataset)
3809   {
3810     if (dataset.getDataset() != null)
3811     {
3812       warn("Serious issue!  Dataset Object passed to getDatasetIdRef is not a Jalview DATASET alignment...");
3813     }
3814     String datasetId = makeHashCode(dataset, null);
3815     if (datasetId == null)
3816     {
3817       // make a new datasetId and record it
3818       if (dataset2Ids == null)
3819       {
3820         dataset2Ids = new IdentityHashMap();
3821       }
3822       else
3823       {
3824         datasetId = (String) dataset2Ids.get(dataset);
3825       }
3826       if (datasetId == null)
3827       {
3828         datasetId = "ds" + dataset2Ids.size() + 1;
3829         dataset2Ids.put(dataset, datasetId);
3830       }
3831     }
3832     return datasetId;
3833   }
3834
3835   private void addDBRefs(SequenceI datasetSequence, Sequence sequence)
3836   {
3837     for (int d = 0; d < sequence.getDBRefCount(); d++)
3838     {
3839       DBRef dr = sequence.getDBRef(d);
3840       jalview.datamodel.DBRefEntry entry = new jalview.datamodel.DBRefEntry(
3841               sequence.getDBRef(d).getSource(), sequence.getDBRef(d)
3842                       .getVersion(), sequence.getDBRef(d).getAccessionId());
3843       if (dr.getMapping() != null)
3844       {
3845         entry.setMap(addMapping(dr.getMapping()));
3846       }
3847       datasetSequence.addDBRef(entry);
3848     }
3849   }
3850
3851   private jalview.datamodel.Mapping addMapping(Mapping m)
3852   {
3853     SequenceI dsto = null;
3854     // Mapping m = dr.getMapping();
3855     int fr[] = new int[m.getMapListFromCount() * 2];
3856     Enumeration f = m.enumerateMapListFrom();
3857     for (int _i = 0; f.hasMoreElements(); _i += 2)
3858     {
3859       MapListFrom mf = (MapListFrom) f.nextElement();
3860       fr[_i] = mf.getStart();
3861       fr[_i + 1] = mf.getEnd();
3862     }
3863     int fto[] = new int[m.getMapListToCount() * 2];
3864     f = m.enumerateMapListTo();
3865     for (int _i = 0; f.hasMoreElements(); _i += 2)
3866     {
3867       MapListTo mf = (MapListTo) f.nextElement();
3868       fto[_i] = mf.getStart();
3869       fto[_i + 1] = mf.getEnd();
3870     }
3871     jalview.datamodel.Mapping jmap = new jalview.datamodel.Mapping(dsto,
3872             fr, fto, (int) m.getMapFromUnit(), (int) m.getMapToUnit());
3873     if (m.getMappingChoice() != null)
3874     {
3875       MappingChoice mc = m.getMappingChoice();
3876       if (mc.getDseqFor() != null)
3877       {
3878         String dsfor = "" + mc.getDseqFor();
3879         if (seqRefIds.containsKey(dsfor))
3880         {
3881           /**
3882            * recover from hash
3883            */
3884           jmap.setTo((SequenceI) seqRefIds.get(dsfor));
3885         }
3886         else
3887         {
3888           frefedSequence.add(new Object[]
3889           { dsfor, jmap });
3890         }
3891       }
3892       else
3893       {
3894         /**
3895          * local sequence definition
3896          */
3897         Sequence ms = mc.getSequence();
3898         jalview.datamodel.Sequence djs = null;
3899         String sqid = ms.getDsseqid();
3900         if (sqid != null && sqid.length() > 0)
3901         {
3902           /*
3903            * recover dataset sequence
3904            */
3905           djs = (jalview.datamodel.Sequence) seqRefIds.get(sqid);
3906         }
3907         else
3908         {
3909           System.err
3910                   .println("Warning - making up dataset sequence id for DbRef sequence map reference");
3911           sqid = ((Object) ms).toString(); // make up a new hascode for
3912           // undefined dataset sequence hash
3913           // (unlikely to happen)
3914         }
3915
3916         if (djs == null)
3917         {
3918           /**
3919            * make a new dataset sequence and add it to refIds hash
3920            */
3921           djs = new jalview.datamodel.Sequence(ms.getName(),
3922                   ms.getSequence());
3923           djs.setStart(jmap.getMap().getToLowest());
3924           djs.setEnd(jmap.getMap().getToHighest());
3925           djs.setVamsasId(uniqueSetSuffix + sqid);
3926           jmap.setTo(djs);
3927           seqRefIds.put(sqid, djs);
3928
3929         }
3930         jalview.bin.Cache.log.debug("about to recurse on addDBRefs.");
3931         addDBRefs(djs, ms);
3932
3933       }
3934     }
3935     return (jmap);
3936
3937   }
3938
3939   public jalview.gui.AlignmentPanel copyAlignPanel(AlignmentPanel ap,
3940           boolean keepSeqRefs)
3941   {
3942     initSeqRefs();
3943     jalview.schemabinding.version2.JalviewModel jm = SaveState(ap, null,
3944             null);
3945
3946     if (!keepSeqRefs)
3947     {
3948       clearSeqRefs();
3949       jm.getJalviewModelSequence().getViewport(0).setSequenceSetId(null);
3950     }
3951     else
3952     {
3953       uniqueSetSuffix = "";
3954       jm.getJalviewModelSequence().getViewport(0).setId(null); // we don't
3955       // overwrite the
3956       // view we just
3957       // copied
3958     }
3959     if (this.frefedSequence == null)
3960     {
3961       frefedSequence = new Vector();
3962     }
3963
3964     viewportsAdded = new Hashtable();
3965
3966     AlignFrame af = LoadFromObject(jm, null, false, null);
3967     af.alignPanels.clear();
3968     af.closeMenuItem_actionPerformed(true);
3969
3970     /*
3971      * if(ap.av.getAlignment().getAlignmentAnnotation()!=null) { for(int i=0;
3972      * i<ap.av.getAlignment().getAlignmentAnnotation().length; i++) {
3973      * if(!ap.av.getAlignment().getAlignmentAnnotation()[i].autoCalculated) {
3974      * af.alignPanel.av.getAlignment().getAlignmentAnnotation()[i] =
3975      * ap.av.getAlignment().getAlignmentAnnotation()[i]; } } }
3976      */
3977
3978     return af.alignPanel;
3979   }
3980
3981   /**
3982    * flag indicating if hashtables should be cleared on finalization TODO this
3983    * flag may not be necessary
3984    */
3985   private final boolean _cleartables = true;
3986
3987   private Hashtable jvids2vobj;
3988
3989   /*
3990    * (non-Javadoc)
3991    * 
3992    * @see java.lang.Object#finalize()
3993    */
3994   @Override
3995   protected void finalize() throws Throwable
3996   {
3997     // really make sure we have no buried refs left.
3998     if (_cleartables)
3999     {
4000       clearSeqRefs();
4001     }
4002     this.seqRefIds = null;
4003     this.seqsToIds = null;
4004     super.finalize();
4005   }
4006
4007   private void warn(String msg)
4008   {
4009     warn(msg, null);
4010   }
4011
4012   private void warn(String msg, Exception e)
4013   {
4014     if (Cache.log != null)
4015     {
4016       if (e != null)
4017       {
4018         Cache.log.warn(msg, e);
4019       }
4020       else
4021       {
4022         Cache.log.warn(msg);
4023       }
4024     }
4025     else
4026     {
4027       System.err.println("Warning: " + msg);
4028       if (e != null)
4029       {
4030         e.printStackTrace();
4031       }
4032     }
4033   }
4034
4035   private void debug(String string)
4036   {
4037     debug(string, null);
4038   }
4039
4040   private void debug(String msg, Exception e)
4041   {
4042     if (Cache.log != null)
4043     {
4044       if (e != null)
4045       {
4046         Cache.log.debug(msg, e);
4047       }
4048       else
4049       {
4050         Cache.log.debug(msg);
4051       }
4052     }
4053     else
4054     {
4055       System.err.println("Warning: " + msg);
4056       if (e != null)
4057       {
4058         e.printStackTrace();
4059       }
4060     }
4061   }
4062
4063   /**
4064    * set the object to ID mapping tables used to write/recover objects and XML
4065    * ID strings for the jalview project. If external tables are provided then
4066    * finalize and clearSeqRefs will not clear the tables when the Jalview2XML
4067    * object goes out of scope. - also populates the datasetIds hashtable with
4068    * alignment objects containing dataset sequences
4069    * 
4070    * @param vobj2jv
4071    *          Map from ID strings to jalview datamodel
4072    * @param jv2vobj
4073    *          Map from jalview datamodel to ID strings
4074    * 
4075    * 
4076    */
4077   public void setObjectMappingTables(Hashtable vobj2jv,
4078           IdentityHashMap jv2vobj)
4079   {
4080     this.jv2vobj = jv2vobj;
4081     this.vobj2jv = vobj2jv;
4082     Iterator ds = jv2vobj.keySet().iterator();
4083     String id;
4084     while (ds.hasNext())
4085     {
4086       Object jvobj = ds.next();
4087       id = jv2vobj.get(jvobj).toString();
4088       if (jvobj instanceof jalview.datamodel.Alignment)
4089       {
4090         if (((jalview.datamodel.Alignment) jvobj).getDataset() == null)
4091         {
4092           addDatasetRef(id, (jalview.datamodel.Alignment) jvobj);
4093         }
4094       }
4095       else if (jvobj instanceof jalview.datamodel.Sequence)
4096       {
4097         // register sequence object so the XML parser can recover it.
4098         if (seqRefIds == null)
4099         {
4100           seqRefIds = new Hashtable();
4101         }
4102         if (seqsToIds == null)
4103         {
4104           seqsToIds = new IdentityHashMap();
4105         }
4106         seqRefIds.put(jv2vobj.get(jvobj).toString(), jvobj);
4107         seqsToIds.put(jvobj, id);
4108       }
4109       else if (jvobj instanceof jalview.datamodel.AlignmentAnnotation)
4110       {
4111         if (annotationIds == null)
4112         {
4113           annotationIds = new Hashtable();
4114         }
4115         String anid;
4116         annotationIds.put(anid = jv2vobj.get(jvobj).toString(), jvobj);
4117         jalview.datamodel.AlignmentAnnotation jvann = (jalview.datamodel.AlignmentAnnotation) jvobj;
4118         if (jvann.annotationId == null)
4119         {
4120           jvann.annotationId = anid;
4121         }
4122         if (!jvann.annotationId.equals(anid))
4123         {
4124           // TODO verify that this is the correct behaviour
4125           this.warn("Overriding Annotation ID for " + anid
4126                   + " from different id : " + jvann.annotationId);
4127           jvann.annotationId = anid;
4128         }
4129       }
4130       else if (jvobj instanceof String)
4131       {
4132         if (jvids2vobj == null)
4133         {
4134           jvids2vobj = new Hashtable();
4135           jvids2vobj.put(jvobj, jv2vobj.get(jvobj).toString());
4136         }
4137       }
4138       else
4139         Cache.log.debug("Ignoring " + jvobj.getClass() + " (ID = " + id);
4140     }
4141   }
4142
4143   /**
4144    * set the uniqueSetSuffix used to prefix/suffix object IDs for jalview
4145    * objects created from the project archive. If string is null (default for
4146    * construction) then suffix will be set automatically.
4147    * 
4148    * @param string
4149    */
4150   public void setUniqueSetSuffix(String string)
4151   {
4152     uniqueSetSuffix = string;
4153
4154   }
4155
4156   /**
4157    * uses skipList2 as the skipList for skipping views on sequence sets
4158    * associated with keys in the skipList
4159    * 
4160    * @param skipList2
4161    */
4162   public void setSkipList(Hashtable skipList2)
4163   {
4164     skipList = skipList2;
4165   }
4166
4167 }