X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fstructure%2FStructureSelectionManager.java;h=2f1ddc07533637b08921013383d27f639ec12612;hb=6785b0c3d7bdc780e0ca1737082b82374eaf8335;hp=e1aa9dc8970945f3568d06682a149c2821eb50da;hpb=2026a420963f094072607c0495b6369ba96e60c0;p=jalview.git diff --git a/src/jalview/structure/StructureSelectionManager.java b/src/jalview/structure/StructureSelectionManager.java index e1aa9dc..2f1ddc0 100644 --- a/src/jalview/structure/StructureSelectionManager.java +++ b/src/jalview/structure/StructureSelectionManager.java @@ -1,64 +1,245 @@ /* - * Jalview - A Sequence Alignment Editor and Viewer (Version 2.7) - * Copyright (C) 2011 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle + * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$) + * Copyright (C) $$Year-Rel$$ The Jalview Authors * * This file is part of Jalview. * * Jalview is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. - * + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * * Jalview is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty * of MERCHANTABILITY or FITNESS FOR A PARTICULAR * PURPOSE. See the GNU General Public License for more details. * - * You should have received a copy of the GNU General Public License along with Jalview. If not, see . + * You should have received a copy of the GNU General Public License + * along with Jalview. If not, see . + * The Jalview Authors are detailed in the 'AUTHORS' file. */ package jalview.structure; -import java.io.*; -import java.util.*; - -import MCview.*; -import jalview.analysis.*; -import jalview.api.AlignmentViewPanel; +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Vector; + +import jalview.analysis.AlignSeq; import jalview.api.StructureSelectionManagerProvider; -import jalview.datamodel.*; +import jalview.bin.Console; +import jalview.commands.CommandI; +import jalview.commands.EditCommand; +import jalview.commands.OrderCommand; +import jalview.datamodel.AlignedCodonFrame; +import jalview.datamodel.AlignmentAnnotation; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.Annotation; +import jalview.datamodel.ContiguousI; +import jalview.datamodel.HiddenColumns; +import jalview.datamodel.PDBEntry; +import jalview.datamodel.SearchResults; +import jalview.datamodel.SearchResultsI; +import jalview.datamodel.SequenceI; +import jalview.ext.jmol.JmolParser; +import jalview.gui.IProgressIndicator; +import jalview.io.AppletFormatAdapter; +import jalview.io.DataSourceType; +import jalview.io.StructureFile; +import jalview.structure.StructureImportSettings.TFType; +import jalview.util.MappingUtils; +import jalview.util.MessageManager; +import jalview.util.Platform; +import jalview.ws.sifts.SiftsClient; +import jalview.ws.sifts.SiftsException; +import jalview.ws.sifts.SiftsSettings; +import mc_view.Atom; +import mc_view.PDBChain; +import mc_view.PDBfile; public class StructureSelectionManager { - static IdentityHashMap instances; + public final static String NEWLINE = System.lineSeparator(); + + static IdentityHashMap instances; + + private List mappings = new ArrayList<>(); + + private boolean processSecondaryStructure = false; + + private boolean secStructServices = false; + + private boolean addTempFacAnnot = false; + + /* + * Set of any registered mappings between (dataset) sequences. + */ + private List seqmappings = new ArrayList<>(); + + private List commandListeners = new ArrayList<>(); + + private List sel_listeners = new ArrayList<>(); + + /** + * @return true if will try to use external services for processing secondary + * structure + */ + public boolean isSecStructServices() + { + return secStructServices; + } + + /** + * control use of external services for processing secondary structure + * + * @param secStructServices + */ + public void setSecStructServices(boolean secStructServices) + { + this.secStructServices = secStructServices; + } + + /** + * flag controlling addition of any kind of structural annotation + * + * @return true if temperature factor annotation will be added + */ + public boolean isAddTempFacAnnot() + { + return addTempFacAnnot; + } + + /** + * set flag controlling addition of structural annotation + * + * @param addTempFacAnnot + */ + public void setAddTempFacAnnot(boolean addTempFacAnnot) + { + this.addTempFacAnnot = addTempFacAnnot; + } + + /** + * + * @return if true, the structure manager will attempt to add secondary + * structure lines for unannotated sequences + */ + + public boolean isProcessSecondaryStructure() + { + return processSecondaryStructure; + } - StructureMapping[] mappings; + /** + * Control whether structure manager will try to annotate mapped sequences + * with secondary structure from PDB data. + * + * @param enable + */ + public void setProcessSecondaryStructure(boolean enable) + { + processSecondaryStructure = enable; + } /** * debug function - write all mappings to stdout */ - public void reportMapping() { - if (mappings==null) - { - System.err.println("reportMapping: No PDB/Sequence mappings."); - }else{ - System.err.println("reportMapping: There are "+mappings.length+" mappings."); - for (int m=0;m pdbIdFileName = new HashMap<>(); + + Map pdbFileNameId = new HashMap<>(); + + public void registerPDBFile(String idForFile, String absoluteFile) + { + pdbIdFileName.put(idForFile, absoluteFile); + pdbFileNameId.put(absoluteFile, idForFile); + } + + public String findIdForPDBFile(String idOrFile) + { + String id = pdbFileNameId.get(idOrFile); + return id; + } + + public String findFileForPDBId(String idOrFile) + { + String id = pdbIdFileName.get(idOrFile); + return id; + } + + public boolean isPDBFileRegistered(String idOrFile) + { + return pdbFileNameId.containsKey(idOrFile) + || pdbIdFileName.containsKey(idOrFile); + } + + private static StructureSelectionManager nullProvider = null; + + public static StructureSelectionManager getStructureSelectionManager( + StructureSelectionManagerProvider context) { + if (context == null) + { + if (nullProvider == null) + { + if (instances != null) + { + throw new Error(MessageManager.getString( + "error.implementation_error_structure_selection_manager_null"), + new NullPointerException(MessageManager + .getString("exception.ssm_context_is_null"))); + } + else + { + nullProvider = new StructureSelectionManager(); + } + return nullProvider; + } + } if (instances == null) { - instances = new java.util.IdentityHashMap(); + instances = new java.util.IdentityHashMap<>(); } - StructureSelectionManager instance=instances.get(context); - if (instance==null) + StructureSelectionManager instance = instances.get(context); + if (instance == null) { - instances.put(context, instance=new StructureSelectionManager()); + if (nullProvider != null) + { + instance = nullProvider; + } + else + { + instance = new StructureSelectionManager(); + } + instances.put(context, instance); } return instance; } @@ -96,6 +277,7 @@ public class StructureSelectionManager /** * register a listener for alignment sequence mouseover events + * * @param svl */ public void addStructureViewerListener(Object svl) @@ -106,24 +288,28 @@ public class StructureSelectionManager } } + /** + * Returns the filename the PDB id is already mapped to if known, or null if + * it is not mapped + * + * @param pdbid + * @return + */ public String alreadyMappedToFile(String pdbid) { - if (mappings != null) + for (StructureMapping sm : mappings) { - for (int i = 0; i < mappings.length; i++) + if (sm.getPdbId().equalsIgnoreCase(pdbid)) { - if (mappings[i].getPdbId().equals(pdbid)) - { - return mappings[i].pdbfile; - } + return sm.pdbfile; } } return null; } /** - * create sequence structure mappings between each sequence and the given - * pdbFile (retrieved via the given protocol). + * Import structure data and register a structure mapping for broadcasting + * colouring, mouseovers and selection events (convenience wrapper). * * @param sequence * - one or more sequences to be mapped to pdbFile @@ -136,75 +322,248 @@ public class StructureSelectionManager * - how to resolve data from resource * @return null or the structure data parsed as a pdb file */ - synchronized public MCview.PDBfile setMapping(SequenceI[] sequence, - String[] targetChains, String pdbFile, String protocol) + synchronized public StructureFile setMapping(SequenceI[] sequence, + String[] targetChains, String pdbFile, DataSourceType protocol, + IProgressIndicator progress) { - /* - * There will be better ways of doing this in the future, for now we'll use - * the tried and tested MCview pdb mapping + return computeMapping(true, sequence, targetChains, pdbFile, protocol, + progress, null, null, true); + } + + /** + * Import a single structure file and register sequence structure mappings for + * broadcasting colouring, mouseovers and selection events (convenience + * wrapper). + * + * @param forStructureView + * when true, record the mapping for use in mouseOvers + * @param sequence + * - one or more sequences to be mapped to pdbFile + * @param targetChains + * - optional chain specification for mapping each sequence to pdb + * (may be nill, individual elements may be nill) + * @param pdbFile + * - structure data resource + * @param protocol + * - how to resolve data from resource + * @return null or the structure data parsed as a pdb file + */ + synchronized public StructureFile setMapping(boolean forStructureView, + SequenceI[] sequenceArray, String[] targetChainIds, + String pdbFile, DataSourceType sourceType, TFType tft, + String paeFilename) + { + return setMapping(forStructureView, sequenceArray, targetChainIds, + pdbFile, sourceType, tft, paeFilename, true); + } + + + /** + * create sequence structure mappings between each sequence and the given + * pdbFile (retrieved via the given protocol). Either constructs a mapping + * using NW alignment or derives one from any available SIFTS mapping data. + * + * @param forStructureView + * when true, record the mapping for use in mouseOvers + * + * @param sequenceArray + * - one or more sequences to be mapped to pdbFile + * @param targetChainIds + * - optional chain specification for mapping each sequence to pdb + * (may be nill, individual elements may be nill) - JBPNote: JAL-2693 + * - this should be List>, empty lists indicate no + * predefined mappings + * @param pdbFile + * - structure data resource + * @param sourceType + * - how to resolve data from resource + * @param tft - specify how to interpret the temperature factor column in the atom data + * @param paeFilename - when not null, specifies a filename containing a matrix formatted in JSON using one of the known PAE formats + * @param doXferSettings - when true, transfer annotation to mapped sequences in sequenceArray + * @return null or the structure data parsed as a pdb file + */ + synchronized public StructureFile setMapping(boolean forStructureView, + SequenceI[] sequenceArray, String[] targetChainIds, + String pdbFile, DataSourceType sourceType, TFType tft, + String paeFilename, boolean doXferSettings) + { + return computeMapping(forStructureView, sequenceArray, targetChainIds, + pdbFile, sourceType, null, tft, paeFilename, doXferSettings); + } + + /** + * create sequence structure mappings between each sequence and the given + * pdbFile (retrieved via the given protocol). Either constructs a mapping + * using NW alignment or derives one from any available SIFTS mapping data. + * + * @param forStructureView + * when true, record the mapping for use in mouseOvers + * + * @param sequenceArray + * - one or more sequences to be mapped to pdbFile + * @param targetChainIds + * - optional chain specification for mapping each sequence to pdb + * (may be nill, individual elements may be nill) - JBPNote: JAL-2693 + * - this should be List>, empty lists indicate no + * predefined mappings + * @param pdbFile + * - structure data resource + * @param sourceType + * - how to resolve data from resource + * @param IProgressIndicator + * reference to UI component that maintains a progress bar for the + * mapping operation + * @param tft - specify how to interpret the temperature factor column in the atom data + * @param paeFilename - when not null, specifies a filename containing a matrix formatted in JSON using one of the known PAE formats + * @param doXferSettings - when true, transfer annotation to mapped sequences in sequenceArray + * @return null or the structure data parsed as a pdb file + */ + synchronized public StructureFile computeMapping(boolean forStructureView, + SequenceI[] sequenceArray, String[] targetChainIds, + String pdbFile, DataSourceType sourceType, + IProgressIndicator progress, TFType tft, String paeFilename, + boolean doXferSettings) + { + long progressSessionId = System.currentTimeMillis() * 3; + + /** + * do we extract and transfer annotation from 3D data ? */ - MCview.PDBfile pdb = null; + // FIXME: possibly should just delete + + boolean parseSecStr = processSecondaryStructure + && !isStructureFileProcessed(pdbFile, sequenceArray); + + StructureFile pdb = null; + boolean isMapUsingSIFTs = SiftsSettings.isMapWithSifts(); try { - pdb = new MCview.PDBfile(pdbFile, protocol); + // FIXME if sourceType is not null, we've lost data here + sourceType = AppletFormatAdapter.checkProtocol(pdbFile); + pdb = new JmolParser(false, pdbFile, sourceType); + if (paeFilename != null) + { + pdb.setPAEMatrix(paeFilename); + } + pdb.setTemperatureFactorType(tft); + pdb.addSettings(parseSecStr && processSecondaryStructure, + parseSecStr && addTempFacAnnot, + parseSecStr && secStructServices); + // save doXferSettings and reset after doParse() + boolean temp = pdb.getDoXferSettings(); + pdb.setDoXferSettings(doXferSettings); + pdb.doParse(); + pdb.setDoXferSettings(temp); + if (pdb.getId() != null && pdb.getId().trim().length() > 0 + && DataSourceType.FILE == sourceType) + { + registerPDBFile(pdb.getId().trim(), pdbFile); + } + // if PDBId is unavailable then skip SIFTS mapping execution path + // TODO: JAL-3868 need to know if structure is actually from + // PDB (has valid PDB ID and has provenance suggesting it + // actually came from PDB) + boolean isProtein = false; + for (SequenceI s : sequenceArray) + { + if (s.isProtein()) + { + isProtein = true; + break; + } + } + isMapUsingSIFTs = isMapUsingSIFTs && pdb.isPPDBIdAvailable() + && !pdb.getId().startsWith("AF-") && isProtein; + } catch (Exception ex) { ex.printStackTrace(); return null; } - - String targetChain; - for (int s = 0; s < sequence.length; s++) + /* + * sifts client - non null if SIFTS mappings are to be used + */ + SiftsClient siftsClient = null; + try + { + if (isMapUsingSIFTs) + { + siftsClient = new SiftsClient(pdb); + } + } catch (SiftsException e) + { + isMapUsingSIFTs = false; + Console.error("SIFTS mapping failed", e); + Console.error("Falling back on Needleman & Wunsch alignment"); + siftsClient = null; + } + + String targetChainId; + for (int s = 0; s < sequenceArray.length; s++) { boolean infChain = true; - if (targetChains != null && targetChains[s] != null) + final SequenceI seq = sequenceArray[s]; + SequenceI ds = seq; + while (ds.getDatasetSequence() != null) + { + ds = ds.getDatasetSequence(); + } + + if (targetChainIds != null && targetChainIds[s] != null) { infChain = false; - targetChain = targetChains[s]; + targetChainId = targetChainIds[s]; } - else if (sequence[s].getName().indexOf("|") > -1) + else if (seq.getName().indexOf("|") > -1) { - targetChain = sequence[s].getName().substring( - sequence[s].getName().lastIndexOf("|") + 1); - if (targetChain.length() > 1) + targetChainId = seq.getName() + .substring(seq.getName().lastIndexOf("|") + 1); + if (targetChainId.length() > 1) { - if (targetChain.trim().length() == 0) + if (targetChainId.trim().length() == 0) { - targetChain = " "; + targetChainId = " "; } else { // not a valid chain identifier - targetChain = ""; + targetChainId = ""; } } } else - targetChain = ""; + { + targetChainId = ""; + } - int max = -10; + /* + * Attempt pairwise alignment of the sequence with each chain in the PDB, + * and remember the highest scoring chain + */ + float max = -10; AlignSeq maxAlignseq = null; String maxChainId = " "; PDBChain maxChain = null; boolean first = true; - for (int i = 0; i < pdb.chains.size(); i++) + for (PDBChain chain : pdb.getChains()) { - PDBChain chain = ((PDBChain) pdb.chains.elementAt(i)); - if (targetChain.length() > 0 && !targetChain.equals(chain.id) && !infChain) + if (targetChainId.length() > 0 && !targetChainId.equals(chain.id) + && !infChain) { continue; // don't try to map chains don't match. } // TODO: correctly determine sequence type for mixed na/peptide // structures - AlignSeq as = new AlignSeq(sequence[s], - ((PDBChain) pdb.chains.elementAt(i)).sequence, - ((PDBChain) pdb.chains.elementAt(i)).isNa ? AlignSeq.DNA - : AlignSeq.PEP); - as.calcScoreMatrix(); - as.traceAlignment(); + final String type = chain.isNa ? AlignSeq.DNA : AlignSeq.PEP; + AlignSeq as = AlignSeq.doGlobalNWAlignment(seq, chain.sequence, + type); + // equivalent to: + // AlignSeq as = new AlignSeq(sequence[s], chain.sequence, type); + // as.calcScoreMatrix(); + // as.traceAlignment(); if (first || as.maxscore > max - || (as.maxscore == max && chain.id.equals(targetChain))) + || (as.maxscore == max && chain.id.equals(targetChainId))) { first = false; maxChain = chain; @@ -217,302 +576,661 @@ public class StructureSelectionManager { continue; } - final StringBuffer mappingDetails = new StringBuffer(); - mappingDetails.append("\n\nPDB Sequence is :\nSequence = " - + maxChain.sequence.getSequenceAsString()); - mappingDetails.append("\nNo of residues = " - + maxChain.residues.size() + "\n\n"); - PrintStream ps = new PrintStream(System.out) + + if (sourceType == DataSourceType.PASTE) + { + pdbFile = "INLINE" + pdb.getId(); + } + + List seqToStrucMapping = new ArrayList<>(); + if (isMapUsingSIFTs && seq.isProtein()) + { + if (progress != null) + { + progress.setProgressBar( + MessageManager + .getString("status.obtaining_mapping_with_sifts"), + progressSessionId); + } + jalview.datamodel.Mapping sqmpping = maxAlignseq + .getMappingFromS1(false); + if (targetChainId != null && !targetChainId.trim().isEmpty()) + { + StructureMapping siftsMapping; + try + { + siftsMapping = getStructureMapping(seq, pdbFile, targetChainId, + pdb, maxChain, sqmpping, maxAlignseq, siftsClient); + seqToStrucMapping.add(siftsMapping); + maxChain.makeExactMapping(siftsMapping, seq); + maxChain.transferRESNUMFeatures(seq, "IEA: SIFTS", + pdb.getId().toLowerCase(Locale.ROOT)); + maxChain.transferResidueAnnotation(siftsMapping, null); + ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0)); + + } catch (SiftsException e) + { + // fall back to NW alignment + Console.error(e.getMessage()); + StructureMapping nwMapping = getNWMappings(seq, pdbFile, + targetChainId, maxChain, pdb, maxAlignseq); + seqToStrucMapping.add(nwMapping); + maxChain.makeExactMapping(maxAlignseq, seq); + maxChain.transferRESNUMFeatures(seq, "IEA:Jalview", + pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is + // this + // "IEA:Jalview" ? + maxChain.transferResidueAnnotation(nwMapping, sqmpping); + ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0)); + } + } + else + { + List foundSiftsMappings = new ArrayList<>(); + for (PDBChain chain : pdb.getChains()) + { + StructureMapping siftsMapping = null; + try + { + siftsMapping = getStructureMapping(seq, pdbFile, chain.id, + pdb, chain, sqmpping, maxAlignseq, siftsClient); + foundSiftsMappings.add(siftsMapping); + chain.makeExactMapping(siftsMapping, seq); + chain.transferRESNUMFeatures(seq, "IEA: SIFTS", + pdb.getId().toLowerCase(Locale.ROOT));// FIXME: is this + // "IEA:SIFTS" ? + chain.transferResidueAnnotation(siftsMapping, null); + } catch (SiftsException e) + { + jalview.bin.Console.errPrintln(e.getMessage()); + } catch (Exception e) + { + jalview.bin.Console.errPrintln( + "Unexpected exception during SIFTS mapping - falling back to NW for this sequence/structure pair"); + jalview.bin.Console.errPrintln(e.getMessage()); + } + } + if (!foundSiftsMappings.isEmpty()) + { + seqToStrucMapping.addAll(foundSiftsMappings); + ds.addPDBId(sqmpping.getTo().getAllPDBEntries().get(0)); + } + else + { + StructureMapping nwMapping = getNWMappings(seq, pdbFile, + maxChainId, maxChain, pdb, maxAlignseq); + seqToStrucMapping.add(nwMapping); + maxChain.transferRESNUMFeatures(seq, null, + pdb.getId().toLowerCase(Locale.ROOT)); // FIXME: is this + // "IEA:Jalview" ? + maxChain.transferResidueAnnotation(nwMapping, sqmpping); + ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0)); + } + } + } + else + { + if (progress != null) + { + progress.setProgressBar( + MessageManager.getString( + "status.obtaining_mapping_with_nw_alignment"), + progressSessionId); + } + StructureMapping nwMapping = getNWMappings(seq, pdbFile, maxChainId, + maxChain, pdb, maxAlignseq); + seqToStrucMapping.add(nwMapping); + ds.addPDBId(maxChain.sequence.getAllPDBEntries().get(0)); + } + if (forStructureView) { - public void print(String x) + for (StructureMapping sm : seqToStrucMapping) { - mappingDetails.append(x); + addStructureMapping(sm); // not addAll! } + } + if (progress != null) + { + progress.setProgressBar(null, progressSessionId); + } + } + return pdb; + } - public void println() + /** + * check if we need to extract secondary structure from given pdbFile and + * transfer to sequences + * + * @param pdbFile + * @param sequenceArray + * @return + */ + private boolean isStructureFileProcessed(String pdbFile, + SequenceI[] sequenceArray) + { + boolean processed = false; + if (isPDBFileRegistered(pdbFile)) + { + for (SequenceI sq : sequenceArray) + { + SequenceI ds = sq; + while (ds.getDatasetSequence() != null) { - mappingDetails.append("\n"); + ds = ds.getDatasetSequence(); + } + ; + if (ds.getAnnotation() != null) + { + for (AlignmentAnnotation ala : ds.getAnnotation()) + { + // false if any annotation present from this structure + // JBPNote this fails for jmol/chimera view because the *file* is + // passed, not the structure data ID - + if (PDBfile.isCalcIdForFile(ala, findIdForPDBFile(pdbFile))) + { + processed = true; + } + } } - }; + } + } + return processed; + } - maxAlignseq.printAlignment(ps); + public void addStructureMapping(StructureMapping sm) + { + if (!mappings.contains(sm)) + { + mappings.add(sm); + } + } - mappingDetails.append("\nPDB start/end " + maxAlignseq.seq2start - + " " + maxAlignseq.seq2end); - mappingDetails.append("\nSEQ start/end " - + (maxAlignseq.seq1start + sequence[s].getStart() - 1) + " " - + (maxAlignseq.seq1end + sequence[s].getEnd() - 1)); + /** + * retrieve a mapping for seq from SIFTs using associated DBRefEntry for + * uniprot or PDB + * + * @param seq + * @param pdbFile + * @param targetChainId + * @param pdb + * @param maxChain + * @param sqmpping + * @param maxAlignseq + * @param siftsClient + * client for retrieval of SIFTS mappings for this structure + * @return + * @throws SiftsException + */ + private StructureMapping getStructureMapping(SequenceI seq, + String pdbFile, String targetChainId, StructureFile pdb, + PDBChain maxChain, jalview.datamodel.Mapping sqmpping, + AlignSeq maxAlignseq, SiftsClient siftsClient) + throws SiftsException + { + StructureMapping curChainMapping = siftsClient + .getSiftsStructureMapping(seq, pdbFile, targetChainId); + try + { + PDBChain chain = pdb.findChain(targetChainId); + if (chain != null) + { + chain.transferResidueAnnotation(curChainMapping, null); + } + } catch (Exception e) + { + e.printStackTrace(); + } + return curChainMapping; + } - maxChain.makeExactMapping(maxAlignseq, sequence[s]); + private StructureMapping getNWMappings(SequenceI seq, String pdbFile, + String maxChainId, PDBChain maxChain, StructureFile pdb, + AlignSeq maxAlignseq) + { + final StringBuilder mappingDetails = new StringBuilder(128); + mappingDetails.append(NEWLINE) + .append("Sequence \u27f7 Structure mapping details"); + mappingDetails.append(NEWLINE); + mappingDetails + .append("Method: inferred with Needleman & Wunsch alignment"); + mappingDetails.append(NEWLINE).append("PDB Sequence is :") + .append(NEWLINE).append("Sequence = ") + .append(maxChain.sequence.getSequenceAsString()); + mappingDetails.append(NEWLINE).append("No of residues = ") + .append(maxChain.residues.size()).append(NEWLINE) + .append(NEWLINE); + PrintStream ps = new PrintStream(System.out) + { + @Override + public void print(String x) + { + mappingDetails.append(x); + } + + @Override + public void println() + { + mappingDetails.append(NEWLINE); + } + }; + + maxAlignseq.printAlignment(ps); + + mappingDetails.append(NEWLINE).append("PDB start/end "); + mappingDetails.append(String.valueOf(maxAlignseq.seq2start)) + .append(" "); + mappingDetails.append(String.valueOf(maxAlignseq.seq2end)); + mappingDetails.append(NEWLINE).append("SEQ start/end "); + mappingDetails + .append(String + .valueOf(maxAlignseq.seq1start + (seq.getStart() - 1))) + .append(" "); + mappingDetails.append( + String.valueOf(maxAlignseq.seq1end + (seq.getStart() - 1))); + mappingDetails.append(NEWLINE); + maxChain.makeExactMapping(maxAlignseq, seq); + jalview.datamodel.Mapping sqmpping = maxAlignseq + .getMappingFromS1(false); + maxChain.transferRESNUMFeatures(seq, null, + pdb.getId().toLowerCase(Locale.ROOT)); + + HashMap mapping = new HashMap<>(); + int resNum = -10000; + int index = 0; + char insCode = ' '; + + do + { + Atom tmp = maxChain.atoms.elementAt(index); + if ((resNum != tmp.resNumber || insCode != tmp.insCode) + && tmp.alignmentMapping != -1) + { + resNum = tmp.resNumber; + insCode = tmp.insCode; + if (tmp.alignmentMapping >= -1) + { + mapping.put(tmp.alignmentMapping + 1, + new int[] + { tmp.resNumber, tmp.atomIndex }); + } + } - maxChain.transferRESNUMFeatures(sequence[s], null); + index++; + } while (index < maxChain.atoms.size()); - // allocate enough slots to store the mapping from positions in - // sequence[s] to the associated chain - int[][] mapping = new int[sequence[s].findPosition(sequence[s].getLength()) + 2][2]; - int resNum = -10000; - int index = 0; + StructureMapping nwMapping = new StructureMapping(seq, pdbFile, + pdb.getId(), maxChainId, mapping, mappingDetails.toString()); + maxChain.transferResidueAnnotation(nwMapping, sqmpping); + return nwMapping; + } - do + public void removeStructureViewerListener(Object svl, String[] pdbfiles) + { + listeners.removeElement(svl); + if (svl instanceof SequenceListener) + { + for (int i = 0; i < listeners.size(); i++) { - Atom tmp = (Atom) maxChain.atoms.elementAt(index); - if (resNum != tmp.resNumber && tmp.alignmentMapping != -1) + if (listeners.elementAt(i) instanceof StructureListener) { - resNum = tmp.resNumber; - mapping[tmp.alignmentMapping + 1][0] = tmp.resNumber; - mapping[tmp.alignmentMapping + 1][1] = tmp.atomIndex; + ((StructureListener) listeners.elementAt(i)) + .releaseReferences(svl); } + } + } - index++; - } while (index < maxChain.atoms.size()); + if (pdbfiles == null) + { + return; + } + + /* + * Remove mappings to the closed listener's PDB files, but first check if + * another listener is still interested + */ + List pdbs = new ArrayList<>(Arrays.asList(pdbfiles)); - if (mappings == null) + StructureListener sl; + for (int i = 0; i < listeners.size(); i++) + { + if (listeners.elementAt(i) instanceof StructureListener) { - mappings = new StructureMapping[1]; + sl = (StructureListener) listeners.elementAt(i); + for (String pdbfile : sl.getStructureFiles()) + { + pdbs.remove(pdbfile); + } } - else + } + + /* + * Rebuild the mappings set, retaining only those which are for 'other' PDB + * files + */ + if (pdbs.size() > 0) + { + List tmp = new ArrayList<>(); + for (StructureMapping sm : mappings) { - StructureMapping[] tmp = new StructureMapping[mappings.length + 1]; - System.arraycopy(mappings, 0, tmp, 0, mappings.length); - mappings = tmp; + if (!pdbs.contains(sm.pdbfile)) + { + tmp.add(sm); + } } - if (protocol.equals(jalview.io.AppletFormatAdapter.PASTE)) - pdbFile = "INLINE" + pdb.id; + mappings = tmp; + } + } - mappings[mappings.length - 1] = new StructureMapping(sequence[s], - pdbFile, pdb.id, maxChainId, mapping, - mappingDetails.toString()); - maxChain.transferResidueAnnotation(mappings[mappings.length - 1]); + /** + * hack to highlight a range of positions at once on any structure views + * + * @param sequenceRef + * @param is + * - series of int start-end ranges as positions on sequenceRef + * @param i + * @param object + */ + public void highlightPositionsOn(SequenceI sequenceRef, int[][] is, + Object source) + { + boolean hasSequenceListeners = handlingVamsasMo + || !seqmappings.isEmpty(); + SearchResultsI results = null; + ArrayList listOfPositions = new ArrayList(); + for (int[] s_e : is) + { + for (int p = s_e[0]; p <= s_e[1]; listOfPositions.add(p++)) + ; + } + int seqpos[] = new int[listOfPositions.size()]; + int i = 0; + for (Integer p : listOfPositions) + { + seqpos[i++] = p; } - // /////// - return pdb; + for (i = 0; i < listeners.size(); i++) + { + Object listener = listeners.elementAt(i); + if (listener == source) + { + // TODO listener (e.g. SeqPanel) is never == source (AlignViewport) + // Temporary fudge with SequenceListener.getVamsasSource() + continue; + } + if (listener instanceof StructureListener) + { + highlightStructure((StructureListener) listener, sequenceRef, + seqpos); + } + + } } - public void removeStructureViewerListener(Object svl, String[] pdbfiles) + /** + * Propagate mouseover of a single position in a structure + * + * @param pdbResNum + * @param chain + * @param pdbfile + * @return + */ + public String mouseOverStructure(int pdbResNum, String chain, + String pdbfile) { - listeners.removeElement(svl); - if (svl instanceof SequenceListener) + AtomSpec atomSpec = new AtomSpec(pdbfile, chain, pdbResNum, 0); + List atoms = Collections.singletonList(atomSpec); + return mouseOverStructure(atoms); + } + + /** + * Propagate mouseover or selection of multiple positions in a structure + * + * @param atoms + */ + public String mouseOverStructure(List atoms) + { + if (listeners == null) { - for (int i=0;i 0 && mappings != null) + /** + * Constructs a SearchResults object holding regions (if any) in the Jalview + * alignment which have a mapping to the structure viewer positions in the + * supplied list + * + * @param atoms + * @return + */ + public SearchResultsI findAlignmentPositionsForStructurePositions( + List atoms) + { + SearchResultsI results = new SearchResults(); + for (AtomSpec atom : atoms) { - Vector tmp = new Vector(); - for (int i = 0; i < mappings.length; i++) + SequenceI lastseq = null; + int lastipos = -1; + for (StructureMapping sm : mappings) { - if (!pdbs.contains(mappings[i].pdbfile)) + if (sm.pdbfile.equals(atom.getPdbFile()) + && sm.pdbchain.equals(atom.getChain())) { - tmp.addElement(mappings[i]); + int indexpos = sm.getSeqPos(atom.getPdbResNum()); + if (lastipos != indexpos || lastseq != sm.sequence) + { + results.appendResult(sm.sequence, indexpos, indexpos); + lastipos = indexpos; + lastseq = sm.sequence; + // construct highlighted sequence list + for (AlignedCodonFrame acf : seqmappings) + { + acf.markMappedRegion(sm.sequence, indexpos, results); + } + } } } - - mappings = new StructureMapping[tmp.size()]; - tmp.copyInto(mappings); } + return results; } - public void mouseOverStructure(int pdbResNum, String chain, String pdbfile) + /** + * highlight regions associated with a position (indexpos) in seq + * + * @param seq + * the sequence that the mouse over occurred on + * @param indexpos + * the absolute position being mouseovered in seq (0 to seq.length()) + * @param seqPos + * the sequence position (if -1, seq.findPosition is called to + * resolve the residue number) + */ + public void mouseOverSequence(SequenceI seq, int indexpos, int seqPos, + VamsasSource source) { - if (listeners==null) + boolean hasSequenceListeners = handlingVamsasMo + || !seqmappings.isEmpty(); + SearchResultsI results = null; + if (seqPos == -1) { - // old or prematurely sent event - return; + seqPos = seq.findPosition(indexpos); } - boolean hasSequenceListeners = handlingVamsasMo || seqmappings != null; - SearchResults results = null; - SequenceI lastseq = null; - int lastipos = -1, indexpos; for (int i = 0; i < listeners.size(); i++) { - if (listeners.elementAt(i) instanceof SequenceListener) + Object listener = listeners.elementAt(i); + if (listener == source) { - if (results == null) - { - results = new SearchResults(); - } - if (mappings != null) + // TODO listener (e.g. SeqPanel) is never == source (AlignViewport) + // Temporary fudge with SequenceListener.getVamsasSource() + continue; + } + if (listener instanceof StructureListener) + { + highlightStructure((StructureListener) listener, seq, seqPos); + } + else + { + if (listener instanceof SequenceListener) { - for (int j = 0; j < mappings.length; j++) + final SequenceListener seqListener = (SequenceListener) listener; + if (hasSequenceListeners + && seqListener.getVamsasSource() != source) { - if (mappings[j].pdbfile.equals(pdbfile) - && mappings[j].pdbchain.equals(chain)) + if (relaySeqMappings) { - indexpos = mappings[j].getSeqPos(pdbResNum); - if (lastipos != indexpos && lastseq != mappings[j].sequence) + if (results == null) { - results.addResult(mappings[j].sequence, indexpos, indexpos); - lastipos = indexpos; - lastseq = mappings[j].sequence; - // construct highlighted sequence list - if (seqmappings != null) - { - - Enumeration e = seqmappings.elements(); - while (e.hasMoreElements()) - - { - ((AlignedCodonFrame) e.nextElement()).markMappedRegion( - mappings[j].sequence, indexpos, results); - } - } + results = MappingUtils.buildSearchResults(seq, seqPos, + seqmappings); } + if (handlingVamsasMo) + { + results.addResult(seq, seqPos, seqPos); + } + if (!results.isEmpty()) + { + seqListener.highlightSequence(results); + } } } } - } - } - if (results!=null) - { - for (int i = 0; i < listeners.size(); i++) - { - Object li = listeners.elementAt(i); - if (li instanceof SequenceListener) - ((SequenceListener) li).highlightSequence(results); + else if (listener instanceof VamsasListener && !handlingVamsasMo) + { + ((VamsasListener) listener).mouseOverSequence(seq, indexpos, + source); + } + else if (listener instanceof SecondaryStructureListener) + { + ((SecondaryStructureListener) listener).mouseOverSequence(seq, + indexpos, seqPos); + } } } } - Vector seqmappings = null; // should be a simpler list of mapped seuqence - /** - * highlight regions associated with a position (indexpos) in seq + * Send suitable messages to a StructureListener to highlight atoms + * corresponding to the given sequence position(s) * + * @param sl * @param seq - * the sequeence that the mouse over occured on - * @param indexpos - * the absolute position being mouseovered in seq (0 to seq.length()) - * @param index - * the sequence position (if -1, seq.findPosition is called to - * resolve the residue number) + * @param positions */ - public void mouseOverSequence(SequenceI seq, int indexpos, int index, - VamsasSource source) + public void highlightStructure(StructureListener sl, SequenceI seq, + int... positions) { - boolean hasSequenceListeners = handlingVamsasMo || seqmappings != null; - SearchResults results = null; - if (index == -1) - index = seq.findPosition(indexpos); - StructureListener sl; - int atomNo = 0; - for (int i = 0; i < listeners.size(); i++) + if (!sl.isListeningFor(seq)) { - if (listeners.elementAt(i) instanceof StructureListener) + return; + } + int atomNo; + List atoms = new ArrayList<>(); + for (StructureMapping sm : mappings) + { + if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence() + || (sm.sequence.getDatasetSequence() != null && sm.sequence + .getDatasetSequence() == seq.getDatasetSequence())) { - sl = (StructureListener) listeners.elementAt(i); - if (mappings == null) - { - continue; - } - for (int j = 0; j < mappings.length; j++) + for (int index : positions) { - if (mappings[j].sequence == seq - || mappings[j].sequence == seq.getDatasetSequence()) - { - atomNo = mappings[j].getAtomNum(index); + atomNo = sm.getAtomNum(index); - if (atomNo > 0) - { - sl.highlightAtom(atomNo, mappings[j].getPDBResNum(index), - mappings[j].pdbchain, mappings[j].pdbfile); - } + if (atomNo > 0) + { + atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain, + sm.getPDBResNum(index), atomNo)); } } } - else + } + sl.highlightAtoms(atoms); + } + + public void highlightStructureRegionsFor(StructureListener sl, + SequenceI[] seqs, int... columns) + { + List to_highlight = new ArrayList(); + for (SequenceI seq : seqs) + { + if (sl.isListeningFor(seq)) + { + to_highlight.add(seq); + } + } + if (to_highlight.size() == 0) + { + return; + } + List atoms = new ArrayList<>(); + for (SequenceI seq : to_highlight) + { + int atomNo; + for (StructureMapping sm : mappings) { - if (relaySeqMappings && hasSequenceListeners - && listeners.elementAt(i) instanceof SequenceListener) + if (sm.sequence == seq || sm.sequence == seq.getDatasetSequence() + || (sm.sequence.getDatasetSequence() != null && sm.sequence + .getDatasetSequence() == seq.getDatasetSequence())) { - // DEBUG - // System.err.println("relay Seq " + seq.getDisplayId(false) + " " + - // index); - if (results == null) + for (int i = 0; i < columns.length; i += 2) { - results = new SearchResults(); - if (index >= seq.getStart() && index <= seq.getEnd()) + ContiguousI positions = seq.findPositions(columns[i] + 1, + columns[i + 1] + 1); + if (positions == null) + { + continue; + } + for (int index = positions.getBegin(); index <= positions + .getEnd(); index++) { - // construct highlighted sequence list - if (seqmappings != null) - { - Enumeration e = seqmappings.elements(); - while (e.hasMoreElements()) + atomNo = sm.getAtomNum(index); - { - ((AlignedCodonFrame) e.nextElement()).markMappedRegion( - seq, index, results); - } - } - // hasSequenceListeners = results.getSize() > 0; - if (handlingVamsasMo) + if (atomNo > 0) { - // maybe have to resolve seq to a dataset seqeunce... - // add in additional direct sequence and/or dataset sequence - // highlighting - results.addResult(seq, index, index); + atoms.add(new AtomSpec(sm.pdbfile, sm.pdbchain, + sm.getPDBResNum(index), atomNo)); } } } - if (hasSequenceListeners) - { - ((SequenceListener) listeners.elementAt(i)) - .highlightSequence(results); - } - } - else if (listeners.elementAt(i) instanceof VamsasListener - && !handlingVamsasMo) - { - // DEBUG - // System.err.println("Vamsas from Seq " + seq.getDisplayId(false) + " - // " + - // index); - // pass the mouse over and absolute position onto the - // VamsasListener(s) - ((VamsasListener) listeners.elementAt(i)).mouseOver(seq, - indexpos, source); - } - else if(listeners.elementAt(i) instanceof SecondaryStructureListener){ - ((SecondaryStructureListener) listeners.elementAt(i)).mouseOverSequence(seq,indexpos); } } + if (atoms.size() > 0) + { + sl.highlightAtoms(atoms); + } } } @@ -561,7 +1279,7 @@ public class StructureSelectionManager * * if (mappings[j].sequence == seq && mappings[j].getPdbId().equals(pdbid) * && mappings[j].pdbfile.equals(sl.getPdbFile())) { - * System.out.println(pdbid+" "+mappings[j].getPdbId() +" + * jalview.bin.Console.outPrintln(pdbid+" "+mappings[j].getPdbId() +" * "+mappings[j].pdbfile); * * java.awt.Color col; for(int index=0; index tmp = new ArrayList<>(); + for (StructureMapping sm : mappings) { - for (int i = 0; i < mappings.length; i++) + if (sm.pdbfile.equals(pdbfile)) { - if (mappings[i].pdbfile.equals(pdbfile)) - { - tmp.addElement(mappings[i]); - } + tmp.add(sm); } } - StructureMapping[] ret = new StructureMapping[tmp.size()]; - for (int i = 0; i < tmp.size(); i++) - { - ret[i] = (StructureMapping) tmp.elementAt(i); - } - - return ret; + return tmp.toArray(new StructureMapping[tmp.size()]); } - public String printMapping(String pdbfile) + /** + * Returns a readable description of all mappings for the given pdbfile to any + * of the given sequences + * + * @param pdbfile + * @param seqs + * @return + */ + public String printMappings(String pdbfile, List seqs) { - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < mappings.length; i++) + if (pdbfile == null || seqs == null || seqs.isEmpty()) + { + return ""; + } + + StringBuilder sb = new StringBuilder(64); + for (StructureMapping sm : mappings) { - if (mappings[i].pdbfile.equals(pdbfile)) + if (Platform.pathEquals(sm.pdbfile, pdbfile) + && seqs.contains(sm.sequence)) { - sb.append(mappings[i].mappingDetails); + sb.append(sm.mappingDetails); + sb.append(NEWLINE); + // separator makes it easier to read multiple mappings + sb.append("====================="); + sb.append(NEWLINE); } } + sb.append(NEWLINE); return sb.toString(); } - private int[] seqmappingrefs = null; // refcount for seqmappings elements + /** + * Remove the given mapping + * + * @param acf + */ + public void deregisterMapping(AlignedCodonFrame acf) + { + if (acf != null) + { + boolean removed = seqmappings.remove(acf); + if (removed && seqmappings.isEmpty()) + { // debug + jalview.bin.Console.outPrintln("All mappings removed"); + } + } + } - private synchronized void modifySeqMappingList(boolean add, - AlignedCodonFrame[] codonFrames) + /** + * Add each of the given codonFrames to the stored set, if not aready present. + * + * @param mappings + */ + public void registerMappings(List mappings) { - if (!add && (seqmappings == null || seqmappings.size() == 0)) - return; - if (seqmappings == null) - seqmappings = new Vector(); - if (codonFrames != null && codonFrames.length > 0) + if (mappings != null) { - for (int cf = 0; cf < codonFrames.length; cf++) + for (AlignedCodonFrame acf : mappings) { - if (seqmappings.contains(codonFrames[cf])) - { - if (add) - { - seqmappingrefs[seqmappings.indexOf(codonFrames[cf])]++; - } - else - { - if (--seqmappingrefs[seqmappings.indexOf(codonFrames[cf])] <= 0) - { - int pos = seqmappings.indexOf(codonFrames[cf]); - int[] nr = new int[seqmappingrefs.length - 1]; - if (pos > 0) - { - System.arraycopy(seqmappingrefs, 0, nr, 0, pos); - } - if (pos < seqmappingrefs.length - 1) - { - System.arraycopy(seqmappingrefs, pos + 1, nr, 0, - seqmappingrefs.length - pos - 2); - } - } - } - } - else - { - if (add) - { - seqmappings.addElement(codonFrames[cf]); - - int[] nsr = new int[(seqmappingrefs == null) ? 1 - : seqmappingrefs.length + 1]; - if (seqmappingrefs != null && seqmappingrefs.length > 0) - System.arraycopy(seqmappingrefs, 0, nsr, 0, - seqmappingrefs.length); - nsr[(seqmappingrefs == null) ? 0 : seqmappingrefs.length] = 1; - seqmappingrefs = nsr; - } - } + registerMapping(acf); } } } - public void removeMappings(AlignedCodonFrame[] codonFrames) + /** + * Add the given mapping to the stored set, unless already stored. + */ + public void registerMapping(AlignedCodonFrame acf) { - modifySeqMappingList(false, codonFrames); + if (acf != null) + { + if (!seqmappings.contains(acf)) + { + seqmappings.add(acf); + } + } } - public void addMappings(AlignedCodonFrame[] codonFrames) + /** + * Resets this object to its initial state by removing all registered + * listeners, codon mappings, PDB file mappings + */ + public void resetAll() { - modifySeqMappingList(true, codonFrames); + if (mappings != null) + { + mappings.clear(); + } + if (seqmappings != null) + { + seqmappings.clear(); + } + if (sel_listeners != null) + { + sel_listeners.clear(); + } + if (listeners != null) + { + listeners.clear(); + } + if (commandListeners != null) + { + commandListeners.clear(); + } + if (view_listeners != null) + { + view_listeners.clear(); + } + if (pdbFileNameId != null) + { + pdbFileNameId.clear(); + } + if (pdbIdFileName != null) + { + pdbIdFileName.clear(); + } } - Vector sel_listeners = new Vector(); - public void addSelectionListener(SelectionListener selecter) { if (!sel_listeners.contains(selecter)) { - sel_listeners.addElement(selecter); + sel_listeners.add(selecter); } } @@ -716,42 +1463,38 @@ public class StructureSelectionManager { if (sel_listeners.contains(toremove)) { - sel_listeners.removeElement(toremove); + sel_listeners.remove(toremove); } } public synchronized void sendSelection( jalview.datamodel.SequenceGroup selection, - jalview.datamodel.ColumnSelection colsel, SelectionSource source) + jalview.datamodel.ColumnSelection colsel, HiddenColumns hidden, + SelectionSource source) { - if (sel_listeners != null && sel_listeners.size() > 0) + for (SelectionListener slis : sel_listeners) { - Enumeration listeners = sel_listeners.elements(); - while (listeners.hasMoreElements()) + if (slis != source) { - SelectionListener slis = ((SelectionListener) listeners - .nextElement()); - if (slis != source) - { - slis.selection(selection, colsel, source); - } - ; + slis.selection(selection, colsel, hidden, source); } } } - - Vector view_listeners=new Vector(); - public synchronized void sendViewPosition(jalview.api.AlignmentViewPanel source, int startRes, - int endRes, int startSeq, int endSeq) + + Vector view_listeners = new Vector<>(); + + public synchronized void sendViewPosition( + jalview.api.AlignmentViewPanel source, int startRes, int endRes, + int startSeq, int endSeq) { if (view_listeners != null && view_listeners.size() > 0) { - Enumeration listeners = view_listeners.elements(); + Enumeration listeners = view_listeners + .elements(); while (listeners.hasMoreElements()) { - AlignmentViewPanelListener slis = listeners - .nextElement(); + AlignmentViewPanelListener slis = listeners.nextElement(); if (slis != source) { slis.viewPosition(startRes, endRes, startSeq, endSeq, source); @@ -760,39 +1503,15 @@ public class StructureSelectionManager } } } - - - public void finalize() throws Throwable { - if (listeners!=null) { - listeners.clear(); - listeners=null; - } - if (mappingData!=null) - { - mappingData.clear(); - mappingData=null; - } - if (sel_listeners!=null) - { - sel_listeners.clear(); - sel_listeners=null; - } - if (view_listeners!=null) - { - view_listeners.clear(); - view_listeners=null; - } - mappings=null; - seqmappingrefs=null; - } /** * release all references associated with this manager provider + * * @param jalviewLite */ public static void release(StructureSelectionManagerProvider jalviewLite) { -// synchronized (instances) + // synchronized (instances) { if (instances == null) { @@ -804,13 +1523,133 @@ public class StructureSelectionManager instances.remove(jalviewLite); try { - mnger.finalize(); + /* bsoares 2019-03-20 finalize deprecated, no apparent external + * resources to close + */ + // mnger.finalize(); } catch (Throwable x) { } - ; } } } + public void registerPDBEntry(PDBEntry pdbentry) + { + if (pdbentry.getFile() != null + && pdbentry.getFile().trim().length() > 0) + { + registerPDBFile(pdbentry.getId(), pdbentry.getFile()); + } + } + + public void addCommandListener(CommandListener cl) + { + if (!commandListeners.contains(cl)) + { + commandListeners.add(cl); + } + } + + public boolean hasCommandListener(CommandListener cl) + { + return this.commandListeners.contains(cl); + } + + public boolean removeCommandListener(CommandListener l) + { + return commandListeners.remove(l); + } + + /** + * Forward a command to any command listeners (except for the command's + * source). + * + * @param command + * the command to be broadcast (in its form after being performed) + * @param undo + * if true, the command was being 'undone' + * @param source + */ + public void commandPerformed(CommandI command, boolean undo, + VamsasSource source) + { + for (CommandListener listener : commandListeners) + { + listener.mirrorCommand(command, undo, this, source); + } + } + + /** + * Returns a new CommandI representing the given command as mapped to the + * given sequences. If no mapping could be made, or the command is not of a + * mappable kind, returns null. + * + * @param command + * @param undo + * @param mapTo + * @param gapChar + * @return + */ + public CommandI mapCommand(CommandI command, boolean undo, + final AlignmentI mapTo, char gapChar) + { + if (command instanceof EditCommand) + { + return MappingUtils.mapEditCommand((EditCommand) command, undo, mapTo, + gapChar, seqmappings); + } + else if (command instanceof OrderCommand) + { + return MappingUtils.mapOrderCommand((OrderCommand) command, undo, + mapTo, seqmappings); + } + return null; + } + + public List getSequenceMappings() + { + return seqmappings; + } + + /** + * quick and dirty route to just highlight all structure positions for a range + * of columns + * + * @param sequencesArray + * @param is + * start-end columns on sequencesArray + * @param source + * origin parent AlignmentPanel + */ + public void highlightPositionsOnMany(SequenceI[] sequencesArray, int[] is, + Object source) + { + for (int i = 0; i < listeners.size(); i++) + { + Object listener = listeners.elementAt(i); + if (listener == source) + { + // TODO listener (e.g. SeqPanel) is never == source (AlignViewport) + // Temporary fudge with SequenceListener.getVamsasSource() + continue; + } + if (listener instanceof StructureListener) + { + highlightStructureRegionsFor((StructureListener) listener, + sequencesArray, is); + } + } + } + + public Map getPdbFileNameIdMap() + { + return pdbFileNameId; + } + + public Map getPdbIdFileNameMap() + { + return pdbIdFileName; + } + }