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