Clover icon

Coverage Report

  1. Project Clover database Wed Jan 7 2026 02:49:01 GMT
  2. Package jalview.structure

File StructureSelectionManager.java

 

Coverage histogram

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

Code metrics

252
502
70
1
1,951
1,270
240
0.48
7.17
70
3.43

Classes

Class Line # Actions
StructureSelectionManager 76 502 240
0.56553456.6%
 

Contributing tests

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