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 isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable() && !pdb.getId().startsWith("AF-");
418 } catch (Exception ex)
420 ex.printStackTrace();
424 * sifts client - non null if SIFTS mappings are to be used
426 SiftsClient siftsClient = null;
431 siftsClient = new SiftsClient(pdb);
433 } catch (SiftsException e)
435 isMapUsingSIFTs = false;
436 Cache.log.error("SIFTS mapping failed", e);
437 Cache.log.error("Falling back on Needleman & Wunsch alignment");
441 String targetChainId;
442 for (int s = 0; s < sequenceArray.length; s++)
444 boolean infChain = true;
445 final SequenceI seq = sequenceArray[s];
447 while (ds.getDatasetSequence() != null)
449 ds = ds.getDatasetSequence();
452 if (targetChainIds != null && targetChainIds[s] != null)
455 targetChainId = targetChainIds[s];
457 else if (seq.getName().indexOf("|") > -1)
459 targetChainId = seq.getName()
460 .substring(seq.getName().lastIndexOf("|") + 1);
461 if (targetChainId.length() > 1)
463 if (targetChainId.trim().length() == 0)
469 // not a valid chain identifier
480 * Attempt pairwise alignment of the sequence with each chain in the PDB,
481 * and remember the highest scoring chain
484 AlignSeq maxAlignseq = null;
485 String maxChainId = " ";
486 PDBChain maxChain = null;
487 boolean first = true;
488 for (PDBChain chain : pdb.getChains())
490 if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
493 continue; // don't try to map chains don't match.
495 // TODO: correctly determine sequence type for mixed na/peptide
497 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
498 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
501 // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
502 // as.calcScoreMatrix();
503 // as.traceAlignment();
505 if (first || as.maxscore > max
506 || (as.maxscore == max && chain.id.equals(targetChainId)))
512 maxChainId = chain.id;
515 if (maxChain == null)
520 if (sourceType == DataSourceType.PASTE)
522 pdbFile = "INLINE" + pdb.getId();
525 List<StructureMapping> seqToStrucMapping = new ArrayList<>();
526 if (isMapUsingSIFTs && seq.isProtein())
528 if (progress!=null) {
529 progress.setProgressBar(MessageManager
530 .getString("status.obtaining_mapping_with_sifts"),
533 jalview.datamodel.Mapping sqmpping = maxAlignseq
534 .getMappingFromS1(false);
535 if (targetChainId != null && !targetChainId.trim().isEmpty())
537 StructureMapping siftsMapping;
540 siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
541 pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
542 seqToStrucMapping.add(siftsMapping);
543 maxChain.makeExactMapping(siftsMapping, seq);
544 maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS");
545 maxChain.transferResidueAnnotation(siftsMapping, null);
546 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
548 } catch (SiftsException e)
550 // fall back to NW alignment
551 Cache.log.error(e.getMessage());
552 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
553 targetChainId, maxChain, pdb, maxAlignseq);
554 seqToStrucMapping.add(nwMapping);
555 maxChain.makeExactMapping(maxAlignseq, seq);
556 maxChain.transferRESNUMFeatures(seq, "IEA:Jalview"); // FIXME: is
559 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
560 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
565 List<StructureMapping> foundSiftsMappings = new ArrayList<>();
566 for (PDBChain chain : pdb.getChains())
568 StructureMapping siftsMapping = null;
571 siftsMapping = getStructureMapping(seq,
572 pdbFile, chain.id, pdb, chain, sqmpping, maxAlignseq,
574 foundSiftsMappings.add(siftsMapping);
575 chain.makeExactMapping(siftsMapping, seq);
576 chain.transferRESNUMFeatures(seq, "IEA: SIFTS");// FIXME: is this
578 chain.transferResidueAnnotation(siftsMapping, null);
579 } catch (SiftsException e)
581 System.err.println(e.getMessage());
587 "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
588 System.err.println(e.getMessage());
591 if (!foundSiftsMappings.isEmpty())
593 seqToStrucMapping.addAll(foundSiftsMappings);
594 ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
598 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
599 maxChainId, maxChain, pdb, maxAlignseq);
600 seqToStrucMapping.add(nwMapping);
601 maxChain.transferRESNUMFeatures(seq, null); // FIXME: is this
603 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
604 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
610 if (progress != null)
612 progress.setProgressBar(MessageManager
613 .getString("status.obtaining_mapping_with_nw_alignment"),
616 StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
617 maxChain, pdb, maxAlignseq);
618 seqToStrucMapping.add(nwMapping);
619 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
621 if (forStructureView)
623 for (StructureMapping sm : seqToStrucMapping)
625 addStructureMapping(sm); // not addAll!
628 if (progress != null)
630 progress.setProgressBar(null, progressSessionId);
637 * check if we need to extract secondary structure from given pdbFile and
638 * transfer to sequences
641 * @param sequenceArray
644 private boolean isStructureFileProcessed(String pdbFile,
645 SequenceI[] sequenceArray)
647 boolean parseSecStr = true;
648 if (isPDBFileRegistered(pdbFile))
650 for (SequenceI sq : sequenceArray)
653 while (ds.getDatasetSequence() != null)
655 ds = ds.getDatasetSequence();
658 if (ds.getAnnotation() != null)
660 for (AlignmentAnnotation ala : ds.getAnnotation())
662 // false if any annotation present from this structure
663 // JBPNote this fails for jmol/chimera view because the *file* is
664 // passed, not the structure data ID -
665 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
676 public void addStructureMapping(StructureMapping sm)
678 if (!mappings.contains(sm))
685 * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
690 * @param targetChainId
696 * client for retrieval of SIFTS mappings for this structure
698 * @throws SiftsException
700 private StructureMapping getStructureMapping(SequenceI seq,
701 String pdbFile, String targetChainId, StructureFile pdb,
702 PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
703 AlignSeq maxAlignseq, SiftsClient siftsClient) throws SiftsException
705 StructureMapping curChainMapping = siftsClient
706 .getSiftsStructureMapping(seq, pdbFile, targetChainId);
709 PDBChain chain = pdb.findChain(targetChainId);
712 chain.transferResidueAnnotation(curChainMapping, null);
714 } catch (Exception e)
718 return curChainMapping;
721 private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
722 String maxChainId, PDBChain maxChain, StructureFile pdb,
723 AlignSeq maxAlignseq)
725 final StringBuilder mappingDetails = new StringBuilder(128);
726 mappingDetails.append(NEWLINE)
727 .append("Sequence \u27f7 Structure mapping details");
728 mappingDetails.append(NEWLINE);
730 .append("Method: inferred with Needleman & Wunsch alignment");
731 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
732 .append(NEWLINE).append("Sequence = ")
733 .append(maxChain.sequence.getSequenceAsString());
734 mappingDetails.append(NEWLINE).append("No of residues = ")
735 .append(maxChain.residues.size()).append(NEWLINE)
737 PrintStream ps = new PrintStream(System.out)
740 public void print(String x)
742 mappingDetails.append(x);
746 public void println()
748 mappingDetails.append(NEWLINE);
752 maxAlignseq.printAlignment(ps);
754 mappingDetails.append(NEWLINE).append("PDB start/end ");
755 mappingDetails.append(String.valueOf(maxAlignseq.seq2start))
757 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
758 mappingDetails.append(NEWLINE).append("SEQ start/end ");
761 .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1)))
763 mappingDetails.append(
764 String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1)));
765 mappingDetails.append(NEWLINE);
766 maxChain.makeExactMapping(maxAlignseq, seq);
767 jalview.datamodel.Mapping sqmpping = maxAlignseq
768 .getMappingFromS1(false);
769 maxChain.transferRESNUMFeatures(seq, null);
771 HashMap<Integer, int[]> mapping = new HashMap<>();
778 Atom tmp = maxChain.atoms.elementAt(index);
779 if ((resNum != tmp.resNumber || insCode != tmp.insCode)
780 && tmp.alignmentMapping != -1)
782 resNum = tmp.resNumber;
783 insCode = tmp.insCode;
784 if (tmp.alignmentMapping >= -1)
786 mapping.put(tmp.alignmentMapping + 1,
788 { tmp.resNumber, tmp.atomIndex });
793 } while (index < maxChain.atoms.size());
795 StructureMapping nwMapping = new StructureMapping(seq, pdbFile,
796 pdb.getId(), maxChainId, mapping, mappingDetails.toString());
797 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
801 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
803 listeners.removeElement(svl);
804 if (svl instanceof SequenceListener)
806 for (int i = 0; i < listeners.size(); i++)
808 if (listeners.elementAt(i) instanceof StructureListener)
810 ((StructureListener) listeners.elementAt(i))
811 .releaseReferences(svl);
816 if (pdbfiles == null)
822 * Remove mappings to the closed listener's PDB files, but first check if
823 * another listener is still interested
825 List<String> pdbs = new ArrayList<>(Arrays.asList(pdbfiles));
827 StructureListener sl;
828 for (int i = 0; i < listeners.size(); i++)
830 if (listeners.elementAt(i) instanceof StructureListener)
832 sl = (StructureListener) listeners.elementAt(i);
833 for (String pdbfile : sl.getStructureFiles())
835 pdbs.remove(pdbfile);
841 * Rebuild the mappings set, retaining only those which are for 'other' PDB
846 List<StructureMapping> tmp = new ArrayList<>();
847 for (StructureMapping sm : mappings)
849 if (!pdbs.contains(sm.pdbfile))
860 * Propagate mouseover of a single position in a structure
867 public String mouseOverStructure(int pdbResNum, String chain,
870 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
871 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
872 return mouseOverStructure(atoms);
876 * Propagate mouseover or selection of multiple positions in a structure
880 public String mouseOverStructure(List<AtomSpec> atoms)
882 if (listeners == null)
884 // old or prematurely sent event
887 boolean hasSequenceListener = false;
888 for (int i = 0; i < listeners.size(); i++)
890 if (listeners.elementAt(i) instanceof SequenceListener)
892 hasSequenceListener = true;
895 if (!hasSequenceListener)
900 SearchResultsI results = findAlignmentPositionsForStructurePositions(
902 String result = null;
903 for (Object li : listeners)
905 if (li instanceof SequenceListener)
907 String s = ((SequenceListener) li).highlightSequence(results);
918 * Constructs a SearchResults object holding regions (if any) in the Jalview
919 * alignment which have a mapping to the structure viewer positions in the
925 public SearchResultsI findAlignmentPositionsForStructurePositions(
926 List<AtomSpec> atoms)
928 SearchResultsI results = new SearchResults();
929 for (AtomSpec atom : atoms)
931 SequenceI lastseq = null;
933 for (StructureMapping sm : mappings)
935 if (sm.pdbfile.equals(atom.getPdbFile())
936 && sm.pdbchain.equals(atom.getChain()))
938 int indexpos = sm.getSeqPos(atom.getPdbResNum());
939 if (lastipos != indexpos || lastseq != sm.sequence)
941 results.addResult(sm.sequence, indexpos, indexpos);
943 lastseq = sm.sequence;
944 // construct highlighted sequence list
945 for (AlignedCodonFrame acf : seqmappings)
947 acf.markMappedRegion(sm.sequence, indexpos, results);
957 * highlight regions associated with a position (indexpos) in seq
960 * the sequence that the mouse over occurred on
962 * the absolute position being mouseovered in seq (0 to seq.length())
964 * the sequence position (if -1, seq.findPosition is called to
965 * resolve the residue number)
967 public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
970 boolean hasSequenceListeners = handlingVamsasMo
971 || !seqmappings.isEmpty();
972 SearchResultsI results = null;
975 seqPos = seq.findPosition(indexpos);
977 for (int i = 0; i < listeners.size(); i++)
979 Object listener = listeners.elementAt(i);
980 if (listener == source)
982 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
983 // Temporary fudge with SequenceListener.getVamsasSource()
986 if (listener instanceof StructureListener)
988 highlightStructure((StructureListener) listener, seq, seqPos);
992 if (listener instanceof SequenceListener)
994 final SequenceListener seqListener = (SequenceListener) listener;
995 if (hasSequenceListeners
996 && seqListener.getVamsasSource() != source)
998 if (relaySeqMappings)
1000 if (results == null)
1002 results = MappingUtils.buildSearchResults(seq, seqPos,
1005 if (handlingVamsasMo)
1007 results.addResult(seq, seqPos, seqPos);
1010 if (!results.isEmpty())
1012 seqListener.highlightSequence(results);
1017 else if (listener instanceof VamsasListener && !handlingVamsasMo)
1019 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
1022 else if (listener instanceof SecondaryStructureListener)
1024 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
1032 * Send suitable messages to a StructureListener to highlight atoms
1033 * corresponding to the given sequence position(s)
1039 public void highlightStructure(StructureListener sl, SequenceI seq,
1042 if (!sl.isListeningFor(seq))
1047 List<AtomSpec> atoms = new ArrayList<>();
1048 for (StructureMapping sm : mappings)
1050 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1051 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1052 .getDatasetSequence() == seq.getDatasetSequence()))
1054 for (int index : positions)
1056 atomNo = sm.getAtomNum(index);
1060 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1061 sm.getPDBResNum(index), atomNo));
1066 sl.highlightAtoms(atoms);
1070 * true if a mouse over event from an external (ie Vamsas) source is being
1073 boolean handlingVamsasMo = false;
1078 * as mouseOverSequence but only route event to SequenceListeners
1082 * in an alignment sequence
1084 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
1085 VamsasSource source)
1087 handlingVamsasMo = true;
1088 long msg = sequenceI.hashCode() * (1 + position);
1092 mouseOverSequence(sequenceI, position, -1, source);
1094 handlingVamsasMo = false;
1097 public Annotation[] colourSequenceFromStructure(SequenceI seq,
1101 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
1102 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
1104 * Annotation [] annotations = new Annotation[seq.getLength()];
1106 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
1107 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
1108 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
1110 * for (int j = 0; j < mappings.length; j++) {
1112 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
1113 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
1114 * System.out.println(pdbid+" "+mappings[j].getPdbId() +"
1115 * "+mappings[j].pdbfile);
1117 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
1118 * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
1120 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
1121 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
1122 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
1123 * mappings[j].pdbfile); }
1125 * annotations[index] = new Annotation("X",null,' ',0,col); } return
1126 * annotations; } } } }
1128 * return annotations;
1132 public void structureSelectionChanged()
1136 public void sequenceSelectionChanged()
1140 public void sequenceColoursChanged(Object source)
1142 StructureListener sl;
1143 for (int i = 0; i < listeners.size(); i++)
1145 if (listeners.elementAt(i) instanceof StructureListener)
1147 sl = (StructureListener) listeners.elementAt(i);
1148 sl.updateColours(source);
1153 public StructureMapping[] getMapping(String pdbfile)
1155 List<StructureMapping> tmp = new ArrayList<>();
1156 for (StructureMapping sm : mappings)
1158 if (sm.pdbfile.equals(pdbfile))
1163 return tmp.toArray(new StructureMapping[tmp.size()]);
1167 * Returns a readable description of all mappings for the given pdbfile to any
1168 * of the given sequences
1174 public String printMappings(String pdbfile, List<SequenceI> seqs)
1176 if (pdbfile == null || seqs == null || seqs.isEmpty())
1181 StringBuilder sb = new StringBuilder(64);
1182 for (StructureMapping sm : mappings)
1184 if (Platform.pathEquals(sm.pdbfile, pdbfile)
1185 && seqs.contains(sm.sequence))
1187 sb.append(sm.mappingDetails);
1189 // separator makes it easier to read multiple mappings
1190 sb.append("=====================");
1196 return sb.toString();
1200 * Remove the given mapping
1204 public void deregisterMapping(AlignedCodonFrame acf)
1208 boolean removed = seqmappings.remove(acf);
1209 if (removed && seqmappings.isEmpty())
1211 System.out.println("All mappings removed");
1217 * Add each of the given codonFrames to the stored set, if not aready present.
1221 public void registerMappings(List<AlignedCodonFrame> mappings)
1223 if (mappings != null)
1225 for (AlignedCodonFrame acf : mappings)
1227 registerMapping(acf);
1233 * Add the given mapping to the stored set, unless already stored.
1235 public void registerMapping(AlignedCodonFrame acf)
1239 if (!seqmappings.contains(acf))
1241 seqmappings.add(acf);
1247 * Resets this object to its initial state by removing all registered
1248 * listeners, codon mappings, PDB file mappings
1250 public void resetAll()
1252 if (mappings != null)
1256 if (seqmappings != null)
1258 seqmappings.clear();
1260 if (sel_listeners != null)
1262 sel_listeners.clear();
1264 if (listeners != null)
1268 if (commandListeners != null)
1270 commandListeners.clear();
1272 if (view_listeners != null)
1274 view_listeners.clear();
1276 if (pdbFileNameId != null)
1278 pdbFileNameId.clear();
1280 if (pdbIdFileName != null)
1282 pdbIdFileName.clear();
1286 public void addSelectionListener(SelectionListener selecter)
1288 if (!sel_listeners.contains(selecter))
1290 sel_listeners.add(selecter);
1294 public void removeSelectionListener(SelectionListener toremove)
1296 if (sel_listeners.contains(toremove))
1298 sel_listeners.remove(toremove);
1302 public synchronized void sendSelection(
1303 jalview.datamodel.SequenceGroup selection,
1304 jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden,
1305 SelectionSource source)
1307 for (SelectionListener slis : sel_listeners)
1311 slis.selection(selection, colsel, hidden, source);
1316 Vector<AlignmentViewPanelListener> view_listeners = new Vector<>();
1318 public synchronized void sendViewPosition(
1319 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1320 int startSeq, int endSeq)
1323 if (view_listeners != null && view_listeners.size() > 0)
1325 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1327 while (listeners.hasMoreElements())
1329 AlignmentViewPanelListener slis = listeners.nextElement();
1332 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1340 * release all references associated with this manager provider
1342 * @param jalviewLite
1344 public static void release(StructureSelectionManagerProvider jalviewLite)
1346 // synchronized (instances)
1348 if (instances == null)
1352 StructureSelectionManager mnger = (instances.get(jalviewLite));
1355 instances.remove(jalviewLite);
1358 /* bsoares 2019-03-20 finalize deprecated, no apparent external
1359 * resources to close
1361 // mnger.finalize();
1362 } catch (Throwable x)
1369 public void registerPDBEntry(PDBEntry pdbentry)
1371 if (pdbentry.getFile() != null
1372 && pdbentry.getFile().trim().length() > 0)
1374 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1378 public void addCommandListener(CommandListener cl)
1380 if (!commandListeners.contains(cl))
1382 commandListeners.add(cl);
1386 public boolean hasCommandListener(CommandListener cl)
1388 return this.commandListeners.contains(cl);
1391 public boolean removeCommandListener(CommandListener l)
1393 return commandListeners.remove(l);
1397 * Forward a command to any command listeners (except for the command's
1401 * the command to be broadcast (in its form after being performed)
1403 * if true, the command was being 'undone'
1406 public void commandPerformed(CommandI command, boolean undo,
1407 VamsasSource source)
1409 for (CommandListener listener : commandListeners)
1411 listener.mirrorCommand(command, undo, this, source);
1416 * Returns a new CommandI representing the given command as mapped to the
1417 * given sequences. If no mapping could be made, or the command is not of a
1418 * mappable kind, returns null.
1426 public CommandI mapCommand(CommandI command, boolean undo,
1427 final AlignmentI mapTo, char gapChar)
1429 if (command instanceof EditCommand)
1431 return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo,
1432 gapChar, seqmappings);
1434 else if (command instanceof OrderCommand)
1436 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1437 mapTo, seqmappings);
1442 public List<AlignedCodonFrame> getSequenceMappings()