Skip to content

Commit

Permalink
feat(py-target): add support for Python-based custom targets
Browse files Browse the repository at this point in the history
  • Loading branch information
ashleycaselli committed May 29, 2024
1 parent b65cf78 commit 36a3054
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 51 deletions.
112 changes: 63 additions & 49 deletions src/main/java/org/topbraid/shacl/targets/CustomTargets.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<CustomTargetLanguage> 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<CustomTargetLanguage> 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();
}
}

}
3 changes: 1 addition & 2 deletions src/main/java/org/topbraid/shacl/util/SHACLPreferences.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}


Expand Down
79 changes: 79 additions & 0 deletions src/main/java/org/topbraid/shacl/validation/py/PyTarget.java
Original file line number Diff line number Diff line change
@@ -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<RDFNode> 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<RDFNode> set = new HashSet<>();
addTargetNodes(dataset, set);
return set.contains(node);
}

}
Original file line number Diff line number Diff line change
@@ -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);
}

}

0 comments on commit 36a3054

Please sign in to comment.