JAL-1177 patches to loader to cope with dataset only jalview xml documents
[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,_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 (object.getJalviewModelSequence().getViewportCount() > 0)
1868             {
1869               af = _af;
1870               if (object.getJalviewModelSequence().getViewportCount() > 1
1871                       && af.viewport.gatherViewsHere)
1872               {
1873                 gatherToThisFrame.put(af.viewport.getSequenceSetId(), af);
1874               }
1875             }
1876           }
1877           entryCount++;
1878         }
1879         else if (jarentry != null)
1880         {
1881           // Some other file here.
1882           entryCount++;
1883         }
1884       } while (jarentry != null);
1885       resolveFrefedSequences();
1886     } catch (java.io.FileNotFoundException ex)
1887     {
1888       ex.printStackTrace();
1889       errorMessage = "Couldn't locate Jalview XML file : " + file;
1890       System.err.println("Exception whilst loading jalview XML file : "
1891               + ex + "\n");
1892     } catch (java.net.UnknownHostException ex)
1893     {
1894       ex.printStackTrace();
1895       errorMessage = "Couldn't locate Jalview XML file : " + file;
1896       System.err.println("Exception whilst loading jalview XML file : "
1897               + ex + "\n");
1898     } catch (Exception ex)
1899     {
1900       System.err.println("Parsing as Jalview Version 2 file failed.");
1901       ex.printStackTrace(System.err);
1902       if (attemptversion1parse)
1903       {
1904         // Is Version 1 Jar file?
1905         try
1906         {
1907           af = new Jalview2XML_V1(raiseGUI).LoadJalviewAlign(jprovider);
1908         } catch (Exception ex2)
1909         {
1910           System.err.println("Exception whilst loading as jalviewXMLV1:");
1911           ex2.printStackTrace();
1912           af = null;
1913         }
1914       }
1915       if (Desktop.instance != null)
1916       {
1917         Desktop.instance.stopLoading();
1918       }
1919       if (af != null)
1920       {
1921         System.out.println("Successfully loaded archive file");
1922         return af;
1923       }
1924       ex.printStackTrace();
1925
1926       System.err.println("Exception whilst loading jalview XML file : "
1927               + ex + "\n");
1928     } catch (OutOfMemoryError e)
1929     {
1930       // Don't use the OOM Window here
1931       errorMessage = "Out of memory loading jalview XML file";
1932       System.err.println("Out of memory whilst loading jalview XML file");
1933       e.printStackTrace();
1934     }
1935
1936     if (Desktop.instance != null)
1937     {
1938       Desktop.instance.stopLoading();
1939     }
1940
1941     Enumeration en = gatherToThisFrame.elements();
1942     while (en.hasMoreElements())
1943     {
1944       Desktop.instance.gatherViews((AlignFrame) en.nextElement());
1945     }
1946     if (errorMessage != null)
1947     {
1948       reportErrors();
1949     }
1950     return af;
1951   }
1952
1953   /**
1954    * check errorMessage for a valid error message and raise an error box in the
1955    * GUI or write the current errorMessage to stderr and then clear the error
1956    * state.
1957    */
1958   protected void reportErrors()
1959   {
1960     reportErrors(false);
1961   }
1962
1963   protected void reportErrors(final boolean saving)
1964   {
1965     if (errorMessage != null)
1966     {
1967       final String finalErrorMessage = errorMessage;
1968       if (raiseGUI)
1969       {
1970         javax.swing.SwingUtilities.invokeLater(new Runnable()
1971         {
1972           @Override
1973           public void run()
1974           {
1975             JOptionPane.showInternalMessageDialog(Desktop.desktop,
1976                     finalErrorMessage, "Error "
1977                             + (saving ? "saving" : "loading")
1978                             + " Jalview file", JOptionPane.WARNING_MESSAGE);
1979           }
1980         });
1981       }
1982       else
1983       {
1984         System.err.println("Problem loading Jalview file: " + errorMessage);
1985       }
1986     }
1987     errorMessage = null;
1988   }
1989
1990   Hashtable alreadyLoadedPDB;
1991
1992   /**
1993    * when set, local views will be updated from view stored in JalviewXML
1994    * Currently (28th Sep 2008) things will go horribly wrong in vamsas document
1995    * sync if this is set to true.
1996    */
1997   private final boolean updateLocalViews = false;
1998
1999   String loadPDBFile(jarInputStreamProvider jprovider, String pdbId)
2000   {
2001     if (alreadyLoadedPDB == null)
2002       alreadyLoadedPDB = new Hashtable();
2003
2004     if (alreadyLoadedPDB.containsKey(pdbId))
2005       return alreadyLoadedPDB.get(pdbId).toString();
2006
2007     try
2008     {
2009       JarInputStream jin = jprovider.getJarInputStream();
2010       /*
2011        * if (jprovider.startsWith("http://")) { jin = new JarInputStream(new
2012        * URL(jprovider).openStream()); } else { jin = new JarInputStream(new
2013        * FileInputStream(jprovider)); }
2014        */
2015
2016       JarEntry entry = null;
2017       do
2018       {
2019         entry = jin.getNextJarEntry();
2020       } while (entry != null && !entry.getName().equals(pdbId));
2021       if (entry != null)
2022       {
2023         BufferedReader in = new BufferedReader(new InputStreamReader(jin));
2024         File outFile = File.createTempFile("jalview_pdb", ".txt");
2025         outFile.deleteOnExit();
2026         PrintWriter out = new PrintWriter(new FileOutputStream(outFile));
2027         String data;
2028
2029         while ((data = in.readLine()) != null)
2030         {
2031           out.println(data);
2032         }
2033         try
2034         {
2035           out.flush();
2036         } catch (Exception foo)
2037         {
2038         }
2039         ;
2040         out.close();
2041         String t = outFile.getAbsolutePath();
2042         alreadyLoadedPDB.put(pdbId, t);
2043         return t;
2044       }
2045       else
2046       {
2047         warn("Couldn't find PDB file entry in Jalview Jar for " + pdbId);
2048       }
2049     } catch (Exception ex)
2050     {
2051       ex.printStackTrace();
2052     }
2053
2054     return null;
2055   }
2056
2057   private class JvAnnotRow
2058   {
2059     public JvAnnotRow(int i, AlignmentAnnotation jaa)
2060     {
2061       order = i;
2062       template = jaa;
2063     }
2064
2065     /**
2066      * persisted version of annotation row from which to take vis properties
2067      */
2068     public jalview.datamodel.AlignmentAnnotation template;
2069
2070     /**
2071      * original position of the annotation row in the alignment
2072      */
2073     public int order;
2074   }
2075
2076   /**
2077    * Load alignment frame from jalview XML DOM object
2078    * 
2079    * @param object
2080    *          DOM
2081    * @param file
2082    *          filename source string
2083    * @param loadTreesAndStructures
2084    *          when false only create Viewport
2085    * @param jprovider
2086    *          data source provider
2087    * @return alignment frame created from view stored in DOM
2088    */
2089   AlignFrame LoadFromObject(JalviewModel object, String file,
2090           boolean loadTreesAndStructures, jarInputStreamProvider jprovider)
2091   {
2092     SequenceSet vamsasSet = object.getVamsasModel().getSequenceSet(0);
2093     Sequence[] vamsasSeq = vamsasSet.getSequence();
2094
2095     JalviewModelSequence jms = object.getJalviewModelSequence();
2096
2097     Viewport view = (jms.getViewportCount()>0) ? jms.getViewport(0) : null;
2098     
2099     // ////////////////////////////////
2100     // LOAD SEQUENCES
2101
2102     Vector hiddenSeqs = null;
2103     jalview.datamodel.Sequence jseq;
2104
2105     ArrayList tmpseqs = new ArrayList();
2106
2107     boolean multipleView = false;
2108
2109     JSeq[] JSEQ = object.getJalviewModelSequence().getJSeq();
2110     int vi = 0; // counter in vamsasSeq array
2111     for (int i = 0; i < JSEQ.length; i++)
2112     {
2113       String seqId = JSEQ[i].getId();
2114
2115       if (seqRefIds.get(seqId) != null)
2116       {
2117         tmpseqs.add(seqRefIds.get(seqId));
2118         multipleView = true;
2119       }
2120       else
2121       {
2122         jseq = new jalview.datamodel.Sequence(vamsasSeq[vi].getName(),
2123                 vamsasSeq[vi].getSequence());
2124         jseq.setDescription(vamsasSeq[vi].getDescription());
2125         jseq.setStart(JSEQ[i].getStart());
2126         jseq.setEnd(JSEQ[i].getEnd());
2127         jseq.setVamsasId(uniqueSetSuffix + seqId);
2128         seqRefIds.put(vamsasSeq[vi].getId(), jseq);
2129         tmpseqs.add(jseq);
2130         vi++;
2131       }
2132
2133       if (JSEQ[i].getHidden())
2134       {
2135         if (hiddenSeqs == null)
2136         {
2137           hiddenSeqs = new Vector();
2138         }
2139
2140         hiddenSeqs.addElement(seqRefIds.get(seqId));
2141       }
2142
2143     }
2144
2145     // /
2146     // Create the alignment object from the sequence set
2147     // ///////////////////////////////
2148     jalview.datamodel.Sequence[] orderedSeqs = new jalview.datamodel.Sequence[tmpseqs
2149             .size()];
2150
2151     tmpseqs.toArray(orderedSeqs);
2152
2153     jalview.datamodel.Alignment al = new jalview.datamodel.Alignment(
2154             orderedSeqs);
2155
2156     // / Add the alignment properties
2157     for (int i = 0; i < vamsasSet.getSequenceSetPropertiesCount(); i++)
2158     {
2159       SequenceSetProperties ssp = vamsasSet.getSequenceSetProperties(i);
2160       al.setProperty(ssp.getKey(), ssp.getValue());
2161     }
2162
2163     // /
2164     // SequenceFeatures are added to the DatasetSequence,
2165     // so we must create or recover the dataset before loading features
2166     // ///////////////////////////////
2167     if (vamsasSet.getDatasetId() == null || vamsasSet.getDatasetId() == "")
2168     {
2169       // older jalview projects do not have a dataset id.
2170       al.setDataset(null);
2171     }
2172     else
2173     {
2174       recoverDatasetFor(vamsasSet, al);
2175     }
2176     // ///////////////////////////////
2177
2178     Hashtable pdbloaded = new Hashtable();
2179     if (!multipleView)
2180     {
2181       // load sequence features, database references and any associated PDB
2182       // structures for the alignment
2183       for (int i = 0; i < vamsasSeq.length; i++)
2184       {
2185         if (JSEQ[i].getFeaturesCount() > 0)
2186         {
2187           Features[] features = JSEQ[i].getFeatures();
2188           for (int f = 0; f < features.length; f++)
2189           {
2190             jalview.datamodel.SequenceFeature sf = new jalview.datamodel.SequenceFeature(
2191                     features[f].getType(), features[f].getDescription(),
2192                     features[f].getStatus(), features[f].getBegin(),
2193                     features[f].getEnd(), features[f].getFeatureGroup());
2194
2195             sf.setScore(features[f].getScore());
2196             for (int od = 0; od < features[f].getOtherDataCount(); od++)
2197             {
2198               OtherData keyValue = features[f].getOtherData(od);
2199               if (keyValue.getKey().startsWith("LINK"))
2200               {
2201                 sf.addLink(keyValue.getValue());
2202               }
2203               else
2204               {
2205                 sf.setValue(keyValue.getKey(), keyValue.getValue());
2206               }
2207
2208             }
2209
2210             al.getSequenceAt(i).getDatasetSequence().addSequenceFeature(sf);
2211           }
2212         }
2213         if (vamsasSeq[i].getDBRefCount() > 0)
2214         {
2215           addDBRefs(al.getSequenceAt(i).getDatasetSequence(), vamsasSeq[i]);
2216         }
2217         if (JSEQ[i].getPdbidsCount() > 0)
2218         {
2219           Pdbids[] ids = JSEQ[i].getPdbids();
2220           for (int p = 0; p < ids.length; p++)
2221           {
2222             jalview.datamodel.PDBEntry entry = new jalview.datamodel.PDBEntry();
2223             entry.setId(ids[p].getId());
2224             entry.setType(ids[p].getType());
2225             if (ids[p].getFile() != null)
2226             {
2227               if (!pdbloaded.containsKey(ids[p].getFile()))
2228               {
2229                 entry.setFile(loadPDBFile(jprovider, ids[p].getId()));
2230               }
2231               else
2232               {
2233                 entry.setFile(pdbloaded.get(ids[p].getId()).toString());
2234               }
2235             }
2236
2237             al.getSequenceAt(i).getDatasetSequence().addPDBId(entry);
2238           }
2239         }
2240       }
2241     } // end !multipleview
2242
2243     // ///////////////////////////////
2244     // LOAD SEQUENCE MAPPINGS
2245
2246     if (vamsasSet.getAlcodonFrameCount() > 0)
2247     {
2248       // TODO Potentially this should only be done once for all views of an
2249       // alignment
2250       AlcodonFrame[] alc = vamsasSet.getAlcodonFrame();
2251       for (int i = 0; i < alc.length; i++)
2252       {
2253         jalview.datamodel.AlignedCodonFrame cf = new jalview.datamodel.AlignedCodonFrame(
2254                 alc[i].getAlcodonCount());
2255         if (alc[i].getAlcodonCount() > 0)
2256         {
2257           Alcodon[] alcods = alc[i].getAlcodon();
2258           for (int p = 0; p < cf.codons.length; p++)
2259           {
2260             if (alcods[p].hasPos1() && alcods[p].hasPos2()
2261                     && alcods[p].hasPos3())
2262             {
2263               // translated codons require three valid positions
2264               cf.codons[p] = new int[3];
2265               cf.codons[p][0] = (int) alcods[p].getPos1();
2266               cf.codons[p][1] = (int) alcods[p].getPos2();
2267               cf.codons[p][2] = (int) alcods[p].getPos3();
2268             }
2269             else
2270             {
2271               cf.codons[p] = null;
2272             }
2273           }
2274         }
2275         if (alc[i].getAlcodMapCount() > 0)
2276         {
2277           AlcodMap[] maps = alc[i].getAlcodMap();
2278           for (int m = 0; m < maps.length; m++)
2279           {
2280             SequenceI dnaseq = (SequenceI) seqRefIds
2281                     .get(maps[m].getDnasq());
2282             // Load Mapping
2283             jalview.datamodel.Mapping mapping = null;
2284             // attach to dna sequence reference.
2285             if (maps[m].getMapping() != null)
2286             {
2287               mapping = addMapping(maps[m].getMapping());
2288             }
2289             if (dnaseq != null)
2290             {
2291               cf.addMap(dnaseq, mapping.getTo(), mapping.getMap());
2292             }
2293             else
2294             {
2295               // defer to later
2296               frefedSequence.add(new Object[]
2297               { maps[m].getDnasq(), cf, mapping });
2298             }
2299           }
2300         }
2301         al.addCodonFrame(cf);
2302       }
2303
2304     }
2305
2306     // ////////////////////////////////
2307     // LOAD ANNOTATIONS
2308     ArrayList<JvAnnotRow> autoAlan = new ArrayList<JvAnnotRow>();
2309     /**
2310      * store any annotations which forward reference a group's ID
2311      */
2312     Hashtable<String, ArrayList<jalview.datamodel.AlignmentAnnotation>> groupAnnotRefs = new Hashtable<String, ArrayList<jalview.datamodel.AlignmentAnnotation>>();
2313
2314     if (vamsasSet.getAnnotationCount() > 0)
2315     {
2316       Annotation[] an = vamsasSet.getAnnotation();
2317
2318       for (int i = 0; i < an.length; i++)
2319       {
2320         /**
2321          * test if annotation is automatically calculated for this view only
2322          */
2323         boolean autoForView = false;
2324         if (an[i].getLabel().equals("Quality")
2325                 || an[i].getLabel().equals("Conservation")
2326                 || an[i].getLabel().equals("Consensus"))
2327         {
2328           // Kludge for pre 2.5 projects which lacked the autocalculated flag
2329           autoForView = true;
2330           if (!an[i].hasAutoCalculated())
2331           {
2332             an[i].setAutoCalculated(true);
2333           }
2334         }
2335         if (autoForView
2336                 || (an[i].hasAutoCalculated() && an[i].isAutoCalculated()))
2337         {
2338           // remove ID - we don't recover annotation from other views for
2339           // view-specific annotation
2340           an[i].setId(null);
2341         }
2342
2343         // set visiblity for other annotation in this view
2344         if (an[i].getId() != null
2345                 && annotationIds.containsKey(an[i].getId()))
2346         {
2347           jalview.datamodel.AlignmentAnnotation jda = (jalview.datamodel.AlignmentAnnotation) annotationIds
2348                   .get(an[i].getId());
2349           // in principle Visible should always be true for annotation displayed
2350           // in multiple views
2351           if (an[i].hasVisible())
2352             jda.visible = an[i].getVisible();
2353
2354           al.addAnnotation(jda);
2355
2356           continue;
2357         }
2358         // Construct new annotation from model.
2359         AnnotationElement[] ae = an[i].getAnnotationElement();
2360         jalview.datamodel.Annotation[] anot = null;
2361         java.awt.Color firstColour = null;
2362         int anpos;
2363         if (!an[i].getScoreOnly())
2364         {
2365           anot = new jalview.datamodel.Annotation[al.getWidth()];
2366           for (int aa = 0; aa < ae.length && aa < anot.length; aa++)
2367           {
2368             anpos = ae[aa].getPosition();
2369
2370             if (anpos >= anot.length)
2371               continue;
2372
2373             anot[anpos] = new jalview.datamodel.Annotation(
2374
2375             ae[aa].getDisplayCharacter(), ae[aa].getDescription(),
2376                     (ae[aa].getSecondaryStructure() == null || ae[aa]
2377                             .getSecondaryStructure().length() == 0) ? ' '
2378                             : ae[aa].getSecondaryStructure().charAt(0),
2379                     ae[aa].getValue()
2380
2381             );
2382             // JBPNote: Consider verifying dataflow for IO of secondary
2383             // structure annotation read from Stockholm files
2384             // this was added to try to ensure that
2385             // if (anot[ae[aa].getPosition()].secondaryStructure>' ')
2386             // {
2387             // anot[ae[aa].getPosition()].displayCharacter = "";
2388             // }
2389             anot[anpos].colour = new java.awt.Color(ae[aa].getColour());
2390             if (firstColour == null)
2391             {
2392               firstColour = anot[anpos].colour;
2393             }
2394           }
2395         }
2396         jalview.datamodel.AlignmentAnnotation jaa = null;
2397
2398         if (an[i].getGraph())
2399         {
2400           float llim = 0, hlim = 0;
2401           // if (autoForView || an[i].isAutoCalculated()) {
2402           // hlim=11f;
2403           // }
2404           jaa = new jalview.datamodel.AlignmentAnnotation(an[i].getLabel(),
2405                   an[i].getDescription(), anot, llim, hlim,
2406                   an[i].getGraphType());
2407
2408           jaa.graphGroup = an[i].getGraphGroup();
2409           jaa._linecolour = firstColour;
2410           if (an[i].getThresholdLine() != null)
2411           {
2412             jaa.setThreshold(new jalview.datamodel.GraphLine(an[i]
2413                     .getThresholdLine().getValue(), an[i]
2414                     .getThresholdLine().getLabel(), new java.awt.Color(
2415                     an[i].getThresholdLine().getColour())));
2416
2417           }
2418           if (autoForView || an[i].isAutoCalculated())
2419           {
2420             // Hardwire the symbol display line to ensure that labels for
2421             // histograms are displayed
2422             jaa.hasText = true;
2423           }
2424         }
2425         else
2426         {
2427           jaa = new jalview.datamodel.AlignmentAnnotation(an[i].getLabel(),
2428                   an[i].getDescription(), anot);
2429           jaa._linecolour = firstColour;
2430         }
2431         // register new annotation
2432         if (an[i].getId() != null)
2433         {
2434           annotationIds.put(an[i].getId(), jaa);
2435           jaa.annotationId = an[i].getId();
2436         }
2437         // recover sequence association
2438         if (an[i].getSequenceRef() != null)
2439         {
2440           if (al.findName(an[i].getSequenceRef()) != null)
2441           {
2442             jaa.createSequenceMapping(al.findName(an[i].getSequenceRef()),
2443                     1, true);
2444             al.findName(an[i].getSequenceRef()).addAlignmentAnnotation(jaa);
2445           }
2446         }
2447         // and make a note of any group association
2448         if (an[i].getGroupRef() != null && an[i].getGroupRef().length() > 0)
2449         {
2450           ArrayList<jalview.datamodel.AlignmentAnnotation> aal = groupAnnotRefs
2451                   .get(an[i].getGroupRef());
2452           if (aal == null)
2453           {
2454             aal = new ArrayList<jalview.datamodel.AlignmentAnnotation>();
2455             groupAnnotRefs.put(an[i].getGroupRef(), aal);
2456           }
2457           aal.add(jaa);
2458         }
2459
2460         if (an[i].hasScore())
2461         {
2462           jaa.setScore(an[i].getScore());
2463         }
2464         if (an[i].hasVisible())
2465           jaa.visible = an[i].getVisible();
2466
2467         if (an[i].hasCentreColLabels())
2468           jaa.centreColLabels = an[i].getCentreColLabels();
2469
2470         if (an[i].hasScaleColLabels())
2471         {
2472           jaa.scaleColLabel = an[i].getScaleColLabels();
2473         }
2474         if (an[i].hasAutoCalculated() && an[i].isAutoCalculated())
2475         {
2476           // newer files have an 'autoCalculated' flag and store calculation
2477           // state in viewport properties
2478           jaa.autoCalculated = true; // means annotation will be marked for
2479           // update at end of load.
2480         }
2481         if (an[i].hasGraphHeight())
2482         {
2483           jaa.graphHeight = an[i].getGraphHeight();
2484         }
2485         if (an[i].hasBelowAlignment())
2486         {
2487           jaa.belowAlignment = an[i].isBelowAlignment();
2488         }
2489         jaa.setCalcId(an[i].getCalcId());
2490
2491         if (jaa.autoCalculated)
2492         {
2493           autoAlan.add(new JvAnnotRow(i, jaa));
2494         }
2495         else
2496         // if (!autoForView)
2497         {
2498           // add autocalculated group annotation and any user created annotation
2499           // for the view
2500           al.addAnnotation(jaa);
2501         }
2502       }
2503     }
2504
2505     // ///////////////////////
2506     // LOAD GROUPS
2507     // Create alignment markup and styles for this view
2508     if (jms.getJGroupCount() > 0)
2509     {
2510       JGroup[] groups = jms.getJGroup();
2511
2512       for (int i = 0; i < groups.length; i++)
2513       {
2514         ColourSchemeI cs = null;
2515
2516         if (groups[i].getColour() != null)
2517         {
2518           if (groups[i].getColour().startsWith("ucs"))
2519           {
2520             cs = GetUserColourScheme(jms, groups[i].getColour());
2521           }
2522           else
2523           {
2524             cs = ColourSchemeProperty.getColour(al, groups[i].getColour());
2525           }
2526
2527           if (cs != null)
2528           {
2529             cs.setThreshold(groups[i].getPidThreshold(), true);
2530           }
2531         }
2532
2533         Vector seqs = new Vector();
2534
2535         for (int s = 0; s < groups[i].getSeqCount(); s++)
2536         {
2537           String seqId = groups[i].getSeq(s) + "";
2538           jalview.datamodel.SequenceI ts = (jalview.datamodel.SequenceI) seqRefIds
2539                   .get(seqId);
2540
2541           if (ts != null)
2542           {
2543             seqs.addElement(ts);
2544           }
2545         }
2546
2547         if (seqs.size() < 1)
2548         {
2549           continue;
2550         }
2551
2552         jalview.datamodel.SequenceGroup sg = new jalview.datamodel.SequenceGroup(
2553                 seqs, groups[i].getName(), cs, groups[i].getDisplayBoxes(),
2554                 groups[i].getDisplayText(), groups[i].getColourText(),
2555                 groups[i].getStart(), groups[i].getEnd());
2556
2557         sg.setOutlineColour(new java.awt.Color(groups[i].getOutlineColour()));
2558
2559         sg.textColour = new java.awt.Color(groups[i].getTextCol1());
2560         sg.textColour2 = new java.awt.Color(groups[i].getTextCol2());
2561         sg.setShowNonconserved(groups[i].hasShowUnconserved() ? groups[i]
2562                 .isShowUnconserved() : false);
2563         sg.thresholdTextColour = groups[i].getTextColThreshold();
2564         if (groups[i].hasShowConsensusHistogram())
2565         {
2566           sg.setShowConsensusHistogram(groups[i].isShowConsensusHistogram());
2567         }
2568         ;
2569         if (groups[i].hasShowSequenceLogo())
2570         {
2571           sg.setshowSequenceLogo(groups[i].isShowSequenceLogo());
2572         }
2573         if (groups[i].hasNormaliseSequenceLogo())
2574         {
2575           sg.setNormaliseSequenceLogo(groups[i].isNormaliseSequenceLogo());
2576         }
2577         if (groups[i].hasIgnoreGapsinConsensus())
2578         {
2579           sg.setIgnoreGapsConsensus(groups[i].getIgnoreGapsinConsensus());
2580         }
2581         if (groups[i].getConsThreshold() != 0)
2582         {
2583           jalview.analysis.Conservation c = new jalview.analysis.Conservation(
2584                   "All", ResidueProperties.propHash, 3,
2585                   sg.getSequences(null), 0, sg.getWidth() - 1);
2586           c.calculate();
2587           c.verdict(false, 25);
2588           sg.cs.setConservation(c);
2589         }
2590
2591         if (groups[i].getId() != null && groupAnnotRefs.size() > 0)
2592         {
2593           // re-instate unique group/annotation row reference
2594           ArrayList<jalview.datamodel.AlignmentAnnotation> jaal = groupAnnotRefs
2595                   .get(groups[i].getId());
2596           if (jaal != null)
2597           {
2598             for (jalview.datamodel.AlignmentAnnotation jaa : jaal)
2599             {
2600               jaa.groupRef = sg;
2601               if (jaa.autoCalculated)
2602               {
2603                 // match up and try to set group autocalc alignment row for this
2604                 // annotation
2605                 if (jaa.label.startsWith("Consensus for "))
2606                 {
2607                   sg.setConsensus(jaa);
2608                 }
2609                 // match up and try to set group autocalc alignment row for this
2610                 // annotation
2611                 if (jaa.label.startsWith("Conservation for "))
2612                 {
2613                   sg.setConservationRow(jaa);
2614                 }
2615               }
2616             }
2617           }
2618         }
2619         al.addGroup(sg);
2620
2621       }
2622     }
2623     if (view==null)
2624     {
2625       // only dataset in this model, so just return.
2626       return null;
2627     }
2628     // ///////////////////////////////
2629     // LOAD VIEWPORT
2630
2631     // If we just load in the same jar file again, the sequenceSetId
2632     // will be the same, and we end up with multiple references
2633     // to the same sequenceSet. We must modify this id on load
2634     // so that each load of the file gives a unique id
2635     String uniqueSeqSetId = view.getSequenceSetId() + uniqueSetSuffix;
2636     String viewId = (view.getId() == null ? null : view.getId()
2637             + uniqueSetSuffix);
2638     AlignFrame af = null;
2639     AlignViewport av = null;
2640     // now check to see if we really need to create a new viewport.
2641     if (multipleView && viewportsAdded.size() == 0)
2642     {
2643       // We recovered an alignment for which a viewport already exists.
2644       // TODO: fix up any settings necessary for overlaying stored state onto
2645       // state recovered from another document. (may not be necessary).
2646       // we may need a binding from a viewport in memory to one recovered from
2647       // XML.
2648       // and then recover its containing af to allow the settings to be applied.
2649       // TODO: fix for vamsas demo
2650       System.err
2651               .println("About to recover a viewport for existing alignment: Sequence set ID is "
2652                       + uniqueSeqSetId);
2653       Object seqsetobj = retrieveExistingObj(uniqueSeqSetId);
2654       if (seqsetobj != null)
2655       {
2656         if (seqsetobj instanceof String)
2657         {
2658           uniqueSeqSetId = (String) seqsetobj;
2659           System.err
2660                   .println("Recovered extant sequence set ID mapping for ID : New Sequence set ID is "
2661                           + uniqueSeqSetId);
2662         }
2663         else
2664         {
2665           System.err
2666                   .println("Warning : Collision between sequence set ID string and existing jalview object mapping.");
2667         }
2668
2669       }
2670     }
2671     AlignmentPanel ap = null;
2672     boolean isnewview = true;
2673     if (viewId != null)
2674     {
2675       // Check to see if this alignment already has a view id == viewId
2676       jalview.gui.AlignmentPanel views[] = Desktop
2677               .getAlignmentPanels(uniqueSeqSetId);
2678       if (views != null && views.length > 0)
2679       {
2680         for (int v = 0; v < views.length; v++)
2681         {
2682           if (views[v].av.getViewId().equalsIgnoreCase(viewId))
2683           {
2684             // recover the existing alignpanel, alignframe, viewport
2685             af = views[v].alignFrame;
2686             av = views[v].av;
2687             ap = views[v];
2688             // TODO: could even skip resetting view settings if we don't want to
2689             // change the local settings from other jalview processes
2690             isnewview = false;
2691           }
2692         }
2693       }
2694     }
2695
2696     if (isnewview)
2697     {
2698       af = loadViewport(file, JSEQ, hiddenSeqs, al, jms, view,
2699               uniqueSeqSetId, viewId, autoAlan);
2700       av = af.viewport;
2701       ap = af.alignPanel;
2702     }
2703     // LOAD TREES
2704     // /////////////////////////////////////
2705     if (loadTreesAndStructures && jms.getTreeCount() > 0)
2706     {
2707       try
2708       {
2709         for (int t = 0; t < jms.getTreeCount(); t++)
2710         {
2711
2712           Tree tree = jms.getTree(t);
2713
2714           TreePanel tp = (TreePanel) retrieveExistingObj(tree.getId());
2715           if (tp == null)
2716           {
2717             tp = af.ShowNewickTree(
2718                     new jalview.io.NewickFile(tree.getNewick()),
2719                     tree.getTitle(), tree.getWidth(), tree.getHeight(),
2720                     tree.getXpos(), tree.getYpos());
2721             if (tree.getId() != null)
2722             {
2723               // perhaps bind the tree id to something ?
2724             }
2725           }
2726           else
2727           {
2728             // update local tree attributes ?
2729             // TODO: should check if tp has been manipulated by user - if so its
2730             // settings shouldn't be modified
2731             tp.setTitle(tree.getTitle());
2732             tp.setBounds(new Rectangle(tree.getXpos(), tree.getYpos(), tree
2733                     .getWidth(), tree.getHeight()));
2734             tp.av = av; // af.viewport; // TODO: verify 'associate with all
2735             // views'
2736             // works still
2737             tp.treeCanvas.av = av; // af.viewport;
2738             tp.treeCanvas.ap = ap; // af.alignPanel;
2739
2740           }
2741           if (tp == null)
2742           {
2743             warn("There was a problem recovering stored Newick tree: \n"
2744                     + tree.getNewick());
2745             continue;
2746           }
2747
2748           tp.fitToWindow.setState(tree.getFitToWindow());
2749           tp.fitToWindow_actionPerformed(null);
2750
2751           if (tree.getFontName() != null)
2752           {
2753             tp.setTreeFont(new java.awt.Font(tree.getFontName(), tree
2754                     .getFontStyle(), tree.getFontSize()));
2755           }
2756           else
2757           {
2758             tp.setTreeFont(new java.awt.Font(view.getFontName(), view
2759                     .getFontStyle(), tree.getFontSize()));
2760           }
2761
2762           tp.showPlaceholders(tree.getMarkUnlinked());
2763           tp.showBootstrap(tree.getShowBootstrap());
2764           tp.showDistances(tree.getShowDistances());
2765
2766           tp.treeCanvas.threshold = tree.getThreshold();
2767
2768           if (tree.getCurrentTree())
2769           {
2770             af.viewport.setCurrentTree(tp.getTree());
2771           }
2772         }
2773
2774       } catch (Exception ex)
2775       {
2776         ex.printStackTrace();
2777       }
2778     }
2779
2780     // //LOAD STRUCTURES
2781     if (loadTreesAndStructures)
2782     {
2783       // run through all PDB ids on the alignment, and collect mappings between
2784       // jmol view ids and all sequences referring to it
2785       Hashtable<String, Object[]> jmolViewIds = new Hashtable();
2786
2787       for (int i = 0; i < JSEQ.length; i++)
2788       {
2789         if (JSEQ[i].getPdbidsCount() > 0)
2790         {
2791           Pdbids[] ids = JSEQ[i].getPdbids();
2792           for (int p = 0; p < ids.length; p++)
2793           {
2794             for (int s = 0; s < ids[p].getStructureStateCount(); s++)
2795             {
2796               // check to see if we haven't already created this structure view
2797               String sviewid = (ids[p].getStructureState(s).getViewId() == null) ? null
2798                       : ids[p].getStructureState(s).getViewId()
2799                               + uniqueSetSuffix;
2800               jalview.datamodel.PDBEntry jpdb = new jalview.datamodel.PDBEntry();
2801               // Originally : ids[p].getFile()
2802               // : TODO: verify external PDB file recovery still works in normal
2803               // jalview project load
2804               jpdb.setFile(loadPDBFile(jprovider, ids[p].getId()));
2805               jpdb.setId(ids[p].getId());
2806
2807               int x = ids[p].getStructureState(s).getXpos();
2808               int y = ids[p].getStructureState(s).getYpos();
2809               int width = ids[p].getStructureState(s).getWidth();
2810               int height = ids[p].getStructureState(s).getHeight();
2811
2812               // Probably don't need to do this anymore...
2813               // Desktop.desktop.getComponentAt(x, y);
2814               // TODO: NOW: check that this recovers the PDB file correctly.
2815               String pdbFile = loadPDBFile(jprovider, ids[p].getId());
2816               jalview.datamodel.SequenceI seq = (jalview.datamodel.SequenceI) seqRefIds
2817                       .get(JSEQ[i].getId() + "");
2818               if (sviewid == null)
2819               {
2820                 sviewid = "_jalview_pre2_4_" + x + "," + y + "," + width
2821                         + "," + height;
2822               }
2823               if (!jmolViewIds.containsKey(sviewid))
2824               {
2825                 jmolViewIds.put(sviewid, new Object[]
2826                 { new int[]
2827                 { x, y, width, height }, "",
2828                     new Hashtable<String, Object[]>(), new boolean[]
2829                     { false, false, true } });
2830                 // Legacy pre-2.7 conversion JAL-823 :
2831                 // do not assume any view has to be linked for colour by
2832                 // sequence
2833               }
2834
2835               // assemble String[] { pdb files }, String[] { id for each
2836               // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
2837               // seqs_file 2}, boolean[] {
2838               // linkAlignPanel,superposeWithAlignpanel}} from hash
2839               Object[] jmoldat = jmolViewIds.get(sviewid);
2840               ((boolean[]) jmoldat[3])[0] |= ids[p].getStructureState(s)
2841                       .hasAlignwithAlignPanel() ? ids[p].getStructureState(
2842                       s).getAlignwithAlignPanel() : false;
2843               // never colour by linked panel if not specified
2844               ((boolean[]) jmoldat[3])[1] |= ids[p].getStructureState(s)
2845                       .hasColourwithAlignPanel() ? ids[p]
2846                       .getStructureState(s).getColourwithAlignPanel()
2847                       : false;
2848               // default for pre-2.7 projects is that Jmol colouring is enabled
2849               ((boolean[]) jmoldat[3])[2] &= ids[p].getStructureState(s)
2850                       .hasColourByJmol() ? ids[p].getStructureState(s)
2851                       .getColourByJmol() : true;
2852
2853               if (((String) jmoldat[1]).length() < ids[p]
2854                       .getStructureState(s).getContent().length())
2855               {
2856                 {
2857                   jmoldat[1] = ids[p].getStructureState(s).getContent();
2858                 }
2859               }
2860               if (ids[p].getFile() != null)
2861               {
2862                 File mapkey = new File(ids[p].getFile());
2863                 Object[] seqstrmaps = (Object[]) ((Hashtable) jmoldat[2])
2864                         .get(mapkey);
2865                 if (seqstrmaps == null)
2866                 {
2867                   ((Hashtable) jmoldat[2]).put(mapkey,
2868                           seqstrmaps = new Object[]
2869                           { pdbFile, ids[p].getId(), new Vector(),
2870                               new Vector() });
2871                 }
2872                 if (!((Vector) seqstrmaps[2]).contains(seq))
2873                 {
2874                   ((Vector) seqstrmaps[2]).addElement(seq);
2875                   // ((Vector)seqstrmaps[3]).addElement(n) :
2876                   // in principle, chains
2877                   // should be stored here : do we need to
2878                   // TODO: store and recover seq/pdb_id :
2879                   // chain mappings
2880                 }
2881               }
2882               else
2883               {
2884                 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");
2885                 warn(errorMessage);
2886               }
2887             }
2888           }
2889         }
2890       }
2891       {
2892
2893         // Instantiate the associated Jmol views
2894         for (Entry<String, Object[]> entry : jmolViewIds.entrySet())
2895         {
2896           String sviewid = entry.getKey();
2897           Object[] svattrib = entry.getValue();
2898           int[] geom = (int[]) svattrib[0];
2899           String state = (String) svattrib[1];
2900           Hashtable<File, Object[]> oldFiles = (Hashtable<File, Object[]>) svattrib[2];
2901           final boolean useinJmolsuperpos = ((boolean[]) svattrib[3])[0], usetoColourbyseq = ((boolean[]) svattrib[3])[1], jmolColouring = ((boolean[]) svattrib[3])[2];
2902           int x = geom[0], y = geom[1], width = geom[2], height = geom[3];
2903           // collate the pdbfile -> sequence mappings from this view
2904           Vector<String> pdbfilenames = new Vector<String>();
2905           Vector<SequenceI[]> seqmaps = new Vector<SequenceI[]>();
2906           Vector<String> pdbids = new Vector<String>();
2907
2908           // Search to see if we've already created this Jmol view
2909           AppJmol comp = null;
2910           JInternalFrame[] frames = null;
2911           do
2912           {
2913             try
2914             {
2915               frames = Desktop.desktop.getAllFrames();
2916             } catch (ArrayIndexOutOfBoundsException e)
2917             {
2918               // occasional No such child exceptions are thrown here...
2919               frames = null;
2920               try
2921               {
2922                 Thread.sleep(10);
2923               } catch (Exception f)
2924               {
2925               }
2926               ;
2927             }
2928           } while (frames == null);
2929           // search for any Jmol windows already open from other
2930           // alignment views that exactly match the stored structure state
2931           for (int f = 0; comp == null && f < frames.length; f++)
2932           {
2933             if (frames[f] instanceof AppJmol)
2934             {
2935               if (sviewid != null
2936                       && ((AppJmol) frames[f]).getViewId().equals(sviewid))
2937               {
2938                 // post jalview 2.4 schema includes structure view id
2939                 comp = (AppJmol) frames[f];
2940               }
2941               else if (frames[f].getX() == x && frames[f].getY() == y
2942                       && frames[f].getHeight() == height
2943                       && frames[f].getWidth() == width)
2944               {
2945                 comp = (AppJmol) frames[f];
2946               }
2947             }
2948           }
2949
2950           if (comp == null)
2951           {
2952             // create a new Jmol window.
2953             // First parse the Jmol state to translate filenames loaded into the
2954             // view, and record the order in which files are shown in the Jmol
2955             // view, so we can add the sequence mappings in same order.
2956             StringBuffer newFileLoc = null;
2957             int cp = 0, ncp, ecp;
2958             while ((ncp = state.indexOf("load ", cp)) > -1)
2959             {
2960               if (newFileLoc == null)
2961               {
2962                 newFileLoc = new StringBuffer();
2963               }
2964               do
2965               {
2966                 // look for next filename in load statement
2967                 newFileLoc.append(state.substring(cp,
2968                         ncp = (state.indexOf("\"", ncp + 1) + 1)));
2969                 String oldfilenam = state.substring(ncp,
2970                         ecp = state.indexOf("\"", ncp));
2971                 // recover the new mapping data for this old filename
2972                 // have to normalize filename - since Jmol and jalview do
2973                 // filename
2974                 // translation differently.
2975                 Object[] filedat = oldFiles.get(new File(oldfilenam));
2976                 newFileLoc.append(Platform
2977                         .escapeString((String) filedat[0]));
2978                 pdbfilenames.addElement((String) filedat[0]);
2979                 pdbids.addElement((String) filedat[1]);
2980                 seqmaps.addElement(((Vector<SequenceI>) filedat[2])
2981                         .toArray(new SequenceI[0]));
2982                 newFileLoc.append("\"");
2983                 cp = ecp + 1; // advance beyond last \" and set cursor so we can
2984                               // look for next file statement.
2985               } while ((ncp = state.indexOf("/*file*/", cp)) > -1);
2986             }
2987             if (cp > 0)
2988             {
2989               // just append rest of state
2990               newFileLoc.append(state.substring(cp));
2991             }
2992             else
2993             {
2994               System.err
2995                       .print("Ignoring incomplete Jmol state for PDB ids: ");
2996               newFileLoc = new StringBuffer(state);
2997               newFileLoc.append("; load append ");
2998               for (File id : oldFiles.keySet())
2999               {
3000                 // add this and any other pdb files that should be present in
3001                 // the viewer
3002                 Object[] filedat = oldFiles.get(id);
3003                 String nfilename;
3004                 newFileLoc.append(((String) filedat[0]));
3005                 pdbfilenames.addElement((String) filedat[0]);
3006                 pdbids.addElement((String) filedat[1]);
3007                 seqmaps.addElement(((Vector<SequenceI>) filedat[2])
3008                         .toArray(new SequenceI[0]));
3009                 newFileLoc.append(" \"");
3010                 newFileLoc.append((String) filedat[0]);
3011                 newFileLoc.append("\"");
3012
3013               }
3014               newFileLoc.append(";");
3015             }
3016
3017             if (newFileLoc != null)
3018             {
3019               int histbug = newFileLoc.indexOf("history = ");
3020               histbug += 10;
3021               int diff = histbug == -1 ? -1 : newFileLoc.indexOf(";",
3022                       histbug);
3023               String val = (diff == -1) ? null : newFileLoc.substring(
3024                       histbug, diff);
3025               if (val != null && val.length() >= 4)
3026               {
3027                 if (val.contains("e"))
3028                 {
3029                   if (val.trim().equals("true"))
3030                   {
3031                     val = "1";
3032                   }
3033                   else
3034                   {
3035                     val = "0";
3036                   }
3037                   newFileLoc.replace(histbug, diff, val);
3038                 }
3039               }
3040               // TODO: assemble String[] { pdb files }, String[] { id for each
3041               // file }, orig_fileloc, SequenceI[][] {{ seqs_file 1 }, {
3042               // seqs_file 2}} from hash
3043               final String[] pdbf = pdbfilenames
3044                       .toArray(new String[pdbfilenames.size()]), id = pdbids
3045                       .toArray(new String[pdbids.size()]);
3046               final SequenceI[][] sq = seqmaps
3047                       .toArray(new SequenceI[seqmaps.size()][]);
3048               final String fileloc = newFileLoc.toString(), vid = sviewid;
3049               final AlignFrame alf = af;
3050               final java.awt.Rectangle rect = new java.awt.Rectangle(x, y,
3051                       width, height);
3052               try
3053               {
3054                 javax.swing.SwingUtilities.invokeAndWait(new Runnable()
3055                 {
3056                   @Override
3057                   public void run()
3058                   {
3059                     AppJmol sview = null;
3060                     try
3061                     {
3062                       sview = new AppJmol(pdbf, id, sq, alf.alignPanel,
3063                               useinJmolsuperpos, usetoColourbyseq,
3064                               jmolColouring, fileloc, rect, vid);
3065                       addNewStructureViewer(sview);
3066                     } catch (OutOfMemoryError ex)
3067                     {
3068                       new OOMWarning("restoring structure view for PDB id "
3069                               + id, (OutOfMemoryError) ex.getCause());
3070                       if (sview != null && sview.isVisible())
3071                       {
3072                         sview.closeViewer();
3073                         sview.setVisible(false);
3074                         sview.dispose();
3075                       }
3076                     }
3077                   }
3078                 });
3079               } catch (InvocationTargetException ex)
3080               {
3081                 warn("Unexpected error when opening Jmol view.", ex);
3082
3083               } catch (InterruptedException e)
3084               {
3085                 // e.printStackTrace();
3086               }
3087             }
3088
3089           }
3090           else
3091           // if (comp != null)
3092           {
3093             // NOTE: if the jalview project is part of a shared session then
3094             // view synchronization should/could be done here.
3095
3096             // add mapping for sequences in this view to an already open Jmol
3097             // instance
3098             for (File id : oldFiles.keySet())
3099             {
3100               // add this and any other pdb files that should be present in the
3101               // viewer
3102               Object[] filedat = oldFiles.get(id);
3103               String pdbFile = (String) filedat[0];
3104               SequenceI[] seq = ((Vector<SequenceI>) filedat[2])
3105                       .toArray(new SequenceI[0]);
3106               comp.jmb.ssm.setMapping(seq, null, pdbFile,
3107                       jalview.io.AppletFormatAdapter.FILE);
3108               comp.jmb.addSequenceForStructFile(pdbFile, seq);
3109             }
3110             // and add the AlignmentPanel's reference to the Jmol view
3111             comp.addAlignmentPanel(ap);
3112             if (useinJmolsuperpos)
3113             {
3114               comp.useAlignmentPanelForSuperposition(ap);
3115             }
3116             else
3117             {
3118               comp.excludeAlignmentPanelForSuperposition(ap);
3119             }
3120             if (usetoColourbyseq)
3121             {
3122               comp.useAlignmentPanelForColourbyseq(ap, !jmolColouring);
3123             }
3124             else
3125             {
3126               comp.excludeAlignmentPanelForColourbyseq(ap);
3127             }
3128           }
3129         }
3130       }
3131     }
3132     // and finally return.
3133     return af;
3134   }
3135   Vector<AppJmol> newStructureViewers=null;
3136   protected void addNewStructureViewer(AppJmol sview)
3137   {
3138     if (newStructureViewers!=null)
3139     {
3140       sview.jmb.setFinishedLoadingFromArchive(false);
3141       newStructureViewers.add(sview);
3142     }
3143   }
3144   protected void setLoadingFinishedForNewStructureViewers()
3145   {
3146     if (newStructureViewers!=null)
3147     {
3148       for (AppJmol sview:newStructureViewers)
3149       {
3150         sview.jmb.setFinishedLoadingFromArchive(true);
3151       }
3152       newStructureViewers.clear();
3153       newStructureViewers=null;
3154     }
3155   }
3156
3157   AlignFrame loadViewport(String file, JSeq[] JSEQ, Vector hiddenSeqs,
3158           Alignment al, JalviewModelSequence jms, Viewport view,
3159           String uniqueSeqSetId, String viewId,
3160           ArrayList<JvAnnotRow> autoAlan)
3161   {
3162     AlignFrame af = null;
3163     af = new AlignFrame(al, view.getWidth(), view.getHeight(),
3164             uniqueSeqSetId, viewId);
3165
3166     af.setFileName(file, "Jalview");
3167
3168     for (int i = 0; i < JSEQ.length; i++)
3169     {
3170       af.viewport.setSequenceColour(af.viewport.getAlignment()
3171               .getSequenceAt(i), new java.awt.Color(JSEQ[i].getColour()));
3172     }
3173
3174     af.viewport.gatherViewsHere = view.getGatheredViews();
3175
3176     if (view.getSequenceSetId() != null)
3177     {
3178       jalview.gui.AlignViewport av = (jalview.gui.AlignViewport) viewportsAdded
3179               .get(uniqueSeqSetId);
3180
3181       af.viewport.setSequenceSetId(uniqueSeqSetId);
3182       if (av != null)
3183       {
3184         // propagate shared settings to this new view
3185         af.viewport.historyList = av.historyList;
3186         af.viewport.redoList = av.redoList;
3187       }
3188       else
3189       {
3190         viewportsAdded.put(uniqueSeqSetId, af.viewport);
3191       }
3192       // TODO: check if this method can be called repeatedly without
3193       // side-effects if alignpanel already registered.
3194       PaintRefresher.Register(af.alignPanel, uniqueSeqSetId);
3195     }
3196     // apply Hidden regions to view.
3197     if (hiddenSeqs != null)
3198     {
3199       for (int s = 0; s < JSEQ.length; s++)
3200       {
3201         jalview.datamodel.SequenceGroup hidden = new jalview.datamodel.SequenceGroup();
3202
3203         for (int r = 0; r < JSEQ[s].getHiddenSequencesCount(); r++)
3204         {
3205           hidden.addSequence(
3206                   al.getSequenceAt(JSEQ[s].getHiddenSequences(r)), false);
3207         }
3208         af.viewport.hideRepSequences(al.getSequenceAt(s), hidden);
3209       }
3210
3211       jalview.datamodel.SequenceI[] hseqs = new jalview.datamodel.SequenceI[hiddenSeqs
3212               .size()];
3213
3214       for (int s = 0; s < hiddenSeqs.size(); s++)
3215       {
3216         hseqs[s] = (jalview.datamodel.SequenceI) hiddenSeqs.elementAt(s);
3217       }
3218
3219       af.viewport.hideSequence(hseqs);
3220
3221     }
3222     // recover view properties and display parameters
3223     if (view.getViewName() != null)
3224     {
3225       af.viewport.viewName = view.getViewName();
3226       af.setInitialTabVisible();
3227     }
3228     af.setBounds(view.getXpos(), view.getYpos(), view.getWidth(),
3229             view.getHeight());
3230
3231     af.viewport.setShowAnnotation(view.getShowAnnotation());
3232     af.viewport.setAbovePIDThreshold(view.getPidSelected());
3233
3234     af.viewport.setColourText(view.getShowColourText());
3235
3236     af.viewport.setConservationSelected(view.getConservationSelected());
3237     af.viewport.setShowJVSuffix(view.getShowFullId());
3238     af.viewport.rightAlignIds = view.getRightAlignIds();
3239     af.viewport.setFont(new java.awt.Font(view.getFontName(), view
3240             .getFontStyle(), view.getFontSize()));
3241     af.alignPanel.fontChanged();
3242     af.viewport.setRenderGaps(view.getRenderGaps());
3243     af.viewport.setWrapAlignment(view.getWrapAlignment());
3244     af.alignPanel.setWrapAlignment(view.getWrapAlignment());
3245     af.viewport.setShowAnnotation(view.getShowAnnotation());
3246     af.alignPanel.setAnnotationVisible(view.getShowAnnotation());
3247
3248     af.viewport.setShowBoxes(view.getShowBoxes());
3249
3250     af.viewport.setShowText(view.getShowText());
3251
3252     af.viewport.textColour = new java.awt.Color(view.getTextCol1());
3253     af.viewport.textColour2 = new java.awt.Color(view.getTextCol2());
3254     af.viewport.thresholdTextColour = view.getTextColThreshold();
3255     af.viewport.setShowUnconserved(view.hasShowUnconserved() ? view
3256             .isShowUnconserved() : false);
3257     af.viewport.setStartRes(view.getStartRes());
3258     af.viewport.setStartSeq(view.getStartSeq());
3259
3260     ColourSchemeI cs = null;
3261     // apply colourschemes
3262     if (view.getBgColour() != null)
3263     {
3264       if (view.getBgColour().startsWith("ucs"))
3265       {
3266         cs = GetUserColourScheme(jms, view.getBgColour());
3267       }
3268       else if (view.getBgColour().startsWith("Annotation"))
3269       {
3270         // int find annotation
3271         if (af.viewport.getAlignment().getAlignmentAnnotation() != null)
3272         {
3273           for (int i = 0; i < af.viewport.getAlignment()
3274                   .getAlignmentAnnotation().length; i++)
3275           {
3276             if (af.viewport.getAlignment().getAlignmentAnnotation()[i].label
3277                     .equals(view.getAnnotationColours().getAnnotation()))
3278             {
3279               if (af.viewport.getAlignment().getAlignmentAnnotation()[i]
3280                       .getThreshold() == null)
3281               {
3282                 af.viewport.getAlignment().getAlignmentAnnotation()[i]
3283                         .setThreshold(new jalview.datamodel.GraphLine(view
3284                                 .getAnnotationColours().getThreshold(),
3285                                 "Threshold", java.awt.Color.black)
3286
3287                         );
3288               }
3289
3290               if (view.getAnnotationColours().getColourScheme()
3291                       .equals("None"))
3292               {
3293                 cs = new AnnotationColourGradient(af.viewport
3294                         .getAlignment().getAlignmentAnnotation()[i],
3295                         new java.awt.Color(view.getAnnotationColours()
3296                                 .getMinColour()), new java.awt.Color(view
3297                                 .getAnnotationColours().getMaxColour()),
3298                         view.getAnnotationColours().getAboveThreshold());
3299               }
3300               else if (view.getAnnotationColours().getColourScheme()
3301                       .startsWith("ucs"))
3302               {
3303                 cs = new AnnotationColourGradient(af.viewport
3304                         .getAlignment().getAlignmentAnnotation()[i],
3305                         GetUserColourScheme(jms, view
3306                                 .getAnnotationColours().getColourScheme()),
3307                         view.getAnnotationColours().getAboveThreshold());
3308               }
3309               else
3310               {
3311                 cs = new AnnotationColourGradient(af.viewport
3312                         .getAlignment().getAlignmentAnnotation()[i],
3313                         ColourSchemeProperty.getColour(al, view
3314                                 .getAnnotationColours().getColourScheme()),
3315                         view.getAnnotationColours().getAboveThreshold());
3316               }
3317               if (view.getAnnotationColours().hasPerSequence())
3318               {
3319                 ((AnnotationColourGradient)cs).setSeqAssociated(view.getAnnotationColours().isPerSequence());
3320               }
3321               if (view.getAnnotationColours().hasPredefinedColours())
3322               {
3323                 ((AnnotationColourGradient)cs).setPredefinedColours(view.getAnnotationColours().isPredefinedColours());
3324               }
3325               // Also use these settings for all the groups
3326               if (al.getGroups() != null)
3327               {
3328                 for (int g = 0; g < al.getGroups().size(); g++)
3329                 {
3330                   jalview.datamodel.SequenceGroup sg = al.getGroups()
3331                           .get(g);
3332
3333                   if (sg.cs == null)
3334                   {
3335                     continue;
3336                   }
3337
3338                   /*
3339                    * if
3340                    * (view.getAnnotationColours().getColourScheme().equals("None"
3341                    * )) { sg.cs = new AnnotationColourGradient(
3342                    * af.viewport.getAlignment().getAlignmentAnnotation()[i], new
3343                    * java.awt.Color(view.getAnnotationColours().
3344                    * getMinColour()), new
3345                    * java.awt.Color(view.getAnnotationColours().
3346                    * getMaxColour()),
3347                    * view.getAnnotationColours().getAboveThreshold()); } else
3348                    */
3349                   {
3350                     sg.cs = new AnnotationColourGradient(af.viewport
3351                             .getAlignment().getAlignmentAnnotation()[i],
3352                             sg.cs, view.getAnnotationColours()
3353                                     .getAboveThreshold());
3354                     if (cs instanceof AnnotationColourGradient) 
3355                     {
3356                       if (view.getAnnotationColours().hasPerSequence())
3357                       { 
3358                         ((AnnotationColourGradient)cs).setSeqAssociated(view.getAnnotationColours().isPerSequence());
3359                       }
3360                       if (view.getAnnotationColours().hasPredefinedColours())
3361                       {
3362                         ((AnnotationColourGradient)cs).setPredefinedColours(view.getAnnotationColours().isPredefinedColours());
3363                       }
3364                     }
3365                   }
3366
3367                 }
3368               }
3369
3370               break;
3371             }
3372
3373           }
3374         }
3375       }
3376       else
3377       {
3378         cs = ColourSchemeProperty.getColour(al, view.getBgColour());
3379       }
3380
3381       if (cs != null)
3382       {
3383         cs.setThreshold(view.getPidThreshold(), true);
3384         cs.setConsensus(af.viewport.getSequenceConsensusHash());
3385       }
3386     }
3387
3388     af.viewport.setGlobalColourScheme(cs);
3389     af.viewport.setColourAppliesToAllGroups(false);
3390
3391     if (view.getConservationSelected() && cs != null)
3392     {
3393       cs.setConservationInc(view.getConsThreshold());
3394     }
3395
3396     af.changeColour(cs);
3397
3398     af.viewport.setColourAppliesToAllGroups(true);
3399
3400     if (view.getShowSequenceFeatures())
3401     {
3402       af.viewport.showSequenceFeatures = true;
3403     }
3404     if (view.hasCentreColumnLabels())
3405     {
3406       af.viewport.setCentreColumnLabels(view.getCentreColumnLabels());
3407     }
3408     if (view.hasIgnoreGapsinConsensus())
3409     {
3410       af.viewport.setIgnoreGapsConsensus(view.getIgnoreGapsinConsensus(),
3411               null);
3412     }
3413     if (view.hasFollowHighlight())
3414     {
3415       af.viewport.followHighlight = view.getFollowHighlight();
3416     }
3417     if (view.hasFollowSelection())
3418     {
3419       af.viewport.followSelection = view.getFollowSelection();
3420     }
3421     if (view.hasShowConsensusHistogram())
3422     {
3423       af.viewport.setShowConsensusHistogram(view
3424               .getShowConsensusHistogram());
3425     }
3426     else
3427     {
3428       af.viewport.setShowConsensusHistogram(true);
3429     }
3430     if (view.hasShowSequenceLogo())
3431     {
3432       af.viewport.setShowSequenceLogo(view.getShowSequenceLogo());
3433     }
3434     else
3435     {
3436       af.viewport.setShowSequenceLogo(false);
3437     }
3438     if (view.hasNormaliseSequenceLogo())
3439     {
3440       af.viewport.setNormaliseSequenceLogo(view.getNormaliseSequenceLogo());
3441     }
3442     if (view.hasShowDbRefTooltip())
3443     {
3444       af.viewport.setShowDbRefs(view.getShowDbRefTooltip());
3445     }
3446     if (view.hasShowNPfeatureTooltip())
3447     {
3448       af.viewport.setShowNpFeats(view.hasShowNPfeatureTooltip());
3449     }
3450     if (view.hasShowGroupConsensus())
3451     {
3452       af.viewport.setShowGroupConsensus(view.getShowGroupConsensus());
3453     }
3454     else
3455     {
3456       af.viewport.setShowGroupConsensus(false);
3457     }
3458     if (view.hasShowGroupConservation())
3459     {
3460       af.viewport.setShowGroupConservation(view.getShowGroupConservation());
3461     }
3462     else
3463     {
3464       af.viewport.setShowGroupConservation(false);
3465     }
3466
3467     // recover featre settings
3468     if (jms.getFeatureSettings() != null)
3469     {
3470       af.viewport.featuresDisplayed = new Hashtable();
3471       String[] renderOrder = new String[jms.getFeatureSettings()
3472               .getSettingCount()];
3473       for (int fs = 0; fs < jms.getFeatureSettings().getSettingCount(); fs++)
3474       {
3475         Setting setting = jms.getFeatureSettings().getSetting(fs);
3476         if (setting.hasMincolour())
3477         {
3478           GraduatedColor gc = setting.hasMin() ? new GraduatedColor(
3479                   new java.awt.Color(setting.getMincolour()),
3480                   new java.awt.Color(setting.getColour()),
3481                   setting.getMin(), setting.getMax()) : new GraduatedColor(
3482                   new java.awt.Color(setting.getMincolour()),
3483                   new java.awt.Color(setting.getColour()), 0, 1);
3484           if (setting.hasThreshold())
3485           {
3486             gc.setThresh(setting.getThreshold());
3487             gc.setThreshType(setting.getThreshstate());
3488           }
3489           gc.setAutoScaled(true); // default
3490           if (setting.hasAutoScale())
3491           {
3492             gc.setAutoScaled(setting.getAutoScale());
3493           }
3494           if (setting.hasColourByLabel())
3495           {
3496             gc.setColourByLabel(setting.getColourByLabel());
3497           }
3498           // and put in the feature colour table.
3499           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setColour(
3500                   setting.getType(), gc);
3501         }
3502         else
3503         {
3504           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setColour(
3505                   setting.getType(),
3506                   new java.awt.Color(setting.getColour()));
3507         }
3508         renderOrder[fs] = setting.getType();
3509         if (setting.hasOrder())
3510           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setOrder(
3511                   setting.getType(), setting.getOrder());
3512         else
3513           af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().setOrder(
3514                   setting.getType(),
3515                   fs / jms.getFeatureSettings().getSettingCount());
3516         if (setting.getDisplay())
3517         {
3518           af.viewport.featuresDisplayed.put(setting.getType(), new Integer(
3519                   setting.getColour()));
3520         }
3521       }
3522       af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().renderOrder = renderOrder;
3523       Hashtable fgtable;
3524       af.alignPanel.seqPanel.seqCanvas.getFeatureRenderer().featureGroups = fgtable = new Hashtable();
3525       for (int gs = 0; gs < jms.getFeatureSettings().getGroupCount(); gs++)
3526       {
3527         Group grp = jms.getFeatureSettings().getGroup(gs);
3528         fgtable.put(grp.getName(), new Boolean(grp.getDisplay()));
3529       }
3530     }
3531
3532     if (view.getHiddenColumnsCount() > 0)
3533     {
3534       for (int c = 0; c < view.getHiddenColumnsCount(); c++)
3535       {
3536         af.viewport.hideColumns(view.getHiddenColumns(c).getStart(), view
3537                 .getHiddenColumns(c).getEnd() // +1
3538                 );
3539       }
3540     }
3541     if (view.getCalcIdParam() != null)
3542     {
3543       for (CalcIdParam calcIdParam : view.getCalcIdParam())
3544       {
3545         if (calcIdParam != null)
3546         {
3547           if (recoverCalcIdParam(calcIdParam, af.viewport))
3548           {
3549           }
3550           else
3551           {
3552             warn("Couldn't recover parameters for "
3553                     + calcIdParam.getCalcId());
3554           }
3555         }
3556       }
3557     }
3558     af.setMenusFromViewport(af.viewport);
3559     // TODO: we don't need to do this if the viewport is aready visible.
3560     Desktop.addInternalFrame(af, view.getTitle(), view.getWidth(),
3561             view.getHeight());
3562     af.alignPanel.updateAnnotation(false, true); // recompute any autoannotation
3563     reorderAutoannotation(af, al, autoAlan);
3564     af.alignPanel.alignmentChanged();
3565     return af;
3566   }
3567
3568   private void reorderAutoannotation(AlignFrame af, Alignment al,
3569           ArrayList<JvAnnotRow> autoAlan)
3570   {
3571     // copy over visualization settings for autocalculated annotation in the
3572     // view
3573     if (al.getAlignmentAnnotation() != null)
3574     {
3575       /**
3576        * Kludge for magic autoannotation names (see JAL-811)
3577        */
3578       String[] magicNames = new String[]
3579       { "Consensus", "Quality", "Conservation" };
3580       JvAnnotRow nullAnnot = new JvAnnotRow(-1, null);
3581       Hashtable<String, JvAnnotRow> visan = new Hashtable<String, JvAnnotRow>();
3582       for (String nm : magicNames)
3583       {
3584         visan.put(nm, nullAnnot);
3585       }
3586       for (JvAnnotRow auan : autoAlan)
3587       {
3588         visan.put(auan.template.label
3589                 + (auan.template.getCalcId() == null ? "" : "\t"
3590                         + auan.template.getCalcId()), auan);
3591       }
3592       int hSize = al.getAlignmentAnnotation().length;
3593       ArrayList<JvAnnotRow> reorder = new ArrayList<JvAnnotRow>();
3594       // work through any autoCalculated annotation already on the view
3595       // removing it if it should be placed in a different location on the
3596       // annotation panel.
3597       List<String> remains = new ArrayList(visan.keySet());
3598       for (int h = 0; h < hSize; h++)
3599       {
3600         jalview.datamodel.AlignmentAnnotation jalan = al
3601                 .getAlignmentAnnotation()[h];
3602         if (jalan.autoCalculated)
3603         {
3604           String k;
3605           JvAnnotRow valan = visan.get(k = jalan.label);
3606           if (jalan.getCalcId() != null)
3607           {
3608             valan = visan.get(k = jalan.label + "\t" + jalan.getCalcId());
3609           }
3610
3611           if (valan != null)
3612           {
3613             // delete the auto calculated row from the alignment
3614             al.deleteAnnotation(jalan, false);
3615             remains.remove(k);
3616             hSize--;
3617             h--;
3618             if (valan != nullAnnot)
3619             {
3620               if (jalan != valan.template)
3621               {
3622                 // newly created autoannotation row instance
3623                 // so keep a reference to the visible annotation row
3624                 // and copy over all relevant attributes
3625                 if (valan.template.graphHeight >= 0)
3626
3627                 {
3628                   jalan.graphHeight = valan.template.graphHeight;
3629                 }
3630                 jalan.visible = valan.template.visible;
3631               }
3632               reorder.add(new JvAnnotRow(valan.order, jalan));
3633             }
3634           }
3635         }
3636       }
3637       // Add any (possibly stale) autocalculated rows that were not appended to
3638       // the view during construction
3639       for (String other : remains)
3640       {
3641         JvAnnotRow othera = visan.get(other);
3642         if (othera != nullAnnot && othera.template.getCalcId() != null
3643                 && othera.template.getCalcId().length() > 0)
3644         {
3645           reorder.add(othera);
3646         }
3647       }
3648       // now put the automatic annotation in its correct place
3649       int s = 0, srt[] = new int[reorder.size()];
3650       JvAnnotRow[] rws = new JvAnnotRow[reorder.size()];
3651       for (JvAnnotRow jvar : reorder)
3652       {
3653         rws[s] = jvar;
3654         srt[s++] = jvar.order;
3655       }
3656       reorder.clear();
3657       jalview.util.QuickSort.sort(srt, rws);
3658       // and re-insert the annotation at its correct position
3659       for (JvAnnotRow jvar : rws)
3660       {
3661         al.addAnnotation(jvar.template, jvar.order);
3662       }
3663       af.alignPanel.adjustAnnotationHeight();
3664     }
3665   }
3666
3667   Hashtable skipList = null;
3668
3669   /**
3670    * TODO remove this method
3671    * 
3672    * @param view
3673    * @return AlignFrame bound to sequenceSetId from view, if one exists. private
3674    *         AlignFrame getSkippedFrame(Viewport view) { if (skipList==null) {
3675    *         throw new Error("Implementation Error. No skipList defined for this
3676    *         Jalview2XML instance."); } return (AlignFrame)
3677    *         skipList.get(view.getSequenceSetId()); }
3678    */
3679
3680   /**
3681    * Check if the Jalview view contained in object should be skipped or not.
3682    * 
3683    * @param object
3684    * @return true if view's sequenceSetId is a key in skipList
3685    */
3686   private boolean skipViewport(JalviewModel object)
3687   {
3688     if (skipList == null)
3689     {
3690       return false;
3691     }
3692     String id;
3693     if (skipList.containsKey(id = object.getJalviewModelSequence()
3694             .getViewport()[0].getSequenceSetId()))
3695     {
3696       if (Cache.log != null && Cache.log.isDebugEnabled())
3697       {
3698         Cache.log.debug("Skipping seuqence set id " + id);
3699       }
3700       return true;
3701     }
3702     return false;
3703   }
3704
3705   public void AddToSkipList(AlignFrame af)
3706   {
3707     if (skipList == null)
3708     {
3709       skipList = new Hashtable();
3710     }
3711     skipList.put(af.getViewport().getSequenceSetId(), af);
3712   }
3713
3714   public void clearSkipList()
3715   {
3716     if (skipList != null)
3717     {
3718       skipList.clear();
3719       skipList = null;
3720     }
3721   }
3722
3723   private void recoverDatasetFor(SequenceSet vamsasSet, Alignment al)
3724   {
3725     jalview.datamodel.Alignment ds = getDatasetFor(vamsasSet.getDatasetId());
3726     Vector dseqs = null;
3727     if (ds == null)
3728     {
3729       // create a list of new dataset sequences
3730       dseqs = new Vector();
3731     }
3732     for (int i = 0, iSize = vamsasSet.getSequenceCount(); i < iSize; i++)
3733     {
3734       Sequence vamsasSeq = vamsasSet.getSequence(i);
3735       ensureJalviewDatasetSequence(vamsasSeq, ds, dseqs);
3736     }
3737     // create a new dataset
3738     if (ds == null)
3739     {
3740       SequenceI[] dsseqs = new SequenceI[dseqs.size()];
3741       dseqs.copyInto(dsseqs);
3742       ds = new jalview.datamodel.Alignment(dsseqs);
3743       debug("Created new dataset " + vamsasSet.getDatasetId()
3744               + " for alignment " + System.identityHashCode(al));
3745       addDatasetRef(vamsasSet.getDatasetId(), ds);
3746     }
3747     // set the dataset for the newly imported alignment.
3748     if (al.getDataset() == null)
3749     {
3750       al.setDataset(ds);
3751     }
3752   }
3753
3754   /**
3755    * 
3756    * @param vamsasSeq
3757    *          sequence definition to create/merge dataset sequence for
3758    * @param ds
3759    *          dataset alignment
3760    * @param dseqs
3761    *          vector to add new dataset sequence to
3762    */
3763   private void ensureJalviewDatasetSequence(Sequence vamsasSeq,
3764           AlignmentI ds, Vector dseqs)
3765   {
3766     // JBP TODO: Check this is called for AlCodonFrames to support recovery of
3767     // xRef Codon Maps
3768     jalview.datamodel.Sequence sq = (jalview.datamodel.Sequence) seqRefIds
3769             .get(vamsasSeq.getId());
3770     jalview.datamodel.SequenceI dsq = null;
3771     if (sq != null && sq.getDatasetSequence() != null)
3772     {
3773       dsq = sq.getDatasetSequence();
3774     }
3775
3776     String sqid = vamsasSeq.getDsseqid();
3777     if (dsq == null)
3778     {
3779       // need to create or add a new dataset sequence reference to this sequence
3780       if (sqid != null)
3781       {
3782         dsq = (jalview.datamodel.SequenceI) seqRefIds.get(sqid);
3783       }
3784       // check again
3785       if (dsq == null)
3786       {
3787         // make a new dataset sequence
3788         dsq = sq.createDatasetSequence();
3789         if (sqid == null)
3790         {
3791           // make up a new dataset reference for this sequence
3792           sqid = seqHash(dsq);
3793         }
3794         dsq.setVamsasId(uniqueSetSuffix + sqid);
3795         seqRefIds.put(sqid, dsq);
3796         if (ds == null)
3797         {
3798           if (dseqs != null)
3799           {
3800             dseqs.addElement(dsq);
3801           }
3802         }
3803         else
3804         {
3805           ds.addSequence(dsq);
3806         }
3807       }
3808       else
3809       {
3810         if (sq != dsq)
3811         { // make this dataset sequence sq's dataset sequence
3812           sq.setDatasetSequence(dsq);
3813           // and update the current dataset alignment
3814           if (ds==null) {
3815             if (dseqs!=null) {
3816               if (!dseqs.contains(dsq))
3817               {
3818                 dseqs.add(dsq);
3819               }
3820             } else {
3821               if (ds.findIndex(dsq)<0)
3822               {
3823                 ds.addSequence(dsq);
3824               }
3825             }
3826           }
3827         }
3828       }
3829     }
3830     // TODO: refactor this as a merge dataset sequence function
3831     // now check that sq (the dataset sequence) sequence really is the union of
3832     // all references to it
3833     // boolean pre = sq.getStart() < dsq.getStart();
3834     // boolean post = sq.getEnd() > dsq.getEnd();
3835     // if (pre || post)
3836     if (sq != dsq)
3837     {
3838       StringBuffer sb = new StringBuffer();
3839       String newres = jalview.analysis.AlignSeq.extractGaps(
3840               jalview.util.Comparison.GapChars, sq.getSequenceAsString());
3841       if (!newres.equalsIgnoreCase(dsq.getSequenceAsString())
3842               && newres.length() > dsq.getLength())
3843       {
3844         // Update with the longer sequence.
3845         synchronized (dsq)
3846         {
3847           /*
3848            * if (pre) { sb.insert(0, newres .substring(0, dsq.getStart() -
3849            * sq.getStart())); dsq.setStart(sq.getStart()); } if (post) {
3850            * sb.append(newres.substring(newres.length() - sq.getEnd() -
3851            * dsq.getEnd())); dsq.setEnd(sq.getEnd()); }
3852            */
3853           dsq.setSequence(newres);
3854         }
3855         // TODO: merges will never happen if we 'know' we have the real dataset
3856         // sequence - this should be detected when id==dssid
3857         System.err.println("DEBUG Notice:  Merged dataset sequence (if you see this often, post at http://issues.jalview.org/browse/JAL-1474)"); // ("
3858         // + (pre ? "prepended" : "") + " "
3859         // + (post ? "appended" : ""));
3860       }
3861     }
3862   }
3863
3864   java.util.Hashtable datasetIds = null;
3865
3866   java.util.IdentityHashMap dataset2Ids = null;
3867
3868   private Alignment getDatasetFor(String datasetId)
3869   {
3870     if (datasetIds == null)
3871     {
3872       datasetIds = new Hashtable();
3873       return null;
3874     }
3875     if (datasetIds.containsKey(datasetId))
3876     {
3877       return (Alignment) datasetIds.get(datasetId);
3878     }
3879     return null;
3880   }
3881
3882   private void addDatasetRef(String datasetId, Alignment dataset)
3883   {
3884     if (datasetIds == null)
3885     {
3886       datasetIds = new Hashtable();
3887     }
3888     datasetIds.put(datasetId, dataset);
3889   }
3890
3891   /**
3892    * make a new dataset ID for this jalview dataset alignment
3893    * 
3894    * @param dataset
3895    * @return
3896    */
3897   private String getDatasetIdRef(jalview.datamodel.Alignment dataset)
3898   {
3899     if (dataset.getDataset() != null)
3900     {
3901       warn("Serious issue!  Dataset Object passed to getDatasetIdRef is not a Jalview DATASET alignment...");
3902     }
3903     String datasetId = makeHashCode(dataset, null);
3904     if (datasetId == null)
3905     {
3906       // make a new datasetId and record it
3907       if (dataset2Ids == null)
3908       {
3909         dataset2Ids = new IdentityHashMap();
3910       }
3911       else
3912       {
3913         datasetId = (String) dataset2Ids.get(dataset);
3914       }
3915       if (datasetId == null)
3916       {
3917         datasetId = "ds" + dataset2Ids.size() + 1;
3918         dataset2Ids.put(dataset, datasetId);
3919       }
3920     }
3921     return datasetId;
3922   }
3923
3924   private void addDBRefs(SequenceI datasetSequence, Sequence sequence)
3925   {
3926     for (int d = 0; d < sequence.getDBRefCount(); d++)
3927     {
3928       DBRef dr = sequence.getDBRef(d);
3929       jalview.datamodel.DBRefEntry entry = new jalview.datamodel.DBRefEntry(
3930               sequence.getDBRef(d).getSource(), sequence.getDBRef(d)
3931                       .getVersion(), sequence.getDBRef(d).getAccessionId());
3932       if (dr.getMapping() != null)
3933       {
3934         entry.setMap(addMapping(dr.getMapping()));
3935       }
3936       datasetSequence.addDBRef(entry);
3937     }
3938   }
3939
3940   private jalview.datamodel.Mapping addMapping(Mapping m)
3941   {
3942     SequenceI dsto = null;
3943     // Mapping m = dr.getMapping();
3944     int fr[] = new int[m.getMapListFromCount() * 2];
3945     Enumeration f = m.enumerateMapListFrom();
3946     for (int _i = 0; f.hasMoreElements(); _i += 2)
3947     {
3948       MapListFrom mf = (MapListFrom) f.nextElement();
3949       fr[_i] = mf.getStart();
3950       fr[_i + 1] = mf.getEnd();
3951     }
3952     int fto[] = new int[m.getMapListToCount() * 2];
3953     f = m.enumerateMapListTo();
3954     for (int _i = 0; f.hasMoreElements(); _i += 2)
3955     {
3956       MapListTo mf = (MapListTo) f.nextElement();
3957       fto[_i] = mf.getStart();
3958       fto[_i + 1] = mf.getEnd();
3959     }
3960     jalview.datamodel.Mapping jmap = new jalview.datamodel.Mapping(dsto,
3961             fr, fto, (int) m.getMapFromUnit(), (int) m.getMapToUnit());
3962     if (m.getMappingChoice() != null)
3963     {
3964       MappingChoice mc = m.getMappingChoice();
3965       if (mc.getDseqFor() != null)
3966       {
3967         String dsfor = "" + mc.getDseqFor();
3968         if (seqRefIds.containsKey(dsfor))
3969         {
3970           /**
3971            * recover from hash
3972            */
3973           jmap.setTo((SequenceI) seqRefIds.get(dsfor));
3974         }
3975         else
3976         {
3977           frefedSequence.add(new Object[]
3978           { dsfor, jmap });
3979         }
3980       }
3981       else
3982       {
3983         /**
3984          * local sequence definition
3985          */
3986         Sequence ms = mc.getSequence();
3987         jalview.datamodel.Sequence djs = null;
3988         String sqid = ms.getDsseqid();
3989         if (sqid != null && sqid.length() > 0)
3990         {
3991           /*
3992            * recover dataset sequence
3993            */
3994           djs = (jalview.datamodel.Sequence) seqRefIds.get(sqid);
3995         }
3996         else
3997         {
3998           System.err
3999                   .println("Warning - making up dataset sequence id for DbRef sequence map reference");
4000           sqid = ((Object) ms).toString(); // make up a new hascode for
4001           // undefined dataset sequence hash
4002           // (unlikely to happen)
4003         }
4004
4005         if (djs == null)
4006         {
4007           /**
4008            * make a new dataset sequence and add it to refIds hash
4009            */
4010           djs = new jalview.datamodel.Sequence(ms.getName(),
4011                   ms.getSequence());
4012           djs.setStart(jmap.getMap().getToLowest());
4013           djs.setEnd(jmap.getMap().getToHighest());
4014           djs.setVamsasId(uniqueSetSuffix + sqid);
4015           jmap.setTo(djs);
4016           seqRefIds.put(sqid, djs);
4017
4018         }
4019         jalview.bin.Cache.log.debug("about to recurse on addDBRefs.");
4020         addDBRefs(djs, ms);
4021
4022       }
4023     }
4024     return (jmap);
4025
4026   }
4027
4028   public jalview.gui.AlignmentPanel copyAlignPanel(AlignmentPanel ap,
4029           boolean keepSeqRefs)
4030   {
4031     initSeqRefs();
4032     jalview.schemabinding.version2.JalviewModel jm = SaveState(ap, null,
4033             null);
4034
4035     if (!keepSeqRefs)
4036     {
4037       clearSeqRefs();
4038       jm.getJalviewModelSequence().getViewport(0).setSequenceSetId(null);
4039     }
4040     else
4041     {
4042       uniqueSetSuffix = "";
4043       jm.getJalviewModelSequence().getViewport(0).setId(null); // we don't
4044       // overwrite the
4045       // view we just
4046       // copied
4047     }
4048     if (this.frefedSequence == null)
4049     {
4050       frefedSequence = new Vector();
4051     }
4052
4053     viewportsAdded = new Hashtable();
4054
4055     AlignFrame af = LoadFromObject(jm, null, false, null);
4056     af.alignPanels.clear();
4057     af.closeMenuItem_actionPerformed(true);
4058
4059     /*
4060      * if(ap.av.getAlignment().getAlignmentAnnotation()!=null) { for(int i=0;
4061      * i<ap.av.getAlignment().getAlignmentAnnotation().length; i++) {
4062      * if(!ap.av.getAlignment().getAlignmentAnnotation()[i].autoCalculated) {
4063      * af.alignPanel.av.getAlignment().getAlignmentAnnotation()[i] =
4064      * ap.av.getAlignment().getAlignmentAnnotation()[i]; } } }
4065      */
4066
4067     return af.alignPanel;
4068   }
4069
4070   /**
4071    * flag indicating if hashtables should be cleared on finalization TODO this
4072    * flag may not be necessary
4073    */
4074   private final boolean _cleartables = true;
4075
4076   private Hashtable jvids2vobj;
4077
4078   /*
4079    * (non-Javadoc)
4080    * 
4081    * @see java.lang.Object#finalize()
4082    */
4083   @Override
4084   protected void finalize() throws Throwable
4085   {
4086     // really make sure we have no buried refs left.
4087     if (_cleartables)
4088     {
4089       clearSeqRefs();
4090     }
4091     this.seqRefIds = null;
4092     this.seqsToIds = null;
4093     super.finalize();
4094   }
4095
4096   private void warn(String msg)
4097   {
4098     warn(msg, null);
4099   }
4100
4101   private void warn(String msg, Exception e)
4102   {
4103     if (Cache.log != null)
4104     {
4105       if (e != null)
4106       {
4107         Cache.log.warn(msg, e);
4108       }
4109       else
4110       {
4111         Cache.log.warn(msg);
4112       }
4113     }
4114     else
4115     {
4116       System.err.println("Warning: " + msg);
4117       if (e != null)
4118       {
4119         e.printStackTrace();
4120       }
4121     }
4122   }
4123
4124   private void debug(String string)
4125   {
4126     debug(string, null);
4127   }
4128
4129   private void debug(String msg, Exception e)
4130   {
4131     if (Cache.log != null)
4132     {
4133       if (e != null)
4134       {
4135         Cache.log.debug(msg, e);
4136       }
4137       else
4138       {
4139         Cache.log.debug(msg);
4140       }
4141     }
4142     else
4143     {
4144       System.err.println("Warning: " + msg);
4145       if (e != null)
4146       {
4147         e.printStackTrace();
4148       }
4149     }
4150   }
4151
4152   /**
4153    * set the object to ID mapping tables used to write/recover objects and XML
4154    * ID strings for the jalview project. If external tables are provided then
4155    * finalize and clearSeqRefs will not clear the tables when the Jalview2XML
4156    * object goes out of scope. - also populates the datasetIds hashtable with
4157    * alignment objects containing dataset sequences
4158    * 
4159    * @param vobj2jv
4160    *          Map from ID strings to jalview datamodel
4161    * @param jv2vobj
4162    *          Map from jalview datamodel to ID strings
4163    * 
4164    * 
4165    */
4166   public void setObjectMappingTables(Hashtable vobj2jv,
4167           IdentityHashMap jv2vobj)
4168   {
4169     this.jv2vobj = jv2vobj;
4170     this.vobj2jv = vobj2jv;
4171     Iterator ds = jv2vobj.keySet().iterator();
4172     String id;
4173     while (ds.hasNext())
4174     {
4175       Object jvobj = ds.next();
4176       id = jv2vobj.get(jvobj).toString();
4177       if (jvobj instanceof jalview.datamodel.Alignment)
4178       {
4179         if (((jalview.datamodel.Alignment) jvobj).getDataset() == null)
4180         {
4181           addDatasetRef(id, (jalview.datamodel.Alignment) jvobj);
4182         }
4183       }
4184       else if (jvobj instanceof jalview.datamodel.Sequence)
4185       {
4186         // register sequence object so the XML parser can recover it.
4187         if (seqRefIds == null)
4188         {
4189           seqRefIds = new Hashtable();
4190         }
4191         if (seqsToIds == null)
4192         {
4193           seqsToIds = new IdentityHashMap();
4194         }
4195         seqRefIds.put(jv2vobj.get(jvobj).toString(), jvobj);
4196         seqsToIds.put(jvobj, id);
4197       }
4198       else if (jvobj instanceof jalview.datamodel.AlignmentAnnotation)
4199       {
4200         if (annotationIds == null)
4201         {
4202           annotationIds = new Hashtable();
4203         }
4204         String anid;
4205         annotationIds.put(anid = jv2vobj.get(jvobj).toString(), jvobj);
4206         jalview.datamodel.AlignmentAnnotation jvann = (jalview.datamodel.AlignmentAnnotation) jvobj;
4207         if (jvann.annotationId == null)
4208         {
4209           jvann.annotationId = anid;
4210         }
4211         if (!jvann.annotationId.equals(anid))
4212         {
4213           // TODO verify that this is the correct behaviour
4214           this.warn("Overriding Annotation ID for " + anid
4215                   + " from different id : " + jvann.annotationId);
4216           jvann.annotationId = anid;
4217         }
4218       }
4219       else if (jvobj instanceof String)
4220       {
4221         if (jvids2vobj == null)
4222         {
4223           jvids2vobj = new Hashtable();
4224           jvids2vobj.put(jvobj, jv2vobj.get(jvobj).toString());
4225         }
4226       }
4227       else
4228         Cache.log.debug("Ignoring " + jvobj.getClass() + " (ID = " + id);
4229     }
4230   }
4231
4232   /**
4233    * set the uniqueSetSuffix used to prefix/suffix object IDs for jalview
4234    * objects created from the project archive. If string is null (default for
4235    * construction) then suffix will be set automatically.
4236    * 
4237    * @param string
4238    */
4239   public void setUniqueSetSuffix(String string)
4240   {
4241     uniqueSetSuffix = string;
4242
4243   }
4244
4245   /**
4246    * uses skipList2 as the skipList for skipping views on sequence sets
4247    * associated with keys in the skipList
4248    * 
4249    * @param skipList2
4250    */
4251   public void setSkipList(Hashtable skipList2)
4252   {
4253     skipList = skipList2;
4254   }
4255
4256 }