package jalview.structures.models; import jalview.api.StructureSelectionManagerProvider; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; import jalview.structure.StructureListener; import jalview.structure.StructureSelectionManager; import jalview.util.MessageManager; import java.awt.event.ComponentEvent; import java.util.ArrayList; import java.util.List; /** * A base class to hold common function for protein structure model binding. * Initial version created by refactoring JMol and Chimera binding models, but * other structure viewers could in principle be accommodated in future. * * @author gmcarstairs * */ public abstract class AAStructureBindingModel extends SequenceStructureBindingModel implements StructureListener, StructureSelectionManagerProvider { private StructureSelectionManager ssm; private PDBEntry[] pdbEntry; /* * sequences mapped to each pdbentry */ private SequenceI[][] sequence; /* * array of target chains for sequences - tied to pdbentry and sequence[] */ private String[][] chains; /* * datasource protocol for access to PDBEntrylatest */ String protocol = null; protected boolean colourBySequence = true; /** * Constructor * * @param ssm * @param seqs */ public AAStructureBindingModel(StructureSelectionManager ssm, SequenceI[][] seqs) { this.ssm = ssm; this.sequence = seqs; } /** * Constructor * * @param ssm * @param pdbentry * @param sequenceIs * @param chains * @param protocol */ public AAStructureBindingModel(StructureSelectionManager ssm, PDBEntry[] pdbentry, SequenceI[][] sequenceIs, String[][] chains, String protocol) { this.ssm = ssm; this.sequence = sequenceIs; this.chains = chains; this.pdbEntry = pdbentry; this.protocol = protocol; if (chains == null) { this.chains = new String[pdbentry.length][]; } } public StructureSelectionManager getSsm() { return ssm; } /** * Returns the i'th PDBEntry (or null) * * @param i * @return */ public PDBEntry getPdbEntry(int i) { return (pdbEntry != null && pdbEntry.length > i) ? pdbEntry[i] : null; } /** * Returns the number of modelled PDB file entries. * * @return */ public int getPdbCount() { return pdbEntry == null ? 0 : pdbEntry.length; } public SequenceI[][] getSequence() { return sequence; } public String[][] getChains() { return chains; } public String getProtocol() { return protocol; } // TODO may remove this if calling methods can be pulled up here protected void setPdbentry(PDBEntry[] pdbentry) { this.pdbEntry = pdbentry; } protected void setSequence(SequenceI[][] sequence) { this.sequence = sequence; } protected void setChains(String[][] chains) { this.chains = chains; } /** * Construct a title string for the viewer window based on the data Jalview * knows about * @param viewerName TODO * @param verbose * * @return */ public String getViewerTitle(String viewerName, boolean verbose) { if (getSequence() == null || getSequence().length < 1 || getPdbCount() < 1 || getSequence()[0].length < 1) { return ("Jalview " + viewerName + " Window"); } // TODO: give a more informative title when multiple structures are // displayed. StringBuilder title = new StringBuilder(64); final PDBEntry pdbEntry = getPdbEntry(0); title.append(viewerName + " view for " + getSequence()[0][0].getName() + ":" + pdbEntry.getId()); if (verbose) { if (pdbEntry.getProperty() != null) { if (pdbEntry.getProperty().get("method") != null) { title.append(" Method: "); title.append(pdbEntry.getProperty().get("method")); } if (pdbEntry.getProperty().get("chains") != null) { title.append(" Chain:"); title.append(pdbEntry.getProperty().get("chains")); } } } return title.toString(); } /** * Called by after closeViewer is called, to release any resources and * references so they can be garbage collected. Override if needed. */ protected void releaseUIResources() { } public boolean isColourBySequence() { return colourBySequence; } public void setColourBySequence(boolean colourBySequence) { this.colourBySequence = colourBySequence; } protected void addSequenceAndChain(int pe, SequenceI[] seq, String[] tchain) { if (pe < 0 || pe >= getPdbCount()) { throw new Error(MessageManager.formatMessage( "error.implementation_error_no_pdbentry_from_index", new Object[] { Integer.valueOf(pe).toString() })); } final String nullChain = "TheNullChain"; List s = new ArrayList(); List c = new ArrayList(); if (getChains() == null) { setChains(new String[getPdbCount()][]); } if (getSequence()[pe] != null) { for (int i = 0; i < getSequence()[pe].length; i++) { s.add(getSequence()[pe][i]); if (getChains()[pe] != null) { if (i < getChains()[pe].length) { c.add(getChains()[pe][i]); } else { c.add(nullChain); } } else { if (tchain != null && tchain.length > 0) { c.add(nullChain); } } } } for (int i = 0; i < seq.length; i++) { if (!s.contains(seq[i])) { s.add(seq[i]); if (tchain != null && i < tchain.length) { c.add(tchain[i] == null ? nullChain : tchain[i]); } } } SequenceI[] tmp = s.toArray(new SequenceI[s.size()]); getSequence()[pe] = tmp; if (c.size() > 0) { String[] tch = c.toArray(new String[c.size()]); for (int i = 0; i < tch.length; i++) { if (tch[i] == nullChain) { tch[i] = null; } } getChains()[pe] = tch; } else { getChains()[pe] = null; } } /** * add structures and any known sequence associations * * @returns the pdb entries added to the current set. */ public synchronized PDBEntry[] addSequenceAndChain(PDBEntry[] pdbe, SequenceI[][] seq, String[][] chns) { List v = new ArrayList(); List rtn = new ArrayList(); for (int i = 0; i < getPdbCount(); i++) { v.add(getPdbEntry(i)); } for (int i = 0; i < pdbe.length; i++) { int r = v.indexOf(pdbe[i]); if (r == -1 || r >= getPdbCount()) { rtn.add(new int[] { v.size(), i }); v.add(pdbe[i]); } else { // just make sure the sequence/chain entries are all up to date addSequenceAndChain(r, seq[i], chns[i]); } } pdbe = v.toArray(new PDBEntry[v.size()]); setPdbentry(pdbe); if (rtn.size() > 0) { // expand the tied sequence[] and string[] arrays SequenceI[][] sqs = new SequenceI[getPdbCount()][]; String[][] sch = new String[getPdbCount()][]; System.arraycopy(getSequence(), 0, sqs, 0, getSequence().length); System.arraycopy(getChains(), 0, sch, 0, this.getChains().length); setSequence(sqs); setChains(sch); pdbe = new PDBEntry[rtn.size()]; for (int r = 0; r < pdbe.length; r++) { int[] stri = (rtn.get(r)); // record the pdb file as a new addition pdbe[r] = getPdbEntry(stri[0]); // and add the new sequence/chain entries addSequenceAndChain(stri[0], seq[stri[1]], chns[stri[1]]); } } else { pdbe = null; } return pdbe; } /** * Add sequences to the pe'th pdbentry's sequence set. * * @param pe * @param seq */ public void addSequence(int pe, SequenceI[] seq) { addSequenceAndChain(pe, seq, null); } /** * add the given sequences to the mapping scope for the given pdb file handle * * @param pdbFile * - pdbFile identifier * @param seq * - set of sequences it can be mapped to */ public void addSequenceForStructFile(String pdbFile, SequenceI[] seq) { for (int pe = 0; pe < getPdbCount(); pe++) { if (getPdbEntry(pe).getFile().equals(pdbFile)) { addSequence(pe, seq); } } } }