2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.structure;
23 import java.io.PrintStream;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.Enumeration;
28 import java.util.HashMap;
29 import java.util.IdentityHashMap;
30 import java.util.List;
31 import java.util.Locale;
33 import java.util.Vector;
35 import jalview.analysis.AlignSeq;
36 import jalview.api.StructureSelectionManagerProvider;
37 import jalview.bin.Console;
38 import jalview.commands.CommandI;
39 import jalview.commands.EditCommand;
40 import jalview.commands.OrderCommand;
41 import jalview.datamodel.AlignedCodonFrame;
42 import jalview.datamodel.AlignmentAnnotation;
43 import jalview.datamodel.AlignmentI;
44 import jalview.datamodel.Annotation;
45 import jalview.datamodel.ContiguousI;
46 import jalview.datamodel.HiddenColumns;
47 import jalview.datamodel.PDBEntry;
48 import jalview.datamodel.SearchResults;
49 import jalview.datamodel.SearchResultsI;
50 import jalview.datamodel.SequenceI;
51 import jalview.ext.jmol.JmolParser;
52 import jalview.gui.IProgressIndicator;
53 import jalview.io.AppletFormatAdapter;
54 import jalview.io.DataSourceType;
55 import jalview.io.StructureFile;
56 import jalview.structure.StructureImportSettings.TFType;
57 import jalview.util.MappingUtils;
58 import jalview.util.MessageManager;
59 import jalview.util.Platform;
60 import jalview.ws.sifts.SiftsClient;
61 import jalview.ws.sifts.SiftsException;
62 import jalview.ws.sifts.SiftsSettings;
64 import mc_view.PDBChain;
65 import mc_view.PDBfile;
67 public class StructureSelectionManager
69 public final static String NEWLINE = System.lineSeparator();
71 static IdentityHashMap<StructureSelectionManagerProvider, StructureSelectionManager> instances;
73 private List<StructureMapping> mappings = new ArrayList<>();
75 private boolean processSecondaryStructure = false;
77 private boolean secStructServices = false;
79 private boolean addTempFacAnnot = false;
82 * Set of any registered mappings between (dataset) sequences.
84 private List<AlignedCodonFrame> seqmappings = new ArrayList<>();
86 private List<CommandListener> commandListeners = new ArrayList<>();
88 private List<SelectionListener> sel_listeners = new ArrayList<>();
91 * @return true if will try to use external services for processing secondary
94 public boolean isSecStructServices()
96 return secStructServices;
100 * control use of external services for processing secondary structure
102 * @param secStructServices
104 public void setSecStructServices(boolean secStructServices)
106 this.secStructServices = secStructServices;
110 * flag controlling addition of any kind of structural annotation
112 * @return true if temperature factor annotation will be added
114 public boolean isAddTempFacAnnot()
116 return addTempFacAnnot;
120 * set flag controlling addition of structural annotation
122 * @param addTempFacAnnot
124 public void setAddTempFacAnnot(boolean addTempFacAnnot)
126 this.addTempFacAnnot = addTempFacAnnot;
131 * @return if true, the structure manager will attempt to add secondary
132 * structure lines for unannotated sequences
135 public boolean isProcessSecondaryStructure()
137 return processSecondaryStructure;
141 * Control whether structure manager will try to annotate mapped sequences
142 * with secondary structure from PDB data.
146 public void setProcessSecondaryStructure(boolean enable)
148 processSecondaryStructure = enable;
152 * debug function - write all mappings to stdout
154 public void reportMapping()
156 if (mappings.isEmpty())
158 jalview.bin.Console.errPrintln("reportMapping: No PDB/Sequence mappings.");
162 jalview.bin.Console.errPrintln(
163 "reportMapping: There are " + mappings.size() + " mappings.");
165 for (StructureMapping sm : mappings)
167 jalview.bin.Console.errPrintln("mapping " + i++ + " : " + sm.pdbfile);
173 * map between the PDB IDs (or structure identifiers) used by Jalview and the
174 * absolute filenames for PDB data that corresponds to it
176 Map<String, String> pdbIdFileName = new HashMap<>();
178 Map<String, String> pdbFileNameId = new HashMap<>();
180 public void registerPDBFile(String idForFile, String absoluteFile)
182 pdbIdFileName.put(idForFile, absoluteFile);
183 pdbFileNameId.put(absoluteFile, idForFile);
186 public String findIdForPDBFile(String idOrFile)
188 String id = pdbFileNameId.get(idOrFile);
192 public String findFileForPDBId(String idOrFile)
194 String id = pdbIdFileName.get(idOrFile);
198 public boolean isPDBFileRegistered(String idOrFile)
200 return pdbFileNameId.containsKey(idOrFile)
201 || pdbIdFileName.containsKey(idOrFile);
204 private static StructureSelectionManager nullProvider = null;
206 public static StructureSelectionManager getStructureSelectionManager(
207 StructureSelectionManagerProvider context)
211 if (nullProvider == null)
213 if (instances != null)
215 throw new Error(MessageManager.getString(
216 "error.implementation_error_structure_selection_manager_null"),
217 new NullPointerException(MessageManager
218 .getString("exception.ssm_context_is_null")));
222 nullProvider = new StructureSelectionManager();
227 if (instances == null)
229 instances = new java.util.IdentityHashMap<>();
231 StructureSelectionManager instance = instances.get(context);
232 if (instance == null)
234 if (nullProvider != null)
236 instance = nullProvider;
240 instance = new StructureSelectionManager();
242 instances.put(context, instance);
248 * flag controlling whether SeqMappings are relayed from received sequence
249 * mouse over events to other sequences
251 boolean relaySeqMappings = true;
254 * Enable or disable relay of seqMapping events to other sequences. You might
255 * want to do this if there are many sequence mappings and the host computer
260 public void setRelaySeqMappings(boolean relay)
262 relaySeqMappings = relay;
266 * get the state of the relay seqMappings flag.
268 * @return true if sequence mouse overs are being relayed to other mapped
271 public boolean isRelaySeqMappingsEnabled()
273 return relaySeqMappings;
276 Vector listeners = new Vector();
279 * register a listener for alignment sequence mouseover events
283 public void addStructureViewerListener(Object svl)
285 if (!listeners.contains(svl))
287 listeners.addElement(svl);
292 * Returns the filename the PDB id is already mapped to if known, or null if
298 public String alreadyMappedToFile(String pdbid)
300 for (StructureMapping sm : mappings)
302 if (sm.getPdbId().equalsIgnoreCase(pdbid))
311 * Import structure data and register a structure mapping for broadcasting
312 * colouring, mouseovers and selection events (convenience wrapper).
315 * - one or more sequences to be mapped to pdbFile
316 * @param targetChains
317 * - optional chain specification for mapping each sequence to pdb
318 * (may be nill, individual elements may be nill)
320 * - structure data resource
322 * - how to resolve data from resource
323 * @return null or the structure data parsed as a pdb file
325 synchronized public StructureFile setMapping(SequenceI[] sequence,
326 String[] targetChains, String pdbFile, DataSourceType protocol,
327 IProgressIndicator progress)
329 return computeMapping(true, sequence, targetChains, pdbFile, protocol,
330 progress, null, null, true);
334 * Import a single structure file and register sequence structure mappings for
335 * broadcasting colouring, mouseovers and selection events (convenience
338 * @param forStructureView
339 * when true, record the mapping for use in mouseOvers
341 * - one or more sequences to be mapped to pdbFile
342 * @param targetChains
343 * - optional chain specification for mapping each sequence to pdb
344 * (may be nill, individual elements may be nill)
346 * - structure data resource
348 * - how to resolve data from resource
349 * @return null or the structure data parsed as a pdb file
351 synchronized public StructureFile setMapping(boolean forStructureView,
352 SequenceI[] sequenceArray, String[] targetChainIds,
353 String pdbFile, DataSourceType sourceType, TFType tft,
356 return setMapping(forStructureView, sequenceArray, targetChainIds,
357 pdbFile, sourceType, tft, paeFilename, true);
362 * create sequence structure mappings between each sequence and the given
363 * pdbFile (retrieved via the given protocol). Either constructs a mapping
364 * using NW alignment or derives one from any available SIFTS mapping data.
366 * @param forStructureView
367 * when true, record the mapping for use in mouseOvers
369 * @param sequenceArray
370 * - one or more sequences to be mapped to pdbFile
371 * @param targetChainIds
372 * - optional chain specification for mapping each sequence to pdb
373 * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
374 * - this should be List<List<String>>, empty lists indicate no
375 * predefined mappings
377 * - structure data resource
379 * - how to resolve data from resource
380 * @param tft - specify how to interpret the temperature factor column in the atom data
381 * @param paeFilename - when not null, specifies a filename containing a matrix formatted in JSON using one of the known PAE formats
382 * @param doXferSettings - when true, transfer annotation to mapped sequences in sequenceArray
383 * @return null or the structure data parsed as a pdb file
385 synchronized public StructureFile setMapping(boolean forStructureView,
386 SequenceI[] sequenceArray, String[] targetChainIds,
387 String pdbFile, DataSourceType sourceType, TFType tft,
388 String paeFilename, boolean doXferSettings)
390 return computeMapping(forStructureView, sequenceArray, targetChainIds,
391 pdbFile, sourceType, null, tft, paeFilename, doXferSettings);
395 * create sequence structure mappings between each sequence and the given
396 * pdbFile (retrieved via the given protocol). Either constructs a mapping
397 * using NW alignment or derives one from any available SIFTS mapping data.
399 * @param forStructureView
400 * when true, record the mapping for use in mouseOvers
402 * @param sequenceArray
403 * - one or more sequences to be mapped to pdbFile
404 * @param targetChainIds
405 * - optional chain specification for mapping each sequence to pdb
406 * (may be nill, individual elements may be nill) - JBPNote: JAL-2693
407 * - this should be List<List<String>>, empty lists indicate no
408 * predefined mappings
410 * - structure data resource
412 * - how to resolve data from resource
413 * @param IProgressIndicator
414 * reference to UI component that maintains a progress bar for the
416 * @param tft - specify how to interpret the temperature factor column in the atom data
417 * @param paeFilename - when not null, specifies a filename containing a matrix formatted in JSON using one of the known PAE formats
418 * @param doXferSettings - when true, transfer annotation to mapped sequences in sequenceArray
419 * @return null or the structure data parsed as a pdb file
421 synchronized public StructureFile computeMapping(boolean forStructureView,
422 SequenceI[] sequenceArray, String[] targetChainIds,
423 String pdbFile, DataSourceType sourceType,
424 IProgressIndicator progress, TFType tft, String paeFilename,
425 boolean doXferSettings)
427 long progressSessionId = System.currentTimeMillis() * 3;
429 boolean inspectPdbEntry=false;
430 PDBEntry barepdbe=null;
431 if (tft==null || paeFilename==null)
433 for (SequenceI s:sequenceArray)
435 while (s.getDatasetSequence()!=null)
437 s = s.getDatasetSequence();
439 if (s.getAllPDBEntries()!=null)
441 for (PDBEntry p:s.getAllPDBEntries())
443 if (p.getFile()!=null && p.getFile().equals(pdbFile))
447 barepdbe=new PDBEntry(p);
449 // todo - may need to copy and null the chaincode so we can do a faithful update
450 barepdbe.updateFrom(p);
458 if (barepdbe.hasTempFacType())
461 tft = TFType.valueOf(barepdbe.getTempFacType().toUpperCase(Locale.ROOT));
462 } catch (IllegalArgumentException ia)
464 Console.warn("Ignoring unknown temperature factor type '"+barepdbe.getTempFacType()+"'");
471 * do we extract and transfer annotation from 3D data ?
473 // FIXME: possibly should just delete
475 boolean parseSecStr = processSecondaryStructure
476 && !isStructureFileProcessed(pdbFile, sequenceArray);
478 StructureFile pdb = null;
479 boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts();
482 // FIXME if sourceType is not null, we've lost data here
483 sourceType = AppletFormatAdapter.checkProtocol(pdbFile);
484 pdb = new JmolParser(false, pdbFile, sourceType);
485 if (paeFilename != null)
487 pdb.setPAEMatrix(paeFilename);
489 pdb.setTemperatureFactorType(tft);
490 pdb.addSettings(parseSecStr && processSecondaryStructure,
491 parseSecStr && addTempFacAnnot,
492 parseSecStr && secStructServices);
493 // save doXferSettings and reset after doParse()
494 boolean temp = pdb.getDoXferSettings();
495 pdb.setDoXferSettings(doXferSettings);
497 pdb.setDoXferSettings(temp);
498 if (pdb.getId() != null && pdb.getId().trim().length() > 0
499 && DataSourceType.FILE == sourceType)
501 registerPDBFile(pdb.getId().trim(), pdbFile);
503 // if PDBId is unavailable then skip SIFTS mapping execution path
504 // TODO: JAL-3868 need to know if structure is actually from
505 // PDB (has valid PDB ID and has provenance suggesting it
506 // actually came from PDB)
507 boolean isProtein = false;
508 for (SequenceI s : sequenceArray)
516 isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable()
517 && !pdb.getId().startsWith("AF-") && isProtein;
519 } catch (Exception ex)
521 ex.printStackTrace();
525 * sifts client - non null if SIFTS mappings are to be used
527 SiftsClient siftsClient = null;
532 siftsClient = new SiftsClient(pdb);
534 } catch (SiftsException e)
536 isMapUsingSIFTs = false;
537 Console.error("SIFTS mapping failed", e);
538 Console.error("Falling back on Needleman & Wunsch alignment");
542 String targetChainId;
543 for (int s = 0; s < sequenceArray.length; s++)
545 boolean infChain = true;
546 final SequenceI seq = sequenceArray[s];
548 while (ds.getDatasetSequence() != null)
550 ds = ds.getDatasetSequence();
553 if (targetChainIds != null && targetChainIds[s] != null)
556 targetChainId = targetChainIds[s];
558 else if (seq.getName().indexOf("|") > -1)
560 targetChainId = seq.getName()
561 .substring(seq.getName().lastIndexOf("|") + 1);
562 if (targetChainId.length() > 1)
564 if (targetChainId.trim().length() == 0)
570 // not a valid chain identifier
581 * Attempt pairwise alignment of the sequence with each chain in the PDB,
582 * and remember the highest scoring chain
585 AlignSeq maxAlignseq = null;
586 String maxChainId = " ";
587 PDBChain maxChain = null;
588 boolean first = true;
589 for (PDBChain chain : pdb.getChains())
591 if (targetChainId.length() > 0 && !targetChainId.equals(chain.id)
594 continue; // don't try to map chains don't match.
596 // TODO: correctly determine sequence type for mixed na/peptide
598 final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP;
599 AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence,
602 // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type);
603 // as.calcScoreMatrix();
604 // as.traceAlignment();
606 if (first || as.maxscore > max
607 || (as.maxscore == max && chain.id.equals(targetChainId)))
613 maxChainId = chain.id;
616 if (maxChain == null)
621 if (sourceType == DataSourceType.PASTE)
623 pdbFile = "INLINE" + pdb.getId();
626 List<StructureMapping> seqToStrucMapping = new ArrayList<>();
627 if (isMapUsingSIFTs && seq.isProtein())
629 if (progress != null)
631 progress.setProgressBar(
633 .getString("status.obtaining_mapping_with_sifts"),
636 jalview.datamodel.Mapping sqmpping = maxAlignseq
637 .getMappingFromS1(false);
638 if (targetChainId != null && !targetChainId.trim().isEmpty())
640 StructureMapping siftsMapping;
643 siftsMapping = getStructureMapping(seq, pdbFile, targetChainId,
644 pdb, maxChain, sqmpping, maxAlignseq, siftsClient);
645 seqToStrucMapping.add(siftsMapping);
646 maxChain.makeExactMapping(siftsMapping, seq);
647 maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS",
648 pdb.getId().toLowerCase(Locale.ROOT));
649 maxChain.transferResidueAnnotation(siftsMapping, null);
650 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
652 } catch (SiftsException e)
654 // fall back to NW alignment
655 Console.error(e.getMessage());
656 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
657 targetChainId, maxChain, pdb, maxAlignseq);
658 seqToStrucMapping.add(nwMapping);
659 maxChain.makeExactMapping(maxAlignseq, seq);
660 maxChain.transferRESNUMFeatures(seq, "IEA:Jalview",
661 pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is
664 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
665 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
670 List<StructureMapping> foundSiftsMappings = new ArrayList<>();
671 for (PDBChain chain : pdb.getChains())
673 StructureMapping siftsMapping = null;
676 siftsMapping = getStructureMapping(seq, pdbFile, chain.id,
677 pdb, chain, sqmpping, maxAlignseq, siftsClient);
678 foundSiftsMappings.add(siftsMapping);
679 chain.makeExactMapping(siftsMapping, seq);
680 chain.transferRESNUMFeatures(seq, "IEA: SIFTS",
681 pdb.getId().toLowerCase(Locale.ROOT));// FIXME: is this
683 chain.transferResidueAnnotation(siftsMapping, null);
684 } catch (SiftsException e)
686 jalview.bin.Console.errPrintln(e.getMessage());
687 } catch (Exception e)
689 jalview.bin.Console.errPrintln(
690 "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair");
691 jalview.bin.Console.errPrintln(e.getMessage());
694 if (!foundSiftsMappings.isEmpty())
696 seqToStrucMapping.addAll(foundSiftsMappings);
697 ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0));
701 StructureMapping nwMapping = getNWMappings(seq, pdbFile,
702 maxChainId, maxChain, pdb, maxAlignseq);
703 seqToStrucMapping.add(nwMapping);
704 maxChain.transferRESNUMFeatures(seq, null,
705 pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is this
707 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
708 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
714 if (progress != null)
716 progress.setProgressBar(
717 MessageManager.getString(
718 "status.obtaining_mapping_with_nw_alignment"),
721 StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId,
722 maxChain, pdb, maxAlignseq);
723 seqToStrucMapping.add(nwMapping);
724 ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0));
726 if (forStructureView)
728 for (StructureMapping sm : seqToStrucMapping)
730 addStructureMapping(sm); // not addAll!
733 if (progress != null)
735 progress.setProgressBar(null, progressSessionId);
742 * check if we need to extract secondary structure from given pdbFile and
743 * transfer to sequences
746 * @param sequenceArray
749 private boolean isStructureFileProcessed(String pdbFile,
750 SequenceI[] sequenceArray)
752 boolean processed = false;
753 if (isPDBFileRegistered(pdbFile))
755 for (SequenceI sq : sequenceArray)
758 while (ds.getDatasetSequence() != null)
760 ds = ds.getDatasetSequence();
763 if (ds.getAnnotation() != null)
765 for (AlignmentAnnotation ala : ds.getAnnotation())
767 // false if any annotation present from this structure
768 // JBPNote this fails for jmol/chimera view because the *file* is
769 // passed, not the structure data ID -
770 if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile)))
781 public void addStructureMapping(StructureMapping sm)
783 if (!mappings.contains(sm))
790 * retrieve a mapping for seq from SIFTs using associated DBRefEntry for
795 * @param targetChainId
801 * client for retrieval of SIFTS mappings for this structure
803 * @throws SiftsException
805 private StructureMapping getStructureMapping(SequenceI seq,
806 String pdbFile, String targetChainId, StructureFile pdb,
807 PDBChain maxChain, jalview.datamodel.Mapping sqmpping,
808 AlignSeq maxAlignseq, SiftsClient siftsClient)
809 throws SiftsException
811 StructureMapping curChainMapping = siftsClient
812 .getSiftsStructureMapping(seq, pdbFile, targetChainId);
815 PDBChain chain = pdb.findChain(targetChainId);
818 chain.transferResidueAnnotation(curChainMapping, null);
820 } catch (Exception e)
824 return curChainMapping;
827 private StructureMapping getNWMappings(SequenceI seq, String pdbFile,
828 String maxChainId, PDBChain maxChain, StructureFile pdb,
829 AlignSeq maxAlignseq)
831 final StringBuilder mappingDetails = new StringBuilder(128);
832 mappingDetails.append(NEWLINE)
833 .append("Sequence \u27f7 Structure mapping details");
834 mappingDetails.append(NEWLINE);
836 .append("Method: inferred with Needleman & Wunsch alignment");
837 mappingDetails.append(NEWLINE).append("PDB Sequence is :")
838 .append(NEWLINE).append("Sequence = ")
839 .append(maxChain.sequence.getSequenceAsString());
840 mappingDetails.append(NEWLINE).append("No of residues = ")
841 .append(maxChain.residues.size()).append(NEWLINE)
843 PrintStream ps = new PrintStream(System.out)
846 public void print(String x)
848 mappingDetails.append(x);
852 public void println()
854 mappingDetails.append(NEWLINE);
858 maxAlignseq.printAlignment(ps);
860 mappingDetails.append(NEWLINE).append("PDB start/end ");
861 mappingDetails.append(String.valueOf(maxAlignseq.seq2start))
863 mappingDetails.append(String.valueOf(maxAlignseq.seq2end));
864 mappingDetails.append(NEWLINE).append("SEQ start/end ");
867 .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1)))
869 mappingDetails.append(
870 String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1)));
871 mappingDetails.append(NEWLINE);
872 maxChain.makeExactMapping(maxAlignseq, seq);
873 jalview.datamodel.Mapping sqmpping = maxAlignseq
874 .getMappingFromS1(false);
875 maxChain.transferRESNUMFeatures(seq, null,
876 pdb.getId().toLowerCase(Locale.ROOT));
878 HashMap<Integer, int[]> mapping = new HashMap<>();
885 Atom tmp = maxChain.atoms.elementAt(index);
886 if ((resNum != tmp.resNumber || insCode != tmp.insCode)
887 && tmp.alignmentMapping != -1)
889 resNum = tmp.resNumber;
890 insCode = tmp.insCode;
891 if (tmp.alignmentMapping >= -1)
893 mapping.put(tmp.alignmentMapping + 1,
895 { tmp.resNumber, tmp.atomIndex });
900 } while (index < maxChain.atoms.size());
902 StructureMapping nwMapping = new StructureMapping(seq, pdbFile,
903 pdb.getId(), maxChainId, mapping, mappingDetails.toString());
904 maxChain.transferResidueAnnotation(nwMapping, sqmpping);
908 public void removeStructureViewerListener(Object svl, String[] pdbfiles)
910 listeners.removeElement(svl);
911 if (svl instanceof SequenceListener)
913 for (int i = 0; i < listeners.size(); i++)
915 if (listeners.elementAt(i) instanceof StructureListener)
917 ((StructureListener) listeners.elementAt(i))
918 .releaseReferences(svl);
923 if (pdbfiles == null)
929 * Remove mappings to the closed listener's PDB files, but first check if
930 * another listener is still interested
932 List<String> pdbs = new ArrayList<>(Arrays.asList(pdbfiles));
934 StructureListener sl;
935 for (int i = 0; i < listeners.size(); i++)
937 if (listeners.elementAt(i) instanceof StructureListener)
939 sl = (StructureListener) listeners.elementAt(i);
940 for (String pdbfile : sl.getStructureFiles())
942 pdbs.remove(pdbfile);
948 * Rebuild the mappings set, retaining only those which are for 'other' PDB
953 List<StructureMapping> tmp = new ArrayList<>();
954 for (StructureMapping sm : mappings)
956 if (!pdbs.contains(sm.pdbfile))
967 * hack to highlight a range of positions at once on any structure views
971 * - series of int start-end ranges as positions on sequenceRef
975 public void highlightPositionsOn(SequenceI sequenceRef, int[][] is,
978 boolean hasSequenceListeners = handlingVamsasMo
979 || !seqmappings.isEmpty();
980 SearchResultsI results = null;
981 ArrayList<Integer> listOfPositions = new ArrayList<Integer>();
984 for (int p = s_e[0]; p <= s_e[1]; listOfPositions.add(p++))
987 int seqpos[] = new int[listOfPositions.size()];
989 for (Integer p : listOfPositions)
994 for (i = 0; i < listeners.size(); i++)
996 Object listener = listeners.elementAt(i);
997 if (listener == source)
999 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1000 // Temporary fudge with SequenceListener.getVamsasSource()
1003 if (listener instanceof StructureListener)
1005 highlightStructure((StructureListener) listener, sequenceRef,
1013 * Propagate mouseover of a single position in a structure
1020 public String mouseOverStructure(int pdbResNum, String chain,
1023 AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0);
1024 List<AtomSpec> atoms = Collections.singletonList(atomSpec);
1025 return mouseOverStructure(atoms);
1029 * Propagate mouseover or selection of multiple positions in a structure
1033 public String mouseOverStructure(List<AtomSpec> atoms)
1035 if (listeners == null)
1037 // old or prematurely sent event
1040 boolean hasSequenceListener = false;
1041 for (int i = 0; i < listeners.size(); i++)
1043 if (listeners.elementAt(i) instanceof SequenceListener)
1045 hasSequenceListener = true;
1048 if (!hasSequenceListener)
1053 SearchResultsI results = findAlignmentPositionsForStructurePositions(
1055 String result = null;
1056 for (Object li : listeners)
1058 if (li instanceof SequenceListener)
1060 String s = ((SequenceListener) li).highlightSequence(results);
1071 * Constructs a SearchResults object holding regions (if any) in the Jalview
1072 * alignment which have a mapping to the structure viewer positions in the
1078 public SearchResultsI findAlignmentPositionsForStructurePositions(
1079 List<AtomSpec> atoms)
1081 SearchResultsI results = new SearchResults();
1082 for (AtomSpec atom : atoms)
1084 SequenceI lastseq = null;
1086 for (StructureMapping sm : mappings)
1088 if (sm.pdbfile.equals(atom.getPdbFile())
1089 && sm.pdbchain.equals(atom.getChain()))
1091 int indexpos = sm.getSeqPos(atom.getPdbResNum());
1092 if (lastipos != indexpos || lastseq != sm.sequence)
1094 results.appendResult(sm.sequence, indexpos, indexpos);
1095 lastipos = indexpos;
1096 lastseq = sm.sequence;
1097 // construct highlighted sequence list
1098 for (AlignedCodonFrame acf : seqmappings)
1100 acf.markMappedRegion(sm.sequence, indexpos, results);
1110 * highlight regions associated with a position (indexpos) in seq
1113 * the sequence that the mouse over occurred on
1115 * the absolute position being mouseovered in seq (0 to seq.length())
1117 * the sequence position (if -1, seq.findPosition is called to
1118 * resolve the residue number)
1120 public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos,
1121 VamsasSource source)
1123 boolean hasSequenceListeners = handlingVamsasMo
1124 || !seqmappings.isEmpty();
1125 SearchResultsI results = null;
1128 seqPos = seq.findPosition(indexpos);
1130 for (int i = 0; i < listeners.size(); i++)
1132 Object listener = listeners.elementAt(i);
1133 if (listener == source)
1135 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1136 // Temporary fudge with SequenceListener.getVamsasSource()
1139 if (listener instanceof StructureListener)
1141 highlightStructure((StructureListener) listener, seq, seqPos);
1145 if (listener instanceof SequenceListener)
1147 final SequenceListener seqListener = (SequenceListener) listener;
1148 if (hasSequenceListeners
1149 && seqListener.getVamsasSource() != source)
1151 if (relaySeqMappings)
1153 if (results == null)
1155 results = MappingUtils.buildSearchResults(seq, seqPos,
1158 if (handlingVamsasMo)
1160 results.addResult(seq, seqPos, seqPos);
1163 if (!results.isEmpty())
1165 seqListener.highlightSequence(results);
1170 else if (listener instanceof VamsasListener && !handlingVamsasMo)
1172 ((VamsasListener) listener).mouseOverSequence(seq, indexpos,
1175 else if (listener instanceof SecondaryStructureListener)
1177 ((SecondaryStructureListener) listener).mouseOverSequence(seq,
1185 * Send suitable messages to a StructureListener to highlight atoms
1186 * corresponding to the given sequence position(s)
1192 public void highlightStructure(StructureListener sl, SequenceI seq,
1195 if (!sl.isListeningFor(seq))
1200 List<AtomSpec> atoms = new ArrayList<>();
1201 for (StructureMapping sm : mappings)
1203 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1204 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1205 .getDatasetSequence() == seq.getDatasetSequence()))
1207 for (int index : positions)
1209 atomNo = sm.getAtomNum(index);
1213 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1214 sm.getPDBResNum(index), atomNo));
1219 sl.highlightAtoms(atoms);
1222 public void highlightStructureRegionsFor(StructureListener sl,
1223 SequenceI[] seqs, int... columns)
1225 List<SequenceI> to_highlight = new ArrayList<SequenceI>();
1226 for (SequenceI seq : seqs)
1228 if (sl.isListeningFor(seq))
1230 to_highlight.add(seq);
1233 if (to_highlight.size() == 0)
1237 List<AtomSpec> atoms = new ArrayList<>();
1238 for (SequenceI seq : to_highlight)
1241 for (StructureMapping sm : mappings)
1243 if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence()
1244 || (sm.sequence.getDatasetSequence() != null && sm.sequence
1245 .getDatasetSequence() == seq.getDatasetSequence()))
1248 for (int i = 0; i < columns.length; i += 2)
1250 ContiguousI positions = seq.findPositions(columns[i] + 1,
1251 columns[i + 1] + 1);
1252 if (positions == null)
1256 for (int index = positions.getBegin(); index <= positions
1260 atomNo = sm.getAtomNum(index);
1264 atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain,
1265 sm.getPDBResNum(index), atomNo));
1271 if (atoms.size() > 0)
1273 sl.highlightAtoms(atoms);
1279 * true if a mouse over event from an external (ie Vamsas) source is being
1282 boolean handlingVamsasMo = false;
1287 * as mouseOverSequence but only route event to SequenceListeners
1291 * in an alignment sequence
1293 public void mouseOverVamsasSequence(SequenceI sequenceI, int position,
1294 VamsasSource source)
1296 handlingVamsasMo = true;
1297 long msg = sequenceI.hashCode() * (1 + position);
1301 mouseOverSequence(sequenceI, position, -1, source);
1303 handlingVamsasMo = false;
1306 public Annotation[] colourSequenceFromStructure(SequenceI seq,
1310 // THIS WILL NOT BE AVAILABLE IN JALVIEW 2.3,
1311 // UNTIL THE COLOUR BY ANNOTATION IS REWORKED
1313 * Annotation [] annotations = new Annotation[seq.getLength()];
1315 * StructureListener sl; int atomNo = 0; for (int i = 0; i <
1316 * listeners.size(); i++) { if (listeners.elementAt(i) instanceof
1317 * StructureListener) { sl = (StructureListener) listeners.elementAt(i);
1319 * for (int j = 0; j < mappings.length; j++) {
1321 * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid)
1322 * && mappings[j].pdbfile.equals(sl.getPdbFile())) {
1323 * jalview.bin.Console.outPrintln(pdbid+" "+mappings[j].getPdbId() +"
1324 * "+mappings[j].pdbfile);
1326 * java.awt.Color col; for(int index=0; index<seq.getLength(); index++) {
1327 * if(jalview.util.Comparison.isGap(seq.getCharAt(index))) continue;
1329 * atomNo = mappings[j].getAtomNum(seq.findPosition(index)); col =
1330 * java.awt.Color.white; if (atomNo > 0) { col = sl.getColour(atomNo,
1331 * mappings[j].getPDBResNum(index), mappings[j].pdbchain,
1332 * mappings[j].pdbfile); }
1334 * annotations[index] = new Annotation("X",null,' ',0,col); } return
1335 * annotations; } } } }
1337 * return annotations;
1341 public void structureSelectionChanged()
1345 public void sequenceSelectionChanged()
1349 public void sequenceColoursChanged(Object source)
1351 StructureListener sl;
1352 for (int i = 0; i < listeners.size(); i++)
1354 if (listeners.elementAt(i) instanceof StructureListener)
1356 sl = (StructureListener) listeners.elementAt(i);
1357 sl.updateColours(source);
1362 public StructureMapping[] getMapping(String pdbfile)
1364 List<StructureMapping> tmp = new ArrayList<>();
1365 for (StructureMapping sm : mappings)
1367 if (sm.pdbfile.equals(pdbfile))
1372 return tmp.toArray(new StructureMapping[tmp.size()]);
1376 * Returns a readable description of all mappings for the given pdbfile to any
1377 * of the given sequences
1383 public String printMappings(String pdbfile, List<SequenceI> seqs)
1385 if (pdbfile == null || seqs == null || seqs.isEmpty())
1390 StringBuilder sb = new StringBuilder(64);
1391 for (StructureMapping sm : mappings)
1393 if (Platform.pathEquals(sm.pdbfile, pdbfile)
1394 && seqs.contains(sm.sequence))
1396 sb.append(sm.mappingDetails);
1398 // separator makes it easier to read multiple mappings
1399 sb.append("=====================");
1405 return sb.toString();
1409 * Remove the given mapping
1413 public void deregisterMapping(AlignedCodonFrame acf)
1417 boolean removed = seqmappings.remove(acf);
1418 if (removed && seqmappings.isEmpty())
1420 jalview.bin.Console.outPrintln("All mappings removed");
1426 * Add each of the given codonFrames to the stored set, if not aready present.
1430 public void registerMappings(List<AlignedCodonFrame> mappings)
1432 if (mappings != null)
1434 for (AlignedCodonFrame acf : mappings)
1436 registerMapping(acf);
1442 * Add the given mapping to the stored set, unless already stored.
1444 public void registerMapping(AlignedCodonFrame acf)
1448 if (!seqmappings.contains(acf))
1450 seqmappings.add(acf);
1456 * Resets this object to its initial state by removing all registered
1457 * listeners, codon mappings, PDB file mappings
1459 public void resetAll()
1461 if (mappings != null)
1465 if (seqmappings != null)
1467 seqmappings.clear();
1469 if (sel_listeners != null)
1471 sel_listeners.clear();
1473 if (listeners != null)
1477 if (commandListeners != null)
1479 commandListeners.clear();
1481 if (view_listeners != null)
1483 view_listeners.clear();
1485 if (pdbFileNameId != null)
1487 pdbFileNameId.clear();
1489 if (pdbIdFileName != null)
1491 pdbIdFileName.clear();
1495 public void addSelectionListener(SelectionListener selecter)
1497 if (!sel_listeners.contains(selecter))
1499 sel_listeners.add(selecter);
1503 public void removeSelectionListener(SelectionListener toremove)
1505 if (sel_listeners.contains(toremove))
1507 sel_listeners.remove(toremove);
1511 public synchronized void sendSelection(
1512 jalview.datamodel.SequenceGroup selection,
1513 jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden,
1514 SelectionSource source)
1516 for (SelectionListener slis : sel_listeners)
1520 slis.selection(selection, colsel, hidden, source);
1525 Vector<AlignmentViewPanelListener> view_listeners = new Vector<>();
1527 public synchronized void sendViewPosition(
1528 jalview.api.AlignmentViewPanel source, int startRes, int endRes,
1529 int startSeq, int endSeq)
1532 if (view_listeners != null && view_listeners.size() > 0)
1534 Enumeration<AlignmentViewPanelListener> listeners = view_listeners
1536 while (listeners.hasMoreElements())
1538 AlignmentViewPanelListener slis = listeners.nextElement();
1541 slis.viewPosition(startRes, endRes, startSeq, endSeq, source);
1549 * release all references associated with this manager provider
1551 * @param jalviewLite
1553 public static void release(StructureSelectionManagerProvider jalviewLite)
1555 // synchronized (instances)
1557 if (instances == null)
1561 StructureSelectionManager mnger = (instances.get(jalviewLite));
1564 instances.remove(jalviewLite);
1567 /* bsoares 2019-03-20 finalize deprecated, no apparent external
1568 * resources to close
1570 // mnger.finalize();
1571 } catch (Throwable x)
1578 public void registerPDBEntry(PDBEntry pdbentry)
1580 if (pdbentry.getFile() != null
1581 && pdbentry.getFile().trim().length() > 0)
1583 registerPDBFile(pdbentry.getId(), pdbentry.getFile());
1587 public void addCommandListener(CommandListener cl)
1589 if (!commandListeners.contains(cl))
1591 commandListeners.add(cl);
1595 public boolean hasCommandListener(CommandListener cl)
1597 return this.commandListeners.contains(cl);
1600 public boolean removeCommandListener(CommandListener l)
1602 return commandListeners.remove(l);
1606 * Forward a command to any command listeners (except for the command's
1610 * the command to be broadcast (in its form after being performed)
1612 * if true, the command was being 'undone'
1615 public void commandPerformed(CommandI command, boolean undo,
1616 VamsasSource source)
1618 for (CommandListener listener : commandListeners)
1620 listener.mirrorCommand(command, undo, this, source);
1625 * Returns a new CommandI representing the given command as mapped to the
1626 * given sequences. If no mapping could be made, or the command is not of a
1627 * mappable kind, returns null.
1635 public CommandI mapCommand(CommandI command, boolean undo,
1636 final AlignmentI mapTo, char gapChar)
1638 if (command instanceof EditCommand)
1640 return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo,
1641 gapChar, seqmappings);
1643 else if (command instanceof OrderCommand)
1645 return MappingUtils.mapOrderCommand((OrderCommand) command, undo,
1646 mapTo, seqmappings);
1651 public List<AlignedCodonFrame> getSequenceMappings()
1657 * quick and dirty route to just highlight all structure positions for a range
1660 * @param sequencesArray
1662 * start-end columns on sequencesArray
1664 * origin parent AlignmentPanel
1666 public void highlightPositionsOnMany(SequenceI[] sequencesArray, int[] is,
1669 for (int i = 0; i < listeners.size(); i++)
1671 Object listener = listeners.elementAt(i);
1672 if (listener == source)
1674 // TODO listener (e.g. SeqPanel) is never == source (AlignViewport)
1675 // Temporary fudge with SequenceListener.getVamsasSource()
1678 if (listener instanceof StructureListener)
1680 highlightStructureRegionsFor((StructureListener) listener,
1681 sequencesArray, is);
1686 public Map<String, String> getPdbFileNameIdMap()
1688 return pdbFileNameId;
1691 public Map<String, String> getPdbIdFileNameMap()
1693 return pdbIdFileName;