From 36a30547485d02db25563112a21590595b2ad141 Mon Sep 17 00:00:00 2001 From: Ashley Caselli Date: Wed, 29 May 2024 11:58:12 +0200 Subject: [PATCH] feat(py-target): add support for Python-based custom targets --- .../topbraid/shacl/targets/CustomTargets.java | 112 ++++++++++-------- .../topbraid/shacl/util/SHACLPreferences.java | 3 +- .../shacl/validation/py/PyTarget.java | 79 ++++++++++++ .../shacl/validation/py/PyTargetLanguage.java | 40 +++++++ 4 files changed, 183 insertions(+), 51 deletions(-) create mode 100644 src/main/java/org/topbraid/shacl/validation/py/PyTarget.java create mode 100644 src/main/java/org/topbraid/shacl/validation/py/PyTargetLanguage.java diff --git a/src/main/java/org/topbraid/shacl/targets/CustomTargets.java b/src/main/java/org/topbraid/shacl/targets/CustomTargets.java index 985f866e..969afd50 100644 --- a/src/main/java/org/topbraid/shacl/targets/CustomTargets.java +++ b/src/main/java/org/topbraid/shacl/targets/CustomTargets.java @@ -16,64 +16,78 @@ */ package org.topbraid.shacl.targets; -import java.util.LinkedList; -import java.util.List; - import org.apache.jena.rdf.model.Resource; import org.topbraid.shacl.validation.js.JSTargetLanguage; +import org.topbraid.shacl.validation.py.PyTargetLanguage; import org.topbraid.shacl.validation.sparql.SPARQLTargetLanguage; +import java.util.LinkedList; +import java.util.List; + /** * A singleton managing the available custom target plugins. * The only currently supported custom target is for SPARQL. - * + * * @author Holger Knublauch */ public class CustomTargets { - private static CustomTargets singleton = new CustomTargets(); - - public static CustomTargets get() { - return singleton; - } - - - private final List languages = new LinkedList<>(); - - CustomTargets() { - init(); - } - - - public void addLanguage(CustomTargetLanguage plugin) { - languages.add(plugin); - } - - - public CustomTargetLanguage getLanguageForTarget(Resource executable) { - for(CustomTargetLanguage language : languages) { - if(language.canHandle(executable)) { - return language; - } - } - return null; - } - - - private void init() { - addLanguage(new SPARQLTargetLanguage()); - addLanguage(new JSTargetLanguage()); - } - - - public void setJSPreferred(boolean value) { - languages.clear(); - if(value) { - addLanguage(new JSTargetLanguage()); - addLanguage(new SPARQLTargetLanguage()); - } - else { - init(); - } - } + private static CustomTargets singleton = new CustomTargets(); + + public static CustomTargets get() { + return singleton; + } + + + private final List languages = new LinkedList<>(); + + CustomTargets() { + init(); + } + + + public void addLanguage(CustomTargetLanguage plugin) { + languages.add(plugin); + } + + + public CustomTargetLanguage getLanguageForTarget(Resource executable) { + for (CustomTargetLanguage language : languages) { + if (language.canHandle(executable)) { + return language; + } + } + return null; + } + + + private void init() { + addLanguage(new SPARQLTargetLanguage()); + addLanguage(new JSTargetLanguage()); + addLanguage(new PyTargetLanguage()); + } + + + public void setJSPreferred(boolean value) { + languages.clear(); + if (value) { + addLanguage(new JSTargetLanguage()); + addLanguage(new SPARQLTargetLanguage()); + addLanguage(new PyTargetLanguage()); + } else { + init(); + } + } + + public void setPyPreferred(boolean value) { + languages.clear(); + if (value) { + addLanguage(new PyTargetLanguage()); + addLanguage(new JSTargetLanguage()); + addLanguage(new SPARQLTargetLanguage()); + } else { + init(); + } + } + } diff --git a/src/main/java/org/topbraid/shacl/util/SHACLPreferences.java b/src/main/java/org/topbraid/shacl/util/SHACLPreferences.java index ce179c26..5fb59103 100644 --- a/src/main/java/org/topbraid/shacl/util/SHACLPreferences.java +++ b/src/main/java/org/topbraid/shacl/util/SHACLPreferences.java @@ -54,8 +54,7 @@ public static void setPyPreferred(boolean value) { pyPreferred = value; ConstraintExecutors.get().setPyPreferred(value); SHACLFunctionDriver.setPyPreferred(value); - //CustomTargets.get().setPyPreferred(value); - // TODO custom targets Python executor + CustomTargets.get().setPyPreferred(value); } diff --git a/src/main/java/org/topbraid/shacl/validation/py/PyTarget.java b/src/main/java/org/topbraid/shacl/validation/py/PyTarget.java new file mode 100644 index 00000000..9e9015bf --- /dev/null +++ b/src/main/java/org/topbraid/shacl/validation/py/PyTarget.java @@ -0,0 +1,79 @@ +package org.topbraid.shacl.validation.py; + +import org.apache.jena.graph.Node; +import org.apache.jena.query.Dataset; +import org.apache.jena.query.QuerySolutionMap; +import org.apache.jena.rdf.model.Model; +import org.apache.jena.rdf.model.RDFNode; +import org.apache.jena.rdf.model.Resource; +import org.topbraid.jenax.util.ExceptionUtil; +import org.topbraid.shacl.js.SHACLScriptEngineManager; +import org.topbraid.shacl.js.ScriptEngine; +import org.topbraid.shacl.js.ScriptEngineUtil; +import org.topbraid.shacl.model.SHParameterizableTarget; +import org.topbraid.shacl.model.SHPyExecutable; +import org.topbraid.shacl.py.PyGraph; +import org.topbraid.shacl.py.model.PyFactory; +import org.topbraid.shacl.targets.Target; +import org.topbraid.shacl.vocabulary.SH; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +/** + * @author Ashley Caselli + */ +public class PyTarget implements Target { + + private final SHPyExecutable as; + + private SHParameterizableTarget parameterizableTarget; + + public PyTarget(Resource executable, SHParameterizableTarget parameterizableTarget) { + if (parameterizableTarget != null) { + as = parameterizableTarget.getParameterizable().as(SHPyExecutable.class); + } else { + as = executable.as(SHPyExecutable.class); + } + } + + @Override + public void addTargetNodes(Dataset dataset, Collection results) { + boolean nested = SHACLScriptEngineManager.begin(); + ScriptEngine engine = SHACLScriptEngineManager.getCurrentPyEngine(); + + Model model = dataset.getDefaultModel(); + PyGraph dataPyGraph = new PyGraph(model.getGraph(), engine); + try { + engine.executeLibraries(as); + engine.put(SH.Py_DATA_VAR, dataPyGraph); + + QuerySolutionMap bindings = new QuerySolutionMap(); + if (parameterizableTarget != null) { + parameterizableTarget.addBindings(bindings); + } + + Object result = engine.invokeFunction(as.getFunctionName(), bindings); + if (ScriptEngineUtil.isArray(result)) { + for (Object obj : ScriptEngineUtil.asArray(result)) { + Node node = PyFactory.getNode(obj); + results.add(model.asRDFNode(node)); + } + } + } catch (Exception ex) { + ExceptionUtil.throwUnchecked(ex); + } finally { + dataPyGraph.close(); + SHACLScriptEngineManager.end(nested); + } + } + + @Override + public boolean contains(Dataset dataset, RDFNode node) { + Set set = new HashSet<>(); + addTargetNodes(dataset, set); + return set.contains(node); + } + +} diff --git a/src/main/java/org/topbraid/shacl/validation/py/PyTargetLanguage.java b/src/main/java/org/topbraid/shacl/validation/py/PyTargetLanguage.java new file mode 100644 index 00000000..d05ee3d9 --- /dev/null +++ b/src/main/java/org/topbraid/shacl/validation/py/PyTargetLanguage.java @@ -0,0 +1,40 @@ +/* + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * See the NOTICE file distributed with this work for additional + * information regarding copyright ownership. + */ +package org.topbraid.shacl.validation.py; + +import org.apache.jena.rdf.model.Resource; +import org.topbraid.shacl.model.SHParameterizableTarget; +import org.topbraid.shacl.targets.CustomTargetLanguage; +import org.topbraid.shacl.targets.Target; +import org.topbraid.shacl.vocabulary.SH; + +/** + * @author Ashley Caselli + */ +public class PyTargetLanguage implements CustomTargetLanguage { + + @Override + public boolean canHandle(Resource target) { + return target.hasProperty(SH.pyFunctionName); + } + + @Override + public Target createTarget(Resource executable, SHParameterizableTarget parameterizableTarget) { + return new PyTarget(executable, parameterizableTarget); + } + +} \ No newline at end of file