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 jalview.bin.Console.errPrintln("reportMapping: No PDB/Sequence mappings.");
162 jalview.bin.Console.errPrintln(
163 "reportMapping: There are " + mappings.size() + " mappings.");
165 for (StructureMapping sm : mappings)
167 jalview.bin.Console.errPrintln("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);
362 * create sequence structure mappings between each sequence and the given
363 * pdbFile (retrieved via the given protocol). Either constructs a mapping
364 * using NW alignment or derives one from any available SIFTS mapping data.
366 * @param forStructureView
367 * when true, record the mapping for use in mouseOvers
369 * @param sequenceArray
370 * - one or more sequences to be mapped to pdbFile
371 * @param targetChainIds
372 * - optional chain specification for mapping each sequence to pdb
373 * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
374 * - this should be List<List<String>>, empty lists indicate no
375 * predefined mappings
377 * - structure data resource
379 * - how to resolve data from resource
380 * @param tft - specify how to interpret the temperature factor column in the atom data
381 * @param paeFilename - when not null, specifies a filename containing a matrix formatted in JSON using one of the known PAE formats
382 * @param doXferSettings - when true, transfer annotation to mapped sequences in sequenceArray
383 * @return null or the structure data parsed as a pdb file
385 synchronized public StructureFile setMapping(boolean forStructureView,
386 SequenceI[] sequenceArray, String[] targetChainIds,
387 String pdbFile, DataSourceType sourceType, TFType tft,
388 String paeFilename, boolean doXferSettings)
390 return computeMapping(forStructureView, sequenceArray, targetChainIds,
391 pdbFile, sourceType, null, tft, paeFilename, doXferSettings);
395 * create sequence structure mappings between each sequence and the given
396 * pdbFile (retrieved via the given protocol). Either constructs a mapping
397 * using NW alignment or derives one from any available SIFTS mapping data.
399 * @param forStructureView
400 * when true, record the mapping for use in mouseOvers
402 * @param sequenceArray
403 * - one or more sequences to be mapped to pdbFile
404 * @param targetChainIds
405 * - optional chain specification for mapping each sequence to pdb
406 * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
407 * - this should be List<List<String>>, empty lists indicate no
408 * predefined mappings
410 * - structure data resource
412 * - how to resolve data from resource
413 * @param IProgressIndicator
414 * reference to UI component that maintains a progress bar for the
416 * @param tft - specify how to interpret the temperature factor column in the atom data
417 * @param paeFilename - when not null, specifies a filename containing a matrix formatted in JSON using one of the known PAE formats
418 * @param doXferSettings - when true, transfer annotation to mapped sequences in sequenceArray
419 * @return null or the structure data parsed as a pdb file
421 synchronized public StructureFile computeMapping(boolean forStructureView,
422 SequenceI[] sequenceArray, String[] targetChainIds,
423 String pdbFile, DataSourceType sourceType,
424 IProgressIndicator progress, TFType tft, String paeFilename,
425 boolean doXferSettings)
427 long progressSessionId = System.currentTimeMillis() * 3;
430 * do we extract and transfer annotation from 3D data ?
432 // FIXME: possibly should just delete
434 boolean parseSecStr = processSecondaryStructure
435 && !isStructureFileProcessed(pdbFile, sequenceArray);
437 StructureFile pdb = null;
438 boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
441 // FIXME if sourceType is not null, we've lost data here
442 sourceType = AppletFormatAdapter.checkProtocol(pdbFile);
443 pdb = new JmolParser(false, pdbFile, sourceType);
444 if (paeFilename != null)
446 pdb.setPAEMatrix(paeFilename);
448 pdb.setTemperatureFactorType(tft);
449 pdb.addSettings(parseSecStr && processSecondaryStructure,
450 parseSecStr && addTempFacAnnot,
451 parseSecStr && secStructServices);
452 // save doXferSettings and reset after doParse()
453 boolean temp = pdb.getDoXferSettings();
454 pdb.setDoXferSettings(doXferSettings);
456 pdb.setDoXferSettings(temp);
457 if (pdb.getId() != null && pdb.getId().trim().length() > 0
458 && DataSourceType.FILE == sourceType)
460 registerPDBFile(pdb.getId().trim(), pdbFile);
462 // if PDBId is unavailable then skip SIFTS mapping execution path
463 // TODO: JAL-3868 need to know if structure is actually from
464 // PDB (has valid PDB ID and has provenance suggesting it
465 // actually came from PDB)
466 boolean isProtein = false;
467 for (SequenceI s : sequenceArray)
475 isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable()
476 && !pdb.getId().startsWith("AF-") && isProtein;
478 } catch (Exception ex)
480 ex.printStackTrace();
484 * sifts client - non null if SIFTS mappings are to be used
486 SiftsClient siftsClient = null;
491 siftsClient = new SiftsClient(pdb);
493 } catch (SiftsException e)
495 isMapUsingSIFTs = false;
496 Console.error("SIFTS mapping failed", e);
497 Console.error("Falling back on Needleman & Wunsch alignment");
501 String targetChainId;
502 for (int s = 0; s < sequenceArray.length; s++)
504 boolean infChain = true;
505 final SequenceI seq = sequenceArray[s];
507 while (ds.getDatasetSequence() != null)
509 ds = ds.getDatasetSequence();
512 if (targetChainIds != null && targetChainIds[s] != null)
515 targetChainId = targetChainIds[s];
517 else if (seq.getName().indexOf("|") > -1)
519 targetChainId = seq.getName()
520 .substring(seq.getName().lastIndexOf("|") + 1);
521 if (targetChainId.length() > 1)
523 if (targetChainId.trim().length() == 0)
529 // not a valid chain identifier
540 * Attempt pairwise alignment of the sequence with each chain in the PDB,
541 * and remember the highest scoring chain
544 AlignSeq maxAlignseq = null;
545 String maxChainId = " ";
546 PDBChain maxChain = null;
547 boolean first = true;
548 for (PDBChain chain : pdb.getChains())
550 if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
553 continue; // don't try to map chains don't match.
555 // TODO: correctly determine sequence type for mixed na/peptide
557 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
558 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
561 // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
562 // as.calcScoreMatrix();
563 // as.traceAlignment();
565 if (first || as.maxscore > max
566 || (as.maxscore == max && chain.id.equals(targetChainId)))
572 maxChainId = chain.id;
575 if (maxChain == null)
580 if (sourceType == DataSourceType.PASTE)
582 pdbFile = "INLINE" + pdb.getId();
585 List<StructureMapping> seqToStrucMapping = new ArrayList<>();
586 if (isMapUsingSIFTs && seq.isProtein())
588 if (progress != null)
590 progress.setProgressBar(
592 .getString("status.obtaining_mapping_with_sifts"),
595 jalview.datamodel.Mapping sqmpping = maxAlignseq
596 .getMappingFromS1(false);
597 if (targetChainId != null && !targetChainId.trim().isEmpty())
599 StructureMapping siftsMapping;
602 siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
603 pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
604 seqToStrucMapping.add(siftsMapping);
605 maxChain.makeExactMapping(siftsMapping, seq);
606 maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS",
607 pdb.getId().toLowerCase(Locale.ROOT));
608 maxChain.transferResidueAnnotation(siftsMapping, null);
609 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
611 } catch (SiftsException e)
613 // fall back to NW alignment
614 Console.error(e.getMessage());
615 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
616 targetChainId, maxChain, pdb, maxAlignseq);
617 seqToStrucMapping.add(nwMapping);
618 maxChain.makeExactMapping(maxAlignseq, seq);
619 maxChain.transferRESNUMFeatures(seq, "IEA:Jalview",
620 pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is
623 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
624 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
629 List<StructureMapping> foundSiftsMappings = new ArrayList<>();
630 for (PDBChain chain : pdb.getChains())
632 StructureMapping siftsMapping = null;
635 siftsMapping = getStructureMapping(seq, pdbFile, chain.id,
636 pdb, chain, sqmpping, maxAlignseq, siftsClient);
637 foundSiftsMappings.add(siftsMapping);
638 chain.makeExactMapping(siftsMapping, seq);
639 chain.transferRESNUMFeatures(seq, "IEA: SIFTS",
640 pdb.getId().toLowerCase(Locale.ROOT));// FIXME: is this
642 chain.transferResidueAnnotation(siftsMapping, null);
643 } catch (SiftsException e)
645 jalview.bin.Console.errPrintln(e.getMessage());
646 } catch (Exception e)
648 jalview.bin.Console.errPrintln(
649 "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
650 jalview.bin.Console.errPrintln(e.getMessage());
653 if (!foundSiftsMappings.isEmpty())
655 seqToStrucMapping.addAll(foundSiftsMappings);
656 ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
660 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
661 maxChainId, maxChain, pdb, maxAlignseq);
662 seqToStrucMapping.add(nwMapping);
663 maxChain.transferRESNUMFeatures(seq, null,
664 pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is this
666 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
667 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
673 if (progress != null)
675 progress.setProgressBar(
676 MessageManager.getString(
677 "status.obtaining_mapping_with_nw_alignment"),
680 StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
681 maxChain, pdb, maxAlignseq);
682 seqToStrucMapping.add(nwMapping);
683 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
685 if (forStructureView)
687 for (StructureMapping sm : seqToStrucMapping)
689 addStructureMapping(sm); // not addAll!
692 if (progress != null)
694 progress.setProgressBar(null, progressSessionId);
701 * check if we need to extract secondary structure from given pdbFile and
702 * transfer to sequences
705 * @param sequenceArray
708 private boolean isStructureFileProcessed(String pdbFile,
709 SequenceI[] sequenceArray)
711 boolean processed = false;
712 if (isPDBFileRegistered(pdbFile))
714 for (SequenceI sq : sequenceArray)
717 while (ds.getDatasetSequence() != null)
719 ds = ds.getDatasetSequence();
722 if (ds.getAnnotation() != null)
724 for (AlignmentAnnotation ala : ds.getAnnotation())
726 // false if any annotation present from this structure
727 // JBPNote this fails for jmol/chimera view because the *file* is
728 // passed, not the structure data ID -
729 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
740 public void addStructureMapping(StructureMapping sm)
742 if (!mappings.contains(sm))
749 * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
754 * @param targetChainId
760 * client for retrieval of SIFTS mappings for this structure
762 * @throws SiftsException
764 private StructureMapping getStructureMapping(SequenceI seq,
765 String pdbFile, String targetChainId, StructureFile pdb,
766 PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
767 AlignSeq maxAlignseq, SiftsClient siftsClient)
768 throws SiftsException
770 StructureMapping curChainMapping = siftsClient
771 .getSiftsStructureMapping(seq, pdbFile, targetChainId);
774 PDBChain chain = pdb.findChain(targetChainId);
777 chain.transferResidueAnnotation(curChainMapping, null);
779 } catch (Exception e)
783 return curChainMapping;
786 private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
787 String maxChainId, PDBChain maxChain, StructureFile pdb,
788 AlignSeq maxAlignseq)
790 final StringBuilder mappingDetails = new StringBuilder(128);
791 mappingDetails.append(NEWLINE)
792 .append("Sequence \u27f7 Structure mapping details");
793 mappingDetails.append(NEWLINE);
795 .append("Method: inferred with Needleman & Wunsch alignment");
796 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
797 .append(NEWLINE).append("Sequence = ")
798 .append(maxChain.sequence.getSequenceAsString());
799 mappingDetails.append(NEWLINE).append("No of residues = ")
800 .append(maxChain.residues.size()).append(NEWLINE)
802 PrintStream ps = new PrintStream(System.out)
805 public void print(String x)
807 mappingDetails.append(x);
811 public void println()
813 mappingDetails.append(NEWLINE);
817 maxAlignseq.printAlignment(ps);
819 mappingDetails.append(NEWLINE).append("PDB start/end ");
820 mappingDetails.append(String.valueOf(maxAlignseq.seq2start))
822 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
823 mappingDetails.append(NEWLINE).append("SEQ start/end ");
826 .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1)))
828 mappingDetails.append(
829 String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1)));
830 mappingDetails.append(NEWLINE);
831 maxChain.makeExactMapping(maxAlignseq, seq);
832 jalview.datamodel.Mapping sqmpping = maxAlignseq
833 .getMappingFromS1(false);
834 maxChain.transferRESNUMFeatures(seq, null,
835 pdb.getId().toLowerCase(Locale.ROOT));
837 HashMap<Integer, int[]> mapping = new HashMap<>();
844 Atom tmp = maxChain.atoms.elementAt(index);
845 if ((resNum != tmp.resNumber || insCode != tmp.insCode)
846 && tmp.alignmentMapping != -1)
848 resNum = tmp.resNumber;
849 insCode = tmp.insCode;
850 if (tmp.alignmentMapping >= -1)
852 mapping.put(tmp.alignmentMapping + 1,
854 { tmp.resNumber, tmp.atomIndex });
859 } while (index < maxChain.atoms.size());
861 StructureMapping nwMapping = new StructureMapping(seq, pdbFile,
862 pdb.getId(), maxChainId, mapping, mappingDetails.toString());
863 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
867 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
869 listeners.removeElement(svl);
870 if (svl instanceof SequenceListener)
872 for (int i = 0; i < listeners.size(); i++)
874 if (listeners.elementAt(i) instanceof StructureListener)
876 ((StructureListener) listeners.elementAt(i))
877 .releaseReferences(svl);
882 if (pdbfiles == null)
888 * Remove mappings to the closed listener's PDB files, but first check if
889 * another listener is still interested
891 List<String> pdbs = new ArrayList<>(Arrays.asList(pdbfiles));
893 StructureListener sl;
894 for (int i = 0; i < listeners.size(); i++)
896 if (listeners.elementAt(i) instanceof StructureListener)
898 sl = (StructureListener) listeners.elementAt(i);
899 for (String pdbfile : sl.getStructureFiles())
901 pdbs.remove(pdbfile);
907 * Rebuild the mappings set, retaining only those which are for 'other' PDB
912 List<StructureMapping> tmp = new ArrayList<>();
913 for (StructureMapping sm : mappings)
915 if (!pdbs.contains(sm.pdbfile))
926 * hack to highlight a range of positions at once on any structure views
930 * - series of int start-end ranges as positions on sequenceRef
934 public void highlightPositionsOn(SequenceI sequenceRef, int[][] is,
937 boolean hasSequenceListeners = handlingVamsasMo
938 || !seqmappings.isEmpty();
939 SearchResultsI results = null;
940 ArrayList<Integer> listOfPositions = new ArrayList<Integer>();
943 for (int p = s_e[0]; p <= s_e[1]; listOfPositions.add(p++))
946 int seqpos[] = new int[listOfPositions.size()];
948 for (Integer p : listOfPositions)
953 for (i = 0; i < listeners.size(); i++)
955 Object listener = listeners.elementAt(i);
956 if (listener == source)
958 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
959 // Temporary fudge with SequenceListener.getVamsasSource()
962 if (listener instanceof StructureListener)
964 highlightStructure((StructureListener) listener, sequenceRef,
972 * Propagate mouseover of a single position in a structure
979 public String mouseOverStructure(int pdbResNum, String chain,
982 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
983 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
984 return mouseOverStructure(atoms);
988 * Propagate mouseover or selection of multiple positions in a structure
992 public String mouseOverStructure(List<AtomSpec> atoms)
994 if (listeners == null)
996 // old or prematurely sent event
999 boolean hasSequenceListener = false;
1000 for (int i = 0; i < listeners.size(); i++)
1002 if (listeners.elementAt(i) instanceof SequenceListener)
1004 hasSequenceListener = true;
1007 if (!hasSequenceListener)
1012 SearchResultsI results = findAlignmentPositionsForStructurePositions(
1014 String result = null;
1015 for (Object li : listeners)
1017 if (li instanceof SequenceListener)
1019 String s = ((SequenceListener) li).highlightSequence(results);
1030 * Constructs a SearchResults object holding regions (if any) in the Jalview
1031 * alignment which have a mapping to the structure viewer positions in the
1037 public SearchResultsI findAlignmentPositionsForStructurePositions(
1038 List<AtomSpec> atoms)
1040 SearchResultsI results = new SearchResults();
1041 for (AtomSpec atom : atoms)
1043 SequenceI lastseq = null;
1045 for (StructureMapping sm : mappings)
1047 if (sm.pdbfile.equals(atom.getPdbFile())
1048 && sm.pdbchain.equals(atom.getChain()))
1050 int indexpos = sm.getSeqPos(atom.getPdbResNum());
1051 if (lastipos != indexpos || lastseq != sm.sequence)
1053 results.addResult(sm.sequence, indexpos, indexpos);
1054 lastipos = indexpos;
1055 lastseq = sm.sequence;
1056 // construct highlighted sequence list
1057 for (AlignedCodonFrame acf : seqmappings)
1059 acf.markMappedRegion(sm.sequence, indexpos, results);
1069 * highlight regions associated with a position (indexpos) in seq
1072 * the sequence that the mouse over occurred on
1074 * the absolute position being mouseovered in seq (0 to seq.length())
1076 * the sequence position (if -1, seq.findPosition is called to
1077 * resolve the residue number)
1079 public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
1080 VamsasSource source)
1082 boolean hasSequenceListeners = handlingVamsasMo
1083 || !seqmappings.isEmpty();
1084 SearchResultsI results = null;
1087 seqPos = seq.findPosition(indexpos);
1089 for (int i = 0; i < listeners.size(); i++)
1091 Object listener = listeners.elementAt(i);
1092 if (listener == source)
1094 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1095 // Temporary fudge with SequenceListener.getVamsasSource()
1098 if (listener instanceof StructureListener)
1100 highlightStructure((StructureListener) listener, seq, seqPos);
1104 if (listener instanceof SequenceListener)
1106 final SequenceListener seqListener = (SequenceListener) listener;
1107 if (hasSequenceListeners
1108 && seqListener.getVamsasSource() != source)
1110 if (relaySeqMappings)
1112 if (results == null)
1114 results = MappingUtils.buildSearchResults(seq, seqPos,
1117 if (handlingVamsasMo)
1119 results.addResult(seq, seqPos, seqPos);
1122 if (!results.isEmpty())
1124 seqListener.highlightSequence(results);
1129 else if (listener instanceof VamsasListener && !handlingVamsasMo)
1131 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
1134 else if (listener instanceof SecondaryStructureListener)
1136 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
1144 * Send suitable messages to a StructureListener to highlight atoms
1145 * corresponding to the given sequence position(s)
1151 public void highlightStructure(StructureListener sl, SequenceI seq,
1154 if (!sl.isListeningFor(seq))
1159 List<AtomSpec> atoms = new ArrayList<>();
1160 for (StructureMapping sm : mappings)
1162 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1163 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1164 .getDatasetSequence() == seq.getDatasetSequence()))
1166 for (int index : positions)
1168 atomNo = sm.getAtomNum(index);
1172 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1173 sm.getPDBResNum(index), atomNo));
1178 sl.highlightAtoms(atoms);
1181 public void highlightStructureRegionsFor(StructureListener sl,
1182 SequenceI[] seqs, int... columns)
1184 List<SequenceI> to_highlight = new ArrayList<SequenceI>();
1185 for (SequenceI seq : seqs)
1187 if (sl.isListeningFor(seq))
1189 to_highlight.add(seq);
1192 if (to_highlight.size() == 0)
1196 List<AtomSpec> atoms = new ArrayList<>();
1197 for (SequenceI seq : to_highlight)
1200 for (StructureMapping sm : mappings)
1202 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1203 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1204 .getDatasetSequence() == seq.getDatasetSequence()))
1207 for (int i = 0; i < columns.length; i += 2)
1209 ContiguousI positions = seq.findPositions(columns[i] + 1,
1210 columns[i + 1] + 1);
1211 if (positions == null)
1215 for (int index = positions.getBegin(); index <= positions
1219 atomNo = sm.getAtomNum(index);
1223 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1224 sm.getPDBResNum(index), atomNo));
1230 if (atoms.size() > 0)
1232 sl.highlightAtoms(atoms);
1238 * true if a mouse over event from an external (ie Vamsas) source is being
1241 boolean handlingVamsasMo = false;
1246 * as mouseOverSequence but only route event to SequenceListeners
1250 * in an alignment sequence
1252 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
1253 VamsasSource source)
1255 handlingVamsasMo = true;
1256 long msg = sequenceI.hashCode() * (1 + position);
1260 mouseOverSequence(sequenceI, position, -1, source);
1262 handlingVamsasMo = false;
1265 public Annotation[] colourSequenceFromStructure(SequenceI seq,
1269 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
1270 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
1272 * Annotation [] annotations = new Annotation[seq.getLength()];
1274 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
1275 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
1276 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
1278 * for (int j = 0; j < mappings.length; j++) {
1280 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
1281 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
1282 * jalview.bin.Console.outPrintln(pdbid+" "+mappings[j].getPdbId() +"
1283 * "+mappings[j].pdbfile);
1285 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
1286 * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
1288 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
1289 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
1290 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
1291 * mappings[j].pdbfile); }
1293 * annotations[index] = new Annotation("X",null,' ',0,col); } return
1294 * annotations; } } } }
1296 * return annotations;
1300 public void structureSelectionChanged()
1304 public void sequenceSelectionChanged()
1308 public void sequenceColoursChanged(Object source)
1310 StructureListener sl;
1311 for (int i = 0; i < listeners.size(); i++)
1313 if (listeners.elementAt(i) instanceof StructureListener)
1315 sl = (StructureListener) listeners.elementAt(i);
1316 sl.updateColours(source);
1321 public StructureMapping[] getMapping(String pdbfile)
1323 List<StructureMapping> tmp = new ArrayList<>();
1324 for (StructureMapping sm : mappings)
1326 if (sm.pdbfile.equals(pdbfile))
1331 return tmp.toArray(new StructureMapping[tmp.size()]);
1335 * Returns a readable description of all mappings for the given pdbfile to any
1336 * of the given sequences
1342 public String printMappings(String pdbfile, List<SequenceI> seqs)
1344 if (pdbfile == null || seqs == null || seqs.isEmpty())
1349 StringBuilder sb = new StringBuilder(64);
1350 for (StructureMapping sm : mappings)
1352 if (Platform.pathEquals(sm.pdbfile, pdbfile)
1353 && seqs.contains(sm.sequence))
1355 sb.append(sm.mappingDetails);
1357 // separator makes it easier to read multiple mappings
1358 sb.append("=====================");
1364 return sb.toString();
1368 * Remove the given mapping
1372 public void deregisterMapping(AlignedCodonFrame acf)
1376 boolean removed = seqmappings.remove(acf);
1377 if (removed && seqmappings.isEmpty())
1379 jalview.bin.Console.outPrintln("All mappings removed");
1385 * Add each of the given codonFrames to the stored set, if not aready present.
1389 public void registerMappings(List<AlignedCodonFrame> mappings)
1391 if (mappings != null)
1393 for (AlignedCodonFrame acf : mappings)
1395 registerMapping(acf);
1401 * Add the given mapping to the stored set, unless already stored.
1403 public void registerMapping(AlignedCodonFrame acf)
1407 if (!seqmappings.contains(acf))
1409 seqmappings.add(acf);
1415 * Resets this object to its initial state by removing all registered
1416 * listeners, codon mappings, PDB file mappings
1418 public void resetAll()
1420 if (mappings != null)
1424 if (seqmappings != null)
1426 seqmappings.clear();
1428 if (sel_listeners != null)
1430 sel_listeners.clear();
1432 if (listeners != null)
1436 if (commandListeners != null)
1438 commandListeners.clear();
1440 if (view_listeners != null)
1442 view_listeners.clear();
1444 if (pdbFileNameId != null)
1446 pdbFileNameId.clear();
1448 if (pdbIdFileName != null)
1450 pdbIdFileName.clear();
1454 public void addSelectionListener(SelectionListener selecter)
1456 if (!sel_listeners.contains(selecter))
1458 sel_listeners.add(selecter);
1462 public void removeSelectionListener(SelectionListener toremove)
1464 if (sel_listeners.contains(toremove))
1466 sel_listeners.remove(toremove);
1470 public synchronized void sendSelection(
1471 jalview.datamodel.SequenceGroup selection,
1472 jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden,
1473 SelectionSource source)
1475 for (SelectionListener slis : sel_listeners)
1479 slis.selection(selection, colsel, hidden, source);
1484 Vector<AlignmentViewPanelListener> view_listeners = new Vector<>();
1486 public synchronized void sendViewPosition(
1487 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1488 int startSeq, int endSeq)
1491 if (view_listeners != null && view_listeners.size() > 0)
1493 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1495 while (listeners.hasMoreElements())
1497 AlignmentViewPanelListener slis = listeners.nextElement();
1500 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1508 * release all references associated with this manager provider
1510 * @param jalviewLite
1512 public static void release(StructureSelectionManagerProvider jalviewLite)
1514 // synchronized (instances)
1516 if (instances == null)
1520 StructureSelectionManager mnger = (instances.get(jalviewLite));
1523 instances.remove(jalviewLite);
1526 /* bsoares 2019-03-20 finalize deprecated, no apparent external
1527 * resources to close
1529 // mnger.finalize();
1530 } catch (Throwable x)
1537 public void registerPDBEntry(PDBEntry pdbentry)
1539 if (pdbentry.getFile() != null
1540 && pdbentry.getFile().trim().length() > 0)
1542 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1546 public void addCommandListener(CommandListener cl)
1548 if (!commandListeners.contains(cl))
1550 commandListeners.add(cl);
1554 public boolean hasCommandListener(CommandListener cl)
1556 return this.commandListeners.contains(cl);
1559 public boolean removeCommandListener(CommandListener l)
1561 return commandListeners.remove(l);
1565 * Forward a command to any command listeners (except for the command's
1569 * the command to be broadcast (in its form after being performed)
1571 * if true, the command was being 'undone'
1574 public void commandPerformed(CommandI command, boolean undo,
1575 VamsasSource source)
1577 for (CommandListener listener : commandListeners)
1579 listener.mirrorCommand(command, undo, this, source);
1584 * Returns a new CommandI representing the given command as mapped to the
1585 * given sequences. If no mapping could be made, or the command is not of a
1586 * mappable kind, returns null.
1594 public CommandI mapCommand(CommandI command, boolean undo,
1595 final AlignmentI mapTo, char gapChar)
1597 if (command instanceof EditCommand)
1599 return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo,
1600 gapChar, seqmappings);
1602 else if (command instanceof OrderCommand)
1604 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1605 mapTo, seqmappings);
1610 public List<AlignedCodonFrame> getSequenceMappings()
1616 * quick and dirty route to just highlight all structure positions for a range
1619 * @param sequencesArray
1621 * start-end columns on sequencesArray
1623 * origin parent AlignmentPanel
1625 public void highlightPositionsOnMany(SequenceI[] sequencesArray, int[] is,
1628 for (int i = 0; i < listeners.size(); i++)
1630 Object listener = listeners.elementAt(i);
1631 if (listener == source)
1633 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1634 // Temporary fudge with SequenceListener.getVamsasSource()
1637 if (listener instanceof StructureListener)
1639 highlightStructureRegionsFor((StructureListener) listener,
1640 sequencesArray, is);
1645 public Map<String, String> getPdbFileNameIdMap()
1647 return pdbFileNameId;
1650 public Map<String, String> getPdbIdFileNameMap()
1652 return pdbIdFileName;