2 * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3 * Copyright (C) $$Year-Rel$$ The Jalview Authors
5 * This file is part of Jalview.
7 * Jalview is free software: you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation, either version 3
10 * of the License, or (at your option) any later version.
12 * Jalview is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty
14 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 * PURPOSE. See the GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with Jalview. If not, see <http://www.gnu.org/licenses/>.
19 * The Jalview Authors are detailed in the 'AUTHORS' file.
21 package jalview.ext.rbvi.chimera;
23 import static org.testng.Assert.assertEquals;
24 import static org.testng.Assert.assertTrue;
26 import jalview.api.AlignmentViewPanel;
27 import jalview.api.FeatureRenderer;
28 import jalview.datamodel.Alignment;
29 import jalview.datamodel.AlignmentI;
30 import jalview.datamodel.ColumnSelection;
31 import jalview.datamodel.HiddenColumns;
32 import jalview.datamodel.Sequence;
33 import jalview.datamodel.SequenceI;
34 import jalview.gui.AlignFrame;
35 import jalview.gui.JvOptionPane;
36 import jalview.gui.SequenceRenderer;
37 import jalview.schemes.ColourSchemeI;
38 import jalview.schemes.JalviewColourScheme;
39 import jalview.structure.AtomSpec;
40 import jalview.structure.StructureMapping;
41 import jalview.structure.StructureSelectionManager;
42 import jalview.structures.models.AAStructureBindingModel;
43 import jalview.util.StructureCommands;
45 import java.awt.Color;
46 import java.util.HashMap;
47 import java.util.LinkedHashMap;
48 import java.util.List;
51 import org.testng.annotations.BeforeClass;
52 import org.testng.annotations.Test;
54 import junit.extensions.PA;
56 public class ChimeraCommandsTest
58 private SequenceRenderer sr;
60 private String[] files;
62 private AAStructureBindingModel mockBinding = new AAStructureBindingModel(
66 public void releaseReferences(Object svl)
71 public void highlightAtoms(List<AtomSpec> atoms)
76 public void setJalviewColourScheme(ColourSchemeI cs)
81 public String superposeStructures(AlignmentI[] alignments,
82 int[] structureIndices, HiddenColumns[] hiddenCols)
88 public void setBackgroundColour(Color col)
93 public jalview.api.SequenceRenderer getSequenceRenderer(
94 AlignmentViewPanel alignment)
100 protected void colourBySequence(AlignmentViewPanel avp)
105 public void colourByChain()
110 public void colourByCharge()
115 public FeatureRenderer getFeatureRenderer(AlignmentViewPanel alignment)
121 public String[] getStructureFiles()
127 public String getModelSpec(int model)
129 return "#" + String.valueOf(model);
133 @BeforeClass(alwaysRun = true)
134 public void setUpJvOptionPane()
136 JvOptionPane.setInteractiveMode(false);
137 JvOptionPane.setMockResponse(JvOptionPane.CANCEL_OPTION);
140 @Test(groups = { "Functional" })
141 public void testBuildColourCommands()
143 Map<Object, AtomSpecModel> map = new LinkedHashMap<>();
144 ChimeraCommands.addAtomSpecRange(map, Color.blue, 0, 2, 5, "A");
145 ChimeraCommands.addAtomSpecRange(map, Color.blue, 0, 7, 7, "B");
146 ChimeraCommands.addAtomSpecRange(map, Color.blue, 0, 9, 23, "A");
147 ChimeraCommands.addAtomSpecRange(map, Color.blue, 1, 1, 1, "A");
148 ChimeraCommands.addAtomSpecRange(map, Color.blue, 1, 4, 7, "B");
149 ChimeraCommands.addAtomSpecRange(map, Color.yellow, 1, 8, 8, "A");
150 ChimeraCommands.addAtomSpecRange(map, Color.yellow, 1, 3, 5, "A");
151 ChimeraCommands.addAtomSpecRange(map, Color.red, 0, 3, 5, "A");
152 ChimeraCommands.addAtomSpecRange(map, Color.red, 0, 6, 9, "A");
154 // Colours should appear in the Chimera command in the order in which
155 // they were added; within colour, by model, by chain, ranges in start order
156 // all prefixed with #808080 to colour hidden regions (if shown) gray
157 String command = ChimeraCommands.buildColourCommands(map, mockBinding)
161 "color #808080; color #0000ff #0:2-5.A,9-23.A,7.B|#1:1.A,4-7.B; color #ffff00 #1:3-5.A,8.A; color #ff0000 #0:3-9.A");
164 @Test(groups = { "Functional" })
165 public void testBuildSetAttributeCommands()
168 * make a map of { featureType, {featureValue, {residue range specification } } }
170 Map<String, Map<Object, AtomSpecModel>> featuresMap = new LinkedHashMap<>();
171 Map<Object, AtomSpecModel> featureValues = new HashMap<>();
174 * start with just one feature/value...
176 featuresMap.put("chain", featureValues);
177 ChimeraCommands.addAtomSpecRange(featureValues, "X", 0, 8, 20, "A");
179 List<String> commands = ChimeraCommands
180 .buildSetAttributeCommands(featuresMap, mockBinding);
181 assertEquals(1, commands.size());
184 * feature name gets a jv_ namespace prefix
185 * feature value is quoted in case it contains spaces
187 assertEquals(commands.get(0), "setattr r jv_chain 'X' #0:8-20.A");
189 // add same feature value, overlapping range
190 ChimeraCommands.addAtomSpecRange(featureValues, "X", 0, 3, 9, "A");
191 // same feature value, contiguous range
192 ChimeraCommands.addAtomSpecRange(featureValues, "X", 0, 21, 25, "A");
193 commands = ChimeraCommands.buildSetAttributeCommands(featuresMap,
195 assertEquals(1, commands.size());
196 assertEquals(commands.get(0), "setattr r jv_chain 'X' #0:3-25.A");
198 // same feature value and model, different chain
199 ChimeraCommands.addAtomSpecRange(featureValues, "X", 0, 21, 25, "B");
200 // same feature value and chain, different model
201 ChimeraCommands.addAtomSpecRange(featureValues, "X", 1, 26, 30, "A");
202 commands = ChimeraCommands.buildSetAttributeCommands(featuresMap,
204 assertEquals(1, commands.size());
205 assertEquals(commands.get(0),
206 "setattr r jv_chain 'X' #0:3-25.A,21-25.B|#1:26-30.A");
208 // same feature, different value
209 ChimeraCommands.addAtomSpecRange(featureValues, "Y", 0, 40, 50, "A");
210 commands = ChimeraCommands.buildSetAttributeCommands(featuresMap,
212 assertEquals(2, commands.size());
213 // commands are ordered by feature type but not by value
214 // so use contains to test for the expected command:
216 .contains("setattr r jv_chain 'X' #0:3-25.A,21-25.B|#1:26-30.A"));
217 assertTrue(commands.contains("setattr r jv_chain 'Y' #0:40-50.A"));
220 featureValues.clear();
221 featuresMap.put("side-chain binding!", featureValues);
222 ChimeraCommands.addAtomSpecRange(featureValues,
223 "<html>metal <a href=\"http:a.b.c/x\"> 'ion!", 0, 7, 15,
225 // feature names are sanitised to change non-alphanumeric to underscore
226 // feature values are sanitised to encode single quote characters
227 commands = ChimeraCommands.buildSetAttributeCommands(featuresMap,
230 .contains("setattr r jv_side_chain_binding_ '<html>metal <a href=\"http:a.b.c/x\"> 'ion!' #0:7-15.A"));
234 * Tests for the method that prefixes and sanitises a feature name so it can
235 * be used as a valid, namespaced attribute name in Chimera
237 @Test(groups = { "Functional" })
238 public void testMakeAttributeName()
240 assertEquals(ChimeraCommands.makeAttributeName(null), "jv_");
241 assertEquals(ChimeraCommands.makeAttributeName(""), "jv_");
242 assertEquals(ChimeraCommands.makeAttributeName("helix"), "jv_helix");
243 assertEquals(ChimeraCommands.makeAttributeName("Hello World 24"),
244 "jv_Hello_World_24");
246 ChimeraCommands.makeAttributeName("!this is-a_very*{odd(name"),
247 "jv__this_is_a_very__odd_name");
248 // name ending in color gets underscore appended
249 assertEquals(ChimeraCommands.makeAttributeName("helixColor"),
253 @Test(groups = { "Functional" })
254 public void testGetColourBySequenceCommands_hiddenColumns()
257 * load these sequences, coloured by Strand propensity,
258 * with columns 2-4 hidden
260 SequenceI seq1 = new Sequence("seq1", "MHRSQSSSGG");
261 SequenceI seq2 = new Sequence("seq2", "MVRSNGGSSS");
262 AlignmentI al = new Alignment(new SequenceI[] { seq1, seq2 });
263 AlignFrame af = new AlignFrame(al, 800, 500);
264 af.changeColour_actionPerformed(JalviewColourScheme.Strand.toString());
265 ColumnSelection cs = new ColumnSelection();
269 af.getViewport().setColumnSelection(cs);
270 af.hideSelColumns_actionPerformed(null);
271 sr = new SequenceRenderer(af.getViewport());
272 SequenceI[][] seqs = new SequenceI[][] { { seq1 }, { seq2 } };
273 files = new String[] { "seq1.pdb", "seq2.pdb" };
274 StructureSelectionManager ssm = new StructureSelectionManager();
277 * map residues 1-10 to residues 21-30 (atoms 105-150) in structures
279 HashMap<Integer, int[]> map = new HashMap<>();
280 for (int pos = 1; pos <= seq1.getLength(); pos++)
282 map.put(pos, new int[] { 20 + pos, 5 * (20 + pos) });
284 StructureMapping sm1 = new StructureMapping(seq1, "seq1.pdb", "pdb1",
286 ssm.addStructureMapping(sm1);
287 StructureMapping sm2 = new StructureMapping(seq2, "seq2.pdb", "pdb2",
289 ssm.addStructureMapping(sm2);
292 * put data into the mock binding object
294 PA.setValue(mockBinding, "ssm", ssm);
295 PA.setValue(mockBinding, "sequence", seqs);
297 Map<Object, AtomSpecModel> colourMap = StructureCommands.buildColoursMap(mockBinding, af.alignPanel);
298 String[] commands = ChimeraCommands
299 .getColourBySequenceCommand(colourMap, mockBinding);
300 assertEquals(1, commands.length);
301 String theCommand = commands[0];
302 // M colour is #82827d (see strand.html help page)
303 assertTrue(theCommand.contains("color #82827d #0:21.A|#1:21.B"));
304 // H colour is #60609f
305 assertTrue(theCommand.contains("color #60609f #0:22.A"));
306 // V colour is #ffff00
307 assertTrue(theCommand.contains("color #ffff00 #1:22.B"));
308 // hidden columns are Gray (128, 128, 128)
309 assertTrue(theCommand.contains("color #808080 #0:23-25.A|#1:23-25.B"));
310 // S and G are both coloured #4949b6
311 assertTrue(theCommand.contains("color #4949b6 #0:26-30.A|#1:26-30.B"));
314 @Test(groups = "Functional")
315 public void testGetAtomSpec()
317 AtomSpecModel model = new AtomSpecModel();
318 assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding), "");
320 model.addRange(1, 2, 4, "A");
321 assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding),
324 model.addRange(1, 8, 8, "A");
325 assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding),
328 model.addRange(1, 5, 7, "B");
329 assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding),
330 "#1:2-4.A,8.A,5-7.B");
332 model.addRange(1, 3, 5, "A");
333 // 3-5 combines with 2-4 to make 2-5:
334 assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding),
335 "#1:2-5.A,8.A,5-7.B");
337 model.addRange(0, 1, 4, "B");
338 assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding),
339 "#0:1-4.B|#1:2-5.A,8.A,5-7.B");
341 model.addRange(0, 5, 9, "C");
342 assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding),
343 "#0:1-4.B,5-9.C|#1:2-5.A,8.A,5-7.B");
345 model.addRange(1, 8, 10, "B");
346 // 8-10 extends 5-7 to make 5-10
347 assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding),
348 "#0:1-4.B,5-9.C|#1:2-5.A,8.A,5-10.B");
350 model.addRange(1, 8, 9, "B");
351 // subsumed range makes no difference
352 assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding),
353 "#0:1-4.B,5-9.C|#1:2-5.A,8.A,5-10.B");
355 model.addRange(0, 3, 10, "C");
356 // subsumes 5-9 so replaces it
357 assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding),
358 "#0:1-4.B,3-10.C|#1:2-5.A,8.A,5-10.B");
360 // empty chain code - e.g. from homology modelling
361 model.addRange(5, 25, 35, " ");
362 assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding),
363 "#0:1-4.B,3-10.C|#1:2-5.A,8.A,5-10.B|#5:25-35.");