
org.topbraid.shacl.util.SHACLUtil Maven / Gradle / Ivy
The newest version!
/*
* 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.util;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.apache.jena.graph.Graph;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.Triple;
import org.apache.jena.graph.compose.MultiUnion;
import org.apache.jena.query.Dataset;
import org.apache.jena.query.Query;
import org.apache.jena.query.QueryExecution;
import org.apache.jena.query.QuerySolutionMap;
import org.apache.jena.query.ResultSet;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.ModelFactory;
import org.apache.jena.rdf.model.Property;
import org.apache.jena.rdf.model.RDFNode;
import org.apache.jena.rdf.model.Resource;
import org.apache.jena.rdf.model.Statement;
import org.apache.jena.rdf.model.StmtIterator;
import org.apache.jena.util.iterator.ExtendedIterator;
import org.apache.jena.vocabulary.OWL;
import org.apache.jena.vocabulary.RDF;
import org.apache.jena.vocabulary.RDFS;
import org.topbraid.jenax.util.ARQFactory;
import org.topbraid.jenax.util.JenaDatatypes;
import org.topbraid.jenax.util.JenaNodeUtil;
import org.topbraid.jenax.util.JenaUtil;
import org.topbraid.shacl.model.SHConstraintComponent;
import org.topbraid.shacl.model.SHFactory;
import org.topbraid.shacl.model.SHNodeShape;
import org.topbraid.shacl.model.SHParameter;
import org.topbraid.shacl.model.SHParameterizableTarget;
import org.topbraid.shacl.model.SHPropertyShape;
import org.topbraid.shacl.model.SHResult;
import org.topbraid.shacl.optimize.OntologyOptimizations;
import org.topbraid.shacl.optimize.OptimizedMultiUnion;
import org.topbraid.shacl.targets.CustomTargetLanguage;
import org.topbraid.shacl.targets.CustomTargets;
import org.topbraid.shacl.vocabulary.DASH;
import org.topbraid.shacl.vocabulary.SH;
/**
* Various SHACL-related utility methods that didn't fit elsewhere.
*
* @author Holger Knublauch
*/
public class SHACLUtil {
public final static Resource[] RESULT_TYPES = {
DASH.FailureResult,
DASH.SuccessResult,
SH.ValidationResult
};
public final static String SHAPES_FILE_PART = ".shapes.";
public static final String URN_X_SHACL = "urn:x-shacl:";
private static final Set SPARQL_PROPERTIES = new HashSet();
static {
SPARQL_PROPERTIES.add(SH.ask);
SPARQL_PROPERTIES.add(SH.construct);
SPARQL_PROPERTIES.add(SH.select);
SPARQL_PROPERTIES.add(SH.update);
}
private static Query propertyLabelQuery = ARQFactory.get().createQuery(
"PREFIX rdfs: <" + RDFS.getURI() + ">\n" +
"PREFIX sh: <" + SH.NS + ">\n" +
"SELECT ?label\n" +
"WHERE {\n" +
" ?arg2 a ?type .\n" +
" ?type rdfs:subClassOf* ?class .\n" +
" ?shape <" + SH.targetClass + ">* ?class .\n" +
" ?shape <" + SH.property + ">|<" + SH.parameter + "> ?p .\n" +
" ?p <" + SH.path + "> ?arg1 .\n" +
" ?p rdfs:label ?label .\n" +
"}");
public static void addDirectPropertiesOfClass(Resource cls, Collection results) {
for(Resource argument : JenaUtil.getResourceProperties(cls, SH.parameter)) {
Resource predicate = argument.getPropertyResourceValue(SH.path);
if(predicate != null && predicate.isURIResource() && !results.contains(predicate)) {
results.add(JenaUtil.asProperty(predicate));
}
}
for(Resource property : JenaUtil.getResourceProperties(cls, SH.property)) {
Resource predicate = property.getPropertyResourceValue(SH.path);
if(predicate != null && predicate.isURIResource() && !results.contains(predicate)) {
results.add(JenaUtil.asProperty(predicate));
}
}
}
private static void addIncludes(Graph model, String uri, Set graphs, Set reachedURIs) {
graphs.add(model);
reachedURIs.add(uri);
for(Triple t : model.find(null, OWL.imports.asNode(), null).toList()) {
if(t.getObject().isURI()) {
String includeURI = t.getObject().getURI();
if(!reachedURIs.contains(includeURI)) {
Model includeModel = ARQFactory.getNamedModel(includeURI);
if(includeModel != null) {
Graph includeGraph = includeModel.getGraph();
addIncludes(includeGraph, includeURI, graphs, reachedURIs);
}
}
}
}
}
/**
* Adds all resources from a given sh:target to a given results Set of Nodes.
* @param target the value of sh:target (parameterized or SPARQL target)
* @param dataset the dataset to operate on
* @param results the Set to add the resulting Nodes to
*/
public static void addNodesInTarget(Resource target, Dataset dataset, Set results) {
for(RDFNode focusNode : getResourcesInTarget(target, dataset)) {
results.add(focusNode.asNode());
}
}
/**
* Creates an includes Model for a given input Model.
* The includes Model is the union of the input Model will all graphs linked via
* sh:include (or owl:imports), transitively.
* @param model the Model to create the includes Model for
* @param graphURI the URI of the named graph represented by Model
* @return a Model including the semantics
*/
public static Model createIncludesModel(Model model, String graphURI) {
Set graphs = new HashSet();
Graph baseGraph = model.getGraph();
addIncludes(baseGraph, graphURI, graphs, new HashSet());
if(graphs.size() == 1) {
return model;
}
else {
MultiUnion union = new MultiUnion(graphs.iterator());
union.setBaseGraph(baseGraph);
return ModelFactory.createModelForGraph(union);
}
}
public static URI createRandomShapesGraphURI() {
return URI.create(URN_X_SHACL + UUID.randomUUID());
}
/**
* Gets all focus nodes from the default Model of a given dataset.
* This includes all targets of all defined targets as well as all instances of classes that
* are also shapes.
* @param dataset the Dataset
* @param validateShapes true to include the validation of constraint components
* @return a Set of focus Nodes
*/
public static Set getAllFocusNodes(Dataset dataset, boolean validateShapes) {
Set results = new HashSet();
// Add all instances of classes that are also shapes
Model model = dataset.getDefaultModel();
for(Resource shape : JenaUtil.getAllInstances(SH.Shape.inModel(model))) {
if(JenaUtil.hasIndirectType(shape, RDFS.Class)) {
for(Resource instance : JenaUtil.getAllInstances(shape)) {
results.add(instance.asNode());
}
}
}
// Add all instances of classes mentioned in sh:targetClass triples
for(Statement s : model.listStatements(null, SH.targetClass, (RDFNode)null).toList()) {
if(s.getObject().isResource()) {
if(validateShapes || (!JenaUtil.hasIndirectType(s.getSubject(), SH.ConstraintComponent) &&
!SH.PropertyShape.equals(s.getObject())) &&
!SH.Constraint.equals(s.getObject())) {
for(Resource instance : JenaUtil.getAllInstances(s.getResource())) {
results.add(instance.asNode());
}
}
}
}
// Add all objects of sh:targetNode triples
for(Statement s : model.listStatements(null, SH.targetNode, (RDFNode)null).toList()) {
results.add(s.getObject().asNode());
}
// Add all target nodes of sh:target triples
for(Statement s : model.listStatements(null, SH.target, (RDFNode)null).toList()) {
if(s.getObject().isResource()) {
Resource target = s.getResource();
for(RDFNode focusNode : SHACLUtil.getResourcesInTarget(target, dataset)) {
results.add(focusNode.asNode());
}
}
}
// Add all objects of the predicate used as sh:targetObjectsOf
for(RDFNode property : model.listObjectsOfProperty(SH.targetObjectsOf).toList()) {
if(property.isURIResource()) {
Property predicate = JenaUtil.asProperty((Resource)property);
for(RDFNode focusNode : model.listObjectsOfProperty(predicate).toList()) {
results.add(focusNode.asNode());
}
}
}
// Add all subjects of the predicate used as sh:targetSubjectsOf
for(RDFNode property : model.listObjectsOfProperty(SH.targetSubjectsOf).toList()) {
if(property.isURIResource()) {
Property predicate = JenaUtil.asProperty((Resource)property);
for(RDFNode focusNode : model.listSubjectsWithProperty(predicate).toList()) {
results.add(focusNode.asNode());
}
}
}
return results;
}
public static List getAllTopLevelResults(Model model) {
List results = new LinkedList();
for(Resource type : RESULT_TYPES) {
for(Resource r : model.listResourcesWithProperty(RDF.type, type).toList()) {
if(!model.contains(null, SH.detail, r)) {
results.add(r.as(SHResult.class));
}
}
}
return results;
}
/**
* Gets all (transitive) superclasses including shapes that reference a class via sh:targetClass.
* @param cls the class to start at
* @return a Set of classes and shapes
*/
public static Set getAllSuperClassesAndShapesStar(Resource cls) {
Set results = new HashSet();
getAllSuperClassesAndShapesStarHelper(cls, results);
return results;
}
private static void getAllSuperClassesAndShapesStarHelper(Resource node, Set results) {
if(!results.contains(node)) {
results.add(node);
{
StmtIterator it = node.listProperties(RDFS.subClassOf);
while(it.hasNext()) {
Statement s = it.next();
if(s.getObject().isResource()) {
getAllSuperClassesAndShapesStarHelper(s.getResource(), results);
}
}
}
{
StmtIterator it = node.getModel().listStatements(null, SH.targetClass, node);
while(it.hasNext()) {
getAllSuperClassesAndShapesStarHelper(it.next().getSubject(), results);
}
}
}
}
public static SHConstraintComponent getConstraintComponentOfValidator(Resource validator) {
for(Statement s : validator.getModel().listStatements(null, null, validator).toList()) {
if(SH.validator.equals(s.getPredicate()) || SH.nodeValidator.equals(s.getPredicate()) || SH.propertyValidator.equals(s.getPredicate())) {
return s.getSubject().as(SHConstraintComponent.class);
}
}
return null;
}
public static Resource getDefaultTypeForConstraintPredicate(Property predicate) {
if(SH.property.equals(predicate)) {
return SH.PropertyShape;
}
else if(SH.parameter.equals(predicate)) {
return SH.Parameter;
}
else {
throw new IllegalArgumentException();
}
}
public static SHParameter getParameterAtClass(Resource cls, Property predicate) {
for(Resource c : JenaUtil.getAllSuperClassesStar(cls)) {
for(Resource arg : JenaUtil.getResourceProperties(c, SH.parameter)) {
if(arg.hasProperty(SH.path, predicate)) {
return SHFactory.asParameter(arg);
}
}
}
return null;
}
public static SHParameter getParameterAtInstance(Resource instance, Property predicate) {
for(Resource type : JenaUtil.getTypes(instance)) {
SHParameter argument = getParameterAtClass(type, predicate);
if(argument != null) {
return argument;
}
}
return null;
}
// Simplified to only check for sh:property and sh:parameter (not sh:node etc)
public static Resource getResourceDefaultType(Resource resource) {
if(resource.getModel().contains(null, SH.property, resource)) {
return SH.PropertyShape.inModel(resource.getModel());
}
else if(resource.getModel().contains(null, SH.parameter, resource)) {
return SH.Parameter.inModel(resource.getModel());
}
/*
StmtIterator it = resource.getModel().listStatements(null, null, resource);
try {
while(it.hasNext()) {
Statement s = it.next();
Resource defaultValueType = JenaUtil.getResourceProperty(s.getPredicate(), DASH.defaultValueType);
if(defaultValueType != null) {
return defaultValueType;
}
}
}
finally {
it.close();
}*/
return null;
}
/**
* Gets any locally-defined label for a given property.
* The labels are expected to be attached to shapes associated with a given
* context resource (instance).
* That context resource may for example be the subject of the current UI form.
* @param property the property to get the label of
* @param context the context instance
* @return the local label or null if it should fall back to a global label
*/
public static String getLocalPropertyLabel(Resource property, Resource context) {
QuerySolutionMap binding = new QuerySolutionMap();
binding.add("arg1", property);
binding.add("arg2", context);
try(QueryExecution qexec = ARQFactory.get().createQueryExecution(propertyLabelQuery, property.getModel(), binding)) {
ResultSet rs = qexec.execSelect();
if(rs.hasNext()) {
return rs.next().get("label").asLiteral().getLexicalForm();
}
}
return null;
}
public static SHPropertyShape getPropertyConstraintAtClass(Resource cls, Property predicate) {
for(Resource c : JenaUtil.getAllSuperClassesStar(cls)) {
for(Resource arg : JenaUtil.getResourceProperties(c, SH.property)) {
if(arg.hasProperty(SH.path, predicate)) {
return SHFactory.asPropertyShape(arg);
}
}
}
return null;
}
public static SHPropertyShape getPropertyConstraintAtInstance(Resource instance, Property predicate) {
for(Resource type : JenaUtil.getTypes(instance)) {
SHPropertyShape property = getPropertyConstraintAtClass(type, predicate);
if(property != null) {
return property;
}
}
return null;
}
/**
* Gets all the predicates of all declared sh:properties and sh:parameters
* of a given class, including inherited ones.
* @param cls the class to get the predicates of
* @return the declared predicates
*/
public static List getAllPropertiesOfClass(Resource cls) {
List results = new LinkedList();
for(Resource c : getAllSuperClassesAndShapesStar(cls)) {
addDirectPropertiesOfClass(c, results);
}
return results;
}
/**
* Gets all nodes from a given sh:target.
* @param target the value of sh:target (parameterizable or SPARQL target)
* @param dataset the dataset to operate on
* @return an Iterable over the resources
*/
public static Iterable getResourcesInTarget(Resource target, Dataset dataset) {
Resource type = JenaUtil.getType(target);
Resource executable;
SHParameterizableTarget parameterizableTarget = null;
if(SHFactory.isParameterizableInstance(target)) {
executable = type;
parameterizableTarget = SHFactory.asParameterizableTarget(target);
}
else {
executable = target;
}
CustomTargetLanguage plugin = CustomTargets.get().getLanguageForTarget(executable);
if(plugin != null) {
Set results = new HashSet<>();
plugin.createTarget(executable, parameterizableTarget).addTargetNodes(dataset, results);
return results;
}
else {
return new ArrayList<>();
}
}
/**
* Gets all shapes associated with a given focus node.
* This looks for all shapes based on class-based targets.
* Future versions will also look for property-based targets.
* @param node the (focus) node
* @return a List of shapes
*/
public static List getAllShapesAtNode(RDFNode node) {
return getAllShapesAtNode(node, node instanceof Resource ? JenaUtil.getTypes((Resource)node) : null);
}
public static List getAllShapesAtNode(RDFNode node, Iterable types) {
List results = new LinkedList<>();
if(node instanceof Resource) {
Set reached = new HashSet<>();
for(Resource type : types) {
addAllShapesAtClassOrShape(type, results, reached);
}
}
// TODO: support sh:targetObjectsOf and sh:targetSubjectsOf
return results;
}
/**
* Gets all sh:Shapes that have a given class in their target, including ConstraintComponents
* and the class or shape itself if it is marked as sh:Shape.
* Also walks up the class hierarchy.
* @param clsOrShape the class or Shape to get the shapes of
* @return the shapes, ordered by the most specialized (subclass) first
*/
@SuppressWarnings("unchecked")
public static List getAllShapesAtClassOrShape(Resource clsOrShape) {
String key = OntologyOptimizations.get().getKeyIfEnabledFor(clsOrShape.getModel().getGraph());
if(key != null) {
key += ".getAllShapesAtClassOrShape(" + clsOrShape + ")";
return (List) OntologyOptimizations.get().getOrComputeObject(key, (cacheKey) -> {
List results = new LinkedList();
addAllShapesAtClassOrShape(clsOrShape, results, new HashSet());
return results;
});
}
else {
List results = new LinkedList();
addAllShapesAtClassOrShape(clsOrShape, results, new HashSet());
return results;
}
}
private static void addAllShapesAtClassOrShape(Resource clsOrShape, List results, Set reached) {
addDirectShapesAtClassOrShape(clsOrShape, results);
reached.add(clsOrShape);
for(Resource superClass : JenaUtil.getSuperClasses(clsOrShape)) {
if(!reached.contains(superClass)) {
addAllShapesAtClassOrShape(superClass, results, reached);
}
}
}
/**
* Gets the directly associated sh:Shapes that have a given class in their target,
* including ConstraintComponents and the class or shape itself if it is marked as sh:Shape.
* Does not walk up the class hierarchy.
* @param clsOrShape the class or Shape to get the shapes of
* @return the shapes
*/
public static Collection getDirectShapesAtClassOrShape(Resource clsOrShape) {
List results = new LinkedList();
addDirectShapesAtClassOrShape(clsOrShape, results);
return results;
}
private static void addDirectShapesAtClassOrShape(Resource clsOrShape, List results) {
if(JenaUtil.hasIndirectType(clsOrShape, SH.Shape) && !results.contains(clsOrShape)) {
SHNodeShape shape = SHFactory.asNodeShape(clsOrShape);
if(!shape.isDeactivated()) {
results.add(shape);
}
}
// More correct would be: if(JenaUtil.hasIndirectType(clsOrShape, RDFS.Class)) {
{
StmtIterator it = clsOrShape.getModel().listStatements(null, SH.targetClass, clsOrShape);
while(it.hasNext()) {
Resource subject = it.next().getSubject();
if(!results.contains(subject)) {
SHNodeShape shape = SHFactory.asNodeShape(subject);
if(!shape.isDeactivated()) {
results.add(shape);
}
}
}
}
}
public static Set getDirectShapesAtResource(Resource resource) {
Set shapes = new HashSet<>();
for(Resource type : JenaUtil.getResourceProperties(resource, RDF.type)) {
if(JenaUtil.hasIndirectType(type, SH.NodeShape)) {
shapes.add(type);
}
Set ts = JenaUtil.getAllSuperClassesStar(type);
for(Resource s : ts) {
{
StmtIterator it = type.getModel().listStatements(null, DASH.applicableToClass, s);
while(it.hasNext()) {
Resource shape = it.next().getSubject();
shapes.add(shape);
}
}
{
StmtIterator it = type.getModel().listStatements(null, SH.targetClass, s);
while(it.hasNext()) {
Resource shape = it.next().getSubject();
shapes.add(shape);
}
}
}
}
return shapes;
}
public static List getTargetNodes(Resource shape, Dataset dataset) {
return getTargetNodes(shape, dataset, false);
}
public static List getTargetNodes(Resource shape, Dataset dataset, boolean includeApplicableToClass) {
Model dataModel = dataset.getDefaultModel();
Set results = new HashSet();
if(JenaUtil.hasIndirectType(shape, RDFS.Class)) {
results.addAll(JenaUtil.getAllInstances(shape.inModel(dataModel)));
}
for(Resource targetClass : JenaUtil.getResourceProperties(shape, SH.targetClass)) {
results.addAll(JenaUtil.getAllInstances(targetClass.inModel(dataModel)));
}
for(RDFNode targetNode : shape.getModel().listObjectsOfProperty(shape, SH.targetNode).toList()) {
results.add(targetNode.inModel(dataModel));
}
for(Resource sof : JenaUtil.getResourceProperties(shape, SH.targetSubjectsOf)) {
for(Statement s : dataModel.listStatements(null, JenaUtil.asProperty(sof), (RDFNode)null).toList()) {
results.add(s.getSubject());
}
}
for(Resource sof : JenaUtil.getResourceProperties(shape, SH.targetObjectsOf)) {
for(Statement s : dataModel.listStatements(null, JenaUtil.asProperty(sof), (RDFNode)null).toList()) {
results.add(s.getObject());
}
}
for(Resource target : JenaUtil.getResourceProperties(shape, SH.target)) {
for(RDFNode targetNode : SHACLUtil.getResourcesInTarget(target, dataset)) {
results.add(targetNode);
}
}
if(includeApplicableToClass) {
for(Resource targetClass : JenaUtil.getResourceProperties(shape, DASH.applicableToClass)) {
results.addAll(JenaUtil.getAllInstances(targetClass.inModel(dataModel)));
}
}
return new ArrayList(results);
}
public static List getTypes(Resource subject) {
List types = JenaUtil.getTypes(subject);
if(types.isEmpty()) {
Resource defaultType = getResourceDefaultType(subject);
if(defaultType != null) {
return Collections.singletonList(defaultType);
}
}
return types;
}
public static boolean hasMinSeverity(Resource severity, Resource minSeverity) {
if(minSeverity == null || SH.Info.equals(minSeverity)) {
return true;
}
if(SH.Warning.equals(minSeverity)) {
return !SH.Info.equals(severity);
}
else { // SH.Error
return SH.Violation.equals(severity);
}
}
public static boolean isDeactivated(Resource resource) {
return resource.hasProperty(SH.deactivated, JenaDatatypes.TRUE);
}
public static boolean isParameterAtInstance(Resource subject, Property predicate) {
for(Resource type : getTypes(subject)) {
Resource arg = getParameterAtClass(type, predicate);
if(arg != null) {
return true;
}
}
return false;
}
public static boolean isSPARQLProperty(Property property) {
return SPARQL_PROPERTIES.contains(property);
}
/**
* Checks whether the SHACL vocabulary is present in a given Model.
* The condition is that the SHACL namespace must be declared and
* sh:Constraint must have an rdf:type.
* @param model the Model to check
* @return true if SHACL is present
*/
public static boolean exists(Model model) {
return model != null && exists(model.getGraph());
}
public static boolean exists(Graph graph) {
if(graph instanceof OptimizedMultiUnion) {
return ((OptimizedMultiUnion)graph).getIncludesSHACL();
}
else {
return graph != null &&
SH.NS.equals(graph.getPrefixMapping().getNsPrefixURI(SH.PREFIX)) &&
graph.contains(SH.Shape.asNode(), RDF.type.asNode(), Node.ANY);
}
}
public static URI withShapesGraph(Dataset dataset) {
URI shapesGraphURI = createRandomShapesGraphURI();
Model shapesModel = createShapesModel(dataset);
dataset.addNamedModel(shapesGraphURI.toString(), shapesModel);
return shapesGraphURI;
}
/**
* Creates a shapes Model for a given input Model.
* The shapes Model is the union of the input Model with all graphs referenced via
* the sh:shapesGraph property (and transitive includes or shapesGraphs of those).
* @param model the Model to create the shapes Model for
* @return a shapes graph Model
*/
private static Model createShapesModel(Dataset dataset) {
Model model = dataset.getDefaultModel();
Set graphs = new HashSet();
Graph baseGraph = model.getGraph();
graphs.add(baseGraph);
for(Statement s : model.listStatements(null, SH.shapesGraph, (RDFNode)null).toList()) {
if(s.getObject().isURIResource()) {
String graphURI = s.getResource().getURI();
Model sm = dataset.getNamedModel(graphURI);
graphs.add(sm.getGraph());
// TODO: Include includes of sm
}
}
if(graphs.size() > 1) {
MultiUnion union = new MultiUnion(graphs.iterator());
union.setBaseGraph(baseGraph);
return ModelFactory.createModelForGraph(union);
}
else {
return model;
}
}
public static boolean isInTarget(RDFNode focusNode, Dataset dataset, Resource target) {
SHParameterizableTarget parameterizableTarget = null;
Resource executable = target;
if(SHFactory.isParameterizableInstance(target)) {
parameterizableTarget = SHFactory.asParameterizableTarget(target);
executable = parameterizableTarget.getParameterizable();
}
CustomTargetLanguage plugin = CustomTargets.get().getLanguageForTarget(executable);
if(plugin != null) {
return plugin.createTarget(executable, parameterizableTarget).contains(dataset, focusNode);
}
else {
return false;
}
}
public static Node walkPropertyShapesHelper(Node propertyShape, Graph graph) {
Node valueType = JenaNodeUtil.getObject(propertyShape, SH.class_.asNode(), graph);
if(valueType != null) {
return valueType;
}
Node datatype = JenaNodeUtil.getObject(propertyShape, SH.datatype.asNode(), graph);
if(datatype != null) {
return datatype;
}
ExtendedIterator ors = graph.find(propertyShape, SH.or.asNode(), Node.ANY);
while(ors.hasNext()) {
Node or = ors.next().getObject();
Node first = JenaNodeUtil.getObject(or, RDF.first.asNode(), graph);
if(!first.isLiteral()) {
Node cls = JenaNodeUtil.getObject(first, SH.class_.asNode(), graph);
if(cls != null) {
ors.close();
return cls;
}
datatype = JenaNodeUtil.getObject(first, SH.datatype.asNode(), graph);
if(datatype != null) {
ors.close();
return datatype;
}
}
}
if(graph.contains(propertyShape, SH.node.asNode(), DASH.ListShape.asNode())) {
return RDF.List.asNode();
}
return null;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy