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