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