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.Cache;
38 import jalview.bin.Console;
39 import jalview.commands.CommandI;
40 import jalview.commands.EditCommand;
41 import jalview.commands.OrderCommand;
42 import jalview.datamodel.AlignedCodonFrame;
43 import jalview.datamodel.AlignmentAnnotation;
44 import jalview.datamodel.AlignmentI;
45 import jalview.datamodel.Annotation;
46 import jalview.datamodel.ContiguousI;
47 import jalview.datamodel.HiddenColumns;
48 import jalview.datamodel.PDBEntry;
49 import jalview.datamodel.SearchResultMatchI;
50 import jalview.datamodel.SearchResults;
51 import jalview.datamodel.SearchResultsI;
52 import jalview.datamodel.SequenceI;
53 import jalview.ext.jmol.JmolParser;
54 import jalview.gui.IProgressIndicator;
55 import jalview.gui.Preferences;
56 import jalview.io.AppletFormatAdapter;
57 import jalview.io.DataSourceType;
58 import jalview.io.StructureFile;
59 import jalview.structure.StructureImportSettings.TFType;
60 import jalview.util.MappingUtils;
61 import jalview.util.MessageManager;
62 import jalview.util.Platform;
63 import jalview.ws.sifts.SiftsClient;
64 import jalview.ws.sifts.SiftsException;
65 import jalview.ws.sifts.SiftsSettings;
67 import mc_view.PDBChain;
68 import mc_view.PDBfile;
70 public class StructureSelectionManager
72 public final static String NEWLINE = System.lineSeparator();
74 static IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> instances;
76 private List<StructureMapping> mappings = new ArrayList<>();
78 private boolean processSecondaryStructure = false;
80 private boolean secStructServices = false;
82 private boolean addTempFacAnnot = false;
85 * Set of any registered mappings between (dataset) sequences.
87 private List<AlignedCodonFrame> seqmappings = new ArrayList<>();
89 private List<CommandListener> commandListeners = new ArrayList<>();
91 private List<SelectionListener> sel_listeners = new ArrayList<>();
94 * @return true if will try to use external services for processing secondary
97 public boolean isSecStructServices()
99 return secStructServices;
103 * control use of external services for processing secondary structure
105 * @param secStructServices
107 public void setSecStructServices(boolean secStructServices)
109 this.secStructServices = secStructServices;
113 * flag controlling addition of any kind of structural annotation
115 * @return true if temperature factor annotation will be added
117 public boolean isAddTempFacAnnot()
119 return addTempFacAnnot;
123 * set flag controlling addition of structural annotation
125 * @param addTempFacAnnot
127 public void setAddTempFacAnnot(boolean addTempFacAnnot)
129 this.addTempFacAnnot = addTempFacAnnot;
134 * @return if true, the structure manager will attempt to add secondary
135 * structure lines for unannotated sequences
138 public boolean isProcessSecondaryStructure()
140 return processSecondaryStructure;
144 * Control whether structure manager will try to annotate mapped sequences
145 * with secondary structure from PDB data.
149 public void setProcessSecondaryStructure(boolean enable)
151 processSecondaryStructure = enable;
155 * debug function - write all mappings to stdout
157 public void reportMapping()
159 if (mappings.isEmpty())
162 .errPrintln("reportMapping: No PDB/Sequence mappings.");
166 jalview.bin.Console.errPrintln(
167 "reportMapping: There are " + mappings.size() + " mappings.");
169 for (StructureMapping sm : mappings)
172 .errPrintln("mapping " + i++ + " : " + sm.pdbfile);
178 * map between the PDB IDs (or structure identifiers) used by Jalview and the
179 * absolute filenames for PDB data that corresponds to it
181 Map<String, String> pdbIdFileName = new HashMap<>();
183 Map<String, String> pdbFileNameId = new HashMap<>();
185 public void registerPDBFile(String idForFile, String absoluteFile)
187 pdbIdFileName.put(idForFile, absoluteFile);
188 pdbFileNameId.put(absoluteFile, idForFile);
191 public String findIdForPDBFile(String idOrFile)
193 String id = pdbFileNameId.get(idOrFile);
197 public String findFileForPDBId(String idOrFile)
199 String id = pdbIdFileName.get(idOrFile);
203 public boolean isPDBFileRegistered(String idOrFile)
205 return pdbFileNameId.containsKey(idOrFile)
206 || pdbIdFileName.containsKey(idOrFile);
209 private static StructureSelectionManager nullProvider = null;
211 public static StructureSelectionManager getStructureSelectionManager(
212 StructureSelectionManagerProvider context)
216 if (nullProvider == null)
218 if (instances != null)
220 throw new Error(MessageManager.getString(
221 "error.implementation_error_structure_selection_manager_null"),
222 new NullPointerException(MessageManager
223 .getString("exception.ssm_context_is_null")));
227 nullProvider = new StructureSelectionManager();
232 if (instances == null)
234 instances = new java.util.IdentityHashMap<>();
236 StructureSelectionManager instance = instances.get(context);
237 if (instance == null)
239 if (nullProvider != null)
241 instance = nullProvider;
245 instance = new StructureSelectionManager();
247 instances.put(context, instance);
253 * flag controlling whether SeqMappings are relayed from received sequence
254 * mouse over events to other sequences
256 boolean relaySeqMappings = true;
259 * Enable or disable relay of seqMapping events to other sequences. You might
260 * want to do this if there are many sequence mappings and the host computer
265 public void setRelaySeqMappings(boolean relay)
267 relaySeqMappings = relay;
271 * get the state of the relay seqMappings flag.
273 * @return true if sequence mouse overs are being relayed to other mapped
276 public boolean isRelaySeqMappingsEnabled()
278 return relaySeqMappings;
281 Vector listeners = new Vector();
284 * register a listener for alignment sequence mouseover events
288 public void addStructureViewerListener(Object svl)
290 if (!listeners.contains(svl))
292 listeners.addElement(svl);
297 * Returns the filename the PDB id is already mapped to if known, or null if
303 public String alreadyMappedToFile(String pdbid)
305 for (StructureMapping sm : mappings)
307 if (sm.getPdbId().equalsIgnoreCase(pdbid))
316 * Import structure data and register a structure mapping for broadcasting
317 * colouring, mouseovers and selection events (convenience wrapper).
320 * - one or more sequences to be mapped to pdbFile
321 * @param targetChains
322 * - optional chain specification for mapping each sequence to pdb
323 * (may be nill, individual elements may be nill)
325 * - structure data resource
327 * - how to resolve data from resource
328 * @return null or the structure data parsed as a pdb file
330 synchronized public StructureFile setMapping(SequenceI[] sequence,
331 String[] targetChains, String pdbFile, DataSourceType protocol,
332 IProgressIndicator progress)
334 return computeMapping(true, sequence, targetChains, pdbFile, protocol,
335 progress, null, null, true);
339 * Import a single structure file and register sequence structure mappings for
340 * broadcasting colouring, mouseovers and selection events (convenience
343 * @param forStructureView
344 * when true, record the mapping for use in mouseOvers
346 * - one or more sequences to be mapped to pdbFile
347 * @param targetChains
348 * - optional chain specification for mapping each sequence to pdb
349 * (may be nill, individual elements may be nill)
351 * - structure data resource
353 * - how to resolve data from resource
354 * @return null or the structure data parsed as a pdb file
356 synchronized public StructureFile setMapping(boolean forStructureView,
357 SequenceI[] sequenceArray, String[] targetChainIds,
358 String pdbFile, DataSourceType sourceType, TFType tft,
361 return setMapping(forStructureView, sequenceArray, targetChainIds,
362 pdbFile, sourceType, tft, paeFilename, true);
366 * create sequence structure mappings between each sequence and the given
367 * pdbFile (retrieved via the given protocol). Either constructs a mapping
368 * using NW alignment or derives one from any available SIFTS mapping data.
370 * @param forStructureView
371 * when true, record the mapping for use in mouseOvers
373 * @param sequenceArray
374 * - one or more sequences to be mapped to pdbFile
375 * @param targetChainIds
376 * - optional chain specification for mapping each sequence to pdb
377 * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
378 * - this should be List<List<String>>, empty lists indicate no
379 * predefined mappings
381 * - structure data resource
383 * - how to resolve data from resource
385 * - specify how to interpret the temperature factor column in the
388 * - when not null, specifies a filename containing a matrix
389 * formatted in JSON using one of the known PAE formats
390 * @param doXferSettings
391 * - when true, transfer annotation to mapped sequences in
393 * @return null or the structure data parsed as a pdb file
395 synchronized public StructureFile setMapping(boolean forStructureView,
396 SequenceI[] sequenceArray, String[] targetChainIds,
397 String pdbFile, DataSourceType sourceType, TFType tft,
398 String paeFilename, boolean doXferSettings)
400 return computeMapping(forStructureView, sequenceArray, targetChainIds,
401 pdbFile, sourceType, null, tft, paeFilename, doXferSettings);
405 * create sequence structure mappings between each sequence and the given
406 * pdbFile (retrieved via the given protocol). Either constructs a mapping
407 * using NW alignment or derives one from any available SIFTS mapping data.
409 * @param forStructureView
410 * when true, record the mapping for use in mouseOvers
412 * @param sequenceArray
413 * - one or more sequences to be mapped to pdbFile
414 * @param targetChainIds
415 * - optional chain specification for mapping each sequence to pdb
416 * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
417 * - this should be List<List<String>>, empty lists indicate no
418 * predefined mappings
420 * - structure data resource
422 * - how to resolve data from resource
423 * @param IProgressIndicator
424 * reference to UI component that maintains a progress bar for the
427 * - specify how to interpret the temperature factor column in the
430 * - when not null, specifies a filename containing a matrix
431 * formatted in JSON using one of the known PAE formats
432 * @param doXferSettings
433 * - when true, transfer annotation to mapped sequences in
435 * @return null or the structure data parsed as a pdb file
437 synchronized public StructureFile computeMapping(boolean forStructureView,
438 SequenceI[] sequenceArray, String[] targetChainIds,
439 String pdbFile, DataSourceType sourceType,
440 IProgressIndicator progress, TFType tft, String paeFilename,
441 boolean doXferSettings)
443 long progressSessionId = System.currentTimeMillis() * 3;
446 * do we extract and transfer annotation from 3D data ?
448 // FIXME: possibly should just delete
450 boolean parseSecStr = processSecondaryStructure
451 && !isStructureFileProcessed(pdbFile, sequenceArray);
453 StructureFile pdb = null;
454 boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
457 // FIXME if sourceType is not null, we've lost data here
458 sourceType = AppletFormatAdapter.checkProtocol(pdbFile);
459 pdb = new JmolParser(false, pdbFile, sourceType);
460 if (paeFilename != null)
462 pdb.setPAEMatrix(paeFilename);
464 pdb.setTemperatureFactorType(tft);
465 pdb.addSettings(parseSecStr && processSecondaryStructure,
466 parseSecStr && addTempFacAnnot,
467 parseSecStr && secStructServices);
468 // save doXferSettings and reset after doParse()
469 boolean temp = pdb.getDoXferSettings();
470 pdb.setDoXferSettings(doXferSettings);
472 pdb.setDoXferSettings(temp);
473 if (pdb.getId() != null && pdb.getId().trim().length() > 0
474 && DataSourceType.FILE == sourceType)
476 registerPDBFile(pdb.getId().trim(), pdbFile);
478 // if PDBId is unavailable then skip SIFTS mapping execution path
479 // TODO: JAL-3868 need to know if structure is actually from
480 // PDB (has valid PDB ID and has provenance suggesting it
481 // actually came from PDB)
482 boolean isProtein = false;
483 for (SequenceI s : sequenceArray)
491 isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable()
492 && !pdb.getId().startsWith("AF-") && isProtein;
494 } catch (Exception ex)
496 ex.printStackTrace();
500 * sifts client - non null if SIFTS mappings are to be used
502 SiftsClient siftsClient = null;
507 siftsClient = new SiftsClient(pdb);
509 } catch (SiftsException e)
511 isMapUsingSIFTs = false;
512 Console.error("SIFTS mapping failed", e);
513 Console.error("Falling back on Needleman & Wunsch alignment");
517 String targetChainId;
518 for (int s = 0; s < sequenceArray.length; s++)
520 boolean infChain = true;
521 final SequenceI seq = sequenceArray[s];
523 while (ds.getDatasetSequence() != null)
525 ds = ds.getDatasetSequence();
528 if (targetChainIds != null && targetChainIds[s] != null)
531 targetChainId = targetChainIds[s];
533 else if (seq.getName().indexOf("|") > -1)
535 targetChainId = seq.getName()
536 .substring(seq.getName().lastIndexOf("|") + 1);
537 if (targetChainId.length() > 1)
539 if (targetChainId.trim().length() == 0)
545 // not a valid chain identifier
556 * Attempt pairwise alignment of the sequence with each chain in the PDB,
557 * and remember the highest scoring chain
560 AlignSeq maxAlignseq = null;
561 String maxChainId = " ";
562 PDBChain maxChain = null;
563 boolean first = true;
564 for (PDBChain chain : pdb.getChains())
566 if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
569 continue; // don't try to map chains don't match.
571 // TODO: correctly determine sequence type for mixed na/peptide
573 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
574 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
577 // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
578 // as.calcScoreMatrix();
579 // as.traceAlignment();
581 if (first || as.maxscore > max
582 || (as.maxscore == max && chain.id.equals(targetChainId)))
588 maxChainId = chain.id;
591 if (maxChain == null)
596 if (sourceType == DataSourceType.PASTE)
598 pdbFile = "INLINE" + pdb.getId();
601 List<StructureMapping> seqToStrucMapping = new ArrayList<>();
602 if (isMapUsingSIFTs && seq.isProtein())
604 if (progress != null)
606 progress.setProgressBar(
608 .getString("status.obtaining_mapping_with_sifts"),
611 jalview.datamodel.Mapping sqmpping = maxAlignseq
612 .getMappingFromS1(false);
613 if (targetChainId != null && !targetChainId.trim().isEmpty())
615 StructureMapping siftsMapping;
618 siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
619 pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
620 seqToStrucMapping.add(siftsMapping);
621 maxChain.makeExactMapping(siftsMapping, seq);
622 maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS",
623 pdb.getId().toLowerCase(Locale.ROOT));
624 maxChain.transferResidueAnnotation(siftsMapping, null);
625 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
627 } catch (SiftsException e)
629 // fall back to NW alignment
630 Console.error(e.getMessage());
631 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
632 targetChainId, maxChain, pdb, maxAlignseq);
633 seqToStrucMapping.add(nwMapping);
634 maxChain.makeExactMapping(maxAlignseq, seq);
635 maxChain.transferRESNUMFeatures(seq, "IEA:Jalview",
636 pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is
639 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
640 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
645 List<StructureMapping> foundSiftsMappings = new ArrayList<>();
646 for (PDBChain chain : pdb.getChains())
648 StructureMapping siftsMapping = null;
651 siftsMapping = getStructureMapping(seq, pdbFile, chain.id,
652 pdb, chain, sqmpping, maxAlignseq, siftsClient);
653 foundSiftsMappings.add(siftsMapping);
654 chain.makeExactMapping(siftsMapping, seq);
655 chain.transferRESNUMFeatures(seq, "IEA: SIFTS",
656 pdb.getId().toLowerCase(Locale.ROOT));// FIXME: is this
658 chain.transferResidueAnnotation(siftsMapping, null);
659 } catch (SiftsException e)
661 jalview.bin.Console.errPrintln(e.getMessage());
662 } catch (Exception e)
664 jalview.bin.Console.errPrintln(
665 "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
666 jalview.bin.Console.errPrintln(e.getMessage());
669 if (!foundSiftsMappings.isEmpty())
671 seqToStrucMapping.addAll(foundSiftsMappings);
672 ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
676 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
677 maxChainId, maxChain, pdb, maxAlignseq);
678 seqToStrucMapping.add(nwMapping);
679 maxChain.transferRESNUMFeatures(seq, null,
680 pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is this
682 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
683 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
689 if (progress != null)
691 progress.setProgressBar(
692 MessageManager.getString(
693 "status.obtaining_mapping_with_nw_alignment"),
696 StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
697 maxChain, pdb, maxAlignseq);
698 seqToStrucMapping.add(nwMapping);
699 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
701 if (forStructureView)
703 for (StructureMapping sm : seqToStrucMapping)
705 addStructureMapping(sm); // not addAll!
708 if (progress != null)
710 progress.setProgressBar(null, progressSessionId);
717 * check if we need to extract secondary structure from given pdbFile and
718 * transfer to sequences
721 * @param sequenceArray
724 private boolean isStructureFileProcessed(String pdbFile,
725 SequenceI[] sequenceArray)
727 boolean processed = false;
728 if (isPDBFileRegistered(pdbFile))
730 for (SequenceI sq : sequenceArray)
733 while (ds.getDatasetSequence() != null)
735 ds = ds.getDatasetSequence();
738 if (ds.getAnnotation() != null)
740 for (AlignmentAnnotation ala : ds.getAnnotation())
742 // false if any annotation present from this structure
743 // JBPNote this fails for jmol/chimera view because the *file* is
744 // passed, not the structure data ID -
745 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
756 public void addStructureMapping(StructureMapping sm)
758 if (!mappings.contains(sm))
765 * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
770 * @param targetChainId
776 * client for retrieval of SIFTS mappings for this structure
778 * @throws SiftsException
780 private StructureMapping getStructureMapping(SequenceI seq,
781 String pdbFile, String targetChainId, StructureFile pdb,
782 PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
783 AlignSeq maxAlignseq, SiftsClient siftsClient)
784 throws SiftsException
786 StructureMapping curChainMapping = siftsClient
787 .getSiftsStructureMapping(seq, pdbFile, targetChainId);
790 PDBChain chain = pdb.findChain(targetChainId);
793 chain.transferResidueAnnotation(curChainMapping, null);
795 } catch (Exception e)
799 return curChainMapping;
802 private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
803 String maxChainId, PDBChain maxChain, StructureFile pdb,
804 AlignSeq maxAlignseq)
806 final StringBuilder mappingDetails = new StringBuilder(128);
807 mappingDetails.append(NEWLINE)
808 .append("Sequence \u27f7 Structure mapping details");
809 mappingDetails.append(NEWLINE);
811 .append("Method: inferred with Needleman & Wunsch alignment");
812 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
813 .append(NEWLINE).append("Sequence = ")
814 .append(maxChain.sequence.getSequenceAsString());
815 mappingDetails.append(NEWLINE).append("No of residues = ")
816 .append(maxChain.residues.size()).append(NEWLINE)
818 PrintStream ps = new PrintStream(System.out)
821 public void print(String x)
823 mappingDetails.append(x);
827 public void println()
829 mappingDetails.append(NEWLINE);
833 maxAlignseq.printAlignment(ps);
835 mappingDetails.append(NEWLINE).append("PDB start/end ");
836 mappingDetails.append(String.valueOf(maxAlignseq.seq2start))
838 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
839 mappingDetails.append(NEWLINE).append("SEQ start/end ");
842 .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1)))
844 mappingDetails.append(
845 String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1)));
846 mappingDetails.append(NEWLINE);
847 maxChain.makeExactMapping(maxAlignseq, seq);
848 jalview.datamodel.Mapping sqmpping = maxAlignseq
849 .getMappingFromS1(false);
850 maxChain.transferRESNUMFeatures(seq, null,
851 pdb.getId().toLowerCase(Locale.ROOT));
853 HashMap<Integer, int[]> mapping = new HashMap<>();
860 Atom tmp = maxChain.atoms.elementAt(index);
861 if ((resNum != tmp.resNumber || insCode != tmp.insCode)
862 && tmp.alignmentMapping != -1)
864 resNum = tmp.resNumber;
865 insCode = tmp.insCode;
866 if (tmp.alignmentMapping >= -1)
868 mapping.put(tmp.alignmentMapping + 1,
870 { tmp.resNumber, tmp.atomIndex });
875 } while (index < maxChain.atoms.size());
877 StructureMapping nwMapping = new StructureMapping(seq, pdbFile,
878 pdb.getId(), maxChainId, mapping, mappingDetails.toString());
879 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
883 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
885 listeners.removeElement(svl);
886 if (svl instanceof SequenceListener)
888 for (int i = 0; i < listeners.size(); i++)
890 if (listeners.elementAt(i) instanceof StructureListener)
892 ((StructureListener) listeners.elementAt(i))
893 .releaseReferences(svl);
898 if (pdbfiles == null)
904 * Remove mappings to the closed listener's PDB files, but first check if
905 * another listener is still interested
907 List<String> pdbs = new ArrayList<>(Arrays.asList(pdbfiles));
909 StructureListener sl;
910 for (int i = 0; i < listeners.size(); i++)
912 if (listeners.elementAt(i) instanceof StructureListener)
914 sl = (StructureListener) listeners.elementAt(i);
915 for (String pdbfile : sl.getStructureFiles())
917 pdbs.remove(pdbfile);
923 * Rebuild the mappings set, retaining only those which are for 'other' PDB
928 List<StructureMapping> tmp = new ArrayList<>();
929 for (StructureMapping sm : mappings)
931 if (!pdbs.contains(sm.pdbfile))
942 * hack to highlight a range of positions at once on any structure views
946 * - series of int start-end ranges as positions on sequenceRef
950 public void highlightPositionsOn(SequenceI sequenceRef, int[][] is,
953 boolean hasSequenceListeners = handlingVamsasMo
954 || !seqmappings.isEmpty();
955 SearchResultsI results = null;
956 ArrayList<Integer> listOfPositions = new ArrayList<Integer>();
959 for (int p = s_e[0]; p <= s_e[1]; listOfPositions.add(p++))
962 int seqpos[] = new int[listOfPositions.size()];
964 for (Integer p : listOfPositions)
969 for (i = 0; i < listeners.size(); i++)
971 Object listener = listeners.elementAt(i);
972 if (listener == source)
974 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
975 // Temporary fudge with SequenceListener.getVamsasSource()
978 if (listener instanceof StructureListener)
980 highlightStructure((StructureListener) listener, sequenceRef,
988 * Propagate mouseover of a single position in a structure
995 public String mouseOverStructure(int pdbResNum, String chain,
998 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
999 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
1000 return mouseOverStructure(atoms);
1004 * Propagate mouseover or selection of multiple positions in a structure
1008 public String mouseOverStructure(List<AtomSpec> atoms)
1010 if (listeners == null)
1012 // old or prematurely sent event
1015 boolean hasSequenceListener = false;
1016 for (int i = 0; i < listeners.size(); i++)
1018 if (listeners.elementAt(i) instanceof SequenceListener)
1020 hasSequenceListener = true;
1023 if (!hasSequenceListener)
1028 SearchResultsI results = findAlignmentPositionsForStructurePositions(
1030 String result = null;
1031 for (Object li : listeners)
1033 if (li instanceof SequenceListener)
1035 String s = ((SequenceListener) li).highlightSequence(results);
1046 * Constructs a SearchResults object holding regions (if any) in the Jalview
1047 * alignment which have a mapping to the structure viewer positions in the
1053 public SearchResultsI findAlignmentPositionsForStructurePositions(
1054 List<AtomSpec> atoms)
1056 SearchResultsI results = new SearchResults();
1057 for (AtomSpec atom : atoms)
1059 SequenceI lastseq = null;
1061 for (StructureMapping sm : mappings)
1063 if (sm.pdbfile.equals(atom.getPdbFile())
1064 && sm.pdbchain.equals(atom.getChain()))
1066 int indexpos = sm.getSeqPos(atom.getPdbResNum());
1067 if (lastipos != indexpos || lastseq != sm.sequence)
1069 results.appendResult(sm.sequence, indexpos, indexpos);
1070 lastipos = indexpos;
1071 lastseq = sm.sequence;
1072 // construct highlighted sequence list
1073 for (AlignedCodonFrame acf : seqmappings)
1075 acf.markMappedRegion(sm.sequence, indexpos, results);
1085 * highlight regions associated with a position (indexpos) in seq
1088 * the sequence that the mouse over occurred on
1090 * the absolute position being mouseovered in seq (0 to seq.length())
1092 * the sequence position (if -1, seq.findPosition is called to
1093 * resolve the residue number)
1095 public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
1096 VamsasSource source)
1098 boolean hasSequenceListeners = handlingVamsasMo
1099 || !seqmappings.isEmpty();
1100 SearchResultsI results = null;
1103 seqPos = seq.findPosition(indexpos);
1106 // precompute so we can also relay structure highlights
1107 if (results == null)
1109 results = MappingUtils.buildSearchResults(seq, seqPos,
1112 if (handlingVamsasMo)
1114 results.addResult(seq, seqPos, seqPos);
1118 for (int i = 0; i < listeners.size(); i++)
1120 Object listener = listeners.elementAt(i);
1121 if (listener == source)
1123 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1124 // Temporary fudge with SequenceListener.getVamsasSource()
1127 if (listener instanceof StructureListener)
1129 StructureListener sl = (StructureListener) listener;
1130 // TODO: consider merging highlightStructure variants second call
1131 // functionally same as first if seq/seqPos is part of the searchResults
1132 if (highlightStructure(sl, seq, seqPos)==0 && relaySeqMappings)
1134 // structure highlights for mapped sequences
1135 highlightStructure(sl,results);
1140 if (listener instanceof SequenceListener)
1142 final SequenceListener seqListener = (SequenceListener) listener;
1143 if (hasSequenceListeners
1144 && seqListener.getVamsasSource() != source)
1146 if (relaySeqMappings)
1148 if (!results.isEmpty())
1150 seqListener.highlightSequence(results);
1155 else if (listener instanceof VamsasListener && !handlingVamsasMo)
1157 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
1160 else if (listener instanceof SecondaryStructureListener)
1162 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
1170 * highlights positions in a structure viewer corresponding to one or more positions on sequences
1172 * @param searchResults
1173 * @return 0 or number of structure regions highlighted
1175 public int highlightStructure(StructureListener sl, SearchResultsI searchResults)
1178 List<AtomSpec> atoms = new ArrayList<>();
1180 for (SearchResultMatchI sr: searchResults.getResults())
1182 SequenceI seq = sr.getSequence();
1183 for (StructureMapping sm : mappings)
1185 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1186 || (sm.sequence.getDatasetSequence() != null
1187 && (sm.sequence.getDatasetSequence() == seq
1188 || sm.sequence.getDatasetSequence() == seq
1189 .getDatasetSequence())))
1191 for (int index=sr.getStart();index<=sr.getEnd();index++)
1193 atomNo = sm.getAtomNum(index);
1197 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1198 sm.getPDBResNum(index), atomNo));
1204 sl.highlightAtoms(atoms);
1205 return atoms.size();
1208 * Send suitable messages to a StructureListener to highlight atoms
1209 * corresponding to the given sequence position(s)
1214 * @return 0 if no atoms identified for position(s)
1216 public int highlightStructure(StructureListener sl, SequenceI seq,
1219 if (!sl.isListeningFor(seq))
1224 List<AtomSpec> atoms = new ArrayList<>();
1225 for (StructureMapping sm : mappings)
1227 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1228 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1229 .getDatasetSequence() == seq.getDatasetSequence()))
1231 for (int index : positions)
1233 atomNo = sm.getAtomNum(index);
1237 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1238 sm.getPDBResNum(index), atomNo));
1243 sl.highlightAtoms(atoms);
1244 return atoms.size();
1247 public void highlightStructureRegionsFor(StructureListener sl,
1248 SequenceI[] seqs, int... columns)
1250 List<SequenceI> to_highlight = new ArrayList<SequenceI>();
1251 for (SequenceI seq : seqs)
1253 if (sl.isListeningFor(seq))
1255 to_highlight.add(seq);
1258 if (to_highlight.size() == 0)
1262 List<AtomSpec> atoms = new ArrayList<>();
1263 for (SequenceI seq : to_highlight)
1266 for (StructureMapping sm : mappings)
1268 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1269 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1270 .getDatasetSequence() == seq.getDatasetSequence()))
1273 for (int i = 0; i < columns.length; i += 2)
1275 ContiguousI positions = seq.findPositions(columns[i] + 1,
1276 columns[i + 1] + 1);
1277 if (positions == null)
1281 for (int index = positions.getBegin(); index <= positions
1285 atomNo = sm.getAtomNum(index);
1289 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1290 sm.getPDBResNum(index), atomNo));
1296 if (atoms.size() > 0)
1298 sl.highlightAtoms(atoms);
1304 * true if a mouse over event from an external (ie Vamsas) source is being
1307 boolean handlingVamsasMo = false;
1312 * as mouseOverSequence but only route event to SequenceListeners
1316 * in an alignment sequence
1318 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
1319 VamsasSource source)
1321 handlingVamsasMo = true;
1322 long msg = sequenceI.hashCode() * (1 + position);
1326 mouseOverSequence(sequenceI, position, -1, source);
1328 handlingVamsasMo = false;
1331 public Annotation[] colourSequenceFromStructure(SequenceI seq,
1335 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
1336 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
1338 * Annotation [] annotations = new Annotation[seq.getLength()];
1340 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
1341 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
1342 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
1344 * for (int j = 0; j < mappings.length; j++) {
1346 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
1347 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
1348 * jalview.bin.Console.outPrintln(pdbid+" "+mappings[j].getPdbId() +"
1349 * "+mappings[j].pdbfile);
1351 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
1352 * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
1354 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
1355 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
1356 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
1357 * mappings[j].pdbfile); }
1359 * annotations[index] = new Annotation("X",null,' ',0,col); } return
1360 * annotations; } } } }
1362 * return annotations;
1366 public void structureSelectionChanged()
1370 public void sequenceSelectionChanged()
1374 public void sequenceColoursChanged(Object source)
1376 StructureListener sl;
1377 for (int i = 0; i < listeners.size(); i++)
1379 if (listeners.elementAt(i) instanceof StructureListener)
1381 sl = (StructureListener) listeners.elementAt(i);
1382 sl.updateColours(source);
1387 public StructureMapping[] getMapping(String pdbfile)
1389 List<StructureMapping> tmp = new ArrayList<>();
1390 for (StructureMapping sm : mappings)
1392 if (sm.pdbfile.equals(pdbfile))
1397 return tmp.toArray(new StructureMapping[tmp.size()]);
1401 * Returns a readable description of all mappings for the given pdbfile to any
1402 * of the given sequences
1408 public String printMappings(String pdbfile, List<SequenceI> seqs)
1410 if (pdbfile == null || seqs == null || seqs.isEmpty())
1415 StringBuilder sb = new StringBuilder(64);
1416 for (StructureMapping sm : mappings)
1418 if (Platform.pathEquals(sm.pdbfile, pdbfile)
1419 && seqs.contains(sm.sequence))
1421 sb.append(sm.mappingDetails);
1423 // separator makes it easier to read multiple mappings
1424 sb.append("=====================");
1430 return sb.toString();
1434 * Remove the given mapping
1438 public void deregisterMapping(AlignedCodonFrame acf)
1442 boolean removed = seqmappings.remove(acf);
1443 if (removed && seqmappings.isEmpty())
1445 jalview.bin.Console.outPrintln("All mappings removed");
1451 * Add each of the given codonFrames to the stored set, if not aready present.
1455 public void registerMappings(List<AlignedCodonFrame> mappings)
1457 if (mappings != null)
1459 for (AlignedCodonFrame acf : mappings)
1461 registerMapping(acf);
1467 * Add the given mapping to the stored set, unless already stored.
1469 public void registerMapping(AlignedCodonFrame acf)
1473 if (!seqmappings.contains(acf))
1475 seqmappings.add(acf);
1481 * Resets this object to its initial state by removing all registered
1482 * listeners, codon mappings, PDB file mappings
1484 public void resetAll()
1486 if (mappings != null)
1490 if (seqmappings != null)
1492 seqmappings.clear();
1494 if (sel_listeners != null)
1496 sel_listeners.clear();
1498 if (listeners != null)
1502 if (commandListeners != null)
1504 commandListeners.clear();
1506 if (view_listeners != null)
1508 view_listeners.clear();
1510 if (pdbFileNameId != null)
1512 pdbFileNameId.clear();
1514 if (pdbIdFileName != null)
1516 pdbIdFileName.clear();
1520 public void addSelectionListener(SelectionListener selecter)
1522 if (!sel_listeners.contains(selecter))
1524 sel_listeners.add(selecter);
1528 public void removeSelectionListener(SelectionListener toremove)
1530 if (sel_listeners.contains(toremove))
1532 sel_listeners.remove(toremove);
1536 public synchronized void sendSelection(
1537 jalview.datamodel.SequenceGroup selection,
1538 jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden,
1539 SelectionSource source)
1541 for (SelectionListener slis : sel_listeners)
1545 slis.selection(selection, colsel, hidden, source);
1550 Vector<AlignmentViewPanelListener> view_listeners = new Vector<>();
1552 public synchronized void sendViewPosition(
1553 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1554 int startSeq, int endSeq)
1557 if (view_listeners != null && view_listeners.size() > 0)
1559 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1561 while (listeners.hasMoreElements())
1563 AlignmentViewPanelListener slis = listeners.nextElement();
1566 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1574 * release all references associated with this manager provider
1576 * @param jalviewLite
1578 public static void release(StructureSelectionManagerProvider jalviewLite)
1580 // synchronized (instances)
1582 if (instances == null)
1586 StructureSelectionManager mnger = (instances.get(jalviewLite));
1589 instances.remove(jalviewLite);
1592 /* bsoares 2019-03-20 finalize deprecated, no apparent external
1593 * resources to close
1595 // mnger.finalize();
1596 } catch (Throwable x)
1603 public void registerPDBEntry(PDBEntry pdbentry)
1605 if (pdbentry.getFile() != null
1606 && pdbentry.getFile().trim().length() > 0)
1608 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1612 public void addCommandListener(CommandListener cl)
1614 if (!commandListeners.contains(cl))
1616 commandListeners.add(cl);
1620 public boolean hasCommandListener(CommandListener cl)
1622 return this.commandListeners.contains(cl);
1625 public boolean removeCommandListener(CommandListener l)
1627 return commandListeners.remove(l);
1631 * Forward a command to any command listeners (except for the command's
1635 * the command to be broadcast (in its form after being performed)
1637 * if true, the command was being 'undone'
1640 public void commandPerformed(CommandI command, boolean undo,
1641 VamsasSource source)
1643 for (CommandListener listener : commandListeners)
1645 listener.mirrorCommand(command, undo, this, source);
1650 * Returns a new CommandI representing the given command as mapped to the
1651 * given sequences. If no mapping could be made, or the command is not of a
1652 * mappable kind, returns null.
1660 public CommandI mapCommand(CommandI command, boolean undo,
1661 final AlignmentI mapTo, char gapChar)
1663 if (command instanceof EditCommand)
1665 return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo,
1666 gapChar, seqmappings);
1668 else if (command instanceof OrderCommand)
1670 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1671 mapTo, seqmappings);
1676 public List<AlignedCodonFrame> getSequenceMappings()
1682 * quick and dirty route to just highlight all structure positions for a range
1685 * @param sequencesArray
1687 * start-end columns on sequencesArray
1689 * origin parent AlignmentPanel
1691 public void highlightPositionsOnMany(SequenceI[] sequencesArray, int[] is,
1694 for (int i = 0; i < listeners.size(); i++)
1696 Object listener = listeners.elementAt(i);
1697 if (listener == source)
1699 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1700 // Temporary fudge with SequenceListener.getVamsasSource()
1703 if (listener instanceof StructureListener)
1705 highlightStructureRegionsFor((StructureListener) listener,
1706 sequencesArray, is);
1711 public Map<String, String> getPdbFileNameIdMap()
1713 return pdbFileNameId;
1716 public Map<String, String> getPdbIdFileNameMap()
1718 return pdbIdFileName;
1721 public static void doConfigureStructurePrefs(
1722 StructureSelectionManager ssm)
1724 doConfigureStructurePrefs(ssm,
1725 Cache.getDefault(Preferences.ADD_SS_ANN, true),
1726 Cache.getDefault(Preferences.ADD_TEMPFACT_ANN, true),
1727 Cache.getDefault(Preferences.STRUCT_FROM_PDB, true),
1728 Cache.getDefault(Preferences.USE_RNAVIEW, false));
1731 public static void doConfigureStructurePrefs(
1732 StructureSelectionManager ssm, boolean add_ss_ann,
1733 boolean add_tempfact_ann, boolean struct_from_pdb,
1734 boolean use_rnaview)
1738 ssm.setAddTempFacAnnot(add_tempfact_ann);
1739 ssm.setProcessSecondaryStructure(struct_from_pdb);
1740 // JAL-3915 - RNAView is no longer an option so this has no effect
1741 ssm.setSecStructServices(use_rnaview);
1745 ssm.setAddTempFacAnnot(false);
1746 ssm.setProcessSecondaryStructure(false);
1747 ssm.setSecStructServices(false);