2ceb245c3e9ebf6301fe47a52d1374ee35c2e98a
[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 = MessageManager.formatMessage("label.dataset_for", new String[]{fileName,_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.getShowAnnotation());
1129       view.setShowBoxes(av.getShowBoxes());
1130       view.setShowColourText(av.getColourText());
1131       view.setShowFullId(av.getShowJVSuffix());
1132       view.setRightAlignIds(av.rightAlignIds);
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.seqPanel.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.seqPanel.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.seqPanel.seqCanvas.getFeatureRenderer()
1183                       .getColour(renderOrder[ro]).getRGB());
1184             }
1185
1186             setting.setDisplay(av.getFeaturesDisplayed().isVisible(
1187                     renderOrder[ro]));
1188             float rorder = ap.seqPanel.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.seqPanel.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.seqPanel.seqCanvas.getFeatureRenderer()
1213                   .getColour(key).getRGB());
1214
1215           setting.setDisplay(false);
1216           float rorder = ap.seqPanel.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.seqPanel.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.seqPanel.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                   && aa[i].annotations[a].secondaryStructure != '\0')
1466           {
1467             ae.setSecondaryStructure(aa[i].annotations[a].secondaryStructure
1468                     + "");
1469           }
1470
1471           if (aa[i].annotations[a].colour != null
1472                   && aa[i].annotations[a].colour != java.awt.Color.black)
1473           {
1474             ae.setColour(aa[i].annotations[a].colour.getRGB());
1475           }
1476
1477           an.addAnnotationElement(ae);
1478           if (aa[i].autoCalculated)
1479           {
1480             // only write one non-null entry into the annotation row -
1481             // sufficient to get the visualization attributes necessary to
1482             // display data
1483             continue;
1484           }
1485         }
1486       }
1487       else
1488       {
1489         an.setScoreOnly(true);
1490       }
1491       if (!storeDS || (storeDS && !aa[i].autoCalculated))
1492       {
1493         // skip autocalculated annotation - these are only provided for
1494         // alignments
1495         vamsasSet.addAnnotation(an);
1496       }
1497     }
1498
1499   }
1500
1501   private CalcIdParam createCalcIdParam(String calcId, AlignViewport av)
1502   {
1503     AutoCalcSetting settings = av.getCalcIdSettingsFor(calcId);
1504     if (settings != null)
1505     {
1506       CalcIdParam vCalcIdParam = new CalcIdParam();
1507       vCalcIdParam.setCalcId(calcId);
1508       vCalcIdParam.addServiceURL(settings.getServiceURI());
1509       // generic URI allowing a third party to resolve another instance of the
1510       // service used for this calculation
1511       for (String urls : settings.getServiceURLs())
1512       {
1513         vCalcIdParam.addServiceURL(urls);
1514       }
1515       vCalcIdParam.setVersion("1.0");
1516       if (settings.getPreset() != null)
1517       {
1518         WsParamSetI setting = settings.getPreset();
1519         vCalcIdParam.setName(setting.getName());
1520         vCalcIdParam.setDescription(setting.getDescription());
1521       }
1522       else
1523       {
1524         vCalcIdParam.setName("");
1525         vCalcIdParam.setDescription("Last used parameters");
1526       }
1527       // need to be able to recover 1) settings 2) user-defined presets or
1528       // recreate settings from preset 3) predefined settings provided by
1529       // service - or settings that can be transferred (or discarded)
1530       vCalcIdParam.setParameters(settings.getWsParamFile().replace("\n",
1531               "|\\n|"));
1532       vCalcIdParam.setAutoUpdate(settings.isAutoUpdate());
1533       // todo - decide if updateImmediately is needed for any projects.
1534
1535       return vCalcIdParam;
1536     }
1537     return null;
1538   }
1539
1540   private boolean recoverCalcIdParam(CalcIdParam calcIdParam,
1541           AlignViewport av)
1542   {
1543     if (calcIdParam.getVersion().equals("1.0"))
1544     {
1545       Jws2Instance service = Jws2Discoverer.getDiscoverer()
1546               .getPreferredServiceFor(calcIdParam.getServiceURL());
1547       if (service != null)
1548       {
1549         WsParamSetI parmSet = null;
1550         try
1551         {
1552           parmSet = service.getParamStore().parseServiceParameterFile(
1553                   calcIdParam.getName(), calcIdParam.getDescription(),
1554                   calcIdParam.getServiceURL(),
1555                   calcIdParam.getParameters().replace("|\\n|", "\n"));
1556         } catch (IOException x)
1557         {
1558           warn("Couldn't parse parameter data for "
1559                   + calcIdParam.getCalcId(), x);
1560           return false;
1561         }
1562         List<ArgumentI> argList = null;
1563         if (calcIdParam.getName().length() > 0)
1564         {
1565           parmSet = service.getParamStore()
1566                   .getPreset(calcIdParam.getName());
1567           if (parmSet != null)
1568           {
1569             // TODO : check we have a good match with settings in AACon -
1570             // otherwise we'll need to create a new preset
1571           }
1572         }
1573         else
1574         {
1575           argList = parmSet.getArguments();
1576           parmSet = null;
1577         }
1578         AAConSettings settings = new AAConSettings(
1579                 calcIdParam.isAutoUpdate(), service, parmSet, argList);
1580         av.setCalcIdSettingsFor(calcIdParam.getCalcId(), settings,
1581                 calcIdParam.isNeedsUpdate());
1582         return true;
1583       }
1584       else
1585       {
1586         warn("Cannot resolve a service for the parameters used in this project. Try configuring a JABAWS server.");
1587         return false;
1588       }
1589     }
1590     throw new Error(MessageManager.formatMessage("error.unsupported_version_calcIdparam", new String[]{calcIdParam.toString()}));
1591   }
1592
1593   /**
1594    * External mapping between jalview objects and objects yielding a valid and
1595    * unique object ID string. This is null for normal Jalview project IO, but
1596    * non-null when a jalview project is being read or written as part of a
1597    * vamsas session.
1598    */
1599   IdentityHashMap jv2vobj = null;
1600
1601   /**
1602    * Construct a unique ID for jvobj using either existing bindings or if none
1603    * exist, the result of the hashcode call for the object.
1604    * 
1605    * @param jvobj
1606    *          jalview data object
1607    * @return unique ID for referring to jvobj
1608    */
1609   private String makeHashCode(Object jvobj, String altCode)
1610   {
1611     if (jv2vobj != null)
1612     {
1613       Object id = jv2vobj.get(jvobj);
1614       if (id != null)
1615       {
1616         return id.toString();
1617       }
1618       // check string ID mappings
1619       if (jvids2vobj != null && jvobj instanceof String)
1620       {
1621         id = jvids2vobj.get(jvobj);
1622       }
1623       if (id != null)
1624       {
1625         return id.toString();
1626       }
1627       // give up and warn that something has gone wrong
1628       warn("Cannot find ID for object in external mapping : " + jvobj);
1629     }
1630     return altCode;
1631   }
1632
1633   /**
1634    * return local jalview object mapped to ID, if it exists
1635    * 
1636    * @param idcode
1637    *          (may be null)
1638    * @return null or object bound to idcode
1639    */
1640   private Object retrieveExistingObj(String idcode)
1641   {
1642     if (idcode != null && vobj2jv != null)
1643     {
1644       return vobj2jv.get(idcode);
1645     }
1646     return null;
1647   }
1648
1649   /**
1650    * binding from ID strings from external mapping table to jalview data model
1651    * objects.
1652    */
1653   private Hashtable vobj2jv;
1654
1655   private Sequence createVamsasSequence(String id, SequenceI jds)
1656   {
1657     return createVamsasSequence(true, id, jds, null);
1658   }
1659
1660   private Sequence createVamsasSequence(boolean recurse, String id,
1661           SequenceI jds, SequenceI parentseq)
1662   {
1663     Sequence vamsasSeq = new Sequence();
1664     vamsasSeq.setId(id);
1665     vamsasSeq.setName(jds.getName());
1666     vamsasSeq.setSequence(jds.getSequenceAsString());
1667     vamsasSeq.setDescription(jds.getDescription());
1668     jalview.datamodel.DBRefEntry[] dbrefs = null;
1669     if (jds.getDatasetSequence() != null)
1670     {
1671       vamsasSeq.setDsseqid(seqHash(jds.getDatasetSequence()));
1672       if (jds.getDatasetSequence().getDBRef() != null)
1673       {
1674         dbrefs = jds.getDatasetSequence().getDBRef();
1675       }
1676     }
1677     else
1678     {
1679       vamsasSeq.setDsseqid(id); // so we can tell which sequences really are
1680       // dataset sequences only
1681       dbrefs = jds.getDBRef();
1682     }
1683     if (dbrefs != null)
1684     {
1685       for (int d = 0; d < dbrefs.length; d++)
1686       {
1687         DBRef dbref = new DBRef();
1688         dbref.setSource(dbrefs[d].getSource());
1689         dbref.setVersion(dbrefs[d].getVersion());
1690         dbref.setAccessionId(dbrefs[d].getAccessionId());
1691         if (dbrefs[d].hasMap())
1692         {
1693           Mapping mp = createVamsasMapping(dbrefs[d].getMap(), parentseq,
1694                   jds, recurse);
1695           dbref.setMapping(mp);
1696         }
1697         vamsasSeq.addDBRef(dbref);
1698       }
1699     }
1700     return vamsasSeq;
1701   }
1702
1703   private Mapping createVamsasMapping(jalview.datamodel.Mapping jmp,
1704           SequenceI parentseq, SequenceI jds, boolean recurse)
1705   {
1706     Mapping mp = null;
1707     if (jmp.getMap() != null)
1708     {
1709       mp = new Mapping();
1710
1711       jalview.util.MapList mlst = jmp.getMap();
1712       int r[] = mlst.getFromRanges();
1713       for (int s = 0; s < r.length; s += 2)
1714       {
1715         MapListFrom mfrom = new MapListFrom();
1716         mfrom.setStart(r[s]);
1717         mfrom.setEnd(r[s + 1]);
1718         mp.addMapListFrom(mfrom);
1719       }
1720       r = mlst.getToRanges();
1721       for (int s = 0; s < r.length; s += 2)
1722       {
1723         MapListTo mto = new MapListTo();
1724         mto.setStart(r[s]);
1725         mto.setEnd(r[s + 1]);
1726         mp.addMapListTo(mto);
1727       }
1728       mp.setMapFromUnit(mlst.getFromRatio());
1729       mp.setMapToUnit(mlst.getToRatio());
1730       if (jmp.getTo() != null)
1731       {
1732         MappingChoice mpc = new MappingChoice();
1733         if (recurse
1734                 && (parentseq != jmp.getTo() || parentseq
1735                         .getDatasetSequence() != jmp.getTo()))
1736         {
1737           mpc.setSequence(createVamsasSequence(false, seqHash(jmp.getTo()),
1738                   jmp.getTo(), jds));
1739         }
1740         else
1741         {
1742           String jmpid = "";
1743           SequenceI ps = null;
1744           if (parentseq != jmp.getTo()
1745                   && parentseq.getDatasetSequence() != jmp.getTo())
1746           {
1747             // chaining dbref rather than a handshaking one
1748             jmpid = seqHash(ps = jmp.getTo());
1749           }
1750           else
1751           {
1752             jmpid = seqHash(ps = parentseq);
1753           }
1754           mpc.setDseqFor(jmpid);
1755           if (!seqRefIds.containsKey(mpc.getDseqFor()))
1756           {
1757             jalview.bin.Cache.log.debug("creatign new DseqFor ID");
1758             seqRefIds.put(mpc.getDseqFor(), ps);
1759           }
1760           else
1761           {
1762             jalview.bin.Cache.log.debug("reusing DseqFor ID");
1763           }
1764         }
1765         mp.setMappingChoice(mpc);
1766       }
1767     }
1768     return mp;
1769   }
1770
1771   String SetUserColourScheme(jalview.schemes.ColourSchemeI cs,
1772           Vector userColours, JalviewModelSequence jms)
1773   {
1774     String id = null;
1775     jalview.schemes.UserColourScheme ucs = (jalview.schemes.UserColourScheme) cs;
1776     boolean newucs = false;
1777     if (!userColours.contains(ucs))
1778     {
1779       userColours.add(ucs);
1780       newucs = true;
1781     }
1782     id = "ucs" + userColours.indexOf(ucs);
1783     if (newucs)
1784     {
1785       // actually create the scheme's entry in the XML model
1786       java.awt.Color[] colours = ucs.getColours();
1787       jalview.schemabinding.version2.UserColours uc = new jalview.schemabinding.version2.UserColours();
1788       jalview.schemabinding.version2.UserColourScheme jbucs = new jalview.schemabinding.version2.UserColourScheme();
1789
1790       for (int i = 0; i < colours.length; i++)
1791       {
1792         jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
1793         col.setName(ResidueProperties.aa[i]);
1794         col.setRGB(jalview.util.Format.getHexString(colours[i]));
1795         jbucs.addColour(col);
1796       }
1797       if (ucs.getLowerCaseColours() != null)
1798       {
1799         colours = ucs.getLowerCaseColours();
1800         for (int i = 0; i < colours.length; i++)
1801         {
1802           jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
1803           col.setName(ResidueProperties.aa[i].toLowerCase());
1804           col.setRGB(jalview.util.Format.getHexString(colours[i]));
1805           jbucs.addColour(col);
1806         }
1807       }
1808
1809       uc.setId(id);
1810       uc.setUserColourScheme(jbucs);
1811       jms.addUserColours(uc);
1812     }
1813
1814     return id;
1815   }
1816
1817   jalview.schemes.UserColourScheme GetUserColourScheme(
1818           JalviewModelSequence jms, String id)
1819   {
1820     UserColours[] uc = jms.getUserColours();
1821     UserColours colours = null;
1822
1823     for (int i = 0; i < uc.length; i++)
1824     {
1825       if (uc[i].getId().equals(id))
1826       {
1827         colours = uc[i];
1828
1829         break;
1830       }
1831     }
1832
1833     java.awt.Color[] newColours = new java.awt.Color[24];
1834
1835     for (int i = 0; i < 24; i++)
1836     {
1837       newColours[i] = new java.awt.Color(Integer.parseInt(colours
1838               .getUserColourScheme().getColour(i).getRGB(), 16));
1839     }
1840
1841     jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme(
1842             newColours);
1843
1844     if (colours.getUserColourScheme().getColourCount() > 24)
1845     {
1846       newColours = new java.awt.Color[23];
1847       for (int i = 0; i < 23; i++)
1848       {
1849         newColours[i] = new java.awt.Color(Integer.parseInt(colours
1850                 .getUserColourScheme().getColour(i + 24).getRGB(), 16));
1851       }
1852       ucs.setLowerCaseColours(newColours);
1853     }
1854
1855     return ucs;
1856   }
1857
1858   /**
1859    * contains last error message (if any) encountered by XML loader.
1860    */
1861   String errorMessage = null;
1862
1863   /**
1864    * flag to control whether the Jalview2XML_V1 parser should be deferred to if
1865    * exceptions are raised during project XML parsing
1866    */
1867   public boolean attemptversion1parse = true;
1868
1869   /**
1870    * Load a jalview project archive from a jar file
1871    * 
1872    * @param file
1873    *          - HTTP URL or filename
1874    */
1875   public AlignFrame LoadJalviewAlign(final String file)
1876   {
1877
1878     jalview.gui.AlignFrame af = null;
1879
1880     try
1881     {
1882       // create list to store references for any new Jmol viewers created
1883       newStructureViewers = new Vector<JalviewStructureDisplayI>();
1884       // UNMARSHALLER SEEMS TO CLOSE JARINPUTSTREAM, MOST ANNOYING
1885       // Workaround is to make sure caller implements the JarInputStreamProvider
1886       // interface
1887       // so we can re-open the jar input stream for each entry.
1888
1889       jarInputStreamProvider jprovider = createjarInputStreamProvider(file);
1890       af = LoadJalviewAlign(jprovider);
1891
1892     } catch (MalformedURLException e)
1893     {
1894       errorMessage = "Invalid URL format for '" + file + "'";
1895       reportErrors();
1896     } finally
1897     {
1898       try
1899       {
1900         SwingUtilities.invokeAndWait(new Runnable()
1901         {
1902           public void run()
1903           {
1904             setLoadingFinishedForNewStructureViewers();
1905           };
1906         });
1907       } catch (Exception x)
1908       {
1909
1910       }
1911     }
1912     return af;
1913   }
1914
1915   private jarInputStreamProvider createjarInputStreamProvider(
1916           final String file) throws MalformedURLException
1917   {
1918     URL url = null;
1919     errorMessage = null;
1920     uniqueSetSuffix = null;
1921     seqRefIds = null;
1922     viewportsAdded = null;
1923     frefedSequence = null;
1924
1925     if (file.startsWith("http://"))
1926     {
1927       url = new URL(file);
1928     }
1929     final URL _url = url;
1930     return new jarInputStreamProvider()
1931     {
1932
1933       @Override
1934       public JarInputStream getJarInputStream() throws IOException
1935       {
1936         if (_url != null)
1937         {
1938           return new JarInputStream(_url.openStream());
1939         }
1940         else
1941         {
1942           return new JarInputStream(new FileInputStream(file));
1943         }
1944       }
1945
1946       @Override
1947       public String getFilename()
1948       {
1949         return file;
1950       }
1951     };
1952   }
1953
1954   /**
1955    * Recover jalview session from a jalview project archive. Caller may
1956    * initialise uniqueSetSuffix, seqRefIds, viewportsAdded and frefedSequence
1957    * themselves. Any null fields will be initialised with default values,
1958    * non-null fields are left alone.
1959    * 
1960    * @param jprovider
1961    * @return
1962    */
1963   public AlignFrame LoadJalviewAlign(final jarInputStreamProvider jprovider)
1964   {
1965     errorMessage = null;
1966     if (uniqueSetSuffix == null)
1967     {
1968       uniqueSetSuffix = System.currentTimeMillis() % 100000 + "";
1969     }
1970     if (seqRefIds == null)
1971     {
1972       seqRefIds = new Hashtable();
1973     }
1974     if (viewportsAdded == null)
1975     {
1976       viewportsAdded = new Hashtable();
1977     }
1978     if (frefedSequence == null)
1979     {
1980       frefedSequence = new Vector();
1981     }
1982
1983     jalview.gui.AlignFrame af = null, _af = null;
1984     Hashtable gatherToThisFrame = new Hashtable();
1985     final String file = jprovider.getFilename();
1986     try
1987     {
1988       JarInputStream jin = null;
1989       JarEntry jarentry = null;
1990       int entryCount = 1;
1991
1992       do
1993       {
1994         jin = jprovider.getJarInputStream();
1995         for (int i = 0; i < entryCount; i++)
1996         {
1997           jarentry = jin.getNextJarEntry();
1998         }
1999
2000         if (jarentry != null && jarentry.getName().endsWith(".xml"))
2001         {
2002           InputStreamReader in = new InputStreamReader(jin, "UTF-8");
2003           JalviewModel object = new JalviewModel();
2004
2005           Unmarshaller unmar = new Unmarshaller(object);
2006           unmar.setValidation(false);
2007           object = (JalviewModel) unmar.unmarshal(in);
2008           if (true) // !skipViewport(object))
2009           {
2010             _af = LoadFromObject(object, file, true, jprovider);
2011             if (object.getJalviewModelSequence().getViewportCount() > 0)
2012             {
2013               af = _af;
2014               if (af.viewport.gatherViewsHere)
2015               {
2016                 gatherToThisFrame.put(af.viewport.getSequenceSetId(), af);
2017               }
2018             }
2019           }
2020           entryCount++;
2021         }
2022         else if (jarentry != null)
2023         {
2024           // Some other file here.
2025           entryCount++;
2026         }
2027       } while (jarentry != null);
2028       resolveFrefedSequences();
2029     } catch (java.io.FileNotFoundException ex)
2030     {
2031       ex.printStackTrace();
2032       errorMessage = "Couldn't locate Jalview XML file : " + file;
2033       System.err.println("Exception whilst loading jalview XML file : "
2034               + ex + "\n");
2035     } catch (java.net.UnknownHostException ex)
2036     {
2037       ex.printStackTrace();
2038       errorMessage = "Couldn't locate Jalview XML file : " + file;
2039       System.err.println("Exception whilst loading jalview XML file : "
2040               + ex + "\n");
2041     } catch (Exception ex)
2042     {
2043       System.err.println("Parsing as Jalview Version 2 file failed.");
2044       ex.printStackTrace(System.err);
2045       if (attemptversion1parse)
2046       {
2047         // Is Version 1 Jar file?
2048         try
2049         {
2050           af = new Jalview2XML_V1(raiseGUI).LoadJalviewAlign(jprovider);
2051         } catch (Exception ex2)
2052         {
2053           System.err.println("Exception whilst loading as jalviewXMLV1:");
2054           ex2.printStackTrace();
2055           af = null;
2056         }
2057       }
2058       if (Desktop.instance != null)
2059       {
2060         Desktop.instance.stopLoading();
2061       }
2062       if (af != null)
2063       {
2064         System.out.println("Successfully loaded archive file");
2065         return af;
2066       }
2067       ex.printStackTrace();
2068
2069       System.err.println("Exception whilst loading jalview XML file : "
2070               + ex + "\n");
2071     } catch (OutOfMemoryError e)
2072     {
2073       // Don't use the OOM Window here
2074       errorMessage = "Out of memory loading jalview XML file";
2075       System.err.println("Out of memory whilst loading jalview XML file");
2076       e.printStackTrace();
2077     }
2078
2079     if (Desktop.instance != null)
2080     {
2081       Desktop.instance.stopLoading();
2082     }
2083
2084     Enumeration en = gatherToThisFrame.elements();
2085     while (en.hasMoreElements())
2086     {
2087       Desktop.instance.gatherViews((AlignFrame) en.nextElement());
2088     }
2089     if (errorMessage != null)
2090     {
2091       reportErrors();
2092     }
2093     return af;
2094   }
2095
2096   /**
2097    * check errorMessage for a valid error message and raise an error box in the
2098    * GUI or write the current errorMessage to stderr and then clear the error
2099    * state.
2100    */
2101   protected void reportErrors()
2102   {
2103     reportErrors(false);
2104   }
2105
2106   protected void reportErrors(final boolean saving)
2107   {
2108     if (errorMessage != null)
2109     {
2110       final String finalErrorMessage = errorMessage;
2111       if (raiseGUI)
2112       {
2113         javax.swing.SwingUtilities.invokeLater(new Runnable()
2114         {
2115           @Override
2116           public void run()
2117           {
2118             JOptionPane.showInternalMessageDialog(Desktop.desktop,
2119                     finalErrorMessage, "Error "
2120                             + (saving ? "saving" : "loading")
2121                             + " Jalview file", JOptionPane.WARNING_MESSAGE);
2122           }
2123         });
2124       }
2125       else
2126       {
2127         System.err.println("Problem loading Jalview file: " + errorMessage);
2128       }
2129     }
2130     errorMessage = null;
2131   }
2132
2133   Hashtable<String, String> alreadyLoadedPDB;
2134
2135   /**
2136    * when set, local views will be updated from view stored in JalviewXML
2137    * Currently (28th Sep 2008) things will go horribly wrong in vamsas document
2138    * sync if this is set to true.
2139    */
2140   private final boolean updateLocalViews = false;
2141
2142   String loadPDBFile(jarInputStreamProvider jprovider, String pdbId)
2143   {
2144     if (alreadyLoadedPDB == null)
2145     {
2146       alreadyLoadedPDB = new Hashtable();
2147     }
2148
2149     if (alreadyLoadedPDB.containsKey(pdbId))
2150     {
2151       return alreadyLoadedPDB.get(pdbId).toString();
2152     }
2153
2154     try
2155     {
2156       JarInputStream jin = jprovider.getJarInputStream();
2157       /*
2158        * if (jprovider.startsWith("http://")) { jin = new JarInputStream(new
2159        * URL(jprovider).openStream()); } else { jin = new JarInputStream(new
2160        * FileInputStream(jprovider)); }
2161        */
2162
2163       JarEntry entry = null;
2164       do
2165       {
2166         entry = jin.getNextJarEntry();
2167       } while (entry != null && !entry.getName().equals(pdbId));
2168       if (entry != null)
2169       {
2170         BufferedReader in = new BufferedReader(new InputStreamReader(jin));
2171         File outFile = File.createTempFile("jalview_pdb", ".txt");
2172         outFile.deleteOnExit();
2173         PrintWriter out = new PrintWriter(new FileOutputStream(outFile));
2174         String data;
2175
2176         while ((data = in.readLine()) != null)
2177         {
2178           out.println(data);
2179         }
2180         try
2181         {
2182           out.flush();
2183         } catch (Exception foo)
2184         {
2185         }
2186         ;
2187         out.close();
2188         String t = outFile.getAbsolutePath();
2189         alreadyLoadedPDB.put(pdbId, t);
2190         return t;
2191       }
2192       else
2193       {
2194         warn("Couldn't find PDB file entry in Jalview Jar for " + pdbId);
2195       }
2196     } catch (Exception ex)
2197     {
2198       ex.printStackTrace();
2199     }
2200
2201     return null;
2202   }
2203
2204   private class JvAnnotRow
2205   {
2206     public JvAnnotRow(int i, AlignmentAnnotation jaa)
2207     {
2208       order = i;
2209       template = jaa;
2210     }
2211
2212     /**
2213      * persisted version of annotation row from which to take vis properties
2214      */
2215     public jalview.datamodel.AlignmentAnnotation template;
2216
2217     /**
2218      * original position of the annotation row in the alignment
2219      */
2220     public int order;
2221   }
2222
2223   /**
2224    * Load alignment frame from jalview XML DOM object
2225    * 
2226    * @param object
2227    *          DOM
2228    * @param file
2229    *          filename source string
2230    * @param loadTreesAndStructures
2231    *          when false only create Viewport
2232    * @param jprovider
2233    *          data source provider
2234    * @return alignment frame created from view stored in DOM
2235    */
2236   AlignFrame LoadFromObject(JalviewModel object, String file,
2237           boolean loadTreesAndStructures, jarInputStreamProvider jprovider)
2238   {
2239     SequenceSet vamsasSet = object.getVamsasModel().getSequenceSet(0);
2240     Sequence[] vamsasSeq = vamsasSet.getSequence();
2241
2242     JalviewModelSequence jms = object.getJalviewModelSequence();
2243
2244     Viewport view = (jms.getViewportCount() > 0) ? jms.getViewport(0)
2245             : null;
2246
2247     // ////////////////////////////////
2248     // LOAD SEQUENCES
2249
2250     Vector hiddenSeqs = null;
2251     jalview.datamodel.Sequence jseq;
2252
2253     ArrayList tmpseqs = new ArrayList();
2254
2255     boolean multipleView = false;
2256
2257     JSeq[] JSEQ = object.getJalviewModelSequence().getJSeq();
2258     int vi = 0; // counter in vamsasSeq array
2259     for (int i = 0; i < JSEQ.length; i++)
2260     {
2261       String seqId = JSEQ[i].getId();
2262
2263       if (seqRefIds.get(seqId) != null)
2264       {
2265         tmpseqs.add(seqRefIds.get(seqId));
2266         multipleView = true;
2267       }
2268       else
2269       {
2270         jseq = new jalview.datamodel.Sequence(vamsasSeq[vi].getName(),
2271                 vamsasSeq[vi].getSequence());
2272         jseq.setDescription(vamsasSeq[vi].getDescription());
2273         jseq.setStart(JSEQ[i].getStart());
2274         jseq.setEnd(JSEQ[i].getEnd());
2275         jseq.setVamsasId(uniqueSetSuffix + seqId);
2276         seqRefIds.put(vamsasSeq[vi].getId(), jseq);
2277         tmpseqs.add(jseq);
2278         vi++;
2279       }
2280
2281       if (JSEQ[i].getHidden())
2282       {
2283         if (hiddenSeqs == null)
2284         {
2285           hiddenSeqs = new Vector();
2286         }
2287
2288         hiddenSeqs.addElement(seqRefIds.get(seqId));
2289       }
2290
2291     }
2292
2293     // /
2294     // Create the alignment object from the sequence set
2295     // ///////////////////////////////
2296     jalview.datamodel.Sequence[] orderedSeqs = new jalview.datamodel.Sequence[tmpseqs
2297             .size()];
2298
2299     tmpseqs.toArray(orderedSeqs);
2300
2301     jalview.datamodel.Alignment al = new jalview.datamodel.Alignment(
2302             orderedSeqs);
2303
2304     // / Add the alignment properties
2305     for (int i = 0; i < vamsasSet.getSequenceSetPropertiesCount(); i++)
2306     {
2307       SequenceSetProperties ssp = vamsasSet.getSequenceSetProperties(i);
2308       al.setProperty(ssp.getKey(), ssp.getValue());
2309     }
2310
2311     // /
2312     // SequenceFeatures are added to the DatasetSequence,
2313     // so we must create or recover the dataset before loading features
2314     // ///////////////////////////////
2315     if (vamsasSet.getDatasetId() == null || vamsasSet.getDatasetId() == "")
2316     {
2317       // older jalview projects do not have a dataset id.
2318       al.setDataset(null);
2319     }
2320     else
2321     {
2322       recoverDatasetFor(vamsasSet, al);
2323     }
2324     // ///////////////////////////////
2325
2326     Hashtable pdbloaded = new Hashtable();
2327     if (!multipleView)
2328     {
2329       // load sequence features, database references and any associated PDB
2330       // structures for the alignment
2331       for (int i = 0; i < vamsasSeq.length; i++)
2332       {
2333         if (JSEQ[i].getFeaturesCount() > 0)
2334         {
2335           Features[] features = JSEQ[i].getFeatures();
2336           for (int f = 0; f < features.length; f++)
2337           {
2338             jalview.datamodel.SequenceFeature sf = new jalview.datamodel.SequenceFeature(
2339                     features[f].getType(), features[f].getDescription(),
2340                     features[f].getStatus(), features[f].getBegin(),
2341                     features[f].getEnd(), features[f].getFeatureGroup());
2342
2343             sf.setScore(features[f].getScore());
2344             for (int od = 0; od < features[f].getOtherDataCount(); od++)
2345             {
2346               OtherData keyValue = features[f].getOtherData(od);
2347               if (keyValue.getKey().startsWith("LINK"))
2348               {
2349                 sf.addLink(keyValue.getValue());
2350               }
2351               else
2352               {
2353                 sf.setValue(keyValue.getKey(), keyValue.getValue());
2354               }
2355
2356             }
2357
2358             al.getSequenceAt(i).getDatasetSequence().addSequenceFeature(sf);
2359           }
2360         }
2361         if (vamsasSeq[i].getDBRefCount() > 0)
2362         {
2363           addDBRefs(al.getSequenceAt(i).getDatasetSequence(), vamsasSeq[i]);
2364         }
2365         if (JSEQ[i].getPdbidsCount() > 0)
2366         {
2367           Pdbids[] ids = JSEQ[i].getPdbids();
2368           for (int p = 0; p < ids.length; p++)
2369           {
2370             jalview.datamodel.PDBEntry entry = new jalview.datamodel.PDBEntry();
2371             entry.setId(ids[p].getId());
2372             entry.setType(ids[p].getType());
2373             if (ids[p].getFile() != null)
2374             {
2375               if (!pdbloaded.containsKey(ids[p].getFile()))
2376               {
2377                 entry.setFile(loadPDBFile(jprovider, ids[p].getId()));
2378               }
2379               else
2380               {
2381                 entry.setFile(pdbloaded.get(ids[p].getId()).toString());
2382               }
2383             }
2384             StructureSelectionManager.getStructureSelectionManager(
2385                     Desktop.instance)
2386                     .registerPDBEntry(entry);
2387             al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
2388           }
2389         }
2390       }
2391     } // end !multipleview
2392
2393     // ///////////////////////////////
2394     // LOAD SEQUENCE MAPPINGS
2395
2396     if (vamsasSet.getAlcodonFrameCount() > 0)
2397     {
2398       // TODO Potentially this should only be done once for all views of an
2399       // alignment
2400       AlcodonFrame[] alc = vamsasSet.getAlcodonFrame();
2401       for (int i = 0; i < alc.length; i++)
2402       {
2403         jalview.datamodel.AlignedCodonFrame cf = new jalview.datamodel.AlignedCodonFrame(
2404                 alc[i].getAlcodonCount());
2405         if (alc[i].getAlcodonCount() > 0)
2406         {
2407           Alcodon[] alcods = alc[i].getAlcodon();
2408           for (int p = 0; p < cf.codons.length; p++)
2409           {
2410             if (alcods[p].hasPos1() && alcods[p].hasPos2()
2411                     && alcods[p].hasPos3())
2412             {
2413               // translated codons require three valid positions
2414               cf.codons[p] = new int[3];
2415               cf.codons[p][0] = (int) alcods[p].getPos1();
2416               cf.codons[p][1] = (int) alcods[p].getPos2();
2417               cf.codons[p][2] = (int) alcods[p].getPos3();
2418             }
2419             else
2420             {
2421               cf.codons[p] = null;
2422             }
2423           }
2424         }
2425         if (alc[i].getAlcodMapCount() > 0)
2426         {
2427           AlcodMap[] maps = alc[i].getAlcodMap();
2428           for (int m = 0; m < maps.length; m++)
2429           {
2430             SequenceI dnaseq = (SequenceI) seqRefIds
2431                     .get(maps[m].getDnasq());
2432             // Load Mapping
2433             jalview.datamodel.Mapping mapping = null;
2434             // attach to dna sequence reference.
2435             if (maps[m].getMapping() != null)
2436             {
2437               mapping = addMapping(maps[m].getMapping());
2438             }
2439             if (dnaseq != null)
2440             {
2441               cf.addMap(dnaseq, mapping.getTo(), mapping.getMap());
2442             }
2443             else
2444             {
2445               // defer to later
2446               frefedSequence.add(new Object[]
2447               { maps[m].getDnasq(), cf, mapping });
2448             }
2449           }
2450         }
2451         al.addCodonFrame(cf);
2452       }
2453
2454     }
2455
2456     // ////////////////////////////////
2457     // LOAD ANNOTATIONS
2458     ArrayList<JvAnnotRow> autoAlan = new ArrayList<JvAnnotRow>();
2459     /**
2460      * store any annotations which forward reference a group's ID
2461      */
2462     Hashtable<String, ArrayList<jalview.datamodel.AlignmentAnnotation>> groupAnnotRefs = new Hashtable<String, ArrayList<jalview.datamodel.AlignmentAnnotation>>();
2463
2464     if (vamsasSet.getAnnotationCount() > 0)
2465     {
2466       Annotation[] an = vamsasSet.getAnnotation();
2467
2468       for (int i = 0; i < an.length; i++)
2469       {
2470         /**
2471          * test if annotation is automatically calculated for this view only
2472          */
2473         boolean autoForView = false;
2474         if (an[i].getLabel().equals("Quality")
2475                 || an[i].getLabel().equals("Conservation")
2476                 || an[i].getLabel().equals("Consensus"))
2477         {
2478           // Kludge for pre 2.5 projects which lacked the autocalculated flag
2479           autoForView = true;
2480           if (!an[i].hasAutoCalculated())
2481           {
2482             an[i].setAutoCalculated(true);
2483           }
2484         }
2485         if (autoForView
2486                 || (an[i].hasAutoCalculated() && an[i].isAutoCalculated()))
2487         {
2488           // remove ID - we don't recover annotation from other views for
2489           // view-specific annotation
2490           an[i].setId(null);
2491         }
2492
2493         // set visiblity for other annotation in this view
2494         if (an[i].getId() != null
2495                 && annotationIds.containsKey(an[i].getId()))
2496         {
2497           jalview.datamodel.AlignmentAnnotation jda = (jalview.datamodel.AlignmentAnnotation) annotationIds
2498                   .get(an[i].getId());
2499           // in principle Visible should always be true for annotation displayed
2500           // in multiple views
2501           if (an[i].hasVisible())
2502           {
2503             jda.visible = an[i].getVisible();
2504           }
2505
2506           al.addAnnotation(jda);
2507
2508           continue;
2509         }
2510         // Construct new annotation from model.
2511         AnnotationElement[] ae = an[i].getAnnotationElement();
2512         jalview.datamodel.Annotation[] anot = null;
2513         java.awt.Color firstColour = null;
2514         int anpos;
2515         if (!an[i].getScoreOnly())
2516         {
2517           anot = new jalview.datamodel.Annotation[al.getWidth()];
2518           for (int aa = 0; aa < ae.length && aa < anot.length; aa++)
2519           {
2520             anpos = ae[aa].getPosition();
2521
2522             if (anpos >= anot.length)
2523             {
2524               continue;
2525             }
2526
2527             anot[anpos] = new jalview.datamodel.Annotation(
2528
2529             ae[aa].getDisplayCharacter(), ae[aa].getDescription(),
2530                     (ae[aa].getSecondaryStructure() == null || ae[aa]
2531                             .getSecondaryStructure().length() == 0) ? ' '
2532                             : ae[aa].getSecondaryStructure().charAt(0),
2533                     ae[aa].getValue()
2534
2535             );
2536             // JBPNote: Consider verifying dataflow for IO of secondary
2537             // structure annotation read from Stockholm files
2538             // this was added to try to ensure that
2539             // if (anot[ae[aa].getPosition()].secondaryStructure>' ')
2540             // {
2541             // anot[ae[aa].getPosition()].displayCharacter = "";
2542             // }
2543             anot[anpos].colour = new java.awt.Color(ae[aa].getColour());
2544             if (firstColour == null)
2545             {
2546               firstColour = anot[anpos].colour;
2547             }
2548           }
2549         }
2550         jalview.datamodel.AlignmentAnnotation jaa = null;
2551
2552         if (an[i].getGraph())
2553         {
2554           float llim = 0, hlim = 0;
2555           // if (autoForView || an[i].isAutoCalculated()) {
2556           // hlim=11f;
2557           // }
2558           jaa = new jalview.datamodel.AlignmentAnnotation(an[i].getLabel(),
2559                   an[i].getDescription(), anot, llim, hlim,
2560                   an[i].getGraphType());
2561
2562           jaa.graphGroup = an[i].getGraphGroup();
2563           jaa._linecolour = firstColour;
2564           if (an[i].getThresholdLine() != null)
2565           {
2566             jaa.setThreshold(new jalview.datamodel.GraphLine(an[i]
2567                     .getThresholdLine().getValue(), an[i]
2568                     .getThresholdLine().getLabel(), new java.awt.Color(
2569                     an[i].getThresholdLine().getColour())));
2570
2571           }
2572           if (autoForView || an[i].isAutoCalculated())
2573           {
2574             // Hardwire the symbol display line to ensure that labels for
2575             // histograms are displayed
2576             jaa.hasText = true;
2577           }
2578         }
2579         else
2580         {
2581           jaa = new jalview.datamodel.AlignmentAnnotation(an[i].getLabel(),
2582                   an[i].getDescription(), anot);
2583           jaa._linecolour = firstColour;
2584         }
2585         // register new annotation
2586         if (an[i].getId() != null)
2587         {
2588           annotationIds.put(an[i].getId(), jaa);
2589           jaa.annotationId = an[i].getId();
2590         }
2591         // recover sequence association
2592         if (an[i].getSequenceRef() != null)
2593         {
2594           if (al.findName(an[i].getSequenceRef()) != null)
2595           {
2596             jaa.createSequenceMapping(al.findName(an[i].getSequenceRef()),
2597                     1, true);
2598             al.findName(an[i].getSequenceRef()).addAlignmentAnnotation(jaa);
2599           }
2600         }
2601         // and make a note of any group association
2602         if (an[i].getGroupRef() != null && an[i].getGroupRef().length() > 0)
2603         {
2604           ArrayList<jalview.datamodel.AlignmentAnnotation> aal = groupAnnotRefs
2605                   .get(an[i].getGroupRef());
2606           if (aal == null)
2607           {
2608             aal = new ArrayList<jalview.datamodel.AlignmentAnnotation>();
2609             groupAnnotRefs.put(an[i].getGroupRef(), aal);
2610           }
2611           aal.add(jaa);
2612         }
2613
2614         if (an[i].hasScore())
2615         {
2616           jaa.setScore(an[i].getScore());
2617         }
2618         if (an[i].hasVisible())
2619         {
2620           jaa.visible = an[i].getVisible();
2621         }
2622
2623         if (an[i].hasCentreColLabels())
2624         {
2625           jaa.centreColLabels = an[i].getCentreColLabels();
2626         }
2627
2628         if (an[i].hasScaleColLabels())
2629         {
2630           jaa.scaleColLabel = an[i].getScaleColLabels();
2631         }
2632         if (an[i].hasAutoCalculated() && an[i].isAutoCalculated())
2633         {
2634           // newer files have an 'autoCalculated' flag and store calculation
2635           // state in viewport properties
2636           jaa.autoCalculated = true; // means annotation will be marked for
2637           // update at end of load.
2638         }
2639         if (an[i].hasGraphHeight())
2640         {
2641           jaa.graphHeight = an[i].getGraphHeight();
2642         }
2643         if (an[i].hasBelowAlignment())
2644         {
2645           jaa.belowAlignment = an[i].isBelowAlignment();
2646         }
2647         jaa.setCalcId(an[i].getCalcId());
2648         if (an[i].getPropertyCount() > 0)
2649         {
2650           for (jalview.schemabinding.version2.Property prop : an[i]
2651                   .getProperty())
2652           {
2653             jaa.setProperty(prop.getName(), prop.getValue());
2654           }
2655         }
2656         if (jaa.autoCalculated)
2657         {
2658           autoAlan.add(new JvAnnotRow(i, jaa));
2659         }
2660         else
2661         // if (!autoForView)
2662         {
2663           // add autocalculated group annotation and any user created annotation
2664           // for the view
2665           al.addAnnotation(jaa);
2666         }
2667       }
2668     }
2669     // ///////////////////////
2670     // LOAD GROUPS
2671     // Create alignment markup and styles for this view
2672     if (jms.getJGroupCount() > 0)
2673     {
2674       JGroup[] groups = jms.getJGroup();
2675       boolean addAnnotSchemeGroup = false;
2676       for (int i = 0; i < groups.length; i++)
2677       {
2678         ColourSchemeI cs = null;
2679
2680         if (groups[i].getColour() != null)
2681         {
2682           if (groups[i].getColour().startsWith("ucs"))
2683           {
2684             cs = GetUserColourScheme(jms, groups[i].getColour());
2685           }
2686           else if (groups[i].getColour().equals("AnnotationColourGradient")
2687                   && groups[i].getAnnotationColours() != null)
2688           {
2689             addAnnotSchemeGroup = true;
2690             cs = null;
2691           }
2692           else
2693           {
2694             cs = ColourSchemeProperty.getColour(al, groups[i].getColour());
2695           }
2696
2697           if (cs != null)
2698           {
2699             cs.setThreshold(groups[i].getPidThreshold(), true);
2700           }
2701         }
2702
2703         Vector seqs = new Vector();
2704
2705         for (int s = 0; s < groups[i].getSeqCount(); s++)
2706         {
2707           String seqId = groups[i].getSeq(s) + "";
2708           jalview.datamodel.SequenceI ts = (jalview.datamodel.SequenceI) seqRefIds
2709                   .get(seqId);
2710
2711           if (ts != null)
2712           {
2713             seqs.addElement(ts);
2714           }
2715         }
2716
2717         if (seqs.size() < 1)
2718         {
2719           continue;
2720         }
2721
2722         jalview.datamodel.SequenceGroup sg = new jalview.datamodel.SequenceGroup(
2723                 seqs, groups[i].getName(), cs, groups[i].getDisplayBoxes(),
2724                 groups[i].getDisplayText(), groups[i].getColourText(),
2725                 groups[i].getStart(), groups[i].getEnd());
2726
2727         sg.setOutlineColour(new java.awt.Color(groups[i].getOutlineColour()));
2728
2729         sg.textColour = new java.awt.Color(groups[i].getTextCol1());
2730         sg.textColour2 = new java.awt.Color(groups[i].getTextCol2());
2731         sg.setShowNonconserved(groups[i].hasShowUnconserved() ? groups[i]
2732                 .isShowUnconserved() : false);
2733         sg.thresholdTextColour = groups[i].getTextColThreshold();
2734         if (groups[i].hasShowConsensusHistogram())
2735         {
2736           sg.setShowConsensusHistogram(groups[i].isShowConsensusHistogram());
2737         }
2738         ;
2739         if (groups[i].hasShowSequenceLogo())
2740         {
2741           sg.setshowSequenceLogo(groups[i].isShowSequenceLogo());
2742         }
2743         if (groups[i].hasNormaliseSequenceLogo())
2744         {
2745           sg.setNormaliseSequenceLogo(groups[i].isNormaliseSequenceLogo());
2746         }
2747         if (groups[i].hasIgnoreGapsinConsensus())
2748         {
2749           sg.setIgnoreGapsConsensus(groups[i].getIgnoreGapsinConsensus());
2750         }
2751         if (groups[i].getConsThreshold() != 0)
2752         {
2753           jalview.analysis.Conservation c = new jalview.analysis.Conservation(
2754                   "All", ResidueProperties.propHash, 3,
2755                   sg.getSequences(null), 0, sg.getWidth() - 1);
2756           c.calculate();
2757           c.verdict(false, 25);
2758           sg.cs.setConservation(c);
2759         }
2760
2761         if (groups[i].getId() != null && groupAnnotRefs.size() > 0)
2762         {
2763           // re-instate unique group/annotation row reference
2764           ArrayList<jalview.datamodel.AlignmentAnnotation> jaal = groupAnnotRefs
2765                   .get(groups[i].getId());
2766           if (jaal != null)
2767           {
2768             for (jalview.datamodel.AlignmentAnnotation jaa : jaal)
2769             {
2770               jaa.groupRef = sg;
2771               if (jaa.autoCalculated)
2772               {
2773                 // match up and try to set group autocalc alignment row for this
2774                 // annotation
2775                 if (jaa.label.startsWith("Consensus for "))
2776                 {
2777                   sg.setConsensus(jaa);
2778                 }
2779                 // match up and try to set group autocalc alignment row for this
2780                 // annotation
2781                 if (jaa.label.startsWith("Conservation for "))
2782                 {
2783                   sg.setConservationRow(jaa);
2784                 }
2785               }
2786             }
2787           }
2788         }
2789         al.addGroup(sg);
2790         if (addAnnotSchemeGroup)
2791         {
2792           // reconstruct the annotation colourscheme
2793           sg.cs = constructAnnotationColour(
2794                   groups[i].getAnnotationColours(), null, al, jms, false);
2795         }
2796       }
2797     }
2798     if (view == null)
2799     {
2800       // only dataset in this model, so just return.
2801       return null;
2802     }
2803     // ///////////////////////////////
2804     // LOAD VIEWPORT
2805
2806     // If we just load in the same jar file again, the sequenceSetId
2807     // will be the same, and we end up with multiple references
2808     // to the same sequenceSet. We must modify this id on load
2809     // so that each load of the file gives a unique id
2810     String uniqueSeqSetId = view.getSequenceSetId() + uniqueSetSuffix;
2811     String viewId = (view.getId() == null ? null : view.getId()
2812             + uniqueSetSuffix);
2813     AlignFrame af = null;
2814     AlignViewport av = null;
2815     // now check to see if we really need to create a new viewport.
2816     if (multipleView && viewportsAdded.size() == 0)
2817     {
2818       // We recovered an alignment for which a viewport already exists.
2819       // TODO: fix up any settings necessary for overlaying stored state onto
2820       // state recovered from another document. (may not be necessary).
2821       // we may need a binding from a viewport in memory to one recovered from
2822       // XML.
2823       // and then recover its containing af to allow the settings to be applied.
2824       // TODO: fix for vamsas demo
2825       System.err
2826               .println("About to recover a viewport for existing alignment: Sequence set ID is "
2827                       + uniqueSeqSetId);
2828       Object seqsetobj = retrieveExistingObj(uniqueSeqSetId);
2829       if (seqsetobj != null)
2830       {
2831         if (seqsetobj instanceof String)
2832         {
2833           uniqueSeqSetId = (String) seqsetobj;
2834           System.err
2835                   .println("Recovered extant sequence set ID mapping for ID : New Sequence set ID is "
2836                           + uniqueSeqSetId);
2837         }
2838         else
2839         {
2840           System.err
2841                   .println("Warning : Collision between sequence set ID string and existing jalview object mapping.");
2842         }
2843
2844       }
2845     }
2846     /**
2847      * indicate that annotation colours are applied across all groups (pre
2848      * Jalview 2.8.1 behaviour)
2849      */
2850     boolean doGroupAnnColour = isVersionStringLaterThan("2.8.1",
2851             object.getVersion());
2852
2853     AlignmentPanel ap = null;
2854     boolean isnewview = true;
2855     if (viewId != null)
2856     {
2857       // Check to see if this alignment already has a view id == viewId
2858       jalview.gui.AlignmentPanel views[] = Desktop
2859               .getAlignmentPanels(uniqueSeqSetId);
2860       if (views != null && views.length > 0)
2861       {
2862         for (int v = 0; v < views.length; v++)
2863         {
2864           if (views[v].av.getViewId().equalsIgnoreCase(viewId))
2865           {
2866             // recover the existing alignpanel, alignframe, viewport
2867             af = views[v].alignFrame;
2868             av = views[v].av;
2869             ap = views[v];
2870             // TODO: could even skip resetting view settings if we don't want to
2871             // change the local settings from other jalview processes
2872             isnewview = false;
2873           }
2874         }
2875       }
2876     }
2877
2878     if (isnewview)
2879     {
2880       af = loadViewport(file, JSEQ, hiddenSeqs, al, jms, view,
2881               uniqueSeqSetId, viewId, autoAlan);
2882       av = af.viewport;
2883       ap = af.alignPanel;
2884     }
2885     // LOAD TREES
2886     // /////////////////////////////////////
2887     if (loadTreesAndStructures && jms.getTreeCount() > 0)
2888     {
2889       try
2890       {
2891         for (int t = 0; t < jms.getTreeCount(); t++)
2892         {
2893
2894           Tree tree = jms.getTree(t);
2895
2896           TreePanel tp = (TreePanel) retrieveExistingObj(tree.getId());
2897           if (tp == null)
2898           {
2899             tp = af.ShowNewickTree(
2900                     new jalview.io.NewickFile(tree.getNewick()),
2901                     tree.getTitle(), tree.getWidth(), tree.getHeight(),
2902                     tree.getXpos(), tree.getYpos());
2903             if (tree.getId() != null)
2904             {
2905               // perhaps bind the tree id to something ?
2906             }
2907           }
2908           else
2909           {
2910             // update local tree attributes ?
2911             // TODO: should check if tp has been manipulated by user - if so its
2912             // settings shouldn't be modified
2913             tp.setTitle(tree.getTitle());
2914             tp.setBounds(new Rectangle(tree.getXpos(), tree.getYpos(), tree
2915                     .getWidth(), tree.getHeight()));
2916             tp.av = av; // af.viewport; // TODO: verify 'associate with all
2917             // views'
2918             // works still
2919             tp.treeCanvas.av = av; // af.viewport;
2920             tp.treeCanvas.ap = ap; // af.alignPanel;
2921
2922           }
2923           if (tp == null)
2924           {
2925             warn("There was a problem recovering stored Newick tree: \n"
2926                     + tree.getNewick());
2927             continue;
2928           }
2929
2930           tp.fitToWindow.setState(tree.getFitToWindow());
2931           tp.fitToWindow_actionPerformed(null);
2932
2933           if (tree.getFontName() != null)
2934           {
2935             tp.setTreeFont(new java.awt.Font(tree.getFontName(), tree
2936                     .getFontStyle(), tree.getFontSize()));
2937           }
2938           else
2939           {
2940             tp.setTreeFont(new java.awt.Font(view.getFontName(), view
2941                     .getFontStyle(), tree.getFontSize()));
2942           }
2943
2944           tp.showPlaceholders(tree.getMarkUnlinked());
2945           tp.showBootstrap(tree.getShowBootstrap());
2946           tp.showDistances(tree.getShowDistances());
2947
2948           tp.treeCanvas.threshold = tree.getThreshold();
2949
2950           if (tree.getCurrentTree())
2951           {
2952             af.viewport.setCurrentTree(tp.getTree());
2953           }
2954         }
2955
2956       } catch (Exception ex)
2957       {
2958         ex.printStackTrace();
2959       }
2960     }
2961
2962     // //LOAD STRUCTURES
2963     if (loadTreesAndStructures)
2964     {
2965       // run through all PDB ids on the alignment, and collect mappings between
2966       // jmol view ids and all sequences referring to it
2967       Hashtable<String, Object[]> jmolViewIds = new Hashtable();
2968
2969       for (int i = 0; i < JSEQ.length; i++)
2970       {
2971         if (JSEQ[i].getPdbidsCount() > 0)
2972         {
2973           Pdbids[] ids = JSEQ[i].getPdbids();
2974           for (int p = 0; p < ids.length; p++)
2975           {
2976             for (int s = 0; s < ids[p].getStructureStateCount(); s++)
2977             {
2978               // check to see if we haven't already created this structure view
2979               String sviewid = (ids[p].getStructureState(s).getViewId() == null) ? null
2980                       : ids[p].getStructureState(s).getViewId()
2981                               + uniqueSetSuffix;
2982               jalview.datamodel.PDBEntry jpdb = new jalview.datamodel.PDBEntry();
2983               // Originally : ids[p].getFile()
2984               // : TODO: verify external PDB file recovery still works in normal
2985               // jalview project load
2986               jpdb.setFile(loadPDBFile(jprovider, ids[p].getId()));
2987               jpdb.setId(ids[p].getId());
2988
2989               int x = ids[p].getStructureState(s).getXpos();
2990               int y = ids[p].getStructureState(s).getYpos();
2991               int width = ids[p].getStructureState(s).getWidth();
2992               int height = ids[p].getStructureState(s).getHeight();
2993
2994               // Probably don't need to do this anymore...
2995               // Desktop.desktop.getComponentAt(x, y);
2996               // TODO: NOW: check that this recovers the PDB file correctly.
2997               String pdbFile = loadPDBFile(jprovider, ids[p].getId());
2998               jalview.datamodel.SequenceI seq = (jalview.datamodel.SequenceI) seqRefIds
2999                       .get(JSEQ[i].getId() + "");
3000               if (sviewid == null)
3001               {
3002                 sviewid = "_jalview_pre2_4_" + x + "," + y + "," + width
3003                         + "," + height;
3004               }
3005               if (!jmolViewIds.containsKey(sviewid))
3006               {
3007                 jmolViewIds.put(sviewid, new Object[]
3008                 { new int[]
3009                 { x, y, width, height }, "",
3010                     new Hashtable<String, Object[]>(), new boolean[]
3011                     { false, false, true } });
3012                 // Legacy pre-2.7 conversion JAL-823 :
3013                 // do not assume any view has to be linked for colour by
3014                 // sequence
3015               }
3016
3017               // assemble String[] { pdb files }, String[] { id for each
3018               // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
3019               // seqs_file 2}, boolean[] {
3020               // linkAlignPanel,superposeWithAlignpanel}} from hash
3021               Object[] jmoldat = jmolViewIds.get(sviewid);
3022               ((boolean[]) jmoldat[3])[0] |= ids[p].getStructureState(s)
3023                       .hasAlignwithAlignPanel() ? ids[p].getStructureState(
3024                       s).getAlignwithAlignPanel() : false;
3025               // never colour by linked panel if not specified
3026               ((boolean[]) jmoldat[3])[1] |= ids[p].getStructureState(s)
3027                       .hasColourwithAlignPanel() ? ids[p]
3028                       .getStructureState(s).getColourwithAlignPanel()
3029                       : false;
3030               // default for pre-2.7 projects is that Jmol colouring is enabled
3031               ((boolean[]) jmoldat[3])[2] &= ids[p].getStructureState(s)
3032                       .hasColourByJmol() ? ids[p].getStructureState(s)
3033                       .getColourByJmol() : true;
3034
3035               if (((String) jmoldat[1]).length() < ids[p]
3036                       .getStructureState(s).getContent().length())
3037               {
3038                 {
3039                   jmoldat[1] = ids[p].getStructureState(s).getContent();
3040                 }
3041               }
3042               if (ids[p].getFile() != null)
3043               {
3044                 File mapkey = new File(ids[p].getFile());
3045                 Object[] seqstrmaps = (Object[]) ((Hashtable) jmoldat[2])
3046                         .get(mapkey);
3047                 if (seqstrmaps == null)
3048                 {
3049                   ((Hashtable) jmoldat[2]).put(mapkey,
3050                           seqstrmaps = new Object[]
3051                           { pdbFile, ids[p].getId(), new Vector(),
3052                               new Vector() });
3053                 }
3054                 if (!((Vector) seqstrmaps[2]).contains(seq))
3055                 {
3056                   ((Vector) seqstrmaps[2]).addElement(seq);
3057                   // ((Vector)seqstrmaps[3]).addElement(n) :
3058                   // in principle, chains
3059                   // should be stored here : do we need to
3060                   // TODO: store and recover seq/pdb_id :
3061                   // chain mappings
3062                 }
3063               }
3064               else
3065               {
3066                 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");
3067                 warn(errorMessage);
3068               }
3069             }
3070           }
3071         }
3072       }
3073       {
3074
3075         // Instantiate the associated Jmol views
3076         for (Entry<String, Object[]> entry : jmolViewIds.entrySet())
3077         {
3078           String sviewid = entry.getKey();
3079           Object[] svattrib = entry.getValue();
3080           int[] geom = (int[]) svattrib[0];
3081           String state = (String) svattrib[1];
3082           Hashtable<File, Object[]> oldFiles = (Hashtable<File, Object[]>) svattrib[2];
3083           final boolean useinJmolsuperpos = ((boolean[]) svattrib[3])[0], usetoColourbyseq = ((boolean[]) svattrib[3])[1], jmolColouring = ((boolean[]) svattrib[3])[2];
3084           int x = geom[0], y = geom[1], width = geom[2], height = geom[3];
3085           // collate the pdbfile -> sequence mappings from this view
3086           Vector<String> pdbfilenames = new Vector<String>();
3087           Vector<SequenceI[]> seqmaps = new Vector<SequenceI[]>();
3088           Vector<String> pdbids = new Vector<String>();
3089
3090           // Search to see if we've already created this Jmol view
3091           AppJmol comp = null;
3092           JInternalFrame[] frames = null;
3093           do
3094           {
3095             try
3096             {
3097               frames = Desktop.desktop.getAllFrames();
3098             } catch (ArrayIndexOutOfBoundsException e)
3099             {
3100               // occasional No such child exceptions are thrown here...
3101               frames = null;
3102               try
3103               {
3104                 Thread.sleep(10);
3105               } catch (Exception f)
3106               {
3107               }
3108               ;
3109             }
3110           } while (frames == null);
3111           // search for any Jmol windows already open from other
3112           // alignment views that exactly match the stored structure state
3113           for (int f = 0; comp == null && f < frames.length; f++)
3114           {
3115             if (frames[f] instanceof AppJmol)
3116             {
3117               if (sviewid != null
3118                       && ((AppJmol) frames[f]).getViewId().equals(sviewid))
3119               {
3120                 // post jalview 2.4 schema includes structure view id
3121                 comp = (AppJmol) frames[f];
3122               }
3123               else if (frames[f].getX() == x && frames[f].getY() == y
3124                       && frames[f].getHeight() == height
3125                       && frames[f].getWidth() == width)
3126               {
3127                 comp = (AppJmol) frames[f];
3128               }
3129             }
3130           }
3131
3132           if (comp == null)
3133           {
3134             // create a new Jmol window.
3135             // First parse the Jmol state to translate filenames loaded into the
3136             // view, and record the order in which files are shown in the Jmol
3137             // view, so we can add the sequence mappings in same order.
3138             StringBuffer newFileLoc = null;
3139             int cp = 0, ncp, ecp;
3140             while ((ncp = state.indexOf("load ", cp)) > -1)
3141             {
3142               if (newFileLoc == null)
3143               {
3144                 newFileLoc = new StringBuffer();
3145               }
3146               do
3147               {
3148                 // look for next filename in load statement
3149                 newFileLoc.append(state.substring(cp,
3150                         ncp = (state.indexOf("\"", ncp + 1) + 1)));
3151                 String oldfilenam = state.substring(ncp,
3152                         ecp = state.indexOf("\"", ncp));
3153                 // recover the new mapping data for this old filename
3154                 // have to normalize filename - since Jmol and jalview do
3155                 // filename
3156                 // translation differently.
3157                 Object[] filedat = oldFiles.get(new File(oldfilenam));
3158                 newFileLoc.append(Platform
3159                         .escapeString((String) filedat[0]));
3160                 pdbfilenames.addElement((String) filedat[0]);
3161                 pdbids.addElement((String) filedat[1]);
3162                 seqmaps.addElement(((Vector<SequenceI>) filedat[2])
3163                         .toArray(new SequenceI[0]));
3164                 newFileLoc.append("\"");
3165                 cp = ecp + 1; // advance beyond last \" and set cursor so we can
3166                               // look for next file statement.
3167               } while ((ncp = state.indexOf("/*file*/", cp)) > -1);
3168             }
3169             if (cp > 0)
3170             {
3171               // just append rest of state
3172               newFileLoc.append(state.substring(cp));
3173             }
3174             else
3175             {
3176               System.err
3177                       .print("Ignoring incomplete Jmol state for PDB ids: ");
3178               newFileLoc = new StringBuffer(state);
3179               newFileLoc.append("; load append ");
3180               for (File id : oldFiles.keySet())
3181               {
3182                 // add this and any other pdb files that should be present in
3183                 // the viewer
3184                 Object[] filedat = oldFiles.get(id);
3185                 String nfilename;
3186                 newFileLoc.append(((String) filedat[0]));
3187                 pdbfilenames.addElement((String) filedat[0]);
3188                 pdbids.addElement((String) filedat[1]);
3189                 seqmaps.addElement(((Vector<SequenceI>) filedat[2])
3190                         .toArray(new SequenceI[0]));
3191                 newFileLoc.append(" \"");
3192                 newFileLoc.append((String) filedat[0]);
3193                 newFileLoc.append("\"");
3194
3195               }
3196               newFileLoc.append(";");
3197             }
3198
3199             if (newFileLoc != null)
3200             {
3201               int histbug = newFileLoc.indexOf("history = ");
3202               histbug += 10;
3203               int diff = histbug == -1 ? -1 : newFileLoc.indexOf(";",
3204                       histbug);
3205               String val = (diff == -1) ? null : newFileLoc.substring(
3206                       histbug, diff);
3207               if (val != null && val.length() >= 4)
3208               {
3209                 if (val.contains("e"))
3210                 {
3211                   if (val.trim().equals("true"))
3212                   {
3213                     val = "1";
3214                   }
3215                   else
3216                   {
3217                     val = "0";
3218                   }
3219                   newFileLoc.replace(histbug, diff, val);
3220                 }
3221               }
3222               // TODO: assemble String[] { pdb files }, String[] { id for each
3223               // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
3224               // seqs_file 2}} from hash
3225               final String[] pdbf = pdbfilenames
3226                       .toArray(new String[pdbfilenames.size()]), id = pdbids
3227                       .toArray(new String[pdbids.size()]);
3228               final SequenceI[][] sq = seqmaps
3229                       .toArray(new SequenceI[seqmaps.size()][]);
3230               final String fileloc = newFileLoc.toString(), vid = sviewid;
3231               final AlignFrame alf = af;
3232               final java.awt.Rectangle rect = new java.awt.Rectangle(x, y,
3233                       width, height);
3234               try
3235               {
3236                 javax.swing.SwingUtilities.invokeAndWait(new Runnable()
3237                 {
3238                   @Override
3239                   public void run()
3240                   {
3241                     JalviewStructureDisplayI sview = null;
3242                     try
3243                     {
3244                       // JAL-1333 note - we probably can't migrate Jmol views to UCSF Chimera!
3245                       sview = new StructureViewer(alf.alignPanel.getStructureSelectionManager()).createView(StructureViewer.Viewer.JMOL, pdbf, id, sq, alf.alignPanel,
3246                               useinJmolsuperpos, usetoColourbyseq,
3247                               jmolColouring, fileloc, rect, vid);
3248                       addNewStructureViewer(sview);
3249                     } catch (OutOfMemoryError ex)
3250                     {
3251                       new OOMWarning("restoring structure view for PDB id "
3252                               + id, (OutOfMemoryError) ex.getCause());
3253                       if (sview != null && sview.isVisible())
3254                       {
3255                         sview.closeViewer();
3256                         sview.setVisible(false);
3257                         sview.dispose();
3258                       }
3259                     }
3260                   }
3261                 });
3262               } catch (InvocationTargetException ex)
3263               {
3264                 warn("Unexpected error when opening Jmol view.", ex);
3265
3266               } catch (InterruptedException e)
3267               {
3268                 // e.printStackTrace();
3269               }
3270             }
3271
3272           }
3273           else
3274           // if (comp != null)
3275           {
3276             // NOTE: if the jalview project is part of a shared session then
3277             // view synchronization should/could be done here.
3278
3279             // add mapping for sequences in this view to an already open Jmol
3280             // instance
3281             for (File id : oldFiles.keySet())
3282             {
3283               // add this and any other pdb files that should be present in the
3284               // viewer
3285               Object[] filedat = oldFiles.get(id);
3286               String pdbFile = (String) filedat[0];
3287               SequenceI[] seq = ((Vector<SequenceI>) filedat[2])
3288                       .toArray(new SequenceI[0]);
3289               comp.jmb.ssm.setMapping(seq, null, pdbFile,
3290                       jalview.io.AppletFormatAdapter.FILE);
3291               comp.jmb.addSequenceForStructFile(pdbFile, seq);
3292             }
3293             // and add the AlignmentPanel's reference to the Jmol view
3294             comp.addAlignmentPanel(ap);
3295             if (useinJmolsuperpos)
3296             {
3297               comp.useAlignmentPanelForSuperposition(ap);
3298             }
3299             else
3300             {
3301               comp.excludeAlignmentPanelForSuperposition(ap);
3302             }
3303             if (usetoColourbyseq)
3304             {
3305               comp.useAlignmentPanelForColourbyseq(ap, !jmolColouring);
3306             }
3307             else
3308             {
3309               comp.excludeAlignmentPanelForColourbyseq(ap);
3310             }
3311           }
3312         }
3313       }
3314     }
3315     // and finally return.
3316     return af;
3317   }
3318
3319   /**
3320    * 
3321    * @param supported
3322    *          - minimum version we are comparing against
3323    * @param version
3324    *          - version of data being processsed.
3325    * @return true if version is development/null or evaluates to the same or
3326    *         later X.Y.Z (where X,Y,Z are like [0-9]+b?[0-9]*)
3327    */
3328   private boolean isVersionStringLaterThan(String supported, String version)
3329   {
3330     if (version == null || version.equalsIgnoreCase("DEVELOPMENT BUILD")
3331             || version.equalsIgnoreCase("Test")
3332             || version.equalsIgnoreCase("AUTOMATED BUILD"))
3333     {
3334       System.err.println("Assuming project file with "
3335               + (version == null ? "null" : version)
3336               + " is compatible with Jalview version " + supported);
3337       return true;
3338     }
3339     else
3340     {
3341       StringTokenizer currentV = new StringTokenizer(supported, "."), fileV = new StringTokenizer(
3342               version, ".");
3343       while (currentV.hasMoreTokens() && fileV.hasMoreTokens())
3344       {
3345         // convert b to decimal to catch bugfix releases within a series
3346         String curT = currentV.nextToken().toLowerCase().replace('b', '.');
3347         String fileT = fileV.nextToken().toLowerCase().replace('b', '.');
3348         try
3349         {
3350           if (Float.valueOf(curT) > Float.valueOf(fileT))
3351           {
3352             // current version is newer than the version that wrote the file
3353             return false;
3354           }
3355         } catch (NumberFormatException nfe)
3356         {
3357           System.err
3358                   .println("** WARNING: Version comparison failed for tokens ("
3359                           + curT
3360                           + ") and ("
3361                           + fileT
3362                           + ")\n** Current: '"
3363                           + supported + "' and Version: '" + version + "'");
3364         }
3365       }
3366       if (currentV.hasMoreElements())
3367       {
3368         // fileV has no minor version but identical series to current
3369         return false;
3370       }
3371     }
3372     return true;
3373   }
3374
3375   Vector<JalviewStructureDisplayI> newStructureViewers = null;
3376
3377   protected void addNewStructureViewer(JalviewStructureDisplayI sview)
3378   {
3379     if (newStructureViewers != null)
3380     {
3381       sview.getBinding().setFinishedLoadingFromArchive(false);
3382       newStructureViewers.add(sview);
3383     }
3384   }
3385
3386   protected void setLoadingFinishedForNewStructureViewers()
3387   {
3388     if (newStructureViewers != null)
3389     {
3390       for (JalviewStructureDisplayI sview : newStructureViewers)
3391       {
3392         sview.getBinding().setFinishedLoadingFromArchive(true);
3393       }
3394       newStructureViewers.clear();
3395       newStructureViewers = null;
3396     }
3397   }
3398
3399   AlignFrame loadViewport(String file, JSeq[] JSEQ, Vector hiddenSeqs,
3400           Alignment al, JalviewModelSequence jms, Viewport view,
3401           String uniqueSeqSetId, String viewId,
3402           ArrayList<JvAnnotRow> autoAlan)
3403   {
3404     AlignFrame af = null;
3405     af = new AlignFrame(al, view.getWidth(), view.getHeight(),
3406             uniqueSeqSetId, viewId);
3407
3408     af.setFileName(file, "Jalview");
3409
3410     for (int i = 0; i < JSEQ.length; i++)
3411     {
3412       af.viewport.setSequenceColour(af.viewport.getAlignment()
3413               .getSequenceAt(i), new java.awt.Color(JSEQ[i].getColour()));
3414     }
3415
3416     af.viewport.gatherViewsHere = view.getGatheredViews();
3417
3418     if (view.getSequenceSetId() != null)
3419     {
3420       jalview.gui.AlignViewport av = (jalview.gui.AlignViewport) viewportsAdded
3421               .get(uniqueSeqSetId);
3422
3423       af.viewport.setSequenceSetId(uniqueSeqSetId);
3424       if (av != null)
3425       {
3426         // propagate shared settings to this new view
3427         af.viewport.historyList = av.historyList;
3428         af.viewport.redoList = av.redoList;
3429       }
3430       else
3431       {
3432         viewportsAdded.put(uniqueSeqSetId, af.viewport);
3433       }
3434       // TODO: check if this method can be called repeatedly without
3435       // side-effects if alignpanel already registered.
3436       PaintRefresher.Register(af.alignPanel, uniqueSeqSetId);
3437     }
3438     // apply Hidden regions to view.
3439     if (hiddenSeqs != null)
3440     {
3441       for (int s = 0; s < JSEQ.length; s++)
3442       {
3443         jalview.datamodel.SequenceGroup hidden = new jalview.datamodel.SequenceGroup();
3444
3445         for (int r = 0; r < JSEQ[s].getHiddenSequencesCount(); r++)
3446         {
3447           hidden.addSequence(
3448                   al.getSequenceAt(JSEQ[s].getHiddenSequences(r)), false);
3449         }
3450         af.viewport.hideRepSequences(al.getSequenceAt(s), hidden);
3451       }
3452
3453       jalview.datamodel.SequenceI[] hseqs = new jalview.datamodel.SequenceI[hiddenSeqs
3454               .size()];
3455
3456       for (int s = 0; s < hiddenSeqs.size(); s++)
3457       {
3458         hseqs[s] = (jalview.datamodel.SequenceI) hiddenSeqs.elementAt(s);
3459       }
3460
3461       af.viewport.hideSequence(hseqs);
3462
3463     }
3464     // recover view properties and display parameters
3465     if (view.getViewName() != null)
3466     {
3467       af.viewport.viewName = view.getViewName();
3468       af.setInitialTabVisible();
3469     }
3470     af.setBounds(view.getXpos(), view.getYpos(), view.getWidth(),
3471             view.getHeight());
3472
3473     af.viewport.setShowAnnotation(view.getShowAnnotation());
3474     af.viewport.setAbovePIDThreshold(view.getPidSelected());
3475
3476     af.viewport.setColourText(view.getShowColourText());
3477
3478     af.viewport.setConservationSelected(view.getConservationSelected());
3479     af.viewport.setShowJVSuffix(view.getShowFullId());
3480     af.viewport.rightAlignIds = view.getRightAlignIds();
3481     af.viewport.setFont(new java.awt.Font(view.getFontName(), view
3482             .getFontStyle(), view.getFontSize()));
3483     af.alignPanel.fontChanged();
3484     af.viewport.setRenderGaps(view.getRenderGaps());
3485     af.viewport.setWrapAlignment(view.getWrapAlignment());
3486     af.alignPanel.setWrapAlignment(view.getWrapAlignment());
3487     af.viewport.setShowAnnotation(view.getShowAnnotation());
3488     af.alignPanel.setAnnotationVisible(view.getShowAnnotation());
3489
3490     af.viewport.setShowBoxes(view.getShowBoxes());
3491
3492     af.viewport.setShowText(view.getShowText());
3493
3494     af.viewport.textColour = new java.awt.Color(view.getTextCol1());
3495     af.viewport.textColour2 = new java.awt.Color(view.getTextCol2());
3496     af.viewport.thresholdTextColour = view.getTextColThreshold();
3497     af.viewport.setShowUnconserved(view.hasShowUnconserved() ? view
3498             .isShowUnconserved() : false);
3499     af.viewport.setStartRes(view.getStartRes());
3500     af.viewport.setStartSeq(view.getStartSeq());
3501
3502     ColourSchemeI cs = null;
3503     // apply colourschemes
3504     if (view.getBgColour() != null)
3505     {
3506       if (view.getBgColour().startsWith("ucs"))
3507       {
3508         cs = GetUserColourScheme(jms, view.getBgColour());
3509       }
3510       else if (view.getBgColour().startsWith("Annotation"))
3511       {
3512         AnnotationColours viewAnnColour = view.getAnnotationColours();
3513         cs = constructAnnotationColour(viewAnnColour, af, al, jms, true);
3514
3515         // annpos
3516
3517       }
3518       else
3519       {
3520         cs = ColourSchemeProperty.getColour(al, view.getBgColour());
3521       }
3522
3523       if (cs != null)
3524       {
3525         cs.setThreshold(view.getPidThreshold(), true);
3526         cs.setConsensus(af.viewport.getSequenceConsensusHash());
3527       }
3528     }
3529
3530     af.viewport.setGlobalColourScheme(cs);
3531     af.viewport.setColourAppliesToAllGroups(false);
3532
3533     if (view.getConservationSelected() && cs != null)
3534     {
3535       cs.setConservationInc(view.getConsThreshold());
3536     }
3537
3538     af.changeColour(cs);
3539
3540     af.viewport.setColourAppliesToAllGroups(true);
3541
3542     af.viewport.setShowSequenceFeatures(view.getShowSequenceFeatures());
3543
3544     if (view.hasCentreColumnLabels())
3545     {
3546       af.viewport.setCentreColumnLabels(view.getCentreColumnLabels());
3547     }
3548     if (view.hasIgnoreGapsinConsensus())
3549     {
3550       af.viewport.setIgnoreGapsConsensus(view.getIgnoreGapsinConsensus(),
3551               null);
3552     }
3553     if (view.hasFollowHighlight())
3554     {
3555       af.viewport.followHighlight = view.getFollowHighlight();
3556     }
3557     if (view.hasFollowSelection())
3558     {
3559       af.viewport.followSelection = view.getFollowSelection();
3560     }
3561     if (view.hasShowConsensusHistogram())
3562     {
3563       af.viewport.setShowConsensusHistogram(view
3564               .getShowConsensusHistogram());
3565     }
3566     else
3567     {
3568       af.viewport.setShowConsensusHistogram(true);
3569     }
3570     if (view.hasShowSequenceLogo())
3571     {
3572       af.viewport.setShowSequenceLogo(view.getShowSequenceLogo());
3573     }
3574     else
3575     {
3576       af.viewport.setShowSequenceLogo(false);
3577     }
3578     if (view.hasNormaliseSequenceLogo())
3579     {
3580       af.viewport.setNormaliseSequenceLogo(view.getNormaliseSequenceLogo());
3581     }
3582     if (view.hasShowDbRefTooltip())
3583     {
3584       af.viewport.setShowDbRefs(view.getShowDbRefTooltip());
3585     }
3586     if (view.hasShowNPfeatureTooltip())
3587     {
3588       af.viewport.setShowNpFeats(view.hasShowNPfeatureTooltip());
3589     }
3590     if (view.hasShowGroupConsensus())
3591     {
3592       af.viewport.setShowGroupConsensus(view.getShowGroupConsensus());
3593     }
3594     else
3595     {
3596       af.viewport.setShowGroupConsensus(false);
3597     }
3598     if (view.hasShowGroupConservation())
3599     {
3600       af.viewport.setShowGroupConservation(view.getShowGroupConservation());
3601     }
3602     else
3603     {
3604       af.viewport.setShowGroupConservation(false);
3605     }
3606
3607     // recover featre settings
3608     if (jms.getFeatureSettings() != null)
3609     {
3610       FeaturesDisplayed fdi;
3611       af.viewport.setFeaturesDisplayed(fdi = new FeaturesDisplayed());
3612       String[] renderOrder = new String[jms.getFeatureSettings()
3613               .getSettingCount()];
3614       Hashtable featureGroups = new Hashtable();
3615       Hashtable featureColours = new Hashtable();
3616       Hashtable featureOrder = new Hashtable();
3617
3618       for (int fs = 0; fs < jms.getFeatureSettings().getSettingCount(); fs++)
3619       {
3620         Setting setting = jms.getFeatureSettings().getSetting(fs);
3621         if (setting.hasMincolour())
3622         {
3623           GraduatedColor gc = setting.hasMin() ? new GraduatedColor(
3624                   new java.awt.Color(setting.getMincolour()),
3625                   new java.awt.Color(setting.getColour()),
3626                   setting.getMin(), setting.getMax()) : new GraduatedColor(
3627                   new java.awt.Color(setting.getMincolour()),
3628                   new java.awt.Color(setting.getColour()), 0, 1);
3629           if (setting.hasThreshold())
3630           {
3631             gc.setThresh(setting.getThreshold());
3632             gc.setThreshType(setting.getThreshstate());
3633           }
3634           gc.setAutoScaled(true); // default
3635           if (setting.hasAutoScale())
3636           {
3637             gc.setAutoScaled(setting.getAutoScale());
3638           }
3639           if (setting.hasColourByLabel())
3640           {
3641             gc.setColourByLabel(setting.getColourByLabel());
3642           }
3643           // and put in the feature colour table.
3644           featureColours.put(setting.getType(), gc);
3645         }
3646         else
3647         {
3648           featureColours.put(setting.getType(),
3649                   new java.awt.Color(setting.getColour()));
3650         }
3651         renderOrder[fs] = setting.getType();
3652         if (setting.hasOrder())
3653         {
3654           featureOrder.put(setting.getType(), setting.getOrder());
3655         }
3656         else
3657         {
3658           featureOrder.put(setting.getType(), new Float(fs
3659                   / jms.getFeatureSettings().getSettingCount()));
3660         }
3661         if (setting.getDisplay())
3662         {
3663           fdi.setVisible(setting.getType());
3664         }
3665       }
3666       Hashtable fgtable = new Hashtable();
3667       for (int gs = 0; gs < jms.getFeatureSettings().getGroupCount(); gs++)
3668       {
3669         Group grp = jms.getFeatureSettings().getGroup(gs);
3670         fgtable.put(grp.getName(), new Boolean(grp.getDisplay()));
3671       }
3672       // FeatureRendererSettings frs = new FeatureRendererSettings(renderOrder,
3673       // fgtable, featureColours, jms.getFeatureSettings().hasTransparency() ?
3674       // jms.getFeatureSettings().getTransparency() : 0.0, featureOrder);
3675       FeatureRendererSettings frs = new FeatureRendererSettings(
3676               renderOrder, fgtable, featureColours, 1.0f, featureOrder);
3677       af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer()
3678               .transferSettings(frs);
3679
3680     }
3681
3682     if (view.getHiddenColumnsCount() > 0)
3683     {
3684       for (int c = 0; c < view.getHiddenColumnsCount(); c++)
3685       {
3686         af.viewport.hideColumns(view.getHiddenColumns(c).getStart(), view
3687                 .getHiddenColumns(c).getEnd() // +1
3688                 );
3689       }
3690     }
3691     if (view.getCalcIdParam() != null)
3692     {
3693       for (CalcIdParam calcIdParam : view.getCalcIdParam())
3694       {
3695         if (calcIdParam != null)
3696         {
3697           if (recoverCalcIdParam(calcIdParam, af.viewport))
3698           {
3699           }
3700           else
3701           {
3702             warn("Couldn't recover parameters for "
3703                     + calcIdParam.getCalcId());
3704           }
3705         }
3706       }
3707     }
3708     af.setMenusFromViewport(af.viewport);
3709     // TODO: we don't need to do this if the viewport is aready visible.
3710     Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(),
3711             view.getHeight());
3712     af.alignPanel.updateAnnotation(false, true); // recompute any autoannotation
3713     reorderAutoannotation(af, al, autoAlan);
3714     af.alignPanel.alignmentChanged();
3715     return af;
3716   }
3717
3718   private ColourSchemeI constructAnnotationColour(
3719           AnnotationColours viewAnnColour, AlignFrame af, Alignment al,
3720           JalviewModelSequence jms, boolean checkGroupAnnColour)
3721   {
3722     boolean propagateAnnColour = false;
3723     ColourSchemeI cs = null;
3724     AlignmentI annAlignment = af != null ? af.viewport.getAlignment() : al;
3725     if (checkGroupAnnColour && al.getGroups() != null
3726             && al.getGroups().size() > 0)
3727     {
3728       // pre 2.8.1 behaviour
3729       // check to see if we should transfer annotation colours
3730       propagateAnnColour = true;
3731       for (jalview.datamodel.SequenceGroup sg : al.getGroups())
3732       {
3733         if (sg.cs instanceof AnnotationColourGradient)
3734         {
3735           propagateAnnColour = false;
3736         }
3737       }
3738     }
3739     // int find annotation
3740     if (annAlignment.getAlignmentAnnotation() != null)
3741     {
3742       for (int i = 0; i < annAlignment.getAlignmentAnnotation().length; i++)
3743       {
3744         if (annAlignment.getAlignmentAnnotation()[i].label
3745                 .equals(viewAnnColour.getAnnotation()))
3746         {
3747           if (annAlignment.getAlignmentAnnotation()[i].getThreshold() == null)
3748           {
3749             annAlignment.getAlignmentAnnotation()[i]
3750                     .setThreshold(new jalview.datamodel.GraphLine(
3751                             viewAnnColour.getThreshold(), "Threshold",
3752                             java.awt.Color.black)
3753
3754                     );
3755           }
3756
3757           if (viewAnnColour.getColourScheme().equals("None"))
3758           {
3759             cs = new AnnotationColourGradient(
3760                     annAlignment.getAlignmentAnnotation()[i],
3761                     new java.awt.Color(viewAnnColour.getMinColour()),
3762                     new java.awt.Color(viewAnnColour.getMaxColour()),
3763                     viewAnnColour.getAboveThreshold());
3764           }
3765           else if (viewAnnColour.getColourScheme().startsWith("ucs"))
3766           {
3767             cs = new AnnotationColourGradient(
3768                     annAlignment.getAlignmentAnnotation()[i],
3769                     GetUserColourScheme(jms,
3770                             viewAnnColour.getColourScheme()),
3771                     viewAnnColour.getAboveThreshold());
3772           }
3773           else
3774           {
3775             cs = new AnnotationColourGradient(
3776                     annAlignment.getAlignmentAnnotation()[i],
3777                     ColourSchemeProperty.getColour(al,
3778                             viewAnnColour.getColourScheme()),
3779                     viewAnnColour.getAboveThreshold());
3780           }
3781           if (viewAnnColour.hasPerSequence())
3782           {
3783             ((AnnotationColourGradient) cs).setSeqAssociated(viewAnnColour
3784                     .isPerSequence());
3785           }
3786           if (viewAnnColour.hasPredefinedColours())
3787           {
3788             ((AnnotationColourGradient) cs)
3789                     .setPredefinedColours(viewAnnColour
3790                             .isPredefinedColours());
3791           }
3792           if (propagateAnnColour && al.getGroups() != null)
3793           {
3794             // Also use these settings for all the groups
3795             for (int g = 0; g < al.getGroups().size(); g++)
3796             {
3797               jalview.datamodel.SequenceGroup sg = al.getGroups().get(g);
3798
3799               if (sg.cs == null)
3800               {
3801                 continue;
3802               }
3803
3804               /*
3805                * if (viewAnnColour.getColourScheme().equals("None" )) { sg.cs =
3806                * new AnnotationColourGradient(
3807                * annAlignment.getAlignmentAnnotation()[i], new
3808                * java.awt.Color(viewAnnColour. getMinColour()), new
3809                * java.awt.Color(viewAnnColour. getMaxColour()),
3810                * viewAnnColour.getAboveThreshold()); } else
3811                */
3812               {
3813                 sg.cs = new AnnotationColourGradient(
3814                         annAlignment.getAlignmentAnnotation()[i], sg.cs,
3815                         viewAnnColour.getAboveThreshold());
3816                 if (cs instanceof AnnotationColourGradient)
3817                 {
3818                   if (viewAnnColour.hasPerSequence())
3819                   {
3820                     ((AnnotationColourGradient) cs)
3821                             .setSeqAssociated(viewAnnColour.isPerSequence());
3822                   }
3823                   if (viewAnnColour.hasPredefinedColours())
3824                   {
3825                     ((AnnotationColourGradient) cs)
3826                             .setPredefinedColours(viewAnnColour
3827                                     .isPredefinedColours());
3828                   }
3829                 }
3830               }
3831
3832             }
3833           }
3834
3835           break;
3836         }
3837
3838       }
3839     }
3840     return cs;
3841   }
3842
3843   private void reorderAutoannotation(AlignFrame af, Alignment al,
3844           ArrayList<JvAnnotRow> autoAlan)
3845   {
3846     // copy over visualization settings for autocalculated annotation in the
3847     // view
3848     if (al.getAlignmentAnnotation() != null)
3849     {
3850       /**
3851        * Kludge for magic autoannotation names (see JAL-811)
3852        */
3853       String[] magicNames = new String[]
3854       { "Consensus", "Quality", "Conservation" };
3855       JvAnnotRow nullAnnot = new JvAnnotRow(-1, null);
3856       Hashtable<String, JvAnnotRow> visan = new Hashtable<String, JvAnnotRow>();
3857       for (String nm : magicNames)
3858       {
3859         visan.put(nm, nullAnnot);
3860       }
3861       for (JvAnnotRow auan : autoAlan)
3862       {
3863         visan.put(auan.template.label
3864                 + (auan.template.getCalcId() == null ? "" : "\t"
3865                         + auan.template.getCalcId()), auan);
3866       }
3867       int hSize = al.getAlignmentAnnotation().length;
3868       ArrayList<JvAnnotRow> reorder = new ArrayList<JvAnnotRow>();
3869       // work through any autoCalculated annotation already on the view
3870       // removing it if it should be placed in a different location on the
3871       // annotation panel.
3872       List<String> remains = new ArrayList(visan.keySet());
3873       for (int h = 0; h < hSize; h++)
3874       {
3875         jalview.datamodel.AlignmentAnnotation jalan = al
3876                 .getAlignmentAnnotation()[h];
3877         if (jalan.autoCalculated)
3878         {
3879           String k;
3880           JvAnnotRow valan = visan.get(k = jalan.label);
3881           if (jalan.getCalcId() != null)
3882           {
3883             valan = visan.get(k = jalan.label + "\t" + jalan.getCalcId());
3884           }
3885
3886           if (valan != null)
3887           {
3888             // delete the auto calculated row from the alignment
3889             al.deleteAnnotation(jalan, false);
3890             remains.remove(k);
3891             hSize--;
3892             h--;
3893             if (valan != nullAnnot)
3894             {
3895               if (jalan != valan.template)
3896               {
3897                 // newly created autoannotation row instance
3898                 // so keep a reference to the visible annotation row
3899                 // and copy over all relevant attributes
3900                 if (valan.template.graphHeight >= 0)
3901
3902                 {
3903                   jalan.graphHeight = valan.template.graphHeight;
3904                 }
3905                 jalan.visible = valan.template.visible;
3906               }
3907               reorder.add(new JvAnnotRow(valan.order, jalan));
3908             }
3909           }
3910         }
3911       }
3912       // Add any (possibly stale) autocalculated rows that were not appended to
3913       // the view during construction
3914       for (String other : remains)
3915       {
3916         JvAnnotRow othera = visan.get(other);
3917         if (othera != nullAnnot && othera.template.getCalcId() != null
3918                 && othera.template.getCalcId().length() > 0)
3919         {
3920           reorder.add(othera);
3921         }
3922       }
3923       // now put the automatic annotation in its correct place
3924       int s = 0, srt[] = new int[reorder.size()];
3925       JvAnnotRow[] rws = new JvAnnotRow[reorder.size()];
3926       for (JvAnnotRow jvar : reorder)
3927       {
3928         rws[s] = jvar;
3929         srt[s++] = jvar.order;
3930       }
3931       reorder.clear();
3932       jalview.util.QuickSort.sort(srt, rws);
3933       // and re-insert the annotation at its correct position
3934       for (JvAnnotRow jvar : rws)
3935       {
3936         al.addAnnotation(jvar.template, jvar.order);
3937       }
3938       af.alignPanel.adjustAnnotationHeight();
3939     }
3940   }
3941
3942   Hashtable skipList = null;
3943
3944   /**
3945    * TODO remove this method
3946    * 
3947    * @param view
3948    * @return AlignFrame bound to sequenceSetId from view, if one exists. private
3949    *         AlignFrame getSkippedFrame(Viewport view) { if (skipList==null) {
3950    *         throw new Error("Implementation Error. No skipList defined for this
3951    *         Jalview2XML instance."); } return (AlignFrame)
3952    *         skipList.get(view.getSequenceSetId()); }
3953    */
3954
3955   /**
3956    * Check if the Jalview view contained in object should be skipped or not.
3957    * 
3958    * @param object
3959    * @return true if view's sequenceSetId is a key in skipList
3960    */
3961   private boolean skipViewport(JalviewModel object)
3962   {
3963     if (skipList == null)
3964     {
3965       return false;
3966     }
3967     String id;
3968     if (skipList.containsKey(id = object.getJalviewModelSequence()
3969             .getViewport()[0].getSequenceSetId()))
3970     {
3971       if (Cache.log != null && Cache.log.isDebugEnabled())
3972       {
3973         Cache.log.debug("Skipping seuqence set id " + id);
3974       }
3975       return true;
3976     }
3977     return false;
3978   }
3979
3980   public void AddToSkipList(AlignFrame af)
3981   {
3982     if (skipList == null)
3983     {
3984       skipList = new Hashtable();
3985     }
3986     skipList.put(af.getViewport().getSequenceSetId(), af);
3987   }
3988
3989   public void clearSkipList()
3990   {
3991     if (skipList != null)
3992     {
3993       skipList.clear();
3994       skipList = null;
3995     }
3996   }
3997
3998   private void recoverDatasetFor(SequenceSet vamsasSet, Alignment al)
3999   {
4000     jalview.datamodel.Alignment ds = getDatasetFor(vamsasSet.getDatasetId());
4001     Vector dseqs = null;
4002     if (ds == null)
4003     {
4004       // create a list of new dataset sequences
4005       dseqs = new Vector();
4006     }
4007     for (int i = 0, iSize = vamsasSet.getSequenceCount(); i < iSize; i++)
4008     {
4009       Sequence vamsasSeq = vamsasSet.getSequence(i);
4010       ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs);
4011     }
4012     // create a new dataset
4013     if (ds == null)
4014     {
4015       SequenceI[] dsseqs = new SequenceI[dseqs.size()];
4016       dseqs.copyInto(dsseqs);
4017       ds = new jalview.datamodel.Alignment(dsseqs);
4018       debug("Created new dataset " + vamsasSet.getDatasetId()
4019               + " for alignment " + System.identityHashCode(al));
4020       addDatasetRef(vamsasSet.getDatasetId(), ds);
4021     }
4022     // set the dataset for the newly imported alignment.
4023     if (al.getDataset() == null)
4024     {
4025       al.setDataset(ds);
4026     }
4027   }
4028
4029   /**
4030    * 
4031    * @param vamsasSeq
4032    *          sequence definition to create/merge dataset sequence for
4033    * @param ds
4034    *          dataset alignment
4035    * @param dseqs
4036    *          vector to add new dataset sequence to
4037    */
4038   private void ensureJalviewDatasetSequence(Sequence vamsasSeq,
4039           AlignmentI ds, Vector dseqs)
4040   {
4041     // JBP TODO: Check this is called for AlCodonFrames to support recovery of
4042     // xRef Codon Maps
4043     jalview.datamodel.Sequence sq = (jalview.datamodel.Sequence) seqRefIds
4044             .get(vamsasSeq.getId());
4045     jalview.datamodel.SequenceI dsq = null;
4046     if (sq != null && sq.getDatasetSequence() != null)
4047     {
4048       dsq = sq.getDatasetSequence();
4049     }
4050
4051     String sqid = vamsasSeq.getDsseqid();
4052     if (dsq == null)
4053     {
4054       // need to create or add a new dataset sequence reference to this sequence
4055       if (sqid != null)
4056       {
4057         dsq = (jalview.datamodel.SequenceI) seqRefIds.get(sqid);
4058       }
4059       // check again
4060       if (dsq == null)
4061       {
4062         // make a new dataset sequence
4063         dsq = sq.createDatasetSequence();
4064         if (sqid == null)
4065         {
4066           // make up a new dataset reference for this sequence
4067           sqid = seqHash(dsq);
4068         }
4069         dsq.setVamsasId(uniqueSetSuffix + sqid);
4070         seqRefIds.put(sqid, dsq);
4071         if (ds == null)
4072         {
4073           if (dseqs != null)
4074           {
4075             dseqs.addElement(dsq);
4076           }
4077         }
4078         else
4079         {
4080           ds.addSequence(dsq);
4081         }
4082       }
4083       else
4084       {
4085         if (sq != dsq)
4086         { // make this dataset sequence sq's dataset sequence
4087           sq.setDatasetSequence(dsq);
4088           // and update the current dataset alignment
4089           if (ds == null)
4090           {
4091             if (dseqs != null)
4092             {
4093               if (!dseqs.contains(dsq))
4094               {
4095                 dseqs.add(dsq);
4096               }
4097             }
4098             else
4099             {
4100               if (ds.findIndex(dsq) < 0)
4101               {
4102                 ds.addSequence(dsq);
4103               }
4104             }
4105           }
4106         }
4107       }
4108     }
4109     // TODO: refactor this as a merge dataset sequence function
4110     // now check that sq (the dataset sequence) sequence really is the union of
4111     // all references to it
4112     // boolean pre = sq.getStart() < dsq.getStart();
4113     // boolean post = sq.getEnd() > dsq.getEnd();
4114     // if (pre || post)
4115     if (sq != dsq)
4116     {
4117       StringBuffer sb = new StringBuffer();
4118       String newres = jalview.analysis.AlignSeq.extractGaps(
4119               jalview.util.Comparison.GapChars, sq.getSequenceAsString());
4120       if (!newres.equalsIgnoreCase(dsq.getSequenceAsString())
4121               && newres.length() > dsq.getLength())
4122       {
4123         // Update with the longer sequence.
4124         synchronized (dsq)
4125         {
4126           /*
4127            * if (pre) { sb.insert(0, newres .substring(0, dsq.getStart() -
4128            * sq.getStart())); dsq.setStart(sq.getStart()); } if (post) {
4129            * sb.append(newres.substring(newres.length() - sq.getEnd() -
4130            * dsq.getEnd())); dsq.setEnd(sq.getEnd()); }
4131            */
4132           dsq.setSequence(newres);
4133         }
4134         // TODO: merges will never happen if we 'know' we have the real dataset
4135         // sequence - this should be detected when id==dssid
4136         System.err
4137                 .println("DEBUG Notice:  Merged dataset sequence (if you see this often, post at http://issues.jalview.org/browse/JAL-1474)"); // ("
4138         // + (pre ? "prepended" : "") + " "
4139         // + (post ? "appended" : ""));
4140       }
4141     }
4142   }
4143
4144   java.util.Hashtable datasetIds = null;
4145
4146   java.util.IdentityHashMap dataset2Ids = null;
4147
4148   private Alignment getDatasetFor(String datasetId)
4149   {
4150     if (datasetIds == null)
4151     {
4152       datasetIds = new Hashtable();
4153       return null;
4154     }
4155     if (datasetIds.containsKey(datasetId))
4156     {
4157       return (Alignment) datasetIds.get(datasetId);
4158     }
4159     return null;
4160   }
4161
4162   private void addDatasetRef(String datasetId, Alignment dataset)
4163   {
4164     if (datasetIds == null)
4165     {
4166       datasetIds = new Hashtable();
4167     }
4168     datasetIds.put(datasetId, dataset);
4169   }
4170
4171   /**
4172    * make a new dataset ID for this jalview dataset alignment
4173    * 
4174    * @param dataset
4175    * @return
4176    */
4177   private String getDatasetIdRef(jalview.datamodel.Alignment dataset)
4178   {
4179     if (dataset.getDataset() != null)
4180     {
4181       warn("Serious issue!  Dataset Object passed to getDatasetIdRef is not a Jalview DATASET alignment...");
4182     }
4183     String datasetId = makeHashCode(dataset, null);
4184     if (datasetId == null)
4185     {
4186       // make a new datasetId and record it
4187       if (dataset2Ids == null)
4188       {
4189         dataset2Ids = new IdentityHashMap();
4190       }
4191       else
4192       {
4193         datasetId = (String) dataset2Ids.get(dataset);
4194       }
4195       if (datasetId == null)
4196       {
4197         datasetId = "ds" + dataset2Ids.size() + 1;
4198         dataset2Ids.put(dataset, datasetId);
4199       }
4200     }
4201     return datasetId;
4202   }
4203
4204   private void addDBRefs(SequenceI datasetSequence, Sequence sequence)
4205   {
4206     for (int d = 0; d < sequence.getDBRefCount(); d++)
4207     {
4208       DBRef dr = sequence.getDBRef(d);
4209       jalview.datamodel.DBRefEntry entry = new jalview.datamodel.DBRefEntry(
4210               sequence.getDBRef(d).getSource(), sequence.getDBRef(d)
4211                       .getVersion(), sequence.getDBRef(d).getAccessionId());
4212       if (dr.getMapping() != null)
4213       {
4214         entry.setMap(addMapping(dr.getMapping()));
4215       }
4216       datasetSequence.addDBRef(entry);
4217     }
4218   }
4219
4220   private jalview.datamodel.Mapping addMapping(Mapping m)
4221   {
4222     SequenceI dsto = null;
4223     // Mapping m = dr.getMapping();
4224     int fr[] = new int[m.getMapListFromCount() * 2];
4225     Enumeration f = m.enumerateMapListFrom();
4226     for (int _i = 0; f.hasMoreElements(); _i += 2)
4227     {
4228       MapListFrom mf = (MapListFrom) f.nextElement();
4229       fr[_i] = mf.getStart();
4230       fr[_i + 1] = mf.getEnd();
4231     }
4232     int fto[] = new int[m.getMapListToCount() * 2];
4233     f = m.enumerateMapListTo();
4234     for (int _i = 0; f.hasMoreElements(); _i += 2)
4235     {
4236       MapListTo mf = (MapListTo) f.nextElement();
4237       fto[_i] = mf.getStart();
4238       fto[_i + 1] = mf.getEnd();
4239     }
4240     jalview.datamodel.Mapping jmap = new jalview.datamodel.Mapping(dsto,
4241             fr, fto, (int) m.getMapFromUnit(), (int) m.getMapToUnit());
4242     if (m.getMappingChoice() != null)
4243     {
4244       MappingChoice mc = m.getMappingChoice();
4245       if (mc.getDseqFor() != null)
4246       {
4247         String dsfor = "" + mc.getDseqFor();
4248         if (seqRefIds.containsKey(dsfor))
4249         {
4250           /**
4251            * recover from hash
4252            */
4253           jmap.setTo((SequenceI) seqRefIds.get(dsfor));
4254         }
4255         else
4256         {
4257           frefedSequence.add(new Object[]
4258           { dsfor, jmap });
4259         }
4260       }
4261       else
4262       {
4263         /**
4264          * local sequence definition
4265          */
4266         Sequence ms = mc.getSequence();
4267         jalview.datamodel.Sequence djs = null;
4268         String sqid = ms.getDsseqid();
4269         if (sqid != null && sqid.length() > 0)
4270         {
4271           /*
4272            * recover dataset sequence
4273            */
4274           djs = (jalview.datamodel.Sequence) seqRefIds.get(sqid);
4275         }
4276         else
4277         {
4278           System.err
4279                   .println("Warning - making up dataset sequence id for DbRef sequence map reference");
4280           sqid = ((Object) ms).toString(); // make up a new hascode for
4281           // undefined dataset sequence hash
4282           // (unlikely to happen)
4283         }
4284
4285         if (djs == null)
4286         {
4287           /**
4288            * make a new dataset sequence and add it to refIds hash
4289            */
4290           djs = new jalview.datamodel.Sequence(ms.getName(),
4291                   ms.getSequence());
4292           djs.setStart(jmap.getMap().getToLowest());
4293           djs.setEnd(jmap.getMap().getToHighest());
4294           djs.setVamsasId(uniqueSetSuffix + sqid);
4295           jmap.setTo(djs);
4296           seqRefIds.put(sqid, djs);
4297
4298         }
4299         jalview.bin.Cache.log.debug("about to recurse on addDBRefs.");
4300         addDBRefs(djs, ms);
4301
4302       }
4303     }
4304     return (jmap);
4305
4306   }
4307
4308   public jalview.gui.AlignmentPanel copyAlignPanel(AlignmentPanel ap,
4309           boolean keepSeqRefs)
4310   {
4311     initSeqRefs();
4312     jalview.schemabinding.version2.JalviewModel jm = SaveState(ap, null,
4313             null);
4314
4315     if (!keepSeqRefs)
4316     {
4317       clearSeqRefs();
4318       jm.getJalviewModelSequence().getViewport(0).setSequenceSetId(null);
4319     }
4320     else
4321     {
4322       uniqueSetSuffix = "";
4323       jm.getJalviewModelSequence().getViewport(0).setId(null); // we don't
4324       // overwrite the
4325       // view we just
4326       // copied
4327     }
4328     if (this.frefedSequence == null)
4329     {
4330       frefedSequence = new Vector();
4331     }
4332
4333     viewportsAdded = new Hashtable();
4334
4335     AlignFrame af = LoadFromObject(jm, null, false, null);
4336     af.alignPanels.clear();
4337     af.closeMenuItem_actionPerformed(true);
4338
4339     /*
4340      * if(ap.av.getAlignment().getAlignmentAnnotation()!=null) { for(int i=0;
4341      * i<ap.av.getAlignment().getAlignmentAnnotation().length; i++) {
4342      * if(!ap.av.getAlignment().getAlignmentAnnotation()[i].autoCalculated) {
4343      * af.alignPanel.av.getAlignment().getAlignmentAnnotation()[i] =
4344      * ap.av.getAlignment().getAlignmentAnnotation()[i]; } } }
4345      */
4346
4347     return af.alignPanel;
4348   }
4349
4350   /**
4351    * flag indicating if hashtables should be cleared on finalization TODO this
4352    * flag may not be necessary
4353    */
4354   private final boolean _cleartables = true;
4355
4356   private Hashtable jvids2vobj;
4357
4358   /*
4359    * (non-Javadoc)
4360    * 
4361    * @see java.lang.Object#finalize()
4362    */
4363   @Override
4364   protected void finalize() throws Throwable
4365   {
4366     // really make sure we have no buried refs left.
4367     if (_cleartables)
4368     {
4369       clearSeqRefs();
4370     }
4371     this.seqRefIds = null;
4372     this.seqsToIds = null;
4373     super.finalize();
4374   }
4375
4376   private void warn(String msg)
4377   {
4378     warn(msg, null);
4379   }
4380
4381   private void warn(String msg, Exception e)
4382   {
4383     if (Cache.log != null)
4384     {
4385       if (e != null)
4386       {
4387         Cache.log.warn(msg, e);
4388       }
4389       else
4390       {
4391         Cache.log.warn(msg);
4392       }
4393     }
4394     else
4395     {
4396       System.err.println("Warning: " + msg);
4397       if (e != null)
4398       {
4399         e.printStackTrace();
4400       }
4401     }
4402   }
4403
4404   private void debug(String string)
4405   {
4406     debug(string, null);
4407   }
4408
4409   private void debug(String msg, Exception e)
4410   {
4411     if (Cache.log != null)
4412     {
4413       if (e != null)
4414       {
4415         Cache.log.debug(msg, e);
4416       }
4417       else
4418       {
4419         Cache.log.debug(msg);
4420       }
4421     }
4422     else
4423     {
4424       System.err.println("Warning: " + msg);
4425       if (e != null)
4426       {
4427         e.printStackTrace();
4428       }
4429     }
4430   }
4431
4432   /**
4433    * set the object to ID mapping tables used to write/recover objects and XML
4434    * ID strings for the jalview project. If external tables are provided then
4435    * finalize and clearSeqRefs will not clear the tables when the Jalview2XML
4436    * object goes out of scope. - also populates the datasetIds hashtable with
4437    * alignment objects containing dataset sequences
4438    * 
4439    * @param vobj2jv
4440    *          Map from ID strings to jalview datamodel
4441    * @param jv2vobj
4442    *          Map from jalview datamodel to ID strings
4443    * 
4444    * 
4445    */
4446   public void setObjectMappingTables(Hashtable vobj2jv,
4447           IdentityHashMap jv2vobj)
4448   {
4449     this.jv2vobj = jv2vobj;
4450     this.vobj2jv = vobj2jv;
4451     Iterator ds = jv2vobj.keySet().iterator();
4452     String id;
4453     while (ds.hasNext())
4454     {
4455       Object jvobj = ds.next();
4456       id = jv2vobj.get(jvobj).toString();
4457       if (jvobj instanceof jalview.datamodel.Alignment)
4458       {
4459         if (((jalview.datamodel.Alignment) jvobj).getDataset() == null)
4460         {
4461           addDatasetRef(id, (jalview.datamodel.Alignment) jvobj);
4462         }
4463       }
4464       else if (jvobj instanceof jalview.datamodel.Sequence)
4465       {
4466         // register sequence object so the XML parser can recover it.
4467         if (seqRefIds == null)
4468         {
4469           seqRefIds = new Hashtable();
4470         }
4471         if (seqsToIds == null)
4472         {
4473           seqsToIds = new IdentityHashMap();
4474         }
4475         seqRefIds.put(jv2vobj.get(jvobj).toString(), jvobj);
4476         seqsToIds.put(jvobj, id);
4477       }
4478       else if (jvobj instanceof jalview.datamodel.AlignmentAnnotation)
4479       {
4480         if (annotationIds == null)
4481         {
4482           annotationIds = new Hashtable();
4483         }
4484         String anid;
4485         annotationIds.put(anid = jv2vobj.get(jvobj).toString(), jvobj);
4486         jalview.datamodel.AlignmentAnnotation jvann = (jalview.datamodel.AlignmentAnnotation) jvobj;
4487         if (jvann.annotationId == null)
4488         {
4489           jvann.annotationId = anid;
4490         }
4491         if (!jvann.annotationId.equals(anid))
4492         {
4493           // TODO verify that this is the correct behaviour
4494           this.warn("Overriding Annotation ID for " + anid
4495                   + " from different id : " + jvann.annotationId);
4496           jvann.annotationId = anid;
4497         }
4498       }
4499       else if (jvobj instanceof String)
4500       {
4501         if (jvids2vobj == null)
4502         {
4503           jvids2vobj = new Hashtable();
4504           jvids2vobj.put(jvobj, jv2vobj.get(jvobj).toString());
4505         }
4506       }
4507       else
4508       {
4509         Cache.log.debug("Ignoring " + jvobj.getClass() + " (ID = " + id);
4510       }
4511     }
4512   }
4513
4514   /**
4515    * set the uniqueSetSuffix used to prefix/suffix object IDs for jalview
4516    * objects created from the project archive. If string is null (default for
4517    * construction) then suffix will be set automatically.
4518    * 
4519    * @param string
4520    */
4521   public void setUniqueSetSuffix(String string)
4522   {
4523     uniqueSetSuffix = string;
4524
4525   }
4526
4527   /**
4528    * uses skipList2 as the skipList for skipping views on sequence sets
4529    * associated with keys in the skipList
4530    * 
4531    * @param skipList2
4532    */
4533   public void setSkipList(Hashtable skipList2)
4534   {
4535     skipList = skipList2;
4536   }
4537 }