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.SearchResults;
50 import jalview.datamodel.SearchResultsI;
51 import jalview.datamodel.SequenceI;
52 import jalview.ext.jmol.JmolParser;
53 import jalview.gui.IProgressIndicator;
54 import jalview.gui.Preferences;
55 import jalview.io.AppletFormatAdapter;
56 import jalview.io.DataSourceType;
57 import jalview.io.StructureFile;
58 import jalview.structure.StructureImportSettings.TFType;
59 import jalview.util.MappingUtils;
60 import jalview.util.MessageManager;
61 import jalview.util.Platform;
62 import jalview.ws.sifts.SiftsClient;
63 import jalview.ws.sifts.SiftsException;
64 import jalview.ws.sifts.SiftsSettings;
66 import mc_view.PDBChain;
67 import mc_view.PDBfile;
69 public class StructureSelectionManager
71 public final static String NEWLINE = System.lineSeparator();
73 static IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> instances;
75 private List<StructureMapping> mappings = new ArrayList<>();
77 private boolean processSecondaryStructure = false;
79 private boolean secStructServices = false;
81 private boolean addTempFacAnnot = false;
84 * Set of any registered mappings between (dataset) sequences.
86 private List<AlignedCodonFrame> seqmappings = new ArrayList<>();
88 private List<CommandListener> commandListeners = new ArrayList<>();
90 private List<SelectionListener> sel_listeners = new ArrayList<>();
93 * @return true if will try to use external services for processing secondary
96 public boolean isSecStructServices()
98 return secStructServices;
102 * control use of external services for processing secondary structure
104 * @param secStructServices
106 public void setSecStructServices(boolean secStructServices)
108 this.secStructServices = secStructServices;
112 * flag controlling addition of any kind of structural annotation
114 * @return true if temperature factor annotation will be added
116 public boolean isAddTempFacAnnot()
118 return addTempFacAnnot;
122 * set flag controlling addition of structural annotation
124 * @param addTempFacAnnot
126 public void setAddTempFacAnnot(boolean addTempFacAnnot)
128 this.addTempFacAnnot = addTempFacAnnot;
133 * @return if true, the structure manager will attempt to add secondary
134 * structure lines for unannotated sequences
137 public boolean isProcessSecondaryStructure()
139 return processSecondaryStructure;
143 * Control whether structure manager will try to annotate mapped sequences
144 * with secondary structure from PDB data.
148 public void setProcessSecondaryStructure(boolean enable)
150 processSecondaryStructure = enable;
154 * debug function - write all mappings to stdout
156 public void reportMapping()
158 if (mappings.isEmpty())
161 .errPrintln("reportMapping: No PDB/Sequence mappings.");
165 jalview.bin.Console.errPrintln(
166 "reportMapping: There are " + mappings.size() + " mappings.");
168 for (StructureMapping sm : mappings)
171 .errPrintln("mapping " + i++ + " : " + sm.pdbfile);
177 * map between the PDB IDs (or structure identifiers) used by Jalview and the
178 * absolute filenames for PDB data that corresponds to it
180 Map<String, String> pdbIdFileName = new HashMap<>();
182 Map<String, String> pdbFileNameId = new HashMap<>();
184 public void registerPDBFile(String idForFile, String absoluteFile)
186 pdbIdFileName.put(idForFile, absoluteFile);
187 pdbFileNameId.put(absoluteFile, idForFile);
190 public String findIdForPDBFile(String idOrFile)
192 String id = pdbFileNameId.get(idOrFile);
196 public String findFileForPDBId(String idOrFile)
198 String id = pdbIdFileName.get(idOrFile);
202 public boolean isPDBFileRegistered(String idOrFile)
204 return pdbFileNameId.containsKey(idOrFile)
205 || pdbIdFileName.containsKey(idOrFile);
208 private static StructureSelectionManager nullProvider = null;
210 public static StructureSelectionManager getStructureSelectionManager(
211 StructureSelectionManagerProvider context)
215 if (nullProvider == null)
217 if (instances != null)
219 throw new Error(MessageManager.getString(
220 "error.implementation_error_structure_selection_manager_null"),
221 new NullPointerException(MessageManager
222 .getString("exception.ssm_context_is_null")));
226 nullProvider = new StructureSelectionManager();
231 if (instances == null)
233 instances = new java.util.IdentityHashMap<>();
235 StructureSelectionManager instance = instances.get(context);
236 if (instance == null)
238 if (nullProvider != null)
240 instance = nullProvider;
244 instance = new StructureSelectionManager();
246 instances.put(context, instance);
252 * flag controlling whether SeqMappings are relayed from received sequence
253 * mouse over events to other sequences
255 boolean relaySeqMappings = true;
258 * Enable or disable relay of seqMapping events to other sequences. You might
259 * want to do this if there are many sequence mappings and the host computer
264 public void setRelaySeqMappings(boolean relay)
266 relaySeqMappings = relay;
270 * get the state of the relay seqMappings flag.
272 * @return true if sequence mouse overs are being relayed to other mapped
275 public boolean isRelaySeqMappingsEnabled()
277 return relaySeqMappings;
280 Vector listeners = new Vector();
283 * register a listener for alignment sequence mouseover events
287 public void addStructureViewerListener(Object svl)
289 if (!listeners.contains(svl))
291 listeners.addElement(svl);
296 * Returns the filename the PDB id is already mapped to if known, or null if
302 public String alreadyMappedToFile(String pdbid)
304 for (StructureMapping sm : mappings)
306 if (sm.getPdbId().equalsIgnoreCase(pdbid))
315 * Import structure data and register a structure mapping for broadcasting
316 * colouring, mouseovers and selection events (convenience wrapper).
319 * - one or more sequences to be mapped to pdbFile
320 * @param targetChains
321 * - optional chain specification for mapping each sequence to pdb
322 * (may be nill, individual elements may be nill)
324 * - structure data resource
326 * - how to resolve data from resource
327 * @return null or the structure data parsed as a pdb file
329 synchronized public StructureFile setMapping(SequenceI[] sequence,
330 String[] targetChains, String pdbFile, DataSourceType protocol,
331 IProgressIndicator progress)
333 return computeMapping(true, sequence, targetChains, pdbFile, protocol,
334 progress, null, null, true);
338 * Import a single structure file and register sequence structure mappings for
339 * broadcasting colouring, mouseovers and selection events (convenience
342 * @param forStructureView
343 * when true, record the mapping for use in mouseOvers
345 * - one or more sequences to be mapped to pdbFile
346 * @param targetChains
347 * - optional chain specification for mapping each sequence to pdb
348 * (may be nill, individual elements may be nill)
350 * - structure data resource
352 * - how to resolve data from resource
353 * @return null or the structure data parsed as a pdb file
355 synchronized public StructureFile setMapping(boolean forStructureView,
356 SequenceI[] sequenceArray, String[] targetChainIds,
357 String pdbFile, DataSourceType sourceType, TFType tft,
360 return setMapping(forStructureView, sequenceArray, targetChainIds,
361 pdbFile, sourceType, tft, paeFilename, true);
365 * create sequence structure mappings between each sequence and the given
366 * pdbFile (retrieved via the given protocol). Either constructs a mapping
367 * using NW alignment or derives one from any available SIFTS mapping data.
369 * @param forStructureView
370 * when true, record the mapping for use in mouseOvers
372 * @param sequenceArray
373 * - one or more sequences to be mapped to pdbFile
374 * @param targetChainIds
375 * - optional chain specification for mapping each sequence to pdb
376 * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
377 * - this should be List<List<String>>, empty lists indicate no
378 * predefined mappings
380 * - structure data resource
382 * - how to resolve data from resource
384 * - specify how to interpret the temperature factor column in the
387 * - when not null, specifies a filename containing a matrix
388 * formatted in JSON using one of the known PAE formats
389 * @param doXferSettings
390 * - when true, transfer annotation to mapped sequences in
392 * @return null or the structure data parsed as a pdb file
394 synchronized public StructureFile setMapping(boolean forStructureView,
395 SequenceI[] sequenceArray, String[] targetChainIds,
396 String pdbFile, DataSourceType sourceType, TFType tft,
397 String paeFilename, boolean doXferSettings)
399 return computeMapping(forStructureView, sequenceArray, targetChainIds,
400 pdbFile, sourceType, null, tft, paeFilename, doXferSettings);
404 * create sequence structure mappings between each sequence and the given
405 * pdbFile (retrieved via the given protocol). Either constructs a mapping
406 * using NW alignment or derives one from any available SIFTS mapping data.
408 * @param forStructureView
409 * when true, record the mapping for use in mouseOvers
411 * @param sequenceArray
412 * - one or more sequences to be mapped to pdbFile
413 * @param targetChainIds
414 * - optional chain specification for mapping each sequence to pdb
415 * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
416 * - this should be List<List<String>>, empty lists indicate no
417 * predefined mappings
419 * - structure data resource
421 * - how to resolve data from resource
422 * @param IProgressIndicator
423 * reference to UI component that maintains a progress bar for the
426 * - specify how to interpret the temperature factor column in the
429 * - when not null, specifies a filename containing a matrix
430 * formatted in JSON using one of the known PAE formats
431 * @param doXferSettings
432 * - when true, transfer annotation to mapped sequences in
434 * @return null or the structure data parsed as a pdb file
436 synchronized public StructureFile computeMapping(boolean forStructureView,
437 SequenceI[] sequenceArray, String[] targetChainIds,
438 String pdbFile, DataSourceType sourceType,
439 IProgressIndicator progress, TFType tft, String paeFilename,
440 boolean doXferSettings)
442 long progressSessionId = System.currentTimeMillis() * 3;
445 * do we extract and transfer annotation from 3D data ?
447 // FIXME: possibly should just delete
449 boolean parseSecStr = processSecondaryStructure
450 && !isStructureFileProcessed(pdbFile, sequenceArray);
452 StructureFile pdb = null;
453 boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
456 // FIXME if sourceType is not null, we've lost data here
457 sourceType = AppletFormatAdapter.checkProtocol(pdbFile);
458 pdb = new JmolParser(false, pdbFile, sourceType);
459 if (paeFilename != null)
461 pdb.setPAEMatrix(paeFilename);
463 pdb.setTemperatureFactorType(tft);
464 pdb.addSettings(parseSecStr && processSecondaryStructure,
465 parseSecStr && addTempFacAnnot,
466 parseSecStr && secStructServices);
467 // save doXferSettings and reset after doParse()
468 boolean temp = pdb.getDoXferSettings();
469 pdb.setDoXferSettings(doXferSettings);
471 pdb.setDoXferSettings(temp);
472 if (pdb.getId() != null && pdb.getId().trim().length() > 0
473 && DataSourceType.FILE == sourceType)
475 registerPDBFile(pdb.getId().trim(), pdbFile);
477 // if PDBId is unavailable then skip SIFTS mapping execution path
478 // TODO: JAL-3868 need to know if structure is actually from
479 // PDB (has valid PDB ID and has provenance suggesting it
480 // actually came from PDB)
481 boolean isProtein = false;
482 for (SequenceI s : sequenceArray)
490 isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable()
491 && !pdb.getId().startsWith("AF-") && isProtein;
493 } catch (Exception ex)
495 ex.printStackTrace();
499 * sifts client - non null if SIFTS mappings are to be used
501 SiftsClient siftsClient = null;
506 siftsClient = new SiftsClient(pdb);
508 } catch (SiftsException e)
510 isMapUsingSIFTs = false;
511 Console.error("SIFTS mapping failed", e);
512 Console.error("Falling back on Needleman & Wunsch alignment");
516 String targetChainId;
517 for (int s = 0; s < sequenceArray.length; s++)
519 boolean infChain = true;
520 final SequenceI seq = sequenceArray[s];
522 while (ds.getDatasetSequence() != null)
524 ds = ds.getDatasetSequence();
527 if (targetChainIds != null && targetChainIds[s] != null)
530 targetChainId = targetChainIds[s];
532 else if (seq.getName().indexOf("|") > -1)
534 targetChainId = seq.getName()
535 .substring(seq.getName().lastIndexOf("|") + 1);
536 if (targetChainId.length() > 1)
538 if (targetChainId.trim().length() == 0)
544 // not a valid chain identifier
555 * Attempt pairwise alignment of the sequence with each chain in the PDB,
556 * and remember the highest scoring chain
559 AlignSeq maxAlignseq = null;
560 String maxChainId = " ";
561 PDBChain maxChain = null;
562 boolean first = true;
563 for (PDBChain chain : pdb.getChains())
565 if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
568 continue; // don't try to map chains don't match.
570 // TODO: correctly determine sequence type for mixed na/peptide
572 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
573 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
576 // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
577 // as.calcScoreMatrix();
578 // as.traceAlignment();
580 if (first || as.maxscore > max
581 || (as.maxscore == max && chain.id.equals(targetChainId)))
587 maxChainId = chain.id;
590 if (maxChain == null)
595 if (sourceType == DataSourceType.PASTE)
597 pdbFile = "INLINE" + pdb.getId();
600 List<StructureMapping> seqToStrucMapping = new ArrayList<>();
601 if (isMapUsingSIFTs && seq.isProtein())
603 if (progress != null)
605 progress.setProgressBar(
607 .getString("status.obtaining_mapping_with_sifts"),
610 jalview.datamodel.Mapping sqmpping = maxAlignseq
611 .getMappingFromS1(false);
612 if (targetChainId != null && !targetChainId.trim().isEmpty())
614 StructureMapping siftsMapping;
617 siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
618 pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
619 seqToStrucMapping.add(siftsMapping);
620 maxChain.makeExactMapping(siftsMapping, seq);
621 maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS",
622 pdb.getId().toLowerCase(Locale.ROOT));
623 maxChain.transferResidueAnnotation(siftsMapping, null);
624 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
626 } catch (SiftsException e)
628 // fall back to NW alignment
629 Console.error(e.getMessage());
630 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
631 targetChainId, maxChain, pdb, maxAlignseq);
632 seqToStrucMapping.add(nwMapping);
633 maxChain.makeExactMapping(maxAlignseq, seq);
634 maxChain.transferRESNUMFeatures(seq, "IEA:Jalview",
635 pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is
638 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
639 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
644 List<StructureMapping> foundSiftsMappings = new ArrayList<>();
645 for (PDBChain chain : pdb.getChains())
647 StructureMapping siftsMapping = null;
650 siftsMapping = getStructureMapping(seq, pdbFile, chain.id,
651 pdb, chain, sqmpping, maxAlignseq, siftsClient);
652 foundSiftsMappings.add(siftsMapping);
653 chain.makeExactMapping(siftsMapping, seq);
654 chain.transferRESNUMFeatures(seq, "IEA: SIFTS",
655 pdb.getId().toLowerCase(Locale.ROOT));// FIXME: is this
657 chain.transferResidueAnnotation(siftsMapping, null);
658 } catch (SiftsException e)
660 jalview.bin.Console.errPrintln(e.getMessage());
661 } catch (Exception e)
663 jalview.bin.Console.errPrintln(
664 "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
665 jalview.bin.Console.errPrintln(e.getMessage());
668 if (!foundSiftsMappings.isEmpty())
670 seqToStrucMapping.addAll(foundSiftsMappings);
671 ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
675 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
676 maxChainId, maxChain, pdb, maxAlignseq);
677 seqToStrucMapping.add(nwMapping);
678 maxChain.transferRESNUMFeatures(seq, null,
679 pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is this
681 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
682 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
688 if (progress != null)
690 progress.setProgressBar(
691 MessageManager.getString(
692 "status.obtaining_mapping_with_nw_alignment"),
695 StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
696 maxChain, pdb, maxAlignseq);
697 seqToStrucMapping.add(nwMapping);
698 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
700 if (forStructureView)
702 for (StructureMapping sm : seqToStrucMapping)
704 addStructureMapping(sm); // not addAll!
707 if (progress != null)
709 progress.setProgressBar(null, progressSessionId);
716 * check if we need to extract secondary structure from given pdbFile and
717 * transfer to sequences
720 * @param sequenceArray
723 private boolean isStructureFileProcessed(String pdbFile,
724 SequenceI[] sequenceArray)
726 boolean processed = false;
727 if (isPDBFileRegistered(pdbFile))
729 for (SequenceI sq : sequenceArray)
732 while (ds.getDatasetSequence() != null)
734 ds = ds.getDatasetSequence();
737 if (ds.getAnnotation() != null)
739 for (AlignmentAnnotation ala : ds.getAnnotation())
741 // false if any annotation present from this structure
742 // JBPNote this fails for jmol/chimera view because the *file* is
743 // passed, not the structure data ID -
744 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
755 public void addStructureMapping(StructureMapping sm)
757 if (!mappings.contains(sm))
764 * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
769 * @param targetChainId
775 * client for retrieval of SIFTS mappings for this structure
777 * @throws SiftsException
779 private StructureMapping getStructureMapping(SequenceI seq,
780 String pdbFile, String targetChainId, StructureFile pdb,
781 PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
782 AlignSeq maxAlignseq, SiftsClient siftsClient)
783 throws SiftsException
785 StructureMapping curChainMapping = siftsClient
786 .getSiftsStructureMapping(seq, pdbFile, targetChainId);
789 PDBChain chain = pdb.findChain(targetChainId);
792 chain.transferResidueAnnotation(curChainMapping, null);
794 } catch (Exception e)
798 return curChainMapping;
801 private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
802 String maxChainId, PDBChain maxChain, StructureFile pdb,
803 AlignSeq maxAlignseq)
805 final StringBuilder mappingDetails = new StringBuilder(128);
806 mappingDetails.append(NEWLINE)
807 .append("Sequence \u27f7 Structure mapping details");
808 mappingDetails.append(NEWLINE);
810 .append("Method: inferred with Needleman & Wunsch alignment");
811 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
812 .append(NEWLINE).append("Sequence = ")
813 .append(maxChain.sequence.getSequenceAsString());
814 mappingDetails.append(NEWLINE).append("No of residues = ")
815 .append(maxChain.residues.size()).append(NEWLINE)
817 PrintStream ps = new PrintStream(System.out)
820 public void print(String x)
822 mappingDetails.append(x);
826 public void println()
828 mappingDetails.append(NEWLINE);
832 maxAlignseq.printAlignment(ps);
834 mappingDetails.append(NEWLINE).append("PDB start/end ");
835 mappingDetails.append(String.valueOf(maxAlignseq.seq2start))
837 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
838 mappingDetails.append(NEWLINE).append("SEQ start/end ");
841 .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1)))
843 mappingDetails.append(
844 String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1)));
845 mappingDetails.append(NEWLINE);
846 maxChain.makeExactMapping(maxAlignseq, seq);
847 jalview.datamodel.Mapping sqmpping = maxAlignseq
848 .getMappingFromS1(false);
849 maxChain.transferRESNUMFeatures(seq, null,
850 pdb.getId().toLowerCase(Locale.ROOT));
852 HashMap<Integer, int[]> mapping = new HashMap<>();
859 Atom tmp = maxChain.atoms.elementAt(index);
860 if ((resNum != tmp.resNumber || insCode != tmp.insCode)
861 && tmp.alignmentMapping != -1)
863 resNum = tmp.resNumber;
864 insCode = tmp.insCode;
865 if (tmp.alignmentMapping >= -1)
867 mapping.put(tmp.alignmentMapping + 1,
869 { tmp.resNumber, tmp.atomIndex });
874 } while (index < maxChain.atoms.size());
876 StructureMapping nwMapping = new StructureMapping(seq, pdbFile,
877 pdb.getId(), maxChainId, mapping, mappingDetails.toString());
878 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
882 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
884 listeners.removeElement(svl);
885 if (svl instanceof SequenceListener)
887 for (int i = 0; i < listeners.size(); i++)
889 if (listeners.elementAt(i) instanceof StructureListener)
891 ((StructureListener) listeners.elementAt(i))
892 .releaseReferences(svl);
897 if (pdbfiles == null)
903 * Remove mappings to the closed listener's PDB files, but first check if
904 * another listener is still interested
906 List<String> pdbs = new ArrayList<>(Arrays.asList(pdbfiles));
908 StructureListener sl;
909 for (int i = 0; i < listeners.size(); i++)
911 if (listeners.elementAt(i) instanceof StructureListener)
913 sl = (StructureListener) listeners.elementAt(i);
914 for (String pdbfile : sl.getStructureFiles())
916 pdbs.remove(pdbfile);
922 * Rebuild the mappings set, retaining only those which are for 'other' PDB
927 List<StructureMapping> tmp = new ArrayList<>();
928 for (StructureMapping sm : mappings)
930 if (!pdbs.contains(sm.pdbfile))
941 * hack to highlight a range of positions at once on any structure views
945 * - series of int start-end ranges as positions on sequenceRef
949 public void highlightPositionsOn(SequenceI sequenceRef, int[][] is,
952 boolean hasSequenceListeners = handlingVamsasMo
953 || !seqmappings.isEmpty();
954 SearchResultsI results = null;
955 ArrayList<Integer> listOfPositions = new ArrayList<Integer>();
958 for (int p = s_e[0]; p <= s_e[1]; listOfPositions.add(p++))
961 int seqpos[] = new int[listOfPositions.size()];
963 for (Integer p : listOfPositions)
968 for (i = 0; i < listeners.size(); i++)
970 Object listener = listeners.elementAt(i);
971 if (listener == source)
973 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
974 // Temporary fudge with SequenceListener.getVamsasSource()
977 if (listener instanceof StructureListener)
979 highlightStructure((StructureListener) listener, sequenceRef,
987 * Propagate mouseover of a single position in a structure
994 public String mouseOverStructure(int pdbResNum, String chain,
997 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
998 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
999 return mouseOverStructure(atoms);
1003 * Propagate mouseover or selection of multiple positions in a structure
1007 public String mouseOverStructure(List<AtomSpec> atoms)
1009 if (listeners == null)
1011 // old or prematurely sent event
1014 boolean hasSequenceListener = false;
1015 for (int i = 0; i < listeners.size(); i++)
1017 if (listeners.elementAt(i) instanceof SequenceListener)
1019 hasSequenceListener = true;
1022 if (!hasSequenceListener)
1027 SearchResultsI results = findAlignmentPositionsForStructurePositions(
1029 String result = null;
1030 for (Object li : listeners)
1032 if (li instanceof SequenceListener)
1034 String s = ((SequenceListener) li).highlightSequence(results);
1045 * Constructs a SearchResults object holding regions (if any) in the Jalview
1046 * alignment which have a mapping to the structure viewer positions in the
1052 public SearchResultsI findAlignmentPositionsForStructurePositions(
1053 List<AtomSpec> atoms)
1055 SearchResultsI results = new SearchResults();
1056 for (AtomSpec atom : atoms)
1058 SequenceI lastseq = null;
1060 for (StructureMapping sm : mappings)
1062 if (sm.pdbfile.equals(atom.getPdbFile())
1063 && sm.pdbchain.equals(atom.getChain()))
1065 int indexpos = sm.getSeqPos(atom.getPdbResNum());
1066 if (lastipos != indexpos || lastseq != sm.sequence)
1068 results.appendResult(sm.sequence, indexpos, indexpos);
1069 lastipos = indexpos;
1070 lastseq = sm.sequence;
1071 // construct highlighted sequence list
1072 for (AlignedCodonFrame acf : seqmappings)
1074 acf.markMappedRegion(sm.sequence, indexpos, results);
1084 * highlight regions associated with a position (indexpos) in seq
1087 * the sequence that the mouse over occurred on
1089 * the absolute position being mouseovered in seq (0 to seq.length())
1091 * the sequence position (if -1, seq.findPosition is called to
1092 * resolve the residue number)
1094 public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
1095 VamsasSource source)
1097 boolean hasSequenceListeners = handlingVamsasMo
1098 || !seqmappings.isEmpty();
1099 SearchResultsI results = null;
1102 seqPos = seq.findPosition(indexpos);
1104 for (int i = 0; i < listeners.size(); i++)
1106 Object listener = listeners.elementAt(i);
1107 if (listener == source)
1109 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1110 // Temporary fudge with SequenceListener.getVamsasSource()
1113 if (listener instanceof StructureListener)
1115 highlightStructure((StructureListener) listener, seq, seqPos);
1119 if (listener instanceof SequenceListener)
1121 final SequenceListener seqListener = (SequenceListener) listener;
1122 if (hasSequenceListeners
1123 && seqListener.getVamsasSource() != source)
1125 if (relaySeqMappings)
1127 if (results == null)
1129 results = MappingUtils.buildSearchResults(seq, seqPos,
1132 if (handlingVamsasMo)
1134 results.addResult(seq, seqPos, seqPos);
1137 if (!results.isEmpty())
1139 seqListener.highlightSequence(results);
1144 else if (listener instanceof VamsasListener && !handlingVamsasMo)
1146 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
1149 else if (listener instanceof SecondaryStructureListener)
1151 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
1159 * Send suitable messages to a StructureListener to highlight atoms
1160 * corresponding to the given sequence position(s)
1166 public void highlightStructure(StructureListener sl, SequenceI seq,
1169 if (!sl.isListeningFor(seq))
1174 List<AtomSpec> atoms = new ArrayList<>();
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()))
1181 for (int index : positions)
1183 atomNo = sm.getAtomNum(index);
1187 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1188 sm.getPDBResNum(index), atomNo));
1193 sl.highlightAtoms(atoms);
1196 public void highlightStructureRegionsFor(StructureListener sl,
1197 SequenceI[] seqs, int... columns)
1199 List<SequenceI> to_highlight = new ArrayList<SequenceI>();
1200 for (SequenceI seq : seqs)
1202 if (sl.isListeningFor(seq))
1204 to_highlight.add(seq);
1207 if (to_highlight.size() == 0)
1211 List<AtomSpec> atoms = new ArrayList<>();
1212 for (SequenceI seq : to_highlight)
1215 for (StructureMapping sm : mappings)
1217 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1218 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1219 .getDatasetSequence() == seq.getDatasetSequence()))
1222 for (int i = 0; i < columns.length; i += 2)
1224 ContiguousI positions = seq.findPositions(columns[i] + 1,
1225 columns[i + 1] + 1);
1226 if (positions == null)
1230 for (int index = positions.getBegin(); index <= positions
1234 atomNo = sm.getAtomNum(index);
1238 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1239 sm.getPDBResNum(index), atomNo));
1245 if (atoms.size() > 0)
1247 sl.highlightAtoms(atoms);
1253 * true if a mouse over event from an external (ie Vamsas) source is being
1256 boolean handlingVamsasMo = false;
1261 * as mouseOverSequence but only route event to SequenceListeners
1265 * in an alignment sequence
1267 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
1268 VamsasSource source)
1270 handlingVamsasMo = true;
1271 long msg = sequenceI.hashCode() * (1 + position);
1275 mouseOverSequence(sequenceI, position, -1, source);
1277 handlingVamsasMo = false;
1280 public Annotation[] colourSequenceFromStructure(SequenceI seq,
1284 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
1285 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
1287 * Annotation [] annotations = new Annotation[seq.getLength()];
1289 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
1290 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
1291 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
1293 * for (int j = 0; j < mappings.length; j++) {
1295 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
1296 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
1297 * jalview.bin.Console.outPrintln(pdbid+" "+mappings[j].getPdbId() +"
1298 * "+mappings[j].pdbfile);
1300 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
1301 * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
1303 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
1304 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
1305 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
1306 * mappings[j].pdbfile); }
1308 * annotations[index] = new Annotation("X",null,' ',0,col); } return
1309 * annotations; } } } }
1311 * return annotations;
1315 public void structureSelectionChanged()
1319 public void sequenceSelectionChanged()
1323 public void sequenceColoursChanged(Object source)
1325 StructureListener sl;
1326 for (int i = 0; i < listeners.size(); i++)
1328 if (listeners.elementAt(i) instanceof StructureListener)
1330 sl = (StructureListener) listeners.elementAt(i);
1331 sl.updateColours(source);
1336 public StructureMapping[] getMapping(String pdbfile)
1338 List<StructureMapping> tmp = new ArrayList<>();
1339 for (StructureMapping sm : mappings)
1341 if (sm.pdbfile.equals(pdbfile))
1346 return tmp.toArray(new StructureMapping[tmp.size()]);
1350 * Returns a readable description of all mappings for the given pdbfile to any
1351 * of the given sequences
1357 public String printMappings(String pdbfile, List<SequenceI> seqs)
1359 if (pdbfile == null || seqs == null || seqs.isEmpty())
1364 StringBuilder sb = new StringBuilder(64);
1365 for (StructureMapping sm : mappings)
1367 if (Platform.pathEquals(sm.pdbfile, pdbfile)
1368 && seqs.contains(sm.sequence))
1370 sb.append(sm.mappingDetails);
1372 // separator makes it easier to read multiple mappings
1373 sb.append("=====================");
1379 return sb.toString();
1383 * Remove the given mapping
1387 public void deregisterMapping(AlignedCodonFrame acf)
1391 boolean removed = seqmappings.remove(acf);
1392 if (removed && seqmappings.isEmpty())
1394 jalview.bin.Console.outPrintln("All mappings removed");
1400 * Add each of the given codonFrames to the stored set, if not aready present.
1404 public void registerMappings(List<AlignedCodonFrame> mappings)
1406 if (mappings != null)
1408 for (AlignedCodonFrame acf : mappings)
1410 registerMapping(acf);
1416 * Add the given mapping to the stored set, unless already stored.
1418 public void registerMapping(AlignedCodonFrame acf)
1422 if (!seqmappings.contains(acf))
1424 seqmappings.add(acf);
1430 * Resets this object to its initial state by removing all registered
1431 * listeners, codon mappings, PDB file mappings
1433 public void resetAll()
1435 if (mappings != null)
1439 if (seqmappings != null)
1441 seqmappings.clear();
1443 if (sel_listeners != null)
1445 sel_listeners.clear();
1447 if (listeners != null)
1451 if (commandListeners != null)
1453 commandListeners.clear();
1455 if (view_listeners != null)
1457 view_listeners.clear();
1459 if (pdbFileNameId != null)
1461 pdbFileNameId.clear();
1463 if (pdbIdFileName != null)
1465 pdbIdFileName.clear();
1469 public void addSelectionListener(SelectionListener selecter)
1471 if (!sel_listeners.contains(selecter))
1473 sel_listeners.add(selecter);
1477 public void removeSelectionListener(SelectionListener toremove)
1479 if (sel_listeners.contains(toremove))
1481 sel_listeners.remove(toremove);
1485 public synchronized void sendSelection(
1486 jalview.datamodel.SequenceGroup selection,
1487 jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden,
1488 SelectionSource source)
1490 for (SelectionListener slis : sel_listeners)
1494 slis.selection(selection, colsel, hidden, source);
1499 Vector<AlignmentViewPanelListener> view_listeners = new Vector<>();
1501 public synchronized void sendViewPosition(
1502 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1503 int startSeq, int endSeq)
1506 if (view_listeners != null && view_listeners.size() > 0)
1508 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1510 while (listeners.hasMoreElements())
1512 AlignmentViewPanelListener slis = listeners.nextElement();
1515 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1523 * release all references associated with this manager provider
1525 * @param jalviewLite
1527 public static void release(StructureSelectionManagerProvider jalviewLite)
1529 // synchronized (instances)
1531 if (instances == null)
1535 StructureSelectionManager mnger = (instances.get(jalviewLite));
1538 instances.remove(jalviewLite);
1541 /* bsoares 2019-03-20 finalize deprecated, no apparent external
1542 * resources to close
1544 // mnger.finalize();
1545 } catch (Throwable x)
1552 public void registerPDBEntry(PDBEntry pdbentry)
1554 if (pdbentry.getFile() != null
1555 && pdbentry.getFile().trim().length() > 0)
1557 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1561 public void addCommandListener(CommandListener cl)
1563 if (!commandListeners.contains(cl))
1565 commandListeners.add(cl);
1569 public boolean hasCommandListener(CommandListener cl)
1571 return this.commandListeners.contains(cl);
1574 public boolean removeCommandListener(CommandListener l)
1576 return commandListeners.remove(l);
1580 * Forward a command to any command listeners (except for the command's
1584 * the command to be broadcast (in its form after being performed)
1586 * if true, the command was being 'undone'
1589 public void commandPerformed(CommandI command, boolean undo,
1590 VamsasSource source)
1592 for (CommandListener listener : commandListeners)
1594 listener.mirrorCommand(command, undo, this, source);
1599 * Returns a new CommandI representing the given command as mapped to the
1600 * given sequences. If no mapping could be made, or the command is not of a
1601 * mappable kind, returns null.
1609 public CommandI mapCommand(CommandI command, boolean undo,
1610 final AlignmentI mapTo, char gapChar)
1612 if (command instanceof EditCommand)
1614 return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo,
1615 gapChar, seqmappings);
1617 else if (command instanceof OrderCommand)
1619 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1620 mapTo, seqmappings);
1625 public List<AlignedCodonFrame> getSequenceMappings()
1631 * quick and dirty route to just highlight all structure positions for a range
1634 * @param sequencesArray
1636 * start-end columns on sequencesArray
1638 * origin parent AlignmentPanel
1640 public void highlightPositionsOnMany(SequenceI[] sequencesArray, int[] is,
1643 for (int i = 0; i < listeners.size(); i++)
1645 Object listener = listeners.elementAt(i);
1646 if (listener == source)
1648 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1649 // Temporary fudge with SequenceListener.getVamsasSource()
1652 if (listener instanceof StructureListener)
1654 highlightStructureRegionsFor((StructureListener) listener,
1655 sequencesArray, is);
1660 public Map<String, String> getPdbFileNameIdMap()
1662 return pdbFileNameId;
1665 public Map<String, String> getPdbIdFileNameMap()
1667 return pdbIdFileName;
1670 public static void doConfigureStructurePrefs(
1671 StructureSelectionManager ssm)
1673 doConfigureStructurePrefs(ssm,
1674 Cache.getDefault(Preferences.ADD_SS_ANN, true),
1675 Cache.getDefault(Preferences.ADD_TEMPFACT_ANN, true),
1676 Cache.getDefault(Preferences.STRUCT_FROM_PDB, true),
1677 Cache.getDefault(Preferences.USE_RNAVIEW, false));
1680 public static void doConfigureStructurePrefs(
1681 StructureSelectionManager ssm, boolean add_ss_ann,
1682 boolean add_tempfact_ann, boolean struct_from_pdb,
1683 boolean use_rnaview)
1687 ssm.setAddTempFacAnnot(add_tempfact_ann);
1688 ssm.setProcessSecondaryStructure(struct_from_pdb);
1689 // JAL-3915 - RNAView is no longer an option so this has no effect
1690 ssm.setSecStructServices(use_rnaview);
1694 ssm.setAddTempFacAnnot(false);
1695 ssm.setProcessSecondaryStructure(false);
1696 ssm.setSecStructServices(false);