use of org.knime.base.node.jsnippet.util.field.OutCol in project knime-core by knime.
the class JavaSnippet method getAdditionalBuildPaths.
@Override
public File[] getAdditionalBuildPaths() {
final Set<File> result = new LinkedHashSet<>();
for (InCol colField : m_fields.getInColFields()) {
final String factoryId = colField.getConverterFactoryId();
final Collection<? extends File> buildPath = getBuildPathFromCache(factoryId);
if (buildPath != null) {
result.addAll(buildPath);
}
}
for (OutCol colField : m_fields.getOutColFields()) {
final String factoryId = colField.getConverterFactoryId();
final Collection<? extends File> buildPath = getBuildPathFromCache(factoryId);
if (buildPath != null) {
result.addAll(buildPath);
}
}
return result.toArray(new File[result.size()]);
}
use of org.knime.base.node.jsnippet.util.field.OutCol in project knime-core by knime.
the class JavaSnippet method createFieldsSection.
/**
* Create the system variable (input and output) section of the snippet.
*/
private String createFieldsSection() {
StringBuilder out = new StringBuilder();
out.append("// system variables\n");
out.append("public class JSnippet extends AbstractJSnippet {\n");
if (m_fields.getInColFields().size() > 0) {
out.append(" // Fields for input columns\n");
for (InCol field : m_fields.getInColFields()) {
out.append("/** Input column: \"");
out.append(field.getKnimeName());
out.append("\" */\n");
appendFields(out, field);
}
}
if (m_fields.getInVarFields().size() > 0) {
out.append(" // Fields for input flow variables\n");
for (InVar field : m_fields.getInVarFields()) {
out.append("/** Input flow variable: \"");
out.append(field.getKnimeName());
out.append("\" */\n");
appendFields(out, field);
}
}
out.append("\n");
if (m_fields.getOutColFields().size() > 0) {
out.append(" // Fields for output columns\n");
for (OutCol field : m_fields.getOutColFields()) {
out.append("/** Output column: \"");
out.append(field.getKnimeName());
out.append("\" */\n");
appendFields(out, field);
}
}
if (m_fields.getOutVarFields().size() > 0) {
out.append(" // Fields for output flow variables\n");
for (OutVar field : m_fields.getOutVarFields()) {
out.append("/** Output flow variable: \"");
out.append(field.getKnimeName());
out.append("\" */\n");
appendFields(out, field);
}
}
out.append("\n");
return out.toString();
}
use of org.knime.base.node.jsnippet.util.field.OutCol in project knime-core by knime.
the class JavaSnippet method createSnippetClass.
/**
* Create the class file of the snippet.
*
* Creates a URLClassLoader which may open jar files referenced by the Java Snippet class. Make sure to match every
* call to this method with a call to {@link #close()} in order for KNIME to be able to clean up .jar files that
* were downloaded from URLs.
*
* @return the compiled snippet
*/
@SuppressWarnings("unchecked")
private Class<? extends AbstractJSnippet> createSnippetClass() {
JavaSnippetCompiler compiler = new JavaSnippetCompiler(this);
/* Recompile/Reload either if the code changed or the class loader has been closed since */
if (m_classLoader != null && m_snippetCache.isValid(getDocument())) {
if (!m_snippetCache.hasCustomFields()) {
return m_snippetCache.getSnippetClass();
}
} else {
// recompile
m_snippetCache.invalidate();
StringWriter log = new StringWriter();
DiagnosticCollector<JavaFileObject> digsCollector = new DiagnosticCollector<>();
CompilationTask compileTask = null;
try {
compileTask = compiler.getTask(log, digsCollector);
} catch (IOException e) {
throw new IllegalStateException("Compile with errors: " + e.getMessage(), e);
}
boolean success = compileTask.call();
if (!success) {
StringBuilder msg = new StringBuilder();
msg.append("Compile with errors:\n");
for (Diagnostic<? extends JavaFileObject> d : digsCollector.getDiagnostics()) {
boolean isSnippet = this.isSnippetSource(d.getSource());
if (isSnippet && d.getKind().equals(javax.tools.Diagnostic.Kind.ERROR)) {
long line = d.getLineNumber();
if (line != Diagnostic.NOPOS) {
msg.append("Error in line " + line + ": ");
} else {
msg.append("Error: ");
}
msg.append(d.getMessage(Locale.US));
msg.append('\n');
}
}
throw new IllegalStateException(msg.toString());
}
}
try {
close();
final LinkedHashSet<ClassLoader> customTypeClassLoaders = new LinkedHashSet<>();
customTypeClassLoaders.add(JavaSnippet.class.getClassLoader());
for (final InCol col : m_fields.getInColFields()) {
customTypeClassLoaders.addAll(getClassLoadersFor(col.getConverterFactoryId()));
}
for (final OutCol col : m_fields.getOutColFields()) {
customTypeClassLoaders.addAll(getClassLoadersFor(col.getConverterFactoryId()));
}
/* Add class loaders of additional bundles */
customTypeClassLoaders.addAll(getAdditionalBundlesClassLoaders());
// remove core class loader:
// (a) it's referenced via JavaSnippet.class classloader and
// (b) it would collect buddies when used directly (see support ticket #1943)
customTypeClassLoaders.remove(DataCellToJavaConverterRegistry.class.getClassLoader());
final MultiParentClassLoader customTypeLoader = new MultiParentClassLoader(customTypeClassLoaders.stream().toArray(ClassLoader[]::new));
// TODO (Next version bump) change return value of createClassLoader instead of cast
m_classLoader = (URLClassLoader) compiler.createClassLoader(customTypeLoader);
Class<? extends AbstractJSnippet> snippetClass = (Class<? extends AbstractJSnippet>) m_classLoader.loadClass("JSnippet");
m_snippetCache.update(getDocument(), snippetClass, m_settings);
return snippetClass;
} catch (ClassNotFoundException e) {
throw new IllegalStateException("Could not load class file.", e);
} catch (IOException e) {
throw new IllegalStateException("Could not load jar files.", e);
}
}
use of org.knime.base.node.jsnippet.util.field.OutCol in project knime-core by knime.
the class JavaSnippet method validateSettings.
/**
* Validate settings which is typically called in the configure method of a node.
*
* What is checked:
* <ul>
* <li>Whether converter factories matching the ids from the settings exist</li>
* <li>Whether the code compiles</li>
* <li>Whether columns required by input mappings still exist.</li>
* </ul>
*
* @param spec the spec of the data table at the inport
* @param flowVariableRepository the flow variables at the inport
* @return the validation results
*/
public ValidationReport validateSettings(final DataTableSpec spec, final FlowVariableRepository flowVariableRepository) {
List<String> errors = new ArrayList<>();
List<String> warnings = new ArrayList<>();
// check input fields
for (final InCol field : m_fields.getInColFields()) {
final int index = spec.findColumnIndex(field.getKnimeName());
if (index < 0) {
errors.add("The column \"" + field.getKnimeName() + "\" is not found in the input table.");
} else {
final DataColumnSpec colSpec = spec.getColumnSpec(index);
final DataType type = colSpec.getType();
if (!type.equals(field.getDataType())) {
// Input column type changed, try to find new converter
final Optional<?> factory = ConverterUtil.getConverterFactory(type, field.getJavaType());
if (factory.isPresent()) {
warnings.add("The type of the column \"" + field.getKnimeName() + "\" has changed but is compatible.");
field.setConverterFactory(type, (DataCellToJavaConverterFactory<?, ?>) factory.get());
} else {
errors.add("The type of the column \"" + field.getKnimeName() + "\" has changed.");
}
}
}
if (!field.getConverterFactory().isPresent()) {
errors.add(String.format("Missing converter for column '%s' to java field '%s' (converter id: '%s')", field.getKnimeName(), field.getJavaName(), field.getConverterFactoryId()));
}
}
// check input variables
for (InVar field : m_fields.getInVarFields()) {
FlowVariable var = flowVariableRepository.getFlowVariable(field.getKnimeName());
if (var != null) {
if (!var.getType().equals(field.getFlowVarType())) {
errors.add("The type of the flow variable \"" + field.getKnimeName() + "\" has changed.");
}
} else {
errors.add("The flow variable \"" + field.getKnimeName() + "\" is not found in the input.");
}
}
// check output fields
for (OutCol field : m_fields.getOutColFields()) {
if (field.getJavaType() == null) {
errors.add("Java type could not be loaded. Providing plugin may be missing.");
}
int index = spec.findColumnIndex(field.getKnimeName());
if (field.getReplaceExisting() && index < 0) {
errors.add("The output column \"" + field.getKnimeName() + "\" is marked to be a replacement, " + "but an input with this name does not exist.");
}
if (!field.getReplaceExisting() && index > 0) {
errors.add("The output column \"" + field.getKnimeName() + "\" is marked to be new, " + "but an input with this name does exist.");
}
if (!field.getConverterFactory().isPresent()) {
errors.add(String.format("Missing converter for java field '%s' to column '%s' (converter id: '%s')", field.getJavaName(), field.getKnimeName(), field.getConverterFactoryId()));
}
}
// check output variables
for (OutVar field : m_fields.getOutVarFields()) {
FlowVariable var = flowVariableRepository.getFlowVariable(field.getKnimeName());
if (field.getReplaceExisting() && var == null) {
errors.add("The output flow variable \"" + field.getKnimeName() + "\" is marked to be a replacement, " + "but an input with this name does not exist.");
}
if (!field.getReplaceExisting() && var != null) {
errors.add("The output flow variable \"" + field.getKnimeName() + "\" is marked to be new, " + "but an input with this name does exist.");
}
}
// Check additional bundles
for (final String bundleName : m_settings.getBundles()) {
final Bundle bundle = Platform.getBundle(bundleName);
if (bundle == null) {
errors.add("Bundle \"" + bundleName + "\" required by this snippet was not found.");
} else {
// TODO Version warning?
}
}
try {
// test if snippet compiles and if the file can be created
createSnippetClass();
} catch (Exception e) {
errors.add(e.getMessage());
} finally {
close();
}
return new ValidationReport(errors.toArray(new String[errors.size()]), warnings.toArray(new String[warnings.size()]));
}
use of org.knime.base.node.jsnippet.util.field.OutCol in project knime-core by knime.
the class JavaSnippetCellFactory method getCells.
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
@Override
public DataCell[] getCells(final DataRow row) {
try {
if (null == m_jsnippet) {
m_jsnippet = m_snippet.createSnippetInstance();
// populate the fields in the m_jsnippet that are constant
// across the rows.
Field[] fs = m_jsnippet.getClass().getSuperclass().getDeclaredFields();
for (Field field : fs) {
if (field.getName().equals("m_flowVars")) {
field.setAccessible(true);
field.set(m_jsnippet, m_flowVars);
}
if (field.getName().equals(JavaSnippet.ROWCOUNT)) {
field.setAccessible(true);
field.set(m_jsnippet, m_rowCount);
}
}
}
// populate data structure with the input cells
Map<String, Cell> cellsMap = createCellsMap(row);
if (null == m_columns) {
m_columns = new ArrayList<>();
m_columns.addAll(cellsMap.keySet());
}
Field[] fs = m_jsnippet.getClass().getSuperclass().getDeclaredFields();
for (Field field : fs) {
if (field.getName().equals("m_cellsMap")) {
field.setAccessible(true);
field.set(m_jsnippet, cellsMap);
}
if (field.getName().equals("m_cells")) {
field.setAccessible(true);
List<Cell> cells = new ArrayList<>();
cells.addAll(cellsMap.values());
field.set(m_jsnippet, cells);
}
if (field.getName().equals("m_columns")) {
field.setAccessible(true);
field.set(m_jsnippet, m_columns);
}
if (field.getName().equals("m_inSpec")) {
field.setAccessible(true);
field.set(m_jsnippet, m_spec);
}
if (field.getName().equals(JavaSnippet.ROWID)) {
field.setAccessible(true);
field.set(m_jsnippet, row.getKey().getString());
}
if (field.getName().equals(JavaSnippet.ROWINDEX)) {
field.setAccessible(true);
field.set(m_jsnippet, m_rowIndex);
}
}
// populate the system input column fields with data
for (InCol inCol : m_snippet.getSystemFields().getInColFields()) {
Field field = m_jsnippet.getClass().getField(inCol.getJavaName());
final DataCell cell = row.getCell(m_spec.findColumnIndex(inCol.getKnimeName()));
if (cell.isMissing()) {
field.set(m_jsnippet, null);
continue;
}
// Get the converter factory for this column
final Optional<DataCellToJavaConverterFactory<?, ?>> factory = ConverterUtil.getDataCellToJavaConverterFactory(inCol.getConverterFactoryId());
if (!factory.isPresent()) {
throw new RuntimeException("Missing converter factory with ID: " + inCol.getConverterFactoryId());
}
final Object converted = factory.get().create().convertUnsafe(cell);
field.set(m_jsnippet, converted);
}
// reset the system output fields to null (see also bug 3781)
for (OutCol outCol : m_snippet.getSystemFields().getOutColFields()) {
Field field = m_jsnippet.getClass().getField(outCol.getJavaName());
field.set(m_jsnippet, null);
}
// populate the system input flow variable fields with data
for (InVar inCol : m_snippet.getSystemFields().getInVarFields()) {
Field field = m_jsnippet.getClass().getField(inCol.getJavaName());
Object v = m_flowVars.getValueOfType(inCol.getKnimeName(), inCol.getJavaType());
field.set(m_jsnippet, v);
}
} catch (Exception e) {
// re-throw exception
throw new RuntimeException(e);
}
try {
// evaluate user script
m_jsnippet.snippet();
} catch (Throwable thr) {
if (thr instanceof Abort) {
StringBuilder builder = new StringBuilder("Calculation aborted: ");
String message = thr.getMessage();
builder.append(message == null ? "<no details>" : message);
throw new RuntimeException(builder.toString(), thr);
} else {
Integer lineNumber = null;
for (StackTraceElement ste : thr.getStackTrace()) {
if (ste.getClassName().equals("JSnippet")) {
lineNumber = ste.getLineNumber();
}
}
StringBuilder msg = new StringBuilder();
msg.append("Evaluation of java snippet failed for row \"");
msg.append(row.getKey());
msg.append("\". ");
if (lineNumber != null) {
msg.append("The exception is caused by line ");
msg.append(lineNumber);
msg.append(" of the snippet. ");
}
if (thr.getMessage() != null) {
msg.append("Exception message:");
msg.append(thr.getMessage());
}
LOGGER.warn(msg.toString(), thr);
OutVarList outVars = m_snippet.getSystemFields().getOutVarFields();
if (outVars.size() > 0) {
// Abort if flow variables are defined
throw new RuntimeException("An error occured in an " + "expression with output flow variables.", thr);
}
OutColList outFields = m_snippet.getSystemFields().getOutColFields();
DataCell[] out = new DataCell[outFields.size()];
for (int i = 0; i < out.length; i++) {
// Return missing values for output fields
out[i] = DataType.getMissingCell();
}
m_rowIndex++;
return out;
}
}
try {
// update m_flowVars with output flow variable fields.
for (OutVar var : m_snippet.getSystemFields().getOutVarFields()) {
Field field = m_jsnippet.getClass().getField(var.getJavaName());
Object value = field.get(m_jsnippet);
if (null != value) {
Type type = var.getFlowVarType();
FlowVariable flowVar = null;
if (type.equals(Type.INTEGER)) {
flowVar = new FlowVariable(var.getKnimeName(), (Integer) value);
} else if (type.equals(Type.DOUBLE)) {
flowVar = new FlowVariable(var.getKnimeName(), (Double) value);
} else {
// case type.equals(Type.String)
flowVar = new FlowVariable(var.getKnimeName(), (String) value);
}
m_flowVars.put(flowVar);
} else {
throw new RuntimeException("Flow variable \"" + var.getKnimeName() + "\" has no value.");
}
}
// get output column fields
OutColList outFields = m_snippet.getSystemFields().getOutColFields();
DataCell[] out = new DataCell[outFields.size()];
for (int i = 0; i < out.length; i++) {
OutCol outField = outFields.get(i);
Field field = m_jsnippet.getClass().getField(outField.getJavaName());
Object value = field.get(m_jsnippet);
if (null == value) {
out[i] = DataType.getMissingCell();
} else {
final String id = outField.getConverterFactoryId();
Optional<JavaToDataCellConverterFactory<?>> factory = ConverterUtil.getJavaToDataCellConverterFactory(id);
if (!factory.isPresent()) {
throw new RuntimeException("Missing converter factory with ID: " + id);
}
out[i] = ((JavaToDataCellConverterFactory<Object>) factory.get()).create(m_context).convert(value);
}
}
// Cleanup Closeable inputs
for (final OutCol outCol : m_snippet.getSystemFields().getOutColFields()) {
final Field field = m_jsnippet.getClass().getField(outCol.getJavaName());
final Object value = field.get(m_jsnippet);
if (value instanceof Closeable) {
((Closeable) value).close();
}
if (value instanceof AutoCloseable) {
// From the doc: Calling close more than once *can* have visible side effects!
((AutoCloseable) value).close();
}
}
m_rowIndex++;
return out;
} catch (Exception e) {
// but in case re-throw exception
throw new RuntimeException(e);
}
}
Aggregations