From 1dd1e3cd8272449af3fe4dfa3f69239ca4ae471b Mon Sep 17 00:00:00 2001 From: gmungoc Date: Thu, 30 Apr 2020 11:50:27 +0100 Subject: [PATCH] JAL-3551 save structure viewer session refactorings, PyMol added --- src/jalview/ext/jmol/JalviewJmolBinding.java | 38 ++- src/jalview/ext/jmol/JmolCommands.java | 17 +- src/jalview/ext/pymol/PymolCommands.java | 13 +- .../ext/rbvi/chimera/JalviewChimeraBinding.java | 36 +-- src/jalview/gui/AppJmol.java | 34 ++- src/jalview/gui/AppJmolBinding.java | 49 +++- src/jalview/gui/ChimeraViewFrame.java | 71 ------ src/jalview/gui/JalviewChimeraXBindingModel.java | 14 +- src/jalview/gui/PymolBindingModel.java | 29 ++- src/jalview/gui/PymolViewer.java | 56 +++-- src/jalview/gui/StructureViewerBase.java | 50 ++-- src/jalview/project/Jalview2XML.java | 260 ++++++++++++-------- .../structures/models/AAStructureBindingModel.java | 76 +++++- 13 files changed, 426 insertions(+), 317 deletions(-) diff --git a/src/jalview/ext/jmol/JalviewJmolBinding.java b/src/jalview/ext/jmol/JalviewJmolBinding.java index 4c19f6e..17433c5 100644 --- a/src/jalview/ext/jmol/JalviewJmolBinding.java +++ b/src/jalview/ext/jmol/JalviewJmolBinding.java @@ -20,19 +20,6 @@ */ package jalview.ext.jmol; -import jalview.api.FeatureRenderer; -import jalview.datamodel.PDBEntry; -import jalview.datamodel.SequenceI; -import jalview.gui.IProgressIndicator; -import jalview.gui.StructureViewer.ViewerType; -import jalview.io.DataSourceType; -import jalview.io.StructureFile; -import jalview.structure.AtomSpec; -import jalview.structure.StructureCommand; -import jalview.structure.StructureCommandI; -import jalview.structure.StructureSelectionManager; -import jalview.structures.models.AAStructureBindingModel; - import java.awt.Container; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; @@ -52,6 +39,19 @@ import org.jmol.api.JmolViewer; import org.jmol.c.CBK; import org.jmol.viewer.Viewer; +import jalview.api.FeatureRenderer; +import jalview.datamodel.PDBEntry; +import jalview.datamodel.SequenceI; +import jalview.gui.IProgressIndicator; +import jalview.gui.StructureViewer.ViewerType; +import jalview.io.DataSourceType; +import jalview.io.StructureFile; +import jalview.structure.AtomSpec; +import jalview.structure.StructureCommand; +import jalview.structure.StructureCommandI; +import jalview.structure.StructureSelectionManager; +import jalview.structures.models.AAStructureBindingModel; + public abstract class JalviewJmolBinding extends AAStructureBindingModel implements JmolStatusListener, JmolSelectionListener, ComponentListener @@ -974,4 +974,16 @@ public abstract class JalviewJmolBinding extends AAStructureBindingModel { return String.valueOf(pdbfnum + 1); } + + /** + * Returns ".spt" - the Jmol session file extension + * + * @return + * @see https://chemapps.stolaf.edu/jmol/docs/#writemodel + */ + @Override + public String getSessionFileExtension() + { + return ".spt"; + } } diff --git a/src/jalview/ext/jmol/JmolCommands.java b/src/jalview/ext/jmol/JmolCommands.java index bd44921..797c25b 100644 --- a/src/jalview/ext/jmol/JmolCommands.java +++ b/src/jalview/ext/jmol/JmolCommands.java @@ -20,6 +20,12 @@ */ package jalview.ext.jmol; +import java.awt.Color; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + import jalview.api.AlignViewportI; import jalview.api.AlignmentViewPanel; import jalview.api.FeatureRenderer; @@ -36,12 +42,6 @@ import jalview.structure.StructureMapping; import jalview.structure.StructureSelectionManager; import jalview.util.Comparison; -import java.awt.Color; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Map; - /** * Routines for generating Jmol commands for Jalview/Jmol binding * @@ -372,10 +372,9 @@ public class JmolCommands extends StructureCommandsBase public StructureCommandI saveSession(String filepath) { /* - * https://chemapps.stolaf.edu/jmol/docs/#write - * not currently used in Jalview + * https://chemapps.stolaf.edu/jmol/docs/#writemodel */ - return new StructureCommand("write \"" + filepath + "\""); + return new StructureCommand("write STATE \"" + filepath + "\""); } @Override diff --git a/src/jalview/ext/pymol/PymolCommands.java b/src/jalview/ext/pymol/PymolCommands.java index 910aae1..115efa1 100644 --- a/src/jalview/ext/pymol/PymolCommands.java +++ b/src/jalview/ext/pymol/PymolCommands.java @@ -1,14 +1,14 @@ package jalview.ext.pymol; +import java.awt.Color; +import java.util.ArrayList; +import java.util.List; + import jalview.structure.AtomSpecModel; import jalview.structure.StructureCommand; import jalview.structure.StructureCommandI; import jalview.structure.StructureCommandsBase; -import java.awt.Color; -import java.util.ArrayList; -import java.util.List; - /** * A class that generates commands to send to PyMol over its XML-RPC interface. *

@@ -119,9 +119,8 @@ public class PymolCommands extends StructureCommandsBase @Override public StructureCommandI openCommandFile(String path) { - // where is this documented by PyMol? - // todo : xml-rpc answers 'method "@" is not supported' - return new StructureCommand("@" + path); // should be .pml + // https://pymolwiki.org/index.php/Run + return new StructureCommand("run", path); // should be .pml } @Override diff --git a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java index 3553d68..732fb16 100644 --- a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java +++ b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java @@ -20,6 +20,22 @@ */ package jalview.ext.rbvi.chimera; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.net.BindException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import ext.edu.ucsf.rbvi.strucviz2.ChimeraManager; +import ext.edu.ucsf.rbvi.strucviz2.ChimeraModel; +import ext.edu.ucsf.rbvi.strucviz2.StructureManager; +import ext.edu.ucsf.rbvi.strucviz2.StructureManager.ModelType; import jalview.api.AlignmentViewPanel; import jalview.api.structures.JalviewStructureDisplayI; import jalview.bin.Cache; @@ -38,23 +54,6 @@ import jalview.structure.StructureCommandI; import jalview.structure.StructureSelectionManager; import jalview.structures.models.AAStructureBindingModel; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintWriter; -import java.net.BindException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import ext.edu.ucsf.rbvi.strucviz2.ChimeraManager; -import ext.edu.ucsf.rbvi.strucviz2.ChimeraModel; -import ext.edu.ucsf.rbvi.strucviz2.StructureManager; -import ext.edu.ucsf.rbvi.strucviz2.StructureManager.ModelType; - public abstract class JalviewChimeraBinding extends AAStructureBindingModel { public static final String CHIMERA_FEATURE_GROUP = "Chimera"; @@ -864,10 +863,11 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel } /** - * Returns the file extension to use for a saved viewer session file + * Returns the file extension to use for a saved viewer session file (.py) * * @return */ + @Override public String getSessionFileExtension() { return ".py"; diff --git a/src/jalview/gui/AppJmol.java b/src/jalview/gui/AppJmol.java index 4087e63..581dc3d 100644 --- a/src/jalview/gui/AppJmol.java +++ b/src/jalview/gui/AppJmol.java @@ -20,20 +20,6 @@ */ package jalview.gui; -import jalview.api.AlignmentViewPanel; -import jalview.bin.Cache; -import jalview.datamodel.AlignmentI; -import jalview.datamodel.PDBEntry; -import jalview.datamodel.SequenceI; -import jalview.gui.StructureViewer.ViewerType; -import jalview.structure.StructureCommand; -import jalview.structures.models.AAStructureBindingModel; -import jalview.util.BrowserLauncher; -import jalview.util.ImageMaker; -import jalview.util.MessageManager; -import jalview.util.Platform; -import jalview.ws.dbsources.Pdb; - import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; @@ -50,6 +36,20 @@ import javax.swing.SwingUtilities; import javax.swing.event.InternalFrameAdapter; import javax.swing.event.InternalFrameEvent; +import jalview.api.AlignmentViewPanel; +import jalview.bin.Cache; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.PDBEntry; +import jalview.datamodel.SequenceI; +import jalview.gui.StructureViewer.ViewerType; +import jalview.structure.StructureCommand; +import jalview.structures.models.AAStructureBindingModel; +import jalview.util.BrowserLauncher; +import jalview.util.ImageMaker; +import jalview.util.MessageManager; +import jalview.util.Platform; +import jalview.ws.dbsources.Pdb; + public class AppJmol extends StructureViewerBase { // ms to wait for Jmol to load files @@ -666,12 +666,6 @@ public class AppJmol extends StructureViewerBase } @Override - public String getStateInfo() - { - return jmb == null ? null : jmb.jmolViewer.getStateInfo(); - } - - @Override public ViewerType getViewerType() { return ViewerType.JMOL; diff --git a/src/jalview/gui/AppJmolBinding.java b/src/jalview/gui/AppJmolBinding.java index db698ac..34ff7b3 100644 --- a/src/jalview/gui/AppJmolBinding.java +++ b/src/jalview/gui/AppJmolBinding.java @@ -20,16 +20,10 @@ */ package jalview.gui; -import jalview.api.AlignmentViewPanel; -import jalview.api.structures.JalviewStructureDisplayI; -import jalview.bin.Cache; -import jalview.datamodel.PDBEntry; -import jalview.datamodel.SequenceI; -import jalview.ext.jmol.JalviewJmolBinding; -import jalview.io.DataSourceType; -import jalview.structure.StructureSelectionManager; - import java.awt.Container; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.util.Map; import javax.swing.JComponent; @@ -38,6 +32,15 @@ import org.jmol.api.JmolAppConsoleInterface; import org.jmol.java.BS; import org.openscience.jmol.app.jmolpanel.console.AppConsole; +import jalview.api.AlignmentViewPanel; +import jalview.api.structures.JalviewStructureDisplayI; +import jalview.bin.Cache; +import jalview.datamodel.PDBEntry; +import jalview.datamodel.SequenceI; +import jalview.ext.jmol.JalviewJmolBinding; +import jalview.io.DataSourceType; +import jalview.structure.StructureSelectionManager; + public class AppJmolBinding extends JalviewJmolBinding { public AppJmolBinding(AppJmol appJmol, StructureSelectionManager sSm, @@ -165,4 +168,32 @@ public class AppJmolBinding extends JalviewJmolBinding // TODO Auto-generated method stub return null; } + + /** + * Overrides the default method to save a session to file, in order to + * guarantee it is done synchronously. Jmol command 'write STATE path' would + * execute asynchronously, so instead we get the state and write it directly + * here. + */ + @Override + protected void saveSession(File f) + { + String state = jmolViewer.getStateInfo(); + if (state != null) + { + try + { + FileWriter fw = new FileWriter(f); + fw.write(state); + fw.close(); + } catch (IOException e) + { + Cache.log.error("Error writing Jmol state: " + e.toString()); + } + } + else + { + Cache.log.error("Error requesting Jmol state to save"); + } + } } diff --git a/src/jalview/gui/ChimeraViewFrame.java b/src/jalview/gui/ChimeraViewFrame.java index a6e479d..0d6a216 100644 --- a/src/jalview/gui/ChimeraViewFrame.java +++ b/src/jalview/gui/ChimeraViewFrame.java @@ -604,77 +604,6 @@ public class ChimeraViewFrame extends StructureViewerBase return jmb; } - /** - * Ask Chimera to save its session to the designated file path, or to a - * temporary file if the path is null. Returns the file path if successful, - * else null. - * - * @param filepath - * @see getStateInfo - */ - protected String saveSession(String filepath) - { - String pathUsed = filepath; - try - { - if (pathUsed == null) - { - String suffix = jmb.getSessionFileExtension(); - File tempFile = File.createTempFile("chimera", suffix); - tempFile.deleteOnExit(); - pathUsed = tempFile.getPath(); - } - boolean result = jmb.saveSession(pathUsed); - if (result) - { - this.chimeraSessionFile = pathUsed; - return pathUsed; - } - } catch (IOException e) - { - } - return null; - } - - /** - * Returns a string representing the state of the Chimera session. This is - * done by requesting Chimera to save its session to a temporary file, then - * reading the file contents. Returns an empty string on any error. - */ - @Override - public String getStateInfo() - { - String sessionFile = saveSession(null); - if (sessionFile == null) - { - return ""; - } - InputStream is = null; - try - { - File f = new File(sessionFile); - byte[] bytes = new byte[(int) f.length()]; - is = new FileInputStream(sessionFile); - is.read(bytes); - return new String(bytes); - } catch (IOException e) - { - return ""; - } finally - { - if (is != null) - { - try - { - is.close(); - } catch (IOException e) - { - // ignore - } - } - } - } - @Override protected void fitToWindow_actionPerformed() { diff --git a/src/jalview/gui/JalviewChimeraXBindingModel.java b/src/jalview/gui/JalviewChimeraXBindingModel.java index 3124fc1..d7b5a94 100644 --- a/src/jalview/gui/JalviewChimeraXBindingModel.java +++ b/src/jalview/gui/JalviewChimeraXBindingModel.java @@ -1,5 +1,10 @@ package jalview.gui; +import java.util.List; + +import ext.edu.ucsf.rbvi.strucviz2.ChimeraModel; +import ext.edu.ucsf.rbvi.strucviz2.StructureManager; +import ext.edu.ucsf.rbvi.strucviz2.StructureManager.ModelType; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; import jalview.ext.rbvi.chimera.ChimeraXCommands; @@ -8,12 +13,6 @@ import jalview.io.DataSourceType; import jalview.structure.StructureCommand; import jalview.structure.StructureSelectionManager; -import java.util.List; - -import ext.edu.ucsf.rbvi.strucviz2.ChimeraModel; -import ext.edu.ucsf.rbvi.strucviz2.StructureManager; -import ext.edu.ucsf.rbvi.strucviz2.StructureManager.ModelType; - public class JalviewChimeraXBindingModel extends JalviewChimeraBindingModel { @@ -58,9 +57,10 @@ public class JalviewChimeraXBindingModel extends JalviewChimeraBindingModel } /** - * Returns the file extension to use for a saved viewer session file + * Returns the file extension to use for a saved viewer session file (.cxs) * * @return + * @see https://www.cgl.ucsf.edu/chimerax/docs/user/commands/save.html#sesformat */ @Override public String getSessionFileExtension() diff --git a/src/jalview/gui/PymolBindingModel.java b/src/jalview/gui/PymolBindingModel.java index af4afb0..6787c8a 100644 --- a/src/jalview/gui/PymolBindingModel.java +++ b/src/jalview/gui/PymolBindingModel.java @@ -1,5 +1,10 @@ package jalview.gui; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + import jalview.api.AlignmentViewPanel; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; @@ -11,11 +16,6 @@ import jalview.structure.StructureCommandI; import jalview.structure.StructureSelectionManager; import jalview.structures.models.AAStructureBindingModel; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - public class PymolBindingModel extends AAStructureBindingModel { private PymolManager pymolManager; @@ -72,7 +72,7 @@ public class PymolBindingModel extends AAStructureBindingModel protected List executeCommand(StructureCommandI command, boolean getReply) { - System.out.println(command.toString()); // debug + // System.out.println(command.toString()); // debug return pymolManager.sendCommand(command, getReply); } @@ -100,11 +100,6 @@ public class PymolBindingModel extends AAStructureBindingModel { pymolManager.exitPymol(); } - // if (this.pymolListener != null) - // { - // pymolListener.shutdown(); - // pymolListener = null; - // } pymolManager = null; if (pymolMonitor != null) @@ -173,4 +168,16 @@ public class PymolBindingModel extends AAStructureBindingModel return file; } + /** + * Returns the file extension to use for a saved viewer session file (.pse) + * + * @return + * @see https://pymolwiki.org/index.php/Save + */ + @Override + public String getSessionFileExtension() + { + return ".pse"; + } + } diff --git a/src/jalview/gui/PymolViewer.java b/src/jalview/gui/PymolViewer.java index 8f7f2c1..d0c9ea2 100644 --- a/src/jalview/gui/PymolViewer.java +++ b/src/jalview/gui/PymolViewer.java @@ -1,5 +1,13 @@ package jalview.gui; +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JInternalFrame; +import javax.swing.event.InternalFrameAdapter; +import javax.swing.event.InternalFrameEvent; + import jalview.api.AlignmentViewPanel; import jalview.api.FeatureRenderer; import jalview.bin.Cache; @@ -11,14 +19,6 @@ import jalview.io.StructureFile; import jalview.structures.models.AAStructureBindingModel; import jalview.util.MessageManager; -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -import javax.swing.JInternalFrame; -import javax.swing.event.InternalFrameAdapter; -import javax.swing.event.InternalFrameEvent; - public class PymolViewer extends StructureViewerBase { private static final int myWidth = 500; @@ -57,6 +57,40 @@ public class PymolViewer extends StructureViewerBase openNewPymol(ap, pe, seqs); } + /** + * Constructor given a session file to be restored + * + * @param sessionFile + * @param alignPanel + * @param pdbArray + * @param seqsArray + * @param colourByPymol + * @param colourBySequence + * @param newViewId + */ + public PymolViewer(String sessionFile, AlignmentPanel alignPanel, + PDBEntry[] pdbArray, SequenceI[][] seqsArray, + boolean colourByPymol, boolean colourBySequence, String newViewId) + { + // TODO convert to base/factory class method + this(); + setViewId(newViewId); + this.pymolSessionFile = sessionFile; + openNewPymol(alignPanel, pdbArray, seqsArray); + if (colourByPymol) + { + binding.setColourBySequence(false); + seqColour.setSelected(false); + viewerColour.setSelected(true); + } + else if (colourBySequence) + { + binding.setColourBySequence(true); + seqColour.setSelected(true); + viewerColour.setSelected(false); + } + } + private void openNewPymol(AlignmentPanel ap, PDBEntry[] pe, SequenceI[][] seqs) { @@ -320,12 +354,6 @@ public class PymolViewer extends StructureViewerBase } @Override - public String getStateInfo() - { - return null; - } - - @Override public ViewerType getViewerType() { return ViewerType.PYMOL; diff --git a/src/jalview/gui/StructureViewerBase.java b/src/jalview/gui/StructureViewerBase.java index 25d9998..0c5c5f0 100644 --- a/src/jalview/gui/StructureViewerBase.java +++ b/src/jalview/gui/StructureViewerBase.java @@ -20,24 +20,6 @@ */ package jalview.gui; -import jalview.api.AlignmentViewPanel; -import jalview.bin.Cache; -import jalview.datamodel.AlignmentI; -import jalview.datamodel.PDBEntry; -import jalview.datamodel.SequenceI; -import jalview.gui.StructureViewer.ViewerType; -import jalview.gui.ViewSelectionMenu.ViewSetProvider; -import jalview.io.DataSourceType; -import jalview.io.JalviewFileChooser; -import jalview.io.JalviewFileView; -import jalview.jbgui.GStructureViewer; -import jalview.schemes.ColourSchemeI; -import jalview.schemes.ColourSchemes; -import jalview.structure.StructureMapping; -import jalview.structures.models.AAStructureBindingModel; -import jalview.util.MessageManager; -import jalview.ws.dbsources.Pdb; - import java.awt.Color; import java.awt.Component; import java.awt.event.ActionEvent; @@ -64,6 +46,24 @@ import javax.swing.JRadioButtonMenuItem; import javax.swing.event.MenuEvent; import javax.swing.event.MenuListener; +import jalview.api.AlignmentViewPanel; +import jalview.bin.Cache; +import jalview.datamodel.AlignmentI; +import jalview.datamodel.PDBEntry; +import jalview.datamodel.SequenceI; +import jalview.gui.StructureViewer.ViewerType; +import jalview.gui.ViewSelectionMenu.ViewSetProvider; +import jalview.io.DataSourceType; +import jalview.io.JalviewFileChooser; +import jalview.io.JalviewFileView; +import jalview.jbgui.GStructureViewer; +import jalview.schemes.ColourSchemeI; +import jalview.schemes.ColourSchemes; +import jalview.structure.StructureMapping; +import jalview.structures.models.AAStructureBindingModel; +import jalview.util.MessageManager; +import jalview.ws.dbsources.Pdb; + /** * Base class with common functionality for JMol, Chimera or other structure * viewers. @@ -198,8 +198,6 @@ public abstract class StructureViewerBase extends GStructureViewer this.viewId = viewId; } - public abstract String getStateInfo(); - protected void buildActionMenu() { if (_alignwith == null) @@ -1141,4 +1139,16 @@ public abstract class StructureViewerBase extends GStructureViewer return filePath; } + /** + * If supported, saves the state of the structure viewer to a temporary file + * and returns the file, else returns null + * + * @return + */ + public File saveSession() + { + // TODO: a wait loop to ensure the file is written fully before returning? + return getBinding() == null ? null : getBinding().saveSession(); + } + } diff --git a/src/jalview/project/Jalview2XML.java b/src/jalview/project/Jalview2XML.java index df4a93e..e7af837 100644 --- a/src/jalview/project/Jalview2XML.java +++ b/src/jalview/project/Jalview2XML.java @@ -24,6 +24,55 @@ import static jalview.math.RotatableMatrix.Axis.X; import static jalview.math.RotatableMatrix.Axis.Y; import static jalview.math.RotatableMatrix.Axis.Z; +import java.awt.Color; +import java.awt.Font; +import java.awt.Rectangle; +import java.io.BufferedReader; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.lang.reflect.InvocationTargetException; +import java.math.BigInteger; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.Vector; +import java.util.jar.JarEntry; +import java.util.jar.JarInputStream; +import java.util.jar.JarOutputStream; + +import javax.swing.JInternalFrame; +import javax.swing.SwingUtilities; +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBElement; +import javax.xml.bind.Marshaller; +import javax.xml.datatype.DatatypeConfigurationException; +import javax.xml.datatype.DatatypeFactory; +import javax.xml.datatype.XMLGregorianCalendar; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamReader; + import jalview.analysis.Conservation; import jalview.analysis.PCA; import jalview.analysis.scoremodels.ScoreModels; @@ -64,6 +113,7 @@ import jalview.gui.JvOptionPane; import jalview.gui.OOMWarning; import jalview.gui.PCAPanel; import jalview.gui.PaintRefresher; +import jalview.gui.PymolViewer; import jalview.gui.SplitFrame; import jalview.gui.StructureViewer; import jalview.gui.StructureViewer.ViewerType; @@ -150,54 +200,6 @@ import jalview.xml.binding.jalview.SequenceSet.SequenceSetProperties; import jalview.xml.binding.jalview.ThresholdType; import jalview.xml.binding.jalview.VAMSAS; -import java.awt.Color; -import java.awt.Font; -import java.awt.Rectangle; -import java.io.BufferedReader; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.lang.reflect.InvocationTargetException; -import java.math.BigInteger; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Enumeration; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Hashtable; -import java.util.IdentityHashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.Vector; -import java.util.jar.JarEntry; -import java.util.jar.JarInputStream; -import java.util.jar.JarOutputStream; - -import javax.swing.JInternalFrame; -import javax.swing.SwingUtilities; -import javax.xml.bind.JAXBContext; -import javax.xml.bind.JAXBElement; -import javax.xml.bind.Marshaller; -import javax.xml.datatype.DatatypeConfigurationException; -import javax.xml.datatype.DatatypeFactory; -import javax.xml.datatype.XMLGregorianCalendar; -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.XMLStreamReader; - /** * Write out the current jalview desktop state as a Jalview XML stream. * @@ -1087,15 +1089,17 @@ public class Jalview2XML if (!storeDS && !viewIds.contains(viewId)) { viewIds.add(viewId); - try + File viewerState = viewFrame.saveSession(); + if (viewerState != null) { - String viewerState = viewFrame.getStateInfo(); - writeJarEntry(jout, getViewerJarEntryName(viewId), - viewerState.getBytes()); - } catch (IOException e) + copyFileToJar(jout, viewerState.getPath(), + getViewerJarEntryName(viewId)); + } + else { - System.err.println( - "Error saving viewer state: " + e.getMessage()); + Cache.log.error("Failed to save viewer state for " + + + viewFrame.getViewerType().toString()); } } } @@ -1992,32 +1996,23 @@ public class Jalview2XML protected void copyFileToJar(JarOutputStream jout, String infilePath, String jarEntryName) { - DataInputStream dis = null; - try + try (InputStream is = new FileInputStream(infilePath)) { File file = new File(infilePath); if (file.exists() && jout != null) { - dis = new DataInputStream(new FileInputStream(file)); - byte[] data = new byte[(int) file.length()]; - dis.readFully(data); - writeJarEntry(jout, jarEntryName, data); + System.out.println("Writing jar entry " + jarEntryName); + jout.putNextEntry(new JarEntry(jarEntryName)); + copyAll(is, jout); + jout.closeEntry(); + // dis = new DataInputStream(new FileInputStream(file)); + // byte[] data = new byte[(int) file.length()]; + // dis.readFully(data); + // writeJarEntry(jout, jarEntryName, data); } } catch (Exception ex) { ex.printStackTrace(); - } finally - { - if (dis != null) - { - try - { - dis.close(); - } catch (IOException e) - { - // ignore - } - } } } @@ -2044,6 +2039,24 @@ public class Jalview2XML } /** + * Copies input to output, in 4K buffers; handles any data (text or binary) + * + * @param in + * @param out + * @throws IOException + */ + protected void copyAll(InputStream in, OutputStream out) + throws IOException + { + byte[] buffer = new byte[4096]; + int bytesRead = 0; + while ((bytesRead = in.read(buffer)) != -1) + { + out.write(buffer, 0, bytesRead); + } + } + + /** * Save the state of a structure viewer * * @param ap @@ -3171,8 +3184,6 @@ public class Jalview2XML protected String copyJarEntry(jarInputStreamProvider jprovider, String jarEntryName, String prefix, String suffixModel) { - BufferedReader in = null; - PrintWriter out = null; String suffix = ".tmp"; if (suffixModel == null) { @@ -3183,33 +3194,24 @@ public class Jalview2XML { suffix = "." + suffixModel.substring(sfpos + 1); } - try - { - JarInputStream jin = jprovider.getJarInputStream(); - /* - * if (jprovider.startsWith("http://")) { jin = new JarInputStream(new - * URL(jprovider).openStream()); } else { jin = new JarInputStream(new - * FileInputStream(jprovider)); } - */ + try (JarInputStream jin = jprovider.getJarInputStream()) + { JarEntry entry = null; do { entry = jin.getNextJarEntry(); } while (entry != null && !entry.getName().equals(jarEntryName)); + if (entry != null) { - in = new BufferedReader(new InputStreamReader(jin, UTF_8)); + // in = new BufferedReader(new InputStreamReader(jin, UTF_8)); File outFile = File.createTempFile(prefix, suffix); outFile.deleteOnExit(); - out = new PrintWriter(new FileOutputStream(outFile)); - String data; - - while ((data = in.readLine()) != null) + try (OutputStream os = new FileOutputStream(outFile)) { - out.println(data); + copyAll(jin, os); } - out.flush(); String t = outFile.getAbsolutePath(); return t; } @@ -3220,22 +3222,6 @@ public class Jalview2XML } catch (Exception ex) { ex.printStackTrace(); - } finally - { - if (in != null) - { - try - { - in.close(); - } catch (IOException e) - { - // ignore - } - } - if (out != null) - { - out.close(); - } } return null; @@ -4414,10 +4400,15 @@ public class Jalview2XML * From 2.9: stateData.type contains JMOL or CHIMERA, data is in jar entry * "viewer_"+stateData.viewId */ - if (ViewerType.CHIMERA.toString().equals(stateData.getType())) + String viewerType = stateData.getType(); + if (ViewerType.CHIMERA.toString().equals(viewerType)) { createChimeraViewer(viewerData, af, jprovider); } + if (ViewerType.PYMOL.toString().equals(viewerType)) + { + createPymolViewer(viewerData, af, jprovider); + } else { /* @@ -6335,6 +6326,61 @@ public class Jalview2XML } /** + * Create a new PyMol viewer + * + * @param data + * @param af + * @param jprovider + */ + protected void createPymolViewer( + Entry viewerData, AlignFrame af, + jarInputStreamProvider jprovider) + { + StructureViewerModel data = viewerData.getValue(); + String pymolSessionFile = data.getStateData(); + + /* + * Copy PyMol session from jar entry "viewer_"+viewId to a temporary file + * + * NB this is the 'saved' viewId as in the project file XML, _not_ the + * 'uniquified' sviewid used to reconstruct the viewer here + */ + String viewerJarEntryName = getViewerJarEntryName(data.getViewId()); + pymolSessionFile = copyJarEntry(jprovider, viewerJarEntryName, + "pymol", ".pse"); + + Set> fileData = data.getFileData() + .entrySet(); + List pdbs = new ArrayList<>(); + List allseqs = new ArrayList<>(); + for (Entry pdb : fileData) + { + String filePath = pdb.getValue().getFilePath(); + String pdbId = pdb.getValue().getPdbId(); + // pdbs.add(new PDBEntry(filePath, pdbId)); + pdbs.add(new PDBEntry(pdbId, null, PDBEntry.Type.PDB, filePath)); + final List seqList = pdb.getValue().getSeqList(); + SequenceI[] seqs = seqList.toArray(new SequenceI[seqList.size()]); + allseqs.add(seqs); + } + + boolean colourByPymol = data.isColourByViewer(); + boolean colourBySequence = data.isColourWithAlignPanel(); + + // TODO use StructureViewer as a factory here, see JAL-1761 + final PDBEntry[] pdbArray = pdbs.toArray(new PDBEntry[pdbs.size()]); + final SequenceI[][] seqsArray = allseqs + .toArray(new SequenceI[allseqs.size()][]); + String newViewId = viewerData.getKey(); + + PymolViewer pv = new PymolViewer(pymolSessionFile, + af.alignPanel, pdbArray, seqsArray, colourByPymol, + colourBySequence, newViewId); + pv.setSize(data.getWidth(), data.getHeight()); + pv.setLocation(data.getX(), data.getY()); + } + + /** * Populates an XML model of the feature colour scheme for one feature type * * @param featureType diff --git a/src/jalview/structures/models/AAStructureBindingModel.java b/src/jalview/structures/models/AAStructureBindingModel.java index db523f9..7899c40 100644 --- a/src/jalview/structures/models/AAStructureBindingModel.java +++ b/src/jalview/structures/models/AAStructureBindingModel.java @@ -20,12 +20,26 @@ */ 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; @@ -46,17 +60,6 @@ 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. @@ -1513,4 +1516,55 @@ 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); + } + } } -- 1.7.10.2