JAL-1503 update version in GPL header
[jalview.git] / src / jalview / gui / Jalview2XML.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.1)
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 (af.viewport.gatherViewsHere)
1928               {
1929                 gatherToThisFrame.put(af.viewport.getSequenceSetId(), af);
1930               }
1931             }
1932           }
1933           entryCount++;
1934         }
1935         else if (jarentry != null)
1936         {
1937           // Some other file here.
1938           entryCount++;
1939         }
1940       } while (jarentry != null);
1941       resolveFrefedSequences();
1942     } catch (java.io.FileNotFoundException ex)
1943     {
1944       ex.printStackTrace();
1945       errorMessage = "Couldn't locate Jalview XML file : " + file;
1946       System.err.println("Exception whilst loading jalview XML file : "
1947               + ex + "\n");
1948     } catch (java.net.UnknownHostException ex)
1949     {
1950       ex.printStackTrace();
1951       errorMessage = "Couldn't locate Jalview XML file : " + file;
1952       System.err.println("Exception whilst loading jalview XML file : "
1953               + ex + "\n");
1954     } catch (Exception ex)
1955     {
1956       System.err.println("Parsing as Jalview Version 2 file failed.");
1957       ex.printStackTrace(System.err);
1958       if (attemptversion1parse)
1959       {
1960         // Is Version 1 Jar file?
1961         try
1962         {
1963           af = new Jalview2XML_V1(raiseGUI).LoadJalviewAlign(jprovider);
1964         } catch (Exception ex2)
1965         {
1966           System.err.println("Exception whilst loading as jalviewXMLV1:");
1967           ex2.printStackTrace();
1968           af = null;
1969         }
1970       }
1971       if (Desktop.instance != null)
1972       {
1973         Desktop.instance.stopLoading();
1974       }
1975       if (af != null)
1976       {
1977         System.out.println("Successfully loaded archive file");
1978         return af;
1979       }
1980       ex.printStackTrace();
1981
1982       System.err.println("Exception whilst loading jalview XML file : "
1983               + ex + "\n");
1984     } catch (OutOfMemoryError e)
1985     {
1986       // Don't use the OOM Window here
1987       errorMessage = "Out of memory loading jalview XML file";
1988       System.err.println("Out of memory whilst loading jalview XML file");
1989       e.printStackTrace();
1990     }
1991
1992     if (Desktop.instance != null)
1993     {
1994       Desktop.instance.stopLoading();
1995     }
1996
1997     Enumeration en = gatherToThisFrame.elements();
1998     while (en.hasMoreElements())
1999     {
2000       Desktop.instance.gatherViews((AlignFrame) en.nextElement());
2001     }
2002     if (errorMessage != null)
2003     {
2004       reportErrors();
2005     }
2006     return af;
2007   }
2008
2009   /**
2010    * check errorMessage for a valid error message and raise an error box in the
2011    * GUI or write the current errorMessage to stderr and then clear the error
2012    * state.
2013    */
2014   protected void reportErrors()
2015   {
2016     reportErrors(false);
2017   }
2018
2019   protected void reportErrors(final boolean saving)
2020   {
2021     if (errorMessage != null)
2022     {
2023       final String finalErrorMessage = errorMessage;
2024       if (raiseGUI)
2025       {
2026         javax.swing.SwingUtilities.invokeLater(new Runnable()
2027         {
2028           @Override
2029           public void run()
2030           {
2031             JOptionPane.showInternalMessageDialog(Desktop.desktop,
2032                     finalErrorMessage, "Error "
2033                             + (saving ? "saving" : "loading")
2034                             + " Jalview file", JOptionPane.WARNING_MESSAGE);
2035           }
2036         });
2037       }
2038       else
2039       {
2040         System.err.println("Problem loading Jalview file: " + errorMessage);
2041       }
2042     }
2043     errorMessage = null;
2044   }
2045
2046   Hashtable alreadyLoadedPDB;
2047
2048   /**
2049    * when set, local views will be updated from view stored in JalviewXML
2050    * Currently (28th Sep 2008) things will go horribly wrong in vamsas document
2051    * sync if this is set to true.
2052    */
2053   private final boolean updateLocalViews = false;
2054
2055   String loadPDBFile(jarInputStreamProvider jprovider, String pdbId)
2056   {
2057     if (alreadyLoadedPDB == null)
2058       alreadyLoadedPDB = new Hashtable();
2059
2060     if (alreadyLoadedPDB.containsKey(pdbId))
2061       return alreadyLoadedPDB.get(pdbId).toString();
2062
2063     try
2064     {
2065       JarInputStream jin = jprovider.getJarInputStream();
2066       /*
2067        * if (jprovider.startsWith("http://")) { jin = new JarInputStream(new
2068        * URL(jprovider).openStream()); } else { jin = new JarInputStream(new
2069        * FileInputStream(jprovider)); }
2070        */
2071
2072       JarEntry entry = null;
2073       do
2074       {
2075         entry = jin.getNextJarEntry();
2076       } while (entry != null && !entry.getName().equals(pdbId));
2077       if (entry != null)
2078       {
2079         BufferedReader in = new BufferedReader(new InputStreamReader(jin));
2080         File outFile = File.createTempFile("jalview_pdb", ".txt");
2081         outFile.deleteOnExit();
2082         PrintWriter out = new PrintWriter(new FileOutputStream(outFile));
2083         String data;
2084
2085         while ((data = in.readLine()) != null)
2086         {
2087           out.println(data);
2088         }
2089         try
2090         {
2091           out.flush();
2092         } catch (Exception foo)
2093         {
2094         }
2095         ;
2096         out.close();
2097         String t = outFile.getAbsolutePath();
2098         alreadyLoadedPDB.put(pdbId, t);
2099         return t;
2100       }
2101       else
2102       {
2103         warn("Couldn't find PDB file entry in Jalview Jar for " + pdbId);
2104       }
2105     } catch (Exception ex)
2106     {
2107       ex.printStackTrace();
2108     }
2109
2110     return null;
2111   }
2112
2113   private class JvAnnotRow
2114   {
2115     public JvAnnotRow(int i, AlignmentAnnotation jaa)
2116     {
2117       order = i;
2118       template = jaa;
2119     }
2120
2121     /**
2122      * persisted version of annotation row from which to take vis properties
2123      */
2124     public jalview.datamodel.AlignmentAnnotation template;
2125
2126     /**
2127      * original position of the annotation row in the alignment
2128      */
2129     public int order;
2130   }
2131
2132   /**
2133    * Load alignment frame from jalview XML DOM object
2134    * 
2135    * @param object
2136    *          DOM
2137    * @param file
2138    *          filename source string
2139    * @param loadTreesAndStructures
2140    *          when false only create Viewport
2141    * @param jprovider
2142    *          data source provider
2143    * @return alignment frame created from view stored in DOM
2144    */
2145   AlignFrame LoadFromObject(JalviewModel object, String file,
2146           boolean loadTreesAndStructures, jarInputStreamProvider jprovider)
2147   {
2148     SequenceSet vamsasSet = object.getVamsasModel().getSequenceSet(0);
2149     Sequence[] vamsasSeq = vamsasSet.getSequence();
2150
2151     JalviewModelSequence jms = object.getJalviewModelSequence();
2152
2153     Viewport view = (jms.getViewportCount() > 0) ? jms.getViewport(0)
2154             : null;
2155
2156     // ////////////////////////////////
2157     // LOAD SEQUENCES
2158
2159     Vector hiddenSeqs = null;
2160     jalview.datamodel.Sequence jseq;
2161
2162     ArrayList tmpseqs = new ArrayList();
2163
2164     boolean multipleView = false;
2165
2166     JSeq[] JSEQ = object.getJalviewModelSequence().getJSeq();
2167     int vi = 0; // counter in vamsasSeq array
2168     for (int i = 0; i < JSEQ.length; i++)
2169     {
2170       String seqId = JSEQ[i].getId();
2171
2172       if (seqRefIds.get(seqId) != null)
2173       {
2174         tmpseqs.add(seqRefIds.get(seqId));
2175         multipleView = true;
2176       }
2177       else
2178       {
2179         jseq = new jalview.datamodel.Sequence(vamsasSeq[vi].getName(),
2180                 vamsasSeq[vi].getSequence());
2181         jseq.setDescription(vamsasSeq[vi].getDescription());
2182         jseq.setStart(JSEQ[i].getStart());
2183         jseq.setEnd(JSEQ[i].getEnd());
2184         jseq.setVamsasId(uniqueSetSuffix + seqId);
2185         seqRefIds.put(vamsasSeq[vi].getId(), jseq);
2186         tmpseqs.add(jseq);
2187         vi++;
2188       }
2189
2190       if (JSEQ[i].getHidden())
2191       {
2192         if (hiddenSeqs == null)
2193         {
2194           hiddenSeqs = new Vector();
2195         }
2196
2197         hiddenSeqs.addElement(seqRefIds.get(seqId));
2198       }
2199
2200     }
2201
2202     // /
2203     // Create the alignment object from the sequence set
2204     // ///////////////////////////////
2205     jalview.datamodel.Sequence[] orderedSeqs = new jalview.datamodel.Sequence[tmpseqs
2206             .size()];
2207
2208     tmpseqs.toArray(orderedSeqs);
2209
2210     jalview.datamodel.Alignment al = new jalview.datamodel.Alignment(
2211             orderedSeqs);
2212
2213     // / Add the alignment properties
2214     for (int i = 0; i < vamsasSet.getSequenceSetPropertiesCount(); i++)
2215     {
2216       SequenceSetProperties ssp = vamsasSet.getSequenceSetProperties(i);
2217       al.setProperty(ssp.getKey(), ssp.getValue());
2218     }
2219
2220     // /
2221     // SequenceFeatures are added to the DatasetSequence,
2222     // so we must create or recover the dataset before loading features
2223     // ///////////////////////////////
2224     if (vamsasSet.getDatasetId() == null || vamsasSet.getDatasetId() == "")
2225     {
2226       // older jalview projects do not have a dataset id.
2227       al.setDataset(null);
2228     }
2229     else
2230     {
2231       recoverDatasetFor(vamsasSet, al);
2232     }
2233     // ///////////////////////////////
2234
2235     Hashtable pdbloaded = new Hashtable();
2236     if (!multipleView)
2237     {
2238       // load sequence features, database references and any associated PDB
2239       // structures for the alignment
2240       for (int i = 0; i < vamsasSeq.length; i++)
2241       {
2242         if (JSEQ[i].getFeaturesCount() > 0)
2243         {
2244           Features[] features = JSEQ[i].getFeatures();
2245           for (int f = 0; f < features.length; f++)
2246           {
2247             jalview.datamodel.SequenceFeature sf = new jalview.datamodel.SequenceFeature(
2248                     features[f].getType(), features[f].getDescription(),
2249                     features[f].getStatus(), features[f].getBegin(),
2250                     features[f].getEnd(), features[f].getFeatureGroup());
2251
2252             sf.setScore(features[f].getScore());
2253             for (int od = 0; od < features[f].getOtherDataCount(); od++)
2254             {
2255               OtherData keyValue = features[f].getOtherData(od);
2256               if (keyValue.getKey().startsWith("LINK"))
2257               {
2258                 sf.addLink(keyValue.getValue());
2259               }
2260               else
2261               {
2262                 sf.setValue(keyValue.getKey(), keyValue.getValue());
2263               }
2264
2265             }
2266
2267             al.getSequenceAt(i).getDatasetSequence().addSequenceFeature(sf);
2268           }
2269         }
2270         if (vamsasSeq[i].getDBRefCount() > 0)
2271         {
2272           addDBRefs(al.getSequenceAt(i).getDatasetSequence(), vamsasSeq[i]);
2273         }
2274         if (JSEQ[i].getPdbidsCount() > 0)
2275         {
2276           Pdbids[] ids = JSEQ[i].getPdbids();
2277           for (int p = 0; p < ids.length; p++)
2278           {
2279             jalview.datamodel.PDBEntry entry = new jalview.datamodel.PDBEntry();
2280             entry.setId(ids[p].getId());
2281             entry.setType(ids[p].getType());
2282             if (ids[p].getFile() != null)
2283             {
2284               if (!pdbloaded.containsKey(ids[p].getFile()))
2285               {
2286                 entry.setFile(loadPDBFile(jprovider, ids[p].getId()));
2287               }
2288               else
2289               {
2290                 entry.setFile(pdbloaded.get(ids[p].getId()).toString());
2291               }
2292             }
2293
2294             al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
2295           }
2296         }
2297       }
2298     } // end !multipleview
2299
2300     // ///////////////////////////////
2301     // LOAD SEQUENCE MAPPINGS
2302
2303     if (vamsasSet.getAlcodonFrameCount() > 0)
2304     {
2305       // TODO Potentially this should only be done once for all views of an
2306       // alignment
2307       AlcodonFrame[] alc = vamsasSet.getAlcodonFrame();
2308       for (int i = 0; i < alc.length; i++)
2309       {
2310         jalview.datamodel.AlignedCodonFrame cf = new jalview.datamodel.AlignedCodonFrame(
2311                 alc[i].getAlcodonCount());
2312         if (alc[i].getAlcodonCount() > 0)
2313         {
2314           Alcodon[] alcods = alc[i].getAlcodon();
2315           for (int p = 0; p < cf.codons.length; p++)
2316           {
2317             if (alcods[p].hasPos1() && alcods[p].hasPos2()
2318                     && alcods[p].hasPos3())
2319             {
2320               // translated codons require three valid positions
2321               cf.codons[p] = new int[3];
2322               cf.codons[p][0] = (int) alcods[p].getPos1();
2323               cf.codons[p][1] = (int) alcods[p].getPos2();
2324               cf.codons[p][2] = (int) alcods[p].getPos3();
2325             }
2326             else
2327             {
2328               cf.codons[p] = null;
2329             }
2330           }
2331         }
2332         if (alc[i].getAlcodMapCount() > 0)
2333         {
2334           AlcodMap[] maps = alc[i].getAlcodMap();
2335           for (int m = 0; m < maps.length; m++)
2336           {
2337             SequenceI dnaseq = (SequenceI) seqRefIds
2338                     .get(maps[m].getDnasq());
2339             // Load Mapping
2340             jalview.datamodel.Mapping mapping = null;
2341             // attach to dna sequence reference.
2342             if (maps[m].getMapping() != null)
2343             {
2344               mapping = addMapping(maps[m].getMapping());
2345             }
2346             if (dnaseq != null)
2347             {
2348               cf.addMap(dnaseq, mapping.getTo(), mapping.getMap());
2349             }
2350             else
2351             {
2352               // defer to later
2353               frefedSequence.add(new Object[]
2354               { maps[m].getDnasq(), cf, mapping });
2355             }
2356           }
2357         }
2358         al.addCodonFrame(cf);
2359       }
2360
2361     }
2362
2363     // ////////////////////////////////
2364     // LOAD ANNOTATIONS
2365     ArrayList<JvAnnotRow> autoAlan = new ArrayList<JvAnnotRow>();
2366     /**
2367      * store any annotations which forward reference a group's ID
2368      */
2369     Hashtable<String, ArrayList<jalview.datamodel.AlignmentAnnotation>> groupAnnotRefs = new Hashtable<String, ArrayList<jalview.datamodel.AlignmentAnnotation>>();
2370
2371     if (vamsasSet.getAnnotationCount() > 0)
2372     {
2373       Annotation[] an = vamsasSet.getAnnotation();
2374
2375       for (int i = 0; i < an.length; i++)
2376       {
2377         /**
2378          * test if annotation is automatically calculated for this view only
2379          */
2380         boolean autoForView = false;
2381         if (an[i].getLabel().equals("Quality")
2382                 || an[i].getLabel().equals("Conservation")
2383                 || an[i].getLabel().equals("Consensus"))
2384         {
2385           // Kludge for pre 2.5 projects which lacked the autocalculated flag
2386           autoForView = true;
2387           if (!an[i].hasAutoCalculated())
2388           {
2389             an[i].setAutoCalculated(true);
2390           }
2391         }
2392         if (autoForView
2393                 || (an[i].hasAutoCalculated() && an[i].isAutoCalculated()))
2394         {
2395           // remove ID - we don't recover annotation from other views for
2396           // view-specific annotation
2397           an[i].setId(null);
2398         }
2399
2400         // set visiblity for other annotation in this view
2401         if (an[i].getId() != null
2402                 && annotationIds.containsKey(an[i].getId()))
2403         {
2404           jalview.datamodel.AlignmentAnnotation jda = (jalview.datamodel.AlignmentAnnotation) annotationIds
2405                   .get(an[i].getId());
2406           // in principle Visible should always be true for annotation displayed
2407           // in multiple views
2408           if (an[i].hasVisible())
2409             jda.visible = an[i].getVisible();
2410
2411           al.addAnnotation(jda);
2412
2413           continue;
2414         }
2415         // Construct new annotation from model.
2416         AnnotationElement[] ae = an[i].getAnnotationElement();
2417         jalview.datamodel.Annotation[] anot = null;
2418         java.awt.Color firstColour = null;
2419         int anpos;
2420         if (!an[i].getScoreOnly())
2421         {
2422           anot = new jalview.datamodel.Annotation[al.getWidth()];
2423           for (int aa = 0; aa < ae.length && aa < anot.length; aa++)
2424           {
2425             anpos = ae[aa].getPosition();
2426
2427             if (anpos >= anot.length)
2428               continue;
2429
2430             anot[anpos] = new jalview.datamodel.Annotation(
2431
2432             ae[aa].getDisplayCharacter(), ae[aa].getDescription(),
2433                     (ae[aa].getSecondaryStructure() == null || ae[aa]
2434                             .getSecondaryStructure().length() == 0) ? ' '
2435                             : ae[aa].getSecondaryStructure().charAt(0),
2436                     ae[aa].getValue()
2437
2438             );
2439             // JBPNote: Consider verifying dataflow for IO of secondary
2440             // structure annotation read from Stockholm files
2441             // this was added to try to ensure that
2442             // if (anot[ae[aa].getPosition()].secondaryStructure>' ')
2443             // {
2444             // anot[ae[aa].getPosition()].displayCharacter = "";
2445             // }
2446             anot[anpos].colour = new java.awt.Color(ae[aa].getColour());
2447             if (firstColour == null)
2448             {
2449               firstColour = anot[anpos].colour;
2450             }
2451           }
2452         }
2453         jalview.datamodel.AlignmentAnnotation jaa = null;
2454
2455         if (an[i].getGraph())
2456         {
2457           float llim = 0, hlim = 0;
2458           // if (autoForView || an[i].isAutoCalculated()) {
2459           // hlim=11f;
2460           // }
2461           jaa = new jalview.datamodel.AlignmentAnnotation(an[i].getLabel(),
2462                   an[i].getDescription(), anot, llim, hlim,
2463                   an[i].getGraphType());
2464
2465           jaa.graphGroup = an[i].getGraphGroup();
2466           jaa._linecolour = firstColour;
2467           if (an[i].getThresholdLine() != null)
2468           {
2469             jaa.setThreshold(new jalview.datamodel.GraphLine(an[i]
2470                     .getThresholdLine().getValue(), an[i]
2471                     .getThresholdLine().getLabel(), new java.awt.Color(
2472                     an[i].getThresholdLine().getColour())));
2473
2474           }
2475           if (autoForView || an[i].isAutoCalculated())
2476           {
2477             // Hardwire the symbol display line to ensure that labels for
2478             // histograms are displayed
2479             jaa.hasText = true;
2480           }
2481         }
2482         else
2483         {
2484           jaa = new jalview.datamodel.AlignmentAnnotation(an[i].getLabel(),
2485                   an[i].getDescription(), anot);
2486           jaa._linecolour = firstColour;
2487         }
2488         // register new annotation
2489         if (an[i].getId() != null)
2490         {
2491           annotationIds.put(an[i].getId(), jaa);
2492           jaa.annotationId = an[i].getId();
2493         }
2494         // recover sequence association
2495         if (an[i].getSequenceRef() != null)
2496         {
2497           if (al.findName(an[i].getSequenceRef()) != null)
2498           {
2499             jaa.createSequenceMapping(al.findName(an[i].getSequenceRef()),
2500                     1, true);
2501             al.findName(an[i].getSequenceRef()).addAlignmentAnnotation(jaa);
2502           }
2503         }
2504         // and make a note of any group association
2505         if (an[i].getGroupRef() != null && an[i].getGroupRef().length() > 0)
2506         {
2507           ArrayList<jalview.datamodel.AlignmentAnnotation> aal = groupAnnotRefs
2508                   .get(an[i].getGroupRef());
2509           if (aal == null)
2510           {
2511             aal = new ArrayList<jalview.datamodel.AlignmentAnnotation>();
2512             groupAnnotRefs.put(an[i].getGroupRef(), aal);
2513           }
2514           aal.add(jaa);
2515         }
2516
2517         if (an[i].hasScore())
2518         {
2519           jaa.setScore(an[i].getScore());
2520         }
2521         if (an[i].hasVisible())
2522           jaa.visible = an[i].getVisible();
2523
2524         if (an[i].hasCentreColLabels())
2525           jaa.centreColLabels = an[i].getCentreColLabels();
2526
2527         if (an[i].hasScaleColLabels())
2528         {
2529           jaa.scaleColLabel = an[i].getScaleColLabels();
2530         }
2531         if (an[i].hasAutoCalculated() && an[i].isAutoCalculated())
2532         {
2533           // newer files have an 'autoCalculated' flag and store calculation
2534           // state in viewport properties
2535           jaa.autoCalculated = true; // means annotation will be marked for
2536           // update at end of load.
2537         }
2538         if (an[i].hasGraphHeight())
2539         {
2540           jaa.graphHeight = an[i].getGraphHeight();
2541         }
2542         if (an[i].hasBelowAlignment())
2543         {
2544           jaa.belowAlignment = an[i].isBelowAlignment();
2545         }
2546         jaa.setCalcId(an[i].getCalcId());
2547
2548         if (jaa.autoCalculated)
2549         {
2550           autoAlan.add(new JvAnnotRow(i, jaa));
2551         }
2552         else
2553         // if (!autoForView)
2554         {
2555           // add autocalculated group annotation and any user created annotation
2556           // for the view
2557           al.addAnnotation(jaa);
2558         }
2559       }
2560     }
2561     // ///////////////////////
2562     // LOAD GROUPS
2563     // Create alignment markup and styles for this view
2564     if (jms.getJGroupCount() > 0)
2565     {
2566       JGroup[] groups = jms.getJGroup();
2567       boolean addAnnotSchemeGroup = false;
2568       for (int i = 0; i < groups.length; i++)
2569       {
2570         ColourSchemeI cs = null;
2571
2572         if (groups[i].getColour() != null)
2573         {
2574           if (groups[i].getColour().startsWith("ucs"))
2575           {
2576             cs = GetUserColourScheme(jms, groups[i].getColour());
2577           }
2578           else if (groups[i].getColour().equals("AnnotationColourGradient")
2579                   && groups[i].getAnnotationColours() != null)
2580           {
2581             addAnnotSchemeGroup = true;
2582             cs = null;
2583           }
2584           else
2585           {
2586             cs = ColourSchemeProperty.getColour(al, groups[i].getColour());
2587           }
2588
2589           if (cs != null)
2590           {
2591             cs.setThreshold(groups[i].getPidThreshold(), true);
2592           }
2593         }
2594
2595         Vector seqs = new Vector();
2596
2597         for (int s = 0; s < groups[i].getSeqCount(); s++)
2598         {
2599           String seqId = groups[i].getSeq(s) + "";
2600           jalview.datamodel.SequenceI ts = (jalview.datamodel.SequenceI) seqRefIds
2601                   .get(seqId);
2602
2603           if (ts != null)
2604           {
2605             seqs.addElement(ts);
2606           }
2607         }
2608
2609         if (seqs.size() < 1)
2610         {
2611           continue;
2612         }
2613
2614         jalview.datamodel.SequenceGroup sg = new jalview.datamodel.SequenceGroup(
2615                 seqs, groups[i].getName(), cs, groups[i].getDisplayBoxes(),
2616                 groups[i].getDisplayText(), groups[i].getColourText(),
2617                 groups[i].getStart(), groups[i].getEnd());
2618
2619         sg.setOutlineColour(new java.awt.Color(groups[i].getOutlineColour()));
2620
2621         sg.textColour = new java.awt.Color(groups[i].getTextCol1());
2622         sg.textColour2 = new java.awt.Color(groups[i].getTextCol2());
2623         sg.setShowNonconserved(groups[i].hasShowUnconserved() ? groups[i]
2624                 .isShowUnconserved() : false);
2625         sg.thresholdTextColour = groups[i].getTextColThreshold();
2626         if (groups[i].hasShowConsensusHistogram())
2627         {
2628           sg.setShowConsensusHistogram(groups[i].isShowConsensusHistogram());
2629         }
2630         ;
2631         if (groups[i].hasShowSequenceLogo())
2632         {
2633           sg.setshowSequenceLogo(groups[i].isShowSequenceLogo());
2634         }
2635         if (groups[i].hasNormaliseSequenceLogo())
2636         {
2637           sg.setNormaliseSequenceLogo(groups[i].isNormaliseSequenceLogo());
2638         }
2639         if (groups[i].hasIgnoreGapsinConsensus())
2640         {
2641           sg.setIgnoreGapsConsensus(groups[i].getIgnoreGapsinConsensus());
2642         }
2643         if (groups[i].getConsThreshold() != 0)
2644         {
2645           jalview.analysis.Conservation c = new jalview.analysis.Conservation(
2646                   "All", ResidueProperties.propHash, 3,
2647                   sg.getSequences(null), 0, sg.getWidth() - 1);
2648           c.calculate();
2649           c.verdict(false, 25);
2650           sg.cs.setConservation(c);
2651         }
2652
2653         if (groups[i].getId() != null && groupAnnotRefs.size() > 0)
2654         {
2655           // re-instate unique group/annotation row reference
2656           ArrayList<jalview.datamodel.AlignmentAnnotation> jaal = groupAnnotRefs
2657                   .get(groups[i].getId());
2658           if (jaal != null)
2659           {
2660             for (jalview.datamodel.AlignmentAnnotation jaa : jaal)
2661             {
2662               jaa.groupRef = sg;
2663               if (jaa.autoCalculated)
2664               {
2665                 // match up and try to set group autocalc alignment row for this
2666                 // annotation
2667                 if (jaa.label.startsWith("Consensus for "))
2668                 {
2669                   sg.setConsensus(jaa);
2670                 }
2671                 // match up and try to set group autocalc alignment row for this
2672                 // annotation
2673                 if (jaa.label.startsWith("Conservation for "))
2674                 {
2675                   sg.setConservationRow(jaa);
2676                 }
2677               }
2678             }
2679           }
2680         }
2681         al.addGroup(sg);
2682         if (addAnnotSchemeGroup)
2683         {
2684           // reconstruct the annotation colourscheme
2685           sg.cs = constructAnnotationColour(
2686                   groups[i].getAnnotationColours(), null, al, jms, false);
2687         }
2688       }
2689     }
2690     if (view == null)
2691     {
2692       // only dataset in this model, so just return.
2693       return null;
2694     }
2695     // ///////////////////////////////
2696     // LOAD VIEWPORT
2697
2698     // If we just load in the same jar file again, the sequenceSetId
2699     // will be the same, and we end up with multiple references
2700     // to the same sequenceSet. We must modify this id on load
2701     // so that each load of the file gives a unique id
2702     String uniqueSeqSetId = view.getSequenceSetId() + uniqueSetSuffix;
2703     String viewId = (view.getId() == null ? null : view.getId()
2704             + uniqueSetSuffix);
2705     AlignFrame af = null;
2706     AlignViewport av = null;
2707     // now check to see if we really need to create a new viewport.
2708     if (multipleView && viewportsAdded.size() == 0)
2709     {
2710       // We recovered an alignment for which a viewport already exists.
2711       // TODO: fix up any settings necessary for overlaying stored state onto
2712       // state recovered from another document. (may not be necessary).
2713       // we may need a binding from a viewport in memory to one recovered from
2714       // XML.
2715       // and then recover its containing af to allow the settings to be applied.
2716       // TODO: fix for vamsas demo
2717       System.err
2718               .println("About to recover a viewport for existing alignment: Sequence set ID is "
2719                       + uniqueSeqSetId);
2720       Object seqsetobj = retrieveExistingObj(uniqueSeqSetId);
2721       if (seqsetobj != null)
2722       {
2723         if (seqsetobj instanceof String)
2724         {
2725           uniqueSeqSetId = (String) seqsetobj;
2726           System.err
2727                   .println("Recovered extant sequence set ID mapping for ID : New Sequence set ID is "
2728                           + uniqueSeqSetId);
2729         }
2730         else
2731         {
2732           System.err
2733                   .println("Warning : Collision between sequence set ID string and existing jalview object mapping.");
2734         }
2735
2736       }
2737     }
2738     /**
2739      * indicate that annotation colours are applied across all groups (pre
2740      * Jalview 2.8.1 behaviour)
2741      */
2742     boolean doGroupAnnColour = isVersionStringLaterThan("2.8.1",
2743             object.getVersion());
2744
2745     AlignmentPanel ap = null;
2746     boolean isnewview = true;
2747     if (viewId != null)
2748     {
2749       // Check to see if this alignment already has a view id == viewId
2750       jalview.gui.AlignmentPanel views[] = Desktop
2751               .getAlignmentPanels(uniqueSeqSetId);
2752       if (views != null && views.length > 0)
2753       {
2754         for (int v = 0; v < views.length; v++)
2755         {
2756           if (views[v].av.getViewId().equalsIgnoreCase(viewId))
2757           {
2758             // recover the existing alignpanel, alignframe, viewport
2759             af = views[v].alignFrame;
2760             av = views[v].av;
2761             ap = views[v];
2762             // TODO: could even skip resetting view settings if we don't want to
2763             // change the local settings from other jalview processes
2764             isnewview = false;
2765           }
2766         }
2767       }
2768     }
2769
2770     if (isnewview)
2771     {
2772       af = loadViewport(file, JSEQ, hiddenSeqs, al, jms, view,
2773               uniqueSeqSetId, viewId, autoAlan);
2774       av = af.viewport;
2775       ap = af.alignPanel;
2776     }
2777     // LOAD TREES
2778     // /////////////////////////////////////
2779     if (loadTreesAndStructures && jms.getTreeCount() > 0)
2780     {
2781       try
2782       {
2783         for (int t = 0; t < jms.getTreeCount(); t++)
2784         {
2785
2786           Tree tree = jms.getTree(t);
2787
2788           TreePanel tp = (TreePanel) retrieveExistingObj(tree.getId());
2789           if (tp == null)
2790           {
2791             tp = af.ShowNewickTree(
2792                     new jalview.io.NewickFile(tree.getNewick()),
2793                     tree.getTitle(), tree.getWidth(), tree.getHeight(),
2794                     tree.getXpos(), tree.getYpos());
2795             if (tree.getId() != null)
2796             {
2797               // perhaps bind the tree id to something ?
2798             }
2799           }
2800           else
2801           {
2802             // update local tree attributes ?
2803             // TODO: should check if tp has been manipulated by user - if so its
2804             // settings shouldn't be modified
2805             tp.setTitle(tree.getTitle());
2806             tp.setBounds(new Rectangle(tree.getXpos(), tree.getYpos(), tree
2807                     .getWidth(), tree.getHeight()));
2808             tp.av = av; // af.viewport; // TODO: verify 'associate with all
2809             // views'
2810             // works still
2811             tp.treeCanvas.av = av; // af.viewport;
2812             tp.treeCanvas.ap = ap; // af.alignPanel;
2813
2814           }
2815           if (tp == null)
2816           {
2817             warn("There was a problem recovering stored Newick tree: \n"
2818                     + tree.getNewick());
2819             continue;
2820           }
2821
2822           tp.fitToWindow.setState(tree.getFitToWindow());
2823           tp.fitToWindow_actionPerformed(null);
2824
2825           if (tree.getFontName() != null)
2826           {
2827             tp.setTreeFont(new java.awt.Font(tree.getFontName(), tree
2828                     .getFontStyle(), tree.getFontSize()));
2829           }
2830           else
2831           {
2832             tp.setTreeFont(new java.awt.Font(view.getFontName(), view
2833                     .getFontStyle(), tree.getFontSize()));
2834           }
2835
2836           tp.showPlaceholders(tree.getMarkUnlinked());
2837           tp.showBootstrap(tree.getShowBootstrap());
2838           tp.showDistances(tree.getShowDistances());
2839
2840           tp.treeCanvas.threshold = tree.getThreshold();
2841
2842           if (tree.getCurrentTree())
2843           {
2844             af.viewport.setCurrentTree(tp.getTree());
2845           }
2846         }
2847
2848       } catch (Exception ex)
2849       {
2850         ex.printStackTrace();
2851       }
2852     }
2853
2854     // //LOAD STRUCTURES
2855     if (loadTreesAndStructures)
2856     {
2857       // run through all PDB ids on the alignment, and collect mappings between
2858       // jmol view ids and all sequences referring to it
2859       Hashtable<String, Object[]> jmolViewIds = new Hashtable();
2860
2861       for (int i = 0; i < JSEQ.length; i++)
2862       {
2863         if (JSEQ[i].getPdbidsCount() > 0)
2864         {
2865           Pdbids[] ids = JSEQ[i].getPdbids();
2866           for (int p = 0; p < ids.length; p++)
2867           {
2868             for (int s = 0; s < ids[p].getStructureStateCount(); s++)
2869             {
2870               // check to see if we haven't already created this structure view
2871               String sviewid = (ids[p].getStructureState(s).getViewId() == null) ? null
2872                       : ids[p].getStructureState(s).getViewId()
2873                               + uniqueSetSuffix;
2874               jalview.datamodel.PDBEntry jpdb = new jalview.datamodel.PDBEntry();
2875               // Originally : ids[p].getFile()
2876               // : TODO: verify external PDB file recovery still works in normal
2877               // jalview project load
2878               jpdb.setFile(loadPDBFile(jprovider, ids[p].getId()));
2879               jpdb.setId(ids[p].getId());
2880
2881               int x = ids[p].getStructureState(s).getXpos();
2882               int y = ids[p].getStructureState(s).getYpos();
2883               int width = ids[p].getStructureState(s).getWidth();
2884               int height = ids[p].getStructureState(s).getHeight();
2885
2886               // Probably don't need to do this anymore...
2887               // Desktop.desktop.getComponentAt(x, y);
2888               // TODO: NOW: check that this recovers the PDB file correctly.
2889               String pdbFile = loadPDBFile(jprovider, ids[p].getId());
2890               jalview.datamodel.SequenceI seq = (jalview.datamodel.SequenceI) seqRefIds
2891                       .get(JSEQ[i].getId() + "");
2892               if (sviewid == null)
2893               {
2894                 sviewid = "_jalview_pre2_4_" + x + "," + y + "," + width
2895                         + "," + height;
2896               }
2897               if (!jmolViewIds.containsKey(sviewid))
2898               {
2899                 jmolViewIds.put(sviewid, new Object[]
2900                 { new int[]
2901                 { x, y, width, height }, "",
2902                     new Hashtable<String, Object[]>(), new boolean[]
2903                     { false, false, true } });
2904                 // Legacy pre-2.7 conversion JAL-823 :
2905                 // do not assume any view has to be linked for colour by
2906                 // sequence
2907               }
2908
2909               // assemble String[] { pdb files }, String[] { id for each
2910               // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
2911               // seqs_file 2}, boolean[] {
2912               // linkAlignPanel,superposeWithAlignpanel}} from hash
2913               Object[] jmoldat = jmolViewIds.get(sviewid);
2914               ((boolean[]) jmoldat[3])[0] |= ids[p].getStructureState(s)
2915                       .hasAlignwithAlignPanel() ? ids[p].getStructureState(
2916                       s).getAlignwithAlignPanel() : false;
2917               // never colour by linked panel if not specified
2918               ((boolean[]) jmoldat[3])[1] |= ids[p].getStructureState(s)
2919                       .hasColourwithAlignPanel() ? ids[p]
2920                       .getStructureState(s).getColourwithAlignPanel()
2921                       : false;
2922               // default for pre-2.7 projects is that Jmol colouring is enabled
2923               ((boolean[]) jmoldat[3])[2] &= ids[p].getStructureState(s)
2924                       .hasColourByJmol() ? ids[p].getStructureState(s)
2925                       .getColourByJmol() : true;
2926
2927               if (((String) jmoldat[1]).length() < ids[p]
2928                       .getStructureState(s).getContent().length())
2929               {
2930                 {
2931                   jmoldat[1] = ids[p].getStructureState(s).getContent();
2932                 }
2933               }
2934               if (ids[p].getFile() != null)
2935               {
2936                 File mapkey = new File(ids[p].getFile());
2937                 Object[] seqstrmaps = (Object[]) ((Hashtable) jmoldat[2])
2938                         .get(mapkey);
2939                 if (seqstrmaps == null)
2940                 {
2941                   ((Hashtable) jmoldat[2]).put(mapkey,
2942                           seqstrmaps = new Object[]
2943                           { pdbFile, ids[p].getId(), new Vector(),
2944                               new Vector() });
2945                 }
2946                 if (!((Vector) seqstrmaps[2]).contains(seq))
2947                 {
2948                   ((Vector) seqstrmaps[2]).addElement(seq);
2949                   // ((Vector)seqstrmaps[3]).addElement(n) :
2950                   // in principle, chains
2951                   // should be stored here : do we need to
2952                   // TODO: store and recover seq/pdb_id :
2953                   // chain mappings
2954                 }
2955               }
2956               else
2957               {
2958                 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");
2959                 warn(errorMessage);
2960               }
2961             }
2962           }
2963         }
2964       }
2965       {
2966
2967         // Instantiate the associated Jmol views
2968         for (Entry<String, Object[]> entry : jmolViewIds.entrySet())
2969         {
2970           String sviewid = entry.getKey();
2971           Object[] svattrib = entry.getValue();
2972           int[] geom = (int[]) svattrib[0];
2973           String state = (String) svattrib[1];
2974           Hashtable<File, Object[]> oldFiles = (Hashtable<File, Object[]>) svattrib[2];
2975           final boolean useinJmolsuperpos = ((boolean[]) svattrib[3])[0], usetoColourbyseq = ((boolean[]) svattrib[3])[1], jmolColouring = ((boolean[]) svattrib[3])[2];
2976           int x = geom[0], y = geom[1], width = geom[2], height = geom[3];
2977           // collate the pdbfile -> sequence mappings from this view
2978           Vector<String> pdbfilenames = new Vector<String>();
2979           Vector<SequenceI[]> seqmaps = new Vector<SequenceI[]>();
2980           Vector<String> pdbids = new Vector<String>();
2981
2982           // Search to see if we've already created this Jmol view
2983           AppJmol comp = null;
2984           JInternalFrame[] frames = null;
2985           do
2986           {
2987             try
2988             {
2989               frames = Desktop.desktop.getAllFrames();
2990             } catch (ArrayIndexOutOfBoundsException e)
2991             {
2992               // occasional No such child exceptions are thrown here...
2993               frames = null;
2994               try
2995               {
2996                 Thread.sleep(10);
2997               } catch (Exception f)
2998               {
2999               }
3000               ;
3001             }
3002           } while (frames == null);
3003           // search for any Jmol windows already open from other
3004           // alignment views that exactly match the stored structure state
3005           for (int f = 0; comp == null && f < frames.length; f++)
3006           {
3007             if (frames[f] instanceof AppJmol)
3008             {
3009               if (sviewid != null
3010                       && ((AppJmol) frames[f]).getViewId().equals(sviewid))
3011               {
3012                 // post jalview 2.4 schema includes structure view id
3013                 comp = (AppJmol) frames[f];
3014               }
3015               else if (frames[f].getX() == x && frames[f].getY() == y
3016                       && frames[f].getHeight() == height
3017                       && frames[f].getWidth() == width)
3018               {
3019                 comp = (AppJmol) frames[f];
3020               }
3021             }
3022           }
3023
3024           if (comp == null)
3025           {
3026             // create a new Jmol window.
3027             // First parse the Jmol state to translate filenames loaded into the
3028             // view, and record the order in which files are shown in the Jmol
3029             // view, so we can add the sequence mappings in same order.
3030             StringBuffer newFileLoc = null;
3031             int cp = 0, ncp, ecp;
3032             while ((ncp = state.indexOf("load ", cp)) > -1)
3033             {
3034               if (newFileLoc == null)
3035               {
3036                 newFileLoc = new StringBuffer();
3037               }
3038               do
3039               {
3040                 // look for next filename in load statement
3041                 newFileLoc.append(state.substring(cp,
3042                         ncp = (state.indexOf("\"", ncp + 1) + 1)));
3043                 String oldfilenam = state.substring(ncp,
3044                         ecp = state.indexOf("\"", ncp));
3045                 // recover the new mapping data for this old filename
3046                 // have to normalize filename - since Jmol and jalview do
3047                 // filename
3048                 // translation differently.
3049                 Object[] filedat = oldFiles.get(new File(oldfilenam));
3050                 newFileLoc.append(Platform
3051                         .escapeString((String) filedat[0]));
3052                 pdbfilenames.addElement((String) filedat[0]);
3053                 pdbids.addElement((String) filedat[1]);
3054                 seqmaps.addElement(((Vector<SequenceI>) filedat[2])
3055                         .toArray(new SequenceI[0]));
3056                 newFileLoc.append("\"");
3057                 cp = ecp + 1; // advance beyond last \" and set cursor so we can
3058                               // look for next file statement.
3059               } while ((ncp = state.indexOf("/*file*/", cp)) > -1);
3060             }
3061             if (cp > 0)
3062             {
3063               // just append rest of state
3064               newFileLoc.append(state.substring(cp));
3065             }
3066             else
3067             {
3068               System.err
3069                       .print("Ignoring incomplete Jmol state for PDB ids: ");
3070               newFileLoc = new StringBuffer(state);
3071               newFileLoc.append("; load append ");
3072               for (File id : oldFiles.keySet())
3073               {
3074                 // add this and any other pdb files that should be present in
3075                 // the viewer
3076                 Object[] filedat = oldFiles.get(id);
3077                 String nfilename;
3078                 newFileLoc.append(((String) filedat[0]));
3079                 pdbfilenames.addElement((String) filedat[0]);
3080                 pdbids.addElement((String) filedat[1]);
3081                 seqmaps.addElement(((Vector<SequenceI>) filedat[2])
3082                         .toArray(new SequenceI[0]));
3083                 newFileLoc.append(" \"");
3084                 newFileLoc.append((String) filedat[0]);
3085                 newFileLoc.append("\"");
3086
3087               }
3088               newFileLoc.append(";");
3089             }
3090
3091             if (newFileLoc != null)
3092             {
3093               int histbug = newFileLoc.indexOf("history = ");
3094               histbug += 10;
3095               int diff = histbug == -1 ? -1 : newFileLoc.indexOf(";",
3096                       histbug);
3097               String val = (diff == -1) ? null : newFileLoc.substring(
3098                       histbug, diff);
3099               if (val != null && val.length() >= 4)
3100               {
3101                 if (val.contains("e"))
3102                 {
3103                   if (val.trim().equals("true"))
3104                   {
3105                     val = "1";
3106                   }
3107                   else
3108                   {
3109                     val = "0";
3110                   }
3111                   newFileLoc.replace(histbug, diff, val);
3112                 }
3113               }
3114               // TODO: assemble String[] { pdb files }, String[] { id for each
3115               // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
3116               // seqs_file 2}} from hash
3117               final String[] pdbf = pdbfilenames
3118                       .toArray(new String[pdbfilenames.size()]), id = pdbids
3119                       .toArray(new String[pdbids.size()]);
3120               final SequenceI[][] sq = seqmaps
3121                       .toArray(new SequenceI[seqmaps.size()][]);
3122               final String fileloc = newFileLoc.toString(), vid = sviewid;
3123               final AlignFrame alf = af;
3124               final java.awt.Rectangle rect = new java.awt.Rectangle(x, y,
3125                       width, height);
3126               try
3127               {
3128                 javax.swing.SwingUtilities.invokeAndWait(new Runnable()
3129                 {
3130                   @Override
3131                   public void run()
3132                   {
3133                     AppJmol sview = null;
3134                     try
3135                     {
3136                       sview = new AppJmol(pdbf, id, sq, alf.alignPanel,
3137                               useinJmolsuperpos, usetoColourbyseq,
3138                               jmolColouring, fileloc, rect, vid);
3139                       addNewStructureViewer(sview);
3140                     } catch (OutOfMemoryError ex)
3141                     {
3142                       new OOMWarning("restoring structure view for PDB id "
3143                               + id, (OutOfMemoryError) ex.getCause());
3144                       if (sview != null && sview.isVisible())
3145                       {
3146                         sview.closeViewer();
3147                         sview.setVisible(false);
3148                         sview.dispose();
3149                       }
3150                     }
3151                   }
3152                 });
3153               } catch (InvocationTargetException ex)
3154               {
3155                 warn("Unexpected error when opening Jmol view.", ex);
3156
3157               } catch (InterruptedException e)
3158               {
3159                 // e.printStackTrace();
3160               }
3161             }
3162
3163           }
3164           else
3165           // if (comp != null)
3166           {
3167             // NOTE: if the jalview project is part of a shared session then
3168             // view synchronization should/could be done here.
3169
3170             // add mapping for sequences in this view to an already open Jmol
3171             // instance
3172             for (File id : oldFiles.keySet())
3173             {
3174               // add this and any other pdb files that should be present in the
3175               // viewer
3176               Object[] filedat = oldFiles.get(id);
3177               String pdbFile = (String) filedat[0];
3178               SequenceI[] seq = ((Vector<SequenceI>) filedat[2])
3179                       .toArray(new SequenceI[0]);
3180               comp.jmb.ssm.setMapping(seq, null, pdbFile,
3181                       jalview.io.AppletFormatAdapter.FILE);
3182               comp.jmb.addSequenceForStructFile(pdbFile, seq);
3183             }
3184             // and add the AlignmentPanel's reference to the Jmol view
3185             comp.addAlignmentPanel(ap);
3186             if (useinJmolsuperpos)
3187             {
3188               comp.useAlignmentPanelForSuperposition(ap);
3189             }
3190             else
3191             {
3192               comp.excludeAlignmentPanelForSuperposition(ap);
3193             }
3194             if (usetoColourbyseq)
3195             {
3196               comp.useAlignmentPanelForColourbyseq(ap, !jmolColouring);
3197             }
3198             else
3199             {
3200               comp.excludeAlignmentPanelForColourbyseq(ap);
3201             }
3202           }
3203         }
3204       }
3205     }
3206     // and finally return.
3207     return af;
3208   }
3209
3210   /**
3211    * 
3212    * @param supported - minimum version we are comparing against
3213    * @param version - version of data being processsed.
3214    * @return true if version is development/null or evaluates to the same or
3215    *         later X.Y.Z (where X,Y,Z are like [0-9]+b?[0-9]*)
3216    */
3217   private boolean isVersionStringLaterThan(String supported, String version)
3218   {
3219     if (version == null || version.equalsIgnoreCase("DEVELOPMENT BUILD")
3220             || version.equalsIgnoreCase("Test") || version.equalsIgnoreCase("AUTOMATED BUILD"))
3221     {
3222       System.err.println("Assuming project file with "
3223               + (version == null ? "null" : version)
3224               + " is compatible with Jalview version " + supported);
3225       return true;
3226     }
3227     else
3228     {
3229       StringTokenizer currentV = new StringTokenizer(supported, "."), fileV = new StringTokenizer(
3230               version, ".");
3231       while (currentV.hasMoreTokens() && fileV.hasMoreTokens())
3232       {
3233         // convert b to decimal to catch bugfix releases within a series
3234         String curT = currentV.nextToken().toLowerCase().replace('b', '.');
3235         String fileT = fileV.nextToken().toLowerCase().replace('b', '.');
3236         try
3237         {
3238           if (Float.valueOf(curT) > Float.valueOf(fileT))
3239           {
3240             // current version is newer than the version that wrote the file
3241             return false;
3242           }
3243         } catch (NumberFormatException nfe)
3244         {
3245           System.err
3246                   .println("** WARNING: Version comparison failed for tokens ("
3247                           + curT
3248                           + ") and ("
3249                           + fileT
3250                           + ")\n** Current: '"
3251                           + supported + "' and Version: '" + version + "'");
3252         }
3253       }
3254       if (currentV.hasMoreElements())
3255       {
3256         // fileV has no minor version but identical series to current
3257         return false;
3258       }
3259     }
3260     return true;
3261   }
3262
3263   Vector<AppJmol> newStructureViewers = null;
3264
3265   protected void addNewStructureViewer(AppJmol sview)
3266   {
3267     if (newStructureViewers != null)
3268     {
3269       sview.jmb.setFinishedLoadingFromArchive(false);
3270       newStructureViewers.add(sview);
3271     }
3272   }
3273
3274   protected void setLoadingFinishedForNewStructureViewers()
3275   {
3276     if (newStructureViewers != null)
3277     {
3278       for (AppJmol sview : newStructureViewers)
3279       {
3280         sview.jmb.setFinishedLoadingFromArchive(true);
3281       }
3282       newStructureViewers.clear();
3283       newStructureViewers = null;
3284     }
3285   }
3286
3287   AlignFrame loadViewport(String file, JSeq[] JSEQ, Vector hiddenSeqs,
3288           Alignment al, JalviewModelSequence jms, Viewport view,
3289           String uniqueSeqSetId, String viewId,
3290           ArrayList<JvAnnotRow> autoAlan)
3291   {
3292     AlignFrame af = null;
3293     af = new AlignFrame(al, view.getWidth(), view.getHeight(),
3294             uniqueSeqSetId, viewId);
3295
3296     af.setFileName(file, "Jalview");
3297
3298     for (int i = 0; i < JSEQ.length; i++)
3299     {
3300       af.viewport.setSequenceColour(af.viewport.getAlignment()
3301               .getSequenceAt(i), new java.awt.Color(JSEQ[i].getColour()));
3302     }
3303
3304     af.viewport.gatherViewsHere = view.getGatheredViews();
3305
3306     if (view.getSequenceSetId() != null)
3307     {
3308       jalview.gui.AlignViewport av = (jalview.gui.AlignViewport) viewportsAdded
3309               .get(uniqueSeqSetId);
3310
3311       af.viewport.setSequenceSetId(uniqueSeqSetId);
3312       if (av != null)
3313       {
3314         // propagate shared settings to this new view
3315         af.viewport.historyList = av.historyList;
3316         af.viewport.redoList = av.redoList;
3317       }
3318       else
3319       {
3320         viewportsAdded.put(uniqueSeqSetId, af.viewport);
3321       }
3322       // TODO: check if this method can be called repeatedly without
3323       // side-effects if alignpanel already registered.
3324       PaintRefresher.Register(af.alignPanel, uniqueSeqSetId);
3325     }
3326     // apply Hidden regions to view.
3327     if (hiddenSeqs != null)
3328     {
3329       for (int s = 0; s < JSEQ.length; s++)
3330       {
3331         jalview.datamodel.SequenceGroup hidden = new jalview.datamodel.SequenceGroup();
3332
3333         for (int r = 0; r < JSEQ[s].getHiddenSequencesCount(); r++)
3334         {
3335           hidden.addSequence(
3336                   al.getSequenceAt(JSEQ[s].getHiddenSequences(r)), false);
3337         }
3338         af.viewport.hideRepSequences(al.getSequenceAt(s), hidden);
3339       }
3340
3341       jalview.datamodel.SequenceI[] hseqs = new jalview.datamodel.SequenceI[hiddenSeqs
3342               .size()];
3343
3344       for (int s = 0; s < hiddenSeqs.size(); s++)
3345       {
3346         hseqs[s] = (jalview.datamodel.SequenceI) hiddenSeqs.elementAt(s);
3347       }
3348
3349       af.viewport.hideSequence(hseqs);
3350
3351     }
3352     // recover view properties and display parameters
3353     if (view.getViewName() != null)
3354     {
3355       af.viewport.viewName = view.getViewName();
3356       af.setInitialTabVisible();
3357     }
3358     af.setBounds(view.getXpos(), view.getYpos(), view.getWidth(),
3359             view.getHeight());
3360
3361     af.viewport.setShowAnnotation(view.getShowAnnotation());
3362     af.viewport.setAbovePIDThreshold(view.getPidSelected());
3363
3364     af.viewport.setColourText(view.getShowColourText());
3365
3366     af.viewport.setConservationSelected(view.getConservationSelected());
3367     af.viewport.setShowJVSuffix(view.getShowFullId());
3368     af.viewport.rightAlignIds = view.getRightAlignIds();
3369     af.viewport.setFont(new java.awt.Font(view.getFontName(), view
3370             .getFontStyle(), view.getFontSize()));
3371     af.alignPanel.fontChanged();
3372     af.viewport.setRenderGaps(view.getRenderGaps());
3373     af.viewport.setWrapAlignment(view.getWrapAlignment());
3374     af.alignPanel.setWrapAlignment(view.getWrapAlignment());
3375     af.viewport.setShowAnnotation(view.getShowAnnotation());
3376     af.alignPanel.setAnnotationVisible(view.getShowAnnotation());
3377
3378     af.viewport.setShowBoxes(view.getShowBoxes());
3379
3380     af.viewport.setShowText(view.getShowText());
3381
3382     af.viewport.textColour = new java.awt.Color(view.getTextCol1());
3383     af.viewport.textColour2 = new java.awt.Color(view.getTextCol2());
3384     af.viewport.thresholdTextColour = view.getTextColThreshold();
3385     af.viewport.setShowUnconserved(view.hasShowUnconserved() ? view
3386             .isShowUnconserved() : false);
3387     af.viewport.setStartRes(view.getStartRes());
3388     af.viewport.setStartSeq(view.getStartSeq());
3389
3390     ColourSchemeI cs = null;
3391     // apply colourschemes
3392     if (view.getBgColour() != null)
3393     {
3394       if (view.getBgColour().startsWith("ucs"))
3395       {
3396         cs = GetUserColourScheme(jms, view.getBgColour());
3397       }
3398       else if (view.getBgColour().startsWith("Annotation"))
3399       {
3400         AnnotationColours viewAnnColour = view.getAnnotationColours();
3401         cs = constructAnnotationColour(viewAnnColour, af, al, jms, true);
3402
3403         // annpos
3404
3405       }
3406       else
3407       {
3408         cs = ColourSchemeProperty.getColour(al, view.getBgColour());
3409       }
3410
3411       if (cs != null)
3412       {
3413         cs.setThreshold(view.getPidThreshold(), true);
3414         cs.setConsensus(af.viewport.getSequenceConsensusHash());
3415       }
3416     }
3417
3418     af.viewport.setGlobalColourScheme(cs);
3419     af.viewport.setColourAppliesToAllGroups(false);
3420
3421     if (view.getConservationSelected() && cs != null)
3422     {
3423       cs.setConservationInc(view.getConsThreshold());
3424     }
3425
3426     af.changeColour(cs);
3427
3428     af.viewport.setColourAppliesToAllGroups(true);
3429
3430     if (view.getShowSequenceFeatures())
3431     {
3432       af.viewport.showSequenceFeatures = true;
3433     }
3434     if (view.hasCentreColumnLabels())
3435     {
3436       af.viewport.setCentreColumnLabels(view.getCentreColumnLabels());
3437     }
3438     if (view.hasIgnoreGapsinConsensus())
3439     {
3440       af.viewport.setIgnoreGapsConsensus(view.getIgnoreGapsinConsensus(),
3441               null);
3442     }
3443     if (view.hasFollowHighlight())
3444     {
3445       af.viewport.followHighlight = view.getFollowHighlight();
3446     }
3447     if (view.hasFollowSelection())
3448     {
3449       af.viewport.followSelection = view.getFollowSelection();
3450     }
3451     if (view.hasShowConsensusHistogram())
3452     {
3453       af.viewport.setShowConsensusHistogram(view
3454               .getShowConsensusHistogram());
3455     }
3456     else
3457     {
3458       af.viewport.setShowConsensusHistogram(true);
3459     }
3460     if (view.hasShowSequenceLogo())
3461     {
3462       af.viewport.setShowSequenceLogo(view.getShowSequenceLogo());
3463     }
3464     else
3465     {
3466       af.viewport.setShowSequenceLogo(false);
3467     }
3468     if (view.hasNormaliseSequenceLogo())
3469     {
3470       af.viewport.setNormaliseSequenceLogo(view.getNormaliseSequenceLogo());
3471     }
3472     if (view.hasShowDbRefTooltip())
3473     {
3474       af.viewport.setShowDbRefs(view.getShowDbRefTooltip());
3475     }
3476     if (view.hasShowNPfeatureTooltip())
3477     {
3478       af.viewport.setShowNpFeats(view.hasShowNPfeatureTooltip());
3479     }
3480     if (view.hasShowGroupConsensus())
3481     {
3482       af.viewport.setShowGroupConsensus(view.getShowGroupConsensus());
3483     }
3484     else
3485     {
3486       af.viewport.setShowGroupConsensus(false);
3487     }
3488     if (view.hasShowGroupConservation())
3489     {
3490       af.viewport.setShowGroupConservation(view.getShowGroupConservation());
3491     }
3492     else
3493     {
3494       af.viewport.setShowGroupConservation(false);
3495     }
3496
3497     // recover featre settings
3498     if (jms.getFeatureSettings() != null)
3499     {
3500       af.viewport.featuresDisplayed = new Hashtable();
3501       String[] renderOrder = new String[jms.getFeatureSettings()
3502               .getSettingCount()];
3503       for (int fs = 0; fs < jms.getFeatureSettings().getSettingCount(); fs++)
3504       {
3505         Setting setting = jms.getFeatureSettings().getSetting(fs);
3506         if (setting.hasMincolour())
3507         {
3508           GraduatedColor gc = setting.hasMin() ? new GraduatedColor(
3509                   new java.awt.Color(setting.getMincolour()),
3510                   new java.awt.Color(setting.getColour()),
3511                   setting.getMin(), setting.getMax()) : new GraduatedColor(
3512                   new java.awt.Color(setting.getMincolour()),
3513                   new java.awt.Color(setting.getColour()), 0, 1);
3514           if (setting.hasThreshold())
3515           {
3516             gc.setThresh(setting.getThreshold());
3517             gc.setThreshType(setting.getThreshstate());
3518           }
3519           gc.setAutoScaled(true); // default
3520           if (setting.hasAutoScale())
3521           {
3522             gc.setAutoScaled(setting.getAutoScale());
3523           }
3524           if (setting.hasColourByLabel())
3525           {
3526             gc.setColourByLabel(setting.getColourByLabel());
3527           }
3528           // and put in the feature colour table.
3529           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setColour(
3530                   setting.getType(), gc);
3531         }
3532         else
3533         {
3534           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setColour(
3535                   setting.getType(),
3536                   new java.awt.Color(setting.getColour()));
3537         }
3538         renderOrder[fs] = setting.getType();
3539         if (setting.hasOrder())
3540           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setOrder(
3541                   setting.getType(), setting.getOrder());
3542         else
3543           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setOrder(
3544                   setting.getType(),
3545                   fs / jms.getFeatureSettings().getSettingCount());
3546         if (setting.getDisplay())
3547         {
3548           af.viewport.featuresDisplayed.put(setting.getType(), new Integer(
3549                   setting.getColour()));
3550         }
3551       }
3552       af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().renderOrder = renderOrder;
3553       Hashtable fgtable;
3554       af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().featureGroups = fgtable = new Hashtable();
3555       for (int gs = 0; gs < jms.getFeatureSettings().getGroupCount(); gs++)
3556       {
3557         Group grp = jms.getFeatureSettings().getGroup(gs);
3558         fgtable.put(grp.getName(), new Boolean(grp.getDisplay()));
3559       }
3560     }
3561
3562     if (view.getHiddenColumnsCount() > 0)
3563     {
3564       for (int c = 0; c < view.getHiddenColumnsCount(); c++)
3565       {
3566         af.viewport.hideColumns(view.getHiddenColumns(c).getStart(), view
3567                 .getHiddenColumns(c).getEnd() // +1
3568                 );
3569       }
3570     }
3571     if (view.getCalcIdParam() != null)
3572     {
3573       for (CalcIdParam calcIdParam : view.getCalcIdParam())
3574       {
3575         if (calcIdParam != null)
3576         {
3577           if (recoverCalcIdParam(calcIdParam, af.viewport))
3578           {
3579           }
3580           else
3581           {
3582             warn("Couldn't recover parameters for "
3583                     + calcIdParam.getCalcId());
3584           }
3585         }
3586       }
3587     }
3588     af.setMenusFromViewport(af.viewport);
3589     // TODO: we don't need to do this if the viewport is aready visible.
3590     Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(),
3591             view.getHeight());
3592     af.alignPanel.updateAnnotation(false, true); // recompute any autoannotation
3593     reorderAutoannotation(af, al, autoAlan);
3594     af.alignPanel.alignmentChanged();
3595     return af;
3596   }
3597
3598   private ColourSchemeI constructAnnotationColour(
3599           AnnotationColours viewAnnColour, AlignFrame af, Alignment al,
3600           JalviewModelSequence jms, boolean checkGroupAnnColour)
3601   {
3602     boolean propagateAnnColour = false;
3603     ColourSchemeI cs = null;
3604     AlignmentI annAlignment = af != null ? af.viewport.getAlignment() : al;
3605     if (checkGroupAnnColour && al.getGroups() != null
3606             && al.getGroups().size() > 0)
3607     {
3608       // pre 2.8.1 behaviour
3609       // check to see if we should transfer annotation colours
3610       propagateAnnColour = true;
3611       for (jalview.datamodel.SequenceGroup sg : al.getGroups())
3612       {
3613         if (sg.cs instanceof AnnotationColourGradient)
3614         {
3615           propagateAnnColour = false;
3616         }
3617       }
3618     }
3619     // int find annotation
3620     if (annAlignment.getAlignmentAnnotation() != null)
3621     {
3622       for (int i = 0; i < annAlignment.getAlignmentAnnotation().length; i++)
3623       {
3624         if (annAlignment.getAlignmentAnnotation()[i].label
3625                 .equals(viewAnnColour.getAnnotation()))
3626         {
3627           if (annAlignment.getAlignmentAnnotation()[i].getThreshold() == null)
3628           {
3629             annAlignment.getAlignmentAnnotation()[i]
3630                     .setThreshold(new jalview.datamodel.GraphLine(
3631                             viewAnnColour.getThreshold(), "Threshold",
3632                             java.awt.Color.black)
3633
3634                     );
3635           }
3636
3637           if (viewAnnColour.getColourScheme().equals("None"))
3638           {
3639             cs = new AnnotationColourGradient(
3640                     annAlignment.getAlignmentAnnotation()[i],
3641                     new java.awt.Color(viewAnnColour.getMinColour()),
3642                     new java.awt.Color(viewAnnColour.getMaxColour()),
3643                     viewAnnColour.getAboveThreshold());
3644           }
3645           else if (viewAnnColour.getColourScheme().startsWith("ucs"))
3646           {
3647             cs = new AnnotationColourGradient(
3648                     annAlignment.getAlignmentAnnotation()[i],
3649                     GetUserColourScheme(jms,
3650                             viewAnnColour.getColourScheme()),
3651                     viewAnnColour.getAboveThreshold());
3652           }
3653           else
3654           {
3655             cs = new AnnotationColourGradient(
3656                     annAlignment.getAlignmentAnnotation()[i],
3657                     ColourSchemeProperty.getColour(al,
3658                             viewAnnColour.getColourScheme()),
3659                     viewAnnColour.getAboveThreshold());
3660           }
3661           if (viewAnnColour.hasPerSequence())
3662           {
3663             ((AnnotationColourGradient) cs).setSeqAssociated(viewAnnColour
3664                     .isPerSequence());
3665           }
3666           if (viewAnnColour.hasPredefinedColours())
3667           {
3668             ((AnnotationColourGradient) cs)
3669                     .setPredefinedColours(viewAnnColour
3670                             .isPredefinedColours());
3671           }
3672           if (propagateAnnColour && al.getGroups() != null)
3673           {
3674             // Also use these settings for all the groups
3675             for (int g = 0; g < al.getGroups().size(); g++)
3676             {
3677               jalview.datamodel.SequenceGroup sg = al.getGroups().get(g);
3678
3679               if (sg.cs == null)
3680               {
3681                 continue;
3682               }
3683
3684               /*
3685                * if (viewAnnColour.getColourScheme().equals("None" )) { sg.cs =
3686                * new AnnotationColourGradient(
3687                * annAlignment.getAlignmentAnnotation()[i], new
3688                * java.awt.Color(viewAnnColour. getMinColour()), new
3689                * java.awt.Color(viewAnnColour. getMaxColour()),
3690                * viewAnnColour.getAboveThreshold()); } else
3691                */
3692               {
3693                 sg.cs = new AnnotationColourGradient(
3694                         annAlignment.getAlignmentAnnotation()[i], sg.cs,
3695                         viewAnnColour.getAboveThreshold());
3696                 if (cs instanceof AnnotationColourGradient)
3697                 {
3698                   if (viewAnnColour.hasPerSequence())
3699                   {
3700                     ((AnnotationColourGradient) cs)
3701                             .setSeqAssociated(viewAnnColour.isPerSequence());
3702                   }
3703                   if (viewAnnColour.hasPredefinedColours())
3704                   {
3705                     ((AnnotationColourGradient) cs)
3706                             .setPredefinedColours(viewAnnColour
3707                                     .isPredefinedColours());
3708                   }
3709                 }
3710               }
3711
3712             }
3713           }
3714
3715           break;
3716         }
3717
3718       }
3719     }
3720     return cs;
3721   }
3722
3723   private void reorderAutoannotation(AlignFrame af, Alignment al,
3724           ArrayList<JvAnnotRow> autoAlan)
3725   {
3726     // copy over visualization settings for autocalculated annotation in the
3727     // view
3728     if (al.getAlignmentAnnotation() != null)
3729     {
3730       /**
3731        * Kludge for magic autoannotation names (see JAL-811)
3732        */
3733       String[] magicNames = new String[]
3734       { "Consensus", "Quality", "Conservation" };
3735       JvAnnotRow nullAnnot = new JvAnnotRow(-1, null);
3736       Hashtable<String, JvAnnotRow> visan = new Hashtable<String, JvAnnotRow>();
3737       for (String nm : magicNames)
3738       {
3739         visan.put(nm, nullAnnot);
3740       }
3741       for (JvAnnotRow auan : autoAlan)
3742       {
3743         visan.put(auan.template.label
3744                 + (auan.template.getCalcId() == null ? "" : "\t"
3745                         + auan.template.getCalcId()), auan);
3746       }
3747       int hSize = al.getAlignmentAnnotation().length;
3748       ArrayList<JvAnnotRow> reorder = new ArrayList<JvAnnotRow>();
3749       // work through any autoCalculated annotation already on the view
3750       // removing it if it should be placed in a different location on the
3751       // annotation panel.
3752       List<String> remains = new ArrayList(visan.keySet());
3753       for (int h = 0; h < hSize; h++)
3754       {
3755         jalview.datamodel.AlignmentAnnotation jalan = al
3756                 .getAlignmentAnnotation()[h];
3757         if (jalan.autoCalculated)
3758         {
3759           String k;
3760           JvAnnotRow valan = visan.get(k = jalan.label);
3761           if (jalan.getCalcId() != null)
3762           {
3763             valan = visan.get(k = jalan.label + "\t" + jalan.getCalcId());
3764           }
3765
3766           if (valan != null)
3767           {
3768             // delete the auto calculated row from the alignment
3769             al.deleteAnnotation(jalan, false);
3770             remains.remove(k);
3771             hSize--;
3772             h--;
3773             if (valan != nullAnnot)
3774             {
3775               if (jalan != valan.template)
3776               {
3777                 // newly created autoannotation row instance
3778                 // so keep a reference to the visible annotation row
3779                 // and copy over all relevant attributes
3780                 if (valan.template.graphHeight >= 0)
3781
3782                 {
3783                   jalan.graphHeight = valan.template.graphHeight;
3784                 }
3785                 jalan.visible = valan.template.visible;
3786               }
3787               reorder.add(new JvAnnotRow(valan.order, jalan));
3788             }
3789           }
3790         }
3791       }
3792       // Add any (possibly stale) autocalculated rows that were not appended to
3793       // the view during construction
3794       for (String other : remains)
3795       {
3796         JvAnnotRow othera = visan.get(other);
3797         if (othera != nullAnnot && othera.template.getCalcId() != null
3798                 && othera.template.getCalcId().length() > 0)
3799         {
3800           reorder.add(othera);
3801         }
3802       }
3803       // now put the automatic annotation in its correct place
3804       int s = 0, srt[] = new int[reorder.size()];
3805       JvAnnotRow[] rws = new JvAnnotRow[reorder.size()];
3806       for (JvAnnotRow jvar : reorder)
3807       {
3808         rws[s] = jvar;
3809         srt[s++] = jvar.order;
3810       }
3811       reorder.clear();
3812       jalview.util.QuickSort.sort(srt, rws);
3813       // and re-insert the annotation at its correct position
3814       for (JvAnnotRow jvar : rws)
3815       {
3816         al.addAnnotation(jvar.template, jvar.order);
3817       }
3818       af.alignPanel.adjustAnnotationHeight();
3819     }
3820   }
3821
3822   Hashtable skipList = null;
3823
3824   /**
3825    * TODO remove this method
3826    * 
3827    * @param view
3828    * @return AlignFrame bound to sequenceSetId from view, if one exists. private
3829    *         AlignFrame getSkippedFrame(Viewport view) { if (skipList==null) {
3830    *         throw new Error("Implementation Error. No skipList defined for this
3831    *         Jalview2XML instance."); } return (AlignFrame)
3832    *         skipList.get(view.getSequenceSetId()); }
3833    */
3834
3835   /**
3836    * Check if the Jalview view contained in object should be skipped or not.
3837    * 
3838    * @param object
3839    * @return true if view's sequenceSetId is a key in skipList
3840    */
3841   private boolean skipViewport(JalviewModel object)
3842   {
3843     if (skipList == null)
3844     {
3845       return false;
3846     }
3847     String id;
3848     if (skipList.containsKey(id = object.getJalviewModelSequence()
3849             .getViewport()[0].getSequenceSetId()))
3850     {
3851       if (Cache.log != null && Cache.log.isDebugEnabled())
3852       {
3853         Cache.log.debug("Skipping seuqence set id " + id);
3854       }
3855       return true;
3856     }
3857     return false;
3858   }
3859
3860   public void AddToSkipList(AlignFrame af)
3861   {
3862     if (skipList == null)
3863     {
3864       skipList = new Hashtable();
3865     }
3866     skipList.put(af.getViewport().getSequenceSetId(), af);
3867   }
3868
3869   public void clearSkipList()
3870   {
3871     if (skipList != null)
3872     {
3873       skipList.clear();
3874       skipList = null;
3875     }
3876   }
3877
3878   private void recoverDatasetFor(SequenceSet vamsasSet, Alignment al)
3879   {
3880     jalview.datamodel.Alignment ds = getDatasetFor(vamsasSet.getDatasetId());
3881     Vector dseqs = null;
3882     if (ds == null)
3883     {
3884       // create a list of new dataset sequences
3885       dseqs = new Vector();
3886     }
3887     for (int i = 0, iSize = vamsasSet.getSequenceCount(); i < iSize; i++)
3888     {
3889       Sequence vamsasSeq = vamsasSet.getSequence(i);
3890       ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs);
3891     }
3892     // create a new dataset
3893     if (ds == null)
3894     {
3895       SequenceI[] dsseqs = new SequenceI[dseqs.size()];
3896       dseqs.copyInto(dsseqs);
3897       ds = new jalview.datamodel.Alignment(dsseqs);
3898       debug("Created new dataset " + vamsasSet.getDatasetId()
3899               + " for alignment " + System.identityHashCode(al));
3900       addDatasetRef(vamsasSet.getDatasetId(), ds);
3901     }
3902     // set the dataset for the newly imported alignment.
3903     if (al.getDataset() == null)
3904     {
3905       al.setDataset(ds);
3906     }
3907   }
3908
3909   /**
3910    * 
3911    * @param vamsasSeq
3912    *          sequence definition to create/merge dataset sequence for
3913    * @param ds
3914    *          dataset alignment
3915    * @param dseqs
3916    *          vector to add new dataset sequence to
3917    */
3918   private void ensureJalviewDatasetSequence(Sequence vamsasSeq,
3919           AlignmentI ds, Vector dseqs)
3920   {
3921     // JBP TODO: Check this is called for AlCodonFrames to support recovery of
3922     // xRef Codon Maps
3923     jalview.datamodel.Sequence sq = (jalview.datamodel.Sequence) seqRefIds
3924             .get(vamsasSeq.getId());
3925     jalview.datamodel.SequenceI dsq = null;
3926     if (sq != null && sq.getDatasetSequence() != null)
3927     {
3928       dsq = sq.getDatasetSequence();
3929     }
3930
3931     String sqid = vamsasSeq.getDsseqid();
3932     if (dsq == null)
3933     {
3934       // need to create or add a new dataset sequence reference to this sequence
3935       if (sqid != null)
3936       {
3937         dsq = (jalview.datamodel.SequenceI) seqRefIds.get(sqid);
3938       }
3939       // check again
3940       if (dsq == null)
3941       {
3942         // make a new dataset sequence
3943         dsq = sq.createDatasetSequence();
3944         if (sqid == null)
3945         {
3946           // make up a new dataset reference for this sequence
3947           sqid = seqHash(dsq);
3948         }
3949         dsq.setVamsasId(uniqueSetSuffix + sqid);
3950         seqRefIds.put(sqid, dsq);
3951         if (ds == null)
3952         {
3953           if (dseqs != null)
3954           {
3955             dseqs.addElement(dsq);
3956           }
3957         }
3958         else
3959         {
3960           ds.addSequence(dsq);
3961         }
3962       }
3963       else
3964       {
3965         if (sq != dsq)
3966         { // make this dataset sequence sq's dataset sequence
3967           sq.setDatasetSequence(dsq);
3968           // and update the current dataset alignment
3969           if (ds == null)
3970           {
3971             if (dseqs != null)
3972             {
3973               if (!dseqs.contains(dsq))
3974               {
3975                 dseqs.add(dsq);
3976               }
3977             }
3978             else
3979             {
3980               if (ds.findIndex(dsq) < 0)
3981               {
3982                 ds.addSequence(dsq);
3983               }
3984             }
3985           }
3986         }
3987       }
3988     }
3989     // TODO: refactor this as a merge dataset sequence function
3990     // now check that sq (the dataset sequence) sequence really is the union of
3991     // all references to it
3992     // boolean pre = sq.getStart() < dsq.getStart();
3993     // boolean post = sq.getEnd() > dsq.getEnd();
3994     // if (pre || post)
3995     if (sq != dsq)
3996     {
3997       StringBuffer sb = new StringBuffer();
3998       String newres = jalview.analysis.AlignSeq.extractGaps(
3999               jalview.util.Comparison.GapChars, sq.getSequenceAsString());
4000       if (!newres.equalsIgnoreCase(dsq.getSequenceAsString())
4001               && newres.length() > dsq.getLength())
4002       {
4003         // Update with the longer sequence.
4004         synchronized (dsq)
4005         {
4006           /*
4007            * if (pre) { sb.insert(0, newres .substring(0, dsq.getStart() -
4008            * sq.getStart())); dsq.setStart(sq.getStart()); } if (post) {
4009            * sb.append(newres.substring(newres.length() - sq.getEnd() -
4010            * dsq.getEnd())); dsq.setEnd(sq.getEnd()); }
4011            */
4012           dsq.setSequence(newres);
4013         }
4014         // TODO: merges will never happen if we 'know' we have the real dataset
4015         // sequence - this should be detected when id==dssid
4016         System.err
4017                 .println("DEBUG Notice:  Merged dataset sequence (if you see this often, post at http://issues.jalview.org/browse/JAL-1474)"); // ("
4018         // + (pre ? "prepended" : "") + " "
4019         // + (post ? "appended" : ""));
4020       }
4021     }
4022   }
4023
4024   java.util.Hashtable datasetIds = null;
4025
4026   java.util.IdentityHashMap dataset2Ids = null;
4027
4028   private Alignment getDatasetFor(String datasetId)
4029   {
4030     if (datasetIds == null)
4031     {
4032       datasetIds = new Hashtable();
4033       return null;
4034     }
4035     if (datasetIds.containsKey(datasetId))
4036     {
4037       return (Alignment) datasetIds.get(datasetId);
4038     }
4039     return null;
4040   }
4041
4042   private void addDatasetRef(String datasetId, Alignment dataset)
4043   {
4044     if (datasetIds == null)
4045     {
4046       datasetIds = new Hashtable();
4047     }
4048     datasetIds.put(datasetId, dataset);
4049   }
4050
4051   /**
4052    * make a new dataset ID for this jalview dataset alignment
4053    * 
4054    * @param dataset
4055    * @return
4056    */
4057   private String getDatasetIdRef(jalview.datamodel.Alignment dataset)
4058   {
4059     if (dataset.getDataset() != null)
4060     {
4061       warn("Serious issue!  Dataset Object passed to getDatasetIdRef is not a Jalview DATASET alignment...");
4062     }
4063     String datasetId = makeHashCode(dataset, null);
4064     if (datasetId == null)
4065     {
4066       // make a new datasetId and record it
4067       if (dataset2Ids == null)
4068       {
4069         dataset2Ids = new IdentityHashMap();
4070       }
4071       else
4072       {
4073         datasetId = (String) dataset2Ids.get(dataset);
4074       }
4075       if (datasetId == null)
4076       {
4077         datasetId = "ds" + dataset2Ids.size() + 1;
4078         dataset2Ids.put(dataset, datasetId);
4079       }
4080     }
4081     return datasetId;
4082   }
4083
4084   private void addDBRefs(SequenceI datasetSequence, Sequence sequence)
4085   {
4086     for (int d = 0; d < sequence.getDBRefCount(); d++)
4087     {
4088       DBRef dr = sequence.getDBRef(d);
4089       jalview.datamodel.DBRefEntry entry = new jalview.datamodel.DBRefEntry(
4090               sequence.getDBRef(d).getSource(), sequence.getDBRef(d)
4091                       .getVersion(), sequence.getDBRef(d).getAccessionId());
4092       if (dr.getMapping() != null)
4093       {
4094         entry.setMap(addMapping(dr.getMapping()));
4095       }
4096       datasetSequence.addDBRef(entry);
4097     }
4098   }
4099
4100   private jalview.datamodel.Mapping addMapping(Mapping m)
4101   {
4102     SequenceI dsto = null;
4103     // Mapping m = dr.getMapping();
4104     int fr[] = new int[m.getMapListFromCount() * 2];
4105     Enumeration f = m.enumerateMapListFrom();
4106     for (int _i = 0; f.hasMoreElements(); _i += 2)
4107     {
4108       MapListFrom mf = (MapListFrom) f.nextElement();
4109       fr[_i] = mf.getStart();
4110       fr[_i + 1] = mf.getEnd();
4111     }
4112     int fto[] = new int[m.getMapListToCount() * 2];
4113     f = m.enumerateMapListTo();
4114     for (int _i = 0; f.hasMoreElements(); _i += 2)
4115     {
4116       MapListTo mf = (MapListTo) f.nextElement();
4117       fto[_i] = mf.getStart();
4118       fto[_i + 1] = mf.getEnd();
4119     }
4120     jalview.datamodel.Mapping jmap = new jalview.datamodel.Mapping(dsto,
4121             fr, fto, (int) m.getMapFromUnit(), (int) m.getMapToUnit());
4122     if (m.getMappingChoice() != null)
4123     {
4124       MappingChoice mc = m.getMappingChoice();
4125       if (mc.getDseqFor() != null)
4126       {
4127         String dsfor = "" + mc.getDseqFor();
4128         if (seqRefIds.containsKey(dsfor))
4129         {
4130           /**
4131            * recover from hash
4132            */
4133           jmap.setTo((SequenceI) seqRefIds.get(dsfor));
4134         }
4135         else
4136         {
4137           frefedSequence.add(new Object[]
4138           { dsfor, jmap });
4139         }
4140       }
4141       else
4142       {
4143         /**
4144          * local sequence definition
4145          */
4146         Sequence ms = mc.getSequence();
4147         jalview.datamodel.Sequence djs = null;
4148         String sqid = ms.getDsseqid();
4149         if (sqid != null && sqid.length() > 0)
4150         {
4151           /*
4152            * recover dataset sequence
4153            */
4154           djs = (jalview.datamodel.Sequence) seqRefIds.get(sqid);
4155         }
4156         else
4157         {
4158           System.err
4159                   .println("Warning - making up dataset sequence id for DbRef sequence map reference");
4160           sqid = ((Object) ms).toString(); // make up a new hascode for
4161           // undefined dataset sequence hash
4162           // (unlikely to happen)
4163         }
4164
4165         if (djs == null)
4166         {
4167           /**
4168            * make a new dataset sequence and add it to refIds hash
4169            */
4170           djs = new jalview.datamodel.Sequence(ms.getName(),
4171                   ms.getSequence());
4172           djs.setStart(jmap.getMap().getToLowest());
4173           djs.setEnd(jmap.getMap().getToHighest());
4174           djs.setVamsasId(uniqueSetSuffix + sqid);
4175           jmap.setTo(djs);
4176           seqRefIds.put(sqid, djs);
4177
4178         }
4179         jalview.bin.Cache.log.debug("about to recurse on addDBRefs.");
4180         addDBRefs(djs, ms);
4181
4182       }
4183     }
4184     return (jmap);
4185
4186   }
4187
4188   public jalview.gui.AlignmentPanel copyAlignPanel(AlignmentPanel ap,
4189           boolean keepSeqRefs)
4190   {
4191     initSeqRefs();
4192     jalview.schemabinding.version2.JalviewModel jm = SaveState(ap, null,
4193             null);
4194
4195     if (!keepSeqRefs)
4196     {
4197       clearSeqRefs();
4198       jm.getJalviewModelSequence().getViewport(0).setSequenceSetId(null);
4199     }
4200     else
4201     {
4202       uniqueSetSuffix = "";
4203       jm.getJalviewModelSequence().getViewport(0).setId(null); // we don't
4204       // overwrite the
4205       // view we just
4206       // copied
4207     }
4208     if (this.frefedSequence == null)
4209     {
4210       frefedSequence = new Vector();
4211     }
4212
4213     viewportsAdded = new Hashtable();
4214
4215     AlignFrame af = LoadFromObject(jm, null, false, null);
4216     af.alignPanels.clear();
4217     af.closeMenuItem_actionPerformed(true);
4218
4219     /*
4220      * if(ap.av.getAlignment().getAlignmentAnnotation()!=null) { for(int i=0;
4221      * i<ap.av.getAlignment().getAlignmentAnnotation().length; i++) {
4222      * if(!ap.av.getAlignment().getAlignmentAnnotation()[i].autoCalculated) {
4223      * af.alignPanel.av.getAlignment().getAlignmentAnnotation()[i] =
4224      * ap.av.getAlignment().getAlignmentAnnotation()[i]; } } }
4225      */
4226
4227     return af.alignPanel;
4228   }
4229
4230   /**
4231    * flag indicating if hashtables should be cleared on finalization TODO this
4232    * flag may not be necessary
4233    */
4234   private final boolean _cleartables = true;
4235
4236   private Hashtable jvids2vobj;
4237
4238   /*
4239    * (non-Javadoc)
4240    * 
4241    * @see java.lang.Object#finalize()
4242    */
4243   @Override
4244   protected void finalize() throws Throwable
4245   {
4246     // really make sure we have no buried refs left.
4247     if (_cleartables)
4248     {
4249       clearSeqRefs();
4250     }
4251     this.seqRefIds = null;
4252     this.seqsToIds = null;
4253     super.finalize();
4254   }
4255
4256   private void warn(String msg)
4257   {
4258     warn(msg, null);
4259   }
4260
4261   private void warn(String msg, Exception e)
4262   {
4263     if (Cache.log != null)
4264     {
4265       if (e != null)
4266       {
4267         Cache.log.warn(msg, e);
4268       }
4269       else
4270       {
4271         Cache.log.warn(msg);
4272       }
4273     }
4274     else
4275     {
4276       System.err.println("Warning: " + msg);
4277       if (e != null)
4278       {
4279         e.printStackTrace();
4280       }
4281     }
4282   }
4283
4284   private void debug(String string)
4285   {
4286     debug(string, null);
4287   }
4288
4289   private void debug(String msg, Exception e)
4290   {
4291     if (Cache.log != null)
4292     {
4293       if (e != null)
4294       {
4295         Cache.log.debug(msg, e);
4296       }
4297       else
4298       {
4299         Cache.log.debug(msg);
4300       }
4301     }
4302     else
4303     {
4304       System.err.println("Warning: " + msg);
4305       if (e != null)
4306       {
4307         e.printStackTrace();
4308       }
4309     }
4310   }
4311
4312   /**
4313    * set the object to ID mapping tables used to write/recover objects and XML
4314    * ID strings for the jalview project. If external tables are provided then
4315    * finalize and clearSeqRefs will not clear the tables when the Jalview2XML
4316    * object goes out of scope. - also populates the datasetIds hashtable with
4317    * alignment objects containing dataset sequences
4318    * 
4319    * @param vobj2jv
4320    *          Map from ID strings to jalview datamodel
4321    * @param jv2vobj
4322    *          Map from jalview datamodel to ID strings
4323    * 
4324    * 
4325    */
4326   public void setObjectMappingTables(Hashtable vobj2jv,
4327           IdentityHashMap jv2vobj)
4328   {
4329     this.jv2vobj = jv2vobj;
4330     this.vobj2jv = vobj2jv;
4331     Iterator ds = jv2vobj.keySet().iterator();
4332     String id;
4333     while (ds.hasNext())
4334     {
4335       Object jvobj = ds.next();
4336       id = jv2vobj.get(jvobj).toString();
4337       if (jvobj instanceof jalview.datamodel.Alignment)
4338       {
4339         if (((jalview.datamodel.Alignment) jvobj).getDataset() == null)
4340         {
4341           addDatasetRef(id, (jalview.datamodel.Alignment) jvobj);
4342         }
4343       }
4344       else if (jvobj instanceof jalview.datamodel.Sequence)
4345       {
4346         // register sequence object so the XML parser can recover it.
4347         if (seqRefIds == null)
4348         {
4349           seqRefIds = new Hashtable();
4350         }
4351         if (seqsToIds == null)
4352         {
4353           seqsToIds = new IdentityHashMap();
4354         }
4355         seqRefIds.put(jv2vobj.get(jvobj).toString(), jvobj);
4356         seqsToIds.put(jvobj, id);
4357       }
4358       else if (jvobj instanceof jalview.datamodel.AlignmentAnnotation)
4359       {
4360         if (annotationIds == null)
4361         {
4362           annotationIds = new Hashtable();
4363         }
4364         String anid;
4365         annotationIds.put(anid = jv2vobj.get(jvobj).toString(), jvobj);
4366         jalview.datamodel.AlignmentAnnotation jvann = (jalview.datamodel.AlignmentAnnotation) jvobj;
4367         if (jvann.annotationId == null)
4368         {
4369           jvann.annotationId = anid;
4370         }
4371         if (!jvann.annotationId.equals(anid))
4372         {
4373           // TODO verify that this is the correct behaviour
4374           this.warn("Overriding Annotation ID for " + anid
4375                   + " from different id : " + jvann.annotationId);
4376           jvann.annotationId = anid;
4377         }
4378       }
4379       else if (jvobj instanceof String)
4380       {
4381         if (jvids2vobj == null)
4382         {
4383           jvids2vobj = new Hashtable();
4384           jvids2vobj.put(jvobj, jv2vobj.get(jvobj).toString());
4385         }
4386       }
4387       else
4388         Cache.log.debug("Ignoring " + jvobj.getClass() + " (ID = " + id);
4389     }
4390   }
4391
4392   /**
4393    * set the uniqueSetSuffix used to prefix/suffix object IDs for jalview
4394    * objects created from the project archive. If string is null (default for
4395    * construction) then suffix will be set automatically.
4396    * 
4397    * @param string
4398    */
4399   public void setUniqueSetSuffix(String string)
4400   {
4401     uniqueSetSuffix = string;
4402
4403   }
4404
4405   /**
4406    * uses skipList2 as the skipList for skipping views on sequence sets
4407    * associated with keys in the skipList
4408    * 
4409    * @param skipList2
4410    */
4411   public void setSkipList(Hashtable skipList2)
4412   {
4413     skipList = skipList2;
4414   }
4415
4416 }