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;
24 import jalview.analysis.AlignSeq;
25 import jalview.api.StructureSelectionManagerProvider;
26 import jalview.bin.ApplicationSingletonProvider;
27 import jalview.bin.ApplicationSingletonProvider.ApplicationSingletonI;
28 import jalview.bin.Console;
29 import jalview.commands.CommandI;
30 import jalview.commands.EditCommand;
31 import jalview.commands.OrderCommand;
32 import jalview.datamodel.AlignedCodonFrame;
33 import jalview.datamodel.AlignmentAnnotation;
34 import jalview.datamodel.AlignmentI;
35 import jalview.datamodel.Annotation;
36 import jalview.datamodel.HiddenColumns;
37 import jalview.datamodel.PDBEntry;
38 import jalview.datamodel.SearchResults;
39 import jalview.datamodel.SearchResultsI;
40 import jalview.datamodel.SequenceI;
41 import jalview.ext.jmol.JmolParser;
42 import jalview.gui.IProgressIndicator;
43 import jalview.io.AppletFormatAdapter;
44 import jalview.io.DataSourceType;
45 import jalview.io.StructureFile;
46 import jalview.util.MappingUtils;
47 import jalview.util.MessageManager;
48 import jalview.util.Platform;
49 import jalview.ws.sifts.SiftsClient;
50 import jalview.ws.sifts.SiftsException;
51 import jalview.ws.sifts.SiftsSettings;
53 import java.io.PrintStream;
54 import java.util.ArrayList;
55 import java.util.Arrays;
56 import java.util.Collections;
57 import java.util.Enumeration;
58 import java.util.HashMap;
59 import java.util.IdentityHashMap;
60 import java.util.List;
61 import java.util.Locale;
63 import java.util.Vector;
66 import mc_view.PDBChain;
67 import mc_view.PDBfile;
69 public class StructureSelectionManager implements ApplicationSingletonI
71 public final static String NEWLINE = System.lineSeparator();
74 private List<StructureMapping> mappings = new ArrayList<>();
76 private boolean processSecondaryStructure = false;
78 private boolean secStructServices = false;
80 private boolean addTempFacAnnot = false;
83 * Set of any registered mappings between (dataset) sequences.
85 private List<AlignedCodonFrame> seqmappings = new ArrayList<>();
87 private List<CommandListener> commandListeners = new ArrayList<>();
89 private List<SelectionListener> sel_listeners = new ArrayList<>();
91 * instances of this class scoped by some context class
93 private IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> selectionManagers;
96 * Answers an instance of this class for the current application (Java or JS
101 private static StructureSelectionManager getInstance()
103 return (StructureSelectionManager) ApplicationSingletonProvider
104 .getInstance(StructureSelectionManager.class);
108 * Private constructor as all 'singleton' instances are managed here or by
109 * ApplicationSingletonProvider
111 private StructureSelectionManager()
113 selectionManagers = new IdentityHashMap<>();
117 * Answers an instance of this class for the current application (Java or JS
118 * 'applet') scope, and scoped to the specified context
123 public static StructureSelectionManager getStructureSelectionManager(
124 StructureSelectionManagerProvider context)
126 return getInstance().getInstanceForContext(context);
130 * Answers an instance of this class scoped to the given context. The instance
131 * is created on the first request for the context, thereafter the same
132 * instance is returned. Note that the context may be null (this is the case
133 * when running headless without a Desktop).
138 StructureSelectionManager getInstanceForContext(
139 StructureSelectionManagerProvider context)
141 StructureSelectionManager instance = selectionManagers.get(context);
142 if (instance == null)
144 instance = new StructureSelectionManager();
145 selectionManagers.put(context, instance);
149 /** Null provider in 2.11.2
152 private static StructureSelectionManager nullProvider = null;
154 public static StructureSelectionManager getStructureSelectionManager(
155 StructureSelectionManagerProvider context)
159 if (nullProvider == null)
161 if (instances != null)
163 throw new Error(MessageManager.getString(
164 "error.implementation_error_structure_selection_manager_null"),
165 new NullPointerException(MessageManager
166 .getString("exception.ssm_context_is_null")));
170 nullProvider = new StructureSelectionManager();
175 if (instances == null)
177 instances = new java.util.IdentityHashMap<>();
179 StructureSelectionManager instance = instances.get(context);
180 if (instance == null)
182 if (nullProvider != null)
184 instance = nullProvider;
188 instance = new StructureSelectionManager();
190 instances.put(context, instance);
197 * @return true if will try to use external services for processing secondary
200 public boolean isSecStructServices()
202 return secStructServices;
206 * control use of external services for processing secondary structure
208 * @param secStructServices
210 public void setSecStructServices(boolean secStructServices)
212 this.secStructServices = secStructServices;
216 * flag controlling addition of any kind of structural annotation
218 * @return true if temperature factor annotation will be added
220 public boolean isAddTempFacAnnot()
222 return addTempFacAnnot;
226 * set flag controlling addition of structural annotation
228 * @param addTempFacAnnot
230 public void setAddTempFacAnnot(boolean addTempFacAnnot)
232 this.addTempFacAnnot = addTempFacAnnot;
237 * @return if true, the structure manager will attempt to add secondary
238 * structure lines for unannotated sequences
241 public boolean isProcessSecondaryStructure()
243 return processSecondaryStructure;
247 * Control whether structure manager will try to annotate mapped sequences
248 * with secondary structure from PDB data.
252 public void setProcessSecondaryStructure(boolean enable)
254 processSecondaryStructure = enable;
258 * debug function - write all mappings to stdout
260 public void reportMapping()
262 if (mappings.isEmpty())
264 System.err.println("reportMapping: No PDB/Sequence mappings.");
269 "reportMapping: There are " + mappings.size() + " mappings.");
271 for (StructureMapping sm : mappings)
273 System.err.println("mapping " + i++ + " : " + sm.pdbfile);
279 * map between the PDB IDs (or structure identifiers) used by Jalview and the
280 * absolute filenames for PDB data that corresponds to it
282 Map<String, String> pdbIdFileName = new HashMap<>();
284 Map<String, String> pdbFileNameId = new HashMap<>();
286 public void registerPDBFile(String idForFile, String absoluteFile)
288 pdbIdFileName.put(idForFile, absoluteFile);
289 pdbFileNameId.put(absoluteFile, idForFile);
292 public String findIdForPDBFile(String idOrFile)
294 String id = pdbFileNameId.get(idOrFile);
298 public String findFileForPDBId(String idOrFile)
300 String id = pdbIdFileName.get(idOrFile);
304 public boolean isPDBFileRegistered(String idOrFile)
306 return pdbFileNameId.containsKey(idOrFile)
307 || pdbIdFileName.containsKey(idOrFile);
311 * flag controlling whether SeqMappings are relayed from received sequence
312 * mouse over events to other sequences
314 boolean relaySeqMappings = true;
317 * Enable or disable relay of seqMapping events to other sequences. You might
318 * want to do this if there are many sequence mappings and the host computer
323 public void setRelaySeqMappings(boolean relay)
325 relaySeqMappings = relay;
329 * get the state of the relay seqMappings flag.
331 * @return true if sequence mouse overs are being relayed to other mapped
334 public boolean isRelaySeqMappingsEnabled()
336 return relaySeqMappings;
339 Vector<Object> listeners = new Vector<>();
342 * register a listener for alignment sequence mouseover events
346 public void addStructureViewerListener(Object svl)
348 if (!listeners.contains(svl))
350 listeners.addElement(svl);
355 * Returns the filename the PDB id is already mapped to if known, or null if
361 public String alreadyMappedToFile(String pdbid)
363 for (StructureMapping sm : mappings)
365 if (sm.getPdbId().equalsIgnoreCase(pdbid))
374 * Import structure data and register a structure mapping for broadcasting
375 * colouring, mouseovers and selection events (convenience wrapper).
377 * This is the standard entry point.
380 * - one or more sequences to be mapped to pdbFile
381 * @param targetChains
382 * - optional chain specification for mapping each sequence to pdb
383 * (may be nill, individual elements may be nill)
385 * - structure data resource
387 * - how to resolve data from resource
388 * @return null or the structure data parsed as a pdb file
390 synchronized public StructureFile setMapping(SequenceI[] sequence,
391 String[] targetChains, String pdbFile, DataSourceType protocol,
392 IProgressIndicator progress)
394 return computeMapping(true, sequence, targetChains, pdbFile, protocol,
399 * Import a single structure file and register sequence structure mappings for
400 * broadcasting colouring, mouseovers and selection events (convenience
405 * @param forStructureView
406 * when true (testng only), record the mapping for use in mouseOvers
409 * - one or more sequences to be mapped to pdbFile
410 * @param targetChains
411 * - optional chain specification for mapping each sequence to pdb
412 * (may be nill, individual elements may be nill)
414 * - structure data resource
416 * - how to resolve data from resource
417 * @return null or the structure data parsed as a pdb file
419 synchronized public StructureFile setMapping(boolean forStructureView,
420 SequenceI[] sequenceArray, String[] targetChainIds,
421 String pdbFile, DataSourceType sourceType)
423 return computeMapping(forStructureView, sequenceArray, targetChainIds,
424 pdbFile, sourceType, null);
428 * create sequence structure mappings between each sequence and the given
429 * pdbFile (retrieved via the given protocol). Either constructs a mapping
430 * using NW alignment or derives one from any available SIFTS mapping data.
432 * @param forStructureView
433 * when true, record the mapping for use in mouseOvers
435 * @param sequenceArray
436 * - one or more sequences to be mapped to pdbFile
437 * @param targetChainIds
438 * - optional chain specification for mapping each sequence to pdb
439 * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
440 * - this should be List<List<String>>, empty lists indicate no
441 * predefined mappings
443 * - structure data resource
445 * - how to resolve data from resource
446 * @param IProgressIndicator
447 * reference to UI component that maintains a progress bar for the
449 * @return null or the structure data parsed as a pdb file
451 synchronized private StructureFile computeMapping(
452 boolean forStructureView, SequenceI[] sequenceArray,
453 String[] targetChainIds, String pdbFile, DataSourceType sourceType,
454 IProgressIndicator progress)
456 long progressSessionId = System.currentTimeMillis() * 3;
459 * do we extract and transfer annotation from 3D data ?
461 // FIXME: possibly should just delete
463 boolean parseSecStr = processSecondaryStructure
464 ? isStructureFileProcessed(pdbFile, sequenceArray)
467 StructureFile pdb = null;
468 boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
471 // FIXME if sourceType is not null, we've lost data here
472 sourceType = AppletFormatAdapter.checkProtocol(pdbFile);
473 pdb = new JmolParser(false, pdbFile, sourceType);
474 pdb.addSettings(parseSecStr && processSecondaryStructure,
475 parseSecStr && addTempFacAnnot,
476 parseSecStr && secStructServices);
478 if (pdb.getId() != null && pdb.getId().trim().length() > 0
479 && DataSourceType.FILE == sourceType)
481 registerPDBFile(pdb.getId().trim(), pdbFile);
483 // if PDBId is unavailable then skip SIFTS mapping execution path
484 // TODO: JAL-3868 need to know if structure is actually from
485 // PDB (has valid PDB ID and has provenance suggesting it
486 // actually came from PDB)
487 boolean isProtein = false;
488 for (SequenceI s:sequenceArray) {
494 isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable() && !pdb.getId().startsWith("AF-") && isProtein;
496 } catch (Exception ex)
498 ex.printStackTrace();
502 * sifts client - non null if SIFTS mappings are to be used
504 SiftsClient siftsClient = null;
509 siftsClient = new SiftsClient(pdb);
511 } catch (SiftsException e)
513 isMapUsingSIFTs = false;
514 Console.error("SIFTS mapping failed", e);
515 Console.error("Falling back on Needleman & Wunsch alignment");
519 String targetChainId;
520 for (int s = 0; s < sequenceArray.length; s++)
522 boolean infChain = true;
523 final SequenceI seq = sequenceArray[s];
525 while (ds.getDatasetSequence() != null)
527 ds = ds.getDatasetSequence();
530 if (targetChainIds != null && targetChainIds[s] != null)
533 targetChainId = targetChainIds[s];
535 else if (seq.getName().indexOf("|") > -1)
537 targetChainId = seq.getName()
538 .substring(seq.getName().lastIndexOf("|") + 1);
539 if (targetChainId.length() > 1)
541 if (targetChainId.trim().length() == 0)
547 // not a valid chain identifier
558 * Attempt pairwise alignment of the sequence with each chain in the PDB,
559 * and remember the highest scoring chain
562 AlignSeq maxAlignseq = null;
563 String maxChainId = " ";
564 PDBChain maxChain = null;
565 boolean first = true;
566 for (PDBChain chain : pdb.getChains())
568 if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
571 continue; // don't try to map chains don't match.
573 // TODO: correctly determine sequence type for mixed na/peptide
575 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
576 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
579 // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
580 // as.calcScoreMatrix();
581 // as.traceAlignment();
583 if (first || as.maxscore > max
584 || (as.maxscore == max && chain.id.equals(targetChainId)))
590 maxChainId = chain.id;
593 if (maxChain == null)
598 if (sourceType == DataSourceType.PASTE)
600 pdbFile = "INLINE" + pdb.getId();
603 List<StructureMapping> seqToStrucMapping = new ArrayList<>();
604 if (isMapUsingSIFTs && seq.isProtein())
606 if (progress!=null) {
607 progress.setProgressBar(MessageManager
608 .getString("status.obtaining_mapping_with_sifts"),
611 jalview.datamodel.Mapping sqmpping = maxAlignseq
612 .getMappingFromS1(false);
613 if (targetChainId != null && !targetChainId.trim().isEmpty())
615 StructureMapping siftsMapping;
618 siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
619 pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
620 seqToStrucMapping.add(siftsMapping);
621 maxChain.makeExactMapping(siftsMapping, seq);
622 maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS",
623 pdb.getId().toLowerCase(Locale.ROOT));
624 maxChain.transferResidueAnnotation(siftsMapping, null);
625 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
627 } catch (SiftsException e)
629 // fall back to NW alignment
630 Console.error(e.getMessage());
631 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
632 targetChainId, maxChain, pdb, maxAlignseq);
633 seqToStrucMapping.add(nwMapping);
634 maxChain.makeExactMapping(maxAlignseq, seq);
635 maxChain.transferRESNUMFeatures(seq, "IEA:Jalview",
636 pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is
639 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
640 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
645 List<StructureMapping> foundSiftsMappings = new ArrayList<>();
646 for (PDBChain chain : pdb.getChains())
648 StructureMapping siftsMapping = null;
651 siftsMapping = getStructureMapping(seq,
652 pdbFile, chain.id, pdb, chain, sqmpping, maxAlignseq,
654 foundSiftsMappings.add(siftsMapping);
655 chain.makeExactMapping(siftsMapping, seq);
656 chain.transferRESNUMFeatures(seq, "IEA: SIFTS",
657 pdb.getId().toLowerCase(Locale.ROOT));// FIXME: is this
659 chain.transferResidueAnnotation(siftsMapping, null);
660 } catch (SiftsException e)
662 System.err.println(e.getMessage());
668 "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
669 System.err.println(e.getMessage());
672 if (!foundSiftsMappings.isEmpty())
674 seqToStrucMapping.addAll(foundSiftsMappings);
675 ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
679 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
680 maxChainId, maxChain, pdb, maxAlignseq);
681 seqToStrucMapping.add(nwMapping);
682 maxChain.transferRESNUMFeatures(seq, null,
683 pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is this
685 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
686 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
692 if (progress != null)
694 progress.setProgressBar(MessageManager
695 .getString("status.obtaining_mapping_with_nw_alignment"),
698 StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
699 maxChain, pdb, maxAlignseq);
700 seqToStrucMapping.add(nwMapping);
701 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
703 if (forStructureView)
705 for (StructureMapping sm : seqToStrucMapping)
707 addStructureMapping(sm); // not addAll!
710 if (progress != null)
712 progress.setProgressBar(null, progressSessionId);
719 * check if we need to extract secondary structure from given pdbFile and
720 * transfer to sequences
723 * @param sequenceArray
726 private boolean isStructureFileProcessed(String pdbFile,
727 SequenceI[] sequenceArray)
729 boolean parseSecStr = true;
730 if (isPDBFileRegistered(pdbFile))
732 for (SequenceI sq : sequenceArray)
735 while (ds.getDatasetSequence() != null)
737 ds = ds.getDatasetSequence();
740 if (ds.getAnnotation() != null)
742 for (AlignmentAnnotation ala : ds.getAnnotation())
744 // false if any annotation present from this structure
745 // JBPNote this fails for jmol/chimera view because the *file* is
746 // passed, not the structure data ID -
747 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
758 public void addStructureMapping(StructureMapping sm)
760 if (!mappings.contains(sm))
767 * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
772 * @param targetChainId
778 * client for retrieval of SIFTS mappings for this structure
780 * @throws SiftsException
782 private StructureMapping getStructureMapping(SequenceI seq,
783 String pdbFile, String targetChainId, StructureFile pdb,
784 PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
785 AlignSeq maxAlignseq, SiftsClient siftsClient) throws SiftsException
787 StructureMapping curChainMapping = siftsClient
788 .getSiftsStructureMapping(seq, pdbFile, targetChainId);
791 PDBChain chain = pdb.findChain(targetChainId);
794 chain.transferResidueAnnotation(curChainMapping, null);
796 } catch (Exception e)
800 return curChainMapping;
803 private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
804 String maxChainId, PDBChain maxChain, StructureFile pdb,
805 AlignSeq maxAlignseq)
807 final StringBuilder mappingDetails = new StringBuilder(128);
808 mappingDetails.append(NEWLINE)
809 .append("Sequence \u27f7 Structure mapping details");
810 mappingDetails.append(NEWLINE);
812 .append("Method: inferred with Needleman & Wunsch alignment");
813 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
814 .append(NEWLINE).append("Sequence = ")
815 .append(maxChain.sequence.getSequenceAsString());
816 mappingDetails.append(NEWLINE).append("No of residues = ")
817 .append(maxChain.residues.size()).append(NEWLINE)
819 PrintStream ps = new PrintStream(System.out)
822 public void print(String x)
824 mappingDetails.append(x);
828 public void println()
830 mappingDetails.append(NEWLINE);
834 maxAlignseq.printAlignment(ps);
836 mappingDetails.append(NEWLINE).append("PDB start/end ");
837 mappingDetails.append(String.valueOf(maxAlignseq.seq2start))
839 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
840 mappingDetails.append(NEWLINE).append("SEQ start/end ");
843 .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1)))
845 mappingDetails.append(
846 String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1)));
847 mappingDetails.append(NEWLINE);
848 maxChain.makeExactMapping(maxAlignseq, seq);
849 jalview.datamodel.Mapping sqmpping = maxAlignseq
850 .getMappingFromS1(false);
851 maxChain.transferRESNUMFeatures(seq, null,
852 pdb.getId().toLowerCase(Locale.ROOT));
854 HashMap<Integer, int[]> mapping = new HashMap<>();
861 Atom tmp = maxChain.atoms.elementAt(index);
862 if ((resNum != tmp.resNumber || insCode != tmp.insCode)
863 && tmp.alignmentMapping != -1)
865 resNum = tmp.resNumber;
866 insCode = tmp.insCode;
867 if (tmp.alignmentMapping >= -1)
869 mapping.put(tmp.alignmentMapping + 1,
871 { tmp.resNumber, tmp.atomIndex });
876 } while (index < maxChain.atoms.size());
878 StructureMapping nwMapping = new StructureMapping(seq, pdbFile,
879 pdb.getId(), maxChainId, mapping, mappingDetails.toString());
880 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
884 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
886 listeners.removeElement(svl);
887 if (svl instanceof SequenceListener)
889 for (int i = 0; i < listeners.size(); i++)
891 if (listeners.elementAt(i) instanceof StructureListener)
893 ((StructureListener) listeners.elementAt(i))
894 .releaseReferences(svl);
899 if (pdbfiles == null)
905 * Remove mappings to the closed listener's PDB files, but first check if
906 * another listener is still interested
908 List<String> pdbs = new ArrayList<>(Arrays.asList(pdbfiles));
910 StructureListener sl;
911 for (int i = 0; i < listeners.size(); i++)
913 if (listeners.elementAt(i) instanceof StructureListener)
915 sl = (StructureListener) listeners.elementAt(i);
916 for (String pdbfile : sl.getStructureFiles())
918 pdbs.remove(pdbfile);
924 * Rebuild the mappings set, retaining only those which are for 'other' PDB
929 List<StructureMapping> tmp = new ArrayList<>();
930 for (StructureMapping sm : mappings)
932 if (!pdbs.contains(sm.pdbfile))
943 * Propagate mouseover of a single position in a structure
950 public String mouseOverStructure(int pdbResNum, String chain,
953 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
954 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
955 return mouseOverStructure(atoms);
959 * Propagate mouseover or selection of multiple positions in a structure
963 public String mouseOverStructure(List<AtomSpec> atoms)
965 if (listeners == null)
967 // old or prematurely sent event
970 boolean hasSequenceListener = false;
971 for (int i = 0; i < listeners.size(); i++)
973 if (listeners.elementAt(i) instanceof SequenceListener)
975 hasSequenceListener = true;
978 if (!hasSequenceListener)
983 SearchResultsI results = findAlignmentPositionsForStructurePositions(
985 String result = null;
986 for (Object li : listeners)
988 if (li instanceof SequenceListener)
990 String s = ((SequenceListener) li).highlightSequence(results);
1001 * Constructs a SearchResults object holding regions (if any) in the Jalview
1002 * alignment which have a mapping to the structure viewer positions in the
1008 public SearchResultsI findAlignmentPositionsForStructurePositions(
1009 List<AtomSpec> atoms)
1011 SearchResultsI results = new SearchResults();
1012 for (AtomSpec atom : atoms)
1014 SequenceI lastseq = null;
1016 for (StructureMapping sm : mappings)
1018 if (sm.pdbfile.equals(atom.getPdbFile())
1019 && sm.pdbchain.equals(atom.getChain()))
1021 int indexpos = sm.getSeqPos(atom.getPdbResNum());
1022 if (lastipos != indexpos || lastseq != sm.sequence)
1024 results.addResult(sm.sequence, indexpos, indexpos);
1025 lastipos = indexpos;
1026 lastseq = sm.sequence;
1027 // construct highlighted sequence list
1028 for (AlignedCodonFrame acf : seqmappings)
1030 acf.markMappedRegion(sm.sequence, indexpos, results);
1040 * highlight regions associated with a position (indexpos) in seq
1043 * the sequence that the mouse over occurred on
1045 * the absolute position being mouseovered in seq (0 to seq.length())
1047 * the sequence position (if -1, seq.findPosition is called to
1048 * resolve the residue number)
1050 public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
1051 VamsasSource source)
1053 boolean hasSequenceListeners = handlingVamsasMo
1054 || !seqmappings.isEmpty();
1055 SearchResultsI results = null;
1058 seqPos = seq.findPosition(indexpos);
1060 for (int i = 0; i < listeners.size(); i++)
1062 Object listener = listeners.elementAt(i);
1063 if (listener == source)
1065 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1066 // Temporary fudge with SequenceListener.getVamsasSource()
1069 if (listener instanceof StructureListener)
1071 highlightStructure((StructureListener) listener, seq, seqPos);
1075 if (listener instanceof SequenceListener)
1077 final SequenceListener seqListener = (SequenceListener) listener;
1078 if (hasSequenceListeners
1079 && seqListener.getVamsasSource() != source)
1081 if (relaySeqMappings)
1083 if (results == null)
1085 results = MappingUtils.buildSearchResults(seq, seqPos,
1088 if (handlingVamsasMo)
1090 results.addResult(seq, seqPos, seqPos);
1093 if (!results.isEmpty())
1095 seqListener.highlightSequence(results);
1100 else if (listener instanceof VamsasListener && !handlingVamsasMo)
1102 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
1105 else if (listener instanceof SecondaryStructureListener)
1107 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
1115 * Send suitable messages to a StructureListener to highlight atoms
1116 * corresponding to the given sequence position(s)
1122 public void highlightStructure(StructureListener sl, SequenceI seq,
1125 if (!sl.isListeningFor(seq))
1130 List<AtomSpec> atoms = new ArrayList<>();
1131 for (StructureMapping sm : mappings)
1133 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1134 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1135 .getDatasetSequence() == seq.getDatasetSequence()))
1137 for (int index : positions)
1139 atomNo = sm.getAtomNum(index);
1143 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1144 sm.getPDBResNum(index), atomNo));
1149 sl.highlightAtoms(atoms);
1153 * true if a mouse over event from an external (ie Vamsas) source is being
1156 boolean handlingVamsasMo = false;
1161 * as mouseOverSequence but only route event to SequenceListeners
1165 * in an alignment sequence
1167 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
1168 VamsasSource source)
1170 handlingVamsasMo = true;
1171 long msg = sequenceI.hashCode() * (1 + position);
1175 mouseOverSequence(sequenceI, position, -1, source);
1177 handlingVamsasMo = false;
1180 public Annotation[] colourSequenceFromStructure(SequenceI seq,
1184 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
1185 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
1187 * Annotation [] annotations = new Annotation[seq.getLength()];
1189 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
1190 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
1191 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
1193 * for (int j = 0; j < mappings.length; j++) {
1195 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
1196 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
1197 * System.out.println(pdbid+" "+mappings[j].getPdbId() +"
1198 * "+mappings[j].pdbfile);
1200 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
1201 * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
1203 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
1204 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
1205 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
1206 * mappings[j].pdbfile); }
1208 * annotations[index] = new Annotation("X",null,' ',0,col); } return
1209 * annotations; } } } }
1211 * return annotations;
1215 public void structureSelectionChanged()
1219 public void sequenceSelectionChanged()
1223 public void sequenceColoursChanged(Object source)
1225 StructureListener sl;
1226 for (int i = 0; i < listeners.size(); i++)
1228 if (listeners.elementAt(i) instanceof StructureListener)
1230 sl = (StructureListener) listeners.elementAt(i);
1231 sl.updateColours(source);
1236 public StructureMapping[] getMapping(String pdbfile)
1238 List<StructureMapping> tmp = new ArrayList<>();
1239 for (StructureMapping sm : mappings)
1241 if (sm.pdbfile.equals(pdbfile))
1246 return tmp.toArray(new StructureMapping[tmp.size()]);
1250 * Returns a readable description of all mappings for the given pdbfile to any
1251 * of the given sequences
1257 public String printMappings(String pdbfile, List<SequenceI> seqs)
1259 if (pdbfile == null || seqs == null || seqs.isEmpty())
1264 StringBuilder sb = new StringBuilder(64);
1265 for (StructureMapping sm : mappings)
1267 if (Platform.pathEquals(sm.pdbfile, pdbfile)
1268 && seqs.contains(sm.sequence))
1270 sb.append(sm.mappingDetails);
1272 // separator makes it easier to read multiple mappings
1273 sb.append("=====================");
1279 return sb.toString();
1283 * Remove the given mapping
1287 public void deregisterMapping(AlignedCodonFrame acf)
1291 boolean removed = seqmappings.remove(acf);
1292 if (removed && seqmappings.isEmpty())
1294 System.out.println("All mappings removed");
1300 * Add each of the given codonFrames to the stored set, if not aready present.
1304 public void registerMappings(List<AlignedCodonFrame> mappings)
1306 if (mappings != null)
1308 for (AlignedCodonFrame acf : mappings)
1310 registerMapping(acf);
1316 * Add the given mapping to the stored set, unless already stored.
1318 public void registerMapping(AlignedCodonFrame acf)
1322 if (!seqmappings.contains(acf))
1324 seqmappings.add(acf);
1330 * Reset this object to its initial state by removing all registered
1331 * listeners, codon mappings, PDB file mappings.
1333 * Called only by Desktop and testng.
1336 public void resetAll()
1338 if (mappings != null)
1342 if (seqmappings != null)
1344 seqmappings.clear();
1346 if (sel_listeners != null)
1348 sel_listeners.clear();
1350 if (listeners != null)
1354 if (commandListeners != null)
1356 commandListeners.clear();
1358 if (view_listeners != null)
1360 view_listeners.clear();
1362 if (pdbFileNameId != null)
1364 pdbFileNameId.clear();
1366 if (pdbIdFileName != null)
1368 pdbIdFileName.clear();
1372 public List<SelectionListener> getListeners() {
1373 return sel_listeners;
1376 public void addSelectionListener(SelectionListener selecter)
1378 if (!sel_listeners.contains(selecter))
1380 sel_listeners.add(selecter);
1384 public void removeSelectionListener(SelectionListener toremove)
1386 if (sel_listeners.contains(toremove))
1388 sel_listeners.remove(toremove);
1392 public synchronized void sendSelection(
1393 jalview.datamodel.SequenceGroup selection,
1394 jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden,
1395 SelectionSource source)
1397 for (SelectionListener slis : sel_listeners)
1401 slis.selection(selection, colsel, hidden, source);
1406 Vector<AlignmentViewPanelListener> view_listeners = new Vector<>();
1408 public synchronized void sendViewPosition(
1409 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1410 int startSeq, int endSeq)
1413 if (view_listeners != null && view_listeners.size() > 0)
1415 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1417 while (listeners.hasMoreElements())
1419 AlignmentViewPanelListener slis = listeners.nextElement();
1422 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1431 * Removes the instance associated with this provider
1435 public static void release(StructureSelectionManagerProvider provider)
1437 getInstance().selectionManagers.remove(provider);
1440 public void registerPDBEntry(PDBEntry pdbentry)
1442 if (pdbentry.getFile() != null
1443 && pdbentry.getFile().trim().length() > 0)
1445 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1449 public void addCommandListener(CommandListener cl)
1451 if (!commandListeners.contains(cl))
1453 commandListeners.add(cl);
1457 public boolean hasCommandListener(CommandListener cl)
1459 return this.commandListeners.contains(cl);
1462 public boolean removeCommandListener(CommandListener l)
1464 return commandListeners.remove(l);
1468 * Forward a command to any command listeners (except for the command's
1472 * the command to be broadcast (in its form after being performed)
1474 * if true, the command was being 'undone'
1477 public void commandPerformed(CommandI command, boolean undo,
1478 VamsasSource source)
1480 for (CommandListener listener : commandListeners)
1482 listener.mirrorCommand(command, undo, this, source);
1487 * Returns a new CommandI representing the given command as mapped to the
1488 * given sequences. If no mapping could be made, or the command is not of a
1489 * mappable kind, returns null.
1497 public CommandI mapCommand(CommandI command, boolean undo,
1498 final AlignmentI mapTo, char gapChar)
1500 if (command instanceof EditCommand)
1502 return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo,
1503 gapChar, seqmappings);
1505 else if (command instanceof OrderCommand)
1507 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1508 mapTo, seqmappings);
1513 public List<AlignedCodonFrame> getSequenceMappings()