Clover icon

Coverage Report

  1. Project Clover database Thu Mar 26 2026 08:01:43 GMT
  2. Package jalview.structure

File StructureSelectionManager.java

 

Coverage histogram

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

Code metrics

258
496
65
1
1,836
1,267
234
0.47
7.63
65
3.6

Classes

Class Line # Actions
StructureSelectionManager 71 496 234
0.592185659.2%
 

Contributing tests

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