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