package de.mpicbg.knime.scripting.r.utils;
import de.mpicbg.knime.knutils.AbstractNodeModel;
import de.mpicbg.knime.knutils.Attribute;
import de.mpicbg.knime.knutils.AttributeUtils;
import de.mpicbg.knime.scripting.core.exceptions.KnimeScriptingException;
import de.mpicbg.knime.scripting.r.RUtils;
import org.apache.commons.lang.StringEscapeUtils;
import org.knime.core.data.*;
import org.knime.core.node.BufferedDataTable;
import org.knime.core.node.ExecutionContext;
import org.knime.core.node.InvalidSettingsException;
import org.knime.core.node.defaultnodesettings.SettingsModelBoolean;
import org.rosuda.REngine.REXPMismatchException;
import org.rosuda.REngine.Rserve.RConnection;
import org.rosuda.REngine.Rserve.RserveException;
import java.util.ArrayList;
import java.util.List;
/**
* This is the model implementation of RSnippet. Improved R Integration for Knime
*
* @author Holger Brandl (MPI-CBG)
*/
public class FixColumnsNamesNodeModel extends AbstractNodeModel {
SettingsModelBoolean useMakeNames = FixColumnsNamesNodeFactory.createPropStrictRNames();
/**
* Constructor for the node model.
*/
protected FixColumnsNamesNodeModel() {
super(1, 1);
addSetting(useMakeNames);
}
/**
* {@inheritDoc}
*/
@Override
protected BufferedDataTable[] execute(final BufferedDataTable[] inData,
final ExecutionContext exec) throws Exception {
BufferedDataTable inputTable = inData[0];
DataTableSpec inputSpecs = inputTable.getDataTableSpec();
DataTableSpec dts = fixSpec(inputSpecs);
BufferedDataTable replacerTable = exec.createSpecReplacerTable(inputTable, dts);
return new BufferedDataTable[]{replacerTable};
}
// the better approach might be
// http://stat.ethz.ch/R-manual/R-devel/library/base/html/make.names.html
private DataTableSpec fixSpec(DataTableSpec inputSpecs) throws KnimeScriptingException, RserveException, REXPMismatchException {
List<Attribute> inputAttributes = AttributeUtils.convert(inputSpecs);
List<DataColumnSpec> outputSpec = new ArrayList<DataColumnSpec>();
//establish connection if names should be fixed by R
RConnection connection = null;
if(useMakeNames.getBooleanValue())
connection = RUtils.createConnection();
try {
for (Attribute attribute : inputAttributes) {
String originalName = attribute.getName();
String fixedName = null;
if (useMakeNames.getBooleanValue()) {
fixedName = fixNameWithR(originalName, connection);
} else {
fixedName = fixName(originalName);
}
if (!originalName.equals(fixedName)) {
DataColumnSpecCreator columnSpecCreator = new DataColumnSpecCreator(fixedName, attribute.getType());
if (attribute.getColumnSpec().getDomain() != null) {
DataColumnDomain domain = attribute.getColumnSpec().getDomain();
DataColumnDomain dataColumnDomain = new DataColumnDomainCreator(domain).createDomain();
columnSpecCreator.setDomain(dataColumnDomain);
}
outputSpec.add(columnSpecCreator.createSpec());
} else {
outputSpec.add(attribute.getColumnSpec());
}
}
} catch(Exception e) {
if(connection != null) connection.close();
throw e;
}
// close connection to R
if(useMakeNames.getBooleanValue()) {
assert(connection != null);
connection.close();
}
return new DataTableSpec(outputSpec.toArray(new DataColumnSpec[outputSpec.size()]));
}
private String fixNameWithR(String originalName, RConnection connection) throws RserveException, REXPMismatchException {
// mask " in column names to avoid syntax errors with R
originalName = StringEscapeUtils.escapeJava(originalName); //create an additional plugin dependency
//originalName = originalName.replace('"', '.');
//originalName = originalName.replace('\', '.');
// connection should not be null, as it throws exception when creation fails
assert(connection != null);
return connection.eval("make.unique(make.names(\"" + originalName + "\"))").asStrings()[0];
}
private String fixName(String originalName) {
while (originalName.startsWith("+")) {
originalName = originalName.replaceAll("[+]", "Plus");
}
while (originalName.startsWith("-")) {
originalName = originalName.replaceAll("[-]", "Minus");
}
while (originalName.startsWith("*")) {
originalName = originalName.replaceAll("[*]", "Times");
}
while (originalName.startsWith("%")) {
originalName = originalName.replaceAll("[%]", "Percent");
}
while (originalName.startsWith(":")) {
originalName = originalName.replaceAll("[:]", "DivBy");
}
while (originalName.startsWith("/")) {
originalName = originalName.replaceFirst("[/]", "DivBy");
}
return originalName;
}
public static void main(String[] args) {
System.err.println(new FixColumnsNamesNodeModel().fixName("++serse%1324"));
;
}
@Override
protected DataTableSpec[] configure(DataTableSpec[] inSpecs) throws InvalidSettingsException {
try {
return new DataTableSpec[]{fixSpec(inSpecs[0])};
} catch (RserveException e) {
e.printStackTrace();
} catch (REXPMismatchException e) {
e.printStackTrace();
} catch (KnimeScriptingException e) {
e.printStackTrace();
}
logger.warn("generating output table spec failed");
return null;
}
}