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