2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.structure;
23 import java.io.PrintStream;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.Enumeration;
28 import java.util.HashMap;
29 import java.util.IdentityHashMap;
30 import java.util.List;
31 import java.util.Locale;
33 import java.util.Vector;
35 import jalview.analysis.AlignSeq;
36 import jalview.api.StructureSelectionManagerProvider;
37 import jalview.bin.Console;
38 import jalview.commands.CommandI;
39 import jalview.commands.EditCommand;
40 import jalview.commands.OrderCommand;
41 import jalview.datamodel.AlignedCodonFrame;
42 import jalview.datamodel.AlignmentAnnotation;
43 import jalview.datamodel.AlignmentI;
44 import jalview.datamodel.Annotation;
45 import jalview.datamodel.ContiguousI;
46 import jalview.datamodel.HiddenColumns;
47 import jalview.datamodel.PDBEntry;
48 import jalview.datamodel.SearchResults;
49 import jalview.datamodel.SearchResultsI;
50 import jalview.datamodel.SequenceI;
51 import jalview.ext.jmol.JmolParser;
52 import jalview.gui.IProgressIndicator;
53 import jalview.io.AppletFormatAdapter;
54 import jalview.io.DataSourceType;
55 import jalview.io.StructureFile;
56 import jalview.structure.StructureImportSettings.TFType;
57 import jalview.util.MappingUtils;
58 import jalview.util.MessageManager;
59 import jalview.util.Platform;
60 import jalview.ws.sifts.SiftsClient;
61 import jalview.ws.sifts.SiftsException;
62 import jalview.ws.sifts.SiftsSettings;
64 import mc_view.PDBChain;
65 import mc_view.PDBfile;
67 public class StructureSelectionManager
69 public final static String NEWLINE = System.lineSeparator();
71 static IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> instances;
73 private List<StructureMapping> mappings = new ArrayList<>();
75 private boolean processSecondaryStructure = false;
77 private boolean secStructServices = false;
79 private boolean addTempFacAnnot = false;
82 * Set of any registered mappings between (dataset) sequences.
84 private List<AlignedCodonFrame> seqmappings = new ArrayList<>();
86 private List<CommandListener> commandListeners = new ArrayList<>();
88 private List<SelectionListener> sel_listeners = new ArrayList<>();
91 * @return true if will try to use external services for processing secondary
94 public boolean isSecStructServices()
96 return secStructServices;
100 * control use of external services for processing secondary structure
102 * @param secStructServices
104 public void setSecStructServices(boolean secStructServices)
106 this.secStructServices = secStructServices;
110 * flag controlling addition of any kind of structural annotation
112 * @return true if temperature factor annotation will be added
114 public boolean isAddTempFacAnnot()
116 return addTempFacAnnot;
120 * set flag controlling addition of structural annotation
122 * @param addTempFacAnnot
124 public void setAddTempFacAnnot(boolean addTempFacAnnot)
126 this.addTempFacAnnot = addTempFacAnnot;
131 * @return if true, the structure manager will attempt to add secondary
132 * structure lines for unannotated sequences
135 public boolean isProcessSecondaryStructure()
137 return processSecondaryStructure;
141 * Control whether structure manager will try to annotate mapped sequences
142 * with secondary structure from PDB data.
146 public void setProcessSecondaryStructure(boolean enable)
148 processSecondaryStructure = enable;
152 * debug function - write all mappings to stdout
154 public void reportMapping()
156 if (mappings.isEmpty())
158 System.err.println("reportMapping: No PDB/Sequence mappings.");
163 "reportMapping: There are " + mappings.size() + " mappings.");
165 for (StructureMapping sm : mappings)
167 System.err.println("mapping " + i++ + " : " + sm.pdbfile);
173 * map between the PDB IDs (or structure identifiers) used by Jalview and the
174 * absolute filenames for PDB data that corresponds to it
176 Map<String, String> pdbIdFileName = new HashMap<>();
178 Map<String, String> pdbFileNameId = new HashMap<>();
180 public void registerPDBFile(String idForFile, String absoluteFile)
182 pdbIdFileName.put(idForFile, absoluteFile);
183 pdbFileNameId.put(absoluteFile, idForFile);
186 public String findIdForPDBFile(String idOrFile)
188 String id = pdbFileNameId.get(idOrFile);
192 public String findFileForPDBId(String idOrFile)
194 String id = pdbIdFileName.get(idOrFile);
198 public boolean isPDBFileRegistered(String idOrFile)
200 return pdbFileNameId.containsKey(idOrFile)
201 || pdbIdFileName.containsKey(idOrFile);
204 private static StructureSelectionManager nullProvider = null;
206 public static StructureSelectionManager getStructureSelectionManager(
207 StructureSelectionManagerProvider context)
211 if (nullProvider == null)
213 if (instances != null)
215 throw new Error(MessageManager.getString(
216 "error.implementation_error_structure_selection_manager_null"),
217 new NullPointerException(MessageManager
218 .getString("exception.ssm_context_is_null")));
222 nullProvider = new StructureSelectionManager();
227 if (instances == null)
229 instances = new java.util.IdentityHashMap<>();
231 StructureSelectionManager instance = instances.get(context);
232 if (instance == null)
234 if (nullProvider != null)
236 instance = nullProvider;
240 instance = new StructureSelectionManager();
242 instances.put(context, instance);
248 * flag controlling whether SeqMappings are relayed from received sequence
249 * mouse over events to other sequences
251 boolean relaySeqMappings = true;
254 * Enable or disable relay of seqMapping events to other sequences. You might
255 * want to do this if there are many sequence mappings and the host computer
260 public void setRelaySeqMappings(boolean relay)
262 relaySeqMappings = relay;
266 * get the state of the relay seqMappings flag.
268 * @return true if sequence mouse overs are being relayed to other mapped
271 public boolean isRelaySeqMappingsEnabled()
273 return relaySeqMappings;
276 Vector listeners = new Vector();
279 * register a listener for alignment sequence mouseover events
283 public void addStructureViewerListener(Object svl)
285 if (!listeners.contains(svl))
287 listeners.addElement(svl);
292 * Returns the filename the PDB id is already mapped to if known, or null if
298 public String alreadyMappedToFile(String pdbid)
300 for (StructureMapping sm : mappings)
302 if (sm.getPdbId().equalsIgnoreCase(pdbid))
311 * Import structure data and register a structure mapping for broadcasting
312 * colouring, mouseovers and selection events (convenience wrapper).
315 * - one or more sequences to be mapped to pdbFile
316 * @param targetChains
317 * - optional chain specification for mapping each sequence to pdb
318 * (may be nill, individual elements may be nill)
320 * - structure data resource
322 * - how to resolve data from resource
323 * @return null or the structure data parsed as a pdb file
325 synchronized public StructureFile setMapping(SequenceI[] sequence,
326 String[] targetChains, String pdbFile, DataSourceType protocol,
327 IProgressIndicator progress)
329 return computeMapping(true, sequence, targetChains, pdbFile, protocol,
330 progress, null, null, true);
334 * Import a single structure file and register sequence structure mappings for
335 * broadcasting colouring, mouseovers and selection events (convenience
338 * @param forStructureView
339 * when true, record the mapping for use in mouseOvers
341 * - one or more sequences to be mapped to pdbFile
342 * @param targetChains
343 * - optional chain specification for mapping each sequence to pdb
344 * (may be nill, individual elements may be nill)
346 * - structure data resource
348 * - how to resolve data from resource
349 * @return null or the structure data parsed as a pdb file
351 synchronized public StructureFile setMapping(boolean forStructureView,
352 SequenceI[] sequenceArray, String[] targetChainIds,
353 String pdbFile, DataSourceType sourceType, TFType tft,
356 return setMapping(forStructureView, sequenceArray, targetChainIds,
357 pdbFile, sourceType, tft, paeFilename, true);
360 synchronized public StructureFile setMapping(boolean forStructureView,
361 SequenceI[] sequenceArray, String[] targetChainIds,
362 String pdbFile, DataSourceType sourceType, TFType tft,
363 String paeFilename, boolean doXferSettings)
365 return computeMapping(forStructureView, sequenceArray, targetChainIds,
366 pdbFile, sourceType, null, tft, paeFilename, doXferSettings);
370 * create sequence structure mappings between each sequence and the given
371 * pdbFile (retrieved via the given protocol). Either constructs a mapping
372 * using NW alignment or derives one from any available SIFTS mapping data.
374 * @param forStructureView
375 * when true, record the mapping for use in mouseOvers
377 * @param sequenceArray
378 * - one or more sequences to be mapped to pdbFile
379 * @param targetChainIds
380 * - optional chain specification for mapping each sequence to pdb
381 * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
382 * - this should be List<List<String>>, empty lists indicate no
383 * predefined mappings
385 * - structure data resource
387 * - how to resolve data from resource
388 * @param IProgressIndicator
389 * reference to UI component that maintains a progress bar for the
391 * @return null or the structure data parsed as a pdb file
393 synchronized public StructureFile computeMapping(boolean forStructureView,
394 SequenceI[] sequenceArray, String[] targetChainIds,
395 String pdbFile, DataSourceType sourceType,
396 IProgressIndicator progress, TFType tft, String paeFilename,
397 boolean doXferSettings)
399 long progressSessionId = System.currentTimeMillis() * 3;
402 * do we extract and transfer annotation from 3D data ?
404 // FIXME: possibly should just delete
406 boolean parseSecStr = processSecondaryStructure
407 && !isStructureFileProcessed(pdbFile, sequenceArray);
409 StructureFile pdb = null;
410 boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
413 // FIXME if sourceType is not null, we've lost data here
414 sourceType = AppletFormatAdapter.checkProtocol(pdbFile);
415 pdb = new JmolParser(false, pdbFile, sourceType);
416 if (paeFilename != null)
418 pdb.setPAEMatrix(paeFilename);
420 pdb.setTemperatureFactorType(tft);
421 pdb.addSettings(parseSecStr && processSecondaryStructure,
422 parseSecStr && addTempFacAnnot,
423 parseSecStr && secStructServices);
424 // save doXferSettings and reset after doParse()
425 boolean temp = pdb.getDoXferSettings();
426 pdb.setDoXferSettings(doXferSettings);
428 pdb.setDoXferSettings(temp);
429 if (pdb.getId() != null && pdb.getId().trim().length() > 0
430 && DataSourceType.FILE == sourceType)
432 registerPDBFile(pdb.getId().trim(), pdbFile);
434 // if PDBId is unavailable then skip SIFTS mapping execution path
435 // TODO: JAL-3868 need to know if structure is actually from
436 // PDB (has valid PDB ID and has provenance suggesting it
437 // actually came from PDB)
438 boolean isProtein = false;
439 for (SequenceI s : sequenceArray)
447 isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable()
448 && !pdb.getId().startsWith("AF-") && isProtein;
450 } catch (Exception ex)
452 ex.printStackTrace();
456 * sifts client - non null if SIFTS mappings are to be used
458 SiftsClient siftsClient = null;
463 siftsClient = new SiftsClient(pdb);
465 } catch (SiftsException e)
467 isMapUsingSIFTs = false;
468 Console.error("SIFTS mapping failed", e);
469 Console.error("Falling back on Needleman & Wunsch alignment");
473 String targetChainId;
474 for (int s = 0; s < sequenceArray.length; s++)
476 boolean infChain = true;
477 final SequenceI seq = sequenceArray[s];
479 while (ds.getDatasetSequence() != null)
481 ds = ds.getDatasetSequence();
484 if (targetChainIds != null && targetChainIds[s] != null)
487 targetChainId = targetChainIds[s];
489 else if (seq.getName().indexOf("|") > -1)
491 targetChainId = seq.getName()
492 .substring(seq.getName().lastIndexOf("|") + 1);
493 if (targetChainId.length() > 1)
495 if (targetChainId.trim().length() == 0)
501 // not a valid chain identifier
512 * Attempt pairwise alignment of the sequence with each chain in the PDB,
513 * and remember the highest scoring chain
516 AlignSeq maxAlignseq = null;
517 String maxChainId = " ";
518 PDBChain maxChain = null;
519 boolean first = true;
520 for (PDBChain chain : pdb.getChains())
522 if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
525 continue; // don't try to map chains don't match.
527 // TODO: correctly determine sequence type for mixed na/peptide
529 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
530 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
533 // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
534 // as.calcScoreMatrix();
535 // as.traceAlignment();
537 if (first || as.maxscore > max
538 || (as.maxscore == max && chain.id.equals(targetChainId)))
544 maxChainId = chain.id;
547 if (maxChain == null)
552 if (sourceType == DataSourceType.PASTE)
554 pdbFile = "INLINE" + pdb.getId();
557 List<StructureMapping> seqToStrucMapping = new ArrayList<>();
558 if (isMapUsingSIFTs && seq.isProtein())
560 if (progress != null)
562 progress.setProgressBar(
564 .getString("status.obtaining_mapping_with_sifts"),
567 jalview.datamodel.Mapping sqmpping = maxAlignseq
568 .getMappingFromS1(false);
569 if (targetChainId != null && !targetChainId.trim().isEmpty())
571 StructureMapping siftsMapping;
574 siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
575 pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
576 seqToStrucMapping.add(siftsMapping);
577 maxChain.makeExactMapping(siftsMapping, seq);
578 maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS",
579 pdb.getId().toLowerCase(Locale.ROOT));
580 maxChain.transferResidueAnnotation(siftsMapping, null);
581 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
583 } catch (SiftsException e)
585 // fall back to NW alignment
586 Console.error(e.getMessage());
587 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
588 targetChainId, maxChain, pdb, maxAlignseq);
589 seqToStrucMapping.add(nwMapping);
590 maxChain.makeExactMapping(maxAlignseq, seq);
591 maxChain.transferRESNUMFeatures(seq, "IEA:Jalview",
592 pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is
595 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
596 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
601 List<StructureMapping> foundSiftsMappings = new ArrayList<>();
602 for (PDBChain chain : pdb.getChains())
604 StructureMapping siftsMapping = null;
607 siftsMapping = getStructureMapping(seq, pdbFile, chain.id,
608 pdb, chain, sqmpping, maxAlignseq, siftsClient);
609 foundSiftsMappings.add(siftsMapping);
610 chain.makeExactMapping(siftsMapping, seq);
611 chain.transferRESNUMFeatures(seq, "IEA: SIFTS",
612 pdb.getId().toLowerCase(Locale.ROOT));// FIXME: is this
614 chain.transferResidueAnnotation(siftsMapping, null);
615 } catch (SiftsException e)
617 System.err.println(e.getMessage());
618 } catch (Exception e)
621 "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
622 System.err.println(e.getMessage());
625 if (!foundSiftsMappings.isEmpty())
627 seqToStrucMapping.addAll(foundSiftsMappings);
628 ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
632 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
633 maxChainId, maxChain, pdb, maxAlignseq);
634 seqToStrucMapping.add(nwMapping);
635 maxChain.transferRESNUMFeatures(seq, null,
636 pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is this
638 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
639 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
645 if (progress != null)
647 progress.setProgressBar(
648 MessageManager.getString(
649 "status.obtaining_mapping_with_nw_alignment"),
652 StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
653 maxChain, pdb, maxAlignseq);
654 seqToStrucMapping.add(nwMapping);
655 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
657 if (forStructureView)
659 for (StructureMapping sm : seqToStrucMapping)
661 addStructureMapping(sm); // not addAll!
664 if (progress != null)
666 progress.setProgressBar(null, progressSessionId);
673 * check if we need to extract secondary structure from given pdbFile and
674 * transfer to sequences
677 * @param sequenceArray
680 private boolean isStructureFileProcessed(String pdbFile,
681 SequenceI[] sequenceArray)
683 boolean processed = false;
684 if (isPDBFileRegistered(pdbFile))
686 for (SequenceI sq : sequenceArray)
689 while (ds.getDatasetSequence() != null)
691 ds = ds.getDatasetSequence();
694 if (ds.getAnnotation() != null)
696 for (AlignmentAnnotation ala : ds.getAnnotation())
698 // false if any annotation present from this structure
699 // JBPNote this fails for jmol/chimera view because the *file* is
700 // passed, not the structure data ID -
701 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
712 public void addStructureMapping(StructureMapping sm)
714 if (!mappings.contains(sm))
721 * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
726 * @param targetChainId
732 * client for retrieval of SIFTS mappings for this structure
734 * @throws SiftsException
736 private StructureMapping getStructureMapping(SequenceI seq,
737 String pdbFile, String targetChainId, StructureFile pdb,
738 PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
739 AlignSeq maxAlignseq, SiftsClient siftsClient)
740 throws SiftsException
742 StructureMapping curChainMapping = siftsClient
743 .getSiftsStructureMapping(seq, pdbFile, targetChainId);
746 PDBChain chain = pdb.findChain(targetChainId);
749 chain.transferResidueAnnotation(curChainMapping, null);
751 } catch (Exception e)
755 return curChainMapping;
758 private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
759 String maxChainId, PDBChain maxChain, StructureFile pdb,
760 AlignSeq maxAlignseq)
762 final StringBuilder mappingDetails = new StringBuilder(128);
763 mappingDetails.append(NEWLINE)
764 .append("Sequence \u27f7 Structure mapping details");
765 mappingDetails.append(NEWLINE);
767 .append("Method: inferred with Needleman & Wunsch alignment");
768 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
769 .append(NEWLINE).append("Sequence = ")
770 .append(maxChain.sequence.getSequenceAsString());
771 mappingDetails.append(NEWLINE).append("No of residues = ")
772 .append(maxChain.residues.size()).append(NEWLINE)
774 PrintStream ps = new PrintStream(System.out)
777 public void print(String x)
779 mappingDetails.append(x);
783 public void println()
785 mappingDetails.append(NEWLINE);
789 maxAlignseq.printAlignment(ps);
791 mappingDetails.append(NEWLINE).append("PDB start/end ");
792 mappingDetails.append(String.valueOf(maxAlignseq.seq2start))
794 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
795 mappingDetails.append(NEWLINE).append("SEQ start/end ");
798 .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1)))
800 mappingDetails.append(
801 String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1)));
802 mappingDetails.append(NEWLINE);
803 maxChain.makeExactMapping(maxAlignseq, seq);
804 jalview.datamodel.Mapping sqmpping = maxAlignseq
805 .getMappingFromS1(false);
806 maxChain.transferRESNUMFeatures(seq, null,
807 pdb.getId().toLowerCase(Locale.ROOT));
809 HashMap<Integer, int[]> mapping = new HashMap<>();
816 Atom tmp = maxChain.atoms.elementAt(index);
817 if ((resNum != tmp.resNumber || insCode != tmp.insCode)
818 && tmp.alignmentMapping != -1)
820 resNum = tmp.resNumber;
821 insCode = tmp.insCode;
822 if (tmp.alignmentMapping >= -1)
824 mapping.put(tmp.alignmentMapping + 1,
826 { tmp.resNumber, tmp.atomIndex });
831 } while (index < maxChain.atoms.size());
833 StructureMapping nwMapping = new StructureMapping(seq, pdbFile,
834 pdb.getId(), maxChainId, mapping, mappingDetails.toString());
835 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
839 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
841 listeners.removeElement(svl);
842 if (svl instanceof SequenceListener)
844 for (int i = 0; i < listeners.size(); i++)
846 if (listeners.elementAt(i) instanceof StructureListener)
848 ((StructureListener) listeners.elementAt(i))
849 .releaseReferences(svl);
854 if (pdbfiles == null)
860 * Remove mappings to the closed listener's PDB files, but first check if
861 * another listener is still interested
863 List<String> pdbs = new ArrayList<>(Arrays.asList(pdbfiles));
865 StructureListener sl;
866 for (int i = 0; i < listeners.size(); i++)
868 if (listeners.elementAt(i) instanceof StructureListener)
870 sl = (StructureListener) listeners.elementAt(i);
871 for (String pdbfile : sl.getStructureFiles())
873 pdbs.remove(pdbfile);
879 * Rebuild the mappings set, retaining only those which are for 'other' PDB
884 List<StructureMapping> tmp = new ArrayList<>();
885 for (StructureMapping sm : mappings)
887 if (!pdbs.contains(sm.pdbfile))
898 * hack to highlight a range of positions at once on any structure views
902 * - series of int start-end ranges as positions on sequenceRef
906 public void highlightPositionsOn(SequenceI sequenceRef, int[][] is,
909 boolean hasSequenceListeners = handlingVamsasMo
910 || !seqmappings.isEmpty();
911 SearchResultsI results = null;
912 ArrayList<Integer> listOfPositions = new ArrayList<Integer>();
915 for (int p = s_e[0]; p <= s_e[1]; listOfPositions.add(p++))
918 int seqpos[] = new int[listOfPositions.size()];
920 for (Integer p : listOfPositions)
925 for (i = 0; i < listeners.size(); i++)
927 Object listener = listeners.elementAt(i);
928 if (listener == source)
930 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
931 // Temporary fudge with SequenceListener.getVamsasSource()
934 if (listener instanceof StructureListener)
936 highlightStructure((StructureListener) listener, sequenceRef,
944 * Propagate mouseover of a single position in a structure
951 public String mouseOverStructure(int pdbResNum, String chain,
954 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
955 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
956 return mouseOverStructure(atoms);
960 * Propagate mouseover or selection of multiple positions in a structure
964 public String mouseOverStructure(List<AtomSpec> atoms)
966 if (listeners == null)
968 // old or prematurely sent event
971 boolean hasSequenceListener = false;
972 for (int i = 0; i < listeners.size(); i++)
974 if (listeners.elementAt(i) instanceof SequenceListener)
976 hasSequenceListener = true;
979 if (!hasSequenceListener)
984 SearchResultsI results = findAlignmentPositionsForStructurePositions(
986 String result = null;
987 for (Object li : listeners)
989 if (li instanceof SequenceListener)
991 String s = ((SequenceListener) li).highlightSequence(results);
1002 * Constructs a SearchResults object holding regions (if any) in the Jalview
1003 * alignment which have a mapping to the structure viewer positions in the
1009 public SearchResultsI findAlignmentPositionsForStructurePositions(
1010 List<AtomSpec> atoms)
1012 SearchResultsI results = new SearchResults();
1013 for (AtomSpec atom : atoms)
1015 SequenceI lastseq = null;
1017 for (StructureMapping sm : mappings)
1019 if (sm.pdbfile.equals(atom.getPdbFile())
1020 && sm.pdbchain.equals(atom.getChain()))
1022 int indexpos = sm.getSeqPos(atom.getPdbResNum());
1023 if (lastipos != indexpos || lastseq != sm.sequence)
1025 results.addResult(sm.sequence, indexpos, indexpos);
1026 lastipos = indexpos;
1027 lastseq = sm.sequence;
1028 // construct highlighted sequence list
1029 for (AlignedCodonFrame acf : seqmappings)
1031 acf.markMappedRegion(sm.sequence, indexpos, results);
1041 * highlight regions associated with a position (indexpos) in seq
1044 * the sequence that the mouse over occurred on
1046 * the absolute position being mouseovered in seq (0 to seq.length())
1048 * the sequence position (if -1, seq.findPosition is called to
1049 * resolve the residue number)
1051 public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
1052 VamsasSource source)
1054 boolean hasSequenceListeners = handlingVamsasMo
1055 || !seqmappings.isEmpty();
1056 SearchResultsI results = null;
1059 seqPos = seq.findPosition(indexpos);
1061 for (int i = 0; i < listeners.size(); i++)
1063 Object listener = listeners.elementAt(i);
1064 if (listener == source)
1066 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1067 // Temporary fudge with SequenceListener.getVamsasSource()
1070 if (listener instanceof StructureListener)
1072 highlightStructure((StructureListener) listener, seq, seqPos);
1076 if (listener instanceof SequenceListener)
1078 final SequenceListener seqListener = (SequenceListener) listener;
1079 if (hasSequenceListeners
1080 && seqListener.getVamsasSource() != source)
1082 if (relaySeqMappings)
1084 if (results == null)
1086 results = MappingUtils.buildSearchResults(seq, seqPos,
1089 if (handlingVamsasMo)
1091 results.addResult(seq, seqPos, seqPos);
1094 if (!results.isEmpty())
1096 seqListener.highlightSequence(results);
1101 else if (listener instanceof VamsasListener && !handlingVamsasMo)
1103 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
1106 else if (listener instanceof SecondaryStructureListener)
1108 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
1116 * Send suitable messages to a StructureListener to highlight atoms
1117 * corresponding to the given sequence position(s)
1123 public void highlightStructure(StructureListener sl, SequenceI seq,
1126 if (!sl.isListeningFor(seq))
1131 List<AtomSpec> atoms = new ArrayList<>();
1132 for (StructureMapping sm : mappings)
1134 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1135 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1136 .getDatasetSequence() == seq.getDatasetSequence()))
1138 for (int index : positions)
1140 atomNo = sm.getAtomNum(index);
1144 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1145 sm.getPDBResNum(index), atomNo));
1150 sl.highlightAtoms(atoms);
1153 public void highlightStructureRegionsFor(StructureListener sl,
1154 SequenceI[] seqs, int... columns)
1156 List<SequenceI> to_highlight = new ArrayList<SequenceI>();
1157 for (SequenceI seq : seqs)
1159 if (sl.isListeningFor(seq))
1161 to_highlight.add(seq);
1164 if (to_highlight.size() == 0)
1168 List<AtomSpec> atoms = new ArrayList<>();
1169 for (SequenceI seq : to_highlight)
1172 for (StructureMapping sm : mappings)
1174 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1175 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1176 .getDatasetSequence() == seq.getDatasetSequence()))
1179 for (int i = 0; i < columns.length; i += 2)
1181 ContiguousI positions = seq.findPositions(columns[i] + 1,
1182 columns[i + 1] + 1);
1183 if (positions == null)
1187 for (int index = positions.getBegin(); index <= positions
1191 atomNo = sm.getAtomNum(index);
1195 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1196 sm.getPDBResNum(index), atomNo));
1202 if (atoms.size() > 0)
1204 sl.highlightAtoms(atoms);
1210 * true if a mouse over event from an external (ie Vamsas) source is being
1213 boolean handlingVamsasMo = false;
1218 * as mouseOverSequence but only route event to SequenceListeners
1222 * in an alignment sequence
1224 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
1225 VamsasSource source)
1227 handlingVamsasMo = true;
1228 long msg = sequenceI.hashCode() * (1 + position);
1232 mouseOverSequence(sequenceI, position, -1, source);
1234 handlingVamsasMo = false;
1237 public Annotation[] colourSequenceFromStructure(SequenceI seq,
1241 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
1242 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
1244 * Annotation [] annotations = new Annotation[seq.getLength()];
1246 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
1247 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
1248 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
1250 * for (int j = 0; j < mappings.length; j++) {
1252 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
1253 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
1254 * System.out.println(pdbid+" "+mappings[j].getPdbId() +"
1255 * "+mappings[j].pdbfile);
1257 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
1258 * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
1260 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
1261 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
1262 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
1263 * mappings[j].pdbfile); }
1265 * annotations[index] = new Annotation("X",null,' ',0,col); } return
1266 * annotations; } } } }
1268 * return annotations;
1272 public void structureSelectionChanged()
1276 public void sequenceSelectionChanged()
1280 public void sequenceColoursChanged(Object source)
1282 StructureListener sl;
1283 for (int i = 0; i < listeners.size(); i++)
1285 if (listeners.elementAt(i) instanceof StructureListener)
1287 sl = (StructureListener) listeners.elementAt(i);
1288 sl.updateColours(source);
1293 public StructureMapping[] getMapping(String pdbfile)
1295 List<StructureMapping> tmp = new ArrayList<>();
1296 for (StructureMapping sm : mappings)
1298 if (sm.pdbfile.equals(pdbfile))
1303 return tmp.toArray(new StructureMapping[tmp.size()]);
1307 * Returns a readable description of all mappings for the given pdbfile to any
1308 * of the given sequences
1314 public String printMappings(String pdbfile, List<SequenceI> seqs)
1316 if (pdbfile == null || seqs == null || seqs.isEmpty())
1321 StringBuilder sb = new StringBuilder(64);
1322 for (StructureMapping sm : mappings)
1324 if (Platform.pathEquals(sm.pdbfile, pdbfile)
1325 && seqs.contains(sm.sequence))
1327 sb.append(sm.mappingDetails);
1329 // separator makes it easier to read multiple mappings
1330 sb.append("=====================");
1336 return sb.toString();
1340 * Remove the given mapping
1344 public void deregisterMapping(AlignedCodonFrame acf)
1348 boolean removed = seqmappings.remove(acf);
1349 if (removed && seqmappings.isEmpty())
1351 System.out.println("All mappings removed");
1357 * Add each of the given codonFrames to the stored set, if not aready present.
1361 public void registerMappings(List<AlignedCodonFrame> mappings)
1363 if (mappings != null)
1365 for (AlignedCodonFrame acf : mappings)
1367 registerMapping(acf);
1373 * Add the given mapping to the stored set, unless already stored.
1375 public void registerMapping(AlignedCodonFrame acf)
1379 if (!seqmappings.contains(acf))
1381 seqmappings.add(acf);
1387 * Resets this object to its initial state by removing all registered
1388 * listeners, codon mappings, PDB file mappings
1390 public void resetAll()
1392 if (mappings != null)
1396 if (seqmappings != null)
1398 seqmappings.clear();
1400 if (sel_listeners != null)
1402 sel_listeners.clear();
1404 if (listeners != null)
1408 if (commandListeners != null)
1410 commandListeners.clear();
1412 if (view_listeners != null)
1414 view_listeners.clear();
1416 if (pdbFileNameId != null)
1418 pdbFileNameId.clear();
1420 if (pdbIdFileName != null)
1422 pdbIdFileName.clear();
1426 public void addSelectionListener(SelectionListener selecter)
1428 if (!sel_listeners.contains(selecter))
1430 sel_listeners.add(selecter);
1434 public void removeSelectionListener(SelectionListener toremove)
1436 if (sel_listeners.contains(toremove))
1438 sel_listeners.remove(toremove);
1442 public synchronized void sendSelection(
1443 jalview.datamodel.SequenceGroup selection,
1444 jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden,
1445 SelectionSource source)
1447 for (SelectionListener slis : sel_listeners)
1451 slis.selection(selection, colsel, hidden, source);
1456 Vector<AlignmentViewPanelListener> view_listeners = new Vector<>();
1458 public synchronized void sendViewPosition(
1459 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1460 int startSeq, int endSeq)
1463 if (view_listeners != null && view_listeners.size() > 0)
1465 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1467 while (listeners.hasMoreElements())
1469 AlignmentViewPanelListener slis = listeners.nextElement();
1472 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1480 * release all references associated with this manager provider
1482 * @param jalviewLite
1484 public static void release(StructureSelectionManagerProvider jalviewLite)
1486 // synchronized (instances)
1488 if (instances == null)
1492 StructureSelectionManager mnger = (instances.get(jalviewLite));
1495 instances.remove(jalviewLite);
1498 /* bsoares 2019-03-20 finalize deprecated, no apparent external
1499 * resources to close
1501 // mnger.finalize();
1502 } catch (Throwable x)
1509 public void registerPDBEntry(PDBEntry pdbentry)
1511 if (pdbentry.getFile() != null
1512 && pdbentry.getFile().trim().length() > 0)
1514 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1518 public void addCommandListener(CommandListener cl)
1520 if (!commandListeners.contains(cl))
1522 commandListeners.add(cl);
1526 public boolean hasCommandListener(CommandListener cl)
1528 return this.commandListeners.contains(cl);
1531 public boolean removeCommandListener(CommandListener l)
1533 return commandListeners.remove(l);
1537 * Forward a command to any command listeners (except for the command's
1541 * the command to be broadcast (in its form after being performed)
1543 * if true, the command was being 'undone'
1546 public void commandPerformed(CommandI command, boolean undo,
1547 VamsasSource source)
1549 for (CommandListener listener : commandListeners)
1551 listener.mirrorCommand(command, undo, this, source);
1556 * Returns a new CommandI representing the given command as mapped to the
1557 * given sequences. If no mapping could be made, or the command is not of a
1558 * mappable kind, returns null.
1566 public CommandI mapCommand(CommandI command, boolean undo,
1567 final AlignmentI mapTo, char gapChar)
1569 if (command instanceof EditCommand)
1571 return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo,
1572 gapChar, seqmappings);
1574 else if (command instanceof OrderCommand)
1576 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1577 mapTo, seqmappings);
1582 public List<AlignedCodonFrame> getSequenceMappings()
1588 * quick and dirty route to just highlight all structure positions for a range
1591 * @param sequencesArray
1593 * start-end columns on sequencesArray
1595 * origin parent AlignmentPanel
1597 public void highlightPositionsOnMany(SequenceI[] sequencesArray, int[] is,
1600 for (int i = 0; i < listeners.size(); i++)
1602 Object listener = listeners.elementAt(i);
1603 if (listener == source)
1605 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1606 // Temporary fudge with SequenceListener.getVamsasSource()
1609 if (listener instanceof StructureListener)
1611 highlightStructureRegionsFor((StructureListener) listener,
1612 sequencesArray, is);
1617 public Map<String, String> getPdbFileNameIdMap()
1619 return pdbFileNameId;
1622 public Map<String, String> getPdbIdFileNameMap()
1624 return pdbIdFileName;