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.IdentityHashMap;
30 import java.util.List;
31 import java.util.Locale;
33 import java.util.Vector;
35 import jalview.analysis.AlignSeq;
36 import jalview.api.StructureSelectionManagerProvider;
37 import jalview.bin.Console;
38 import jalview.commands.CommandI;
39 import jalview.commands.EditCommand;
40 import jalview.commands.OrderCommand;
41 import jalview.datamodel.AlignedCodonFrame;
42 import jalview.datamodel.AlignmentAnnotation;
43 import jalview.datamodel.AlignmentI;
44 import jalview.datamodel.Annotation;
45 import jalview.datamodel.HiddenColumns;
46 import jalview.datamodel.PDBEntry;
47 import jalview.datamodel.SearchResultMatchI;
48 import jalview.datamodel.SearchResults;
49 import jalview.datamodel.SearchResultsI;
50 import jalview.datamodel.SequenceI;
51 import jalview.ext.jmol.JmolParser;
52 import jalview.gui.IProgressIndicator;
53 import jalview.gui.SeqPanel;
54 import jalview.io.AppletFormatAdapter;
55 import jalview.io.DataSourceType;
56 import jalview.io.StructureFile;
57 import jalview.util.MappingUtils;
58 import jalview.util.MessageManager;
59 import jalview.util.Platform;
60 import jalview.ws.sifts.SiftsClient;
61 import jalview.ws.sifts.SiftsException;
62 import jalview.ws.sifts.SiftsSettings;
64 import mc_view.PDBChain;
65 import mc_view.PDBfile;
67 public class StructureSelectionManager
69 public final static String NEWLINE = System.lineSeparator();
71 static IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> instances;
73 private List<StructureMapping> mappings = new ArrayList<>();
75 private boolean processSecondaryStructure = false;
77 private boolean secStructServices = false;
79 private boolean addTempFacAnnot = false;
82 * Set of any registered mappings between (dataset) sequences.
84 private List<AlignedCodonFrame> seqmappings = new ArrayList<>();
86 private List<CommandListener> commandListeners = new ArrayList<>();
88 private List<SelectionListener> sel_listeners = new ArrayList<>();
91 * @return true if will try to use external services for processing secondary
94 public boolean isSecStructServices()
96 return secStructServices;
100 * control use of external services for processing secondary structure
102 * @param secStructServices
104 public void setSecStructServices(boolean secStructServices)
106 this.secStructServices = secStructServices;
110 * flag controlling addition of any kind of structural annotation
112 * @return true if temperature factor annotation will be added
114 public boolean isAddTempFacAnnot()
116 return addTempFacAnnot;
120 * set flag controlling addition of structural annotation
122 * @param addTempFacAnnot
124 public void setAddTempFacAnnot(boolean addTempFacAnnot)
126 this.addTempFacAnnot = addTempFacAnnot;
131 * @return if true, the structure manager will attempt to add secondary
132 * structure lines for unannotated sequences
135 public boolean isProcessSecondaryStructure()
137 return processSecondaryStructure;
141 * Control whether structure manager will try to annotate mapped sequences
142 * with secondary structure from PDB data.
146 public void setProcessSecondaryStructure(boolean enable)
148 processSecondaryStructure = enable;
152 * debug function - write all mappings to stdout
154 public void reportMapping()
156 if (mappings.isEmpty())
158 System.err.println("reportMapping: No PDB/Sequence mappings.");
163 "reportMapping: There are " + mappings.size() + " mappings.");
165 for (StructureMapping sm : mappings)
167 System.err.println("mapping " + i++ + " : " + sm.pdbfile);
173 * map between the PDB IDs (or structure identifiers) used by Jalview and the
174 * absolute filenames for PDB data that corresponds to it
176 Map<String, String> pdbIdFileName = new HashMap<>();
178 Map<String, String> pdbFileNameId = new HashMap<>();
180 public void registerPDBFile(String idForFile, String absoluteFile)
182 pdbIdFileName.put(idForFile, absoluteFile);
183 pdbFileNameId.put(absoluteFile, idForFile);
186 public String findIdForPDBFile(String idOrFile)
188 String id = pdbFileNameId.get(idOrFile);
192 public String findFileForPDBId(String idOrFile)
194 String id = pdbIdFileName.get(idOrFile);
198 public boolean isPDBFileRegistered(String idOrFile)
200 return pdbFileNameId.containsKey(idOrFile)
201 || pdbIdFileName.containsKey(idOrFile);
204 private static StructureSelectionManager nullProvider = null;
206 public static StructureSelectionManager getStructureSelectionManager(
207 StructureSelectionManagerProvider context)
211 if (nullProvider == null)
213 if (instances != null)
215 throw new Error(MessageManager.getString(
216 "error.implementation_error_structure_selection_manager_null"),
217 new NullPointerException(MessageManager
218 .getString("exception.ssm_context_is_null")));
222 nullProvider = new StructureSelectionManager();
227 if (instances == null)
229 instances = new java.util.IdentityHashMap<>();
231 StructureSelectionManager instance = instances.get(context);
232 if (instance == null)
234 if (nullProvider != null)
236 instance = nullProvider;
240 instance = new StructureSelectionManager();
242 instances.put(context, instance);
248 * flag controlling whether SeqMappings are relayed from received sequence
249 * mouse over events to other sequences
251 boolean relaySeqMappings = true;
254 * Enable or disable relay of seqMapping events to other sequences. You might
255 * want to do this if there are many sequence mappings and the host computer
260 public void setRelaySeqMappings(boolean relay)
262 relaySeqMappings = relay;
266 * get the state of the relay seqMappings flag.
268 * @return true if sequence mouse overs are being relayed to other mapped
271 public boolean isRelaySeqMappingsEnabled()
273 return relaySeqMappings;
276 Vector listeners = new Vector();
279 * register a listener for alignment sequence mouseover events
283 public void addStructureViewerListener(Object svl)
285 if (!listeners.contains(svl))
287 listeners.addElement(svl);
292 * Returns the filename the PDB id is already mapped to if known, or null if
298 public String alreadyMappedToFile(String pdbid)
300 for (StructureMapping sm : mappings)
302 if (sm.getPdbId().equalsIgnoreCase(pdbid))
311 * Import structure data and register a structure mapping for broadcasting
312 * colouring, mouseovers and selection events (convenience wrapper).
315 * - one or more sequences to be mapped to pdbFile
316 * @param targetChains
317 * - optional chain specification for mapping each sequence to pdb
318 * (may be nill, individual elements may be nill)
320 * - structure data resource
322 * - how to resolve data from resource
323 * @return null or the structure data parsed as a pdb file
325 synchronized public StructureFile setMapping(SequenceI[] sequence,
326 String[] targetChains, String pdbFile, DataSourceType protocol,
327 IProgressIndicator progress)
329 return computeMapping(true, sequence, targetChains, pdbFile, protocol,
334 * Import a single structure file and register sequence structure mappings for
335 * broadcasting colouring, mouseovers and selection events (convenience
338 * @param forStructureView
339 * when true, record the mapping for use in mouseOvers
341 * - one or more sequences to be mapped to pdbFile
342 * @param targetChains
343 * - optional chain specification for mapping each sequence to pdb
344 * (may be nill, individual elements may be nill)
346 * - structure data resource
348 * - how to resolve data from resource
349 * @return null or the structure data parsed as a pdb file
351 synchronized public StructureFile setMapping(boolean forStructureView,
352 SequenceI[] sequenceArray, String[] targetChainIds,
353 String pdbFile, DataSourceType sourceType)
355 return computeMapping(forStructureView, sequenceArray, targetChainIds,
356 pdbFile, sourceType, null);
360 * create sequence structure mappings between each sequence and the given
361 * pdbFile (retrieved via the given protocol). Either constructs a mapping
362 * using NW alignment or derives one from any available SIFTS mapping data.
364 * @param forStructureView
365 * when true, record the mapping for use in mouseOvers
367 * @param sequenceArray
368 * - one or more sequences to be mapped to pdbFile
369 * @param targetChainIds
370 * - optional chain specification for mapping each sequence to pdb
371 * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
372 * - this should be List<List<String>>, empty lists indicate no
373 * predefined mappings
375 * - structure data resource
377 * - how to resolve data from resource
378 * @param IProgressIndicator
379 * reference to UI component that maintains a progress bar for the
381 * @return null or the structure data parsed as a pdb file
383 synchronized public StructureFile computeMapping(boolean forStructureView,
384 SequenceI[] sequenceArray, String[] targetChainIds,
385 String pdbFile, DataSourceType sourceType,
386 IProgressIndicator progress)
388 long progressSessionId = System.currentTimeMillis() * 3;
391 * do we extract and transfer annotation from 3D data ?
393 // FIXME: possibly should just delete
395 boolean parseSecStr = processSecondaryStructure
396 ? isStructureFileProcessed(pdbFile, sequenceArray)
399 StructureFile pdb = null;
400 boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
403 // FIXME if sourceType is not null, we've lost data here
404 sourceType = AppletFormatAdapter.checkProtocol(pdbFile);
405 pdb = new JmolParser(false, pdbFile, sourceType);
406 pdb.addSettings(parseSecStr && processSecondaryStructure,
407 parseSecStr && addTempFacAnnot,
408 parseSecStr && secStructServices);
410 if (pdb.getId() != null && pdb.getId().trim().length() > 0
411 && DataSourceType.FILE == sourceType)
413 registerPDBFile(pdb.getId().trim(), pdbFile);
415 // if PDBId is unavailable then skip SIFTS mapping execution path
416 // TODO: JAL-3868 need to know if structure is actually from
417 // PDB (has valid PDB ID and has provenance suggesting it
418 // actually came from PDB)
419 boolean isProtein = false;
420 for (SequenceI s : sequenceArray)
428 isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable()
429 && !pdb.getId().startsWith("AF-") && isProtein;
431 } catch (Exception ex)
433 ex.printStackTrace();
437 * sifts client - non null if SIFTS mappings are to be used
439 SiftsClient siftsClient = null;
444 siftsClient = new SiftsClient(pdb);
446 } catch (SiftsException e)
448 isMapUsingSIFTs = false;
449 Console.error("SIFTS mapping failed", e);
450 Console.error("Falling back on Needleman & Wunsch alignment");
454 String targetChainId;
455 for (int s = 0; s < sequenceArray.length; s++)
457 boolean infChain = true;
458 final SequenceI seq = sequenceArray[s];
460 while (ds.getDatasetSequence() != null)
462 ds = ds.getDatasetSequence();
465 if (targetChainIds != null && targetChainIds[s] != null)
468 targetChainId = targetChainIds[s];
470 else if (seq.getName().indexOf("|") > -1)
472 targetChainId = seq.getName()
473 .substring(seq.getName().lastIndexOf("|") + 1);
474 if (targetChainId.length() > 1)
476 if (targetChainId.trim().length() == 0)
482 // not a valid chain identifier
493 * Attempt pairwise alignment of the sequence with each chain in the PDB,
494 * and remember the highest scoring chain
497 AlignSeq maxAlignseq = null;
498 String maxChainId = " ";
499 PDBChain maxChain = null;
500 boolean first = true;
501 for (PDBChain chain : pdb.getChains())
503 if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
506 continue; // don't try to map chains don't match.
508 // TODO: correctly determine sequence type for mixed na/peptide
510 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
511 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
514 // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
515 // as.calcScoreMatrix();
516 // as.traceAlignment();
518 if (first || as.maxscore > max
519 || (as.maxscore == max && chain.id.equals(targetChainId)))
525 maxChainId = chain.id;
528 if (maxChain == null)
533 if (sourceType == DataSourceType.PASTE)
535 pdbFile = "INLINE" + pdb.getId();
538 List<StructureMapping> seqToStrucMapping = new ArrayList<>();
539 if (isMapUsingSIFTs && seq.isProtein())
541 if (progress != null)
543 progress.setProgressBar(
545 .getString("status.obtaining_mapping_with_sifts"),
548 jalview.datamodel.Mapping sqmpping = maxAlignseq
549 .getMappingFromS1(false);
550 if (targetChainId != null && !targetChainId.trim().isEmpty())
552 StructureMapping siftsMapping;
555 siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
556 pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
557 seqToStrucMapping.add(siftsMapping);
558 maxChain.makeExactMapping(siftsMapping, seq);
559 maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS",
560 pdb.getId().toLowerCase(Locale.ROOT));
561 maxChain.transferResidueAnnotation(siftsMapping, null);
562 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
564 } catch (SiftsException e)
566 // fall back to NW alignment
567 Console.error(e.getMessage());
568 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
569 targetChainId, maxChain, pdb, maxAlignseq);
570 seqToStrucMapping.add(nwMapping);
571 maxChain.makeExactMapping(maxAlignseq, seq);
572 maxChain.transferRESNUMFeatures(seq, "IEA:Jalview",
573 pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is
576 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
577 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
582 List<StructureMapping> foundSiftsMappings = new ArrayList<>();
583 for (PDBChain chain : pdb.getChains())
585 StructureMapping siftsMapping = null;
588 siftsMapping = getStructureMapping(seq, pdbFile, chain.id,
589 pdb, chain, sqmpping, maxAlignseq, siftsClient);
590 foundSiftsMappings.add(siftsMapping);
591 chain.makeExactMapping(siftsMapping, seq);
592 chain.transferRESNUMFeatures(seq, "IEA: SIFTS",
593 pdb.getId().toLowerCase(Locale.ROOT));// FIXME: is this
595 chain.transferResidueAnnotation(siftsMapping, null);
596 } catch (SiftsException e)
598 System.err.println(e.getMessage());
599 } catch (Exception e)
602 "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
603 System.err.println(e.getMessage());
606 if (!foundSiftsMappings.isEmpty())
608 seqToStrucMapping.addAll(foundSiftsMappings);
609 ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
613 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
614 maxChainId, maxChain, pdb, maxAlignseq);
615 seqToStrucMapping.add(nwMapping);
616 maxChain.transferRESNUMFeatures(seq, null,
617 pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is this
619 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
620 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
626 if (progress != null)
628 progress.setProgressBar(
629 MessageManager.getString(
630 "status.obtaining_mapping_with_nw_alignment"),
633 StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
634 maxChain, pdb, maxAlignseq);
635 seqToStrucMapping.add(nwMapping);
636 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
638 if (forStructureView)
640 for (StructureMapping sm : seqToStrucMapping)
642 addStructureMapping(sm); // not addAll!
645 if (progress != null)
647 progress.setProgressBar(null, progressSessionId);
654 * check if we need to extract secondary structure from given pdbFile and
655 * transfer to sequences
658 * @param sequenceArray
661 private boolean isStructureFileProcessed(String pdbFile,
662 SequenceI[] sequenceArray)
664 boolean parseSecStr = true;
665 if (isPDBFileRegistered(pdbFile))
667 for (SequenceI sq : sequenceArray)
670 while (ds.getDatasetSequence() != null)
672 ds = ds.getDatasetSequence();
675 if (ds.getAnnotation() != null)
677 for (AlignmentAnnotation ala : ds.getAnnotation())
679 // false if any annotation present from this structure
680 // JBPNote this fails for jmol/chimera view because the *file* is
681 // passed, not the structure data ID -
682 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
693 public void addStructureMapping(StructureMapping sm)
695 if (!mappings.contains(sm))
702 * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
707 * @param targetChainId
713 * client for retrieval of SIFTS mappings for this structure
715 * @throws SiftsException
717 private StructureMapping getStructureMapping(SequenceI seq,
718 String pdbFile, String targetChainId, StructureFile pdb,
719 PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
720 AlignSeq maxAlignseq, SiftsClient siftsClient)
721 throws SiftsException
723 StructureMapping curChainMapping = siftsClient
724 .getSiftsStructureMapping(seq, pdbFile, targetChainId);
727 PDBChain chain = pdb.findChain(targetChainId);
730 chain.transferResidueAnnotation(curChainMapping, null);
732 } catch (Exception e)
736 return curChainMapping;
739 private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
740 String maxChainId, PDBChain maxChain, StructureFile pdb,
741 AlignSeq maxAlignseq)
743 final StringBuilder mappingDetails = new StringBuilder(128);
744 mappingDetails.append(NEWLINE)
745 .append("Sequence \u27f7 Structure mapping details");
746 mappingDetails.append(NEWLINE);
748 .append("Method: inferred with Needleman & Wunsch alignment");
749 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
750 .append(NEWLINE).append("Sequence = ")
751 .append(maxChain.sequence.getSequenceAsString());
752 mappingDetails.append(NEWLINE).append("No of residues = ")
753 .append(maxChain.residues.size()).append(NEWLINE)
755 PrintStream ps = new PrintStream(System.out)
758 public void print(String x)
760 mappingDetails.append(x);
764 public void println()
766 mappingDetails.append(NEWLINE);
770 maxAlignseq.printAlignment(ps);
772 mappingDetails.append(NEWLINE).append("PDB start/end ");
773 mappingDetails.append(String.valueOf(maxAlignseq.seq2start))
775 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
776 mappingDetails.append(NEWLINE).append("SEQ start/end ");
779 .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1)))
781 mappingDetails.append(
782 String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1)));
783 mappingDetails.append(NEWLINE);
784 maxChain.makeExactMapping(maxAlignseq, seq);
785 jalview.datamodel.Mapping sqmpping = maxAlignseq
786 .getMappingFromS1(false);
787 maxChain.transferRESNUMFeatures(seq, null,
788 pdb.getId().toLowerCase(Locale.ROOT));
790 HashMap<Integer, int[]> mapping = new HashMap<>();
797 Atom tmp = maxChain.atoms.elementAt(index);
798 if ((resNum != tmp.resNumber || insCode != tmp.insCode)
799 && tmp.alignmentMapping != -1)
801 resNum = tmp.resNumber;
802 insCode = tmp.insCode;
803 if (tmp.alignmentMapping >= -1)
805 mapping.put(tmp.alignmentMapping + 1,
807 { tmp.resNumber, tmp.atomIndex });
812 } while (index < maxChain.atoms.size());
814 StructureMapping nwMapping = new StructureMapping(seq, pdbFile,
815 pdb.getId(), maxChainId, mapping, mappingDetails.toString());
816 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
820 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
822 listeners.removeElement(svl);
823 if (svl instanceof SequenceListener)
825 for (int i = 0; i < listeners.size(); i++)
827 if (listeners.elementAt(i) instanceof StructureListener)
829 ((StructureListener) listeners.elementAt(i))
830 .releaseReferences(svl);
835 if (pdbfiles == null)
841 * Remove mappings to the closed listener's PDB files, but first check if
842 * another listener is still interested
844 List<String> pdbs = new ArrayList<>(Arrays.asList(pdbfiles));
846 StructureListener sl;
847 for (int i = 0; i < listeners.size(); i++)
849 if (listeners.elementAt(i) instanceof StructureListener)
851 sl = (StructureListener) listeners.elementAt(i);
852 for (String pdbfile : sl.getStructureFiles())
854 pdbs.remove(pdbfile);
860 * Rebuild the mappings set, retaining only those which are for 'other' PDB
865 List<StructureMapping> tmp = new ArrayList<>();
866 for (StructureMapping sm : mappings)
868 if (!pdbs.contains(sm.pdbfile))
879 * Propagate mouseover of a single position in a structure
886 public String mouseOverStructure(int pdbResNum, String chain,
889 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
890 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
891 return mouseOverStructure(atoms);
895 * Propagate mouseover or selection of multiple positions in a structure
899 public String mouseOverStructure(List<AtomSpec> atoms)
901 if (listeners == null)
903 // old or prematurely sent event
906 boolean hasSequenceListener = false;
907 for (int i = 0; i < listeners.size(); i++)
909 if (listeners.elementAt(i) instanceof SequenceListener)
911 hasSequenceListener = true;
914 if (!hasSequenceListener)
919 SearchResultsI results = findAlignmentPositionsForStructurePositions(
921 String result = null;
922 for (Object li : listeners)
924 if (li instanceof SequenceListener)
926 String s = ((SequenceListener) li).highlightSequence(results);
937 * Constructs a SearchResults object holding regions (if any) in the Jalview
938 * alignment which have a mapping to the structure viewer positions in the
944 public SearchResultsI findAlignmentPositionsForStructurePositions(
945 List<AtomSpec> atoms)
947 SearchResultsI results = new SearchResults();
948 for (AtomSpec atom : atoms)
950 SequenceI lastseq = null;
952 for (StructureMapping sm : mappings)
954 if (sm.pdbfile.equals(atom.getPdbFile())
955 && sm.pdbchain.equals(atom.getChain()))
957 int indexpos = sm.getSeqPos(atom.getPdbResNum());
958 if (lastipos != indexpos || lastseq != sm.sequence)
960 results.addResult(sm.sequence, indexpos, indexpos);
962 lastseq = sm.sequence;
963 // construct highlighted sequence list
964 for (AlignedCodonFrame acf : seqmappings)
966 acf.markMappedRegion(sm.sequence, indexpos, results);
976 * highlight regions associated with a position (indexpos) in seq
979 * the sequence that the mouse over occurred on
981 * the absolute position being mouseovered in seq (0 to seq.length())
983 * the sequence position (if -1, seq.findPosition is called to
984 * resolve the residue number)
986 public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
989 mouseOverSequence(seq, indexpos, seqPos, source, false);
992 public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
993 VamsasSource source, boolean setStatusMessage)
995 boolean hasSequenceListeners = handlingVamsasMo
996 || !seqmappings.isEmpty();
997 SearchResultsI results = null;
1000 seqPos = seq.findPosition(indexpos);
1003 // precompute so we can also relay structure highlights
1004 if (results == null)
1006 results = MappingUtils.buildSearchResults(seq, seqPos, seqmappings);
1008 if (handlingVamsasMo)
1010 results.addResult(seq, seqPos, seqPos);
1014 Console.debug("MOUSEOVER SEARCH RESULTS: "
1015 + (results.isEmpty() ? "EMPTY" : results.getCount()));
1017 for (int i = 0; i < listeners.size(); i++)
1019 Object listener = listeners.elementAt(i);
1020 if (listener == source)
1022 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1023 // Temporary fudge with SequenceListener.getVamsasSource()
1026 if (listener instanceof StructureListener)
1028 StructureListener sl = (StructureListener) listener;
1029 // TODO: consider merging highlightStructure variants second call
1030 // functionally same as first if seq/seqPos is part of the searchResults
1031 if (highlightStructure(sl, seq, seqPos) == 0 && relaySeqMappings)
1033 // structure highlights for mapped sequences
1034 highlightStructure(sl, results);
1039 if (listener instanceof SequenceListener)
1041 final SequenceListener seqListener = (SequenceListener) listener;
1042 if (setStatusMessage && seqListener instanceof SeqPanel)
1044 SeqPanel sp = (SeqPanel) seqListener;
1045 // THIS IS HIGHLY SUSPECT, ONLY IN FOR DEMO OF
1046 // jalview.rest.HighlightSequenceEndpoint
1047 sp.setStatusMessage(seq, seqPos - 1, -1);
1049 if (hasSequenceListeners
1050 && seqListener.getVamsasSource() != source)
1052 if (relaySeqMappings)
1054 if (!results.isEmpty())
1056 seqListener.highlightSequence(results);
1061 else if (listener instanceof VamsasListener && !handlingVamsasMo)
1063 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
1066 else if (listener instanceof SecondaryStructureListener)
1068 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
1076 * highlights positions in a structure viewer corresponding to one or more
1077 * positions on sequences
1080 * @param searchResults
1081 * @return 0 or number of structure regions highlighted
1083 public int highlightStructure(StructureListener sl,
1084 SearchResultsI searchResults)
1087 List<AtomSpec> atoms = new ArrayList<>();
1089 for (SearchResultMatchI sr : searchResults.getResults())
1091 SequenceI seq = sr.getSequence();
1092 for (StructureMapping sm : mappings)
1094 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1095 || (sm.sequence.getDatasetSequence() != null
1096 && (sm.sequence.getDatasetSequence() == seq
1097 || sm.sequence.getDatasetSequence() == seq
1098 .getDatasetSequence())))
1100 for (int index = sr.getStart(); index <= sr.getEnd(); index++)
1102 atomNo = sm.getAtomNum(index);
1106 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1107 sm.getPDBResNum(index), atomNo));
1113 sl.highlightAtoms(atoms);
1114 return atoms.size();
1118 * Send suitable messages to a StructureListener to highlight atoms
1119 * corresponding to the given sequence position(s)
1124 * @return 0 if no atoms identified for position(s)
1126 public int highlightStructure(StructureListener sl, SequenceI seq,
1129 if (!sl.isListeningFor(seq))
1134 List<AtomSpec> atoms = new ArrayList<>();
1135 for (StructureMapping sm : mappings)
1137 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1138 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1139 .getDatasetSequence() == seq.getDatasetSequence()))
1141 for (int index : positions)
1143 atomNo = sm.getAtomNum(index);
1147 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1148 sm.getPDBResNum(index), atomNo));
1153 sl.highlightAtoms(atoms);
1154 return atoms.size();
1158 * true if a mouse over event from an external (ie Vamsas) source is being
1161 boolean handlingVamsasMo = false;
1166 * as mouseOverSequence but only route event to SequenceListeners
1170 * in an alignment sequence
1172 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
1173 VamsasSource source)
1175 handlingVamsasMo = true;
1176 long msg = sequenceI.hashCode() * (1 + position);
1180 mouseOverSequence(sequenceI, position, -1, source);
1182 handlingVamsasMo = false;
1185 public Annotation[] colourSequenceFromStructure(SequenceI seq,
1189 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
1190 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
1192 * Annotation [] annotations = new Annotation[seq.getLength()];
1194 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
1195 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
1196 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
1198 * for (int j = 0; j < mappings.length; j++) {
1200 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
1201 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
1202 * System.out.println(pdbid+" "+mappings[j].getPdbId() +"
1203 * "+mappings[j].pdbfile);
1205 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
1206 * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
1208 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
1209 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
1210 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
1211 * mappings[j].pdbfile); }
1213 * annotations[index] = new Annotation("X",null,' ',0,col); } return
1214 * annotations; } } } }
1216 * return annotations;
1220 public void structureSelectionChanged()
1224 public void sequenceSelectionChanged()
1228 public void sequenceColoursChanged(Object source)
1230 StructureListener sl;
1231 for (int i = 0; i < listeners.size(); i++)
1233 if (listeners.elementAt(i) instanceof StructureListener)
1235 sl = (StructureListener) listeners.elementAt(i);
1236 sl.updateColours(source);
1241 public StructureMapping[] getMapping(String pdbfile)
1243 List<StructureMapping> tmp = new ArrayList<>();
1244 for (StructureMapping sm : mappings)
1246 if (sm.pdbfile.equals(pdbfile))
1251 return tmp.toArray(new StructureMapping[tmp.size()]);
1255 * Returns a readable description of all mappings for the given pdbfile to any
1256 * of the given sequences
1262 public String printMappings(String pdbfile, List<SequenceI> seqs)
1264 if (pdbfile == null || seqs == null || seqs.isEmpty())
1269 StringBuilder sb = new StringBuilder(64);
1270 for (StructureMapping sm : mappings)
1272 if (Platform.pathEquals(sm.pdbfile, pdbfile)
1273 && seqs.contains(sm.sequence))
1275 sb.append(sm.mappingDetails);
1277 // separator makes it easier to read multiple mappings
1278 sb.append("=====================");
1284 return sb.toString();
1288 * Remove the given mapping
1292 public void deregisterMapping(AlignedCodonFrame acf)
1296 boolean removed = seqmappings.remove(acf);
1297 if (removed && seqmappings.isEmpty())
1299 System.out.println("All mappings removed");
1305 * Add each of the given codonFrames to the stored set, if not aready present.
1309 public void registerMappings(List<AlignedCodonFrame> mappings)
1311 if (mappings != null)
1313 for (AlignedCodonFrame acf : mappings)
1315 registerMapping(acf);
1321 * Add the given mapping to the stored set, unless already stored.
1323 public void registerMapping(AlignedCodonFrame acf)
1327 if (!seqmappings.contains(acf))
1329 seqmappings.add(acf);
1335 * Resets this object to its initial state by removing all registered
1336 * listeners, codon mappings, PDB file mappings
1338 public void resetAll()
1340 if (mappings != null)
1344 if (seqmappings != null)
1346 seqmappings.clear();
1348 if (sel_listeners != null)
1350 sel_listeners.clear();
1352 if (listeners != null)
1356 if (commandListeners != null)
1358 commandListeners.clear();
1360 if (view_listeners != null)
1362 view_listeners.clear();
1364 if (pdbFileNameId != null)
1366 pdbFileNameId.clear();
1368 if (pdbIdFileName != null)
1370 pdbIdFileName.clear();
1374 public void addSelectionListener(SelectionListener selecter)
1376 if (!sel_listeners.contains(selecter))
1378 sel_listeners.add(selecter);
1382 public void removeSelectionListener(SelectionListener toremove)
1384 if (sel_listeners.contains(toremove))
1386 sel_listeners.remove(toremove);
1390 public synchronized void sendSelection(
1391 jalview.datamodel.SequenceGroup selection,
1392 jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden,
1393 SelectionSource source)
1395 for (SelectionListener slis : sel_listeners)
1399 slis.selection(selection, colsel, hidden, source);
1404 Vector<AlignmentViewPanelListener> view_listeners = new Vector<>();
1406 public synchronized void sendViewPosition(
1407 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1408 int startSeq, int endSeq)
1411 if (view_listeners != null && view_listeners.size() > 0)
1413 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1415 while (listeners.hasMoreElements())
1417 AlignmentViewPanelListener slis = listeners.nextElement();
1420 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1428 * release all references associated with this manager provider
1430 * @param jalviewLite
1432 public static void release(StructureSelectionManagerProvider jalviewLite)
1434 // synchronized (instances)
1436 if (instances == null)
1440 StructureSelectionManager mnger = (instances.get(jalviewLite));
1443 instances.remove(jalviewLite);
1446 /* bsoares 2019-03-20 finalize deprecated, no apparent external
1447 * resources to close
1449 // mnger.finalize();
1450 } catch (Throwable x)
1457 public void registerPDBEntry(PDBEntry pdbentry)
1459 if (pdbentry.getFile() != null
1460 && pdbentry.getFile().trim().length() > 0)
1462 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1466 public void addCommandListener(CommandListener cl)
1468 if (!commandListeners.contains(cl))
1470 commandListeners.add(cl);
1474 public boolean hasCommandListener(CommandListener cl)
1476 return this.commandListeners.contains(cl);
1479 public boolean removeCommandListener(CommandListener l)
1481 return commandListeners.remove(l);
1485 * Forward a command to any command listeners (except for the command's
1489 * the command to be broadcast (in its form after being performed)
1491 * if true, the command was being 'undone'
1494 public void commandPerformed(CommandI command, boolean undo,
1495 VamsasSource source)
1497 for (CommandListener listener : commandListeners)
1499 listener.mirrorCommand(command, undo, this, source);
1504 * Returns a new CommandI representing the given command as mapped to the
1505 * given sequences. If no mapping could be made, or the command is not of a
1506 * mappable kind, returns null.
1514 public CommandI mapCommand(CommandI command, boolean undo,
1515 final AlignmentI mapTo, char gapChar)
1517 if (command instanceof EditCommand)
1519 return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo,
1520 gapChar, seqmappings);
1522 else if (command instanceof OrderCommand)
1524 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1525 mapTo, seqmappings);
1530 public List<AlignedCodonFrame> getSequenceMappings()