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