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