Clover icon

Coverage Report

  1. Project Clover database Wed Dec 3 2025 15:58:31 GMT
  2. Package jalview.structure

File StructureSelectionManager.java

 

Coverage histogram

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

Code metrics

256
486
65
1
1,821
1,247
233
0.48
7.48
65
3.58

Classes

Class Line # Actions
StructureSelectionManager 70 486 233
0.5885997458.9%
 

Contributing tests

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