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