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