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