Clover icon

Coverage Report

  1. Project Clover database Mon Nov 11 2024 20:42:03 GMT
  2. Package jalview.structure

File StructureSelectionManager.java

 

Coverage histogram

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

Code metrics

250
471
62
1
1,760
1,218
226
0.48
7.6
62
3.65

Classes

Class Line # Actions
StructureSelectionManager 70 471 226
0.560664156.1%
 

Contributing tests

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