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