2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
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.
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.
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.
21 package jalview.structure;
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.HashSet;
30 import java.util.IdentityHashMap;
31 import java.util.List;
32 import java.util.Locale;
35 import java.util.Vector;
37 import jalview.analysis.AlignSeq;
38 import jalview.api.StructureSelectionManagerProvider;
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.io.AppletFormatAdapter;
56 import jalview.io.DataSourceType;
57 import jalview.io.StructureFile;
58 import jalview.structure.StructureImportSettings.TFType;
59 import jalview.struture.PDBEntryUtils;
60 import jalview.util.MapList;
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;
68 import mc_view.PDBChain;
69 import mc_view.PDBfile;
71 public class StructureSelectionManager
73 public final static String NEWLINE = System.lineSeparator();
75 static IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> instances;
77 private List<StructureMapping> mappings = new ArrayList<>();
79 private boolean processSecondaryStructure = false;
81 private boolean secStructServices = false;
83 private boolean addTempFacAnnot = false;
86 * Set of any registered mappings between (dataset) sequences.
88 private List<AlignedCodonFrame> seqmappings = new ArrayList<>();
90 private List<CommandListener> commandListeners = new ArrayList<>();
92 private List<SelectionListener> sel_listeners = new ArrayList<>();
95 * @return true if will try to use external services for processing secondary
98 public boolean isSecStructServices()
100 return secStructServices;
104 * control use of external services for processing secondary structure
106 * @param secStructServices
108 public void setSecStructServices(boolean secStructServices)
110 this.secStructServices = secStructServices;
114 * flag controlling addition of any kind of structural annotation
116 * @return true if temperature factor annotation will be added
118 public boolean isAddTempFacAnnot()
120 return addTempFacAnnot;
124 * set flag controlling addition of structural annotation
126 * @param addTempFacAnnot
128 public void setAddTempFacAnnot(boolean addTempFacAnnot)
130 this.addTempFacAnnot = addTempFacAnnot;
135 * @return if true, the structure manager will attempt to add secondary
136 * structure lines for unannotated sequences
139 public boolean isProcessSecondaryStructure()
141 return processSecondaryStructure;
145 * Control whether structure manager will try to annotate mapped sequences
146 * with secondary structure from PDB data.
150 public void setProcessSecondaryStructure(boolean enable)
152 processSecondaryStructure = enable;
156 * debug function - write all mappings to stdout
158 public void reportMapping()
160 if (mappings.isEmpty())
163 .errPrintln("reportMapping: No PDB/Sequence mappings.");
167 jalview.bin.Console.errPrintln(
168 "reportMapping: There are " + mappings.size() + " mappings.");
170 for (StructureMapping sm : mappings)
173 .errPrintln("mapping " + i++ + " : " + sm.pdbfile);
179 * map between the PDB IDs (or structure identifiers) used by Jalview and the
180 * absolute filenames for PDB data that corresponds to it
182 Map<String, String> pdbIdFileName = new HashMap<>();
184 Map<String, String> pdbFileNameId = new HashMap<>();
186 public void registerPDBFile(String idForFile, String absoluteFile)
188 pdbIdFileName.put(idForFile, absoluteFile);
189 pdbFileNameId.put(absoluteFile, idForFile);
192 public String findIdForPDBFile(String idOrFile)
194 String id = pdbFileNameId.get(idOrFile);
198 public String findFileForPDBId(String idOrFile)
200 String id = pdbIdFileName.get(idOrFile);
204 public boolean isPDBFileRegistered(String idOrFile)
206 return pdbFileNameId.containsKey(idOrFile)
207 || pdbIdFileName.containsKey(idOrFile);
210 private static StructureSelectionManager nullProvider = null;
212 public static StructureSelectionManager getStructureSelectionManager(
213 StructureSelectionManagerProvider context)
217 if (nullProvider == null)
219 if (instances != null)
221 throw new Error(MessageManager.getString(
222 "error.implementation_error_structure_selection_manager_null"),
223 new NullPointerException(MessageManager
224 .getString("exception.ssm_context_is_null")));
228 nullProvider = new StructureSelectionManager();
233 if (instances == null)
235 instances = new java.util.IdentityHashMap<>();
237 StructureSelectionManager instance = instances.get(context);
238 if (instance == null)
240 if (nullProvider != null)
242 instance = nullProvider;
246 instance = new StructureSelectionManager();
248 instances.put(context, instance);
254 * flag controlling whether SeqMappings are relayed from received sequence
255 * mouse over events to other sequences
257 boolean relaySeqMappings = true;
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
266 public void setRelaySeqMappings(boolean relay)
268 relaySeqMappings = relay;
272 * get the state of the relay seqMappings flag.
274 * @return true if sequence mouse overs are being relayed to other mapped
277 public boolean isRelaySeqMappingsEnabled()
279 return relaySeqMappings;
282 Vector listeners = new Vector();
285 * register a listener for alignment sequence mouseover events
289 public void addStructureViewerListener(Object svl)
291 if (!listeners.contains(svl))
293 listeners.addElement(svl);
298 * Returns the filename the PDB id is already mapped to if known, or null if
304 public String alreadyMappedToFile(String pdbid)
306 for (StructureMapping sm : mappings)
308 if (sm.getPdbId().equalsIgnoreCase(pdbid))
317 * Import structure data and register a structure mapping for broadcasting
318 * colouring, mouseovers and selection events (convenience wrapper).
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)
326 * - structure data resource
328 * - how to resolve data from resource
329 * @return null or the structure data parsed as a pdb file
331 synchronized public StructureFile setMapping(SequenceI[] sequence,
332 String[] targetChains, String pdbFile, DataSourceType protocol,
333 IProgressIndicator progress)
335 return computeMapping(true, sequence, targetChains, pdbFile, protocol,
336 progress, null, null, true);
340 * Import a single structure file and register sequence structure mappings for
341 * broadcasting colouring, mouseovers and selection events (convenience
344 * @param forStructureView
345 * when true, record the mapping for use in mouseOvers
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)
352 * - structure data resource
354 * - how to resolve data from resource
355 * @return null or the structure data parsed as a pdb file
357 synchronized public StructureFile setMapping(boolean forStructureView,
358 SequenceI[] sequenceArray, String[] targetChainIds,
359 String pdbFile, DataSourceType sourceType, TFType tft,
362 return setMapping(forStructureView, sequenceArray, targetChainIds,
363 pdbFile, sourceType, tft, paeFilename, true);
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.
371 * @param forStructureView
372 * when true, record the mapping for use in mouseOvers
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
382 * - structure data resource
384 * - how to resolve data from resource
386 * - specify how to interpret the temperature factor column in the
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
394 * @return null or the structure data parsed as a pdb file
396 synchronized public StructureFile setMapping(boolean forStructureView,
397 SequenceI[] sequenceArray, String[] targetChainIds,
398 String pdbFile, DataSourceType sourceType, TFType tft,
399 String paeFilename, boolean doXferSettings)
401 return computeMapping(forStructureView, sequenceArray, targetChainIds,
402 pdbFile, sourceType, null, tft, paeFilename, doXferSettings);
406 * create sequence structure mappings between each sequence and the given
407 * pdbFile (retrieved via the given protocol). Either constructs a mapping
408 * using NW alignment or derives one from any available SIFTS mapping data.
410 * @param forStructureView
411 * when true, record the mapping for use in mouseOvers
413 * @param sequenceArray
414 * - one or more sequences to be mapped to pdbFile
415 * @param targetChainIds
416 * - optional chain specification for mapping each sequence to pdb
417 * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
418 * - this should be List<List<String>>, empty lists indicate no
419 * predefined mappings
421 * - structure data resource
423 * - how to resolve data from resource
424 * @param IProgressIndicator
425 * reference to UI component that maintains a progress bar for the
428 * - specify how to interpret the temperature factor column in the
431 * - when not null, specifies a filename containing a matrix
432 * formatted in JSON using one of the known PAE formats
433 * @param doXferSettings
434 * - when true, transfer annotation to mapped sequences in
436 * @return null or the structure data parsed as a pdb file
438 synchronized public StructureFile computeMapping(boolean forStructureView,
439 SequenceI[] sequenceArray, String[] targetChainIds,
440 String pdbFile, DataSourceType sourceType,
441 IProgressIndicator progress, TFType tft, String paeFilename,
442 boolean doXferSettings)
444 long progressSessionId = System.currentTimeMillis() * 3;
447 * do we extract and transfer annotation from 3D data ?
449 // FIXME: possibly should just delete
451 boolean parseSecStr = processSecondaryStructure
452 && !isStructureFileProcessed(pdbFile, sequenceArray);
454 StructureFile pdb = null;
455 boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
458 // FIXME if sourceType is not null, we've lost data here
459 sourceType = AppletFormatAdapter.checkProtocol(pdbFile);
460 pdb = new JmolParser(false, pdbFile, sourceType);
461 if (paeFilename != null)
463 pdb.setPAEMatrix(paeFilename);
465 pdb.setTemperatureFactorType(tft);
466 pdb.addSettings(parseSecStr && processSecondaryStructure,
467 parseSecStr && addTempFacAnnot,
468 parseSecStr && secStructServices);
469 // save doXferSettings and reset after doParse()
470 boolean temp = pdb.getDoXferSettings();
471 pdb.setDoXferSettings(doXferSettings);
473 pdb.setDoXferSettings(temp);
474 if (pdb.getId() != null && pdb.getId().trim().length() > 0
475 && DataSourceType.FILE == sourceType)
477 registerPDBFile(pdb.getId().trim(), pdbFile);
479 // if PDBId is unavailable then skip SIFTS mapping execution path
480 // TODO: JAL-3868 need to know if structure is actually from
481 // PDB (has valid PDB ID and has provenance suggesting it
482 // actually came from PDB)
483 boolean isProtein = false;
484 for (SequenceI s : sequenceArray)
492 isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable()
493 && !pdb.getId().startsWith("AF-") && isProtein;
495 } catch (Exception ex)
497 ex.printStackTrace();
501 * sifts client - non null if SIFTS mappings are to be used
503 SiftsClient siftsClient = null;
508 siftsClient = new SiftsClient(pdb);
510 } catch (SiftsException e)
512 isMapUsingSIFTs = false;
513 Console.error("SIFTS mapping failed", e);
514 Console.error("Falling back on Needleman & Wunsch alignment");
518 String targetChainId;
519 for (int s = 0; s < sequenceArray.length; s++)
521 boolean infChain = true;
522 final SequenceI seq = sequenceArray[s];
524 while (ds.getDatasetSequence() != null)
526 ds = ds.getDatasetSequence();
528 List <PDBEntry> putativePDBe = PDBEntryUtils.selectPutativePDBe(seq,ds, pdb);
530 if (targetChainIds != null && targetChainIds[s] != null)
533 targetChainId = targetChainIds[s];
536 targetChainId = PDBEntryUtils.inferChainId(seq);
540 * Attempt pairwise alignment of the sequence with each chain in the PDB,
541 * and remember the highest scoring chain
544 AlignSeq maxAlignseq = null;
545 String maxChainId = " ";
546 PDBChain maxChain = null;
547 boolean first = true;
548 PDBChain idLengthChain = null;
549 for (PDBChain chain : pdb.getChains())
551 if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
554 continue; // don't try to map chains don't match.
556 PDBEntry putativeChain = null;
557 if (!putativePDBe.isEmpty() && (putativeChain = PDBEntryUtils
558 .selectPutativePDBEntry(putativePDBe, chain)) == null)
562 // TODO: correctly determine sequence type for mixed na/peptide
564 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
565 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
567 // TODO: JAL-4366 determinine of a crummy alignment but exact match should make this chain the one to be mapped to a 3di sequence
568 if (as.s1str.length() == as.s2str.length())
570 idLengthChain = chain;
573 if (first || as.maxscore > max
574 || (as.maxscore == max && chain.id.equals(targetChainId)))
580 maxChainId = chain.id;
583 if (maxChain == null)
587 if (sourceType == DataSourceType.PASTE)
589 pdbFile = "INLINE" + pdb.getId();
591 List<StructureMapping> seqToStrucMapping = new ArrayList<>();
593 List<StructureMapping> foundSiftsMappings = new ArrayList<>();
594 if (isMapUsingSIFTs && seq.isProtein())
596 if (progress != null)
598 progress.setProgressBar(
600 .getString("status.obtaining_mapping_with_sifts"),
603 jalview.datamodel.Mapping sqmpping = maxAlignseq
604 .getMappingFromS1(false);
605 if (targetChainId != null && !targetChainId.trim().isEmpty())
607 StructureMapping siftsMapping;
610 siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
611 pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
612 seqToStrucMapping.add(siftsMapping);
613 maxChain.makeExactMapping(siftsMapping, seq);
614 maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS",
615 pdb.getId().toLowerCase(Locale.ROOT));
616 maxChain.transferResidueAnnotation(siftsMapping, null);
617 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
618 foundSiftsMappings.add(siftsMapping);
620 } catch (SiftsException e)
622 Console.error(e.getMessage());
627 for (PDBChain chain : pdb.getChains())
629 StructureMapping siftsMapping = null;
632 siftsMapping = getStructureMapping(seq, pdbFile, chain.id,
633 pdb, chain, sqmpping, maxAlignseq, siftsClient);
634 foundSiftsMappings.add(siftsMapping);
635 chain.makeExactMapping(siftsMapping, seq);
636 chain.transferRESNUMFeatures(seq, "IEA: SIFTS",
637 pdb.getId().toLowerCase(Locale.ROOT));// FIXME: is this
639 chain.transferResidueAnnotation(siftsMapping, null);
640 } catch (SiftsException e)
642 jalview.bin.Console.errPrintln(e.getMessage());
643 } catch (Exception e)
645 jalview.bin.Console.errPrintln(
646 "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
647 jalview.bin.Console.errPrintln(e.getMessage());
650 // If sifts was successful, add mappings and return
651 if (!foundSiftsMappings.isEmpty())
653 ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
657 // If sifts was successful, add mappings and return
658 if (!foundSiftsMappings.isEmpty())
660 seqToStrucMapping.addAll(foundSiftsMappings);
663 if (foundSiftsMappings.isEmpty())
665 // Not doing SIFTS, or SIFTS failed for some reason.
667 // first check if we should use an identity mapping
668 if (idLengthChain != null && maxAlignseq.getS2Coverage() < 0.75)
671 "Assuming 3Dsi identity mapping between structure and sequence");
672 StructureMapping matchMapping = getIdMappings(seq, pdbFile,
673 idLengthChain.id, idLengthChain, pdb);
674 seqToStrucMapping.add(matchMapping);
675 ds.addPDBId(idLengthChain.sequence.getAllPDBEntries().get(0));
676 Console.info("Mapping added.");
680 if (maxAlignseq.getS1Coverage()<0.15 && maxAlignseq.getS2Coverage()<0.15)
682 // skip this - the NW alignment is spurious
685 // Construct a needleman wunsch mapping instead.
686 if (progress != null)
688 progress.setProgressBar(
689 MessageManager.getString(
690 "status.obtaining_mapping_with_nw_alignment"),
693 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
694 maxChainId, maxChain, pdb, maxAlignseq);
695 seqToStrucMapping.add(nwMapping);
696 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
699 if (forStructureView)
701 for (StructureMapping sm : seqToStrucMapping)
703 addStructureMapping(sm); // not addAll!
706 if (progress != null)
708 progress.setProgressBar(null, progressSessionId);
715 * check if we need to extract secondary structure from given pdbFile and
716 * transfer to sequences
719 * @param sequenceArray
722 private boolean isStructureFileProcessed(String pdbFile,
723 SequenceI[] sequenceArray)
725 boolean processed = false;
726 if (isPDBFileRegistered(pdbFile))
728 for (SequenceI sq : sequenceArray)
731 while (ds.getDatasetSequence() != null)
733 ds = ds.getDatasetSequence();
736 if (ds.getAnnotation() != null)
738 for (AlignmentAnnotation ala : ds.getAnnotation())
740 // false if any annotation present from this structure
741 // JBPNote this fails for jmol/chimera view because the *file* is
742 // passed, not the structure data ID -
743 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
754 public void addStructureMapping(StructureMapping sm)
756 if (!mappings.contains(sm))
763 * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
768 * @param targetChainId
774 * client for retrieval of SIFTS mappings for this structure
776 * @throws SiftsException
778 private StructureMapping getStructureMapping(SequenceI seq,
779 String pdbFile, String targetChainId, StructureFile pdb,
780 PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
781 AlignSeq maxAlignseq, SiftsClient siftsClient)
782 throws SiftsException
784 StructureMapping curChainMapping = siftsClient
785 .getSiftsStructureMapping(seq, pdbFile, targetChainId);
788 PDBChain chain = pdb.findChain(targetChainId);
791 chain.transferResidueAnnotation(curChainMapping, null);
793 } catch (Exception e)
797 return curChainMapping;
801 * construct a mapping based on a pairwise alignment of the sequence and chain
811 private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
812 String maxChainId, PDBChain maxChain, StructureFile pdb,
813 AlignSeq maxAlignseq)
815 final StringBuilder mappingDetails = new StringBuilder(128);
816 mappingDetails.append(NEWLINE)
817 .append("Sequence \u27f7 Structure mapping details");
818 mappingDetails.append(NEWLINE);
820 .append("Method: inferred with Needleman & Wunsch alignment");
821 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
822 .append(NEWLINE).append("Sequence = ")
823 .append(maxChain.sequence.getSequenceAsString());
824 mappingDetails.append(NEWLINE).append("No of residues = ")
825 .append(maxChain.residues.size()).append(NEWLINE)
827 PrintStream ps = new PrintStream(System.out)
830 public void print(String x)
832 mappingDetails.append(x);
836 public void println()
838 mappingDetails.append(NEWLINE);
842 maxAlignseq.printAlignment(ps);
844 mappingDetails.append(NEWLINE).append("PDB start/end ");
845 mappingDetails.append(String.valueOf(maxAlignseq.seq2start))
847 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
848 mappingDetails.append(NEWLINE).append("SEQ start/end ");
851 .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1)))
853 mappingDetails.append(
854 String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1)));
855 mappingDetails.append(NEWLINE);
856 maxChain.makeExactMapping(maxAlignseq, seq);
857 jalview.datamodel.Mapping sqmpping = maxAlignseq
858 .getMappingFromS1(false);
859 maxChain.transferRESNUMFeatures(seq, null,
860 pdb.getId().toLowerCase(Locale.ROOT));
862 HashMap<Integer, int[]> mapping = new HashMap<>();
869 Atom tmp = maxChain.atoms.elementAt(index);
870 if ((resNum != tmp.resNumber || insCode != tmp.insCode)
871 && tmp.alignmentMapping != -1)
873 resNum = tmp.resNumber;
874 insCode = tmp.insCode;
875 if (tmp.alignmentMapping >= -1)
877 mapping.put(tmp.alignmentMapping + 1,
879 { tmp.resNumber, tmp.atomIndex });
884 } while (index < maxChain.atoms.size());
886 StructureMapping nwMapping = new StructureMapping(seq, pdbFile,
887 pdb.getId(), maxChainId, mapping, mappingDetails.toString());
888 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
893 * construct a 1:1 mapping using given residue and sequence numbering
896 * @param identityChainId
897 * @param identityChain
902 private StructureMapping getIdMappings(SequenceI seq, String pdbFile,
903 String identityChainId, PDBChain identityChain, StructureFile pdb)
905 final StringBuilder mappingDetails = new StringBuilder(128);
906 mappingDetails.append(NEWLINE)
907 .append("Sequence \u27f7 Structure mapping details");
908 mappingDetails.append(NEWLINE);
909 mappingDetails.append("Method: Matching length 1:1");
910 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
911 .append(NEWLINE).append("Sequence = ")
912 .append(identityChain.sequence.getSequenceAsString());
913 mappingDetails.append(NEWLINE).append("No of residues = ")
914 .append(identityChain.residues.size()).append(NEWLINE)
917 mappingDetails.append(NEWLINE)
918 .append("Aligned Sequence is: " + seq.getDisplayId(true));
919 mappingDetails.append(NEWLINE)
920 .append("Sequence = " + seq.getSequenceAsString());
922 int from = Math.max(seq.getStart(),identityChain.sequence.getStart());
923 int to = Math.min(seq.getEnd(), identityChain.sequence.getEnd());
924 jalview.datamodel.Mapping sqmpping = new jalview.datamodel.Mapping(seq,
925 new MapList(new int[]
930 identityChain.mapChainWith(sqmpping, seq);
932 identityChain.transferRESNUMFeatures(seq, null,
933 pdb.getId().toLowerCase(Locale.ROOT));
936 // TODO REFACTOR TO PDBChain as a builder
937 HashMap<Integer, int[]> mapping = new HashMap<>();
944 Atom tmp = identityChain.atoms.elementAt(index);
945 if ((resNum != tmp.resNumber || insCode != tmp.insCode)
946 && tmp.alignmentMapping != -1)
948 resNum = tmp.resNumber;
949 insCode = tmp.insCode;
950 if (tmp.alignmentMapping >= -1)
952 mapping.put(tmp.alignmentMapping + 1,
954 { tmp.resNumber, tmp.atomIndex });
959 } while (index < identityChain.atoms.size());
961 StructureMapping idMapping = new StructureMapping(seq, pdbFile,
962 pdb.getId(), identityChainId, mapping,
963 mappingDetails.toString());
964 identityChain.transferResidueAnnotation(idMapping, sqmpping);
968 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
970 listeners.removeElement(svl);
971 if (svl instanceof SequenceListener)
973 for (int i = 0; i < listeners.size(); i++)
975 if (listeners.elementAt(i) instanceof StructureListener)
977 ((StructureListener) listeners.elementAt(i))
978 .releaseReferences(svl);
983 if (pdbfiles == null)
989 * Remove mappings to the closed listener's PDB files, but first check if
990 * another listener is still interested
992 List<String> pdbs = new ArrayList<>(Arrays.asList(pdbfiles));
994 StructureListener sl;
995 for (int i = 0; i < listeners.size(); i++)
997 if (listeners.elementAt(i) instanceof StructureListener)
999 sl = (StructureListener) listeners.elementAt(i);
1000 for (String pdbfile : sl.getStructureFiles())
1002 pdbs.remove(pdbfile);
1008 * Rebuild the mappings set, retaining only those which are for 'other' PDB
1011 if (pdbs.size() > 0)
1013 List<StructureMapping> tmp = new ArrayList<>();
1014 for (StructureMapping sm : mappings)
1016 if (!pdbs.contains(sm.pdbfile))
1027 * hack to highlight a range of positions at once on any structure views
1029 * @param sequenceRef
1031 * - series of int start-end ranges as positions on sequenceRef
1035 public void highlightPositionsOn(SequenceI sequenceRef, int[][] is,
1038 boolean hasSequenceListeners = handlingVamsasMo
1039 || !seqmappings.isEmpty();
1040 SearchResultsI results = null;
1041 ArrayList<Integer> listOfPositions = new ArrayList<Integer>();
1042 for (int[] s_e : is)
1044 for (int p = s_e[0]; p <= s_e[1]; listOfPositions.add(p++))
1047 int seqpos[] = new int[listOfPositions.size()];
1049 for (Integer p : listOfPositions)
1054 for (i = 0; i < listeners.size(); i++)
1056 Object listener = listeners.elementAt(i);
1057 if (listener == source)
1059 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1060 // Temporary fudge with SequenceListener.getVamsasSource()
1063 if (listener instanceof StructureListener)
1065 highlightStructure((StructureListener) listener, sequenceRef,
1073 * Propagate mouseover of a single position in a structure
1080 public String mouseOverStructure(int pdbResNum, String chain,
1083 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
1084 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
1085 return mouseOverStructure(atoms);
1089 * Propagate mouseover or selection of multiple positions in a structure
1093 public String mouseOverStructure(List<AtomSpec> atoms)
1095 if (listeners == null)
1097 // old or prematurely sent event
1100 boolean hasSequenceListener = false;
1101 for (int i = 0; i < listeners.size(); i++)
1103 if (listeners.elementAt(i) instanceof SequenceListener)
1105 hasSequenceListener = true;
1108 if (!hasSequenceListener)
1113 SearchResultsI results = findAlignmentPositionsForStructurePositions(
1115 String result = null;
1116 for (Object li : listeners)
1118 if (li instanceof SequenceListener)
1120 String s = ((SequenceListener) li).highlightSequence(results);
1131 * Constructs a SearchResults object holding regions (if any) in the Jalview
1132 * alignment which have a mapping to the structure viewer positions in the
1138 public SearchResultsI findAlignmentPositionsForStructurePositions(
1139 List<AtomSpec> atoms)
1141 SearchResultsI results = new SearchResults();
1142 for (AtomSpec atom : atoms)
1144 SequenceI lastseq = null;
1146 for (StructureMapping sm : mappings)
1148 if (sm.pdbfile.equals(atom.getPdbFile())
1149 && sm.pdbchain.equals(atom.getChain()))
1151 int indexpos = sm.getSeqPos(atom.getPdbResNum());
1152 if (lastipos != indexpos || lastseq != sm.sequence)
1154 results.appendResult(sm.sequence, indexpos, indexpos);
1155 lastipos = indexpos;
1156 lastseq = sm.sequence;
1157 // construct highlighted sequence list
1158 for (AlignedCodonFrame acf : seqmappings)
1160 acf.markMappedRegion(sm.sequence, indexpos, results);
1170 * highlight regions associated with a position (indexpos) in seq
1173 * the sequence that the mouse over occurred on
1175 * the absolute position being mouseovered in seq (0 to seq.length())
1177 * the sequence position (if -1, seq.findPosition is called to
1178 * resolve the residue number)
1180 public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
1181 VamsasSource source)
1183 boolean hasSequenceListeners = handlingVamsasMo
1184 || !seqmappings.isEmpty();
1185 SearchResultsI results = null;
1188 seqPos = seq.findPosition(indexpos);
1190 for (int i = 0; i < listeners.size(); i++)
1192 Object listener = listeners.elementAt(i);
1193 if (listener == source)
1195 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1196 // Temporary fudge with SequenceListener.getVamsasSource()
1199 if (listener instanceof StructureListener)
1201 highlightStructure((StructureListener) listener, seq, seqPos);
1205 if (listener instanceof SequenceListener)
1207 final SequenceListener seqListener = (SequenceListener) listener;
1208 if (hasSequenceListeners
1209 && seqListener.getVamsasSource() != source)
1211 if (relaySeqMappings)
1213 if (results == null)
1215 results = MappingUtils.buildSearchResults(seq, seqPos,
1218 if (handlingVamsasMo)
1220 results.addResult(seq, seqPos, seqPos);
1223 if (!results.isEmpty())
1225 seqListener.highlightSequence(results);
1230 else if (listener instanceof VamsasListener && !handlingVamsasMo)
1232 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
1235 else if (listener instanceof SecondaryStructureListener)
1237 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
1245 * Send suitable messages to a StructureListener to highlight atoms
1246 * corresponding to the given sequence position(s)
1252 public void highlightStructure(StructureListener sl, SequenceI seq,
1255 if (!sl.isListeningFor(seq))
1260 List<AtomSpec> atoms = new ArrayList<>();
1261 for (StructureMapping sm : mappings)
1263 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1264 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1265 .getDatasetSequence() == seq.getDatasetSequence()))
1267 for (int index : positions)
1269 atomNo = sm.getAtomNum(index);
1273 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1274 sm.getPDBResNum(index), atomNo));
1279 sl.highlightAtoms(atoms);
1282 public void highlightStructureRegionsFor(StructureListener sl,
1283 SequenceI[] seqs, int... columns)
1285 List<SequenceI> to_highlight = new ArrayList<SequenceI>();
1286 for (SequenceI seq : seqs)
1288 if (sl.isListeningFor(seq))
1290 to_highlight.add(seq);
1293 if (to_highlight.size() == 0)
1297 List<AtomSpec> atoms = new ArrayList<>();
1298 for (SequenceI seq : to_highlight)
1301 for (StructureMapping sm : mappings)
1303 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1304 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1305 .getDatasetSequence() == seq.getDatasetSequence()))
1308 for (int i = 0; i < columns.length; i += 2)
1310 ContiguousI positions = seq.findPositions(columns[i] + 1,
1311 columns[i + 1] + 1);
1312 if (positions == null)
1316 for (int index = positions.getBegin(); index <= positions
1320 atomNo = sm.getAtomNum(index);
1324 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1325 sm.getPDBResNum(index), atomNo));
1331 if (atoms.size() > 0)
1333 sl.highlightAtoms(atoms);
1339 * true if a mouse over event from an external (ie Vamsas) source is being
1342 boolean handlingVamsasMo = false;
1347 * as mouseOverSequence but only route event to SequenceListeners
1351 * in an alignment sequence
1353 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
1354 VamsasSource source)
1356 handlingVamsasMo = true;
1357 long msg = sequenceI.hashCode() * (1 + position);
1361 mouseOverSequence(sequenceI, position, -1, source);
1363 handlingVamsasMo = false;
1366 public Annotation[] colourSequenceFromStructure(SequenceI seq,
1370 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
1371 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
1373 * Annotation [] annotations = new Annotation[seq.getLength()];
1375 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
1376 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
1377 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
1379 * for (int j = 0; j < mappings.length; j++) {
1381 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
1382 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
1383 * jalview.bin.Console.outPrintln(pdbid+" "+mappings[j].getPdbId() +"
1384 * "+mappings[j].pdbfile);
1386 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
1387 * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
1389 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
1390 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
1391 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
1392 * mappings[j].pdbfile); }
1394 * annotations[index] = new Annotation("X",null,' ',0,col); } return
1395 * annotations; } } } }
1397 * return annotations;
1401 public void structureSelectionChanged()
1405 public void sequenceSelectionChanged()
1409 public void sequenceColoursChanged(Object source)
1411 StructureListener sl;
1412 for (int i = 0; i < listeners.size(); i++)
1414 if (listeners.elementAt(i) instanceof StructureListener)
1416 sl = (StructureListener) listeners.elementAt(i);
1417 sl.updateColours(source);
1422 public StructureMapping[] getMapping(String pdbfile)
1424 List<StructureMapping> tmp = new ArrayList<>();
1425 for (StructureMapping sm : mappings)
1427 if (sm.pdbfile.equals(pdbfile))
1432 return tmp.toArray(new StructureMapping[tmp.size()]);
1436 * Returns a readable description of all mappings for the given pdbfile to any
1437 * of the given sequences
1443 public String printMappings(String pdbfile, List<SequenceI> seqs)
1445 if (pdbfile == null || seqs == null || seqs.isEmpty())
1450 StringBuilder sb = new StringBuilder(64);
1451 for (StructureMapping sm : mappings)
1453 if (Platform.pathEquals(sm.pdbfile, pdbfile)
1454 && seqs.contains(sm.sequence))
1456 sb.append(sm.mappingDetails);
1458 // separator makes it easier to read multiple mappings
1459 sb.append("=====================");
1465 return sb.toString();
1469 * Remove the given mapping
1473 public void deregisterMapping(AlignedCodonFrame acf)
1477 boolean removed = seqmappings.remove(acf);
1478 if (removed && seqmappings.isEmpty())
1480 jalview.bin.Console.outPrintln("All mappings removed");
1486 * Add each of the given codonFrames to the stored set, if not aready present.
1490 public void registerMappings(List<AlignedCodonFrame> mappings)
1492 if (mappings != null)
1494 for (AlignedCodonFrame acf : mappings)
1496 registerMapping(acf);
1502 * Add the given mapping to the stored set, unless already stored.
1504 public void registerMapping(AlignedCodonFrame acf)
1508 if (!seqmappings.contains(acf))
1510 seqmappings.add(acf);
1516 * Resets this object to its initial state by removing all registered
1517 * listeners, codon mappings, PDB file mappings
1519 public void resetAll()
1521 if (mappings != null)
1525 if (seqmappings != null)
1527 seqmappings.clear();
1529 if (sel_listeners != null)
1531 sel_listeners.clear();
1533 if (listeners != null)
1537 if (commandListeners != null)
1539 commandListeners.clear();
1541 if (view_listeners != null)
1543 view_listeners.clear();
1545 if (pdbFileNameId != null)
1547 pdbFileNameId.clear();
1549 if (pdbIdFileName != null)
1551 pdbIdFileName.clear();
1555 public void addSelectionListener(SelectionListener selecter)
1557 if (!sel_listeners.contains(selecter))
1559 sel_listeners.add(selecter);
1563 public void removeSelectionListener(SelectionListener toremove)
1565 if (sel_listeners.contains(toremove))
1567 sel_listeners.remove(toremove);
1571 public synchronized void sendSelection(
1572 jalview.datamodel.SequenceGroup selection,
1573 jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden,
1574 SelectionSource source)
1576 for (SelectionListener slis : sel_listeners)
1580 slis.selection(selection, colsel, hidden, source);
1585 Vector<AlignmentViewPanelListener> view_listeners = new Vector<>();
1587 public synchronized void sendViewPosition(
1588 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1589 int startSeq, int endSeq)
1592 if (view_listeners != null && view_listeners.size() > 0)
1594 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1596 while (listeners.hasMoreElements())
1598 AlignmentViewPanelListener slis = listeners.nextElement();
1601 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1609 * release all references associated with this manager provider
1611 * @param jalviewLite
1613 public static void release(StructureSelectionManagerProvider jalviewLite)
1615 // synchronized (instances)
1617 if (instances == null)
1621 StructureSelectionManager mnger = (instances.get(jalviewLite));
1624 instances.remove(jalviewLite);
1627 /* bsoares 2019-03-20 finalize deprecated, no apparent external
1628 * resources to close
1630 // mnger.finalize();
1631 } catch (Throwable x)
1638 public void registerPDBEntry(PDBEntry pdbentry)
1640 if (pdbentry.getFile() != null
1641 && pdbentry.getFile().trim().length() > 0)
1643 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1647 public void addCommandListener(CommandListener cl)
1649 if (!commandListeners.contains(cl))
1651 commandListeners.add(cl);
1655 public boolean hasCommandListener(CommandListener cl)
1657 return this.commandListeners.contains(cl);
1660 public boolean removeCommandListener(CommandListener l)
1662 return commandListeners.remove(l);
1666 * Forward a command to any command listeners (except for the command's
1670 * the command to be broadcast (in its form after being performed)
1672 * if true, the command was being 'undone'
1675 public void commandPerformed(CommandI command, boolean undo,
1676 VamsasSource source)
1678 for (CommandListener listener : commandListeners)
1680 listener.mirrorCommand(command, undo, this, source);
1685 * Returns a new CommandI representing the given command as mapped to the
1686 * given sequences. If no mapping could be made, or the command is not of a
1687 * mappable kind, returns null.
1695 public CommandI mapCommand(CommandI command, boolean undo,
1696 final AlignmentI mapTo, char gapChar)
1698 if (command instanceof EditCommand)
1700 return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo,
1701 gapChar, seqmappings);
1703 else if (command instanceof OrderCommand)
1705 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1706 mapTo, seqmappings);
1711 public List<AlignedCodonFrame> getSequenceMappings()
1717 * quick and dirty route to just highlight all structure positions for a range
1720 * @param sequencesArray
1722 * start-end columns on sequencesArray
1724 * origin parent AlignmentPanel
1726 public void highlightPositionsOnMany(SequenceI[] sequencesArray, int[] is,
1729 for (int i = 0; i < listeners.size(); i++)
1731 Object listener = listeners.elementAt(i);
1732 if (listener == source)
1734 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1735 // Temporary fudge with SequenceListener.getVamsasSource()
1738 if (listener instanceof StructureListener)
1740 highlightStructureRegionsFor((StructureListener) listener,
1741 sequencesArray, is);
1746 public Map<String, String> getPdbFileNameIdMap()
1748 return pdbFileNameId;
1751 public Map<String, String> getPdbIdFileNameMap()
1753 return pdbIdFileName;