2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.structure;
23 import java.io.PrintStream;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.Enumeration;
28 import java.util.HashMap;
29 import java.util.IdentityHashMap;
30 import java.util.List;
31 import java.util.Locale;
33 import java.util.Vector;
35 import jalview.analysis.AlignSeq;
36 import jalview.api.StructureSelectionManagerProvider;
37 import jalview.bin.Cache;
38 import jalview.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.HiddenColumns;
46 import jalview.datamodel.PDBEntry;
47 import jalview.datamodel.SearchResults;
48 import jalview.datamodel.SearchResultsI;
49 import jalview.datamodel.SequenceI;
50 import jalview.ext.jmol.JmolParser;
51 import jalview.gui.IProgressIndicator;
52 import jalview.io.AppletFormatAdapter;
53 import jalview.io.DataSourceType;
54 import jalview.io.StructureFile;
55 import jalview.util.MappingUtils;
56 import jalview.util.MessageManager;
57 import jalview.util.Platform;
58 import jalview.ws.sifts.SiftsClient;
59 import jalview.ws.sifts.SiftsException;
60 import jalview.ws.sifts.SiftsSettings;
62 import mc_view.PDBChain;
63 import mc_view.PDBfile;
65 public class StructureSelectionManager
67 public final static String NEWLINE = System.lineSeparator();
69 static IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> instances;
71 private List<StructureMapping> mappings = new ArrayList<>();
73 private boolean processSecondaryStructure = false;
75 private boolean secStructServices = false;
77 private boolean addTempFacAnnot = false;
80 * Set of any registered mappings between (dataset) sequences.
82 private List<AlignedCodonFrame> seqmappings = new ArrayList<>();
84 private List<CommandListener> commandListeners = new ArrayList<>();
86 private List<SelectionListener> sel_listeners = new ArrayList<>();
89 * @return true if will try to use external services for processing secondary
92 public boolean isSecStructServices()
94 return secStructServices;
98 * control use of external services for processing secondary structure
100 * @param secStructServices
102 public void setSecStructServices(boolean secStructServices)
104 this.secStructServices = secStructServices;
108 * flag controlling addition of any kind of structural annotation
110 * @return true if temperature factor annotation will be added
112 public boolean isAddTempFacAnnot()
114 return addTempFacAnnot;
118 * set flag controlling addition of structural annotation
120 * @param addTempFacAnnot
122 public void setAddTempFacAnnot(boolean addTempFacAnnot)
124 this.addTempFacAnnot = addTempFacAnnot;
129 * @return if true, the structure manager will attempt to add secondary
130 * structure lines for unannotated sequences
133 public boolean isProcessSecondaryStructure()
135 return processSecondaryStructure;
139 * Control whether structure manager will try to annotate mapped sequences
140 * with secondary structure from PDB data.
144 public void setProcessSecondaryStructure(boolean enable)
146 processSecondaryStructure = enable;
150 * debug function - write all mappings to stdout
152 public void reportMapping()
154 if (mappings.isEmpty())
156 System.err.println("reportMapping: No PDB/Sequence mappings.");
161 "reportMapping: There are " + mappings.size() + " mappings.");
163 for (StructureMapping sm : mappings)
165 System.err.println("mapping " + i++ + " : " + sm.pdbfile);
171 * map between the PDB IDs (or structure identifiers) used by Jalview and the
172 * absolute filenames for PDB data that corresponds to it
174 Map<String, String> pdbIdFileName = new HashMap<>();
176 Map<String, String> pdbFileNameId = new HashMap<>();
178 public void registerPDBFile(String idForFile, String absoluteFile)
180 pdbIdFileName.put(idForFile, absoluteFile);
181 pdbFileNameId.put(absoluteFile, idForFile);
184 public String findIdForPDBFile(String idOrFile)
186 String id = pdbFileNameId.get(idOrFile);
190 public String findFileForPDBId(String idOrFile)
192 String id = pdbIdFileName.get(idOrFile);
196 public boolean isPDBFileRegistered(String idOrFile)
198 return pdbFileNameId.containsKey(idOrFile)
199 || pdbIdFileName.containsKey(idOrFile);
202 private static StructureSelectionManager nullProvider = null;
204 public static StructureSelectionManager getStructureSelectionManager(
205 StructureSelectionManagerProvider context)
209 if (nullProvider == null)
211 if (instances != null)
213 throw new Error(MessageManager.getString(
214 "error.implementation_error_structure_selection_manager_null"),
215 new NullPointerException(MessageManager
216 .getString("exception.ssm_context_is_null")));
220 nullProvider = new StructureSelectionManager();
225 if (instances == null)
227 instances = new java.util.IdentityHashMap<>();
229 StructureSelectionManager instance = instances.get(context);
230 if (instance == null)
232 if (nullProvider != null)
234 instance = nullProvider;
238 instance = new StructureSelectionManager();
240 instances.put(context, instance);
246 * flag controlling whether SeqMappings are relayed from received sequence
247 * mouse over events to other sequences
249 boolean relaySeqMappings = true;
252 * Enable or disable relay of seqMapping events to other sequences. You might
253 * want to do this if there are many sequence mappings and the host computer
258 public void setRelaySeqMappings(boolean relay)
260 relaySeqMappings = relay;
264 * get the state of the relay seqMappings flag.
266 * @return true if sequence mouse overs are being relayed to other mapped
269 public boolean isRelaySeqMappingsEnabled()
271 return relaySeqMappings;
274 Vector listeners = new Vector();
277 * register a listener for alignment sequence mouseover events
281 public void addStructureViewerListener(Object svl)
283 if (!listeners.contains(svl))
285 listeners.addElement(svl);
290 * Returns the filename the PDB id is already mapped to if known, or null if
296 public String alreadyMappedToFile(String pdbid)
298 for (StructureMapping sm : mappings)
300 if (sm.getPdbId().equalsIgnoreCase(pdbid))
309 * Import structure data and register a structure mapping for broadcasting
310 * colouring, mouseovers and selection events (convenience wrapper).
313 * - one or more sequences to be mapped to pdbFile
314 * @param targetChains
315 * - optional chain specification for mapping each sequence to pdb
316 * (may be nill, individual elements may be nill)
318 * - structure data resource
320 * - how to resolve data from resource
321 * @return null or the structure data parsed as a pdb file
323 synchronized public StructureFile setMapping(SequenceI[] sequence,
324 String[] targetChains, String pdbFile, DataSourceType protocol,
325 IProgressIndicator progress)
327 return computeMapping(true, sequence, targetChains, pdbFile, protocol,
332 * Import a single structure file and register sequence structure mappings for
333 * broadcasting colouring, mouseovers and selection events (convenience
336 * @param forStructureView
337 * when true, record the mapping for use in mouseOvers
339 * - one or more sequences to be mapped to pdbFile
340 * @param targetChains
341 * - optional chain specification for mapping each sequence to pdb
342 * (may be nill, individual elements may be nill)
344 * - structure data resource
346 * - how to resolve data from resource
347 * @return null or the structure data parsed as a pdb file
349 synchronized public StructureFile setMapping(boolean forStructureView,
350 SequenceI[] sequenceArray, String[] targetChainIds,
351 String pdbFile, DataSourceType sourceType)
353 return computeMapping(forStructureView, sequenceArray, targetChainIds,
354 pdbFile, sourceType, null);
358 * create sequence structure mappings between each sequence and the given
359 * pdbFile (retrieved via the given protocol). Either constructs a mapping
360 * using NW alignment or derives one from any available SIFTS mapping data.
362 * @param forStructureView
363 * when true, record the mapping for use in mouseOvers
365 * @param sequenceArray
366 * - one or more sequences to be mapped to pdbFile
367 * @param targetChainIds
368 * - optional chain specification for mapping each sequence to pdb
369 * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
370 * - this should be List<List<String>>, empty lists indicate no
371 * predefined mappings
373 * - structure data resource
375 * - how to resolve data from resource
376 * @param IProgressIndicator
377 * reference to UI component that maintains a progress bar for the
379 * @return null or the structure data parsed as a pdb file
381 synchronized public StructureFile computeMapping(
382 boolean forStructureView, SequenceI[] sequenceArray,
383 String[] targetChainIds, String pdbFile, DataSourceType sourceType,
384 IProgressIndicator progress)
386 long progressSessionId = System.currentTimeMillis() * 3;
389 * do we extract and transfer annotation from 3D data ?
391 // FIXME: possibly should just delete
393 boolean parseSecStr = processSecondaryStructure
394 ? isStructureFileProcessed(pdbFile, sequenceArray)
397 StructureFile pdb = null;
398 boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
401 // FIXME if sourceType is not null, we've lost data here
402 sourceType = AppletFormatAdapter.checkProtocol(pdbFile);
403 pdb = new JmolParser(false, pdbFile, sourceType);
404 pdb.addSettings(parseSecStr && processSecondaryStructure,
405 parseSecStr && addTempFacAnnot,
406 parseSecStr && secStructServices);
408 if (pdb.getId() != null && pdb.getId().trim().length() > 0
409 && DataSourceType.FILE == sourceType)
411 registerPDBFile(pdb.getId().trim(), pdbFile);
413 // if PDBId is unavailable then skip SIFTS mapping execution path
414 // TODO: JAL-3868 need to know if structure is actually from
415 // PDB (has valid PDB ID and has provenance suggesting it
416 // actually came from PDB)
417 boolean isProtein = false;
418 for (SequenceI s:sequenceArray) {
424 isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable() && !pdb.getId().startsWith("AF-") && isProtein;
426 } catch (Exception ex)
428 ex.printStackTrace();
432 * sifts client - non null if SIFTS mappings are to be used
434 SiftsClient siftsClient = null;
439 siftsClient = new SiftsClient(pdb);
441 } catch (SiftsException e)
443 isMapUsingSIFTs = false;
444 Cache.log.error("SIFTS mapping failed", e);
445 Cache.log.error("Falling back on Needleman & Wunsch alignment");
449 String targetChainId;
450 for (int s = 0; s < sequenceArray.length; s++)
452 boolean infChain = true;
453 final SequenceI seq = sequenceArray[s];
455 while (ds.getDatasetSequence() != null)
457 ds = ds.getDatasetSequence();
460 if (targetChainIds != null && targetChainIds[s] != null)
463 targetChainId = targetChainIds[s];
465 else if (seq.getName().indexOf("|") > -1)
467 targetChainId = seq.getName()
468 .substring(seq.getName().lastIndexOf("|") + 1);
469 if (targetChainId.length() > 1)
471 if (targetChainId.trim().length() == 0)
477 // not a valid chain identifier
488 * Attempt pairwise alignment of the sequence with each chain in the PDB,
489 * and remember the highest scoring chain
492 AlignSeq maxAlignseq = null;
493 String maxChainId = " ";
494 PDBChain maxChain = null;
495 boolean first = true;
496 for (PDBChain chain : pdb.getChains())
498 if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
501 continue; // don't try to map chains don't match.
503 // TODO: correctly determine sequence type for mixed na/peptide
505 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
506 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
509 // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
510 // as.calcScoreMatrix();
511 // as.traceAlignment();
513 if (first || as.maxscore > max
514 || (as.maxscore == max && chain.id.equals(targetChainId)))
520 maxChainId = chain.id;
523 if (maxChain == null)
528 if (sourceType == DataSourceType.PASTE)
530 pdbFile = "INLINE" + pdb.getId();
533 List<StructureMapping> seqToStrucMapping = new ArrayList<>();
534 if (isMapUsingSIFTs && seq.isProtein())
536 if (progress!=null) {
537 progress.setProgressBar(MessageManager
538 .getString("status.obtaining_mapping_with_sifts"),
541 jalview.datamodel.Mapping sqmpping = maxAlignseq
542 .getMappingFromS1(false);
543 if (targetChainId != null && !targetChainId.trim().isEmpty())
545 StructureMapping siftsMapping;
548 siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
549 pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
550 seqToStrucMapping.add(siftsMapping);
551 maxChain.makeExactMapping(siftsMapping, seq);
552 maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS",pdb.getId().toLowerCase(Locale.ROOT));
553 maxChain.transferResidueAnnotation(siftsMapping, null);
554 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
556 } catch (SiftsException e)
558 // fall back to NW alignment
559 Cache.log.error(e.getMessage());
560 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
561 targetChainId, maxChain, pdb, maxAlignseq);
562 seqToStrucMapping.add(nwMapping);
563 maxChain.makeExactMapping(maxAlignseq, seq);
564 maxChain.transferRESNUMFeatures(seq, "IEA:Jalview",pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is
567 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
568 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
573 List<StructureMapping> foundSiftsMappings = new ArrayList<>();
574 for (PDBChain chain : pdb.getChains())
576 StructureMapping siftsMapping = null;
579 siftsMapping = getStructureMapping(seq,
580 pdbFile, chain.id, pdb, chain, sqmpping, maxAlignseq,
582 foundSiftsMappings.add(siftsMapping);
583 chain.makeExactMapping(siftsMapping, seq);
584 chain.transferRESNUMFeatures(seq, "IEA: SIFTS",pdb.getId().toLowerCase(Locale.ROOT));// FIXME: is this
586 chain.transferResidueAnnotation(siftsMapping, null);
587 } catch (SiftsException e)
589 System.err.println(e.getMessage());
595 "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
596 System.err.println(e.getMessage());
599 if (!foundSiftsMappings.isEmpty())
601 seqToStrucMapping.addAll(foundSiftsMappings);
602 ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
606 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
607 maxChainId, maxChain, pdb, maxAlignseq);
608 seqToStrucMapping.add(nwMapping);
609 maxChain.transferRESNUMFeatures(seq, null,pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is this
611 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
612 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
618 if (progress != null)
620 progress.setProgressBar(MessageManager
621 .getString("status.obtaining_mapping_with_nw_alignment"),
624 StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
625 maxChain, pdb, maxAlignseq);
626 seqToStrucMapping.add(nwMapping);
627 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
629 if (forStructureView)
631 for (StructureMapping sm : seqToStrucMapping)
633 addStructureMapping(sm); // not addAll!
636 if (progress != null)
638 progress.setProgressBar(null, progressSessionId);
645 * check if we need to extract secondary structure from given pdbFile and
646 * transfer to sequences
649 * @param sequenceArray
652 private boolean isStructureFileProcessed(String pdbFile,
653 SequenceI[] sequenceArray)
655 boolean parseSecStr = true;
656 if (isPDBFileRegistered(pdbFile))
658 for (SequenceI sq : sequenceArray)
661 while (ds.getDatasetSequence() != null)
663 ds = ds.getDatasetSequence();
666 if (ds.getAnnotation() != null)
668 for (AlignmentAnnotation ala : ds.getAnnotation())
670 // false if any annotation present from this structure
671 // JBPNote this fails for jmol/chimera view because the *file* is
672 // passed, not the structure data ID -
673 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
684 public void addStructureMapping(StructureMapping sm)
686 if (!mappings.contains(sm))
693 * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
698 * @param targetChainId
704 * client for retrieval of SIFTS mappings for this structure
706 * @throws SiftsException
708 private StructureMapping getStructureMapping(SequenceI seq,
709 String pdbFile, String targetChainId, StructureFile pdb,
710 PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
711 AlignSeq maxAlignseq, SiftsClient siftsClient) throws SiftsException
713 StructureMapping curChainMapping = siftsClient
714 .getSiftsStructureMapping(seq, pdbFile, targetChainId);
717 PDBChain chain = pdb.findChain(targetChainId);
720 chain.transferResidueAnnotation(curChainMapping, null);
722 } catch (Exception e)
726 return curChainMapping;
729 private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
730 String maxChainId, PDBChain maxChain, StructureFile pdb,
731 AlignSeq maxAlignseq)
733 final StringBuilder mappingDetails = new StringBuilder(128);
734 mappingDetails.append(NEWLINE)
735 .append("Sequence \u27f7 Structure mapping details");
736 mappingDetails.append(NEWLINE);
738 .append("Method: inferred with Needleman & Wunsch alignment");
739 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
740 .append(NEWLINE).append("Sequence = ")
741 .append(maxChain.sequence.getSequenceAsString());
742 mappingDetails.append(NEWLINE).append("No of residues = ")
743 .append(maxChain.residues.size()).append(NEWLINE)
745 PrintStream ps = new PrintStream(System.out)
748 public void print(String x)
750 mappingDetails.append(x);
754 public void println()
756 mappingDetails.append(NEWLINE);
760 maxAlignseq.printAlignment(ps);
762 mappingDetails.append(NEWLINE).append("PDB start/end ");
763 mappingDetails.append(String.valueOf(maxAlignseq.seq2start))
765 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
766 mappingDetails.append(NEWLINE).append("SEQ start/end ");
769 .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1)))
771 mappingDetails.append(
772 String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1)));
773 mappingDetails.append(NEWLINE);
774 maxChain.makeExactMapping(maxAlignseq, seq);
775 jalview.datamodel.Mapping sqmpping = maxAlignseq
776 .getMappingFromS1(false);
777 maxChain.transferRESNUMFeatures(seq, null, pdb.getId().toLowerCase(Locale.ROOT));
779 HashMap<Integer, int[]> mapping = new HashMap<>();
786 Atom tmp = maxChain.atoms.elementAt(index);
787 if ((resNum != tmp.resNumber || insCode != tmp.insCode)
788 && tmp.alignmentMapping != -1)
790 resNum = tmp.resNumber;
791 insCode = tmp.insCode;
792 if (tmp.alignmentMapping >= -1)
794 mapping.put(tmp.alignmentMapping + 1,
796 { tmp.resNumber, tmp.atomIndex });
801 } while (index < maxChain.atoms.size());
803 StructureMapping nwMapping = new StructureMapping(seq, pdbFile,
804 pdb.getId(), maxChainId, mapping, mappingDetails.toString());
805 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
809 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
811 listeners.removeElement(svl);
812 if (svl instanceof SequenceListener)
814 for (int i = 0; i < listeners.size(); i++)
816 if (listeners.elementAt(i) instanceof StructureListener)
818 ((StructureListener) listeners.elementAt(i))
819 .releaseReferences(svl);
824 if (pdbfiles == null)
830 * Remove mappings to the closed listener's PDB files, but first check if
831 * another listener is still interested
833 List<String> pdbs = new ArrayList<>(Arrays.asList(pdbfiles));
835 StructureListener sl;
836 for (int i = 0; i < listeners.size(); i++)
838 if (listeners.elementAt(i) instanceof StructureListener)
840 sl = (StructureListener) listeners.elementAt(i);
841 for (String pdbfile : sl.getStructureFiles())
843 pdbs.remove(pdbfile);
849 * Rebuild the mappings set, retaining only those which are for 'other' PDB
854 List<StructureMapping> tmp = new ArrayList<>();
855 for (StructureMapping sm : mappings)
857 if (!pdbs.contains(sm.pdbfile))
868 * Propagate mouseover of a single position in a structure
875 public String mouseOverStructure(int pdbResNum, String chain,
878 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
879 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
880 return mouseOverStructure(atoms);
884 * Propagate mouseover or selection of multiple positions in a structure
888 public String mouseOverStructure(List<AtomSpec> atoms)
890 if (listeners == null)
892 // old or prematurely sent event
895 boolean hasSequenceListener = false;
896 for (int i = 0; i < listeners.size(); i++)
898 if (listeners.elementAt(i) instanceof SequenceListener)
900 hasSequenceListener = true;
903 if (!hasSequenceListener)
908 SearchResultsI results = findAlignmentPositionsForStructurePositions(
910 String result = null;
911 for (Object li : listeners)
913 if (li instanceof SequenceListener)
915 String s = ((SequenceListener) li).highlightSequence(results);
926 * Constructs a SearchResults object holding regions (if any) in the Jalview
927 * alignment which have a mapping to the structure viewer positions in the
933 public SearchResultsI findAlignmentPositionsForStructurePositions(
934 List<AtomSpec> atoms)
936 SearchResultsI results = new SearchResults();
937 for (AtomSpec atom : atoms)
939 SequenceI lastseq = null;
941 for (StructureMapping sm : mappings)
943 if (sm.pdbfile.equals(atom.getPdbFile())
944 && sm.pdbchain.equals(atom.getChain()))
946 int indexpos = sm.getSeqPos(atom.getPdbResNum());
947 if (lastipos != indexpos || lastseq != sm.sequence)
949 results.addResult(sm.sequence, indexpos, indexpos);
951 lastseq = sm.sequence;
952 // construct highlighted sequence list
953 for (AlignedCodonFrame acf : seqmappings)
955 acf.markMappedRegion(sm.sequence, indexpos, results);
965 * highlight regions associated with a position (indexpos) in seq
968 * the sequence that the mouse over occurred on
970 * the absolute position being mouseovered in seq (0 to seq.length())
972 * the sequence position (if -1, seq.findPosition is called to
973 * resolve the residue number)
975 public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
978 boolean hasSequenceListeners = handlingVamsasMo
979 || !seqmappings.isEmpty();
980 SearchResultsI results = null;
983 seqPos = seq.findPosition(indexpos);
985 for (int i = 0; i < listeners.size(); i++)
987 Object listener = listeners.elementAt(i);
988 if (listener == source)
990 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
991 // Temporary fudge with SequenceListener.getVamsasSource()
994 if (listener instanceof StructureListener)
996 highlightStructure((StructureListener) listener, seq, seqPos);
1000 if (listener instanceof SequenceListener)
1002 final SequenceListener seqListener = (SequenceListener) listener;
1003 if (hasSequenceListeners
1004 && seqListener.getVamsasSource() != source)
1006 if (relaySeqMappings)
1008 if (results == null)
1010 results = MappingUtils.buildSearchResults(seq, seqPos,
1013 if (handlingVamsasMo)
1015 results.addResult(seq, seqPos, seqPos);
1018 if (!results.isEmpty())
1020 seqListener.highlightSequence(results);
1025 else if (listener instanceof VamsasListener && !handlingVamsasMo)
1027 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
1030 else if (listener instanceof SecondaryStructureListener)
1032 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
1040 * Send suitable messages to a StructureListener to highlight atoms
1041 * corresponding to the given sequence position(s)
1047 public void highlightStructure(StructureListener sl, SequenceI seq,
1050 if (!sl.isListeningFor(seq))
1055 List<AtomSpec> atoms = new ArrayList<>();
1056 for (StructureMapping sm : mappings)
1058 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1059 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1060 .getDatasetSequence() == seq.getDatasetSequence()))
1062 for (int index : positions)
1064 atomNo = sm.getAtomNum(index);
1068 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1069 sm.getPDBResNum(index), atomNo));
1074 sl.highlightAtoms(atoms);
1078 * true if a mouse over event from an external (ie Vamsas) source is being
1081 boolean handlingVamsasMo = false;
1086 * as mouseOverSequence but only route event to SequenceListeners
1090 * in an alignment sequence
1092 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
1093 VamsasSource source)
1095 handlingVamsasMo = true;
1096 long msg = sequenceI.hashCode() * (1 + position);
1100 mouseOverSequence(sequenceI, position, -1, source);
1102 handlingVamsasMo = false;
1105 public Annotation[] colourSequenceFromStructure(SequenceI seq,
1109 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
1110 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
1112 * Annotation [] annotations = new Annotation[seq.getLength()];
1114 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
1115 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
1116 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
1118 * for (int j = 0; j < mappings.length; j++) {
1120 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
1121 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
1122 * System.out.println(pdbid+" "+mappings[j].getPdbId() +"
1123 * "+mappings[j].pdbfile);
1125 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
1126 * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
1128 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
1129 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
1130 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
1131 * mappings[j].pdbfile); }
1133 * annotations[index] = new Annotation("X",null,' ',0,col); } return
1134 * annotations; } } } }
1136 * return annotations;
1140 public void structureSelectionChanged()
1144 public void sequenceSelectionChanged()
1148 public void sequenceColoursChanged(Object source)
1150 StructureListener sl;
1151 for (int i = 0; i < listeners.size(); i++)
1153 if (listeners.elementAt(i) instanceof StructureListener)
1155 sl = (StructureListener) listeners.elementAt(i);
1156 sl.updateColours(source);
1161 public StructureMapping[] getMapping(String pdbfile)
1163 List<StructureMapping> tmp = new ArrayList<>();
1164 for (StructureMapping sm : mappings)
1166 if (sm.pdbfile.equals(pdbfile))
1171 return tmp.toArray(new StructureMapping[tmp.size()]);
1175 * Returns a readable description of all mappings for the given pdbfile to any
1176 * of the given sequences
1182 public String printMappings(String pdbfile, List<SequenceI> seqs)
1184 if (pdbfile == null || seqs == null || seqs.isEmpty())
1189 StringBuilder sb = new StringBuilder(64);
1190 for (StructureMapping sm : mappings)
1192 if (Platform.pathEquals(sm.pdbfile, pdbfile)
1193 && seqs.contains(sm.sequence))
1195 sb.append(sm.mappingDetails);
1197 // separator makes it easier to read multiple mappings
1198 sb.append("=====================");
1204 return sb.toString();
1208 * Remove the given mapping
1212 public void deregisterMapping(AlignedCodonFrame acf)
1216 boolean removed = seqmappings.remove(acf);
1217 if (removed && seqmappings.isEmpty())
1219 System.out.println("All mappings removed");
1225 * Add each of the given codonFrames to the stored set, if not aready present.
1229 public void registerMappings(List<AlignedCodonFrame> mappings)
1231 if (mappings != null)
1233 for (AlignedCodonFrame acf : mappings)
1235 registerMapping(acf);
1241 * Add the given mapping to the stored set, unless already stored.
1243 public void registerMapping(AlignedCodonFrame acf)
1247 if (!seqmappings.contains(acf))
1249 seqmappings.add(acf);
1255 * Resets this object to its initial state by removing all registered
1256 * listeners, codon mappings, PDB file mappings
1258 public void resetAll()
1260 if (mappings != null)
1264 if (seqmappings != null)
1266 seqmappings.clear();
1268 if (sel_listeners != null)
1270 sel_listeners.clear();
1272 if (listeners != null)
1276 if (commandListeners != null)
1278 commandListeners.clear();
1280 if (view_listeners != null)
1282 view_listeners.clear();
1284 if (pdbFileNameId != null)
1286 pdbFileNameId.clear();
1288 if (pdbIdFileName != null)
1290 pdbIdFileName.clear();
1294 public void addSelectionListener(SelectionListener selecter)
1296 if (!sel_listeners.contains(selecter))
1298 sel_listeners.add(selecter);
1302 public void removeSelectionListener(SelectionListener toremove)
1304 if (sel_listeners.contains(toremove))
1306 sel_listeners.remove(toremove);
1310 public synchronized void sendSelection(
1311 jalview.datamodel.SequenceGroup selection,
1312 jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden,
1313 SelectionSource source)
1315 for (SelectionListener slis : sel_listeners)
1319 slis.selection(selection, colsel, hidden, source);
1324 Vector<AlignmentViewPanelListener> view_listeners = new Vector<>();
1326 public synchronized void sendViewPosition(
1327 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1328 int startSeq, int endSeq)
1331 if (view_listeners != null && view_listeners.size() > 0)
1333 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1335 while (listeners.hasMoreElements())
1337 AlignmentViewPanelListener slis = listeners.nextElement();
1340 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1348 * release all references associated with this manager provider
1350 * @param jalviewLite
1352 public static void release(StructureSelectionManagerProvider jalviewLite)
1354 // synchronized (instances)
1356 if (instances == null)
1360 StructureSelectionManager mnger = (instances.get(jalviewLite));
1363 instances.remove(jalviewLite);
1366 /* bsoares 2019-03-20 finalize deprecated, no apparent external
1367 * resources to close
1369 // mnger.finalize();
1370 } catch (Throwable x)
1377 public void registerPDBEntry(PDBEntry pdbentry)
1379 if (pdbentry.getFile() != null
1380 && pdbentry.getFile().trim().length() > 0)
1382 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1386 public void addCommandListener(CommandListener cl)
1388 if (!commandListeners.contains(cl))
1390 commandListeners.add(cl);
1394 public boolean hasCommandListener(CommandListener cl)
1396 return this.commandListeners.contains(cl);
1399 public boolean removeCommandListener(CommandListener l)
1401 return commandListeners.remove(l);
1405 * Forward a command to any command listeners (except for the command's
1409 * the command to be broadcast (in its form after being performed)
1411 * if true, the command was being 'undone'
1414 public void commandPerformed(CommandI command, boolean undo,
1415 VamsasSource source)
1417 for (CommandListener listener : commandListeners)
1419 listener.mirrorCommand(command, undo, this, source);
1424 * Returns a new CommandI representing the given command as mapped to the
1425 * given sequences. If no mapping could be made, or the command is not of a
1426 * mappable kind, returns null.
1434 public CommandI mapCommand(CommandI command, boolean undo,
1435 final AlignmentI mapTo, char gapChar)
1437 if (command instanceof EditCommand)
1439 return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo,
1440 gapChar, seqmappings);
1442 else if (command instanceof OrderCommand)
1444 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1445 mapTo, seqmappings);
1450 public List<AlignedCodonFrame> getSequenceMappings()