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;
32 import java.util.Vector;
34 import jalview.analysis.AlignSeq;
35 import jalview.api.StructureSelectionManagerProvider;
36 import jalview.bin.Cache;
37 import jalview.commands.CommandI;
38 import jalview.commands.EditCommand;
39 import jalview.commands.OrderCommand;
40 import jalview.datamodel.AlignedCodonFrame;
41 import jalview.datamodel.AlignmentAnnotation;
42 import jalview.datamodel.AlignmentI;
43 import jalview.datamodel.Annotation;
44 import jalview.datamodel.HiddenColumns;
45 import jalview.datamodel.PDBEntry;
46 import jalview.datamodel.SearchResults;
47 import jalview.datamodel.SearchResultsI;
48 import jalview.datamodel.SequenceI;
49 import jalview.ext.jmol.JmolParser;
50 import jalview.gui.IProgressIndicator;
51 import jalview.io.AppletFormatAdapter;
52 import jalview.io.DataSourceType;
53 import jalview.io.StructureFile;
54 import jalview.util.MappingUtils;
55 import jalview.util.MessageManager;
56 import jalview.util.Platform;
57 import jalview.ws.sifts.SiftsClient;
58 import jalview.ws.sifts.SiftsException;
59 import jalview.ws.sifts.SiftsSettings;
61 import mc_view.PDBChain;
62 import mc_view.PDBfile;
64 public class StructureSelectionManager
66 public final static String NEWLINE = System.lineSeparator();
68 static IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> instances;
70 private List<StructureMapping> mappings = new ArrayList<>();
72 private boolean processSecondaryStructure = false;
74 private boolean secStructServices = false;
76 private boolean addTempFacAnnot = false;
79 * Set of any registered mappings between (dataset) sequences.
81 private List<AlignedCodonFrame> seqmappings = new ArrayList<>();
83 private List<CommandListener> commandListeners = new ArrayList<>();
85 private List<SelectionListener> sel_listeners = new ArrayList<>();
88 * @return true if will try to use external services for processing secondary
91 public boolean isSecStructServices()
93 return secStructServices;
97 * control use of external services for processing secondary structure
99 * @param secStructServices
101 public void setSecStructServices(boolean secStructServices)
103 this.secStructServices = secStructServices;
107 * flag controlling addition of any kind of structural annotation
109 * @return true if temperature factor annotation will be added
111 public boolean isAddTempFacAnnot()
113 return addTempFacAnnot;
117 * set flag controlling addition of structural annotation
119 * @param addTempFacAnnot
121 public void setAddTempFacAnnot(boolean addTempFacAnnot)
123 this.addTempFacAnnot = addTempFacAnnot;
128 * @return if true, the structure manager will attempt to add secondary
129 * structure lines for unannotated sequences
132 public boolean isProcessSecondaryStructure()
134 return processSecondaryStructure;
138 * Control whether structure manager will try to annotate mapped sequences
139 * with secondary structure from PDB data.
143 public void setProcessSecondaryStructure(boolean enable)
145 processSecondaryStructure = enable;
149 * debug function - write all mappings to stdout
151 public void reportMapping()
153 if (mappings.isEmpty())
155 System.err.println("reportMapping: No PDB/Sequence mappings.");
160 "reportMapping: There are " + mappings.size() + " mappings.");
162 for (StructureMapping sm : mappings)
164 System.err.println("mapping " + i++ + " : " + sm.pdbfile);
170 * map between the PDB IDs (or structure identifiers) used by Jalview and the
171 * absolute filenames for PDB data that corresponds to it
173 Map<String, String> pdbIdFileName = new HashMap<>();
175 Map<String, String> pdbFileNameId = new HashMap<>();
177 public void registerPDBFile(String idForFile, String absoluteFile)
179 pdbIdFileName.put(idForFile, absoluteFile);
180 pdbFileNameId.put(absoluteFile, idForFile);
183 public String findIdForPDBFile(String idOrFile)
185 String id = pdbFileNameId.get(idOrFile);
189 public String findFileForPDBId(String idOrFile)
191 String id = pdbIdFileName.get(idOrFile);
195 public boolean isPDBFileRegistered(String idOrFile)
197 return pdbFileNameId.containsKey(idOrFile)
198 || pdbIdFileName.containsKey(idOrFile);
201 private static StructureSelectionManager nullProvider = null;
203 public static StructureSelectionManager getStructureSelectionManager(
204 StructureSelectionManagerProvider context)
208 if (nullProvider == null)
210 if (instances != null)
212 throw new Error(MessageManager.getString(
213 "error.implementation_error_structure_selection_manager_null"),
214 new NullPointerException(MessageManager
215 .getString("exception.ssm_context_is_null")));
219 nullProvider = new StructureSelectionManager();
224 if (instances == null)
226 instances = new java.util.IdentityHashMap<>();
228 StructureSelectionManager instance = instances.get(context);
229 if (instance == null)
231 if (nullProvider != null)
233 instance = nullProvider;
237 instance = new StructureSelectionManager();
239 instances.put(context, instance);
245 * flag controlling whether SeqMappings are relayed from received sequence
246 * mouse over events to other sequences
248 boolean relaySeqMappings = true;
251 * Enable or disable relay of seqMapping events to other sequences. You might
252 * want to do this if there are many sequence mappings and the host computer
257 public void setRelaySeqMappings(boolean relay)
259 relaySeqMappings = relay;
263 * get the state of the relay seqMappings flag.
265 * @return true if sequence mouse overs are being relayed to other mapped
268 public boolean isRelaySeqMappingsEnabled()
270 return relaySeqMappings;
273 Vector listeners = new Vector();
276 * register a listener for alignment sequence mouseover events
280 public void addStructureViewerListener(Object svl)
282 if (!listeners.contains(svl))
284 listeners.addElement(svl);
289 * Returns the filename the PDB id is already mapped to if known, or null if
295 public String alreadyMappedToFile(String pdbid)
297 for (StructureMapping sm : mappings)
299 if (sm.getPdbId().equalsIgnoreCase(pdbid))
308 * Import structure data and register a structure mapping for broadcasting
309 * colouring, mouseovers and selection events (convenience wrapper).
312 * - one or more sequences to be mapped to pdbFile
313 * @param targetChains
314 * - optional chain specification for mapping each sequence to pdb
315 * (may be nill, individual elements may be nill)
317 * - structure data resource
319 * - how to resolve data from resource
320 * @return null or the structure data parsed as a pdb file
322 synchronized public StructureFile setMapping(SequenceI[] sequence,
323 String[] targetChains, String pdbFile, DataSourceType protocol,
324 IProgressIndicator progress)
326 return computeMapping(true, sequence, targetChains, pdbFile, protocol,
331 * Import a single structure file and register sequence structure mappings for
332 * broadcasting colouring, mouseovers and selection events (convenience
335 * @param forStructureView
336 * when true, record the mapping for use in mouseOvers
338 * - one or more sequences to be mapped to pdbFile
339 * @param targetChains
340 * - optional chain specification for mapping each sequence to pdb
341 * (may be nill, individual elements may be nill)
343 * - structure data resource
345 * - how to resolve data from resource
346 * @return null or the structure data parsed as a pdb file
348 synchronized public StructureFile setMapping(boolean forStructureView,
349 SequenceI[] sequenceArray, String[] targetChainIds,
350 String pdbFile, DataSourceType sourceType)
352 return computeMapping(forStructureView, sequenceArray, targetChainIds,
353 pdbFile, sourceType, null);
357 * create sequence structure mappings between each sequence and the given
358 * pdbFile (retrieved via the given protocol). Either constructs a mapping
359 * using NW alignment or derives one from any available SIFTS mapping data.
361 * @param forStructureView
362 * when true, record the mapping for use in mouseOvers
364 * @param sequenceArray
365 * - one or more sequences to be mapped to pdbFile
366 * @param targetChainIds
367 * - optional chain specification for mapping each sequence to pdb
368 * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
369 * - this should be List<List<String>>, empty lists indicate no
370 * predefined mappings
372 * - structure data resource
374 * - how to resolve data from resource
375 * @param IProgressIndicator
376 * reference to UI component that maintains a progress bar for the
378 * @return null or the structure data parsed as a pdb file
380 synchronized public StructureFile computeMapping(
381 boolean forStructureView, SequenceI[] sequenceArray,
382 String[] targetChainIds, String pdbFile, DataSourceType sourceType,
383 IProgressIndicator progress)
385 long progressSessionId = System.currentTimeMillis() * 3;
388 * do we extract and transfer annotation from 3D data ?
390 // FIXME: possibly should just delete
392 boolean parseSecStr = processSecondaryStructure
393 ? isStructureFileProcessed(pdbFile, sequenceArray)
396 StructureFile pdb = null;
397 boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
400 // FIXME if sourceType is not null, we've lost data here
401 sourceType = AppletFormatAdapter.checkProtocol(pdbFile);
402 pdb = new JmolParser(false, pdbFile, sourceType);
403 pdb.addSettings(parseSecStr && processSecondaryStructure,
404 parseSecStr && addTempFacAnnot,
405 parseSecStr && secStructServices);
407 if (pdb.getId() != null && pdb.getId().trim().length() > 0
408 && DataSourceType.FILE == sourceType)
410 registerPDBFile(pdb.getId().trim(), pdbFile);
412 // if PDBId is unavailable then skip SIFTS mapping execution path
413 isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable();
415 } catch (Exception ex)
417 ex.printStackTrace();
421 * sifts client - non null if SIFTS mappings are to be used
423 SiftsClient siftsClient = null;
428 siftsClient = new SiftsClient(pdb);
430 } catch (SiftsException e)
432 isMapUsingSIFTs = false;
433 Cache.log.error("SIFTS mapping failed", e);
434 Cache.log.error("Falling back on Needleman & Wunsch alignment");
438 String targetChainId;
439 for (int s = 0; s < sequenceArray.length; s++)
441 boolean infChain = true;
442 final SequenceI seq = sequenceArray[s];
444 while (ds.getDatasetSequence() != null)
446 ds = ds.getDatasetSequence();
449 if (targetChainIds != null && targetChainIds[s] != null)
452 targetChainId = targetChainIds[s];
454 else if (seq.getName().indexOf("|") > -1)
456 targetChainId = seq.getName()
457 .substring(seq.getName().lastIndexOf("|") + 1);
458 if (targetChainId.length() > 1)
460 if (targetChainId.trim().length() == 0)
466 // not a valid chain identifier
477 * Attempt pairwise alignment of the sequence with each chain in the PDB,
478 * and remember the highest scoring chain
481 AlignSeq maxAlignseq = null;
482 String maxChainId = " ";
483 PDBChain maxChain = null;
484 boolean first = true;
485 for (PDBChain chain : pdb.getChains())
487 if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
490 continue; // don't try to map chains don't match.
492 // TODO: correctly determine sequence type for mixed na/peptide
494 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
495 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
498 // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
499 // as.calcScoreMatrix();
500 // as.traceAlignment();
502 if (first || as.maxscore > max
503 || (as.maxscore == max && chain.id.equals(targetChainId)))
509 maxChainId = chain.id;
512 if (maxChain == null)
517 if (sourceType == DataSourceType.PASTE)
519 pdbFile = "INLINE" + pdb.getId();
522 List<StructureMapping> seqToStrucMapping = new ArrayList<>();
523 if (isMapUsingSIFTs && seq.isProtein())
525 if (progress!=null) {
526 progress.setProgressBar(MessageManager
527 .getString("status.obtaining_mapping_with_sifts"),
530 jalview.datamodel.Mapping sqmpping = maxAlignseq
531 .getMappingFromS1(false);
532 if (targetChainId != null && !targetChainId.trim().isEmpty())
534 StructureMapping siftsMapping;
537 siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
538 pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
539 seqToStrucMapping.add(siftsMapping);
540 maxChain.makeExactMapping(siftsMapping, seq);
541 maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS");// FIXME: is this
543 maxChain.transferResidueAnnotation(siftsMapping, null);
544 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
546 } catch (SiftsException e)
548 // fall back to NW alignment
549 System.err.println(e.getMessage());
550 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
551 targetChainId, maxChain, pdb, maxAlignseq);
552 seqToStrucMapping.add(nwMapping);
553 maxChain.makeExactMapping(maxAlignseq, seq);
554 maxChain.transferRESNUMFeatures(seq, "IEA:Jalview"); // FIXME: is
557 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
558 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
563 List<StructureMapping> foundSiftsMappings = new ArrayList<>();
564 for (PDBChain chain : pdb.getChains())
566 StructureMapping siftsMapping = null;
569 siftsMapping = getStructureMapping(seq,
570 pdbFile, chain.id, pdb, chain, sqmpping, maxAlignseq,
572 foundSiftsMappings.add(siftsMapping);
573 chain.makeExactMapping(siftsMapping, seq);
574 chain.transferRESNUMFeatures(seq, "IEA: SIFTS");// FIXME: is this
576 chain.transferResidueAnnotation(siftsMapping, null);
577 } catch (SiftsException e)
579 System.err.println(e.getMessage());
585 "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
586 System.err.println(e.getMessage());
589 if (!foundSiftsMappings.isEmpty())
591 seqToStrucMapping.addAll(foundSiftsMappings);
592 ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
596 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
597 maxChainId, maxChain, pdb, maxAlignseq);
598 seqToStrucMapping.add(nwMapping);
599 maxChain.transferRESNUMFeatures(seq, null); // FIXME: is this
601 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
602 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
608 if (progress != null)
610 progress.setProgressBar(MessageManager
611 .getString("status.obtaining_mapping_with_nw_alignment"),
614 StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
615 maxChain, pdb, maxAlignseq);
616 seqToStrucMapping.add(nwMapping);
617 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
619 if (forStructureView)
621 for (StructureMapping sm : seqToStrucMapping)
623 addStructureMapping(sm); // not addAll!
626 if (progress != null)
628 progress.setProgressBar(null, progressSessionId);
635 * check if we need to extract secondary structure from given pdbFile and
636 * transfer to sequences
639 * @param sequenceArray
642 private boolean isStructureFileProcessed(String pdbFile,
643 SequenceI[] sequenceArray)
645 boolean parseSecStr = true;
646 if (isPDBFileRegistered(pdbFile))
648 for (SequenceI sq : sequenceArray)
651 while (ds.getDatasetSequence() != null)
653 ds = ds.getDatasetSequence();
656 if (ds.getAnnotation() != null)
658 for (AlignmentAnnotation ala : ds.getAnnotation())
660 // false if any annotation present from this structure
661 // JBPNote this fails for jmol/chimera view because the *file* is
662 // passed, not the structure data ID -
663 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
674 public void addStructureMapping(StructureMapping sm)
676 if (!mappings.contains(sm))
683 * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
688 * @param targetChainId
694 * client for retrieval of SIFTS mappings for this structure
696 * @throws SiftsException
698 private StructureMapping getStructureMapping(SequenceI seq,
699 String pdbFile, String targetChainId, StructureFile pdb,
700 PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
701 AlignSeq maxAlignseq, SiftsClient siftsClient) throws SiftsException
703 StructureMapping curChainMapping = siftsClient
704 .getSiftsStructureMapping(seq, pdbFile, targetChainId);
707 PDBChain chain = pdb.findChain(targetChainId);
710 chain.transferResidueAnnotation(curChainMapping, null);
712 } catch (Exception e)
716 return curChainMapping;
719 private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
720 String maxChainId, PDBChain maxChain, StructureFile pdb,
721 AlignSeq maxAlignseq)
723 final StringBuilder mappingDetails = new StringBuilder(128);
724 mappingDetails.append(NEWLINE)
725 .append("Sequence \u27f7 Structure mapping details");
726 mappingDetails.append(NEWLINE);
728 .append("Method: inferred with Needleman & Wunsch alignment");
729 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
730 .append(NEWLINE).append("Sequence = ")
731 .append(maxChain.sequence.getSequenceAsString());
732 mappingDetails.append(NEWLINE).append("No of residues = ")
733 .append(maxChain.residues.size()).append(NEWLINE)
735 PrintStream ps = new PrintStream(System.out)
738 public void print(String x)
740 mappingDetails.append(x);
744 public void println()
746 mappingDetails.append(NEWLINE);
750 maxAlignseq.printAlignment(ps);
752 mappingDetails.append(NEWLINE).append("PDB start/end ");
753 mappingDetails.append(String.valueOf(maxAlignseq.seq2start))
755 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
756 mappingDetails.append(NEWLINE).append("SEQ start/end ");
759 .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1)))
761 mappingDetails.append(
762 String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1)));
763 mappingDetails.append(NEWLINE);
764 maxChain.makeExactMapping(maxAlignseq, seq);
765 jalview.datamodel.Mapping sqmpping = maxAlignseq
766 .getMappingFromS1(false);
767 maxChain.transferRESNUMFeatures(seq, null);
769 HashMap<Integer, int[]> mapping = new HashMap<>();
776 Atom tmp = maxChain.atoms.elementAt(index);
777 if ((resNum != tmp.resNumber || insCode != tmp.insCode)
778 && tmp.alignmentMapping != -1)
780 resNum = tmp.resNumber;
781 insCode = tmp.insCode;
782 if (tmp.alignmentMapping >= -1)
784 mapping.put(tmp.alignmentMapping + 1,
786 { tmp.resNumber, tmp.atomIndex });
791 } while (index < maxChain.atoms.size());
793 StructureMapping nwMapping = new StructureMapping(seq, pdbFile,
794 pdb.getId(), maxChainId, mapping, mappingDetails.toString());
795 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
799 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
801 listeners.removeElement(svl);
802 if (svl instanceof SequenceListener)
804 for (int i = 0; i < listeners.size(); i++)
806 if (listeners.elementAt(i) instanceof StructureListener)
808 ((StructureListener) listeners.elementAt(i))
809 .releaseReferences(svl);
814 if (pdbfiles == null)
820 * Remove mappings to the closed listener's PDB files, but first check if
821 * another listener is still interested
823 List<String> pdbs = new ArrayList<>(Arrays.asList(pdbfiles));
825 StructureListener sl;
826 for (int i = 0; i < listeners.size(); i++)
828 if (listeners.elementAt(i) instanceof StructureListener)
830 sl = (StructureListener) listeners.elementAt(i);
831 for (String pdbfile : sl.getStructureFiles())
833 pdbs.remove(pdbfile);
839 * Rebuild the mappings set, retaining only those which are for 'other' PDB
844 List<StructureMapping> tmp = new ArrayList<>();
845 for (StructureMapping sm : mappings)
847 if (!pdbs.contains(sm.pdbfile))
858 * Propagate mouseover of a single position in a structure
865 public String mouseOverStructure(int pdbResNum, String chain,
868 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
869 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
870 return mouseOverStructure(atoms);
874 * Propagate mouseover or selection of multiple positions in a structure
878 public String mouseOverStructure(List<AtomSpec> atoms)
880 if (listeners == null)
882 // old or prematurely sent event
885 boolean hasSequenceListener = false;
886 for (int i = 0; i < listeners.size(); i++)
888 if (listeners.elementAt(i) instanceof SequenceListener)
890 hasSequenceListener = true;
893 if (!hasSequenceListener)
898 SearchResultsI results = findAlignmentPositionsForStructurePositions(
900 String result = null;
901 for (Object li : listeners)
903 if (li instanceof SequenceListener)
905 String s = ((SequenceListener) li).highlightSequence(results);
916 * Constructs a SearchResults object holding regions (if any) in the Jalview
917 * alignment which have a mapping to the structure viewer positions in the
923 public SearchResultsI findAlignmentPositionsForStructurePositions(
924 List<AtomSpec> atoms)
926 SearchResultsI results = new SearchResults();
927 for (AtomSpec atom : atoms)
929 SequenceI lastseq = null;
931 for (StructureMapping sm : mappings)
933 if (sm.pdbfile.equals(atom.getPdbFile())
934 && sm.pdbchain.equals(atom.getChain()))
936 int indexpos = sm.getSeqPos(atom.getPdbResNum());
937 if (lastipos != indexpos || lastseq != sm.sequence)
939 results.addResult(sm.sequence, indexpos, indexpos);
941 lastseq = sm.sequence;
942 // construct highlighted sequence list
943 for (AlignedCodonFrame acf : seqmappings)
945 acf.markMappedRegion(sm.sequence, indexpos, results);
955 * highlight regions associated with a position (indexpos) in seq
958 * the sequence that the mouse over occurred on
960 * the absolute position being mouseovered in seq (0 to seq.length())
962 * the sequence position (if -1, seq.findPosition is called to
963 * resolve the residue number)
965 public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
968 boolean hasSequenceListeners = handlingVamsasMo
969 || !seqmappings.isEmpty();
970 SearchResultsI results = null;
973 seqPos = seq.findPosition(indexpos);
975 for (int i = 0; i < listeners.size(); i++)
977 Object listener = listeners.elementAt(i);
978 if (listener == source)
980 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
981 // Temporary fudge with SequenceListener.getVamsasSource()
984 if (listener instanceof StructureListener)
986 highlightStructure((StructureListener) listener, seq, seqPos);
990 if (listener instanceof SequenceListener)
992 final SequenceListener seqListener = (SequenceListener) listener;
993 if (hasSequenceListeners
994 && seqListener.getVamsasSource() != source)
996 if (relaySeqMappings)
1000 results = MappingUtils.buildSearchResults(seq, seqPos,
1003 if (handlingVamsasMo)
1005 results.addResult(seq, seqPos, seqPos);
1008 if (!results.isEmpty())
1010 seqListener.highlightSequence(results);
1015 else if (listener instanceof VamsasListener && !handlingVamsasMo)
1017 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
1020 else if (listener instanceof SecondaryStructureListener)
1022 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
1030 * Send suitable messages to a StructureListener to highlight atoms
1031 * corresponding to the given sequence position(s)
1037 public void highlightStructure(StructureListener sl, SequenceI seq,
1040 if (!sl.isListeningFor(seq))
1045 List<AtomSpec> atoms = new ArrayList<>();
1046 for (StructureMapping sm : mappings)
1048 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1049 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1050 .getDatasetSequence() == seq.getDatasetSequence()))
1052 for (int index : positions)
1054 atomNo = sm.getAtomNum(index);
1058 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1059 sm.getPDBResNum(index), atomNo));
1064 sl.highlightAtoms(atoms);
1068 * true if a mouse over event from an external (ie Vamsas) source is being
1071 boolean handlingVamsasMo = false;
1076 * as mouseOverSequence but only route event to SequenceListeners
1080 * in an alignment sequence
1082 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
1083 VamsasSource source)
1085 handlingVamsasMo = true;
1086 long msg = sequenceI.hashCode() * (1 + position);
1090 mouseOverSequence(sequenceI, position, -1, source);
1092 handlingVamsasMo = false;
1095 public Annotation[] colourSequenceFromStructure(SequenceI seq,
1099 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
1100 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
1102 * Annotation [] annotations = new Annotation[seq.getLength()];
1104 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
1105 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
1106 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
1108 * for (int j = 0; j < mappings.length; j++) {
1110 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
1111 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
1112 * System.out.println(pdbid+" "+mappings[j].getPdbId() +"
1113 * "+mappings[j].pdbfile);
1115 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
1116 * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
1118 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
1119 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
1120 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
1121 * mappings[j].pdbfile); }
1123 * annotations[index] = new Annotation("X",null,' ',0,col); } return
1124 * annotations; } } } }
1126 * return annotations;
1130 public void structureSelectionChanged()
1134 public void sequenceSelectionChanged()
1138 public void sequenceColoursChanged(Object source)
1140 StructureListener sl;
1141 for (int i = 0; i < listeners.size(); i++)
1143 if (listeners.elementAt(i) instanceof StructureListener)
1145 sl = (StructureListener) listeners.elementAt(i);
1146 sl.updateColours(source);
1151 public StructureMapping[] getMapping(String pdbfile)
1153 List<StructureMapping> tmp = new ArrayList<>();
1154 for (StructureMapping sm : mappings)
1156 if (sm.pdbfile.equals(pdbfile))
1161 return tmp.toArray(new StructureMapping[tmp.size()]);
1165 * Returns a readable description of all mappings for the given pdbfile to any
1166 * of the given sequences
1172 public String printMappings(String pdbfile, List<SequenceI> seqs)
1174 if (pdbfile == null || seqs == null || seqs.isEmpty())
1179 StringBuilder sb = new StringBuilder(64);
1180 for (StructureMapping sm : mappings)
1182 if (Platform.pathEquals(sm.pdbfile, pdbfile)
1183 && seqs.contains(sm.sequence))
1185 sb.append(sm.mappingDetails);
1187 // separator makes it easier to read multiple mappings
1188 sb.append("=====================");
1194 return sb.toString();
1198 * Remove the given mapping
1202 public void deregisterMapping(AlignedCodonFrame acf)
1206 boolean removed = seqmappings.remove(acf);
1207 if (removed && seqmappings.isEmpty())
1209 System.out.println("All mappings removed");
1215 * Add each of the given codonFrames to the stored set, if not aready present.
1219 public void registerMappings(List<AlignedCodonFrame> mappings)
1221 if (mappings != null)
1223 for (AlignedCodonFrame acf : mappings)
1225 registerMapping(acf);
1231 * Add the given mapping to the stored set, unless already stored.
1233 public void registerMapping(AlignedCodonFrame acf)
1237 if (!seqmappings.contains(acf))
1239 seqmappings.add(acf);
1245 * Resets this object to its initial state by removing all registered
1246 * listeners, codon mappings, PDB file mappings
1248 public void resetAll()
1250 if (mappings != null)
1254 if (seqmappings != null)
1256 seqmappings.clear();
1258 if (sel_listeners != null)
1260 sel_listeners.clear();
1262 if (listeners != null)
1266 if (commandListeners != null)
1268 commandListeners.clear();
1270 if (view_listeners != null)
1272 view_listeners.clear();
1274 if (pdbFileNameId != null)
1276 pdbFileNameId.clear();
1278 if (pdbIdFileName != null)
1280 pdbIdFileName.clear();
1284 public void addSelectionListener(SelectionListener selecter)
1286 if (!sel_listeners.contains(selecter))
1288 sel_listeners.add(selecter);
1292 public void removeSelectionListener(SelectionListener toremove)
1294 if (sel_listeners.contains(toremove))
1296 sel_listeners.remove(toremove);
1300 public synchronized void sendSelection(
1301 jalview.datamodel.SequenceGroup selection,
1302 jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden,
1303 SelectionSource source)
1305 for (SelectionListener slis : sel_listeners)
1309 slis.selection(selection, colsel, hidden, source);
1314 Vector<AlignmentViewPanelListener> view_listeners = new Vector<>();
1316 public synchronized void sendViewPosition(
1317 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1318 int startSeq, int endSeq)
1321 if (view_listeners != null && view_listeners.size() > 0)
1323 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1325 while (listeners.hasMoreElements())
1327 AlignmentViewPanelListener slis = listeners.nextElement();
1330 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1338 * release all references associated with this manager provider
1340 * @param jalviewLite
1342 public static void release(StructureSelectionManagerProvider jalviewLite)
1344 // synchronized (instances)
1346 if (instances == null)
1350 StructureSelectionManager mnger = (instances.get(jalviewLite));
1353 instances.remove(jalviewLite);
1356 /* bsoares 2019-03-20 finalize deprecated, no apparent external
1357 * resources to close
1359 // mnger.finalize();
1360 } catch (Throwable x)
1367 public void registerPDBEntry(PDBEntry pdbentry)
1369 if (pdbentry.getFile() != null
1370 && pdbentry.getFile().trim().length() > 0)
1372 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1376 public void addCommandListener(CommandListener cl)
1378 if (!commandListeners.contains(cl))
1380 commandListeners.add(cl);
1384 public boolean hasCommandListener(CommandListener cl)
1386 return this.commandListeners.contains(cl);
1389 public boolean removeCommandListener(CommandListener l)
1391 return commandListeners.remove(l);
1395 * Forward a command to any command listeners (except for the command's
1399 * the command to be broadcast (in its form after being performed)
1401 * if true, the command was being 'undone'
1404 public void commandPerformed(CommandI command, boolean undo,
1405 VamsasSource source)
1407 for (CommandListener listener : commandListeners)
1409 listener.mirrorCommand(command, undo, this, source);
1414 * Returns a new CommandI representing the given command as mapped to the
1415 * given sequences. If no mapping could be made, or the command is not of a
1416 * mappable kind, returns null.
1424 public CommandI mapCommand(CommandI command, boolean undo,
1425 final AlignmentI mapTo, char gapChar)
1427 if (command instanceof EditCommand)
1429 return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo,
1430 gapChar, seqmappings);
1432 else if (command instanceof OrderCommand)
1434 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1435 mapTo, seqmappings);
1440 public List<AlignedCodonFrame> getSequenceMappings()