quick fix for nullpointer in column selection when saving jalview project (David...
[jalview.git] / src / jalview / gui / Jalview2XML.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Development Version 2.4.1)
3  * Copyright (C) 2009 AM Waterhouse, J Procter, G Barton, M Clamp, S Searle
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  * 
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18  */
19 package jalview.gui;
20
21 import java.awt.Rectangle;
22 import java.io.*;
23 import java.net.*;
24 import java.util.*;
25 import java.util.jar.*;
26
27 import javax.swing.*;
28
29 import org.exolab.castor.xml.*;
30
31 import uk.ac.vamsas.objects.utils.MapList;
32 import jalview.bin.Cache;
33 import jalview.datamodel.Alignment;
34 import jalview.datamodel.AlignmentI;
35 import jalview.datamodel.SequenceI;
36 import jalview.schemabinding.version2.*;
37 import jalview.schemes.*;
38 import jalview.structure.StructureSelectionManager;
39 import jalview.util.jarInputStreamProvider;
40
41 /**
42  * Write out the current jalview desktop state as a Jalview XML stream.
43  * 
44  * Note: the vamsas objects referred to here are primitive versions of the
45  * VAMSAS project schema elements - they are not the same and most likely never
46  * will be :)
47  * 
48  * @author $author$
49  * @version $Revision$
50  */
51 public class Jalview2XML
52 {
53   /**
54    * create/return unique hash string for sq
55    * 
56    * @param sq
57    * @return new or existing unique string for sq
58    */
59   String seqHash(SequenceI sq)
60   {
61     if (seqsToIds == null)
62     {
63       initSeqRefs();
64     }
65     if (seqsToIds.containsKey(sq))
66     {
67       return (String) seqsToIds.get(sq);
68     }
69     else
70     {
71       // create sequential key
72       String key = "sq" + (seqsToIds.size() + 1);
73       key = makeHashCode(sq, key); // check we don't have an external reference
74       // for it already.
75       seqsToIds.put(sq, key);
76       return key;
77     }
78   }
79
80   void clearSeqRefs()
81   {
82     if (_cleartables)
83     {
84       if (seqRefIds != null)
85       {
86         seqRefIds.clear();
87       }
88       if (seqsToIds != null)
89       {
90         seqsToIds.clear();
91       }
92       // seqRefIds = null;
93       // seqsToIds = null;
94     }
95     else
96     {
97       // do nothing
98       warn("clearSeqRefs called when _cleartables was not set. Doing nothing.");
99       // seqRefIds = new Hashtable();
100       // seqsToIds = new IdentityHashMap();
101     }
102   }
103
104   void initSeqRefs()
105   {
106     if (seqsToIds == null)
107     {
108       seqsToIds = new IdentityHashMap();
109     }
110     if (seqRefIds == null)
111     {
112       seqRefIds = new Hashtable();
113     }
114   }
115
116   /**
117    * SequenceI reference -> XML ID string in jalview XML. Populated as XML reps
118    * of sequence objects are created.
119    */
120   java.util.IdentityHashMap seqsToIds = null;
121
122   /**
123    * jalview XML Sequence ID to jalview sequence object reference (both dataset
124    * and alignment sequences. Populated as XML reps of sequence objects are
125    * created.)
126    */
127   java.util.Hashtable seqRefIds = null; // key->SequenceI resolution
128
129   Vector frefedSequence = null;
130
131   boolean raiseGUI = true; // whether errors are raised in dialog boxes or not
132
133   public Jalview2XML()
134   {
135   }
136
137   public Jalview2XML(boolean raiseGUI)
138   {
139     this.raiseGUI = raiseGUI;
140   }
141
142   public void resolveFrefedSequences()
143   {
144     if (frefedSequence.size() > 0)
145     {
146       int r = 0, rSize = frefedSequence.size();
147       while (r < rSize)
148       {
149         Object[] ref = (Object[]) frefedSequence.elementAt(r);
150         if (ref != null)
151         {
152           String sref = (String) ref[0];
153           if (seqRefIds.containsKey(sref))
154           {
155             if (ref[1] instanceof jalview.datamodel.Mapping)
156             {
157               SequenceI seq = (SequenceI) seqRefIds.get(sref);
158               while (seq.getDatasetSequence() != null)
159               {
160                 seq = seq.getDatasetSequence();
161               }
162               ((jalview.datamodel.Mapping) ref[1]).setTo(seq);
163             }
164             else
165             {
166               if (ref[1] instanceof jalview.datamodel.AlignedCodonFrame)
167               {
168                 SequenceI seq = (SequenceI) seqRefIds.get(sref);
169                 while (seq.getDatasetSequence() != null)
170                 {
171                   seq = seq.getDatasetSequence();
172                 }
173                 if (ref[2] != null
174                         && ref[2] instanceof jalview.datamodel.Mapping)
175                 {
176                   jalview.datamodel.Mapping mp = (jalview.datamodel.Mapping) ref[2];
177                   ((jalview.datamodel.AlignedCodonFrame) ref[1]).addMap(
178                           seq, mp.getTo(), mp.getMap());
179                 }
180                 else
181                 {
182                   System.err
183                           .println("IMPLEMENTATION ERROR: Unimplemented forward sequence references for AlcodonFrames involving "
184                                   + ref[2].getClass() + " type objects.");
185                 }
186               }
187               else
188               {
189                 System.err
190                         .println("IMPLEMENTATION ERROR: Unimplemented forward sequence references for "
191                                 + ref[1].getClass() + " type objects.");
192               }
193             }
194             frefedSequence.remove(r);
195             rSize--;
196           }
197           else
198           {
199             System.err
200                     .println("IMPLEMENTATION WARNING: Unresolved forward reference for hash string "
201                             + ref[0]
202                             + " with objecttype "
203                             + ref[1].getClass());
204             r++;
205           }
206         }
207         else
208         {
209           // empty reference
210           frefedSequence.remove(r);
211           rSize--;
212         }
213       }
214     }
215   }
216
217   /**
218    * This maintains a list of viewports, the key being the seqSetId. Important
219    * to set historyItem and redoList for multiple views
220    */
221   Hashtable viewportsAdded;
222
223   Hashtable annotationIds = new Hashtable();
224
225   String uniqueSetSuffix = "";
226
227   /**
228    * List of pdbfiles added to Jar
229    */
230   Vector pdbfiles = null;
231
232   // SAVES SEVERAL ALIGNMENT WINDOWS TO SAME JARFILE
233   public void SaveState(File statefile)
234   {
235     try
236     {
237       FileOutputStream fos = new FileOutputStream(statefile);
238       JarOutputStream jout = new JarOutputStream(fos);
239       SaveState(jout);
240
241     } catch (Exception e)
242     {
243       // TODO: inform user of the problem - they need to know if their data was
244       // not saved !
245       if (errorMessage == null)
246       {
247         errorMessage = "Couldn't write Jalview Archive to output file '"
248                 + statefile + "' - See console error log for details";
249       }
250       else
251       {
252         errorMessage += "(output file was '" + statefile + "')";
253       }
254       e.printStackTrace();
255     }
256     reportErrors();
257   }
258
259   /**
260    * Writes a jalview project archive to the given Jar output stream.
261    * 
262    * @param jout
263    */
264   public void SaveState(JarOutputStream jout)
265   {
266     JInternalFrame[] frames = Desktop.desktop.getAllFrames();
267
268     if (frames == null)
269     {
270       return;
271     }
272
273     try
274     {
275
276       // NOTE UTF-8 MUST BE USED FOR WRITING UNICODE CHARS
277       // //////////////////////////////////////////////////
278       // NOTE ALSO new PrintWriter must be used for each new JarEntry
279       PrintWriter out = null;
280
281       Vector shortNames = new Vector();
282
283       // REVERSE ORDER
284       for (int i = frames.length - 1; i > -1; i--)
285       {
286         if (frames[i] instanceof AlignFrame)
287         {
288           AlignFrame af = (AlignFrame) frames[i];
289           // skip ?
290           if (skipList != null
291                   && skipList.containsKey(af.getViewport()
292                           .getSequenceSetId()))
293           {
294             continue;
295           }
296
297           String shortName = af.getTitle();
298
299           if (shortName.indexOf(File.separatorChar) > -1)
300           {
301             shortName = shortName.substring(shortName
302                     .lastIndexOf(File.separatorChar) + 1);
303           }
304
305           int count = 1;
306
307           while (shortNames.contains(shortName))
308           {
309             if (shortName.endsWith("_" + (count - 1)))
310             {
311               shortName = shortName
312                       .substring(0, shortName.lastIndexOf("_"));
313             }
314
315             shortName = shortName.concat("_" + count);
316             count++;
317           }
318
319           shortNames.addElement(shortName);
320
321           if (!shortName.endsWith(".xml"))
322           {
323             shortName = shortName + ".xml";
324           }
325
326           int ap, apSize = af.alignPanels.size();
327           for (ap = 0; ap < apSize; ap++)
328           {
329             AlignmentPanel apanel = (AlignmentPanel) af.alignPanels
330                     .elementAt(ap);
331             String fileName = apSize == 1 ? shortName : ap + shortName;
332             if (!fileName.endsWith(".xml"))
333             {
334               fileName = fileName + ".xml";
335             }
336
337             SaveState(apanel, fileName, jout);
338           }
339         }
340       }
341       try
342       {
343         jout.flush();
344       } catch (Exception foo)
345       {
346       }
347       ;
348       jout.close();
349     } catch (Exception ex)
350     {
351       // TODO: inform user of the problem - they need to know if their data was
352       // not saved !
353       if (errorMessage == null)
354       {
355         errorMessage = "Couldn't write Jalview Archive - see error output for details";
356       }
357       ex.printStackTrace();
358     }
359   }
360
361   // USE THIS METHOD TO SAVE A SINGLE ALIGNMENT WINDOW
362   public boolean SaveAlignment(AlignFrame af, String jarFile,
363           String fileName)
364   {
365     try
366     {
367       int ap, apSize = af.alignPanels.size();
368       FileOutputStream fos = new FileOutputStream(jarFile);
369       JarOutputStream jout = new JarOutputStream(fos);
370       for (ap = 0; ap < apSize; ap++)
371       {
372         AlignmentPanel apanel = (AlignmentPanel) af.alignPanels
373                 .elementAt(ap);
374         String jfileName = apSize == 1 ? fileName : fileName + ap;
375         if (!jfileName.endsWith(".xml"))
376         {
377           jfileName = jfileName + ".xml";
378         }
379         SaveState(apanel, jfileName, jout);
380       }
381
382       try
383       {
384         jout.flush();
385       } catch (Exception foo)
386       {
387       }
388       ;
389       jout.close();
390       return true;
391     } catch (Exception ex)
392     {
393       errorMessage = "Couldn't Write alignment view to Jalview Archive - see error output for details";
394       ex.printStackTrace();
395       return false;
396     }
397   }
398
399   /**
400    * create a JalviewModel from an algnment view and marshall it to a
401    * JarOutputStream
402    * 
403    * @param ap
404    *          panel to create jalview model for
405    * @param fileName
406    *          name of alignment panel written to output stream
407    * @param jout
408    *          jar output stream
409    * @param out
410    *          jar entry name
411    */
412   public JalviewModel SaveState(AlignmentPanel ap, String fileName,
413           JarOutputStream jout)
414   {
415     initSeqRefs();
416
417     Vector userColours = new Vector();
418
419     AlignViewport av = ap.av;
420
421     JalviewModel object = new JalviewModel();
422     object.setVamsasModel(new jalview.schemabinding.version2.VamsasModel());
423
424     object.setCreationDate(new java.util.Date(System.currentTimeMillis()));
425     object.setVersion(jalview.bin.Cache.getProperty("VERSION"));
426
427     jalview.datamodel.AlignmentI jal = av.alignment;
428
429     if (av.hasHiddenRows)
430     {
431       jal = jal.getHiddenSequences().getFullAlignment();
432     }
433
434     SequenceSet vamsasSet = new SequenceSet();
435     Sequence vamsasSeq;
436     JalviewModelSequence jms = new JalviewModelSequence();
437
438     vamsasSet.setGapChar(jal.getGapCharacter() + "");
439
440     if (jal.getDataset() != null)
441     {
442       // dataset id is the dataset's hashcode
443       vamsasSet.setDatasetId(getDatasetIdRef(jal.getDataset()));
444     }
445     if (jal.getProperties() != null)
446     {
447       Enumeration en = jal.getProperties().keys();
448       while (en.hasMoreElements())
449       {
450         String key = en.nextElement().toString();
451         SequenceSetProperties ssp = new SequenceSetProperties();
452         ssp.setKey(key);
453         ssp.setValue(jal.getProperties().get(key).toString());
454         vamsasSet.addSequenceSetProperties(ssp);
455       }
456     }
457
458     JSeq jseq;
459
460     // SAVE SEQUENCES
461     String id = "";
462     jalview.datamodel.SequenceI jds;
463     for (int i = 0; i < jal.getHeight(); i++)
464     {
465       jds = jal.getSequenceAt(i);
466       id = seqHash(jds);
467
468       if (seqRefIds.get(id) != null)
469       {
470         // This happens for two reasons: 1. multiple views are being serialised.
471         // 2. the hashCode has collided with another sequence's code. This DOES
472         // HAPPEN! (PF00072.15.stk does this)
473         // JBPNote: Uncomment to debug writing out of files that do not read
474         // back in due to ArrayOutOfBoundExceptions.
475         // System.err.println("vamsasSeq backref: "+id+"");
476         // System.err.println(jds.getName()+"
477         // "+jds.getStart()+"-"+jds.getEnd()+" "+jds.getSequenceAsString());
478         // System.err.println("Hashcode: "+seqHash(jds));
479         // SequenceI rsq = (SequenceI) seqRefIds.get(id + "");
480         // System.err.println(rsq.getName()+"
481         // "+rsq.getStart()+"-"+rsq.getEnd()+" "+rsq.getSequenceAsString());
482         // System.err.println("Hashcode: "+seqHash(rsq));
483       }
484       else
485       {
486         vamsasSeq = createVamsasSequence(id, jds);
487         vamsasSet.addSequence(vamsasSeq);
488         seqRefIds.put(id, jds);
489       }
490
491       jseq = new JSeq();
492       jseq.setStart(jds.getStart());
493       jseq.setEnd(jds.getEnd());
494       jseq.setColour(av.getSequenceColour(jds).getRGB());
495
496       jseq.setId(id); // jseq id should be a string not a number
497
498       if (av.hasHiddenRows)
499       {
500         jseq.setHidden(av.alignment.getHiddenSequences().isHidden(jds));
501
502         if (av.hiddenRepSequences != null
503                 && av.hiddenRepSequences.containsKey(jal.getSequenceAt(i)))
504         {
505           jalview.datamodel.SequenceI[] reps = ((jalview.datamodel.SequenceGroup) av.hiddenRepSequences
506                   .get(jal.getSequenceAt(i))).getSequencesInOrder(jal);
507
508           for (int h = 0; h < reps.length; h++)
509           {
510             if (reps[h] != jal.getSequenceAt(i))
511             {
512               jseq.addHiddenSequences(jal.findIndex(reps[h]));
513             }
514           }
515         }
516       }
517
518       if (jds.getDatasetSequence().getSequenceFeatures() != null)
519       {
520         jalview.datamodel.SequenceFeature[] sf = jds.getDatasetSequence()
521                 .getSequenceFeatures();
522         int index = 0;
523         while (index < sf.length)
524         {
525           Features features = new Features();
526
527           features.setBegin(sf[index].getBegin());
528           features.setEnd(sf[index].getEnd());
529           features.setDescription(sf[index].getDescription());
530           features.setType(sf[index].getType());
531           features.setFeatureGroup(sf[index].getFeatureGroup());
532           features.setScore(sf[index].getScore());
533           if (sf[index].links != null)
534           {
535             for (int l = 0; l < sf[index].links.size(); l++)
536             {
537               OtherData keyValue = new OtherData();
538               keyValue.setKey("LINK_" + l);
539               keyValue.setValue(sf[index].links.elementAt(l).toString());
540               features.addOtherData(keyValue);
541             }
542           }
543           if (sf[index].otherDetails != null)
544           {
545             String key;
546             Enumeration keys = sf[index].otherDetails.keys();
547             while (keys.hasMoreElements())
548             {
549               key = keys.nextElement().toString();
550               OtherData keyValue = new OtherData();
551               keyValue.setKey(key);
552               keyValue.setValue(sf[index].otherDetails.get(key).toString());
553               features.addOtherData(keyValue);
554             }
555           }
556
557           jseq.addFeatures(features);
558           index++;
559         }
560       }
561
562       if (jds.getDatasetSequence().getPDBId() != null)
563       {
564         Enumeration en = jds.getDatasetSequence().getPDBId().elements();
565         while (en.hasMoreElements())
566         {
567           Pdbids pdb = new Pdbids();
568           jalview.datamodel.PDBEntry entry = (jalview.datamodel.PDBEntry) en
569                   .nextElement();
570
571           pdb.setId(entry.getId());
572           pdb.setType(entry.getType());
573
574           AppJmol jmol;
575           // This must have been loaded, is it still visible?
576           JInternalFrame[] frames = Desktop.desktop.getAllFrames();
577           String matchedFile = null;
578           for (int f = frames.length - 1; f > -1; f--)
579           {
580             if (frames[f] instanceof AppJmol)
581             {
582               jmol = (AppJmol) frames[f];
583               if (!jmol.pdbentry.getId().equals(entry.getId())
584                       && !(entry.getId().length() > 4 && entry.getId()
585                               .toLowerCase().startsWith(
586                                       jmol.pdbentry.getId().toLowerCase())))
587                 continue;
588               matchedFile = jmol.pdbentry.getFile(); // record the file so we
589                                                      // can get at it if the ID
590                                                      // match is ambiguous (e.g.
591                                                      // 1QIP==1qipA)
592               StructureState state = new StructureState();
593               state.setVisible(true);
594               state.setXpos(jmol.getX());
595               state.setYpos(jmol.getY());
596               state.setWidth(jmol.getWidth());
597               state.setHeight(jmol.getHeight());
598               state.setViewId(jmol.getViewId());
599               String statestring = jmol.viewer.getStateInfo();
600               if (state != null)
601               {
602                 state.setContent(statestring.replaceAll("\n", ""));
603               }
604               for (int s = 0; s < jmol.sequence.length; s++)
605               {
606                 if (jal.findIndex(jmol.sequence[s]) > -1)
607                 {
608                   pdb.addStructureState(state);
609                 }
610               }
611             }
612           }
613
614           if (matchedFile != null || entry.getFile() != null)
615           {
616             if (entry.getFile() != null)
617             {
618               // use entry's file
619               matchedFile = entry.getFile();
620             }
621             pdb.setFile(matchedFile); // entry.getFile());
622             if (pdbfiles == null)
623             {
624               pdbfiles = new Vector();
625             }
626
627             if (!pdbfiles.contains(entry.getId()))
628             {
629               pdbfiles.addElement(entry.getId());
630               try
631               {
632                 File file = new File(matchedFile);
633                 if (file.exists() && jout != null)
634                 {
635                   byte[] data = new byte[(int) file.length()];
636                   jout.putNextEntry(new JarEntry(entry.getId()));
637                   DataInputStream dis = new DataInputStream(
638                           new FileInputStream(file));
639                   dis.readFully(data);
640
641                   DataOutputStream dout = new DataOutputStream(jout);
642                   dout.write(data, 0, data.length);
643                   dout.flush();
644                   jout.closeEntry();
645                 }
646               } catch (Exception ex)
647               {
648                 ex.printStackTrace();
649               }
650
651             }
652           }
653
654           if (entry.getProperty() != null)
655           {
656             PdbentryItem item = new PdbentryItem();
657             Hashtable properties = entry.getProperty();
658             Enumeration en2 = properties.keys();
659             while (en2.hasMoreElements())
660             {
661               Property prop = new Property();
662               String key = en2.nextElement().toString();
663               prop.setName(key);
664               prop.setValue(properties.get(key).toString());
665               item.addProperty(prop);
666             }
667             pdb.addPdbentryItem(item);
668           }
669
670           jseq.addPdbids(pdb);
671         }
672       }
673
674       jms.addJSeq(jseq);
675     }
676
677     if (av.hasHiddenRows)
678     {
679       jal = av.alignment;
680     }
681     // SAVE MAPPINGS
682     if (jal.getCodonFrames() != null && jal.getCodonFrames().length > 0)
683     {
684       jalview.datamodel.AlignedCodonFrame[] jac = jal.getCodonFrames();
685       for (int i = 0; i < jac.length; i++)
686       {
687         AlcodonFrame alc = new AlcodonFrame();
688         vamsasSet.addAlcodonFrame(alc);
689         for (int p = 0; p < jac[i].aaWidth; p++)
690         {
691           Alcodon cmap = new Alcodon();
692           if (jac[i].codons[p] != null)
693           {
694             // Null codons indicate a gapped column in the translated peptide
695             // alignment.
696             cmap.setPos1(jac[i].codons[p][0]);
697             cmap.setPos2(jac[i].codons[p][1]);
698             cmap.setPos3(jac[i].codons[p][2]);
699           }
700           alc.addAlcodon(cmap);
701         }
702         if (jac[i].getProtMappings() != null
703                 && jac[i].getProtMappings().length > 0)
704         {
705           SequenceI[] dnas = jac[i].getdnaSeqs();
706           jalview.datamodel.Mapping[] pmaps = jac[i].getProtMappings();
707           for (int m = 0; m < pmaps.length; m++)
708           {
709             AlcodMap alcmap = new AlcodMap();
710             alcmap.setDnasq(seqHash(dnas[m]));
711             alcmap.setMapping(createVamsasMapping(pmaps[m], dnas[m], null,
712                     false));
713             alc.addAlcodMap(alcmap);
714           }
715         }
716       }
717     }
718
719     // SAVE TREES
720     // /////////////////////////////////
721     if (av.currentTree != null)
722     {
723       // FIND ANY ASSOCIATED TREES
724       // NOT IMPLEMENTED FOR HEADLESS STATE AT PRESENT
725       if (Desktop.desktop != null)
726       {
727         JInternalFrame[] frames = Desktop.desktop.getAllFrames();
728
729         for (int t = 0; t < frames.length; t++)
730         {
731           if (frames[t] instanceof TreePanel)
732           {
733             TreePanel tp = (TreePanel) frames[t];
734
735             if (tp.treeCanvas.av.alignment == jal)
736             {
737               Tree tree = new Tree();
738               tree.setTitle(tp.getTitle());
739               tree.setCurrentTree((av.currentTree == tp.getTree()));
740               tree.setNewick(tp.getTree().toString());
741               tree.setThreshold(tp.treeCanvas.threshold);
742
743               tree.setFitToWindow(tp.fitToWindow.getState());
744               tree.setFontName(tp.getTreeFont().getName());
745               tree.setFontSize(tp.getTreeFont().getSize());
746               tree.setFontStyle(tp.getTreeFont().getStyle());
747               tree.setMarkUnlinked(tp.placeholdersMenu.getState());
748
749               tree.setShowBootstrap(tp.bootstrapMenu.getState());
750               tree.setShowDistances(tp.distanceMenu.getState());
751
752               tree.setHeight(tp.getHeight());
753               tree.setWidth(tp.getWidth());
754               tree.setXpos(tp.getX());
755               tree.setYpos(tp.getY());
756               tree.setId(makeHashCode(tp, null));
757               jms.addTree(tree);
758             }
759           }
760         }
761       }
762     }
763
764     // SAVE ANNOTATIONS
765     if (jal.getAlignmentAnnotation() != null)
766     {
767       jalview.datamodel.AlignmentAnnotation[] aa = jal
768               .getAlignmentAnnotation();
769
770       for (int i = 0; i < aa.length; i++)
771       {
772         Annotation an = new Annotation();
773
774         if (aa[i].annotationId != null)
775         {
776           annotationIds.put(aa[i].annotationId, aa[i]);
777         }
778
779         an.setId(aa[i].annotationId);
780
781         if (aa[i] == av.quality || aa[i] == av.conservation
782                 || aa[i] == av.consensus)
783         {
784           an.setLabel(aa[i].label);
785           an.setGraph(true);
786           vamsasSet.addAnnotation(an);
787           continue;
788         }
789
790         an.setVisible(aa[i].visible);
791
792         an.setDescription(aa[i].description);
793
794         if (aa[i].sequenceRef != null)
795         {
796           // TODO later annotation sequenceRef should be the XML ID of the
797           // sequence rather than its display name
798           an.setSequenceRef(aa[i].sequenceRef.getName());
799         }
800
801         if (aa[i].graph > 0)
802         {
803           an.setGraph(true);
804           an.setGraphType(aa[i].graph);
805           an.setGraphGroup(aa[i].graphGroup);
806           if (aa[i].getThreshold() != null)
807           {
808             ThresholdLine line = new ThresholdLine();
809             line.setLabel(aa[i].getThreshold().label);
810             line.setValue(aa[i].getThreshold().value);
811             line.setColour(aa[i].getThreshold().colour.getRGB());
812             an.setThresholdLine(line);
813           }
814         }
815         else
816         {
817           an.setGraph(false);
818         }
819
820         an.setLabel(aa[i].label);
821         if (aa[i].hasScore())
822         {
823           an.setScore(aa[i].getScore());
824         }
825         AnnotationElement ae;
826         if (aa[i].annotations != null)
827         {
828           an.setScoreOnly(false);
829           for (int a = 0; a < aa[i].annotations.length; a++)
830           {
831             if ((aa[i] == null) || (aa[i].annotations[a] == null))
832             {
833               continue;
834             }
835
836             ae = new AnnotationElement();
837             if (aa[i].annotations[a].description != null)
838               ae.setDescription(aa[i].annotations[a].description);
839             if (aa[i].annotations[a].displayCharacter != null)
840               ae.setDisplayCharacter(aa[i].annotations[a].displayCharacter);
841
842             if (!Float.isNaN(aa[i].annotations[a].value))
843               ae.setValue(aa[i].annotations[a].value);
844
845             ae.setPosition(a);
846             if (aa[i].annotations[a].secondaryStructure != ' '
847                     && aa[i].annotations[a].secondaryStructure != '\0')
848               ae
849                       .setSecondaryStructure(aa[i].annotations[a].secondaryStructure
850                               + "");
851
852             if (aa[i].annotations[a].colour != null
853                     && aa[i].annotations[a].colour != java.awt.Color.black)
854             {
855               ae.setColour(aa[i].annotations[a].colour.getRGB());
856             }
857
858             an.addAnnotationElement(ae);
859           }
860         }
861         else
862         {
863           an.setScoreOnly(true);
864         }
865         vamsasSet.addAnnotation(an);
866       }
867     }
868
869     // SAVE GROUPS
870     if (jal.getGroups() != null)
871     {
872       JGroup[] groups = new JGroup[jal.getGroups().size()];
873
874       for (int i = 0; i < groups.length; i++)
875       {
876         groups[i] = new JGroup();
877
878         jalview.datamodel.SequenceGroup sg = (jalview.datamodel.SequenceGroup) jal
879                 .getGroups().elementAt(i);
880         groups[i].setStart(sg.getStartRes());
881         groups[i].setEnd(sg.getEndRes());
882         groups[i].setName(sg.getName()); // TODO later sequence group should
883         // specify IDs of sequences, not just
884         // names
885         if (sg.cs != null)
886         {
887           if (sg.cs.conservationApplied())
888           {
889             groups[i].setConsThreshold(sg.cs.getConservationInc());
890
891             if (sg.cs instanceof jalview.schemes.UserColourScheme)
892             {
893               groups[i].setColour(SetUserColourScheme(sg.cs, userColours,
894                       jms));
895             }
896             else
897             {
898               groups[i]
899                       .setColour(ColourSchemeProperty.getColourName(sg.cs));
900             }
901           }
902           else if (sg.cs instanceof jalview.schemes.AnnotationColourGradient)
903           {
904             groups[i]
905                     .setColour(ColourSchemeProperty
906                             .getColourName(((jalview.schemes.AnnotationColourGradient) sg.cs)
907                                     .getBaseColour()));
908           }
909           else if (sg.cs instanceof jalview.schemes.UserColourScheme)
910           {
911             groups[i]
912                     .setColour(SetUserColourScheme(sg.cs, userColours, jms));
913           }
914           else
915           {
916             groups[i].setColour(ColourSchemeProperty.getColourName(sg.cs));
917           }
918
919           groups[i].setPidThreshold(sg.cs.getThreshold());
920         }
921
922         groups[i].setOutlineColour(sg.getOutlineColour().getRGB());
923         groups[i].setDisplayBoxes(sg.getDisplayBoxes());
924         groups[i].setDisplayText(sg.getDisplayText());
925         groups[i].setColourText(sg.getColourText());
926         groups[i].setTextCol1(sg.textColour.getRGB());
927         groups[i].setTextCol2(sg.textColour2.getRGB());
928         groups[i].setTextColThreshold(sg.thresholdTextColour);
929         groups[i].setShowUnconserved(sg.getShowunconserved());
930         for (int s = 0; s < sg.getSize(); s++)
931         {
932           jalview.datamodel.Sequence seq = (jalview.datamodel.Sequence) sg
933                   .getSequenceAt(s);
934           groups[i].addSeq(seqHash(seq));
935         }
936       }
937
938       jms.setJGroup(groups);
939     }
940
941     // /////////SAVE VIEWPORT
942     Viewport view = new Viewport();
943     view.setTitle(ap.alignFrame.getTitle());
944     view.setSequenceSetId(makeHashCode(av.getSequenceSetId(), av
945             .getSequenceSetId()));
946     view.setId(av.getViewId());
947     view.setViewName(av.viewName);
948     view.setGatheredViews(av.gatherViewsHere);
949
950     if (ap.av.explodedPosition != null)
951     {
952       view.setXpos(av.explodedPosition.x);
953       view.setYpos(av.explodedPosition.y);
954       view.setWidth(av.explodedPosition.width);
955       view.setHeight(av.explodedPosition.height);
956     }
957     else
958     {
959       view.setXpos(ap.alignFrame.getBounds().x);
960       view.setYpos(ap.alignFrame.getBounds().y);
961       view.setWidth(ap.alignFrame.getBounds().width);
962       view.setHeight(ap.alignFrame.getBounds().height);
963     }
964
965     view.setStartRes(av.startRes);
966     view.setStartSeq(av.startSeq);
967
968     if (av.getGlobalColourScheme() instanceof jalview.schemes.UserColourScheme)
969     {
970       view.setBgColour(SetUserColourScheme(av.getGlobalColourScheme(),
971               userColours, jms));
972     }
973     else if (av.getGlobalColourScheme() instanceof jalview.schemes.AnnotationColourGradient)
974     {
975       jalview.schemes.AnnotationColourGradient acg = (jalview.schemes.AnnotationColourGradient) av
976               .getGlobalColourScheme();
977
978       AnnotationColours ac = new AnnotationColours();
979       ac.setAboveThreshold(acg.getAboveThreshold());
980       ac.setThreshold(acg.getAnnotationThreshold());
981       ac.setAnnotation(acg.getAnnotation());
982       if (acg.getBaseColour() instanceof jalview.schemes.UserColourScheme)
983       {
984         ac.setColourScheme(SetUserColourScheme(acg.getBaseColour(),
985                 userColours, jms));
986       }
987       else
988       {
989         ac.setColourScheme(ColourSchemeProperty.getColourName(acg
990                 .getBaseColour()));
991       }
992
993       ac.setMaxColour(acg.getMaxColour().getRGB());
994       ac.setMinColour(acg.getMinColour().getRGB());
995       view.setAnnotationColours(ac);
996       view.setBgColour("AnnotationColourGradient");
997     }
998     else
999     {
1000       view.setBgColour(ColourSchemeProperty.getColourName(av
1001               .getGlobalColourScheme()));
1002     }
1003
1004     ColourSchemeI cs = av.getGlobalColourScheme();
1005
1006     if (cs != null)
1007     {
1008       if (cs.conservationApplied())
1009       {
1010         view.setConsThreshold(cs.getConservationInc());
1011         if (cs instanceof jalview.schemes.UserColourScheme)
1012         {
1013           view.setBgColour(SetUserColourScheme(cs, userColours, jms));
1014         }
1015       }
1016
1017       if (cs instanceof ResidueColourScheme)
1018       {
1019         view.setPidThreshold(cs.getThreshold());
1020       }
1021     }
1022
1023     view.setConservationSelected(av.getConservationSelected());
1024     view.setPidSelected(av.getAbovePIDThreshold());
1025     view.setFontName(av.font.getName());
1026     view.setFontSize(av.font.getSize());
1027     view.setFontStyle(av.font.getStyle());
1028     view.setRenderGaps(av.renderGaps);
1029     view.setShowAnnotation(av.getShowAnnotation());
1030     view.setShowBoxes(av.getShowBoxes());
1031     view.setShowColourText(av.getColourText());
1032     view.setShowFullId(av.getShowJVSuffix());
1033     view.setRightAlignIds(av.rightAlignIds);
1034     view.setShowSequenceFeatures(av.showSequenceFeatures);
1035     view.setShowText(av.getShowText());
1036     view.setShowUnconserved(av.getShowUnconserved());
1037     view.setWrapAlignment(av.getWrapAlignment());
1038     view.setTextCol1(av.textColour.getRGB());
1039     view.setTextCol2(av.textColour2.getRGB());
1040     view.setTextColThreshold(av.thresholdTextColour);
1041
1042     if (av.featuresDisplayed != null)
1043     {
1044       jalview.schemabinding.version2.FeatureSettings fs = new jalview.schemabinding.version2.FeatureSettings();
1045
1046       String[] renderOrder = ap.seqPanel.seqCanvas.getFeatureRenderer().renderOrder;
1047
1048       Vector settingsAdded = new Vector();
1049       for (int ro = 0; ro < renderOrder.length; ro++)
1050       {
1051         Setting setting = new Setting();
1052         setting.setType(renderOrder[ro]);
1053         setting.setColour(ap.seqPanel.seqCanvas.getFeatureRenderer()
1054                 .getColour(renderOrder[ro]).getRGB());
1055
1056         setting.setDisplay(av.featuresDisplayed
1057                 .containsKey(renderOrder[ro]));
1058         float rorder = ap.seqPanel.seqCanvas.getFeatureRenderer().getOrder(
1059                 renderOrder[ro]);
1060         if (rorder > -1)
1061         {
1062           setting.setOrder(rorder);
1063         }
1064         fs.addSetting(setting);
1065         settingsAdded.addElement(renderOrder[ro]);
1066       }
1067
1068       // Make sure we save none displayed feature settings
1069       Enumeration en = ap.seqPanel.seqCanvas.getFeatureRenderer().featureColours
1070               .keys();
1071       while (en.hasMoreElements())
1072       {
1073         String key = en.nextElement().toString();
1074         if (settingsAdded.contains(key))
1075         {
1076           continue;
1077         }
1078
1079         Setting setting = new Setting();
1080         setting.setType(key);
1081         setting.setColour(ap.seqPanel.seqCanvas.getFeatureRenderer()
1082                 .getColour(key).getRGB());
1083
1084         setting.setDisplay(false);
1085         float rorder = ap.seqPanel.seqCanvas.getFeatureRenderer().getOrder(
1086                 key);
1087         if (rorder > -1)
1088         {
1089           setting.setOrder(rorder);
1090         }
1091         fs.addSetting(setting);
1092         settingsAdded.addElement(key);
1093       }
1094       en = ap.seqPanel.seqCanvas.getFeatureRenderer().featureGroups.keys();
1095       Vector groupsAdded = new Vector();
1096       while (en.hasMoreElements())
1097       {
1098         String grp = en.nextElement().toString();
1099         if (groupsAdded.contains(grp))
1100         {
1101           continue;
1102         }
1103         Group g = new Group();
1104         g.setName(grp);
1105         g
1106                 .setDisplay(((Boolean) ap.seqPanel.seqCanvas
1107                         .getFeatureRenderer().featureGroups.get(grp))
1108                         .booleanValue());
1109         fs.addGroup(g);
1110         groupsAdded.addElement(grp);
1111       }
1112       jms.setFeatureSettings(fs);
1113
1114     }
1115
1116     if (av.hasHiddenColumns)
1117     {
1118       if (av.getColumnSelection() == null
1119               || av.getColumnSelection().getHiddenColumns() == null)
1120       {
1121         warn("REPORT BUG: avoided null columnselection bug (DMAM reported). Please contact Jim about this.");
1122       }
1123       else
1124       {
1125         for (int c = 0; c < av.getColumnSelection().getHiddenColumns()
1126                 .size(); c++)
1127         {
1128           int[] region = (int[]) av.getColumnSelection().getHiddenColumns()
1129                   .elementAt(c);
1130           HiddenColumns hc = new HiddenColumns();
1131           hc.setStart(region[0]);
1132           hc.setEnd(region[1]);
1133           view.addHiddenColumns(hc);
1134         }
1135       }
1136     }
1137
1138     jms.addViewport(view);
1139
1140     object.setJalviewModelSequence(jms);
1141     object.getVamsasModel().addSequenceSet(vamsasSet);
1142
1143     if (jout != null && fileName != null)
1144     {
1145       // We may not want to write the object to disk,
1146       // eg we can copy the alignViewport to a new view object
1147       // using save and then load
1148       try
1149       {
1150         JarEntry entry = new JarEntry(fileName);
1151         jout.putNextEntry(entry);
1152         PrintWriter pout = new PrintWriter(new OutputStreamWriter(jout,
1153                 "UTF-8"));
1154         org.exolab.castor.xml.Marshaller marshaller = new org.exolab.castor.xml.Marshaller(
1155                 pout);
1156         marshaller.marshal(object);
1157         pout.flush();
1158         jout.closeEntry();
1159       } catch (Exception ex)
1160       {
1161         // TODO: raise error in GUI if marshalling failed.
1162         ex.printStackTrace();
1163       }
1164     }
1165     return object;
1166   }
1167
1168   /**
1169    * External mapping between jalview objects and objects yielding a valid and
1170    * unique object ID string. This is null for normal Jalview project IO, but
1171    * non-null when a jalview project is being read or written as part of a
1172    * vamsas session.
1173    */
1174   IdentityHashMap jv2vobj = null;
1175
1176   /**
1177    * Construct a unique ID for jvobj using either existing bindings or if none
1178    * exist, the result of the hashcode call for the object.
1179    * 
1180    * @param jvobj
1181    *          jalview data object
1182    * @return unique ID for referring to jvobj
1183    */
1184   private String makeHashCode(Object jvobj, String altCode)
1185   {
1186     if (jv2vobj != null)
1187     {
1188       Object id = jv2vobj.get(jvobj);
1189       if (id != null)
1190       {
1191         return id.toString();
1192       }
1193       // check string ID mappings
1194       if (jvids2vobj != null && jvobj instanceof String)
1195       {
1196         id = jvids2vobj.get(jvobj);
1197       }
1198       if (id != null)
1199       {
1200         return id.toString();
1201       }
1202       // give up and warn that something has gone wrong
1203       warn("Cannot find ID for object in external mapping : " + jvobj);
1204     }
1205     return altCode;
1206   }
1207
1208   /**
1209    * return local jalview object mapped to ID, if it exists
1210    * 
1211    * @param idcode
1212    *          (may be null)
1213    * @return null or object bound to idcode
1214    */
1215   private Object retrieveExistingObj(String idcode)
1216   {
1217     if (idcode != null && vobj2jv != null)
1218     {
1219       return vobj2jv.get(idcode);
1220     }
1221     return null;
1222   }
1223
1224   /**
1225    * binding from ID strings from external mapping table to jalview data model
1226    * objects.
1227    */
1228   private Hashtable vobj2jv;
1229
1230   private Sequence createVamsasSequence(String id, SequenceI jds)
1231   {
1232     return createVamsasSequence(true, id, jds, null);
1233   }
1234
1235   private Sequence createVamsasSequence(boolean recurse, String id,
1236           SequenceI jds, SequenceI parentseq)
1237   {
1238     Sequence vamsasSeq = new Sequence();
1239     vamsasSeq.setId(id);
1240     vamsasSeq.setName(jds.getName());
1241     vamsasSeq.setSequence(jds.getSequenceAsString());
1242     vamsasSeq.setDescription(jds.getDescription());
1243     jalview.datamodel.DBRefEntry[] dbrefs = null;
1244     if (jds.getDatasetSequence() != null)
1245     {
1246       vamsasSeq.setDsseqid(seqHash(jds.getDatasetSequence()));
1247       if (jds.getDatasetSequence().getDBRef() != null)
1248       {
1249         dbrefs = jds.getDatasetSequence().getDBRef();
1250       }
1251     }
1252     else
1253     {
1254       vamsasSeq.setDsseqid(id); // so we can tell which sequences really are
1255       // dataset sequences only
1256       dbrefs = jds.getDBRef();
1257     }
1258     if (dbrefs != null)
1259     {
1260       for (int d = 0; d < dbrefs.length; d++)
1261       {
1262         DBRef dbref = new DBRef();
1263         dbref.setSource(dbrefs[d].getSource());
1264         dbref.setVersion(dbrefs[d].getVersion());
1265         dbref.setAccessionId(dbrefs[d].getAccessionId());
1266         if (dbrefs[d].hasMap())
1267         {
1268           Mapping mp = createVamsasMapping(dbrefs[d].getMap(), parentseq,
1269                   jds, recurse);
1270           dbref.setMapping(mp);
1271         }
1272         vamsasSeq.addDBRef(dbref);
1273       }
1274     }
1275     return vamsasSeq;
1276   }
1277
1278   private Mapping createVamsasMapping(jalview.datamodel.Mapping jmp,
1279           SequenceI parentseq, SequenceI jds, boolean recurse)
1280   {
1281     Mapping mp = null;
1282     if (jmp.getMap() != null)
1283     {
1284       mp = new Mapping();
1285
1286       jalview.util.MapList mlst = jmp.getMap();
1287       int r[] = mlst.getFromRanges();
1288       for (int s = 0; s < r.length; s += 2)
1289       {
1290         MapListFrom mfrom = new MapListFrom();
1291         mfrom.setStart(r[s]);
1292         mfrom.setEnd(r[s + 1]);
1293         mp.addMapListFrom(mfrom);
1294       }
1295       r = mlst.getToRanges();
1296       for (int s = 0; s < r.length; s += 2)
1297       {
1298         MapListTo mto = new MapListTo();
1299         mto.setStart(r[s]);
1300         mto.setEnd(r[s + 1]);
1301         mp.addMapListTo(mto);
1302       }
1303       mp.setMapFromUnit(mlst.getFromRatio());
1304       mp.setMapToUnit(mlst.getToRatio());
1305       if (jmp.getTo() != null)
1306       {
1307         MappingChoice mpc = new MappingChoice();
1308         if (recurse
1309                 && (parentseq != jmp.getTo() || parentseq
1310                         .getDatasetSequence() != jmp.getTo()))
1311         {
1312           mpc.setSequence(createVamsasSequence(false, seqHash(jmp.getTo()),
1313                   jmp.getTo(), jds));
1314         }
1315         else
1316         {
1317           String jmpid = "";
1318           SequenceI ps = null;
1319           if (parentseq != jmp.getTo()
1320                   && parentseq.getDatasetSequence() != jmp.getTo())
1321           {
1322             // chaining dbref rather than a handshaking one
1323             jmpid = seqHash(ps = jmp.getTo());
1324           }
1325           else
1326           {
1327             jmpid = seqHash(ps = parentseq);
1328           }
1329           mpc.setDseqFor(jmpid);
1330           if (!seqRefIds.containsKey(mpc.getDseqFor()))
1331           {
1332             jalview.bin.Cache.log.debug("creatign new DseqFor ID");
1333             seqRefIds.put(mpc.getDseqFor(), ps);
1334           }
1335           else
1336           {
1337             jalview.bin.Cache.log.debug("reusing DseqFor ID");
1338           }
1339         }
1340         mp.setMappingChoice(mpc);
1341       }
1342     }
1343     return mp;
1344   }
1345
1346   String SetUserColourScheme(jalview.schemes.ColourSchemeI cs,
1347           Vector userColours, JalviewModelSequence jms)
1348   {
1349     String id = null;
1350     jalview.schemes.UserColourScheme ucs = (jalview.schemes.UserColourScheme) cs;
1351
1352     if (!userColours.contains(ucs))
1353     {
1354       userColours.add(ucs);
1355
1356       java.awt.Color[] colours = ucs.getColours();
1357       jalview.schemabinding.version2.UserColours uc = new jalview.schemabinding.version2.UserColours();
1358       jalview.schemabinding.version2.UserColourScheme jbucs = new jalview.schemabinding.version2.UserColourScheme();
1359
1360       for (int i = 0; i < colours.length; i++)
1361       {
1362         jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
1363         col.setName(ResidueProperties.aa[i]);
1364         col.setRGB(jalview.util.Format.getHexString(colours[i]));
1365         jbucs.addColour(col);
1366       }
1367       if (ucs.getLowerCaseColours() != null)
1368       {
1369         colours = ucs.getLowerCaseColours();
1370         for (int i = 0; i < colours.length; i++)
1371         {
1372           jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
1373           col.setName(ResidueProperties.aa[i].toLowerCase());
1374           col.setRGB(jalview.util.Format.getHexString(colours[i]));
1375           jbucs.addColour(col);
1376         }
1377       }
1378
1379       id = "ucs" + userColours.indexOf(ucs);
1380       uc.setId(id);
1381       uc.setUserColourScheme(jbucs);
1382       jms.addUserColours(uc);
1383     }
1384
1385     return id;
1386   }
1387
1388   jalview.schemes.UserColourScheme GetUserColourScheme(
1389           JalviewModelSequence jms, String id)
1390   {
1391     UserColours[] uc = jms.getUserColours();
1392     UserColours colours = null;
1393
1394     for (int i = 0; i < uc.length; i++)
1395     {
1396       if (uc[i].getId().equals(id))
1397       {
1398         colours = uc[i];
1399
1400         break;
1401       }
1402     }
1403
1404     java.awt.Color[] newColours = new java.awt.Color[24];
1405
1406     for (int i = 0; i < 24; i++)
1407     {
1408       newColours[i] = new java.awt.Color(Integer.parseInt(colours
1409               .getUserColourScheme().getColour(i).getRGB(), 16));
1410     }
1411
1412     jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme(
1413             newColours);
1414
1415     if (colours.getUserColourScheme().getColourCount() > 24)
1416     {
1417       newColours = new java.awt.Color[23];
1418       for (int i = 0; i < 23; i++)
1419       {
1420         newColours[i] = new java.awt.Color(Integer.parseInt(colours
1421                 .getUserColourScheme().getColour(i + 24).getRGB(), 16));
1422       }
1423       ucs.setLowerCaseColours(newColours);
1424     }
1425
1426     return ucs;
1427   }
1428
1429   /**
1430    * contains last error message (if any) encountered by XML loader.
1431    */
1432   String errorMessage = null;
1433
1434   /**
1435    * flag to control whether the Jalview2XML_V1 parser should be deferred to if
1436    * exceptions are raised during project XML parsing
1437    */
1438   public boolean attemptversion1parse = true;
1439
1440   /**
1441    * Load a jalview project archive from a jar file
1442    * 
1443    * @param file
1444    *          - HTTP URL or filename
1445    */
1446   public AlignFrame LoadJalviewAlign(final String file)
1447   {
1448
1449     jalview.gui.AlignFrame af = null;
1450
1451     try
1452     {
1453       // UNMARSHALLER SEEMS TO CLOSE JARINPUTSTREAM, MOST ANNOYING
1454       // Workaround is to make sure caller implements the JarInputStreamProvider
1455       // interface
1456       // so we can re-open the jar input stream for each entry.
1457
1458       jarInputStreamProvider jprovider = createjarInputStreamProvider(file);
1459       af = LoadJalviewAlign(jprovider);
1460     } catch (MalformedURLException e)
1461     {
1462       errorMessage = "Invalid URL format for '" + file + "'";
1463       reportErrors();
1464     }
1465     return af;
1466   }
1467
1468   private jarInputStreamProvider createjarInputStreamProvider(
1469           final String file) throws MalformedURLException
1470   {
1471     URL url = null;
1472     errorMessage = null;
1473     uniqueSetSuffix = null;
1474     seqRefIds = null;
1475     viewportsAdded = null;
1476     frefedSequence = null;
1477
1478     if (file.startsWith("http://"))
1479     {
1480       url = new URL(file);
1481     }
1482     final URL _url = url;
1483     return new jarInputStreamProvider()
1484     {
1485
1486       public JarInputStream getJarInputStream() throws IOException
1487       {
1488         if (_url != null)
1489         {
1490           return new JarInputStream(_url.openStream());
1491         }
1492         else
1493         {
1494           return new JarInputStream(new FileInputStream(file));
1495         }
1496       }
1497
1498       public String getFilename()
1499       {
1500         return file;
1501       }
1502     };
1503   }
1504
1505   /**
1506    * Recover jalview session from a jalview project archive. Caller may
1507    * initialise uniqueSetSuffix, seqRefIds, viewportsAdded and frefedSequence
1508    * themselves. Any null fields will be initialised with default values,
1509    * non-null fields are left alone.
1510    * 
1511    * @param jprovider
1512    * @return
1513    */
1514   public AlignFrame LoadJalviewAlign(final jarInputStreamProvider jprovider)
1515   {
1516     errorMessage = null;
1517     if (uniqueSetSuffix == null)
1518     {
1519       uniqueSetSuffix = System.currentTimeMillis() % 100000 + "";
1520     }
1521     if (seqRefIds == null)
1522     {
1523       seqRefIds = new Hashtable();
1524     }
1525     if (viewportsAdded == null)
1526     {
1527       viewportsAdded = new Hashtable();
1528     }
1529     if (frefedSequence == null)
1530     {
1531       frefedSequence = new Vector();
1532     }
1533
1534     jalview.gui.AlignFrame af = null;
1535     Hashtable gatherToThisFrame = new Hashtable();
1536     final String file = jprovider.getFilename();
1537     try
1538     {
1539       JarInputStream jin = null;
1540       JarEntry jarentry = null;
1541       int entryCount = 1;
1542
1543       do
1544       {
1545         jin = jprovider.getJarInputStream();
1546         for (int i = 0; i < entryCount; i++)
1547         {
1548           jarentry = jin.getNextJarEntry();
1549         }
1550
1551         if (jarentry != null && jarentry.getName().endsWith(".xml"))
1552         {
1553           InputStreamReader in = new InputStreamReader(jin, "UTF-8");
1554           JalviewModel object = new JalviewModel();
1555
1556           Unmarshaller unmar = new Unmarshaller(object);
1557           unmar.setValidation(false);
1558           object = (JalviewModel) unmar.unmarshal(in);
1559           if (true) // !skipViewport(object))
1560           {
1561             af = LoadFromObject(object, file, true, jprovider);
1562             if (af.viewport.gatherViewsHere)
1563             {
1564               gatherToThisFrame.put(af.viewport.getSequenceSetId(), af);
1565             }
1566           }
1567           entryCount++;
1568         }
1569         else if (jarentry != null)
1570         {
1571           // Some other file here.
1572           entryCount++;
1573         }
1574       } while (jarentry != null);
1575       resolveFrefedSequences();
1576     } catch (java.io.FileNotFoundException ex)
1577     {
1578       ex.printStackTrace();
1579       errorMessage = "Couldn't locate Jalview XML file : " + file;
1580       System.err.println("Exception whilst loading jalview XML file : "
1581               + ex + "\n");
1582     } catch (java.net.UnknownHostException ex)
1583     {
1584       ex.printStackTrace();
1585       errorMessage = "Couldn't locate Jalview XML file : " + file;
1586       System.err.println("Exception whilst loading jalview XML file : "
1587               + ex + "\n");
1588     } catch (Exception ex)
1589     {
1590       System.err.println("Parsing as Jalview Version 2 file failed.");
1591       ex.printStackTrace(System.err);
1592       if (attemptversion1parse)
1593       {
1594         // Is Version 1 Jar file?
1595         try
1596         {
1597           af = new Jalview2XML_V1(raiseGUI).LoadJalviewAlign(jprovider);
1598         } catch (Exception ex2)
1599         {
1600           System.err.println("Exception whilst loading as jalviewXMLV1:");
1601           ex2.printStackTrace();
1602           af = null;
1603         }
1604       }
1605       if (Desktop.instance != null)
1606       {
1607         Desktop.instance.stopLoading();
1608       }
1609       if (af != null)
1610       {
1611         System.out.println("Successfully loaded archive file");
1612         return af;
1613       }
1614       ex.printStackTrace();
1615
1616       System.err.println("Exception whilst loading jalview XML file : "
1617               + ex + "\n");
1618     } catch (OutOfMemoryError e)
1619     {
1620       // Don't use the OOM Window here
1621       errorMessage = "Out of memory loading jalview XML file";
1622       System.err.println("Out of memory whilst loading jalview XML file");
1623       e.printStackTrace();
1624     }
1625
1626     if (Desktop.instance != null)
1627     {
1628       Desktop.instance.stopLoading();
1629     }
1630
1631     Enumeration en = gatherToThisFrame.elements();
1632     while (en.hasMoreElements())
1633     {
1634       Desktop.instance.gatherViews((AlignFrame) en.nextElement());
1635     }
1636     if (errorMessage != null)
1637     {
1638       reportErrors();
1639     }
1640     return af;
1641   }
1642
1643   /**
1644    * check errorMessage for a valid error message and raise an error box in the
1645    * GUI or write the current errorMessage to stderr and then clear the error
1646    * state.
1647    */
1648   protected void reportErrors()
1649   {
1650     reportErrors(false);
1651   }
1652
1653   protected void reportErrors(final boolean saving)
1654   {
1655     if (errorMessage != null)
1656     {
1657       final String finalErrorMessage = errorMessage;
1658       if (raiseGUI)
1659       {
1660         javax.swing.SwingUtilities.invokeLater(new Runnable()
1661         {
1662           public void run()
1663           {
1664             JOptionPane.showInternalMessageDialog(Desktop.desktop,
1665                     finalErrorMessage, "Error "
1666                             + (saving ? "saving" : "loading")
1667                             + " Jalview file", JOptionPane.WARNING_MESSAGE);
1668           }
1669         });
1670       }
1671       else
1672       {
1673         System.err.println("Problem loading Jalview file: " + errorMessage);
1674       }
1675     }
1676     errorMessage = null;
1677   }
1678
1679   Hashtable alreadyLoadedPDB;
1680
1681   /**
1682    * when set, local views will be updated from view stored in JalviewXML
1683    * Currently (28th Sep 2008) things will go horribly wrong in vamsas document
1684    * sync if this is set to true.
1685    */
1686   private boolean updateLocalViews = false;
1687
1688   String loadPDBFile(jarInputStreamProvider jprovider, String pdbId)
1689   {
1690     if (alreadyLoadedPDB == null)
1691       alreadyLoadedPDB = new Hashtable();
1692
1693     if (alreadyLoadedPDB.containsKey(pdbId))
1694       return alreadyLoadedPDB.get(pdbId).toString();
1695
1696     try
1697     {
1698       JarInputStream jin = jprovider.getJarInputStream();
1699       /*
1700        * if (jprovider.startsWith("http://")) { jin = new JarInputStream(new
1701        * URL(jprovider).openStream()); } else { jin = new JarInputStream(new
1702        * FileInputStream(jprovider)); }
1703        */
1704
1705       JarEntry entry = null;
1706       do
1707       {
1708         entry = jin.getNextJarEntry();
1709       } while (entry != null && !entry.getName().equals(pdbId));
1710       if (entry != null)
1711       {
1712         BufferedReader in = new BufferedReader(new InputStreamReader(jin));
1713         File outFile = File.createTempFile("jalview_pdb", ".txt");
1714         outFile.deleteOnExit();
1715         PrintWriter out = new PrintWriter(new FileOutputStream(outFile));
1716         String data;
1717
1718         while ((data = in.readLine()) != null)
1719         {
1720           out.println(data);
1721         }
1722         try
1723         {
1724           out.flush();
1725         } catch (Exception foo)
1726         {
1727         }
1728         ;
1729         out.close();
1730
1731         alreadyLoadedPDB.put(pdbId, outFile.getAbsolutePath());
1732         return outFile.getAbsolutePath();
1733       }
1734       else
1735       {
1736         warn("Couldn't find PDB file entry in Jalview Jar for " + pdbId);
1737       }
1738     } catch (Exception ex)
1739     {
1740       ex.printStackTrace();
1741     }
1742
1743     return null;
1744   }
1745
1746   /**
1747    * Load alignment frame from jalview XML DOM object
1748    * 
1749    * @param object
1750    *          DOM
1751    * @param file
1752    *          filename source string
1753    * @param loadTreesAndStructures
1754    *          when false only create Viewport
1755    * @param jprovider
1756    *          data source provider
1757    * @return alignment frame created from view stored in DOM
1758    */
1759   AlignFrame LoadFromObject(JalviewModel object, String file,
1760           boolean loadTreesAndStructures, jarInputStreamProvider jprovider)
1761   {
1762     SequenceSet vamsasSet = object.getVamsasModel().getSequenceSet(0);
1763     Sequence[] vamsasSeq = vamsasSet.getSequence();
1764
1765     JalviewModelSequence jms = object.getJalviewModelSequence();
1766
1767     Viewport view = jms.getViewport(0);
1768     // ////////////////////////////////
1769     // LOAD SEQUENCES
1770
1771     Vector hiddenSeqs = null;
1772     jalview.datamodel.Sequence jseq;
1773
1774     ArrayList tmpseqs = new ArrayList();
1775
1776     boolean multipleView = false;
1777
1778     JSeq[] JSEQ = object.getJalviewModelSequence().getJSeq();
1779     int vi = 0; // counter in vamsasSeq array
1780     for (int i = 0; i < JSEQ.length; i++)
1781     {
1782       String seqId = JSEQ[i].getId();
1783
1784       if (seqRefIds.get(seqId) != null)
1785       {
1786         tmpseqs.add((jalview.datamodel.Sequence) seqRefIds.get(seqId));
1787         multipleView = true;
1788       }
1789       else
1790       {
1791         jseq = new jalview.datamodel.Sequence(vamsasSeq[vi].getName(),
1792                 vamsasSeq[vi].getSequence());
1793         jseq.setDescription(vamsasSeq[vi].getDescription());
1794         jseq.setStart(JSEQ[i].getStart());
1795         jseq.setEnd(JSEQ[i].getEnd());
1796         jseq.setVamsasId(uniqueSetSuffix + seqId);
1797         seqRefIds.put(vamsasSeq[vi].getId(), jseq);
1798         tmpseqs.add(jseq);
1799         vi++;
1800       }
1801
1802       if (JSEQ[i].getHidden())
1803       {
1804         if (hiddenSeqs == null)
1805         {
1806           hiddenSeqs = new Vector();
1807         }
1808
1809         hiddenSeqs.addElement((jalview.datamodel.Sequence) seqRefIds
1810                 .get(seqId));
1811       }
1812
1813     }
1814
1815     // /
1816     // Create the alignment object from the sequence set
1817     // ///////////////////////////////
1818     jalview.datamodel.Sequence[] orderedSeqs = new jalview.datamodel.Sequence[tmpseqs
1819             .size()];
1820
1821     tmpseqs.toArray(orderedSeqs);
1822
1823     jalview.datamodel.Alignment al = new jalview.datamodel.Alignment(
1824             orderedSeqs);
1825
1826     // / Add the alignment properties
1827     for (int i = 0; i < vamsasSet.getSequenceSetPropertiesCount(); i++)
1828     {
1829       SequenceSetProperties ssp = vamsasSet.getSequenceSetProperties(i);
1830       al.setProperty(ssp.getKey(), ssp.getValue());
1831     }
1832
1833     // /
1834     // SequenceFeatures are added to the DatasetSequence,
1835     // so we must create or recover the dataset before loading features
1836     // ///////////////////////////////
1837     if (vamsasSet.getDatasetId() == null || vamsasSet.getDatasetId() == "")
1838     {
1839       // older jalview projects do not have a dataset id.
1840       al.setDataset(null);
1841     }
1842     else
1843     {
1844       recoverDatasetFor(vamsasSet, al);
1845     }
1846     // ///////////////////////////////
1847
1848     Hashtable pdbloaded = new Hashtable();
1849     if (!multipleView)
1850     {
1851       // load sequence features, database references and any associated PDB
1852       // structures for the alignment
1853       for (int i = 0; i < vamsasSeq.length; i++)
1854       {
1855         if (JSEQ[i].getFeaturesCount() > 0)
1856         {
1857           Features[] features = JSEQ[i].getFeatures();
1858           for (int f = 0; f < features.length; f++)
1859           {
1860             jalview.datamodel.SequenceFeature sf = new jalview.datamodel.SequenceFeature(
1861                     features[f].getType(), features[f].getDescription(),
1862                     features[f].getStatus(), features[f].getBegin(),
1863                     features[f].getEnd(), features[f].getFeatureGroup());
1864
1865             sf.setScore(features[f].getScore());
1866             for (int od = 0; od < features[f].getOtherDataCount(); od++)
1867             {
1868               OtherData keyValue = features[f].getOtherData(od);
1869               if (keyValue.getKey().startsWith("LINK"))
1870               {
1871                 sf.addLink(keyValue.getValue());
1872               }
1873               else
1874               {
1875                 sf.setValue(keyValue.getKey(), keyValue.getValue());
1876               }
1877
1878             }
1879
1880             al.getSequenceAt(i).getDatasetSequence().addSequenceFeature(sf);
1881           }
1882         }
1883         if (vamsasSeq[i].getDBRefCount() > 0)
1884         {
1885           addDBRefs(al.getSequenceAt(i).getDatasetSequence(), vamsasSeq[i]);
1886         }
1887         if (JSEQ[i].getPdbidsCount() > 0)
1888         {
1889           Pdbids[] ids = JSEQ[i].getPdbids();
1890           for (int p = 0; p < ids.length; p++)
1891           {
1892             jalview.datamodel.PDBEntry entry = new jalview.datamodel.PDBEntry();
1893             entry.setId(ids[p].getId());
1894             entry.setType(ids[p].getType());
1895             if (ids[p].getFile() != null)
1896             {
1897               if (!pdbloaded.containsKey(ids[p].getFile()))
1898               {
1899                 entry.setFile(loadPDBFile(jprovider, ids[p].getId()));
1900               }
1901               else
1902               {
1903                 entry.setFile(pdbloaded.get(ids[p].getId()).toString());
1904               }
1905             }
1906
1907             al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
1908           }
1909         }
1910       }
1911     } // end !multipleview
1912
1913     // ///////////////////////////////
1914     // LOAD SEQUENCE MAPPINGS
1915
1916     if (vamsasSet.getAlcodonFrameCount() > 0)
1917     {
1918       // TODO Potentially this should only be done once for all views of an
1919       // alignment
1920       AlcodonFrame[] alc = vamsasSet.getAlcodonFrame();
1921       for (int i = 0; i < alc.length; i++)
1922       {
1923         jalview.datamodel.AlignedCodonFrame cf = new jalview.datamodel.AlignedCodonFrame(
1924                 alc[i].getAlcodonCount());
1925         if (alc[i].getAlcodonCount() > 0)
1926         {
1927           Alcodon[] alcods = alc[i].getAlcodon();
1928           for (int p = 0; p < cf.codons.length; p++)
1929           {
1930             if (alcods[p].hasPos1() && alcods[p].hasPos2()
1931                     && alcods[p].hasPos3())
1932             {
1933               // translated codons require three valid positions
1934               cf.codons[p] = new int[3];
1935               cf.codons[p][0] = (int) alcods[p].getPos1();
1936               cf.codons[p][1] = (int) alcods[p].getPos2();
1937               cf.codons[p][2] = (int) alcods[p].getPos3();
1938             }
1939             else
1940             {
1941               cf.codons[p] = null;
1942             }
1943           }
1944         }
1945         if (alc[i].getAlcodMapCount() > 0)
1946         {
1947           AlcodMap[] maps = alc[i].getAlcodMap();
1948           for (int m = 0; m < maps.length; m++)
1949           {
1950             SequenceI dnaseq = (SequenceI) seqRefIds
1951                     .get(maps[m].getDnasq());
1952             // Load Mapping
1953             jalview.datamodel.Mapping mapping = null;
1954             // attach to dna sequence reference.
1955             if (maps[m].getMapping() != null)
1956             {
1957               mapping = addMapping(maps[m].getMapping());
1958             }
1959             if (dnaseq != null)
1960             {
1961               cf.addMap(dnaseq, mapping.getTo(), mapping.getMap());
1962             }
1963             else
1964             {
1965               // defer to later
1966               frefedSequence.add(new Object[]
1967               { maps[m].getDnasq(), cf, mapping });
1968             }
1969           }
1970         }
1971         al.addCodonFrame(cf);
1972       }
1973
1974     }
1975
1976     // ////////////////////////////////
1977     // LOAD ANNOTATIONS
1978     boolean hideQuality = true, hideConservation = true, hideConsensus = true;
1979
1980     if (vamsasSet.getAnnotationCount() > 0)
1981     {
1982       Annotation[] an = vamsasSet.getAnnotation();
1983
1984       for (int i = 0; i < an.length; i++)
1985       {
1986         // set visibility for automatic annotation for this view
1987         if (an[i].getLabel().equals("Quality"))
1988         {
1989           hideQuality = false;
1990           continue;
1991         }
1992         else if (an[i].getLabel().equals("Conservation"))
1993         {
1994           hideConservation = false;
1995           continue;
1996         }
1997         else if (an[i].getLabel().equals("Consensus"))
1998         {
1999           hideConsensus = false;
2000           continue;
2001         }
2002         // set visiblity for other annotation in this view
2003         if (an[i].getId() != null
2004                 && annotationIds.containsKey(an[i].getId()))
2005         {
2006           jalview.datamodel.AlignmentAnnotation jda = (jalview.datamodel.AlignmentAnnotation) annotationIds
2007                   .get(an[i].getId());
2008           // in principle Visible should always be true for annotation displayed
2009           // in multiple views
2010           if (an[i].hasVisible())
2011             jda.visible = an[i].getVisible();
2012
2013           al.addAnnotation(jda);
2014
2015           continue;
2016         }
2017         // Construct new annotation from model.
2018         AnnotationElement[] ae = an[i].getAnnotationElement();
2019         jalview.datamodel.Annotation[] anot = null;
2020
2021         if (!an[i].getScoreOnly())
2022         {
2023           anot = new jalview.datamodel.Annotation[al.getWidth()];
2024
2025           for (int aa = 0; aa < ae.length && aa < anot.length; aa++)
2026           {
2027             if (ae[aa].getPosition() >= anot.length)
2028               continue;
2029
2030             anot[ae[aa].getPosition()] = new jalview.datamodel.Annotation(
2031
2032             ae[aa].getDisplayCharacter(), ae[aa].getDescription(), (ae[aa]
2033                     .getSecondaryStructure() == null || ae[aa]
2034                     .getSecondaryStructure().length() == 0) ? ' ' : ae[aa]
2035                     .getSecondaryStructure().charAt(0), ae[aa].getValue()
2036
2037             );
2038             // JBPNote: Consider verifying dataflow for IO of secondary
2039             // structure annotation read from Stockholm files
2040             // this was added to try to ensure that
2041             // if (anot[ae[aa].getPosition()].secondaryStructure>' ')
2042             // {
2043             // anot[ae[aa].getPosition()].displayCharacter = "";
2044             // }
2045             anot[ae[aa].getPosition()].colour = new java.awt.Color(ae[aa]
2046                     .getColour());
2047           }
2048         }
2049         jalview.datamodel.AlignmentAnnotation jaa = null;
2050
2051         if (an[i].getGraph())
2052         {
2053           jaa = new jalview.datamodel.AlignmentAnnotation(an[i].getLabel(),
2054                   an[i].getDescription(), anot, 0, 0, an[i].getGraphType());
2055
2056           jaa.graphGroup = an[i].getGraphGroup();
2057
2058           if (an[i].getThresholdLine() != null)
2059           {
2060             jaa.setThreshold(new jalview.datamodel.GraphLine(an[i]
2061                     .getThresholdLine().getValue(), an[i]
2062                     .getThresholdLine().getLabel(), new java.awt.Color(
2063                     an[i].getThresholdLine().getColour())));
2064
2065           }
2066
2067         }
2068         else
2069         {
2070           jaa = new jalview.datamodel.AlignmentAnnotation(an[i].getLabel(),
2071                   an[i].getDescription(), anot);
2072         }
2073         // register new annotation
2074         if (an[i].getId() != null)
2075         {
2076           annotationIds.put(an[i].getId(), jaa);
2077           jaa.annotationId = an[i].getId();
2078         }
2079         // recover sequence association
2080         if (an[i].getSequenceRef() != null)
2081         {
2082           if (al.findName(an[i].getSequenceRef()) != null)
2083           {
2084             jaa.createSequenceMapping(al.findName(an[i].getSequenceRef()),
2085                     1, true);
2086             al.findName(an[i].getSequenceRef()).addAlignmentAnnotation(jaa);
2087           }
2088         }
2089         if (an[i].hasScore())
2090         {
2091           jaa.setScore(an[i].getScore());
2092         }
2093
2094         if (an[i].hasVisible())
2095           jaa.visible = an[i].getVisible();
2096
2097         al.addAnnotation(jaa);
2098       }
2099     }
2100
2101     // ///////////////////////
2102     // LOAD GROUPS
2103     // Create alignment markup and styles for this view
2104     if (jms.getJGroupCount() > 0)
2105     {
2106       JGroup[] groups = jms.getJGroup();
2107
2108       for (int i = 0; i < groups.length; i++)
2109       {
2110         ColourSchemeI cs = null;
2111
2112         if (groups[i].getColour() != null)
2113         {
2114           if (groups[i].getColour().startsWith("ucs"))
2115           {
2116             cs = GetUserColourScheme(jms, groups[i].getColour());
2117           }
2118           else
2119           {
2120             cs = ColourSchemeProperty.getColour(al, groups[i].getColour());
2121           }
2122
2123           if (cs != null)
2124           {
2125             cs.setThreshold(groups[i].getPidThreshold(), true);
2126           }
2127         }
2128
2129         Vector seqs = new Vector();
2130
2131         for (int s = 0; s < groups[i].getSeqCount(); s++)
2132         {
2133           String seqId = groups[i].getSeq(s) + "";
2134           jalview.datamodel.SequenceI ts = (jalview.datamodel.SequenceI) seqRefIds
2135                   .get(seqId);
2136
2137           if (ts != null)
2138           {
2139             seqs.addElement(ts);
2140           }
2141         }
2142
2143         if (seqs.size() < 1)
2144         {
2145           continue;
2146         }
2147
2148         jalview.datamodel.SequenceGroup sg = new jalview.datamodel.SequenceGroup(
2149                 seqs, groups[i].getName(), cs, groups[i].getDisplayBoxes(),
2150                 groups[i].getDisplayText(), groups[i].getColourText(),
2151                 groups[i].getStart(), groups[i].getEnd());
2152
2153         sg
2154                 .setOutlineColour(new java.awt.Color(groups[i]
2155                         .getOutlineColour()));
2156
2157         sg.textColour = new java.awt.Color(groups[i].getTextCol1());
2158         sg.textColour2 = new java.awt.Color(groups[i].getTextCol2());
2159         sg.setShowunconserved(groups[i].hasShowUnconserved() ? groups[i]
2160                 .isShowUnconserved() : false);
2161         sg.thresholdTextColour = groups[i].getTextColThreshold();
2162
2163         if (groups[i].getConsThreshold() != 0)
2164         {
2165           jalview.analysis.Conservation c = new jalview.analysis.Conservation(
2166                   "All", ResidueProperties.propHash, 3, sg
2167                           .getSequences(null), 0, sg.getWidth() - 1);
2168           c.calculate();
2169           c.verdict(false, 25);
2170           sg.cs.setConservation(c);
2171         }
2172
2173         al.addGroup(sg);
2174       }
2175     }
2176
2177     // ///////////////////////////////
2178     // LOAD VIEWPORT
2179
2180     // If we just load in the same jar file again, the sequenceSetId
2181     // will be the same, and we end up with multiple references
2182     // to the same sequenceSet. We must modify this id on load
2183     // so that each load of the file gives a unique id
2184     String uniqueSeqSetId = view.getSequenceSetId() + uniqueSetSuffix;
2185     String viewId = (view.getId() == null ? null : view.getId()
2186             + uniqueSetSuffix);
2187     AlignFrame af = null;
2188     AlignViewport av = null;
2189     // now check to see if we really need to create a new viewport.
2190     if (multipleView && viewportsAdded.size() == 0)
2191     {
2192       // We recovered an alignment for which a viewport already exists.
2193       // TODO: fix up any settings necessary for overlaying stored state onto
2194       // state recovered from another document. (may not be necessary).
2195       // we may need a binding from a viewport in memory to one recovered from
2196       // XML.
2197       // and then recover its containing af to allow the settings to be applied.
2198       // TODO: fix for vamsas demo
2199       System.err
2200               .println("About to recover a viewport for existing alignment: Sequence set ID is "
2201                       + uniqueSeqSetId);
2202       Object seqsetobj = retrieveExistingObj(uniqueSeqSetId);
2203       if (seqsetobj != null)
2204       {
2205         if (seqsetobj instanceof String)
2206         {
2207           uniqueSeqSetId = (String) seqsetobj;
2208           System.err
2209                   .println("Recovered extant sequence set ID mapping for ID : New Sequence set ID is "
2210                           + uniqueSeqSetId);
2211         }
2212         else
2213         {
2214           System.err
2215                   .println("Warning : Collision between sequence set ID string and existing jalview object mapping.");
2216         }
2217
2218       }
2219     }
2220     AlignmentPanel ap = null;
2221     boolean isnewview = true;
2222     if (viewId != null)
2223     {
2224       // Check to see if this alignment already has a view id == viewId
2225       jalview.gui.AlignmentPanel views[] = Desktop
2226               .getAlignmentPanels(uniqueSeqSetId);
2227       if (views != null && views.length > 0)
2228       {
2229         for (int v = 0; v < views.length; v++)
2230         {
2231           if (views[v].av.getViewId().equalsIgnoreCase(viewId))
2232           {
2233             // recover the existing alignpanel, alignframe, viewport
2234             af = views[v].alignFrame;
2235             av = views[v].av;
2236             ap = views[v];
2237             // TODO: could even skip resetting view settings if we don't want to
2238             // change the local settings from other jalview processes
2239             isnewview = false;
2240           }
2241         }
2242       }
2243     }
2244
2245     if (isnewview)
2246     {
2247       af = loadViewport(file, JSEQ, hiddenSeqs, al, hideConsensus,
2248               hideQuality, hideConservation, jms, view, uniqueSeqSetId,
2249               viewId);
2250       av = af.viewport;
2251       ap = af.alignPanel;
2252     }
2253     // LOAD TREES
2254     // /////////////////////////////////////
2255     if (loadTreesAndStructures && jms.getTreeCount() > 0)
2256     {
2257       try
2258       {
2259         for (int t = 0; t < jms.getTreeCount(); t++)
2260         {
2261
2262           Tree tree = jms.getTree(t);
2263
2264           TreePanel tp = (TreePanel) retrieveExistingObj(tree.getId());
2265           if (tp == null)
2266           {
2267             tp = af.ShowNewickTree(new jalview.io.NewickFile(tree
2268                     .getNewick()), tree.getTitle(), tree.getWidth(), tree
2269                     .getHeight(), tree.getXpos(), tree.getYpos());
2270             if (tree.getId() != null)
2271             {
2272               // perhaps bind the tree id to something ?
2273             }
2274           }
2275           else
2276           {
2277             // update local tree attributes ?
2278             // TODO: should check if tp has been manipulated by user - if so its
2279             // settings shouldn't be modified
2280             tp.setTitle(tree.getTitle());
2281             tp.setBounds(new Rectangle(tree.getXpos(), tree.getYpos(), tree
2282                     .getWidth(), tree.getHeight()));
2283             tp.av = av; // af.viewport; // TODO: verify 'associate with all
2284             // views'
2285             // works still
2286             tp.treeCanvas.av = av; // af.viewport;
2287             tp.treeCanvas.ap = ap; // af.alignPanel;
2288
2289           }
2290           if (tp == null)
2291           {
2292             warn("There was a problem recovering stored Newick tree: \n"
2293                     + tree.getNewick());
2294             continue;
2295           }
2296
2297           tp.fitToWindow.setState(tree.getFitToWindow());
2298           tp.fitToWindow_actionPerformed(null);
2299
2300           if (tree.getFontName() != null)
2301           {
2302             tp.setTreeFont(new java.awt.Font(tree.getFontName(), tree
2303                     .getFontStyle(), tree.getFontSize()));
2304           }
2305           else
2306           {
2307             tp.setTreeFont(new java.awt.Font(view.getFontName(), view
2308                     .getFontStyle(), tree.getFontSize()));
2309           }
2310
2311           tp.showPlaceholders(tree.getMarkUnlinked());
2312           tp.showBootstrap(tree.getShowBootstrap());
2313           tp.showDistances(tree.getShowDistances());
2314
2315           tp.treeCanvas.threshold = tree.getThreshold();
2316
2317           if (tree.getCurrentTree())
2318           {
2319             af.viewport.setCurrentTree(tp.getTree());
2320           }
2321         }
2322
2323       } catch (Exception ex)
2324       {
2325         ex.printStackTrace();
2326       }
2327     }
2328
2329     // //LOAD STRUCTURES
2330     if (loadTreesAndStructures)
2331     {
2332       for (int i = 0; i < JSEQ.length; i++)
2333       {
2334         if (JSEQ[i].getPdbidsCount() > 0)
2335         {
2336           Pdbids[] ids = JSEQ[i].getPdbids();
2337           for (int p = 0; p < ids.length; p++)
2338           {
2339             for (int s = 0; s < ids[p].getStructureStateCount(); s++)
2340             {
2341               // check to see if we haven't already created this structure view
2342               String sviewid = (ids[p].getStructureState(s).getViewId() == null) ? null
2343                       : ids[p].getStructureState(s).getViewId()
2344                               + uniqueSetSuffix;
2345               jalview.datamodel.PDBEntry jpdb = new jalview.datamodel.PDBEntry();
2346               // Originally : ids[p].getFile()
2347               // : TODO: verify external PDB file recovery still works in normal
2348               // jalview project load
2349               jpdb.setFile(loadPDBFile(jprovider, ids[p].getId()));
2350               jpdb.setId(ids[p].getId());
2351
2352               int x = ids[p].getStructureState(s).getXpos();
2353               int y = ids[p].getStructureState(s).getYpos();
2354               int width = ids[p].getStructureState(s).getWidth();
2355               int height = ids[p].getStructureState(s).getHeight();
2356               AppJmol comp = null;
2357               JInternalFrame[] frames = null;
2358               do
2359               {
2360                 try
2361                 {
2362                   frames = Desktop.desktop.getAllFrames();
2363                 } catch (ArrayIndexOutOfBoundsException e)
2364                 {
2365                   // occasional No such child exceptions are thrown here...
2366                   frames = null;
2367                   try
2368                   {
2369                     Thread.sleep(10);
2370                   } catch (Exception f)
2371                   {
2372                   }
2373                   ;
2374                 }
2375               } while (frames == null);
2376               // search for any Jmol windows already open from other
2377               // alignment views that exactly match the stored structure state
2378               for (int f = 0; comp == null && f < frames.length; f++)
2379               {
2380                 if (frames[f] instanceof AppJmol)
2381                 {
2382                   if (sviewid != null
2383                           && ((AppJmol) frames[f]).getViewId().equals(
2384                                   sviewid))
2385                   {
2386                     // post jalview 2.4 schema includes structure view id
2387                     comp = (AppJmol) frames[f];
2388                   }
2389                   else if (frames[f].getX() == x && frames[f].getY() == y
2390                           && frames[f].getHeight() == height
2391                           && frames[f].getWidth() == width)
2392                   {
2393                     comp = (AppJmol) frames[f];
2394                   }
2395                 }
2396               }
2397               // Probably don't need to do this anymore...
2398               // Desktop.desktop.getComponentAt(x, y);
2399               // TODO: NOW: check that this recovers the PDB file correctly.
2400               String pdbFile = loadPDBFile(jprovider, ids[p].getId());
2401
2402               jalview.datamodel.SequenceI[] seq = new jalview.datamodel.SequenceI[]
2403               { (jalview.datamodel.SequenceI) seqRefIds.get(JSEQ[i].getId()
2404                       + "") };
2405
2406               if (comp == null)
2407               {
2408                 // create a new Jmol window
2409                 String state = ids[p].getStructureState(s).getContent();
2410                 StringBuffer newFileLoc = null;
2411                 if (state.indexOf("load") > -1)
2412                 {
2413                   newFileLoc = new StringBuffer(state.substring(0, state
2414                           .indexOf("\"", state.indexOf("load")) + 1));
2415
2416                   newFileLoc.append(jpdb.getFile());
2417                   newFileLoc.append(state.substring(state.indexOf("\"",
2418                           state.indexOf("load \"") + 6)));
2419                 }
2420                 else
2421                 {
2422                   System.err
2423                           .println("Ignoring incomplete Jmol state for PDB "
2424                                   + ids[p].getId());
2425
2426                   newFileLoc = new StringBuffer(state);
2427                   newFileLoc.append("; load \"");
2428                   newFileLoc.append(jpdb.getFile());
2429                   newFileLoc.append("\";");
2430                 }
2431
2432                 if (newFileLoc != null)
2433                 {
2434                   new AppJmol(pdbFile, ids[p].getId(), seq, af.alignPanel,
2435                           newFileLoc.toString(), new java.awt.Rectangle(x,
2436                                   y, width, height), sviewid);
2437                 }
2438
2439               }
2440               else
2441               // if (comp != null)
2442               {
2443                 // NOTE: if the jalview project is part of a shared session then
2444                 // view synchronization should/could be done here.
2445
2446                 // add mapping for this sequence to the already open Jmol
2447                 // instance (if it doesn't already exist)
2448                 // These
2449                 StructureSelectionManager.getStructureSelectionManager()
2450                         .setMapping(seq, null, pdbFile,
2451                                 jalview.io.AppletFormatAdapter.FILE);
2452
2453                 ((AppJmol) comp).addSequence(seq);
2454               }
2455             }
2456           }
2457         }
2458       }
2459     }
2460
2461     return af;
2462   }
2463
2464   AlignFrame loadViewport(String file, JSeq[] JSEQ, Vector hiddenSeqs,
2465           Alignment al, boolean hideConsensus, boolean hideQuality,
2466           boolean hideConservation, JalviewModelSequence jms,
2467           Viewport view, String uniqueSeqSetId, String viewId)
2468   {
2469     AlignFrame af = null;
2470     af = new AlignFrame(al, view.getWidth(), view.getHeight(),
2471             uniqueSeqSetId, viewId);
2472
2473     af.setFileName(file, "Jalview");
2474
2475     for (int i = 0; i < JSEQ.length; i++)
2476     {
2477       af.viewport.setSequenceColour(af.viewport.alignment.getSequenceAt(i),
2478               new java.awt.Color(JSEQ[i].getColour()));
2479     }
2480
2481     af.viewport.gatherViewsHere = view.getGatheredViews();
2482
2483     if (view.getSequenceSetId() != null)
2484     {
2485       jalview.gui.AlignViewport av = (jalview.gui.AlignViewport) viewportsAdded
2486               .get(uniqueSeqSetId);
2487
2488       af.viewport.sequenceSetID = uniqueSeqSetId;
2489       if (av != null)
2490       {
2491         // propagate shared settings to this new view
2492         af.viewport.historyList = av.historyList;
2493         af.viewport.redoList = av.redoList;
2494       }
2495       else
2496       {
2497         viewportsAdded.put(uniqueSeqSetId, af.viewport);
2498       }
2499       // TODO: check if this method can be called repeatedly without
2500       // side-effects if alignpanel already registered.
2501       PaintRefresher.Register(af.alignPanel, uniqueSeqSetId);
2502     }
2503     // apply Hidden regions to view.
2504     if (hiddenSeqs != null)
2505     {
2506       for (int s = 0; s < JSEQ.length; s++)
2507       {
2508         jalview.datamodel.SequenceGroup hidden = new jalview.datamodel.SequenceGroup();
2509
2510         for (int r = 0; r < JSEQ[s].getHiddenSequencesCount(); r++)
2511         {
2512           hidden.addSequence(al
2513                   .getSequenceAt(JSEQ[s].getHiddenSequences(r)), false);
2514         }
2515         af.viewport.hideRepSequences(al.getSequenceAt(s), hidden);
2516       }
2517
2518       jalview.datamodel.SequenceI[] hseqs = new jalview.datamodel.SequenceI[hiddenSeqs
2519               .size()];
2520
2521       for (int s = 0; s < hiddenSeqs.size(); s++)
2522       {
2523         hseqs[s] = (jalview.datamodel.SequenceI) hiddenSeqs.elementAt(s);
2524       }
2525
2526       af.viewport.hideSequence(hseqs);
2527
2528     }
2529     // set visibility of annotation in view
2530     if ((hideConsensus || hideQuality || hideConservation)
2531             && al.getAlignmentAnnotation() != null)
2532     {
2533       int hSize = al.getAlignmentAnnotation().length;
2534       for (int h = 0; h < hSize; h++)
2535       {
2536         if ((hideConsensus && al.getAlignmentAnnotation()[h].label
2537                 .equals("Consensus"))
2538                 || (hideQuality && al.getAlignmentAnnotation()[h].label
2539                         .equals("Quality"))
2540                 || (hideConservation && al.getAlignmentAnnotation()[h].label
2541                         .equals("Conservation")))
2542         {
2543           al.deleteAnnotation(al.getAlignmentAnnotation()[h]);
2544           hSize--;
2545           h--;
2546         }
2547       }
2548       af.alignPanel.adjustAnnotationHeight();
2549     }
2550     // recover view properties and display parameters
2551     if (view.getViewName() != null)
2552     {
2553       af.viewport.viewName = view.getViewName();
2554       af.setInitialTabVisible();
2555     }
2556     af.setBounds(view.getXpos(), view.getYpos(), view.getWidth(), view
2557             .getHeight());
2558
2559     af.viewport.setShowAnnotation(view.getShowAnnotation());
2560     af.viewport.setAbovePIDThreshold(view.getPidSelected());
2561
2562     af.viewport.setColourText(view.getShowColourText());
2563
2564     af.viewport.setConservationSelected(view.getConservationSelected());
2565     af.viewport.setShowJVSuffix(view.getShowFullId());
2566     af.viewport.rightAlignIds = view.getRightAlignIds();
2567     af.viewport.setFont(new java.awt.Font(view.getFontName(), view
2568             .getFontStyle(), view.getFontSize()));
2569     af.alignPanel.fontChanged();
2570     af.viewport.setRenderGaps(view.getRenderGaps());
2571     af.viewport.setWrapAlignment(view.getWrapAlignment());
2572     af.alignPanel.setWrapAlignment(view.getWrapAlignment());
2573     af.viewport.setShowAnnotation(view.getShowAnnotation());
2574     af.alignPanel.setAnnotationVisible(view.getShowAnnotation());
2575
2576     af.viewport.setShowBoxes(view.getShowBoxes());
2577
2578     af.viewport.setShowText(view.getShowText());
2579
2580     af.viewport.textColour = new java.awt.Color(view.getTextCol1());
2581     af.viewport.textColour2 = new java.awt.Color(view.getTextCol2());
2582     af.viewport.thresholdTextColour = view.getTextColThreshold();
2583     af.viewport.setShowUnconserved(view.hasShowUnconserved() ? view
2584             .isShowUnconserved() : false);
2585     af.viewport.setStartRes(view.getStartRes());
2586     af.viewport.setStartSeq(view.getStartSeq());
2587
2588     ColourSchemeI cs = null;
2589     // apply colourschemes
2590     if (view.getBgColour() != null)
2591     {
2592       if (view.getBgColour().startsWith("ucs"))
2593       {
2594         cs = GetUserColourScheme(jms, view.getBgColour());
2595       }
2596       else if (view.getBgColour().startsWith("Annotation"))
2597       {
2598         // int find annotation
2599         for (int i = 0; i < af.viewport.alignment.getAlignmentAnnotation().length; i++)
2600         {
2601           if (af.viewport.alignment.getAlignmentAnnotation()[i].label
2602                   .equals(view.getAnnotationColours().getAnnotation()))
2603           {
2604             if (af.viewport.alignment.getAlignmentAnnotation()[i]
2605                     .getThreshold() == null)
2606             {
2607               af.viewport.alignment.getAlignmentAnnotation()[i]
2608                       .setThreshold(new jalview.datamodel.GraphLine(view
2609                               .getAnnotationColours().getThreshold(),
2610                               "Threshold", java.awt.Color.black)
2611
2612                       );
2613             }
2614
2615             if (view.getAnnotationColours().getColourScheme()
2616                     .equals("None"))
2617             {
2618               cs = new AnnotationColourGradient(af.viewport.alignment
2619                       .getAlignmentAnnotation()[i], new java.awt.Color(view
2620                       .getAnnotationColours().getMinColour()),
2621                       new java.awt.Color(view.getAnnotationColours()
2622                               .getMaxColour()), view.getAnnotationColours()
2623                               .getAboveThreshold());
2624             }
2625             else if (view.getAnnotationColours().getColourScheme()
2626                     .startsWith("ucs"))
2627             {
2628               cs = new AnnotationColourGradient(af.viewport.alignment
2629                       .getAlignmentAnnotation()[i], GetUserColourScheme(
2630                       jms, view.getAnnotationColours().getColourScheme()),
2631                       view.getAnnotationColours().getAboveThreshold());
2632             }
2633             else
2634             {
2635               cs = new AnnotationColourGradient(af.viewport.alignment
2636                       .getAlignmentAnnotation()[i], ColourSchemeProperty
2637                       .getColour(al, view.getAnnotationColours()
2638                               .getColourScheme()), view
2639                       .getAnnotationColours().getAboveThreshold());
2640             }
2641
2642             // Also use these settings for all the groups
2643             if (al.getGroups() != null)
2644             {
2645               for (int g = 0; g < al.getGroups().size(); g++)
2646               {
2647                 jalview.datamodel.SequenceGroup sg = (jalview.datamodel.SequenceGroup) al
2648                         .getGroups().elementAt(g);
2649
2650                 if (sg.cs == null)
2651                 {
2652                   continue;
2653                 }
2654
2655                 /*
2656                  * if
2657                  * (view.getAnnotationColours().getColourScheme().equals("None"
2658                  * )) { sg.cs = new AnnotationColourGradient(
2659                  * af.viewport.alignment.getAlignmentAnnotation()[i], new
2660                  * java.awt.Color(view.getAnnotationColours(). getMinColour()),
2661                  * new java.awt.Color(view.getAnnotationColours().
2662                  * getMaxColour()),
2663                  * view.getAnnotationColours().getAboveThreshold()); } else
2664                  */
2665                 {
2666                   sg.cs = new AnnotationColourGradient(
2667                           af.viewport.alignment.getAlignmentAnnotation()[i],
2668                           sg.cs, view.getAnnotationColours()
2669                                   .getAboveThreshold());
2670                 }
2671
2672               }
2673             }
2674
2675             break;
2676           }
2677
2678         }
2679       }
2680       else
2681       {
2682         cs = ColourSchemeProperty.getColour(al, view.getBgColour());
2683       }
2684
2685       if (cs != null)
2686       {
2687         cs.setThreshold(view.getPidThreshold(), true);
2688         cs.setConsensus(af.viewport.hconsensus);
2689       }
2690     }
2691
2692     af.viewport.setGlobalColourScheme(cs);
2693     af.viewport.setColourAppliesToAllGroups(false);
2694
2695     if (view.getConservationSelected() && cs != null)
2696     {
2697       cs.setConservationInc(view.getConsThreshold());
2698     }
2699
2700     af.changeColour(cs);
2701
2702     af.viewport.setColourAppliesToAllGroups(true);
2703
2704     if (view.getShowSequenceFeatures())
2705     {
2706       af.viewport.showSequenceFeatures = true;
2707     }
2708     // recover featre settings
2709     if (jms.getFeatureSettings() != null)
2710     {
2711       af.viewport.featuresDisplayed = new Hashtable();
2712       String[] renderOrder = new String[jms.getFeatureSettings()
2713               .getSettingCount()];
2714       for (int fs = 0; fs < jms.getFeatureSettings().getSettingCount(); fs++)
2715       {
2716         Setting setting = jms.getFeatureSettings().getSetting(fs);
2717         if (setting.hasMincolour())
2718         {
2719           // TODO: determine how to set data independent bounds for a graduated
2720           // colour scheme's range.
2721           GraduatedColor gc = new GraduatedColor(new java.awt.Color(setting
2722                   .getMincolour()),
2723                   new java.awt.Color(setting.getColour()), 0, 1);
2724           if (setting.hasThreshold())
2725           {
2726             gc.setThresh(setting.getThreshold());
2727             gc.setThreshType(setting.getThreshstate());
2728           }
2729         }
2730         else
2731         {
2732           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setColour(
2733                   setting.getType(),
2734                   new java.awt.Color(setting.getColour()));
2735         }
2736         renderOrder[fs] = setting.getType();
2737         if (setting.hasOrder())
2738           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setOrder(
2739                   setting.getType(), setting.getOrder());
2740         else
2741           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setOrder(
2742                   setting.getType(),
2743                   fs / jms.getFeatureSettings().getSettingCount());
2744         if (setting.getDisplay())
2745         {
2746           af.viewport.featuresDisplayed.put(setting.getType(), new Integer(
2747                   setting.getColour()));
2748         }
2749       }
2750       af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().renderOrder = renderOrder;
2751       Hashtable fgtable;
2752       af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().featureGroups = fgtable = new Hashtable();
2753       for (int gs = 0; gs < jms.getFeatureSettings().getGroupCount(); gs++)
2754       {
2755         Group grp = jms.getFeatureSettings().getGroup(gs);
2756         fgtable.put(grp.getName(), new Boolean(grp.getDisplay()));
2757       }
2758     }
2759
2760     if (view.getHiddenColumnsCount() > 0)
2761     {
2762       for (int c = 0; c < view.getHiddenColumnsCount(); c++)
2763       {
2764         af.viewport.hideColumns(view.getHiddenColumns(c).getStart(), view
2765                 .getHiddenColumns(c).getEnd() // +1
2766                 );
2767       }
2768     }
2769
2770     af.setMenusFromViewport(af.viewport);
2771     // TODO: we don't need to do this if the viewport is aready visible.
2772     Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(), view
2773             .getHeight());
2774     return af;
2775   }
2776
2777   Hashtable skipList = null;
2778
2779   /**
2780    * TODO remove this method
2781    * 
2782    * @param view
2783    * @return AlignFrame bound to sequenceSetId from view, if one exists. private
2784    *         AlignFrame getSkippedFrame(Viewport view) { if (skipList==null) {
2785    *         throw new Error("Implementation Error. No skipList defined for this
2786    *         Jalview2XML instance."); } return (AlignFrame)
2787    *         skipList.get(view.getSequenceSetId()); }
2788    */
2789
2790   /**
2791    * Check if the Jalview view contained in object should be skipped or not.
2792    * 
2793    * @param object
2794    * @return true if view's sequenceSetId is a key in skipList
2795    */
2796   private boolean skipViewport(JalviewModel object)
2797   {
2798     if (skipList == null)
2799     {
2800       return false;
2801     }
2802     String id;
2803     if (skipList.containsKey(id = object.getJalviewModelSequence()
2804             .getViewport()[0].getSequenceSetId()))
2805     {
2806       if (Cache.log != null && Cache.log.isDebugEnabled())
2807       {
2808         Cache.log.debug("Skipping seuqence set id " + id);
2809       }
2810       return true;
2811     }
2812     return false;
2813   }
2814
2815   public void AddToSkipList(AlignFrame af)
2816   {
2817     if (skipList == null)
2818     {
2819       skipList = new Hashtable();
2820     }
2821     skipList.put(af.getViewport().getSequenceSetId(), af);
2822   }
2823
2824   public void clearSkipList()
2825   {
2826     if (skipList != null)
2827     {
2828       skipList.clear();
2829       skipList = null;
2830     }
2831   }
2832
2833   private void recoverDatasetFor(SequenceSet vamsasSet, Alignment al)
2834   {
2835     jalview.datamodel.Alignment ds = getDatasetFor(vamsasSet.getDatasetId());
2836     Vector dseqs = null;
2837     if (ds == null)
2838     {
2839       // create a list of new dataset sequences
2840       dseqs = new Vector();
2841     }
2842     for (int i = 0, iSize = vamsasSet.getSequenceCount(); i < iSize; i++)
2843     {
2844       Sequence vamsasSeq = vamsasSet.getSequence(i);
2845       ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs);
2846     }
2847     // create a new dataset
2848     if (ds == null)
2849     {
2850       SequenceI[] dsseqs = new SequenceI[dseqs.size()];
2851       dseqs.copyInto(dsseqs);
2852       ds = new jalview.datamodel.Alignment(dsseqs);
2853       debug("Created new dataset " + vamsasSet.getDatasetId()
2854               + " for alignment " + System.identityHashCode(al));
2855       addDatasetRef(vamsasSet.getDatasetId(), ds);
2856     }
2857     // set the dataset for the newly imported alignment.
2858     if (al.getDataset() == null)
2859     {
2860       al.setDataset(ds);
2861     }
2862   }
2863
2864   /**
2865    * 
2866    * @param vamsasSeq
2867    *          sequence definition to create/merge dataset sequence for
2868    * @param ds
2869    *          dataset alignment
2870    * @param dseqs
2871    *          vector to add new dataset sequence to
2872    */
2873   private void ensureJalviewDatasetSequence(Sequence vamsasSeq,
2874           AlignmentI ds, Vector dseqs)
2875   {
2876     // JBP TODO: Check this is called for AlCodonFrames to support recovery of
2877     // xRef Codon Maps
2878     jalview.datamodel.Sequence sq = (jalview.datamodel.Sequence) seqRefIds
2879             .get(vamsasSeq.getId());
2880     jalview.datamodel.SequenceI dsq = null;
2881     if (sq != null && sq.getDatasetSequence() != null)
2882     {
2883       dsq = (jalview.datamodel.SequenceI) sq.getDatasetSequence();
2884     }
2885
2886     String sqid = vamsasSeq.getDsseqid();
2887     if (dsq == null)
2888     {
2889       // need to create or add a new dataset sequence reference to this sequence
2890       if (sqid != null)
2891       {
2892         dsq = (jalview.datamodel.SequenceI) seqRefIds.get(sqid);
2893       }
2894       // check again
2895       if (dsq == null)
2896       {
2897         // make a new dataset sequence
2898         dsq = sq.createDatasetSequence();
2899         if (sqid == null)
2900         {
2901           // make up a new dataset reference for this sequence
2902           sqid = seqHash(dsq);
2903         }
2904         dsq.setVamsasId(uniqueSetSuffix + sqid);
2905         seqRefIds.put(sqid, dsq);
2906         if (ds == null)
2907         {
2908           if (dseqs != null)
2909           {
2910             dseqs.addElement(dsq);
2911           }
2912         }
2913         else
2914         {
2915           ds.addSequence(dsq);
2916         }
2917       }
2918       else
2919       {
2920         if (sq != dsq)
2921         { // make this dataset sequence sq's dataset sequence
2922           sq.setDatasetSequence(dsq);
2923         }
2924       }
2925     }
2926     // TODO: refactor this as a merge dataset sequence function
2927     // now check that sq (the dataset sequence) sequence really is the union of
2928     // all references to it
2929     // boolean pre = sq.getStart() < dsq.getStart();
2930     // boolean post = sq.getEnd() > dsq.getEnd();
2931     // if (pre || post)
2932     if (sq != dsq)
2933     {
2934       StringBuffer sb = new StringBuffer();
2935       String newres = jalview.analysis.AlignSeq.extractGaps(
2936               jalview.util.Comparison.GapChars, sq.getSequenceAsString());
2937       if (!newres.equalsIgnoreCase(dsq.getSequenceAsString())
2938               && newres.length() > dsq.getLength())
2939       {
2940         // Update with the longer sequence.
2941         synchronized (dsq)
2942         {
2943           /*
2944            * if (pre) { sb.insert(0, newres .substring(0, dsq.getStart() -
2945            * sq.getStart())); dsq.setStart(sq.getStart()); } if (post) {
2946            * sb.append(newres.substring(newres.length() - sq.getEnd() -
2947            * dsq.getEnd())); dsq.setEnd(sq.getEnd()); }
2948            */
2949           dsq.setSequence(sb.toString());
2950         }
2951         // TODO: merges will never happen if we 'know' we have the real dataset
2952         // sequence - this should be detected when id==dssid
2953         System.err.println("DEBUG Notice:  Merged dataset sequence"); // ("
2954         // + (pre ? "prepended" : "") + " "
2955         // + (post ? "appended" : ""));
2956       }
2957     }
2958   }
2959
2960   java.util.Hashtable datasetIds = null;
2961
2962   java.util.IdentityHashMap dataset2Ids = null;
2963
2964   private Alignment getDatasetFor(String datasetId)
2965   {
2966     if (datasetIds == null)
2967     {
2968       datasetIds = new Hashtable();
2969       return null;
2970     }
2971     if (datasetIds.containsKey(datasetId))
2972     {
2973       return (Alignment) datasetIds.get(datasetId);
2974     }
2975     return null;
2976   }
2977
2978   private void addDatasetRef(String datasetId, Alignment dataset)
2979   {
2980     if (datasetIds == null)
2981     {
2982       datasetIds = new Hashtable();
2983     }
2984     datasetIds.put(datasetId, dataset);
2985   }
2986
2987   /**
2988    * make a new dataset ID for this jalview dataset alignment
2989    * 
2990    * @param dataset
2991    * @return
2992    */
2993   private String getDatasetIdRef(jalview.datamodel.Alignment dataset)
2994   {
2995     if (dataset.getDataset() != null)
2996     {
2997       warn("Serious issue!  Dataset Object passed to getDatasetIdRef is not a Jalview DATASET alignment...");
2998     }
2999     String datasetId = makeHashCode(dataset, null);
3000     if (datasetId == null)
3001     {
3002       // make a new datasetId and record it
3003       if (dataset2Ids == null)
3004       {
3005         dataset2Ids = new IdentityHashMap();
3006       }
3007       else
3008       {
3009         datasetId = (String) dataset2Ids.get(dataset);
3010       }
3011       if (datasetId == null)
3012       {
3013         datasetId = "ds" + dataset2Ids.size() + 1;
3014         dataset2Ids.put(dataset, datasetId);
3015       }
3016     }
3017     return datasetId;
3018   }
3019
3020   private void addDBRefs(SequenceI datasetSequence, Sequence sequence)
3021   {
3022     for (int d = 0; d < sequence.getDBRefCount(); d++)
3023     {
3024       DBRef dr = sequence.getDBRef(d);
3025       jalview.datamodel.DBRefEntry entry = new jalview.datamodel.DBRefEntry(
3026               sequence.getDBRef(d).getSource(), sequence.getDBRef(d)
3027                       .getVersion(), sequence.getDBRef(d).getAccessionId());
3028       if (dr.getMapping() != null)
3029       {
3030         entry.setMap(addMapping(dr.getMapping()));
3031       }
3032       datasetSequence.addDBRef(entry);
3033     }
3034   }
3035
3036   private jalview.datamodel.Mapping addMapping(Mapping m)
3037   {
3038     SequenceI dsto = null;
3039     // Mapping m = dr.getMapping();
3040     int fr[] = new int[m.getMapListFromCount() * 2];
3041     Enumeration f = m.enumerateMapListFrom();
3042     for (int _i = 0; f.hasMoreElements(); _i += 2)
3043     {
3044       MapListFrom mf = (MapListFrom) f.nextElement();
3045       fr[_i] = mf.getStart();
3046       fr[_i + 1] = mf.getEnd();
3047     }
3048     int fto[] = new int[m.getMapListToCount() * 2];
3049     f = m.enumerateMapListTo();
3050     for (int _i = 0; f.hasMoreElements(); _i += 2)
3051     {
3052       MapListTo mf = (MapListTo) f.nextElement();
3053       fto[_i] = mf.getStart();
3054       fto[_i + 1] = mf.getEnd();
3055     }
3056     jalview.datamodel.Mapping jmap = new jalview.datamodel.Mapping(dsto,
3057             fr, fto, (int) m.getMapFromUnit(), (int) m.getMapToUnit());
3058     if (m.getMappingChoice() != null)
3059     {
3060       MappingChoice mc = m.getMappingChoice();
3061       if (mc.getDseqFor() != null)
3062       {
3063         String dsfor = "" + mc.getDseqFor();
3064         if (seqRefIds.containsKey(dsfor))
3065         {
3066           /**
3067            * recover from hash
3068            */
3069           jmap.setTo((SequenceI) seqRefIds.get(dsfor));
3070         }
3071         else
3072         {
3073           frefedSequence.add(new Object[]
3074           { dsfor, jmap });
3075         }
3076       }
3077       else
3078       {
3079         /**
3080          * local sequence definition
3081          */
3082         Sequence ms = mc.getSequence();
3083         jalview.datamodel.Sequence djs = null;
3084         String sqid = ms.getDsseqid();
3085         if (sqid != null && sqid.length() > 0)
3086         {
3087           /*
3088            * recover dataset sequence
3089            */
3090           djs = (jalview.datamodel.Sequence) seqRefIds.get(sqid);
3091         }
3092         else
3093         {
3094           System.err
3095                   .println("Warning - making up dataset sequence id for DbRef sequence map reference");
3096           sqid = ((Object) ms).toString(); // make up a new hascode for
3097           // undefined dataset sequence hash
3098           // (unlikely to happen)
3099         }
3100
3101         if (djs == null)
3102         {
3103           /**
3104            * make a new dataset sequence and add it to refIds hash
3105            */
3106           djs = new jalview.datamodel.Sequence(ms.getName(), ms
3107                   .getSequence());
3108           djs.setStart(jmap.getMap().getToLowest());
3109           djs.setEnd(jmap.getMap().getToHighest());
3110           djs.setVamsasId(uniqueSetSuffix + sqid);
3111           jmap.setTo(djs);
3112           seqRefIds.put(sqid, djs);
3113
3114         }
3115         jalview.bin.Cache.log.debug("about to recurse on addDBRefs.");
3116         addDBRefs(djs, ms);
3117
3118       }
3119     }
3120     return (jmap);
3121
3122   }
3123
3124   public jalview.gui.AlignmentPanel copyAlignPanel(AlignmentPanel ap,
3125           boolean keepSeqRefs)
3126   {
3127     initSeqRefs();
3128     jalview.schemabinding.version2.JalviewModel jm = SaveState(ap, null,
3129             null);
3130
3131     if (!keepSeqRefs)
3132     {
3133       clearSeqRefs();
3134       jm.getJalviewModelSequence().getViewport(0).setSequenceSetId(null);
3135     }
3136     else
3137     {
3138       uniqueSetSuffix = "";
3139       jm.getJalviewModelSequence().getViewport(0).setId(null); // we don't
3140                                                                // overwrite the
3141                                                                // view we just
3142                                                                // copied
3143     }
3144     if (this.frefedSequence == null)
3145     {
3146       frefedSequence = new Vector();
3147     }
3148
3149     viewportsAdded = new Hashtable();
3150
3151     AlignFrame af = LoadFromObject(jm, null, false, null);
3152     af.alignPanels.clear();
3153     af.closeMenuItem_actionPerformed(true);
3154
3155     /*
3156      * if(ap.av.alignment.getAlignmentAnnotation()!=null) { for(int i=0;
3157      * i<ap.av.alignment.getAlignmentAnnotation().length; i++) {
3158      * if(!ap.av.alignment.getAlignmentAnnotation()[i].autoCalculated) {
3159      * af.alignPanel.av.alignment.getAlignmentAnnotation()[i] =
3160      * ap.av.alignment.getAlignmentAnnotation()[i]; } } }
3161      */
3162
3163     return af.alignPanel;
3164   }
3165
3166   /**
3167    * flag indicating if hashtables should be cleared on finalization TODO this
3168    * flag may not be necessary
3169    */
3170   private boolean _cleartables = true;
3171
3172   private Hashtable jvids2vobj;
3173
3174   /*
3175    * (non-Javadoc)
3176    * 
3177    * @see java.lang.Object#finalize()
3178    */
3179   protected void finalize() throws Throwable
3180   {
3181     // really make sure we have no buried refs left.
3182     if (_cleartables)
3183     {
3184       clearSeqRefs();
3185     }
3186     this.seqRefIds = null;
3187     this.seqsToIds = null;
3188     super.finalize();
3189   }
3190
3191   private void warn(String msg)
3192   {
3193     warn(msg, null);
3194   }
3195
3196   private void warn(String msg, Exception e)
3197   {
3198     if (Cache.log != null)
3199     {
3200       if (e != null)
3201       {
3202         Cache.log.warn(msg, e);
3203       }
3204       else
3205       {
3206         Cache.log.warn(msg);
3207       }
3208     }
3209     else
3210     {
3211       System.err.println("Warning: " + msg);
3212       if (e != null)
3213       {
3214         e.printStackTrace();
3215       }
3216     }
3217   }
3218
3219   private void debug(String string)
3220   {
3221     debug(string, null);
3222   }
3223
3224   private void debug(String msg, Exception e)
3225   {
3226     if (Cache.log != null)
3227     {
3228       if (e != null)
3229       {
3230         Cache.log.debug(msg, e);
3231       }
3232       else
3233       {
3234         Cache.log.debug(msg);
3235       }
3236     }
3237     else
3238     {
3239       System.err.println("Warning: " + msg);
3240       if (e != null)
3241       {
3242         e.printStackTrace();
3243       }
3244     }
3245   }
3246
3247   /**
3248    * set the object to ID mapping tables used to write/recover objects and XML
3249    * ID strings for the jalview project. If external tables are provided then
3250    * finalize and clearSeqRefs will not clear the tables when the Jalview2XML
3251    * object goes out of scope. - also populates the datasetIds hashtable with
3252    * alignment objects containing dataset sequences
3253    * 
3254    * @param vobj2jv
3255    *          Map from ID strings to jalview datamodel
3256    * @param jv2vobj
3257    *          Map from jalview datamodel to ID strings
3258    * 
3259    * 
3260    */
3261   public void setObjectMappingTables(Hashtable vobj2jv,
3262           IdentityHashMap jv2vobj)
3263   {
3264     this.jv2vobj = jv2vobj;
3265     this.vobj2jv = vobj2jv;
3266     Iterator ds = jv2vobj.keySet().iterator();
3267     String id;
3268     while (ds.hasNext())
3269     {
3270       Object jvobj = ds.next();
3271       id = jv2vobj.get(jvobj).toString();
3272       if (jvobj instanceof jalview.datamodel.Alignment)
3273       {
3274         if (((jalview.datamodel.Alignment) jvobj).getDataset() == null)
3275         {
3276           addDatasetRef(id, (jalview.datamodel.Alignment) jvobj);
3277         }
3278       }
3279       else if (jvobj instanceof jalview.datamodel.Sequence)
3280       {
3281         // register sequence object so the XML parser can recover it.
3282         if (seqRefIds == null)
3283         {
3284           seqRefIds = new Hashtable();
3285         }
3286         if (seqsToIds == null)
3287         {
3288           seqsToIds = new IdentityHashMap();
3289         }
3290         seqRefIds.put(jv2vobj.get(jvobj).toString(), jvobj);
3291         seqsToIds.put(jvobj, id);
3292       }
3293       else if (jvobj instanceof jalview.datamodel.AlignmentAnnotation)
3294       {
3295         if (annotationIds == null)
3296         {
3297           annotationIds = new Hashtable();
3298         }
3299         String anid;
3300         annotationIds.put(anid = jv2vobj.get(jvobj).toString(), jvobj);
3301         jalview.datamodel.AlignmentAnnotation jvann = (jalview.datamodel.AlignmentAnnotation) jvobj;
3302         if (jvann.annotationId == null)
3303         {
3304           jvann.annotationId = anid;
3305         }
3306         if (!jvann.annotationId.equals(anid))
3307         {
3308           // TODO verify that this is the correct behaviour
3309           this.warn("Overriding Annotation ID for " + anid
3310                   + " from different id : " + jvann.annotationId);
3311           jvann.annotationId = anid;
3312         }
3313       }
3314       else if (jvobj instanceof String)
3315       {
3316         if (jvids2vobj == null)
3317         {
3318           jvids2vobj = new Hashtable();
3319           jvids2vobj.put(jvobj, jv2vobj.get(jvobj).toString());
3320         }
3321       }
3322       else
3323         Cache.log.debug("Ignoring " + jvobj.getClass() + " (ID = " + id);
3324     }
3325   }
3326
3327   /**
3328    * set the uniqueSetSuffix used to prefix/suffix object IDs for jalview
3329    * objects created from the project archive. If string is null (default for
3330    * construction) then suffix will be set automatically.
3331    * 
3332    * @param string
3333    */
3334   public void setUniqueSetSuffix(String string)
3335   {
3336     uniqueSetSuffix = string;
3337
3338   }
3339
3340   /**
3341    * uses skipList2 as the skipList for skipping views on sequence sets
3342    * associated with keys in the skipList
3343    * 
3344    * @param skipList2
3345    */
3346   public void setSkipList(Hashtable skipList2)
3347   {
3348     skipList = skipList2;
3349   }
3350
3351 }