2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.structure;
23 import java.io.PrintStream;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.Enumeration;
28 import java.util.HashMap;
29 import java.util.IdentityHashMap;
30 import java.util.List;
31 import java.util.Locale;
33 import java.util.Vector;
35 import jalview.analysis.AlignSeq;
36 import jalview.api.StructureSelectionManagerProvider;
37 import jalview.bin.Console;
38 import jalview.commands.CommandI;
39 import jalview.commands.EditCommand;
40 import jalview.commands.OrderCommand;
41 import jalview.datamodel.AlignedCodonFrame;
42 import jalview.datamodel.AlignmentAnnotation;
43 import jalview.datamodel.AlignmentI;
44 import jalview.datamodel.Annotation;
45 import jalview.datamodel.HiddenColumns;
46 import jalview.datamodel.PDBEntry;
47 import jalview.datamodel.SearchResultMatchI;
48 import jalview.datamodel.SearchResults;
49 import jalview.datamodel.SearchResultsI;
50 import jalview.datamodel.SequenceI;
51 import jalview.ext.jmol.JmolParser;
52 import jalview.gui.IProgressIndicator;
53 import jalview.io.AppletFormatAdapter;
54 import jalview.io.DataSourceType;
55 import jalview.io.StructureFile;
56 import jalview.util.MappingUtils;
57 import jalview.util.MessageManager;
58 import jalview.util.Platform;
59 import jalview.ws.sifts.SiftsClient;
60 import jalview.ws.sifts.SiftsException;
61 import jalview.ws.sifts.SiftsSettings;
63 import mc_view.PDBChain;
64 import mc_view.PDBfile;
66 public class StructureSelectionManager
68 public final static String NEWLINE = System.lineSeparator();
70 static IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> instances;
72 private List<StructureMapping> mappings = new ArrayList<>();
74 private boolean processSecondaryStructure = false;
76 private boolean secStructServices = false;
78 private boolean addTempFacAnnot = false;
81 * Set of any registered mappings between (dataset) sequences.
83 private List<AlignedCodonFrame> seqmappings = new ArrayList<>();
85 private List<CommandListener> commandListeners = new ArrayList<>();
87 private List<SelectionListener> sel_listeners = new ArrayList<>();
90 * @return true if will try to use external services for processing secondary
93 public boolean isSecStructServices()
95 return secStructServices;
99 * control use of external services for processing secondary structure
101 * @param secStructServices
103 public void setSecStructServices(boolean secStructServices)
105 this.secStructServices = secStructServices;
109 * flag controlling addition of any kind of structural annotation
111 * @return true if temperature factor annotation will be added
113 public boolean isAddTempFacAnnot()
115 return addTempFacAnnot;
119 * set flag controlling addition of structural annotation
121 * @param addTempFacAnnot
123 public void setAddTempFacAnnot(boolean addTempFacAnnot)
125 this.addTempFacAnnot = addTempFacAnnot;
130 * @return if true, the structure manager will attempt to add secondary
131 * structure lines for unannotated sequences
134 public boolean isProcessSecondaryStructure()
136 return processSecondaryStructure;
140 * Control whether structure manager will try to annotate mapped sequences
141 * with secondary structure from PDB data.
145 public void setProcessSecondaryStructure(boolean enable)
147 processSecondaryStructure = enable;
151 * debug function - write all mappings to stdout
153 public void reportMapping()
155 if (mappings.isEmpty())
157 System.err.println("reportMapping: No PDB/Sequence mappings.");
162 "reportMapping: There are " + mappings.size() + " mappings.");
164 for (StructureMapping sm : mappings)
166 System.err.println("mapping " + i++ + " : " + sm.pdbfile);
172 * map between the PDB IDs (or structure identifiers) used by Jalview and the
173 * absolute filenames for PDB data that corresponds to it
175 Map<String, String> pdbIdFileName = new HashMap<>();
177 Map<String, String> pdbFileNameId = new HashMap<>();
179 public void registerPDBFile(String idForFile, String absoluteFile)
181 pdbIdFileName.put(idForFile, absoluteFile);
182 pdbFileNameId.put(absoluteFile, idForFile);
185 public String findIdForPDBFile(String idOrFile)
187 String id = pdbFileNameId.get(idOrFile);
191 public String findFileForPDBId(String idOrFile)
193 String id = pdbIdFileName.get(idOrFile);
197 public boolean isPDBFileRegistered(String idOrFile)
199 return pdbFileNameId.containsKey(idOrFile)
200 || pdbIdFileName.containsKey(idOrFile);
203 private static StructureSelectionManager nullProvider = null;
205 public static StructureSelectionManager getStructureSelectionManager(
206 StructureSelectionManagerProvider context)
210 if (nullProvider == null)
212 if (instances != null)
214 throw new Error(MessageManager.getString(
215 "error.implementation_error_structure_selection_manager_null"),
216 new NullPointerException(MessageManager
217 .getString("exception.ssm_context_is_null")));
221 nullProvider = new StructureSelectionManager();
226 if (instances == null)
228 instances = new java.util.IdentityHashMap<>();
230 StructureSelectionManager instance = instances.get(context);
231 if (instance == null)
233 if (nullProvider != null)
235 instance = nullProvider;
239 instance = new StructureSelectionManager();
241 instances.put(context, instance);
247 * flag controlling whether SeqMappings are relayed from received sequence
248 * mouse over events to other sequences
250 boolean relaySeqMappings = true;
253 * Enable or disable relay of seqMapping events to other sequences. You might
254 * want to do this if there are many sequence mappings and the host computer
259 public void setRelaySeqMappings(boolean relay)
261 relaySeqMappings = relay;
265 * get the state of the relay seqMappings flag.
267 * @return true if sequence mouse overs are being relayed to other mapped
270 public boolean isRelaySeqMappingsEnabled()
272 return relaySeqMappings;
275 Vector listeners = new Vector();
278 * register a listener for alignment sequence mouseover events
282 public void addStructureViewerListener(Object svl)
284 if (!listeners.contains(svl))
286 listeners.addElement(svl);
291 * Returns the filename the PDB id is already mapped to if known, or null if
297 public String alreadyMappedToFile(String pdbid)
299 for (StructureMapping sm : mappings)
301 if (sm.getPdbId().equalsIgnoreCase(pdbid))
310 * Import structure data and register a structure mapping for broadcasting
311 * colouring, mouseovers and selection events (convenience wrapper).
314 * - one or more sequences to be mapped to pdbFile
315 * @param targetChains
316 * - optional chain specification for mapping each sequence to pdb
317 * (may be nill, individual elements may be nill)
319 * - structure data resource
321 * - how to resolve data from resource
322 * @return null or the structure data parsed as a pdb file
324 synchronized public StructureFile setMapping(SequenceI[] sequence,
325 String[] targetChains, String pdbFile, DataSourceType protocol,
326 IProgressIndicator progress)
328 return computeMapping(true, sequence, targetChains, pdbFile, protocol,
333 * Import a single structure file and register sequence structure mappings for
334 * broadcasting colouring, mouseovers and selection events (convenience
337 * @param forStructureView
338 * when true, record the mapping for use in mouseOvers
340 * - one or more sequences to be mapped to pdbFile
341 * @param targetChains
342 * - optional chain specification for mapping each sequence to pdb
343 * (may be nill, individual elements may be nill)
345 * - structure data resource
347 * - how to resolve data from resource
348 * @return null or the structure data parsed as a pdb file
350 synchronized public StructureFile setMapping(boolean forStructureView,
351 SequenceI[] sequenceArray, String[] targetChainIds,
352 String pdbFile, DataSourceType sourceType)
354 return computeMapping(forStructureView, sequenceArray, targetChainIds,
355 pdbFile, sourceType, null);
359 * create sequence structure mappings between each sequence and the given
360 * pdbFile (retrieved via the given protocol). Either constructs a mapping
361 * using NW alignment or derives one from any available SIFTS mapping data.
363 * @param forStructureView
364 * when true, record the mapping for use in mouseOvers
366 * @param sequenceArray
367 * - one or more sequences to be mapped to pdbFile
368 * @param targetChainIds
369 * - optional chain specification for mapping each sequence to pdb
370 * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
371 * - this should be List<List<String>>, empty lists indicate no
372 * predefined mappings
374 * - structure data resource
376 * - how to resolve data from resource
377 * @param IProgressIndicator
378 * reference to UI component that maintains a progress bar for the
380 * @return null or the structure data parsed as a pdb file
382 synchronized public StructureFile computeMapping(boolean forStructureView,
383 SequenceI[] sequenceArray, String[] targetChainIds,
384 String pdbFile, DataSourceType sourceType,
385 IProgressIndicator progress)
387 long progressSessionId = System.currentTimeMillis() * 3;
390 * do we extract and transfer annotation from 3D data ?
392 // FIXME: possibly should just delete
394 boolean parseSecStr = processSecondaryStructure
395 ? isStructureFileProcessed(pdbFile, sequenceArray)
398 StructureFile pdb = null;
399 boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
402 // FIXME if sourceType is not null, we've lost data here
403 sourceType = AppletFormatAdapter.checkProtocol(pdbFile);
404 pdb = new JmolParser(false, pdbFile, sourceType);
405 pdb.addSettings(parseSecStr && processSecondaryStructure,
406 parseSecStr && addTempFacAnnot,
407 parseSecStr && secStructServices);
409 if (pdb.getId() != null && pdb.getId().trim().length() > 0
410 && DataSourceType.FILE == sourceType)
412 registerPDBFile(pdb.getId().trim(), pdbFile);
414 // if PDBId is unavailable then skip SIFTS mapping execution path
415 // TODO: JAL-3868 need to know if structure is actually from
416 // PDB (has valid PDB ID and has provenance suggesting it
417 // actually came from PDB)
418 boolean isProtein = false;
419 for (SequenceI s : sequenceArray)
427 isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable()
428 && !pdb.getId().startsWith("AF-") && isProtein;
430 } catch (Exception ex)
432 ex.printStackTrace();
436 * sifts client - non null if SIFTS mappings are to be used
438 SiftsClient siftsClient = null;
443 siftsClient = new SiftsClient(pdb);
445 } catch (SiftsException e)
447 isMapUsingSIFTs = false;
448 Console.error("SIFTS mapping failed", e);
449 Console.error("Falling back on Needleman & Wunsch alignment");
453 String targetChainId;
454 for (int s = 0; s < sequenceArray.length; s++)
456 boolean infChain = true;
457 final SequenceI seq = sequenceArray[s];
459 while (ds.getDatasetSequence() != null)
461 ds = ds.getDatasetSequence();
464 if (targetChainIds != null && targetChainIds[s] != null)
467 targetChainId = targetChainIds[s];
469 else if (seq.getName().indexOf("|") > -1)
471 targetChainId = seq.getName()
472 .substring(seq.getName().lastIndexOf("|") + 1);
473 if (targetChainId.length() > 1)
475 if (targetChainId.trim().length() == 0)
481 // not a valid chain identifier
492 * Attempt pairwise alignment of the sequence with each chain in the PDB,
493 * and remember the highest scoring chain
496 AlignSeq maxAlignseq = null;
497 String maxChainId = " ";
498 PDBChain maxChain = null;
499 boolean first = true;
500 for (PDBChain chain : pdb.getChains())
502 if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
505 continue; // don't try to map chains don't match.
507 // TODO: correctly determine sequence type for mixed na/peptide
509 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
510 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
513 // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
514 // as.calcScoreMatrix();
515 // as.traceAlignment();
517 if (first || as.maxscore > max
518 || (as.maxscore == max && chain.id.equals(targetChainId)))
524 maxChainId = chain.id;
527 if (maxChain == null)
532 if (sourceType == DataSourceType.PASTE)
534 pdbFile = "INLINE" + pdb.getId();
537 List<StructureMapping> seqToStrucMapping = new ArrayList<>();
538 if (isMapUsingSIFTs && seq.isProtein())
540 if (progress != null)
542 progress.setProgressBar(
544 .getString("status.obtaining_mapping_with_sifts"),
547 jalview.datamodel.Mapping sqmpping = maxAlignseq
548 .getMappingFromS1(false);
549 if (targetChainId != null && !targetChainId.trim().isEmpty())
551 StructureMapping siftsMapping;
554 siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
555 pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
556 seqToStrucMapping.add(siftsMapping);
557 maxChain.makeExactMapping(siftsMapping, seq);
558 maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS",
559 pdb.getId().toLowerCase(Locale.ROOT));
560 maxChain.transferResidueAnnotation(siftsMapping, null);
561 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
563 } catch (SiftsException e)
565 // fall back to NW alignment
566 Console.error(e.getMessage());
567 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
568 targetChainId, maxChain, pdb, maxAlignseq);
569 seqToStrucMapping.add(nwMapping);
570 maxChain.makeExactMapping(maxAlignseq, seq);
571 maxChain.transferRESNUMFeatures(seq, "IEA:Jalview",
572 pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is
575 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
576 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
581 List<StructureMapping> foundSiftsMappings = new ArrayList<>();
582 for (PDBChain chain : pdb.getChains())
584 StructureMapping siftsMapping = null;
587 siftsMapping = getStructureMapping(seq, pdbFile, chain.id,
588 pdb, chain, sqmpping, maxAlignseq, siftsClient);
589 foundSiftsMappings.add(siftsMapping);
590 chain.makeExactMapping(siftsMapping, seq);
591 chain.transferRESNUMFeatures(seq, "IEA: SIFTS",
592 pdb.getId().toLowerCase(Locale.ROOT));// FIXME: is this
594 chain.transferResidueAnnotation(siftsMapping, null);
595 } catch (SiftsException e)
597 System.err.println(e.getMessage());
598 } catch (Exception e)
601 "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
602 System.err.println(e.getMessage());
605 if (!foundSiftsMappings.isEmpty())
607 seqToStrucMapping.addAll(foundSiftsMappings);
608 ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
612 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
613 maxChainId, maxChain, pdb, maxAlignseq);
614 seqToStrucMapping.add(nwMapping);
615 maxChain.transferRESNUMFeatures(seq, null,
616 pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is this
618 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
619 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
625 if (progress != null)
627 progress.setProgressBar(
628 MessageManager.getString(
629 "status.obtaining_mapping_with_nw_alignment"),
632 StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
633 maxChain, pdb, maxAlignseq);
634 seqToStrucMapping.add(nwMapping);
635 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
637 if (forStructureView)
639 for (StructureMapping sm : seqToStrucMapping)
641 addStructureMapping(sm); // not addAll!
644 if (progress != null)
646 progress.setProgressBar(null, progressSessionId);
653 * check if we need to extract secondary structure from given pdbFile and
654 * transfer to sequences
657 * @param sequenceArray
660 private boolean isStructureFileProcessed(String pdbFile,
661 SequenceI[] sequenceArray)
663 boolean parseSecStr = true;
664 if (isPDBFileRegistered(pdbFile))
666 for (SequenceI sq : sequenceArray)
669 while (ds.getDatasetSequence() != null)
671 ds = ds.getDatasetSequence();
674 if (ds.getAnnotation() != null)
676 for (AlignmentAnnotation ala : ds.getAnnotation())
678 // false if any annotation present from this structure
679 // JBPNote this fails for jmol/chimera view because the *file* is
680 // passed, not the structure data ID -
681 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
692 public void addStructureMapping(StructureMapping sm)
694 if (!mappings.contains(sm))
701 * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
706 * @param targetChainId
712 * client for retrieval of SIFTS mappings for this structure
714 * @throws SiftsException
716 private StructureMapping getStructureMapping(SequenceI seq,
717 String pdbFile, String targetChainId, StructureFile pdb,
718 PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
719 AlignSeq maxAlignseq, SiftsClient siftsClient)
720 throws SiftsException
722 StructureMapping curChainMapping = siftsClient
723 .getSiftsStructureMapping(seq, pdbFile, targetChainId);
726 PDBChain chain = pdb.findChain(targetChainId);
729 chain.transferResidueAnnotation(curChainMapping, null);
731 } catch (Exception e)
735 return curChainMapping;
738 private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
739 String maxChainId, PDBChain maxChain, StructureFile pdb,
740 AlignSeq maxAlignseq)
742 final StringBuilder mappingDetails = new StringBuilder(128);
743 mappingDetails.append(NEWLINE)
744 .append("Sequence \u27f7 Structure mapping details");
745 mappingDetails.append(NEWLINE);
747 .append("Method: inferred with Needleman & Wunsch alignment");
748 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
749 .append(NEWLINE).append("Sequence = ")
750 .append(maxChain.sequence.getSequenceAsString());
751 mappingDetails.append(NEWLINE).append("No of residues = ")
752 .append(maxChain.residues.size()).append(NEWLINE)
754 PrintStream ps = new PrintStream(System.out)
757 public void print(String x)
759 mappingDetails.append(x);
763 public void println()
765 mappingDetails.append(NEWLINE);
769 maxAlignseq.printAlignment(ps);
771 mappingDetails.append(NEWLINE).append("PDB start/end ");
772 mappingDetails.append(String.valueOf(maxAlignseq.seq2start))
774 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
775 mappingDetails.append(NEWLINE).append("SEQ start/end ");
778 .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1)))
780 mappingDetails.append(
781 String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1)));
782 mappingDetails.append(NEWLINE);
783 maxChain.makeExactMapping(maxAlignseq, seq);
784 jalview.datamodel.Mapping sqmpping = maxAlignseq
785 .getMappingFromS1(false);
786 maxChain.transferRESNUMFeatures(seq, null,
787 pdb.getId().toLowerCase(Locale.ROOT));
789 HashMap<Integer, int[]> mapping = new HashMap<>();
796 Atom tmp = maxChain.atoms.elementAt(index);
797 if ((resNum != tmp.resNumber || insCode != tmp.insCode)
798 && tmp.alignmentMapping != -1)
800 resNum = tmp.resNumber;
801 insCode = tmp.insCode;
802 if (tmp.alignmentMapping >= -1)
804 mapping.put(tmp.alignmentMapping + 1,
806 { tmp.resNumber, tmp.atomIndex });
811 } while (index < maxChain.atoms.size());
813 StructureMapping nwMapping = new StructureMapping(seq, pdbFile,
814 pdb.getId(), maxChainId, mapping, mappingDetails.toString());
815 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
819 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
821 listeners.removeElement(svl);
822 if (svl instanceof SequenceListener)
824 for (int i = 0; i < listeners.size(); i++)
826 if (listeners.elementAt(i) instanceof StructureListener)
828 ((StructureListener) listeners.elementAt(i))
829 .releaseReferences(svl);
834 if (pdbfiles == null)
840 * Remove mappings to the closed listener's PDB files, but first check if
841 * another listener is still interested
843 List<String> pdbs = new ArrayList<>(Arrays.asList(pdbfiles));
845 StructureListener sl;
846 for (int i = 0; i < listeners.size(); i++)
848 if (listeners.elementAt(i) instanceof StructureListener)
850 sl = (StructureListener) listeners.elementAt(i);
851 for (String pdbfile : sl.getStructureFiles())
853 pdbs.remove(pdbfile);
859 * Rebuild the mappings set, retaining only those which are for 'other' PDB
864 List<StructureMapping> tmp = new ArrayList<>();
865 for (StructureMapping sm : mappings)
867 if (!pdbs.contains(sm.pdbfile))
878 * Propagate mouseover of a single position in a structure
885 public String mouseOverStructure(int pdbResNum, String chain,
888 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
889 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
890 return mouseOverStructure(atoms);
894 * Propagate mouseover or selection of multiple positions in a structure
898 public String mouseOverStructure(List<AtomSpec> atoms)
900 if (listeners == null)
902 // old or prematurely sent event
905 boolean hasSequenceListener = false;
906 for (int i = 0; i < listeners.size(); i++)
908 if (listeners.elementAt(i) instanceof SequenceListener)
910 hasSequenceListener = true;
913 if (!hasSequenceListener)
918 SearchResultsI results = findAlignmentPositionsForStructurePositions(
920 String result = null;
921 for (Object li : listeners)
923 if (li instanceof SequenceListener)
925 String s = ((SequenceListener) li).highlightSequence(results);
936 * Constructs a SearchResults object holding regions (if any) in the Jalview
937 * alignment which have a mapping to the structure viewer positions in the
943 public SearchResultsI findAlignmentPositionsForStructurePositions(
944 List<AtomSpec> atoms)
946 SearchResultsI results = new SearchResults();
947 for (AtomSpec atom : atoms)
949 SequenceI lastseq = null;
951 for (StructureMapping sm : mappings)
953 if (sm.pdbfile.equals(atom.getPdbFile())
954 && sm.pdbchain.equals(atom.getChain()))
956 int indexpos = sm.getSeqPos(atom.getPdbResNum());
957 if (lastipos != indexpos || lastseq != sm.sequence)
959 results.addResult(sm.sequence, indexpos, indexpos);
961 lastseq = sm.sequence;
962 // construct highlighted sequence list
963 for (AlignedCodonFrame acf : seqmappings)
965 acf.markMappedRegion(sm.sequence, indexpos, results);
975 * highlight regions associated with a position (indexpos) in seq
978 * the sequence that the mouse over occurred on
980 * the absolute position being mouseovered in seq (0 to seq.length())
982 * the sequence position (if -1, seq.findPosition is called to
983 * resolve the residue number)
985 public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
988 boolean hasSequenceListeners = handlingVamsasMo
989 || !seqmappings.isEmpty();
990 SearchResultsI results = null;
993 seqPos = seq.findPosition(indexpos);
996 // precompute so we can also relay structure highlights
999 results = MappingUtils.buildSearchResults(seq, seqPos,
1002 if (handlingVamsasMo)
1004 results.addResult(seq, seqPos, seqPos);
1008 for (int i = 0; i < listeners.size(); i++)
1010 Object listener = listeners.elementAt(i);
1011 if (listener == source)
1013 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1014 // Temporary fudge with SequenceListener.getVamsasSource()
1017 if (listener instanceof StructureListener)
1019 StructureListener sl = (StructureListener) listener;
1020 // TODO: consider merging highlightStructure variants second call
1021 // functionally same as first if seq/seqPos is part of the searchResults
1022 if (highlightStructure(sl, seq, seqPos)==0 && relaySeqMappings)
1024 // structure highlights for mapped sequences
1025 highlightStructure(sl,results);
1030 if (listener instanceof SequenceListener)
1032 final SequenceListener seqListener = (SequenceListener) listener;
1033 if (hasSequenceListeners
1034 && seqListener.getVamsasSource() != source)
1036 if (relaySeqMappings)
1038 if (!results.isEmpty())
1040 seqListener.highlightSequence(results);
1045 else if (listener instanceof VamsasListener && !handlingVamsasMo)
1047 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
1050 else if (listener instanceof SecondaryStructureListener)
1052 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
1060 * highlights positions in a structure viewer corresponding to one or more positions on sequences
1062 * @param searchResults
1063 * @return 0 or number of structure regions highlighted
1065 public int highlightStructure(StructureListener sl, SearchResultsI searchResults)
1068 List<AtomSpec> atoms = new ArrayList<>();
1070 for (SearchResultMatchI sr: searchResults.getResults())
1072 SequenceI seq = sr.getSequence();
1073 for (StructureMapping sm : mappings)
1075 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1076 || (sm.sequence.getDatasetSequence() != null
1077 && (sm.sequence.getDatasetSequence() == seq
1078 || sm.sequence.getDatasetSequence() == seq
1079 .getDatasetSequence())))
1081 for (int index=sr.getStart();index<=sr.getEnd();index++)
1083 atomNo = sm.getAtomNum(index);
1087 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1088 sm.getPDBResNum(index), atomNo));
1094 sl.highlightAtoms(atoms);
1095 return atoms.size();
1098 * Send suitable messages to a StructureListener to highlight atoms
1099 * corresponding to the given sequence position(s)
1104 * @return 0 if no atoms identified for position(s)
1106 public int highlightStructure(StructureListener sl, SequenceI seq,
1109 if (!sl.isListeningFor(seq))
1114 List<AtomSpec> atoms = new ArrayList<>();
1115 for (StructureMapping sm : mappings)
1117 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1118 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1119 .getDatasetSequence() == seq.getDatasetSequence()))
1121 for (int index : positions)
1123 atomNo = sm.getAtomNum(index);
1127 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1128 sm.getPDBResNum(index), atomNo));
1133 sl.highlightAtoms(atoms);
1134 return atoms.size();
1138 * true if a mouse over event from an external (ie Vamsas) source is being
1141 boolean handlingVamsasMo = false;
1146 * as mouseOverSequence but only route event to SequenceListeners
1150 * in an alignment sequence
1152 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
1153 VamsasSource source)
1155 handlingVamsasMo = true;
1156 long msg = sequenceI.hashCode() * (1 + position);
1160 mouseOverSequence(sequenceI, position, -1, source);
1162 handlingVamsasMo = false;
1165 public Annotation[] colourSequenceFromStructure(SequenceI seq,
1169 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
1170 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
1172 * Annotation [] annotations = new Annotation[seq.getLength()];
1174 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
1175 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
1176 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
1178 * for (int j = 0; j < mappings.length; j++) {
1180 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
1181 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
1182 * System.out.println(pdbid+" "+mappings[j].getPdbId() +"
1183 * "+mappings[j].pdbfile);
1185 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
1186 * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
1188 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
1189 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
1190 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
1191 * mappings[j].pdbfile); }
1193 * annotations[index] = new Annotation("X",null,' ',0,col); } return
1194 * annotations; } } } }
1196 * return annotations;
1200 public void structureSelectionChanged()
1204 public void sequenceSelectionChanged()
1208 public void sequenceColoursChanged(Object source)
1210 StructureListener sl;
1211 for (int i = 0; i < listeners.size(); i++)
1213 if (listeners.elementAt(i) instanceof StructureListener)
1215 sl = (StructureListener) listeners.elementAt(i);
1216 sl.updateColours(source);
1221 public StructureMapping[] getMapping(String pdbfile)
1223 List<StructureMapping> tmp = new ArrayList<>();
1224 for (StructureMapping sm : mappings)
1226 if (sm.pdbfile.equals(pdbfile))
1231 return tmp.toArray(new StructureMapping[tmp.size()]);
1235 * Returns a readable description of all mappings for the given pdbfile to any
1236 * of the given sequences
1242 public String printMappings(String pdbfile, List<SequenceI> seqs)
1244 if (pdbfile == null || seqs == null || seqs.isEmpty())
1249 StringBuilder sb = new StringBuilder(64);
1250 for (StructureMapping sm : mappings)
1252 if (Platform.pathEquals(sm.pdbfile, pdbfile)
1253 && seqs.contains(sm.sequence))
1255 sb.append(sm.mappingDetails);
1257 // separator makes it easier to read multiple mappings
1258 sb.append("=====================");
1264 return sb.toString();
1268 * Remove the given mapping
1272 public void deregisterMapping(AlignedCodonFrame acf)
1276 boolean removed = seqmappings.remove(acf);
1277 if (removed && seqmappings.isEmpty())
1279 System.out.println("All mappings removed");
1285 * Add each of the given codonFrames to the stored set, if not aready present.
1289 public void registerMappings(List<AlignedCodonFrame> mappings)
1291 if (mappings != null)
1293 for (AlignedCodonFrame acf : mappings)
1295 registerMapping(acf);
1301 * Add the given mapping to the stored set, unless already stored.
1303 public void registerMapping(AlignedCodonFrame acf)
1307 if (!seqmappings.contains(acf))
1309 seqmappings.add(acf);
1315 * Resets this object to its initial state by removing all registered
1316 * listeners, codon mappings, PDB file mappings
1318 public void resetAll()
1320 if (mappings != null)
1324 if (seqmappings != null)
1326 seqmappings.clear();
1328 if (sel_listeners != null)
1330 sel_listeners.clear();
1332 if (listeners != null)
1336 if (commandListeners != null)
1338 commandListeners.clear();
1340 if (view_listeners != null)
1342 view_listeners.clear();
1344 if (pdbFileNameId != null)
1346 pdbFileNameId.clear();
1348 if (pdbIdFileName != null)
1350 pdbIdFileName.clear();
1354 public void addSelectionListener(SelectionListener selecter)
1356 if (!sel_listeners.contains(selecter))
1358 sel_listeners.add(selecter);
1362 public void removeSelectionListener(SelectionListener toremove)
1364 if (sel_listeners.contains(toremove))
1366 sel_listeners.remove(toremove);
1370 public synchronized void sendSelection(
1371 jalview.datamodel.SequenceGroup selection,
1372 jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden,
1373 SelectionSource source)
1375 for (SelectionListener slis : sel_listeners)
1379 slis.selection(selection, colsel, hidden, source);
1384 Vector<AlignmentViewPanelListener> view_listeners = new Vector<>();
1386 public synchronized void sendViewPosition(
1387 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1388 int startSeq, int endSeq)
1391 if (view_listeners != null && view_listeners.size() > 0)
1393 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1395 while (listeners.hasMoreElements())
1397 AlignmentViewPanelListener slis = listeners.nextElement();
1400 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1408 * release all references associated with this manager provider
1410 * @param jalviewLite
1412 public static void release(StructureSelectionManagerProvider jalviewLite)
1414 // synchronized (instances)
1416 if (instances == null)
1420 StructureSelectionManager mnger = (instances.get(jalviewLite));
1423 instances.remove(jalviewLite);
1426 /* bsoares 2019-03-20 finalize deprecated, no apparent external
1427 * resources to close
1429 // mnger.finalize();
1430 } catch (Throwable x)
1437 public void registerPDBEntry(PDBEntry pdbentry)
1439 if (pdbentry.getFile() != null
1440 && pdbentry.getFile().trim().length() > 0)
1442 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1446 public void addCommandListener(CommandListener cl)
1448 if (!commandListeners.contains(cl))
1450 commandListeners.add(cl);
1454 public boolean hasCommandListener(CommandListener cl)
1456 return this.commandListeners.contains(cl);
1459 public boolean removeCommandListener(CommandListener l)
1461 return commandListeners.remove(l);
1465 * Forward a command to any command listeners (except for the command's
1469 * the command to be broadcast (in its form after being performed)
1471 * if true, the command was being 'undone'
1474 public void commandPerformed(CommandI command, boolean undo,
1475 VamsasSource source)
1477 for (CommandListener listener : commandListeners)
1479 listener.mirrorCommand(command, undo, this, source);
1484 * Returns a new CommandI representing the given command as mapped to the
1485 * given sequences. If no mapping could be made, or the command is not of a
1486 * mappable kind, returns null.
1494 public CommandI mapCommand(CommandI command, boolean undo,
1495 final AlignmentI mapTo, char gapChar)
1497 if (command instanceof EditCommand)
1499 return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo,
1500 gapChar, seqmappings);
1502 else if (command instanceof OrderCommand)
1504 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1505 mapTo, seqmappings);
1510 public List<AlignedCodonFrame> getSequenceMappings()