/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.scripting;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.script.ScriptEngine;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import de.rcenvironment.core.component.api.ComponentConstants;
import de.rcenvironment.core.component.api.ComponentException;
import de.rcenvironment.core.component.datamanagement.api.CommonComponentHistoryDataItem;
import de.rcenvironment.core.component.datamanagement.api.ComponentDataManagementService;
import de.rcenvironment.core.component.datamanagement.api.ComponentHistoryDataItem;
import de.rcenvironment.core.component.execution.api.ComponentContext;
import de.rcenvironment.core.component.model.endpoint.api.EndpointDefinition.InputExecutionContraint;
import de.rcenvironment.core.datamodel.api.DataType;
import de.rcenvironment.core.datamodel.api.TypedDatum;
import de.rcenvironment.core.datamodel.api.TypedDatumFactory;
import de.rcenvironment.core.datamodel.api.TypedDatumService;
import de.rcenvironment.core.datamodel.types.api.BooleanTD;
import de.rcenvironment.core.datamodel.types.api.DirectoryReferenceTD;
import de.rcenvironment.core.datamodel.types.api.FileReferenceTD;
import de.rcenvironment.core.datamodel.types.api.FloatTD;
import de.rcenvironment.core.datamodel.types.api.IntegerTD;
import de.rcenvironment.core.datamodel.types.api.MatrixTD;
import de.rcenvironment.core.datamodel.types.api.ShortTextTD;
import de.rcenvironment.core.datamodel.types.api.SmallTableTD;
import de.rcenvironment.core.datamodel.types.api.VectorTD;
import de.rcenvironment.core.utils.common.StringUtils;
/**
* Utils for all scripting elements.
*
* @author Sascha Zur
* @author Jascha Riedel (#14029)
* @author David Scholz (#14550, #14548)
*/
public final class ScriptingUtils {
/**
* Execution of Jython scripts must be synchronized with this lock object to ensure that only one Jython script is executed at the same
* time within the entire JVM. The reason is that the Jython script engine is not thread safe (console outputs of multiple script
* executions are mixed).
*/
public static final Object SCRIPT_EVAL_LOCK_OBJECT = new Object();
protected static final Log LOGGER = LogFactory.getLog(ScriptingUtils.class);
private static final String JYTHON_JAR = "jython-standalone-2.5.1.jar";
private static final String NOT_A_VALUE_UUID = "not_a_value_7fdc603e";
private static final String USE_PYTHON_AS_SCRIPT_LANGUAGE_INSTEAD_STRING = " use Python as script language instead";
private static String jythonPath = null;
private static final String SLASH = "/";
private static final String ESCAPESLASH = "\\\\";
private static final String QUOTE = "\"";
private static final String CLOSE_LIST_NEWLINE = "]\n";
private static final String COMMA = ",";
private static final int MAXIMUM_SMALL_TABLE_ENTRIES = 10000;
private static TypedDatumFactory typedDatumFactory;
private static ComponentDataManagementService componentDatamanagementService;
public ScriptingUtils() {
}
/**
* Determines the location of the jython.jar.
*
* @throws IOException no valid file
* @return path if found, else null
*/
public static synchronized String getJythonPath() throws IOException {
if (jythonPath == null) {
// getting the Path where the Jython.jar is located. This is needed to
// import Libraries to the jython script, e.g. os or re.
String osgiConfigurationArea = System.getProperty("osgi.configuration.area");
String decode = URLDecoder.decode(osgiConfigurationArea, "UTF-8");
File osgiBundlestoreDir = null;
if (decode.startsWith("file:/")) {
osgiBundlestoreDir = new File(decode.substring(decode.indexOf("/")));
}
if (osgiBundlestoreDir != null) {
jythonPath = findJythonPath(osgiBundlestoreDir);
}
}
// return null;
return jythonPath;
}
/**
* Set jython path for tests.
*
* @param path to set
*/
public static synchronized void setJythonPath(String path) {
jythonPath = path;
}
// /**
// * Set JVM properties required for proper Jython 2.7.0 support.
// */
// public static void setJVMPropertiesForJython270Support() {
// System.setProperty("python.import.site", "false");
// System.setProperty("python.console.encoding", "UTF-8");
// }
/**
* Determines the location of the jython.jar.
*
* @param file directory
* @throws IOException no valid file
* @return path if found, else null
*/
private static String findJythonPath(File file) throws IOException {
if (file.isDirectory()) {
File[] children = file.listFiles();
for (File element : children) {
String path = findJythonPath(element);
if (path != null) {
return path;
}
}
} else {
String path = file.getAbsolutePath();
path = path.replaceAll(ESCAPESLASH, SLASH);
String[] splitted = path.split(SLASH);
if (splitted[splitted.length - 1].equals(JYTHON_JAR)) {
return path + "/Lib";
}
}
return null;
}
/**
* Prepares a header script for using RCE Python API with Jython.
*
* @param localStateMap state map of the component
* @param componentContext of the component
* @param tempDir for creating temp files
* @param tempFiles to delete removed
* @return prepared header script
* @throws ComponentException on unexpected error
*/
public static String prepareHeaderScript(Map<String, Object> localStateMap, ComponentContext componentContext, File tempDir,
List<File> tempFiles) throws ComponentException {
String currentHeader = "";
try (InputStream in = ScriptingUtils.class.getResourceAsStream("/resources/RCE_Jython.py")) {
currentHeader = IOUtils.toString(in);
} catch (IOException e) {
throw new ComponentException("Internal error: Failed to intialize script that is wrapped around the actual script", e);
}
String stateMapDefinition = "RCE_STATE_VARIABLES = {";
boolean first = true;
for (String key : localStateMap.keySet()) {
if (!first) {
stateMapDefinition += COMMA;
} else {
first = false;
}
stateMapDefinition += QUOTE + key + "\" : " + localStateMap.get(key);
}
stateMapDefinition += "}";
currentHeader += stateMapDefinition + "\n";
// loading all input values
currentHeader += prepareInput(tempDir, componentContext, tempFiles);
String wrappingScript = "RCE_LIST_OUTPUTNAMES = [";
first = true;
for (String outputName : componentContext.getOutputs()) {
if (!first) {
wrappingScript += COMMA;
} else {
first = false;
}
wrappingScript += " \"" + outputName + QUOTE;
}
wrappingScript += "]\n";
currentHeader += StringUtils.format("RCE_CURRENT_RUN_NUMBER = %s\n", componentContext.getExecutionCount());
currentHeader += wrappingScript;
currentHeader += "RCE.setDictionary_internal(RCE_Dict_InputChannels)\nimport shutil\n";
List<String> notConnected = new LinkedList<>();
for (String input : componentContext.getInputsNotConnected()) {
if (componentContext.getInputMetaDataValue(input, ComponentConstants.INPUT_METADATA_KEY_INPUT_EXECUTION_CONSTRAINT) != null
&& (componentContext.getInputMetaDataValue(input, ComponentConstants.INPUT_METADATA_KEY_INPUT_EXECUTION_CONSTRAINT).equals(
InputExecutionContraint.RequiredIfConnected.name())
|| componentContext.getInputMetaDataValue(input, ComponentConstants.INPUT_METADATA_KEY_INPUT_EXECUTION_CONSTRAINT)
.equals(
InputExecutionContraint.NotRequired.name()))) {
notConnected.add(input);
}
}
for (String input : componentContext.getInputs()) {
if (componentContext.getInputMetaDataValue(input, ComponentConstants.INPUT_METADATA_KEY_INPUT_EXECUTION_CONSTRAINT) != null
&& componentContext.getInputMetaDataValue(input, ComponentConstants.INPUT_METADATA_KEY_INPUT_EXECUTION_CONSTRAINT).equals(
InputExecutionContraint.NotRequired.name())
&& !componentContext.getInputsWithDatum().contains(input)) {
notConnected.add(input);
}
}
String notConnectedValues = "[";
for (String input : notConnected) {
notConnectedValues += "\"" + input + "\",";
}
notConnectedValues.substring(0, notConnectedValues.length() - 1);
notConnectedValues += "]";
currentHeader += StringUtils.format("RCE_LIST_REQ_IF_CONNECTED_INPUTS = %s\n", notConnectedValues);
return currentHeader;
}
private static String prepareInput(File tempDir, ComponentContext compContext, List<File> tempFiles) throws ComponentException {
final String openBracket = "[";
String dataDefinition = "RCE_Dict_InputChannels = { ";
String nameAndValue = "";
for (String inputName : compContext.getInputsWithDatum()) {
nameAndValue = " \"" + StringEscapeUtils.escapeJava(inputName) + "\" : ";
TypedDatum input = compContext.readInput(inputName);
switch (compContext.getInputDataType(inputName)) {
case FileReference:
File fileInputDir = new File(tempDir, inputName);
tempFiles.add(fileInputDir);
File file = new File(fileInputDir, ((FileReferenceTD) input).getFileName());
try {
// Since the code is shared for the script component and tool integration, there
// must be some difference here:
// In tool integration, copying the data is done by the component so at this
// point, it is not needed any more (is already exists)
// For the script component, this should always run.
if (!file.exists()) {
componentDatamanagementService.copyFileReferenceTDToLocalFile(compContext, (FileReferenceTD) input, file);
}
} catch (IOException e) {
throw new ComponentException("Failed to read input file from the data management", e);
}
nameAndValue += QUOTE + file.getAbsolutePath().toString().replaceAll(ESCAPESLASH, SLASH) + QUOTE;
break;
case DirectoryReference:
File dirInputDir = new File(tempDir, inputName);
tempFiles.add(dirInputDir);
File dir = new File(dirInputDir, ((DirectoryReferenceTD) input).getDirectoryName());
try {
// see comment of file above
if (!dir.exists()) {
componentDatamanagementService.copyDirectoryReferenceTDToLocalDirectory(compContext,
(DirectoryReferenceTD) input, dirInputDir);
}
} catch (IOException e) {
throw new ComponentException("Failed to read input directory from the data management", e);
}
nameAndValue += QUOTE + dir.getAbsolutePath().toString().replaceAll(ESCAPESLASH, SLASH) + QUOTE;
break;
case Boolean:
boolean bool = (((BooleanTD) input).getBooleanValue());
if (bool) {
nameAndValue += "True";
} else {
nameAndValue += "False";
}
break;
case ShortText:
String value = ((ShortTextTD) input).getShortTextValue();
if (value.contains("\n")) {
nameAndValue += QUOTE + QUOTE + QUOTE + StringEscapeUtils.escapeJava(value) + QUOTE + QUOTE + QUOTE;
} else {
nameAndValue += QUOTE + StringEscapeUtils.escapeJava(value) + QUOTE;
}
break;
case Integer:
nameAndValue += input;
break;
case Float:
String append = replaceNonNumericValue(((FloatTD) input).getFloatValue());
if (append.isEmpty()) {
nameAndValue += ((FloatTD) input).getFloatValue();
} else {
nameAndValue += append;
}
break;
case Empty:
nameAndValue = "None";
break;
case Vector:
VectorTD vector = (VectorTD) input;
nameAndValue += openBracket;
if (vector.getRowDimension() > MAXIMUM_SMALL_TABLE_ENTRIES) {
throw new ComponentException(
StringUtils.format(
"Vector of input '%s' exceeds maximum number of entries allowed for Jython (entries: %s; maximum: %s);"
+ USE_PYTHON_AS_SCRIPT_LANGUAGE_INSTEAD_STRING,
inputName,
vector.getRowDimension(), MAXIMUM_SMALL_TABLE_ENTRIES));
}
for (int i = 0; i < vector.getRowDimension(); i++) {
String appending = replaceNonNumericValue(vector.getFloatTDOfElement(i).getFloatValue());
if (appending.isEmpty()) {
nameAndValue += vector.getFloatTDOfElement(i).getFloatValue();
} else {
nameAndValue += appending;
}
nameAndValue += COMMA;
}
if (vector.getRowDimension() > 0) {
nameAndValue = nameAndValue.substring(0, nameAndValue.length() - 1);
}
nameAndValue += CLOSE_LIST_NEWLINE;
break;
case Matrix:
MatrixTD matrix = (MatrixTD) input;
nameAndValue += openBracket;
nameAndValue = getMatrix(openBracket, nameAndValue, inputName, matrix);
nameAndValue = nameAndValue.substring(0, nameAndValue.length() - 1);
nameAndValue += CLOSE_LIST_NEWLINE;
break;
case SmallTable:
nameAndValue = convertSmallTable(openBracket, nameAndValue, inputName, input);
break;
default:
break;
}
dataDefinition += nameAndValue;
// prepare next input.
dataDefinition += " ,";
}
// deleting COMMA and close the dictionary
dataDefinition = dataDefinition.substring(0, dataDefinition.length() - 1);
dataDefinition += "}\n";
return dataDefinition;
}
private static String convertSmallTable(final String openBracket, String nameAndValue, String inputName, TypedDatum input)
throws ComponentException {
SmallTableTD table = (SmallTableTD) input;
nameAndValue += openBracket;
if (table.getRowCount() * table.getColumnCount() > MAXIMUM_SMALL_TABLE_ENTRIES) {
throw new ComponentException(
StringUtils.format(
"Small Table of input '%s' exceeds maximum number of entries allowed for Jython (entries: %s; maximum: %s);"
+ USE_PYTHON_AS_SCRIPT_LANGUAGE_INSTEAD_STRING,
inputName,
table.getRowCount() * table.getColumnCount(), MAXIMUM_SMALL_TABLE_ENTRIES));
}
for (int i = 0; i < table.getRowCount(); i++) {
if (table.getRowCount() > 1) {
nameAndValue += openBracket;
}
for (int j = 0; j < table.getColumnCount(); j++) {
if (ScriptDataTypeHelper.getObjectOfEntryForPythonOrJython(
table.getTypedDatumOfCell(i, j)) instanceof String) {
nameAndValue += QUOTE
+ ScriptDataTypeHelper.getObjectOfEntryForPythonOrJython(table.getTypedDatumOfCell(i, j))
+ QUOTE + COMMA;
} else if (ScriptDataTypeHelper.getObjectOfEntryForPythonOrJython(
table.getTypedDatumOfCell(i, j)) instanceof Double) {
nameAndValue += replaceNonNumericValue(((FloatTD) table.getTypedDatumOfCell(i, j)).getFloatValue()) + COMMA;
} else {
nameAndValue +=
ScriptDataTypeHelper.getObjectOfEntryForPythonOrJython(table.getTypedDatumOfCell(i, j)) + COMMA;
}
}
nameAndValue = nameAndValue.substring(0, nameAndValue.length() - 1);
if (table.getRowCount() > 1) {
nameAndValue += "],";
} else {
nameAndValue += COMMA;
}
}
nameAndValue = nameAndValue.substring(0, nameAndValue.length() - 1);
nameAndValue += CLOSE_LIST_NEWLINE;
return nameAndValue;
}
private static String replaceNonNumericValue(double floatValue) {
String result = "";
if (floatValue == Double.NEGATIVE_INFINITY) {
result += "float(\"-INF\")";
} else if (floatValue == Double.POSITIVE_INFINITY) {
result += "float(\"INF\")";
} else if (Double.isNaN(floatValue)) {
result += "float(\"nan\")";
} else {
result += floatValue;
}
return result;
}
private static String getMatrix(final String openBracket, String nameAndValue, String inputName, MatrixTD matrix)
throws ComponentException {
if (matrix.getRowDimension() * matrix.getColumnDimension() > MAXIMUM_SMALL_TABLE_ENTRIES) {
throw new ComponentException(
StringUtils.format(
"Small Table of input '%s' exceeds maximum number of entries allowed for Jython (entries: %s; maximum: %s);"
+ USE_PYTHON_AS_SCRIPT_LANGUAGE_INSTEAD_STRING,
inputName,
matrix.getRowDimension() * matrix.getColumnDimension(), MAXIMUM_SMALL_TABLE_ENTRIES));
}
for (int i = 0; i < matrix.getRowDimension(); i++) {
if (matrix.getRowDimension() > 1) {
nameAndValue += openBracket;
}
for (int j = 0; j < matrix.getColumnDimension(); j++) {
if (ScriptDataTypeHelper.getObjectOfEntryForPythonOrJython(
matrix.getFloatTDOfElement(i, j)) instanceof String) {
nameAndValue += QUOTE
+ ScriptDataTypeHelper.getObjectOfEntryForPythonOrJython(matrix.getFloatTDOfElement(i, j))
+ QUOTE + COMMA;
} else if (ScriptDataTypeHelper.getObjectOfEntryForPythonOrJython(
matrix.getFloatTDOfElement(i, j)) instanceof Double) {
String append = replaceNonNumericValue(matrix.getFloatTDOfElement(i, j).getFloatValue());
if (append.isEmpty()) {
nameAndValue += ScriptDataTypeHelper.getObjectOfEntryForPythonOrJython(matrix.getFloatTDOfElement(i, j));
} else {
nameAndValue += append;
}
nameAndValue += COMMA;
} else {
nameAndValue +=
ScriptDataTypeHelper.getObjectOfEntryForPythonOrJython(matrix.getFloatTDOfElement(i, j)) + COMMA;
}
}
nameAndValue = nameAndValue.substring(0, nameAndValue.length() - 1);
if (matrix.getRowDimension() > 1) {
nameAndValue += "],";
} else {
nameAndValue += COMMA;
}
}
return nameAndValue;
}
/**
* Write all output written with the RCE Script API.
*
* @param stateMap current state map of script
* @param componentContext from component
* @param engine script engine
* @param workingPath for files
* @param historyDataItem of component instance
* @throws ComponentException e
*/
@SuppressWarnings("unchecked")
public static void writeAPIOutput(Map<String, Object> stateMap, ComponentContext componentContext, ScriptEngine engine,
String workingPath, ComponentHistoryDataItem historyDataItem) throws ComponentException {
Map<String, ArrayList<Object>> map = (Map<String, ArrayList<Object>>) engine.get("RCE_Dict_OutputChannels");
// send values to outputs, using the Map
// this block sends the values when the user calls the method RCE.write_output()
for (String outputName : componentContext.getOutputs()) {
DataType type = componentContext.getOutputDataType(outputName);
List<Object> datas = map.get(outputName);
if (datas != null) {
for (Object value : datas) {
if (value != null && !String.valueOf(value).equals(NOT_A_VALUE_UUID)) {
writeOutputByType(value, type, outputName, workingPath, engine, historyDataItem, componentContext);
} else if (String.valueOf(value).equals(NOT_A_VALUE_UUID)) {
componentContext.writeOutput(outputName, typedDatumFactory.createNotAValue());
}
}
}
}
Map<String, Object> stateMapOutput = (Map<String, Object>) engine.get("RCE_STATE_VARIABLES");
for (String key : stateMapOutput.keySet()) {
stateMap.put(key, stateMapOutput.get(key));
}
for (String endpointName : (List<String>) engine.get("RCE_CloseOutputChannelsList")) {
componentContext.closeOutput(endpointName);
}
}
/**
* @param engine the {@link ScriptEngine} executing the script which should be considered
* @param componentContext the {@link ComponentContext} of the component
* @return set with names of those output for which a not-a-value {@link TypedDatum} was written
*/
@SuppressWarnings("unchecked")
public static Set<String> getOutputsSendingNotAValue(ScriptEngine engine, ComponentContext componentContext) {
Map<String, ArrayList<Object>> map = (Map<String, ArrayList<Object>>) engine.get("RCE_Dict_OutputChannels");
Set<String> returnSet = new HashSet<>();
for (String outputName : componentContext.getOutputs()) {
List<Object> datas = map.get(outputName);
if (datas != null) {
for (Object value : datas) {
if (value != null && String.valueOf(value).equals((NOT_A_VALUE_UUID))) {
returnSet.add(outputName);
break;
}
}
}
}
return returnSet;
}
@SuppressWarnings("unchecked")
protected static void writeOutputByType(Object value, DataType type, String name, String workingPath, ScriptEngine engine,
ComponentHistoryDataItem historyDataItem, ComponentContext componentContext) throws ComponentException {
TypedDatum outputValue = null;
switch (type) {
case ShortText:
outputValue = typedDatumFactory.createShortText(value.toString());
break;
case Boolean:
String stringValue = value.toString();
boolean isNumber = true;
try {
float numberValue = Float.parseFloat(stringValue);
if (Math.abs(numberValue) > 0) {
outputValue = typedDatumFactory.createBoolean(true);
} else {
outputValue = typedDatumFactory.createBoolean(false);
}
} catch (NumberFormatException e) {
isNumber = false;
}
if (!isNumber && (stringValue.equalsIgnoreCase("0") || stringValue.equalsIgnoreCase("0L") || stringValue.equalsIgnoreCase("0.0")
|| stringValue.equalsIgnoreCase("0j") || stringValue.equalsIgnoreCase("()") || stringValue.equalsIgnoreCase("[]")
|| stringValue.isEmpty() || stringValue.equalsIgnoreCase("{}") || stringValue.equalsIgnoreCase("false")
|| stringValue.equalsIgnoreCase("none"))) {
outputValue = typedDatumFactory.createBoolean(false);
} else if (!isNumber) {
outputValue = typedDatumFactory.createBoolean(true);
}
break;
case Float:
try {
outputValue = typedDatumFactory.createFloat(Double.parseDouble(value.toString()));
} catch (NumberFormatException e) {
throw new ComponentException(StringUtils.format("Failed to parse output value '%s' to data type Float."
+ " Possible reasons (not restricted): Output value too big (max. %.1e),"
+ " or output value contains non numeric characters.",
value.toString(), Double.MAX_VALUE));
}
break;
case Integer:
try {
outputValue = typedDatumFactory.createInteger(Long.parseLong(value.toString()));
} catch (NumberFormatException e) {
throw new ComponentException(StringUtils.format("Failed to parse output value '%s' to data type Integer."
+ " Possible reasons (not restricted): Output value too big (max. 2E"
+ Long.toBinaryString(Long.MAX_VALUE).length() + " - 1),"
+ " or output value contains non numeric characters.",
value.toString()));
}
break;
case FileReference:
outputValue = handeFileOrDirectoryOutput(value, "file", name, workingPath, componentContext, outputValue);
break;
case DirectoryReference:
outputValue = handeFileOrDirectoryOutput(value, "directory ", name, workingPath, componentContext, outputValue);
break;
case Vector:
VectorTD vector = null;
List<Object> vectorRow = (List<Object>) value;
vector = typedDatumFactory.createVector(vectorRow.size());
int index = 0;
for (Object element : vectorRow) {
double convertedValue = 0;
if (element instanceof List) {
throw new ComponentException(StringUtils
.format("Value for endpoint %s was a matrix, but endpoint is of type Vector", name));
}
if (element == null) {
throw new ComponentException(StringUtils
.format("Value \"None\" of cell %s is not valid for type Vector \"%s\"", index, name));
} else if (element instanceof Integer) {
convertedValue = (Integer) element;
} else {
convertedValue = (Double) element;
}
vector.setFloatTDForElement(typedDatumFactory.createFloat(convertedValue), index);
index++;
}
outputValue = vector;
break;
case Matrix:
outputValue = createResultMatrix(value, name);
break;
case SmallTable:
TypedDatum[][] result = createResultArray(value);
outputValue = typedDatumFactory.createSmallTable(result);
break;
default:
outputValue = typedDatumFactory.createShortText(engine.get(name).toString());
break;
}
componentContext.writeOutput(name, outputValue);
addOutputToHistoryDataItem(name, outputValue, historyDataItem);
}
/**
* Converts the output of the script to a {@link MatrixTD}.
*
* @param value result of script
* @param name of output
* @return created matrix
* @throws ComponentException if the dimensions are not correct.
*/
@SuppressWarnings("unchecked")
public static MatrixTD createResultMatrix(Object value, String name) throws ComponentException {
if (!(value instanceof List)) {
throw new ComponentException(StringUtils.format("Value \"%s\" of output \"%s\" is not of type matrix.", value, name));
}
List<Object> rowArray = (List<Object>) value;
if (rowArray.size() > 0 && rowArray.get(0) instanceof List) {
int columnDimension = ((List<Object>) rowArray.get(0)).size();
MatrixTD matrix = typedDatumFactory.createMatrix(rowArray.size(), columnDimension);
int i = 0;
for (Object columnObject : rowArray) {
if (!(columnObject instanceof List)) {
throw new ComponentException(
StringUtils.format("Value \"%s\" of output \"%s\" is not of type matrix.", columnObject, name));
}
List<Object> columnArray = (List<Object>) columnObject;
if (columnArray.size() == 0) {
throw new ComponentException(StringUtils.format("Column %s of matrix \"%s\" does not contain any elements.", i, name));
}
if (columnArray.size() != columnDimension) {
throw new ComponentException(
StringUtils.format("Column %s of matrix %s has the wrong dimension (%s, should be %s).", i, name,
columnArray.size(), columnDimension));
}
int j = 0;
for (Object element : columnArray) {
TypedDatum cellValue = ScriptDataTypeHelper.getTypedDatum(element, typedDatumFactory);
if (cellValue instanceof FloatTD) {
matrix.setFloatTDForElement((FloatTD) cellValue, i, j++);
} else if (cellValue instanceof IntegerTD) {
matrix.setFloatTDForElement(typedDatumFactory.createFloat(((IntegerTD) cellValue).getIntValue()), i, j++);
} else if (cellValue instanceof ShortTextTD && ((ShortTextTD) cellValue).getShortTextValue().equals("+Infinity")) {
matrix.setFloatTDForElement(typedDatumFactory.createFloat(Double.POSITIVE_INFINITY), i, j++);
} else {
String elementString = "None";
if (element != null) {
elementString = element.toString();
}
throw new ComponentException(
StringUtils.format("Value \"%s\" of cell (%s, %s) of matrix \"%s\" is not an integer or a float value.",
elementString, i, j, name));
}
}
i++;
}
return matrix;
}
throw new ComponentException(StringUtils.format("Dimensions of %s not correct to convert to a Matrix", name));
}
@SuppressWarnings("unchecked")
private static TypedDatum[][] createResultArray(Object value) throws ComponentException {
if (!(value instanceof List)) {
throw new ComponentException(StringUtils.format("Value \"%s\" is not of type small table.", value));
}
List<Object> rowArray = (List<Object>) value;
TypedDatum[][] result = new TypedDatum[rowArray.size()][];
Object first = "";
if (rowArray.size() > 0 && rowArray.get(0) instanceof List) {
int i = 0;
int size = 0;
for (Object columnObject : rowArray) {
if (!(columnObject instanceof List)) {
throw new ComponentException(StringUtils.format("Value \"%s\" is not of type small table.", columnObject));
}
List<Object> columnArray = (List<Object>) columnObject;
if (size == 0) {
first = columnObject;
size = columnArray.size();
}
if (size != columnArray.size()) {
throw new ComponentException(StringUtils.format(
"Each row must have the same number of elements in a small table. "
+ "Element count of \"%s\" and \"%s\" does not match.",
first, columnObject));
}
if (columnArray.size() == 0) {
result[i] = new TypedDatum[1];
result[i][0] = typedDatumFactory.createEmpty();
} else {
result[i] = new TypedDatum[columnArray.size()];
}
int j = 0;
for (Object element : columnArray) {
result[i][j++] = ScriptDataTypeHelper.getTypedDatum(element, typedDatumFactory);
}
i++;
}
} else {
int i = 0;
// ??
for (Object element : rowArray) {
result[i] = new TypedDatum[1];
result[i][0] = ScriptDataTypeHelper.getTypedDatum(element, typedDatumFactory);
i++;
}
}
return result;
}
private static TypedDatum handeFileOrDirectoryOutput(Object value, String type, String name, String workingPath,
ComponentContext componentContext, TypedDatum outputValue) throws ComponentException {
try {
File file = new File(value.toString());
if (!file.isAbsolute()) {
file = new File(workingPath, value.toString());
}
if (file.exists()) {
if (type.equals("file")) {
if (file.isDirectory()) {
throw new ComponentException(StringUtils.format(
"Failed to write %s to output '%s' as it is a directory.", file.getAbsolutePath(), name));
}
outputValue = componentDatamanagementService.createFileReferenceTDFromLocalFile(
componentContext, file, file.getName());
} else {
if (!file.isDirectory()) {
throw new ComponentException(StringUtils.format(
"Failed to write %s to output '%s' as it is not a directory.", file.getAbsolutePath(), name));
}
outputValue = componentDatamanagementService.createDirectoryReferenceTDFromLocalDirectory(componentContext, file,
file.getName());
}
} else {
throw new ComponentException(StringUtils.format(
"Failed to write %s to output '%s' as it does not exist: %s", type, name, file.getAbsolutePath()));
}
} catch (IOException e) {
throw new ComponentException(StringUtils.format("Failed to store %s into the data management - "
+ "if it is not stored in the data management, it can not be sent as output value", type), e);
}
return outputValue;
}
private static void addOutputToHistoryDataItem(String name, TypedDatum outputValue, ComponentHistoryDataItem historyDataItem) {
if (historyDataItem != null) {
((CommonComponentHistoryDataItem) historyDataItem).addOutput(name, outputValue);
}
}
/**
* OSGI method.
*
* @param newTypedDatumService new service
*/
public void bindTypedDatumService(TypedDatumService newTypedDatumService) {
typedDatumFactory = newTypedDatumService.getFactory();
}
/**
* OSGI method.
*
* @param oldTypedDatumService new service
*/
public void unbindTypedDatumService(TypedDatumService oldTypedDatumService) {
/*
* nothing to do here, this unbind method is only needed, because DS is throwing an exception when disposing otherwise. probably a
* bug
*/
}
/**
* OSGI method.
*
* @param compDataManagementService new service
*/
public void bindComponentDataManagementService(ComponentDataManagementService compDataManagementService) {
componentDatamanagementService = compDataManagementService;
}
/**
* OSGI method.
*
* @param compDataManagementService new service
*/
public void unbindComponentDataManagementService(ComponentDataManagementService compDataManagementService) {
componentDatamanagementService = null;
}
}