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