X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fstructure%2FStructureSelectionManager.java;h=e9aa575e1b76c402fe5a599f84fb5d0aa80eb88e;hb=ab43013b7e357b84b4abade0dba949668dfb2a0e;hp=0cd3094697b5963fc74bd645835b97fc2db5886a;hpb=433d3fc7bc039b3dae3827b595b7a419cc3c0b27;p=jalview.git diff --git a/src/jalview/structure/StructureSelectionManager.java b/src/jalview/structure/StructureSelectionManager.java index 0cd3094..e9aa575 100644 --- a/src/jalview/structure/StructureSelectionManager.java +++ b/src/jalview/structure/StructureSelectionManager.java @@ -1,44 +1,204 @@ /* - * Jalview - A Sequence Alignment Editor and Viewer (Version 2.6) - * Copyright (C) 2010 J Procter, AM Waterhouse, G Barton, M Clamp, S Searle + * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.2b1) + * Copyright (C) 2014 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 jalview.analysis.AlignSeq; +import jalview.api.StructureSelectionManagerProvider; +import jalview.datamodel.AlignedCodonFrame; +import jalview.datamodel.AlignmentAnnotation; +import jalview.datamodel.Annotation; +import jalview.datamodel.PDBEntry; +import jalview.datamodel.SearchResults; +import jalview.datamodel.SequenceI; +import jalview.io.AppletFormatAdapter; +import jalview.util.MessageManager; + +import java.io.PrintStream; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.IdentityHashMap; +import java.util.Vector; -import MCview.*; -import jalview.analysis.*; -import jalview.datamodel.*; +import MCview.Atom; +import MCview.PDBChain; public class StructureSelectionManager { - static StructureSelectionManager instance; + static IdentityHashMap instances; StructureMapping[] mappings; - Hashtable mappingData = new Hashtable(); + private boolean processSecondaryStructure = false, + secStructServices = false, addTempFacAnnot = false; - public static StructureSelectionManager getStructureSelectionManager() + /** + * @return true if will try to use external services for processing secondary + * structure + */ + public boolean isSecStructServices() { - if (instance == null) + 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; + } + + /** + * 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) { - instance = new StructureSelectionManager(); + System.err.println("reportMapping: No PDB/Sequence mappings."); } + else + { + System.err.println("reportMapping: There are " + mappings.length + + " mappings."); + for (int m = 0; m < mappings.length; m++) + { + System.err.println("mapping " + m + " : " + mappings[m].pdbfile); + } + } + } + + /** + * map between the PDB IDs (or structure identifiers) used by Jalview and the + * absolute filenames for PDB data that corresponds to it + */ + HashMap pdbIdFileName = new HashMap(), + 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(); + } + StructureSelectionManager instance = instances.get(context); + if (instance == null) + { + if (nullProvider != null) + { + instance = nullProvider; + } + else + { + instance = new StructureSelectionManager(); + } + instances.put(context, instance); + } return instance; } @@ -75,6 +235,7 @@ public class StructureSelectionManager /** * register a listener for alignment sequence mouseover events + * * @param svl */ public void addStructureViewerListener(Object svl) @@ -101,9 +262,33 @@ public class StructureSelectionManager } /** + * 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 + * @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 MCview.PDBfile setMapping(SequenceI[] sequence, + String[] targetChains, String pdbFile, String protocol) + { + return setMapping(true, sequence, targetChains, pdbFile, protocol); + } + + /** * create sequence structure mappings between each sequence and the given * pdbFile (retrieved via the given protocol). * + * @param forStructureView + * when true, record the mapping for use in mouseOvers + * * @param sequence * - one or more sequences to be mapped to pdbFile * @param targetChains @@ -115,7 +300,8 @@ 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, + synchronized public MCview.PDBfile setMapping(boolean forStructureView, + SequenceI[] sequence, String[] targetChains, String pdbFile, String protocol) { /* @@ -123,9 +309,42 @@ public class StructureSelectionManager * the tried and tested MCview pdb mapping */ MCview.PDBfile pdb = null; + boolean parseSecStr = processSecondaryStructure; + if (isPDBFileRegistered(pdbFile)) + { + for (SequenceI sq : sequence) + { + SequenceI ds = sq; + while (ds.getDatasetSequence() != null) + { + 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 (MCview.PDBfile.isCalcIdForFile(ala, + findIdForPDBFile(pdbFile))) + { + parseSecStr = false; + } + } + } + } + } try { - pdb = new MCview.PDBfile(pdbFile, protocol); + pdb = new MCview.PDBfile(addTempFacAnnot, parseSecStr, + secStructServices, pdbFile, protocol); + if (pdb.id != null && pdb.id.trim().length() > 0 + && AppletFormatAdapter.FILE.equals(protocol)) + { + registerPDBFile(pdb.id.trim(), pdbFile); + } } catch (Exception ex) { ex.printStackTrace(); @@ -135,15 +354,33 @@ public class StructureSelectionManager String targetChain; for (int s = 0; s < sequence.length; s++) { + boolean infChain = true; if (targetChains != null && targetChains[s] != null) + { + infChain = false; targetChain = targetChains[s]; + } else if (sequence[s].getName().indexOf("|") > -1) { targetChain = sequence[s].getName().substring( sequence[s].getName().lastIndexOf("|") + 1); + if (targetChain.length() > 1) + { + if (targetChain.trim().length() == 0) + { + targetChain = " "; + } + else + { + // not a valid chain identifier + targetChain = ""; + } + } } else + { targetChain = ""; + } int max = -10; AlignSeq maxAlignseq = null; @@ -152,19 +389,17 @@ public class StructureSelectionManager boolean first = true; for (int i = 0; i < pdb.chains.size(); i++) { - // TODO: re http://issues.jalview.org/browse/JAL-583 : this patch may - // need to be revoked - PDBChain chain = ((PDBChain) pdb.chains.elementAt(i)); - if (targetChain.length() > 0 && !targetChain.equals(chain.id)) + PDBChain chain = (pdb.chains.elementAt(i)); + if (targetChain.length() > 0 && !targetChain.equals(chain.id) + && !infChain) { continue; // don't try to map chains don't match. } - // end of patch for limiting computed mappings // 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 + pdb.chains.elementAt(i).sequence, + pdb.chains.elementAt(i).isNa ? AlignSeq.DNA : AlignSeq.PEP); as.calcScoreMatrix(); as.traceAlignment(); @@ -190,11 +425,13 @@ public class StructureSelectionManager + maxChain.residues.size() + "\n\n"); PrintStream ps = new PrintStream(System.out) { + @Override public void print(String x) { mappingDetails.append(x); } + @Override public void println() { mappingDetails.append("\n"); @@ -210,10 +447,16 @@ public class StructureSelectionManager + (maxAlignseq.seq1end + sequence[s].getEnd() - 1)); maxChain.makeExactMapping(maxAlignseq, sequence[s]); - + jalview.datamodel.Mapping sqmpping = maxAlignseq + .getMappingFromS1(false); + jalview.datamodel.Mapping omap = new jalview.datamodel.Mapping( + sqmpping.getMap().getInverse()); maxChain.transferRESNUMFeatures(sequence[s], null); - int[][] mapping = new int[sequence[s].getEnd() + 2][2]; + // 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; @@ -230,24 +473,30 @@ public class StructureSelectionManager index++; } while (index < maxChain.atoms.size()); - if (mappings == null) + if (protocol.equals(jalview.io.AppletFormatAdapter.PASTE)) { - mappings = new StructureMapping[1]; + pdbFile = "INLINE" + pdb.id; } - else + StructureMapping newMapping = new StructureMapping(sequence[s], + pdbFile, pdb.id, maxChainId, mapping, + mappingDetails.toString()); + if (forStructureView) { - StructureMapping[] tmp = new StructureMapping[mappings.length + 1]; - System.arraycopy(mappings, 0, tmp, 0, mappings.length); - mappings = tmp; - } - if (protocol.equals(jalview.io.AppletFormatAdapter.PASTE)) - pdbFile = "INLINE" + pdb.id; + if (mappings == null) + { + mappings = new StructureMapping[1]; + } + else + { + StructureMapping[] tmp = new StructureMapping[mappings.length + 1]; + System.arraycopy(mappings, 0, tmp, 0, mappings.length); + mappings = tmp; + } - mappings[mappings.length - 1] = new StructureMapping(sequence[s], - pdbFile, pdb.id, maxChainId, mapping, - mappingDetails.toString()); - maxChain.transferResidueAnnotation(mappings[mappings.length - 1]); + mappings[mappings.length - 1] = newMapping; + } + maxChain.transferResidueAnnotation(newMapping, sqmpping); } // /////// @@ -257,15 +506,28 @@ public class StructureSelectionManager public void removeStructureViewerListener(Object svl, String[] pdbfiles) { listeners.removeElement(svl); + if (svl instanceof SequenceListener) + { + for (int i = 0; i < listeners.size(); i++) + { + if (listeners.elementAt(i) instanceof StructureListener) + { + ((StructureListener) listeners.elementAt(i)) + .releaseReferences(svl); + } + } + } + if (pdbfiles == null) { return; } - boolean removeMapping = true; String[] handlepdbs; Vector pdbs = new Vector(); for (int i = 0; i < pdbfiles.length; pdbs.addElement(pdbfiles[i++])) + { ; + } StructureListener sl; for (int i = 0; i < listeners.size(); i++) { @@ -302,8 +564,15 @@ public class StructureSelectionManager public void mouseOverStructure(int pdbResNum, String chain, String pdbfile) { + if (listeners == null) + { + // old or prematurely sent event + return; + } 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) @@ -312,7 +581,6 @@ public class StructureSelectionManager { results = new SearchResults(); } - int indexpos; if (mappings != null) { for (int j = 0; j < mappings.length; j++) @@ -321,31 +589,39 @@ public class StructureSelectionManager && mappings[j].pdbchain.equals(chain)) { indexpos = mappings[j].getSeqPos(pdbResNum); - results.addResult(mappings[j].sequence, indexpos, indexpos); - // construct highlighted sequence list - if (seqmappings != null) + if (lastipos != indexpos && lastseq != mappings[j].sequence) { + 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()) + Enumeration e = seqmappings.elements(); + while (e.hasMoreElements()) - { - ((AlignedCodonFrame) e.nextElement()).markMappedRegion( - mappings[j].sequence, indexpos, results); + { + ((AlignedCodonFrame) e.nextElement()).markMappedRegion( + mappings[j].sequence, indexpos, results); + } } } + } } } } } - if (results.getSize() > 0) + if (results != null) { for (int i = 0; i < listeners.size(); i++) { Object li = listeners.elementAt(i); if (li instanceof SequenceListener) + { ((SequenceListener) li).highlightSequence(results); + } } } } @@ -363,19 +639,27 @@ public class StructureSelectionManager * the sequence position (if -1, seq.findPosition is called to * resolve the residue number) */ - public void mouseOverSequence(SequenceI seq, int indexpos, int index) + public void mouseOverSequence(SequenceI seq, int indexpos, int index, + VamsasSource source) { 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 (listeners.elementAt(i) instanceof StructureListener) + Object listener = listeners.elementAt(i); + if (listener == source) { - sl = (StructureListener) listeners.elementAt(i); + continue; + } + if (listener instanceof StructureListener) + { + sl = (StructureListener) listener; if (mappings == null) { continue; @@ -398,7 +682,7 @@ public class StructureSelectionManager else { if (relaySeqMappings && hasSequenceListeners - && listeners.elementAt(i) instanceof SequenceListener) + && listener instanceof SequenceListener) { // DEBUG // System.err.println("relay Seq " + seq.getDisplayId(false) + " " + @@ -433,12 +717,10 @@ public class StructureSelectionManager } if (hasSequenceListeners) { - ((SequenceListener) listeners.elementAt(i)) - .highlightSequence(results); + ((SequenceListener) listener).highlightSequence(results); } } - else if (listeners.elementAt(i) instanceof VamsasListener - && !handlingVamsasMo) + else if (listener instanceof VamsasListener && !handlingVamsasMo) { // DEBUG // System.err.println("Vamsas from Seq " + seq.getDisplayId(false) + " @@ -446,8 +728,12 @@ public class StructureSelectionManager // index); // pass the mouse over and absolute position onto the // VamsasListener(s) - ((VamsasListener) listeners.elementAt(i)) - .mouseOver(seq, indexpos); + ((VamsasListener) listener).mouseOver(seq, indexpos, source); + } + else if (listener instanceof SecondaryStructureListener) + { + ((SecondaryStructureListener) listener).mouseOverSequence(seq, + indexpos); } } } @@ -468,14 +754,15 @@ public class StructureSelectionManager * @param position * in an alignment sequence */ - public void mouseOverVamsasSequence(SequenceI sequenceI, int position) + public void mouseOverVamsasSequence(SequenceI sequenceI, int position, + VamsasSource source) { handlingVamsasMo = true; long msg = sequenceI.hashCode() * (1 + position); if (lastmsg != msg) { lastmsg = msg; - mouseOverSequence(sequenceI, position, -1); + mouseOverSequence(sequenceI, position, -1, source); } handlingVamsasMo = false; } @@ -578,9 +865,13 @@ public class StructureSelectionManager AlignedCodonFrame[] codonFrames) { if (!add && (seqmappings == null || seqmappings.size() == 0)) + { return; + } if (seqmappings == null) + { seqmappings = new Vector(); + } if (codonFrames != null && codonFrames.length > 0) { for (int cf = 0; cf < codonFrames.length; cf++) @@ -618,8 +909,10 @@ public class StructureSelectionManager 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; } @@ -638,7 +931,7 @@ public class StructureSelectionManager modifySeqMappingList(true, codonFrames); } - Vector sel_listeners = new Vector(); + Vector sel_listeners = new Vector(); public void addSelectionListener(SelectionListener selecter) { @@ -675,4 +968,91 @@ public class StructureSelectionManager } } } + + 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(); + while (listeners.hasMoreElements()) + { + AlignmentViewPanelListener slis = listeners.nextElement(); + if (slis != source) + { + slis.viewPosition(startRes, endRes, startSeq, endSeq, source); + } + ; + } + } + } + + public void finalize() throws Throwable + { + if (listeners != null) + { + listeners.clear(); + listeners = null; + } + if (pdbIdFileName != null) + { + pdbIdFileName.clear(); + pdbIdFileName = 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) + { + if (instances == null) + { + return; + } + StructureSelectionManager mnger = (instances.get(jalviewLite)); + if (mnger != null) + { + instances.remove(jalviewLite); + try + { + mnger.finalize(); + } catch (Throwable x) + { + } + ; + } + } + } + + public void registerPDBEntry(PDBEntry pdbentry) + { + if (pdbentry.getFile() != null + && pdbentry.getFile().trim().length() > 0) + { + registerPDBFile(pdbentry.getId(), pdbentry.getFile()); + } + } + }