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