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