Clover icon

Coverage Report

  1. Project Clover database Thu Nov 28 2024 18:06:40 GMT
  2. Package jalview.structure

File StructureSelectionManager.java

 

Coverage histogram

../../img/srcFileCovDistChart6.png
38% of files have more coverage

Code metrics

236
452
62
1
1,700
1,171
215
0.48
7.29
62
3.47

Classes

Class Line # Actions
StructureSelectionManager 69 452 215
0.55655.6%
 

Contributing tests

This file is covered by 227 tests. .

Source view

1    /*
2    * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3    * Copyright (C) $$Year-Rel$$ 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 java.io.PrintStream;
24    import java.util.ArrayList;
25    import java.util.Arrays;
26    import java.util.Collections;
27    import java.util.Enumeration;
28    import java.util.HashMap;
29    import java.util.IdentityHashMap;
30    import java.util.List;
31    import java.util.Locale;
32    import java.util.Map;
33    import java.util.Vector;
34   
35    import jalview.analysis.AlignSeq;
36    import jalview.api.StructureSelectionManagerProvider;
37    import jalview.bin.Cache;
38    import jalview.bin.Console;
39    import jalview.commands.CommandI;
40    import jalview.commands.EditCommand;
41    import jalview.commands.OrderCommand;
42    import jalview.datamodel.AlignedCodonFrame;
43    import jalview.datamodel.AlignmentAnnotation;
44    import jalview.datamodel.AlignmentI;
45    import jalview.datamodel.Annotation;
46    import jalview.datamodel.ContiguousI;
47    import jalview.datamodel.HiddenColumns;
48    import jalview.datamodel.PDBEntry;
49    import jalview.datamodel.SearchResults;
50    import jalview.datamodel.SearchResultsI;
51    import jalview.datamodel.SequenceI;
52    import jalview.ext.jmol.JmolParser;
53    import jalview.gui.IProgressIndicator;
54    import jalview.gui.Preferences;
55    import jalview.io.AppletFormatAdapter;
56    import jalview.io.DataSourceType;
57    import jalview.io.StructureFile;
58    import jalview.structure.StructureImportSettings.TFType;
59    import jalview.util.MappingUtils;
60    import jalview.util.MessageManager;
61    import jalview.util.Platform;
62    import jalview.ws.sifts.SiftsClient;
63    import jalview.ws.sifts.SiftsException;
64    import jalview.ws.sifts.SiftsSettings;
65    import mc_view.Atom;
66    import mc_view.PDBChain;
67    import mc_view.PDBfile;
68   
 
69    public class StructureSelectionManager
70    {
71    public final static String NEWLINE = System.lineSeparator();
72   
73    static IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> instances;
74   
75    private List<StructureMapping> mappings = new ArrayList<>();
76   
77    private boolean processSecondaryStructure = false;
78   
79    private boolean secStructServices = false;
80   
81    private boolean addTempFacAnnot = false;
82   
83    /*
84    * Set of any registered mappings between (dataset) sequences.
85    */
86    private List<AlignedCodonFrame> seqmappings = new ArrayList<>();
87   
88    private List<CommandListener> commandListeners = new ArrayList<>();
89   
90    private List<SelectionListener> sel_listeners = new ArrayList<>();
91   
92    /**
93    * @return true if will try to use external services for processing secondary
94    * structure
95    */
 
96  0 toggle public boolean isSecStructServices()
97    {
98  0 return secStructServices;
99    }
100   
101    /**
102    * control use of external services for processing secondary structure
103    *
104    * @param secStructServices
105    */
 
106  127 toggle public void setSecStructServices(boolean secStructServices)
107    {
108  127 this.secStructServices = secStructServices;
109    }
110   
111    /**
112    * flag controlling addition of any kind of structural annotation
113    *
114    * @return true if temperature factor annotation will be added
115    */
 
116  0 toggle public boolean isAddTempFacAnnot()
117    {
118  0 return addTempFacAnnot;
119    }
120   
121    /**
122    * set flag controlling addition of structural annotation
123    *
124    * @param addTempFacAnnot
125    */
 
126  129 toggle public void setAddTempFacAnnot(boolean addTempFacAnnot)
127    {
128  129 this.addTempFacAnnot = addTempFacAnnot;
129    }
130   
131    /**
132    *
133    * @return if true, the structure manager will attempt to add secondary
134    * structure lines for unannotated sequences
135    */
136   
 
137  0 toggle public boolean isProcessSecondaryStructure()
138    {
139  0 return processSecondaryStructure;
140    }
141   
142    /**
143    * Control whether structure manager will try to annotate mapped sequences
144    * with secondary structure from PDB data.
145    *
146    * @param enable
147    */
 
148  129 toggle public void setProcessSecondaryStructure(boolean enable)
149    {
150  129 processSecondaryStructure = enable;
151    }
152   
153    /**
154    * debug function - write all mappings to stdout
155    */
 
156  0 toggle public void reportMapping()
157    {
158  0 if (mappings.isEmpty())
159    {
160  0 jalview.bin.Console
161    .errPrintln("reportMapping: No PDB/Sequence mappings.");
162    }
163    else
164    {
165  0 jalview.bin.Console.errPrintln(
166    "reportMapping: There are " + mappings.size() + " mappings.");
167  0 int i = 0;
168  0 for (StructureMapping sm : mappings)
169    {
170  0 jalview.bin.Console
171    .errPrintln("mapping " + i++ + " : " + sm.pdbfile);
172    }
173    }
174    }
175   
176    /**
177    * map between the PDB IDs (or structure identifiers) used by Jalview and the
178    * absolute filenames for PDB data that corresponds to it
179    */
180    Map<String, String> pdbIdFileName = new HashMap<>();
181   
182    Map<String, String> pdbFileNameId = new HashMap<>();
183   
 
184  277 toggle public void registerPDBFile(String idForFile, String absoluteFile)
185    {
186  277 pdbIdFileName.put(idForFile, absoluteFile);
187  277 pdbFileNameId.put(absoluteFile, idForFile);
188    }
189   
 
190  254 toggle public String findIdForPDBFile(String idOrFile)
191    {
192  254 String id = pdbFileNameId.get(idOrFile);
193  254 return id;
194    }
195   
 
196  1 toggle public String findFileForPDBId(String idOrFile)
197    {
198  1 String id = pdbIdFileName.get(idOrFile);
199  1 return id;
200    }
201   
 
202  135 toggle public boolean isPDBFileRegistered(String idOrFile)
203    {
204  135 return pdbFileNameId.containsKey(idOrFile)
205    || pdbIdFileName.containsKey(idOrFile);
206    }
207   
208    private static StructureSelectionManager nullProvider = null;
209   
 
210  5135 toggle public static StructureSelectionManager getStructureSelectionManager(
211    StructureSelectionManagerProvider context)
212    {
213  5135 if (context == null)
214    {
215  498 if (nullProvider == null)
216    {
217  47 if (instances != null)
218    {
219  0 throw new Error(MessageManager.getString(
220    "error.implementation_error_structure_selection_manager_null"),
221    new NullPointerException(MessageManager
222    .getString("exception.ssm_context_is_null")));
223    }
224    else
225    {
226  47 nullProvider = new StructureSelectionManager();
227    }
228  47 return nullProvider;
229    }
230    }
231  5088 if (instances == null)
232    {
233  54 instances = new java.util.IdentityHashMap<>();
234    }
235  5088 StructureSelectionManager instance = instances.get(context);
236  5088 if (instance == null)
237    {
238  122 if (nullProvider != null)
239    {
240  115 instance = nullProvider;
241    }
242    else
243    {
244  7 instance = new StructureSelectionManager();
245    }
246  122 instances.put(context, instance);
247    }
248  5088 return instance;
249    }
250   
251    /**
252    * flag controlling whether SeqMappings are relayed from received sequence
253    * mouse over events to other sequences
254    */
255    boolean relaySeqMappings = true;
256   
257    /**
258    * Enable or disable relay of seqMapping events to other sequences. You might
259    * want to do this if there are many sequence mappings and the host computer
260    * is slow
261    *
262    * @param relay
263    */
 
264  0 toggle public void setRelaySeqMappings(boolean relay)
265    {
266  0 relaySeqMappings = relay;
267    }
268   
269    /**
270    * get the state of the relay seqMappings flag.
271    *
272    * @return true if sequence mouse overs are being relayed to other mapped
273    * sequences
274    */
 
275  0 toggle public boolean isRelaySeqMappingsEnabled()
276    {
277  0 return relaySeqMappings;
278    }
279   
280    Vector listeners = new Vector();
281   
282    /**
283    * register a listener for alignment sequence mouseover events
284    *
285    * @param svl
286    */
 
287  525 toggle public void addStructureViewerListener(Object svl)
288    {
289  525 if (!listeners.contains(svl))
290    {
291  523 listeners.addElement(svl);
292    }
293    }
294   
295    /**
296    * Returns the filename the PDB id is already mapped to if known, or null if
297    * it is not mapped
298    *
299    * @param pdbid
300    * @return
301    */
 
302  1 toggle public String alreadyMappedToFile(String pdbid)
303    {
304  1 for (StructureMapping sm : mappings)
305    {
306  1 if (sm.getPdbId().equalsIgnoreCase(pdbid))
307    {
308  0 return sm.pdbfile;
309    }
310    }
311  1 return null;
312    }
313   
314    /**
315    * Import structure data and register a structure mapping for broadcasting
316    * colouring, mouseovers and selection events (convenience wrapper).
317    *
318    * @param sequence
319    * - one or more sequences to be mapped to pdbFile
320    * @param targetChains
321    * - optional chain specification for mapping each sequence to pdb
322    * (may be nill, individual elements may be nill)
323    * @param pdbFile
324    * - structure data resource
325    * @param protocol
326    * - how to resolve data from resource
327    * @return null or the structure data parsed as a pdb file
328    */
 
329  90 toggle synchronized public StructureFile setMapping(SequenceI[] sequence,
330    String[] targetChains, String pdbFile, DataSourceType protocol,
331    IProgressIndicator progress)
332    {
333  90 return computeMapping(true, sequence, targetChains, pdbFile, protocol,
334    progress, null, null, true);
335    }
336   
337    /**
338    * Import a single structure file and register sequence structure mappings for
339    * broadcasting colouring, mouseovers and selection events (convenience
340    * wrapper).
341    *
342    * @param forStructureView
343    * when true, record the mapping for use in mouseOvers
344    * @param sequence
345    * - one or more sequences to be mapped to pdbFile
346    * @param targetChains
347    * - optional chain specification for mapping each sequence to pdb
348    * (may be nill, individual elements may be nill)
349    * @param pdbFile
350    * - structure data resource
351    * @param protocol
352    * - how to resolve data from resource
353    * @return null or the structure data parsed as a pdb file
354    */
 
355  3 toggle synchronized public StructureFile setMapping(boolean forStructureView,
356    SequenceI[] sequenceArray, String[] targetChainIds,
357    String pdbFile, DataSourceType sourceType, TFType tft,
358    String paeFilename)
359    {
360  3 return setMapping(forStructureView, sequenceArray, targetChainIds,
361    pdbFile, sourceType, tft, paeFilename, true);
362    }
363   
364    /**
365    * create sequence structure mappings between each sequence and the given
366    * pdbFile (retrieved via the given protocol). Either constructs a mapping
367    * using NW alignment or derives one from any available SIFTS mapping data.
368    *
369    * @param forStructureView
370    * when true, record the mapping for use in mouseOvers
371    *
372    * @param sequenceArray
373    * - one or more sequences to be mapped to pdbFile
374    * @param targetChainIds
375    * - optional chain specification for mapping each sequence to pdb
376    * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
377    * - this should be List<List<String>>, empty lists indicate no
378    * predefined mappings
379    * @param pdbFile
380    * - structure data resource
381    * @param sourceType
382    * - how to resolve data from resource
383    * @param tft
384    * - specify how to interpret the temperature factor column in the
385    * atom data
386    * @param paeFilename
387    * - when not null, specifies a filename containing a matrix
388    * formatted in JSON using one of the known PAE formats
389    * @param doXferSettings
390    * - when true, transfer annotation to mapped sequences in
391    * sequenceArray
392    * @return null or the structure data parsed as a pdb file
393    */
 
394  58 toggle synchronized public StructureFile setMapping(boolean forStructureView,
395    SequenceI[] sequenceArray, String[] targetChainIds,
396    String pdbFile, DataSourceType sourceType, TFType tft,
397    String paeFilename, boolean doXferSettings)
398    {
399  58 return computeMapping(forStructureView, sequenceArray, targetChainIds,
400    pdbFile, sourceType, null, tft, paeFilename, doXferSettings);
401    }
402   
403    /**
404    * create sequence structure mappings between each sequence and the given
405    * pdbFile (retrieved via the given protocol). Either constructs a mapping
406    * using NW alignment or derives one from any available SIFTS mapping data.
407    *
408    * @param forStructureView
409    * when true, record the mapping for use in mouseOvers
410    *
411    * @param sequenceArray
412    * - one or more sequences to be mapped to pdbFile
413    * @param targetChainIds
414    * - optional chain specification for mapping each sequence to pdb
415    * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
416    * - this should be List<List<String>>, empty lists indicate no
417    * predefined mappings
418    * @param pdbFile
419    * - structure data resource
420    * @param sourceType
421    * - how to resolve data from resource
422    * @param IProgressIndicator
423    * reference to UI component that maintains a progress bar for the
424    * mapping operation
425    * @param tft
426    * - specify how to interpret the temperature factor column in the
427    * atom data
428    * @param paeFilename
429    * - when not null, specifies a filename containing a matrix
430    * formatted in JSON using one of the known PAE formats
431    * @param doXferSettings
432    * - when true, transfer annotation to mapped sequences in
433    * sequenceArray
434    * @return null or the structure data parsed as a pdb file
435    */
 
436  150 toggle synchronized public StructureFile computeMapping(boolean forStructureView,
437    SequenceI[] sequenceArray, String[] targetChainIds,
438    String pdbFile, DataSourceType sourceType,
439    IProgressIndicator progress, TFType tft, String paeFilename,
440    boolean doXferSettings)
441    {
442  150 long progressSessionId = System.currentTimeMillis() * 3;
443   
444    /**
445    * do we extract and transfer annotation from 3D data ?
446    */
447    // FIXME: possibly should just delete
448   
449  150 boolean parseSecStr = processSecondaryStructure
450    && !isStructureFileProcessed(pdbFile, sequenceArray);
451   
452  150 StructureFile pdb = null;
453  150 boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
454  150 try
455    {
456    // FIXME if sourceType is not null, we've lost data here
457  150 sourceType = AppletFormatAdapter.checkProtocol(pdbFile);
458  150 pdb = new JmolParser(false, pdbFile, sourceType);
459  150 if (paeFilename != null)
460    {
461  36 pdb.setPAEMatrix(paeFilename);
462    }
463  150 pdb.setTemperatureFactorType(tft);
464  150 pdb.addSettings(parseSecStr && processSecondaryStructure,
465    parseSecStr && addTempFacAnnot,
466    parseSecStr && secStructServices);
467    // save doXferSettings and reset after doParse()
468  150 boolean temp = pdb.getDoXferSettings();
469  150 pdb.setDoXferSettings(doXferSettings);
470  150 pdb.doParse();
471  150 pdb.setDoXferSettings(temp);
472  150 if (pdb.getId() != null && pdb.getId().trim().length() > 0
473    && DataSourceType.FILE == sourceType)
474    {
475  138 registerPDBFile(pdb.getId().trim(), pdbFile);
476    }
477    // if PDBId is unavailable then skip SIFTS mapping execution path
478    // TODO: JAL-3868 need to know if structure is actually from
479    // PDB (has valid PDB ID and has provenance suggesting it
480    // actually came from PDB)
481  150 boolean isProtein = false;
482  150 for (SequenceI s : sequenceArray)
483    {
484  150 if (s.isProtein())
485    {
486  150 isProtein = true;
487  150 break;
488    }
489    }
490  150 isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable()
491    && !pdb.getId().startsWith("AF-") && isProtein;
492   
493    } catch (Exception ex)
494    {
495  0 ex.printStackTrace();
496  0 return null;
497    }
498    /*
499    * sifts client - non null if SIFTS mappings are to be used
500    */
501  150 SiftsClient siftsClient = null;
502  150 try
503    {
504  150 if (isMapUsingSIFTs)
505    {
506  0 siftsClient = new SiftsClient(pdb);
507    }
508    } catch (SiftsException e)
509    {
510  0 isMapUsingSIFTs = false;
511  0 Console.error("SIFTS mapping failed", e);
512  0 Console.error("Falling back on Needleman & Wunsch alignment");
513  0 siftsClient = null;
514    }
515   
516  150 String targetChainId;
517  345 for (int s = 0; s < sequenceArray.length; s++)
518    {
519  195 boolean infChain = true;
520  195 final SequenceI seq = sequenceArray[s];
521  195 SequenceI ds = seq;
522  388 while (ds.getDatasetSequence() != null)
523    {
524  193 ds = ds.getDatasetSequence();
525    }
526   
527  195 if (targetChainIds != null && targetChainIds[s] != null)
528    {
529  40 infChain = false;
530  40 targetChainId = targetChainIds[s];
531    }
532  155 else if (seq.getName().indexOf("|") > -1)
533    {
534  59 targetChainId = seq.getName()
535    .substring(seq.getName().lastIndexOf("|") + 1);
536  59 if (targetChainId.length() > 1)
537    {
538  1 if (targetChainId.trim().length() == 0)
539    {
540  0 targetChainId = " ";
541    }
542    else
543    {
544    // not a valid chain identifier
545  1 targetChainId = "";
546    }
547    }
548    }
549    else
550    {
551  96 targetChainId = "";
552    }
553   
554    /*
555    * Attempt pairwise alignment of the sequence with each chain in the PDB,
556    * and remember the highest scoring chain
557    */
558  195 float max = -10;
559  195 AlignSeq maxAlignseq = null;
560  195 String maxChainId = " ";
561  195 PDBChain maxChain = null;
562  195 boolean first = true;
563  195 for (PDBChain chain : pdb.getChains())
564    {
565  247 if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
566    && !infChain)
567    {
568  16 continue; // don't try to map chains don't match.
569    }
570    // TODO: correctly determine sequence type for mixed na/peptide
571    // structures
572  231 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
573  231 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
574    type);
575    // equivalent to:
576    // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
577    // as.calcScoreMatrix();
578    // as.traceAlignment();
579   
580  231 if (first || as.maxscore > max
581    || (as.maxscore == max && chain.id.equals(targetChainId)))
582    {
583  203 first = false;
584  203 maxChain = chain;
585  203 max = as.maxscore;
586  203 maxAlignseq = as;
587  203 maxChainId = chain.id;
588    }
589    }
590  195 if (maxChain == null)
591    {
592  0 continue;
593    }
594   
595  195 if (sourceType == DataSourceType.PASTE)
596    {
597  16 pdbFile = "INLINE" + pdb.getId();
598    }
599   
600  195 List<StructureMapping> seqToStrucMapping = new ArrayList<>();
601  195 if (isMapUsingSIFTs && seq.isProtein())
602    {
603  0 if (progress != null)
604    {
605  0 progress.setProgressBar(
606    MessageManager
607    .getString("status.obtaining_mapping_with_sifts"),
608    progressSessionId);
609    }
610  0 jalview.datamodel.Mapping sqmpping = maxAlignseq
611    .getMappingFromS1(false);
612  0 if (targetChainId != null && !targetChainId.trim().isEmpty())
613    {
614  0 StructureMapping siftsMapping;
615  0 try
616    {
617  0 siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
618    pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
619  0 seqToStrucMapping.add(siftsMapping);
620  0 maxChain.makeExactMapping(siftsMapping, seq);
621  0 maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS",
622    pdb.getId().toLowerCase(Locale.ROOT));
623  0 maxChain.transferResidueAnnotation(siftsMapping, null);
624  0 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
625   
626    } catch (SiftsException e)
627    {
628    // fall back to NW alignment
629  0 Console.error(e.getMessage());
630  0 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
631    targetChainId, maxChain, pdb, maxAlignseq);
632  0 seqToStrucMapping.add(nwMapping);
633  0 maxChain.makeExactMapping(maxAlignseq, seq);
634  0 maxChain.transferRESNUMFeatures(seq, "IEA:Jalview",
635    pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is
636    // this
637    // "IEA:Jalview" ?
638  0 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
639  0 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
640    }
641    }
642    else
643    {
644  0 List<StructureMapping> foundSiftsMappings = new ArrayList<>();
645  0 for (PDBChain chain : pdb.getChains())
646    {
647  0 StructureMapping siftsMapping = null;
648  0 try
649    {
650  0 siftsMapping = getStructureMapping(seq, pdbFile, chain.id,
651    pdb, chain, sqmpping, maxAlignseq, siftsClient);
652  0 foundSiftsMappings.add(siftsMapping);
653  0 chain.makeExactMapping(siftsMapping, seq);
654  0 chain.transferRESNUMFeatures(seq, "IEA: SIFTS",
655    pdb.getId().toLowerCase(Locale.ROOT));// FIXME: is this
656    // "IEA:SIFTS" ?
657  0 chain.transferResidueAnnotation(siftsMapping, null);
658    } catch (SiftsException e)
659    {
660  0 jalview.bin.Console.errPrintln(e.getMessage());
661    } catch (Exception e)
662    {
663  0 jalview.bin.Console.errPrintln(
664    "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
665  0 jalview.bin.Console.errPrintln(e.getMessage());
666    }
667    }
668  0 if (!foundSiftsMappings.isEmpty())
669    {
670  0 seqToStrucMapping.addAll(foundSiftsMappings);
671  0 ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
672    }
673    else
674    {
675  0 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
676    maxChainId, maxChain, pdb, maxAlignseq);
677  0 seqToStrucMapping.add(nwMapping);
678  0 maxChain.transferRESNUMFeatures(seq, null,
679    pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is this
680    // "IEA:Jalview" ?
681  0 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
682  0 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
683    }
684    }
685    }
686    else
687    {
688  195 if (progress != null)
689    {
690  0 progress.setProgressBar(
691    MessageManager.getString(
692    "status.obtaining_mapping_with_nw_alignment"),
693    progressSessionId);
694    }
695  195 StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
696    maxChain, pdb, maxAlignseq);
697  195 seqToStrucMapping.add(nwMapping);
698  195 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
699    }
700  195 if (forStructureView)
701    {
702  138 for (StructureMapping sm : seqToStrucMapping)
703    {
704  138 addStructureMapping(sm); // not addAll!
705    }
706    }
707  195 if (progress != null)
708    {
709  0 progress.setProgressBar(null, progressSessionId);
710    }
711    }
712  150 return pdb;
713    }
714   
715    /**
716    * check if we need to extract secondary structure from given pdbFile and
717    * transfer to sequences
718    *
719    * @param pdbFile
720    * @param sequenceArray
721    * @return
722    */
 
723  135 toggle private boolean isStructureFileProcessed(String pdbFile,
724    SequenceI[] sequenceArray)
725    {
726  135 boolean processed = false;
727  135 if (isPDBFileRegistered(pdbFile))
728    {
729  84 for (SequenceI sq : sequenceArray)
730    {
731  125 SequenceI ds = sq;
732  250 while (ds.getDatasetSequence() != null)
733    {
734  125 ds = ds.getDatasetSequence();
735    }
736  125 ;
737  125 if (ds.getAnnotation() != null)
738    {
739  103 for (AlignmentAnnotation ala : ds.getAnnotation())
740    {
741    // false if any annotation present from this structure
742    // JBPNote this fails for jmol/chimera view because the *file* is
743    // passed, not the structure data ID -
744  254 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
745    {
746  0 processed = true;
747    }
748    }
749    }
750    }
751    }
752  135 return processed;
753    }
754   
 
755  145 toggle public void addStructureMapping(StructureMapping sm)
756    {
757  145 if (!mappings.contains(sm))
758    {
759  78 mappings.add(sm);
760    }
761    }
762   
763    /**
764    * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
765    * uniprot or PDB
766    *
767    * @param seq
768    * @param pdbFile
769    * @param targetChainId
770    * @param pdb
771    * @param maxChain
772    * @param sqmpping
773    * @param maxAlignseq
774    * @param siftsClient
775    * client for retrieval of SIFTS mappings for this structure
776    * @return
777    * @throws SiftsException
778    */
 
779  0 toggle private StructureMapping getStructureMapping(SequenceI seq,
780    String pdbFile, String targetChainId, StructureFile pdb,
781    PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
782    AlignSeq maxAlignseq, SiftsClient siftsClient)
783    throws SiftsException
784    {
785  0 StructureMapping curChainMapping = siftsClient
786    .getSiftsStructureMapping(seq, pdbFile, targetChainId);
787  0 try
788    {
789  0 PDBChain chain = pdb.findChain(targetChainId);
790  0 if (chain != null)
791    {
792  0 chain.transferResidueAnnotation(curChainMapping, null);
793    }
794    } catch (Exception e)
795    {
796  0 e.printStackTrace();
797    }
798  0 return curChainMapping;
799    }
800   
 
801  195 toggle private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
802    String maxChainId, PDBChain maxChain, StructureFile pdb,
803    AlignSeq maxAlignseq)
804    {
805  195 final StringBuilder mappingDetails = new StringBuilder(128);
806  195 mappingDetails.append(NEWLINE)
807    .append("Sequence \u27f7 Structure mapping details");
808  195 mappingDetails.append(NEWLINE);
809  195 mappingDetails
810    .append("Method: inferred with Needleman & Wunsch alignment");
811  195 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
812    .append(NEWLINE).append("Sequence = ")
813    .append(maxChain.sequence.getSequenceAsString());
814  195 mappingDetails.append(NEWLINE).append("No of residues = ")
815    .append(maxChain.residues.size()).append(NEWLINE)
816    .append(NEWLINE);
817  195 PrintStream ps = new PrintStream(System.out)
818    {
 
819  195 toggle @Override
820    public void print(String x)
821    {
822  195 mappingDetails.append(x);
823    }
824   
 
825  0 toggle @Override
826    public void println()
827    {
828  0 mappingDetails.append(NEWLINE);
829    }
830    };
831   
832  195 maxAlignseq.printAlignment(ps);
833   
834  195 mappingDetails.append(NEWLINE).append("PDB start/end ");
835  195 mappingDetails.append(String.valueOf(maxAlignseq.seq2start))
836    .append(" ");
837  195 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
838  195 mappingDetails.append(NEWLINE).append("SEQ start/end ");
839  195 mappingDetails
840    .append(String
841    .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1)))
842    .append(" ");
843  195 mappingDetails.append(
844    String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1)));
845  195 mappingDetails.append(NEWLINE);
846  195 maxChain.makeExactMapping(maxAlignseq, seq);
847  195 jalview.datamodel.Mapping sqmpping = maxAlignseq
848    .getMappingFromS1(false);
849  195 maxChain.transferRESNUMFeatures(seq, null,
850    pdb.getId().toLowerCase(Locale.ROOT));
851   
852  195 HashMap<Integer, int[]> mapping = new HashMap<>();
853  195 int resNum = -10000;
854  195 int index = 0;
855  195 char insCode = ' ';
856   
857  195 do
858    {
859  21404 Atom tmp = maxChain.atoms.elementAt(index);
860  21404 if ((resNum != tmp.resNumber || insCode != tmp.insCode)
861    && tmp.alignmentMapping != -1)
862    {
863  21354 resNum = tmp.resNumber;
864  21354 insCode = tmp.insCode;
865  21354 if (tmp.alignmentMapping >= -1)
866    {
867  21354 mapping.put(tmp.alignmentMapping + 1,
868    new int[]
869    { tmp.resNumber, tmp.atomIndex });
870    }
871    }
872   
873  21404 index++;
874  21404 } while (index < maxChain.atoms.size());
875   
876  195 StructureMapping nwMapping = new StructureMapping(seq, pdbFile,
877    pdb.getId(), maxChainId, mapping, mappingDetails.toString());
878  195 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
879  195 return nwMapping;
880    }
881   
 
882  529 toggle public void removeStructureViewerListener(Object svl, String[] pdbfiles)
883    {
884  529 listeners.removeElement(svl);
885  529 if (svl instanceof SequenceListener)
886    {
887  1340 for (int i = 0; i < listeners.size(); i++)
888    {
889  834 if (listeners.elementAt(i) instanceof StructureListener)
890    {
891  42 ((StructureListener) listeners.elementAt(i))
892    .releaseReferences(svl);
893    }
894    }
895    }
896   
897  529 if (pdbfiles == null)
898    {
899  506 return;
900    }
901   
902    /*
903    * Remove mappings to the closed listener's PDB files, but first check if
904    * another listener is still interested
905    */
906  23 List<String> pdbs = new ArrayList<>(Arrays.asList(pdbfiles));
907   
908  23 StructureListener sl;
909  78 for (int i = 0; i < listeners.size(); i++)
910    {
911  55 if (listeners.elementAt(i) instanceof StructureListener)
912    {
913  6 sl = (StructureListener) listeners.elementAt(i);
914  6 for (String pdbfile : sl.getStructureFiles())
915    {
916  7 pdbs.remove(pdbfile);
917    }
918    }
919    }
920   
921    /*
922    * Rebuild the mappings set, retaining only those which are for 'other' PDB
923    * files
924    */
925  23 if (pdbs.size() > 0)
926    {
927  22 List<StructureMapping> tmp = new ArrayList<>();
928  22 for (StructureMapping sm : mappings)
929    {
930  37 if (!pdbs.contains(sm.pdbfile))
931    {
932  7 tmp.add(sm);
933    }
934    }
935   
936  22 mappings = tmp;
937    }
938    }
939   
940    /**
941    * hack to highlight a range of positions at once on any structure views
942    *
943    * @param sequenceRef
944    * @param is
945    * - series of int start-end ranges as positions on sequenceRef
946    * @param i
947    * @param object
948    */
 
949  0 toggle public void highlightPositionsOn(SequenceI sequenceRef, int[][] is,
950    Object source)
951    {
952  0 boolean hasSequenceListeners = handlingVamsasMo
953    || !seqmappings.isEmpty();
954  0 SearchResultsI results = null;
955  0 ArrayList<Integer> listOfPositions = new ArrayList<Integer>();
956  0 for (int[] s_e : is)
957    {
958  0 for (int p = s_e[0]; p <= s_e[1]; listOfPositions.add(p++))
959  0 ;
960    }
961  0 int seqpos[] = new int[listOfPositions.size()];
962  0 int i = 0;
963  0 for (Integer p : listOfPositions)
964    {
965  0 seqpos[i++] = p;
966    }
967   
968  0 for (i = 0; i < listeners.size(); i++)
969    {
970  0 Object listener = listeners.elementAt(i);
971  0 if (listener == source)
972    {
973    // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
974    // Temporary fudge with SequenceListener.getVamsasSource()
975  0 continue;
976    }
977  0 if (listener instanceof StructureListener)
978    {
979  0 highlightStructure((StructureListener) listener, sequenceRef,
980    seqpos);
981    }
982   
983    }
984    }
985   
986    /**
987    * Propagate mouseover of a single position in a structure
988    *
989    * @param pdbResNum
990    * @param chain
991    * @param pdbfile
992    * @return
993    */
 
994  1 toggle public String mouseOverStructure(int pdbResNum, String chain,
995    String pdbfile)
996    {
997  1 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
998  1 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
999  1 return mouseOverStructure(atoms);
1000    }
1001   
1002    /**
1003    * Propagate mouseover or selection of multiple positions in a structure
1004    *
1005    * @param atoms
1006    */
 
1007  1 toggle public String mouseOverStructure(List<AtomSpec> atoms)
1008    {
1009  1 if (listeners == null)
1010    {
1011    // old or prematurely sent event
1012  0 return null;
1013    }
1014  1 boolean hasSequenceListener = false;
1015  4 for (int i = 0; i < listeners.size(); i++)
1016    {
1017  3 if (listeners.elementAt(i) instanceof SequenceListener)
1018    {
1019  2 hasSequenceListener = true;
1020    }
1021    }
1022  1 if (!hasSequenceListener)
1023    {
1024  0 return null;
1025    }
1026   
1027  1 SearchResultsI results = findAlignmentPositionsForStructurePositions(
1028    atoms);
1029  1 String result = null;
1030  1 for (Object li : listeners)
1031    {
1032  3 if (li instanceof SequenceListener)
1033    {
1034  2 String s = ((SequenceListener) li).highlightSequence(results);
1035  2 if (s != null)
1036    {
1037  0 result = s;
1038    }
1039    }
1040    }
1041  1 return result;
1042    }
1043   
1044    /**
1045    * Constructs a SearchResults object holding regions (if any) in the Jalview
1046    * alignment which have a mapping to the structure viewer positions in the
1047    * supplied list
1048    *
1049    * @param atoms
1050    * @return
1051    */
 
1052  1 toggle public SearchResultsI findAlignmentPositionsForStructurePositions(
1053    List<AtomSpec> atoms)
1054    {
1055  1 SearchResultsI results = new SearchResults();
1056  1 for (AtomSpec atom : atoms)
1057    {
1058  1 SequenceI lastseq = null;
1059  1 int lastipos = -1;
1060  1 for (StructureMapping sm : mappings)
1061    {
1062  2 if (sm.pdbfile.equals(atom.getPdbFile())
1063    && sm.pdbchain.equals(atom.getChain()))
1064    {
1065  1 int indexpos = sm.getSeqPos(atom.getPdbResNum());
1066  1 if (lastipos != indexpos || lastseq != sm.sequence)
1067    {
1068  1 results.appendResult(sm.sequence, indexpos, indexpos);
1069  1 lastipos = indexpos;
1070  1 lastseq = sm.sequence;
1071    // construct highlighted sequence list
1072  1 for (AlignedCodonFrame acf : seqmappings)
1073    {
1074  0 acf.markMappedRegion(sm.sequence, indexpos, results);
1075    }
1076    }
1077    }
1078    }
1079    }
1080  1 return results;
1081    }
1082   
1083    /**
1084    * highlight regions associated with a position (indexpos) in seq
1085    *
1086    * @param seq
1087    * the sequence that the mouse over occurred on
1088    * @param indexpos
1089    * the absolute position being mouseovered in seq (0 to seq.length())
1090    * @param seqPos
1091    * the sequence position (if -1, seq.findPosition is called to
1092    * resolve the residue number)
1093    */
 
1094  0 toggle public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
1095    VamsasSource source)
1096    {
1097  0 boolean hasSequenceListeners = handlingVamsasMo
1098    || !seqmappings.isEmpty();
1099  0 SearchResultsI results = null;
1100  0 if (seqPos == -1)
1101    {
1102  0 seqPos = seq.findPosition(indexpos);
1103    }
1104  0 for (int i = 0; i < listeners.size(); i++)
1105    {
1106  0 Object listener = listeners.elementAt(i);
1107  0 if (listener == source)
1108    {
1109    // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1110    // Temporary fudge with SequenceListener.getVamsasSource()
1111  0 continue;
1112    }
1113  0 if (listener instanceof StructureListener)
1114    {
1115  0 highlightStructure((StructureListener) listener, seq, seqPos);
1116    }
1117    else
1118    {
1119  0 if (listener instanceof SequenceListener)
1120    {
1121  0 final SequenceListener seqListener = (SequenceListener) listener;
1122  0 if (hasSequenceListeners
1123    && seqListener.getVamsasSource() != source)
1124    {
1125  0 if (relaySeqMappings)
1126    {
1127  0 if (results == null)
1128    {
1129  0 results = MappingUtils.buildSearchResults(seq, seqPos,
1130    seqmappings);
1131    }
1132  0 if (handlingVamsasMo)
1133    {
1134  0 results.addResult(seq, seqPos, seqPos);
1135   
1136    }
1137  0 if (!results.isEmpty())
1138    {
1139  0 seqListener.highlightSequence(results);
1140    }
1141    }
1142    }
1143    }
1144  0 else if (listener instanceof VamsasListener && !handlingVamsasMo)
1145    {
1146  0 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
1147    source);
1148    }
1149  0 else if (listener instanceof SecondaryStructureListener)
1150    {
1151  0 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
1152    indexpos, seqPos);
1153    }
1154    }
1155    }
1156    }
1157   
1158    /**
1159    * Send suitable messages to a StructureListener to highlight atoms
1160    * corresponding to the given sequence position(s)
1161    *
1162    * @param sl
1163    * @param seq
1164    * @param positions
1165    */
 
1166  0 toggle public void highlightStructure(StructureListener sl, SequenceI seq,
1167    int... positions)
1168    {
1169  0 if (!sl.isListeningFor(seq))
1170    {
1171  0 return;
1172    }
1173  0 int atomNo;
1174  0 List<AtomSpec> atoms = new ArrayList<>();
1175  0 for (StructureMapping sm : mappings)
1176    {
1177  0 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1178    || (sm.sequence.getDatasetSequence() != null && sm.sequence
1179    .getDatasetSequence() == seq.getDatasetSequence()))
1180    {
1181  0 for (int index : positions)
1182    {
1183  0 atomNo = sm.getAtomNum(index);
1184   
1185  0 if (atomNo > 0)
1186    {
1187  0 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1188    sm.getPDBResNum(index), atomNo));
1189    }
1190    }
1191    }
1192    }
1193  0 sl.highlightAtoms(atoms);
1194    }
1195   
 
1196  0 toggle public void highlightStructureRegionsFor(StructureListener sl,
1197    SequenceI[] seqs, int... columns)
1198    {
1199  0 List<SequenceI> to_highlight = new ArrayList<SequenceI>();
1200  0 for (SequenceI seq : seqs)
1201    {
1202  0 if (sl.isListeningFor(seq))
1203    {
1204  0 to_highlight.add(seq);
1205    }
1206    }
1207  0 if (to_highlight.size() == 0)
1208    {
1209  0 return;
1210    }
1211  0 List<AtomSpec> atoms = new ArrayList<>();
1212  0 for (SequenceI seq : to_highlight)
1213    {
1214  0 int atomNo;
1215  0 for (StructureMapping sm : mappings)
1216    {
1217  0 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1218    || (sm.sequence.getDatasetSequence() != null && sm.sequence
1219    .getDatasetSequence() == seq.getDatasetSequence()))
1220    {
1221   
1222  0 for (int i = 0; i < columns.length; i += 2)
1223    {
1224  0 ContiguousI positions = seq.findPositions(columns[i] + 1,
1225    columns[i + 1] + 1);
1226  0 if (positions == null)
1227    {
1228  0 continue;
1229    }
1230  0 for (int index = positions.getBegin(); index <= positions
1231    .getEnd(); index++)
1232    {
1233   
1234  0 atomNo = sm.getAtomNum(index);
1235   
1236  0 if (atomNo > 0)
1237    {
1238  0 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1239    sm.getPDBResNum(index), atomNo));
1240    }
1241    }
1242    }
1243    }
1244    }
1245  0 if (atoms.size() > 0)
1246    {
1247  0 sl.highlightAtoms(atoms);
1248    }
1249    }
1250    }
1251   
1252    /**
1253    * true if a mouse over event from an external (ie Vamsas) source is being
1254    * handled
1255    */
1256    boolean handlingVamsasMo = false;
1257   
1258    long lastmsg = 0;
1259   
1260    /**
1261    * as mouseOverSequence but only route event to SequenceListeners
1262    *
1263    * @param sequenceI
1264    * @param position
1265    * in an alignment sequence
1266    */
 
1267  0 toggle public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
1268    VamsasSource source)
1269    {
1270  0 handlingVamsasMo = true;
1271  0 long msg = sequenceI.hashCode() * (1 + position);
1272  0 if (lastmsg != msg)
1273    {
1274  0 lastmsg = msg;
1275  0 mouseOverSequence(sequenceI, position, -1, source);
1276    }
1277  0 handlingVamsasMo = false;
1278    }
1279   
 
1280  0 toggle public Annotation[] colourSequenceFromStructure(SequenceI seq,
1281    String pdbid)
1282    {
1283  0 return null;
1284    // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
1285    // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
1286    /*
1287    * Annotation [] annotations = new Annotation[seq.getLength()];
1288    *
1289    * StructureListener sl; int atomNo = 0; for (int i = 0; i <
1290    * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
1291    * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
1292    *
1293    * for (int j = 0; j < mappings.length; j++) {
1294    *
1295    * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
1296    * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
1297    * jalview.bin.Console.outPrintln(pdbid+" "+mappings[j].getPdbId() +"
1298    * "+mappings[j].pdbfile);
1299    *
1300    * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
1301    * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
1302    *
1303    * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
1304    * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
1305    * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
1306    * mappings[j].pdbfile); }
1307    *
1308    * annotations[index] = new Annotation("X",null,' ',0,col); } return
1309    * annotations; } } } }
1310    *
1311    * return annotations;
1312    */
1313    }
1314   
 
1315  0 toggle public void structureSelectionChanged()
1316    {
1317    }
1318   
 
1319  0 toggle public void sequenceSelectionChanged()
1320    {
1321    }
1322   
 
1323  2722 toggle public void sequenceColoursChanged(Object source)
1324    {
1325  2722 StructureListener sl;
1326  12605 for (int i = 0; i < listeners.size(); i++)
1327    {
1328  9883 if (listeners.elementAt(i) instanceof StructureListener)
1329    {
1330  893 sl = (StructureListener) listeners.elementAt(i);
1331  893 sl.updateColours(source);
1332    }
1333    }
1334    }
1335   
 
1336  604 toggle public StructureMapping[] getMapping(String pdbfile)
1337    {
1338  604 List<StructureMapping> tmp = new ArrayList<>();
1339  604 for (StructureMapping sm : mappings)
1340    {
1341  625 if (sm.pdbfile.equals(pdbfile))
1342    {
1343  439 tmp.add(sm);
1344    }
1345    }
1346  604 return tmp.toArray(new StructureMapping[tmp.size()]);
1347    }
1348   
1349    /**
1350    * Returns a readable description of all mappings for the given pdbfile to any
1351    * of the given sequences
1352    *
1353    * @param pdbfile
1354    * @param seqs
1355    * @return
1356    */
 
1357  0 toggle public String printMappings(String pdbfile, List<SequenceI> seqs)
1358    {
1359  0 if (pdbfile == null || seqs == null || seqs.isEmpty())
1360    {
1361  0 return "";
1362    }
1363   
1364  0 StringBuilder sb = new StringBuilder(64);
1365  0 for (StructureMapping sm : mappings)
1366    {
1367  0 if (Platform.pathEquals(sm.pdbfile, pdbfile)
1368    && seqs.contains(sm.sequence))
1369    {
1370  0 sb.append(sm.mappingDetails);
1371  0 sb.append(NEWLINE);
1372    // separator makes it easier to read multiple mappings
1373  0 sb.append("=====================");
1374  0 sb.append(NEWLINE);
1375    }
1376    }
1377  0 sb.append(NEWLINE);
1378   
1379  0 return sb.toString();
1380    }
1381   
1382    /**
1383    * Remove the given mapping
1384    *
1385    * @param acf
1386    */
 
1387  27 toggle public void deregisterMapping(AlignedCodonFrame acf)
1388    {
1389  27 if (acf != null)
1390    {
1391  27 boolean removed = seqmappings.remove(acf);
1392  27 if (removed && seqmappings.isEmpty())
1393    { // debug
1394  2 jalview.bin.Console.outPrintln("All mappings removed");
1395    }
1396    }
1397    }
1398   
1399    /**
1400    * Add each of the given codonFrames to the stored set, if not aready present.
1401    *
1402    * @param mappings
1403    */
 
1404  554 toggle public void registerMappings(List<AlignedCodonFrame> mappings)
1405    {
1406  554 if (mappings != null)
1407    {
1408  554 for (AlignedCodonFrame acf : mappings)
1409    {
1410  152 registerMapping(acf);
1411    }
1412    }
1413    }
1414   
1415    /**
1416    * Add the given mapping to the stored set, unless already stored.
1417    */
 
1418  161 toggle public void registerMapping(AlignedCodonFrame acf)
1419    {
1420  161 if (acf != null)
1421    {
1422  161 if (!seqmappings.contains(acf))
1423    {
1424  38 seqmappings.add(acf);
1425    }
1426    }
1427    }
1428   
1429    /**
1430    * Resets this object to its initial state by removing all registered
1431    * listeners, codon mappings, PDB file mappings
1432    */
 
1433  237 toggle public void resetAll()
1434    {
1435  237 if (mappings != null)
1436    {
1437  237 mappings.clear();
1438    }
1439  237 if (seqmappings != null)
1440    {
1441  237 seqmappings.clear();
1442    }
1443  237 if (sel_listeners != null)
1444    {
1445  237 sel_listeners.clear();
1446    }
1447  237 if (listeners != null)
1448    {
1449  237 listeners.clear();
1450    }
1451  237 if (commandListeners != null)
1452    {
1453  237 commandListeners.clear();
1454    }
1455  237 if (view_listeners != null)
1456    {
1457  237 view_listeners.clear();
1458    }
1459  237 if (pdbFileNameId != null)
1460    {
1461  237 pdbFileNameId.clear();
1462    }
1463  237 if (pdbIdFileName != null)
1464    {
1465  237 pdbIdFileName.clear();
1466    }
1467    }
1468   
 
1469  478 toggle public void addSelectionListener(SelectionListener selecter)
1470    {
1471  478 if (!sel_listeners.contains(selecter))
1472    {
1473  478 sel_listeners.add(selecter);
1474    }
1475    }
1476   
 
1477  506 toggle public void removeSelectionListener(SelectionListener toremove)
1478    {
1479  506 if (sel_listeners.contains(toremove))
1480    {
1481  253 sel_listeners.remove(toremove);
1482    }
1483    }
1484   
 
1485  23 toggle public synchronized void sendSelection(
1486    jalview.datamodel.SequenceGroup selection,
1487    jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden,
1488    SelectionSource source)
1489    {
1490  23 for (SelectionListener slis : sel_listeners)
1491    {
1492  106 if (slis != source)
1493    {
1494  106 slis.selection(selection, colsel, hidden, source);
1495    }
1496    }
1497    }
1498   
1499    Vector<AlignmentViewPanelListener> view_listeners = new Vector<>();
1500   
 
1501  0 toggle public synchronized void sendViewPosition(
1502    jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1503    int startSeq, int endSeq)
1504    {
1505   
1506  0 if (view_listeners != null && view_listeners.size() > 0)
1507    {
1508  0 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1509    .elements();
1510  0 while (listeners.hasMoreElements())
1511    {
1512  0 AlignmentViewPanelListener slis = listeners.nextElement();
1513  0 if (slis != source)
1514    {
1515  0 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1516    }
1517  0 ;
1518    }
1519    }
1520    }
1521   
1522    /**
1523    * release all references associated with this manager provider
1524    *
1525    * @param jalviewLite
1526    */
 
1527  0 toggle public static void release(StructureSelectionManagerProvider jalviewLite)
1528    {
1529    // synchronized (instances)
1530    {
1531  0 if (instances == null)
1532    {
1533  0 return;
1534    }
1535  0 StructureSelectionManager mnger = (instances.get(jalviewLite));
1536  0 if (mnger != null)
1537    {
1538  0 instances.remove(jalviewLite);
1539  0 try
1540    {
1541    /* bsoares 2019-03-20 finalize deprecated, no apparent external
1542    * resources to close
1543    */
1544    // mnger.finalize();
1545    } catch (Throwable x)
1546    {
1547    }
1548    }
1549    }
1550    }
1551   
 
1552  359 toggle public void registerPDBEntry(PDBEntry pdbentry)
1553    {
1554  359 if (pdbentry.getFile() != null
1555    && pdbentry.getFile().trim().length() > 0)
1556    {
1557  139 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1558    }
1559    }
1560   
 
1561  6 toggle public void addCommandListener(CommandListener cl)
1562    {
1563  6 if (!commandListeners.contains(cl))
1564    {
1565  6 commandListeners.add(cl);
1566    }
1567    }
1568   
 
1569  0 toggle public boolean hasCommandListener(CommandListener cl)
1570    {
1571  0 return this.commandListeners.contains(cl);
1572    }
1573   
 
1574  253 toggle public boolean removeCommandListener(CommandListener l)
1575    {
1576  253 return commandListeners.remove(l);
1577    }
1578   
1579    /**
1580    * Forward a command to any command listeners (except for the command's
1581    * source).
1582    *
1583    * @param command
1584    * the command to be broadcast (in its form after being performed)
1585    * @param undo
1586    * if true, the command was being 'undone'
1587    * @param source
1588    */
 
1589  1 toggle public void commandPerformed(CommandI command, boolean undo,
1590    VamsasSource source)
1591    {
1592  1 for (CommandListener listener : commandListeners)
1593    {
1594  0 listener.mirrorCommand(command, undo, this, source);
1595    }
1596    }
1597   
1598    /**
1599    * Returns a new CommandI representing the given command as mapped to the
1600    * given sequences. If no mapping could be made, or the command is not of a
1601    * mappable kind, returns null.
1602    *
1603    * @param command
1604    * @param undo
1605    * @param mapTo
1606    * @param gapChar
1607    * @return
1608    */
 
1609  0 toggle public CommandI mapCommand(CommandI command, boolean undo,
1610    final AlignmentI mapTo, char gapChar)
1611    {
1612  0 if (command instanceof EditCommand)
1613    {
1614  0 return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo,
1615    gapChar, seqmappings);
1616    }
1617  0 else if (command instanceof OrderCommand)
1618    {
1619  0 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1620    mapTo, seqmappings);
1621    }
1622  0 return null;
1623    }
1624   
 
1625  15 toggle public List<AlignedCodonFrame> getSequenceMappings()
1626    {
1627  15 return seqmappings;
1628    }
1629   
1630    /**
1631    * quick and dirty route to just highlight all structure positions for a range
1632    * of columns
1633    *
1634    * @param sequencesArray
1635    * @param is
1636    * start-end columns on sequencesArray
1637    * @param source
1638    * origin parent AlignmentPanel
1639    */
 
1640  8 toggle public void highlightPositionsOnMany(SequenceI[] sequencesArray, int[] is,
1641    Object source)
1642    {
1643  16 for (int i = 0; i < listeners.size(); i++)
1644    {
1645  8 Object listener = listeners.elementAt(i);
1646  8 if (listener == source)
1647    {
1648    // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1649    // Temporary fudge with SequenceListener.getVamsasSource()
1650  0 continue;
1651    }
1652  8 if (listener instanceof StructureListener)
1653    {
1654  0 highlightStructureRegionsFor((StructureListener) listener,
1655    sequencesArray, is);
1656    }
1657    }
1658    }
1659   
 
1660  0 toggle public Map<String, String> getPdbFileNameIdMap()
1661    {
1662  0 return pdbFileNameId;
1663    }
1664   
 
1665  0 toggle public Map<String, String> getPdbIdFileNameMap()
1666    {
1667  0 return pdbIdFileName;
1668    }
1669   
 
1670  127 toggle public static void doConfigureStructurePrefs(
1671    StructureSelectionManager ssm)
1672    {
1673  127 doConfigureStructurePrefs(ssm,
1674    Cache.getDefault(Preferences.ADD_SS_ANN, true),
1675    Cache.getDefault(Preferences.ADD_TEMPFACT_ANN, true),
1676    Cache.getDefault(Preferences.STRUCT_FROM_PDB, true),
1677    Cache.getDefault(Preferences.USE_RNAVIEW, false));
1678    }
1679   
 
1680  127 toggle public static void doConfigureStructurePrefs(
1681    StructureSelectionManager ssm, boolean add_ss_ann,
1682    boolean add_tempfact_ann, boolean struct_from_pdb,
1683    boolean use_rnaview)
1684    {
1685  127 if (add_ss_ann)
1686    {
1687  127 ssm.setAddTempFacAnnot(add_tempfact_ann);
1688  127 ssm.setProcessSecondaryStructure(struct_from_pdb);
1689    // JAL-3915 - RNAView is no longer an option so this has no effect
1690  127 ssm.setSecStructServices(use_rnaview);
1691    }
1692    else
1693    {
1694  0 ssm.setAddTempFacAnnot(false);
1695  0 ssm.setProcessSecondaryStructure(false);
1696  0 ssm.setSecStructServices(false);
1697    }
1698    }
1699   
1700    }