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