From 34567bb79dfa8f163f44c3f3448083fcd145f308 Mon Sep 17 00:00:00 2001 From: gmungoc Date: Tue, 14 Jan 2020 16:09:09 +0000 Subject: [PATCH] JAL-2422 proof of concept adaptations for ChimeraX commands --- .../edu/ucsf/rbvi/strucviz2/ChimeraManager.java | 45 +++-- src/jalview/ext/rbvi/chimera/ChimeraListener.java | 24 ++- .../ext/rbvi/chimera/JalviewChimeraBinding.java | 182 ++++++++++++++++---- src/jalview/gui/ChimeraViewFrame.java | 34 ++-- src/jalview/structure/AtomSpec.java | 44 +++-- test/jalview/structure/AtomSpecTest.java | 80 ++++++++- 6 files changed, 316 insertions(+), 93 deletions(-) diff --git a/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraManager.java b/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraManager.java index a322f0b..1a8bd76 100644 --- a/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraManager.java +++ b/src/ext/edu/ucsf/rbvi/strucviz2/ChimeraManager.java @@ -334,7 +334,11 @@ public class ChimeraManager public void stopListening() { - sendChimeraCommand("listen stop models ; listen stop selection ", false); + // TODO send this command when viewer connection is closed in Jalview + String command = isChimeraX + ? "info notify stop models jalview; info notify stop selection jalview" + : "listen stop models ; listen stop selection "; + sendChimeraCommand(command, false); } /** @@ -344,9 +348,15 @@ public class ChimeraManager */ public void startListening(String uri) { - sendChimeraCommand("listen start models url " + uri - + ";listen start select prefix SelectionChanged url " + uri, - false); + String command = isChimeraX + ? ("info notify start models prefix ModelChanged jalview url " + + uri + + "; info notify start selection jalview prefix SelectionChanged url " + + uri) + : ("listen start models url " + uri + + ";listen start select prefix SelectionChanged url " + + uri); + sendChimeraCommand(command, false); } /** @@ -420,19 +430,33 @@ public class ChimeraManager public List getSelectedResidueSpecs() { List selectedResidues = new ArrayList<>(); - List chimeraReply = sendChimeraCommand( - "list selection level residue", true); + + /* + * skip for now if ChimeraX - times out + */ + if (isChimeraX) + { + return selectedResidues; + } + + // in fact 'listinfo' (undocumented) works in ChimeraX + String command = (isChimeraX ? "info" : "list") + + " selection level residue"; + List chimeraReply = sendChimeraCommand(command, true); if (chimeraReply != null) { /* - * expect 0, 1 or more lines of the format + * expect 0, 1 or more lines of the format either + * Chimera: * residue id #0:43.A type GLY - * where we are only interested in the atomspec #0.43.A + * ChimeraX: + * residue id /A:89 name THR index 88 + * We are only interested in the atomspec (third token of the reply) */ for (String inputLine : chimeraReply) { String[] inputLineParts = inputLine.split("\\s+"); - if (inputLineParts.length == 5) + if (inputLineParts.length >= 5) { selectedResidues.add(inputLineParts[2]); } @@ -722,7 +746,8 @@ public class ChimeraManager public List getAttrList() { List attributes = new ArrayList<>(); - final List reply = sendChimeraCommand("list resattr", true); + String command = (isChimeraX ? "info " : "list") + "resattr"; + final List reply = sendChimeraCommand(command, true); if (reply != null) { for (String inputLine : reply) diff --git a/src/jalview/ext/rbvi/chimera/ChimeraListener.java b/src/jalview/ext/rbvi/chimera/ChimeraListener.java index a0d74bc..40b0ff0 100644 --- a/src/jalview/ext/rbvi/chimera/ChimeraListener.java +++ b/src/jalview/ext/rbvi/chimera/ChimeraListener.java @@ -114,17 +114,25 @@ public class ChimeraListener extends AbstractRequestHandler { // dumpRequest(request); String message = request.getParameter(CHIMERA_NOTIFICATION); - if (SELECTION_CHANGED.equals(message)) + if (message == null) { - this.chimeraBinding.highlightChimeraSelection(); + message = request.getParameter("chimerax_notification"); } - else if (message != null && message.startsWith(MODEL_CHANGED)) + if (message != null) { - processModelChanged(message.substring(MODEL_CHANGED.length())); - } - else - { - System.err.println("Unexpected chimeraNotification: " + message); + if (message.startsWith("SelectionChanged")) + { + this.chimeraBinding.highlightChimeraSelection(); + } + else if (message.startsWith(MODEL_CHANGED)) + { + System.err.println(message); + processModelChanged(message.substring(MODEL_CHANGED.length())); + } + else + { + System.err.println("Unexpected chimeraNotification: " + message); + } } } diff --git a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java index 01deea8..6e02efe 100644 --- a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java +++ b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java @@ -51,6 +51,7 @@ import java.util.ArrayList; import java.util.BitSet; import java.util.Collections; import java.util.Hashtable; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -349,7 +350,9 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel public void colourByCharge() { colourBySequence = false; - String command = "color white;color red ::ASP;color red ::GLU;color blue ::LYS;color blue ::ARG;color yellow ::CYS"; + String command = viewer.isChimeraX() + ? "color white;color :ASP,GLU red;color :LYS,ARG blue;color :CYS yellow" + : "color white;color red ::ASP;color red ::GLU;color blue ::LYS;color blue ::ARG;color yellow ::CYS"; sendAsynchronousCommand(command, COLOURING_CHIMERA); } @@ -370,6 +373,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel refreshPdbEntries(); StringBuilder selectioncom = new StringBuilder(256); + boolean chimeraX = viewer.isChimeraX(); for (int a = 0; a < _alignment.length; a++) { int refStructure = _refStructure[a]; @@ -430,10 +434,16 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel String[] selcom = new String[files.length]; for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++) { + final int modelNo = pdbfnum + (chimeraX ? 1 : 0); + // todo correct resolution to model number String chainCd = "." + structures[pdbfnum].chain; int lpos = -1; boolean run = false; StringBuilder molsel = new StringBuilder(); + if (chimeraX) + { + molsel.append("/" + structures[pdbfnum].chain + ":"); + } int nextColumnMatch = matched.nextSetBit(0); while (nextColumnMatch != -1) @@ -447,7 +457,10 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel if (lpos != -1) { molsel.append(String.valueOf(lpos)); - molsel.append(chainCd); + if (!chimeraX) + { + molsel.append(chainCd); + } molsel.append(","); } run = false; @@ -477,18 +490,24 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel if (lpos != -1) { molsel.append(String.valueOf(lpos)); - molsel.append(chainCd); + if (!chimeraX) + { + molsel.append(chainCd); + } } if (molsel.length() > 1) { selcom[pdbfnum] = molsel.toString(); - selectioncom.append("#").append(String.valueOf(pdbfnum)) - .append(":"); + selectioncom.append("#").append(String.valueOf(modelNo)); + if (!chimeraX) + { + selectioncom.append(":"); + } selectioncom.append(selcom[pdbfnum]); - selectioncom.append(" "); + // selectioncom.append(" "); if (pdbfnum < files.length - 1) { - selectioncom.append("| "); + selectioncom.append("|"); } } else @@ -500,6 +519,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel StringBuilder command = new StringBuilder(256); for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++) { + final int modelNo = pdbfnum + (chimeraX ? 1 : 0); if (pdbfnum == refStructure || selcom[pdbfnum] == null || selcom[refStructure] == null) { @@ -519,17 +539,33 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel * @see * https://www.cgl.ucsf.edu/chimera/docs/UsersGuide/midas/match.html */ - command.append("match ").append(getModelSpec(pdbfnum)).append(":"); + command.append(chimeraX ? "align " : "match "); + command.append(getModelSpec(modelNo)); + if (!chimeraX) + { + command.append(":"); + } command.append(selcom[pdbfnum]); command.append("@").append( structures[pdbfnum].isRna ? PHOSPHORUS : ALPHACARBON); - // JAL-1757 exclude alternate CA locations - command.append(NO_ALTLOCS); - command.append(" ").append(getModelSpec(refStructure)).append(":"); + // JAL-1757 exclude alternate CA locations - ChimeraX syntax tbd + if (!chimeraX) + { + command.append(NO_ALTLOCS); + } + command.append(chimeraX ? " toAtoms " : " ") + .append(getModelSpec(refStructure + (chimeraX ? 1 : 0))); + if (!chimeraX) + { + command.append(":"); + } command.append(selcom[refStructure]); command.append("@").append( structures[refStructure].isRna ? PHOSPHORUS : ALPHACARBON); - command.append(NO_ALTLOCS); + if (!chimeraX) + { + command.append(NO_ALTLOCS); + } } if (selectioncom.length() > 0) { @@ -539,9 +575,21 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel System.out.println( "Superimpose command(s):\n" + command.toString()); } - allComs.append("~display all; chain @CA|P; ribbon ") - .append(selectioncom.toString()) - .append(";" + command.toString()); + // allComs.append("~display all; "); + // if (chimeraX) + // { + // allComs.append("show ").append(selectioncom.toString()) + // .append(" pbonds"); + // } + // else + // { + // allComs.append("chain @CA|P; ribbon "); + // allComs.append(selectioncom.toString()); + // } + if (allComs.length() > 0) { + allComs.append(";"); + } + allComs.append(command.toString()); } } @@ -557,8 +605,19 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel { System.out.println("Select regions:\n" + selectioncom.toString()); } - allComs.append("; ~display all; chain @CA|P; ribbon ") - .append(selectioncom.toString()).append("; focus"); + allComs.append(";~display all; "); + if (chimeraX) + { + allComs.append("show @CA|P pbonds; show ") + .append(selectioncom.toString()).append(" ribbons; view"); + } + else + { + allComs.append("chain @CA|P; ribbon ; focus"); + allComs.append(selectioncom.toString()); + } + // allComs.append("; ~display all; chain @CA|P; ribbon ") + // .append(selectioncom.toString()).append("; focus"); List chimeraReplies = sendChimeraCommand(allComs.toString(), true); for (String reply : chimeraReplies) @@ -589,7 +648,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel { if (pdbfnum < 0 || pdbfnum >= getPdbCount()) { - return ""; + return "#" + pdbfnum; // temp hack for ChimeraX } /* @@ -885,12 +944,13 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel protected List convertStructureResiduesToAlignment( List structureSelection) { + boolean chimeraX = viewer.isChimeraX(); List atomSpecs = new ArrayList<>(); for (String atomSpec : structureSelection) { try { - AtomSpec spec = AtomSpec.fromChimeraAtomspec(atomSpec); + AtomSpec spec = AtomSpec.fromChimeraAtomspec(atomSpec, chimeraX); String pdbfilename = getPdbFileForModel(spec.getModelNumber()); spec.setPdbFile(pdbfilename); atomSpecs.add(spec); @@ -952,22 +1012,35 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel return; } - // Chimera expects RBG values in the range 0-1 - final double normalise = 255D; viewerCommandHistory(false); StringBuilder command = new StringBuilder(128); List residueSet = ResidueProperties.getResidues(isNucleotide(), false); + + /* + * concatenate colour commands, one per residue symbol + * Chimera format: color 0.000000,0.372549,0.627451 ::VAL + * ChimeraX format: color :VAL rgb(73,73,182) + */ + boolean chimeraX = viewer.isChimeraX(); for (String resName : residueSet) { char res = resName.length() == 3 ? ResidueProperties.getSingleCharacterCode(resName) : resName.charAt(0); Color col = cs.findColour(res, 0, null, null, 0f); - command.append("color " + col.getRed() / normalise + "," - + col.getGreen() / normalise + "," + col.getBlue() / normalise - + " ::" + resName + ";"); + command.append("color "); + String colorSpec = getRgbDescriptor(col, chimeraX); + if (chimeraX) + { + command.append(":").append(resName).append(" ").append(colorSpec); + } + else + { + command.append(colorSpec).append(" ::").append(resName); + } + command.append(";"); } sendAsynchronousCommand(command.toString(), COLOURING_CHIMERA); @@ -1022,15 +1095,40 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel public void setBackgroundColour(Color col) { viewerCommandHistory(false); - double normalise = 255D; - final String command = "background solid " + col.getRed() / normalise - + "," + col.getGreen() / normalise + "," - + col.getBlue() / normalise + ";"; + String command = "set bgColor " + + getRgbDescriptor(col, viewer.isChimeraX()); viewer.sendChimeraCommand(command, false); viewerCommandHistory(true); } /** + * Answers the Chimera/X format for RGB values of the given colour. + * + *
+   * Chimera: r,g,b with values scaled [0=1]
+   * ChimeraX: rgb(r,g,b) with values scaled 0-255
+   * 
+ * + * @param col + * @param chimeraX + * @return + */ + private static String getRgbDescriptor(Color col, boolean chimeraX) + { + if (chimeraX) + { + return String.format("rgb(%d,%d,%d)", col.getRed(), col.getGreen(), + col.getBlue()); + } + else + { + double scale = 255D; + return String.format("%f,%f,%f", col.getRed() / scale, + col.getGreen() / scale, col.getBlue() / scale); + } + } + + /** * Ask Chimera to save its session to the given file. Returns true if * successful, else false. * @@ -1087,7 +1185,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel */ public void focusView() { - sendChimeraCommand("focus", false); + sendChimeraCommand(viewer.isChimeraX() ? "view" : "focus", false); } /** @@ -1235,6 +1333,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel { boolean featureAdded = false; String featureGroup = getViewerFeatureGroup(); + boolean chimeraX = viewer.isChimeraX(); for (String residue : residues) { @@ -1258,7 +1357,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel try { - spec = AtomSpec.fromChimeraAtomspec(atomSpec); + spec = AtomSpec.fromChimeraAtomspec(atomSpec, chimeraX); } catch (IllegalArgumentException e) { System.err.println("Problem parsing atomspec " + atomSpec); @@ -1338,4 +1437,27 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel } return -1; } + + /** + * Answers a (possibly empty) list of attribute names in Chimera[X], excluding + * any which were added from Jalview + * + * @return + */ + public List getChimeraAttributes() + { + List atts = viewer.getAttrList(); + Iterator it = atts.iterator(); + while (it.hasNext()) + { + if (it.next().startsWith(ChimeraCommands.NAMESPACE_PREFIX)) + { + /* + * attribute added from Jalview - exclude it + */ + it.remove(); + } + } + return atts; + } } diff --git a/src/jalview/gui/ChimeraViewFrame.java b/src/jalview/gui/ChimeraViewFrame.java index a7349b8..430d302 100644 --- a/src/jalview/gui/ChimeraViewFrame.java +++ b/src/jalview/gui/ChimeraViewFrame.java @@ -25,7 +25,6 @@ import jalview.bin.Cache; import jalview.datamodel.AlignmentI; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; -import jalview.ext.rbvi.chimera.ChimeraCommands; import jalview.ext.rbvi.chimera.JalviewChimeraBinding; import jalview.gui.StructureViewer.ViewerType; import jalview.io.DataSourceType; @@ -138,34 +137,21 @@ public class ChimeraViewFrame extends StructureViewerBase */ protected void buildAttributesMenu(JMenu attributesMenu) { - List atts = jmb.sendChimeraCommand("list resattr", true); - if (atts == null) - { - return; - } + List atts = jmb.getChimeraAttributes(); attributesMenu.removeAll(); Collections.sort(atts); - for (String att : atts) + for (String attName : atts) { - final String attName = att.split(" ")[1]; - - /* - * ignore 'jv_*' attributes, as these are Jalview features that have - * been transferred to residue attributes in Chimera! - */ - if (!attName.startsWith(ChimeraCommands.NAMESPACE_PREFIX)) + JMenuItem menuItem = new JMenuItem(attName); + menuItem.addActionListener(new ActionListener() { - JMenuItem menuItem = new JMenuItem(attName); - menuItem.addActionListener(new ActionListener() + @Override + public void actionPerformed(ActionEvent e) { - @Override - public void actionPerformed(ActionEvent e) - { - getChimeraAttributes(attName); - } - }); - attributesMenu.add(menuItem); - } + getChimeraAttributes(attName); + } + }); + attributesMenu.add(menuItem); } } diff --git a/src/jalview/structure/AtomSpec.java b/src/jalview/structure/AtomSpec.java index f20cd31..8b8161f 100644 --- a/src/jalview/structure/AtomSpec.java +++ b/src/jalview/structure/AtomSpec.java @@ -43,52 +43,70 @@ public class AtomSpec * Parses a Chimera atomspec e.g. #1:12.A to construct an AtomSpec model (with * null pdb file name) * + *
+   * Chimera format: 
+   *    #1.2:12-20.A     model 1, submodel 2, chain A, atoms 12-20
+   * ChimeraX format:
+   *    #1.2/A:12-20
+   * 
+ * * @param spec + * @param chimeraX * @return * @throw IllegalArgumentException if the spec cannot be parsed, or represents * more than one residue + * @see https://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/frameatom_spec.html + * @see http://rbvi.ucsf.edu/chimerax/docs/user/commands/atomspec.html */ - public static AtomSpec fromChimeraAtomspec(String spec) + public static AtomSpec fromChimeraAtomspec(String spec, boolean chimeraX) { - int colonPos = spec.indexOf(":"); - if (colonPos == -1) + int modelSeparatorPos = spec.indexOf(chimeraX ? "/" : ":"); + if (modelSeparatorPos == -1) { throw new IllegalArgumentException(spec); } int hashPos = spec.indexOf("#"); - if (hashPos == -1 && colonPos != 0) + if (hashPos == -1 && modelSeparatorPos != 0) { // # is missing but something precedes : - reject throw new IllegalArgumentException(spec); } - String modelSubmodel = spec.substring(hashPos + 1, colonPos); - int dotPos = modelSubmodel.indexOf("."); + String modelSubmodel = spec.substring(hashPos + 1, modelSeparatorPos); int modelId = 0; try { - modelId = Integer.valueOf(dotPos == -1 ? modelSubmodel - : modelSubmodel.substring(0, dotPos)); + int subModelPos = modelSubmodel.indexOf("."); + modelId = Integer.valueOf( + subModelPos > 0 ? modelSubmodel.substring(0, subModelPos) + : modelSubmodel); } catch (NumberFormatException e) { // ignore, default to model 0 } - String residueChain = spec.substring(colonPos + 1); - dotPos = residueChain.indexOf("."); + /* + * now process what follows the model, either + * Chimera: atoms.chain + * ChimeraX: chain:atoms + */ + String atomsAndChain = spec.substring(modelSeparatorPos + 1); + String[] tokens = atomsAndChain.split(chimeraX ? "\\:" : "\\."); + String atoms = tokens.length == 1 ? atomsAndChain + : (chimeraX ? tokens[1] : tokens[0]); int resNum = 0; try { - resNum = Integer.parseInt(dotPos == -1 ? residueChain - : residueChain.substring(0, dotPos)); + resNum = Integer.parseInt(atoms); } catch (NumberFormatException e) { // could be a range e.g. #1:4-7.B throw new IllegalArgumentException(spec); } - String chainId = dotPos == -1 ? "" : residueChain.substring(dotPos + 1); + String chainId = tokens.length == 1 ? "" + : (chimeraX ? tokens[0] : tokens[1]); return new AtomSpec(modelId, chainId, resNum, 0); } diff --git a/test/jalview/structure/AtomSpecTest.java b/test/jalview/structure/AtomSpecTest.java index ea53131..ff6e6cb 100644 --- a/test/jalview/structure/AtomSpecTest.java +++ b/test/jalview/structure/AtomSpecTest.java @@ -9,23 +9,23 @@ import org.testng.annotations.Test; public class AtomSpecTest { @Test - public void testFromChimeraAtomSpec() + public void testFromChimeraAtomSpec_chimera() { - AtomSpec as = AtomSpec.fromChimeraAtomspec("#1:12.B"); + AtomSpec as = AtomSpec.fromChimeraAtomspec("#1:12.B", false); assertEquals(as.getModelNumber(), 1); assertEquals(as.getPdbResNum(), 12); assertEquals(as.getChain(), "B"); assertNull(as.getPdbFile()); // no model - default to zero - as = AtomSpec.fromChimeraAtomspec(":13.C"); + as = AtomSpec.fromChimeraAtomspec(":13.C", false); assertEquals(as.getModelNumber(), 0); assertEquals(as.getPdbResNum(), 13); assertEquals(as.getChain(), "C"); assertNull(as.getPdbFile()); // model.submodel - as = AtomSpec.fromChimeraAtomspec("#3.2:15"); + as = AtomSpec.fromChimeraAtomspec("#3.2:15", false); assertEquals(as.getModelNumber(), 3); assertEquals(as.getPdbResNum(), 15); assertEquals(as.getChain(), ""); @@ -34,7 +34,7 @@ public class AtomSpecTest String spec = "3:12.B"; try { - as = AtomSpec.fromChimeraAtomspec(spec); + as = AtomSpec.fromChimeraAtomspec(spec, false); fail("Expected exception for " + spec); } catch (IllegalArgumentException e) { @@ -44,7 +44,7 @@ public class AtomSpecTest spec = "#3:12-14.B"; try { - as = AtomSpec.fromChimeraAtomspec(spec); + as = AtomSpec.fromChimeraAtomspec(spec, false); fail("Expected exception for " + spec); } catch (IllegalArgumentException e) { @@ -54,7 +54,7 @@ public class AtomSpecTest spec = ""; try { - as = AtomSpec.fromChimeraAtomspec(spec); + as = AtomSpec.fromChimeraAtomspec(spec, false); fail("Expected exception for " + spec); } catch (IllegalArgumentException e) { @@ -64,7 +64,71 @@ public class AtomSpecTest spec = null; try { - as = AtomSpec.fromChimeraAtomspec(spec); + as = AtomSpec.fromChimeraAtomspec(spec, false); + fail("Expected exception for " + spec); + } catch (NullPointerException e) + { + // ok + } + } + + @Test + public void testFromChimeraAtomSpec_chimeraX() + { + AtomSpec as = AtomSpec.fromChimeraAtomspec("#1/B:12", true); + assertEquals(as.getModelNumber(), 1); + assertEquals(as.getPdbResNum(), 12); + assertEquals(as.getChain(), "B"); + assertNull(as.getPdbFile()); + + // no model - default to zero + as = AtomSpec.fromChimeraAtomspec("/C:13", true); + assertEquals(as.getModelNumber(), 0); + assertEquals(as.getPdbResNum(), 13); + assertEquals(as.getChain(), "C"); + assertNull(as.getPdbFile()); + + // model.submodel + as = AtomSpec.fromChimeraAtomspec("#3.2/:15", true); + assertEquals(as.getModelNumber(), 3); + assertEquals(as.getPdbResNum(), 15); + assertEquals(as.getChain(), ""); + assertNull(as.getPdbFile()); + + String spec = "3:12.B"; + try + { + as = AtomSpec.fromChimeraAtomspec(spec, true); + fail("Expected exception for " + spec); + } catch (IllegalArgumentException e) + { + // ok + } + + spec = "#3:12-14.B"; + try + { + as = AtomSpec.fromChimeraAtomspec(spec, true); + fail("Expected exception for " + spec); + } catch (IllegalArgumentException e) + { + // ok + } + + spec = ""; + try + { + as = AtomSpec.fromChimeraAtomspec(spec, true); + fail("Expected exception for " + spec); + } catch (IllegalArgumentException e) + { + // ok + } + + spec = null; + try + { + as = AtomSpec.fromChimeraAtomspec(spec, true); fail("Expected exception for " + spec); } catch (NullPointerException e) { -- 1.7.10.2