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