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