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.ContiguousI;
46 import jalview.datamodel.HiddenColumns;
47 import jalview.datamodel.PDBEntry;
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.structure.StructureImportSettings.TFType;
57 import jalview.util.MappingUtils;
58 import jalview.util.MessageManager;
59 import jalview.util.Platform;
60 import jalview.ws.sifts.SiftsClient;
61 import jalview.ws.sifts.SiftsException;
62 import jalview.ws.sifts.SiftsSettings;
64 import mc_view.PDBChain;
65 import mc_view.PDBfile;
67 public class StructureSelectionManager
69 public final static String NEWLINE = System.lineSeparator();
71 static IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> instances;
73 private List<StructureMapping> mappings = new ArrayList<>();
75 private boolean processSecondaryStructure = false;
77 private boolean secStructServices = false;
79 private boolean addTempFacAnnot = false;
82 * Set of any registered mappings between (dataset) sequences.
84 private List<AlignedCodonFrame> seqmappings = new ArrayList<>();
86 private List<CommandListener> commandListeners = new ArrayList<>();
88 private List<SelectionListener> sel_listeners = new ArrayList<>();
91 * @return true if will try to use external services for processing secondary
94 public boolean isSecStructServices()
96 return secStructServices;
100 * control use of external services for processing secondary structure
102 * @param secStructServices
104 public void setSecStructServices(boolean secStructServices)
106 this.secStructServices = secStructServices;
110 * flag controlling addition of any kind of structural annotation
112 * @return true if temperature factor annotation will be added
114 public boolean isAddTempFacAnnot()
116 return addTempFacAnnot;
120 * set flag controlling addition of structural annotation
122 * @param addTempFacAnnot
124 public void setAddTempFacAnnot(boolean addTempFacAnnot)
126 this.addTempFacAnnot = addTempFacAnnot;
131 * @return if true, the structure manager will attempt to add secondary
132 * structure lines for unannotated sequences
135 public boolean isProcessSecondaryStructure()
137 return processSecondaryStructure;
141 * Control whether structure manager will try to annotate mapped sequences
142 * with secondary structure from PDB data.
146 public void setProcessSecondaryStructure(boolean enable)
148 processSecondaryStructure = enable;
152 * debug function - write all mappings to stdout
154 public void reportMapping()
156 if (mappings.isEmpty())
158 System.err.println("reportMapping: No PDB/Sequence mappings.");
163 "reportMapping: There are " + mappings.size() + " mappings.");
165 for (StructureMapping sm : mappings)
167 System.err.println("mapping " + i++ + " : " + sm.pdbfile);
173 * map between the PDB IDs (or structure identifiers) used by Jalview and the
174 * absolute filenames for PDB data that corresponds to it
176 Map<String, String> pdbIdFileName = new HashMap<>();
178 Map<String, String> pdbFileNameId = new HashMap<>();
180 public void registerPDBFile(String idForFile, String absoluteFile)
182 pdbIdFileName.put(idForFile, absoluteFile);
183 pdbFileNameId.put(absoluteFile, idForFile);
186 public String findIdForPDBFile(String idOrFile)
188 String id = pdbFileNameId.get(idOrFile);
192 public String findFileForPDBId(String idOrFile)
194 String id = pdbIdFileName.get(idOrFile);
198 public boolean isPDBFileRegistered(String idOrFile)
200 return pdbFileNameId.containsKey(idOrFile)
201 || pdbIdFileName.containsKey(idOrFile);
204 private static StructureSelectionManager nullProvider = null;
206 public static StructureSelectionManager getStructureSelectionManager(
207 StructureSelectionManagerProvider context)
211 if (nullProvider == null)
213 if (instances != null)
215 throw new Error(MessageManager.getString(
216 "error.implementation_error_structure_selection_manager_null"),
217 new NullPointerException(MessageManager
218 .getString("exception.ssm_context_is_null")));
222 nullProvider = new StructureSelectionManager();
227 if (instances == null)
229 instances = new java.util.IdentityHashMap<>();
231 StructureSelectionManager instance = instances.get(context);
232 if (instance == null)
234 if (nullProvider != null)
236 instance = nullProvider;
240 instance = new StructureSelectionManager();
242 instances.put(context, instance);
248 * flag controlling whether SeqMappings are relayed from received sequence
249 * mouse over events to other sequences
251 boolean relaySeqMappings = true;
254 * Enable or disable relay of seqMapping events to other sequences. You might
255 * want to do this if there are many sequence mappings and the host computer
260 public void setRelaySeqMappings(boolean relay)
262 relaySeqMappings = relay;
266 * get the state of the relay seqMappings flag.
268 * @return true if sequence mouse overs are being relayed to other mapped
271 public boolean isRelaySeqMappingsEnabled()
273 return relaySeqMappings;
276 Vector listeners = new Vector();
279 * register a listener for alignment sequence mouseover events
283 public void addStructureViewerListener(Object svl)
285 if (!listeners.contains(svl))
287 listeners.addElement(svl);
292 * Returns the filename the PDB id is already mapped to if known, or null if
298 public String alreadyMappedToFile(String pdbid)
300 for (StructureMapping sm : mappings)
302 if (sm.getPdbId().equalsIgnoreCase(pdbid))
311 * Import structure data and register a structure mapping for broadcasting
312 * colouring, mouseovers and selection events (convenience wrapper).
315 * - one or more sequences to be mapped to pdbFile
316 * @param targetChains
317 * - optional chain specification for mapping each sequence to pdb
318 * (may be nill, individual elements may be nill)
320 * - structure data resource
322 * - how to resolve data from resource
323 * @return null or the structure data parsed as a pdb file
325 synchronized public StructureFile setMapping(SequenceI[] sequence,
326 String[] targetChains, String pdbFile, DataSourceType protocol,
327 IProgressIndicator progress)
329 return computeMapping(true, sequence, targetChains, pdbFile, protocol,
330 progress, null, null);
334 * Import a single structure file and register sequence structure mappings for
335 * broadcasting colouring, mouseovers and selection events (convenience
338 * @param forStructureView
339 * when true, record the mapping for use in mouseOvers
341 * - one or more sequences to be mapped to pdbFile
342 * @param targetChains
343 * - optional chain specification for mapping each sequence to pdb
344 * (may be nill, individual elements may be nill)
346 * - structure data resource
348 * - how to resolve data from resource
349 * @return null or the structure data parsed as a pdb file
351 synchronized public StructureFile setMapping(boolean forStructureView,
352 SequenceI[] sequenceArray, String[] targetChainIds,
353 String pdbFile, DataSourceType sourceType, TFType tft,
356 return computeMapping(forStructureView, sequenceArray, targetChainIds,
357 pdbFile, sourceType, null, tft, paeFilename);
361 * create sequence structure mappings between each sequence and the given
362 * pdbFile (retrieved via the given protocol). Either constructs a mapping
363 * using NW alignment or derives one from any available SIFTS mapping data.
365 * @param forStructureView
366 * when true, record the mapping for use in mouseOvers
368 * @param sequenceArray
369 * - one or more sequences to be mapped to pdbFile
370 * @param targetChainIds
371 * - optional chain specification for mapping each sequence to pdb
372 * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
373 * - this should be List<List<String>>, empty lists indicate no
374 * predefined mappings
376 * - structure data resource
378 * - how to resolve data from resource
379 * @param IProgressIndicator
380 * reference to UI component that maintains a progress bar for the
382 * @return null or the structure data parsed as a pdb file
384 synchronized public StructureFile computeMapping(boolean forStructureView,
385 SequenceI[] sequenceArray, String[] targetChainIds,
386 String pdbFile, DataSourceType sourceType,
387 IProgressIndicator progress, TFType tft, String paeFilename)
389 long progressSessionId = System.currentTimeMillis() * 3;
392 * do we extract and transfer annotation from 3D data ?
394 // FIXME: possibly should just delete
396 boolean parseSecStr = processSecondaryStructure
397 ? isStructureFileProcessed(pdbFile, sequenceArray)
400 StructureFile pdb = null;
401 boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
404 // FIXME if sourceType is not null, we've lost data here
405 sourceType = AppletFormatAdapter.checkProtocol(pdbFile);
406 pdb = new JmolParser(false, pdbFile, sourceType);
407 if (paeFilename != null)
409 pdb.setPAEMatrix(paeFilename);
411 pdb.setTemperatureFactorType(tft);
412 pdb.addSettings(parseSecStr && processSecondaryStructure,
413 parseSecStr && addTempFacAnnot,
414 parseSecStr && secStructServices);
416 if (pdb.getId() != null && pdb.getId().trim().length() > 0
417 && DataSourceType.FILE == sourceType)
419 registerPDBFile(pdb.getId().trim(), pdbFile);
421 // if PDBId is unavailable then skip SIFTS mapping execution path
422 // TODO: JAL-3868 need to know if structure is actually from
423 // PDB (has valid PDB ID and has provenance suggesting it
424 // actually came from PDB)
425 boolean isProtein = false;
426 for (SequenceI s : sequenceArray)
434 isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable()
435 && !pdb.getId().startsWith("AF-") && isProtein;
437 } catch (Exception ex)
439 ex.printStackTrace();
443 * sifts client - non null if SIFTS mappings are to be used
445 SiftsClient siftsClient = null;
450 siftsClient = new SiftsClient(pdb);
452 } catch (SiftsException e)
454 isMapUsingSIFTs = false;
455 Console.error("SIFTS mapping failed", e);
456 Console.error("Falling back on Needleman & Wunsch alignment");
460 String targetChainId;
461 for (int s = 0; s < sequenceArray.length; s++)
463 boolean infChain = true;
464 final SequenceI seq = sequenceArray[s];
466 while (ds.getDatasetSequence() != null)
468 ds = ds.getDatasetSequence();
471 if (targetChainIds != null && targetChainIds[s] != null)
474 targetChainId = targetChainIds[s];
476 else if (seq.getName().indexOf("|") > -1)
478 targetChainId = seq.getName()
479 .substring(seq.getName().lastIndexOf("|") + 1);
480 if (targetChainId.length() > 1)
482 if (targetChainId.trim().length() == 0)
488 // not a valid chain identifier
499 * Attempt pairwise alignment of the sequence with each chain in the PDB,
500 * and remember the highest scoring chain
503 AlignSeq maxAlignseq = null;
504 String maxChainId = " ";
505 PDBChain maxChain = null;
506 boolean first = true;
507 for (PDBChain chain : pdb.getChains())
509 if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
512 continue; // don't try to map chains don't match.
514 // TODO: correctly determine sequence type for mixed na/peptide
516 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
517 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
520 // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
521 // as.calcScoreMatrix();
522 // as.traceAlignment();
524 if (first || as.maxscore > max
525 || (as.maxscore == max && chain.id.equals(targetChainId)))
531 maxChainId = chain.id;
534 if (maxChain == null)
539 if (sourceType == DataSourceType.PASTE)
541 pdbFile = "INLINE" + pdb.getId();
544 List<StructureMapping> seqToStrucMapping = new ArrayList<>();
545 if (isMapUsingSIFTs && seq.isProtein())
547 if (progress != null)
549 progress.setProgressBar(
551 .getString("status.obtaining_mapping_with_sifts"),
554 jalview.datamodel.Mapping sqmpping = maxAlignseq
555 .getMappingFromS1(false);
556 if (targetChainId != null && !targetChainId.trim().isEmpty())
558 StructureMapping siftsMapping;
561 siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
562 pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
563 seqToStrucMapping.add(siftsMapping);
564 maxChain.makeExactMapping(siftsMapping, seq);
565 maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS",
566 pdb.getId().toLowerCase(Locale.ROOT));
567 maxChain.transferResidueAnnotation(siftsMapping, null);
568 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
570 } catch (SiftsException e)
572 // fall back to NW alignment
573 Console.error(e.getMessage());
574 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
575 targetChainId, maxChain, pdb, maxAlignseq);
576 seqToStrucMapping.add(nwMapping);
577 maxChain.makeExactMapping(maxAlignseq, seq);
578 maxChain.transferRESNUMFeatures(seq, "IEA:Jalview",
579 pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is
582 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
583 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
588 List<StructureMapping> foundSiftsMappings = new ArrayList<>();
589 for (PDBChain chain : pdb.getChains())
591 StructureMapping siftsMapping = null;
594 siftsMapping = getStructureMapping(seq, pdbFile, chain.id,
595 pdb, chain, sqmpping, maxAlignseq, siftsClient);
596 foundSiftsMappings.add(siftsMapping);
597 chain.makeExactMapping(siftsMapping, seq);
598 chain.transferRESNUMFeatures(seq, "IEA: SIFTS",
599 pdb.getId().toLowerCase(Locale.ROOT));// FIXME: is this
601 chain.transferResidueAnnotation(siftsMapping, null);
602 } catch (SiftsException e)
604 System.err.println(e.getMessage());
605 } catch (Exception e)
608 "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
609 System.err.println(e.getMessage());
612 if (!foundSiftsMappings.isEmpty())
614 seqToStrucMapping.addAll(foundSiftsMappings);
615 ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
619 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
620 maxChainId, maxChain, pdb, maxAlignseq);
621 seqToStrucMapping.add(nwMapping);
622 maxChain.transferRESNUMFeatures(seq, null,
623 pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is this
625 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
626 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
632 if (progress != null)
634 progress.setProgressBar(
635 MessageManager.getString(
636 "status.obtaining_mapping_with_nw_alignment"),
639 StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
640 maxChain, pdb, maxAlignseq);
641 seqToStrucMapping.add(nwMapping);
642 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
644 if (forStructureView)
646 for (StructureMapping sm : seqToStrucMapping)
648 addStructureMapping(sm); // not addAll!
651 if (progress != null)
653 progress.setProgressBar(null, progressSessionId);
660 * check if we need to extract secondary structure from given pdbFile and
661 * transfer to sequences
664 * @param sequenceArray
667 private boolean isStructureFileProcessed(String pdbFile,
668 SequenceI[] sequenceArray)
670 boolean parseSecStr = true;
671 if (isPDBFileRegistered(pdbFile))
673 for (SequenceI sq : sequenceArray)
676 while (ds.getDatasetSequence() != null)
678 ds = ds.getDatasetSequence();
681 if (ds.getAnnotation() != null)
683 for (AlignmentAnnotation ala : ds.getAnnotation())
685 // false if any annotation present from this structure
686 // JBPNote this fails for jmol/chimera view because the *file* is
687 // passed, not the structure data ID -
688 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
699 public void addStructureMapping(StructureMapping sm)
701 if (!mappings.contains(sm))
708 * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
713 * @param targetChainId
719 * client for retrieval of SIFTS mappings for this structure
721 * @throws SiftsException
723 private StructureMapping getStructureMapping(SequenceI seq,
724 String pdbFile, String targetChainId, StructureFile pdb,
725 PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
726 AlignSeq maxAlignseq, SiftsClient siftsClient)
727 throws SiftsException
729 StructureMapping curChainMapping = siftsClient
730 .getSiftsStructureMapping(seq, pdbFile, targetChainId);
733 PDBChain chain = pdb.findChain(targetChainId);
736 chain.transferResidueAnnotation(curChainMapping, null);
738 } catch (Exception e)
742 return curChainMapping;
745 private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
746 String maxChainId, PDBChain maxChain, StructureFile pdb,
747 AlignSeq maxAlignseq)
749 final StringBuilder mappingDetails = new StringBuilder(128);
750 mappingDetails.append(NEWLINE)
751 .append("Sequence \u27f7 Structure mapping details");
752 mappingDetails.append(NEWLINE);
754 .append("Method: inferred with Needleman & Wunsch alignment");
755 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
756 .append(NEWLINE).append("Sequence = ")
757 .append(maxChain.sequence.getSequenceAsString());
758 mappingDetails.append(NEWLINE).append("No of residues = ")
759 .append(maxChain.residues.size()).append(NEWLINE)
761 PrintStream ps = new PrintStream(System.out)
764 public void print(String x)
766 mappingDetails.append(x);
770 public void println()
772 mappingDetails.append(NEWLINE);
776 maxAlignseq.printAlignment(ps);
778 mappingDetails.append(NEWLINE).append("PDB start/end ");
779 mappingDetails.append(String.valueOf(maxAlignseq.seq2start))
781 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
782 mappingDetails.append(NEWLINE).append("SEQ start/end ");
785 .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1)))
787 mappingDetails.append(
788 String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1)));
789 mappingDetails.append(NEWLINE);
790 maxChain.makeExactMapping(maxAlignseq, seq);
791 jalview.datamodel.Mapping sqmpping = maxAlignseq
792 .getMappingFromS1(false);
793 maxChain.transferRESNUMFeatures(seq, null,
794 pdb.getId().toLowerCase(Locale.ROOT));
796 HashMap<Integer, int[]> mapping = new HashMap<>();
803 Atom tmp = maxChain.atoms.elementAt(index);
804 if ((resNum != tmp.resNumber || insCode != tmp.insCode)
805 && tmp.alignmentMapping != -1)
807 resNum = tmp.resNumber;
808 insCode = tmp.insCode;
809 if (tmp.alignmentMapping >= -1)
811 mapping.put(tmp.alignmentMapping + 1,
813 { tmp.resNumber, tmp.atomIndex });
818 } while (index < maxChain.atoms.size());
820 StructureMapping nwMapping = new StructureMapping(seq, pdbFile,
821 pdb.getId(), maxChainId, mapping, mappingDetails.toString());
822 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
826 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
828 listeners.removeElement(svl);
829 if (svl instanceof SequenceListener)
831 for (int i = 0; i < listeners.size(); i++)
833 if (listeners.elementAt(i) instanceof StructureListener)
835 ((StructureListener) listeners.elementAt(i))
836 .releaseReferences(svl);
841 if (pdbfiles == null)
847 * Remove mappings to the closed listener's PDB files, but first check if
848 * another listener is still interested
850 List<String> pdbs = new ArrayList<>(Arrays.asList(pdbfiles));
852 StructureListener sl;
853 for (int i = 0; i < listeners.size(); i++)
855 if (listeners.elementAt(i) instanceof StructureListener)
857 sl = (StructureListener) listeners.elementAt(i);
858 for (String pdbfile : sl.getStructureFiles())
860 pdbs.remove(pdbfile);
866 * Rebuild the mappings set, retaining only those which are for 'other' PDB
871 List<StructureMapping> tmp = new ArrayList<>();
872 for (StructureMapping sm : mappings)
874 if (!pdbs.contains(sm.pdbfile))
885 * hack to highlight a range of positions at once on any structure views
889 * - series of int start-end ranges as positions on sequenceRef
893 public void highlightPositionsOn(SequenceI sequenceRef, int[][] is,
896 boolean hasSequenceListeners = handlingVamsasMo
897 || !seqmappings.isEmpty();
898 SearchResultsI results = null;
899 ArrayList<Integer> listOfPositions = new ArrayList<Integer>();
902 for (int p = s_e[0]; p <= s_e[1]; listOfPositions.add(p++))
905 int seqpos[] = new int[listOfPositions.size()];
907 for (Integer p : listOfPositions)
912 for (i = 0; i < listeners.size(); i++)
914 Object listener = listeners.elementAt(i);
915 if (listener == source)
917 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
918 // Temporary fudge with SequenceListener.getVamsasSource()
921 if (listener instanceof StructureListener)
923 highlightStructure((StructureListener) listener, sequenceRef,
931 * Propagate mouseover of a single position in a structure
938 public String mouseOverStructure(int pdbResNum, String chain,
941 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
942 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
943 return mouseOverStructure(atoms);
947 * Propagate mouseover or selection of multiple positions in a structure
951 public String mouseOverStructure(List<AtomSpec> atoms)
953 if (listeners == null)
955 // old or prematurely sent event
958 boolean hasSequenceListener = false;
959 for (int i = 0; i < listeners.size(); i++)
961 if (listeners.elementAt(i) instanceof SequenceListener)
963 hasSequenceListener = true;
966 if (!hasSequenceListener)
971 SearchResultsI results = findAlignmentPositionsForStructurePositions(
973 String result = null;
974 for (Object li : listeners)
976 if (li instanceof SequenceListener)
978 String s = ((SequenceListener) li).highlightSequence(results);
989 * Constructs a SearchResults object holding regions (if any) in the Jalview
990 * alignment which have a mapping to the structure viewer positions in the
996 public SearchResultsI findAlignmentPositionsForStructurePositions(
997 List<AtomSpec> atoms)
999 SearchResultsI results = new SearchResults();
1000 for (AtomSpec atom : atoms)
1002 SequenceI lastseq = null;
1004 for (StructureMapping sm : mappings)
1006 if (sm.pdbfile.equals(atom.getPdbFile())
1007 && sm.pdbchain.equals(atom.getChain()))
1009 int indexpos = sm.getSeqPos(atom.getPdbResNum());
1010 if (lastipos != indexpos || lastseq != sm.sequence)
1012 results.addResult(sm.sequence, indexpos, indexpos);
1013 lastipos = indexpos;
1014 lastseq = sm.sequence;
1015 // construct highlighted sequence list
1016 for (AlignedCodonFrame acf : seqmappings)
1018 acf.markMappedRegion(sm.sequence, indexpos, results);
1028 * highlight regions associated with a position (indexpos) in seq
1031 * the sequence that the mouse over occurred on
1033 * the absolute position being mouseovered in seq (0 to seq.length())
1035 * the sequence position (if -1, seq.findPosition is called to
1036 * resolve the residue number)
1038 public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
1039 VamsasSource source)
1041 boolean hasSequenceListeners = handlingVamsasMo
1042 || !seqmappings.isEmpty();
1043 SearchResultsI results = null;
1046 seqPos = seq.findPosition(indexpos);
1048 for (int i = 0; i < listeners.size(); i++)
1050 Object listener = listeners.elementAt(i);
1051 if (listener == source)
1053 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1054 // Temporary fudge with SequenceListener.getVamsasSource()
1057 if (listener instanceof StructureListener)
1059 highlightStructure((StructureListener) listener, seq, seqPos);
1063 if (listener instanceof SequenceListener)
1065 final SequenceListener seqListener = (SequenceListener) listener;
1066 if (hasSequenceListeners
1067 && seqListener.getVamsasSource() != source)
1069 if (relaySeqMappings)
1071 if (results == null)
1073 results = MappingUtils.buildSearchResults(seq, seqPos,
1076 if (handlingVamsasMo)
1078 results.addResult(seq, seqPos, seqPos);
1081 if (!results.isEmpty())
1083 seqListener.highlightSequence(results);
1088 else if (listener instanceof VamsasListener && !handlingVamsasMo)
1090 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
1093 else if (listener instanceof SecondaryStructureListener)
1095 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
1103 * Send suitable messages to a StructureListener to highlight atoms
1104 * corresponding to the given sequence position(s)
1110 public void highlightStructure(StructureListener sl, SequenceI seq,
1113 if (!sl.isListeningFor(seq))
1118 List<AtomSpec> atoms = new ArrayList<>();
1119 for (StructureMapping sm : mappings)
1121 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1122 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1123 .getDatasetSequence() == seq.getDatasetSequence()))
1125 for (int index : positions)
1127 atomNo = sm.getAtomNum(index);
1131 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1132 sm.getPDBResNum(index), atomNo));
1137 sl.highlightAtoms(atoms);
1140 public void highlightStructureRegionsFor(StructureListener sl,
1141 SequenceI[] seqs, int... columns)
1143 List<SequenceI> to_highlight = new ArrayList<SequenceI>();
1144 for (SequenceI seq : seqs)
1146 if (sl.isListeningFor(seq))
1148 to_highlight.add(seq);
1151 if (to_highlight.size() == 0)
1155 List<AtomSpec> atoms = new ArrayList<>();
1156 for (SequenceI seq : to_highlight)
1159 for (StructureMapping sm : mappings)
1161 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1162 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1163 .getDatasetSequence() == seq.getDatasetSequence()))
1166 for (int i = 0; i < columns.length; i += 2)
1168 ContiguousI positions = seq.findPositions(columns[i] + 1,
1169 columns[i + 1] + 1);
1170 if (positions == null)
1174 for (int index = positions.getBegin(); index <= positions
1178 atomNo = sm.getAtomNum(index);
1182 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1183 sm.getPDBResNum(index), atomNo));
1189 if (atoms.size() > 0)
1191 sl.highlightAtoms(atoms);
1197 * true if a mouse over event from an external (ie Vamsas) source is being
1200 boolean handlingVamsasMo = false;
1205 * as mouseOverSequence but only route event to SequenceListeners
1209 * in an alignment sequence
1211 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
1212 VamsasSource source)
1214 handlingVamsasMo = true;
1215 long msg = sequenceI.hashCode() * (1 + position);
1219 mouseOverSequence(sequenceI, position, -1, source);
1221 handlingVamsasMo = false;
1224 public Annotation[] colourSequenceFromStructure(SequenceI seq,
1228 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
1229 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
1231 * Annotation [] annotations = new Annotation[seq.getLength()];
1233 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
1234 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
1235 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
1237 * for (int j = 0; j < mappings.length; j++) {
1239 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
1240 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
1241 * System.out.println(pdbid+" "+mappings[j].getPdbId() +"
1242 * "+mappings[j].pdbfile);
1244 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
1245 * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
1247 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
1248 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
1249 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
1250 * mappings[j].pdbfile); }
1252 * annotations[index] = new Annotation("X",null,' ',0,col); } return
1253 * annotations; } } } }
1255 * return annotations;
1259 public void structureSelectionChanged()
1263 public void sequenceSelectionChanged()
1267 public void sequenceColoursChanged(Object source)
1269 StructureListener sl;
1270 for (int i = 0; i < listeners.size(); i++)
1272 if (listeners.elementAt(i) instanceof StructureListener)
1274 sl = (StructureListener) listeners.elementAt(i);
1275 sl.updateColours(source);
1280 public StructureMapping[] getMapping(String pdbfile)
1282 List<StructureMapping> tmp = new ArrayList<>();
1283 for (StructureMapping sm : mappings)
1285 if (sm.pdbfile.equals(pdbfile))
1290 return tmp.toArray(new StructureMapping[tmp.size()]);
1294 * Returns a readable description of all mappings for the given pdbfile to any
1295 * of the given sequences
1301 public String printMappings(String pdbfile, List<SequenceI> seqs)
1303 if (pdbfile == null || seqs == null || seqs.isEmpty())
1308 StringBuilder sb = new StringBuilder(64);
1309 for (StructureMapping sm : mappings)
1311 if (Platform.pathEquals(sm.pdbfile, pdbfile)
1312 && seqs.contains(sm.sequence))
1314 sb.append(sm.mappingDetails);
1316 // separator makes it easier to read multiple mappings
1317 sb.append("=====================");
1323 return sb.toString();
1327 * Remove the given mapping
1331 public void deregisterMapping(AlignedCodonFrame acf)
1335 boolean removed = seqmappings.remove(acf);
1336 if (removed && seqmappings.isEmpty())
1338 System.out.println("All mappings removed");
1344 * Add each of the given codonFrames to the stored set, if not aready present.
1348 public void registerMappings(List<AlignedCodonFrame> mappings)
1350 if (mappings != null)
1352 for (AlignedCodonFrame acf : mappings)
1354 registerMapping(acf);
1360 * Add the given mapping to the stored set, unless already stored.
1362 public void registerMapping(AlignedCodonFrame acf)
1366 if (!seqmappings.contains(acf))
1368 seqmappings.add(acf);
1374 * Resets this object to its initial state by removing all registered
1375 * listeners, codon mappings, PDB file mappings
1377 public void resetAll()
1379 if (mappings != null)
1383 if (seqmappings != null)
1385 seqmappings.clear();
1387 if (sel_listeners != null)
1389 sel_listeners.clear();
1391 if (listeners != null)
1395 if (commandListeners != null)
1397 commandListeners.clear();
1399 if (view_listeners != null)
1401 view_listeners.clear();
1403 if (pdbFileNameId != null)
1405 pdbFileNameId.clear();
1407 if (pdbIdFileName != null)
1409 pdbIdFileName.clear();
1413 public void addSelectionListener(SelectionListener selecter)
1415 if (!sel_listeners.contains(selecter))
1417 sel_listeners.add(selecter);
1421 public void removeSelectionListener(SelectionListener toremove)
1423 if (sel_listeners.contains(toremove))
1425 sel_listeners.remove(toremove);
1429 public synchronized void sendSelection(
1430 jalview.datamodel.SequenceGroup selection,
1431 jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden,
1432 SelectionSource source)
1434 for (SelectionListener slis : sel_listeners)
1438 slis.selection(selection, colsel, hidden, source);
1443 Vector<AlignmentViewPanelListener> view_listeners = new Vector<>();
1445 public synchronized void sendViewPosition(
1446 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1447 int startSeq, int endSeq)
1450 if (view_listeners != null && view_listeners.size() > 0)
1452 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1454 while (listeners.hasMoreElements())
1456 AlignmentViewPanelListener slis = listeners.nextElement();
1459 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1467 * release all references associated with this manager provider
1469 * @param jalviewLite
1471 public static void release(StructureSelectionManagerProvider jalviewLite)
1473 // synchronized (instances)
1475 if (instances == null)
1479 StructureSelectionManager mnger = (instances.get(jalviewLite));
1482 instances.remove(jalviewLite);
1485 /* bsoares 2019-03-20 finalize deprecated, no apparent external
1486 * resources to close
1488 // mnger.finalize();
1489 } catch (Throwable x)
1496 public void registerPDBEntry(PDBEntry pdbentry)
1498 if (pdbentry.getFile() != null
1499 && pdbentry.getFile().trim().length() > 0)
1501 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1505 public void addCommandListener(CommandListener cl)
1507 if (!commandListeners.contains(cl))
1509 commandListeners.add(cl);
1513 public boolean hasCommandListener(CommandListener cl)
1515 return this.commandListeners.contains(cl);
1518 public boolean removeCommandListener(CommandListener l)
1520 return commandListeners.remove(l);
1524 * Forward a command to any command listeners (except for the command's
1528 * the command to be broadcast (in its form after being performed)
1530 * if true, the command was being 'undone'
1533 public void commandPerformed(CommandI command, boolean undo,
1534 VamsasSource source)
1536 for (CommandListener listener : commandListeners)
1538 listener.mirrorCommand(command, undo, this, source);
1543 * Returns a new CommandI representing the given command as mapped to the
1544 * given sequences. If no mapping could be made, or the command is not of a
1545 * mappable kind, returns null.
1553 public CommandI mapCommand(CommandI command, boolean undo,
1554 final AlignmentI mapTo, char gapChar)
1556 if (command instanceof EditCommand)
1558 return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo,
1559 gapChar, seqmappings);
1561 else if (command instanceof OrderCommand)
1563 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1564 mapTo, seqmappings);
1569 public List<AlignedCodonFrame> getSequenceMappings()
1575 * quick and dirty route to just highlight all structure positions for a range
1578 * @param sequencesArray
1580 * start-end columns on sequencesArray
1582 * origin parent AlignmentPanel
1584 public void highlightPositionsOnMany(SequenceI[] sequencesArray, int[] is,
1587 for (int i = 0; i < listeners.size(); i++)
1589 Object listener = listeners.elementAt(i);
1590 if (listener == source)
1592 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1593 // Temporary fudge with SequenceListener.getVamsasSource()
1596 if (listener instanceof StructureListener)
1598 highlightStructureRegionsFor((StructureListener) listener,
1599 sequencesArray, is);
1604 public Map<String, String> getPdbFileNameIdMap()
1606 return pdbFileNameId;
1609 public Map<String, String> getPdbIdFileNameMap()
1611 return pdbIdFileName;