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