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