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