sparnatural
Version:
Visual client-side SPARQL query builder and knowledge graph exploration tool
329 lines • 15.5 kB
JavaScript
import { DataFactory } from 'rdf-data-factory';
import { SHACLSpecificationEntry } from "./SHACLSpecificationEntry";
import { SHACLSpecificationProperty } from "./SHACLSpecificationProperty";
import { Dag } from "../../dag/Dag";
import { GEOSPARQL, Model, RDF, RDFS, SH, XSD, DatatypeRegistry } from 'rdf-shacl-commons';
const factory = new DataFactory();
export class SHACLSpecificationEntity extends SHACLSpecificationEntry {
constructor(uri, provider, n3store, lang) {
super(uri, provider, n3store, lang);
}
getLabel() {
return this.shape.getLabel(this.lang);
}
getTooltip() {
return this.shape.getTooltip(this.lang);
}
getConnectingProperties(range) {
// read all sh:property and find the ones that can target the selected class
var items = [];
let propShapes = this.getProperties();
propShapes.forEach(ps => {
let prop = new SHACLSpecificationProperty(ps, this.provider, this.store, this.lang);
let pRange = prop.getRange();
if (pRange.indexOf(range) > -1) {
items.push(ps);
}
});
// add properties available from parents - recursively
let parents = this.provider.getEntity(range).getParents();
parents.forEach(aParent => {
items = [...items, ...this.getConnectingProperties(aParent)];
});
// dedup, although probably dedup is not necessary here
var dedupItems = [...new Set(items)];
// sort dedups
var sortedDedups = SHACLSpecificationEntry.sort(dedupItems.map(s => new SHACLSpecificationProperty(s, this.provider, this.store, this.lang)));
// return dedup array of strings
return sortedDedups.map(e => e.getId());
}
/**
* Return the tree of properties connecting this entity to the specified range entity
* @param range The URI of the selected target entity
* @returns
*/
getConnectingPropertiesTree(range) {
// 1. get list of properties
let properties = this.getConnectingProperties(range).map(s => this.provider.getProperty(s));
// retrieve the potential parents
while (!properties.every(property => {
return property.getParents().every(parent => {
return (properties.find(anotherProperty => anotherProperty.getId() === parent) != undefined);
});
})) {
let parentsToAdd = [];
properties.forEach(property => {
property.getParents().forEach(parent => {
// if the parent is not already there, add it to the list of parents to add
if (!properties.find(anotherProperty => anotherProperty.getId() === parent)) {
parentsToAdd.push(this.provider.getProperty(parent));
}
});
});
parentsToAdd.forEach(p => properties.push(p));
}
// 2. turn it into a tree
let dag = new Dag();
dag.initFromParentableAndIdAbleEntity(properties, []);
// sort the tree with compare function
dag.sort(SHACLSpecificationProperty.compare);
return dag;
}
getConnectedEntities() {
var items = [];
// read all properties that make sense for Sparnatural
let propShapes = this.getProperties();
propShapes
.forEach(ps => {
// read the property
let prop = new SHACLSpecificationProperty(ps, this.provider, this.store, this.lang);
// and then read their ranges
items.push(...prop.getRange());
});
// dedup
var dedupItems = [...new Set(items)];
// sort dedups
var sortedDedups = SHACLSpecificationEntry.sort(dedupItems.map(s => new SHACLSpecificationEntity(s, this.provider, this.store, this.lang)));
// return dedup array of strings
return sortedDedups.map(e => e.getId());
}
getConnectedEntitiesTree() {
// 1. get directly connected entities
let entities = this.getConnectedEntities().map(s => this.provider.getEntity(s));
// 2. add the children of these entities - recursively
while (!entities.every(entity => {
return entity.getChildren().every(child => {
return (entities.find(anotherEntity => anotherEntity.getId() === child) != undefined);
});
})) {
let childrenToAdd = [];
entities.forEach(entity => {
entity.getChildren().forEach(child => {
if (!entities.find(anotherEntity => anotherEntity.getId() === child)) {
childrenToAdd.push(this.provider.getEntity(child));
}
});
});
childrenToAdd.forEach(p => entities.push(p));
}
// 3. complement the initial list with their parents
let disabledList = new Array();
// while not all parents of all entities are not part of the list...
while (!entities.every(entity => {
return entity.getParents().every(parent => {
return (entities.find(anotherEntity => anotherEntity.getId() === parent) != undefined);
});
})) {
let parentsToAdd = [];
entities.forEach(entity => {
entity.getParents().forEach(parent => {
if (!entities.find(anotherEntity => anotherEntity.getId() === parent)) {
parentsToAdd.push(this.provider.getEntity(parent));
}
});
});
parentsToAdd.forEach(p => entities.push(p));
// also keep that as a disabled node
parentsToAdd.forEach(p => disabledList.push(p.getId()));
}
let dag = new Dag();
dag.initFromParentableAndIdAbleEntity(entities, disabledList);
// sort the tree with compare function
dag.sort(SHACLSpecificationEntity.compare);
return dag;
}
hasConnectedEntities() {
return (this.getConnectedEntities().length > 0);
}
/**
* @returns all (non-deactivated) properties available on this node shape, including inherited properties from superclasses
*/
getProperties() {
var items = [];
let propertyShapes = this.shape.getProperties();
// filter properties not to be shown
propertyShapes
.forEach(ps => {
if (SHACLSpecificationProperty.isSparnaturalSHACLSpecificationProperty(ps.resource.value, this.store)) {
items.push(ps.resource.value);
}
});
// dedup, although probably dedup is not necessary here
var dedupItems = [...new Set(items)];
// sort dedups
var sortedDedups = SHACLSpecificationEntry.sort(dedupItems.map(s => new SHACLSpecificationProperty(s, this.provider, this.store, this.lang)));
// return dedup array of strings
return sortedDedups.map(e => e.getId());
}
/**
*
* @returns true if sh:nodeKind = sh:Literal, or if sh:datatype is present, or if sh:languageIn is present
*/
isLiteralEntity() {
return this.shape.isLiteral();
}
isBlankNodeEntity() {
return this.shape.isBlankNode();
}
/**
* @returns true if this shape has a target, either a targetClass or sh:target, or is itself a Class
*/
hasTypeCriteria() {
var hasTargetClass = this.graph.hasTriple(factory.namedNode(this.uri), SH.TARGET_CLASS, null);
var hasTarget = this.graph.hasTriple(factory.namedNode(this.uri), SH.TARGET, null);
var isItselfClass = this.graph.hasTriple(factory.namedNode(this.uri), RDF.TYPE, RDFS.CLASS);
return (hasTargetClass || hasTarget || isItselfClass);
}
/**
* @returns a property labelled with dash:propertyRole = dash:LabelRole
*/
getDefaultLabelProperty() {
return this.shape.getDefaultLabelProperty()?.resource.value;
}
getParents() {
return this.shape.getParents().map(r => r.value);
}
getChildren() {
return this.shape.getChildren().map(r => r.value);
}
/**
* @returns all values of sh:targetClass on this entity, as RDF Terms
*/
getShTargetClass() {
return this.shape.getShTargetClass();
}
/**
* @returns true if this shape as an sh:target predicate (indicating it is associated to a SPARQL query target)
*/
hasShTarget() {
return this.shape.getShTarget()?.length > 0;
}
couldBeSkosConcept() {
return this.shape.couldBeSkosConcept();
}
static compare(item1, item2) {
return SHACLSpecificationEntry.compare(item1, item2);
}
}
export class SpecialSHACLSpecificationEntity {
constructor(id, icon, label, isRangeOfFunction) {
this.id = id;
this.icon = icon;
this.label = label;
this.isRangeOfFunction = isRangeOfFunction;
}
getId() {
return this.id;
}
getLabel() {
return this.label;
}
getColor() {
// return "slategray";
return undefined;
}
getIcon() {
return this.icon;
}
isRangeOf(n3store, shapeUri) {
return this.isRangeOfFunction(n3store, shapeUri);
}
// default implementation of all other functions
getConnectedEntities() { return new Array(); }
getConnectedEntitiesTree() { return new Dag(); }
hasConnectedEntities() { return false; }
getConnectingProperties(range) { return new Array(); }
getConnectingPropertiesTree(range) { return new Dag(); }
isLiteralEntity() { return true; }
isBlankNodeEntity() { return false; }
hasTypeCriteria() { return false; }
getDefaultLabelProperty() { return undefined; }
getOrder() { return undefined; }
getTooltip() { return undefined; }
getDatasource() { return null; }
getTreeChildrenDatasource() { return null; }
getTreeRootsDatasource() { return null; }
getHighlightedIcon() { return ""; }
getParents() { return []; }
getChildren() { return []; }
couldBeSkosConcept() { return false; }
}
export class SpecialSHACLSpecificationEntityRegistry {
/**
* The Singleton's constructor should always be private to prevent direct
* construction calls with the `new` operator.
*/
constructor() {
this.registry = new Map();
this.registry.set(SpecialSHACLSpecificationEntityRegistry.SPECIAL_SHACL_ENTITY_OTHER, new SpecialSHACLSpecificationEntity(SpecialSHACLSpecificationEntityRegistry.SPECIAL_SHACL_ENTITY_OTHER, "fa-solid fa-ellipsis", "Other", function (n3store, shapeUri) {
// this is in range if nothing else is in range
let graph = new Model(n3store);
return (!graph.hasTriple(factory.namedNode(shapeUri), SH.NODE, null)
&&
!graph.hasTriple(factory.namedNode(shapeUri), SH.CLASS, null)
&&
!graph.hasTriple(factory.namedNode(shapeUri), SH.DATATYPE, XSD.DATE)
&&
!graph.hasTriple(factory.namedNode(shapeUri), SH.DATATYPE, XSD.DATE_TIME)
&&
!graph.hasTriple(factory.namedNode(shapeUri), SH.DATATYPE, XSD.GYEAR)
&&
!graph.hasTriple(factory.namedNode(shapeUri), SH.DATATYPE, GEOSPARQL.WKT_LITERAL)
&&
!graph.hasTriple(factory.namedNode(shapeUri), SH.DATATYPE, XSD.STRING)
&&
!graph.hasTriple(factory.namedNode(shapeUri), SH.DATATYPE, RDF.LANG_STRING));
}));
this.registry.set(SpecialSHACLSpecificationEntityRegistry.SPECIAL_SHACL_ENTITY_DATES, new SpecialSHACLSpecificationEntity(SpecialSHACLSpecificationEntityRegistry.SPECIAL_SHACL_ENTITY_DATES, "fa-solid fa-calendar", "Date", function (n3store, shapeUri) {
let graph = new Model(n3store);
let dt = graph.readSingleProperty(factory.namedNode(shapeUri), SH.DATATYPE);
return dt && DatatypeRegistry.asDatatype(dt).isDateDatatype();
}));
this.registry.set(SpecialSHACLSpecificationEntityRegistry.SPECIAL_SHACL_ENTITY_LOCATION, new SpecialSHACLSpecificationEntity(SpecialSHACLSpecificationEntityRegistry.SPECIAL_SHACL_ENTITY_LOCATION, "fa-solid fa-map-location-dot", "Location", function (n3store, shapeUri) {
let graph = new Model(n3store);
return graph.hasTriple(factory.namedNode(shapeUri), SH.DATATYPE, GEOSPARQL.WKT_LITERAL);
}));
this.registry.set(SpecialSHACLSpecificationEntityRegistry.SPECIAL_SHACL_ENTITY_TEXT, new SpecialSHACLSpecificationEntity(SpecialSHACLSpecificationEntityRegistry.SPECIAL_SHACL_ENTITY_TEXT, "fa-solid fa-font", "Text", function (n3store, shapeUri) {
let graph = new Model(n3store);
return (graph.hasTriple(factory.namedNode(shapeUri), SH.DATATYPE, XSD.STRING)
||
graph.hasTriple(factory.namedNode(shapeUri), SH.DATATYPE, RDF.LANG_STRING)
||
// no datatype but we know it is a Literal
(graph.hasTriple(factory.namedNode(shapeUri), SH.NODE_KIND, SH.LITERAL)
&&
(!graph.hasTriple(factory.namedNode(shapeUri), SH.DATATYPE, null)
||
(!graph.readSingleProperty(factory.namedNode(shapeUri), SH.DATATYPE)?.value.startsWith("http://www.w3.org/2001/XMLSchema#")
&&
!graph.readSingleProperty(factory.namedNode(shapeUri), SH.DATATYPE)?.value.startsWith("http://www.opengis.net/ont/geosparql#")))));
}));
this.registry.set(SpecialSHACLSpecificationEntityRegistry.SPECIAL_SHACL_ENTITY_NUMBER, new SpecialSHACLSpecificationEntity(SpecialSHACLSpecificationEntityRegistry.SPECIAL_SHACL_ENTITY_NUMBER, "fa-solid fa-1", "Number", function (n3store, shapeUri) {
let graph = new Model(n3store);
let dt = graph.readSingleProperty(factory.namedNode(shapeUri), SH.DATATYPE);
return dt && DatatypeRegistry.asDatatype(dt).isNumberDatatype();
}));
}
/**
* The static method that controls the access to the singleton instance.
*
* This implementation let you subclass the Singleton class while keeping
* just one instance of each subclass around.
*/
static getInstance() {
if (!SpecialSHACLSpecificationEntityRegistry.instance) {
SpecialSHACLSpecificationEntityRegistry.instance = new SpecialSHACLSpecificationEntityRegistry();
}
return SpecialSHACLSpecificationEntityRegistry.instance;
}
getRegistry() {
return this.registry;
}
}
SpecialSHACLSpecificationEntityRegistry.SPECIAL_SHACL_ENTITY_DATES = "http://special/Z_Date";
SpecialSHACLSpecificationEntityRegistry.SPECIAL_SHACL_ENTITY_LOCATION = "http://special/Z_Location";
SpecialSHACLSpecificationEntityRegistry.SPECIAL_SHACL_ENTITY_TEXT = "http://special/Z_Text";
SpecialSHACLSpecificationEntityRegistry.SPECIAL_SHACL_ENTITY_NUMBER = "http://special/Z_Number";
SpecialSHACLSpecificationEntityRegistry.SPECIAL_SHACL_ENTITY_OTHER = "http://special/ZZ_Other";
SpecialSHACLSpecificationEntityRegistry.instance = new SpecialSHACLSpecificationEntityRegistry();
//# sourceMappingURL=SHACLSpecificationEntity.js.map