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