package jalview.structure;
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* A base class holding methods useful to all classes that implement commands
* for structure viewers
*
* @author gmcarstairs
*
*/
public abstract class StructureCommandsBase implements StructureCommandsI
{
public static final String NAMESPACE_PREFIX = "jv_";
private static final String CMD_SEPARATOR = ";";
/**
* Returns something that separates concatenated commands
*
* @return
*/
protected String getCommandSeparator()
{
return CMD_SEPARATOR;
}
/**
* 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
*
* a colour, when building a 'colour structure by sequence' command
* a feature value, when building a 'set Chimera attributes from features'
* command
*
*
* @param map
* @param value
* @param model
* @param startPos
* @param endPos
* @param chain
*/
public static final void addAtomSpecRange(Map 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);
}
/**
* Makes a structure viewer attribute name for a Jalview feature type by
* prefixing it with "jv_", and replacing any non-alphanumeric characters with
* an underscore
*
* @param featureType
* @return
*/
protected String makeAttributeName(String featureType)
{
StringBuilder sb = new StringBuilder();
if (featureType != null)
{
for (char c : featureType.toCharArray())
{
sb.append(Character.isLetterOrDigit(c) ? c : '_');
}
}
String attName = NAMESPACE_PREFIX + sb.toString();
return attName;
}
/**
* 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.
*
* The default implementation returns a single command containing one command
* per colour, concatenated.
*
* @param colourMap
* @return
*/
@Override
public List colourBySequence(
Map colourMap)
{
List 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 colourResidues(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 colourResidues(String atomSpec,
Color colour);
@Override
public List colourByResidues(
Map colours)
{
List commands = new ArrayList<>();
for (Entry entry : colours.entrySet())
{
commands.add(colourResidue(entry.getKey(), entry.getValue()));
}
return commands;
}
private StructureCommandI colourResidue(String resName, Color col)
{
String atomSpec = getResidueSpec(resName);
return colourResidues(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);
@Override
public List setAttributes(
Map> featureValues)
{
// default does nothing, override where this is implemented
return null;
}
@Override
public List startNotifications(String uri)
{
return null;
}
@Override
public List stopNotifications()
{
return null;
}
@Override
public StructureCommandI getSelectedResidues()
{
return null;
}
@Override
public StructureCommandI listResidueAttributes()
{
return null;
}
@Override
public StructureCommandI getResidueAttributes(String attName)
{
return null;
}
}