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