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