From 36933f9b768b9f8ace600215c1ddb4882331b92b Mon Sep 17 00:00:00 2001 From: gmungoc Date: Wed, 10 Jun 2015 11:23:17 +0100 Subject: [PATCH] JAL-1764 save Chimera/Jmol state as separate entries in project Jar --- src/jalview/datamodel/StructureViewerModel.java | 10 +- src/jalview/gui/AlignFrame.java | 5 - src/jalview/gui/AppJmol.java | 7 + src/jalview/gui/ChimeraViewFrame.java | 88 +++- src/jalview/gui/Desktop.java | 29 -- src/jalview/gui/Jalview2XML.java | 607 +++++++++++++++-------- src/jalview/gui/StructureViewerBase.java | 9 +- 7 files changed, 505 insertions(+), 250 deletions(-) diff --git a/src/jalview/datamodel/StructureViewerModel.java b/src/jalview/datamodel/StructureViewerModel.java index 098372b..c23e320 100644 --- a/src/jalview/datamodel/StructureViewerModel.java +++ b/src/jalview/datamodel/StructureViewerModel.java @@ -27,6 +27,8 @@ public class StructureViewerModel private String stateData = ""; + private String viewId; + private Map fileData = new HashMap(); public class StructureData @@ -36,7 +38,6 @@ public class StructureViewerModel private String pdbId; private List seqList; - // TODO and possibly a list of chains? /** @@ -85,7 +86,7 @@ public class StructureViewerModel public StructureViewerModel(int x, int y, int width, int height, boolean alignWithPanel, boolean colourWithAlignPanel, - boolean colourByViewer) + boolean colourByViewer, String viewId) { this.x = x; this.y = y; @@ -94,6 +95,7 @@ public class StructureViewerModel this.alignWithPanel = alignWithPanel; this.colourWithAlignPanel = colourWithAlignPanel; this.colourByViewer = colourByViewer; + this.viewId = viewId; } public int getX() @@ -186,4 +188,8 @@ public class StructureViewerModel this.fileData = fileData; } + public String getViewId() + { + return this.viewId; + } } diff --git a/src/jalview/gui/AlignFrame.java b/src/jalview/gui/AlignFrame.java index ad2bbc3..13a4934 100644 --- a/src/jalview/gui/AlignFrame.java +++ b/src/jalview/gui/AlignFrame.java @@ -1098,11 +1098,6 @@ public class AlignFrame extends GAlignFrame implements DropTargetListener, .lastIndexOf(java.io.File.separatorChar) + 1); } - /* - * First save any linked Chimera session. - */ - Desktop.instance.saveChimeraSessions(file); - success = new Jalview2XML().saveAlignment(this, file, shortName); statusBar.setText(MessageManager.formatMessage( diff --git a/src/jalview/gui/AppJmol.java b/src/jalview/gui/AppJmol.java index a3f6778..6139669 100644 --- a/src/jalview/gui/AppJmol.java +++ b/src/jalview/gui/AppJmol.java @@ -57,6 +57,7 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.ColumnSelection; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; +import jalview.gui.StructureViewer.ViewerType; import jalview.io.AppletFormatAdapter; import jalview.io.JalviewFileChooser; import jalview.io.JalviewFileView; @@ -1234,4 +1235,10 @@ public class AppJmol extends StructureViewerBase return jmb == null ? null : jmb.viewer.getStateInfo(); } + @Override + public ViewerType getViewerType() + { + return ViewerType.JMOL; + } + } diff --git a/src/jalview/gui/ChimeraViewFrame.java b/src/jalview/gui/ChimeraViewFrame.java index b933345..1e6da78 100644 --- a/src/jalview/gui/ChimeraViewFrame.java +++ b/src/jalview/gui/ChimeraViewFrame.java @@ -26,9 +26,11 @@ import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.io.BufferedReader; import java.io.File; +import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; +import java.io.InputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; @@ -55,6 +57,7 @@ import jalview.datamodel.ColumnSelection; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; import jalview.ext.rbvi.chimera.JalviewChimeraBinding; +import jalview.gui.StructureViewer.ViewerType; import jalview.io.AppletFormatAdapter; import jalview.io.JalviewFileChooser; import jalview.io.JalviewFileView; @@ -445,23 +448,25 @@ public class ChimeraViewFrame extends StructureViewerBase /** * Create a new viewer from saved session state data including Chimera session - * file. - * - * @param chimeraSession + * file * + * @param chimeraSessionFile * @param alignPanel * @param pdbArray * @param seqsArray * @param colourByChimera * @param colourBySequence + * @param newViewId */ - public ChimeraViewFrame(String chimeraSession, AlignmentPanel alignPanel, + public ChimeraViewFrame(String chimeraSessionFile, + AlignmentPanel alignPanel, PDBEntry[] pdbArray, SequenceI[][] seqsArray, boolean colourByChimera, - boolean colourBySequence) + boolean colourBySequence, String newViewId) { super(); - this.chimeraSessionFile = chimeraSession; + setViewId(newViewId); + this.chimeraSessionFile = chimeraSessionFile; openNewChimera(alignPanel, pdbArray, seqsArray); if (colourByChimera) { @@ -1310,31 +1315,74 @@ public class ChimeraViewFrame extends StructureViewerBase } /** - * Ask Chimera to save its session to the designated file path. Returns true - * if successful, else false. + * 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 */ - public boolean saveSession(String filepath) + protected String saveSession(String filepath) { - boolean result = jmb.saveSession(filepath); - if (result) + String pathUsed = filepath; + try + { + if (pathUsed == null) + { + File tempFile = File.createTempFile("chimera", ".py"); + tempFile.deleteOnExit(); + pathUsed = tempFile.getPath(); + } + boolean result = jmb.saveSession(pathUsed); + if (result) + { + this.chimeraSessionFile = pathUsed; + return pathUsed; + } + } catch (IOException e) { - this.chimeraSessionFile = filepath; } - return result; + return null; } /** - * Returns the file path of the Chimera session file the last time it was - * saved. If it was never saved, returns an empty string. There is no - * guarantee that the Chimera session has not changed since it was saved. + * 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() { - return this.chimeraSessionFile == null ? "" : chimeraSessionFile; + 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ß + } + } + } + // return this.chimeraSessionFile == null ? "" : chimeraSessionFile; } @Override @@ -1342,4 +1390,10 @@ public class ChimeraViewFrame extends StructureViewerBase { jmb.focusView(); } + + @Override + public ViewerType getViewerType() + { + return ViewerType.CHIMERA; + } } diff --git a/src/jalview/gui/Desktop.java b/src/jalview/gui/Desktop.java index e8d7ffd..9e84407 100644 --- a/src/jalview/gui/Desktop.java +++ b/src/jalview/gui/Desktop.java @@ -1485,9 +1485,6 @@ public class Desktop extends jalview.jbgui.GDesktop implements final java.io.File choice = chooser.getSelectedFile(); setProjectFile(choice); - // TODO or move inside the new Thread? - saveChimeraSessions(choice.getAbsolutePath()); - new Thread(new Runnable() { public void run() @@ -1525,32 +1522,6 @@ public class Desktop extends jalview.jbgui.GDesktop implements } } - /** - * Request any open, linked Chimera sessions to save their state. - * - * @param jalviewProjectFilename - * the filename of the Jalview project; Chimera session files should - * be given distinct, but obviously related, names. - */ - public void saveChimeraSessions(String jalviewProjectFilename) - { - int i = 0; - for (JInternalFrame frame : getAllFrames()) - { - if (frame instanceof ChimeraViewFrame) - { - /* - * Construct a filename for the Chimera session by append _chimera.py - * to the Jalview project file name. - */ - String chimeraPath = jalviewProjectFilename + "_chimera_" + i - + ".py"; - ((ChimeraViewFrame) frame).saveSession(chimeraPath); - i++; - } - } - } - private void setProjectFile(File choice) { this.projectFile = choice; diff --git a/src/jalview/gui/Jalview2XML.java b/src/jalview/gui/Jalview2XML.java index f56e531..ce81e1c 100644 --- a/src/jalview/gui/Jalview2XML.java +++ b/src/jalview/gui/Jalview2XML.java @@ -20,6 +20,45 @@ */ package jalview.gui; +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.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Enumeration; +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.StringTokenizer; +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.JOptionPane; +import javax.swing.SwingUtilities; + +import org.exolab.castor.xml.Marshaller; +import org.exolab.castor.xml.Unmarshaller; + import jalview.api.structures.JalviewStructureDisplayI; import jalview.bin.Cache; import jalview.datamodel.AlignedCodonFrame; @@ -30,6 +69,7 @@ import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; import jalview.datamodel.StructureViewerModel; import jalview.datamodel.StructureViewerModel.StructureData; +import jalview.gui.StructureViewer.ViewerType; import jalview.schemabinding.version2.AlcodMap; import jalview.schemabinding.version2.AlcodonFrame; import jalview.schemabinding.version2.Annotation; @@ -83,44 +123,6 @@ import jalview.ws.params.ArgumentI; import jalview.ws.params.AutoCalcSetting; import jalview.ws.params.WsParamSetI; -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.net.MalformedURLException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Enumeration; -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.StringTokenizer; -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.JOptionPane; -import javax.swing.SwingUtilities; - -import org.exolab.castor.xml.Unmarshaller; - /** * Write out the current jalview desktop state as a Jalview XML stream. * @@ -133,6 +135,8 @@ import org.exolab.castor.xml.Unmarshaller; */ public class Jalview2XML { + private static final String UTF_8 = "UTF-8"; + /* * SequenceI reference -> XML ID string in jalview XML. Populated as XML reps * of sequence objects are created. @@ -380,7 +384,7 @@ public class Jalview2XML // NOTE UTF-8 MUST BE USED FOR WRITING UNICODE CHARS // ////////////////////////////////////////////////// - Vector shortNames = new Vector(); + List shortNames = new ArrayList(); // REVERSE ORDER for (int i = frames.length - 1; i > -1; i--) @@ -394,33 +398,7 @@ public class Jalview2XML continue; } - String shortName = af.getTitle(); - - if (shortName.indexOf(File.separatorChar) > -1) - { - shortName = shortName.substring(shortName - .lastIndexOf(File.separatorChar) + 1); - } - - int count = 1; - - while (shortNames.contains(shortName)) - { - if (shortName.endsWith("_" + (count - 1))) - { - shortName = shortName.substring(0, shortName.lastIndexOf("_")); - } - - shortName = shortName.concat("_" + count); - count++; - } - - shortNames.addElement(shortName); - - if (!shortName.endsWith(".xml")) - { - shortName = shortName + ".xml"; - } + String shortName = makeFilename(af, shortNames); int ap, apSize = af.alignPanels.size(); @@ -467,6 +445,47 @@ public class Jalview2XML } } + /** + * Generates a distinct file name, based on the title of the AlignFrame, by + * appending _n for increasing n until an unused name is generated. The new + * name (without its extension) is added to the list. + * + * @param af + * @param namesUsed + * @return the generated name, with .xml extension + */ + protected String makeFilename(AlignFrame af, List namesUsed) + { + String shortName = af.getTitle(); + + if (shortName.indexOf(File.separatorChar) > -1) + { + shortName = shortName.substring(shortName + .lastIndexOf(File.separatorChar) + 1); + } + + int count = 1; + + while (namesUsed.contains(shortName)) + { + if (shortName.endsWith("_" + (count - 1))) + { + shortName = shortName.substring(0, shortName.lastIndexOf("_")); + } + + shortName = shortName.concat("_" + count); + count++; + } + + namesUsed.add(shortName); + + if (!shortName.endsWith(".xml")) + { + shortName = shortName + ".xml"; + } + return shortName; + } + // USE THIS METHOD TO SAVE A SINGLE ALIGNMENT WINDOW public boolean saveAlignment(AlignFrame af, String jarFile, String fileName) @@ -735,7 +754,8 @@ public class Jalview2XML jalview.datamodel.PDBEntry entry = (jalview.datamodel.PDBEntry) en .nextElement(); - pdb.setId(entry.getId()); + String pdbId = entry.getId(); + pdb.setId(pdbId); pdb.setType(entry.getType()); /* @@ -753,6 +773,24 @@ public class Jalview2XML StructureViewerBase viewFrame = (StructureViewerBase) frames[f]; matchedFile = saveStructureState(ap, jds, pdb, entry, viewIds, matchedFile, viewFrame); + /* + * Only store each structure viewer's state once in each XML + * document. First time through only (storeDS==false) + */ + String viewId = viewFrame.getViewId(); + if (!storeDS && !viewIds.contains(viewId)) + { + viewIds.add(viewId); + try + { + writeJarEntry(jout, "viewer_" + viewId, + viewFrame.getStateInfo().getBytes()); + } catch (IOException e) + { + System.err.println("Error saving viewer state: " + + e.getMessage()); + } + } } } @@ -769,46 +807,14 @@ public class Jalview2XML pdbfiles = new ArrayList(); } - if (!pdbfiles.contains(entry.getId())) + if (!pdbfiles.contains(pdbId)) { - pdbfiles.add(entry.getId()); - DataInputStream dis = null; - try - { - File file = new File(matchedFile); - if (file.exists() && jout != null) - { - byte[] data = new byte[(int) file.length()]; - jout.putNextEntry(new JarEntry(entry.getId())); - dis = new DataInputStream(new FileInputStream(file)); - dis.readFully(data); - - DataOutputStream dout = new DataOutputStream(jout); - dout.write(data, 0, data.length); - dout.flush(); - jout.closeEntry(); - } - } catch (Exception ex) - { - ex.printStackTrace(); - } finally - { - if (dis != null) - { - try - { - dis.close(); - } catch (IOException e) - { - // ignore - } - } - } - + pdbfiles.add(pdbId); + copyFileToJar(jout, matchedFile, pdbId); } } - if (entry.getProperty() != null) + if (entry.getProperty() != null && !entry.getProperty().isEmpty()) { PdbentryItem item = new PdbentryItem(); Hashtable properties = entry.getProperty(); @@ -935,6 +941,25 @@ public class Jalview2XML } } } + + /* + * Save associated Varna panels + */ + if (Desktop.desktop != null) + { + for (JInternalFrame frame : Desktop.desktop.getAllFrames()) + { + if (frame instanceof AppVarna) + { + AppVarna vp = (AppVarna) frame; + if (vp.ap == ap) + { + // save Varna state + } + } + } + } + // SAVE ANNOTATIONS /** * store forward refs from an annotationRow to any groups @@ -1288,9 +1313,8 @@ public class Jalview2XML JarEntry entry = new JarEntry(fileName); jout.putNextEntry(entry); PrintWriter pout = new PrintWriter(new OutputStreamWriter(jout, - "UTF-8")); - org.exolab.castor.xml.Marshaller marshaller = new org.exolab.castor.xml.Marshaller( - pout); + UTF_8)); + Marshaller marshaller = new Marshaller(pout); marshaller.marshal(object); pout.flush(); jout.closeEntry(); @@ -1304,6 +1328,66 @@ public class Jalview2XML } /** + * Copy the contents of a file to a new file added to the output jar + * + * @param jout + * @param infilePath + * @param jarfileName + */ + protected void copyFileToJar(JarOutputStream jout, String infilePath, + String jarfileName) + { + DataInputStream dis = null; + try + { + 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, jarfileName, data); + } + } catch (Exception ex) + { + ex.printStackTrace(); + } finally + { + if (dis != null) + { + try + { + dis.close(); + } catch (IOException e) + { + // ignore + } + } + } + } + + /** + * Write the data to a new entry of given name in the output jar file + * + * @param jout + * @param jarfileName + * @param data + * @throws IOException + */ + protected void writeJarEntry(JarOutputStream jout, String jarfileName, + byte[] data) throws IOException + { + if (jout != null) + { + jout.putNextEntry(new JarEntry(jarfileName)); + DataOutputStream dout = new DataOutputStream(jout); + dout.write(data, 0, data.length); + dout.flush(); + jout.closeEntry(); + } + } + + /** * Save the state of a structure viewer * * @param ap @@ -1321,6 +1405,11 @@ public class Jalview2XML String matchedFile, StructureViewerBase viewFrame) { final AAStructureBindingModel bindingModel = viewFrame.getBinding(); + + /* + * Look for any bindings for this viewer to the PDB file of interest + * (including part matches excluding chain id) + */ for (int peid = 0; peid < bindingModel.getPdbCount(); peid++) { final PDBEntry pdbentry = bindingModel.getPdbEntry(peid); @@ -1329,6 +1418,9 @@ public class Jalview2XML && !(entry.getId().length() > 4 && entry.getId() .toLowerCase().startsWith(pdbId.toLowerCase()))) { + /* + * not interested in a binding to a different PDB entry here + */ continue; } if (matchedFile == null) @@ -1346,7 +1438,6 @@ public class Jalview2XML // can get at it if the ID // match is ambiguous (e.g. // 1QIP==1qipA) - String statestring = viewFrame.getStateInfo(); for (int smap = 0; smap < viewFrame.getBinding().getSequence()[peid].length; smap++) { @@ -1364,18 +1455,9 @@ public class Jalview2XML state.setAlignwithAlignPanel(viewFrame.isUsedforaligment(ap)); state.setColourwithAlignPanel(viewFrame.isUsedforcolourby(ap)); state.setColourByJmol(viewFrame.isColouredByViewer()); - /* - * Only store each structure viewer's state once in each XML document. - */ - if (!viewIds.contains(viewId)) - { - viewIds.add(viewId); - state.setContent(statestring.replaceAll("\n", "")); - } - else - { - state.setContent("# duplicate state"); - } + // FIXME add attribute to schema and enable next line + // state.setType(viewFrame.getViewerType().toString()); + state.setContent(viewFrame.getViewerType().toString()); pdb.addStructureState(state); } } @@ -2067,7 +2149,7 @@ public class Jalview2XML if (jarentry != null && jarentry.getName().endsWith(".xml")) { - InputStreamReader in = new InputStreamReader(jin, "UTF-8"); + InputStreamReader in = new InputStreamReader(jin, UTF_8); JalviewModel object = new JalviewModel(); Unmarshaller unmar = new Unmarshaller(object); @@ -2319,6 +2401,16 @@ public class Jalview2XML */ private final boolean updateLocalViews = false; + /** + * Returns the path to a temporary file holding the PDB file for the given PDB + * id. The first time of asking, searches for a file of that name in the + * Jalview project jar, and copies it to a new temporary file. Any repeat + * requests just return the path to the file previously created. + * + * @param jprovider + * @param pdbId + * @return + */ String loadPDBFile(jarInputStreamProvider jprovider, String pdbId) { if (alreadyLoadedPDB.containsKey(pdbId)) @@ -2326,6 +2418,31 @@ public class Jalview2XML return alreadyLoadedPDB.get(pdbId).toString(); } + String tempFile = copyJarEntry(jprovider, pdbId, "jalview_pdb"); + if (tempFile != null) + { + alreadyLoadedPDB.put(pdbId, tempFile); + } + return tempFile; + } + + /** + * Copies the jar entry of given name to a new temporary file and returns the + * path to the file, or null if the entry is not found. + * + * @param jprovider + * @param jarEntryName + * @param prefix + * a prefix for the temporary file name, must be at least three + * characters long + * @return + */ + protected String copyJarEntry(jarInputStreamProvider jprovider, + String jarEntryName, String prefix) + { + BufferedReader in = null; + PrintWriter out = null; + try { JarInputStream jin = jprovider.getJarInputStream(); @@ -2339,38 +2456,46 @@ public class Jalview2XML do { entry = jin.getNextJarEntry(); - } while (entry != null && !entry.getName().equals(pdbId)); + } while (entry != null && !entry.getName().equals(jarEntryName)); if (entry != null) { - BufferedReader in = new BufferedReader(new InputStreamReader(jin)); - File outFile = File.createTempFile("jalview_pdb", ".txt"); + in = new BufferedReader(new InputStreamReader(jin, UTF_8)); + File outFile = File.createTempFile(prefix, ".tmp"); outFile.deleteOnExit(); - PrintWriter out = new PrintWriter(new FileOutputStream(outFile)); + out = new PrintWriter(new FileOutputStream(outFile)); String data; while ((data = in.readLine()) != null) { out.println(data); } - try - { - out.flush(); - } catch (Exception foo) - { - } - ; - out.close(); + out.flush(); String t = outFile.getAbsolutePath(); - alreadyLoadedPDB.put(pdbId, t); return t; } else { - warn("Couldn't find PDB file entry in Jalview Jar for " + pdbId); + warn("Couldn't find entry in Jalview Jar for " + jarEntryName); } } catch (Exception ex) { ex.printStackTrace(); + } finally + { + if (in != null) + { + try + { + in.close(); + } catch (IOException e) + { + // ignore + } + } + if (out != null) + { + out.close(); + } } return null; @@ -3184,8 +3309,9 @@ public class Jalview2XML } if (!structureViewers.containsKey(sviewid)) { - structureViewers.put(sviewid, new StructureViewerModel(x, y, - width, height, false, false, true)); + structureViewers.put(sviewid, + new StructureViewerModel(x, y, width, height, false, + false, true, structureState.getViewId())); // Legacy pre-2.7 conversion JAL-823 : // do not assume any view has to be linked for colour by // sequence @@ -3255,8 +3381,16 @@ public class Jalview2XML // Instantiate the associated structure views for (Entry entry : structureViewers .entrySet()) + { + try + { + createOrLinkStructureViewer(entry, af, ap, jprovider); + } catch (Exception e) { - createOrLinkStructureViewer(entry, af, ap); + System.err.println("Error loading structure viewer: " + + e.getMessage()); + // failed - try the next one + } } } @@ -3265,12 +3399,13 @@ public class Jalview2XML * @param viewerData * @param af * @param ap + * @param jprovider */ protected void createOrLinkStructureViewer( Entry viewerData, AlignFrame af, - AlignmentPanel ap) + AlignmentPanel ap, jarInputStreamProvider jprovider) { - final StructureViewerModel svattrib = viewerData.getValue(); + final StructureViewerModel stateData = viewerData.getValue(); /* * Search for any viewer windows already open from other alignment views @@ -3280,68 +3415,77 @@ public class Jalview2XML if (comp != null) { - linkStructureViewer(ap, comp, svattrib); + linkStructureViewer(ap, comp, stateData); return; } /* - * Pending an XML element for ViewerType, just check if stateData contains - * "chimera" (part of the chimera session filename). + * 2.8.2: stateData contains "chimera..." (session file name), or JMOL state + * string + * + * 2.9: stateData.type contains JMOL or CHIMERA, data is in jar entry + * "viewer_"+stateData.viewId */ - if (svattrib.getStateData().indexOf("chimera") > -1) + // FIXME use stateData.getType() instead once schema updated + if (ViewerType.CHIMERA.toString().equals(stateData.getStateData()) + || stateData.getStateData().indexOf("chimera") > -1) { - createChimeraViewer(viewerData, af); + createChimeraViewer(viewerData, af, jprovider); } else { - createJmolViewer(viewerData, af); + createJmolViewer(viewerData, af, jprovider); } } /** * Create a new Chimera viewer. * - * @param viewerData + * @param data * @param af + * @param jprovider */ - protected void createChimeraViewer( - Entry viewerData, AlignFrame af) + protected void createChimeraViewer(Entry viewerData, + AlignFrame af, + jarInputStreamProvider jprovider) { - final StructureViewerModel data = viewerData.getValue(); - String chimeraSession = data.getStateData(); + StructureViewerModel data = viewerData.getValue(); + String chimeraSessionFile = data.getStateData(); - if (new File(chimeraSession).exists()) - { - 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 colourByChimera = data.isColourByViewer(); - boolean colourBySequence = data.isColourWithAlignPanel(); - - // TODO can/should this be done via StructureViewer (like Jmol)? - final PDBEntry[] pdbArray = pdbs.toArray(new PDBEntry[pdbs.size()]); - final SequenceI[][] seqsArray = allseqs.toArray(new SequenceI[allseqs - .size()][]); - new ChimeraViewFrame(chimeraSession, af.alignPanel, pdbArray, - seqsArray, colourByChimera, colourBySequence); - } - else - { - Cache.log.error("Chimera session file " + chimeraSession - + " not found"); - } + /* + * Copy Chimera session from jar entry "viewer_"+viewId to a temporary file + * + * Note this is the 'saved' viewId as in the project file XML, _not_ the + * 'uniquified' sviewid used to reconstruct the viewer here + */ + chimeraSessionFile = copyJarEntry(jprovider, + "viewer_" + data.getViewId(), "chimera"); + + 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 colourByChimera = 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(); + new ChimeraViewFrame(chimeraSessionFile, af.alignPanel, pdbArray, + seqsArray, colourByChimera, colourBySequence, newViewId); } /** @@ -3351,13 +3495,27 @@ public class Jalview2XML * * @param viewerData * @param af + * @param jprovider */ protected void createJmolViewer( final Entry viewerData, - AlignFrame af) + AlignFrame af, jarInputStreamProvider jprovider) { final StructureViewerModel svattrib = viewerData.getValue(); String state = svattrib.getStateData(); + + /* + * Pre-2.9: state element value is the Jmol state string + * + * 2.9+: @type is "JMOL", state data is in a Jar file member named "viewer_" + * + viewId + */ + // FIXME use getType once Castor regenerated for new attribute + if (ViewerType.JMOL.toString().equals(state /* svattrib.getType() */)) + { + state = readJarEntry(jprovider, "viewer_" + svattrib.getViewId()); + } + List pdbfilenames = new ArrayList(); List seqmaps = new ArrayList(); List pdbids = new ArrayList(); @@ -3457,8 +3615,6 @@ public class Jalview2XML JalviewStructureDisplayI sview = null; try { - // JAL-1333 note - we probably can't migrate Jmol views to UCSF - // Chimera! sview = new StructureViewer(alf.alignPanel .getStructureSelectionManager()).createView( StructureViewer.ViewerType.JMOL, pdbf, id, sq, @@ -3514,8 +3670,8 @@ public class Jalview2XML && ((StructureViewerBase) frame).getViewId() .equals(sviewid)) { - comp = (AppJmol) frame; - // todo: break? + comp = (StructureViewerBase) frame; + break; // break added in 2.9 } /* * Otherwise test for matching position and size of viewer frame @@ -3525,8 +3681,8 @@ public class Jalview2XML && frame.getHeight() == svattrib.getHeight() && frame.getWidth() == svattrib.getWidth()) { - comp = (AppJmol) frame; - // todo: break? + comp = (StructureViewerBase) frame; + // no break in faint hope of an exact match on viewId } } } @@ -3544,15 +3700,15 @@ public class Jalview2XML * @param viewerColouring */ protected void linkStructureViewer(AlignmentPanel ap, - StructureViewerBase viewer, StructureViewerModel svattrib) + StructureViewerBase viewer, StructureViewerModel stateData) { // NOTE: if the jalview project is part of a shared session then // view synchronization should/could be done here. - final boolean useinViewerSuperpos = svattrib.isAlignWithPanel(); - final boolean usetoColourbyseq = svattrib.isColourWithAlignPanel(); - final boolean viewerColouring = svattrib.isColourByViewer(); - Map oldFiles = svattrib.getFileData(); + final boolean useinViewerSuperpos = stateData.isAlignWithPanel(); + final boolean usetoColourbyseq = stateData.isColourWithAlignPanel(); + final boolean viewerColouring = stateData.isColourByViewer(); + Map oldFiles = stateData.getFileData(); /* * Add mapping for sequences in this view to an already open viewer @@ -4851,4 +5007,67 @@ public class Jalview2XML { skipList = skipList2; } + + /** + * Reads the jar entry of given name and returns its contents, or null if the + * entry is not found. + * + * @param jprovider + * @param jarEntryName + * @return + */ + protected String readJarEntry(jarInputStreamProvider jprovider, + String jarEntryName) + { + String result = null; + BufferedReader in = null; + + try + { + /* + * Reopen the jar input stream and traverse its entries to find a matching + * name + */ + JarInputStream jin = jprovider.getJarInputStream(); + JarEntry entry = null; + do + { + entry = jin.getNextJarEntry(); + } while (entry != null && !entry.getName().equals(jarEntryName)); + + if (entry != null) + { + StringBuilder out = new StringBuilder(256); + in = new BufferedReader(new InputStreamReader(jin, UTF_8)); + String data; + + while ((data = in.readLine()) != null) + { + out.append(data); + } + result = out.toString(); + } + else + { + warn("Couldn't find entry in Jalview Jar for " + jarEntryName); + } + } catch (Exception ex) + { + ex.printStackTrace(); + } finally + { + if (in != null) + { + try + { + in.close(); + } catch (IOException e) + { + // ignore + } + } + } + + return result; + } } diff --git a/src/jalview/gui/StructureViewerBase.java b/src/jalview/gui/StructureViewerBase.java index 13af0e8..1feaa7c 100644 --- a/src/jalview/gui/StructureViewerBase.java +++ b/src/jalview/gui/StructureViewerBase.java @@ -1,8 +1,5 @@ package jalview.gui; -import jalview.gui.ViewSelectionMenu.ViewSetProvider; -import jalview.jbgui.GStructureViewer; - import java.awt.Component; import java.util.ArrayList; import java.util.List; @@ -10,6 +7,10 @@ import java.util.Vector; import javax.swing.JMenuItem; +import jalview.gui.StructureViewer.ViewerType; +import jalview.gui.ViewSelectionMenu.ViewSetProvider; +import jalview.jbgui.GStructureViewer; + /** * Base class with common functionality for JMol, Chimera or other structure * viewers. @@ -222,4 +223,6 @@ public abstract class StructureViewerBase extends GStructureViewer _colourwith.remove(nap); } } + + public abstract ViewerType getViewerType(); } -- 1.7.10.2