From: gmungoc Date: Tue, 6 Aug 2019 12:05:45 +0000 (+0100) Subject: JAL-3390 pull up of getShownResidues() to AAStructureBindingModel X-Git-Url: http://source.jalview.org/gitweb/?a=commitdiff_plain;h=2d62933ef95beb94c1ec2444bcced4a3a7ec42c0;p=jalview.git JAL-3390 pull up of getShownResidues() to AAStructureBindingModel --- diff --git a/src/jalview/ext/rbvi/chimera/AtomSpecModel.java b/src/jalview/ext/rbvi/chimera/AtomSpecModel.java index 39d6704..f0d1e84 100644 --- a/src/jalview/ext/rbvi/chimera/AtomSpecModel.java +++ b/src/jalview/ext/rbvi/chimera/AtomSpecModel.java @@ -20,34 +20,16 @@ */ package jalview.ext.rbvi.chimera; -import jalview.util.IntRangeComparator; - import java.util.ArrayList; import java.util.Collections; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.TreeMap; /** - * A class to model a Chimera atomspec pattern, for example - * - *
- * #0:15.A,28.A,54.A,63.A,70-72.A,83-84.A,97-98.A|#1:2.A,6.A,11.A,13-14.A,70.A,82.A,96-97.A
- * 
- * - * where - * - * - *
- * @see http://www.cgl.ucsf.edu/chimera/current/docs/UsersGuide/midas/frameatom_spec.html
- * 
+ * A class to model a Chimera or Jmol residue set, as + * {@code Map>>}. This can then be + * traversed to generate the required display command in Chimera or Jmol syntax. */ public class AtomSpecModel { @@ -58,7 +40,12 @@ public class AtomSpecModel */ public AtomSpecModel() { - atomSpec = new TreeMap>>(); + atomSpec = new TreeMap<>(); + } + + public Map>> getMap() + { + return atomSpec; } /** @@ -77,7 +64,7 @@ public class AtomSpecModel Map> modelData = atomSpec.get(model); if (modelData == null) { - atomSpec.put(model, modelData = new TreeMap>()); + atomSpec.put(model, modelData = new TreeMap<>()); } /* @@ -86,7 +73,7 @@ public class AtomSpecModel List chainData = modelData.get(chain); if (chainData == null) { - chainData = new ArrayList(); + chainData = new ArrayList<>(); modelData.put(chain, chainData); } @@ -98,104 +85,42 @@ public class AtomSpecModel } /** - * Returns the range(s) formatted as a Chimera atomspec + * Answers an iterable set of the structure models in this model * * @return */ - public String getAtomSpec() + public Iterable getModels() { - StringBuilder sb = new StringBuilder(128); - boolean firstModel = true; - for (Integer model : atomSpec.keySet()) - { - if (!firstModel) - { - sb.append("|"); - } - firstModel = false; - sb.append("#").append(model).append(":"); - - boolean firstPositionForModel = true; - final Map> modelData = atomSpec.get(model); - - for (String chain : modelData.keySet()) - { - chain = " ".equals(chain) ? chain : chain.trim(); - - List rangeList = modelData.get(chain); - - /* - * sort ranges into ascending start position order - */ - Collections.sort(rangeList, IntRangeComparator.ASCENDING); - - int start = rangeList.isEmpty() ? 0 : rangeList.get(0)[0]; - int end = rangeList.isEmpty() ? 0 : rangeList.get(0)[1]; - - Iterator iterator = rangeList.iterator(); - while (iterator.hasNext()) - { - int[] range = iterator.next(); - if (range[0] <= end + 1) - { - /* - * range overlaps or is contiguous with the last one - * - so just extend the end position, and carry on - * (unless this is the last in the list) - */ - end = Math.max(end, range[1]); - } - else - { - /* - * we have a break so append the last range - */ - appendRange(sb, start, end, chain, firstPositionForModel); - firstPositionForModel = false; - start = range[0]; - end = range[1]; - } - } - - /* - * and append the last range - */ - if (!rangeList.isEmpty()) - { - appendRange(sb, start, end, chain, firstPositionForModel); - firstPositionForModel = false; - } - } - } - return sb.toString(); + return atomSpec.keySet(); } /** - * @param sb - * @param start - * @param end - * @param chain - * @param firstPositionForModel + * Answers an iterable set of the chains in this model for the given structure + * model, or an empty set if none + * + * @param model + * @return */ - protected void appendRange(StringBuilder sb, int start, int end, - String chain, boolean firstPositionForModel) + public Iterable getChains(Integer model) { - if (!firstPositionForModel) - { - sb.append(","); - } - if (end == start) + if (atomSpec.containsKey(model)) { - sb.append(start); - } - else - { - sb.append(start).append("-").append(end); + return atomSpec.get(model).keySet(); } + return Collections.emptySet(); + } - sb.append("."); - if (!" ".equals(chain)) { - sb.append(chain); + public List getRanges(Integer model, String chain) + { + Map> modelData = atomSpec.get(model); + if (modelData != null) + { + List chainData = modelData.get(chain); + if (chainData != null) + { + return chainData; + } } + return Collections.EMPTY_LIST; } } diff --git a/src/jalview/ext/rbvi/chimera/ChimeraCommands.java b/src/jalview/ext/rbvi/chimera/ChimeraCommands.java index dc53c2b..c52b9a2 100644 --- a/src/jalview/ext/rbvi/chimera/ChimeraCommands.java +++ b/src/jalview/ext/rbvi/chimera/ChimeraCommands.java @@ -35,10 +35,13 @@ import jalview.structure.StructureSelectionManager; import jalview.structures.models.AAStructureBindingModel; import jalview.util.ColorUtils; import jalview.util.Comparison; +import jalview.util.IntRangeComparator; import java.awt.Color; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; +import java.util.Iterator; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -53,42 +56,34 @@ public class ChimeraCommands { public static final String NAMESPACE_PREFIX = "jv_"; + /* + * colour for residues shown in structure but hidden in alignment + */ private static final String COLOR_GRAY_HEX = "color " + ColorUtils.toTkCode(Color.GRAY); /** * Constructs Chimera commands to colour residues as per the Jalview alignment * - * @param ssm * @param files - * @param sequence - * @param sr - * @param fr * @param viewPanel + * @param binding * @return */ public static StructureMappingcommandSet[] getColourBySequenceCommand( - StructureSelectionManager ssm, String[] files, - AAStructureBindingModel binding, AlignmentViewPanel viewPanel) + String[] files, AlignmentViewPanel viewPanel, + AAStructureBindingModel binding) { + StructureSelectionManager ssm = binding.getSsm(); SequenceRenderer sr = binding.getSequenceRenderer(viewPanel); SequenceI[][] sequence = binding.getSequence(); boolean hideHiddenRegions = binding.isShowAlignmentOnly() && binding.isHideHiddenRegions(); - return getColourBySequenceCommand(ssm, files, sequence, sr, - hideHiddenRegions, viewPanel); - } - - static StructureMappingcommandSet[] getColourBySequenceCommand( - StructureSelectionManager ssm, String[] files, - SequenceI[][] sequence, SequenceRenderer sr, - boolean hideHiddenRegions, AlignmentViewPanel viewPanel) - { Map colourMap = buildColoursMap(ssm, files, sequence, sr, hideHiddenRegions, viewPanel); - List colourCommands = buildColourCommands(colourMap); + List colourCommands = buildColourCommands(colourMap, binding); StructureMappingcommandSet cs = new StructureMappingcommandSet( ChimeraCommands.class, null, @@ -110,10 +105,12 @@ public class ChimeraCommands * * * @param colourMap + * @param binding * @return */ protected static List buildColourCommands( - Map colourMap) + Map colourMap, + AAStructureBindingModel binding) { /* * This version concatenates all commands into a single String (semi-colon @@ -131,64 +128,13 @@ public class ChimeraCommands sb.append("; "); sb.append("color ").append(colourCode).append(" "); final AtomSpecModel colourData = colourMap.get(colour); - sb.append(colourData.getAtomSpec()); + sb.append(getAtomSpec(colourData, binding)); } commands.add(sb.toString()); return commands; } /** - * Traverses a map of { modelNumber, {chain, {list of from-to ranges} } } and - * builds a Chimera format atom spec - * - * @param modelAndChainRanges - */ - protected static String getAtomSpec( - Map>> modelAndChainRanges) - { - StringBuilder sb = new StringBuilder(128); - boolean firstModelForColour = true; - for (Integer model : modelAndChainRanges.keySet()) - { - boolean firstPositionForModel = true; - if (!firstModelForColour) - { - sb.append("|"); - } - firstModelForColour = false; - sb.append("#").append(model).append(":"); - - final Map> modelData = modelAndChainRanges - .get(model); - for (String chain : modelData.keySet()) - { - boolean hasChain = !"".equals(chain.trim()); - for (int[] range : modelData.get(chain)) - { - if (!firstPositionForModel) - { - sb.append(","); - } - if (range[0] == range[1]) - { - sb.append(range[0]); - } - else - { - sb.append(range[0]).append("-").append(range[1]); - } - if (hasChain) - { - sb.append(".").append(chain); - } - firstPositionForModel = false; - } - } - } - return sb.toString(); - } - - /** * Build a data structure which records contiguous subsequences for each colour. * From this we can easily generate the Chimera command for colour by sequence. * @@ -339,23 +285,27 @@ public class ChimeraCommands /** * Constructs and returns Chimera commands to set attributes on residues - * corresponding to features in Jalview. Attribute names are the Jalview - * feature type, with a "jv_" prefix. + * corresponding to features in Jalview. Attribute names are the Jalview feature + * type, with a "jv_" prefix. * * @param ssm * @param files * @param seqs * @param viewPanel + * @param binding * @return */ public static StructureMappingcommandSet getSetAttributeCommandsForFeatures( - StructureSelectionManager ssm, String[] files, SequenceI[][] seqs, - AlignmentViewPanel viewPanel) + AlignmentViewPanel viewPanel, AAStructureBindingModel binding) { + StructureSelectionManager ssm = binding.getSsm(); + String[] files = binding.getStructureFiles(); + SequenceI[][] seqs = binding.getSequence(); + Map> featureMap = buildFeaturesMap( ssm, files, seqs, viewPanel); - List commands = buildSetAttributeCommands(featureMap); + List commands = buildSetAttributeCommands(featureMap, binding); StructureMappingcommandSet cs = new StructureMappingcommandSet( ChimeraCommands.class, null, @@ -501,10 +451,12 @@ public class ChimeraCommands * * * @param featureMap + * @param binding * @return */ protected static List buildSetAttributeCommands( - Map> featureMap) + Map> featureMap, + AAStructureBindingModel binding) { List commands = new ArrayList<>(); for (String featureType : featureMap.keySet()) @@ -530,7 +482,7 @@ public class ChimeraCommands featureValue = featureValue.replaceAll("\\'", "'"); sb.append("setattr r ").append(attributeName).append(" '") .append(featureValue).append("' "); - sb.append(values.get(value).getAtomSpec()); + sb.append(getAtomSpec(values.get(value), binding)); commands.add(sb.toString()); } } @@ -574,4 +526,111 @@ public class ChimeraCommands return attName; } + /** + * Returns the range(s) formatted as a Chimera atomspec + * + * @return + */ + public static String getAtomSpec(AtomSpecModel atomSpec, + AAStructureBindingModel binding) + { + StringBuilder sb = new StringBuilder(128); + boolean firstModel = true; + for (Integer model : atomSpec.getModels()) + { + if (!firstModel) + { + sb.append("|"); + } + firstModel = false; + // todo use JalviewChimeraBinding.getModelSpec(model) + // which means this cannot be static + sb.append(binding.getModelSpec(model)).append(":"); + + boolean firstPositionForModel = true; + + for (String chain : atomSpec.getChains(model)) + { + chain = " ".equals(chain) ? chain : chain.trim(); + + List rangeList = atomSpec.getRanges(model, chain); + + /* + * sort ranges into ascending start position order + */ + Collections.sort(rangeList, IntRangeComparator.ASCENDING); + + int start = rangeList.isEmpty() ? 0 : rangeList.get(0)[0]; + int end = rangeList.isEmpty() ? 0 : rangeList.get(0)[1]; + + Iterator iterator = rangeList.iterator(); + while (iterator.hasNext()) + { + int[] range = iterator.next(); + if (range[0] <= end + 1) + { + /* + * range overlaps or is contiguous with the last one + * - so just extend the end position, and carry on + * (unless this is the last in the list) + */ + end = Math.max(end, range[1]); + } + else + { + /* + * we have a break so append the last range + */ + appendRange(sb, start, end, chain, firstPositionForModel); + firstPositionForModel = false; + start = range[0]; + end = range[1]; + } + } + + /* + * and append the last range + */ + if (!rangeList.isEmpty()) + { + appendRange(sb, start, end, chain, firstPositionForModel); + firstPositionForModel = false; + } + } + } + return sb.toString(); + } + + /** + * A helper method that appends one start-end range to a Chimera atomspec + * + * @param sb + * @param start + * @param end + * @param chain + * @param firstPositionForModel + */ + static void appendRange(StringBuilder sb, int start, int end, + String chain, boolean firstPositionForModel) + { + if (!firstPositionForModel) + { + sb.append(","); + } + if (end == start) + { + sb.append(start); + } + else + { + sb.append(start).append("-").append(end); + } + + sb.append("."); + if (!" ".equals(chain)) + { + sb.append(chain); + } + } + } diff --git a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java index 216320f..44bcbe4 100644 --- a/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java +++ b/src/jalview/ext/rbvi/chimera/JalviewChimeraBinding.java @@ -36,7 +36,6 @@ import jalview.io.DataSourceType; import jalview.schemes.ColourSchemeI; import jalview.schemes.ResidueProperties; import jalview.structure.AtomSpec; -import jalview.structure.StructureMapping; import jalview.structure.StructureMappingcommandSet; import jalview.structure.StructureSelectionManager; import jalview.structures.models.AAStructureBindingModel; @@ -52,7 +51,6 @@ 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; @@ -464,7 +462,8 @@ 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("match ").append(getModelSpec(pdbfnum)) + .append(":"); command.append(selcom[pdbfnum]); command.append("@").append( structures[pdbfnum].isRna ? PHOSPHORUS : ALPHACARBON); @@ -541,7 +540,8 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel * @param pdbfnum * @return */ - protected String getModelSpec(int pdbfnum) + @Override + public String getModelSpec(int pdbfnum) { if (pdbfnum < 0 || pdbfnum >= getPdbCount()) { @@ -556,7 +556,8 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel */ List maps = chimeraMaps.get(getStructureFiles()[pdbfnum]); boolean hasSubModels = maps != null && maps.size() > 1; - return "#" + String.valueOf(pdbfnum) + (hasSubModels ? ".1" : ""); + String spec = "#" + String.valueOf(pdbfnum); + return hasSubModels ? spec + ".1" : spec; } /** @@ -673,8 +674,8 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel protected StructureMappingcommandSet[] getColourBySequenceCommands( String[] files, AlignmentViewPanel viewPanel) { - return ChimeraCommands.getColourBySequenceCommand(getSsm(), files, - this, viewPanel); + return ChimeraCommands.getColourBySequenceCommand(files, viewPanel, + this); } /** @@ -1086,8 +1087,7 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel } StructureMappingcommandSet commandSet = ChimeraCommands - .getSetAttributeCommandsForFeatures(getSsm(), files, - getSequence(), avp); + .getSetAttributeCommandsForFeatures(avp, this); String[] commands = commandSet.commands; if (commands.length > 10) { @@ -1291,7 +1291,10 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel { StringBuilder cmd = new StringBuilder(128); cmd.append("~display; ~ribbon;"); - String atomSpec = getMappedResidues(av); + + AtomSpecModel model = getShownResidues(av); + String atomSpec = ChimeraCommands.getAtomSpec(model, this); + cmd.append("ribbon ").append(atomSpec); if (!isShowAlignmentOnly()) { @@ -1303,95 +1306,4 @@ public abstract class JalviewChimeraBinding extends AAStructureBindingModel } sendChimeraCommand(cmd.toString(), false); } - - /** - * Builds a Chimera atomSpec of residues mapped from sequences, of the format - * (#model:residues.chain) - * - *
-   * #0:2-94.A | #1:1-93.C | #2:1-93.A
-   * 
- * - * Only residues visible in the alignment are included, that is, hidden columns - * and sequences are excluded. - * - * @param av - * @return - */ - private String getMappedResidues(AlignViewportI av) - { - AlignmentI alignment = av.getAlignment(); - final int width = alignment.getWidth(); - - String[] files = getStructureFiles(); - - StringBuilder atomSpec = new StringBuilder(256); - - for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++) - { - StructureMapping[] mappings = getSsm().getMapping(files[pdbfnum]); - - /* - * Find the first mapped sequence (if any) for this PDB entry which is in - * the alignment - */ - final int seqCountForPdbFile = getSequence()[pdbfnum].length; - for (int s = 0; s < seqCountForPdbFile; s++) - { - for (StructureMapping mapping : mappings) - { - final SequenceI theSequence = getSequence()[pdbfnum][s]; - if (mapping.getSequence() == theSequence - && alignment.findIndex(theSequence) > -1) - { - String chainCd = mapping.getChain(); - if (!isShowChain(mapping.getPdbId(), chainCd)) - { - continue; - } - Iterator visible; - if (isShowAlignmentOnly() && isHideHiddenRegions()) - { - visible = alignment.getHiddenColumns() - .getVisContigsIterator(0, width, true); - } - else - { - visible = Collections.singletonList(new int[] { 0, width }) - .iterator(); - } - while (visible.hasNext()) - { - int[] visibleRegion = visible.next(); - int seqStartPos = theSequence.findPosition(visibleRegion[0]); - int seqEndPos = theSequence.findPosition(visibleRegion[1]); - List residueRanges = mapping - .getPDBResNumRanges(seqStartPos, seqEndPos); - if (!residueRanges.isEmpty()) - { - if (atomSpec.length() > 0) - { - atomSpec.append("| "); - } - atomSpec.append(getModelSpec(pdbfnum)).append(":"); - boolean first = true; - for (int[] range : residueRanges) - { - if (!first) - { - atomSpec.append(","); - } - first = false; - atomSpec.append(range[0]).append("-").append(range[1]); - atomSpec.append(".").append(chainCd); - } - } - } - } - } - } - } - - return atomSpec.toString(); - } } diff --git a/src/jalview/structures/models/AAStructureBindingModel.java b/src/jalview/structures/models/AAStructureBindingModel.java index d2fff3f..7a89961 100644 --- a/src/jalview/structures/models/AAStructureBindingModel.java +++ b/src/jalview/structures/models/AAStructureBindingModel.java @@ -29,6 +29,7 @@ import jalview.datamodel.AlignmentI; import jalview.datamodel.HiddenColumns; import jalview.datamodel.PDBEntry; import jalview.datamodel.SequenceI; +import jalview.ext.rbvi.chimera.AtomSpecModel; import jalview.io.DataSourceType; import jalview.schemes.ColourSchemeI; import jalview.structure.AtomSpec; @@ -43,6 +44,8 @@ import java.awt.Color; import java.util.ArrayList; import java.util.Arrays; import java.util.BitSet; +import java.util.Collections; +import java.util.Iterator; import java.util.List; /** @@ -954,4 +957,97 @@ public abstract class AAStructureBindingModel } return chainsToShow.contains(pdbId + ":" + chainId); } + + @Override + public abstract String[] getStructureFiles(); + + /** + * Builds a model of residues mapped from sequences to show on structure, taking + * into account user choices of + *
    + *
  • which chains are shown
  • + *
  • whether all structure is shown, or only that mapped to the alignment
  • + *
  • whether hidden regions of the alignment are hidden (excluded) or grayed + * out (included)
  • + *
+ * + * @param av + * @return + */ + protected AtomSpecModel getShownResidues(AlignViewportI av) + { + AlignmentI alignment = av.getAlignment(); + final int width = alignment.getWidth(); + + String[] files = getStructureFiles(); + + AtomSpecModel model = new AtomSpecModel(); + + for (int pdbfnum = 0; pdbfnum < files.length; pdbfnum++) + { + StructureMapping[] mappings = getSsm().getMapping(files[pdbfnum]); + + /* + * Find the first mapped sequence (if any) for this PDB entry which is in + * the alignment + */ + final int seqCountForPdbFile = getSequence()[pdbfnum].length; + for (int s = 0; s < seqCountForPdbFile; s++) + { + for (StructureMapping mapping : mappings) + { + final SequenceI theSequence = getSequence()[pdbfnum][s]; + if (mapping.getSequence() == theSequence + && alignment.findIndex(theSequence) > -1) + { + String chainCd = mapping.getChain(); + if (!isShowChain(mapping.getPdbId(), chainCd)) + { + continue; + } + Iterator visible; + if (isShowAlignmentOnly() && isHideHiddenRegions()) + { + visible = alignment.getHiddenColumns() + .getVisContigsIterator(0, width, true); + } + else + { + visible = Collections.singletonList(new int[] { 0, width }) + .iterator(); + } + while (visible.hasNext()) + { + int[] visibleRegion = visible.next(); + int seqStartPos = theSequence.findPosition(visibleRegion[0]); + int seqEndPos = theSequence.findPosition(visibleRegion[1]); + List residueRanges = mapping + .getPDBResNumRanges(seqStartPos, seqEndPos); + if (!residueRanges.isEmpty()) + { + for (int[] range : residueRanges) + { + model.addRange(pdbfnum, range[0], range[1], chainCd); + } + } + } + } + } + } + } + + return model; + } + + /** + * Answers a default structure model specification which is simply the string + * form of the model number. Override if needed to specify submodels. + * + * @param model + * @return + */ + public String getModelSpec(int model) + { + return String.valueOf(model); + } } diff --git a/test/jalview/ext/rbvi/chimera/AtomSpecModelTest.java b/test/jalview/ext/rbvi/chimera/AtomSpecModelTest.java deleted file mode 100644 index 63d5e4e..0000000 --- a/test/jalview/ext/rbvi/chimera/AtomSpecModelTest.java +++ /dev/null @@ -1,39 +0,0 @@ -package jalview.ext.rbvi.chimera; - -import static org.testng.Assert.assertEquals; - -import org.testng.annotations.Test; - -public class AtomSpecModelTest -{ - @Test(groups = "Functional") - public void testGetAtomSpec() - { - AtomSpecModel model = new AtomSpecModel(); - assertEquals(model.getAtomSpec(), ""); - model.addRange(1, 2, 4, "A"); - assertEquals(model.getAtomSpec(), "#1:2-4.A"); - model.addRange(1, 8, 8, "A"); - assertEquals(model.getAtomSpec(), "#1:2-4.A,8.A"); - model.addRange(1, 5, 7, "B"); - assertEquals(model.getAtomSpec(), "#1:2-4.A,8.A,5-7.B"); - model.addRange(1, 3, 5, "A"); - assertEquals(model.getAtomSpec(), "#1:2-5.A,8.A,5-7.B"); - model.addRange(0, 1, 4, "B"); - assertEquals(model.getAtomSpec(), "#0:1-4.B|#1:2-5.A,8.A,5-7.B"); - model.addRange(0, 5, 9, "C"); - assertEquals(model.getAtomSpec(), "#0:1-4.B,5-9.C|#1:2-5.A,8.A,5-7.B"); - model.addRange(1, 8, 10, "B"); - assertEquals(model.getAtomSpec(), "#0:1-4.B,5-9.C|#1:2-5.A,8.A,5-10.B"); - model.addRange(1, 8, 9, "B"); - assertEquals(model.getAtomSpec(), "#0:1-4.B,5-9.C|#1:2-5.A,8.A,5-10.B"); - model.addRange(0, 3, 10, "C"); // subsumes 5-9 - assertEquals(model.getAtomSpec(), "#0:1-4.B,3-10.C|#1:2-5.A,8.A,5-10.B"); - model.addRange(5, 25, 35, " "); // empty chain code - e.g. from homology - // modelling - assertEquals(model.getAtomSpec(), - "#0:1-4.B,3-10.C|#1:2-5.A,8.A,5-10.B|#5:25-35."); - - } - -} diff --git a/test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java b/test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java index fbfe33c..24ca6e9 100644 --- a/test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java +++ b/test/jalview/ext/rbvi/chimera/ChimeraCommandsTest.java @@ -23,18 +23,24 @@ package jalview.ext.rbvi.chimera; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; +import jalview.api.AlignmentViewPanel; +import jalview.api.FeatureRenderer; import jalview.datamodel.Alignment; import jalview.datamodel.AlignmentI; import jalview.datamodel.ColumnSelection; +import jalview.datamodel.HiddenColumns; import jalview.datamodel.Sequence; import jalview.datamodel.SequenceI; import jalview.gui.AlignFrame; import jalview.gui.JvOptionPane; import jalview.gui.SequenceRenderer; +import jalview.schemes.ColourSchemeI; import jalview.schemes.JalviewColourScheme; +import jalview.structure.AtomSpec; import jalview.structure.StructureMapping; import jalview.structure.StructureMappingcommandSet; import jalview.structure.StructureSelectionManager; +import jalview.structures.models.AAStructureBindingModel; import java.awt.Color; import java.util.HashMap; @@ -45,9 +51,98 @@ import java.util.Map; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import junit.extensions.PA; + public class ChimeraCommandsTest { + private SequenceRenderer sr; + + private AAStructureBindingModel mockBinding = new AAStructureBindingModel( + null, null) + { + @Override + public void releaseReferences(Object svl) + { + } + + @Override + public void highlightAtoms(List atoms) + { + } + + @Override + public List getChainNames() + { + return null; + } + + @Override + public void setJalviewColourScheme(ColourSchemeI cs) + { + } + + @Override + public String superposeStructures(AlignmentI[] alignments, + int[] structureIndices, HiddenColumns[] hiddenCols) + { + return null; + } + + @Override + public void setBackgroundColour(Color col) + { + } + + @Override + protected StructureMappingcommandSet[] getColourBySequenceCommands( + String[] files, AlignmentViewPanel avp) + { + return null; + } + + @Override + public jalview.api.SequenceRenderer getSequenceRenderer( + AlignmentViewPanel alignment) + { + return sr; + } + + @Override + protected void colourBySequence( + StructureMappingcommandSet[] colourBySequenceCommands) + { + } + + @Override + public void colourByChain() + { + } + + @Override + public void colourByCharge() + { + } + + @Override + public FeatureRenderer getFeatureRenderer(AlignmentViewPanel alignment) + { + return null; + } + + @Override + public String[] getStructureFiles() + { + return null; + } + + @Override + public String getModelSpec(int model) + { + return "#" + String.valueOf(model); + } + }; + @BeforeClass(alwaysRun = true) public void setUpJvOptionPane() { @@ -73,7 +168,8 @@ public class ChimeraCommandsTest // Colours should appear in the Chimera command in the order in which // they were added; within colour, by model, by chain, ranges in start order // all prefixed with #808080 to colour hidden regions (if shown) gray - String command = ChimeraCommands.buildColourCommands(map).get(0); + String command = ChimeraCommands.buildColourCommands(map, mockBinding) + .get(0); assertEquals( command, "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"); @@ -95,7 +191,7 @@ public class ChimeraCommandsTest ChimeraCommands.addColourRange(featureValues, "X", 0, 8, 20, "A"); List commands = ChimeraCommands - .buildSetAttributeCommands(featuresMap); + .buildSetAttributeCommands(featuresMap, mockBinding); assertEquals(1, commands.size()); /* @@ -108,7 +204,8 @@ public class ChimeraCommandsTest ChimeraCommands.addColourRange(featureValues, "X", 0, 3, 9, "A"); // same feature value, contiguous range ChimeraCommands.addColourRange(featureValues, "X", 0, 21, 25, "A"); - commands = ChimeraCommands.buildSetAttributeCommands(featuresMap); + commands = ChimeraCommands.buildSetAttributeCommands(featuresMap, + mockBinding); assertEquals(1, commands.size()); assertEquals(commands.get(0), "setattr r jv_chain 'X' #0:3-25.A"); @@ -116,14 +213,16 @@ public class ChimeraCommandsTest ChimeraCommands.addColourRange(featureValues, "X", 0, 21, 25, "B"); // same feature value and chain, different model ChimeraCommands.addColourRange(featureValues, "X", 1, 26, 30, "A"); - commands = ChimeraCommands.buildSetAttributeCommands(featuresMap); + commands = ChimeraCommands.buildSetAttributeCommands(featuresMap, + mockBinding); assertEquals(1, commands.size()); assertEquals(commands.get(0), "setattr r jv_chain 'X' #0:3-25.A,21-25.B|#1:26-30.A"); // same feature, different value ChimeraCommands.addColourRange(featureValues, "Y", 0, 40, 50, "A"); - commands = ChimeraCommands.buildSetAttributeCommands(featuresMap); + commands = ChimeraCommands.buildSetAttributeCommands(featuresMap, + mockBinding); assertEquals(2, commands.size()); // commands are ordered by feature type but not by value // so use contains to test for the expected command: @@ -139,7 +238,8 @@ public class ChimeraCommandsTest "A"); // feature names are sanitised to change non-alphanumeric to underscore // feature values are sanitised to encode single quote characters - commands = ChimeraCommands.buildSetAttributeCommands(featuresMap); + commands = ChimeraCommands.buildSetAttributeCommands(featuresMap, + mockBinding); assertTrue(commands .contains("setattr r jv_side_chain_binding_ 'metal 'ion!' #0:7-15.A")); } @@ -182,7 +282,7 @@ public class ChimeraCommandsTest cs.addElement(4); af.getViewport().setColumnSelection(cs); af.hideSelColumns_actionPerformed(null); - SequenceRenderer sr = new SequenceRenderer(af.getViewport()); + sr = new SequenceRenderer(af.getViewport()); SequenceI[][] seqs = new SequenceI[][] { { seq1 }, { seq2 } }; String[] files = new String[] { "seq1.pdb", "seq2.pdb" }; StructureSelectionManager ssm = new StructureSelectionManager(); @@ -202,9 +302,14 @@ public class ChimeraCommandsTest "B", map, null); ssm.addStructureMapping(sm2); + /* + * put data into the mock binding object + */ + PA.setValue(mockBinding, "ssm", ssm); + PA.setValue(mockBinding, "sequence", seqs); + StructureMappingcommandSet[] commands = ChimeraCommands - .getColourBySequenceCommand(ssm, files, seqs, sr, false, - af.alignPanel); + .getColourBySequenceCommand(files, af.alignPanel, mockBinding); assertEquals(1, commands.length); assertEquals(1, commands[0].commands.length); String theCommand = commands[0].commands[0]; @@ -219,4 +324,43 @@ public class ChimeraCommandsTest // S and G are both coloured #4949b6 assertTrue(theCommand.contains("color #4949b6 #0:26-30.A|#1:26-30.B")); } + + @Test(groups = "Functional") + public void testGetAtomSpec() + { + AtomSpecModel model = new AtomSpecModel(); + assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding), ""); + model.addRange(1, 2, 4, "A"); + assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding), + "#1:2-4.A"); + model.addRange(1, 8, 8, "A"); + assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding), + "#1:2-4.A,8.A"); + model.addRange(1, 5, 7, "B"); + assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding), + "#1:2-4.A,8.A,5-7.B"); + model.addRange(1, 3, 5, "A"); + assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding), + "#1:2-5.A,8.A,5-7.B"); + model.addRange(0, 1, 4, "B"); + assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding), + "#0:1-4.B|#1:2-5.A,8.A,5-7.B"); + model.addRange(0, 5, 9, "C"); + assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding), + "#0:1-4.B,5-9.C|#1:2-5.A,8.A,5-7.B"); + model.addRange(1, 8, 10, "B"); + assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding), + "#0:1-4.B,5-9.C|#1:2-5.A,8.A,5-10.B"); + model.addRange(1, 8, 9, "B"); + assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding), + "#0:1-4.B,5-9.C|#1:2-5.A,8.A,5-10.B"); + model.addRange(0, 3, 10, "C"); // subsumes 5-9 + assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding), + "#0:1-4.B,3-10.C|#1:2-5.A,8.A,5-10.B"); + model.addRange(5, 25, 35, " "); // empty chain code - e.g. from homology + // modelling + assertEquals(ChimeraCommands.getAtomSpec(model, mockBinding), + "#0:1-4.B,3-10.C|#1:2-5.A,8.A,5-10.B|#5:25-35."); + + } }