2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.structure;
23 import java.io.PrintStream;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.Enumeration;
28 import java.util.HashMap;
29 import java.util.IdentityHashMap;
30 import java.util.List;
31 import java.util.Locale;
33 import java.util.Vector;
35 import jalview.analysis.AlignSeq;
36 import jalview.api.StructureSelectionManagerProvider;
37 import jalview.bin.Console;
38 import jalview.commands.CommandI;
39 import jalview.commands.EditCommand;
40 import jalview.commands.OrderCommand;
41 import jalview.datamodel.AlignedCodonFrame;
42 import jalview.datamodel.AlignmentAnnotation;
43 import jalview.datamodel.AlignmentI;
44 import jalview.datamodel.Annotation;
45 import jalview.datamodel.ContiguousI;
46 import jalview.datamodel.HiddenColumns;
47 import jalview.datamodel.PDBEntry;
48 import jalview.datamodel.SearchResults;
49 import jalview.datamodel.SearchResultsI;
50 import jalview.datamodel.SequenceI;
51 import jalview.ext.jmol.JmolParser;
52 import jalview.gui.IProgressIndicator;
53 import jalview.io.AppletFormatAdapter;
54 import jalview.io.DataSourceType;
55 import jalview.io.StructureFile;
56 import jalview.util.MappingUtils;
57 import jalview.util.MessageManager;
58 import jalview.util.Platform;
59 import jalview.ws.sifts.SiftsClient;
60 import jalview.ws.sifts.SiftsException;
61 import jalview.ws.sifts.SiftsSettings;
63 import mc_view.PDBChain;
64 import mc_view.PDBfile;
66 public class StructureSelectionManager
68 public final static String NEWLINE = System.lineSeparator();
70 static IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> instances;
72 private List<StructureMapping> mappings = new ArrayList<>();
74 private boolean processSecondaryStructure = false;
76 private boolean secStructServices = false;
78 private boolean addTempFacAnnot = false;
81 * Set of any registered mappings between (dataset) sequences.
83 private List<AlignedCodonFrame> seqmappings = new ArrayList<>();
85 private List<CommandListener> commandListeners = new ArrayList<>();
87 private List<SelectionListener> sel_listeners = new ArrayList<>();
90 * @return true if will try to use external services for processing secondary
93 public boolean isSecStructServices()
95 return secStructServices;
99 * control use of external services for processing secondary structure
101 * @param secStructServices
103 public void setSecStructServices(boolean secStructServices)
105 this.secStructServices = secStructServices;
109 * flag controlling addition of any kind of structural annotation
111 * @return true if temperature factor annotation will be added
113 public boolean isAddTempFacAnnot()
115 return addTempFacAnnot;
119 * set flag controlling addition of structural annotation
121 * @param addTempFacAnnot
123 public void setAddTempFacAnnot(boolean addTempFacAnnot)
125 this.addTempFacAnnot = addTempFacAnnot;
130 * @return if true, the structure manager will attempt to add secondary
131 * structure lines for unannotated sequences
134 public boolean isProcessSecondaryStructure()
136 return processSecondaryStructure;
140 * Control whether structure manager will try to annotate mapped sequences
141 * with secondary structure from PDB data.
145 public void setProcessSecondaryStructure(boolean enable)
147 processSecondaryStructure = enable;
151 * debug function - write all mappings to stdout
153 public void reportMapping()
155 if (mappings.isEmpty())
157 System.err.println("reportMapping: No PDB/Sequence mappings.");
162 "reportMapping: There are " + mappings.size() + " mappings.");
164 for (StructureMapping sm : mappings)
166 System.err.println("mapping " + i++ + " : " + sm.pdbfile);
172 * map between the PDB IDs (or structure identifiers) used by Jalview and the
173 * absolute filenames for PDB data that corresponds to it
175 Map<String, String> pdbIdFileName = new HashMap<>();
177 Map<String, String> pdbFileNameId = new HashMap<>();
179 public void registerPDBFile(String idForFile, String absoluteFile)
181 pdbIdFileName.put(idForFile, absoluteFile);
182 pdbFileNameId.put(absoluteFile, idForFile);
185 public String findIdForPDBFile(String idOrFile)
187 String id = pdbFileNameId.get(idOrFile);
191 public String findFileForPDBId(String idOrFile)
193 String id = pdbIdFileName.get(idOrFile);
197 public boolean isPDBFileRegistered(String idOrFile)
199 return pdbFileNameId.containsKey(idOrFile)
200 || pdbIdFileName.containsKey(idOrFile);
203 private static StructureSelectionManager nullProvider = null;
205 public static StructureSelectionManager getStructureSelectionManager(
206 StructureSelectionManagerProvider context)
210 if (nullProvider == null)
212 if (instances != null)
214 throw new Error(MessageManager.getString(
215 "error.implementation_error_structure_selection_manager_null"),
216 new NullPointerException(MessageManager
217 .getString("exception.ssm_context_is_null")));
221 nullProvider = new StructureSelectionManager();
226 if (instances == null)
228 instances = new java.util.IdentityHashMap<>();
230 StructureSelectionManager instance = instances.get(context);
231 if (instance == null)
233 if (nullProvider != null)
235 instance = nullProvider;
239 instance = new StructureSelectionManager();
241 instances.put(context, instance);
247 * flag controlling whether SeqMappings are relayed from received sequence
248 * mouse over events to other sequences
250 boolean relaySeqMappings = true;
253 * Enable or disable relay of seqMapping events to other sequences. You might
254 * want to do this if there are many sequence mappings and the host computer
259 public void setRelaySeqMappings(boolean relay)
261 relaySeqMappings = relay;
265 * get the state of the relay seqMappings flag.
267 * @return true if sequence mouse overs are being relayed to other mapped
270 public boolean isRelaySeqMappingsEnabled()
272 return relaySeqMappings;
275 Vector listeners = new Vector();
278 * register a listener for alignment sequence mouseover events
282 public void addStructureViewerListener(Object svl)
284 if (!listeners.contains(svl))
286 listeners.addElement(svl);
291 * Returns the filename the PDB id is already mapped to if known, or null if
297 public String alreadyMappedToFile(String pdbid)
299 for (StructureMapping sm : mappings)
301 if (sm.getPdbId().equalsIgnoreCase(pdbid))
310 * Import structure data and register a structure mapping for broadcasting
311 * colouring, mouseovers and selection events (convenience wrapper).
314 * - one or more sequences to be mapped to pdbFile
315 * @param targetChains
316 * - optional chain specification for mapping each sequence to pdb
317 * (may be nill, individual elements may be nill)
319 * - structure data resource
321 * - how to resolve data from resource
322 * @return null or the structure data parsed as a pdb file
324 synchronized public StructureFile setMapping(SequenceI[] sequence,
325 String[] targetChains, String pdbFile, DataSourceType protocol,
326 IProgressIndicator progress)
328 return computeMapping(true, sequence, targetChains, pdbFile, protocol,
333 * Import a single structure file and register sequence structure mappings for
334 * broadcasting colouring, mouseovers and selection events (convenience
337 * @param forStructureView
338 * when true, record the mapping for use in mouseOvers
340 * - one or more sequences to be mapped to pdbFile
341 * @param targetChains
342 * - optional chain specification for mapping each sequence to pdb
343 * (may be nill, individual elements may be nill)
345 * - structure data resource
347 * - how to resolve data from resource
348 * @return null or the structure data parsed as a pdb file
350 synchronized public StructureFile setMapping(boolean forStructureView,
351 SequenceI[] sequenceArray, String[] targetChainIds,
352 String pdbFile, DataSourceType sourceType)
354 return computeMapping(forStructureView, sequenceArray, targetChainIds,
355 pdbFile, sourceType, null);
359 * create sequence structure mappings between each sequence and the given
360 * pdbFile (retrieved via the given protocol). Either constructs a mapping
361 * using NW alignment or derives one from any available SIFTS mapping data.
363 * @param forStructureView
364 * when true, record the mapping for use in mouseOvers
366 * @param sequenceArray
367 * - one or more sequences to be mapped to pdbFile
368 * @param targetChainIds
369 * - optional chain specification for mapping each sequence to pdb
370 * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
371 * - this should be List<List<String>>, empty lists indicate no
372 * predefined mappings
374 * - structure data resource
376 * - how to resolve data from resource
377 * @param IProgressIndicator
378 * reference to UI component that maintains a progress bar for the
380 * @return null or the structure data parsed as a pdb file
382 synchronized public StructureFile computeMapping(boolean forStructureView,
383 SequenceI[] sequenceArray, String[] targetChainIds,
384 String pdbFile, DataSourceType sourceType,
385 IProgressIndicator progress)
387 long progressSessionId = System.currentTimeMillis() * 3;
390 * do we extract and transfer annotation from 3D data ?
392 // FIXME: possibly should just delete
394 boolean parseSecStr = processSecondaryStructure
395 ? isStructureFileProcessed(pdbFile, sequenceArray)
398 StructureFile pdb = null;
399 boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
402 // FIXME if sourceType is not null, we've lost data here
403 sourceType = AppletFormatAdapter.checkProtocol(pdbFile);
404 pdb = new JmolParser(false, pdbFile, sourceType);
405 pdb.addSettings(parseSecStr && processSecondaryStructure,
406 parseSecStr && addTempFacAnnot,
407 parseSecStr && secStructServices);
409 if (pdb.getId() != null && pdb.getId().trim().length() > 0
410 && DataSourceType.FILE == sourceType)
412 registerPDBFile(pdb.getId().trim(), pdbFile);
414 // if PDBId is unavailable then skip SIFTS mapping execution path
415 // TODO: JAL-3868 need to know if structure is actually from
416 // PDB (has valid PDB ID and has provenance suggesting it
417 // actually came from PDB)
418 boolean isProtein = false;
419 for (SequenceI s : sequenceArray)
427 isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable()
428 && !pdb.getId().startsWith("AF-") && isProtein;
430 } catch (Exception ex)
432 ex.printStackTrace();
436 * sifts client - non null if SIFTS mappings are to be used
438 SiftsClient siftsClient = null;
443 siftsClient = new SiftsClient(pdb);
445 } catch (SiftsException e)
447 isMapUsingSIFTs = false;
448 Console.error("SIFTS mapping failed", e);
449 Console.error("Falling back on Needleman & Wunsch alignment");
453 String targetChainId;
454 for (int s = 0; s < sequenceArray.length; s++)
456 boolean infChain = true;
457 final SequenceI seq = sequenceArray[s];
459 while (ds.getDatasetSequence() != null)
461 ds = ds.getDatasetSequence();
464 if (targetChainIds != null && targetChainIds[s] != null)
467 targetChainId = targetChainIds[s];
469 else if (seq.getName().indexOf("|") > -1)
471 targetChainId = seq.getName()
472 .substring(seq.getName().lastIndexOf("|") + 1);
473 if (targetChainId.length() > 1)
475 if (targetChainId.trim().length() == 0)
481 // not a valid chain identifier
492 * Attempt pairwise alignment of the sequence with each chain in the PDB,
493 * and remember the highest scoring chain
496 AlignSeq maxAlignseq = null;
497 String maxChainId = " ";
498 PDBChain maxChain = null;
499 boolean first = true;
500 for (PDBChain chain : pdb.getChains())
502 if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
505 continue; // don't try to map chains don't match.
507 // TODO: correctly determine sequence type for mixed na/peptide
509 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
510 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
513 // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
514 // as.calcScoreMatrix();
515 // as.traceAlignment();
517 if (first || as.maxscore > max
518 || (as.maxscore == max && chain.id.equals(targetChainId)))
524 maxChainId = chain.id;
527 if (maxChain == null)
532 if (sourceType == DataSourceType.PASTE)
534 pdbFile = "INLINE" + pdb.getId();
537 List<StructureMapping> seqToStrucMapping = new ArrayList<>();
538 if (isMapUsingSIFTs && seq.isProtein())
540 if (progress != null)
542 progress.setProgressBar(
544 .getString("status.obtaining_mapping_with_sifts"),
547 jalview.datamodel.Mapping sqmpping = maxAlignseq
548 .getMappingFromS1(false);
549 if (targetChainId != null && !targetChainId.trim().isEmpty())
551 StructureMapping siftsMapping;
554 siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
555 pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
556 seqToStrucMapping.add(siftsMapping);
557 maxChain.makeExactMapping(siftsMapping, seq);
558 maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS",
559 pdb.getId().toLowerCase(Locale.ROOT));
560 maxChain.transferResidueAnnotation(siftsMapping, null);
561 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
563 } catch (SiftsException e)
565 // fall back to NW alignment
566 Console.error(e.getMessage());
567 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
568 targetChainId, maxChain, pdb, maxAlignseq);
569 seqToStrucMapping.add(nwMapping);
570 maxChain.makeExactMapping(maxAlignseq, seq);
571 maxChain.transferRESNUMFeatures(seq, "IEA:Jalview",
572 pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is
575 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
576 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
581 List<StructureMapping> foundSiftsMappings = new ArrayList<>();
582 for (PDBChain chain : pdb.getChains())
584 StructureMapping siftsMapping = null;
587 siftsMapping = getStructureMapping(seq, pdbFile, chain.id,
588 pdb, chain, sqmpping, maxAlignseq, siftsClient);
589 foundSiftsMappings.add(siftsMapping);
590 chain.makeExactMapping(siftsMapping, seq);
591 chain.transferRESNUMFeatures(seq, "IEA: SIFTS",
592 pdb.getId().toLowerCase(Locale.ROOT));// FIXME: is this
594 chain.transferResidueAnnotation(siftsMapping, null);
595 } catch (SiftsException e)
597 System.err.println(e.getMessage());
598 } catch (Exception e)
601 "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
602 System.err.println(e.getMessage());
605 if (!foundSiftsMappings.isEmpty())
607 seqToStrucMapping.addAll(foundSiftsMappings);
608 ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
612 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
613 maxChainId, maxChain, pdb, maxAlignseq);
614 seqToStrucMapping.add(nwMapping);
615 maxChain.transferRESNUMFeatures(seq, null,
616 pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is this
618 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
619 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
625 if (progress != null)
627 progress.setProgressBar(
628 MessageManager.getString(
629 "status.obtaining_mapping_with_nw_alignment"),
632 StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
633 maxChain, pdb, maxAlignseq);
634 seqToStrucMapping.add(nwMapping);
635 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
637 if (forStructureView)
639 for (StructureMapping sm : seqToStrucMapping)
641 addStructureMapping(sm); // not addAll!
644 if (progress != null)
646 progress.setProgressBar(null, progressSessionId);
653 * check if we need to extract secondary structure from given pdbFile and
654 * transfer to sequences
657 * @param sequenceArray
660 private boolean isStructureFileProcessed(String pdbFile,
661 SequenceI[] sequenceArray)
663 boolean parseSecStr = true;
664 if (isPDBFileRegistered(pdbFile))
666 for (SequenceI sq : sequenceArray)
669 while (ds.getDatasetSequence() != null)
671 ds = ds.getDatasetSequence();
674 if (ds.getAnnotation() != null)
676 for (AlignmentAnnotation ala : ds.getAnnotation())
678 // false if any annotation present from this structure
679 // JBPNote this fails for jmol/chimera view because the *file* is
680 // passed, not the structure data ID -
681 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
692 public void addStructureMapping(StructureMapping sm)
694 if (!mappings.contains(sm))
701 * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
706 * @param targetChainId
712 * client for retrieval of SIFTS mappings for this structure
714 * @throws SiftsException
716 private StructureMapping getStructureMapping(SequenceI seq,
717 String pdbFile, String targetChainId, StructureFile pdb,
718 PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
719 AlignSeq maxAlignseq, SiftsClient siftsClient)
720 throws SiftsException
722 StructureMapping curChainMapping = siftsClient
723 .getSiftsStructureMapping(seq, pdbFile, targetChainId);
726 PDBChain chain = pdb.findChain(targetChainId);
729 chain.transferResidueAnnotation(curChainMapping, null);
731 } catch (Exception e)
735 return curChainMapping;
738 private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
739 String maxChainId, PDBChain maxChain, StructureFile pdb,
740 AlignSeq maxAlignseq)
742 final StringBuilder mappingDetails = new StringBuilder(128);
743 mappingDetails.append(NEWLINE)
744 .append("Sequence \u27f7 Structure mapping details");
745 mappingDetails.append(NEWLINE);
747 .append("Method: inferred with Needleman & Wunsch alignment");
748 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
749 .append(NEWLINE).append("Sequence = ")
750 .append(maxChain.sequence.getSequenceAsString());
751 mappingDetails.append(NEWLINE).append("No of residues = ")
752 .append(maxChain.residues.size()).append(NEWLINE)
754 PrintStream ps = new PrintStream(System.out)
757 public void print(String x)
759 mappingDetails.append(x);
763 public void println()
765 mappingDetails.append(NEWLINE);
769 maxAlignseq.printAlignment(ps);
771 mappingDetails.append(NEWLINE).append("PDB start/end ");
772 mappingDetails.append(String.valueOf(maxAlignseq.seq2start))
774 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
775 mappingDetails.append(NEWLINE).append("SEQ start/end ");
778 .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1)))
780 mappingDetails.append(
781 String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1)));
782 mappingDetails.append(NEWLINE);
783 maxChain.makeExactMapping(maxAlignseq, seq);
784 jalview.datamodel.Mapping sqmpping = maxAlignseq
785 .getMappingFromS1(false);
786 maxChain.transferRESNUMFeatures(seq, null,
787 pdb.getId().toLowerCase(Locale.ROOT));
789 HashMap<Integer, int[]> mapping = new HashMap<>();
796 Atom tmp = maxChain.atoms.elementAt(index);
797 if ((resNum != tmp.resNumber || insCode != tmp.insCode)
798 && tmp.alignmentMapping != -1)
800 resNum = tmp.resNumber;
801 insCode = tmp.insCode;
802 if (tmp.alignmentMapping >= -1)
804 mapping.put(tmp.alignmentMapping + 1,
806 { tmp.resNumber, tmp.atomIndex });
811 } while (index < maxChain.atoms.size());
813 StructureMapping nwMapping = new StructureMapping(seq, pdbFile,
814 pdb.getId(), maxChainId, mapping, mappingDetails.toString());
815 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
819 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
821 listeners.removeElement(svl);
822 if (svl instanceof SequenceListener)
824 for (int i = 0; i < listeners.size(); i++)
826 if (listeners.elementAt(i) instanceof StructureListener)
828 ((StructureListener) listeners.elementAt(i))
829 .releaseReferences(svl);
834 if (pdbfiles == null)
840 * Remove mappings to the closed listener's PDB files, but first check if
841 * another listener is still interested
843 List<String> pdbs = new ArrayList<>(Arrays.asList(pdbfiles));
845 StructureListener sl;
846 for (int i = 0; i < listeners.size(); i++)
848 if (listeners.elementAt(i) instanceof StructureListener)
850 sl = (StructureListener) listeners.elementAt(i);
851 for (String pdbfile : sl.getStructureFiles())
853 pdbs.remove(pdbfile);
859 * Rebuild the mappings set, retaining only those which are for 'other' PDB
864 List<StructureMapping> tmp = new ArrayList<>();
865 for (StructureMapping sm : mappings)
867 if (!pdbs.contains(sm.pdbfile))
878 * hack to highlight a range of positions at once on any structure views
882 * - series of int start-end ranges as positions on sequenceRef
886 public void highlightPositionsOn(SequenceI sequenceRef, int[][] is,
889 boolean hasSequenceListeners = handlingVamsasMo
890 || !seqmappings.isEmpty();
891 SearchResultsI results = null;
892 ArrayList<Integer> listOfPositions = new ArrayList<Integer>();
895 for (int p = s_e[0]; p <= s_e[1]; listOfPositions.add(p++))
898 int seqpos[] = new int[listOfPositions.size()];
900 for (Integer p : listOfPositions)
905 for (i = 0; i < listeners.size(); i++)
907 Object listener = listeners.elementAt(i);
908 if (listener == source)
910 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
911 // Temporary fudge with SequenceListener.getVamsasSource()
914 if (listener instanceof StructureListener)
916 highlightStructure((StructureListener) listener, sequenceRef,
924 * Propagate mouseover of a single position in a structure
931 public String mouseOverStructure(int pdbResNum, String chain,
934 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
935 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
936 return mouseOverStructure(atoms);
940 * Propagate mouseover or selection of multiple positions in a structure
944 public String mouseOverStructure(List<AtomSpec> atoms)
946 if (listeners == null)
948 // old or prematurely sent event
951 boolean hasSequenceListener = false;
952 for (int i = 0; i < listeners.size(); i++)
954 if (listeners.elementAt(i) instanceof SequenceListener)
956 hasSequenceListener = true;
959 if (!hasSequenceListener)
964 SearchResultsI results = findAlignmentPositionsForStructurePositions(
966 String result = null;
967 for (Object li : listeners)
969 if (li instanceof SequenceListener)
971 String s = ((SequenceListener) li).highlightSequence(results);
982 * Constructs a SearchResults object holding regions (if any) in the Jalview
983 * alignment which have a mapping to the structure viewer positions in the
989 public SearchResultsI findAlignmentPositionsForStructurePositions(
990 List<AtomSpec> atoms)
992 SearchResultsI results = new SearchResults();
993 for (AtomSpec atom : atoms)
995 SequenceI lastseq = null;
997 for (StructureMapping sm : mappings)
999 if (sm.pdbfile.equals(atom.getPdbFile())
1000 && sm.pdbchain.equals(atom.getChain()))
1002 int indexpos = sm.getSeqPos(atom.getPdbResNum());
1003 if (lastipos != indexpos || lastseq != sm.sequence)
1005 results.addResult(sm.sequence, indexpos, indexpos);
1006 lastipos = indexpos;
1007 lastseq = sm.sequence;
1008 // construct highlighted sequence list
1009 for (AlignedCodonFrame acf : seqmappings)
1011 acf.markMappedRegion(sm.sequence, indexpos, results);
1021 * highlight regions associated with a position (indexpos) in seq
1024 * the sequence that the mouse over occurred on
1026 * the absolute position being mouseovered in seq (0 to seq.length())
1028 * the sequence position (if -1, seq.findPosition is called to
1029 * resolve the residue number)
1031 public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
1032 VamsasSource source)
1034 boolean hasSequenceListeners = handlingVamsasMo
1035 || !seqmappings.isEmpty();
1036 SearchResultsI results = null;
1039 seqPos = seq.findPosition(indexpos);
1041 for (int i = 0; i < listeners.size(); i++)
1043 Object listener = listeners.elementAt(i);
1044 if (listener == source)
1046 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1047 // Temporary fudge with SequenceListener.getVamsasSource()
1050 if (listener instanceof StructureListener)
1052 highlightStructure((StructureListener) listener, seq, seqPos);
1056 if (listener instanceof SequenceListener)
1058 final SequenceListener seqListener = (SequenceListener) listener;
1059 if (hasSequenceListeners
1060 && seqListener.getVamsasSource() != source)
1062 if (relaySeqMappings)
1064 if (results == null)
1066 results = MappingUtils.buildSearchResults(seq, seqPos,
1069 if (handlingVamsasMo)
1071 results.addResult(seq, seqPos, seqPos);
1074 if (!results.isEmpty())
1076 seqListener.highlightSequence(results);
1081 else if (listener instanceof VamsasListener && !handlingVamsasMo)
1083 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
1086 else if (listener instanceof SecondaryStructureListener)
1088 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
1096 * Send suitable messages to a StructureListener to highlight atoms
1097 * corresponding to the given sequence position(s)
1103 public void highlightStructure(StructureListener sl, SequenceI seq,
1106 if (!sl.isListeningFor(seq))
1111 List<AtomSpec> atoms = new ArrayList<>();
1112 for (StructureMapping sm : mappings)
1114 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1115 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1116 .getDatasetSequence() == seq.getDatasetSequence()))
1118 for (int index : positions)
1120 atomNo = sm.getAtomNum(index);
1124 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1125 sm.getPDBResNum(index), atomNo));
1130 sl.highlightAtoms(atoms);
1133 public void highlightStructureRegionsFor(StructureListener sl,
1134 SequenceI[] seqs, int... columns)
1136 List<SequenceI> to_highlight = new ArrayList<SequenceI>();
1137 for (SequenceI seq : seqs)
1139 if (sl.isListeningFor(seq))
1141 to_highlight.add(seq);
1144 if (to_highlight.size() == 0)
1148 List<AtomSpec> atoms = new ArrayList<>();
1149 for (SequenceI seq : to_highlight)
1152 for (StructureMapping sm : mappings)
1154 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1155 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1156 .getDatasetSequence() == seq.getDatasetSequence()))
1159 for (int i = 0; i < columns.length; i += 2)
1161 ContiguousI positions = seq.findPositions(columns[i] + 1,
1162 columns[i + 1] + 1);
1163 if (positions == null)
1167 for (int index = positions.getBegin(); index <= positions
1171 atomNo = sm.getAtomNum(index);
1175 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1176 sm.getPDBResNum(index), atomNo));
1182 if (atoms.size() > 0)
1184 sl.highlightAtoms(atoms);
1190 * true if a mouse over event from an external (ie Vamsas) source is being
1193 boolean handlingVamsasMo = false;
1198 * as mouseOverSequence but only route event to SequenceListeners
1202 * in an alignment sequence
1204 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
1205 VamsasSource source)
1207 handlingVamsasMo = true;
1208 long msg = sequenceI.hashCode() * (1 + position);
1212 mouseOverSequence(sequenceI, position, -1, source);
1214 handlingVamsasMo = false;
1217 public Annotation[] colourSequenceFromStructure(SequenceI seq,
1221 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
1222 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
1224 * Annotation [] annotations = new Annotation[seq.getLength()];
1226 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
1227 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
1228 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
1230 * for (int j = 0; j < mappings.length; j++) {
1232 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
1233 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
1234 * System.out.println(pdbid+" "+mappings[j].getPdbId() +"
1235 * "+mappings[j].pdbfile);
1237 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
1238 * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
1240 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
1241 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
1242 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
1243 * mappings[j].pdbfile); }
1245 * annotations[index] = new Annotation("X",null,' ',0,col); } return
1246 * annotations; } } } }
1248 * return annotations;
1252 public void structureSelectionChanged()
1256 public void sequenceSelectionChanged()
1260 public void sequenceColoursChanged(Object source)
1262 StructureListener sl;
1263 for (int i = 0; i < listeners.size(); i++)
1265 if (listeners.elementAt(i) instanceof StructureListener)
1267 sl = (StructureListener) listeners.elementAt(i);
1268 sl.updateColours(source);
1273 public StructureMapping[] getMapping(String pdbfile)
1275 List<StructureMapping> tmp = new ArrayList<>();
1276 for (StructureMapping sm : mappings)
1278 if (sm.pdbfile.equals(pdbfile))
1283 return tmp.toArray(new StructureMapping[tmp.size()]);
1287 * Returns a readable description of all mappings for the given pdbfile to any
1288 * of the given sequences
1294 public String printMappings(String pdbfile, List<SequenceI> seqs)
1296 if (pdbfile == null || seqs == null || seqs.isEmpty())
1301 StringBuilder sb = new StringBuilder(64);
1302 for (StructureMapping sm : mappings)
1304 if (Platform.pathEquals(sm.pdbfile, pdbfile)
1305 && seqs.contains(sm.sequence))
1307 sb.append(sm.mappingDetails);
1309 // separator makes it easier to read multiple mappings
1310 sb.append("=====================");
1316 return sb.toString();
1320 * Remove the given mapping
1324 public void deregisterMapping(AlignedCodonFrame acf)
1328 boolean removed = seqmappings.remove(acf);
1329 if (removed && seqmappings.isEmpty())
1331 System.out.println("All mappings removed");
1337 * Add each of the given codonFrames to the stored set, if not aready present.
1341 public void registerMappings(List<AlignedCodonFrame> mappings)
1343 if (mappings != null)
1345 for (AlignedCodonFrame acf : mappings)
1347 registerMapping(acf);
1353 * Add the given mapping to the stored set, unless already stored.
1355 public void registerMapping(AlignedCodonFrame acf)
1359 if (!seqmappings.contains(acf))
1361 seqmappings.add(acf);
1367 * Resets this object to its initial state by removing all registered
1368 * listeners, codon mappings, PDB file mappings
1370 public void resetAll()
1372 if (mappings != null)
1376 if (seqmappings != null)
1378 seqmappings.clear();
1380 if (sel_listeners != null)
1382 sel_listeners.clear();
1384 if (listeners != null)
1388 if (commandListeners != null)
1390 commandListeners.clear();
1392 if (view_listeners != null)
1394 view_listeners.clear();
1396 if (pdbFileNameId != null)
1398 pdbFileNameId.clear();
1400 if (pdbIdFileName != null)
1402 pdbIdFileName.clear();
1406 public void addSelectionListener(SelectionListener selecter)
1408 if (!sel_listeners.contains(selecter))
1410 sel_listeners.add(selecter);
1414 public void removeSelectionListener(SelectionListener toremove)
1416 if (sel_listeners.contains(toremove))
1418 sel_listeners.remove(toremove);
1422 public synchronized void sendSelection(
1423 jalview.datamodel.SequenceGroup selection,
1424 jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden,
1425 SelectionSource source)
1427 for (SelectionListener slis : sel_listeners)
1431 slis.selection(selection, colsel, hidden, source);
1436 Vector<AlignmentViewPanelListener> view_listeners = new Vector<>();
1438 public synchronized void sendViewPosition(
1439 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1440 int startSeq, int endSeq)
1443 if (view_listeners != null && view_listeners.size() > 0)
1445 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1447 while (listeners.hasMoreElements())
1449 AlignmentViewPanelListener slis = listeners.nextElement();
1452 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1460 * release all references associated with this manager provider
1462 * @param jalviewLite
1464 public static void release(StructureSelectionManagerProvider jalviewLite)
1466 // synchronized (instances)
1468 if (instances == null)
1472 StructureSelectionManager mnger = (instances.get(jalviewLite));
1475 instances.remove(jalviewLite);
1478 /* bsoares 2019-03-20 finalize deprecated, no apparent external
1479 * resources to close
1481 // mnger.finalize();
1482 } catch (Throwable x)
1489 public void registerPDBEntry(PDBEntry pdbentry)
1491 if (pdbentry.getFile() != null
1492 && pdbentry.getFile().trim().length() > 0)
1494 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1498 public void addCommandListener(CommandListener cl)
1500 if (!commandListeners.contains(cl))
1502 commandListeners.add(cl);
1506 public boolean hasCommandListener(CommandListener cl)
1508 return this.commandListeners.contains(cl);
1511 public boolean removeCommandListener(CommandListener l)
1513 return commandListeners.remove(l);
1517 * Forward a command to any command listeners (except for the command's
1521 * the command to be broadcast (in its form after being performed)
1523 * if true, the command was being 'undone'
1526 public void commandPerformed(CommandI command, boolean undo,
1527 VamsasSource source)
1529 for (CommandListener listener : commandListeners)
1531 listener.mirrorCommand(command, undo, this, source);
1536 * Returns a new CommandI representing the given command as mapped to the
1537 * given sequences. If no mapping could be made, or the command is not of a
1538 * mappable kind, returns null.
1546 public CommandI mapCommand(CommandI command, boolean undo,
1547 final AlignmentI mapTo, char gapChar)
1549 if (command instanceof EditCommand)
1551 return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo,
1552 gapChar, seqmappings);
1554 else if (command instanceof OrderCommand)
1556 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1557 mapTo, seqmappings);
1562 public List<AlignedCodonFrame> getSequenceMappings()
1568 * quick and dirty route to just highlight all structure positions for a range
1571 * @param sequencesArray
1573 * start-end columns on sequencesArray
1575 * origin parent AlignmentPanel
1577 public void highlightPositionsOnMany(SequenceI[] sequencesArray, int[] is,
1580 for (int i = 0; i < listeners.size(); i++)
1582 Object listener = listeners.elementAt(i);
1583 if (listener == source)
1585 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1586 // Temporary fudge with SequenceListener.getVamsasSource()
1589 if (listener instanceof StructureListener)
1591 highlightStructureRegionsFor((StructureListener) listener,
1592 sequencesArray, is);