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