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;
32 import java.util.Vector;
34 import jalview.analysis.AlignSeq;
35 import jalview.api.StructureSelectionManagerProvider;
36 import jalview.bin.Cache;
37 import jalview.commands.CommandI;
38 import jalview.commands.EditCommand;
39 import jalview.commands.OrderCommand;
40 import jalview.datamodel.AlignedCodonFrame;
41 import jalview.datamodel.AlignmentAnnotation;
42 import jalview.datamodel.AlignmentI;
43 import jalview.datamodel.Annotation;
44 import jalview.datamodel.HiddenColumns;
45 import jalview.datamodel.PDBEntry;
46 import jalview.datamodel.SearchResults;
47 import jalview.datamodel.SearchResultsI;
48 import jalview.datamodel.SequenceI;
49 import jalview.ext.jmol.JmolParser;
50 import jalview.gui.IProgressIndicator;
51 import jalview.io.AppletFormatAdapter;
52 import jalview.io.DataSourceType;
53 import jalview.io.StructureFile;
54 import jalview.util.MappingUtils;
55 import jalview.util.MessageManager;
56 import jalview.util.Platform;
57 import jalview.ws.sifts.SiftsClient;
58 import jalview.ws.sifts.SiftsException;
59 import jalview.ws.sifts.SiftsSettings;
61 import mc_view.PDBChain;
62 import mc_view.PDBfile;
64 public class StructureSelectionManager
66 public final static String NEWLINE = System.lineSeparator();
68 static IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> instances;
70 private List<StructureMapping> mappings = new ArrayList<>();
72 private boolean processSecondaryStructure = false;
74 private boolean secStructServices = false;
76 private boolean addTempFacAnnot = false;
79 * Set of any registered mappings between (dataset) sequences.
81 private List<AlignedCodonFrame> seqmappings = new ArrayList<>();
83 private List<CommandListener> commandListeners = new ArrayList<>();
85 private List<SelectionListener> sel_listeners = new ArrayList<>();
88 * @return true if will try to use external services for processing secondary
91 public boolean isSecStructServices()
93 return secStructServices;
97 * control use of external services for processing secondary structure
99 * @param secStructServices
101 public void setSecStructServices(boolean secStructServices)
103 this.secStructServices = secStructServices;
107 * flag controlling addition of any kind of structural annotation
109 * @return true if temperature factor annotation will be added
111 public boolean isAddTempFacAnnot()
113 return addTempFacAnnot;
117 * set flag controlling addition of structural annotation
119 * @param addTempFacAnnot
121 public void setAddTempFacAnnot(boolean addTempFacAnnot)
123 this.addTempFacAnnot = addTempFacAnnot;
128 * @return if true, the structure manager will attempt to add secondary
129 * structure lines for unannotated sequences
132 public boolean isProcessSecondaryStructure()
134 return processSecondaryStructure;
138 * Control whether structure manager will try to annotate mapped sequences
139 * with secondary structure from PDB data.
143 public void setProcessSecondaryStructure(boolean enable)
145 processSecondaryStructure = enable;
149 * debug function - write all mappings to stdout
151 public void reportMapping()
153 if (mappings.isEmpty())
155 System.err.println("reportMapping: No PDB/Sequence mappings.");
160 "reportMapping: There are " + mappings.size() + " mappings.");
162 for (StructureMapping sm : mappings)
164 System.err.println("mapping " + i++ + " : " + sm.pdbfile);
170 * map between the PDB IDs (or structure identifiers) used by Jalview and the
171 * absolute filenames for PDB data that corresponds to it
173 Map<String, String> pdbIdFileName = new HashMap<>();
175 Map<String, String> pdbFileNameId = new HashMap<>();
177 public void registerPDBFile(String idForFile, String absoluteFile)
179 pdbIdFileName.put(idForFile, absoluteFile);
180 pdbFileNameId.put(absoluteFile, idForFile);
183 public String findIdForPDBFile(String idOrFile)
185 String id = pdbFileNameId.get(idOrFile);
189 public String findFileForPDBId(String idOrFile)
191 String id = pdbIdFileName.get(idOrFile);
195 public boolean isPDBFileRegistered(String idOrFile)
197 return pdbFileNameId.containsKey(idOrFile)
198 || pdbIdFileName.containsKey(idOrFile);
201 private static StructureSelectionManager nullProvider = null;
203 public static StructureSelectionManager getStructureSelectionManager(
204 StructureSelectionManagerProvider context)
208 if (nullProvider == null)
210 if (instances != null)
212 throw new Error(MessageManager.getString(
213 "error.implementation_error_structure_selection_manager_null"),
214 new NullPointerException(MessageManager
215 .getString("exception.ssm_context_is_null")));
219 nullProvider = new StructureSelectionManager();
224 if (instances == null)
226 instances = new java.util.IdentityHashMap<>();
228 StructureSelectionManager instance = instances.get(context);
229 if (instance == null)
231 if (nullProvider != null)
233 instance = nullProvider;
237 instance = new StructureSelectionManager();
239 instances.put(context, instance);
245 * flag controlling whether SeqMappings are relayed from received sequence
246 * mouse over events to other sequences
248 boolean relaySeqMappings = true;
251 * Enable or disable relay of seqMapping events to other sequences. You might
252 * want to do this if there are many sequence mappings and the host computer
257 public void setRelaySeqMappings(boolean relay)
259 relaySeqMappings = relay;
263 * get the state of the relay seqMappings flag.
265 * @return true if sequence mouse overs are being relayed to other mapped
268 public boolean isRelaySeqMappingsEnabled()
270 return relaySeqMappings;
273 Vector listeners = new Vector();
276 * register a listener for alignment sequence mouseover events
280 public void addStructureViewerListener(Object svl)
282 if (!listeners.contains(svl))
284 listeners.addElement(svl);
289 * Returns the filename the PDB id is already mapped to if known, or null if
295 public String alreadyMappedToFile(String pdbid)
297 for (StructureMapping sm : mappings)
299 if (sm.getPdbId().equalsIgnoreCase(pdbid))
308 * Import structure data and register a structure mapping for broadcasting
309 * colouring, mouseovers and selection events (convenience wrapper).
312 * - one or more sequences to be mapped to pdbFile
313 * @param targetChains
314 * - optional chain specification for mapping each sequence to pdb
315 * (may be nill, individual elements may be nill)
317 * - structure data resource
319 * - how to resolve data from resource
320 * @return null or the structure data parsed as a pdb file
322 synchronized public StructureFile setMapping(SequenceI[] sequence,
323 String[] targetChains, String pdbFile, DataSourceType protocol,
324 IProgressIndicator progress)
326 return computeMapping(true, sequence, targetChains, pdbFile, protocol,
331 * Import a single structure file and register sequence structure mappings for
332 * broadcasting colouring, mouseovers and selection events (convenience
335 * @param forStructureView
336 * when true, record the mapping for use in mouseOvers
338 * - one or more sequences to be mapped to pdbFile
339 * @param targetChains
340 * - optional chain specification for mapping each sequence to pdb
341 * (may be nill, individual elements may be nill)
343 * - structure data resource
345 * - how to resolve data from resource
346 * @return null or the structure data parsed as a pdb file
348 synchronized public StructureFile setMapping(boolean forStructureView,
349 SequenceI[] sequenceArray, String[] targetChainIds,
350 String pdbFile, DataSourceType sourceType)
352 return computeMapping(forStructureView, sequenceArray, targetChainIds,
353 pdbFile, sourceType, null);
357 * create sequence structure mappings between each sequence and the given
358 * pdbFile (retrieved via the given protocol). Either constructs a mapping
359 * using NW alignment or derives one from any available SIFTS mapping data.
361 * @param forStructureView
362 * when true, record the mapping for use in mouseOvers
364 * @param sequenceArray
365 * - one or more sequences to be mapped to pdbFile
366 * @param targetChainIds
367 * - optional chain specification for mapping each sequence to pdb
368 * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
369 * - this should be List<List<String>>, empty lists indicate no
370 * predefined mappings
372 * - structure data resource
374 * - how to resolve data from resource
375 * @param IProgressIndicator
376 * reference to UI component that maintains a progress bar for the
378 * @return null or the structure data parsed as a pdb file
380 synchronized public StructureFile computeMapping(
381 boolean forStructureView, SequenceI[] sequenceArray,
382 String[] targetChainIds, String pdbFile, DataSourceType sourceType,
383 IProgressIndicator progress)
385 long progressSessionId = System.currentTimeMillis() * 3;
388 * do we extract and transfer annotation from 3D data ?
390 // FIXME: possibly should just delete
392 boolean parseSecStr = processSecondaryStructure
393 ? isStructureFileProcessed(pdbFile, sequenceArray)
396 StructureFile pdb = null;
397 boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
400 // FIXME if sourceType is not null, we've lost data here
401 sourceType = AppletFormatAdapter.checkProtocol(pdbFile);
402 pdb = new JmolParser(false, pdbFile, sourceType);
403 pdb.addSettings(parseSecStr && processSecondaryStructure,
404 parseSecStr && addTempFacAnnot,
405 parseSecStr && secStructServices);
407 if (pdb.getId() != null && pdb.getId().trim().length() > 0
408 && DataSourceType.FILE == sourceType)
410 registerPDBFile(pdb.getId().trim(), pdbFile);
412 // if PDBId is unavailable then skip SIFTS mapping execution path
413 // TODO: JAL-3868 need to know if structure is actually from
414 // PDB (has valid PDB ID and has provenance suggesting it
415 // actually came from PDB)
416 boolean isProtein = false;
417 for (SequenceI s:sequenceArray) {
423 isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable() && !pdb.getId().startsWith("AF-") && isProtein;
425 } catch (Exception ex)
427 ex.printStackTrace();
431 * sifts client - non null if SIFTS mappings are to be used
433 SiftsClient siftsClient = null;
438 siftsClient = new SiftsClient(pdb);
440 } catch (SiftsException e)
442 isMapUsingSIFTs = false;
443 Cache.log.error("SIFTS mapping failed", e);
444 Cache.log.error("Falling back on Needleman & Wunsch alignment");
448 String targetChainId;
449 for (int s = 0; s < sequenceArray.length; s++)
451 boolean infChain = true;
452 final SequenceI seq = sequenceArray[s];
454 while (ds.getDatasetSequence() != null)
456 ds = ds.getDatasetSequence();
459 if (targetChainIds != null && targetChainIds[s] != null)
462 targetChainId = targetChainIds[s];
464 else if (seq.getName().indexOf("|") > -1)
466 targetChainId = seq.getName()
467 .substring(seq.getName().lastIndexOf("|") + 1);
468 if (targetChainId.length() > 1)
470 if (targetChainId.trim().length() == 0)
476 // not a valid chain identifier
487 * Attempt pairwise alignment of the sequence with each chain in the PDB,
488 * and remember the highest scoring chain
491 AlignSeq maxAlignseq = null;
492 String maxChainId = " ";
493 PDBChain maxChain = null;
494 boolean first = true;
495 for (PDBChain chain : pdb.getChains())
497 if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
500 continue; // don't try to map chains don't match.
502 // TODO: correctly determine sequence type for mixed na/peptide
504 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
505 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
508 // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
509 // as.calcScoreMatrix();
510 // as.traceAlignment();
512 if (first || as.maxscore > max
513 || (as.maxscore == max && chain.id.equals(targetChainId)))
519 maxChainId = chain.id;
522 if (maxChain == null)
527 if (sourceType == DataSourceType.PASTE)
529 pdbFile = "INLINE" + pdb.getId();
532 List<StructureMapping> seqToStrucMapping = new ArrayList<>();
533 if (isMapUsingSIFTs && seq.isProtein())
535 if (progress!=null) {
536 progress.setProgressBar(MessageManager
537 .getString("status.obtaining_mapping_with_sifts"),
540 jalview.datamodel.Mapping sqmpping = maxAlignseq
541 .getMappingFromS1(false);
542 if (targetChainId != null && !targetChainId.trim().isEmpty())
544 StructureMapping siftsMapping;
547 siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
548 pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
549 seqToStrucMapping.add(siftsMapping);
550 maxChain.makeExactMapping(siftsMapping, seq);
551 maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS");
552 maxChain.transferResidueAnnotation(siftsMapping, null);
553 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
555 } catch (SiftsException e)
557 // fall back to NW alignment
558 Cache.log.error(e.getMessage());
559 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
560 targetChainId, maxChain, pdb, maxAlignseq);
561 seqToStrucMapping.add(nwMapping);
562 maxChain.makeExactMapping(maxAlignseq, seq);
563 maxChain.transferRESNUMFeatures(seq, "IEA:Jalview"); // FIXME: is
566 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
567 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
572 List<StructureMapping> foundSiftsMappings = new ArrayList<>();
573 for (PDBChain chain : pdb.getChains())
575 StructureMapping siftsMapping = null;
578 siftsMapping = getStructureMapping(seq,
579 pdbFile, chain.id, pdb, chain, sqmpping, maxAlignseq,
581 foundSiftsMappings.add(siftsMapping);
582 chain.makeExactMapping(siftsMapping, seq);
583 chain.transferRESNUMFeatures(seq, "IEA: SIFTS");// FIXME: is this
585 chain.transferResidueAnnotation(siftsMapping, null);
586 } catch (SiftsException e)
588 System.err.println(e.getMessage());
594 "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
595 System.err.println(e.getMessage());
598 if (!foundSiftsMappings.isEmpty())
600 seqToStrucMapping.addAll(foundSiftsMappings);
601 ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
605 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
606 maxChainId, maxChain, pdb, maxAlignseq);
607 seqToStrucMapping.add(nwMapping);
608 maxChain.transferRESNUMFeatures(seq, null); // FIXME: is this
610 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
611 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
617 if (progress != null)
619 progress.setProgressBar(MessageManager
620 .getString("status.obtaining_mapping_with_nw_alignment"),
623 StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
624 maxChain, pdb, maxAlignseq);
625 seqToStrucMapping.add(nwMapping);
626 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
628 if (forStructureView)
630 for (StructureMapping sm : seqToStrucMapping)
632 addStructureMapping(sm); // not addAll!
635 if (progress != null)
637 progress.setProgressBar(null, progressSessionId);
644 * check if we need to extract secondary structure from given pdbFile and
645 * transfer to sequences
648 * @param sequenceArray
651 private boolean isStructureFileProcessed(String pdbFile,
652 SequenceI[] sequenceArray)
654 boolean parseSecStr = true;
655 if (isPDBFileRegistered(pdbFile))
657 for (SequenceI sq : sequenceArray)
660 while (ds.getDatasetSequence() != null)
662 ds = ds.getDatasetSequence();
665 if (ds.getAnnotation() != null)
667 for (AlignmentAnnotation ala : ds.getAnnotation())
669 // false if any annotation present from this structure
670 // JBPNote this fails for jmol/chimera view because the *file* is
671 // passed, not the structure data ID -
672 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
683 public void addStructureMapping(StructureMapping sm)
685 if (!mappings.contains(sm))
692 * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
697 * @param targetChainId
703 * client for retrieval of SIFTS mappings for this structure
705 * @throws SiftsException
707 private StructureMapping getStructureMapping(SequenceI seq,
708 String pdbFile, String targetChainId, StructureFile pdb,
709 PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
710 AlignSeq maxAlignseq, SiftsClient siftsClient) throws SiftsException
712 StructureMapping curChainMapping = siftsClient
713 .getSiftsStructureMapping(seq, pdbFile, targetChainId);
716 PDBChain chain = pdb.findChain(targetChainId);
719 chain.transferResidueAnnotation(curChainMapping, null);
721 } catch (Exception e)
725 return curChainMapping;
728 private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
729 String maxChainId, PDBChain maxChain, StructureFile pdb,
730 AlignSeq maxAlignseq)
732 final StringBuilder mappingDetails = new StringBuilder(128);
733 mappingDetails.append(NEWLINE)
734 .append("Sequence \u27f7 Structure mapping details");
735 mappingDetails.append(NEWLINE);
737 .append("Method: inferred with Needleman & Wunsch alignment");
738 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
739 .append(NEWLINE).append("Sequence = ")
740 .append(maxChain.sequence.getSequenceAsString());
741 mappingDetails.append(NEWLINE).append("No of residues = ")
742 .append(maxChain.residues.size()).append(NEWLINE)
744 PrintStream ps = new PrintStream(System.out)
747 public void print(String x)
749 mappingDetails.append(x);
753 public void println()
755 mappingDetails.append(NEWLINE);
759 maxAlignseq.printAlignment(ps);
761 mappingDetails.append(NEWLINE).append("PDB start/end ");
762 mappingDetails.append(String.valueOf(maxAlignseq.seq2start))
764 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
765 mappingDetails.append(NEWLINE).append("SEQ start/end ");
768 .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1)))
770 mappingDetails.append(
771 String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1)));
772 mappingDetails.append(NEWLINE);
773 maxChain.makeExactMapping(maxAlignseq, seq);
774 jalview.datamodel.Mapping sqmpping = maxAlignseq
775 .getMappingFromS1(false);
776 maxChain.transferRESNUMFeatures(seq, null);
778 HashMap<Integer, int[]> mapping = new HashMap<>();
785 Atom tmp = maxChain.atoms.elementAt(index);
786 if ((resNum != tmp.resNumber || insCode != tmp.insCode)
787 && tmp.alignmentMapping != -1)
789 resNum = tmp.resNumber;
790 insCode = tmp.insCode;
791 if (tmp.alignmentMapping >= -1)
793 mapping.put(tmp.alignmentMapping + 1,
795 { tmp.resNumber, tmp.atomIndex });
800 } while (index < maxChain.atoms.size());
802 StructureMapping nwMapping = new StructureMapping(seq, pdbFile,
803 pdb.getId(), maxChainId, mapping, mappingDetails.toString());
804 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
808 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
810 listeners.removeElement(svl);
811 if (svl instanceof SequenceListener)
813 for (int i = 0; i < listeners.size(); i++)
815 if (listeners.elementAt(i) instanceof StructureListener)
817 ((StructureListener) listeners.elementAt(i))
818 .releaseReferences(svl);
823 if (pdbfiles == null)
829 * Remove mappings to the closed listener's PDB files, but first check if
830 * another listener is still interested
832 List<String> pdbs = new ArrayList<>(Arrays.asList(pdbfiles));
834 StructureListener sl;
835 for (int i = 0; i < listeners.size(); i++)
837 if (listeners.elementAt(i) instanceof StructureListener)
839 sl = (StructureListener) listeners.elementAt(i);
840 for (String pdbfile : sl.getStructureFiles())
842 pdbs.remove(pdbfile);
848 * Rebuild the mappings set, retaining only those which are for 'other' PDB
853 List<StructureMapping> tmp = new ArrayList<>();
854 for (StructureMapping sm : mappings)
856 if (!pdbs.contains(sm.pdbfile))
867 * Propagate mouseover of a single position in a structure
874 public String mouseOverStructure(int pdbResNum, String chain,
877 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
878 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
879 return mouseOverStructure(atoms);
883 * Propagate mouseover or selection of multiple positions in a structure
887 public String mouseOverStructure(List<AtomSpec> atoms)
889 if (listeners == null)
891 // old or prematurely sent event
894 boolean hasSequenceListener = false;
895 for (int i = 0; i < listeners.size(); i++)
897 if (listeners.elementAt(i) instanceof SequenceListener)
899 hasSequenceListener = true;
902 if (!hasSequenceListener)
907 SearchResultsI results = findAlignmentPositionsForStructurePositions(
909 String result = null;
910 for (Object li : listeners)
912 if (li instanceof SequenceListener)
914 String s = ((SequenceListener) li).highlightSequence(results);
925 * Constructs a SearchResults object holding regions (if any) in the Jalview
926 * alignment which have a mapping to the structure viewer positions in the
932 public SearchResultsI findAlignmentPositionsForStructurePositions(
933 List<AtomSpec> atoms)
935 SearchResultsI results = new SearchResults();
936 for (AtomSpec atom : atoms)
938 SequenceI lastseq = null;
940 for (StructureMapping sm : mappings)
942 if (sm.pdbfile.equals(atom.getPdbFile())
943 && sm.pdbchain.equals(atom.getChain()))
945 int indexpos = sm.getSeqPos(atom.getPdbResNum());
946 if (lastipos != indexpos || lastseq != sm.sequence)
948 results.addResult(sm.sequence, indexpos, indexpos);
950 lastseq = sm.sequence;
951 // construct highlighted sequence list
952 for (AlignedCodonFrame acf : seqmappings)
954 acf.markMappedRegion(sm.sequence, indexpos, results);
964 * highlight regions associated with a position (indexpos) in seq
967 * the sequence that the mouse over occurred on
969 * the absolute position being mouseovered in seq (0 to seq.length())
971 * the sequence position (if -1, seq.findPosition is called to
972 * resolve the residue number)
974 public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
977 boolean hasSequenceListeners = handlingVamsasMo
978 || !seqmappings.isEmpty();
979 SearchResultsI results = null;
982 seqPos = seq.findPosition(indexpos);
984 for (int i = 0; i < listeners.size(); i++)
986 Object listener = listeners.elementAt(i);
987 if (listener == source)
989 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
990 // Temporary fudge with SequenceListener.getVamsasSource()
993 if (listener instanceof StructureListener)
995 highlightStructure((StructureListener) listener, seq, seqPos);
999 if (listener instanceof SequenceListener)
1001 final SequenceListener seqListener = (SequenceListener) listener;
1002 if (hasSequenceListeners
1003 && seqListener.getVamsasSource() != source)
1005 if (relaySeqMappings)
1007 if (results == null)
1009 results = MappingUtils.buildSearchResults(seq, seqPos,
1012 if (handlingVamsasMo)
1014 results.addResult(seq, seqPos, seqPos);
1017 if (!results.isEmpty())
1019 seqListener.highlightSequence(results);
1024 else if (listener instanceof VamsasListener && !handlingVamsasMo)
1026 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
1029 else if (listener instanceof SecondaryStructureListener)
1031 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
1039 * Send suitable messages to a StructureListener to highlight atoms
1040 * corresponding to the given sequence position(s)
1046 public void highlightStructure(StructureListener sl, SequenceI seq,
1049 if (!sl.isListeningFor(seq))
1054 List<AtomSpec> atoms = new ArrayList<>();
1055 for (StructureMapping sm : mappings)
1057 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1058 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1059 .getDatasetSequence() == seq.getDatasetSequence()))
1061 for (int index : positions)
1063 atomNo = sm.getAtomNum(index);
1067 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1068 sm.getPDBResNum(index), atomNo));
1073 sl.highlightAtoms(atoms);
1077 * true if a mouse over event from an external (ie Vamsas) source is being
1080 boolean handlingVamsasMo = false;
1085 * as mouseOverSequence but only route event to SequenceListeners
1089 * in an alignment sequence
1091 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
1092 VamsasSource source)
1094 handlingVamsasMo = true;
1095 long msg = sequenceI.hashCode() * (1 + position);
1099 mouseOverSequence(sequenceI, position, -1, source);
1101 handlingVamsasMo = false;
1104 public Annotation[] colourSequenceFromStructure(SequenceI seq,
1108 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
1109 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
1111 * Annotation [] annotations = new Annotation[seq.getLength()];
1113 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
1114 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
1115 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
1117 * for (int j = 0; j < mappings.length; j++) {
1119 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
1120 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
1121 * System.out.println(pdbid+" "+mappings[j].getPdbId() +"
1122 * "+mappings[j].pdbfile);
1124 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
1125 * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
1127 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
1128 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
1129 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
1130 * mappings[j].pdbfile); }
1132 * annotations[index] = new Annotation("X",null,' ',0,col); } return
1133 * annotations; } } } }
1135 * return annotations;
1139 public void structureSelectionChanged()
1143 public void sequenceSelectionChanged()
1147 public void sequenceColoursChanged(Object source)
1149 StructureListener sl;
1150 for (int i = 0; i < listeners.size(); i++)
1152 if (listeners.elementAt(i) instanceof StructureListener)
1154 sl = (StructureListener) listeners.elementAt(i);
1155 sl.updateColours(source);
1160 public StructureMapping[] getMapping(String pdbfile)
1162 List<StructureMapping> tmp = new ArrayList<>();
1163 for (StructureMapping sm : mappings)
1165 if (sm.pdbfile.equals(pdbfile))
1170 return tmp.toArray(new StructureMapping[tmp.size()]);
1174 * Returns a readable description of all mappings for the given pdbfile to any
1175 * of the given sequences
1181 public String printMappings(String pdbfile, List<SequenceI> seqs)
1183 if (pdbfile == null || seqs == null || seqs.isEmpty())
1188 StringBuilder sb = new StringBuilder(64);
1189 for (StructureMapping sm : mappings)
1191 if (Platform.pathEquals(sm.pdbfile, pdbfile)
1192 && seqs.contains(sm.sequence))
1194 sb.append(sm.mappingDetails);
1196 // separator makes it easier to read multiple mappings
1197 sb.append("=====================");
1203 return sb.toString();
1207 * Remove the given mapping
1211 public void deregisterMapping(AlignedCodonFrame acf)
1215 boolean removed = seqmappings.remove(acf);
1216 if (removed && seqmappings.isEmpty())
1218 System.out.println("All mappings removed");
1224 * Add each of the given codonFrames to the stored set, if not aready present.
1228 public void registerMappings(List<AlignedCodonFrame> mappings)
1230 if (mappings != null)
1232 for (AlignedCodonFrame acf : mappings)
1234 registerMapping(acf);
1240 * Add the given mapping to the stored set, unless already stored.
1242 public void registerMapping(AlignedCodonFrame acf)
1246 if (!seqmappings.contains(acf))
1248 seqmappings.add(acf);
1254 * Resets this object to its initial state by removing all registered
1255 * listeners, codon mappings, PDB file mappings
1257 public void resetAll()
1259 if (mappings != null)
1263 if (seqmappings != null)
1265 seqmappings.clear();
1267 if (sel_listeners != null)
1269 sel_listeners.clear();
1271 if (listeners != null)
1275 if (commandListeners != null)
1277 commandListeners.clear();
1279 if (view_listeners != null)
1281 view_listeners.clear();
1283 if (pdbFileNameId != null)
1285 pdbFileNameId.clear();
1287 if (pdbIdFileName != null)
1289 pdbIdFileName.clear();
1293 public void addSelectionListener(SelectionListener selecter)
1295 if (!sel_listeners.contains(selecter))
1297 sel_listeners.add(selecter);
1301 public void removeSelectionListener(SelectionListener toremove)
1303 if (sel_listeners.contains(toremove))
1305 sel_listeners.remove(toremove);
1309 public synchronized void sendSelection(
1310 jalview.datamodel.SequenceGroup selection,
1311 jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden,
1312 SelectionSource source)
1314 for (SelectionListener slis : sel_listeners)
1318 slis.selection(selection, colsel, hidden, source);
1323 Vector<AlignmentViewPanelListener> view_listeners = new Vector<>();
1325 public synchronized void sendViewPosition(
1326 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1327 int startSeq, int endSeq)
1330 if (view_listeners != null && view_listeners.size() > 0)
1332 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1334 while (listeners.hasMoreElements())
1336 AlignmentViewPanelListener slis = listeners.nextElement();
1339 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1347 * release all references associated with this manager provider
1349 * @param jalviewLite
1351 public static void release(StructureSelectionManagerProvider jalviewLite)
1353 // synchronized (instances)
1355 if (instances == null)
1359 StructureSelectionManager mnger = (instances.get(jalviewLite));
1362 instances.remove(jalviewLite);
1365 /* bsoares 2019-03-20 finalize deprecated, no apparent external
1366 * resources to close
1368 // mnger.finalize();
1369 } catch (Throwable x)
1376 public void registerPDBEntry(PDBEntry pdbentry)
1378 if (pdbentry.getFile() != null
1379 && pdbentry.getFile().trim().length() > 0)
1381 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1385 public void addCommandListener(CommandListener cl)
1387 if (!commandListeners.contains(cl))
1389 commandListeners.add(cl);
1393 public boolean hasCommandListener(CommandListener cl)
1395 return this.commandListeners.contains(cl);
1398 public boolean removeCommandListener(CommandListener l)
1400 return commandListeners.remove(l);
1404 * Forward a command to any command listeners (except for the command's
1408 * the command to be broadcast (in its form after being performed)
1410 * if true, the command was being 'undone'
1413 public void commandPerformed(CommandI command, boolean undo,
1414 VamsasSource source)
1416 for (CommandListener listener : commandListeners)
1418 listener.mirrorCommand(command, undo, this, source);
1423 * Returns a new CommandI representing the given command as mapped to the
1424 * given sequences. If no mapping could be made, or the command is not of a
1425 * mappable kind, returns null.
1433 public CommandI mapCommand(CommandI command, boolean undo,
1434 final AlignmentI mapTo, char gapChar)
1436 if (command instanceof EditCommand)
1438 return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo,
1439 gapChar, seqmappings);
1441 else if (command instanceof OrderCommand)
1443 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1444 mapTo, seqmappings);
1449 public List<AlignedCodonFrame> getSequenceMappings()