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, true);
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 setMapping(forStructureView, sequenceArray, targetChainIds,
357 pdbFile, sourceType, tft, paeFilename, true);
360 synchronized public StructureFile setMapping(boolean forStructureView,
361 SequenceI[] sequenceArray, String[] targetChainIds,
362 String pdbFile, DataSourceType sourceType, TFType tft,
363 String paeFilename, boolean doXferSettings)
365 return computeMapping(forStructureView, sequenceArray, targetChainIds,
366 pdbFile, sourceType, null, tft, paeFilename, doXferSettings);
370 * create sequence structure mappings between each sequence and the given
371 * pdbFile (retrieved via the given protocol). Either constructs a mapping
372 * using NW alignment or derives one from any available SIFTS mapping data.
374 * @param forStructureView
375 * when true, record the mapping for use in mouseOvers
377 * @param sequenceArray
378 * - one or more sequences to be mapped to pdbFile
379 * @param targetChainIds
380 * - optional chain specification for mapping each sequence to pdb
381 * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
382 * - this should be List<List<String>>, empty lists indicate no
383 * predefined mappings
385 * - structure data resource
387 * - how to resolve data from resource
388 * @param IProgressIndicator
389 * reference to UI component that maintains a progress bar for the
391 * @param tft - specify how to interpret the temperature factor column in the atom data
392 * @param paeFilename - when not null, specifies a filename containing a matrix formatted in JSON using one of the known PAE formats
393 * @param doXferSettings - when true, transfer annotation to mapped sequences in sequenceArray
394 * @return null or the structure data parsed as a pdb file
396 synchronized public StructureFile computeMapping(boolean forStructureView,
397 SequenceI[] sequenceArray, String[] targetChainIds,
398 String pdbFile, DataSourceType sourceType,
399 IProgressIndicator progress, TFType tft, String paeFilename,
400 boolean doXferSettings)
402 long progressSessionId = System.currentTimeMillis() * 3;
405 * do we extract and transfer annotation from 3D data ?
407 // FIXME: possibly should just delete
409 boolean parseSecStr = processSecondaryStructure
410 && !isStructureFileProcessed(pdbFile, sequenceArray);
412 StructureFile pdb = null;
413 boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
416 // FIXME if sourceType is not null, we've lost data here
417 sourceType = AppletFormatAdapter.checkProtocol(pdbFile);
418 pdb = new JmolParser(false, pdbFile, sourceType);
419 if (paeFilename != null)
421 pdb.setPAEMatrix(paeFilename);
423 pdb.setTemperatureFactorType(tft);
424 pdb.addSettings(parseSecStr && processSecondaryStructure,
425 parseSecStr && addTempFacAnnot,
426 parseSecStr && secStructServices);
427 // save doXferSettings and reset after doParse()
428 boolean temp = pdb.getDoXferSettings();
429 pdb.setDoXferSettings(doXferSettings);
431 pdb.setDoXferSettings(temp);
432 if (pdb.getId() != null && pdb.getId().trim().length() > 0
433 && DataSourceType.FILE == sourceType)
435 registerPDBFile(pdb.getId().trim(), pdbFile);
437 // if PDBId is unavailable then skip SIFTS mapping execution path
438 // TODO: JAL-3868 need to know if structure is actually from
439 // PDB (has valid PDB ID and has provenance suggesting it
440 // actually came from PDB)
441 boolean isProtein = false;
442 for (SequenceI s : sequenceArray)
450 isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable()
451 && !pdb.getId().startsWith("AF-") && isProtein;
453 } catch (Exception ex)
455 ex.printStackTrace();
459 * sifts client - non null if SIFTS mappings are to be used
461 SiftsClient siftsClient = null;
466 siftsClient = new SiftsClient(pdb);
468 } catch (SiftsException e)
470 isMapUsingSIFTs = false;
471 Console.error("SIFTS mapping failed", e);
472 Console.error("Falling back on Needleman & Wunsch alignment");
476 String targetChainId;
477 for (int s = 0; s < sequenceArray.length; s++)
479 boolean infChain = true;
480 final SequenceI seq = sequenceArray[s];
482 while (ds.getDatasetSequence() != null)
484 ds = ds.getDatasetSequence();
487 if (targetChainIds != null && targetChainIds[s] != null)
490 targetChainId = targetChainIds[s];
492 else if (seq.getName().indexOf("|") > -1)
494 targetChainId = seq.getName()
495 .substring(seq.getName().lastIndexOf("|") + 1);
496 if (targetChainId.length() > 1)
498 if (targetChainId.trim().length() == 0)
504 // not a valid chain identifier
515 * Attempt pairwise alignment of the sequence with each chain in the PDB,
516 * and remember the highest scoring chain
519 AlignSeq maxAlignseq = null;
520 String maxChainId = " ";
521 PDBChain maxChain = null;
522 boolean first = true;
523 for (PDBChain chain : pdb.getChains())
525 if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
528 continue; // don't try to map chains don't match.
530 // TODO: correctly determine sequence type for mixed na/peptide
532 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
533 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
536 // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
537 // as.calcScoreMatrix();
538 // as.traceAlignment();
540 if (first || as.maxscore > max
541 || (as.maxscore == max && chain.id.equals(targetChainId)))
547 maxChainId = chain.id;
550 if (maxChain == null)
555 if (sourceType == DataSourceType.PASTE)
557 pdbFile = "INLINE" + pdb.getId();
560 List<StructureMapping> seqToStrucMapping = new ArrayList<>();
561 if (isMapUsingSIFTs && seq.isProtein())
563 if (progress != null)
565 progress.setProgressBar(
567 .getString("status.obtaining_mapping_with_sifts"),
570 jalview.datamodel.Mapping sqmpping = maxAlignseq
571 .getMappingFromS1(false);
572 if (targetChainId != null && !targetChainId.trim().isEmpty())
574 StructureMapping siftsMapping;
577 siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
578 pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
579 seqToStrucMapping.add(siftsMapping);
580 maxChain.makeExactMapping(siftsMapping, seq);
581 maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS",
582 pdb.getId().toLowerCase(Locale.ROOT));
583 maxChain.transferResidueAnnotation(siftsMapping, null);
584 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
586 } catch (SiftsException e)
588 // fall back to NW alignment
589 Console.error(e.getMessage());
590 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
591 targetChainId, maxChain, pdb, maxAlignseq);
592 seqToStrucMapping.add(nwMapping);
593 maxChain.makeExactMapping(maxAlignseq, seq);
594 maxChain.transferRESNUMFeatures(seq, "IEA:Jalview",
595 pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is
598 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
599 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
604 List<StructureMapping> foundSiftsMappings = new ArrayList<>();
605 for (PDBChain chain : pdb.getChains())
607 StructureMapping siftsMapping = null;
610 siftsMapping = getStructureMapping(seq, pdbFile, chain.id,
611 pdb, chain, sqmpping, maxAlignseq, siftsClient);
612 foundSiftsMappings.add(siftsMapping);
613 chain.makeExactMapping(siftsMapping, seq);
614 chain.transferRESNUMFeatures(seq, "IEA: SIFTS",
615 pdb.getId().toLowerCase(Locale.ROOT));// FIXME: is this
617 chain.transferResidueAnnotation(siftsMapping, null);
618 } catch (SiftsException e)
620 System.err.println(e.getMessage());
621 } catch (Exception e)
624 "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
625 System.err.println(e.getMessage());
628 if (!foundSiftsMappings.isEmpty())
630 seqToStrucMapping.addAll(foundSiftsMappings);
631 ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
635 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
636 maxChainId, maxChain, pdb, maxAlignseq);
637 seqToStrucMapping.add(nwMapping);
638 maxChain.transferRESNUMFeatures(seq, null,
639 pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is this
641 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
642 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
648 if (progress != null)
650 progress.setProgressBar(
651 MessageManager.getString(
652 "status.obtaining_mapping_with_nw_alignment"),
655 StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
656 maxChain, pdb, maxAlignseq);
657 seqToStrucMapping.add(nwMapping);
658 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
660 if (forStructureView)
662 for (StructureMapping sm : seqToStrucMapping)
664 addStructureMapping(sm); // not addAll!
667 if (progress != null)
669 progress.setProgressBar(null, progressSessionId);
676 * check if we need to extract secondary structure from given pdbFile and
677 * transfer to sequences
680 * @param sequenceArray
683 private boolean isStructureFileProcessed(String pdbFile,
684 SequenceI[] sequenceArray)
686 boolean processed = false;
687 if (isPDBFileRegistered(pdbFile))
689 for (SequenceI sq : sequenceArray)
692 while (ds.getDatasetSequence() != null)
694 ds = ds.getDatasetSequence();
697 if (ds.getAnnotation() != null)
699 for (AlignmentAnnotation ala : ds.getAnnotation())
701 // false if any annotation present from this structure
702 // JBPNote this fails for jmol/chimera view because the *file* is
703 // passed, not the structure data ID -
704 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
715 public void addStructureMapping(StructureMapping sm)
717 if (!mappings.contains(sm))
724 * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
729 * @param targetChainId
735 * client for retrieval of SIFTS mappings for this structure
737 * @throws SiftsException
739 private StructureMapping getStructureMapping(SequenceI seq,
740 String pdbFile, String targetChainId, StructureFile pdb,
741 PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
742 AlignSeq maxAlignseq, SiftsClient siftsClient)
743 throws SiftsException
745 StructureMapping curChainMapping = siftsClient
746 .getSiftsStructureMapping(seq, pdbFile, targetChainId);
749 PDBChain chain = pdb.findChain(targetChainId);
752 chain.transferResidueAnnotation(curChainMapping, null);
754 } catch (Exception e)
758 return curChainMapping;
761 private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
762 String maxChainId, PDBChain maxChain, StructureFile pdb,
763 AlignSeq maxAlignseq)
765 final StringBuilder mappingDetails = new StringBuilder(128);
766 mappingDetails.append(NEWLINE)
767 .append("Sequence \u27f7 Structure mapping details");
768 mappingDetails.append(NEWLINE);
770 .append("Method: inferred with Needleman & Wunsch alignment");
771 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
772 .append(NEWLINE).append("Sequence = ")
773 .append(maxChain.sequence.getSequenceAsString());
774 mappingDetails.append(NEWLINE).append("No of residues = ")
775 .append(maxChain.residues.size()).append(NEWLINE)
777 PrintStream ps = new PrintStream(System.out)
780 public void print(String x)
782 mappingDetails.append(x);
786 public void println()
788 mappingDetails.append(NEWLINE);
792 maxAlignseq.printAlignment(ps);
794 mappingDetails.append(NEWLINE).append("PDB start/end ");
795 mappingDetails.append(String.valueOf(maxAlignseq.seq2start))
797 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
798 mappingDetails.append(NEWLINE).append("SEQ start/end ");
801 .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1)))
803 mappingDetails.append(
804 String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1)));
805 mappingDetails.append(NEWLINE);
806 maxChain.makeExactMapping(maxAlignseq, seq);
807 jalview.datamodel.Mapping sqmpping = maxAlignseq
808 .getMappingFromS1(false);
809 maxChain.transferRESNUMFeatures(seq, null,
810 pdb.getId().toLowerCase(Locale.ROOT));
812 HashMap<Integer, int[]> mapping = new HashMap<>();
819 Atom tmp = maxChain.atoms.elementAt(index);
820 if ((resNum != tmp.resNumber || insCode != tmp.insCode)
821 && tmp.alignmentMapping != -1)
823 resNum = tmp.resNumber;
824 insCode = tmp.insCode;
825 if (tmp.alignmentMapping >= -1)
827 mapping.put(tmp.alignmentMapping + 1,
829 { tmp.resNumber, tmp.atomIndex });
834 } while (index < maxChain.atoms.size());
836 StructureMapping nwMapping = new StructureMapping(seq, pdbFile,
837 pdb.getId(), maxChainId, mapping, mappingDetails.toString());
838 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
842 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
844 listeners.removeElement(svl);
845 if (svl instanceof SequenceListener)
847 for (int i = 0; i < listeners.size(); i++)
849 if (listeners.elementAt(i) instanceof StructureListener)
851 ((StructureListener) listeners.elementAt(i))
852 .releaseReferences(svl);
857 if (pdbfiles == null)
863 * Remove mappings to the closed listener's PDB files, but first check if
864 * another listener is still interested
866 List<String> pdbs = new ArrayList<>(Arrays.asList(pdbfiles));
868 StructureListener sl;
869 for (int i = 0; i < listeners.size(); i++)
871 if (listeners.elementAt(i) instanceof StructureListener)
873 sl = (StructureListener) listeners.elementAt(i);
874 for (String pdbfile : sl.getStructureFiles())
876 pdbs.remove(pdbfile);
882 * Rebuild the mappings set, retaining only those which are for 'other' PDB
887 List<StructureMapping> tmp = new ArrayList<>();
888 for (StructureMapping sm : mappings)
890 if (!pdbs.contains(sm.pdbfile))
901 * hack to highlight a range of positions at once on any structure views
905 * - series of int start-end ranges as positions on sequenceRef
909 public void highlightPositionsOn(SequenceI sequenceRef, int[][] is,
912 boolean hasSequenceListeners = handlingVamsasMo
913 || !seqmappings.isEmpty();
914 SearchResultsI results = null;
915 ArrayList<Integer> listOfPositions = new ArrayList<Integer>();
918 for (int p = s_e[0]; p <= s_e[1]; listOfPositions.add(p++))
921 int seqpos[] = new int[listOfPositions.size()];
923 for (Integer p : listOfPositions)
928 for (i = 0; i < listeners.size(); i++)
930 Object listener = listeners.elementAt(i);
931 if (listener == source)
933 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
934 // Temporary fudge with SequenceListener.getVamsasSource()
937 if (listener instanceof StructureListener)
939 highlightStructure((StructureListener) listener, sequenceRef,
947 * Propagate mouseover of a single position in a structure
954 public String mouseOverStructure(int pdbResNum, String chain,
957 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
958 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
959 return mouseOverStructure(atoms);
963 * Propagate mouseover or selection of multiple positions in a structure
967 public String mouseOverStructure(List<AtomSpec> atoms)
969 if (listeners == null)
971 // old or prematurely sent event
974 boolean hasSequenceListener = false;
975 for (int i = 0; i < listeners.size(); i++)
977 if (listeners.elementAt(i) instanceof SequenceListener)
979 hasSequenceListener = true;
982 if (!hasSequenceListener)
987 SearchResultsI results = findAlignmentPositionsForStructurePositions(
989 String result = null;
990 for (Object li : listeners)
992 if (li instanceof SequenceListener)
994 String s = ((SequenceListener) li).highlightSequence(results);
1005 * Constructs a SearchResults object holding regions (if any) in the Jalview
1006 * alignment which have a mapping to the structure viewer positions in the
1012 public SearchResultsI findAlignmentPositionsForStructurePositions(
1013 List<AtomSpec> atoms)
1015 SearchResultsI results = new SearchResults();
1016 for (AtomSpec atom : atoms)
1018 SequenceI lastseq = null;
1020 for (StructureMapping sm : mappings)
1022 if (sm.pdbfile.equals(atom.getPdbFile())
1023 && sm.pdbchain.equals(atom.getChain()))
1025 int indexpos = sm.getSeqPos(atom.getPdbResNum());
1026 if (lastipos != indexpos || lastseq != sm.sequence)
1028 results.addResult(sm.sequence, indexpos, indexpos);
1029 lastipos = indexpos;
1030 lastseq = sm.sequence;
1031 // construct highlighted sequence list
1032 for (AlignedCodonFrame acf : seqmappings)
1034 acf.markMappedRegion(sm.sequence, indexpos, results);
1044 * highlight regions associated with a position (indexpos) in seq
1047 * the sequence that the mouse over occurred on
1049 * the absolute position being mouseovered in seq (0 to seq.length())
1051 * the sequence position (if -1, seq.findPosition is called to
1052 * resolve the residue number)
1054 public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
1055 VamsasSource source)
1057 boolean hasSequenceListeners = handlingVamsasMo
1058 || !seqmappings.isEmpty();
1059 SearchResultsI results = null;
1062 seqPos = seq.findPosition(indexpos);
1064 for (int i = 0; i < listeners.size(); i++)
1066 Object listener = listeners.elementAt(i);
1067 if (listener == source)
1069 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1070 // Temporary fudge with SequenceListener.getVamsasSource()
1073 if (listener instanceof StructureListener)
1075 highlightStructure((StructureListener) listener, seq, seqPos);
1079 if (listener instanceof SequenceListener)
1081 final SequenceListener seqListener = (SequenceListener) listener;
1082 if (hasSequenceListeners
1083 && seqListener.getVamsasSource() != source)
1085 if (relaySeqMappings)
1087 if (results == null)
1089 results = MappingUtils.buildSearchResults(seq, seqPos,
1092 if (handlingVamsasMo)
1094 results.addResult(seq, seqPos, seqPos);
1097 if (!results.isEmpty())
1099 seqListener.highlightSequence(results);
1104 else if (listener instanceof VamsasListener && !handlingVamsasMo)
1106 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
1109 else if (listener instanceof SecondaryStructureListener)
1111 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
1119 * Send suitable messages to a StructureListener to highlight atoms
1120 * corresponding to the given sequence position(s)
1126 public void highlightStructure(StructureListener sl, SequenceI seq,
1129 if (!sl.isListeningFor(seq))
1134 List<AtomSpec> atoms = new ArrayList<>();
1135 for (StructureMapping sm : mappings)
1137 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1138 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1139 .getDatasetSequence() == seq.getDatasetSequence()))
1141 for (int index : positions)
1143 atomNo = sm.getAtomNum(index);
1147 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1148 sm.getPDBResNum(index), atomNo));
1153 sl.highlightAtoms(atoms);
1156 public void highlightStructureRegionsFor(StructureListener sl,
1157 SequenceI[] seqs, int... columns)
1159 List<SequenceI> to_highlight = new ArrayList<SequenceI>();
1160 for (SequenceI seq : seqs)
1162 if (sl.isListeningFor(seq))
1164 to_highlight.add(seq);
1167 if (to_highlight.size() == 0)
1171 List<AtomSpec> atoms = new ArrayList<>();
1172 for (SequenceI seq : to_highlight)
1175 for (StructureMapping sm : mappings)
1177 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1178 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1179 .getDatasetSequence() == seq.getDatasetSequence()))
1182 for (int i = 0; i < columns.length; i += 2)
1184 ContiguousI positions = seq.findPositions(columns[i] + 1,
1185 columns[i + 1] + 1);
1186 if (positions == null)
1190 for (int index = positions.getBegin(); index <= positions
1194 atomNo = sm.getAtomNum(index);
1198 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1199 sm.getPDBResNum(index), atomNo));
1205 if (atoms.size() > 0)
1207 sl.highlightAtoms(atoms);
1213 * true if a mouse over event from an external (ie Vamsas) source is being
1216 boolean handlingVamsasMo = false;
1221 * as mouseOverSequence but only route event to SequenceListeners
1225 * in an alignment sequence
1227 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
1228 VamsasSource source)
1230 handlingVamsasMo = true;
1231 long msg = sequenceI.hashCode() * (1 + position);
1235 mouseOverSequence(sequenceI, position, -1, source);
1237 handlingVamsasMo = false;
1240 public Annotation[] colourSequenceFromStructure(SequenceI seq,
1244 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
1245 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
1247 * Annotation [] annotations = new Annotation[seq.getLength()];
1249 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
1250 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
1251 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
1253 * for (int j = 0; j < mappings.length; j++) {
1255 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
1256 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
1257 * System.out.println(pdbid+" "+mappings[j].getPdbId() +"
1258 * "+mappings[j].pdbfile);
1260 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
1261 * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
1263 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
1264 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
1265 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
1266 * mappings[j].pdbfile); }
1268 * annotations[index] = new Annotation("X",null,' ',0,col); } return
1269 * annotations; } } } }
1271 * return annotations;
1275 public void structureSelectionChanged()
1279 public void sequenceSelectionChanged()
1283 public void sequenceColoursChanged(Object source)
1285 StructureListener sl;
1286 for (int i = 0; i < listeners.size(); i++)
1288 if (listeners.elementAt(i) instanceof StructureListener)
1290 sl = (StructureListener) listeners.elementAt(i);
1291 sl.updateColours(source);
1296 public StructureMapping[] getMapping(String pdbfile)
1298 List<StructureMapping> tmp = new ArrayList<>();
1299 for (StructureMapping sm : mappings)
1301 if (sm.pdbfile.equals(pdbfile))
1306 return tmp.toArray(new StructureMapping[tmp.size()]);
1310 * Returns a readable description of all mappings for the given pdbfile to any
1311 * of the given sequences
1317 public String printMappings(String pdbfile, List<SequenceI> seqs)
1319 if (pdbfile == null || seqs == null || seqs.isEmpty())
1324 StringBuilder sb = new StringBuilder(64);
1325 for (StructureMapping sm : mappings)
1327 if (Platform.pathEquals(sm.pdbfile, pdbfile)
1328 && seqs.contains(sm.sequence))
1330 sb.append(sm.mappingDetails);
1332 // separator makes it easier to read multiple mappings
1333 sb.append("=====================");
1339 return sb.toString();
1343 * Remove the given mapping
1347 public void deregisterMapping(AlignedCodonFrame acf)
1351 boolean removed = seqmappings.remove(acf);
1352 if (removed && seqmappings.isEmpty())
1354 System.out.println("All mappings removed");
1360 * Add each of the given codonFrames to the stored set, if not aready present.
1364 public void registerMappings(List<AlignedCodonFrame> mappings)
1366 if (mappings != null)
1368 for (AlignedCodonFrame acf : mappings)
1370 registerMapping(acf);
1376 * Add the given mapping to the stored set, unless already stored.
1378 public void registerMapping(AlignedCodonFrame acf)
1382 if (!seqmappings.contains(acf))
1384 seqmappings.add(acf);
1390 * Resets this object to its initial state by removing all registered
1391 * listeners, codon mappings, PDB file mappings
1393 public void resetAll()
1395 if (mappings != null)
1399 if (seqmappings != null)
1401 seqmappings.clear();
1403 if (sel_listeners != null)
1405 sel_listeners.clear();
1407 if (listeners != null)
1411 if (commandListeners != null)
1413 commandListeners.clear();
1415 if (view_listeners != null)
1417 view_listeners.clear();
1419 if (pdbFileNameId != null)
1421 pdbFileNameId.clear();
1423 if (pdbIdFileName != null)
1425 pdbIdFileName.clear();
1429 public void addSelectionListener(SelectionListener selecter)
1431 if (!sel_listeners.contains(selecter))
1433 sel_listeners.add(selecter);
1437 public void removeSelectionListener(SelectionListener toremove)
1439 if (sel_listeners.contains(toremove))
1441 sel_listeners.remove(toremove);
1445 public synchronized void sendSelection(
1446 jalview.datamodel.SequenceGroup selection,
1447 jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden,
1448 SelectionSource source)
1450 for (SelectionListener slis : sel_listeners)
1454 slis.selection(selection, colsel, hidden, source);
1459 Vector<AlignmentViewPanelListener> view_listeners = new Vector<>();
1461 public synchronized void sendViewPosition(
1462 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1463 int startSeq, int endSeq)
1466 if (view_listeners != null && view_listeners.size() > 0)
1468 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1470 while (listeners.hasMoreElements())
1472 AlignmentViewPanelListener slis = listeners.nextElement();
1475 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1483 * release all references associated with this manager provider
1485 * @param jalviewLite
1487 public static void release(StructureSelectionManagerProvider jalviewLite)
1489 // synchronized (instances)
1491 if (instances == null)
1495 StructureSelectionManager mnger = (instances.get(jalviewLite));
1498 instances.remove(jalviewLite);
1501 /* bsoares 2019-03-20 finalize deprecated, no apparent external
1502 * resources to close
1504 // mnger.finalize();
1505 } catch (Throwable x)
1512 public void registerPDBEntry(PDBEntry pdbentry)
1514 if (pdbentry.getFile() != null
1515 && pdbentry.getFile().trim().length() > 0)
1517 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1521 public void addCommandListener(CommandListener cl)
1523 if (!commandListeners.contains(cl))
1525 commandListeners.add(cl);
1529 public boolean hasCommandListener(CommandListener cl)
1531 return this.commandListeners.contains(cl);
1534 public boolean removeCommandListener(CommandListener l)
1536 return commandListeners.remove(l);
1540 * Forward a command to any command listeners (except for the command's
1544 * the command to be broadcast (in its form after being performed)
1546 * if true, the command was being 'undone'
1549 public void commandPerformed(CommandI command, boolean undo,
1550 VamsasSource source)
1552 for (CommandListener listener : commandListeners)
1554 listener.mirrorCommand(command, undo, this, source);
1559 * Returns a new CommandI representing the given command as mapped to the
1560 * given sequences. If no mapping could be made, or the command is not of a
1561 * mappable kind, returns null.
1569 public CommandI mapCommand(CommandI command, boolean undo,
1570 final AlignmentI mapTo, char gapChar)
1572 if (command instanceof EditCommand)
1574 return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo,
1575 gapChar, seqmappings);
1577 else if (command instanceof OrderCommand)
1579 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1580 mapTo, seqmappings);
1585 public List<AlignedCodonFrame> getSequenceMappings()
1591 * quick and dirty route to just highlight all structure positions for a range
1594 * @param sequencesArray
1596 * start-end columns on sequencesArray
1598 * origin parent AlignmentPanel
1600 public void highlightPositionsOnMany(SequenceI[] sequencesArray, int[] is,
1603 for (int i = 0; i < listeners.size(); i++)
1605 Object listener = listeners.elementAt(i);
1606 if (listener == source)
1608 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1609 // Temporary fudge with SequenceListener.getVamsasSource()
1612 if (listener instanceof StructureListener)
1614 highlightStructureRegionsFor((StructureListener) listener,
1615 sequencesArray, is);
1620 public Map<String, String> getPdbFileNameIdMap()
1622 return pdbFileNameId;
1625 public Map<String, String> getPdbIdFileNameMap()
1627 return pdbIdFileName;