JAL-674 use PDB ID<>File registry to look up correct ID for PDB file derived annotati...
[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    * Import structure data and register a structure mapping for broadcasting
202    * colouring, mouseovers and selection events (convenience wrapper).
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     return setMapping(true, sequence, targetChains, pdbFile, protocol);
219   }
220
221   /**
222    * create sequence structure mappings between each sequence and the given
223    * pdbFile (retrieved via the given protocol).
224    * 
225    * @param forStructureView
226    *          when true, record the mapping for use in mouseOvers
227    * 
228    * @param sequence
229    *          - one or more sequences to be mapped to pdbFile
230    * @param targetChains
231    *          - optional chain specification for mapping each sequence to pdb
232    *          (may be nill, individual elements may be nill)
233    * @param pdbFile
234    *          - structure data resource
235    * @param protocol
236    *          - how to resolve data from resource
237    * @return null or the structure data parsed as a pdb file
238    */
239   synchronized public MCview.PDBfile setMapping(boolean forStructureView,
240           SequenceI[] sequence,
241           String[] targetChains, String pdbFile, String protocol)
242   {
243     /*
244      * There will be better ways of doing this in the future, for now we'll use
245      * the tried and tested MCview pdb mapping
246      */
247     MCview.PDBfile pdb = null;
248     boolean parseSecStr=true;
249     if (isPDBFileRegistered(pdbFile))
250     {
251       for (SequenceI sq : sequence)
252       {
253         SequenceI ds = sq;
254         while (ds.getDatasetSequence() != null)
255         {
256           ds = ds.getDatasetSequence();
257         }
258         ;
259         if (ds.getAnnotation() != null)
260         {
261           for (AlignmentAnnotation ala : ds.getAnnotation())
262           {
263
264             // false if any annotation present from this structure
265             // JBPNote this fails for jmol/chimera view because the *file* is
266             // passed, not the structure data ID -
267             if (MCview.PDBfile.isCalcIdForFile(ala.getCalcId(),
268                     findIdForPDBFile(pdbFile)))
269             {
270               parseSecStr = false;
271             }
272           }
273         }
274       }
275     }
276     try
277     {
278       pdb = new MCview.PDBfile(true, parseSecStr, pdbFile, protocol);
279       if (pdb.id != null && pdb.id.trim().length() > 0
280               && FormatAdapter.FILE.equals(protocol))
281       {
282         registerPDBFile(pdb.id.trim(), pdbFile);
283       }
284     } catch (Exception ex)
285     {
286       ex.printStackTrace();
287       return null;
288     }
289
290     String targetChain;
291     for (int s = 0; s < sequence.length; s++)
292     {
293       boolean infChain = true;
294       if (targetChains != null && targetChains[s] != null)
295       {
296         infChain = false;
297         targetChain = targetChains[s];
298       }
299       else if (sequence[s].getName().indexOf("|") > -1)
300       {
301         targetChain = sequence[s].getName().substring(
302                 sequence[s].getName().lastIndexOf("|") + 1);
303         if (targetChain.length() > 1)
304         {
305           if (targetChain.trim().length() == 0)
306           {
307             targetChain = " ";
308           }
309           else
310           {
311             // not a valid chain identifier
312             targetChain = "";
313           }
314         }
315       }
316       else
317       {
318         targetChain = "";
319       }
320
321       int max = -10;
322       AlignSeq maxAlignseq = null;
323       String maxChainId = " ";
324       PDBChain maxChain = null;
325       boolean first = true;
326       for (int i = 0; i < pdb.chains.size(); i++)
327       {
328         PDBChain chain = (pdb.chains.elementAt(i));
329         if (targetChain.length() > 0 && !targetChain.equals(chain.id)
330                 && !infChain)
331         {
332           continue; // don't try to map chains don't match.
333         }
334         // TODO: correctly determine sequence type for mixed na/peptide
335         // structures
336         AlignSeq as = new AlignSeq(sequence[s],
337                 pdb.chains.elementAt(i).sequence,
338                 pdb.chains.elementAt(i).isNa ? AlignSeq.DNA
339                         : AlignSeq.PEP);
340         as.calcScoreMatrix();
341         as.traceAlignment();
342
343         if (first || as.maxscore > max
344                 || (as.maxscore == max && chain.id.equals(targetChain)))
345         {
346           first = false;
347           maxChain = chain;
348           max = as.maxscore;
349           maxAlignseq = as;
350           maxChainId = chain.id;
351         }
352       }
353       if (maxChain == null)
354       {
355         continue;
356       }
357       final StringBuffer mappingDetails = new StringBuffer();
358       mappingDetails.append("\n\nPDB Sequence is :\nSequence = "
359               + maxChain.sequence.getSequenceAsString());
360       mappingDetails.append("\nNo of residues = "
361               + maxChain.residues.size() + "\n\n");
362       PrintStream ps = new PrintStream(System.out)
363       {
364         public void print(String x)
365         {
366           mappingDetails.append(x);
367         }
368
369         public void println()
370         {
371           mappingDetails.append("\n");
372         }
373       };
374
375       maxAlignseq.printAlignment(ps);
376
377       mappingDetails.append("\nPDB start/end " + maxAlignseq.seq2start
378               + " " + maxAlignseq.seq2end);
379       mappingDetails.append("\nSEQ start/end "
380               + (maxAlignseq.seq1start + sequence[s].getStart() - 1) + " "
381               + (maxAlignseq.seq1end + sequence[s].getEnd() - 1));
382
383       maxChain.makeExactMapping(maxAlignseq, sequence[s]);
384
385       maxChain.transferRESNUMFeatures(sequence[s], null);
386
387       // allocate enough slots to store the mapping from positions in
388       // sequence[s] to the associated chain
389       int[][] mapping = new int[sequence[s].findPosition(sequence[s]
390               .getLength()) + 2][2];
391       int resNum = -10000;
392       int index = 0;
393
394       do
395       {
396         Atom tmp = (Atom) maxChain.atoms.elementAt(index);
397         if (resNum != tmp.resNumber && tmp.alignmentMapping != -1)
398         {
399           resNum = tmp.resNumber;
400           mapping[tmp.alignmentMapping + 1][0] = tmp.resNumber;
401           mapping[tmp.alignmentMapping + 1][1] = tmp.atomIndex;
402         }
403
404         index++;
405       } while (index < maxChain.atoms.size());
406
407       if (protocol.equals(jalview.io.AppletFormatAdapter.PASTE))
408       {
409         pdbFile = "INLINE" + pdb.id;
410       }
411       StructureMapping newMapping = new StructureMapping(sequence[s],
412               pdbFile, pdb.id, maxChainId, mapping,
413               mappingDetails.toString());
414       if (forStructureView)
415       {
416
417         if (mappings == null)
418         {
419           mappings = new StructureMapping[1];
420         }
421         else
422         {
423           StructureMapping[] tmp = new StructureMapping[mappings.length + 1];
424           System.arraycopy(mappings, 0, tmp, 0, mappings.length);
425           mappings = tmp;
426         }
427
428         mappings[mappings.length - 1] = newMapping;
429       }
430       maxChain.transferResidueAnnotation(newMapping);
431     }
432     // ///////
433
434     return pdb;
435   }
436
437   public void removeStructureViewerListener(Object svl, String[] pdbfiles)
438   {
439     listeners.removeElement(svl);
440     if (svl instanceof SequenceListener)
441     {
442       for (int i = 0; i < listeners.size(); i++)
443       {
444         if (listeners.elementAt(i) instanceof StructureListener)
445         {
446           ((StructureListener) listeners.elementAt(i))
447                   .releaseReferences(svl);
448         }
449       }
450     }
451
452     if (pdbfiles == null)
453     {
454       return;
455     }
456     boolean removeMapping = true;
457     String[] handlepdbs;
458     Vector pdbs = new Vector();
459     for (int i = 0; i < pdbfiles.length; pdbs.addElement(pdbfiles[i++]))
460     {
461       ;
462     }
463     StructureListener sl;
464     for (int i = 0; i < listeners.size(); i++)
465     {
466       if (listeners.elementAt(i) instanceof StructureListener)
467       {
468         sl = (StructureListener) listeners.elementAt(i);
469         handlepdbs = sl.getPdbFile();
470         for (int j = 0; j < handlepdbs.length; j++)
471         {
472           if (pdbs.contains(handlepdbs[j]))
473           {
474             pdbs.removeElement(handlepdbs[j]);
475           }
476         }
477
478       }
479     }
480
481     if (pdbs.size() > 0 && mappings != null)
482     {
483       Vector tmp = new Vector();
484       for (int i = 0; i < mappings.length; i++)
485       {
486         if (!pdbs.contains(mappings[i].pdbfile))
487         {
488           tmp.addElement(mappings[i]);
489         }
490       }
491
492       mappings = new StructureMapping[tmp.size()];
493       tmp.copyInto(mappings);
494     }
495   }
496
497   public void mouseOverStructure(int pdbResNum, String chain, String pdbfile)
498   {
499     if (listeners == null)
500     {
501       // old or prematurely sent event
502       return;
503     }
504     boolean hasSequenceListeners = handlingVamsasMo || seqmappings != null;
505     SearchResults results = null;
506     SequenceI lastseq = null;
507     int lastipos = -1, indexpos;
508     for (int i = 0; i < listeners.size(); i++)
509     {
510       if (listeners.elementAt(i) instanceof SequenceListener)
511       {
512         if (results == null)
513         {
514           results = new SearchResults();
515         }
516         if (mappings != null)
517         {
518           for (int j = 0; j < mappings.length; j++)
519           {
520             if (mappings[j].pdbfile.equals(pdbfile)
521                     && mappings[j].pdbchain.equals(chain))
522             {
523               indexpos = mappings[j].getSeqPos(pdbResNum);
524               if (lastipos != indexpos && lastseq != mappings[j].sequence)
525               {
526                 results.addResult(mappings[j].sequence, indexpos, indexpos);
527                 lastipos = indexpos;
528                 lastseq = mappings[j].sequence;
529                 // construct highlighted sequence list
530                 if (seqmappings != null)
531                 {
532
533                   Enumeration e = seqmappings.elements();
534                   while (e.hasMoreElements())
535
536                   {
537                     ((AlignedCodonFrame) e.nextElement()).markMappedRegion(
538                             mappings[j].sequence, indexpos, results);
539                   }
540                 }
541               }
542
543             }
544           }
545         }
546       }
547     }
548     if (results != null)
549     {
550       for (int i = 0; i < listeners.size(); i++)
551       {
552         Object li = listeners.elementAt(i);
553         if (li instanceof SequenceListener)
554         {
555           ((SequenceListener) li).highlightSequence(results);
556         }
557       }
558     }
559   }
560
561   Vector seqmappings = null; // should be a simpler list of mapped seuqence
562
563   /**
564    * highlight regions associated with a position (indexpos) in seq
565    * 
566    * @param seq
567    *          the sequeence that the mouse over occured on
568    * @param indexpos
569    *          the absolute position being mouseovered in seq (0 to seq.length())
570    * @param index
571    *          the sequence position (if -1, seq.findPosition is called to
572    *          resolve the residue number)
573    */
574   public void mouseOverSequence(SequenceI seq, int indexpos, int index,
575           VamsasSource source)
576   {
577     boolean hasSequenceListeners = handlingVamsasMo || seqmappings != null;
578     SearchResults results = null;
579     if (index == -1)
580     {
581       index = seq.findPosition(indexpos);
582     }
583     StructureListener sl;
584     int atomNo = 0;
585     for (int i = 0; i < listeners.size(); i++)
586     {
587       Object listener = listeners.elementAt(i);
588       if (listener == source)
589       {
590         continue;
591       }
592       if (listener instanceof StructureListener)
593       {
594         sl = (StructureListener) listener;
595         if (mappings == null)
596         {
597           continue;
598         }
599         for (int j = 0; j < mappings.length; j++)
600         {
601           if (mappings[j].sequence == seq
602                   || mappings[j].sequence == seq.getDatasetSequence())
603           {
604             atomNo = mappings[j].getAtomNum(index);
605
606             if (atomNo > 0)
607             {
608               sl.highlightAtom(atomNo, mappings[j].getPDBResNum(index),
609                       mappings[j].pdbchain, mappings[j].pdbfile);
610             }
611           }
612         }
613       }
614       else
615       {
616         if (relaySeqMappings && hasSequenceListeners
617                 && listener instanceof SequenceListener)
618         {
619           // DEBUG
620           // System.err.println("relay Seq " + seq.getDisplayId(false) + " " +
621           // index);
622
623           if (results == null)
624           {
625             results = new SearchResults();
626             if (index >= seq.getStart() && index <= seq.getEnd())
627             {
628               // construct highlighted sequence list
629
630               if (seqmappings != null)
631               {
632                 Enumeration e = seqmappings.elements();
633                 while (e.hasMoreElements())
634
635                 {
636                   ((AlignedCodonFrame) e.nextElement()).markMappedRegion(
637                           seq, index, results);
638                 }
639               }
640               // hasSequenceListeners = results.getSize() > 0;
641               if (handlingVamsasMo)
642               {
643                 // maybe have to resolve seq to a dataset seqeunce...
644                 // add in additional direct sequence and/or dataset sequence
645                 // highlighting
646                 results.addResult(seq, index, index);
647               }
648             }
649           }
650           if (hasSequenceListeners)
651           {
652             ((SequenceListener) listener).highlightSequence(results);
653           }
654         }
655         else if (listener instanceof VamsasListener && !handlingVamsasMo)
656         {
657           // DEBUG
658           // System.err.println("Vamsas from Seq " + seq.getDisplayId(false) + "
659           // " +
660           // index);
661           // pass the mouse over and absolute position onto the
662           // VamsasListener(s)
663           ((VamsasListener) listener).mouseOver(seq, indexpos, source);
664         }
665         else if (listener instanceof SecondaryStructureListener)
666         {
667           ((SecondaryStructureListener) listener).mouseOverSequence(seq,
668                   indexpos);
669         }
670       }
671     }
672   }
673
674   /**
675    * true if a mouse over event from an external (ie Vamsas) source is being
676    * handled
677    */
678   boolean handlingVamsasMo = false;
679
680   long lastmsg = 0;
681
682   /**
683    * as mouseOverSequence but only route event to SequenceListeners
684    * 
685    * @param sequenceI
686    * @param position
687    *          in an alignment sequence
688    */
689   public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
690           VamsasSource source)
691   {
692     handlingVamsasMo = true;
693     long msg = sequenceI.hashCode() * (1 + position);
694     if (lastmsg != msg)
695     {
696       lastmsg = msg;
697       mouseOverSequence(sequenceI, position, -1, source);
698     }
699     handlingVamsasMo = false;
700   }
701
702   public Annotation[] colourSequenceFromStructure(SequenceI seq,
703           String pdbid)
704   {
705     return null;
706     // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
707     // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
708     /*
709      * Annotation [] annotations = new Annotation[seq.getLength()];
710      * 
711      * StructureListener sl; int atomNo = 0; for (int i = 0; i <
712      * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
713      * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
714      * 
715      * for (int j = 0; j < mappings.length; j++) {
716      * 
717      * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
718      * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
719      * System.out.println(pdbid+" "+mappings[j].getPdbId() +"
720      * "+mappings[j].pdbfile);
721      * 
722      * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
723      * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
724      * 
725      * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
726      * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
727      * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
728      * mappings[j].pdbfile); }
729      * 
730      * annotations[index] = new Annotation("X",null,' ',0,col); } return
731      * annotations; } } } }
732      * 
733      * return annotations;
734      */
735   }
736
737   public void structureSelectionChanged()
738   {
739   }
740
741   public void sequenceSelectionChanged()
742   {
743   }
744
745   public void sequenceColoursChanged(Object source)
746   {
747     StructureListener sl;
748     for (int i = 0; i < listeners.size(); i++)
749     {
750       if (listeners.elementAt(i) instanceof StructureListener)
751       {
752         sl = (StructureListener) listeners.elementAt(i);
753         sl.updateColours(source);
754       }
755     }
756   }
757
758   public StructureMapping[] getMapping(String pdbfile)
759   {
760     Vector tmp = new Vector();
761     if (mappings != null)
762     {
763       for (int i = 0; i < mappings.length; i++)
764       {
765         if (mappings[i].pdbfile.equals(pdbfile))
766         {
767           tmp.addElement(mappings[i]);
768         }
769       }
770     }
771     StructureMapping[] ret = new StructureMapping[tmp.size()];
772     for (int i = 0; i < tmp.size(); i++)
773     {
774       ret[i] = (StructureMapping) tmp.elementAt(i);
775     }
776
777     return ret;
778   }
779
780   public String printMapping(String pdbfile)
781   {
782     StringBuffer sb = new StringBuffer();
783     for (int i = 0; i < mappings.length; i++)
784     {
785       if (mappings[i].pdbfile.equals(pdbfile))
786       {
787         sb.append(mappings[i].mappingDetails);
788       }
789     }
790
791     return sb.toString();
792   }
793
794   private int[] seqmappingrefs = null; // refcount for seqmappings elements
795
796   private synchronized void modifySeqMappingList(boolean add,
797           AlignedCodonFrame[] codonFrames)
798   {
799     if (!add && (seqmappings == null || seqmappings.size() == 0))
800     {
801       return;
802     }
803     if (seqmappings == null)
804     {
805       seqmappings = new Vector();
806     }
807     if (codonFrames != null && codonFrames.length > 0)
808     {
809       for (int cf = 0; cf < codonFrames.length; cf++)
810       {
811         if (seqmappings.contains(codonFrames[cf]))
812         {
813           if (add)
814           {
815             seqmappingrefs[seqmappings.indexOf(codonFrames[cf])]++;
816           }
817           else
818           {
819             if (--seqmappingrefs[seqmappings.indexOf(codonFrames[cf])] <= 0)
820             {
821               int pos = seqmappings.indexOf(codonFrames[cf]);
822               int[] nr = new int[seqmappingrefs.length - 1];
823               if (pos > 0)
824               {
825                 System.arraycopy(seqmappingrefs, 0, nr, 0, pos);
826               }
827               if (pos < seqmappingrefs.length - 1)
828               {
829                 System.arraycopy(seqmappingrefs, pos + 1, nr, 0,
830                         seqmappingrefs.length - pos - 2);
831               }
832             }
833           }
834         }
835         else
836         {
837           if (add)
838           {
839             seqmappings.addElement(codonFrames[cf]);
840
841             int[] nsr = new int[(seqmappingrefs == null) ? 1
842                     : seqmappingrefs.length + 1];
843             if (seqmappingrefs != null && seqmappingrefs.length > 0)
844             {
845               System.arraycopy(seqmappingrefs, 0, nsr, 0,
846                       seqmappingrefs.length);
847             }
848             nsr[(seqmappingrefs == null) ? 0 : seqmappingrefs.length] = 1;
849             seqmappingrefs = nsr;
850           }
851         }
852       }
853     }
854   }
855
856   public void removeMappings(AlignedCodonFrame[] codonFrames)
857   {
858     modifySeqMappingList(false, codonFrames);
859   }
860
861   public void addMappings(AlignedCodonFrame[] codonFrames)
862   {
863     modifySeqMappingList(true, codonFrames);
864   }
865
866   Vector<SelectionListener> sel_listeners = new Vector<SelectionListener>();
867
868   public void addSelectionListener(SelectionListener selecter)
869   {
870     if (!sel_listeners.contains(selecter))
871     {
872       sel_listeners.addElement(selecter);
873     }
874   }
875
876   public void removeSelectionListener(SelectionListener toremove)
877   {
878     if (sel_listeners.contains(toremove))
879     {
880       sel_listeners.removeElement(toremove);
881     }
882   }
883
884   public synchronized void sendSelection(
885           jalview.datamodel.SequenceGroup selection,
886           jalview.datamodel.ColumnSelection colsel, SelectionSource source)
887   {
888     if (sel_listeners != null && sel_listeners.size() > 0)
889     {
890       Enumeration listeners = sel_listeners.elements();
891       while (listeners.hasMoreElements())
892       {
893         SelectionListener slis = ((SelectionListener) listeners
894                 .nextElement());
895         if (slis != source)
896         {
897           slis.selection(selection, colsel, source);
898         }
899         ;
900       }
901     }
902   }
903
904   Vector<AlignmentViewPanelListener> view_listeners = new Vector<AlignmentViewPanelListener>();
905
906   public synchronized void sendViewPosition(
907           jalview.api.AlignmentViewPanel source, int startRes, int endRes,
908           int startSeq, int endSeq)
909   {
910
911     if (view_listeners != null && view_listeners.size() > 0)
912     {
913       Enumeration<AlignmentViewPanelListener> listeners = view_listeners
914               .elements();
915       while (listeners.hasMoreElements())
916       {
917         AlignmentViewPanelListener slis = listeners.nextElement();
918         if (slis != source)
919         {
920           slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
921         }
922         ;
923       }
924     }
925   }
926
927   public void finalize() throws Throwable
928   {
929     if (listeners != null)
930     {
931       listeners.clear();
932       listeners = null;
933     }
934     if (pdbIdFileName != null)
935     {
936       pdbIdFileName.clear();
937       pdbIdFileName = null;
938     }
939     if (sel_listeners != null)
940     {
941       sel_listeners.clear();
942       sel_listeners = null;
943     }
944     if (view_listeners != null)
945     {
946       view_listeners.clear();
947       view_listeners = null;
948     }
949     mappings = null;
950     seqmappingrefs = null;
951   }
952
953   /**
954    * release all references associated with this manager provider
955    * 
956    * @param jalviewLite
957    */
958   public static void release(StructureSelectionManagerProvider jalviewLite)
959   {
960     // synchronized (instances)
961     {
962       if (instances == null)
963       {
964         return;
965       }
966       StructureSelectionManager mnger = (instances.get(jalviewLite));
967       if (mnger != null)
968       {
969         instances.remove(jalviewLite);
970         try
971         {
972           mnger.finalize();
973         } catch (Throwable x)
974         {
975         }
976         ;
977       }
978     }
979   }
980
981   public void registerPDBEntry(PDBEntry pdbentry)
982   {
983     if (pdbentry.getFile() != null
984             && pdbentry.getFile().trim().length() > 0)
985     {
986       registerPDBFile(pdbentry.getId(), pdbentry.getFile());
987     }
988   }
989
990 }