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.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(boolean forStructureView,
382 SequenceI[] sequenceArray, String[] targetChainIds,
383 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)
426 isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable()
427 && !pdb.getId().startsWith("AF-") && isProtein;
429 } catch (Exception ex)
431 ex.printStackTrace();
435 * sifts client - non null if SIFTS mappings are to be used
437 SiftsClient siftsClient = null;
442 siftsClient = new SiftsClient(pdb);
444 } catch (SiftsException e)
446 isMapUsingSIFTs = false;
447 Console.error("SIFTS mapping failed", e);
448 Console.error("Falling back on Needleman & Wunsch alignment");
452 String targetChainId;
453 for (int s = 0; s < sequenceArray.length; s++)
455 boolean infChain = true;
456 final SequenceI seq = sequenceArray[s];
458 while (ds.getDatasetSequence() != null)
460 ds = ds.getDatasetSequence();
463 if (targetChainIds != null && targetChainIds[s] != null)
466 targetChainId = targetChainIds[s];
468 else if (seq.getName().indexOf("|") > -1)
470 targetChainId = seq.getName()
471 .substring(seq.getName().lastIndexOf("|") + 1);
472 if (targetChainId.length() > 1)
474 if (targetChainId.trim().length() == 0)
480 // not a valid chain identifier
491 * Attempt pairwise alignment of the sequence with each chain in the PDB,
492 * and remember the highest scoring chain
495 AlignSeq maxAlignseq = null;
496 String maxChainId = " ";
497 PDBChain maxChain = null;
498 boolean first = true;
499 for (PDBChain chain : pdb.getChains())
501 if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
504 continue; // don't try to map chains don't match.
506 // TODO: correctly determine sequence type for mixed na/peptide
508 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
509 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
512 // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
513 // as.calcScoreMatrix();
514 // as.traceAlignment();
516 if (first || as.maxscore > max
517 || (as.maxscore == max && chain.id.equals(targetChainId)))
523 maxChainId = chain.id;
526 if (maxChain == null)
531 if (sourceType == DataSourceType.PASTE)
533 pdbFile = "INLINE" + pdb.getId();
536 List<StructureMapping> seqToStrucMapping = new ArrayList<>();
537 if (isMapUsingSIFTs && seq.isProtein())
539 if (progress != null)
541 progress.setProgressBar(
543 .getString("status.obtaining_mapping_with_sifts"),
546 jalview.datamodel.Mapping sqmpping = maxAlignseq
547 .getMappingFromS1(false);
548 if (targetChainId != null && !targetChainId.trim().isEmpty())
550 StructureMapping siftsMapping;
553 siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
554 pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
555 seqToStrucMapping.add(siftsMapping);
556 maxChain.makeExactMapping(siftsMapping, seq);
557 maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS",
558 pdb.getId().toLowerCase(Locale.ROOT));
559 maxChain.transferResidueAnnotation(siftsMapping, null);
560 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
562 } catch (SiftsException e)
564 // fall back to NW alignment
565 Console.error(e.getMessage());
566 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
567 targetChainId, maxChain, pdb, maxAlignseq);
568 seqToStrucMapping.add(nwMapping);
569 maxChain.makeExactMapping(maxAlignseq, seq);
570 maxChain.transferRESNUMFeatures(seq, "IEA:Jalview",
571 pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is
574 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
575 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
580 List<StructureMapping> foundSiftsMappings = new ArrayList<>();
581 for (PDBChain chain : pdb.getChains())
583 StructureMapping siftsMapping = null;
586 siftsMapping = getStructureMapping(seq, pdbFile, chain.id,
587 pdb, chain, sqmpping, maxAlignseq, siftsClient);
588 foundSiftsMappings.add(siftsMapping);
589 chain.makeExactMapping(siftsMapping, seq);
590 chain.transferRESNUMFeatures(seq, "IEA: SIFTS",
591 pdb.getId().toLowerCase(Locale.ROOT));// FIXME: is this
593 chain.transferResidueAnnotation(siftsMapping, null);
594 } catch (SiftsException e)
596 System.err.println(e.getMessage());
597 } catch (Exception e)
600 "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
601 System.err.println(e.getMessage());
604 if (!foundSiftsMappings.isEmpty())
606 seqToStrucMapping.addAll(foundSiftsMappings);
607 ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
611 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
612 maxChainId, maxChain, pdb, maxAlignseq);
613 seqToStrucMapping.add(nwMapping);
614 maxChain.transferRESNUMFeatures(seq, null,
615 pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is this
617 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
618 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
624 if (progress != null)
626 progress.setProgressBar(
627 MessageManager.getString(
628 "status.obtaining_mapping_with_nw_alignment"),
631 StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
632 maxChain, pdb, maxAlignseq);
633 seqToStrucMapping.add(nwMapping);
634 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
636 if (forStructureView)
638 for (StructureMapping sm : seqToStrucMapping)
640 addStructureMapping(sm); // not addAll!
643 if (progress != null)
645 progress.setProgressBar(null, progressSessionId);
652 * check if we need to extract secondary structure from given pdbFile and
653 * transfer to sequences
656 * @param sequenceArray
659 private boolean isStructureFileProcessed(String pdbFile,
660 SequenceI[] sequenceArray)
662 boolean parseSecStr = true;
663 if (isPDBFileRegistered(pdbFile))
665 for (SequenceI sq : sequenceArray)
668 while (ds.getDatasetSequence() != null)
670 ds = ds.getDatasetSequence();
673 if (ds.getAnnotation() != null)
675 for (AlignmentAnnotation ala : ds.getAnnotation())
677 // false if any annotation present from this structure
678 // JBPNote this fails for jmol/chimera view because the *file* is
679 // passed, not the structure data ID -
680 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
691 public void addStructureMapping(StructureMapping sm)
693 if (!mappings.contains(sm))
700 * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
705 * @param targetChainId
711 * client for retrieval of SIFTS mappings for this structure
713 * @throws SiftsException
715 private StructureMapping getStructureMapping(SequenceI seq,
716 String pdbFile, String targetChainId, StructureFile pdb,
717 PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
718 AlignSeq maxAlignseq, SiftsClient siftsClient)
719 throws SiftsException
721 StructureMapping curChainMapping = siftsClient
722 .getSiftsStructureMapping(seq, pdbFile, targetChainId);
725 PDBChain chain = pdb.findChain(targetChainId);
728 chain.transferResidueAnnotation(curChainMapping, null);
730 } catch (Exception e)
734 return curChainMapping;
737 private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
738 String maxChainId, PDBChain maxChain, StructureFile pdb,
739 AlignSeq maxAlignseq)
741 final StringBuilder mappingDetails = new StringBuilder(128);
742 mappingDetails.append(NEWLINE)
743 .append("Sequence \u27f7 Structure mapping details");
744 mappingDetails.append(NEWLINE);
746 .append("Method: inferred with Needleman & Wunsch alignment");
747 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
748 .append(NEWLINE).append("Sequence = ")
749 .append(maxChain.sequence.getSequenceAsString());
750 mappingDetails.append(NEWLINE).append("No of residues = ")
751 .append(maxChain.residues.size()).append(NEWLINE)
753 PrintStream ps = new PrintStream(System.out)
756 public void print(String x)
758 mappingDetails.append(x);
762 public void println()
764 mappingDetails.append(NEWLINE);
768 maxAlignseq.printAlignment(ps);
770 mappingDetails.append(NEWLINE).append("PDB start/end ");
771 mappingDetails.append(String.valueOf(maxAlignseq.seq2start))
773 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
774 mappingDetails.append(NEWLINE).append("SEQ start/end ");
777 .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1)))
779 mappingDetails.append(
780 String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1)));
781 mappingDetails.append(NEWLINE);
782 maxChain.makeExactMapping(maxAlignseq, seq);
783 jalview.datamodel.Mapping sqmpping = maxAlignseq
784 .getMappingFromS1(false);
785 maxChain.transferRESNUMFeatures(seq, null,
786 pdb.getId().toLowerCase(Locale.ROOT));
788 HashMap<Integer, int[]> mapping = new HashMap<>();
795 Atom tmp = maxChain.atoms.elementAt(index);
796 if ((resNum != tmp.resNumber || insCode != tmp.insCode)
797 && tmp.alignmentMapping != -1)
799 resNum = tmp.resNumber;
800 insCode = tmp.insCode;
801 if (tmp.alignmentMapping >= -1)
803 mapping.put(tmp.alignmentMapping + 1,
805 { tmp.resNumber, tmp.atomIndex });
810 } while (index < maxChain.atoms.size());
812 StructureMapping nwMapping = new StructureMapping(seq, pdbFile,
813 pdb.getId(), maxChainId, mapping, mappingDetails.toString());
814 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
818 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
820 listeners.removeElement(svl);
821 if (svl instanceof SequenceListener)
823 for (int i = 0; i < listeners.size(); i++)
825 if (listeners.elementAt(i) instanceof StructureListener)
827 ((StructureListener) listeners.elementAt(i))
828 .releaseReferences(svl);
833 if (pdbfiles == null)
839 * Remove mappings to the closed listener's PDB files, but first check if
840 * another listener is still interested
842 List<String> pdbs = new ArrayList<>(Arrays.asList(pdbfiles));
844 StructureListener sl;
845 for (int i = 0; i < listeners.size(); i++)
847 if (listeners.elementAt(i) instanceof StructureListener)
849 sl = (StructureListener) listeners.elementAt(i);
850 for (String pdbfile : sl.getStructureFiles())
852 pdbs.remove(pdbfile);
858 * Rebuild the mappings set, retaining only those which are for 'other' PDB
863 List<StructureMapping> tmp = new ArrayList<>();
864 for (StructureMapping sm : mappings)
866 if (!pdbs.contains(sm.pdbfile))
877 * hack to highlight a range of positions at once on any structure views
881 * - series of int start-end ranges as positions on sequenceRef
885 public void highlightPositionsOn(SequenceI sequenceRef, int[][] is,
888 boolean hasSequenceListeners = handlingVamsasMo
889 || !seqmappings.isEmpty();
890 SearchResultsI results = null;
891 ArrayList<Integer> listOfPositions = new ArrayList<Integer>();
894 for (int p = s_e[0]; p <= s_e[1]; listOfPositions.add(p++))
897 int seqpos[] = new int[listOfPositions.size()];
899 for (Integer p : listOfPositions)
904 for (i = 0; i < listeners.size(); i++)
906 Object listener = listeners.elementAt(i);
907 if (listener == source)
909 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
910 // Temporary fudge with SequenceListener.getVamsasSource()
913 if (listener instanceof StructureListener)
915 highlightStructure((StructureListener) listener, sequenceRef,
923 * Propagate mouseover of a single position in a structure
930 public String mouseOverStructure(int pdbResNum, String chain,
933 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
934 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
935 return mouseOverStructure(atoms);
939 * Propagate mouseover or selection of multiple positions in a structure
943 public String mouseOverStructure(List<AtomSpec> atoms)
945 if (listeners == null)
947 // old or prematurely sent event
950 boolean hasSequenceListener = false;
951 for (int i = 0; i < listeners.size(); i++)
953 if (listeners.elementAt(i) instanceof SequenceListener)
955 hasSequenceListener = true;
958 if (!hasSequenceListener)
963 SearchResultsI results = findAlignmentPositionsForStructurePositions(
965 String result = null;
966 for (Object li : listeners)
968 if (li instanceof SequenceListener)
970 String s = ((SequenceListener) li).highlightSequence(results);
981 * Constructs a SearchResults object holding regions (if any) in the Jalview
982 * alignment which have a mapping to the structure viewer positions in the
988 public SearchResultsI findAlignmentPositionsForStructurePositions(
989 List<AtomSpec> atoms)
991 SearchResultsI results = new SearchResults();
992 for (AtomSpec atom : atoms)
994 SequenceI lastseq = null;
996 for (StructureMapping sm : mappings)
998 if (sm.pdbfile.equals(atom.getPdbFile())
999 && sm.pdbchain.equals(atom.getChain()))
1001 int indexpos = sm.getSeqPos(atom.getPdbResNum());
1002 if (lastipos != indexpos || lastseq != sm.sequence)
1004 results.addResult(sm.sequence, indexpos, indexpos);
1005 lastipos = indexpos;
1006 lastseq = sm.sequence;
1007 // construct highlighted sequence list
1008 for (AlignedCodonFrame acf : seqmappings)
1010 acf.markMappedRegion(sm.sequence, indexpos, results);
1020 * highlight regions associated with a position (indexpos) in seq
1023 * the sequence that the mouse over occurred on
1025 * the absolute position being mouseovered in seq (0 to seq.length())
1027 * the sequence position (if -1, seq.findPosition is called to
1028 * resolve the residue number)
1030 public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
1031 VamsasSource source)
1033 boolean hasSequenceListeners = handlingVamsasMo
1034 || !seqmappings.isEmpty();
1035 SearchResultsI results = null;
1038 seqPos = seq.findPosition(indexpos);
1040 for (int i = 0; i < listeners.size(); i++)
1042 Object listener = listeners.elementAt(i);
1043 if (listener == source)
1045 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1046 // Temporary fudge with SequenceListener.getVamsasSource()
1049 if (listener instanceof StructureListener)
1051 highlightStructure((StructureListener) listener, seq, seqPos);
1055 if (listener instanceof SequenceListener)
1057 final SequenceListener seqListener = (SequenceListener) listener;
1058 if (hasSequenceListeners
1059 && seqListener.getVamsasSource() != source)
1061 if (relaySeqMappings)
1063 if (results == null)
1065 results = MappingUtils.buildSearchResults(seq, seqPos,
1068 if (handlingVamsasMo)
1070 results.addResult(seq, seqPos, seqPos);
1073 if (!results.isEmpty())
1075 seqListener.highlightSequence(results);
1080 else if (listener instanceof VamsasListener && !handlingVamsasMo)
1082 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
1085 else if (listener instanceof SecondaryStructureListener)
1087 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
1095 * Send suitable messages to a StructureListener to highlight atoms
1096 * corresponding to the given sequence position(s)
1102 public void highlightStructure(StructureListener sl, SequenceI seq,
1105 if (!sl.isListeningFor(seq))
1110 List<AtomSpec> atoms = new ArrayList<>();
1111 for (StructureMapping sm : mappings)
1113 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1114 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1115 .getDatasetSequence() == seq.getDatasetSequence()))
1117 for (int index : positions)
1119 atomNo = sm.getAtomNum(index);
1123 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1124 sm.getPDBResNum(index), atomNo));
1129 sl.highlightAtoms(atoms);
1133 * true if a mouse over event from an external (ie Vamsas) source is being
1136 boolean handlingVamsasMo = false;
1141 * as mouseOverSequence but only route event to SequenceListeners
1145 * in an alignment sequence
1147 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
1148 VamsasSource source)
1150 handlingVamsasMo = true;
1151 long msg = sequenceI.hashCode() * (1 + position);
1155 mouseOverSequence(sequenceI, position, -1, source);
1157 handlingVamsasMo = false;
1160 public Annotation[] colourSequenceFromStructure(SequenceI seq,
1164 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
1165 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
1167 * Annotation [] annotations = new Annotation[seq.getLength()];
1169 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
1170 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
1171 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
1173 * for (int j = 0; j < mappings.length; j++) {
1175 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
1176 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
1177 * System.out.println(pdbid+" "+mappings[j].getPdbId() +"
1178 * "+mappings[j].pdbfile);
1180 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
1181 * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
1183 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
1184 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
1185 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
1186 * mappings[j].pdbfile); }
1188 * annotations[index] = new Annotation("X",null,' ',0,col); } return
1189 * annotations; } } } }
1191 * return annotations;
1195 public void structureSelectionChanged()
1199 public void sequenceSelectionChanged()
1203 public void sequenceColoursChanged(Object source)
1205 StructureListener sl;
1206 for (int i = 0; i < listeners.size(); i++)
1208 if (listeners.elementAt(i) instanceof StructureListener)
1210 sl = (StructureListener) listeners.elementAt(i);
1211 sl.updateColours(source);
1216 public StructureMapping[] getMapping(String pdbfile)
1218 List<StructureMapping> tmp = new ArrayList<>();
1219 for (StructureMapping sm : mappings)
1221 if (sm.pdbfile.equals(pdbfile))
1226 return tmp.toArray(new StructureMapping[tmp.size()]);
1230 * Returns a readable description of all mappings for the given pdbfile to any
1231 * of the given sequences
1237 public String printMappings(String pdbfile, List<SequenceI> seqs)
1239 if (pdbfile == null || seqs == null || seqs.isEmpty())
1244 StringBuilder sb = new StringBuilder(64);
1245 for (StructureMapping sm : mappings)
1247 if (Platform.pathEquals(sm.pdbfile, pdbfile)
1248 && seqs.contains(sm.sequence))
1250 sb.append(sm.mappingDetails);
1252 // separator makes it easier to read multiple mappings
1253 sb.append("=====================");
1259 return sb.toString();
1263 * Remove the given mapping
1267 public void deregisterMapping(AlignedCodonFrame acf)
1271 boolean removed = seqmappings.remove(acf);
1272 if (removed && seqmappings.isEmpty())
1274 System.out.println("All mappings removed");
1280 * Add each of the given codonFrames to the stored set, if not aready present.
1284 public void registerMappings(List<AlignedCodonFrame> mappings)
1286 if (mappings != null)
1288 for (AlignedCodonFrame acf : mappings)
1290 registerMapping(acf);
1296 * Add the given mapping to the stored set, unless already stored.
1298 public void registerMapping(AlignedCodonFrame acf)
1302 if (!seqmappings.contains(acf))
1304 seqmappings.add(acf);
1310 * Resets this object to its initial state by removing all registered
1311 * listeners, codon mappings, PDB file mappings
1313 public void resetAll()
1315 if (mappings != null)
1319 if (seqmappings != null)
1321 seqmappings.clear();
1323 if (sel_listeners != null)
1325 sel_listeners.clear();
1327 if (listeners != null)
1331 if (commandListeners != null)
1333 commandListeners.clear();
1335 if (view_listeners != null)
1337 view_listeners.clear();
1339 if (pdbFileNameId != null)
1341 pdbFileNameId.clear();
1343 if (pdbIdFileName != null)
1345 pdbIdFileName.clear();
1349 public void addSelectionListener(SelectionListener selecter)
1351 if (!sel_listeners.contains(selecter))
1353 sel_listeners.add(selecter);
1357 public void removeSelectionListener(SelectionListener toremove)
1359 if (sel_listeners.contains(toremove))
1361 sel_listeners.remove(toremove);
1365 public synchronized void sendSelection(
1366 jalview.datamodel.SequenceGroup selection,
1367 jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden,
1368 SelectionSource source)
1370 for (SelectionListener slis : sel_listeners)
1374 slis.selection(selection, colsel, hidden, source);
1379 Vector<AlignmentViewPanelListener> view_listeners = new Vector<>();
1381 public synchronized void sendViewPosition(
1382 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1383 int startSeq, int endSeq)
1386 if (view_listeners != null && view_listeners.size() > 0)
1388 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1390 while (listeners.hasMoreElements())
1392 AlignmentViewPanelListener slis = listeners.nextElement();
1395 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1403 * release all references associated with this manager provider
1405 * @param jalviewLite
1407 public static void release(StructureSelectionManagerProvider jalviewLite)
1409 // synchronized (instances)
1411 if (instances == null)
1415 StructureSelectionManager mnger = (instances.get(jalviewLite));
1418 instances.remove(jalviewLite);
1421 /* bsoares 2019-03-20 finalize deprecated, no apparent external
1422 * resources to close
1424 // mnger.finalize();
1425 } catch (Throwable x)
1432 public void registerPDBEntry(PDBEntry pdbentry)
1434 if (pdbentry.getFile() != null
1435 && pdbentry.getFile().trim().length() > 0)
1437 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1441 public void addCommandListener(CommandListener cl)
1443 if (!commandListeners.contains(cl))
1445 commandListeners.add(cl);
1449 public boolean hasCommandListener(CommandListener cl)
1451 return this.commandListeners.contains(cl);
1454 public boolean removeCommandListener(CommandListener l)
1456 return commandListeners.remove(l);
1460 * Forward a command to any command listeners (except for the command's
1464 * the command to be broadcast (in its form after being performed)
1466 * if true, the command was being 'undone'
1469 public void commandPerformed(CommandI command, boolean undo,
1470 VamsasSource source)
1472 for (CommandListener listener : commandListeners)
1474 listener.mirrorCommand(command, undo, this, source);
1479 * Returns a new CommandI representing the given command as mapped to the
1480 * given sequences. If no mapping could be made, or the command is not of a
1481 * mappable kind, returns null.
1489 public CommandI mapCommand(CommandI command, boolean undo,
1490 final AlignmentI mapTo, char gapChar)
1492 if (command instanceof EditCommand)
1494 return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo,
1495 gapChar, seqmappings);
1497 else if (command instanceof OrderCommand)
1499 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1500 mapTo, seqmappings);
1505 public List<AlignedCodonFrame> getSequenceMappings()