741571210680f2c4b334d23553db31fd2f860892
[jalview.git] / src / jalview / structure / StructureSelectionManager.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8)
3  * Copyright (C) 2012 J Procter, AM Waterhouse, LM Lui, J Engelhardt, G Barton, M Clamp, S Searle
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
10  *  
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  */
18 package jalview.structure;
19
20 import java.io.*;
21 import java.util.*;
22
23 import MCview.*;
24 import jalview.analysis.*;
25 import jalview.api.AlignmentViewPanel;
26 import jalview.api.StructureSelectionManagerProvider;
27 import jalview.datamodel.*;
28
29 public class StructureSelectionManager
30 {
31   static IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> instances;
32
33   StructureMapping[] mappings;
34
35   /**
36    * debug function - write all mappings to stdout
37    */
38   public void reportMapping()
39   {
40     if (mappings == null)
41     {
42       System.err.println("reportMapping: No PDB/Sequence mappings.");
43     }
44     else
45     {
46       System.err.println("reportMapping: There are " + mappings.length
47               + " mappings.");
48       for (int m = 0; m < mappings.length; m++)
49       {
50         System.err.println("mapping " + m + " : " + mappings[m].pdbfile);
51       }
52     }
53   }
54
55   Hashtable mappingData = new Hashtable();
56
57   public static StructureSelectionManager getStructureSelectionManager(
58           StructureSelectionManagerProvider context)
59   {
60     if (context==null)
61     {
62       throw new Error("Implementation error. Structure selection manager's context is 'null'", new NullPointerException("SSM context is null"));
63     }
64     if (instances == null)
65     {
66       instances = new java.util.IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager>();
67     }
68     StructureSelectionManager instance = instances.get(context);
69     if (instance == null)
70     {
71       instances.put(context, instance = new StructureSelectionManager());
72     }
73     return instance;
74   }
75
76   /**
77    * flag controlling whether SeqMappings are relayed from received sequence
78    * mouse over events to other sequences
79    */
80   boolean relaySeqMappings = true;
81
82   /**
83    * Enable or disable relay of seqMapping events to other sequences. You might
84    * want to do this if there are many sequence mappings and the host computer
85    * is slow
86    * 
87    * @param relay
88    */
89   public void setRelaySeqMappings(boolean relay)
90   {
91     relaySeqMappings = relay;
92   }
93
94   /**
95    * get the state of the relay seqMappings flag.
96    * 
97    * @return true if sequence mouse overs are being relayed to other mapped
98    *         sequences
99    */
100   public boolean isRelaySeqMappingsEnabled()
101   {
102     return relaySeqMappings;
103   }
104
105   Vector listeners = new Vector();
106
107   /**
108    * register a listener for alignment sequence mouseover events
109    * 
110    * @param svl
111    */
112   public void addStructureViewerListener(Object svl)
113   {
114     if (!listeners.contains(svl))
115     {
116       listeners.addElement(svl);
117     }
118   }
119
120   public String alreadyMappedToFile(String pdbid)
121   {
122     if (mappings != null)
123     {
124       for (int i = 0; i < mappings.length; i++)
125       {
126         if (mappings[i].getPdbId().equals(pdbid))
127         {
128           return mappings[i].pdbfile;
129         }
130       }
131     }
132     return null;
133   }
134
135   /**
136    * create sequence structure mappings between each sequence and the given
137    * pdbFile (retrieved via the given protocol).
138    * 
139    * @param sequence
140    *          - one or more sequences to be mapped to pdbFile
141    * @param targetChains
142    *          - optional chain specification for mapping each sequence to pdb
143    *          (may be nill, individual elements may be nill)
144    * @param pdbFile
145    *          - structure data resource
146    * @param protocol
147    *          - how to resolve data from resource
148    * @return null or the structure data parsed as a pdb file
149    */
150   synchronized public MCview.PDBfile setMapping(SequenceI[] sequence,
151           String[] targetChains, String pdbFile, String protocol)
152   {
153     /*
154      * There will be better ways of doing this in the future, for now we'll use
155      * the tried and tested MCview pdb mapping
156      */
157     MCview.PDBfile pdb = null;
158     try
159     {
160       pdb = new MCview.PDBfile(pdbFile, protocol);
161     } catch (Exception ex)
162     {
163       ex.printStackTrace();
164       return null;
165     }
166
167     String targetChain;
168     for (int s = 0; s < sequence.length; s++)
169     {
170       boolean infChain = true;
171       if (targetChains != null && targetChains[s] != null)
172       {
173         infChain = false;
174         targetChain = targetChains[s];
175       }
176       else if (sequence[s].getName().indexOf("|") > -1)
177       {
178         targetChain = sequence[s].getName().substring(
179                 sequence[s].getName().lastIndexOf("|") + 1);
180         if (targetChain.length() > 1)
181         {
182           if (targetChain.trim().length() == 0)
183           {
184             targetChain = " ";
185           }
186           else
187           {
188             // not a valid chain identifier
189             targetChain = "";
190           }
191         }
192       }
193       else
194         targetChain = "";
195
196       int max = -10;
197       AlignSeq maxAlignseq = null;
198       String maxChainId = " ";
199       PDBChain maxChain = null;
200       boolean first = true;
201       for (int i = 0; i < pdb.chains.size(); i++)
202       {
203         PDBChain chain = ((PDBChain) pdb.chains.elementAt(i));
204         if (targetChain.length() > 0 && !targetChain.equals(chain.id)
205                 && !infChain)
206         {
207           continue; // don't try to map chains don't match.
208         }
209         // TODO: correctly determine sequence type for mixed na/peptide
210         // structures
211         AlignSeq as = new AlignSeq(sequence[s],
212                 ((PDBChain) pdb.chains.elementAt(i)).sequence,
213                 ((PDBChain) pdb.chains.elementAt(i)).isNa ? AlignSeq.DNA
214                         : AlignSeq.PEP);
215         as.calcScoreMatrix();
216         as.traceAlignment();
217
218         if (first || as.maxscore > max
219                 || (as.maxscore == max && chain.id.equals(targetChain)))
220         {
221           first = false;
222           maxChain = chain;
223           max = as.maxscore;
224           maxAlignseq = as;
225           maxChainId = chain.id;
226         }
227       }
228       if (maxChain == null)
229       {
230         continue;
231       }
232       final StringBuffer mappingDetails = new StringBuffer();
233       mappingDetails.append("\n\nPDB Sequence is :\nSequence = "
234               + maxChain.sequence.getSequenceAsString());
235       mappingDetails.append("\nNo of residues = "
236               + maxChain.residues.size() + "\n\n");
237       PrintStream ps = new PrintStream(System.out)
238       {
239         public void print(String x)
240         {
241           mappingDetails.append(x);
242         }
243
244         public void println()
245         {
246           mappingDetails.append("\n");
247         }
248       };
249
250       maxAlignseq.printAlignment(ps);
251
252       mappingDetails.append("\nPDB start/end " + maxAlignseq.seq2start
253               + " " + maxAlignseq.seq2end);
254       mappingDetails.append("\nSEQ start/end "
255               + (maxAlignseq.seq1start + sequence[s].getStart() - 1) + " "
256               + (maxAlignseq.seq1end + sequence[s].getEnd() - 1));
257
258       maxChain.makeExactMapping(maxAlignseq, sequence[s]);
259
260       maxChain.transferRESNUMFeatures(sequence[s], null);
261
262       // allocate enough slots to store the mapping from positions in
263       // sequence[s] to the associated chain
264       int[][] mapping = new int[sequence[s].findPosition(sequence[s]
265               .getLength()) + 2][2];
266       int resNum = -10000;
267       int index = 0;
268
269       do
270       {
271         Atom tmp = (Atom) maxChain.atoms.elementAt(index);
272         if (resNum != tmp.resNumber && tmp.alignmentMapping != -1)
273         {
274           resNum = tmp.resNumber;
275           mapping[tmp.alignmentMapping + 1][0] = tmp.resNumber;
276           mapping[tmp.alignmentMapping + 1][1] = tmp.atomIndex;
277         }
278
279         index++;
280       } while (index < maxChain.atoms.size());
281
282       if (mappings == null)
283       {
284         mappings = new StructureMapping[1];
285       }
286       else
287       {
288         StructureMapping[] tmp = new StructureMapping[mappings.length + 1];
289         System.arraycopy(mappings, 0, tmp, 0, mappings.length);
290         mappings = tmp;
291       }
292
293       if (protocol.equals(jalview.io.AppletFormatAdapter.PASTE))
294         pdbFile = "INLINE" + pdb.id;
295
296       mappings[mappings.length - 1] = new StructureMapping(sequence[s],
297               pdbFile, pdb.id, maxChainId, mapping,
298               mappingDetails.toString());
299       maxChain.transferResidueAnnotation(mappings[mappings.length - 1]);
300     }
301     // ///////
302
303     return pdb;
304   }
305
306   public void removeStructureViewerListener(Object svl, String[] pdbfiles)
307   {
308     listeners.removeElement(svl);
309     if (svl instanceof SequenceListener)
310     {
311       for (int i = 0; i < listeners.size(); i++)
312       {
313         if (listeners.elementAt(i) instanceof StructureListener)
314         {
315           ((StructureListener) listeners.elementAt(i))
316                   .releaseReferences(svl);
317         }
318       }
319     }
320
321     if (pdbfiles == null)
322     {
323       return;
324     }
325     boolean removeMapping = true;
326     String[] handlepdbs;
327     Vector pdbs = new Vector();
328     for (int i = 0; i < pdbfiles.length; pdbs.addElement(pdbfiles[i++]))
329       ;
330     StructureListener sl;
331     for (int i = 0; i < listeners.size(); i++)
332     {
333       if (listeners.elementAt(i) instanceof StructureListener)
334       {
335         sl = (StructureListener) listeners.elementAt(i);
336         handlepdbs = sl.getPdbFile();
337         for (int j = 0; j < handlepdbs.length; j++)
338         {
339           if (pdbs.contains(handlepdbs[j]))
340           {
341             pdbs.removeElement(handlepdbs[j]);
342           }
343         }
344
345       }
346     }
347
348     if (pdbs.size() > 0 && mappings != null)
349     {
350       Vector tmp = new Vector();
351       for (int i = 0; i < mappings.length; i++)
352       {
353         if (!pdbs.contains(mappings[i].pdbfile))
354         {
355           tmp.addElement(mappings[i]);
356         }
357       }
358
359       mappings = new StructureMapping[tmp.size()];
360       tmp.copyInto(mappings);
361     }
362   }
363
364   public void mouseOverStructure(int pdbResNum, String chain, String pdbfile)
365   {
366     if (listeners == null)
367     {
368       // old or prematurely sent event
369       return;
370     }
371     boolean hasSequenceListeners = handlingVamsasMo || seqmappings != null;
372     SearchResults results = null;
373     SequenceI lastseq = null;
374     int lastipos = -1, indexpos;
375     for (int i = 0; i < listeners.size(); i++)
376     {
377       if (listeners.elementAt(i) instanceof SequenceListener)
378       {
379         if (results == null)
380         {
381           results = new SearchResults();
382         }
383         if (mappings != null)
384         {
385           for (int j = 0; j < mappings.length; j++)
386           {
387             if (mappings[j].pdbfile.equals(pdbfile)
388                     && mappings[j].pdbchain.equals(chain))
389             {
390               indexpos = mappings[j].getSeqPos(pdbResNum);
391               if (lastipos != indexpos && lastseq != mappings[j].sequence)
392               {
393                 results.addResult(mappings[j].sequence, indexpos, indexpos);
394                 lastipos = indexpos;
395                 lastseq = mappings[j].sequence;
396                 // construct highlighted sequence list
397                 if (seqmappings != null)
398                 {
399
400                   Enumeration e = seqmappings.elements();
401                   while (e.hasMoreElements())
402
403                   {
404                     ((AlignedCodonFrame) e.nextElement()).markMappedRegion(
405                             mappings[j].sequence, indexpos, results);
406                   }
407                 }
408               }
409
410             }
411           }
412         }
413       }
414     }
415     if (results != null)
416     {
417       for (int i = 0; i < listeners.size(); i++)
418       {
419         Object li = listeners.elementAt(i);
420         if (li instanceof SequenceListener)
421           ((SequenceListener) li).highlightSequence(results);
422       }
423     }
424   }
425
426   Vector seqmappings = null; // should be a simpler list of mapped seuqence
427
428   /**
429    * highlight regions associated with a position (indexpos) in seq
430    * 
431    * @param seq
432    *          the sequeence that the mouse over occured on
433    * @param indexpos
434    *          the absolute position being mouseovered in seq (0 to seq.length())
435    * @param index
436    *          the sequence position (if -1, seq.findPosition is called to
437    *          resolve the residue number)
438    */
439   public void mouseOverSequence(SequenceI seq, int indexpos, int index,
440           VamsasSource source)
441   {
442     boolean hasSequenceListeners = handlingVamsasMo || seqmappings != null;
443     SearchResults results = null;
444     if (index == -1)
445       index = seq.findPosition(indexpos);
446     StructureListener sl;
447     int atomNo = 0;
448     for (int i = 0; i < listeners.size(); i++)
449     {
450       Object listener = listeners.elementAt(i);
451       if (listener == source)
452       {
453         continue;
454       }
455       if (listener instanceof StructureListener)
456       {
457         sl = (StructureListener) listener;
458         if (mappings == null)
459         {
460           continue;
461         }
462         for (int j = 0; j < mappings.length; j++)
463         {
464           if (mappings[j].sequence == seq
465                   || mappings[j].sequence == seq.getDatasetSequence())
466           {
467             atomNo = mappings[j].getAtomNum(index);
468
469             if (atomNo > 0)
470             {
471               sl.highlightAtom(atomNo, mappings[j].getPDBResNum(index),
472                       mappings[j].pdbchain, mappings[j].pdbfile);
473             }
474           }
475         }
476       }
477       else
478       {
479         if (relaySeqMappings && hasSequenceListeners
480                 && listener instanceof SequenceListener)
481         {
482           // DEBUG
483           // System.err.println("relay Seq " + seq.getDisplayId(false) + " " +
484           // index);
485
486           if (results == null)
487           {
488             results = new SearchResults();
489             if (index >= seq.getStart() && index <= seq.getEnd())
490             {
491               // construct highlighted sequence list
492
493               if (seqmappings != null)
494               {
495                 Enumeration e = seqmappings.elements();
496                 while (e.hasMoreElements())
497
498                 {
499                   ((AlignedCodonFrame) e.nextElement()).markMappedRegion(
500                           seq, index, results);
501                 }
502               }
503               // hasSequenceListeners = results.getSize() > 0;
504               if (handlingVamsasMo)
505               {
506                 // maybe have to resolve seq to a dataset seqeunce...
507                 // add in additional direct sequence and/or dataset sequence
508                 // highlighting
509                 results.addResult(seq, index, index);
510               }
511             }
512           }
513           if (hasSequenceListeners)
514           {
515             ((SequenceListener) listener).highlightSequence(results);
516           }
517         }
518         else if (listener instanceof VamsasListener && !handlingVamsasMo)
519         {
520           // DEBUG
521           // System.err.println("Vamsas from Seq " + seq.getDisplayId(false) + "
522           // " +
523           // index);
524           // pass the mouse over and absolute position onto the
525           // VamsasListener(s)
526           ((VamsasListener) listener).mouseOver(seq, indexpos, source);
527         }
528         else if (listener instanceof SecondaryStructureListener)
529         {
530           ((SecondaryStructureListener) listener).mouseOverSequence(seq,
531                   indexpos);
532         }
533       }
534     }
535   }
536
537   /**
538    * true if a mouse over event from an external (ie Vamsas) source is being
539    * handled
540    */
541   boolean handlingVamsasMo = false;
542
543   long lastmsg = 0;
544
545   /**
546    * as mouseOverSequence but only route event to SequenceListeners
547    * 
548    * @param sequenceI
549    * @param position
550    *          in an alignment sequence
551    */
552   public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
553           VamsasSource source)
554   {
555     handlingVamsasMo = true;
556     long msg = sequenceI.hashCode() * (1 + position);
557     if (lastmsg != msg)
558     {
559       lastmsg = msg;
560       mouseOverSequence(sequenceI, position, -1, source);
561     }
562     handlingVamsasMo = false;
563   }
564
565   public Annotation[] colourSequenceFromStructure(SequenceI seq,
566           String pdbid)
567   {
568     return null;
569     // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
570     // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
571     /*
572      * Annotation [] annotations = new Annotation[seq.getLength()];
573      * 
574      * StructureListener sl; int atomNo = 0; for (int i = 0; i <
575      * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
576      * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
577      * 
578      * for (int j = 0; j < mappings.length; j++) {
579      * 
580      * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
581      * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
582      * System.out.println(pdbid+" "+mappings[j].getPdbId() +"
583      * "+mappings[j].pdbfile);
584      * 
585      * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
586      * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
587      * 
588      * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
589      * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
590      * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
591      * mappings[j].pdbfile); }
592      * 
593      * annotations[index] = new Annotation("X",null,' ',0,col); } return
594      * annotations; } } } }
595      * 
596      * return annotations;
597      */
598   }
599
600   public void structureSelectionChanged()
601   {
602   }
603
604   public void sequenceSelectionChanged()
605   {
606   }
607
608   public void sequenceColoursChanged(Object source)
609   {
610     StructureListener sl;
611     for (int i = 0; i < listeners.size(); i++)
612     {
613       if (listeners.elementAt(i) instanceof StructureListener)
614       {
615         sl = (StructureListener) listeners.elementAt(i);
616         sl.updateColours(source);
617       }
618     }
619   }
620
621   public StructureMapping[] getMapping(String pdbfile)
622   {
623     Vector tmp = new Vector();
624     if (mappings != null)
625     {
626       for (int i = 0; i < mappings.length; i++)
627       {
628         if (mappings[i].pdbfile.equals(pdbfile))
629         {
630           tmp.addElement(mappings[i]);
631         }
632       }
633     }
634     StructureMapping[] ret = new StructureMapping[tmp.size()];
635     for (int i = 0; i < tmp.size(); i++)
636     {
637       ret[i] = (StructureMapping) tmp.elementAt(i);
638     }
639
640     return ret;
641   }
642
643   public String printMapping(String pdbfile)
644   {
645     StringBuffer sb = new StringBuffer();
646     for (int i = 0; i < mappings.length; i++)
647     {
648       if (mappings[i].pdbfile.equals(pdbfile))
649       {
650         sb.append(mappings[i].mappingDetails);
651       }
652     }
653
654     return sb.toString();
655   }
656
657   private int[] seqmappingrefs = null; // refcount for seqmappings elements
658
659   private synchronized void modifySeqMappingList(boolean add,
660           AlignedCodonFrame[] codonFrames)
661   {
662     if (!add && (seqmappings == null || seqmappings.size() == 0))
663       return;
664     if (seqmappings == null)
665       seqmappings = new Vector();
666     if (codonFrames != null && codonFrames.length > 0)
667     {
668       for (int cf = 0; cf < codonFrames.length; cf++)
669       {
670         if (seqmappings.contains(codonFrames[cf]))
671         {
672           if (add)
673           {
674             seqmappingrefs[seqmappings.indexOf(codonFrames[cf])]++;
675           }
676           else
677           {
678             if (--seqmappingrefs[seqmappings.indexOf(codonFrames[cf])] <= 0)
679             {
680               int pos = seqmappings.indexOf(codonFrames[cf]);
681               int[] nr = new int[seqmappingrefs.length - 1];
682               if (pos > 0)
683               {
684                 System.arraycopy(seqmappingrefs, 0, nr, 0, pos);
685               }
686               if (pos < seqmappingrefs.length - 1)
687               {
688                 System.arraycopy(seqmappingrefs, pos + 1, nr, 0,
689                         seqmappingrefs.length - pos - 2);
690               }
691             }
692           }
693         }
694         else
695         {
696           if (add)
697           {
698             seqmappings.addElement(codonFrames[cf]);
699
700             int[] nsr = new int[(seqmappingrefs == null) ? 1
701                     : seqmappingrefs.length + 1];
702             if (seqmappingrefs != null && seqmappingrefs.length > 0)
703               System.arraycopy(seqmappingrefs, 0, nsr, 0,
704                       seqmappingrefs.length);
705             nsr[(seqmappingrefs == null) ? 0 : seqmappingrefs.length] = 1;
706             seqmappingrefs = nsr;
707           }
708         }
709       }
710     }
711   }
712
713   public void removeMappings(AlignedCodonFrame[] codonFrames)
714   {
715     modifySeqMappingList(false, codonFrames);
716   }
717
718   public void addMappings(AlignedCodonFrame[] codonFrames)
719   {
720     modifySeqMappingList(true, codonFrames);
721   }
722
723   Vector<SelectionListener> sel_listeners = new Vector<SelectionListener>();
724
725   public void addSelectionListener(SelectionListener selecter)
726   {
727     if (!sel_listeners.contains(selecter))
728     {
729       sel_listeners.addElement(selecter);
730     }
731   }
732
733   public void removeSelectionListener(SelectionListener toremove)
734   {
735     if (sel_listeners.contains(toremove))
736     {
737       sel_listeners.removeElement(toremove);
738     }
739   }
740
741   public synchronized void sendSelection(
742           jalview.datamodel.SequenceGroup selection,
743           jalview.datamodel.ColumnSelection colsel, SelectionSource source)
744   {
745     if (sel_listeners != null && sel_listeners.size() > 0)
746     {
747       Enumeration listeners = sel_listeners.elements();
748       while (listeners.hasMoreElements())
749       {
750         SelectionListener slis = ((SelectionListener) listeners
751                 .nextElement());
752         if (slis != source)
753         {
754           slis.selection(selection, colsel, source);
755         }
756         ;
757       }
758     }
759   }
760
761   Vector<AlignmentViewPanelListener> view_listeners = new Vector<AlignmentViewPanelListener>();
762
763   public synchronized void sendViewPosition(
764           jalview.api.AlignmentViewPanel source, int startRes, int endRes,
765           int startSeq, int endSeq)
766   {
767
768     if (view_listeners != null && view_listeners.size() > 0)
769     {
770       Enumeration<AlignmentViewPanelListener> listeners = view_listeners
771               .elements();
772       while (listeners.hasMoreElements())
773       {
774         AlignmentViewPanelListener slis = listeners.nextElement();
775         if (slis != source)
776         {
777           slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
778         }
779         ;
780       }
781     }
782   }
783
784   public void finalize() throws Throwable
785   {
786     if (listeners != null)
787     {
788       listeners.clear();
789       listeners = null;
790     }
791     if (mappingData != null)
792     {
793       mappingData.clear();
794       mappingData = null;
795     }
796     if (sel_listeners != null)
797     {
798       sel_listeners.clear();
799       sel_listeners = null;
800     }
801     if (view_listeners != null)
802     {
803       view_listeners.clear();
804       view_listeners = null;
805     }
806     mappings = null;
807     seqmappingrefs = null;
808   }
809
810   /**
811    * release all references associated with this manager provider
812    * 
813    * @param jalviewLite
814    */
815   public static void release(StructureSelectionManagerProvider jalviewLite)
816   {
817     // synchronized (instances)
818     {
819       if (instances == null)
820       {
821         return;
822       }
823       StructureSelectionManager mnger = (instances.get(jalviewLite));
824       if (mnger != null)
825       {
826         instances.remove(jalviewLite);
827         try
828         {
829           mnger.finalize();
830         } catch (Throwable x)
831         {
832         }
833         ;
834       }
835     }
836   }
837
838 }