Clover icon

Coverage Report

  1. Project Clover database Wed Nov 5 2025 13:15:40 GMT
  2. Package jalview.structure

File StructureSelectionManager.java

 

Coverage histogram

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

Code metrics

254
483
65
1
1,810
1,242
232
0.48
7.43
65
3.57

Classes

Class Line # Actions
StructureSelectionManager 70 483 232
0.587281858.7%
 

Contributing tests

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