javatidy
[jalview.git] / src / jalview / gui / Jalview2XML.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7)
3  * Copyright (C) 2011 J Procter, AM Waterhouse, J Engelhardt, LM Lui, 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 jalview.bin.Cache;
33 import jalview.datamodel.Alignment;
34 import jalview.datamodel.AlignmentAnnotation;
35 import jalview.datamodel.AlignmentI;
36 import jalview.datamodel.SequenceI;
37 import jalview.schemabinding.version2.*;
38 import jalview.schemes.*;
39 import jalview.util.Platform;
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: 1.134 $
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.getAlignment();
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.getAlignment().getHiddenSequences().isHidden(jds));
502
503         if (av.isHiddenRepSequence(jal.getSequenceAt(i)))
504         {
505           jalview.datamodel.SequenceI[] reps = av.getRepresentedSequences(jal.getSequenceAt(i)).getSequencesInOrder(jal);
506
507           for (int h = 0; h < reps.length; h++)
508           {
509             if (reps[h] != jal.getSequenceAt(i))
510             {
511               jseq.addHiddenSequences(jal.findIndex(reps[h]));
512             }
513           }
514         }
515       }
516
517       if (jds.getDatasetSequence().getSequenceFeatures() != null)
518       {
519         jalview.datamodel.SequenceFeature[] sf = jds.getDatasetSequence()
520                 .getSequenceFeatures();
521         int index = 0;
522         while (index < sf.length)
523         {
524           Features features = new Features();
525
526           features.setBegin(sf[index].getBegin());
527           features.setEnd(sf[index].getEnd());
528           features.setDescription(sf[index].getDescription());
529           features.setType(sf[index].getType());
530           features.setFeatureGroup(sf[index].getFeatureGroup());
531           features.setScore(sf[index].getScore());
532           if (sf[index].links != null)
533           {
534             for (int l = 0; l < sf[index].links.size(); l++)
535             {
536               OtherData keyValue = new OtherData();
537               keyValue.setKey("LINK_" + l);
538               keyValue.setValue(sf[index].links.elementAt(l).toString());
539               features.addOtherData(keyValue);
540             }
541           }
542           if (sf[index].otherDetails != null)
543           {
544             String key;
545             Enumeration keys = sf[index].otherDetails.keys();
546             while (keys.hasMoreElements())
547             {
548               key = keys.nextElement().toString();
549               OtherData keyValue = new OtherData();
550               keyValue.setKey(key);
551               keyValue.setValue(sf[index].otherDetails.get(key).toString());
552               features.addOtherData(keyValue);
553             }
554           }
555
556           jseq.addFeatures(features);
557           index++;
558         }
559       }
560
561       if (jds.getDatasetSequence().getPDBId() != null)
562       {
563         Enumeration en = jds.getDatasetSequence().getPDBId().elements();
564         while (en.hasMoreElements())
565         {
566           Pdbids pdb = new Pdbids();
567           jalview.datamodel.PDBEntry entry = (jalview.datamodel.PDBEntry) en
568                   .nextElement();
569
570           pdb.setId(entry.getId());
571           pdb.setType(entry.getType());
572
573           AppJmol jmol;
574           // This must have been loaded, is it still visible?
575           JInternalFrame[] frames = Desktop.desktop.getAllFrames();
576           String matchedFile = null;
577           for (int f = frames.length - 1; f > -1; f--)
578           {
579             if (frames[f] instanceof AppJmol)
580             {
581               jmol = (AppJmol) frames[f];
582               for (int peid = 0; peid < jmol.jmb.pdbentry.length; peid++)
583               {
584                 if (!jmol.jmb.pdbentry[peid].getId().equals(entry.getId())
585                         && !(entry.getId().length() > 4 && entry
586                                 .getId()
587                                 .toLowerCase()
588                                 .startsWith(
589                                         jmol.jmb.pdbentry[peid].getId()
590                                                 .toLowerCase())))
591                   continue;
592                 if (matchedFile == null)
593                 {
594                   matchedFile = jmol.jmb.pdbentry[peid].getFile();
595                 }
596                 else if (!matchedFile.equals(jmol.jmb.pdbentry[peid]
597                         .getFile()))
598                 {
599                   Cache.log
600                           .warn("Probably lost some PDB-Sequence mappings for this structure file (which apparently has same PDB Entry code): "
601                                   + jmol.jmb.pdbentry[peid].getFile());
602                   ; // record the
603                 }
604                 // file so we
605                 // can get at it if the ID
606                 // match is ambiguous (e.g.
607                 // 1QIP==1qipA)
608                 String statestring = jmol.jmb.viewer.getStateInfo();
609
610                 for (int smap = 0; smap < jmol.jmb.sequence[peid].length; smap++)
611                 {
612 //                  if (jal.findIndex(jmol.jmb.sequence[peid][smap]) > -1)
613                   if (jds==jmol.jmb.sequence[peid][smap])
614                   {
615                     StructureState state = new StructureState();
616                     state.setVisible(true);
617                     state.setXpos(jmol.getX());
618                     state.setYpos(jmol.getY());
619                     state.setWidth(jmol.getWidth());
620                     state.setHeight(jmol.getHeight());
621                     state.setViewId(jmol.getViewId());
622                     state.setAlignwithAlignPanel(jmol.isUsedforaligment(ap));
623                     state.setColourwithAlignPanel(jmol
624                             .isUsedforcolourby(ap));
625                     state.setColourByJmol(jmol.isColouredByJmol());
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.getAlignment();
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.getAlignment() == 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
838         // store all visualization attributes for annotation
839         an.setGraphHeight(aa[i].graphHeight);
840         an.setCentreColLabels(aa[i].centreColLabels);
841         an.setScaleColLabels(aa[i].scaleColLabel);
842         an.setShowAllColLabels(aa[i].showAllColLabels);
843         an.setBelowAlignment(aa[i].belowAlignment);
844
845         if (aa[i].graph > 0)
846         {
847           an.setGraph(true);
848           an.setGraphType(aa[i].graph);
849           an.setGraphGroup(aa[i].graphGroup);
850           if (aa[i].getThreshold() != null)
851           {
852             ThresholdLine line = new ThresholdLine();
853             line.setLabel(aa[i].getThreshold().label);
854             line.setValue(aa[i].getThreshold().value);
855             line.setColour(aa[i].getThreshold().colour.getRGB());
856             an.setThresholdLine(line);
857           }
858         }
859         else
860         {
861           an.setGraph(false);
862         }
863
864         an.setLabel(aa[i].label);
865
866         if (aa[i] == av.getAlignmentQualityAnnot() || aa[i] == av.getAlignmentConservationAnnotation()
867                 || aa[i] == av.getAlignmentConsensusAnnotation() || aa[i].autoCalculated)
868         {
869           // new way of indicating autocalculated annotation -
870           an.setAutoCalculated(aa[i].autoCalculated);
871         }
872         if (aa[i].hasScore())
873         {
874           an.setScore(aa[i].getScore());
875         }
876
877         if (aa[i].getCalcId()!=null)
878         {
879           an.setCalcId(aa[i].getCalcId());
880         }
881
882         AnnotationElement ae;
883         if (aa[i].annotations != null)
884         {
885           an.setScoreOnly(false);
886           for (int a = 0; a < aa[i].annotations.length; a++)
887           {
888             if ((aa[i] == null) || (aa[i].annotations[a] == null))
889             {
890               continue;
891             }
892
893             ae = new AnnotationElement();
894             if (aa[i].annotations[a].description != null)
895               ae.setDescription(aa[i].annotations[a].description);
896             if (aa[i].annotations[a].displayCharacter != null)
897               ae.setDisplayCharacter(aa[i].annotations[a].displayCharacter);
898
899             if (!Float.isNaN(aa[i].annotations[a].value))
900               ae.setValue(aa[i].annotations[a].value);
901
902             ae.setPosition(a);
903             if (aa[i].annotations[a].secondaryStructure != ' '
904                     && aa[i].annotations[a].secondaryStructure != '\0')
905               ae.setSecondaryStructure(aa[i].annotations[a].secondaryStructure
906                       + "");
907
908             if (aa[i].annotations[a].colour != null
909                     && aa[i].annotations[a].colour != java.awt.Color.black)
910             {
911               ae.setColour(aa[i].annotations[a].colour.getRGB());
912             }
913
914             an.addAnnotationElement(ae);
915             if (aa[i].autoCalculated)
916             {
917               // only write one non-null entry into the annotation row -
918               // sufficient to get the visualization attributes necessary to
919               // display data
920               continue;
921             }
922           }
923         }
924         else
925         {
926           an.setScoreOnly(true);
927         }
928         vamsasSet.addAnnotation(an);
929       }
930     }
931     // SAVE GROUPS
932     if (jal.getGroups() != null)
933     {
934       JGroup[] groups = new JGroup[jal.getGroups().size()];
935       int i = -1;
936       for (jalview.datamodel.SequenceGroup sg:jal.getGroups())
937       {
938         groups[++i] = new JGroup();
939
940         groups[i].setStart(sg.getStartRes());
941         groups[i].setEnd(sg.getEndRes());
942         groups[i].setName(sg.getName());
943         if (groupRefs.containsKey(sg))
944         {
945           // group has references so set it's ID field
946           groups[i].setId(groupRefs.get(sg).toString());
947         }
948         if (sg.cs != null)
949         {
950           if (sg.cs.conservationApplied())
951           {
952             groups[i].setConsThreshold(sg.cs.getConservationInc());
953
954             if (sg.cs instanceof jalview.schemes.UserColourScheme)
955             {
956               groups[i].setColour(SetUserColourScheme(sg.cs, userColours,
957                       jms));
958             }
959             else
960             {
961               groups[i]
962                       .setColour(ColourSchemeProperty.getColourName(sg.cs));
963             }
964           }
965           else if (sg.cs instanceof jalview.schemes.AnnotationColourGradient)
966           {
967             groups[i]
968                     .setColour(ColourSchemeProperty
969                             .getColourName(((jalview.schemes.AnnotationColourGradient) sg.cs)
970                                     .getBaseColour()));
971           }
972           else if (sg.cs instanceof jalview.schemes.UserColourScheme)
973           {
974             groups[i]
975                     .setColour(SetUserColourScheme(sg.cs, userColours, jms));
976           }
977           else
978           {
979             groups[i].setColour(ColourSchemeProperty.getColourName(sg.cs));
980           }
981
982           groups[i].setPidThreshold(sg.cs.getThreshold());
983         }
984
985         groups[i].setOutlineColour(sg.getOutlineColour().getRGB());
986         groups[i].setDisplayBoxes(sg.getDisplayBoxes());
987         groups[i].setDisplayText(sg.getDisplayText());
988         groups[i].setColourText(sg.getColourText());
989         groups[i].setTextCol1(sg.textColour.getRGB());
990         groups[i].setTextCol2(sg.textColour2.getRGB());
991         groups[i].setTextColThreshold(sg.thresholdTextColour);
992         groups[i].setShowUnconserved(sg.getShowNonconserved());
993         groups[i].setIgnoreGapsinConsensus(sg.getIgnoreGapsConsensus());
994         groups[i].setShowConsensusHistogram(sg.isShowConsensusHistogram());
995         groups[i].setShowSequenceLogo(sg.isShowSequenceLogo());
996         for (int s = 0; s < sg.getSize(); s++)
997         {
998           jalview.datamodel.Sequence seq = (jalview.datamodel.Sequence) sg
999                   .getSequenceAt(s);
1000           groups[i].addSeq(seqHash(seq));
1001         }
1002       }
1003
1004       jms.setJGroup(groups);
1005     }
1006
1007     // /////////SAVE VIEWPORT
1008     Viewport view = new Viewport();
1009     view.setTitle(ap.alignFrame.getTitle());
1010     view.setSequenceSetId(makeHashCode(av.getSequenceSetId(),
1011             av.getSequenceSetId()));
1012     view.setId(av.getViewId());
1013     view.setViewName(av.viewName);
1014     view.setGatheredViews(av.gatherViewsHere);
1015
1016     if (ap.av.explodedPosition != null)
1017     {
1018       view.setXpos(av.explodedPosition.x);
1019       view.setYpos(av.explodedPosition.y);
1020       view.setWidth(av.explodedPosition.width);
1021       view.setHeight(av.explodedPosition.height);
1022     }
1023     else
1024     {
1025       view.setXpos(ap.alignFrame.getBounds().x);
1026       view.setYpos(ap.alignFrame.getBounds().y);
1027       view.setWidth(ap.alignFrame.getBounds().width);
1028       view.setHeight(ap.alignFrame.getBounds().height);
1029     }
1030
1031     view.setStartRes(av.startRes);
1032     view.setStartSeq(av.startSeq);
1033
1034     if (av.getGlobalColourScheme() instanceof jalview.schemes.UserColourScheme)
1035     {
1036       view.setBgColour(SetUserColourScheme(av.getGlobalColourScheme(),
1037               userColours, jms));
1038     }
1039     else if (av.getGlobalColourScheme() instanceof jalview.schemes.AnnotationColourGradient)
1040     {
1041       jalview.schemes.AnnotationColourGradient acg = (jalview.schemes.AnnotationColourGradient) av
1042               .getGlobalColourScheme();
1043
1044       AnnotationColours ac = new AnnotationColours();
1045       ac.setAboveThreshold(acg.getAboveThreshold());
1046       ac.setThreshold(acg.getAnnotationThreshold());
1047       ac.setAnnotation(acg.getAnnotation());
1048       if (acg.getBaseColour() instanceof jalview.schemes.UserColourScheme)
1049       {
1050         ac.setColourScheme(SetUserColourScheme(acg.getBaseColour(),
1051                 userColours, jms));
1052       }
1053       else
1054       {
1055         ac.setColourScheme(ColourSchemeProperty.getColourName(acg
1056                 .getBaseColour()));
1057       }
1058
1059       ac.setMaxColour(acg.getMaxColour().getRGB());
1060       ac.setMinColour(acg.getMinColour().getRGB());
1061       view.setAnnotationColours(ac);
1062       view.setBgColour("AnnotationColourGradient");
1063     }
1064     else
1065     {
1066       view.setBgColour(ColourSchemeProperty.getColourName(av
1067               .getGlobalColourScheme()));
1068     }
1069
1070     ColourSchemeI cs = av.getGlobalColourScheme();
1071
1072     if (cs != null)
1073     {
1074       if (cs.conservationApplied())
1075       {
1076         view.setConsThreshold(cs.getConservationInc());
1077         if (cs instanceof jalview.schemes.UserColourScheme)
1078         {
1079           view.setBgColour(SetUserColourScheme(cs, userColours, jms));
1080         }
1081       }
1082
1083       if (cs instanceof ResidueColourScheme)
1084       {
1085         view.setPidThreshold(cs.getThreshold());
1086       }
1087     }
1088
1089     view.setConservationSelected(av.getConservationSelected());
1090     view.setPidSelected(av.getAbovePIDThreshold());
1091     view.setFontName(av.font.getName());
1092     view.setFontSize(av.font.getSize());
1093     view.setFontStyle(av.font.getStyle());
1094     view.setRenderGaps(av.renderGaps);
1095     view.setShowAnnotation(av.getShowAnnotation());
1096     view.setShowBoxes(av.getShowBoxes());
1097     view.setShowColourText(av.getColourText());
1098     view.setShowFullId(av.getShowJVSuffix());
1099     view.setRightAlignIds(av.rightAlignIds);
1100     view.setShowSequenceFeatures(av.showSequenceFeatures);
1101     view.setShowText(av.getShowText());
1102     view.setShowUnconserved(av.getShowUnconserved());
1103     view.setWrapAlignment(av.getWrapAlignment());
1104     view.setTextCol1(av.textColour.getRGB());
1105     view.setTextCol2(av.textColour2.getRGB());
1106     view.setTextColThreshold(av.thresholdTextColour);
1107     view.setShowConsensusHistogram(av.isShowConsensusHistogram());
1108     view.setShowSequenceLogo(av.isShowSequenceLogo());
1109     view.setShowGroupConsensus(av.isShowGroupConsensus());
1110     view.setShowGroupConservation(av.isShowGroupConservation());
1111     view.setShowNPfeatureTooltip(av.isShowNpFeats());
1112     view.setShowDbRefTooltip(av.isShowDbRefs());
1113     view.setFollowHighlight(av.followHighlight);
1114     view.setFollowSelection(av.followSelection);
1115     view.setIgnoreGapsinConsensus(av.getIgnoreGapsConsensus());
1116     if (av.featuresDisplayed != null)
1117     {
1118       jalview.schemabinding.version2.FeatureSettings fs = new jalview.schemabinding.version2.FeatureSettings();
1119
1120       String[] renderOrder = ap.seqPanel.seqCanvas.getFeatureRenderer().renderOrder;
1121
1122       Vector settingsAdded = new Vector();
1123       Object gstyle = null;
1124       GraduatedColor gcol = null;
1125       if (renderOrder != null)
1126       {
1127         for (int ro = 0; ro < renderOrder.length; ro++)
1128         {
1129           gstyle = ap.seqPanel.seqCanvas.getFeatureRenderer()
1130                   .getFeatureStyle(renderOrder[ro]);
1131           Setting setting = new Setting();
1132           setting.setType(renderOrder[ro]);
1133           if (gstyle instanceof GraduatedColor)
1134           {
1135             gcol = (GraduatedColor) gstyle;
1136             setting.setColour(gcol.getMaxColor().getRGB());
1137             setting.setMincolour(gcol.getMinColor().getRGB());
1138             setting.setMin(gcol.getMin());
1139             setting.setMax(gcol.getMax());
1140             setting.setColourByLabel(gcol.isColourByLabel());
1141             setting.setAutoScale(gcol.isAutoScale());
1142             setting.setThreshold(gcol.getThresh());
1143             setting.setThreshstate(gcol.getThreshType());
1144           }
1145           else
1146           {
1147             setting.setColour(ap.seqPanel.seqCanvas.getFeatureRenderer()
1148                     .getColour(renderOrder[ro]).getRGB());
1149           }
1150
1151           setting.setDisplay(av.featuresDisplayed
1152                   .containsKey(renderOrder[ro]));
1153           float rorder = ap.seqPanel.seqCanvas.getFeatureRenderer()
1154                   .getOrder(renderOrder[ro]);
1155           if (rorder > -1)
1156           {
1157             setting.setOrder(rorder);
1158           }
1159           fs.addSetting(setting);
1160           settingsAdded.addElement(renderOrder[ro]);
1161         }
1162       }
1163
1164       // Make sure we save none displayed feature settings
1165       Enumeration en = ap.seqPanel.seqCanvas.getFeatureRenderer().featureColours
1166               .keys();
1167       while (en.hasMoreElements())
1168       {
1169         String key = en.nextElement().toString();
1170         if (settingsAdded.contains(key))
1171         {
1172           continue;
1173         }
1174
1175         Setting setting = new Setting();
1176         setting.setType(key);
1177         setting.setColour(ap.seqPanel.seqCanvas.getFeatureRenderer()
1178                 .getColour(key).getRGB());
1179
1180         setting.setDisplay(false);
1181         float rorder = ap.seqPanel.seqCanvas.getFeatureRenderer().getOrder(
1182                 key);
1183         if (rorder > -1)
1184         {
1185           setting.setOrder(rorder);
1186         }
1187         fs.addSetting(setting);
1188         settingsAdded.addElement(key);
1189       }
1190       en = ap.seqPanel.seqCanvas.getFeatureRenderer().featureGroups.keys();
1191       Vector groupsAdded = new Vector();
1192       while (en.hasMoreElements())
1193       {
1194         String grp = en.nextElement().toString();
1195         if (groupsAdded.contains(grp))
1196         {
1197           continue;
1198         }
1199         Group g = new Group();
1200         g.setName(grp);
1201         g.setDisplay(((Boolean) ap.seqPanel.seqCanvas.getFeatureRenderer().featureGroups
1202                 .get(grp)).booleanValue());
1203         fs.addGroup(g);
1204         groupsAdded.addElement(grp);
1205       }
1206       jms.setFeatureSettings(fs);
1207
1208     }
1209
1210     if (av.hasHiddenColumns())
1211     {
1212       if (av.getColumnSelection() == null
1213               || av.getColumnSelection().getHiddenColumns() == null)
1214       {
1215         warn("REPORT BUG: avoided null columnselection bug (DMAM reported). Please contact Jim about this.");
1216       }
1217       else
1218       {
1219         for (int c = 0; c < av.getColumnSelection().getHiddenColumns()
1220                 .size(); c++)
1221         {
1222           int[] region = (int[]) av.getColumnSelection().getHiddenColumns()
1223                   .elementAt(c);
1224           HiddenColumns hc = new HiddenColumns();
1225           hc.setStart(region[0]);
1226           hc.setEnd(region[1]);
1227           view.addHiddenColumns(hc);
1228         }
1229       }
1230     }
1231
1232     jms.addViewport(view);
1233
1234     object.setJalviewModelSequence(jms);
1235     object.getVamsasModel().addSequenceSet(vamsasSet);
1236
1237     if (jout != null && fileName != null)
1238     {
1239       // We may not want to write the object to disk,
1240       // eg we can copy the alignViewport to a new view object
1241       // using save and then load
1242       try
1243       {
1244         JarEntry entry = new JarEntry(fileName);
1245         jout.putNextEntry(entry);
1246         PrintWriter pout = new PrintWriter(new OutputStreamWriter(jout,
1247                 "UTF-8"));
1248         org.exolab.castor.xml.Marshaller marshaller = new org.exolab.castor.xml.Marshaller(
1249                 pout);
1250         marshaller.marshal(object);
1251         pout.flush();
1252         jout.closeEntry();
1253       } catch (Exception ex)
1254       {
1255         // TODO: raise error in GUI if marshalling failed.
1256         ex.printStackTrace();
1257       }
1258     }
1259     return object;
1260   }
1261
1262   /**
1263    * External mapping between jalview objects and objects yielding a valid and
1264    * unique object ID string. This is null for normal Jalview project IO, but
1265    * non-null when a jalview project is being read or written as part of a
1266    * vamsas session.
1267    */
1268   IdentityHashMap jv2vobj = null;
1269
1270   /**
1271    * Construct a unique ID for jvobj using either existing bindings or if none
1272    * exist, the result of the hashcode call for the object.
1273    *
1274    * @param jvobj
1275    *          jalview data object
1276    * @return unique ID for referring to jvobj
1277    */
1278   private String makeHashCode(Object jvobj, String altCode)
1279   {
1280     if (jv2vobj != null)
1281     {
1282       Object id = jv2vobj.get(jvobj);
1283       if (id != null)
1284       {
1285         return id.toString();
1286       }
1287       // check string ID mappings
1288       if (jvids2vobj != null && jvobj instanceof String)
1289       {
1290         id = jvids2vobj.get(jvobj);
1291       }
1292       if (id != null)
1293       {
1294         return id.toString();
1295       }
1296       // give up and warn that something has gone wrong
1297       warn("Cannot find ID for object in external mapping : " + jvobj);
1298     }
1299     return altCode;
1300   }
1301
1302   /**
1303    * return local jalview object mapped to ID, if it exists
1304    *
1305    * @param idcode
1306    *          (may be null)
1307    * @return null or object bound to idcode
1308    */
1309   private Object retrieveExistingObj(String idcode)
1310   {
1311     if (idcode != null && vobj2jv != null)
1312     {
1313       return vobj2jv.get(idcode);
1314     }
1315     return null;
1316   }
1317
1318   /**
1319    * binding from ID strings from external mapping table to jalview data model
1320    * objects.
1321    */
1322   private Hashtable vobj2jv;
1323
1324   private Sequence createVamsasSequence(String id, SequenceI jds)
1325   {
1326     return createVamsasSequence(true, id, jds, null);
1327   }
1328
1329   private Sequence createVamsasSequence(boolean recurse, String id,
1330           SequenceI jds, SequenceI parentseq)
1331   {
1332     Sequence vamsasSeq = new Sequence();
1333     vamsasSeq.setId(id);
1334     vamsasSeq.setName(jds.getName());
1335     vamsasSeq.setSequence(jds.getSequenceAsString());
1336     vamsasSeq.setDescription(jds.getDescription());
1337     jalview.datamodel.DBRefEntry[] dbrefs = null;
1338     if (jds.getDatasetSequence() != null)
1339     {
1340       vamsasSeq.setDsseqid(seqHash(jds.getDatasetSequence()));
1341       if (jds.getDatasetSequence().getDBRef() != null)
1342       {
1343         dbrefs = jds.getDatasetSequence().getDBRef();
1344       }
1345     }
1346     else
1347     {
1348       vamsasSeq.setDsseqid(id); // so we can tell which sequences really are
1349       // dataset sequences only
1350       dbrefs = jds.getDBRef();
1351     }
1352     if (dbrefs != null)
1353     {
1354       for (int d = 0; d < dbrefs.length; d++)
1355       {
1356         DBRef dbref = new DBRef();
1357         dbref.setSource(dbrefs[d].getSource());
1358         dbref.setVersion(dbrefs[d].getVersion());
1359         dbref.setAccessionId(dbrefs[d].getAccessionId());
1360         if (dbrefs[d].hasMap())
1361         {
1362           Mapping mp = createVamsasMapping(dbrefs[d].getMap(), parentseq,
1363                   jds, recurse);
1364           dbref.setMapping(mp);
1365         }
1366         vamsasSeq.addDBRef(dbref);
1367       }
1368     }
1369     return vamsasSeq;
1370   }
1371
1372   private Mapping createVamsasMapping(jalview.datamodel.Mapping jmp,
1373           SequenceI parentseq, SequenceI jds, boolean recurse)
1374   {
1375     Mapping mp = null;
1376     if (jmp.getMap() != null)
1377     {
1378       mp = new Mapping();
1379
1380       jalview.util.MapList mlst = jmp.getMap();
1381       int r[] = mlst.getFromRanges();
1382       for (int s = 0; s < r.length; s += 2)
1383       {
1384         MapListFrom mfrom = new MapListFrom();
1385         mfrom.setStart(r[s]);
1386         mfrom.setEnd(r[s + 1]);
1387         mp.addMapListFrom(mfrom);
1388       }
1389       r = mlst.getToRanges();
1390       for (int s = 0; s < r.length; s += 2)
1391       {
1392         MapListTo mto = new MapListTo();
1393         mto.setStart(r[s]);
1394         mto.setEnd(r[s + 1]);
1395         mp.addMapListTo(mto);
1396       }
1397       mp.setMapFromUnit(mlst.getFromRatio());
1398       mp.setMapToUnit(mlst.getToRatio());
1399       if (jmp.getTo() != null)
1400       {
1401         MappingChoice mpc = new MappingChoice();
1402         if (recurse
1403                 && (parentseq != jmp.getTo() || parentseq
1404                         .getDatasetSequence() != jmp.getTo()))
1405         {
1406           mpc.setSequence(createVamsasSequence(false, seqHash(jmp.getTo()),
1407                   jmp.getTo(), jds));
1408         }
1409         else
1410         {
1411           String jmpid = "";
1412           SequenceI ps = null;
1413           if (parentseq != jmp.getTo()
1414                   && parentseq.getDatasetSequence() != jmp.getTo())
1415           {
1416             // chaining dbref rather than a handshaking one
1417             jmpid = seqHash(ps = jmp.getTo());
1418           }
1419           else
1420           {
1421             jmpid = seqHash(ps = parentseq);
1422           }
1423           mpc.setDseqFor(jmpid);
1424           if (!seqRefIds.containsKey(mpc.getDseqFor()))
1425           {
1426             jalview.bin.Cache.log.debug("creatign new DseqFor ID");
1427             seqRefIds.put(mpc.getDseqFor(), ps);
1428           }
1429           else
1430           {
1431             jalview.bin.Cache.log.debug("reusing DseqFor ID");
1432           }
1433         }
1434         mp.setMappingChoice(mpc);
1435       }
1436     }
1437     return mp;
1438   }
1439
1440   String SetUserColourScheme(jalview.schemes.ColourSchemeI cs,
1441           Vector userColours, JalviewModelSequence jms)
1442   {
1443     String id = null;
1444     jalview.schemes.UserColourScheme ucs = (jalview.schemes.UserColourScheme) cs;
1445     boolean newucs = false;
1446     if (!userColours.contains(ucs))
1447     {
1448       userColours.add(ucs);
1449       newucs = true;
1450     }
1451     id = "ucs" + userColours.indexOf(ucs);
1452     if (newucs)
1453     {
1454       // actually create the scheme's entry in the XML model
1455       java.awt.Color[] colours = ucs.getColours();
1456       jalview.schemabinding.version2.UserColours uc = new jalview.schemabinding.version2.UserColours();
1457       jalview.schemabinding.version2.UserColourScheme jbucs = new jalview.schemabinding.version2.UserColourScheme();
1458
1459       for (int i = 0; i < colours.length; i++)
1460       {
1461         jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
1462         col.setName(ResidueProperties.aa[i]);
1463         col.setRGB(jalview.util.Format.getHexString(colours[i]));
1464         jbucs.addColour(col);
1465       }
1466       if (ucs.getLowerCaseColours() != null)
1467       {
1468         colours = ucs.getLowerCaseColours();
1469         for (int i = 0; i < colours.length; i++)
1470         {
1471           jalview.schemabinding.version2.Colour col = new jalview.schemabinding.version2.Colour();
1472           col.setName(ResidueProperties.aa[i].toLowerCase());
1473           col.setRGB(jalview.util.Format.getHexString(colours[i]));
1474           jbucs.addColour(col);
1475         }
1476       }
1477
1478       uc.setId(id);
1479       uc.setUserColourScheme(jbucs);
1480       jms.addUserColours(uc);
1481     }
1482
1483     return id;
1484   }
1485
1486   jalview.schemes.UserColourScheme GetUserColourScheme(
1487           JalviewModelSequence jms, String id)
1488   {
1489     UserColours[] uc = jms.getUserColours();
1490     UserColours colours = null;
1491
1492     for (int i = 0; i < uc.length; i++)
1493     {
1494       if (uc[i].getId().equals(id))
1495       {
1496         colours = uc[i];
1497
1498         break;
1499       }
1500     }
1501
1502     java.awt.Color[] newColours = new java.awt.Color[24];
1503
1504     for (int i = 0; i < 24; i++)
1505     {
1506       newColours[i] = new java.awt.Color(Integer.parseInt(colours
1507               .getUserColourScheme().getColour(i).getRGB(), 16));
1508     }
1509
1510     jalview.schemes.UserColourScheme ucs = new jalview.schemes.UserColourScheme(
1511             newColours);
1512
1513     if (colours.getUserColourScheme().getColourCount() > 24)
1514     {
1515       newColours = new java.awt.Color[23];
1516       for (int i = 0; i < 23; i++)
1517       {
1518         newColours[i] = new java.awt.Color(Integer.parseInt(colours
1519                 .getUserColourScheme().getColour(i + 24).getRGB(), 16));
1520       }
1521       ucs.setLowerCaseColours(newColours);
1522     }
1523
1524     return ucs;
1525   }
1526
1527   /**
1528    * contains last error message (if any) encountered by XML loader.
1529    */
1530   String errorMessage = null;
1531
1532   /**
1533    * flag to control whether the Jalview2XML_V1 parser should be deferred to if
1534    * exceptions are raised during project XML parsing
1535    */
1536   public boolean attemptversion1parse = true;
1537
1538   /**
1539    * Load a jalview project archive from a jar file
1540    *
1541    * @param file
1542    *          - HTTP URL or filename
1543    */
1544   public AlignFrame LoadJalviewAlign(final String file)
1545   {
1546
1547     jalview.gui.AlignFrame af = null;
1548
1549     try
1550     {
1551       // UNMARSHALLER SEEMS TO CLOSE JARINPUTSTREAM, MOST ANNOYING
1552       // Workaround is to make sure caller implements the JarInputStreamProvider
1553       // interface
1554       // so we can re-open the jar input stream for each entry.
1555
1556       jarInputStreamProvider jprovider = createjarInputStreamProvider(file);
1557       af = LoadJalviewAlign(jprovider);
1558     } catch (MalformedURLException e)
1559     {
1560       errorMessage = "Invalid URL format for '" + file + "'";
1561       reportErrors();
1562     }
1563     return af;
1564   }
1565
1566   private jarInputStreamProvider createjarInputStreamProvider(
1567           final String file) throws MalformedURLException
1568   {
1569     URL url = null;
1570     errorMessage = null;
1571     uniqueSetSuffix = null;
1572     seqRefIds = null;
1573     viewportsAdded = null;
1574     frefedSequence = null;
1575
1576     if (file.startsWith("http://"))
1577     {
1578       url = new URL(file);
1579     }
1580     final URL _url = url;
1581     return new jarInputStreamProvider()
1582     {
1583
1584       @Override
1585       public JarInputStream getJarInputStream() throws IOException
1586       {
1587         if (_url != null)
1588         {
1589           return new JarInputStream(_url.openStream());
1590         }
1591         else
1592         {
1593           return new JarInputStream(new FileInputStream(file));
1594         }
1595       }
1596
1597       @Override
1598       public String getFilename()
1599       {
1600         return file;
1601       }
1602     };
1603   }
1604
1605   /**
1606    * Recover jalview session from a jalview project archive. Caller may
1607    * initialise uniqueSetSuffix, seqRefIds, viewportsAdded and frefedSequence
1608    * themselves. Any null fields will be initialised with default values,
1609    * non-null fields are left alone.
1610    *
1611    * @param jprovider
1612    * @return
1613    */
1614   public AlignFrame LoadJalviewAlign(final jarInputStreamProvider jprovider)
1615   {
1616     errorMessage = null;
1617     if (uniqueSetSuffix == null)
1618     {
1619       uniqueSetSuffix = System.currentTimeMillis() % 100000 + "";
1620     }
1621     if (seqRefIds == null)
1622     {
1623       seqRefIds = new Hashtable();
1624     }
1625     if (viewportsAdded == null)
1626     {
1627       viewportsAdded = new Hashtable();
1628     }
1629     if (frefedSequence == null)
1630     {
1631       frefedSequence = new Vector();
1632     }
1633
1634     jalview.gui.AlignFrame af = null;
1635     Hashtable gatherToThisFrame = new Hashtable();
1636     final String file = jprovider.getFilename();
1637     try
1638     {
1639       JarInputStream jin = null;
1640       JarEntry jarentry = null;
1641       int entryCount = 1;
1642
1643       do
1644       {
1645         jin = jprovider.getJarInputStream();
1646         for (int i = 0; i < entryCount; i++)
1647         {
1648           jarentry = jin.getNextJarEntry();
1649         }
1650
1651         if (jarentry != null && jarentry.getName().endsWith(".xml"))
1652         {
1653           InputStreamReader in = new InputStreamReader(jin, "UTF-8");
1654           JalviewModel object = new JalviewModel();
1655
1656           Unmarshaller unmar = new Unmarshaller(object);
1657           unmar.setValidation(false);
1658           object = (JalviewModel) unmar.unmarshal(in);
1659           if (true) // !skipViewport(object))
1660           {
1661             af = LoadFromObject(object, file, true, jprovider);
1662             if (af.viewport.gatherViewsHere)
1663             {
1664               gatherToThisFrame.put(af.viewport.getSequenceSetId(), af);
1665             }
1666           }
1667           entryCount++;
1668         }
1669         else if (jarentry != null)
1670         {
1671           // Some other file here.
1672           entryCount++;
1673         }
1674       } while (jarentry != null);
1675       resolveFrefedSequences();
1676     } catch (java.io.FileNotFoundException ex)
1677     {
1678       ex.printStackTrace();
1679       errorMessage = "Couldn't locate Jalview XML file : " + file;
1680       System.err.println("Exception whilst loading jalview XML file : "
1681               + ex + "\n");
1682     } catch (java.net.UnknownHostException ex)
1683     {
1684       ex.printStackTrace();
1685       errorMessage = "Couldn't locate Jalview XML file : " + file;
1686       System.err.println("Exception whilst loading jalview XML file : "
1687               + ex + "\n");
1688     } catch (Exception ex)
1689     {
1690       System.err.println("Parsing as Jalview Version 2 file failed.");
1691       ex.printStackTrace(System.err);
1692       if (attemptversion1parse)
1693       {
1694         // Is Version 1 Jar file?
1695         try
1696         {
1697           af = new Jalview2XML_V1(raiseGUI).LoadJalviewAlign(jprovider);
1698         } catch (Exception ex2)
1699         {
1700           System.err.println("Exception whilst loading as jalviewXMLV1:");
1701           ex2.printStackTrace();
1702           af = null;
1703         }
1704       }
1705       if (Desktop.instance != null)
1706       {
1707         Desktop.instance.stopLoading();
1708       }
1709       if (af != null)
1710       {
1711         System.out.println("Successfully loaded archive file");
1712         return af;
1713       }
1714       ex.printStackTrace();
1715
1716       System.err.println("Exception whilst loading jalview XML file : "
1717               + ex + "\n");
1718     } catch (OutOfMemoryError e)
1719     {
1720       // Don't use the OOM Window here
1721       errorMessage = "Out of memory loading jalview XML file";
1722       System.err.println("Out of memory whilst loading jalview XML file");
1723       e.printStackTrace();
1724     }
1725
1726     if (Desktop.instance != null)
1727     {
1728       Desktop.instance.stopLoading();
1729     }
1730
1731     Enumeration en = gatherToThisFrame.elements();
1732     while (en.hasMoreElements())
1733     {
1734       Desktop.instance.gatherViews((AlignFrame) en.nextElement());
1735     }
1736     if (errorMessage != null)
1737     {
1738       reportErrors();
1739     }
1740     return af;
1741   }
1742
1743   /**
1744    * check errorMessage for a valid error message and raise an error box in the
1745    * GUI or write the current errorMessage to stderr and then clear the error
1746    * state.
1747    */
1748   protected void reportErrors()
1749   {
1750     reportErrors(false);
1751   }
1752
1753   protected void reportErrors(final boolean saving)
1754   {
1755     if (errorMessage != null)
1756     {
1757       final String finalErrorMessage = errorMessage;
1758       if (raiseGUI)
1759       {
1760         javax.swing.SwingUtilities.invokeLater(new Runnable()
1761         {
1762           @Override
1763           public void run()
1764           {
1765             JOptionPane.showInternalMessageDialog(Desktop.desktop,
1766                     finalErrorMessage, "Error "
1767                             + (saving ? "saving" : "loading")
1768                             + " Jalview file", JOptionPane.WARNING_MESSAGE);
1769           }
1770         });
1771       }
1772       else
1773       {
1774         System.err.println("Problem loading Jalview file: " + errorMessage);
1775       }
1776     }
1777     errorMessage = null;
1778   }
1779
1780   Hashtable alreadyLoadedPDB;
1781
1782   /**
1783    * when set, local views will be updated from view stored in JalviewXML
1784    * Currently (28th Sep 2008) things will go horribly wrong in vamsas document
1785    * sync if this is set to true.
1786    */
1787   private final boolean updateLocalViews = false;
1788
1789   String loadPDBFile(jarInputStreamProvider jprovider, String pdbId)
1790   {
1791     if (alreadyLoadedPDB == null)
1792       alreadyLoadedPDB = new Hashtable();
1793
1794     if (alreadyLoadedPDB.containsKey(pdbId))
1795       return alreadyLoadedPDB.get(pdbId).toString();
1796
1797     try
1798     {
1799       JarInputStream jin = jprovider.getJarInputStream();
1800       /*
1801        * if (jprovider.startsWith("http://")) { jin = new JarInputStream(new
1802        * URL(jprovider).openStream()); } else { jin = new JarInputStream(new
1803        * FileInputStream(jprovider)); }
1804        */
1805
1806       JarEntry entry = null;
1807       do
1808       {
1809         entry = jin.getNextJarEntry();
1810       } while (entry != null && !entry.getName().equals(pdbId));
1811       if (entry != null)
1812       {
1813         BufferedReader in = new BufferedReader(new InputStreamReader(jin));
1814         File outFile = File.createTempFile("jalview_pdb", ".txt");
1815         outFile.deleteOnExit();
1816         PrintWriter out = new PrintWriter(new FileOutputStream(outFile));
1817         String data;
1818
1819         while ((data = in.readLine()) != null)
1820         {
1821           out.println(data);
1822         }
1823         try
1824         {
1825           out.flush();
1826         } catch (Exception foo)
1827         {
1828         }
1829         ;
1830         out.close();
1831         String t=outFile.getAbsolutePath();
1832         alreadyLoadedPDB.put(pdbId, t);
1833         return t;
1834       }
1835       else
1836       {
1837         warn("Couldn't find PDB file entry in Jalview Jar for " + pdbId);
1838       }
1839     } catch (Exception ex)
1840     {
1841       ex.printStackTrace();
1842     }
1843
1844     return null;
1845   }
1846
1847   private class JvAnnotRow
1848   {
1849     public JvAnnotRow(int i, AlignmentAnnotation jaa)
1850     {
1851       order = i;
1852       template = jaa;
1853     }
1854
1855     /**
1856      * persisted version of annotation row from which to take vis properties
1857      */
1858     public jalview.datamodel.AlignmentAnnotation template;
1859
1860     /**
1861      * original position of the annotation row in the alignment
1862      */
1863     public int order;
1864   }
1865
1866   /**
1867    * Load alignment frame from jalview XML DOM object
1868    *
1869    * @param object
1870    *          DOM
1871    * @param file
1872    *          filename source string
1873    * @param loadTreesAndStructures
1874    *          when false only create Viewport
1875    * @param jprovider
1876    *          data source provider
1877    * @return alignment frame created from view stored in DOM
1878    */
1879   AlignFrame LoadFromObject(JalviewModel object, String file,
1880           boolean loadTreesAndStructures, jarInputStreamProvider jprovider)
1881   {
1882     SequenceSet vamsasSet = object.getVamsasModel().getSequenceSet(0);
1883     Sequence[] vamsasSeq = vamsasSet.getSequence();
1884
1885     JalviewModelSequence jms = object.getJalviewModelSequence();
1886
1887     Viewport view = jms.getViewport(0);
1888     // ////////////////////////////////
1889     // LOAD SEQUENCES
1890
1891     Vector hiddenSeqs = null;
1892     jalview.datamodel.Sequence jseq;
1893
1894     ArrayList tmpseqs = new ArrayList();
1895
1896     boolean multipleView = false;
1897
1898     JSeq[] JSEQ = object.getJalviewModelSequence().getJSeq();
1899     int vi = 0; // counter in vamsasSeq array
1900     for (int i = 0; i < JSEQ.length; i++)
1901     {
1902       String seqId = JSEQ[i].getId();
1903
1904       if (seqRefIds.get(seqId) != null)
1905       {
1906         tmpseqs.add(seqRefIds.get(seqId));
1907         multipleView = true;
1908       }
1909       else
1910       {
1911         jseq = new jalview.datamodel.Sequence(vamsasSeq[vi].getName(),
1912                 vamsasSeq[vi].getSequence());
1913         jseq.setDescription(vamsasSeq[vi].getDescription());
1914         jseq.setStart(JSEQ[i].getStart());
1915         jseq.setEnd(JSEQ[i].getEnd());
1916         jseq.setVamsasId(uniqueSetSuffix + seqId);
1917         seqRefIds.put(vamsasSeq[vi].getId(), jseq);
1918         tmpseqs.add(jseq);
1919         vi++;
1920       }
1921
1922       if (JSEQ[i].getHidden())
1923       {
1924         if (hiddenSeqs == null)
1925         {
1926           hiddenSeqs = new Vector();
1927         }
1928
1929         hiddenSeqs.addElement(seqRefIds
1930                 .get(seqId));
1931       }
1932
1933     }
1934
1935     // /
1936     // Create the alignment object from the sequence set
1937     // ///////////////////////////////
1938     jalview.datamodel.Sequence[] orderedSeqs = new jalview.datamodel.Sequence[tmpseqs
1939             .size()];
1940
1941     tmpseqs.toArray(orderedSeqs);
1942
1943     jalview.datamodel.Alignment al = new jalview.datamodel.Alignment(
1944             orderedSeqs);
1945
1946     // / Add the alignment properties
1947     for (int i = 0; i < vamsasSet.getSequenceSetPropertiesCount(); i++)
1948     {
1949       SequenceSetProperties ssp = vamsasSet.getSequenceSetProperties(i);
1950       al.setProperty(ssp.getKey(), ssp.getValue());
1951     }
1952
1953     // /
1954     // SequenceFeatures are added to the DatasetSequence,
1955     // so we must create or recover the dataset before loading features
1956     // ///////////////////////////////
1957     if (vamsasSet.getDatasetId() == null || vamsasSet.getDatasetId() == "")
1958     {
1959       // older jalview projects do not have a dataset id.
1960       al.setDataset(null);
1961     }
1962     else
1963     {
1964       recoverDatasetFor(vamsasSet, al);
1965     }
1966     // ///////////////////////////////
1967
1968     Hashtable pdbloaded = new Hashtable();
1969     if (!multipleView)
1970     {
1971       // load sequence features, database references and any associated PDB
1972       // structures for the alignment
1973       for (int i = 0; i < vamsasSeq.length; i++)
1974       {
1975         if (JSEQ[i].getFeaturesCount() > 0)
1976         {
1977           Features[] features = JSEQ[i].getFeatures();
1978           for (int f = 0; f < features.length; f++)
1979           {
1980             jalview.datamodel.SequenceFeature sf = new jalview.datamodel.SequenceFeature(
1981                     features[f].getType(), features[f].getDescription(),
1982                     features[f].getStatus(), features[f].getBegin(),
1983                     features[f].getEnd(), features[f].getFeatureGroup());
1984
1985             sf.setScore(features[f].getScore());
1986             for (int od = 0; od < features[f].getOtherDataCount(); od++)
1987             {
1988               OtherData keyValue = features[f].getOtherData(od);
1989               if (keyValue.getKey().startsWith("LINK"))
1990               {
1991                 sf.addLink(keyValue.getValue());
1992               }
1993               else
1994               {
1995                 sf.setValue(keyValue.getKey(), keyValue.getValue());
1996               }
1997
1998             }
1999
2000             al.getSequenceAt(i).getDatasetSequence().addSequenceFeature(sf);
2001           }
2002         }
2003         if (vamsasSeq[i].getDBRefCount() > 0)
2004         {
2005           addDBRefs(al.getSequenceAt(i).getDatasetSequence(), vamsasSeq[i]);
2006         }
2007         if (JSEQ[i].getPdbidsCount() > 0)
2008         {
2009           Pdbids[] ids = JSEQ[i].getPdbids();
2010           for (int p = 0; p < ids.length; p++)
2011           {
2012             jalview.datamodel.PDBEntry entry = new jalview.datamodel.PDBEntry();
2013             entry.setId(ids[p].getId());
2014             entry.setType(ids[p].getType());
2015             if (ids[p].getFile() != null)
2016             {
2017               if (!pdbloaded.containsKey(ids[p].getFile()))
2018               {
2019                 entry.setFile(loadPDBFile(jprovider, ids[p].getId()));
2020               }
2021               else
2022               {
2023                 entry.setFile(pdbloaded.get(ids[p].getId()).toString());
2024               }
2025             }
2026
2027             al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
2028           }
2029         }
2030       }
2031     } // end !multipleview
2032
2033     // ///////////////////////////////
2034     // LOAD SEQUENCE MAPPINGS
2035
2036     if (vamsasSet.getAlcodonFrameCount() > 0)
2037     {
2038       // TODO Potentially this should only be done once for all views of an
2039       // alignment
2040       AlcodonFrame[] alc = vamsasSet.getAlcodonFrame();
2041       for (int i = 0; i < alc.length; i++)
2042       {
2043         jalview.datamodel.AlignedCodonFrame cf = new jalview.datamodel.AlignedCodonFrame(
2044                 alc[i].getAlcodonCount());
2045         if (alc[i].getAlcodonCount() > 0)
2046         {
2047           Alcodon[] alcods = alc[i].getAlcodon();
2048           for (int p = 0; p < cf.codons.length; p++)
2049           {
2050             if (alcods[p].hasPos1() && alcods[p].hasPos2()
2051                     && alcods[p].hasPos3())
2052             {
2053               // translated codons require three valid positions
2054               cf.codons[p] = new int[3];
2055               cf.codons[p][0] = (int) alcods[p].getPos1();
2056               cf.codons[p][1] = (int) alcods[p].getPos2();
2057               cf.codons[p][2] = (int) alcods[p].getPos3();
2058             }
2059             else
2060             {
2061               cf.codons[p] = null;
2062             }
2063           }
2064         }
2065         if (alc[i].getAlcodMapCount() > 0)
2066         {
2067           AlcodMap[] maps = alc[i].getAlcodMap();
2068           for (int m = 0; m < maps.length; m++)
2069           {
2070             SequenceI dnaseq = (SequenceI) seqRefIds
2071                     .get(maps[m].getDnasq());
2072             // Load Mapping
2073             jalview.datamodel.Mapping mapping = null;
2074             // attach to dna sequence reference.
2075             if (maps[m].getMapping() != null)
2076             {
2077               mapping = addMapping(maps[m].getMapping());
2078             }
2079             if (dnaseq != null)
2080             {
2081               cf.addMap(dnaseq, mapping.getTo(), mapping.getMap());
2082             }
2083             else
2084             {
2085               // defer to later
2086               frefedSequence.add(new Object[]
2087               { maps[m].getDnasq(), cf, mapping });
2088             }
2089           }
2090         }
2091         al.addCodonFrame(cf);
2092       }
2093
2094     }
2095
2096     // ////////////////////////////////
2097     // LOAD ANNOTATIONS
2098     ArrayList<JvAnnotRow> autoAlan = new ArrayList<JvAnnotRow>();
2099     /**
2100      * store any annotations which forward reference a group's ID
2101      */
2102     Hashtable<String, ArrayList<jalview.datamodel.AlignmentAnnotation>> groupAnnotRefs = new Hashtable<String, ArrayList<jalview.datamodel.AlignmentAnnotation>>();
2103
2104     if (vamsasSet.getAnnotationCount() > 0)
2105     {
2106       Annotation[] an = vamsasSet.getAnnotation();
2107
2108       for (int i = 0; i < an.length; i++)
2109       {
2110         /**
2111          * test if annotation is automatically calculated for this view only
2112          */
2113         boolean autoForView = false;
2114         if (an[i].getLabel().equals("Quality")
2115                 || an[i].getLabel().equals("Conservation")
2116                 || an[i].getLabel().equals("Consensus"))
2117         {
2118           // Kludge for pre 2.5 projects which lacked the autocalculated flag
2119           autoForView = true;
2120           if (!an[i].hasAutoCalculated())
2121           {
2122             an[i].setAutoCalculated(true);
2123           }
2124         }
2125         if (autoForView
2126                 || (an[i].hasAutoCalculated() && an[i].isAutoCalculated()))
2127         {
2128           // remove ID - we don't recover annotation from other views for
2129           // view-specific annotation
2130           an[i].setId(null);
2131         }
2132
2133         // set visiblity for other annotation in this view
2134         if (an[i].getId() != null
2135                 && annotationIds.containsKey(an[i].getId()))
2136         {
2137           jalview.datamodel.AlignmentAnnotation jda = (jalview.datamodel.AlignmentAnnotation) annotationIds
2138                   .get(an[i].getId());
2139           // in principle Visible should always be true for annotation displayed
2140           // in multiple views
2141           if (an[i].hasVisible())
2142             jda.visible = an[i].getVisible();
2143
2144           al.addAnnotation(jda);
2145
2146           continue;
2147         }
2148         // Construct new annotation from model.
2149         AnnotationElement[] ae = an[i].getAnnotationElement();
2150         jalview.datamodel.Annotation[] anot = null;
2151
2152         if (!an[i].getScoreOnly())
2153         {
2154           anot = new jalview.datamodel.Annotation[al.getWidth()];
2155           for (int aa = 0; aa < ae.length && aa < anot.length; aa++)
2156           {
2157             if (ae[aa].getPosition() >= anot.length)
2158               continue;
2159
2160             anot[ae[aa].getPosition()] = new jalview.datamodel.Annotation(
2161
2162             ae[aa].getDisplayCharacter(), ae[aa].getDescription(),
2163                     (ae[aa].getSecondaryStructure() == null || ae[aa]
2164                             .getSecondaryStructure().length() == 0) ? ' '
2165                             : ae[aa].getSecondaryStructure().charAt(0),
2166                     ae[aa].getValue()
2167
2168             );
2169             // JBPNote: Consider verifying dataflow for IO of secondary
2170             // structure annotation read from Stockholm files
2171             // this was added to try to ensure that
2172             // if (anot[ae[aa].getPosition()].secondaryStructure>' ')
2173             // {
2174             // anot[ae[aa].getPosition()].displayCharacter = "";
2175             // }
2176             anot[ae[aa].getPosition()].colour = new java.awt.Color(
2177                     ae[aa].getColour());
2178           }
2179         }
2180         jalview.datamodel.AlignmentAnnotation jaa = null;
2181
2182         if (an[i].getGraph())
2183         {
2184           float llim = 0, hlim = 0;
2185           // if (autoForView || an[i].isAutoCalculated()) {
2186           // hlim=11f;
2187           // }
2188           jaa = new jalview.datamodel.AlignmentAnnotation(an[i].getLabel(),
2189                   an[i].getDescription(), anot, llim, hlim,
2190                   an[i].getGraphType());
2191
2192           jaa.graphGroup = an[i].getGraphGroup();
2193
2194           if (an[i].getThresholdLine() != null)
2195           {
2196             jaa.setThreshold(new jalview.datamodel.GraphLine(an[i]
2197                     .getThresholdLine().getValue(), an[i]
2198                     .getThresholdLine().getLabel(), new java.awt.Color(
2199                     an[i].getThresholdLine().getColour())));
2200
2201           }
2202           if (autoForView || an[i].isAutoCalculated())
2203           {
2204             // Hardwire the symbol display line to ensure that labels for
2205             // histograms are displayed
2206             jaa.hasText = true;
2207           }
2208         }
2209         else
2210         {
2211           jaa = new jalview.datamodel.AlignmentAnnotation(an[i].getLabel(),
2212                   an[i].getDescription(), anot);
2213         }
2214         if (autoForView)
2215         {
2216           // register new annotation
2217           if (an[i].getId() != null)
2218           {
2219             annotationIds.put(an[i].getId(), jaa);
2220             jaa.annotationId = an[i].getId();
2221           }
2222           // recover sequence association
2223           if (an[i].getSequenceRef() != null)
2224           {
2225             if (al.findName(an[i].getSequenceRef()) != null)
2226             {
2227               jaa.createSequenceMapping(
2228                       al.findName(an[i].getSequenceRef()), 1, true);
2229               al.findName(an[i].getSequenceRef()).addAlignmentAnnotation(
2230                       jaa);
2231             }
2232           }
2233         }
2234         // and make a note of any group association
2235         if (an[i].getGroupRef() != null && an[i].getGroupRef().length() > 0)
2236         {
2237           ArrayList<jalview.datamodel.AlignmentAnnotation> aal = groupAnnotRefs
2238                   .get(an[i].getGroupRef());
2239           if (aal == null)
2240           {
2241             aal = new ArrayList<jalview.datamodel.AlignmentAnnotation>();
2242             groupAnnotRefs.put(an[i].getGroupRef(), aal);
2243           }
2244           aal.add(jaa);
2245         }
2246
2247         if (an[i].hasScore())
2248         {
2249           jaa.setScore(an[i].getScore());
2250         }
2251         if (an[i].hasVisible())
2252           jaa.visible = an[i].getVisible();
2253
2254         if (an[i].hasCentreColLabels())
2255           jaa.centreColLabels = an[i].getCentreColLabels();
2256
2257         if (an[i].hasScaleColLabels())
2258         {
2259           jaa.scaleColLabel = an[i].getScaleColLabels();
2260         }
2261         if (an[i].hasAutoCalculated() && an[i].isAutoCalculated())
2262         {
2263           // newer files have an 'autoCalculated' flag and store calculation
2264           // state in viewport properties
2265           jaa.autoCalculated = true; // means annotation will be marked for
2266           // update at end of load.
2267         }
2268         if (an[i].hasGraphHeight())
2269         {
2270           jaa.graphHeight = an[i].getGraphHeight();
2271         }
2272         if (an[i].hasBelowAlignment())
2273         {
2274           jaa.belowAlignment=an[i].isBelowAlignment();
2275         }
2276         jaa.setCalcId(an[i].getCalcId());
2277
2278         if (jaa.autoCalculated)
2279         {
2280           autoAlan.add(new JvAnnotRow(i, jaa));
2281         }
2282         else
2283         // if (!autoForView)
2284         {
2285           // add autocalculated group annotation and any user created annotation
2286           // for the view
2287           al.addAnnotation(jaa);
2288         }
2289       }
2290     }
2291
2292     // ///////////////////////
2293     // LOAD GROUPS
2294     // Create alignment markup and styles for this view
2295     if (jms.getJGroupCount() > 0)
2296     {
2297       JGroup[] groups = jms.getJGroup();
2298
2299       for (int i = 0; i < groups.length; i++)
2300       {
2301         ColourSchemeI cs = null;
2302
2303         if (groups[i].getColour() != null)
2304         {
2305           if (groups[i].getColour().startsWith("ucs"))
2306           {
2307             cs = GetUserColourScheme(jms, groups[i].getColour());
2308           }
2309           else
2310           {
2311             cs = ColourSchemeProperty.getColour(al, groups[i].getColour());
2312           }
2313
2314           if (cs != null)
2315           {
2316             cs.setThreshold(groups[i].getPidThreshold(), true);
2317           }
2318         }
2319
2320         Vector seqs = new Vector();
2321
2322         for (int s = 0; s < groups[i].getSeqCount(); s++)
2323         {
2324           String seqId = groups[i].getSeq(s) + "";
2325           jalview.datamodel.SequenceI ts = (jalview.datamodel.SequenceI) seqRefIds
2326                   .get(seqId);
2327
2328           if (ts != null)
2329           {
2330             seqs.addElement(ts);
2331           }
2332         }
2333
2334         if (seqs.size() < 1)
2335         {
2336           continue;
2337         }
2338
2339         jalview.datamodel.SequenceGroup sg = new jalview.datamodel.SequenceGroup(
2340                 seqs, groups[i].getName(), cs, groups[i].getDisplayBoxes(),
2341                 groups[i].getDisplayText(), groups[i].getColourText(),
2342                 groups[i].getStart(), groups[i].getEnd());
2343
2344         sg.setOutlineColour(new java.awt.Color(groups[i].getOutlineColour()));
2345
2346         sg.textColour = new java.awt.Color(groups[i].getTextCol1());
2347         sg.textColour2 = new java.awt.Color(groups[i].getTextCol2());
2348         sg.setShowNonconserved(groups[i].hasShowUnconserved() ? groups[i]
2349                 .isShowUnconserved() : false);
2350         sg.thresholdTextColour = groups[i].getTextColThreshold();
2351         if (groups[i].hasShowConsensusHistogram())
2352         {
2353           sg.setShowConsensusHistogram(groups[i].isShowConsensusHistogram());
2354         }
2355         ;
2356         if (groups[i].hasShowSequenceLogo())
2357         {
2358           sg.setshowSequenceLogo(groups[i].isShowSequenceLogo());
2359         }
2360         if (groups[i].hasIgnoreGapsinConsensus())
2361         {
2362           sg.setIgnoreGapsConsensus(groups[i].getIgnoreGapsinConsensus());
2363         }
2364         if (groups[i].getConsThreshold() != 0)
2365         {
2366           jalview.analysis.Conservation c = new jalview.analysis.Conservation(
2367                   "All", ResidueProperties.propHash, 3,
2368                   sg.getSequences(null), 0, sg.getWidth() - 1);
2369           c.calculate();
2370           c.verdict(false, 25);
2371           sg.cs.setConservation(c);
2372         }
2373
2374         if (groups[i].getId() != null && groupAnnotRefs.size() > 0)
2375         {
2376           // re-instate unique group/annotation row reference
2377           ArrayList<jalview.datamodel.AlignmentAnnotation> jaal = groupAnnotRefs
2378                   .get(groups[i].getId());
2379           if (jaal != null)
2380           {
2381             for (jalview.datamodel.AlignmentAnnotation jaa : jaal)
2382             {
2383               jaa.groupRef = sg;
2384               if (jaa.autoCalculated)
2385               {
2386                 // match up and try to set group autocalc alignment row for this
2387                 // annotation
2388                 if (jaa.label.startsWith("Consensus for "))
2389                 {
2390                   sg.setConsensus(jaa);
2391                 }
2392                 // match up and try to set group autocalc alignment row for this
2393                 // annotation
2394                 if (jaa.label.startsWith("Conservation for "))
2395                 {
2396                   sg.setConservationRow(jaa);
2397                 }
2398               }
2399             }
2400           }
2401         }
2402         al.addGroup(sg);
2403
2404       }
2405     }
2406
2407     // ///////////////////////////////
2408     // LOAD VIEWPORT
2409
2410     // If we just load in the same jar file again, the sequenceSetId
2411     // will be the same, and we end up with multiple references
2412     // to the same sequenceSet. We must modify this id on load
2413     // so that each load of the file gives a unique id
2414     String uniqueSeqSetId = view.getSequenceSetId() + uniqueSetSuffix;
2415     String viewId = (view.getId() == null ? null : view.getId()
2416             + uniqueSetSuffix);
2417     AlignFrame af = null;
2418     AlignViewport av = null;
2419     // now check to see if we really need to create a new viewport.
2420     if (multipleView && viewportsAdded.size() == 0)
2421     {
2422       // We recovered an alignment for which a viewport already exists.
2423       // TODO: fix up any settings necessary for overlaying stored state onto
2424       // state recovered from another document. (may not be necessary).
2425       // we may need a binding from a viewport in memory to one recovered from
2426       // XML.
2427       // and then recover its containing af to allow the settings to be applied.
2428       // TODO: fix for vamsas demo
2429       System.err
2430               .println("About to recover a viewport for existing alignment: Sequence set ID is "
2431                       + uniqueSeqSetId);
2432       Object seqsetobj = retrieveExistingObj(uniqueSeqSetId);
2433       if (seqsetobj != null)
2434       {
2435         if (seqsetobj instanceof String)
2436         {
2437           uniqueSeqSetId = (String) seqsetobj;
2438           System.err
2439                   .println("Recovered extant sequence set ID mapping for ID : New Sequence set ID is "
2440                           + uniqueSeqSetId);
2441         }
2442         else
2443         {
2444           System.err
2445                   .println("Warning : Collision between sequence set ID string and existing jalview object mapping.");
2446         }
2447
2448       }
2449     }
2450     AlignmentPanel ap = null;
2451     boolean isnewview = true;
2452     if (viewId != null)
2453     {
2454       // Check to see if this alignment already has a view id == viewId
2455       jalview.gui.AlignmentPanel views[] = Desktop
2456               .getAlignmentPanels(uniqueSeqSetId);
2457       if (views != null && views.length > 0)
2458       {
2459         for (int v = 0; v < views.length; v++)
2460         {
2461           if (views[v].av.getViewId().equalsIgnoreCase(viewId))
2462           {
2463             // recover the existing alignpanel, alignframe, viewport
2464             af = views[v].alignFrame;
2465             av = views[v].av;
2466             ap = views[v];
2467             // TODO: could even skip resetting view settings if we don't want to
2468             // change the local settings from other jalview processes
2469             isnewview = false;
2470           }
2471         }
2472       }
2473     }
2474
2475     if (isnewview)
2476     {
2477       af = loadViewport(file, JSEQ, hiddenSeqs, al, jms, view,
2478               uniqueSeqSetId, viewId, autoAlan);
2479       av = af.viewport;
2480       ap = af.alignPanel;
2481     }
2482     // LOAD TREES
2483     // /////////////////////////////////////
2484     if (loadTreesAndStructures && jms.getTreeCount() > 0)
2485     {
2486       try
2487       {
2488         for (int t = 0; t < jms.getTreeCount(); t++)
2489         {
2490
2491           Tree tree = jms.getTree(t);
2492
2493           TreePanel tp = (TreePanel) retrieveExistingObj(tree.getId());
2494           if (tp == null)
2495           {
2496             tp = af.ShowNewickTree(
2497                     new jalview.io.NewickFile(tree.getNewick()),
2498                     tree.getTitle(), tree.getWidth(), tree.getHeight(),
2499                     tree.getXpos(), tree.getYpos());
2500             if (tree.getId() != null)
2501             {
2502               // perhaps bind the tree id to something ?
2503             }
2504           }
2505           else
2506           {
2507             // update local tree attributes ?
2508             // TODO: should check if tp has been manipulated by user - if so its
2509             // settings shouldn't be modified
2510             tp.setTitle(tree.getTitle());
2511             tp.setBounds(new Rectangle(tree.getXpos(), tree.getYpos(), tree
2512                     .getWidth(), tree.getHeight()));
2513             tp.av = av; // af.viewport; // TODO: verify 'associate with all
2514             // views'
2515             // works still
2516             tp.treeCanvas.av = av; // af.viewport;
2517             tp.treeCanvas.ap = ap; // af.alignPanel;
2518
2519           }
2520           if (tp == null)
2521           {
2522             warn("There was a problem recovering stored Newick tree: \n"
2523                     + tree.getNewick());
2524             continue;
2525           }
2526
2527           tp.fitToWindow.setState(tree.getFitToWindow());
2528           tp.fitToWindow_actionPerformed(null);
2529
2530           if (tree.getFontName() != null)
2531           {
2532             tp.setTreeFont(new java.awt.Font(tree.getFontName(), tree
2533                     .getFontStyle(), tree.getFontSize()));
2534           }
2535           else
2536           {
2537             tp.setTreeFont(new java.awt.Font(view.getFontName(), view
2538                     .getFontStyle(), tree.getFontSize()));
2539           }
2540
2541           tp.showPlaceholders(tree.getMarkUnlinked());
2542           tp.showBootstrap(tree.getShowBootstrap());
2543           tp.showDistances(tree.getShowDistances());
2544
2545           tp.treeCanvas.threshold = tree.getThreshold();
2546
2547           if (tree.getCurrentTree())
2548           {
2549             af.viewport.setCurrentTree(tp.getTree());
2550           }
2551         }
2552
2553       } catch (Exception ex)
2554       {
2555         ex.printStackTrace();
2556       }
2557     }
2558
2559     // //LOAD STRUCTURES
2560     if (loadTreesAndStructures)
2561     {
2562       // run through all PDB ids on the alignment, and collect mappings between
2563       // jmol view ids and all sequences referring to it
2564       Hashtable<String, Object[]> jmolViewIds = new Hashtable();
2565
2566       for (int i = 0; i < JSEQ.length; i++)
2567       {
2568         if (JSEQ[i].getPdbidsCount() > 0)
2569         {
2570           Pdbids[] ids = JSEQ[i].getPdbids();
2571           for (int p = 0; p < ids.length; p++)
2572           {
2573             for (int s = 0; s < ids[p].getStructureStateCount(); s++)
2574             {
2575               // check to see if we haven't already created this structure view
2576               String sviewid = (ids[p].getStructureState(s).getViewId() == null) ? null
2577                       : ids[p].getStructureState(s).getViewId()
2578                               + uniqueSetSuffix;
2579               jalview.datamodel.PDBEntry jpdb = new jalview.datamodel.PDBEntry();
2580               // Originally : ids[p].getFile()
2581               // : TODO: verify external PDB file recovery still works in normal
2582               // jalview project load
2583               jpdb.setFile(loadPDBFile(jprovider, ids[p].getId()));
2584               jpdb.setId(ids[p].getId());
2585
2586               int x = ids[p].getStructureState(s).getXpos();
2587               int y = ids[p].getStructureState(s).getYpos();
2588               int width = ids[p].getStructureState(s).getWidth();
2589               int height = ids[p].getStructureState(s).getHeight();
2590
2591               // Probably don't need to do this anymore...
2592               // Desktop.desktop.getComponentAt(x, y);
2593               // TODO: NOW: check that this recovers the PDB file correctly.
2594               String pdbFile = loadPDBFile(jprovider, ids[p].getId());
2595               jalview.datamodel.SequenceI seq = (jalview.datamodel.SequenceI) seqRefIds
2596                       .get(JSEQ[i].getId() + "");
2597               if (sviewid == null)
2598               {
2599                 sviewid = "_jalview_pre2_4_" + x + "," + y + "," + width
2600                         + "," + height;
2601               }
2602               if (!jmolViewIds.containsKey(sviewid))
2603               {
2604                 jmolViewIds.put(sviewid, new Object[]
2605                 { new int[]
2606                 { x, y, width, height }, "",
2607                     new Hashtable<String, Object[]>(), new boolean[]
2608                     { false, false, true } });
2609                 // Legacy pre-2.7 conversion JAL-823 :
2610                 // do not assume any view has to be linked for colour by
2611                 // sequence
2612               }
2613
2614               // assemble String[] { pdb files }, String[] { id for each
2615               // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
2616               // seqs_file 2}, boolean[] {
2617               // linkAlignPanel,superposeWithAlignpanel}} from hash
2618               Object[] jmoldat = jmolViewIds.get(sviewid);
2619               ((boolean[]) jmoldat[3])[0] |= ids[p].getStructureState(s)
2620                       .hasAlignwithAlignPanel() ? ids[p].getStructureState(
2621                       s).getAlignwithAlignPanel() : false;
2622               // never colour by linked panel if not specified
2623               ((boolean[]) jmoldat[3])[1] |= ids[p].getStructureState(s)
2624                       .hasColourwithAlignPanel() ? ids[p]
2625                       .getStructureState(s).getColourwithAlignPanel()
2626                       : false;
2627               // default for pre-2.7 projects is that Jmol colouring is enabled
2628               ((boolean[]) jmoldat[3])[2] &= ids[p].getStructureState(s)
2629                       .hasColourByJmol() ? ids[p].getStructureState(s)
2630                       .getColourByJmol() : true;
2631
2632               if (((String) jmoldat[1]).length() < ids[p]
2633                       .getStructureState(s).getContent().length())
2634               {
2635                 {
2636                   jmoldat[1] = ids[p].getStructureState(s).getContent();
2637                 }
2638               }
2639               if (ids[p].getFile() != null)
2640               {
2641                 File mapkey=new File(ids[p].getFile());
2642                 Object[] seqstrmaps = (Object[]) ((Hashtable) jmoldat[2])
2643                         .get(mapkey);
2644                 if (seqstrmaps == null)
2645                 {
2646                   ((Hashtable) jmoldat[2]).put(
2647                           mapkey,
2648                           seqstrmaps = new Object[]
2649                           { pdbFile, ids[p].getId(), new Vector(),
2650                               new Vector() });
2651                 }
2652                 if (!((Vector) seqstrmaps[2]).contains(seq))
2653                 {
2654                   ((Vector) seqstrmaps[2]).addElement(seq);
2655                   // ((Vector)seqstrmaps[3]).addElement(n) :
2656                   // in principle, chains
2657                   // should be stored here : do we need to
2658                   // TODO: store and recover seq/pdb_id :
2659                   // chain mappings
2660                 }
2661               }
2662               else
2663               {
2664                 errorMessage = ("The Jmol views in this project were imported\nfrom an older version of Jalview.\nPlease review the sequence colour associations\nin the Colour by section of the Jmol View menu.\n\nIn the case of problems, see note at\nhttp://issues.jalview.org/browse/JAL-747");
2665                 warn(errorMessage);
2666               }
2667             }
2668           }
2669         }
2670       }
2671       {
2672
2673         // Instantiate the associated Jmol views
2674         for (Entry<String, Object[]> entry : jmolViewIds.entrySet())
2675         {
2676           String sviewid = entry.getKey();
2677           Object[] svattrib = entry.getValue();
2678           int[] geom = (int[]) svattrib[0];
2679           String state = (String) svattrib[1];
2680           Hashtable<File, Object[]> oldFiles = (Hashtable<File, Object[]>) svattrib[2];
2681           final boolean useinJmolsuperpos = ((boolean[]) svattrib[3])[0], usetoColourbyseq = ((boolean[]) svattrib[3])[1], jmolColouring = ((boolean[]) svattrib[3])[2];
2682           int x = geom[0], y = geom[1], width = geom[2], height = geom[3];
2683           // collate the pdbfile -> sequence mappings from this view
2684           Vector<String> pdbfilenames = new Vector<String>();
2685           Vector<SequenceI[]> seqmaps = new Vector<SequenceI[]>();
2686           Vector<String> pdbids = new Vector<String>();
2687
2688           // Search to see if we've already created this Jmol view
2689           AppJmol comp = null;
2690           JInternalFrame[] frames = null;
2691           do
2692           {
2693             try
2694             {
2695               frames = Desktop.desktop.getAllFrames();
2696             } catch (ArrayIndexOutOfBoundsException e)
2697             {
2698               // occasional No such child exceptions are thrown here...
2699               frames = null;
2700               try
2701               {
2702                 Thread.sleep(10);
2703               } catch (Exception f)
2704               {
2705               }
2706               ;
2707             }
2708           } while (frames == null);
2709           // search for any Jmol windows already open from other
2710           // alignment views that exactly match the stored structure state
2711           for (int f = 0; comp == null && f < frames.length; f++)
2712           {
2713             if (frames[f] instanceof AppJmol)
2714             {
2715               if (sviewid != null
2716                       && ((AppJmol) frames[f]).getViewId().equals(sviewid))
2717               {
2718                 // post jalview 2.4 schema includes structure view id
2719                 comp = (AppJmol) frames[f];
2720               }
2721               else if (frames[f].getX() == x && frames[f].getY() == y
2722                       && frames[f].getHeight() == height
2723                       && frames[f].getWidth() == width)
2724               {
2725                 comp = (AppJmol) frames[f];
2726               }
2727             }
2728           }
2729
2730           if (comp == null)
2731           {
2732             // create a new Jmol window.
2733             // First parse the Jmol state to translate filenames loaded into the
2734             // view, and record the order in which files are shown in the Jmol
2735             // view, so we can add the sequence mappings in same order.
2736             StringBuffer newFileLoc = null;
2737             int cp = 0, ncp, ecp;
2738             while ((ncp = state.indexOf("load ", cp)) > -1)
2739             {
2740               if (newFileLoc == null)
2741               {
2742                 newFileLoc = new StringBuffer();
2743               }
2744               do {
2745                 // look for next filename in load statement
2746               newFileLoc.append(state.substring(cp,
2747                       ncp = (state.indexOf("\"", ncp + 1) + 1)));
2748               String oldfilenam = state.substring(ncp,
2749                       ecp = state.indexOf("\"", ncp));
2750               // recover the new mapping data for this old filename
2751               // have to normalize filename - since Jmol and jalview do filename
2752               // translation differently.
2753               Object[] filedat = oldFiles.get(new File(oldfilenam));
2754               newFileLoc.append(Platform.escapeString((String) filedat[0]));
2755               pdbfilenames.addElement((String) filedat[0]);
2756               pdbids.addElement((String) filedat[1]);
2757               seqmaps.addElement(((Vector<SequenceI>) filedat[2])
2758                       .toArray(new SequenceI[0]));
2759               newFileLoc.append("\"");
2760               cp = ecp + 1; // advance beyond last \" and set cursor so we can
2761                             // look for next file statement.
2762               } while ((ncp=state.indexOf("/*file*/",cp))>-1);
2763             }
2764             if (cp > 0)
2765             {
2766               // just append rest of state
2767               newFileLoc.append(state.substring(cp));
2768             }
2769             else
2770             {
2771               System.err
2772                       .print("Ignoring incomplete Jmol state for PDB ids: ");
2773               newFileLoc = new StringBuffer(state);
2774               newFileLoc.append("; load append ");
2775               for (File id : oldFiles.keySet())
2776               {
2777                 // add this and any other pdb files that should be present in
2778                 // the viewer
2779                 Object[] filedat = oldFiles.get(id);
2780                 String nfilename;
2781                 newFileLoc.append(((String) filedat[0]));
2782                 pdbfilenames.addElement((String) filedat[0]);
2783                 pdbids.addElement((String) filedat[1]);
2784                 seqmaps.addElement(((Vector<SequenceI>) filedat[2])
2785                         .toArray(new SequenceI[0]));
2786                 newFileLoc.append(" \"");
2787                 newFileLoc.append((String) filedat[0]);
2788                 newFileLoc.append("\"");
2789
2790               }
2791               newFileLoc.append(";");
2792             }
2793
2794             if (newFileLoc != null)
2795             {
2796               int histbug = newFileLoc.indexOf("history = ");
2797               histbug += 10;
2798               int diff = histbug == -1 ? -1 : newFileLoc.indexOf(";",
2799                       histbug);
2800               String val = (diff == -1) ? null : newFileLoc.substring(
2801                       histbug, diff);
2802               if (val != null && val.length() >= 4)
2803               {
2804                 if (val.contains("e"))
2805                 {
2806                   if (val.trim().equals("true"))
2807                   {
2808                     val = "1";
2809                   }
2810                   else
2811                   {
2812                     val = "0";
2813                   }
2814                   newFileLoc.replace(histbug, diff, val);
2815                 }
2816               }
2817               // TODO: assemble String[] { pdb files }, String[] { id for each
2818               // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
2819               // seqs_file 2}} from hash
2820               final String[] pdbf = pdbfilenames
2821                       .toArray(new String[pdbfilenames.size()]), id = pdbids
2822                       .toArray(new String[pdbids.size()]);
2823               final SequenceI[][] sq = seqmaps
2824                       .toArray(new SequenceI[seqmaps.size()][]);
2825               final String fileloc = newFileLoc.toString(), vid = sviewid;
2826               final AlignFrame alf = af;
2827               final java.awt.Rectangle rect = new java.awt.Rectangle(x, y,
2828                       width, height);
2829               try
2830               {
2831                 javax.swing.SwingUtilities.invokeAndWait(new Runnable()
2832                 {
2833                   @Override
2834                   public void run()
2835                   {
2836                     AppJmol sview = null;
2837                     try
2838                     {
2839                       sview = new AppJmol(pdbf, id, sq, alf.alignPanel,
2840                               useinJmolsuperpos, usetoColourbyseq,
2841                               jmolColouring, fileloc, rect, vid);
2842                     } catch (OutOfMemoryError ex)
2843                     {
2844                       new OOMWarning("restoring structure view for PDB id "
2845                               + id, (OutOfMemoryError) ex.getCause());
2846                       if (sview != null && sview.isVisible())
2847                       {
2848                         sview.closeViewer();
2849                         sview.setVisible(false);
2850                         sview.dispose();
2851                       }
2852                     }
2853                   }
2854                 });
2855               } catch (InvocationTargetException ex)
2856               {
2857                 warn("Unexpected error when opening Jmol view.", ex);
2858
2859               } catch (InterruptedException e)
2860               {
2861                 // e.printStackTrace();
2862               }
2863             }
2864
2865           }
2866           else
2867           // if (comp != null)
2868           {
2869             // NOTE: if the jalview project is part of a shared session then
2870             // view synchronization should/could be done here.
2871
2872             // add mapping for sequences in this view to an already open Jmol
2873             // instance
2874             for (File id : oldFiles.keySet())
2875             {
2876               // add this and any other pdb files that should be present in the
2877               // viewer
2878               Object[] filedat = oldFiles.get(id);
2879               String pdbFile = (String) filedat[0];
2880               SequenceI[] seq = ((Vector<SequenceI>) filedat[2])
2881                       .toArray(new SequenceI[0]);
2882               comp.jmb.ssm.setMapping(seq, null, pdbFile,
2883                       jalview.io.AppletFormatAdapter.FILE);
2884               comp.jmb.addSequenceForStructFile(pdbFile, seq);
2885             }
2886             // and add the AlignmentPanel's reference to the Jmol view
2887             comp.addAlignmentPanel(ap);
2888             if (useinJmolsuperpos)
2889             {
2890               comp.useAlignmentPanelForSuperposition(ap);
2891             }
2892             else
2893             {
2894               comp.excludeAlignmentPanelForSuperposition(ap);
2895             }
2896             if (usetoColourbyseq)
2897             {
2898               comp.useAlignmentPanelForColourbyseq(ap,
2899                       !jmolColouring);
2900             }
2901             else
2902             {
2903               comp.excludeAlignmentPanelForColourbyseq(ap);
2904             }
2905           }
2906         }
2907       }
2908     }
2909     // and finally return.
2910     return af;
2911   }
2912
2913   AlignFrame loadViewport(String file, JSeq[] JSEQ, Vector hiddenSeqs,
2914           Alignment al, JalviewModelSequence jms, Viewport view,
2915           String uniqueSeqSetId, String viewId,
2916           ArrayList<JvAnnotRow> autoAlan)
2917   {
2918     AlignFrame af = null;
2919     af = new AlignFrame(al, view.getWidth(), view.getHeight(),
2920             uniqueSeqSetId, viewId);
2921
2922     af.setFileName(file, "Jalview");
2923
2924     for (int i = 0; i < JSEQ.length; i++)
2925     {
2926       af.viewport.setSequenceColour(af.viewport.getAlignment().getSequenceAt(i),
2927               new java.awt.Color(JSEQ[i].getColour()));
2928     }
2929
2930     af.viewport.gatherViewsHere = view.getGatheredViews();
2931
2932     if (view.getSequenceSetId() != null)
2933     {
2934       jalview.gui.AlignViewport av = (jalview.gui.AlignViewport) viewportsAdded
2935               .get(uniqueSeqSetId);
2936
2937       af.viewport.setSequenceSetId(uniqueSeqSetId);
2938       if (av != null)
2939       {
2940         // propagate shared settings to this new view
2941         af.viewport.historyList = av.historyList;
2942         af.viewport.redoList = av.redoList;
2943       }
2944       else
2945       {
2946         viewportsAdded.put(uniqueSeqSetId, af.viewport);
2947       }
2948       // TODO: check if this method can be called repeatedly without
2949       // side-effects if alignpanel already registered.
2950       PaintRefresher.Register(af.alignPanel, uniqueSeqSetId);
2951     }
2952     // apply Hidden regions to view.
2953     if (hiddenSeqs != null)
2954     {
2955       for (int s = 0; s < JSEQ.length; s++)
2956       {
2957         jalview.datamodel.SequenceGroup hidden = new jalview.datamodel.SequenceGroup();
2958
2959         for (int r = 0; r < JSEQ[s].getHiddenSequencesCount(); r++)
2960         {
2961           hidden.addSequence(
2962                   al.getSequenceAt(JSEQ[s].getHiddenSequences(r)), false);
2963         }
2964         af.viewport.hideRepSequences(al.getSequenceAt(s), hidden);
2965       }
2966
2967       jalview.datamodel.SequenceI[] hseqs = new jalview.datamodel.SequenceI[hiddenSeqs
2968               .size()];
2969
2970       for (int s = 0; s < hiddenSeqs.size(); s++)
2971       {
2972         hseqs[s] = (jalview.datamodel.SequenceI) hiddenSeqs.elementAt(s);
2973       }
2974
2975       af.viewport.hideSequence(hseqs);
2976
2977     }
2978     // recover view properties and display parameters
2979     if (view.getViewName() != null)
2980     {
2981       af.viewport.viewName = view.getViewName();
2982       af.setInitialTabVisible();
2983     }
2984     af.setBounds(view.getXpos(), view.getYpos(), view.getWidth(),
2985             view.getHeight());
2986
2987     af.viewport.setShowAnnotation(view.getShowAnnotation());
2988     af.viewport.setAbovePIDThreshold(view.getPidSelected());
2989
2990     af.viewport.setColourText(view.getShowColourText());
2991
2992     af.viewport.setConservationSelected(view.getConservationSelected());
2993     af.viewport.setShowJVSuffix(view.getShowFullId());
2994     af.viewport.rightAlignIds = view.getRightAlignIds();
2995     af.viewport.setFont(new java.awt.Font(view.getFontName(), view
2996             .getFontStyle(), view.getFontSize()));
2997     af.alignPanel.fontChanged();
2998     af.viewport.setRenderGaps(view.getRenderGaps());
2999     af.viewport.setWrapAlignment(view.getWrapAlignment());
3000     af.alignPanel.setWrapAlignment(view.getWrapAlignment());
3001     af.viewport.setShowAnnotation(view.getShowAnnotation());
3002     af.alignPanel.setAnnotationVisible(view.getShowAnnotation());
3003
3004     af.viewport.setShowBoxes(view.getShowBoxes());
3005
3006     af.viewport.setShowText(view.getShowText());
3007
3008     af.viewport.textColour = new java.awt.Color(view.getTextCol1());
3009     af.viewport.textColour2 = new java.awt.Color(view.getTextCol2());
3010     af.viewport.thresholdTextColour = view.getTextColThreshold();
3011     af.viewport.setShowUnconserved(view.hasShowUnconserved() ? view
3012             .isShowUnconserved() : false);
3013     af.viewport.setStartRes(view.getStartRes());
3014     af.viewport.setStartSeq(view.getStartSeq());
3015
3016     ColourSchemeI cs = null;
3017     // apply colourschemes
3018     if (view.getBgColour() != null)
3019     {
3020       if (view.getBgColour().startsWith("ucs"))
3021       {
3022         cs = GetUserColourScheme(jms, view.getBgColour());
3023       }
3024       else if (view.getBgColour().startsWith("Annotation"))
3025       {
3026         // int find annotation
3027         if (af.viewport.getAlignment().getAlignmentAnnotation() != null)
3028         {
3029           for (int i = 0; i < af.viewport.getAlignment()
3030                   .getAlignmentAnnotation().length; i++)
3031           {
3032             if (af.viewport.getAlignment().getAlignmentAnnotation()[i].label
3033                     .equals(view.getAnnotationColours().getAnnotation()))
3034             {
3035               if (af.viewport.getAlignment().getAlignmentAnnotation()[i]
3036                       .getThreshold() == null)
3037               {
3038                 af.viewport.getAlignment().getAlignmentAnnotation()[i]
3039                         .setThreshold(new jalview.datamodel.GraphLine(view
3040                                 .getAnnotationColours().getThreshold(),
3041                                 "Threshold", java.awt.Color.black)
3042
3043                         );
3044               }
3045
3046               if (view.getAnnotationColours().getColourScheme()
3047                       .equals("None"))
3048               {
3049                 cs = new AnnotationColourGradient(
3050                         af.viewport.getAlignment().getAlignmentAnnotation()[i],
3051                         new java.awt.Color(view.getAnnotationColours()
3052                                 .getMinColour()), new java.awt.Color(view
3053                                 .getAnnotationColours().getMaxColour()),
3054                         view.getAnnotationColours().getAboveThreshold());
3055               }
3056               else if (view.getAnnotationColours().getColourScheme()
3057                       .startsWith("ucs"))
3058               {
3059                 cs = new AnnotationColourGradient(
3060                         af.viewport.getAlignment().getAlignmentAnnotation()[i],
3061                         GetUserColourScheme(jms, view
3062                                 .getAnnotationColours().getColourScheme()),
3063                         view.getAnnotationColours().getAboveThreshold());
3064               }
3065               else
3066               {
3067                 cs = new AnnotationColourGradient(
3068                         af.viewport.getAlignment().getAlignmentAnnotation()[i],
3069                         ColourSchemeProperty.getColour(al, view
3070                                 .getAnnotationColours().getColourScheme()),
3071                         view.getAnnotationColours().getAboveThreshold());
3072               }
3073
3074               // Also use these settings for all the groups
3075               if (al.getGroups() != null)
3076               {
3077                 for (int g = 0; g < al.getGroups().size(); g++)
3078                 {
3079                   jalview.datamodel.SequenceGroup sg = (jalview.datamodel.SequenceGroup) al
3080                           .getGroups().elementAt(g);
3081
3082                   if (sg.cs == null)
3083                   {
3084                     continue;
3085                   }
3086
3087                   /*
3088                    * if
3089                    * (view.getAnnotationColours().getColourScheme().equals("None"
3090                    * )) { sg.cs = new AnnotationColourGradient(
3091                    * af.viewport.getAlignment().getAlignmentAnnotation()[i], new
3092                    * java.awt.Color(view.getAnnotationColours().
3093                    * getMinColour()), new
3094                    * java.awt.Color(view.getAnnotationColours().
3095                    * getMaxColour()),
3096                    * view.getAnnotationColours().getAboveThreshold()); } else
3097                    */
3098                   {
3099                     sg.cs = new AnnotationColourGradient(
3100                             af.viewport.getAlignment().getAlignmentAnnotation()[i],
3101                             sg.cs, view.getAnnotationColours()
3102                                     .getAboveThreshold());
3103                   }
3104
3105                 }
3106               }
3107
3108               break;
3109             }
3110
3111           }
3112         }
3113       }
3114       else
3115       {
3116         cs = ColourSchemeProperty.getColour(al, view.getBgColour());
3117       }
3118
3119       if (cs != null)
3120       {
3121         cs.setThreshold(view.getPidThreshold(), true);
3122         cs.setConsensus(af.viewport.getSequenceConsensusHash());
3123       }
3124     }
3125
3126     af.viewport.setGlobalColourScheme(cs);
3127     af.viewport.setColourAppliesToAllGroups(false);
3128
3129     if (view.getConservationSelected() && cs != null)
3130     {
3131       cs.setConservationInc(view.getConsThreshold());
3132     }
3133
3134     af.changeColour(cs);
3135
3136     af.viewport.setColourAppliesToAllGroups(true);
3137
3138     if (view.getShowSequenceFeatures())
3139     {
3140       af.viewport.showSequenceFeatures = true;
3141     }
3142     if (view.hasCentreColumnLabels())
3143     {
3144       af.viewport.setCentreColumnLabels(view.getCentreColumnLabels());
3145     }
3146     if (view.hasIgnoreGapsinConsensus())
3147     {
3148       af.viewport.setIgnoreGapsConsensus(view
3149               .getIgnoreGapsinConsensus(), null);
3150     }
3151     if (view.hasFollowHighlight())
3152     {
3153       af.viewport.followHighlight = view.getFollowHighlight();
3154     }
3155     if (view.hasFollowSelection())
3156     {
3157       af.viewport.followSelection = view.getFollowSelection();
3158     }
3159     if (view.hasShowConsensusHistogram())
3160     {
3161       af.viewport.setShowConsensusHistogram(view
3162               .getShowConsensusHistogram());
3163     }
3164     else
3165     {
3166       af.viewport.setShowConsensusHistogram(true);
3167     }
3168     if (view.hasShowSequenceLogo())
3169     {
3170       af.viewport.setShowSequenceLogo(view.getShowSequenceLogo());
3171     }
3172     else
3173     {
3174       af.viewport.setShowSequenceLogo(false);
3175     }
3176     if (view.hasShowDbRefTooltip())
3177     {
3178       af.viewport.setShowDbRefs(view.getShowDbRefTooltip());
3179     }
3180     if (view.hasShowNPfeatureTooltip())
3181     {
3182       af.viewport.setShowNpFeats(view.hasShowNPfeatureTooltip());
3183     }
3184     if (view.hasShowGroupConsensus())
3185     {
3186       af.viewport.setShowGroupConsensus(view.getShowGroupConsensus());
3187     }
3188     else
3189     {
3190       af.viewport.setShowGroupConsensus(false);
3191     }
3192     if (view.hasShowGroupConservation())
3193     {
3194       af.viewport.setShowGroupConservation(view.getShowGroupConservation());
3195     }
3196     else
3197     {
3198       af.viewport.setShowGroupConservation(false);
3199     }
3200
3201     // recover featre settings
3202     if (jms.getFeatureSettings() != null)
3203     {
3204       af.viewport.featuresDisplayed = new Hashtable();
3205       String[] renderOrder = new String[jms.getFeatureSettings()
3206               .getSettingCount()];
3207       for (int fs = 0; fs < jms.getFeatureSettings().getSettingCount(); fs++)
3208       {
3209         Setting setting = jms.getFeatureSettings().getSetting(fs);
3210         if (setting.hasMincolour())
3211         {
3212           GraduatedColor gc = setting.hasMin() ? new GraduatedColor(
3213                   new java.awt.Color(setting.getMincolour()),
3214                   new java.awt.Color(setting.getColour()),
3215                   setting.getMin(), setting.getMax()) : new GraduatedColor(
3216                   new java.awt.Color(setting.getMincolour()),
3217                   new java.awt.Color(setting.getColour()), 0, 1);
3218           if (setting.hasThreshold())
3219           {
3220             gc.setThresh(setting.getThreshold());
3221             gc.setThreshType(setting.getThreshstate());
3222           }
3223           gc.setAutoScaled(true); // default
3224           if (setting.hasAutoScale())
3225           {
3226             gc.setAutoScaled(setting.getAutoScale());
3227           }
3228           if (setting.hasColourByLabel())
3229           {
3230             gc.setColourByLabel(setting.getColourByLabel());
3231           }
3232           // and put in the feature colour table.
3233           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setColour(
3234                   setting.getType(), gc);
3235         }
3236         else
3237         {
3238           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setColour(
3239                   setting.getType(),
3240                   new java.awt.Color(setting.getColour()));
3241         }
3242         renderOrder[fs] = setting.getType();
3243         if (setting.hasOrder())
3244           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setOrder(
3245                   setting.getType(), setting.getOrder());
3246         else
3247           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setOrder(
3248                   setting.getType(),
3249                   fs / jms.getFeatureSettings().getSettingCount());
3250         if (setting.getDisplay())
3251         {
3252           af.viewport.featuresDisplayed.put(setting.getType(), new Integer(
3253                   setting.getColour()));
3254         }
3255       }
3256       af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().renderOrder = renderOrder;
3257       Hashtable fgtable;
3258       af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().featureGroups = fgtable = new Hashtable();
3259       for (int gs = 0; gs < jms.getFeatureSettings().getGroupCount(); gs++)
3260       {
3261         Group grp = jms.getFeatureSettings().getGroup(gs);
3262         fgtable.put(grp.getName(), new Boolean(grp.getDisplay()));
3263       }
3264     }
3265
3266     if (view.getHiddenColumnsCount() > 0)
3267     {
3268       for (int c = 0; c < view.getHiddenColumnsCount(); c++)
3269       {
3270         af.viewport.hideColumns(view.getHiddenColumns(c).getStart(), view
3271                 .getHiddenColumns(c).getEnd() // +1
3272                 );
3273       }
3274     }
3275
3276     af.setMenusFromViewport(af.viewport);
3277     // TODO: we don't need to do this if the viewport is aready visible.
3278     Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(),
3279             view.getHeight());
3280     af.alignPanel.updateAnnotation(false); // recompute any autoannotation
3281     reorderAutoannotation(af, al, autoAlan);
3282     return af;
3283   }
3284
3285   private void reorderAutoannotation(AlignFrame af, Alignment al,
3286           ArrayList<JvAnnotRow> autoAlan)
3287   {
3288     // copy over visualization settings for autocalculated annotation in the
3289     // view
3290     if (al.getAlignmentAnnotation() != null)
3291     {
3292       /**
3293        * Kludge for magic autoannotation names (see JAL-811)
3294        */
3295       String[] magicNames = new String[]
3296       { "Consensus", "Quality", "Conservation" };
3297       JvAnnotRow nullAnnot = new JvAnnotRow(-1, null);
3298       Hashtable<String, JvAnnotRow> visan = new Hashtable<String, JvAnnotRow>();
3299       for (String nm : magicNames)
3300       {
3301         visan.put(nm, nullAnnot);
3302       }
3303       for (JvAnnotRow auan : autoAlan)
3304       {
3305         visan.put(auan.template.label, auan);
3306       }
3307       int hSize = al.getAlignmentAnnotation().length;
3308       ArrayList<JvAnnotRow> reorder = new ArrayList<JvAnnotRow>();
3309       for (int h = 0; h < hSize; h++)
3310       {
3311         jalview.datamodel.AlignmentAnnotation jalan = al
3312                 .getAlignmentAnnotation()[h];
3313         if (jalan.autoCalculated)
3314         {
3315           JvAnnotRow valan = visan.get(jalan.label);
3316           if (valan != null)
3317           {
3318             // delete the auto calculated row from the alignment
3319             al.deleteAnnotation(al.getAlignmentAnnotation()[h], false);
3320             hSize--;
3321             h--;
3322             if (valan != nullAnnot)
3323             {
3324               if (jalan != valan.template)
3325               {
3326                 // newly created autoannotation row instance
3327                 // so keep a reference to the visible annotation row
3328                 // and copy over all relevant attributes
3329                 if (valan.template.graphHeight >= 0)
3330
3331                 {
3332                   jalan.graphHeight = valan.template.graphHeight;
3333                 }
3334                 jalan.visible = valan.template.visible;
3335               }
3336               reorder.add(new JvAnnotRow(valan.order, jalan));
3337             }
3338           }
3339         }
3340       }
3341       int s = 0, srt[] = new int[reorder.size()];
3342       JvAnnotRow[] rws = new JvAnnotRow[reorder.size()];
3343       for (JvAnnotRow jvar : reorder)
3344       {
3345         rws[s] = jvar;
3346         srt[s++] = jvar.order;
3347       }
3348       reorder.clear();
3349       jalview.util.QuickSort.sort(srt, rws);
3350       // and re-insert the annotation at its correct position
3351       for (JvAnnotRow jvar : rws)
3352       {
3353         al.addAnnotation(jvar.template, jvar.order);
3354       }
3355       af.alignPanel.adjustAnnotationHeight();
3356     }
3357   }
3358
3359   Hashtable skipList = null;
3360
3361   /**
3362    * TODO remove this method
3363    *
3364    * @param view
3365    * @return AlignFrame bound to sequenceSetId from view, if one exists. private
3366    *         AlignFrame getSkippedFrame(Viewport view) { if (skipList==null) {
3367    *         throw new Error("Implementation Error. No skipList defined for this
3368    *         Jalview2XML instance."); } return (AlignFrame)
3369    *         skipList.get(view.getSequenceSetId()); }
3370    */
3371
3372   /**
3373    * Check if the Jalview view contained in object should be skipped or not.
3374    *
3375    * @param object
3376    * @return true if view's sequenceSetId is a key in skipList
3377    */
3378   private boolean skipViewport(JalviewModel object)
3379   {
3380     if (skipList == null)
3381     {
3382       return false;
3383     }
3384     String id;
3385     if (skipList.containsKey(id = object.getJalviewModelSequence()
3386             .getViewport()[0].getSequenceSetId()))
3387     {
3388       if (Cache.log != null && Cache.log.isDebugEnabled())
3389       {
3390         Cache.log.debug("Skipping seuqence set id " + id);
3391       }
3392       return true;
3393     }
3394     return false;
3395   }
3396
3397   public void AddToSkipList(AlignFrame af)
3398   {
3399     if (skipList == null)
3400     {
3401       skipList = new Hashtable();
3402     }
3403     skipList.put(af.getViewport().getSequenceSetId(), af);
3404   }
3405
3406   public void clearSkipList()
3407   {
3408     if (skipList != null)
3409     {
3410       skipList.clear();
3411       skipList = null;
3412     }
3413   }
3414
3415   private void recoverDatasetFor(SequenceSet vamsasSet, Alignment al)
3416   {
3417     jalview.datamodel.Alignment ds = getDatasetFor(vamsasSet.getDatasetId());
3418     Vector dseqs = null;
3419     if (ds == null)
3420     {
3421       // create a list of new dataset sequences
3422       dseqs = new Vector();
3423     }
3424     for (int i = 0, iSize = vamsasSet.getSequenceCount(); i < iSize; i++)
3425     {
3426       Sequence vamsasSeq = vamsasSet.getSequence(i);
3427       ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs);
3428     }
3429     // create a new dataset
3430     if (ds == null)
3431     {
3432       SequenceI[] dsseqs = new SequenceI[dseqs.size()];
3433       dseqs.copyInto(dsseqs);
3434       ds = new jalview.datamodel.Alignment(dsseqs);
3435       debug("Created new dataset " + vamsasSet.getDatasetId()
3436               + " for alignment " + System.identityHashCode(al));
3437       addDatasetRef(vamsasSet.getDatasetId(), ds);
3438     }
3439     // set the dataset for the newly imported alignment.
3440     if (al.getDataset() == null)
3441     {
3442       al.setDataset(ds);
3443     }
3444   }
3445
3446   /**
3447    *
3448    * @param vamsasSeq
3449    *          sequence definition to create/merge dataset sequence for
3450    * @param ds
3451    *          dataset alignment
3452    * @param dseqs
3453    *          vector to add new dataset sequence to
3454    */
3455   private void ensureJalviewDatasetSequence(Sequence vamsasSeq,
3456           AlignmentI ds, Vector dseqs)
3457   {
3458     // JBP TODO: Check this is called for AlCodonFrames to support recovery of
3459     // xRef Codon Maps
3460     jalview.datamodel.Sequence sq = (jalview.datamodel.Sequence) seqRefIds
3461             .get(vamsasSeq.getId());
3462     jalview.datamodel.SequenceI dsq = null;
3463     if (sq != null && sq.getDatasetSequence() != null)
3464     {
3465       dsq = sq.getDatasetSequence();
3466     }
3467
3468     String sqid = vamsasSeq.getDsseqid();
3469     if (dsq == null)
3470     {
3471       // need to create or add a new dataset sequence reference to this sequence
3472       if (sqid != null)
3473       {
3474         dsq = (jalview.datamodel.SequenceI) seqRefIds.get(sqid);
3475       }
3476       // check again
3477       if (dsq == null)
3478       {
3479         // make a new dataset sequence
3480         dsq = sq.createDatasetSequence();
3481         if (sqid == null)
3482         {
3483           // make up a new dataset reference for this sequence
3484           sqid = seqHash(dsq);
3485         }
3486         dsq.setVamsasId(uniqueSetSuffix + sqid);
3487         seqRefIds.put(sqid, dsq);
3488         if (ds == null)
3489         {
3490           if (dseqs != null)
3491           {
3492             dseqs.addElement(dsq);
3493           }
3494         }
3495         else
3496         {
3497           ds.addSequence(dsq);
3498         }
3499       }
3500       else
3501       {
3502         if (sq != dsq)
3503         { // make this dataset sequence sq's dataset sequence
3504           sq.setDatasetSequence(dsq);
3505         }
3506       }
3507     }
3508     // TODO: refactor this as a merge dataset sequence function
3509     // now check that sq (the dataset sequence) sequence really is the union of
3510     // all references to it
3511     // boolean pre = sq.getStart() < dsq.getStart();
3512     // boolean post = sq.getEnd() > dsq.getEnd();
3513     // if (pre || post)
3514     if (sq != dsq)
3515     {
3516       StringBuffer sb = new StringBuffer();
3517       String newres = jalview.analysis.AlignSeq.extractGaps(
3518               jalview.util.Comparison.GapChars, sq.getSequenceAsString());
3519       if (!newres.equalsIgnoreCase(dsq.getSequenceAsString())
3520               && newres.length() > dsq.getLength())
3521       {
3522         // Update with the longer sequence.
3523         synchronized (dsq)
3524         {
3525           /*
3526            * if (pre) { sb.insert(0, newres .substring(0, dsq.getStart() -
3527            * sq.getStart())); dsq.setStart(sq.getStart()); } if (post) {
3528            * sb.append(newres.substring(newres.length() - sq.getEnd() -
3529            * dsq.getEnd())); dsq.setEnd(sq.getEnd()); }
3530            */
3531           dsq.setSequence(sb.toString());
3532         }
3533         // TODO: merges will never happen if we 'know' we have the real dataset
3534         // sequence - this should be detected when id==dssid
3535         System.err.println("DEBUG Notice:  Merged dataset sequence"); // ("
3536         // + (pre ? "prepended" : "") + " "
3537         // + (post ? "appended" : ""));
3538       }
3539     }
3540   }
3541
3542   java.util.Hashtable datasetIds = null;
3543
3544   java.util.IdentityHashMap dataset2Ids = null;
3545
3546   private Alignment getDatasetFor(String datasetId)
3547   {
3548     if (datasetIds == null)
3549     {
3550       datasetIds = new Hashtable();
3551       return null;
3552     }
3553     if (datasetIds.containsKey(datasetId))
3554     {
3555       return (Alignment) datasetIds.get(datasetId);
3556     }
3557     return null;
3558   }
3559
3560   private void addDatasetRef(String datasetId, Alignment dataset)
3561   {
3562     if (datasetIds == null)
3563     {
3564       datasetIds = new Hashtable();
3565     }
3566     datasetIds.put(datasetId, dataset);
3567   }
3568
3569   /**
3570    * make a new dataset ID for this jalview dataset alignment
3571    *
3572    * @param dataset
3573    * @return
3574    */
3575   private String getDatasetIdRef(jalview.datamodel.Alignment dataset)
3576   {
3577     if (dataset.getDataset() != null)
3578     {
3579       warn("Serious issue!  Dataset Object passed to getDatasetIdRef is not a Jalview DATASET alignment...");
3580     }
3581     String datasetId = makeHashCode(dataset, null);
3582     if (datasetId == null)
3583     {
3584       // make a new datasetId and record it
3585       if (dataset2Ids == null)
3586       {
3587         dataset2Ids = new IdentityHashMap();
3588       }
3589       else
3590       {
3591         datasetId = (String) dataset2Ids.get(dataset);
3592       }
3593       if (datasetId == null)
3594       {
3595         datasetId = "ds" + dataset2Ids.size() + 1;
3596         dataset2Ids.put(dataset, datasetId);
3597       }
3598     }
3599     return datasetId;
3600   }
3601
3602   private void addDBRefs(SequenceI datasetSequence, Sequence sequence)
3603   {
3604     for (int d = 0; d < sequence.getDBRefCount(); d++)
3605     {
3606       DBRef dr = sequence.getDBRef(d);
3607       jalview.datamodel.DBRefEntry entry = new jalview.datamodel.DBRefEntry(
3608               sequence.getDBRef(d).getSource(), sequence.getDBRef(d)
3609                       .getVersion(), sequence.getDBRef(d).getAccessionId());
3610       if (dr.getMapping() != null)
3611       {
3612         entry.setMap(addMapping(dr.getMapping()));
3613       }
3614       datasetSequence.addDBRef(entry);
3615     }
3616   }
3617
3618   private jalview.datamodel.Mapping addMapping(Mapping m)
3619   {
3620     SequenceI dsto = null;
3621     // Mapping m = dr.getMapping();
3622     int fr[] = new int[m.getMapListFromCount() * 2];
3623     Enumeration f = m.enumerateMapListFrom();
3624     for (int _i = 0; f.hasMoreElements(); _i += 2)
3625     {
3626       MapListFrom mf = (MapListFrom) f.nextElement();
3627       fr[_i] = mf.getStart();
3628       fr[_i + 1] = mf.getEnd();
3629     }
3630     int fto[] = new int[m.getMapListToCount() * 2];
3631     f = m.enumerateMapListTo();
3632     for (int _i = 0; f.hasMoreElements(); _i += 2)
3633     {
3634       MapListTo mf = (MapListTo) f.nextElement();
3635       fto[_i] = mf.getStart();
3636       fto[_i + 1] = mf.getEnd();
3637     }
3638     jalview.datamodel.Mapping jmap = new jalview.datamodel.Mapping(dsto,
3639             fr, fto, (int) m.getMapFromUnit(), (int) m.getMapToUnit());
3640     if (m.getMappingChoice() != null)
3641     {
3642       MappingChoice mc = m.getMappingChoice();
3643       if (mc.getDseqFor() != null)
3644       {
3645         String dsfor = "" + mc.getDseqFor();
3646         if (seqRefIds.containsKey(dsfor))
3647         {
3648           /**
3649            * recover from hash
3650            */
3651           jmap.setTo((SequenceI) seqRefIds.get(dsfor));
3652         }
3653         else
3654         {
3655           frefedSequence.add(new Object[]
3656           { dsfor, jmap });
3657         }
3658       }
3659       else
3660       {
3661         /**
3662          * local sequence definition
3663          */
3664         Sequence ms = mc.getSequence();
3665         jalview.datamodel.Sequence djs = null;
3666         String sqid = ms.getDsseqid();
3667         if (sqid != null && sqid.length() > 0)
3668         {
3669           /*
3670            * recover dataset sequence
3671            */
3672           djs = (jalview.datamodel.Sequence) seqRefIds.get(sqid);
3673         }
3674         else
3675         {
3676           System.err
3677                   .println("Warning - making up dataset sequence id for DbRef sequence map reference");
3678           sqid = ((Object) ms).toString(); // make up a new hascode for
3679           // undefined dataset sequence hash
3680           // (unlikely to happen)
3681         }
3682
3683         if (djs == null)
3684         {
3685           /**
3686            * make a new dataset sequence and add it to refIds hash
3687            */
3688           djs = new jalview.datamodel.Sequence(ms.getName(),
3689                   ms.getSequence());
3690           djs.setStart(jmap.getMap().getToLowest());
3691           djs.setEnd(jmap.getMap().getToHighest());
3692           djs.setVamsasId(uniqueSetSuffix + sqid);
3693           jmap.setTo(djs);
3694           seqRefIds.put(sqid, djs);
3695
3696         }
3697         jalview.bin.Cache.log.debug("about to recurse on addDBRefs.");
3698         addDBRefs(djs, ms);
3699
3700       }
3701     }
3702     return (jmap);
3703
3704   }
3705
3706   public jalview.gui.AlignmentPanel copyAlignPanel(AlignmentPanel ap,
3707           boolean keepSeqRefs)
3708   {
3709     initSeqRefs();
3710     jalview.schemabinding.version2.JalviewModel jm = SaveState(ap, null,
3711             null);
3712
3713     if (!keepSeqRefs)
3714     {
3715       clearSeqRefs();
3716       jm.getJalviewModelSequence().getViewport(0).setSequenceSetId(null);
3717     }
3718     else
3719     {
3720       uniqueSetSuffix = "";
3721       jm.getJalviewModelSequence().getViewport(0).setId(null); // we don't
3722       // overwrite the
3723       // view we just
3724       // copied
3725     }
3726     if (this.frefedSequence == null)
3727     {
3728       frefedSequence = new Vector();
3729     }
3730
3731     viewportsAdded = new Hashtable();
3732
3733     AlignFrame af = LoadFromObject(jm, null, false, null);
3734     af.alignPanels.clear();
3735     af.closeMenuItem_actionPerformed(true);
3736
3737     /*
3738      * if(ap.av.getAlignment().getAlignmentAnnotation()!=null) { for(int i=0;
3739      * i<ap.av.getAlignment().getAlignmentAnnotation().length; i++) {
3740      * if(!ap.av.getAlignment().getAlignmentAnnotation()[i].autoCalculated) {
3741      * af.alignPanel.av.getAlignment().getAlignmentAnnotation()[i] =
3742      * ap.av.getAlignment().getAlignmentAnnotation()[i]; } } }
3743      */
3744
3745     return af.alignPanel;
3746   }
3747
3748   /**
3749    * flag indicating if hashtables should be cleared on finalization TODO this
3750    * flag may not be necessary
3751    */
3752   private final boolean _cleartables = true;
3753
3754   private Hashtable jvids2vobj;
3755
3756   /*
3757    * (non-Javadoc)
3758    *
3759    * @see java.lang.Object#finalize()
3760    */
3761   @Override
3762   protected void finalize() throws Throwable
3763   {
3764     // really make sure we have no buried refs left.
3765     if (_cleartables)
3766     {
3767       clearSeqRefs();
3768     }
3769     this.seqRefIds = null;
3770     this.seqsToIds = null;
3771     super.finalize();
3772   }
3773
3774   private void warn(String msg)
3775   {
3776     warn(msg, null);
3777   }
3778
3779   private void warn(String msg, Exception e)
3780   {
3781     if (Cache.log != null)
3782     {
3783       if (e != null)
3784       {
3785         Cache.log.warn(msg, e);
3786       }
3787       else
3788       {
3789         Cache.log.warn(msg);
3790       }
3791     }
3792     else
3793     {
3794       System.err.println("Warning: " + msg);
3795       if (e != null)
3796       {
3797         e.printStackTrace();
3798       }
3799     }
3800   }
3801
3802   private void debug(String string)
3803   {
3804     debug(string, null);
3805   }
3806
3807   private void debug(String msg, Exception e)
3808   {
3809     if (Cache.log != null)
3810     {
3811       if (e != null)
3812       {
3813         Cache.log.debug(msg, e);
3814       }
3815       else
3816       {
3817         Cache.log.debug(msg);
3818       }
3819     }
3820     else
3821     {
3822       System.err.println("Warning: " + msg);
3823       if (e != null)
3824       {
3825         e.printStackTrace();
3826       }
3827     }
3828   }
3829
3830   /**
3831    * set the object to ID mapping tables used to write/recover objects and XML
3832    * ID strings for the jalview project. If external tables are provided then
3833    * finalize and clearSeqRefs will not clear the tables when the Jalview2XML
3834    * object goes out of scope. - also populates the datasetIds hashtable with
3835    * alignment objects containing dataset sequences
3836    *
3837    * @param vobj2jv
3838    *          Map from ID strings to jalview datamodel
3839    * @param jv2vobj
3840    *          Map from jalview datamodel to ID strings
3841    *
3842    *
3843    */
3844   public void setObjectMappingTables(Hashtable vobj2jv,
3845           IdentityHashMap jv2vobj)
3846   {
3847     this.jv2vobj = jv2vobj;
3848     this.vobj2jv = vobj2jv;
3849     Iterator ds = jv2vobj.keySet().iterator();
3850     String id;
3851     while (ds.hasNext())
3852     {
3853       Object jvobj = ds.next();
3854       id = jv2vobj.get(jvobj).toString();
3855       if (jvobj instanceof jalview.datamodel.Alignment)
3856       {
3857         if (((jalview.datamodel.Alignment) jvobj).getDataset() == null)
3858         {
3859           addDatasetRef(id, (jalview.datamodel.Alignment) jvobj);
3860         }
3861       }
3862       else if (jvobj instanceof jalview.datamodel.Sequence)
3863       {
3864         // register sequence object so the XML parser can recover it.
3865         if (seqRefIds == null)
3866         {
3867           seqRefIds = new Hashtable();
3868         }
3869         if (seqsToIds == null)
3870         {
3871           seqsToIds = new IdentityHashMap();
3872         }
3873         seqRefIds.put(jv2vobj.get(jvobj).toString(), jvobj);
3874         seqsToIds.put(jvobj, id);
3875       }
3876       else if (jvobj instanceof jalview.datamodel.AlignmentAnnotation)
3877       {
3878         if (annotationIds == null)
3879         {
3880           annotationIds = new Hashtable();
3881         }
3882         String anid;
3883         annotationIds.put(anid = jv2vobj.get(jvobj).toString(), jvobj);
3884         jalview.datamodel.AlignmentAnnotation jvann = (jalview.datamodel.AlignmentAnnotation) jvobj;
3885         if (jvann.annotationId == null)
3886         {
3887           jvann.annotationId = anid;
3888         }
3889         if (!jvann.annotationId.equals(anid))
3890         {
3891           // TODO verify that this is the correct behaviour
3892           this.warn("Overriding Annotation ID for " + anid
3893                   + " from different id : " + jvann.annotationId);
3894           jvann.annotationId = anid;
3895         }
3896       }
3897       else if (jvobj instanceof String)
3898       {
3899         if (jvids2vobj == null)
3900         {
3901           jvids2vobj = new Hashtable();
3902           jvids2vobj.put(jvobj, jv2vobj.get(jvobj).toString());
3903         }
3904       }
3905       else
3906         Cache.log.debug("Ignoring " + jvobj.getClass() + " (ID = " + id);
3907     }
3908   }
3909
3910   /**
3911    * set the uniqueSetSuffix used to prefix/suffix object IDs for jalview
3912    * objects created from the project archive. If string is null (default for
3913    * construction) then suffix will be set automatically.
3914    *
3915    * @param string
3916    */
3917   public void setUniqueSetSuffix(String string)
3918   {
3919     uniqueSetSuffix = string;
3920
3921   }
3922
3923   /**
3924    * uses skipList2 as the skipList for skipping views on sequence sets
3925    * associated with keys in the skipList
3926    *
3927    * @param skipList2
3928    */
3929   public void setSkipList(Hashtable skipList2)
3930   {
3931     skipList = skipList2;
3932   }
3933
3934 }