diff --git a/src/main/java/com/rapiddweller/benerator/script/GraalValueConverter.java b/src/main/java/com/rapiddweller/benerator/script/GraalValueConverter.java
index cbcd91f6..bab95e73 100644
--- a/src/main/java/com/rapiddweller/benerator/script/GraalValueConverter.java
+++ b/src/main/java/com/rapiddweller/benerator/script/GraalValueConverter.java
@@ -23,7 +23,6 @@
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
-
package com.rapiddweller.benerator.script;
import com.rapiddweller.common.ConversionException;
@@ -32,76 +31,97 @@
import com.rapiddweller.model.data.Entity;
import org.graalvm.polyglot.Value;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Logger;
/**
- * Convert Graal Values into Java Types
- * https://www.graalvm.org/sdk/javadoc/org/graalvm/polyglot/Value.html
- *
- * Created at 30.12.2020
- *
- * @author Alexander Kell
- * @since 1.1.0
+ * Converts GraalVM Polyglot Values into corresponding Java types.
+ * Throws RuntimeException if a value exceeds the int limits.
*/
public class GraalValueConverter extends ThreadSafeConverter {
- /**
- * Instantiates a new Graal value converter.
- */
+ private static final Logger logger = Logger.getLogger(GraalValueConverter.class.getName());
+
public GraalValueConverter() {
super(Value.class, Object.class);
}
- /**
- * Value 2 java converter object.
- *
- * @param value the value
- * @return the object
- */
public static Object value2JavaConverter(Value value) {
- if (value.fitsInInt()) {
- return value.asInt();
- } else if (value.hasArrayElements()) {
- return getArrayFromValue(value);
- } else if (value.fitsInLong()) {
- return value.asLong();
- } else if (value.fitsInFloat()) {
- return value.asFloat();
- } else if (value.fitsInByte()) {
- return value.asByte();
- } else if (value.fitsInDouble()) {
- return value.asDouble();
- } else if (value.isString()) {
- return value.asString();
- } else if (value.isHostObject()) {
- return value.asHostObject();
- } else if (value.isBoolean()) {
- return value.asBoolean();
- } else if (value.isDate()) {
- return value.asDate();
- } else if (value.isNativePointer()) {
- return value.asNativePointer();
- } else if (value.hasMembers()) {
- // Convert the value to a java.util.Map
- Entity result = new Entity((ComplexTypeDescriptor) null);
- value.getMemberKeys().forEach(key -> result.setComponent(key, value2JavaConverter(value.getMember(key))));
- return result;
- } else {
- return null;
+ return value2JavaConverter(value, new HashMap<>(), 0);
+ }
+
+ private static Object value2JavaConverter(Value value, Map referenceMap, int depth) throws ConversionException {
+ logger.fine("Converting value at depth " + depth + ": " + value);
+ if (referenceMap.containsKey(value)) {
+ logger.fine("Value already processed, returning cached result.");
+ return referenceMap.get(value);
+ }
+
+ Object result;
+ try {
+ if (value.fitsInInt()) {
+ result = value.asInt();
+ } else if (value.fitsInLong()) {
+ result = handleLongValue(value);
+ } else if (value.hasArrayElements()) {
+ result = getArrayFromValue(value, referenceMap, depth);
+ } else if (value.isString()) {
+ result = value.asString();
+ } else if (value.isHostObject()) {
+ return value.asHostObject();
+ } else if (value.isBoolean()) {
+ return value.asBoolean();
+ } else if (value.isDate()) {
+ return value.asDate();
+ } else if (value.isNativePointer()) {
+ return value.asNativePointer();
+ } else if (value.hasMembers()) {
+ result = convertValueToEntity(value, referenceMap, depth);
+ } else {
+ result = null;
+ }
+
+ referenceMap.put(value, result);
+ } catch (Exception e) {
+ logger.severe("Error converting value: " + e.getMessage());
+ throw new ConversionException("Error converting value", e);
}
+ return result;
}
- private static Object getArrayFromValue(Value val) {
+ private static Object handleLongValue(Value value) {
+ long longValue = value.asLong();
+ if (longValue > Integer.MAX_VALUE || longValue < Integer.MIN_VALUE) {
+ throw new RuntimeException("Value exceeds int limits: " + longValue);
+ }
+ return (int) longValue;
+ }
+
+ private static Object[] getArrayFromValue(Value val, Map referenceMap, int depth) throws ConversionException {
Object[] out = new Object[(int) val.getArraySize()];
for (int i = 0; i < val.getArraySize(); i++) {
- out[i] = value2JavaConverter(val.getArrayElement(i));
+ out[i] = value2JavaConverter(val.getArrayElement(i), referenceMap, depth + 1);
}
-
return out;
}
+ private static Entity convertValueToEntity(Value value, Map referenceMap, int depth) {
+ Entity result = new Entity((ComplexTypeDescriptor) null);
+ value.getMemberKeys().forEach(key -> {
+ try {
+ result.setComponent(key, value2JavaConverter(value.getMember(key), referenceMap, depth + 1));
+ } catch (ConversionException e) {
+ logger.warning("Error converting entity member: " + key + "; " + e.getMessage());
+ }
+ });
+ return result;
+ }
+
@Override
public Object convert(Value sourceValue) throws ConversionException {
return value2JavaConverter(sourceValue);
}
}
+
diff --git a/src/main/java/com/rapiddweller/benerator/script/PolyglotContext.java b/src/main/java/com/rapiddweller/benerator/script/PolyglotContext.java
index 360271b7..479a29ec 100644
--- a/src/main/java/com/rapiddweller/benerator/script/PolyglotContext.java
+++ b/src/main/java/com/rapiddweller/benerator/script/PolyglotContext.java
@@ -9,79 +9,104 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.nio.file.Paths;
import java.util.Map;
import java.util.Objects;
class PolyglotContext {
- private static final Logger logger = LoggerFactory.getLogger(PolyglotContext.class);
- private String previousMissingObject;
- private final org.graalvm.polyglot.Context polyglotCtx;
+ private static final Logger logger = LoggerFactory.getLogger(PolyglotContext.class);
+ private String previousMissingObject;
+ private final org.graalvm.polyglot.Context polyglotCtx;
- public void updatePolyglotLocalFromGlobal(Context context, String language) {
- for (String entry : polyglotCtx.getBindings(language).getMemberKeys()) {
- migrateBeneratorContext2GraalVM(context, language, entry);
- }
+ public void updatePolyglotLocalFromGlobal(Context context, String language) {
+ for (String entry : polyglotCtx.getBindings(language).getMemberKeys()) {
+ migrateBeneratorContext2GraalVM(context, language, entry);
}
+ }
- public void migrateBeneratorContext2GraalVM(Context context, String language, String valueKey) {
- Object valueType;
- try {
- Object obj = context.get(valueKey);
- if (obj != null) {
- valueType = obj.getClass();
- // check if Entity Object
- if (Entity.class.equals(valueType)) {
- logger.debug("Entity found : {}", valueKey);
- Map map = new Entity2MapConverter().convert((Entity) obj);
- // to access items of map in polyglotCtx it is nessesary to create an ProxyObject
- ProxyObject proxy = ProxyObject.fromMap(map);
- polyglotCtx.getBindings(language).putMember(valueKey, proxy);
- } else {
- logger.debug("{} found : {}", valueType.getClass(), valueKey);
- polyglotCtx.getBindings(language).putMember(valueKey, obj);
- }
- }
- } catch (NullPointerException e) {
- logger.error("Context {} was NULL, this should not happen!", context);
+ public void migrateBeneratorContext2GraalVM(Context context, String language, String valueKey) {
+ Object valueType;
+ try {
+ Object obj = context.get(valueKey);
+ if (obj != null) {
+ valueType = obj.getClass();
+ // check if Entity Object
+ if (Entity.class.equals(valueType)) {
+ logger.debug("Entity found : {}", valueKey);
+ Map map = new Entity2MapConverter().convert((Entity) obj);
+ // to access items of map in polyglotCtx it is nessesary to create an ProxyObject
+ ProxyObject proxy = ProxyObject.fromMap(map);
+ polyglotCtx.getBindings(language).putMember(valueKey, proxy);
+ } else {
+ logger.debug("{} found : {}", valueType.getClass(), valueKey);
+ polyglotCtx.getBindings(language).putMember(valueKey, obj);
}
-
+ }
+ } catch (NullPointerException e) {
+ logger.error("Context {} was NULL, this should not happen!", context);
}
- synchronized public Value evalScript(Context context, String text, String language) throws ScriptException {
+ }
- Value returnValue = null;
- try {
- this.updatePolyglotLocalFromGlobal(context, language);
- returnValue = polyglotCtx.eval(language, text);
- } catch (org.graalvm.polyglot.PolyglotException e) {
- if (e.getMessage().contains("ReferenceError: ")) {
- String missingObject = e.getMessage().replace("ReferenceError: ", "").replace(" is not defined", "");
- if (!Objects.equals(previousMissingObject, missingObject)) {
- this.migrateBeneratorContext2GraalVM(context, language, missingObject);
- returnValue = evalScript(context, text, language);
- previousMissingObject = missingObject;
- }
- }
- else if (e.getMessage().contains(("NameError: "))) {
- String missingObject = e.getMessage().replace("NameError: name '", "").replace("' is not defined", "");
- if (!Objects.equals(previousMissingObject, missingObject)) {
- this.migrateBeneratorContext2GraalVM(context, language, missingObject);
- returnValue = evalScript(context, text, language);
- previousMissingObject = missingObject;
- }
- }
- else {
- throw new ScriptException(e.getMessage(), null);
- }
+ synchronized public Value evalScript(Context context, String text, String language) throws ScriptException {
+ final int MAX_ATTEMPTS = 3; // Maximum attempts for the same missing object
+ Value returnValue = null;
+
+ try {
+ this.updatePolyglotLocalFromGlobal(context, language);
+ returnValue = polyglotCtx.eval(language, text);
+ } catch (org.graalvm.polyglot.PolyglotException e) {
+ String missingObject = extractMissingObjectName(e.getMessage());
+ if (missingObject != null) {
+ if (!Objects.equals(previousMissingObject, missingObject)) {
+ previousMissingObject = missingObject;
+ missingObjectAttempts = 1; // Reset the attempt count for a new missing object
+ this.migrateBeneratorContext2GraalVM(context, language, missingObject);
+ returnValue = evalScript(context, text, language);
+ } else if (missingObjectAttempts < MAX_ATTEMPTS) {
+ missingObjectAttempts++; // Increment attempt count for the same missing object
+ this.migrateBeneratorContext2GraalVM(context, language, missingObject);
+ returnValue = evalScript(context, text, language);
+ } else {
+ throw new ScriptException("This object couldn't be found: " + missingObject, null);
}
- return returnValue;
+ } else {
+ throw new ScriptException(e.getMessage(), null);
+ }
+ }
+ return returnValue;
+ }
+ private String extractMissingObjectName(String errorMessage) {
+ if (errorMessage.contains("ReferenceError: ")) {
+ return errorMessage.replace("ReferenceError: ", "").replace(" is not defined", "");
+ } else if (errorMessage.contains("NameError: ")) {
+ return errorMessage.replace("NameError: name '", "").replace("' is not defined", "");
}
+ return null;
+ }
- public PolyglotContext() {
- this.polyglotCtx = org.graalvm.polyglot.Context
- .newBuilder("js", "python")
- .allowAllAccess(true).build();
+ private int missingObjectAttempts = 0;
+
+ public PolyglotContext() {
+
+ if (isGraalVM()) {
+ this.polyglotCtx = org.graalvm.polyglot.Context
+ .newBuilder("js", "python")
+ .allowIO(true)
+ .option("python.ForceImportSite", "true")
+ .allowAllAccess(true).build();
+ } else {
+ this.polyglotCtx = org.graalvm.polyglot.Context
+ .newBuilder("js")
+ .allowIO(true)
+ .allowAllAccess(true).build();
}
+ }
+
+ private boolean isGraalVM() {
+ String javaVmName = System.getProperty("org.graalvm.home");
+ return javaVmName != null;
+ }
}