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