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())
159 .errPrintln("reportMapping: No PDB/Sequence mappings.");
163 jalview.bin.Console.errPrintln(
164 "reportMapping: There are " + mappings.size() + " mappings.");
166 for (StructureMapping sm : mappings)
169 .errPrintln("mapping " + i++ + " : " + sm.pdbfile);
175 * map between the PDB IDs (or structure identifiers) used by Jalview and the
176 * absolute filenames for PDB data that corresponds to it
178 Map<String, String> pdbIdFileName = new HashMap<>();
180 Map<String, String> pdbFileNameId = new HashMap<>();
182 public void registerPDBFile(String idForFile, String absoluteFile)
184 pdbIdFileName.put(idForFile, absoluteFile);
185 pdbFileNameId.put(absoluteFile, idForFile);
188 public String findIdForPDBFile(String idOrFile)
190 String id = pdbFileNameId.get(idOrFile);
194 public String findFileForPDBId(String idOrFile)
196 String id = pdbIdFileName.get(idOrFile);
200 public boolean isPDBFileRegistered(String idOrFile)
202 return pdbFileNameId.containsKey(idOrFile)
203 || pdbIdFileName.containsKey(idOrFile);
206 private static StructureSelectionManager nullProvider = null;
208 public static StructureSelectionManager getStructureSelectionManager(
209 StructureSelectionManagerProvider context)
213 if (nullProvider == null)
215 if (instances != null)
217 throw new Error(MessageManager.getString(
218 "error.implementation_error_structure_selection_manager_null"),
219 new NullPointerException(MessageManager
220 .getString("exception.ssm_context_is_null")));
224 nullProvider = new StructureSelectionManager();
229 if (instances == null)
231 instances = new java.util.IdentityHashMap<>();
233 StructureSelectionManager instance = instances.get(context);
234 if (instance == null)
236 if (nullProvider != null)
238 instance = nullProvider;
242 instance = new StructureSelectionManager();
244 instances.put(context, instance);
250 * flag controlling whether SeqMappings are relayed from received sequence
251 * mouse over events to other sequences
253 boolean relaySeqMappings = true;
256 * Enable or disable relay of seqMapping events to other sequences. You might
257 * want to do this if there are many sequence mappings and the host computer
262 public void setRelaySeqMappings(boolean relay)
264 relaySeqMappings = relay;
268 * get the state of the relay seqMappings flag.
270 * @return true if sequence mouse overs are being relayed to other mapped
273 public boolean isRelaySeqMappingsEnabled()
275 return relaySeqMappings;
278 Vector listeners = new Vector();
281 * register a listener for alignment sequence mouseover events
285 public void addStructureViewerListener(Object svl)
287 if (!listeners.contains(svl))
289 listeners.addElement(svl);
294 * Returns the filename the PDB id is already mapped to if known, or null if
300 public String alreadyMappedToFile(String pdbid)
302 for (StructureMapping sm : mappings)
304 if (sm.getPdbId().equalsIgnoreCase(pdbid))
313 * Import structure data and register a structure mapping for broadcasting
314 * colouring, mouseovers and selection events (convenience wrapper).
317 * - one or more sequences to be mapped to pdbFile
318 * @param targetChains
319 * - optional chain specification for mapping each sequence to pdb
320 * (may be nill, individual elements may be nill)
322 * - structure data resource
324 * - how to resolve data from resource
325 * @return null or the structure data parsed as a pdb file
327 synchronized public StructureFile setMapping(SequenceI[] sequence,
328 String[] targetChains, String pdbFile, DataSourceType protocol,
329 IProgressIndicator progress)
331 return computeMapping(true, sequence, targetChains, pdbFile, protocol,
332 progress, null, null, true);
336 * Import a single structure file and register sequence structure mappings for
337 * broadcasting colouring, mouseovers and selection events (convenience
340 * @param forStructureView
341 * when true, record the mapping for use in mouseOvers
343 * - one or more sequences to be mapped to pdbFile
344 * @param targetChains
345 * - optional chain specification for mapping each sequence to pdb
346 * (may be nill, individual elements may be nill)
348 * - structure data resource
350 * - how to resolve data from resource
351 * @return null or the structure data parsed as a pdb file
353 synchronized public StructureFile setMapping(boolean forStructureView,
354 SequenceI[] sequenceArray, String[] targetChainIds,
355 String pdbFile, DataSourceType sourceType, TFType tft,
358 return setMapping(forStructureView, sequenceArray, targetChainIds,
359 pdbFile, sourceType, tft, paeFilename, true);
363 * create sequence structure mappings between each sequence and the given
364 * pdbFile (retrieved via the given protocol). Either constructs a mapping
365 * using NW alignment or derives one from any available SIFTS mapping data.
367 * @param forStructureView
368 * when true, record the mapping for use in mouseOvers
370 * @param sequenceArray
371 * - one or more sequences to be mapped to pdbFile
372 * @param targetChainIds
373 * - optional chain specification for mapping each sequence to pdb
374 * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
375 * - this should be List<List<String>>, empty lists indicate no
376 * predefined mappings
378 * - structure data resource
380 * - how to resolve data from resource
382 * - specify how to interpret the temperature factor column in the
385 * - when not null, specifies a filename containing a matrix
386 * formatted in JSON using one of the known PAE formats
387 * @param doXferSettings
388 * - when true, transfer annotation to mapped sequences in
390 * @return null or the structure data parsed as a pdb file
392 synchronized public StructureFile setMapping(boolean forStructureView,
393 SequenceI[] sequenceArray, String[] targetChainIds,
394 String pdbFile, DataSourceType sourceType, TFType tft,
395 String paeFilename, boolean doXferSettings)
397 return computeMapping(forStructureView, sequenceArray, targetChainIds,
398 pdbFile, sourceType, null, tft, paeFilename, doXferSettings);
402 * create sequence structure mappings between each sequence and the given
403 * pdbFile (retrieved via the given protocol). Either constructs a mapping
404 * using NW alignment or derives one from any available SIFTS mapping data.
406 * @param forStructureView
407 * when true, record the mapping for use in mouseOvers
409 * @param sequenceArray
410 * - one or more sequences to be mapped to pdbFile
411 * @param targetChainIds
412 * - optional chain specification for mapping each sequence to pdb
413 * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
414 * - this should be List<List<String>>, empty lists indicate no
415 * predefined mappings
417 * - structure data resource
419 * - how to resolve data from resource
420 * @param IProgressIndicator
421 * reference to UI component that maintains a progress bar for the
424 * - specify how to interpret the temperature factor column in the
427 * - when not null, specifies a filename containing a matrix
428 * formatted in JSON using one of the known PAE formats
429 * @param doXferSettings
430 * - when true, transfer annotation to mapped sequences in
432 * @return null or the structure data parsed as a pdb file
434 synchronized public StructureFile computeMapping(boolean forStructureView,
435 SequenceI[] sequenceArray, String[] targetChainIds,
436 String pdbFile, DataSourceType sourceType,
437 IProgressIndicator progress, TFType tft, String paeFilename,
438 boolean doXferSettings)
440 long progressSessionId = System.currentTimeMillis() * 3;
443 * do we extract and transfer annotation from 3D data ?
445 // FIXME: possibly should just delete
447 boolean parseSecStr = processSecondaryStructure
448 && !isStructureFileProcessed(pdbFile, sequenceArray);
450 StructureFile pdb = null;
451 boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
454 // FIXME if sourceType is not null, we've lost data here
455 sourceType = AppletFormatAdapter.checkProtocol(pdbFile);
456 pdb = new JmolParser(false, pdbFile, sourceType);
457 if (paeFilename != null)
459 pdb.setPAEMatrix(paeFilename);
461 pdb.setTemperatureFactorType(tft);
462 pdb.addSettings(parseSecStr && processSecondaryStructure,
463 parseSecStr && addTempFacAnnot,
464 parseSecStr && secStructServices);
465 // save doXferSettings and reset after doParse()
466 boolean temp = pdb.getDoXferSettings();
467 pdb.setDoXferSettings(doXferSettings);
469 pdb.setDoXferSettings(temp);
470 if (pdb.getId() != null && pdb.getId().trim().length() > 0
471 && DataSourceType.FILE == sourceType)
473 registerPDBFile(pdb.getId().trim(), pdbFile);
475 // if PDBId is unavailable then skip SIFTS mapping execution path
476 // TODO: JAL-3868 need to know if structure is actually from
477 // PDB (has valid PDB ID and has provenance suggesting it
478 // actually came from PDB)
479 boolean isProtein = false;
480 for (SequenceI s : sequenceArray)
488 isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable()
489 && !pdb.getId().startsWith("AF-") && isProtein;
491 } catch (Exception ex)
493 ex.printStackTrace();
497 * sifts client - non null if SIFTS mappings are to be used
499 SiftsClient siftsClient = null;
504 siftsClient = new SiftsClient(pdb);
506 } catch (SiftsException e)
508 isMapUsingSIFTs = false;
509 Console.error("SIFTS mapping failed", e);
510 Console.error("Falling back on Needleman & Wunsch alignment");
514 String targetChainId;
515 for (int s = 0; s < sequenceArray.length; s++)
517 boolean infChain = true;
518 final SequenceI seq = sequenceArray[s];
520 while (ds.getDatasetSequence() != null)
522 ds = ds.getDatasetSequence();
525 if (targetChainIds != null && targetChainIds[s] != null)
528 targetChainId = targetChainIds[s];
530 else if (seq.getName().indexOf("|") > -1)
532 targetChainId = seq.getName()
533 .substring(seq.getName().lastIndexOf("|") + 1);
534 if (targetChainId.length() > 1)
536 if (targetChainId.trim().length() == 0)
542 // not a valid chain identifier
553 * Attempt pairwise alignment of the sequence with each chain in the PDB,
554 * and remember the highest scoring chain
557 AlignSeq maxAlignseq = null;
558 String maxChainId = " ";
559 PDBChain maxChain = null;
560 boolean first = true;
561 for (PDBChain chain : pdb.getChains())
563 if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
566 continue; // don't try to map chains don't match.
568 // TODO: correctly determine sequence type for mixed na/peptide
570 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
571 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
574 // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
575 // as.calcScoreMatrix();
576 // as.traceAlignment();
578 if (first || as.maxscore > max
579 || (as.maxscore == max && chain.id.equals(targetChainId)))
585 maxChainId = chain.id;
588 if (maxChain == null)
593 if (sourceType == DataSourceType.PASTE)
595 pdbFile = "INLINE" + pdb.getId();
598 List<StructureMapping> seqToStrucMapping = new ArrayList<>();
599 if (isMapUsingSIFTs && seq.isProtein())
601 if (progress != null)
603 progress.setProgressBar(
605 .getString("status.obtaining_mapping_with_sifts"),
608 jalview.datamodel.Mapping sqmpping = maxAlignseq
609 .getMappingFromS1(false);
610 if (targetChainId != null && !targetChainId.trim().isEmpty())
612 StructureMapping siftsMapping;
615 siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
616 pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
617 seqToStrucMapping.add(siftsMapping);
618 maxChain.makeExactMapping(siftsMapping, seq);
619 maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS",
620 pdb.getId().toLowerCase(Locale.ROOT));
621 maxChain.transferResidueAnnotation(siftsMapping, null);
622 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
624 } catch (SiftsException e)
626 // fall back to NW alignment
627 Console.error(e.getMessage());
628 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
629 targetChainId, maxChain, pdb, maxAlignseq);
630 seqToStrucMapping.add(nwMapping);
631 maxChain.makeExactMapping(maxAlignseq, seq);
632 maxChain.transferRESNUMFeatures(seq, "IEA:Jalview",
633 pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is
636 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
637 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
642 List<StructureMapping> foundSiftsMappings = new ArrayList<>();
643 for (PDBChain chain : pdb.getChains())
645 StructureMapping siftsMapping = null;
648 siftsMapping = getStructureMapping(seq, pdbFile, chain.id,
649 pdb, chain, sqmpping, maxAlignseq, siftsClient);
650 foundSiftsMappings.add(siftsMapping);
651 chain.makeExactMapping(siftsMapping, seq);
652 chain.transferRESNUMFeatures(seq, "IEA: SIFTS",
653 pdb.getId().toLowerCase(Locale.ROOT));// FIXME: is this
655 chain.transferResidueAnnotation(siftsMapping, null);
656 } catch (SiftsException e)
658 jalview.bin.Console.errPrintln(e.getMessage());
659 } catch (Exception e)
661 jalview.bin.Console.errPrintln(
662 "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
663 jalview.bin.Console.errPrintln(e.getMessage());
666 if (!foundSiftsMappings.isEmpty())
668 seqToStrucMapping.addAll(foundSiftsMappings);
669 ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
673 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
674 maxChainId, maxChain, pdb, maxAlignseq);
675 seqToStrucMapping.add(nwMapping);
676 maxChain.transferRESNUMFeatures(seq, null,
677 pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is this
679 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
680 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
686 if (progress != null)
688 progress.setProgressBar(
689 MessageManager.getString(
690 "status.obtaining_mapping_with_nw_alignment"),
693 StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
694 maxChain, pdb, maxAlignseq);
695 seqToStrucMapping.add(nwMapping);
696 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
698 if (forStructureView)
700 for (StructureMapping sm : seqToStrucMapping)
702 addStructureMapping(sm); // not addAll!
705 if (progress != null)
707 progress.setProgressBar(null, progressSessionId);
714 * check if we need to extract secondary structure from given pdbFile and
715 * transfer to sequences
718 * @param sequenceArray
721 private boolean isStructureFileProcessed(String pdbFile,
722 SequenceI[] sequenceArray)
724 boolean processed = false;
725 if (isPDBFileRegistered(pdbFile))
727 for (SequenceI sq : sequenceArray)
730 while (ds.getDatasetSequence() != null)
732 ds = ds.getDatasetSequence();
735 if (ds.getAnnotation() != null)
737 for (AlignmentAnnotation ala : ds.getAnnotation())
739 // false if any annotation present from this structure
740 // JBPNote this fails for jmol/chimera view because the *file* is
741 // passed, not the structure data ID -
742 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
753 public void addStructureMapping(StructureMapping sm)
755 if (!mappings.contains(sm))
762 * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
767 * @param targetChainId
773 * client for retrieval of SIFTS mappings for this structure
775 * @throws SiftsException
777 private StructureMapping getStructureMapping(SequenceI seq,
778 String pdbFile, String targetChainId, StructureFile pdb,
779 PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
780 AlignSeq maxAlignseq, SiftsClient siftsClient)
781 throws SiftsException
783 StructureMapping curChainMapping = siftsClient
784 .getSiftsStructureMapping(seq, pdbFile, targetChainId);
787 PDBChain chain = pdb.findChain(targetChainId);
790 chain.transferResidueAnnotation(curChainMapping, null);
792 } catch (Exception e)
796 return curChainMapping;
799 private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
800 String maxChainId, PDBChain maxChain, StructureFile pdb,
801 AlignSeq maxAlignseq)
803 final StringBuilder mappingDetails = new StringBuilder(128);
804 mappingDetails.append(NEWLINE)
805 .append("Sequence \u27f7 Structure mapping details");
806 mappingDetails.append(NEWLINE);
808 .append("Method: inferred with Needleman & Wunsch alignment");
809 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
810 .append(NEWLINE).append("Sequence = ")
811 .append(maxChain.sequence.getSequenceAsString());
812 mappingDetails.append(NEWLINE).append("No of residues = ")
813 .append(maxChain.residues.size()).append(NEWLINE)
815 PrintStream ps = new PrintStream(System.out)
818 public void print(String x)
820 mappingDetails.append(x);
824 public void println()
826 mappingDetails.append(NEWLINE);
830 maxAlignseq.printAlignment(ps);
832 mappingDetails.append(NEWLINE).append("PDB start/end ");
833 mappingDetails.append(String.valueOf(maxAlignseq.seq2start))
835 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
836 mappingDetails.append(NEWLINE).append("SEQ start/end ");
839 .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1)))
841 mappingDetails.append(
842 String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1)));
843 mappingDetails.append(NEWLINE);
844 maxChain.makeExactMapping(maxAlignseq, seq);
845 jalview.datamodel.Mapping sqmpping = maxAlignseq
846 .getMappingFromS1(false);
847 maxChain.transferRESNUMFeatures(seq, null,
848 pdb.getId().toLowerCase(Locale.ROOT));
850 HashMap<Integer, int[]> mapping = new HashMap<>();
857 Atom tmp = maxChain.atoms.elementAt(index);
858 if ((resNum != tmp.resNumber || insCode != tmp.insCode)
859 && tmp.alignmentMapping != -1)
861 resNum = tmp.resNumber;
862 insCode = tmp.insCode;
863 if (tmp.alignmentMapping >= -1)
865 mapping.put(tmp.alignmentMapping + 1,
867 { tmp.resNumber, tmp.atomIndex });
872 } while (index < maxChain.atoms.size());
874 StructureMapping nwMapping = new StructureMapping(seq, pdbFile,
875 pdb.getId(), maxChainId, mapping, mappingDetails.toString());
876 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
880 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
882 listeners.removeElement(svl);
883 if (svl instanceof SequenceListener)
885 for (int i = 0; i < listeners.size(); i++)
887 if (listeners.elementAt(i) instanceof StructureListener)
889 ((StructureListener) listeners.elementAt(i))
890 .releaseReferences(svl);
895 if (pdbfiles == null)
901 * Remove mappings to the closed listener's PDB files, but first check if
902 * another listener is still interested
904 List<String> pdbs = new ArrayList<>(Arrays.asList(pdbfiles));
906 StructureListener sl;
907 for (int i = 0; i < listeners.size(); i++)
909 if (listeners.elementAt(i) instanceof StructureListener)
911 sl = (StructureListener) listeners.elementAt(i);
912 for (String pdbfile : sl.getStructureFiles())
914 pdbs.remove(pdbfile);
920 * Rebuild the mappings set, retaining only those which are for 'other' PDB
925 List<StructureMapping> tmp = new ArrayList<>();
926 for (StructureMapping sm : mappings)
928 if (!pdbs.contains(sm.pdbfile))
939 * hack to highlight a range of positions at once on any structure views
943 * - series of int start-end ranges as positions on sequenceRef
947 public void highlightPositionsOn(SequenceI sequenceRef, int[][] is,
950 boolean hasSequenceListeners = handlingVamsasMo
951 || !seqmappings.isEmpty();
952 SearchResultsI results = null;
953 ArrayList<Integer> listOfPositions = new ArrayList<Integer>();
956 for (int p = s_e[0]; p <= s_e[1]; listOfPositions.add(p++))
959 int seqpos[] = new int[listOfPositions.size()];
961 for (Integer p : listOfPositions)
966 for (i = 0; i < listeners.size(); i++)
968 Object listener = listeners.elementAt(i);
969 if (listener == source)
971 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
972 // Temporary fudge with SequenceListener.getVamsasSource()
975 if (listener instanceof StructureListener)
977 highlightStructure((StructureListener) listener, sequenceRef,
985 * Propagate mouseover of a single position in a structure
992 public String mouseOverStructure(int pdbResNum, String chain,
995 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
996 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
997 return mouseOverStructure(atoms);
1001 * Propagate mouseover or selection of multiple positions in a structure
1005 public String mouseOverStructure(List<AtomSpec> atoms)
1007 if (listeners == null)
1009 // old or prematurely sent event
1012 boolean hasSequenceListener = false;
1013 for (int i = 0; i < listeners.size(); i++)
1015 if (listeners.elementAt(i) instanceof SequenceListener)
1017 hasSequenceListener = true;
1020 if (!hasSequenceListener)
1025 SearchResultsI results = findAlignmentPositionsForStructurePositions(
1027 String result = null;
1028 for (Object li : listeners)
1030 if (li instanceof SequenceListener)
1032 String s = ((SequenceListener) li).highlightSequence(results);
1043 * Constructs a SearchResults object holding regions (if any) in the Jalview
1044 * alignment which have a mapping to the structure viewer positions in the
1050 public SearchResultsI findAlignmentPositionsForStructurePositions(
1051 List<AtomSpec> atoms)
1053 SearchResultsI results = new SearchResults();
1054 for (AtomSpec atom : atoms)
1056 SequenceI lastseq = null;
1058 for (StructureMapping sm : mappings)
1060 if (sm.pdbfile.equals(atom.getPdbFile())
1061 && sm.pdbchain.equals(atom.getChain()))
1063 int indexpos = sm.getSeqPos(atom.getPdbResNum());
1064 if (lastipos != indexpos || lastseq != sm.sequence)
1066 results.appendResult(sm.sequence, indexpos, indexpos);
1067 lastipos = indexpos;
1068 lastseq = sm.sequence;
1069 // construct highlighted sequence list
1070 for (AlignedCodonFrame acf : seqmappings)
1072 acf.markMappedRegion(sm.sequence, indexpos, results);
1082 * highlight regions associated with a position (indexpos) in seq
1085 * the sequence that the mouse over occurred on
1087 * the absolute position being mouseovered in seq (0 to seq.length())
1089 * the sequence position (if -1, seq.findPosition is called to
1090 * resolve the residue number)
1092 public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
1093 VamsasSource source)
1095 boolean hasSequenceListeners = handlingVamsasMo
1096 || !seqmappings.isEmpty();
1097 SearchResultsI results = null;
1100 seqPos = seq.findPosition(indexpos);
1102 for (int i = 0; i < listeners.size(); i++)
1104 Object listener = listeners.elementAt(i);
1105 if (listener == source)
1107 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1108 // Temporary fudge with SequenceListener.getVamsasSource()
1111 if (listener instanceof StructureListener)
1113 highlightStructure((StructureListener) listener, seq, seqPos);
1117 if (listener instanceof SequenceListener)
1119 final SequenceListener seqListener = (SequenceListener) listener;
1120 if (hasSequenceListeners
1121 && seqListener.getVamsasSource() != source)
1123 if (relaySeqMappings)
1125 if (results == null)
1127 results = MappingUtils.buildSearchResults(seq, seqPos,
1130 if (handlingVamsasMo)
1132 results.addResult(seq, seqPos, seqPos);
1135 if (!results.isEmpty())
1137 seqListener.highlightSequence(results);
1142 else if (listener instanceof VamsasListener && !handlingVamsasMo)
1144 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
1147 else if (listener instanceof SecondaryStructureListener)
1149 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
1157 * Send suitable messages to a StructureListener to highlight atoms
1158 * corresponding to the given sequence position(s)
1164 public void highlightStructure(StructureListener sl, SequenceI seq,
1167 if (!sl.isListeningFor(seq))
1172 List<AtomSpec> atoms = new ArrayList<>();
1173 for (StructureMapping sm : mappings)
1175 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1176 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1177 .getDatasetSequence() == seq.getDatasetSequence()))
1179 for (int index : positions)
1181 atomNo = sm.getAtomNum(index);
1185 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1186 sm.getPDBResNum(index), atomNo));
1191 sl.highlightAtoms(atoms);
1194 public void highlightStructureRegionsFor(StructureListener sl,
1195 SequenceI[] seqs, int... columns)
1197 List<SequenceI> to_highlight = new ArrayList<SequenceI>();
1198 for (SequenceI seq : seqs)
1200 if (sl.isListeningFor(seq))
1202 to_highlight.add(seq);
1205 if (to_highlight.size() == 0)
1209 List<AtomSpec> atoms = new ArrayList<>();
1210 for (SequenceI seq : to_highlight)
1213 for (StructureMapping sm : mappings)
1215 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1216 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1217 .getDatasetSequence() == seq.getDatasetSequence()))
1220 for (int i = 0; i < columns.length; i += 2)
1222 ContiguousI positions = seq.findPositions(columns[i] + 1,
1223 columns[i + 1] + 1);
1224 if (positions == null)
1228 for (int index = positions.getBegin(); index <= positions
1232 atomNo = sm.getAtomNum(index);
1236 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1237 sm.getPDBResNum(index), atomNo));
1243 if (atoms.size() > 0)
1245 sl.highlightAtoms(atoms);
1251 * true if a mouse over event from an external (ie Vamsas) source is being
1254 boolean handlingVamsasMo = false;
1259 * as mouseOverSequence but only route event to SequenceListeners
1263 * in an alignment sequence
1265 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
1266 VamsasSource source)
1268 handlingVamsasMo = true;
1269 long msg = sequenceI.hashCode() * (1 + position);
1273 mouseOverSequence(sequenceI, position, -1, source);
1275 handlingVamsasMo = false;
1278 public Annotation[] colourSequenceFromStructure(SequenceI seq,
1282 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
1283 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
1285 * Annotation [] annotations = new Annotation[seq.getLength()];
1287 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
1288 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
1289 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
1291 * for (int j = 0; j < mappings.length; j++) {
1293 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
1294 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
1295 * jalview.bin.Console.outPrintln(pdbid+" "+mappings[j].getPdbId() +"
1296 * "+mappings[j].pdbfile);
1298 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
1299 * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
1301 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
1302 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
1303 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
1304 * mappings[j].pdbfile); }
1306 * annotations[index] = new Annotation("X",null,' ',0,col); } return
1307 * annotations; } } } }
1309 * return annotations;
1313 public void structureSelectionChanged()
1317 public void sequenceSelectionChanged()
1321 public void sequenceColoursChanged(Object source)
1323 StructureListener sl;
1324 for (int i = 0; i < listeners.size(); i++)
1326 if (listeners.elementAt(i) instanceof StructureListener)
1328 sl = (StructureListener) listeners.elementAt(i);
1329 sl.updateColours(source);
1334 public StructureMapping[] getMapping(String pdbfile)
1336 List<StructureMapping> tmp = new ArrayList<>();
1337 for (StructureMapping sm : mappings)
1339 if (sm.pdbfile.equals(pdbfile))
1344 return tmp.toArray(new StructureMapping[tmp.size()]);
1348 * Returns a readable description of all mappings for the given pdbfile to any
1349 * of the given sequences
1355 public String printMappings(String pdbfile, List<SequenceI> seqs)
1357 if (pdbfile == null || seqs == null || seqs.isEmpty())
1362 StringBuilder sb = new StringBuilder(64);
1363 for (StructureMapping sm : mappings)
1365 if (Platform.pathEquals(sm.pdbfile, pdbfile)
1366 && seqs.contains(sm.sequence))
1368 sb.append(sm.mappingDetails);
1370 // separator makes it easier to read multiple mappings
1371 sb.append("=====================");
1377 return sb.toString();
1381 * Remove the given mapping
1385 public void deregisterMapping(AlignedCodonFrame acf)
1389 boolean removed = seqmappings.remove(acf);
1390 if (removed && seqmappings.isEmpty())
1392 jalview.bin.Console.outPrintln("All mappings removed");
1398 * Add each of the given codonFrames to the stored set, if not aready present.
1402 public void registerMappings(List<AlignedCodonFrame> mappings)
1404 if (mappings != null)
1406 for (AlignedCodonFrame acf : mappings)
1408 registerMapping(acf);
1414 * Add the given mapping to the stored set, unless already stored.
1416 public void registerMapping(AlignedCodonFrame acf)
1420 if (!seqmappings.contains(acf))
1422 seqmappings.add(acf);
1428 * Resets this object to its initial state by removing all registered
1429 * listeners, codon mappings, PDB file mappings
1431 public void resetAll()
1433 if (mappings != null)
1437 if (seqmappings != null)
1439 seqmappings.clear();
1441 if (sel_listeners != null)
1443 sel_listeners.clear();
1445 if (listeners != null)
1449 if (commandListeners != null)
1451 commandListeners.clear();
1453 if (view_listeners != null)
1455 view_listeners.clear();
1457 if (pdbFileNameId != null)
1459 pdbFileNameId.clear();
1461 if (pdbIdFileName != null)
1463 pdbIdFileName.clear();
1467 public void addSelectionListener(SelectionListener selecter)
1469 if (!sel_listeners.contains(selecter))
1471 sel_listeners.add(selecter);
1475 public void removeSelectionListener(SelectionListener toremove)
1477 if (sel_listeners.contains(toremove))
1479 sel_listeners.remove(toremove);
1483 public synchronized void sendSelection(
1484 jalview.datamodel.SequenceGroup selection,
1485 jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden,
1486 SelectionSource source)
1488 for (SelectionListener slis : sel_listeners)
1492 slis.selection(selection, colsel, hidden, source);
1497 Vector<AlignmentViewPanelListener> view_listeners = new Vector<>();
1499 public synchronized void sendViewPosition(
1500 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1501 int startSeq, int endSeq)
1504 if (view_listeners != null && view_listeners.size() > 0)
1506 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1508 while (listeners.hasMoreElements())
1510 AlignmentViewPanelListener slis = listeners.nextElement();
1513 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1521 * release all references associated with this manager provider
1523 * @param jalviewLite
1525 public static void release(StructureSelectionManagerProvider jalviewLite)
1527 // synchronized (instances)
1529 if (instances == null)
1533 StructureSelectionManager mnger = (instances.get(jalviewLite));
1536 instances.remove(jalviewLite);
1539 /* bsoares 2019-03-20 finalize deprecated, no apparent external
1540 * resources to close
1542 // mnger.finalize();
1543 } catch (Throwable x)
1550 public void registerPDBEntry(PDBEntry pdbentry)
1552 if (pdbentry.getFile() != null
1553 && pdbentry.getFile().trim().length() > 0)
1555 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1559 public void addCommandListener(CommandListener cl)
1561 if (!commandListeners.contains(cl))
1563 commandListeners.add(cl);
1567 public boolean hasCommandListener(CommandListener cl)
1569 return this.commandListeners.contains(cl);
1572 public boolean removeCommandListener(CommandListener l)
1574 return commandListeners.remove(l);
1578 * Forward a command to any command listeners (except for the command's
1582 * the command to be broadcast (in its form after being performed)
1584 * if true, the command was being 'undone'
1587 public void commandPerformed(CommandI command, boolean undo,
1588 VamsasSource source)
1590 for (CommandListener listener : commandListeners)
1592 listener.mirrorCommand(command, undo, this, source);
1597 * Returns a new CommandI representing the given command as mapped to the
1598 * given sequences. If no mapping could be made, or the command is not of a
1599 * mappable kind, returns null.
1607 public CommandI mapCommand(CommandI command, boolean undo,
1608 final AlignmentI mapTo, char gapChar)
1610 if (command instanceof EditCommand)
1612 return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo,
1613 gapChar, seqmappings);
1615 else if (command instanceof OrderCommand)
1617 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1618 mapTo, seqmappings);
1623 public List<AlignedCodonFrame> getSequenceMappings()
1629 * quick and dirty route to just highlight all structure positions for a range
1632 * @param sequencesArray
1634 * start-end columns on sequencesArray
1636 * origin parent AlignmentPanel
1638 public void highlightPositionsOnMany(SequenceI[] sequencesArray, int[] is,
1641 for (int i = 0; i < listeners.size(); i++)
1643 Object listener = listeners.elementAt(i);
1644 if (listener == source)
1646 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1647 // Temporary fudge with SequenceListener.getVamsasSource()
1650 if (listener instanceof StructureListener)
1652 highlightStructureRegionsFor((StructureListener) listener,
1653 sequencesArray, is);
1658 public Map<String, String> getPdbFileNameIdMap()
1660 return pdbFileNameId;
1663 public Map<String, String> getPdbIdFileNameMap()
1665 return pdbIdFileName;