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