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 isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable();
415 } catch (Exception ex)
417 ex.printStackTrace();
421 * sifts client - non null if SIFTS mappings are to be used
423 SiftsClient siftsClient = null;
428 siftsClient = new SiftsClient(pdb);
430 } catch (SiftsException e)
432 isMapUsingSIFTs = false;
437 String targetChainId;
438 for (int s = 0; s < sequenceArray.length; s++)
440 boolean infChain = true;
441 final SequenceI seq = sequenceArray[s];
443 while (ds.getDatasetSequence() != null)
445 ds = ds.getDatasetSequence();
448 if (targetChainIds != null && targetChainIds[s] != null)
451 targetChainId = targetChainIds[s];
453 else if (seq.getName().indexOf("|") > -1)
455 targetChainId = seq.getName()
456 .substring(seq.getName().lastIndexOf("|") + 1);
457 if (targetChainId.length() > 1)
459 if (targetChainId.trim().length() == 0)
465 // not a valid chain identifier
476 * Attempt pairwise alignment of the sequence with each chain in the PDB,
477 * and remember the highest scoring chain
480 AlignSeq maxAlignseq = null;
481 String maxChainId = " ";
482 PDBChain maxChain = null;
483 boolean first = true;
484 for (PDBChain chain : pdb.getChains())
486 if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
489 continue; // don't try to map chains don't match.
491 // TODO: correctly determine sequence type for mixed na/peptide
493 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
494 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
497 // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
498 // as.calcScoreMatrix();
499 // as.traceAlignment();
501 if (first || as.maxscore > max
502 || (as.maxscore == max && chain.id.equals(targetChainId)))
508 maxChainId = chain.id;
511 if (maxChain == null)
516 if (sourceType == DataSourceType.PASTE)
518 pdbFile = "INLINE" + pdb.getId();
521 List<StructureMapping> seqToStrucMapping = new ArrayList<>();
522 if (isMapUsingSIFTs && seq.isProtein())
524 if (progress!=null) {
525 progress.setProgressBar(MessageManager
526 .getString("status.obtaining_mapping_with_sifts"),
529 jalview.datamodel.Mapping sqmpping = maxAlignseq
530 .getMappingFromS1(false);
531 if (targetChainId != null && !targetChainId.trim().isEmpty())
533 StructureMapping siftsMapping;
536 siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
537 pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
538 seqToStrucMapping.add(siftsMapping);
539 maxChain.makeExactMapping(siftsMapping, seq);
540 maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS");
541 maxChain.transferResidueAnnotation(siftsMapping, null);
542 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
544 } catch (SiftsException e)
546 // fall back to NW alignment
547 Cache.log.error(e.getMessage());
548 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
549 targetChainId, maxChain, pdb, maxAlignseq);
550 seqToStrucMapping.add(nwMapping);
551 maxChain.makeExactMapping(maxAlignseq, seq);
552 maxChain.transferRESNUMFeatures(seq, "IEA:Jalview"); // FIXME: is
555 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
556 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
561 List<StructureMapping> foundSiftsMappings = new ArrayList<>();
562 for (PDBChain chain : pdb.getChains())
564 StructureMapping siftsMapping = null;
567 siftsMapping = getStructureMapping(seq,
568 pdbFile, chain.id, pdb, chain, sqmpping, maxAlignseq,
570 foundSiftsMappings.add(siftsMapping);
571 chain.makeExactMapping(siftsMapping, seq);
572 chain.transferRESNUMFeatures(seq, "IEA: SIFTS");// FIXME: is this
574 chain.transferResidueAnnotation(siftsMapping, null);
575 } catch (SiftsException e)
577 System.err.println(e.getMessage());
583 "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
584 System.err.println(e.getMessage());
587 if (!foundSiftsMappings.isEmpty())
589 seqToStrucMapping.addAll(foundSiftsMappings);
590 ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
594 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
595 maxChainId, maxChain, pdb, maxAlignseq);
596 seqToStrucMapping.add(nwMapping);
597 maxChain.transferRESNUMFeatures(seq, null); // FIXME: is this
599 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
600 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
606 if (progress != null)
608 progress.setProgressBar(MessageManager
609 .getString("status.obtaining_mapping_with_nw_alignment"),
612 StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
613 maxChain, pdb, maxAlignseq);
614 seqToStrucMapping.add(nwMapping);
615 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
617 if (forStructureView)
619 for (StructureMapping sm : seqToStrucMapping)
621 addStructureMapping(sm); // not addAll!
624 if (progress != null)
626 progress.setProgressBar(null, progressSessionId);
633 * check if we need to extract secondary structure from given pdbFile and
634 * transfer to sequences
637 * @param sequenceArray
640 private boolean isStructureFileProcessed(String pdbFile,
641 SequenceI[] sequenceArray)
643 boolean parseSecStr = true;
644 if (isPDBFileRegistered(pdbFile))
646 for (SequenceI sq : sequenceArray)
649 while (ds.getDatasetSequence() != null)
651 ds = ds.getDatasetSequence();
654 if (ds.getAnnotation() != null)
656 for (AlignmentAnnotation ala : ds.getAnnotation())
658 // false if any annotation present from this structure
659 // JBPNote this fails for jmol/chimera view because the *file* is
660 // passed, not the structure data ID -
661 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
672 public void addStructureMapping(StructureMapping sm)
674 if (!mappings.contains(sm))
681 * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
686 * @param targetChainId
692 * client for retrieval of SIFTS mappings for this structure
694 * @throws SiftsException
696 private StructureMapping getStructureMapping(SequenceI seq,
697 String pdbFile, String targetChainId, StructureFile pdb,
698 PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
699 AlignSeq maxAlignseq, SiftsClient siftsClient) throws SiftsException
701 StructureMapping curChainMapping = siftsClient
702 .getSiftsStructureMapping(seq, pdbFile, targetChainId);
705 PDBChain chain = pdb.findChain(targetChainId);
708 chain.transferResidueAnnotation(curChainMapping, null);
710 } catch (Exception e)
714 return curChainMapping;
717 private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
718 String maxChainId, PDBChain maxChain, StructureFile pdb,
719 AlignSeq maxAlignseq)
721 final StringBuilder mappingDetails = new StringBuilder(128);
722 mappingDetails.append(NEWLINE)
723 .append("Sequence \u27f7 Structure mapping details");
724 mappingDetails.append(NEWLINE);
726 .append("Method: inferred with Needleman & Wunsch alignment");
727 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
728 .append(NEWLINE).append("Sequence = ")
729 .append(maxChain.sequence.getSequenceAsString());
730 mappingDetails.append(NEWLINE).append("No of residues = ")
731 .append(maxChain.residues.size()).append(NEWLINE)
733 PrintStream ps = new PrintStream(System.out)
736 public void print(String x)
738 mappingDetails.append(x);
742 public void println()
744 mappingDetails.append(NEWLINE);
748 maxAlignseq.printAlignment(ps);
750 mappingDetails.append(NEWLINE).append("PDB start/end ");
751 mappingDetails.append(String.valueOf(maxAlignseq.seq2start))
753 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
754 mappingDetails.append(NEWLINE).append("SEQ start/end ");
757 .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1)))
759 mappingDetails.append(
760 String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1)));
761 mappingDetails.append(NEWLINE);
762 maxChain.makeExactMapping(maxAlignseq, seq);
763 jalview.datamodel.Mapping sqmpping = maxAlignseq
764 .getMappingFromS1(false);
765 maxChain.transferRESNUMFeatures(seq, null);
767 HashMap<Integer, int[]> mapping = new HashMap<>();
774 Atom tmp = maxChain.atoms.elementAt(index);
775 if ((resNum != tmp.resNumber || insCode != tmp.insCode)
776 && tmp.alignmentMapping != -1)
778 resNum = tmp.resNumber;
779 insCode = tmp.insCode;
780 if (tmp.alignmentMapping >= -1)
782 mapping.put(tmp.alignmentMapping + 1,
784 { tmp.resNumber, tmp.atomIndex });
789 } while (index < maxChain.atoms.size());
791 StructureMapping nwMapping = new StructureMapping(seq, pdbFile,
792 pdb.getId(), maxChainId, mapping, mappingDetails.toString());
793 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
797 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
799 listeners.removeElement(svl);
800 if (svl instanceof SequenceListener)
802 for (int i = 0; i < listeners.size(); i++)
804 if (listeners.elementAt(i) instanceof StructureListener)
806 ((StructureListener) listeners.elementAt(i))
807 .releaseReferences(svl);
812 if (pdbfiles == null)
818 * Remove mappings to the closed listener's PDB files, but first check if
819 * another listener is still interested
821 List<String> pdbs = new ArrayList<>(Arrays.asList(pdbfiles));
823 StructureListener sl;
824 for (int i = 0; i < listeners.size(); i++)
826 if (listeners.elementAt(i) instanceof StructureListener)
828 sl = (StructureListener) listeners.elementAt(i);
829 for (String pdbfile : sl.getStructureFiles())
831 pdbs.remove(pdbfile);
837 * Rebuild the mappings set, retaining only those which are for 'other' PDB
842 List<StructureMapping> tmp = new ArrayList<>();
843 for (StructureMapping sm : mappings)
845 if (!pdbs.contains(sm.pdbfile))
856 * Propagate mouseover of a single position in a structure
863 public String mouseOverStructure(int pdbResNum, String chain,
866 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
867 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
868 return mouseOverStructure(atoms);
872 * Propagate mouseover or selection of multiple positions in a structure
876 public String mouseOverStructure(List<AtomSpec> atoms)
878 if (listeners == null)
880 // old or prematurely sent event
883 boolean hasSequenceListener = false;
884 for (int i = 0; i < listeners.size(); i++)
886 if (listeners.elementAt(i) instanceof SequenceListener)
888 hasSequenceListener = true;
891 if (!hasSequenceListener)
896 SearchResultsI results = findAlignmentPositionsForStructurePositions(
898 String result = null;
899 for (Object li : listeners)
901 if (li instanceof SequenceListener)
903 String s = ((SequenceListener) li).highlightSequence(results);
914 * Constructs a SearchResults object holding regions (if any) in the Jalview
915 * alignment which have a mapping to the structure viewer positions in the
921 public SearchResultsI findAlignmentPositionsForStructurePositions(
922 List<AtomSpec> atoms)
924 SearchResultsI results = new SearchResults();
925 for (AtomSpec atom : atoms)
927 SequenceI lastseq = null;
929 for (StructureMapping sm : mappings)
931 if (sm.pdbfile.equals(atom.getPdbFile())
932 && sm.pdbchain.equals(atom.getChain()))
934 int indexpos = sm.getSeqPos(atom.getPdbResNum());
935 if (lastipos != indexpos || lastseq != sm.sequence)
937 results.addResult(sm.sequence, indexpos, indexpos);
939 lastseq = sm.sequence;
940 // construct highlighted sequence list
941 for (AlignedCodonFrame acf : seqmappings)
943 acf.markMappedRegion(sm.sequence, indexpos, results);
953 * highlight regions associated with a position (indexpos) in seq
956 * the sequence that the mouse over occurred on
958 * the absolute position being mouseovered in seq (0 to seq.length())
960 * the sequence position (if -1, seq.findPosition is called to
961 * resolve the residue number)
963 public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
966 boolean hasSequenceListeners = handlingVamsasMo
967 || !seqmappings.isEmpty();
968 SearchResultsI results = null;
971 seqPos = seq.findPosition(indexpos);
973 for (int i = 0; i < listeners.size(); i++)
975 Object listener = listeners.elementAt(i);
976 if (listener == source)
978 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
979 // Temporary fudge with SequenceListener.getVamsasSource()
982 if (listener instanceof StructureListener)
984 highlightStructure((StructureListener) listener, seq, seqPos);
988 if (listener instanceof SequenceListener)
990 final SequenceListener seqListener = (SequenceListener) listener;
991 if (hasSequenceListeners
992 && seqListener.getVamsasSource() != source)
994 if (relaySeqMappings)
998 results = MappingUtils.buildSearchResults(seq, seqPos,
1001 if (handlingVamsasMo)
1003 results.addResult(seq, seqPos, seqPos);
1006 if (!results.isEmpty())
1008 seqListener.highlightSequence(results);
1013 else if (listener instanceof VamsasListener && !handlingVamsasMo)
1015 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
1018 else if (listener instanceof SecondaryStructureListener)
1020 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
1028 * Send suitable messages to a StructureListener to highlight atoms
1029 * corresponding to the given sequence position(s)
1035 public void highlightStructure(StructureListener sl, SequenceI seq,
1038 if (!sl.isListeningFor(seq))
1043 List<AtomSpec> atoms = new ArrayList<>();
1044 for (StructureMapping sm : mappings)
1046 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1047 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1048 .getDatasetSequence() == seq.getDatasetSequence()))
1050 for (int index : positions)
1052 atomNo = sm.getAtomNum(index);
1056 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1057 sm.getPDBResNum(index), atomNo));
1062 sl.highlightAtoms(atoms);
1066 * true if a mouse over event from an external (ie Vamsas) source is being
1069 boolean handlingVamsasMo = false;
1074 * as mouseOverSequence but only route event to SequenceListeners
1078 * in an alignment sequence
1080 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
1081 VamsasSource source)
1083 handlingVamsasMo = true;
1084 long msg = sequenceI.hashCode() * (1 + position);
1088 mouseOverSequence(sequenceI, position, -1, source);
1090 handlingVamsasMo = false;
1093 public Annotation[] colourSequenceFromStructure(SequenceI seq,
1097 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
1098 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
1100 * Annotation [] annotations = new Annotation[seq.getLength()];
1102 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
1103 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
1104 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
1106 * for (int j = 0; j < mappings.length; j++) {
1108 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
1109 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
1110 * System.out.println(pdbid+" "+mappings[j].getPdbId() +"
1111 * "+mappings[j].pdbfile);
1113 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
1114 * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
1116 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
1117 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
1118 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
1119 * mappings[j].pdbfile); }
1121 * annotations[index] = new Annotation("X",null,' ',0,col); } return
1122 * annotations; } } } }
1124 * return annotations;
1128 public void structureSelectionChanged()
1132 public void sequenceSelectionChanged()
1136 public void sequenceColoursChanged(Object source)
1138 StructureListener sl;
1139 for (int i = 0; i < listeners.size(); i++)
1141 if (listeners.elementAt(i) instanceof StructureListener)
1143 sl = (StructureListener) listeners.elementAt(i);
1144 sl.updateColours(source);
1149 public StructureMapping[] getMapping(String pdbfile)
1151 List<StructureMapping> tmp = new ArrayList<>();
1152 for (StructureMapping sm : mappings)
1154 if (sm.pdbfile.equals(pdbfile))
1159 return tmp.toArray(new StructureMapping[tmp.size()]);
1163 * Returns a readable description of all mappings for the given pdbfile to any
1164 * of the given sequences
1170 public String printMappings(String pdbfile, List<SequenceI> seqs)
1172 if (pdbfile == null || seqs == null || seqs.isEmpty())
1177 StringBuilder sb = new StringBuilder(64);
1178 for (StructureMapping sm : mappings)
1180 if (Platform.pathEquals(sm.pdbfile, pdbfile)
1181 && seqs.contains(sm.sequence))
1183 sb.append(sm.mappingDetails);
1185 // separator makes it easier to read multiple mappings
1186 sb.append("=====================");
1192 return sb.toString();
1196 * Remove the given mapping
1200 public void deregisterMapping(AlignedCodonFrame acf)
1204 boolean removed = seqmappings.remove(acf);
1205 if (removed && seqmappings.isEmpty())
1207 System.out.println("All mappings removed");
1213 * Add each of the given codonFrames to the stored set, if not aready present.
1217 public void registerMappings(List<AlignedCodonFrame> mappings)
1219 if (mappings != null)
1221 for (AlignedCodonFrame acf : mappings)
1223 registerMapping(acf);
1229 * Add the given mapping to the stored set, unless already stored.
1231 public void registerMapping(AlignedCodonFrame acf)
1235 if (!seqmappings.contains(acf))
1237 seqmappings.add(acf);
1243 * Resets this object to its initial state by removing all registered
1244 * listeners, codon mappings, PDB file mappings
1246 public void resetAll()
1248 if (mappings != null)
1252 if (seqmappings != null)
1254 seqmappings.clear();
1256 if (sel_listeners != null)
1258 sel_listeners.clear();
1260 if (listeners != null)
1264 if (commandListeners != null)
1266 commandListeners.clear();
1268 if (view_listeners != null)
1270 view_listeners.clear();
1272 if (pdbFileNameId != null)
1274 pdbFileNameId.clear();
1276 if (pdbIdFileName != null)
1278 pdbIdFileName.clear();
1282 public void addSelectionListener(SelectionListener selecter)
1284 if (!sel_listeners.contains(selecter))
1286 sel_listeners.add(selecter);
1290 public void removeSelectionListener(SelectionListener toremove)
1292 if (sel_listeners.contains(toremove))
1294 sel_listeners.remove(toremove);
1298 public synchronized void sendSelection(
1299 jalview.datamodel.SequenceGroup selection,
1300 jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden,
1301 SelectionSource source)
1303 for (SelectionListener slis : sel_listeners)
1307 slis.selection(selection, colsel, hidden, source);
1312 Vector<AlignmentViewPanelListener> view_listeners = new Vector<>();
1314 public synchronized void sendViewPosition(
1315 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1316 int startSeq, int endSeq)
1319 if (view_listeners != null && view_listeners.size() > 0)
1321 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1323 while (listeners.hasMoreElements())
1325 AlignmentViewPanelListener slis = listeners.nextElement();
1328 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1336 * release all references associated with this manager provider
1338 * @param jalviewLite
1340 public static void release(StructureSelectionManagerProvider jalviewLite)
1342 // synchronized (instances)
1344 if (instances == null)
1348 StructureSelectionManager mnger = (instances.get(jalviewLite));
1351 instances.remove(jalviewLite);
1354 /* bsoares 2019-03-20 finalize deprecated, no apparent external
1355 * resources to close
1357 // mnger.finalize();
1358 } catch (Throwable x)
1365 public void registerPDBEntry(PDBEntry pdbentry)
1367 if (pdbentry.getFile() != null
1368 && pdbentry.getFile().trim().length() > 0)
1370 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1374 public void addCommandListener(CommandListener cl)
1376 if (!commandListeners.contains(cl))
1378 commandListeners.add(cl);
1382 public boolean hasCommandListener(CommandListener cl)
1384 return this.commandListeners.contains(cl);
1387 public boolean removeCommandListener(CommandListener l)
1389 return commandListeners.remove(l);
1393 * Forward a command to any command listeners (except for the command's
1397 * the command to be broadcast (in its form after being performed)
1399 * if true, the command was being 'undone'
1402 public void commandPerformed(CommandI command, boolean undo,
1403 VamsasSource source)
1405 for (CommandListener listener : commandListeners)
1407 listener.mirrorCommand(command, undo, this, source);
1412 * Returns a new CommandI representing the given command as mapped to the
1413 * given sequences. If no mapping could be made, or the command is not of a
1414 * mappable kind, returns null.
1422 public CommandI mapCommand(CommandI command, boolean undo,
1423 final AlignmentI mapTo, char gapChar)
1425 if (command instanceof EditCommand)
1427 return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo,
1428 gapChar, seqmappings);
1430 else if (command instanceof OrderCommand)
1432 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1433 mapTo, seqmappings);
1438 public List<AlignedCodonFrame> getSequenceMappings()