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