formatting
[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
2271         if (!an[i].getScoreOnly())
2272         {
2273           anot = new jalview.datamodel.Annotation[al.getWidth()];
2274           for (int aa = 0; aa < ae.length && aa < anot.length; aa++)
2275           {
2276             if (ae[aa].getPosition() >= anot.length)
2277               continue;
2278
2279             anot[ae[aa].getPosition()] = new jalview.datamodel.Annotation(
2280
2281             ae[aa].getDisplayCharacter(), ae[aa].getDescription(),
2282                     (ae[aa].getSecondaryStructure() == null || ae[aa]
2283                             .getSecondaryStructure().length() == 0) ? ' '
2284                             : ae[aa].getSecondaryStructure().charAt(0),
2285                     ae[aa].getValue()
2286
2287             );
2288             // JBPNote: Consider verifying dataflow for IO of secondary
2289             // structure annotation read from Stockholm files
2290             // this was added to try to ensure that
2291             // if (anot[ae[aa].getPosition()].secondaryStructure>' ')
2292             // {
2293             // anot[ae[aa].getPosition()].displayCharacter = "";
2294             // }
2295             anot[ae[aa].getPosition()].colour = new java.awt.Color(
2296                     ae[aa].getColour());
2297           }
2298         }
2299         jalview.datamodel.AlignmentAnnotation jaa = null;
2300
2301         if (an[i].getGraph())
2302         {
2303           float llim = 0, hlim = 0;
2304           // if (autoForView || an[i].isAutoCalculated()) {
2305           // hlim=11f;
2306           // }
2307           jaa = new jalview.datamodel.AlignmentAnnotation(an[i].getLabel(),
2308                   an[i].getDescription(), anot, llim, hlim,
2309                   an[i].getGraphType());
2310
2311           jaa.graphGroup = an[i].getGraphGroup();
2312
2313           if (an[i].getThresholdLine() != null)
2314           {
2315             jaa.setThreshold(new jalview.datamodel.GraphLine(an[i]
2316                     .getThresholdLine().getValue(), an[i]
2317                     .getThresholdLine().getLabel(), new java.awt.Color(
2318                     an[i].getThresholdLine().getColour())));
2319
2320           }
2321           if (autoForView || an[i].isAutoCalculated())
2322           {
2323             // Hardwire the symbol display line to ensure that labels for
2324             // histograms are displayed
2325             jaa.hasText = true;
2326           }
2327         }
2328         else
2329         {
2330           jaa = new jalview.datamodel.AlignmentAnnotation(an[i].getLabel(),
2331                   an[i].getDescription(), anot);
2332         }
2333         // register new annotation
2334         if (an[i].getId() != null)
2335         {
2336           annotationIds.put(an[i].getId(), jaa);
2337           jaa.annotationId = an[i].getId();
2338         }
2339         // recover sequence association
2340         if (an[i].getSequenceRef() != null)
2341         {
2342           if (al.findName(an[i].getSequenceRef()) != null)
2343           {
2344             jaa.createSequenceMapping(al.findName(an[i].getSequenceRef()),
2345                     1, true);
2346             al.findName(an[i].getSequenceRef()).addAlignmentAnnotation(jaa);
2347           }
2348         }
2349         // and make a note of any group association
2350         if (an[i].getGroupRef() != null && an[i].getGroupRef().length() > 0)
2351         {
2352           ArrayList<jalview.datamodel.AlignmentAnnotation> aal = groupAnnotRefs
2353                   .get(an[i].getGroupRef());
2354           if (aal == null)
2355           {
2356             aal = new ArrayList<jalview.datamodel.AlignmentAnnotation>();
2357             groupAnnotRefs.put(an[i].getGroupRef(), aal);
2358           }
2359           aal.add(jaa);
2360         }
2361
2362         if (an[i].hasScore())
2363         {
2364           jaa.setScore(an[i].getScore());
2365         }
2366         if (an[i].hasVisible())
2367           jaa.visible = an[i].getVisible();
2368
2369         if (an[i].hasCentreColLabels())
2370           jaa.centreColLabels = an[i].getCentreColLabels();
2371
2372         if (an[i].hasScaleColLabels())
2373         {
2374           jaa.scaleColLabel = an[i].getScaleColLabels();
2375         }
2376         if (an[i].hasAutoCalculated() && an[i].isAutoCalculated())
2377         {
2378           // newer files have an 'autoCalculated' flag and store calculation
2379           // state in viewport properties
2380           jaa.autoCalculated = true; // means annotation will be marked for
2381           // update at end of load.
2382         }
2383         if (an[i].hasGraphHeight())
2384         {
2385           jaa.graphHeight = an[i].getGraphHeight();
2386         }
2387         if (an[i].hasBelowAlignment())
2388         {
2389           jaa.belowAlignment = an[i].isBelowAlignment();
2390         }
2391         jaa.setCalcId(an[i].getCalcId());
2392
2393         if (jaa.autoCalculated)
2394         {
2395           autoAlan.add(new JvAnnotRow(i, jaa));
2396         }
2397         else
2398         // if (!autoForView)
2399         {
2400           // add autocalculated group annotation and any user created annotation
2401           // for the view
2402           al.addAnnotation(jaa);
2403         }
2404       }
2405     }
2406
2407     // ///////////////////////
2408     // LOAD GROUPS
2409     // Create alignment markup and styles for this view
2410     if (jms.getJGroupCount() > 0)
2411     {
2412       JGroup[] groups = jms.getJGroup();
2413
2414       for (int i = 0; i < groups.length; i++)
2415       {
2416         ColourSchemeI cs = null;
2417
2418         if (groups[i].getColour() != null)
2419         {
2420           if (groups[i].getColour().startsWith("ucs"))
2421           {
2422             cs = GetUserColourScheme(jms, groups[i].getColour());
2423           }
2424           else
2425           {
2426             cs = ColourSchemeProperty.getColour(al, groups[i].getColour());
2427           }
2428
2429           if (cs != null)
2430           {
2431             cs.setThreshold(groups[i].getPidThreshold(), true);
2432           }
2433         }
2434
2435         Vector seqs = new Vector();
2436
2437         for (int s = 0; s < groups[i].getSeqCount(); s++)
2438         {
2439           String seqId = groups[i].getSeq(s) + "";
2440           jalview.datamodel.SequenceI ts = (jalview.datamodel.SequenceI) seqRefIds
2441                   .get(seqId);
2442
2443           if (ts != null)
2444           {
2445             seqs.addElement(ts);
2446           }
2447         }
2448
2449         if (seqs.size() < 1)
2450         {
2451           continue;
2452         }
2453
2454         jalview.datamodel.SequenceGroup sg = new jalview.datamodel.SequenceGroup(
2455                 seqs, groups[i].getName(), cs, groups[i].getDisplayBoxes(),
2456                 groups[i].getDisplayText(), groups[i].getColourText(),
2457                 groups[i].getStart(), groups[i].getEnd());
2458
2459         sg.setOutlineColour(new java.awt.Color(groups[i].getOutlineColour()));
2460
2461         sg.textColour = new java.awt.Color(groups[i].getTextCol1());
2462         sg.textColour2 = new java.awt.Color(groups[i].getTextCol2());
2463         sg.setShowNonconserved(groups[i].hasShowUnconserved() ? groups[i]
2464                 .isShowUnconserved() : false);
2465         sg.thresholdTextColour = groups[i].getTextColThreshold();
2466         if (groups[i].hasShowConsensusHistogram())
2467         {
2468           sg.setShowConsensusHistogram(groups[i].isShowConsensusHistogram());
2469         }
2470         ;
2471         if (groups[i].hasShowSequenceLogo())
2472         {
2473           sg.setshowSequenceLogo(groups[i].isShowSequenceLogo());
2474         }
2475         if (groups[i].hasIgnoreGapsinConsensus())
2476         {
2477           sg.setIgnoreGapsConsensus(groups[i].getIgnoreGapsinConsensus());
2478         }
2479         if (groups[i].getConsThreshold() != 0)
2480         {
2481           jalview.analysis.Conservation c = new jalview.analysis.Conservation(
2482                   "All", ResidueProperties.propHash, 3,
2483                   sg.getSequences(null), 0, sg.getWidth() - 1);
2484           c.calculate();
2485           c.verdict(false, 25);
2486           sg.cs.setConservation(c);
2487         }
2488
2489         if (groups[i].getId() != null && groupAnnotRefs.size() > 0)
2490         {
2491           // re-instate unique group/annotation row reference
2492           ArrayList<jalview.datamodel.AlignmentAnnotation> jaal = groupAnnotRefs
2493                   .get(groups[i].getId());
2494           if (jaal != null)
2495           {
2496             for (jalview.datamodel.AlignmentAnnotation jaa : jaal)
2497             {
2498               jaa.groupRef = sg;
2499               if (jaa.autoCalculated)
2500               {
2501                 // match up and try to set group autocalc alignment row for this
2502                 // annotation
2503                 if (jaa.label.startsWith("Consensus for "))
2504                 {
2505                   sg.setConsensus(jaa);
2506                 }
2507                 // match up and try to set group autocalc alignment row for this
2508                 // annotation
2509                 if (jaa.label.startsWith("Conservation for "))
2510                 {
2511                   sg.setConservationRow(jaa);
2512                 }
2513               }
2514             }
2515           }
2516         }
2517         al.addGroup(sg);
2518
2519       }
2520     }
2521
2522     // ///////////////////////////////
2523     // LOAD VIEWPORT
2524
2525     // If we just load in the same jar file again, the sequenceSetId
2526     // will be the same, and we end up with multiple references
2527     // to the same sequenceSet. We must modify this id on load
2528     // so that each load of the file gives a unique id
2529     String uniqueSeqSetId = view.getSequenceSetId() + uniqueSetSuffix;
2530     String viewId = (view.getId() == null ? null : view.getId()
2531             + uniqueSetSuffix);
2532     AlignFrame af = null;
2533     AlignViewport av = null;
2534     // now check to see if we really need to create a new viewport.
2535     if (multipleView && viewportsAdded.size() == 0)
2536     {
2537       // We recovered an alignment for which a viewport already exists.
2538       // TODO: fix up any settings necessary for overlaying stored state onto
2539       // state recovered from another document. (may not be necessary).
2540       // we may need a binding from a viewport in memory to one recovered from
2541       // XML.
2542       // and then recover its containing af to allow the settings to be applied.
2543       // TODO: fix for vamsas demo
2544       System.err
2545               .println("About to recover a viewport for existing alignment: Sequence set ID is "
2546                       + uniqueSeqSetId);
2547       Object seqsetobj = retrieveExistingObj(uniqueSeqSetId);
2548       if (seqsetobj != null)
2549       {
2550         if (seqsetobj instanceof String)
2551         {
2552           uniqueSeqSetId = (String) seqsetobj;
2553           System.err
2554                   .println("Recovered extant sequence set ID mapping for ID : New Sequence set ID is "
2555                           + uniqueSeqSetId);
2556         }
2557         else
2558         {
2559           System.err
2560                   .println("Warning : Collision between sequence set ID string and existing jalview object mapping.");
2561         }
2562
2563       }
2564     }
2565     AlignmentPanel ap = null;
2566     boolean isnewview = true;
2567     if (viewId != null)
2568     {
2569       // Check to see if this alignment already has a view id == viewId
2570       jalview.gui.AlignmentPanel views[] = Desktop
2571               .getAlignmentPanels(uniqueSeqSetId);
2572       if (views != null && views.length > 0)
2573       {
2574         for (int v = 0; v < views.length; v++)
2575         {
2576           if (views[v].av.getViewId().equalsIgnoreCase(viewId))
2577           {
2578             // recover the existing alignpanel, alignframe, viewport
2579             af = views[v].alignFrame;
2580             av = views[v].av;
2581             ap = views[v];
2582             // TODO: could even skip resetting view settings if we don't want to
2583             // change the local settings from other jalview processes
2584             isnewview = false;
2585           }
2586         }
2587       }
2588     }
2589
2590     if (isnewview)
2591     {
2592       af = loadViewport(file, JSEQ, hiddenSeqs, al, jms, view,
2593               uniqueSeqSetId, viewId, autoAlan);
2594       av = af.viewport;
2595       ap = af.alignPanel;
2596     }
2597     // LOAD TREES
2598     // /////////////////////////////////////
2599     if (loadTreesAndStructures && jms.getTreeCount() > 0)
2600     {
2601       try
2602       {
2603         for (int t = 0; t < jms.getTreeCount(); t++)
2604         {
2605
2606           Tree tree = jms.getTree(t);
2607
2608           TreePanel tp = (TreePanel) retrieveExistingObj(tree.getId());
2609           if (tp == null)
2610           {
2611             tp = af.ShowNewickTree(
2612                     new jalview.io.NewickFile(tree.getNewick()),
2613                     tree.getTitle(), tree.getWidth(), tree.getHeight(),
2614                     tree.getXpos(), tree.getYpos());
2615             if (tree.getId() != null)
2616             {
2617               // perhaps bind the tree id to something ?
2618             }
2619           }
2620           else
2621           {
2622             // update local tree attributes ?
2623             // TODO: should check if tp has been manipulated by user - if so its
2624             // settings shouldn't be modified
2625             tp.setTitle(tree.getTitle());
2626             tp.setBounds(new Rectangle(tree.getXpos(), tree.getYpos(), tree
2627                     .getWidth(), tree.getHeight()));
2628             tp.av = av; // af.viewport; // TODO: verify 'associate with all
2629             // views'
2630             // works still
2631             tp.treeCanvas.av = av; // af.viewport;
2632             tp.treeCanvas.ap = ap; // af.alignPanel;
2633
2634           }
2635           if (tp == null)
2636           {
2637             warn("There was a problem recovering stored Newick tree: \n"
2638                     + tree.getNewick());
2639             continue;
2640           }
2641
2642           tp.fitToWindow.setState(tree.getFitToWindow());
2643           tp.fitToWindow_actionPerformed(null);
2644
2645           if (tree.getFontName() != null)
2646           {
2647             tp.setTreeFont(new java.awt.Font(tree.getFontName(), tree
2648                     .getFontStyle(), tree.getFontSize()));
2649           }
2650           else
2651           {
2652             tp.setTreeFont(new java.awt.Font(view.getFontName(), view
2653                     .getFontStyle(), tree.getFontSize()));
2654           }
2655
2656           tp.showPlaceholders(tree.getMarkUnlinked());
2657           tp.showBootstrap(tree.getShowBootstrap());
2658           tp.showDistances(tree.getShowDistances());
2659
2660           tp.treeCanvas.threshold = tree.getThreshold();
2661
2662           if (tree.getCurrentTree())
2663           {
2664             af.viewport.setCurrentTree(tp.getTree());
2665           }
2666         }
2667
2668       } catch (Exception ex)
2669       {
2670         ex.printStackTrace();
2671       }
2672     }
2673
2674     // //LOAD STRUCTURES
2675     if (loadTreesAndStructures)
2676     {
2677       // run through all PDB ids on the alignment, and collect mappings between
2678       // jmol view ids and all sequences referring to it
2679       Hashtable<String, Object[]> jmolViewIds = new Hashtable();
2680
2681       for (int i = 0; i < JSEQ.length; i++)
2682       {
2683         if (JSEQ[i].getPdbidsCount() > 0)
2684         {
2685           Pdbids[] ids = JSEQ[i].getPdbids();
2686           for (int p = 0; p < ids.length; p++)
2687           {
2688             for (int s = 0; s < ids[p].getStructureStateCount(); s++)
2689             {
2690               // check to see if we haven't already created this structure view
2691               String sviewid = (ids[p].getStructureState(s).getViewId() == null) ? null
2692                       : ids[p].getStructureState(s).getViewId()
2693                               + uniqueSetSuffix;
2694               jalview.datamodel.PDBEntry jpdb = new jalview.datamodel.PDBEntry();
2695               // Originally : ids[p].getFile()
2696               // : TODO: verify external PDB file recovery still works in normal
2697               // jalview project load
2698               jpdb.setFile(loadPDBFile(jprovider, ids[p].getId()));
2699               jpdb.setId(ids[p].getId());
2700
2701               int x = ids[p].getStructureState(s).getXpos();
2702               int y = ids[p].getStructureState(s).getYpos();
2703               int width = ids[p].getStructureState(s).getWidth();
2704               int height = ids[p].getStructureState(s).getHeight();
2705
2706               // Probably don't need to do this anymore...
2707               // Desktop.desktop.getComponentAt(x, y);
2708               // TODO: NOW: check that this recovers the PDB file correctly.
2709               String pdbFile = loadPDBFile(jprovider, ids[p].getId());
2710               jalview.datamodel.SequenceI seq = (jalview.datamodel.SequenceI) seqRefIds
2711                       .get(JSEQ[i].getId() + "");
2712               if (sviewid == null)
2713               {
2714                 sviewid = "_jalview_pre2_4_" + x + "," + y + "," + width
2715                         + "," + height;
2716               }
2717               if (!jmolViewIds.containsKey(sviewid))
2718               {
2719                 jmolViewIds.put(sviewid, new Object[]
2720                 { new int[]
2721                 { x, y, width, height }, "",
2722                     new Hashtable<String, Object[]>(), new boolean[]
2723                     { false, false, true } });
2724                 // Legacy pre-2.7 conversion JAL-823 :
2725                 // do not assume any view has to be linked for colour by
2726                 // sequence
2727               }
2728
2729               // assemble String[] { pdb files }, String[] { id for each
2730               // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
2731               // seqs_file 2}, boolean[] {
2732               // linkAlignPanel,superposeWithAlignpanel}} from hash
2733               Object[] jmoldat = jmolViewIds.get(sviewid);
2734               ((boolean[]) jmoldat[3])[0] |= ids[p].getStructureState(s)
2735                       .hasAlignwithAlignPanel() ? ids[p].getStructureState(
2736                       s).getAlignwithAlignPanel() : false;
2737               // never colour by linked panel if not specified
2738               ((boolean[]) jmoldat[3])[1] |= ids[p].getStructureState(s)
2739                       .hasColourwithAlignPanel() ? ids[p]
2740                       .getStructureState(s).getColourwithAlignPanel()
2741                       : false;
2742               // default for pre-2.7 projects is that Jmol colouring is enabled
2743               ((boolean[]) jmoldat[3])[2] &= ids[p].getStructureState(s)
2744                       .hasColourByJmol() ? ids[p].getStructureState(s)
2745                       .getColourByJmol() : true;
2746
2747               if (((String) jmoldat[1]).length() < ids[p]
2748                       .getStructureState(s).getContent().length())
2749               {
2750                 {
2751                   jmoldat[1] = ids[p].getStructureState(s).getContent();
2752                 }
2753               }
2754               if (ids[p].getFile() != null)
2755               {
2756                 File mapkey = new File(ids[p].getFile());
2757                 Object[] seqstrmaps = (Object[]) ((Hashtable) jmoldat[2])
2758                         .get(mapkey);
2759                 if (seqstrmaps == null)
2760                 {
2761                   ((Hashtable) jmoldat[2]).put(mapkey,
2762                           seqstrmaps = new Object[]
2763                           { pdbFile, ids[p].getId(), new Vector(),
2764                               new Vector() });
2765                 }
2766                 if (!((Vector) seqstrmaps[2]).contains(seq))
2767                 {
2768                   ((Vector) seqstrmaps[2]).addElement(seq);
2769                   // ((Vector)seqstrmaps[3]).addElement(n) :
2770                   // in principle, chains
2771                   // should be stored here : do we need to
2772                   // TODO: store and recover seq/pdb_id :
2773                   // chain mappings
2774                 }
2775               }
2776               else
2777               {
2778                 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");
2779                 warn(errorMessage);
2780               }
2781             }
2782           }
2783         }
2784       }
2785       {
2786
2787         // Instantiate the associated Jmol views
2788         for (Entry<String, Object[]> entry : jmolViewIds.entrySet())
2789         {
2790           String sviewid = entry.getKey();
2791           Object[] svattrib = entry.getValue();
2792           int[] geom = (int[]) svattrib[0];
2793           String state = (String) svattrib[1];
2794           Hashtable<File, Object[]> oldFiles = (Hashtable<File, Object[]>) svattrib[2];
2795           final boolean useinJmolsuperpos = ((boolean[]) svattrib[3])[0], usetoColourbyseq = ((boolean[]) svattrib[3])[1], jmolColouring = ((boolean[]) svattrib[3])[2];
2796           int x = geom[0], y = geom[1], width = geom[2], height = geom[3];
2797           // collate the pdbfile -> sequence mappings from this view
2798           Vector<String> pdbfilenames = new Vector<String>();
2799           Vector<SequenceI[]> seqmaps = new Vector<SequenceI[]>();
2800           Vector<String> pdbids = new Vector<String>();
2801
2802           // Search to see if we've already created this Jmol view
2803           AppJmol comp = null;
2804           JInternalFrame[] frames = null;
2805           do
2806           {
2807             try
2808             {
2809               frames = Desktop.desktop.getAllFrames();
2810             } catch (ArrayIndexOutOfBoundsException e)
2811             {
2812               // occasional No such child exceptions are thrown here...
2813               frames = null;
2814               try
2815               {
2816                 Thread.sleep(10);
2817               } catch (Exception f)
2818               {
2819               }
2820               ;
2821             }
2822           } while (frames == null);
2823           // search for any Jmol windows already open from other
2824           // alignment views that exactly match the stored structure state
2825           for (int f = 0; comp == null && f < frames.length; f++)
2826           {
2827             if (frames[f] instanceof AppJmol)
2828             {
2829               if (sviewid != null
2830                       && ((AppJmol) frames[f]).getViewId().equals(sviewid))
2831               {
2832                 // post jalview 2.4 schema includes structure view id
2833                 comp = (AppJmol) frames[f];
2834               }
2835               else if (frames[f].getX() == x && frames[f].getY() == y
2836                       && frames[f].getHeight() == height
2837                       && frames[f].getWidth() == width)
2838               {
2839                 comp = (AppJmol) frames[f];
2840               }
2841             }
2842           }
2843
2844           if (comp == null)
2845           {
2846             // create a new Jmol window.
2847             // First parse the Jmol state to translate filenames loaded into the
2848             // view, and record the order in which files are shown in the Jmol
2849             // view, so we can add the sequence mappings in same order.
2850             StringBuffer newFileLoc = null;
2851             int cp = 0, ncp, ecp;
2852             while ((ncp = state.indexOf("load ", cp)) > -1)
2853             {
2854               if (newFileLoc == null)
2855               {
2856                 newFileLoc = new StringBuffer();
2857               }
2858               do
2859               {
2860                 // look for next filename in load statement
2861                 newFileLoc.append(state.substring(cp,
2862                         ncp = (state.indexOf("\"", ncp + 1) + 1)));
2863                 String oldfilenam = state.substring(ncp,
2864                         ecp = state.indexOf("\"", ncp));
2865                 // recover the new mapping data for this old filename
2866                 // have to normalize filename - since Jmol and jalview do
2867                 // filename
2868                 // translation differently.
2869                 Object[] filedat = oldFiles.get(new File(oldfilenam));
2870                 newFileLoc.append(Platform
2871                         .escapeString((String) filedat[0]));
2872                 pdbfilenames.addElement((String) filedat[0]);
2873                 pdbids.addElement((String) filedat[1]);
2874                 seqmaps.addElement(((Vector<SequenceI>) filedat[2])
2875                         .toArray(new SequenceI[0]));
2876                 newFileLoc.append("\"");
2877                 cp = ecp + 1; // advance beyond last \" and set cursor so we can
2878                               // look for next file statement.
2879               } while ((ncp = state.indexOf("/*file*/", cp)) > -1);
2880             }
2881             if (cp > 0)
2882             {
2883               // just append rest of state
2884               newFileLoc.append(state.substring(cp));
2885             }
2886             else
2887             {
2888               System.err
2889                       .print("Ignoring incomplete Jmol state for PDB ids: ");
2890               newFileLoc = new StringBuffer(state);
2891               newFileLoc.append("; load append ");
2892               for (File id : oldFiles.keySet())
2893               {
2894                 // add this and any other pdb files that should be present in
2895                 // the viewer
2896                 Object[] filedat = oldFiles.get(id);
2897                 String nfilename;
2898                 newFileLoc.append(((String) filedat[0]));
2899                 pdbfilenames.addElement((String) filedat[0]);
2900                 pdbids.addElement((String) filedat[1]);
2901                 seqmaps.addElement(((Vector<SequenceI>) filedat[2])
2902                         .toArray(new SequenceI[0]));
2903                 newFileLoc.append(" \"");
2904                 newFileLoc.append((String) filedat[0]);
2905                 newFileLoc.append("\"");
2906
2907               }
2908               newFileLoc.append(";");
2909             }
2910
2911             if (newFileLoc != null)
2912             {
2913               int histbug = newFileLoc.indexOf("history = ");
2914               histbug += 10;
2915               int diff = histbug == -1 ? -1 : newFileLoc.indexOf(";",
2916                       histbug);
2917               String val = (diff == -1) ? null : newFileLoc.substring(
2918                       histbug, diff);
2919               if (val != null && val.length() >= 4)
2920               {
2921                 if (val.contains("e"))
2922                 {
2923                   if (val.trim().equals("true"))
2924                   {
2925                     val = "1";
2926                   }
2927                   else
2928                   {
2929                     val = "0";
2930                   }
2931                   newFileLoc.replace(histbug, diff, val);
2932                 }
2933               }
2934               // TODO: assemble String[] { pdb files }, String[] { id for each
2935               // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
2936               // seqs_file 2}} from hash
2937               final String[] pdbf = pdbfilenames
2938                       .toArray(new String[pdbfilenames.size()]), id = pdbids
2939                       .toArray(new String[pdbids.size()]);
2940               final SequenceI[][] sq = seqmaps
2941                       .toArray(new SequenceI[seqmaps.size()][]);
2942               final String fileloc = newFileLoc.toString(), vid = sviewid;
2943               final AlignFrame alf = af;
2944               final java.awt.Rectangle rect = new java.awt.Rectangle(x, y,
2945                       width, height);
2946               try
2947               {
2948                 javax.swing.SwingUtilities.invokeAndWait(new Runnable()
2949                 {
2950                   @Override
2951                   public void run()
2952                   {
2953                     AppJmol sview = null;
2954                     try
2955                     {
2956                       sview = new AppJmol(pdbf, id, sq, alf.alignPanel,
2957                               useinJmolsuperpos, usetoColourbyseq,
2958                               jmolColouring, fileloc, rect, vid);
2959                     } catch (OutOfMemoryError ex)
2960                     {
2961                       new OOMWarning("restoring structure view for PDB id "
2962                               + id, (OutOfMemoryError) ex.getCause());
2963                       if (sview != null && sview.isVisible())
2964                       {
2965                         sview.closeViewer();
2966                         sview.setVisible(false);
2967                         sview.dispose();
2968                       }
2969                     }
2970                   }
2971                 });
2972               } catch (InvocationTargetException ex)
2973               {
2974                 warn("Unexpected error when opening Jmol view.", ex);
2975
2976               } catch (InterruptedException e)
2977               {
2978                 // e.printStackTrace();
2979               }
2980             }
2981
2982           }
2983           else
2984           // if (comp != null)
2985           {
2986             // NOTE: if the jalview project is part of a shared session then
2987             // view synchronization should/could be done here.
2988
2989             // add mapping for sequences in this view to an already open Jmol
2990             // instance
2991             for (File id : oldFiles.keySet())
2992             {
2993               // add this and any other pdb files that should be present in the
2994               // viewer
2995               Object[] filedat = oldFiles.get(id);
2996               String pdbFile = (String) filedat[0];
2997               SequenceI[] seq = ((Vector<SequenceI>) filedat[2])
2998                       .toArray(new SequenceI[0]);
2999               comp.jmb.ssm.setMapping(seq, null, pdbFile,
3000                       jalview.io.AppletFormatAdapter.FILE);
3001               comp.jmb.addSequenceForStructFile(pdbFile, seq);
3002             }
3003             // and add the AlignmentPanel's reference to the Jmol view
3004             comp.addAlignmentPanel(ap);
3005             if (useinJmolsuperpos)
3006             {
3007               comp.useAlignmentPanelForSuperposition(ap);
3008             }
3009             else
3010             {
3011               comp.excludeAlignmentPanelForSuperposition(ap);
3012             }
3013             if (usetoColourbyseq)
3014             {
3015               comp.useAlignmentPanelForColourbyseq(ap, !jmolColouring);
3016             }
3017             else
3018             {
3019               comp.excludeAlignmentPanelForColourbyseq(ap);
3020             }
3021           }
3022         }
3023       }
3024     }
3025     // and finally return.
3026     return af;
3027   }
3028
3029   AlignFrame loadViewport(String file, JSeq[] JSEQ, Vector hiddenSeqs,
3030           Alignment al, JalviewModelSequence jms, Viewport view,
3031           String uniqueSeqSetId, String viewId,
3032           ArrayList<JvAnnotRow> autoAlan)
3033   {
3034     AlignFrame af = null;
3035     af = new AlignFrame(al, view.getWidth(), view.getHeight(),
3036             uniqueSeqSetId, viewId);
3037
3038     af.setFileName(file, "Jalview");
3039
3040     for (int i = 0; i < JSEQ.length; i++)
3041     {
3042       af.viewport.setSequenceColour(af.viewport.getAlignment()
3043               .getSequenceAt(i), new java.awt.Color(JSEQ[i].getColour()));
3044     }
3045
3046     af.viewport.gatherViewsHere = view.getGatheredViews();
3047
3048     if (view.getSequenceSetId() != null)
3049     {
3050       jalview.gui.AlignViewport av = (jalview.gui.AlignViewport) viewportsAdded
3051               .get(uniqueSeqSetId);
3052
3053       af.viewport.setSequenceSetId(uniqueSeqSetId);
3054       if (av != null)
3055       {
3056         // propagate shared settings to this new view
3057         af.viewport.historyList = av.historyList;
3058         af.viewport.redoList = av.redoList;
3059       }
3060       else
3061       {
3062         viewportsAdded.put(uniqueSeqSetId, af.viewport);
3063       }
3064       // TODO: check if this method can be called repeatedly without
3065       // side-effects if alignpanel already registered.
3066       PaintRefresher.Register(af.alignPanel, uniqueSeqSetId);
3067     }
3068     // apply Hidden regions to view.
3069     if (hiddenSeqs != null)
3070     {
3071       for (int s = 0; s < JSEQ.length; s++)
3072       {
3073         jalview.datamodel.SequenceGroup hidden = new jalview.datamodel.SequenceGroup();
3074
3075         for (int r = 0; r < JSEQ[s].getHiddenSequencesCount(); r++)
3076         {
3077           hidden.addSequence(
3078                   al.getSequenceAt(JSEQ[s].getHiddenSequences(r)), false);
3079         }
3080         af.viewport.hideRepSequences(al.getSequenceAt(s), hidden);
3081       }
3082
3083       jalview.datamodel.SequenceI[] hseqs = new jalview.datamodel.SequenceI[hiddenSeqs
3084               .size()];
3085
3086       for (int s = 0; s < hiddenSeqs.size(); s++)
3087       {
3088         hseqs[s] = (jalview.datamodel.SequenceI) hiddenSeqs.elementAt(s);
3089       }
3090
3091       af.viewport.hideSequence(hseqs);
3092
3093     }
3094     // recover view properties and display parameters
3095     if (view.getViewName() != null)
3096     {
3097       af.viewport.viewName = view.getViewName();
3098       af.setInitialTabVisible();
3099     }
3100     af.setBounds(view.getXpos(), view.getYpos(), view.getWidth(),
3101             view.getHeight());
3102
3103     af.viewport.setShowAnnotation(view.getShowAnnotation());
3104     af.viewport.setAbovePIDThreshold(view.getPidSelected());
3105
3106     af.viewport.setColourText(view.getShowColourText());
3107
3108     af.viewport.setConservationSelected(view.getConservationSelected());
3109     af.viewport.setShowJVSuffix(view.getShowFullId());
3110     af.viewport.rightAlignIds = view.getRightAlignIds();
3111     af.viewport.setFont(new java.awt.Font(view.getFontName(), view
3112             .getFontStyle(), view.getFontSize()));
3113     af.alignPanel.fontChanged();
3114     af.viewport.setRenderGaps(view.getRenderGaps());
3115     af.viewport.setWrapAlignment(view.getWrapAlignment());
3116     af.alignPanel.setWrapAlignment(view.getWrapAlignment());
3117     af.viewport.setShowAnnotation(view.getShowAnnotation());
3118     af.alignPanel.setAnnotationVisible(view.getShowAnnotation());
3119
3120     af.viewport.setShowBoxes(view.getShowBoxes());
3121
3122     af.viewport.setShowText(view.getShowText());
3123
3124     af.viewport.textColour = new java.awt.Color(view.getTextCol1());
3125     af.viewport.textColour2 = new java.awt.Color(view.getTextCol2());
3126     af.viewport.thresholdTextColour = view.getTextColThreshold();
3127     af.viewport.setShowUnconserved(view.hasShowUnconserved() ? view
3128             .isShowUnconserved() : false);
3129     af.viewport.setStartRes(view.getStartRes());
3130     af.viewport.setStartSeq(view.getStartSeq());
3131
3132     ColourSchemeI cs = null;
3133     // apply colourschemes
3134     if (view.getBgColour() != null)
3135     {
3136       if (view.getBgColour().startsWith("ucs"))
3137       {
3138         cs = GetUserColourScheme(jms, view.getBgColour());
3139       }
3140       else if (view.getBgColour().startsWith("Annotation"))
3141       {
3142         // int find annotation
3143         if (af.viewport.getAlignment().getAlignmentAnnotation() != null)
3144         {
3145           for (int i = 0; i < af.viewport.getAlignment()
3146                   .getAlignmentAnnotation().length; i++)
3147           {
3148             if (af.viewport.getAlignment().getAlignmentAnnotation()[i].label
3149                     .equals(view.getAnnotationColours().getAnnotation()))
3150             {
3151               if (af.viewport.getAlignment().getAlignmentAnnotation()[i]
3152                       .getThreshold() == null)
3153               {
3154                 af.viewport.getAlignment().getAlignmentAnnotation()[i]
3155                         .setThreshold(new jalview.datamodel.GraphLine(view
3156                                 .getAnnotationColours().getThreshold(),
3157                                 "Threshold", java.awt.Color.black)
3158
3159                         );
3160               }
3161
3162               if (view.getAnnotationColours().getColourScheme()
3163                       .equals("None"))
3164               {
3165                 cs = new AnnotationColourGradient(af.viewport
3166                         .getAlignment().getAlignmentAnnotation()[i],
3167                         new java.awt.Color(view.getAnnotationColours()
3168                                 .getMinColour()), new java.awt.Color(view
3169                                 .getAnnotationColours().getMaxColour()),
3170                         view.getAnnotationColours().getAboveThreshold());
3171               }
3172               else if (view.getAnnotationColours().getColourScheme()
3173                       .startsWith("ucs"))
3174               {
3175                 cs = new AnnotationColourGradient(af.viewport
3176                         .getAlignment().getAlignmentAnnotation()[i],
3177                         GetUserColourScheme(jms, view
3178                                 .getAnnotationColours().getColourScheme()),
3179                         view.getAnnotationColours().getAboveThreshold());
3180               }
3181               else
3182               {
3183                 cs = new AnnotationColourGradient(af.viewport
3184                         .getAlignment().getAlignmentAnnotation()[i],
3185                         ColourSchemeProperty.getColour(al, view
3186                                 .getAnnotationColours().getColourScheme()),
3187                         view.getAnnotationColours().getAboveThreshold());
3188               }
3189
3190               // Also use these settings for all the groups
3191               if (al.getGroups() != null)
3192               {
3193                 for (int g = 0; g < al.getGroups().size(); g++)
3194                 {
3195                   jalview.datamodel.SequenceGroup sg = al.getGroups()
3196                           .get(g);
3197
3198                   if (sg.cs == null)
3199                   {
3200                     continue;
3201                   }
3202
3203                   /*
3204                    * if
3205                    * (view.getAnnotationColours().getColourScheme().equals("None"
3206                    * )) { sg.cs = new AnnotationColourGradient(
3207                    * af.viewport.getAlignment().getAlignmentAnnotation()[i], new
3208                    * java.awt.Color(view.getAnnotationColours().
3209                    * getMinColour()), new
3210                    * java.awt.Color(view.getAnnotationColours().
3211                    * getMaxColour()),
3212                    * view.getAnnotationColours().getAboveThreshold()); } else
3213                    */
3214                   {
3215                     sg.cs = new AnnotationColourGradient(af.viewport
3216                             .getAlignment().getAlignmentAnnotation()[i],
3217                             sg.cs, view.getAnnotationColours()
3218                                     .getAboveThreshold());
3219                   }
3220
3221                 }
3222               }
3223
3224               break;
3225             }
3226
3227           }
3228         }
3229       }
3230       else
3231       {
3232         cs = ColourSchemeProperty.getColour(al, view.getBgColour());
3233       }
3234
3235       if (cs != null)
3236       {
3237         cs.setThreshold(view.getPidThreshold(), true);
3238         cs.setConsensus(af.viewport.getSequenceConsensusHash());
3239       }
3240     }
3241
3242     af.viewport.setGlobalColourScheme(cs);
3243     af.viewport.setColourAppliesToAllGroups(false);
3244
3245     if (view.getConservationSelected() && cs != null)
3246     {
3247       cs.setConservationInc(view.getConsThreshold());
3248     }
3249
3250     af.changeColour(cs);
3251
3252     af.viewport.setColourAppliesToAllGroups(true);
3253
3254     if (view.getShowSequenceFeatures())
3255     {
3256       af.viewport.showSequenceFeatures = true;
3257     }
3258     if (view.hasCentreColumnLabels())
3259     {
3260       af.viewport.setCentreColumnLabels(view.getCentreColumnLabels());
3261     }
3262     if (view.hasIgnoreGapsinConsensus())
3263     {
3264       af.viewport.setIgnoreGapsConsensus(view.getIgnoreGapsinConsensus(),
3265               null);
3266     }
3267     if (view.hasFollowHighlight())
3268     {
3269       af.viewport.followHighlight = view.getFollowHighlight();
3270     }
3271     if (view.hasFollowSelection())
3272     {
3273       af.viewport.followSelection = view.getFollowSelection();
3274     }
3275     if (view.hasShowConsensusHistogram())
3276     {
3277       af.viewport.setShowConsensusHistogram(view
3278               .getShowConsensusHistogram());
3279     }
3280     else
3281     {
3282       af.viewport.setShowConsensusHistogram(true);
3283     }
3284     if (view.hasShowSequenceLogo())
3285     {
3286       af.viewport.setShowSequenceLogo(view.getShowSequenceLogo());
3287     }
3288     else
3289     {
3290       af.viewport.setShowSequenceLogo(false);
3291     }
3292     if (view.hasShowDbRefTooltip())
3293     {
3294       af.viewport.setShowDbRefs(view.getShowDbRefTooltip());
3295     }
3296     if (view.hasShowNPfeatureTooltip())
3297     {
3298       af.viewport.setShowNpFeats(view.hasShowNPfeatureTooltip());
3299     }
3300     if (view.hasShowGroupConsensus())
3301     {
3302       af.viewport.setShowGroupConsensus(view.getShowGroupConsensus());
3303     }
3304     else
3305     {
3306       af.viewport.setShowGroupConsensus(false);
3307     }
3308     if (view.hasShowGroupConservation())
3309     {
3310       af.viewport.setShowGroupConservation(view.getShowGroupConservation());
3311     }
3312     else
3313     {
3314       af.viewport.setShowGroupConservation(false);
3315     }
3316
3317     // recover featre settings
3318     if (jms.getFeatureSettings() != null)
3319     {
3320       af.viewport.featuresDisplayed = new Hashtable();
3321       String[] renderOrder = new String[jms.getFeatureSettings()
3322               .getSettingCount()];
3323       for (int fs = 0; fs < jms.getFeatureSettings().getSettingCount(); fs++)
3324       {
3325         Setting setting = jms.getFeatureSettings().getSetting(fs);
3326         if (setting.hasMincolour())
3327         {
3328           GraduatedColor gc = setting.hasMin() ? new GraduatedColor(
3329                   new java.awt.Color(setting.getMincolour()),
3330                   new java.awt.Color(setting.getColour()),
3331                   setting.getMin(), setting.getMax()) : new GraduatedColor(
3332                   new java.awt.Color(setting.getMincolour()),
3333                   new java.awt.Color(setting.getColour()), 0, 1);
3334           if (setting.hasThreshold())
3335           {
3336             gc.setThresh(setting.getThreshold());
3337             gc.setThreshType(setting.getThreshstate());
3338           }
3339           gc.setAutoScaled(true); // default
3340           if (setting.hasAutoScale())
3341           {
3342             gc.setAutoScaled(setting.getAutoScale());
3343           }
3344           if (setting.hasColourByLabel())
3345           {
3346             gc.setColourByLabel(setting.getColourByLabel());
3347           }
3348           // and put in the feature colour table.
3349           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setColour(
3350                   setting.getType(), gc);
3351         }
3352         else
3353         {
3354           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setColour(
3355                   setting.getType(),
3356                   new java.awt.Color(setting.getColour()));
3357         }
3358         renderOrder[fs] = setting.getType();
3359         if (setting.hasOrder())
3360           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setOrder(
3361                   setting.getType(), setting.getOrder());
3362         else
3363           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setOrder(
3364                   setting.getType(),
3365                   fs / jms.getFeatureSettings().getSettingCount());
3366         if (setting.getDisplay())
3367         {
3368           af.viewport.featuresDisplayed.put(setting.getType(), new Integer(
3369                   setting.getColour()));
3370         }
3371       }
3372       af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().renderOrder = renderOrder;
3373       Hashtable fgtable;
3374       af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().featureGroups = fgtable = new Hashtable();
3375       for (int gs = 0; gs < jms.getFeatureSettings().getGroupCount(); gs++)
3376       {
3377         Group grp = jms.getFeatureSettings().getGroup(gs);
3378         fgtable.put(grp.getName(), new Boolean(grp.getDisplay()));
3379       }
3380     }
3381
3382     if (view.getHiddenColumnsCount() > 0)
3383     {
3384       for (int c = 0; c < view.getHiddenColumnsCount(); c++)
3385       {
3386         af.viewport.hideColumns(view.getHiddenColumns(c).getStart(), view
3387                 .getHiddenColumns(c).getEnd() // +1
3388                 );
3389       }
3390     }
3391     if (view.getCalcIdParam() != null)
3392     {
3393       for (CalcIdParam calcIdParam : view.getCalcIdParam())
3394       {
3395         if (calcIdParam != null)
3396         {
3397           if (recoverCalcIdParam(calcIdParam, af.viewport))
3398           {
3399           }
3400           else
3401           {
3402             warn("Couldn't recover parameters for "
3403                     + calcIdParam.getCalcId());
3404           }
3405         }
3406       }
3407     }
3408     af.setMenusFromViewport(af.viewport);
3409     // TODO: we don't need to do this if the viewport is aready visible.
3410     Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(),
3411             view.getHeight());
3412     af.alignPanel.updateAnnotation(false); // recompute any autoannotation
3413     reorderAutoannotation(af, al, autoAlan);
3414     return af;
3415   }
3416
3417   private void reorderAutoannotation(AlignFrame af, Alignment al,
3418           ArrayList<JvAnnotRow> autoAlan)
3419   {
3420     // copy over visualization settings for autocalculated annotation in the
3421     // view
3422     if (al.getAlignmentAnnotation() != null)
3423     {
3424       /**
3425        * Kludge for magic autoannotation names (see JAL-811)
3426        */
3427       String[] magicNames = new String[]
3428       { "Consensus", "Quality", "Conservation" };
3429       JvAnnotRow nullAnnot = new JvAnnotRow(-1, null);
3430       Hashtable<String, JvAnnotRow> visan = new Hashtable<String, JvAnnotRow>();
3431       for (String nm : magicNames)
3432       {
3433         visan.put(nm, nullAnnot);
3434       }
3435       for (JvAnnotRow auan : autoAlan)
3436       {
3437         visan.put(auan.template.label
3438                 + (auan.template.getCalcId() == null ? "" : "\t"
3439                         + auan.template.getCalcId()), auan);
3440       }
3441       int hSize = al.getAlignmentAnnotation().length;
3442       ArrayList<JvAnnotRow> reorder = new ArrayList<JvAnnotRow>();
3443       // work through any autoCalculated annotation already on the view
3444       // removing it if it should be placed in a different location on the
3445       // annotation panel.
3446       List<String> remains = new ArrayList(visan.keySet());
3447       for (int h = 0; h < hSize; h++)
3448       {
3449         jalview.datamodel.AlignmentAnnotation jalan = al
3450                 .getAlignmentAnnotation()[h];
3451         if (jalan.autoCalculated)
3452         {
3453           String k;
3454           JvAnnotRow valan = visan.get(k = jalan.label);
3455           if (jalan.getCalcId() != null)
3456           {
3457             valan = visan.get(k = jalan.label + "\t" + jalan.getCalcId());
3458           }
3459
3460           if (valan != null)
3461           {
3462             // delete the auto calculated row from the alignment
3463             al.deleteAnnotation(jalan, false);
3464             remains.remove(k);
3465             hSize--;
3466             h--;
3467             if (valan != nullAnnot)
3468             {
3469               if (jalan != valan.template)
3470               {
3471                 // newly created autoannotation row instance
3472                 // so keep a reference to the visible annotation row
3473                 // and copy over all relevant attributes
3474                 if (valan.template.graphHeight >= 0)
3475
3476                 {
3477                   jalan.graphHeight = valan.template.graphHeight;
3478                 }
3479                 jalan.visible = valan.template.visible;
3480               }
3481               reorder.add(new JvAnnotRow(valan.order, jalan));
3482             }
3483           }
3484         }
3485       }
3486       // Add any (possibly stale) autocalculated rows that were not appended to
3487       // the view during construction
3488       for (String other : remains)
3489       {
3490         JvAnnotRow othera = visan.get(other);
3491         if (othera != nullAnnot && othera.template.getCalcId() != null
3492                 && othera.template.getCalcId().length() > 0)
3493         {
3494           reorder.add(othera);
3495         }
3496       }
3497       // now put the automatic annotation in its correct place
3498       int s = 0, srt[] = new int[reorder.size()];
3499       JvAnnotRow[] rws = new JvAnnotRow[reorder.size()];
3500       for (JvAnnotRow jvar : reorder)
3501       {
3502         rws[s] = jvar;
3503         srt[s++] = jvar.order;
3504       }
3505       reorder.clear();
3506       jalview.util.QuickSort.sort(srt, rws);
3507       // and re-insert the annotation at its correct position
3508       for (JvAnnotRow jvar : rws)
3509       {
3510         al.addAnnotation(jvar.template, jvar.order);
3511       }
3512       af.alignPanel.adjustAnnotationHeight();
3513     }
3514   }
3515
3516   Hashtable skipList = null;
3517
3518   /**
3519    * TODO remove this method
3520    *
3521    * @param view
3522    * @return AlignFrame bound to sequenceSetId from view, if one exists. private
3523    *         AlignFrame getSkippedFrame(Viewport view) { if (skipList==null) {
3524    *         throw new Error("Implementation Error. No skipList defined for this
3525    *         Jalview2XML instance."); } return (AlignFrame)
3526    *         skipList.get(view.getSequenceSetId()); }
3527    */
3528
3529   /**
3530    * Check if the Jalview view contained in object should be skipped or not.
3531    *
3532    * @param object
3533    * @return true if view's sequenceSetId is a key in skipList
3534    */
3535   private boolean skipViewport(JalviewModel object)
3536   {
3537     if (skipList == null)
3538     {
3539       return false;
3540     }
3541     String id;
3542     if (skipList.containsKey(id = object.getJalviewModelSequence()
3543             .getViewport()[0].getSequenceSetId()))
3544     {
3545       if (Cache.log != null && Cache.log.isDebugEnabled())
3546       {
3547         Cache.log.debug("Skipping seuqence set id " + id);
3548       }
3549       return true;
3550     }
3551     return false;
3552   }
3553
3554   public void AddToSkipList(AlignFrame af)
3555   {
3556     if (skipList == null)
3557     {
3558       skipList = new Hashtable();
3559     }
3560     skipList.put(af.getViewport().getSequenceSetId(), af);
3561   }
3562
3563   public void clearSkipList()
3564   {
3565     if (skipList != null)
3566     {
3567       skipList.clear();
3568       skipList = null;
3569     }
3570   }
3571
3572   private void recoverDatasetFor(SequenceSet vamsasSet, Alignment al)
3573   {
3574     jalview.datamodel.Alignment ds = getDatasetFor(vamsasSet.getDatasetId());
3575     Vector dseqs = null;
3576     if (ds == null)
3577     {
3578       // create a list of new dataset sequences
3579       dseqs = new Vector();
3580     }
3581     for (int i = 0, iSize = vamsasSet.getSequenceCount(); i < iSize; i++)
3582     {
3583       Sequence vamsasSeq = vamsasSet.getSequence(i);
3584       ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs);
3585     }
3586     // create a new dataset
3587     if (ds == null)
3588     {
3589       SequenceI[] dsseqs = new SequenceI[dseqs.size()];
3590       dseqs.copyInto(dsseqs);
3591       ds = new jalview.datamodel.Alignment(dsseqs);
3592       debug("Created new dataset " + vamsasSet.getDatasetId()
3593               + " for alignment " + System.identityHashCode(al));
3594       addDatasetRef(vamsasSet.getDatasetId(), ds);
3595     }
3596     // set the dataset for the newly imported alignment.
3597     if (al.getDataset() == null)
3598     {
3599       al.setDataset(ds);
3600     }
3601   }
3602
3603   /**
3604    *
3605    * @param vamsasSeq
3606    *          sequence definition to create/merge dataset sequence for
3607    * @param ds
3608    *          dataset alignment
3609    * @param dseqs
3610    *          vector to add new dataset sequence to
3611    */
3612   private void ensureJalviewDatasetSequence(Sequence vamsasSeq,
3613           AlignmentI ds, Vector dseqs)
3614   {
3615     // JBP TODO: Check this is called for AlCodonFrames to support recovery of
3616     // xRef Codon Maps
3617     jalview.datamodel.Sequence sq = (jalview.datamodel.Sequence) seqRefIds
3618             .get(vamsasSeq.getId());
3619     jalview.datamodel.SequenceI dsq = null;
3620     if (sq != null && sq.getDatasetSequence() != null)
3621     {
3622       dsq = sq.getDatasetSequence();
3623     }
3624
3625     String sqid = vamsasSeq.getDsseqid();
3626     if (dsq == null)
3627     {
3628       // need to create or add a new dataset sequence reference to this sequence
3629       if (sqid != null)
3630       {
3631         dsq = (jalview.datamodel.SequenceI) seqRefIds.get(sqid);
3632       }
3633       // check again
3634       if (dsq == null)
3635       {
3636         // make a new dataset sequence
3637         dsq = sq.createDatasetSequence();
3638         if (sqid == null)
3639         {
3640           // make up a new dataset reference for this sequence
3641           sqid = seqHash(dsq);
3642         }
3643         dsq.setVamsasId(uniqueSetSuffix + sqid);
3644         seqRefIds.put(sqid, dsq);
3645         if (ds == null)
3646         {
3647           if (dseqs != null)
3648           {
3649             dseqs.addElement(dsq);
3650           }
3651         }
3652         else
3653         {
3654           ds.addSequence(dsq);
3655         }
3656       }
3657       else
3658       {
3659         if (sq != dsq)
3660         { // make this dataset sequence sq's dataset sequence
3661           sq.setDatasetSequence(dsq);
3662         }
3663       }
3664     }
3665     // TODO: refactor this as a merge dataset sequence function
3666     // now check that sq (the dataset sequence) sequence really is the union of
3667     // all references to it
3668     // boolean pre = sq.getStart() < dsq.getStart();
3669     // boolean post = sq.getEnd() > dsq.getEnd();
3670     // if (pre || post)
3671     if (sq != dsq)
3672     {
3673       StringBuffer sb = new StringBuffer();
3674       String newres = jalview.analysis.AlignSeq.extractGaps(
3675               jalview.util.Comparison.GapChars, sq.getSequenceAsString());
3676       if (!newres.equalsIgnoreCase(dsq.getSequenceAsString())
3677               && newres.length() > dsq.getLength())
3678       {
3679         // Update with the longer sequence.
3680         synchronized (dsq)
3681         {
3682           /*
3683            * if (pre) { sb.insert(0, newres .substring(0, dsq.getStart() -
3684            * sq.getStart())); dsq.setStart(sq.getStart()); } if (post) {
3685            * sb.append(newres.substring(newres.length() - sq.getEnd() -
3686            * dsq.getEnd())); dsq.setEnd(sq.getEnd()); }
3687            */
3688           dsq.setSequence(sb.toString());
3689         }
3690         // TODO: merges will never happen if we 'know' we have the real dataset
3691         // sequence - this should be detected when id==dssid
3692         System.err.println("DEBUG Notice:  Merged dataset sequence"); // ("
3693         // + (pre ? "prepended" : "") + " "
3694         // + (post ? "appended" : ""));
3695       }
3696     }
3697   }
3698
3699   java.util.Hashtable datasetIds = null;
3700
3701   java.util.IdentityHashMap dataset2Ids = null;
3702
3703   private Alignment getDatasetFor(String datasetId)
3704   {
3705     if (datasetIds == null)
3706     {
3707       datasetIds = new Hashtable();
3708       return null;
3709     }
3710     if (datasetIds.containsKey(datasetId))
3711     {
3712       return (Alignment) datasetIds.get(datasetId);
3713     }
3714     return null;
3715   }
3716
3717   private void addDatasetRef(String datasetId, Alignment dataset)
3718   {
3719     if (datasetIds == null)
3720     {
3721       datasetIds = new Hashtable();
3722     }
3723     datasetIds.put(datasetId, dataset);
3724   }
3725
3726   /**
3727    * make a new dataset ID for this jalview dataset alignment
3728    *
3729    * @param dataset
3730    * @return
3731    */
3732   private String getDatasetIdRef(jalview.datamodel.Alignment dataset)
3733   {
3734     if (dataset.getDataset() != null)
3735     {
3736       warn("Serious issue!  Dataset Object passed to getDatasetIdRef is not a Jalview DATASET alignment...");
3737     }
3738     String datasetId = makeHashCode(dataset, null);
3739     if (datasetId == null)
3740     {
3741       // make a new datasetId and record it
3742       if (dataset2Ids == null)
3743       {
3744         dataset2Ids = new IdentityHashMap();
3745       }
3746       else
3747       {
3748         datasetId = (String) dataset2Ids.get(dataset);
3749       }
3750       if (datasetId == null)
3751       {
3752         datasetId = "ds" + dataset2Ids.size() + 1;
3753         dataset2Ids.put(dataset, datasetId);
3754       }
3755     }
3756     return datasetId;
3757   }
3758
3759   private void addDBRefs(SequenceI datasetSequence, Sequence sequence)
3760   {
3761     for (int d = 0; d < sequence.getDBRefCount(); d++)
3762     {
3763       DBRef dr = sequence.getDBRef(d);
3764       jalview.datamodel.DBRefEntry entry = new jalview.datamodel.DBRefEntry(
3765               sequence.getDBRef(d).getSource(), sequence.getDBRef(d)
3766                       .getVersion(), sequence.getDBRef(d).getAccessionId());
3767       if (dr.getMapping() != null)
3768       {
3769         entry.setMap(addMapping(dr.getMapping()));
3770       }
3771       datasetSequence.addDBRef(entry);
3772     }
3773   }
3774
3775   private jalview.datamodel.Mapping addMapping(Mapping m)
3776   {
3777     SequenceI dsto = null;
3778     // Mapping m = dr.getMapping();
3779     int fr[] = new int[m.getMapListFromCount() * 2];
3780     Enumeration f = m.enumerateMapListFrom();
3781     for (int _i = 0; f.hasMoreElements(); _i += 2)
3782     {
3783       MapListFrom mf = (MapListFrom) f.nextElement();
3784       fr[_i] = mf.getStart();
3785       fr[_i + 1] = mf.getEnd();
3786     }
3787     int fto[] = new int[m.getMapListToCount() * 2];
3788     f = m.enumerateMapListTo();
3789     for (int _i = 0; f.hasMoreElements(); _i += 2)
3790     {
3791       MapListTo mf = (MapListTo) f.nextElement();
3792       fto[_i] = mf.getStart();
3793       fto[_i + 1] = mf.getEnd();
3794     }
3795     jalview.datamodel.Mapping jmap = new jalview.datamodel.Mapping(dsto,
3796             fr, fto, (int) m.getMapFromUnit(), (int) m.getMapToUnit());
3797     if (m.getMappingChoice() != null)
3798     {
3799       MappingChoice mc = m.getMappingChoice();
3800       if (mc.getDseqFor() != null)
3801       {
3802         String dsfor = "" + mc.getDseqFor();
3803         if (seqRefIds.containsKey(dsfor))
3804         {
3805           /**
3806            * recover from hash
3807            */
3808           jmap.setTo((SequenceI) seqRefIds.get(dsfor));
3809         }
3810         else
3811         {
3812           frefedSequence.add(new Object[]
3813           { dsfor, jmap });
3814         }
3815       }
3816       else
3817       {
3818         /**
3819          * local sequence definition
3820          */
3821         Sequence ms = mc.getSequence();
3822         jalview.datamodel.Sequence djs = null;
3823         String sqid = ms.getDsseqid();
3824         if (sqid != null && sqid.length() > 0)
3825         {
3826           /*
3827            * recover dataset sequence
3828            */
3829           djs = (jalview.datamodel.Sequence) seqRefIds.get(sqid);
3830         }
3831         else
3832         {
3833           System.err
3834                   .println("Warning - making up dataset sequence id for DbRef sequence map reference");
3835           sqid = ((Object) ms).toString(); // make up a new hascode for
3836           // undefined dataset sequence hash
3837           // (unlikely to happen)
3838         }
3839
3840         if (djs == null)
3841         {
3842           /**
3843            * make a new dataset sequence and add it to refIds hash
3844            */
3845           djs = new jalview.datamodel.Sequence(ms.getName(),
3846                   ms.getSequence());
3847           djs.setStart(jmap.getMap().getToLowest());
3848           djs.setEnd(jmap.getMap().getToHighest());
3849           djs.setVamsasId(uniqueSetSuffix + sqid);
3850           jmap.setTo(djs);
3851           seqRefIds.put(sqid, djs);
3852
3853         }
3854         jalview.bin.Cache.log.debug("about to recurse on addDBRefs.");
3855         addDBRefs(djs, ms);
3856
3857       }
3858     }
3859     return (jmap);
3860
3861   }
3862
3863   public jalview.gui.AlignmentPanel copyAlignPanel(AlignmentPanel ap,
3864           boolean keepSeqRefs)
3865   {
3866     initSeqRefs();
3867     jalview.schemabinding.version2.JalviewModel jm = SaveState(ap, null,
3868             null);
3869
3870     if (!keepSeqRefs)
3871     {
3872       clearSeqRefs();
3873       jm.getJalviewModelSequence().getViewport(0).setSequenceSetId(null);
3874     }
3875     else
3876     {
3877       uniqueSetSuffix = "";
3878       jm.getJalviewModelSequence().getViewport(0).setId(null); // we don't
3879       // overwrite the
3880       // view we just
3881       // copied
3882     }
3883     if (this.frefedSequence == null)
3884     {
3885       frefedSequence = new Vector();
3886     }
3887
3888     viewportsAdded = new Hashtable();
3889
3890     AlignFrame af = LoadFromObject(jm, null, false, null);
3891     af.alignPanels.clear();
3892     af.closeMenuItem_actionPerformed(true);
3893
3894     /*
3895      * if(ap.av.getAlignment().getAlignmentAnnotation()!=null) { for(int i=0;
3896      * i<ap.av.getAlignment().getAlignmentAnnotation().length; i++) {
3897      * if(!ap.av.getAlignment().getAlignmentAnnotation()[i].autoCalculated) {
3898      * af.alignPanel.av.getAlignment().getAlignmentAnnotation()[i] =
3899      * ap.av.getAlignment().getAlignmentAnnotation()[i]; } } }
3900      */
3901
3902     return af.alignPanel;
3903   }
3904
3905   /**
3906    * flag indicating if hashtables should be cleared on finalization TODO this
3907    * flag may not be necessary
3908    */
3909   private final boolean _cleartables = true;
3910
3911   private Hashtable jvids2vobj;
3912
3913   /*
3914    * (non-Javadoc)
3915    *
3916    * @see java.lang.Object#finalize()
3917    */
3918   @Override
3919   protected void finalize() throws Throwable
3920   {
3921     // really make sure we have no buried refs left.
3922     if (_cleartables)
3923     {
3924       clearSeqRefs();
3925     }
3926     this.seqRefIds = null;
3927     this.seqsToIds = null;
3928     super.finalize();
3929   }
3930
3931   private void warn(String msg)
3932   {
3933     warn(msg, null);
3934   }
3935
3936   private void warn(String msg, Exception e)
3937   {
3938     if (Cache.log != null)
3939     {
3940       if (e != null)
3941       {
3942         Cache.log.warn(msg, e);
3943       }
3944       else
3945       {
3946         Cache.log.warn(msg);
3947       }
3948     }
3949     else
3950     {
3951       System.err.println("Warning: " + msg);
3952       if (e != null)
3953       {
3954         e.printStackTrace();
3955       }
3956     }
3957   }
3958
3959   private void debug(String string)
3960   {
3961     debug(string, null);
3962   }
3963
3964   private void debug(String msg, Exception e)
3965   {
3966     if (Cache.log != null)
3967     {
3968       if (e != null)
3969       {
3970         Cache.log.debug(msg, e);
3971       }
3972       else
3973       {
3974         Cache.log.debug(msg);
3975       }
3976     }
3977     else
3978     {
3979       System.err.println("Warning: " + msg);
3980       if (e != null)
3981       {
3982         e.printStackTrace();
3983       }
3984     }
3985   }
3986
3987   /**
3988    * set the object to ID mapping tables used to write/recover objects and XML
3989    * ID strings for the jalview project. If external tables are provided then
3990    * finalize and clearSeqRefs will not clear the tables when the Jalview2XML
3991    * object goes out of scope. - also populates the datasetIds hashtable with
3992    * alignment objects containing dataset sequences
3993    *
3994    * @param vobj2jv
3995    *          Map from ID strings to jalview datamodel
3996    * @param jv2vobj
3997    *          Map from jalview datamodel to ID strings
3998    *
3999    *
4000    */
4001   public void setObjectMappingTables(Hashtable vobj2jv,
4002           IdentityHashMap jv2vobj)
4003   {
4004     this.jv2vobj = jv2vobj;
4005     this.vobj2jv = vobj2jv;
4006     Iterator ds = jv2vobj.keySet().iterator();
4007     String id;
4008     while (ds.hasNext())
4009     {
4010       Object jvobj = ds.next();
4011       id = jv2vobj.get(jvobj).toString();
4012       if (jvobj instanceof jalview.datamodel.Alignment)
4013       {
4014         if (((jalview.datamodel.Alignment) jvobj).getDataset() == null)
4015         {
4016           addDatasetRef(id, (jalview.datamodel.Alignment) jvobj);
4017         }
4018       }
4019       else if (jvobj instanceof jalview.datamodel.Sequence)
4020       {
4021         // register sequence object so the XML parser can recover it.
4022         if (seqRefIds == null)
4023         {
4024           seqRefIds = new Hashtable();
4025         }
4026         if (seqsToIds == null)
4027         {
4028           seqsToIds = new IdentityHashMap();
4029         }
4030         seqRefIds.put(jv2vobj.get(jvobj).toString(), jvobj);
4031         seqsToIds.put(jvobj, id);
4032       }
4033       else if (jvobj instanceof jalview.datamodel.AlignmentAnnotation)
4034       {
4035         if (annotationIds == null)
4036         {
4037           annotationIds = new Hashtable();
4038         }
4039         String anid;
4040         annotationIds.put(anid = jv2vobj.get(jvobj).toString(), jvobj);
4041         jalview.datamodel.AlignmentAnnotation jvann = (jalview.datamodel.AlignmentAnnotation) jvobj;
4042         if (jvann.annotationId == null)
4043         {
4044           jvann.annotationId = anid;
4045         }
4046         if (!jvann.annotationId.equals(anid))
4047         {
4048           // TODO verify that this is the correct behaviour
4049           this.warn("Overriding Annotation ID for " + anid
4050                   + " from different id : " + jvann.annotationId);
4051           jvann.annotationId = anid;
4052         }
4053       }
4054       else if (jvobj instanceof String)
4055       {
4056         if (jvids2vobj == null)
4057         {
4058           jvids2vobj = new Hashtable();
4059           jvids2vobj.put(jvobj, jv2vobj.get(jvobj).toString());
4060         }
4061       }
4062       else
4063         Cache.log.debug("Ignoring " + jvobj.getClass() + " (ID = " + id);
4064     }
4065   }
4066
4067   /**
4068    * set the uniqueSetSuffix used to prefix/suffix object IDs for jalview
4069    * objects created from the project archive. If string is null (default for
4070    * construction) then suffix will be set automatically.
4071    *
4072    * @param string
4073    */
4074   public void setUniqueSetSuffix(String string)
4075   {
4076     uniqueSetSuffix = string;
4077
4078   }
4079
4080   /**
4081    * uses skipList2 as the skipList for skipping views on sequence sets
4082    * associated with keys in the skipList
4083    *
4084    * @param skipList2
4085    */
4086   public void setSkipList(Hashtable skipList2)
4087   {
4088     skipList = skipList2;
4089   }
4090
4091 }