All Downloads are FREE. Search and download functionalities are using the official Maven repository.

js.dash.js Maven / Gradle / Ivy

There is a newer version: 1.4.3
Show newest version
// Functions implementing the validators of SHACL-JS
// Also include validators for the constraint components of the DASH namespace

// Also included: implementations of the standard DASH functions

// There is no validator for sh:property as this is expected to be
// natively implemented by the surrounding engine.

var XSDIntegerTypes = new NodeSet();
XSDIntegerTypes.add(T("xsd:integer"));

var XSDDecimalTypes = new NodeSet();
XSDDecimalTypes.addAll(XSDIntegerTypes.toArray());
XSDDecimalTypes.add(T("xsd:decimal"));
XSDDecimalTypes.add(T("xsd:float"));

function validateAnd($value, $and) {
	var shapes = new RDFQueryUtil($shapes).rdfListToArray($and);
	for(var i = 0; i < shapes.length; i++) {
		if(!SHACL.nodeConformsToShape($value, shapes[i])) {
			return false;
		}
	}
	return true;
}

function validateClass($value, $class) {
	return new RDFQueryUtil($data).isInstanceOf($value, $class);
}

function validateClosed($value, $closed, $ignoredProperties, $currentShape) {
	if(!T("true").equals($closed)) {
		return;
	}
	var allowed = $shapes.query().
		match($currentShape, "sh:property", "?propertyShape").
		match("?propertyShape", "sh:path", "?path").
		filter(function(solution) { return solution.path.isURI() } ).
		getNodeSet("?path");
	if($ignoredProperties) {
		allowed.addAll(new RDFQueryUtil($shapes).rdfListToArray($ignoredProperties));
	}
	var results = [];
	$data.query().
		match($value, "?predicate", "?object").
		filter(function(sol) { return !allowed.contains(sol.predicate)}).
		forEach(function(sol) { 
			results.push({ 
				path : sol.predicate,
				value : sol.object
			});
		});
	return results;
}

function validateClosedByTypesNode($this, $closedByTypes) {
	if(!T("true").equals($closedByTypes)) {
		return;
	}
	var results = [];
	var allowedProperties = new NodeSet();
	$data.query().
		match($this, "rdf:type", "?directType").
		path("?directType", { zeroOrMore : T("rdfs:subClassOf") }, "?type").
		forEachNode("?type", function(type) {
			$shapes.query().
				match(type, "sh:property", "?pshape").
				match("?pshape", "sh:path", "?path").
				filter(function(sol) { return sol.path.isURI() }).
				addAllNodes("?path", allowedProperties);
		});
	$data.query().
		match($this, "?predicate", "?object").
		filter(function(sol) { return !T("rdf:type").equals(sol.predicate) }).
		filter(function(sol) { return !allowedProperties.contains(sol.predicate) }).
		forEach(function(sol) {
			results.push({
				path: sol.predicate,
				value: sol.object
			});
		})
	return results;
}

function validateCoExistsWith($this, $path, $coExistsWith) {
	var path = toRDFQueryPath($path);
	var has1 = $data.query().path($this, path, null).getCount() > 0;
	var has2 = $data.query().match($this, $coExistsWith, null).getCount() > 0;
	return has1 == has2;
}

function validateDatatype($value, $datatype) {
	if($value.isLiteral()) {
		return $datatype.equals($value.datatype) && isValidForDatatype($value.lex, $datatype);
	}
	else {
		return false;
	}
}

function validateDisjoint($this, $value, $disjoint) {
	return !$data.query().match($this, $disjoint, $value).hasSolution();
}

function validateEqualsProperty($this, $path, $equals) {
	var results = [];
	var path = toRDFQueryPath($path);
	$data.query().path($this, path, "?value").forEach(
		function(solution) {
			if(!$data.query().match($this, $equals, solution.value).hasSolution()) {
				results.push({
					value: solution.value
				});
			}
		});
	$data.query().match($this, $equals, "?value").forEach(
		function(solution) {
			if(!$data.query().path($this, path, solution.value).hasSolution()) {
				results.push({
					value: solution.value
				});
			}
		});
	return results;
}

var validateEqualsNode = function ($this, $equals) {
    var results = [];
    var solutions = 0;
    $data.query().path($this, $equals, "?value").forEach(
        function (solution) {
            solutions++;
            if (SHACL.compareNodes($this, solution['value']) !== 0) {
                results.push({
                    value: solution.value
                });
            }
        });
    if (results.length === 0 && solutions === 0) {
        results.push({
            value: $this
        });
    }
    return results;
};

function validateHasValueNode($this, $hasValue) {
	return $this.equals($hasValue);
}

function validateHasValueProperty($this, $path, $hasValue) {
	var count = $data.query().path($this, toRDFQueryPath($path), $hasValue).getCount();
	return count > 0;
}

function validateHasValueWithClass($this, $path, $hasValueWithClass) {
	return $data.query().
			path($this, toRDFQueryPath($path), "?value").
			filter(function(sol) { return new RDFQueryUtil($data).isInstanceOf(sol.value, $hasValueWithClass) }).
			hasSolution();
}

function validateIn($value, $in) {
	var set = new NodeSet();
	set.addAll(new RDFQueryUtil($shapes).rdfListToArray($in));
	return set.contains($value);
}

function validateLanguageIn($value, $languageIn) {
	if(!$value.isLiteral()) {
		return false;
	}
	var lang = $value.language;
	if(!lang || lang === "") {
		return false;
	}
	var ls = new RDFQueryUtil($shapes).rdfListToArray($languageIn);
	for(var i = 0; i < ls.length; i++) {
		if(lang.startsWith(ls[i].lex)) {
			return true;
		}
	}
	return false;
}

function validateLessThanProperty($this, $path, $lessThan) {
	var results = [];
	$data.query().
		path($this, toRDFQueryPath($path), "?value").
		match($this, $lessThan, "?otherValue").
		forEach(function(sol) {
					var c = SHACL.compareNodes(sol.value, sol.otherValue);
					if(c == null || c >= 0) {
						results.push({
							value: sol.value
						});
					}
				});
	return results;
}

function validateLessThanOrEqualsProperty($this, $path, $lessThanOrEquals) {
	var results = [];
	$data.query().
		path($this, toRDFQueryPath($path), "?value").
		match($this, $lessThanOrEquals, "?otherValue").
		forEach(function(sol) {
					var c = SHACL.compareNodes(sol.value, sol.otherValue);
					if(c == null || c > 0) {
						results.push({
							value: sol.value
						});
					}
				});
	return results;
}

function validateMaxCountProperty($this, $path, $maxCount) {
	var count = $data.query().path($this, toRDFQueryPath($path), "?any").getCount();
	return count <= Number($maxCount.value);
}

function validateMaxExclusive($value, $maxExclusive) {
	return $value.isLiteral() && Number($value.lex) < Number($maxExclusive.lex);
}

function validateMaxInclusive($value, $maxInclusive) {
	return $value.isLiteral() && Number($value.lex) <= Number($maxInclusive.lex);
}

function validateMaxLength($value, $maxLength) {
	if($value.isBlankNode()) {
		return false;
	}
	return $value.value.length <= Number($maxLength.lex);
}

function validateMinCountProperty($this, $path, $minCount) {
	var count = $data.query().path($this, toRDFQueryPath($path), "?any").getCount();
	return count >= Number($minCount.value);
}

function validateMinExclusive($value, $minExclusive) {
	return $value.isLiteral() && Number($value.lex) > Number($minExclusive.lex);
}

function validateMinInclusive($value, $minInclusive) {
	return $value.isLiteral() && Number($value.lex) >= Number($minInclusive.lex);
}

function validateMinLength($value, $minLength) {
	if($value.isBlankNode()) {
		return false;
	}
	return $value.value.length >= Number($minLength.lex);
}

function validateNodeKind($value, $nodeKind) {
	if($value.isBlankNode()) {
		return T("sh:BlankNode").equals($nodeKind) || 
			T("sh:BlankNodeOrIRI").equals($nodeKind) ||
			T("sh:BlankNodeOrLiteral").equals($nodeKind);
	}
	else if($value.isURI()) {
		return T("sh:IRI").equals($nodeKind) || 
			T("sh:BlankNodeOrIRI").equals($nodeKind) ||
			T("sh:IRIOrLiteral").equals($nodeKind);
	}
	else if($value.isLiteral()) {
		return T("sh:Literal").equals($nodeKind) || 
			T("sh:BlankNodeOrLiteral").equals($nodeKind) ||
			T("sh:IRIOrLiteral").equals($nodeKind);
	}
}

function validateNode($value, $node) {
	return SHACL.nodeConformsToShape($value, $node);
}

function validateNonRecursiveProperty($this, $path, $nonRecursive) {
	if(T("true").equals($nonRecursive)) {
		if($data.query().path($this, toRDFQueryPath($path), $this).hasSolution()) {
			return {
				path: $path,
				value: $this
			}
		}
	}
}

function validateNot($value, $not) {
	return !SHACL.nodeConformsToShape($value, $not);
}

function validateOr($value, $or) {
	var shapes = new RDFQueryUtil($shapes).rdfListToArray($or);
	for(var i = 0; i < shapes.length; i++) {
		if(SHACL.nodeConformsToShape($value, shapes[i])) {
			return true;
		}
	}
	return false;
}

function validatePattern($value, $pattern, $flags) {
	if($value.isBlankNode()) {
		return false;
	}
	var re = $flags ? new RegExp($pattern.lex, $flags.lex) : new RegExp($pattern.lex);
	return re.test($value.value);
}

function validatePrimaryKeyProperty($this, $path, $uriStart) {
	if(!$this.isURI()) {
		return "Must be an IRI";
	}
	if($data.query().path($this, toRDFQueryPath($path), null).getCount() != 1) {
		return "Must have exactly one value";
	}
	var value = $data.query().path($this, toRDFQueryPath($path), "?value").getNode("?value");
	var uri = $uriStart.lex + encodeURIComponent(value.value);
	if(!$this.uri.equals(uri)) {
		return "Does not have URI " + uri;
	}
}

function validateQualifiedMaxCountProperty($this, $path, $qualifiedValueShape, $qualifiedValueShapesDisjoint, $qualifiedMaxCount, $currentShape) {
	var c = validateQualifiedHelper($this, $path, $qualifiedValueShape, $qualifiedValueShapesDisjoint, $currentShape);
	return c <= Number($qualifiedMaxCount.lex);
}

function validateQualifiedMinCountProperty($this, $path, $qualifiedValueShape, $qualifiedValueShapesDisjoint, $qualifiedMinCount, $currentShape) {
	var c = validateQualifiedHelper($this, $path, $qualifiedValueShape, $qualifiedValueShapesDisjoint, $currentShape);
	return c >= Number($qualifiedMinCount.lex);
}

function validateQualifiedHelper($this, $path, $qualifiedValueShape, $qualifiedValueShapesDisjoint, $currentShape) {
	var siblingShapes = new NodeSet();
	if(T("true").equals($qualifiedValueShapesDisjoint)) {
		$shapes.query().
			match("?parentShape", "sh:property", $currentShape).
			match("?parentShape", "sh:property", "?sibling").
			match("?sibling", "sh:qualifiedValueShape", "?siblingShape").
			filter(exprNotEquals("?siblingShape", $qualifiedValueShape)) .
			addAllNodes("?siblingShape", siblingShapes);
	}
	return $data.query().
		path($this, toRDFQueryPath($path), "?value").
		filter(function(sol) { 
			return SHACL.nodeConformsToShape(sol.value, $qualifiedValueShape) &&
				!validateQualifiedConformsToASibling(sol.value, siblingShapes.toArray()); 
		}).
		getCount();
}

function validateQualifiedConformsToASibling(value, siblingShapes) {
	for(var i = 0; i < siblingShapes.length; i++) {
		if(SHACL.nodeConformsToShape(value, siblingShapes[i])) {
			return true;
		}
	}
	return false;
}

function validateRootClass($value, $rootClass) {
	return $data.query().path($value, { zeroOrMore: T("rdfs:subClassOf") }, $rootClass).hasSolution();
}

function validateSingleLine($value, $singleLine) {
	if($singleLine.lex == 'true' && $value.isLiteral() && ($value.lex.indexOf('\n') >= 0 || $value.lex.indexOf('\r') >= 0)) {
		return false;
	}
	else {
		return true;
	}
}

function validateStem($value, $stem) {
	return $value.isURI() && $value.uri.startsWith($stem.lex);
}

function validateSubSetOf($this, $subSetOf, $value) {
	return $data.query().match($this, $subSetOf, $value).hasSolution();
}

function validateSymmetric($this, $path, $symmetric) {
	var results = [];
	if(T("true").equals($symmetric)) {
		var path = toRDFQueryPath($path);
		$data.query().
			path($this, path, "?value").
			filter(function(sol) {
				return !$data.query().path(sol.value, path, $this).hasSolution();
			}).
			forEach(function(sol) {
				results.push({
					value: sol.value
				})
			});
	}
	return results;
}

function validateUniqueLangProperty($this, $uniqueLang, $path) {
	if(!T("true").equals($uniqueLang)) {
		return;
	}
	var map = {};
	$data.query().path($this, toRDFQueryPath($path), "?value").forEach(function(sol) {
		var lang = sol.value.language;
		if(lang && lang != "") {
			var old = map[lang];
			if(!old) {
				map[lang] = 1;
			}
			else {
				map[lang] = old + 1;
			}
		}
	})
	var results = [];
	for(var lang in map) {
		if(map.hasOwnProperty(lang)) {
			var count = map[lang];
			if(count > 1) {
				results.push("Language \"" + lang + "\" has been used by " + count + " values");
			}
		}
	}
	return results;
}

function validateUniqueValueForClass($this, $uniqueValueForClass, $path) {
	var results = [];
	$data.query().
		path($this, toRDFQueryPath($path), "?value").
		path("?other", toRDFQueryPath($path), "?value").
		filter(function(sol) {
				return !$this.equals(sol.other);
			}).
		filter(function(sol) {
				return new RDFQueryUtil($data).isInstanceOf(sol.other, $uniqueValueForClass)
			}).
		forEach(function(sol) {
			results.push({
				other: sol.other,
				value: sol.value
			})
		});
	return results;
}

function validateXone($value, $xone) {
	var shapes = new RDFQueryUtil($shapes).rdfListToArray($xone);
	var count = 0;
	for(var i = 0; i < shapes.length; i++) {
		if(SHACL.nodeConformsToShape($value, shapes[i])) {
			count++;
		}
	}
	return count == 1;
}


// DASH functions -------------------------------------------------------------

// dash:toString
function dash_toString($arg) {
	if($arg.isLiteral()) {
		return NodeFactory.literal($arg.lex, T("xsd:string"));
	}
	else if($arg.isURI()) {
		return NodeFactory.literal($arg.uri, T("xsd:string"));
	}
	else {
		return null;
	}
}


// DASH targets ---------------------------------------------------------------

// dash:AllObjectsTarget
function dash_allObjects() {
	return $data.query().match(null, null, "?object").getNodeSet("?object").toArray();
}

// dash:AllSubjectsTarget
function dash_allSubjects() {
	return $data.query().match("?subject", null, null).getNodeSet("?subject").toArray();
}


// Utilities ------------------------------------------------------------------

function toRDFQueryPath(shPath) {
    if (shPath.termType === "Collection") {
        var paths = new RDFQueryUtil($shapes).rdfListToArray(shPath);
        var result = [];
        for (var i = 0; i < paths.length; i++) {
            result.push(toRDFQueryPath(paths[i]));
        }
        return result;
    }
	if(shPath.isURI()) {
		return shPath;
	}
	else if(shPath.isBlankNode()) {
		var util = new RDFQueryUtil($shapes);
		if($shapes.query().getObject(shPath, "rdf:first")) {
			var paths = util.rdfListToArray(shPath);
			var result = [];
			for(var i = 0; i < paths.length; i++) {
				result.push(toRDFQueryPath(paths[i]));
			}
			return result;
		}
		var alternativePath = $shapes.query().getObject(shPath, "sh:alternativePath");
		if(alternativePath) {
			var paths = util.rdfListToArray(alternativePath);
			var result = [];
			for(var i = 0; i < paths.length; i++) {
				result.push(toRDFQueryPath(paths[i]));
			}
			return { or : result };
		}
		var zeroOrMorePath = $shapes.query().getObject(shPath, "sh:zeroOrMorePath");
		if(zeroOrMorePath) {
			return { zeroOrMore : toRDFQueryPath(zeroOrMorePath) };
		}
		var oneOrMorePath = $shapes.query().getObject(shPath, "sh:oneOrMorePath");
		if(oneOrMorePath) {
			return { oneOrMore : toRDFQueryPath(oneOrMorePath) };
		}
		var zeroOrOnePath = $shapes.query().getObject(shPath, "sh:zeroOrOnePath");
		if(zeroOrOnePath) {
			return { zeroOrOne : toRDFQueryPath(zeroOrOnePath) };
		}
		var inversePath = $shapes.query().getObject(shPath, "sh:inversePath");
		if(inversePath) {
			return { inverse : toRDFQueryPath(inversePath) };
		}
	}
	throw "Unsupported SHACL path " + shPath;
	// TODO: implement conforming to AbstractQuery.path syntax
	return shPath;
}


// Private helper functions

//TODO: Support more datatypes
function isValidForDatatype(lex, datatype) {
	if(XSDIntegerTypes.contains(datatype)) {
		var r = parseInt(lex);
		return !isNaN(r);
	}
	else if(XSDDecimalTypes.contains(datatype)) {
		var r = parseFloat(lex);
		return !isNaN(r);
	}
	else if (datatype.value === "http://www.w3.org/2001/XMLSchema#boolean") {
        return lex === "true" || lex === "false";
    }
	else {
		return true;
	}
}

function RDFQueryUtil($source) {
	this.source = $source;
}

RDFQueryUtil.prototype.getInstancesOf = function($class) {
	var set = new NodeSet();
	var classes = this.getSubClassesOf($class);
	classes.add($class);
	var car = classes.toArray();
	for(var i = 0; i < car.length; i++) {
		set.addAll(RDFQuery(this.source).match("?instance", "rdf:type", car[i]).getNodeArray("?instance"));
	}
	return set;
}

RDFQueryUtil.prototype.getObject = function($subject, $predicate) {
	if(!$subject) {
		throw "Missing subject";
	}
	if(!$predicate) {
		throw "Missing predicate";
	}
	return RDFQuery(this.source).match($subject, $predicate, "?object").getNode("?object");
}

RDFQueryUtil.prototype.getSubClassesOf = function($class) {
	var set = new NodeSet();
	this.walkSubjects(set, $class, T("rdfs:subClassOf"));
	return set;
}

RDFQueryUtil.prototype.isInstanceOf = function($instance, $class) {
	var classes = this.getSubClassesOf($class);
	var types = $data.query().match($instance, "rdf:type", "?type");
	for(var n = types.nextSolution(); n; n = types.nextSolution()) {
		if(n.type.equals($class) || classes.contains(n.type)) {
			types.close();
			return true;
		}
	}
	return false;
}

RDFQueryUtil.prototype.rdfListToArray = function($rdfList) {
    if ($rdfList.elements != null) {
        return $rdfList.elements;
    } else {
        var array = [];
        while (!T("rdf:nil").equals($rdfList)) {
            array.push(this.getObject($rdfList, T("rdf:first")));
            $rdfList = this.getObject($rdfList, T("rdf:rest"));
        }
        return array;
    }
}

RDFQueryUtil.prototype.walkObjects = function($results, $subject, $predicate) {
	var it = this.source.find($subject, $predicate, null);
	for(var n = it.next(); n; n = it.next()) {
		if(!$results.contains(n.object)) {
			$results.add(n.object);
			this.walkObjects($results, n.object, $predicate);
		}
	}
}

RDFQueryUtil.prototype.walkSubjects = function($results, $object, $predicate) {
	var it = this.source.find(null, $predicate, $object);
	for(var n = it.next(); n; n = it.next()) {
		if(!$results.contains(n.subject)) {
			$results.add(n.subject);
			this.walkSubjects($results, n.subject, $predicate);
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy