JAL-3551 concatenate colour by sequence commands for all except PyMOL
[jalview.git] / src / jalview / structure / StructureCommandsBase.java
index 321fc25..e688b64 100644 (file)
@@ -1,5 +1,14 @@
 package jalview.structure;
 
+import java.awt.Color;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import jalview.api.AlignmentViewPanel;
+import jalview.datamodel.SequenceI;
+
 /**
  * A base class holding methods useful to all classes that implement commands
  * for structure viewers
@@ -9,4 +18,190 @@ package jalview.structure;
  */
 public abstract class StructureCommandsBase implements StructureCommandsI
 {
+  private static final String CMD_SEPARATOR = ";";
+
+  /**
+   * Returns something that separates concatenated commands
+   * 
+   * @return
+   */
+  protected static String getCommandSeparator()
+  {
+    return CMD_SEPARATOR;
+  }
+
+  @Override
+  public List<StructureCommandI> setAttributesForFeatures(
+          StructureSelectionManager ssm,
+          String[] files, SequenceI[][] sequence, AlignmentViewPanel avp)
+  {
+    // default does nothing, override where this is implemented
+    return null;
+  }
+
+  /**
+   * Returns the lowest model number used by the structure viewer
+   * 
+   * @return
+   */
+  @Override
+  public int getModelStartNo()
+  {
+    return 0;
+  }
+
+  /**
+   * Helper method to add one contiguous range to the AtomSpec model for the given
+   * value (creating the model if necessary). As used by Jalview, {@code value} is
+   * <ul>
+   * <li>a colour, when building a 'colour structure by sequence' command</li>
+   * <li>a feature value, when building a 'set Chimera attributes from features'
+   * command</li>
+   * </ul>
+   * 
+   * @param map
+   * @param value
+   * @param model
+   * @param startPos
+   * @param endPos
+   * @param chain
+   */
+  public static final void addAtomSpecRange(Map<Object, AtomSpecModel> map,
+          Object value, String model, int startPos, int endPos,
+          String chain)
+  {
+    /*
+     * Get/initialize map of data for the colour
+     */
+    AtomSpecModel atomSpec = map.get(value);
+    if (atomSpec == null)
+    {
+      atomSpec = new AtomSpecModel();
+      map.put(value, atomSpec);
+    }
+  
+    atomSpec.addRange(model, startPos, endPos, chain);
+  }
+
+  /**
+   * Traverse the map of colours/models/chains/positions to construct a list of
+   * 'color' commands (one per distinct colour used). The format of each command
+   * is specific to the structure viewer.
+   * <p>
+   * The default implementation returns a single command containing one command
+   * per colour, concatenated.
+   * 
+   * @param colourMap
+   * @return
+   */
+  @Override
+  public List<StructureCommandI> colourBySequence(
+          Map<Object, AtomSpecModel> colourMap)
+  {
+    List<StructureCommandI> commands = new ArrayList<>();
+    StringBuilder sb = new StringBuilder(colourMap.size() * 20);
+    boolean first = true;
+    for (Object key : colourMap.keySet())
+    {
+      Color colour = (Color) key;
+      final AtomSpecModel colourData = colourMap.get(colour);
+      StructureCommandI command = getColourCommand(colourData, colour);
+      if (!first)
+      {
+        sb.append(getCommandSeparator());
+      }
+      first = false;
+      sb.append(command.getCommand());
+    }
+
+    commands.add(new StructureCommand(sb.toString()));
+    return commands;
+  }
+
+  /**
+   * Returns a command to colour the atoms represented by {@code atomSpecModel}
+   * with the colour specified by {@code colourCode}.
+   * 
+   * @param atomSpecModel
+   * @param colour
+   * @return
+   */
+  protected StructureCommandI getColourCommand(AtomSpecModel atomSpecModel,
+          Color colour)
+  {
+    String atomSpec = getAtomSpec(atomSpecModel, false);
+    return getColourCommand(atomSpec, colour);
+  }
+
+  /**
+   * Returns a command to colour the atoms described (in viewer command syntax)
+   * by {@code atomSpec} with the colour specified by {@code colourCode}
+   * 
+   * @param atomSpec
+   * @param colour
+   * @return
+   */
+  protected abstract StructureCommandI getColourCommand(String atomSpec,
+          Color colour);
+
+  @Override
+  public List<StructureCommandI> colourByResidues(
+          Map<String, Color> colours)
+  {
+    List<StructureCommandI> commands = new ArrayList<>();
+    for (Entry<String, Color> entry : colours.entrySet())
+    {
+      commands.add(colourResidue(entry.getKey(), entry.getValue()));
+    }
+    return commands;
+  }
+
+  private StructureCommandI colourResidue(String resName, Color col)
+  {
+    String atomSpec = getResidueSpec(resName);
+    return getColourCommand(atomSpec, col);
+  }
+
+  /**
+   * Helper method to append one start-end range to an atomspec string
+   * 
+   * @param sb
+   * @param start
+   * @param end
+   * @param chain
+   * @param firstPositionForModel
+   */
+  protected void appendRange(StringBuilder sb, int start, int end,
+          String chain, boolean firstPositionForModel, boolean isChimeraX)
+  {
+    if (!firstPositionForModel)
+    {
+      sb.append(",");
+    }
+    if (end == start)
+    {
+      sb.append(start);
+    }
+    else
+    {
+      sb.append(start).append("-").append(end);
+    }
+
+    if (!isChimeraX)
+    {
+      sb.append(".");
+      if (!" ".equals(chain))
+      {
+        sb.append(chain);
+      }
+    }
+  }
+
+  /**
+   * Returns the atom specifier meaning all occurrences of the given residue
+   * 
+   * @param residue
+   * @return
+   */
+  protected abstract String getResidueSpec(String residue);
 }