JAL-1193 call setLoadingFromArchiveFinished(true) for any new structure views, but...
[jalview.git] / src / jalview / gui / Jalview2XML.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
3  * Copyright (C) 2012 J Procter, AM Waterhouse, LM Lui, J Engelhardt, G Barton, M Clamp, S Searle
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
10  *  
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 package jalview.gui;
19
20 import java.awt.Rectangle;
21 import java.io.*;
22 import java.lang.reflect.InvocationTargetException;
23 import java.net.*;
24 import java.util.*;
25 import java.util.Map.Entry;
26 import java.util.jar.*;
27
28 import javax.swing.*;
29
30 import org.exolab.castor.xml.*;
31
32 import jalview.bin.Cache;
33 import jalview.datamodel.Alignment;
34 import jalview.datamodel.AlignmentAnnotation;
35 import jalview.datamodel.AlignmentI;
36 import jalview.datamodel.SequenceI;
37 import jalview.schemabinding.version2.*;
38 import jalview.schemes.*;
39 import jalview.util.Platform;
40 import jalview.util.jarInputStreamProvider;
41 import jalview.ws.jws2.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       // create list to store references for any new Jmol viewers created
1674       newStructureViewers=new Vector<AppJmol>();
1675       // UNMARSHALLER SEEMS TO CLOSE JARINPUTSTREAM, MOST ANNOYING
1676       // Workaround is to make sure caller implements the JarInputStreamProvider
1677       // interface
1678       // so we can re-open the jar input stream for each entry.
1679
1680       jarInputStreamProvider jprovider = createjarInputStreamProvider(file);
1681       af = LoadJalviewAlign(jprovider);
1682       setLoadingFinishedForNewStructureViewers();
1683     } catch (MalformedURLException e)
1684     {
1685       errorMessage = "Invalid URL format for '" + file + "'";
1686       reportErrors();
1687     }
1688     return af;
1689   }
1690
1691   private jarInputStreamProvider createjarInputStreamProvider(
1692           final String file) throws MalformedURLException
1693   {
1694     URL url = null;
1695     errorMessage = null;
1696     uniqueSetSuffix = null;
1697     seqRefIds = null;
1698     viewportsAdded = null;
1699     frefedSequence = null;
1700
1701     if (file.startsWith("http://"))
1702     {
1703       url = new URL(file);
1704     }
1705     final URL _url = url;
1706     return new jarInputStreamProvider()
1707     {
1708
1709       @Override
1710       public JarInputStream getJarInputStream() throws IOException
1711       {
1712         if (_url != null)
1713         {
1714           return new JarInputStream(_url.openStream());
1715         }
1716         else
1717         {
1718           return new JarInputStream(new FileInputStream(file));
1719         }
1720       }
1721
1722       @Override
1723       public String getFilename()
1724       {
1725         return file;
1726       }
1727     };
1728   }
1729
1730   /**
1731    * Recover jalview session from a jalview project archive. Caller may
1732    * initialise uniqueSetSuffix, seqRefIds, viewportsAdded and frefedSequence
1733    * themselves. Any null fields will be initialised with default values,
1734    * non-null fields are left alone.
1735    * 
1736    * @param jprovider
1737    * @return
1738    */
1739   public AlignFrame LoadJalviewAlign(final jarInputStreamProvider jprovider)
1740   {
1741     errorMessage = null;
1742     if (uniqueSetSuffix == null)
1743     {
1744       uniqueSetSuffix = System.currentTimeMillis() % 100000 + "";
1745     }
1746     if (seqRefIds == null)
1747     {
1748       seqRefIds = new Hashtable();
1749     }
1750     if (viewportsAdded == null)
1751     {
1752       viewportsAdded = new Hashtable();
1753     }
1754     if (frefedSequence == null)
1755     {
1756       frefedSequence = new Vector();
1757     }
1758
1759     jalview.gui.AlignFrame af = null;
1760     Hashtable gatherToThisFrame = new Hashtable();
1761     final String file = jprovider.getFilename();
1762     try
1763     {
1764       JarInputStream jin = null;
1765       JarEntry jarentry = null;
1766       int entryCount = 1;
1767
1768       do
1769       {
1770         jin = jprovider.getJarInputStream();
1771         for (int i = 0; i < entryCount; i++)
1772         {
1773           jarentry = jin.getNextJarEntry();
1774         }
1775
1776         if (jarentry != null && jarentry.getName().endsWith(".xml"))
1777         {
1778           InputStreamReader in = new InputStreamReader(jin, "UTF-8");
1779           JalviewModel object = new JalviewModel();
1780
1781           Unmarshaller unmar = new Unmarshaller(object);
1782           unmar.setValidation(false);
1783           object = (JalviewModel) unmar.unmarshal(in);
1784           if (true) // !skipViewport(object))
1785           {
1786             af = LoadFromObject(object, file, true, jprovider);
1787             if (af.viewport.gatherViewsHere)
1788             {
1789               gatherToThisFrame.put(af.viewport.getSequenceSetId(), af);
1790             }
1791           }
1792           entryCount++;
1793         }
1794         else if (jarentry != null)
1795         {
1796           // Some other file here.
1797           entryCount++;
1798         }
1799       } while (jarentry != null);
1800       resolveFrefedSequences();
1801     } catch (java.io.FileNotFoundException ex)
1802     {
1803       ex.printStackTrace();
1804       errorMessage = "Couldn't locate Jalview XML file : " + file;
1805       System.err.println("Exception whilst loading jalview XML file : "
1806               + ex + "\n");
1807     } catch (java.net.UnknownHostException ex)
1808     {
1809       ex.printStackTrace();
1810       errorMessage = "Couldn't locate Jalview XML file : " + file;
1811       System.err.println("Exception whilst loading jalview XML file : "
1812               + ex + "\n");
1813     } catch (Exception ex)
1814     {
1815       System.err.println("Parsing as Jalview Version 2 file failed.");
1816       ex.printStackTrace(System.err);
1817       if (attemptversion1parse)
1818       {
1819         // Is Version 1 Jar file?
1820         try
1821         {
1822           af = new Jalview2XML_V1(raiseGUI).LoadJalviewAlign(jprovider);
1823         } catch (Exception ex2)
1824         {
1825           System.err.println("Exception whilst loading as jalviewXMLV1:");
1826           ex2.printStackTrace();
1827           af = null;
1828         }
1829       }
1830       if (Desktop.instance != null)
1831       {
1832         Desktop.instance.stopLoading();
1833       }
1834       if (af != null)
1835       {
1836         System.out.println("Successfully loaded archive file");
1837         return af;
1838       }
1839       ex.printStackTrace();
1840
1841       System.err.println("Exception whilst loading jalview XML file : "
1842               + ex + "\n");
1843     } catch (OutOfMemoryError e)
1844     {
1845       // Don't use the OOM Window here
1846       errorMessage = "Out of memory loading jalview XML file";
1847       System.err.println("Out of memory whilst loading jalview XML file");
1848       e.printStackTrace();
1849     }
1850
1851     if (Desktop.instance != null)
1852     {
1853       Desktop.instance.stopLoading();
1854     }
1855
1856     Enumeration en = gatherToThisFrame.elements();
1857     while (en.hasMoreElements())
1858     {
1859       Desktop.instance.gatherViews((AlignFrame) en.nextElement());
1860     }
1861     if (errorMessage != null)
1862     {
1863       reportErrors();
1864     }
1865     return af;
1866   }
1867
1868   /**
1869    * check errorMessage for a valid error message and raise an error box in the
1870    * GUI or write the current errorMessage to stderr and then clear the error
1871    * state.
1872    */
1873   protected void reportErrors()
1874   {
1875     reportErrors(false);
1876   }
1877
1878   protected void reportErrors(final boolean saving)
1879   {
1880     if (errorMessage != null)
1881     {
1882       final String finalErrorMessage = errorMessage;
1883       if (raiseGUI)
1884       {
1885         javax.swing.SwingUtilities.invokeLater(new Runnable()
1886         {
1887           @Override
1888           public void run()
1889           {
1890             JOptionPane.showInternalMessageDialog(Desktop.desktop,
1891                     finalErrorMessage, "Error "
1892                             + (saving ? "saving" : "loading")
1893                             + " Jalview file", JOptionPane.WARNING_MESSAGE);
1894           }
1895         });
1896       }
1897       else
1898       {
1899         System.err.println("Problem loading Jalview file: " + errorMessage);
1900       }
1901     }
1902     errorMessage = null;
1903   }
1904
1905   Hashtable alreadyLoadedPDB;
1906
1907   /**
1908    * when set, local views will be updated from view stored in JalviewXML
1909    * Currently (28th Sep 2008) things will go horribly wrong in vamsas document
1910    * sync if this is set to true.
1911    */
1912   private final boolean updateLocalViews = false;
1913
1914   String loadPDBFile(jarInputStreamProvider jprovider, String pdbId)
1915   {
1916     if (alreadyLoadedPDB == null)
1917       alreadyLoadedPDB = new Hashtable();
1918
1919     if (alreadyLoadedPDB.containsKey(pdbId))
1920       return alreadyLoadedPDB.get(pdbId).toString();
1921
1922     try
1923     {
1924       JarInputStream jin = jprovider.getJarInputStream();
1925       /*
1926        * if (jprovider.startsWith("http://")) { jin = new JarInputStream(new
1927        * URL(jprovider).openStream()); } else { jin = new JarInputStream(new
1928        * FileInputStream(jprovider)); }
1929        */
1930
1931       JarEntry entry = null;
1932       do
1933       {
1934         entry = jin.getNextJarEntry();
1935       } while (entry != null && !entry.getName().equals(pdbId));
1936       if (entry != null)
1937       {
1938         BufferedReader in = new BufferedReader(new InputStreamReader(jin));
1939         File outFile = File.createTempFile("jalview_pdb", ".txt");
1940         outFile.deleteOnExit();
1941         PrintWriter out = new PrintWriter(new FileOutputStream(outFile));
1942         String data;
1943
1944         while ((data = in.readLine()) != null)
1945         {
1946           out.println(data);
1947         }
1948         try
1949         {
1950           out.flush();
1951         } catch (Exception foo)
1952         {
1953         }
1954         ;
1955         out.close();
1956         String t = outFile.getAbsolutePath();
1957         alreadyLoadedPDB.put(pdbId, t);
1958         return t;
1959       }
1960       else
1961       {
1962         warn("Couldn't find PDB file entry in Jalview Jar for " + pdbId);
1963       }
1964     } catch (Exception ex)
1965     {
1966       ex.printStackTrace();
1967     }
1968
1969     return null;
1970   }
1971
1972   private class JvAnnotRow
1973   {
1974     public JvAnnotRow(int i, AlignmentAnnotation jaa)
1975     {
1976       order = i;
1977       template = jaa;
1978     }
1979
1980     /**
1981      * persisted version of annotation row from which to take vis properties
1982      */
1983     public jalview.datamodel.AlignmentAnnotation template;
1984
1985     /**
1986      * original position of the annotation row in the alignment
1987      */
1988     public int order;
1989   }
1990
1991   /**
1992    * Load alignment frame from jalview XML DOM object
1993    * 
1994    * @param object
1995    *          DOM
1996    * @param file
1997    *          filename source string
1998    * @param loadTreesAndStructures
1999    *          when false only create Viewport
2000    * @param jprovider
2001    *          data source provider
2002    * @return alignment frame created from view stored in DOM
2003    */
2004   AlignFrame LoadFromObject(JalviewModel object, String file,
2005           boolean loadTreesAndStructures, jarInputStreamProvider jprovider)
2006   {
2007     SequenceSet vamsasSet = object.getVamsasModel().getSequenceSet(0);
2008     Sequence[] vamsasSeq = vamsasSet.getSequence();
2009
2010     JalviewModelSequence jms = object.getJalviewModelSequence();
2011
2012     Viewport view = jms.getViewport(0);
2013     // ////////////////////////////////
2014     // LOAD SEQUENCES
2015
2016     Vector hiddenSeqs = null;
2017     jalview.datamodel.Sequence jseq;
2018
2019     ArrayList tmpseqs = new ArrayList();
2020
2021     boolean multipleView = false;
2022
2023     JSeq[] JSEQ = object.getJalviewModelSequence().getJSeq();
2024     int vi = 0; // counter in vamsasSeq array
2025     for (int i = 0; i < JSEQ.length; i++)
2026     {
2027       String seqId = JSEQ[i].getId();
2028
2029       if (seqRefIds.get(seqId) != null)
2030       {
2031         tmpseqs.add(seqRefIds.get(seqId));
2032         multipleView = true;
2033       }
2034       else
2035       {
2036         jseq = new jalview.datamodel.Sequence(vamsasSeq[vi].getName(),
2037                 vamsasSeq[vi].getSequence());
2038         jseq.setDescription(vamsasSeq[vi].getDescription());
2039         jseq.setStart(JSEQ[i].getStart());
2040         jseq.setEnd(JSEQ[i].getEnd());
2041         jseq.setVamsasId(uniqueSetSuffix + seqId);
2042         seqRefIds.put(vamsasSeq[vi].getId(), jseq);
2043         tmpseqs.add(jseq);
2044         vi++;
2045       }
2046
2047       if (JSEQ[i].getHidden())
2048       {
2049         if (hiddenSeqs == null)
2050         {
2051           hiddenSeqs = new Vector();
2052         }
2053
2054         hiddenSeqs.addElement(seqRefIds.get(seqId));
2055       }
2056
2057     }
2058
2059     // /
2060     // Create the alignment object from the sequence set
2061     // ///////////////////////////////
2062     jalview.datamodel.Sequence[] orderedSeqs = new jalview.datamodel.Sequence[tmpseqs
2063             .size()];
2064
2065     tmpseqs.toArray(orderedSeqs);
2066
2067     jalview.datamodel.Alignment al = new jalview.datamodel.Alignment(
2068             orderedSeqs);
2069
2070     // / Add the alignment properties
2071     for (int i = 0; i < vamsasSet.getSequenceSetPropertiesCount(); i++)
2072     {
2073       SequenceSetProperties ssp = vamsasSet.getSequenceSetProperties(i);
2074       al.setProperty(ssp.getKey(), ssp.getValue());
2075     }
2076
2077     // /
2078     // SequenceFeatures are added to the DatasetSequence,
2079     // so we must create or recover the dataset before loading features
2080     // ///////////////////////////////
2081     if (vamsasSet.getDatasetId() == null || vamsasSet.getDatasetId() == "")
2082     {
2083       // older jalview projects do not have a dataset id.
2084       al.setDataset(null);
2085     }
2086     else
2087     {
2088       recoverDatasetFor(vamsasSet, al);
2089     }
2090     // ///////////////////////////////
2091
2092     Hashtable pdbloaded = new Hashtable();
2093     if (!multipleView)
2094     {
2095       // load sequence features, database references and any associated PDB
2096       // structures for the alignment
2097       for (int i = 0; i < vamsasSeq.length; i++)
2098       {
2099         if (JSEQ[i].getFeaturesCount() > 0)
2100         {
2101           Features[] features = JSEQ[i].getFeatures();
2102           for (int f = 0; f < features.length; f++)
2103           {
2104             jalview.datamodel.SequenceFeature sf = new jalview.datamodel.SequenceFeature(
2105                     features[f].getType(), features[f].getDescription(),
2106                     features[f].getStatus(), features[f].getBegin(),
2107                     features[f].getEnd(), features[f].getFeatureGroup());
2108
2109             sf.setScore(features[f].getScore());
2110             for (int od = 0; od < features[f].getOtherDataCount(); od++)
2111             {
2112               OtherData keyValue = features[f].getOtherData(od);
2113               if (keyValue.getKey().startsWith("LINK"))
2114               {
2115                 sf.addLink(keyValue.getValue());
2116               }
2117               else
2118               {
2119                 sf.setValue(keyValue.getKey(), keyValue.getValue());
2120               }
2121
2122             }
2123
2124             al.getSequenceAt(i).getDatasetSequence().addSequenceFeature(sf);
2125           }
2126         }
2127         if (vamsasSeq[i].getDBRefCount() > 0)
2128         {
2129           addDBRefs(al.getSequenceAt(i).getDatasetSequence(), vamsasSeq[i]);
2130         }
2131         if (JSEQ[i].getPdbidsCount() > 0)
2132         {
2133           Pdbids[] ids = JSEQ[i].getPdbids();
2134           for (int p = 0; p < ids.length; p++)
2135           {
2136             jalview.datamodel.PDBEntry entry = new jalview.datamodel.PDBEntry();
2137             entry.setId(ids[p].getId());
2138             entry.setType(ids[p].getType());
2139             if (ids[p].getFile() != null)
2140             {
2141               if (!pdbloaded.containsKey(ids[p].getFile()))
2142               {
2143                 entry.setFile(loadPDBFile(jprovider, ids[p].getId()));
2144               }
2145               else
2146               {
2147                 entry.setFile(pdbloaded.get(ids[p].getId()).toString());
2148               }
2149             }
2150
2151             al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
2152           }
2153         }
2154       }
2155     } // end !multipleview
2156
2157     // ///////////////////////////////
2158     // LOAD SEQUENCE MAPPINGS
2159
2160     if (vamsasSet.getAlcodonFrameCount() > 0)
2161     {
2162       // TODO Potentially this should only be done once for all views of an
2163       // alignment
2164       AlcodonFrame[] alc = vamsasSet.getAlcodonFrame();
2165       for (int i = 0; i < alc.length; i++)
2166       {
2167         jalview.datamodel.AlignedCodonFrame cf = new jalview.datamodel.AlignedCodonFrame(
2168                 alc[i].getAlcodonCount());
2169         if (alc[i].getAlcodonCount() > 0)
2170         {
2171           Alcodon[] alcods = alc[i].getAlcodon();
2172           for (int p = 0; p < cf.codons.length; p++)
2173           {
2174             if (alcods[p].hasPos1() && alcods[p].hasPos2()
2175                     && alcods[p].hasPos3())
2176             {
2177               // translated codons require three valid positions
2178               cf.codons[p] = new int[3];
2179               cf.codons[p][0] = (int) alcods[p].getPos1();
2180               cf.codons[p][1] = (int) alcods[p].getPos2();
2181               cf.codons[p][2] = (int) alcods[p].getPos3();
2182             }
2183             else
2184             {
2185               cf.codons[p] = null;
2186             }
2187           }
2188         }
2189         if (alc[i].getAlcodMapCount() > 0)
2190         {
2191           AlcodMap[] maps = alc[i].getAlcodMap();
2192           for (int m = 0; m < maps.length; m++)
2193           {
2194             SequenceI dnaseq = (SequenceI) seqRefIds
2195                     .get(maps[m].getDnasq());
2196             // Load Mapping
2197             jalview.datamodel.Mapping mapping = null;
2198             // attach to dna sequence reference.
2199             if (maps[m].getMapping() != null)
2200             {
2201               mapping = addMapping(maps[m].getMapping());
2202             }
2203             if (dnaseq != null)
2204             {
2205               cf.addMap(dnaseq, mapping.getTo(), mapping.getMap());
2206             }
2207             else
2208             {
2209               // defer to later
2210               frefedSequence.add(new Object[]
2211               { maps[m].getDnasq(), cf, mapping });
2212             }
2213           }
2214         }
2215         al.addCodonFrame(cf);
2216       }
2217
2218     }
2219
2220     // ////////////////////////////////
2221     // LOAD ANNOTATIONS
2222     ArrayList<JvAnnotRow> autoAlan = new ArrayList<JvAnnotRow>();
2223     /**
2224      * store any annotations which forward reference a group's ID
2225      */
2226     Hashtable<String, ArrayList<jalview.datamodel.AlignmentAnnotation>> groupAnnotRefs = new Hashtable<String, ArrayList<jalview.datamodel.AlignmentAnnotation>>();
2227
2228     if (vamsasSet.getAnnotationCount() > 0)
2229     {
2230       Annotation[] an = vamsasSet.getAnnotation();
2231
2232       for (int i = 0; i < an.length; i++)
2233       {
2234         /**
2235          * test if annotation is automatically calculated for this view only
2236          */
2237         boolean autoForView = false;
2238         if (an[i].getLabel().equals("Quality")
2239                 || an[i].getLabel().equals("Conservation")
2240                 || an[i].getLabel().equals("Consensus"))
2241         {
2242           // Kludge for pre 2.5 projects which lacked the autocalculated flag
2243           autoForView = true;
2244           if (!an[i].hasAutoCalculated())
2245           {
2246             an[i].setAutoCalculated(true);
2247           }
2248         }
2249         if (autoForView
2250                 || (an[i].hasAutoCalculated() && an[i].isAutoCalculated()))
2251         {
2252           // remove ID - we don't recover annotation from other views for
2253           // view-specific annotation
2254           an[i].setId(null);
2255         }
2256
2257         // set visiblity for other annotation in this view
2258         if (an[i].getId() != null
2259                 && annotationIds.containsKey(an[i].getId()))
2260         {
2261           jalview.datamodel.AlignmentAnnotation jda = (jalview.datamodel.AlignmentAnnotation) annotationIds
2262                   .get(an[i].getId());
2263           // in principle Visible should always be true for annotation displayed
2264           // in multiple views
2265           if (an[i].hasVisible())
2266             jda.visible = an[i].getVisible();
2267
2268           al.addAnnotation(jda);
2269
2270           continue;
2271         }
2272         // Construct new annotation from model.
2273         AnnotationElement[] ae = an[i].getAnnotationElement();
2274         jalview.datamodel.Annotation[] anot = null;
2275         java.awt.Color firstColour = null;
2276         int anpos;
2277         if (!an[i].getScoreOnly())
2278         {
2279           anot = new jalview.datamodel.Annotation[al.getWidth()];
2280           for (int aa = 0; aa < ae.length && aa < anot.length; aa++)
2281           {
2282             anpos = ae[aa].getPosition();
2283
2284             if (anpos >= anot.length)
2285               continue;
2286
2287             anot[anpos] = new jalview.datamodel.Annotation(
2288
2289             ae[aa].getDisplayCharacter(), ae[aa].getDescription(),
2290                     (ae[aa].getSecondaryStructure() == null || ae[aa]
2291                             .getSecondaryStructure().length() == 0) ? ' '
2292                             : ae[aa].getSecondaryStructure().charAt(0),
2293                     ae[aa].getValue()
2294
2295             );
2296             // JBPNote: Consider verifying dataflow for IO of secondary
2297             // structure annotation read from Stockholm files
2298             // this was added to try to ensure that
2299             // if (anot[ae[aa].getPosition()].secondaryStructure>' ')
2300             // {
2301             // anot[ae[aa].getPosition()].displayCharacter = "";
2302             // }
2303             anot[anpos].colour = new java.awt.Color(ae[aa].getColour());
2304             if (firstColour == null)
2305             {
2306               firstColour = anot[anpos].colour;
2307             }
2308           }
2309         }
2310         jalview.datamodel.AlignmentAnnotation jaa = null;
2311
2312         if (an[i].getGraph())
2313         {
2314           float llim = 0, hlim = 0;
2315           // if (autoForView || an[i].isAutoCalculated()) {
2316           // hlim=11f;
2317           // }
2318           jaa = new jalview.datamodel.AlignmentAnnotation(an[i].getLabel(),
2319                   an[i].getDescription(), anot, llim, hlim,
2320                   an[i].getGraphType());
2321
2322           jaa.graphGroup = an[i].getGraphGroup();
2323           jaa._linecolour = firstColour;
2324           if (an[i].getThresholdLine() != null)
2325           {
2326             jaa.setThreshold(new jalview.datamodel.GraphLine(an[i]
2327                     .getThresholdLine().getValue(), an[i]
2328                     .getThresholdLine().getLabel(), new java.awt.Color(
2329                     an[i].getThresholdLine().getColour())));
2330
2331           }
2332           if (autoForView || an[i].isAutoCalculated())
2333           {
2334             // Hardwire the symbol display line to ensure that labels for
2335             // histograms are displayed
2336             jaa.hasText = true;
2337           }
2338         }
2339         else
2340         {
2341           jaa = new jalview.datamodel.AlignmentAnnotation(an[i].getLabel(),
2342                   an[i].getDescription(), anot);
2343           jaa._linecolour = firstColour;
2344         }
2345         // register new annotation
2346         if (an[i].getId() != null)
2347         {
2348           annotationIds.put(an[i].getId(), jaa);
2349           jaa.annotationId = an[i].getId();
2350         }
2351         // recover sequence association
2352         if (an[i].getSequenceRef() != null)
2353         {
2354           if (al.findName(an[i].getSequenceRef()) != null)
2355           {
2356             jaa.createSequenceMapping(al.findName(an[i].getSequenceRef()),
2357                     1, true);
2358             al.findName(an[i].getSequenceRef()).addAlignmentAnnotation(jaa);
2359           }
2360         }
2361         // and make a note of any group association
2362         if (an[i].getGroupRef() != null && an[i].getGroupRef().length() > 0)
2363         {
2364           ArrayList<jalview.datamodel.AlignmentAnnotation> aal = groupAnnotRefs
2365                   .get(an[i].getGroupRef());
2366           if (aal == null)
2367           {
2368             aal = new ArrayList<jalview.datamodel.AlignmentAnnotation>();
2369             groupAnnotRefs.put(an[i].getGroupRef(), aal);
2370           }
2371           aal.add(jaa);
2372         }
2373
2374         if (an[i].hasScore())
2375         {
2376           jaa.setScore(an[i].getScore());
2377         }
2378         if (an[i].hasVisible())
2379           jaa.visible = an[i].getVisible();
2380
2381         if (an[i].hasCentreColLabels())
2382           jaa.centreColLabels = an[i].getCentreColLabels();
2383
2384         if (an[i].hasScaleColLabels())
2385         {
2386           jaa.scaleColLabel = an[i].getScaleColLabels();
2387         }
2388         if (an[i].hasAutoCalculated() && an[i].isAutoCalculated())
2389         {
2390           // newer files have an 'autoCalculated' flag and store calculation
2391           // state in viewport properties
2392           jaa.autoCalculated = true; // means annotation will be marked for
2393           // update at end of load.
2394         }
2395         if (an[i].hasGraphHeight())
2396         {
2397           jaa.graphHeight = an[i].getGraphHeight();
2398         }
2399         if (an[i].hasBelowAlignment())
2400         {
2401           jaa.belowAlignment = an[i].isBelowAlignment();
2402         }
2403         jaa.setCalcId(an[i].getCalcId());
2404
2405         if (jaa.autoCalculated)
2406         {
2407           autoAlan.add(new JvAnnotRow(i, jaa));
2408         }
2409         else
2410         // if (!autoForView)
2411         {
2412           // add autocalculated group annotation and any user created annotation
2413           // for the view
2414           al.addAnnotation(jaa);
2415         }
2416       }
2417     }
2418
2419     // ///////////////////////
2420     // LOAD GROUPS
2421     // Create alignment markup and styles for this view
2422     if (jms.getJGroupCount() > 0)
2423     {
2424       JGroup[] groups = jms.getJGroup();
2425
2426       for (int i = 0; i < groups.length; i++)
2427       {
2428         ColourSchemeI cs = null;
2429
2430         if (groups[i].getColour() != null)
2431         {
2432           if (groups[i].getColour().startsWith("ucs"))
2433           {
2434             cs = GetUserColourScheme(jms, groups[i].getColour());
2435           }
2436           else
2437           {
2438             cs = ColourSchemeProperty.getColour(al, groups[i].getColour());
2439           }
2440
2441           if (cs != null)
2442           {
2443             cs.setThreshold(groups[i].getPidThreshold(), true);
2444           }
2445         }
2446
2447         Vector seqs = new Vector();
2448
2449         for (int s = 0; s < groups[i].getSeqCount(); s++)
2450         {
2451           String seqId = groups[i].getSeq(s) + "";
2452           jalview.datamodel.SequenceI ts = (jalview.datamodel.SequenceI) seqRefIds
2453                   .get(seqId);
2454
2455           if (ts != null)
2456           {
2457             seqs.addElement(ts);
2458           }
2459         }
2460
2461         if (seqs.size() < 1)
2462         {
2463           continue;
2464         }
2465
2466         jalview.datamodel.SequenceGroup sg = new jalview.datamodel.SequenceGroup(
2467                 seqs, groups[i].getName(), cs, groups[i].getDisplayBoxes(),
2468                 groups[i].getDisplayText(), groups[i].getColourText(),
2469                 groups[i].getStart(), groups[i].getEnd());
2470
2471         sg.setOutlineColour(new java.awt.Color(groups[i].getOutlineColour()));
2472
2473         sg.textColour = new java.awt.Color(groups[i].getTextCol1());
2474         sg.textColour2 = new java.awt.Color(groups[i].getTextCol2());
2475         sg.setShowNonconserved(groups[i].hasShowUnconserved() ? groups[i]
2476                 .isShowUnconserved() : false);
2477         sg.thresholdTextColour = groups[i].getTextColThreshold();
2478         if (groups[i].hasShowConsensusHistogram())
2479         {
2480           sg.setShowConsensusHistogram(groups[i].isShowConsensusHistogram());
2481         }
2482         ;
2483         if (groups[i].hasShowSequenceLogo())
2484         {
2485           sg.setshowSequenceLogo(groups[i].isShowSequenceLogo());
2486         }
2487         if (groups[i].hasNormaliseSequenceLogo())
2488         {
2489           sg.setNormaliseSequenceLogo(groups[i].isNormaliseSequenceLogo());
2490         }
2491         if (groups[i].hasIgnoreGapsinConsensus())
2492         {
2493           sg.setIgnoreGapsConsensus(groups[i].getIgnoreGapsinConsensus());
2494         }
2495         if (groups[i].getConsThreshold() != 0)
2496         {
2497           jalview.analysis.Conservation c = new jalview.analysis.Conservation(
2498                   "All", ResidueProperties.propHash, 3,
2499                   sg.getSequences(null), 0, sg.getWidth() - 1);
2500           c.calculate();
2501           c.verdict(false, 25);
2502           sg.cs.setConservation(c);
2503         }
2504
2505         if (groups[i].getId() != null && groupAnnotRefs.size() > 0)
2506         {
2507           // re-instate unique group/annotation row reference
2508           ArrayList<jalview.datamodel.AlignmentAnnotation> jaal = groupAnnotRefs
2509                   .get(groups[i].getId());
2510           if (jaal != null)
2511           {
2512             for (jalview.datamodel.AlignmentAnnotation jaa : jaal)
2513             {
2514               jaa.groupRef = sg;
2515               if (jaa.autoCalculated)
2516               {
2517                 // match up and try to set group autocalc alignment row for this
2518                 // annotation
2519                 if (jaa.label.startsWith("Consensus for "))
2520                 {
2521                   sg.setConsensus(jaa);
2522                 }
2523                 // match up and try to set group autocalc alignment row for this
2524                 // annotation
2525                 if (jaa.label.startsWith("Conservation for "))
2526                 {
2527                   sg.setConservationRow(jaa);
2528                 }
2529               }
2530             }
2531           }
2532         }
2533         al.addGroup(sg);
2534
2535       }
2536     }
2537
2538     // ///////////////////////////////
2539     // LOAD VIEWPORT
2540
2541     // If we just load in the same jar file again, the sequenceSetId
2542     // will be the same, and we end up with multiple references
2543     // to the same sequenceSet. We must modify this id on load
2544     // so that each load of the file gives a unique id
2545     String uniqueSeqSetId = view.getSequenceSetId() + uniqueSetSuffix;
2546     String viewId = (view.getId() == null ? null : view.getId()
2547             + uniqueSetSuffix);
2548     AlignFrame af = null;
2549     AlignViewport av = null;
2550     // now check to see if we really need to create a new viewport.
2551     if (multipleView && viewportsAdded.size() == 0)
2552     {
2553       // We recovered an alignment for which a viewport already exists.
2554       // TODO: fix up any settings necessary for overlaying stored state onto
2555       // state recovered from another document. (may not be necessary).
2556       // we may need a binding from a viewport in memory to one recovered from
2557       // XML.
2558       // and then recover its containing af to allow the settings to be applied.
2559       // TODO: fix for vamsas demo
2560       System.err
2561               .println("About to recover a viewport for existing alignment: Sequence set ID is "
2562                       + uniqueSeqSetId);
2563       Object seqsetobj = retrieveExistingObj(uniqueSeqSetId);
2564       if (seqsetobj != null)
2565       {
2566         if (seqsetobj instanceof String)
2567         {
2568           uniqueSeqSetId = (String) seqsetobj;
2569           System.err
2570                   .println("Recovered extant sequence set ID mapping for ID : New Sequence set ID is "
2571                           + uniqueSeqSetId);
2572         }
2573         else
2574         {
2575           System.err
2576                   .println("Warning : Collision between sequence set ID string and existing jalview object mapping.");
2577         }
2578
2579       }
2580     }
2581     AlignmentPanel ap = null;
2582     boolean isnewview = true;
2583     if (viewId != null)
2584     {
2585       // Check to see if this alignment already has a view id == viewId
2586       jalview.gui.AlignmentPanel views[] = Desktop
2587               .getAlignmentPanels(uniqueSeqSetId);
2588       if (views != null && views.length > 0)
2589       {
2590         for (int v = 0; v < views.length; v++)
2591         {
2592           if (views[v].av.getViewId().equalsIgnoreCase(viewId))
2593           {
2594             // recover the existing alignpanel, alignframe, viewport
2595             af = views[v].alignFrame;
2596             av = views[v].av;
2597             ap = views[v];
2598             // TODO: could even skip resetting view settings if we don't want to
2599             // change the local settings from other jalview processes
2600             isnewview = false;
2601           }
2602         }
2603       }
2604     }
2605
2606     if (isnewview)
2607     {
2608       af = loadViewport(file, JSEQ, hiddenSeqs, al, jms, view,
2609               uniqueSeqSetId, viewId, autoAlan);
2610       av = af.viewport;
2611       ap = af.alignPanel;
2612     }
2613     // LOAD TREES
2614     // /////////////////////////////////////
2615     if (loadTreesAndStructures && jms.getTreeCount() > 0)
2616     {
2617       try
2618       {
2619         for (int t = 0; t < jms.getTreeCount(); t++)
2620         {
2621
2622           Tree tree = jms.getTree(t);
2623
2624           TreePanel tp = (TreePanel) retrieveExistingObj(tree.getId());
2625           if (tp == null)
2626           {
2627             tp = af.ShowNewickTree(
2628                     new jalview.io.NewickFile(tree.getNewick()),
2629                     tree.getTitle(), tree.getWidth(), tree.getHeight(),
2630                     tree.getXpos(), tree.getYpos());
2631             if (tree.getId() != null)
2632             {
2633               // perhaps bind the tree id to something ?
2634             }
2635           }
2636           else
2637           {
2638             // update local tree attributes ?
2639             // TODO: should check if tp has been manipulated by user - if so its
2640             // settings shouldn't be modified
2641             tp.setTitle(tree.getTitle());
2642             tp.setBounds(new Rectangle(tree.getXpos(), tree.getYpos(), tree
2643                     .getWidth(), tree.getHeight()));
2644             tp.av = av; // af.viewport; // TODO: verify 'associate with all
2645             // views'
2646             // works still
2647             tp.treeCanvas.av = av; // af.viewport;
2648             tp.treeCanvas.ap = ap; // af.alignPanel;
2649
2650           }
2651           if (tp == null)
2652           {
2653             warn("There was a problem recovering stored Newick tree: \n"
2654                     + tree.getNewick());
2655             continue;
2656           }
2657
2658           tp.fitToWindow.setState(tree.getFitToWindow());
2659           tp.fitToWindow_actionPerformed(null);
2660
2661           if (tree.getFontName() != null)
2662           {
2663             tp.setTreeFont(new java.awt.Font(tree.getFontName(), tree
2664                     .getFontStyle(), tree.getFontSize()));
2665           }
2666           else
2667           {
2668             tp.setTreeFont(new java.awt.Font(view.getFontName(), view
2669                     .getFontStyle(), tree.getFontSize()));
2670           }
2671
2672           tp.showPlaceholders(tree.getMarkUnlinked());
2673           tp.showBootstrap(tree.getShowBootstrap());
2674           tp.showDistances(tree.getShowDistances());
2675
2676           tp.treeCanvas.threshold = tree.getThreshold();
2677
2678           if (tree.getCurrentTree())
2679           {
2680             af.viewport.setCurrentTree(tp.getTree());
2681           }
2682         }
2683
2684       } catch (Exception ex)
2685       {
2686         ex.printStackTrace();
2687       }
2688     }
2689
2690     // //LOAD STRUCTURES
2691     if (loadTreesAndStructures)
2692     {
2693       // run through all PDB ids on the alignment, and collect mappings between
2694       // jmol view ids and all sequences referring to it
2695       Hashtable<String, Object[]> jmolViewIds = new Hashtable();
2696
2697       for (int i = 0; i < JSEQ.length; i++)
2698       {
2699         if (JSEQ[i].getPdbidsCount() > 0)
2700         {
2701           Pdbids[] ids = JSEQ[i].getPdbids();
2702           for (int p = 0; p < ids.length; p++)
2703           {
2704             for (int s = 0; s < ids[p].getStructureStateCount(); s++)
2705             {
2706               // check to see if we haven't already created this structure view
2707               String sviewid = (ids[p].getStructureState(s).getViewId() == null) ? null
2708                       : ids[p].getStructureState(s).getViewId()
2709                               + uniqueSetSuffix;
2710               jalview.datamodel.PDBEntry jpdb = new jalview.datamodel.PDBEntry();
2711               // Originally : ids[p].getFile()
2712               // : TODO: verify external PDB file recovery still works in normal
2713               // jalview project load
2714               jpdb.setFile(loadPDBFile(jprovider, ids[p].getId()));
2715               jpdb.setId(ids[p].getId());
2716
2717               int x = ids[p].getStructureState(s).getXpos();
2718               int y = ids[p].getStructureState(s).getYpos();
2719               int width = ids[p].getStructureState(s).getWidth();
2720               int height = ids[p].getStructureState(s).getHeight();
2721
2722               // Probably don't need to do this anymore...
2723               // Desktop.desktop.getComponentAt(x, y);
2724               // TODO: NOW: check that this recovers the PDB file correctly.
2725               String pdbFile = loadPDBFile(jprovider, ids[p].getId());
2726               jalview.datamodel.SequenceI seq = (jalview.datamodel.SequenceI) seqRefIds
2727                       .get(JSEQ[i].getId() + "");
2728               if (sviewid == null)
2729               {
2730                 sviewid = "_jalview_pre2_4_" + x + "," + y + "," + width
2731                         + "," + height;
2732               }
2733               if (!jmolViewIds.containsKey(sviewid))
2734               {
2735                 jmolViewIds.put(sviewid, new Object[]
2736                 { new int[]
2737                 { x, y, width, height }, "",
2738                     new Hashtable<String, Object[]>(), new boolean[]
2739                     { false, false, true } });
2740                 // Legacy pre-2.7 conversion JAL-823 :
2741                 // do not assume any view has to be linked for colour by
2742                 // sequence
2743               }
2744
2745               // assemble String[] { pdb files }, String[] { id for each
2746               // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
2747               // seqs_file 2}, boolean[] {
2748               // linkAlignPanel,superposeWithAlignpanel}} from hash
2749               Object[] jmoldat = jmolViewIds.get(sviewid);
2750               ((boolean[]) jmoldat[3])[0] |= ids[p].getStructureState(s)
2751                       .hasAlignwithAlignPanel() ? ids[p].getStructureState(
2752                       s).getAlignwithAlignPanel() : false;
2753               // never colour by linked panel if not specified
2754               ((boolean[]) jmoldat[3])[1] |= ids[p].getStructureState(s)
2755                       .hasColourwithAlignPanel() ? ids[p]
2756                       .getStructureState(s).getColourwithAlignPanel()
2757                       : false;
2758               // default for pre-2.7 projects is that Jmol colouring is enabled
2759               ((boolean[]) jmoldat[3])[2] &= ids[p].getStructureState(s)
2760                       .hasColourByJmol() ? ids[p].getStructureState(s)
2761                       .getColourByJmol() : true;
2762
2763               if (((String) jmoldat[1]).length() < ids[p]
2764                       .getStructureState(s).getContent().length())
2765               {
2766                 {
2767                   jmoldat[1] = ids[p].getStructureState(s).getContent();
2768                 }
2769               }
2770               if (ids[p].getFile() != null)
2771               {
2772                 File mapkey = new File(ids[p].getFile());
2773                 Object[] seqstrmaps = (Object[]) ((Hashtable) jmoldat[2])
2774                         .get(mapkey);
2775                 if (seqstrmaps == null)
2776                 {
2777                   ((Hashtable) jmoldat[2]).put(mapkey,
2778                           seqstrmaps = new Object[]
2779                           { pdbFile, ids[p].getId(), new Vector(),
2780                               new Vector() });
2781                 }
2782                 if (!((Vector) seqstrmaps[2]).contains(seq))
2783                 {
2784                   ((Vector) seqstrmaps[2]).addElement(seq);
2785                   // ((Vector)seqstrmaps[3]).addElement(n) :
2786                   // in principle, chains
2787                   // should be stored here : do we need to
2788                   // TODO: store and recover seq/pdb_id :
2789                   // chain mappings
2790                 }
2791               }
2792               else
2793               {
2794                 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");
2795                 warn(errorMessage);
2796               }
2797             }
2798           }
2799         }
2800       }
2801       {
2802
2803         // Instantiate the associated Jmol views
2804         for (Entry<String, Object[]> entry : jmolViewIds.entrySet())
2805         {
2806           String sviewid = entry.getKey();
2807           Object[] svattrib = entry.getValue();
2808           int[] geom = (int[]) svattrib[0];
2809           String state = (String) svattrib[1];
2810           Hashtable<File, Object[]> oldFiles = (Hashtable<File, Object[]>) svattrib[2];
2811           final boolean useinJmolsuperpos = ((boolean[]) svattrib[3])[0], usetoColourbyseq = ((boolean[]) svattrib[3])[1], jmolColouring = ((boolean[]) svattrib[3])[2];
2812           int x = geom[0], y = geom[1], width = geom[2], height = geom[3];
2813           // collate the pdbfile -> sequence mappings from this view
2814           Vector<String> pdbfilenames = new Vector<String>();
2815           Vector<SequenceI[]> seqmaps = new Vector<SequenceI[]>();
2816           Vector<String> pdbids = new Vector<String>();
2817
2818           // Search to see if we've already created this Jmol view
2819           AppJmol comp = null;
2820           JInternalFrame[] frames = null;
2821           do
2822           {
2823             try
2824             {
2825               frames = Desktop.desktop.getAllFrames();
2826             } catch (ArrayIndexOutOfBoundsException e)
2827             {
2828               // occasional No such child exceptions are thrown here...
2829               frames = null;
2830               try
2831               {
2832                 Thread.sleep(10);
2833               } catch (Exception f)
2834               {
2835               }
2836               ;
2837             }
2838           } while (frames == null);
2839           // search for any Jmol windows already open from other
2840           // alignment views that exactly match the stored structure state
2841           for (int f = 0; comp == null && f < frames.length; f++)
2842           {
2843             if (frames[f] instanceof AppJmol)
2844             {
2845               if (sviewid != null
2846                       && ((AppJmol) frames[f]).getViewId().equals(sviewid))
2847               {
2848                 // post jalview 2.4 schema includes structure view id
2849                 comp = (AppJmol) frames[f];
2850               }
2851               else if (frames[f].getX() == x && frames[f].getY() == y
2852                       && frames[f].getHeight() == height
2853                       && frames[f].getWidth() == width)
2854               {
2855                 comp = (AppJmol) frames[f];
2856               }
2857             }
2858           }
2859
2860           if (comp == null)
2861           {
2862             // create a new Jmol window.
2863             // First parse the Jmol state to translate filenames loaded into the
2864             // view, and record the order in which files are shown in the Jmol
2865             // view, so we can add the sequence mappings in same order.
2866             StringBuffer newFileLoc = null;
2867             int cp = 0, ncp, ecp;
2868             while ((ncp = state.indexOf("load ", cp)) > -1)
2869             {
2870               if (newFileLoc == null)
2871               {
2872                 newFileLoc = new StringBuffer();
2873               }
2874               do
2875               {
2876                 // look for next filename in load statement
2877                 newFileLoc.append(state.substring(cp,
2878                         ncp = (state.indexOf("\"", ncp + 1) + 1)));
2879                 String oldfilenam = state.substring(ncp,
2880                         ecp = state.indexOf("\"", ncp));
2881                 // recover the new mapping data for this old filename
2882                 // have to normalize filename - since Jmol and jalview do
2883                 // filename
2884                 // translation differently.
2885                 Object[] filedat = oldFiles.get(new File(oldfilenam));
2886                 newFileLoc.append(Platform
2887                         .escapeString((String) filedat[0]));
2888                 pdbfilenames.addElement((String) filedat[0]);
2889                 pdbids.addElement((String) filedat[1]);
2890                 seqmaps.addElement(((Vector<SequenceI>) filedat[2])
2891                         .toArray(new SequenceI[0]));
2892                 newFileLoc.append("\"");
2893                 cp = ecp + 1; // advance beyond last \" and set cursor so we can
2894                               // look for next file statement.
2895               } while ((ncp = state.indexOf("/*file*/", cp)) > -1);
2896             }
2897             if (cp > 0)
2898             {
2899               // just append rest of state
2900               newFileLoc.append(state.substring(cp));
2901             }
2902             else
2903             {
2904               System.err
2905                       .print("Ignoring incomplete Jmol state for PDB ids: ");
2906               newFileLoc = new StringBuffer(state);
2907               newFileLoc.append("; load append ");
2908               for (File id : oldFiles.keySet())
2909               {
2910                 // add this and any other pdb files that should be present in
2911                 // the viewer
2912                 Object[] filedat = oldFiles.get(id);
2913                 String nfilename;
2914                 newFileLoc.append(((String) filedat[0]));
2915                 pdbfilenames.addElement((String) filedat[0]);
2916                 pdbids.addElement((String) filedat[1]);
2917                 seqmaps.addElement(((Vector<SequenceI>) filedat[2])
2918                         .toArray(new SequenceI[0]));
2919                 newFileLoc.append(" \"");
2920                 newFileLoc.append((String) filedat[0]);
2921                 newFileLoc.append("\"");
2922
2923               }
2924               newFileLoc.append(";");
2925             }
2926
2927             if (newFileLoc != null)
2928             {
2929               int histbug = newFileLoc.indexOf("history = ");
2930               histbug += 10;
2931               int diff = histbug == -1 ? -1 : newFileLoc.indexOf(";",
2932                       histbug);
2933               String val = (diff == -1) ? null : newFileLoc.substring(
2934                       histbug, diff);
2935               if (val != null && val.length() >= 4)
2936               {
2937                 if (val.contains("e"))
2938                 {
2939                   if (val.trim().equals("true"))
2940                   {
2941                     val = "1";
2942                   }
2943                   else
2944                   {
2945                     val = "0";
2946                   }
2947                   newFileLoc.replace(histbug, diff, val);
2948                 }
2949               }
2950               // TODO: assemble String[] { pdb files }, String[] { id for each
2951               // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
2952               // seqs_file 2}} from hash
2953               final String[] pdbf = pdbfilenames
2954                       .toArray(new String[pdbfilenames.size()]), id = pdbids
2955                       .toArray(new String[pdbids.size()]);
2956               final SequenceI[][] sq = seqmaps
2957                       .toArray(new SequenceI[seqmaps.size()][]);
2958               final String fileloc = newFileLoc.toString(), vid = sviewid;
2959               final AlignFrame alf = af;
2960               final java.awt.Rectangle rect = new java.awt.Rectangle(x, y,
2961                       width, height);
2962               try
2963               {
2964                 javax.swing.SwingUtilities.invokeAndWait(new Runnable()
2965                 {
2966                   @Override
2967                   public void run()
2968                   {
2969                     AppJmol sview = null;
2970                     try
2971                     {
2972                       sview = new AppJmol(pdbf, id, sq, alf.alignPanel,
2973                               useinJmolsuperpos, usetoColourbyseq,
2974                               jmolColouring, fileloc, rect, vid);
2975                       addNewStructureViewer(sview);
2976                     } catch (OutOfMemoryError ex)
2977                     {
2978                       new OOMWarning("restoring structure view for PDB id "
2979                               + id, (OutOfMemoryError) ex.getCause());
2980                       if (sview != null && sview.isVisible())
2981                       {
2982                         sview.closeViewer();
2983                         sview.setVisible(false);
2984                         sview.dispose();
2985                       }
2986                     }
2987                   }
2988                 });
2989               } catch (InvocationTargetException ex)
2990               {
2991                 warn("Unexpected error when opening Jmol view.", ex);
2992
2993               } catch (InterruptedException e)
2994               {
2995                 // e.printStackTrace();
2996               }
2997             }
2998
2999           }
3000           else
3001           // if (comp != null)
3002           {
3003             // NOTE: if the jalview project is part of a shared session then
3004             // view synchronization should/could be done here.
3005
3006             // add mapping for sequences in this view to an already open Jmol
3007             // instance
3008             for (File id : oldFiles.keySet())
3009             {
3010               // add this and any other pdb files that should be present in the
3011               // viewer
3012               Object[] filedat = oldFiles.get(id);
3013               String pdbFile = (String) filedat[0];
3014               SequenceI[] seq = ((Vector<SequenceI>) filedat[2])
3015                       .toArray(new SequenceI[0]);
3016               comp.jmb.ssm.setMapping(seq, null, pdbFile,
3017                       jalview.io.AppletFormatAdapter.FILE);
3018               comp.jmb.addSequenceForStructFile(pdbFile, seq);
3019             }
3020             // and add the AlignmentPanel's reference to the Jmol view
3021             comp.addAlignmentPanel(ap);
3022             if (useinJmolsuperpos)
3023             {
3024               comp.useAlignmentPanelForSuperposition(ap);
3025             }
3026             else
3027             {
3028               comp.excludeAlignmentPanelForSuperposition(ap);
3029             }
3030             if (usetoColourbyseq)
3031             {
3032               comp.useAlignmentPanelForColourbyseq(ap, !jmolColouring);
3033             }
3034             else
3035             {
3036               comp.excludeAlignmentPanelForColourbyseq(ap);
3037             }
3038           }
3039         }
3040       }
3041     }
3042     // and finally return.
3043     return af;
3044   }
3045   Vector<AppJmol> newStructureViewers=null;
3046   protected void addNewStructureViewer(AppJmol sview)
3047   {
3048     if (newStructureViewers!=null)
3049     {
3050       sview.jmb.setFinishedLoadingFromArchive(false);
3051       newStructureViewers.add(sview);
3052     }
3053   }
3054   protected void setLoadingFinishedForNewStructureViewers()
3055   {
3056     if (newStructureViewers!=null)
3057     {
3058       for (AppJmol sview:newStructureViewers)
3059       {
3060         sview.jmb.setFinishedLoadingFromArchive(true);
3061       }
3062       newStructureViewers.clear();
3063       newStructureViewers=null;
3064     }
3065   }
3066
3067   AlignFrame loadViewport(String file, JSeq[] JSEQ, Vector hiddenSeqs,
3068           Alignment al, JalviewModelSequence jms, Viewport view,
3069           String uniqueSeqSetId, String viewId,
3070           ArrayList<JvAnnotRow> autoAlan)
3071   {
3072     AlignFrame af = null;
3073     af = new AlignFrame(al, view.getWidth(), view.getHeight(),
3074             uniqueSeqSetId, viewId);
3075
3076     af.setFileName(file, "Jalview");
3077
3078     for (int i = 0; i < JSEQ.length; i++)
3079     {
3080       af.viewport.setSequenceColour(af.viewport.getAlignment()
3081               .getSequenceAt(i), new java.awt.Color(JSEQ[i].getColour()));
3082     }
3083
3084     af.viewport.gatherViewsHere = view.getGatheredViews();
3085
3086     if (view.getSequenceSetId() != null)
3087     {
3088       jalview.gui.AlignViewport av = (jalview.gui.AlignViewport) viewportsAdded
3089               .get(uniqueSeqSetId);
3090
3091       af.viewport.setSequenceSetId(uniqueSeqSetId);
3092       if (av != null)
3093       {
3094         // propagate shared settings to this new view
3095         af.viewport.historyList = av.historyList;
3096         af.viewport.redoList = av.redoList;
3097       }
3098       else
3099       {
3100         viewportsAdded.put(uniqueSeqSetId, af.viewport);
3101       }
3102       // TODO: check if this method can be called repeatedly without
3103       // side-effects if alignpanel already registered.
3104       PaintRefresher.Register(af.alignPanel, uniqueSeqSetId);
3105     }
3106     // apply Hidden regions to view.
3107     if (hiddenSeqs != null)
3108     {
3109       for (int s = 0; s < JSEQ.length; s++)
3110       {
3111         jalview.datamodel.SequenceGroup hidden = new jalview.datamodel.SequenceGroup();
3112
3113         for (int r = 0; r < JSEQ[s].getHiddenSequencesCount(); r++)
3114         {
3115           hidden.addSequence(
3116                   al.getSequenceAt(JSEQ[s].getHiddenSequences(r)), false);
3117         }
3118         af.viewport.hideRepSequences(al.getSequenceAt(s), hidden);
3119       }
3120
3121       jalview.datamodel.SequenceI[] hseqs = new jalview.datamodel.SequenceI[hiddenSeqs
3122               .size()];
3123
3124       for (int s = 0; s < hiddenSeqs.size(); s++)
3125       {
3126         hseqs[s] = (jalview.datamodel.SequenceI) hiddenSeqs.elementAt(s);
3127       }
3128
3129       af.viewport.hideSequence(hseqs);
3130
3131     }
3132     // recover view properties and display parameters
3133     if (view.getViewName() != null)
3134     {
3135       af.viewport.viewName = view.getViewName();
3136       af.setInitialTabVisible();
3137     }
3138     af.setBounds(view.getXpos(), view.getYpos(), view.getWidth(),
3139             view.getHeight());
3140
3141     af.viewport.setShowAnnotation(view.getShowAnnotation());
3142     af.viewport.setAbovePIDThreshold(view.getPidSelected());
3143
3144     af.viewport.setColourText(view.getShowColourText());
3145
3146     af.viewport.setConservationSelected(view.getConservationSelected());
3147     af.viewport.setShowJVSuffix(view.getShowFullId());
3148     af.viewport.rightAlignIds = view.getRightAlignIds();
3149     af.viewport.setFont(new java.awt.Font(view.getFontName(), view
3150             .getFontStyle(), view.getFontSize()));
3151     af.alignPanel.fontChanged();
3152     af.viewport.setRenderGaps(view.getRenderGaps());
3153     af.viewport.setWrapAlignment(view.getWrapAlignment());
3154     af.alignPanel.setWrapAlignment(view.getWrapAlignment());
3155     af.viewport.setShowAnnotation(view.getShowAnnotation());
3156     af.alignPanel.setAnnotationVisible(view.getShowAnnotation());
3157
3158     af.viewport.setShowBoxes(view.getShowBoxes());
3159
3160     af.viewport.setShowText(view.getShowText());
3161
3162     af.viewport.textColour = new java.awt.Color(view.getTextCol1());
3163     af.viewport.textColour2 = new java.awt.Color(view.getTextCol2());
3164     af.viewport.thresholdTextColour = view.getTextColThreshold();
3165     af.viewport.setShowUnconserved(view.hasShowUnconserved() ? view
3166             .isShowUnconserved() : false);
3167     af.viewport.setStartRes(view.getStartRes());
3168     af.viewport.setStartSeq(view.getStartSeq());
3169
3170     ColourSchemeI cs = null;
3171     // apply colourschemes
3172     if (view.getBgColour() != null)
3173     {
3174       if (view.getBgColour().startsWith("ucs"))
3175       {
3176         cs = GetUserColourScheme(jms, view.getBgColour());
3177       }
3178       else if (view.getBgColour().startsWith("Annotation"))
3179       {
3180         // int find annotation
3181         if (af.viewport.getAlignment().getAlignmentAnnotation() != null)
3182         {
3183           for (int i = 0; i < af.viewport.getAlignment()
3184                   .getAlignmentAnnotation().length; i++)
3185           {
3186             if (af.viewport.getAlignment().getAlignmentAnnotation()[i].label
3187                     .equals(view.getAnnotationColours().getAnnotation()))
3188             {
3189               if (af.viewport.getAlignment().getAlignmentAnnotation()[i]
3190                       .getThreshold() == null)
3191               {
3192                 af.viewport.getAlignment().getAlignmentAnnotation()[i]
3193                         .setThreshold(new jalview.datamodel.GraphLine(view
3194                                 .getAnnotationColours().getThreshold(),
3195                                 "Threshold", java.awt.Color.black)
3196
3197                         );
3198               }
3199
3200               if (view.getAnnotationColours().getColourScheme()
3201                       .equals("None"))
3202               {
3203                 cs = new AnnotationColourGradient(af.viewport
3204                         .getAlignment().getAlignmentAnnotation()[i],
3205                         new java.awt.Color(view.getAnnotationColours()
3206                                 .getMinColour()), new java.awt.Color(view
3207                                 .getAnnotationColours().getMaxColour()),
3208                         view.getAnnotationColours().getAboveThreshold());
3209               }
3210               else if (view.getAnnotationColours().getColourScheme()
3211                       .startsWith("ucs"))
3212               {
3213                 cs = new AnnotationColourGradient(af.viewport
3214                         .getAlignment().getAlignmentAnnotation()[i],
3215                         GetUserColourScheme(jms, view
3216                                 .getAnnotationColours().getColourScheme()),
3217                         view.getAnnotationColours().getAboveThreshold());
3218               }
3219               else
3220               {
3221                 cs = new AnnotationColourGradient(af.viewport
3222                         .getAlignment().getAlignmentAnnotation()[i],
3223                         ColourSchemeProperty.getColour(al, view
3224                                 .getAnnotationColours().getColourScheme()),
3225                         view.getAnnotationColours().getAboveThreshold());
3226               }
3227
3228               // Also use these settings for all the groups
3229               if (al.getGroups() != null)
3230               {
3231                 for (int g = 0; g < al.getGroups().size(); g++)
3232                 {
3233                   jalview.datamodel.SequenceGroup sg = al.getGroups()
3234                           .get(g);
3235
3236                   if (sg.cs == null)
3237                   {
3238                     continue;
3239                   }
3240
3241                   /*
3242                    * if
3243                    * (view.getAnnotationColours().getColourScheme().equals("None"
3244                    * )) { sg.cs = new AnnotationColourGradient(
3245                    * af.viewport.getAlignment().getAlignmentAnnotation()[i], new
3246                    * java.awt.Color(view.getAnnotationColours().
3247                    * getMinColour()), new
3248                    * java.awt.Color(view.getAnnotationColours().
3249                    * getMaxColour()),
3250                    * view.getAnnotationColours().getAboveThreshold()); } else
3251                    */
3252                   {
3253                     sg.cs = new AnnotationColourGradient(af.viewport
3254                             .getAlignment().getAlignmentAnnotation()[i],
3255                             sg.cs, view.getAnnotationColours()
3256                                     .getAboveThreshold());
3257                   }
3258
3259                 }
3260               }
3261
3262               break;
3263             }
3264
3265           }
3266         }
3267       }
3268       else
3269       {
3270         cs = ColourSchemeProperty.getColour(al, view.getBgColour());
3271       }
3272
3273       if (cs != null)
3274       {
3275         cs.setThreshold(view.getPidThreshold(), true);
3276         cs.setConsensus(af.viewport.getSequenceConsensusHash());
3277       }
3278     }
3279
3280     af.viewport.setGlobalColourScheme(cs);
3281     af.viewport.setColourAppliesToAllGroups(false);
3282
3283     if (view.getConservationSelected() && cs != null)
3284     {
3285       cs.setConservationInc(view.getConsThreshold());
3286     }
3287
3288     af.changeColour(cs);
3289
3290     af.viewport.setColourAppliesToAllGroups(true);
3291
3292     if (view.getShowSequenceFeatures())
3293     {
3294       af.viewport.showSequenceFeatures = true;
3295     }
3296     if (view.hasCentreColumnLabels())
3297     {
3298       af.viewport.setCentreColumnLabels(view.getCentreColumnLabels());
3299     }
3300     if (view.hasIgnoreGapsinConsensus())
3301     {
3302       af.viewport.setIgnoreGapsConsensus(view.getIgnoreGapsinConsensus(),
3303               null);
3304     }
3305     if (view.hasFollowHighlight())
3306     {
3307       af.viewport.followHighlight = view.getFollowHighlight();
3308     }
3309     if (view.hasFollowSelection())
3310     {
3311       af.viewport.followSelection = view.getFollowSelection();
3312     }
3313     if (view.hasShowConsensusHistogram())
3314     {
3315       af.viewport.setShowConsensusHistogram(view
3316               .getShowConsensusHistogram());
3317     }
3318     else
3319     {
3320       af.viewport.setShowConsensusHistogram(true);
3321     }
3322     if (view.hasShowSequenceLogo())
3323     {
3324       af.viewport.setShowSequenceLogo(view.getShowSequenceLogo());
3325     }
3326     else
3327     {
3328       af.viewport.setShowSequenceLogo(false);
3329     }
3330     if (view.hasNormaliseSequenceLogo())
3331     {
3332       af.viewport.setNormaliseSequenceLogo(view.getNormaliseSequenceLogo());
3333     }
3334     if (view.hasShowDbRefTooltip())
3335     {
3336       af.viewport.setShowDbRefs(view.getShowDbRefTooltip());
3337     }
3338     if (view.hasShowNPfeatureTooltip())
3339     {
3340       af.viewport.setShowNpFeats(view.hasShowNPfeatureTooltip());
3341     }
3342     if (view.hasShowGroupConsensus())
3343     {
3344       af.viewport.setShowGroupConsensus(view.getShowGroupConsensus());
3345     }
3346     else
3347     {
3348       af.viewport.setShowGroupConsensus(false);
3349     }
3350     if (view.hasShowGroupConservation())
3351     {
3352       af.viewport.setShowGroupConservation(view.getShowGroupConservation());
3353     }
3354     else
3355     {
3356       af.viewport.setShowGroupConservation(false);
3357     }
3358
3359     // recover featre settings
3360     if (jms.getFeatureSettings() != null)
3361     {
3362       af.viewport.featuresDisplayed = new Hashtable();
3363       String[] renderOrder = new String[jms.getFeatureSettings()
3364               .getSettingCount()];
3365       for (int fs = 0; fs < jms.getFeatureSettings().getSettingCount(); fs++)
3366       {
3367         Setting setting = jms.getFeatureSettings().getSetting(fs);
3368         if (setting.hasMincolour())
3369         {
3370           GraduatedColor gc = setting.hasMin() ? new GraduatedColor(
3371                   new java.awt.Color(setting.getMincolour()),
3372                   new java.awt.Color(setting.getColour()),
3373                   setting.getMin(), setting.getMax()) : new GraduatedColor(
3374                   new java.awt.Color(setting.getMincolour()),
3375                   new java.awt.Color(setting.getColour()), 0, 1);
3376           if (setting.hasThreshold())
3377           {
3378             gc.setThresh(setting.getThreshold());
3379             gc.setThreshType(setting.getThreshstate());
3380           }
3381           gc.setAutoScaled(true); // default
3382           if (setting.hasAutoScale())
3383           {
3384             gc.setAutoScaled(setting.getAutoScale());
3385           }
3386           if (setting.hasColourByLabel())
3387           {
3388             gc.setColourByLabel(setting.getColourByLabel());
3389           }
3390           // and put in the feature colour table.
3391           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setColour(
3392                   setting.getType(), gc);
3393         }
3394         else
3395         {
3396           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setColour(
3397                   setting.getType(),
3398                   new java.awt.Color(setting.getColour()));
3399         }
3400         renderOrder[fs] = setting.getType();
3401         if (setting.hasOrder())
3402           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setOrder(
3403                   setting.getType(), setting.getOrder());
3404         else
3405           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setOrder(
3406                   setting.getType(),
3407                   fs / jms.getFeatureSettings().getSettingCount());
3408         if (setting.getDisplay())
3409         {
3410           af.viewport.featuresDisplayed.put(setting.getType(), new Integer(
3411                   setting.getColour()));
3412         }
3413       }
3414       af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().renderOrder = renderOrder;
3415       Hashtable fgtable;
3416       af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().featureGroups = fgtable = new Hashtable();
3417       for (int gs = 0; gs < jms.getFeatureSettings().getGroupCount(); gs++)
3418       {
3419         Group grp = jms.getFeatureSettings().getGroup(gs);
3420         fgtable.put(grp.getName(), new Boolean(grp.getDisplay()));
3421       }
3422     }
3423
3424     if (view.getHiddenColumnsCount() > 0)
3425     {
3426       for (int c = 0; c < view.getHiddenColumnsCount(); c++)
3427       {
3428         af.viewport.hideColumns(view.getHiddenColumns(c).getStart(), view
3429                 .getHiddenColumns(c).getEnd() // +1
3430                 );
3431       }
3432     }
3433     if (view.getCalcIdParam() != null)
3434     {
3435       for (CalcIdParam calcIdParam : view.getCalcIdParam())
3436       {
3437         if (calcIdParam != null)
3438         {
3439           if (recoverCalcIdParam(calcIdParam, af.viewport))
3440           {
3441           }
3442           else
3443           {
3444             warn("Couldn't recover parameters for "
3445                     + calcIdParam.getCalcId());
3446           }
3447         }
3448       }
3449     }
3450     af.setMenusFromViewport(af.viewport);
3451     // TODO: we don't need to do this if the viewport is aready visible.
3452     Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(),
3453             view.getHeight());
3454     af.alignPanel.updateAnnotation(false, true); // recompute any autoannotation
3455     reorderAutoannotation(af, al, autoAlan);
3456     return af;
3457   }
3458
3459   private void reorderAutoannotation(AlignFrame af, Alignment al,
3460           ArrayList<JvAnnotRow> autoAlan)
3461   {
3462     // copy over visualization settings for autocalculated annotation in the
3463     // view
3464     if (al.getAlignmentAnnotation() != null)
3465     {
3466       /**
3467        * Kludge for magic autoannotation names (see JAL-811)
3468        */
3469       String[] magicNames = new String[]
3470       { "Consensus", "Quality", "Conservation" };
3471       JvAnnotRow nullAnnot = new JvAnnotRow(-1, null);
3472       Hashtable<String, JvAnnotRow> visan = new Hashtable<String, JvAnnotRow>();
3473       for (String nm : magicNames)
3474       {
3475         visan.put(nm, nullAnnot);
3476       }
3477       for (JvAnnotRow auan : autoAlan)
3478       {
3479         visan.put(auan.template.label
3480                 + (auan.template.getCalcId() == null ? "" : "\t"
3481                         + auan.template.getCalcId()), auan);
3482       }
3483       int hSize = al.getAlignmentAnnotation().length;
3484       ArrayList<JvAnnotRow> reorder = new ArrayList<JvAnnotRow>();
3485       // work through any autoCalculated annotation already on the view
3486       // removing it if it should be placed in a different location on the
3487       // annotation panel.
3488       List<String> remains = new ArrayList(visan.keySet());
3489       for (int h = 0; h < hSize; h++)
3490       {
3491         jalview.datamodel.AlignmentAnnotation jalan = al
3492                 .getAlignmentAnnotation()[h];
3493         if (jalan.autoCalculated)
3494         {
3495           String k;
3496           JvAnnotRow valan = visan.get(k = jalan.label);
3497           if (jalan.getCalcId() != null)
3498           {
3499             valan = visan.get(k = jalan.label + "\t" + jalan.getCalcId());
3500           }
3501
3502           if (valan != null)
3503           {
3504             // delete the auto calculated row from the alignment
3505             al.deleteAnnotation(jalan, false);
3506             remains.remove(k);
3507             hSize--;
3508             h--;
3509             if (valan != nullAnnot)
3510             {
3511               if (jalan != valan.template)
3512               {
3513                 // newly created autoannotation row instance
3514                 // so keep a reference to the visible annotation row
3515                 // and copy over all relevant attributes
3516                 if (valan.template.graphHeight >= 0)
3517
3518                 {
3519                   jalan.graphHeight = valan.template.graphHeight;
3520                 }
3521                 jalan.visible = valan.template.visible;
3522               }
3523               reorder.add(new JvAnnotRow(valan.order, jalan));
3524             }
3525           }
3526         }
3527       }
3528       // Add any (possibly stale) autocalculated rows that were not appended to
3529       // the view during construction
3530       for (String other : remains)
3531       {
3532         JvAnnotRow othera = visan.get(other);
3533         if (othera != nullAnnot && othera.template.getCalcId() != null
3534                 && othera.template.getCalcId().length() > 0)
3535         {
3536           reorder.add(othera);
3537         }
3538       }
3539       // now put the automatic annotation in its correct place
3540       int s = 0, srt[] = new int[reorder.size()];
3541       JvAnnotRow[] rws = new JvAnnotRow[reorder.size()];
3542       for (JvAnnotRow jvar : reorder)
3543       {
3544         rws[s] = jvar;
3545         srt[s++] = jvar.order;
3546       }
3547       reorder.clear();
3548       jalview.util.QuickSort.sort(srt, rws);
3549       // and re-insert the annotation at its correct position
3550       for (JvAnnotRow jvar : rws)
3551       {
3552         al.addAnnotation(jvar.template, jvar.order);
3553       }
3554       af.alignPanel.adjustAnnotationHeight();
3555     }
3556   }
3557
3558   Hashtable skipList = null;
3559
3560   /**
3561    * TODO remove this method
3562    * 
3563    * @param view
3564    * @return AlignFrame bound to sequenceSetId from view, if one exists. private
3565    *         AlignFrame getSkippedFrame(Viewport view) { if (skipList==null) {
3566    *         throw new Error("Implementation Error. No skipList defined for this
3567    *         Jalview2XML instance."); } return (AlignFrame)
3568    *         skipList.get(view.getSequenceSetId()); }
3569    */
3570
3571   /**
3572    * Check if the Jalview view contained in object should be skipped or not.
3573    * 
3574    * @param object
3575    * @return true if view's sequenceSetId is a key in skipList
3576    */
3577   private boolean skipViewport(JalviewModel object)
3578   {
3579     if (skipList == null)
3580     {
3581       return false;
3582     }
3583     String id;
3584     if (skipList.containsKey(id = object.getJalviewModelSequence()
3585             .getViewport()[0].getSequenceSetId()))
3586     {
3587       if (Cache.log != null && Cache.log.isDebugEnabled())
3588       {
3589         Cache.log.debug("Skipping seuqence set id " + id);
3590       }
3591       return true;
3592     }
3593     return false;
3594   }
3595
3596   public void AddToSkipList(AlignFrame af)
3597   {
3598     if (skipList == null)
3599     {
3600       skipList = new Hashtable();
3601     }
3602     skipList.put(af.getViewport().getSequenceSetId(), af);
3603   }
3604
3605   public void clearSkipList()
3606   {
3607     if (skipList != null)
3608     {
3609       skipList.clear();
3610       skipList = null;
3611     }
3612   }
3613
3614   private void recoverDatasetFor(SequenceSet vamsasSet, Alignment al)
3615   {
3616     jalview.datamodel.Alignment ds = getDatasetFor(vamsasSet.getDatasetId());
3617     Vector dseqs = null;
3618     if (ds == null)
3619     {
3620       // create a list of new dataset sequences
3621       dseqs = new Vector();
3622     }
3623     for (int i = 0, iSize = vamsasSet.getSequenceCount(); i < iSize; i++)
3624     {
3625       Sequence vamsasSeq = vamsasSet.getSequence(i);
3626       ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs);
3627     }
3628     // create a new dataset
3629     if (ds == null)
3630     {
3631       SequenceI[] dsseqs = new SequenceI[dseqs.size()];
3632       dseqs.copyInto(dsseqs);
3633       ds = new jalview.datamodel.Alignment(dsseqs);
3634       debug("Created new dataset " + vamsasSet.getDatasetId()
3635               + " for alignment " + System.identityHashCode(al));
3636       addDatasetRef(vamsasSet.getDatasetId(), ds);
3637     }
3638     // set the dataset for the newly imported alignment.
3639     if (al.getDataset() == null)
3640     {
3641       al.setDataset(ds);
3642     }
3643   }
3644
3645   /**
3646    * 
3647    * @param vamsasSeq
3648    *          sequence definition to create/merge dataset sequence for
3649    * @param ds
3650    *          dataset alignment
3651    * @param dseqs
3652    *          vector to add new dataset sequence to
3653    */
3654   private void ensureJalviewDatasetSequence(Sequence vamsasSeq,
3655           AlignmentI ds, Vector dseqs)
3656   {
3657     // JBP TODO: Check this is called for AlCodonFrames to support recovery of
3658     // xRef Codon Maps
3659     jalview.datamodel.Sequence sq = (jalview.datamodel.Sequence) seqRefIds
3660             .get(vamsasSeq.getId());
3661     jalview.datamodel.SequenceI dsq = null;
3662     if (sq != null && sq.getDatasetSequence() != null)
3663     {
3664       dsq = sq.getDatasetSequence();
3665     }
3666
3667     String sqid = vamsasSeq.getDsseqid();
3668     if (dsq == null)
3669     {
3670       // need to create or add a new dataset sequence reference to this sequence
3671       if (sqid != null)
3672       {
3673         dsq = (jalview.datamodel.SequenceI) seqRefIds.get(sqid);
3674       }
3675       // check again
3676       if (dsq == null)
3677       {
3678         // make a new dataset sequence
3679         dsq = sq.createDatasetSequence();
3680         if (sqid == null)
3681         {
3682           // make up a new dataset reference for this sequence
3683           sqid = seqHash(dsq);
3684         }
3685         dsq.setVamsasId(uniqueSetSuffix + sqid);
3686         seqRefIds.put(sqid, dsq);
3687         if (ds == null)
3688         {
3689           if (dseqs != null)
3690           {
3691             dseqs.addElement(dsq);
3692           }
3693         }
3694         else
3695         {
3696           ds.addSequence(dsq);
3697         }
3698       }
3699       else
3700       {
3701         if (sq != dsq)
3702         { // make this dataset sequence sq's dataset sequence
3703           sq.setDatasetSequence(dsq);
3704         }
3705       }
3706     }
3707     // TODO: refactor this as a merge dataset sequence function
3708     // now check that sq (the dataset sequence) sequence really is the union of
3709     // all references to it
3710     // boolean pre = sq.getStart() < dsq.getStart();
3711     // boolean post = sq.getEnd() > dsq.getEnd();
3712     // if (pre || post)
3713     if (sq != dsq)
3714     {
3715       StringBuffer sb = new StringBuffer();
3716       String newres = jalview.analysis.AlignSeq.extractGaps(
3717               jalview.util.Comparison.GapChars, sq.getSequenceAsString());
3718       if (!newres.equalsIgnoreCase(dsq.getSequenceAsString())
3719               && newres.length() > dsq.getLength())
3720       {
3721         // Update with the longer sequence.
3722         synchronized (dsq)
3723         {
3724           /*
3725            * if (pre) { sb.insert(0, newres .substring(0, dsq.getStart() -
3726            * sq.getStart())); dsq.setStart(sq.getStart()); } if (post) {
3727            * sb.append(newres.substring(newres.length() - sq.getEnd() -
3728            * dsq.getEnd())); dsq.setEnd(sq.getEnd()); }
3729            */
3730           dsq.setSequence(sb.toString());
3731         }
3732         // TODO: merges will never happen if we 'know' we have the real dataset
3733         // sequence - this should be detected when id==dssid
3734         System.err.println("DEBUG Notice:  Merged dataset sequence"); // ("
3735         // + (pre ? "prepended" : "") + " "
3736         // + (post ? "appended" : ""));
3737       }
3738     }
3739   }
3740
3741   java.util.Hashtable datasetIds = null;
3742
3743   java.util.IdentityHashMap dataset2Ids = null;
3744
3745   private Alignment getDatasetFor(String datasetId)
3746   {
3747     if (datasetIds == null)
3748     {
3749       datasetIds = new Hashtable();
3750       return null;
3751     }
3752     if (datasetIds.containsKey(datasetId))
3753     {
3754       return (Alignment) datasetIds.get(datasetId);
3755     }
3756     return null;
3757   }
3758
3759   private void addDatasetRef(String datasetId, Alignment dataset)
3760   {
3761     if (datasetIds == null)
3762     {
3763       datasetIds = new Hashtable();
3764     }
3765     datasetIds.put(datasetId, dataset);
3766   }
3767
3768   /**
3769    * make a new dataset ID for this jalview dataset alignment
3770    * 
3771    * @param dataset
3772    * @return
3773    */
3774   private String getDatasetIdRef(jalview.datamodel.Alignment dataset)
3775   {
3776     if (dataset.getDataset() != null)
3777     {
3778       warn("Serious issue!  Dataset Object passed to getDatasetIdRef is not a Jalview DATASET alignment...");
3779     }
3780     String datasetId = makeHashCode(dataset, null);
3781     if (datasetId == null)
3782     {
3783       // make a new datasetId and record it
3784       if (dataset2Ids == null)
3785       {
3786         dataset2Ids = new IdentityHashMap();
3787       }
3788       else
3789       {
3790         datasetId = (String) dataset2Ids.get(dataset);
3791       }
3792       if (datasetId == null)
3793       {
3794         datasetId = "ds" + dataset2Ids.size() + 1;
3795         dataset2Ids.put(dataset, datasetId);
3796       }
3797     }
3798     return datasetId;
3799   }
3800
3801   private void addDBRefs(SequenceI datasetSequence, Sequence sequence)
3802   {
3803     for (int d = 0; d < sequence.getDBRefCount(); d++)
3804     {
3805       DBRef dr = sequence.getDBRef(d);
3806       jalview.datamodel.DBRefEntry entry = new jalview.datamodel.DBRefEntry(
3807               sequence.getDBRef(d).getSource(), sequence.getDBRef(d)
3808                       .getVersion(), sequence.getDBRef(d).getAccessionId());
3809       if (dr.getMapping() != null)
3810       {
3811         entry.setMap(addMapping(dr.getMapping()));
3812       }
3813       datasetSequence.addDBRef(entry);
3814     }
3815   }
3816
3817   private jalview.datamodel.Mapping addMapping(Mapping m)
3818   {
3819     SequenceI dsto = null;
3820     // Mapping m = dr.getMapping();
3821     int fr[] = new int[m.getMapListFromCount() * 2];
3822     Enumeration f = m.enumerateMapListFrom();
3823     for (int _i = 0; f.hasMoreElements(); _i += 2)
3824     {
3825       MapListFrom mf = (MapListFrom) f.nextElement();
3826       fr[_i] = mf.getStart();
3827       fr[_i + 1] = mf.getEnd();
3828     }
3829     int fto[] = new int[m.getMapListToCount() * 2];
3830     f = m.enumerateMapListTo();
3831     for (int _i = 0; f.hasMoreElements(); _i += 2)
3832     {
3833       MapListTo mf = (MapListTo) f.nextElement();
3834       fto[_i] = mf.getStart();
3835       fto[_i + 1] = mf.getEnd();
3836     }
3837     jalview.datamodel.Mapping jmap = new jalview.datamodel.Mapping(dsto,
3838             fr, fto, (int) m.getMapFromUnit(), (int) m.getMapToUnit());
3839     if (m.getMappingChoice() != null)
3840     {
3841       MappingChoice mc = m.getMappingChoice();
3842       if (mc.getDseqFor() != null)
3843       {
3844         String dsfor = "" + mc.getDseqFor();
3845         if (seqRefIds.containsKey(dsfor))
3846         {
3847           /**
3848            * recover from hash
3849            */
3850           jmap.setTo((SequenceI) seqRefIds.get(dsfor));
3851         }
3852         else
3853         {
3854           frefedSequence.add(new Object[]
3855           { dsfor, jmap });
3856         }
3857       }
3858       else
3859       {
3860         /**
3861          * local sequence definition
3862          */
3863         Sequence ms = mc.getSequence();
3864         jalview.datamodel.Sequence djs = null;
3865         String sqid = ms.getDsseqid();
3866         if (sqid != null && sqid.length() > 0)
3867         {
3868           /*
3869            * recover dataset sequence
3870            */
3871           djs = (jalview.datamodel.Sequence) seqRefIds.get(sqid);
3872         }
3873         else
3874         {
3875           System.err
3876                   .println("Warning - making up dataset sequence id for DbRef sequence map reference");
3877           sqid = ((Object) ms).toString(); // make up a new hascode for
3878           // undefined dataset sequence hash
3879           // (unlikely to happen)
3880         }
3881
3882         if (djs == null)
3883         {
3884           /**
3885            * make a new dataset sequence and add it to refIds hash
3886            */
3887           djs = new jalview.datamodel.Sequence(ms.getName(),
3888                   ms.getSequence());
3889           djs.setStart(jmap.getMap().getToLowest());
3890           djs.setEnd(jmap.getMap().getToHighest());
3891           djs.setVamsasId(uniqueSetSuffix + sqid);
3892           jmap.setTo(djs);
3893           seqRefIds.put(sqid, djs);
3894
3895         }
3896         jalview.bin.Cache.log.debug("about to recurse on addDBRefs.");
3897         addDBRefs(djs, ms);
3898
3899       }
3900     }
3901     return (jmap);
3902
3903   }
3904
3905   public jalview.gui.AlignmentPanel copyAlignPanel(AlignmentPanel ap,
3906           boolean keepSeqRefs)
3907   {
3908     initSeqRefs();
3909     jalview.schemabinding.version2.JalviewModel jm = SaveState(ap, null,
3910             null);
3911
3912     if (!keepSeqRefs)
3913     {
3914       clearSeqRefs();
3915       jm.getJalviewModelSequence().getViewport(0).setSequenceSetId(null);
3916     }
3917     else
3918     {
3919       uniqueSetSuffix = "";
3920       jm.getJalviewModelSequence().getViewport(0).setId(null); // we don't
3921       // overwrite the
3922       // view we just
3923       // copied
3924     }
3925     if (this.frefedSequence == null)
3926     {
3927       frefedSequence = new Vector();
3928     }
3929
3930     viewportsAdded = new Hashtable();
3931
3932     AlignFrame af = LoadFromObject(jm, null, false, null);
3933     af.alignPanels.clear();
3934     af.closeMenuItem_actionPerformed(true);
3935
3936     /*
3937      * if(ap.av.getAlignment().getAlignmentAnnotation()!=null) { for(int i=0;
3938      * i<ap.av.getAlignment().getAlignmentAnnotation().length; i++) {
3939      * if(!ap.av.getAlignment().getAlignmentAnnotation()[i].autoCalculated) {
3940      * af.alignPanel.av.getAlignment().getAlignmentAnnotation()[i] =
3941      * ap.av.getAlignment().getAlignmentAnnotation()[i]; } } }
3942      */
3943
3944     return af.alignPanel;
3945   }
3946
3947   /**
3948    * flag indicating if hashtables should be cleared on finalization TODO this
3949    * flag may not be necessary
3950    */
3951   private final boolean _cleartables = true;
3952
3953   private Hashtable jvids2vobj;
3954
3955   /*
3956    * (non-Javadoc)
3957    * 
3958    * @see java.lang.Object#finalize()
3959    */
3960   @Override
3961   protected void finalize() throws Throwable
3962   {
3963     // really make sure we have no buried refs left.
3964     if (_cleartables)
3965     {
3966       clearSeqRefs();
3967     }
3968     this.seqRefIds = null;
3969     this.seqsToIds = null;
3970     super.finalize();
3971   }
3972
3973   private void warn(String msg)
3974   {
3975     warn(msg, null);
3976   }
3977
3978   private void warn(String msg, Exception e)
3979   {
3980     if (Cache.log != null)
3981     {
3982       if (e != null)
3983       {
3984         Cache.log.warn(msg, e);
3985       }
3986       else
3987       {
3988         Cache.log.warn(msg);
3989       }
3990     }
3991     else
3992     {
3993       System.err.println("Warning: " + msg);
3994       if (e != null)
3995       {
3996         e.printStackTrace();
3997       }
3998     }
3999   }
4000
4001   private void debug(String string)
4002   {
4003     debug(string, null);
4004   }
4005
4006   private void debug(String msg, Exception e)
4007   {
4008     if (Cache.log != null)
4009     {
4010       if (e != null)
4011       {
4012         Cache.log.debug(msg, e);
4013       }
4014       else
4015       {
4016         Cache.log.debug(msg);
4017       }
4018     }
4019     else
4020     {
4021       System.err.println("Warning: " + msg);
4022       if (e != null)
4023       {
4024         e.printStackTrace();
4025       }
4026     }
4027   }
4028
4029   /**
4030    * set the object to ID mapping tables used to write/recover objects and XML
4031    * ID strings for the jalview project. If external tables are provided then
4032    * finalize and clearSeqRefs will not clear the tables when the Jalview2XML
4033    * object goes out of scope. - also populates the datasetIds hashtable with
4034    * alignment objects containing dataset sequences
4035    * 
4036    * @param vobj2jv
4037    *          Map from ID strings to jalview datamodel
4038    * @param jv2vobj
4039    *          Map from jalview datamodel to ID strings
4040    * 
4041    * 
4042    */
4043   public void setObjectMappingTables(Hashtable vobj2jv,
4044           IdentityHashMap jv2vobj)
4045   {
4046     this.jv2vobj = jv2vobj;
4047     this.vobj2jv = vobj2jv;
4048     Iterator ds = jv2vobj.keySet().iterator();
4049     String id;
4050     while (ds.hasNext())
4051     {
4052       Object jvobj = ds.next();
4053       id = jv2vobj.get(jvobj).toString();
4054       if (jvobj instanceof jalview.datamodel.Alignment)
4055       {
4056         if (((jalview.datamodel.Alignment) jvobj).getDataset() == null)
4057         {
4058           addDatasetRef(id, (jalview.datamodel.Alignment) jvobj);
4059         }
4060       }
4061       else if (jvobj instanceof jalview.datamodel.Sequence)
4062       {
4063         // register sequence object so the XML parser can recover it.
4064         if (seqRefIds == null)
4065         {
4066           seqRefIds = new Hashtable();
4067         }
4068         if (seqsToIds == null)
4069         {
4070           seqsToIds = new IdentityHashMap();
4071         }
4072         seqRefIds.put(jv2vobj.get(jvobj).toString(), jvobj);
4073         seqsToIds.put(jvobj, id);
4074       }
4075       else if (jvobj instanceof jalview.datamodel.AlignmentAnnotation)
4076       {
4077         if (annotationIds == null)
4078         {
4079           annotationIds = new Hashtable();
4080         }
4081         String anid;
4082         annotationIds.put(anid = jv2vobj.get(jvobj).toString(), jvobj);
4083         jalview.datamodel.AlignmentAnnotation jvann = (jalview.datamodel.AlignmentAnnotation) jvobj;
4084         if (jvann.annotationId == null)
4085         {
4086           jvann.annotationId = anid;
4087         }
4088         if (!jvann.annotationId.equals(anid))
4089         {
4090           // TODO verify that this is the correct behaviour
4091           this.warn("Overriding Annotation ID for " + anid
4092                   + " from different id : " + jvann.annotationId);
4093           jvann.annotationId = anid;
4094         }
4095       }
4096       else if (jvobj instanceof String)
4097       {
4098         if (jvids2vobj == null)
4099         {
4100           jvids2vobj = new Hashtable();
4101           jvids2vobj.put(jvobj, jv2vobj.get(jvobj).toString());
4102         }
4103       }
4104       else
4105         Cache.log.debug("Ignoring " + jvobj.getClass() + " (ID = " + id);
4106     }
4107   }
4108
4109   /**
4110    * set the uniqueSetSuffix used to prefix/suffix object IDs for jalview
4111    * objects created from the project archive. If string is null (default for
4112    * construction) then suffix will be set automatically.
4113    * 
4114    * @param string
4115    */
4116   public void setUniqueSetSuffix(String string)
4117   {
4118     uniqueSetSuffix = string;
4119
4120   }
4121
4122   /**
4123    * uses skipList2 as the skipList for skipping views on sequence sets
4124    * associated with keys in the skipList
4125    * 
4126    * @param skipList2
4127    */
4128   public void setSkipList(Hashtable skipList2)
4129   {
4130     skipList = skipList2;
4131   }
4132
4133 }