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