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