X-Git-Url: http://source.jalview.org/gitweb/?a=blobdiff_plain;f=src%2Fjalview%2Fstructures%2Fmodels%2FAAStructureBindingModel.java;h=6e926df87a475774acbbf2ff05bea75c8b3766fd;hb=d85a2741994c169e1b81db8f9166f5214ff1f561;hp=4dfdc2a4e2ad1d4bb0079f023ec1019446f6feb0;hpb=9c1a9d682a2664d525bfd0f38bae861292dc3921;p=jalview.git diff --git a/src/jalview/structures/models/AAStructureBindingModel.java b/src/jalview/structures/models/AAStructureBindingModel.java index 4dfdc2a..6e926df 100644 --- a/src/jalview/structures/models/AAStructureBindingModel.java +++ b/src/jalview/structures/models/AAStructureBindingModel.java @@ -20,42 +20,46 @@ */ package jalview.structures.models; +import java.awt.Color; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import javax.swing.SwingUtilities; + import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; import jalview.api.FeatureRenderer; import jalview.api.SequenceRenderer; import jalview.api.StructureSelectionManagerProvider; import jalview.api.structures.JalviewStructureDisplayI; +import jalview.bin.Cache; import jalview.datamodel.AlignmentI; import jalview.datamodel.HiddenColumns; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; import jalview.gui.StructureViewer.ViewerType; import jalview.io.DataSourceType; +import jalview.io.StructureFile; import jalview.renderer.seqfeatures.FeatureColourFinder; import jalview.schemes.ColourSchemeI; import jalview.schemes.ResidueProperties; import jalview.structure.AtomSpec; import jalview.structure.AtomSpecModel; +import jalview.structure.StructureCommandI; import jalview.structure.StructureCommandsI; -import jalview.structure.StructureCommandsI.SuperposeData; import jalview.structure.StructureListener; import jalview.structure.StructureMapping; import jalview.structure.StructureSelectionManager; import jalview.util.Comparison; import jalview.util.MessageManager; -import java.awt.Color; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.BitSet; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import javax.swing.SwingUtilities; - /** * * A base class to hold common function for protein structure model binding. @@ -69,6 +73,42 @@ public abstract class AAStructureBindingModel extends SequenceStructureBindingModel implements StructureListener, StructureSelectionManagerProvider { + /** + * Data bean class to simplify parameterisation in superposeStructures + */ + public static class SuperposeData + { + public String filename; + + public String pdbId; + + public String chain = ""; + + public boolean isRna; + + /* + * The pdb residue number (if any) mapped to columns of the alignment + */ + public int[] pdbResNo; // or use SparseIntArray? + + public String modelId; + + /** + * Constructor + * + * @param width + * width of alignment (number of columns that may potentially + * participate in superposition) + * @param model + * structure viewer model number + */ + public SuperposeData(int width, String model) + { + pdbResNo = new int[width]; + modelId = model; + } + } + private static final int MIN_POS_TO_SUPERPOSE = 4; private static final String COLOURING_STRUCTURES = MessageManager @@ -125,7 +165,7 @@ public abstract class AAStructureBindingModel private boolean finishedInit = false; /** - * current set of model filenames loaded in the Jmol instance + * current set of model filenames loaded in the viewer */ protected String[] modelFileNames = null; @@ -613,7 +653,7 @@ public abstract class AAStructureBindingModel * @return */ protected int findSuperposableResidues(AlignmentI alignment, - BitSet matched, SuperposeData[] structures) + BitSet matched, AAStructureBindingModel.SuperposeData[] structures) { int refStructure = -1; String[] files = getStructureFiles(); @@ -828,11 +868,11 @@ public abstract class AAStructureBindingModel } } - SuperposeData[] structures = new SuperposeData[files.length]; + AAStructureBindingModel.SuperposeData[] structures = new AAStructureBindingModel.SuperposeData[files.length]; for (int f = 0; f < files.length; f++) { - structures[f] = new SuperposeData(width, - f + commandGenerator.getModelStartNo()); + structures[f] = new AAStructureBindingModel.SuperposeData(width, + getModelIdForFile(files[f])); } /* @@ -864,19 +904,20 @@ public abstract class AAStructureBindingModel * Show all as backbone before doing superposition(s) * (residues used for matching will be shown as ribbon) */ - executeCommand(commandGenerator.showBackbone(), false); + // todo better way to ensure synchronous than setting getReply true!! + executeCommands(commandGenerator.showBackbone(), true, null); /* - * superpose each (other) sequence to it in turn + * superpose each (other) structure to the reference in turn */ for (int i = 0; i < structures.length; i++) { if (i != refStructure) { AtomSpecModel atomSpec = getAtomSpec(structures[i], matched); - String commands = commandGenerator.superposeStructures(refAtoms, - atomSpec); - List replies = executeCommands(true, commands); + List commands = commandGenerator + .superposeStructures(refAtoms, atomSpec); + List replies = executeCommands(commands, true, null); for (String reply : replies) { // return this error (Chimera only) to the user @@ -892,7 +933,7 @@ public abstract class AAStructureBindingModel return error; } - private AtomSpecModel getAtomSpec(SuperposeData superposeData, + private AtomSpecModel getAtomSpec(AAStructureBindingModel.SuperposeData superposeData, BitSet matched) { AtomSpecModel model = new AtomSpecModel(); @@ -900,7 +941,7 @@ public abstract class AAStructureBindingModel while (nextColumnMatch != -1) { int pdbResNum = superposeData.pdbResNo[nextColumnMatch]; - model.addRange(superposeData.modelNo, pdbResNum, pdbResNum, + model.addRange(superposeData.modelId, pdbResNum, pdbResNum, superposeData.chain); nextColumnMatch = matched.nextSetBit(nextColumnMatch + 1); } @@ -941,7 +982,7 @@ public abstract class AAStructureBindingModel { colourBySequence = false; - executeCommand(commandGenerator.colourByCharge(), false, + executeCommands(commandGenerator.colourByCharge(), false, COLOURING_STRUCTURES); } @@ -980,13 +1021,14 @@ public abstract class AAStructureBindingModel /* * pass to the command constructor, and send the command */ - String cmd = commandGenerator.colourByResidues(colours); - executeCommand(cmd, false, COLOURING_STRUCTURES); + List cmd = commandGenerator + .colourByResidues(colours); + executeCommands(cmd, false, COLOURING_STRUCTURES); } public void setBackgroundColour(Color col) { - String cmd = commandGenerator.setBackgroundColour(col); + StructureCommandI cmd = commandGenerator.setBackgroundColour(col); executeCommand(cmd, false, null); } @@ -1002,95 +1044,119 @@ public abstract class AAStructureBindingModel * @param msg * @return */ - private List executeCommand(String cmd, boolean getReply, - String msg) + private List executeCommand(StructureCommandI cmd, + boolean getReply, String msg) { if (getReply) { - return executeSynchronous(cmd, msg, getReply); + /* + * synchronous (same thread) execution so reply can be returned + */ + final JalviewStructureDisplayI theViewer = getViewer(); + final long handle = msg == null ? 0 : theViewer.startProgressBar(msg); + try + { + return executeCommand(cmd, getReply); + } finally + { + if (msg != null) + { + theViewer.stopProgressBar(null, handle); + } + } } else { - executeAsynchronous(cmd, msg); + /* + * asynchronous (new thread) execution if no reply needed + */ + final JalviewStructureDisplayI theViewer = getViewer(); + final long handle = msg == null ? 0 : theViewer.startProgressBar(msg); + + SwingUtilities.invokeLater(new Runnable() + { + @Override + public void run() + { + try + { + executeCommand(cmd, false); + } finally + { + if (msg != null) + { + theViewer.stopProgressBar(null, handle); + } + } + } + }); return null; } } /** - * Sends the command in the current thread. If a message is supplied, this is - * shown before the thread is started, and removed when it completes. May - * return a reply to the command if requested. + * Execute one structure viewer command. If {@code getReply} is true, may + * optionally return one or more reply messages, else returns null. * * @param cmd - * @param msg * @param getReply - * @return */ - private List executeSynchronous(String cmd, String msg, boolean getReply) + protected abstract List executeCommand(StructureCommandI cmd, + boolean getReply); + + /** + * A helper method that converts list of commands to a vararg array + * + * @param commands + * @param getReply + * @param msg + */ + private List executeCommands(List commands, + boolean getReply, String msg) { - final JalviewStructureDisplayI theViewer = getViewer(); - final long handle = msg == null ? 0 : theViewer.startProgressBar(msg); - try - { - return executeCommand(cmd, getReply); - } finally - { - if (msg != null) - { - theViewer.stopProgressBar(null, handle); - } - } + return executeCommands(getReply, msg, + commands.toArray(new StructureCommandI[commands.size()])); } /** - * Sends the command in a separate thread. If a message is supplied, this is - * shown before the thread is started, and removed when it completes. No value - * is returned. + * Executes one or more structure viewer commands. If a progress message is + * provided, it is shown first, and removed after all commands have been run. * - * @param cmd + * @param getReply * @param msg + * @param commands + * @return */ - private void executeAsynchronous(String cmd, String msg) + protected List executeCommands(boolean getReply, String msg, + StructureCommandI[] commands) { + // todo: tidy this up + + /* + * show progress message if specified + */ final JalviewStructureDisplayI theViewer = getViewer(); final long handle = msg == null ? 0 : theViewer.startProgressBar(msg); - SwingUtilities.invokeLater(new Runnable() + List response = getReply ? new ArrayList<>() : null; + try { - @Override - public void run() + for (StructureCommandI cmd : commands) { - try + List replies = executeCommand(cmd, getReply, null); + if (getReply && replies != null) { - executeCommand(cmd, false); - } finally - { - if (msg != null) - { - theViewer.stopProgressBar(null, handle); - } + response.addAll(replies); } } - }); - } - - protected abstract List executeCommand(String command, - boolean getReply); - - protected List executeCommands(boolean getReply, - String... commands) - { - // todo: tidy this up - List response = getReply ? new ArrayList<>() : null; - for (String cmd : commands) + return response; + } finally { - List replies = executeCommand(cmd, getReply); - if (getReply && replies != null) + if (msg != null) { - response.addAll(replies); + theViewer.stopProgressBar(null, handle); } } - return response; } /** @@ -1114,9 +1180,9 @@ public abstract class AAStructureBindingModel Map colourMap = buildColoursMap(ssm, files, sequence, sr, alignmentv); - String[] colourBySequenceCommands = commandGenerator + List colourBySequenceCommands = commandGenerator .colourBySequence(colourMap); - executeCommands(false, colourBySequenceCommands); + executeCommands(colourBySequenceCommands, false, null); } /** @@ -1124,7 +1190,7 @@ public abstract class AAStructureBindingModel */ public void focusView() { - executeCommand(commandGenerator.focusView(), false); + executeCommand(commandGenerator.focusView(), false, null); } /** @@ -1150,22 +1216,21 @@ public abstract class AAStructureBindingModel if (tokens.length == 2) { String pdbFile = getFileForChain(chainId); - int modelNo = getModelNoForFile(pdbFile); - String model = modelNo == -1 ? "" : String.valueOf(modelNo); + String model = getModelIdForFile(pdbFile); showThese.add(model + ":" + tokens[1]); } } - executeCommand(commandGenerator.showChains(showThese), false); + executeCommands(commandGenerator.showChains(showThese), false, null); } /** - * Answers the structure viewer's model number given a PDB file name. Returns - * -1 if model number is not found. + * Answers the structure viewer's model id given a PDB file name. Returns an + * empty string if model id is not found. * * @param chainId * @return */ - protected abstract int getModelNoForFile(String chainId); + protected abstract String getModelIdForFile(String chainId); public boolean hasFileLoadingError() { @@ -1247,7 +1312,8 @@ public abstract class AAStructureBindingModel * @param command * @param progressMsg */ - protected void sendAsynchronousCommand(String command, String progressMsg) + protected void sendAsynchronousCommand(StructureCommandI command, + String progressMsg) { final JalviewStructureDisplayI theViewer = getViewer(); final long handle = progressMsg == null ? 0 @@ -1259,7 +1325,7 @@ public abstract class AAStructureBindingModel { try { - executeCommand(command, false); + executeCommand(command, false, null); } finally { if (progressMsg != null) @@ -1273,16 +1339,20 @@ public abstract class AAStructureBindingModel } /** + * Builds a data structure which records mapped structure residues for each + * colour. From this we can easily generate the viewer commands for colour by + * sequence. Constructs and returns a map of {@code Color} to + * {@code AtomSpecModel}, where the atomspec model holds + * *
-   * Build a data structure which records residues for each colour. 
-   * From this we can easily generate the viewer command for colour by sequence.
-   * Color
-   *     Model number
-   *         Chain
-   *             Residue positions
-   * Ordering is by order of addition (for colours and positions), natural ordering (for models and chains)
+   *   Model ids
+   *     Chains
+   *       Residue positions
    * 
* + * Ordering is by order of addition (for colours), natural ordering (for + * models and chains) + * * @param ssm * @param files * @param sequence @@ -1304,7 +1374,7 @@ public abstract class AAStructureBindingModel for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++) { - final int modelNumber = pdbfnum + commandGenerator.getModelStartNo(); + final String modelId = getModelIdForFile(files[pdbfnum]); StructureMapping[] mapping = ssm.getMapping(files[pdbfnum]); if (mapping == null || mapping.length < 1) @@ -1361,7 +1431,7 @@ public abstract class AAStructureBindingModel { if (startPos != -1) { - addAtomSpecRange(colourMap, lastColour, modelNumber, + addAtomSpecRange(colourMap, lastColour, modelId, startPos, lastPos, lastChain); } startPos = pos; @@ -1373,7 +1443,7 @@ public abstract class AAStructureBindingModel // final colour range if (lastColour != null) { - addAtomSpecRange(colourMap, lastColour, modelNumber, startPos, + addAtomSpecRange(colourMap, lastColour, modelId, startPos, lastPos, lastChain); } // break; @@ -1385,6 +1455,35 @@ public abstract class AAStructureBindingModel } /** + * todo better refactoring (map lookup or similar to get viewer structure id) + * + * @param pdbfnum + * @param file + * @return + */ + protected String getModelId(int pdbfnum, String file) + { + return String.valueOf(pdbfnum); + } + + /** + * Saves chains, formatted as "pdbId:chainCode", and lookups from this to the + * full PDB file path + * + * @param pdb + * @param file + */ + public void stashFoundChains(StructureFile pdb, String file) + { + for (int i = 0; i < pdb.getChains().size(); i++) + { + String chid = pdb.getId() + ":" + pdb.getChains().elementAt(i).id; + addChainFile(chid, file); + getChainNames().add(chid); + } + } + + /** * Helper method to add one contiguous range to the AtomSpec model for the given * value (creating the model if necessary). As used by Jalview, {@code value} is *
    @@ -1402,7 +1501,7 @@ public abstract class AAStructureBindingModel */ public static final void addAtomSpecRange(Map map, Object value, - int model, int startPos, int endPos, String chain) + String model, int startPos, int endPos, String chain) { /* * Get/initialize map of data for the colour @@ -1416,4 +1515,83 @@ public abstract class AAStructureBindingModel atomSpec.addRange(model, startPos, endPos, chain); } + + /** + * Returns the file extension (including '.' separator) to use for a saved + * viewer session file. Default is to return null (not supported), override as + * required. + * + * @return + */ + public String getSessionFileExtension() + { + return null; + } + + /** + * If supported, saves the state of the structure viewer to a temporary file + * and returns the file. Returns null and logs an error on any failure. + * + * @return + */ + public File saveSession() + { + String prefix = getViewerType().toString(); + String suffix = getSessionFileExtension(); + File f = null; + try + { + f = File.createTempFile(prefix, suffix); + saveSession(f); + } catch (IOException e) + { + Cache.log.error(String.format("Error saving %s session: %s", + prefix, e.toString())); + } + + return f; + } + + /** + * Saves the structure viewer session to the given file + * + * @param f + */ + protected void saveSession(File f) + { + StructureCommandI cmd = commandGenerator + .saveSession(f.getPath()); + if (cmd != null) + { + executeCommand(cmd, false); + } + } + + /** + * Returns true if the viewer is an external structure viewer for which the + * process is still alive, else false (for Jmol, or an external viewer which + * the user has independently closed) + * + * @return + */ + public boolean isViewerRunning() + { + return false; + } + + /** + * Closes Jalview's structure viewer panel and releases associated resources. + * If it is managing an external viewer program, and {@code forceClose} is + * true, also shuts down that program. + * + * @param forceClose + */ + public void closeViewer(boolean forceClose) + { + getSsm().removeStructureViewerListener(this, this.getStructureFiles()); + releaseUIResources(); + + // add external viewer shutdown in overrides + // todo - or can maybe pull up to here + } }