lincd
Version:
LINCD is a JavaScript library for building user interfaces with linked data (also known as 'structured data', or RDF)
831 lines (830 loc) • 36.2 kB
JavaScript
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
import nextTick from 'next-tick';
import { Literal, NamedNode, Node } from '../models.js';
import { rdf } from '../ontologies/rdf.js';
import { NodeValuesSet } from '../collections/NodeValuesSet.js';
import { rdfs } from '../ontologies/rdfs.js';
import { NodeSet } from '../collections/NodeSet.js';
import { Find } from '../utils/Find.js';
import { ShapeSet } from '../collections/ShapeSet.js';
import { CoreSet } from '../collections/CoreSet.js';
import { ShapeValuesSet } from '../collections/ShapeValuesSet.js';
import { getMostSpecificShapesByType, getPropertyShapeByLabel, getShapeOrSubShape, getSubShapesClasses, } from '../utils/ShapeClass.js';
import { QueryShape, SelectQueryFactory, } from '../queries/SelectQuery.js';
import { TestNode } from '../utils/TraceShape.js';
/**
* The base class of all classes that represent a rdfs:Class in the graph.
*
* This class helps form a bridge between the graph (RDF) world & the Object-Oriented typescript world.
* Each Shape class has a static type property pointing to the rdfs:Class that it represents.
* Each instance of a class that extends this Shape class points to a single node (NamedNode or Literal), that MUST have this rdfs:Class as its rdf:type in the graph.
*
* Classes that extend this class can thereby help simplify interactions with nodes that have a certain rdf:type by replacing low level property access (NamedNode.getAll(), getOne() etc) with high level methods that do not require knowledge of the underlying graph structure.
*
* @example
* An Example:
* ```tsx
* @linkedShape
* class Person extends Shape {
* static type = foaf.Person
* get friends() {
* return this.getAll(foaf.hasFriend)
* }
* }
*
* let personNode = NamedNode.getOrCreate();
* personNode.set(rdf.type,foaf.Person);
*
* //creates an instance of the class Person, which points to (represents) personResource.
* let person = new Person(personNode);
*
* //will log all the friends of the personResource (currently none)
* console.log(person.friends);
* ```
*/
export class Shape {
/**
* Creates a new instance of this class.
* If no node is given, a new NamedNode will be generated and it's rdf:type will be set.
* Only use this constructor directly if you want to create a new node as well.
* If you want to create an instance of an existing node, use `node.getAs(Class)` or `Class.getOf(node)`
* @param node
*/
constructor(node) {
this.setupNode(node);
}
/**
* Returns the node this instance represents.
*
* Since each node in RDF can have multiple types, each node can have multiple instances (multiple representations of itself reflecting the different things it 'is')
* But each instance always only represents a single node
*/
get node() {
//Instances of rdfs:Literal overwrite this method to return literalResource instead
return this._node;
}
/**
* returns the rdf:Class that this type of instance represents.
*/
get nodeShape() {
return this.constructor.shape;
// if (this.constructor['targetClass'])
// {
// return this.constructor['targetClass'];
// }
// throw new Error('The constructor of this instance has not defined a static targetClass.');
}
/**
* Returns the NamedNode that this instance represents.
*
* Since each node in RDF can have multiple types, each node can have multiple instances (multiple representations of itself reflecting the different things it 'is')
* But each instance always only represents a single node
*
* NOTE: the node of an instance is NOT GUARANTEED to be a NamedNode. There are also instance of Literals.
* Therefore only use this method if you are certain that the instance you have represents a NamedNode.
* In that case this method - which works exactly the same as `.node` - simply tells the compiler that the return node is certainly a NamedNode.
*/
get namedNode() {
//Instances of rdfs:Literal will return null so we can just return the node as is here, and use this method for type casting
return this._node;
}
get value() {
return this._node.value;
}
get uri() {
return this._node.value;
}
//TODO: move to rdfs:Resource or owl:Thing shape? (and decide which one of those we want to promote)
get label() {
return this.getValue(rdfs.label);
}
set label(val) {
this.overwrite(rdfs.label, new Literal(val));
}
static create(updateObjectOrFn) {
return this.queryParser.createQuery(updateObjectOrFn, this);
}
static delete(id) {
return this.queryParser.deleteQuery(id, this);
}
/**
* @internal
* @param shapeClass
* @param type
*/
static registerByType(shapeClass, type) {
if (!type) {
if (shapeClass === Shape) {
return;
}
//TODO: add support for sh:targetNode, sh:targetObjectsOf and sh:targetSubjectsOf. Those would be fine as alternatives to targetClass (and the latter 2 define a PropertyShape)
//warn developers against a common mistake: if no static shape is set by the Component it will inherit the one of the class it extends
if (!shapeClass.hasOwnProperty('targetClass')) {
console.warn(`Shape ${shapeClass.name} is not linked to a targetClass. Please define 'static targetClass:NamedNode'`);
return;
}
type = shapeClass.targetClass;
}
//save in a map for finding the Shape back based on the type
if (!this.typesToShapes.has(type)) {
this.typesToShapes.set(type, new CoreSet());
}
this.typesToShapes.get(type).add(shapeClass);
}
/**
* Get a the matching shape classes that have a targetClass equal to the given type node
* @internal
* @param type
* @param allowSuperClass
*/
static getClassesForType(type, allowSuperClass = false) {
let instanceClasses = this.typesToShapes.get(type);
if (allowSuperClass) {
let subClasses = type.getDeep(rdfs.subClassOf);
// subClasses = Order.typesByDepth(subClasses);
subClasses.delete(type); //<-- only delete after ordering, as it will be a new set and not the original PropertySet
subClasses.forEach((subViewType) => {
if (this.typesToShapes.has(subViewType)) {
instanceClasses = instanceClasses.concat(this.typesToShapes.get(subViewType));
}
});
}
return instanceClasses;
}
static isValidNode(node) {
this.ensureLinkedShape();
return this.shape.validateNode(node);
}
static query(subject, queryFn) {
const _queryFn = subject && queryFn ? queryFn : subject;
let _subject = queryFn ? subject : undefined;
if (_subject instanceof QueryShape) {
_subject = { id: _subject.id };
}
const query = new SelectQueryFactory(this, _queryFn, _subject);
return query;
}
static select(targetOrSelectFn, selectFn) {
let _selectFn;
let subject;
if (selectFn) {
_selectFn = selectFn;
subject = targetOrSelectFn;
}
else {
_selectFn = targetOrSelectFn;
}
const query = new SelectQueryFactory(this, _selectFn, subject);
let p = new Promise((resolve, reject) => {
nextTick(() => {
this.queryParser
.selectQuery(query)
.then((result) => {
resolve(result);
})
.catch((err) => {
reject(err);
});
});
});
return query.patchResultPromise(p);
// return this.queryParser.query<ResultType>(query);
}
static update(id, updateObjectOrFn) {
return this.queryParser.updateQuery(id, updateObjectOrFn, this);
}
static mapPropertyShapes(mapFunction) {
let dummyNode = new TestNode();
let dummyShape = new this(dummyNode);
//store the proxy on the shape, so we can access it later
dummyShape.proxy = new Proxy(dummyShape, {
get(target, key, receiver) {
//if the key is a string
if (typeof key === 'string') {
//if this is a get method that is implemented by the QueryShape, then use that
if (key in dummyShape) {
//if it's a function, then bind it to the queryShape and return it so it can be called
if (typeof dummyShape[key] === 'function') {
return target[key].bind(target);
}
//if not, then a method/accessor of the original shape was called
//then check if we have indexed any property shapes with that name for this shapes NodeShape
let propertyShape = getPropertyShapeByLabel(dummyShape.constructor, key.toString());
if (propertyShape) {
//this method does not allow any further chaining, so we return the value of the property
return propertyShape;
}
//otherwise return the value of the property on the original shape
throw new Error(`${this.name}.${key.toString()} is missing a decorator. This method can only access decorated get/set methods.`);
}
}
},
});
//call the provided method with the proxy. When the method requests get/set methods, it will get the property shapes instead
return mapFunction(dummyShape.proxy);
}
static isInstanceOfTargetClass(node) {
return node.has(rdf.type, this.targetClass);
}
static getInstanceByType(node, ...shapes) {
let matchingShape = shapes.find((shape) => {
return node.has(rdf.type, shape.targetClass);
});
if (matchingShape) {
return matchingShape.getOf(node);
}
}
/**
* Searches instances with the given properties only from the local graph
* @param properties
* @param sanitized
*/
static searchLocal(properties, sanitized = false) {
let quads = Find.byPropertyValues(properties, this.targetClass, true, true, sanitized);
let set = new ShapeSet();
for (var node of quads.getSubjects()) {
set.add(new this(node));
}
return set;
}
/**
* Searches instances with given properties
* And if results are returned, it returns an instance of the first result, else null
* @param properties
*/
static findLocal(properties, sanitized = false) {
let results = this.searchLocal(properties, sanitized);
if (results.size > 0) {
return results.first();
}
}
/**
* Finds all the instances whos rdf:type matches the targetClass of this shape
* Ignores if the nodes are valid instances of the shape
* Returns a set of shape instances.
* This is helpful when using partly loaded data
* @deprecated
*/
static getLocalInstancesByType() {
return this.getSetOf(this.getLocalInstanceNodesByType());
}
/**
* Finds all the instances whos rdf:type matches the targetClass of this shape
* Ignores if the nodes are valid instances of the shape
* Returns a set of shape instances.
* This is helpful when using partly loaded data
* @deprecated
*/
static getLocalInstanceNodesByType() {
//get all instances of the target class of this shape
let nodes = this.targetClass.getAllInverse(rdf.type);
//also look for shapes that extend this shape
getSubShapesClasses(this).forEach((shapeClass) => {
//and add instances of those classes as well
if (shapeClass.targetClass) {
return shapeClass.targetClass
.getAllInverse(rdf.type)
.forEach((node) => {
nodes.add(node);
});
}
});
return nodes;
}
/**
* @deprecated
* @param explicitInstancesOnly
*/
static getLocalInstances(explicitInstancesOnly = false) {
//'this' is listed as a parameter ti be able to return a set of instances with the type of the actual class that extends Shape
// https://www.typescriptlang.org/docs/handbook/generics.html#using-class-types-in-generics
// https://stackoverflow.com/questions/34098023/typescript-self-referencing-return-type-for-static-methods-in-inheriting-classe?rq=1
return this.getSetOf(this.getLocalInstanceNodes());
}
//TODO: to find Shape instances we need to not just check type, but all the constraints of this shape class
/**
* @deprecated
*/
static getNumLocalInstances() {
return this.getLocalInstanceNodes().size;
}
/**
* @deprecated
* @param explicitInstancesOnly
*/
static getLocalInstanceNodes(explicitInstancesOnly = false) {
let instanceNodes = new NodeSet();
//by default, look for instances of this shape class and all classes that extend it
let targetClasses = [this].concat(getSubShapesClasses(this));
targetClasses.forEach((shapeClass) => {
if (!shapeClass.targetClass) {
console.warn('Shape class ' +
shapeClass.name +
' does not have a targetClass. Please define a static targetClass:NamedNode');
return;
}
let potentialInstances = new NodeSet();
if (explicitInstancesOnly) {
potentialInstances = shapeClass.targetClass
.getInverseQuads(rdf.type)
.filter((quad) => !quad.implicit)
.getSubjects();
}
else {
potentialInstances = shapeClass.targetClass.getAllInverse(rdf.type);
}
//return only those instance nodes that are actual valid instances of this shape
instanceNodes = instanceNodes.concat(potentialInstances.filter((node) => shapeClass.isValidNode(node)));
});
return instanceNodes;
}
/**
* use new Shape(node) instead, where Shape can be any class that extends Shape
* @deprecated
* @param node
*/
static getOf(node) {
return new this(node);
}
/**
* Retrieves an existing node or creates a new (temporary) node and then sets the right rdf:type
* Then uses that node to return an instance of the Shape that you call this method from
* So it works just like NamedNode.getOrCreate() but creates an instance of the right shape straight away.
* Note that if the URI did not yet exist, it creates a temporary node, and hence only once you SAVE that node or shape
* Will it (and its properties) be stored in permanent storage.
*
* @param uri
* @param isTemporaryNodeIfNew
*/
static getFromURI(uri, isTemporaryNodeIfNew = true) {
let node = NamedNode.getNamedNode(uri);
if (node) {
return new this(node);
}
else {
node = NamedNode.getOrCreate(uri, isTemporaryNodeIfNew);
if (this.targetClass) {
node.set(rdf.type, this.targetClass);
}
return new this(node);
}
return new this(NamedNode.getOrCreate(uri));
}
/**
* Generates a URI from the given prefixURI + optional unique parameters
* Then returns an instance of this shape with that URI, either from an existing or new node
* This method is intended to be extended by other shapes.
* The base implementation in Shape.ts will generate a unique URI if no uniqueParams are given, so extending methods may use super.getFromParams() when no params are given
* @param prefixURI
* @param uniqueParams
*/
static getFromParams(prefixURI, ...uniqueParams) {
let postfix;
if (uniqueParams.length) {
postfix = uniqueParams.join('/');
}
else {
//here we expect that we'll create a new node, so the counter will be increased when we actually create it
postfix = NamedNode.getCounter() + 1;
}
let uri = prefixURI + this.name + '/' + postfix;
return this.getFromURI(uri);
}
static getSetOf(nodes, allowSubShapes = false) {
if (!nodes) {
throw new Error('No nodes provided to create shape instances of');
}
if (nodes instanceof NodeValuesSet && nodes.subject instanceof NamedNode) {
return new ShapeValuesSet(nodes.subject, nodes.property, this, allowSubShapes);
}
return new ShapeSet(nodes.map((node) => {
return allowSubShapes
? getShapeOrSubShape(node, this)
: new this(node);
}));
}
static ensureLinkedShape() {
if (!this.shape) {
console.warn(this.name +
' is not a linked shape. Did you forget to use the @linkedShape decorator?');
}
}
/**
* Get all values of a certain property as instances of a certain shape.
* The returned set of shape will automatically update when the property values change in the graph.
* @param property
* @param shapeClass
*/
getAllAs(property, shapeClass, allowSubShapes = false) {
return new ShapeValuesSet(this.namedNode, property, shapeClass, allowSubShapes);
// return (shapeClass as any).getSetOf(this.getAll(property),allowSubShapes);
}
/**
* If a value exists for the given property, this returns that value as an instance of the given shape
* If not, returns null
* @param property
* @param shape
*/
getOneAs(property, shape, allowSubShapes = false) {
if (this.hasProperty(property)) {
const value = this.getOne(property);
if (allowSubShapes) {
shape =
(shape
? getMostSpecificShapesByType(value, shape)[0] || shape
: getMostSpecificShapesByType(value)[0]) || Shape;
}
return new shape(value);
}
// return this.hasProperty(property) ? new (shape as any)(this.getOne(property)) as S : null;
}
equals(other, checkShapeType = false) {
return (other instanceof Shape &&
other.node === this.node &&
(!checkShapeType ||
Object.getPrototypeOf(other) === Object.getPrototypeOf(this)));
}
/**
* Makes sure that the node that this instance represents has the right rdf.type
* Also makes sure that this instance is destructed if the node is removed
* @internal
* @param node
*/
setupNode(node) {
if (node) {
if (!(node instanceof Node)) {
console.error('Invalid argument to constructor of shape:', node);
throw new Error('Invalid argument provided to constructor of shape. Please provide an instance of a node.');
}
this._node = node;
}
else {
//this code gets triggered when you call new SomeShapeClass() without providing a node
//some classes prefer a certain term type. E.g. RdfsLiteral will create a Literal node, and NodeShape will create a BlankNode
//TODO: also look at inheritance chain, so that a class without preferredNodeKind that extends a class with preferredTermType still gets that inherited termType
let termType = this.constructor['nodeKind'] ||
this.constructor['preferredNodeKind'] ||
NamedNode;
//create a new temporary node, a Literal, NamedNode or BlankNode
this._node = termType.create(true);
let nodeShape = this.nodeShape;
if (nodeShape && nodeShape.targetClass) {
this._node.set(rdf.type, nodeShape.targetClass);
}
}
//@TODO: do this for RdfsLiteral as well if they implement events at some point?
// if (this._node instanceof NamedNode) {
// this._node.on(NamedNode.NODE_REMOVED, this.destruct.bind(this));
// }
}
/**
* Destructs the instance. Removes event listeners etc. Overwrite in each subclass of this class that uses custom event listeners
*/
destruct() {
if (this._node instanceof NamedNode) {
this._node.removeAllListeners();
}
}
validate() {
var _a;
return ((_a = this.nodeShape) === null || _a === void 0 ? void 0 : _a.validateNode(this.node)) || false;
}
getOne(property) {
return this._node.getOne(property);
}
getAll(property) {
return this._node.getAll(property);
}
getAllExplicit(property) {
return this._node.getAllExplicit(property);
}
getOneFromPath(...properties) {
return this.node.getOneFromPath(...properties);
}
getAllFromPath(...properties) {
return this.node.getAllFromPath(...properties);
}
getOneInverse(property) {
return this._node.getOneInverse(property);
}
getAllInverse(property) {
return this._node.getAllInverse(property);
}
set(property, value) {
return this._node.set(property, value);
}
setValue(property, value) {
return this._node.setValue(property, value);
}
mset(property, values) {
return this._node.mset(property, values);
}
overwrite(property, value) {
return this._node.overwrite(property, value);
}
moverwrite(property, values) {
return this._node.moverwrite(property, values);
}
remove() {
return this.namedNode.remove();
}
/**
* @deprecated
*/
save() {
return this.namedNode.save();
}
unset(property, value) {
return this._node.unset(property, value);
}
unsetAll(property) {
return this._node.unsetAll(property);
}
has(property, value) {
return this._node.has(property, value);
}
hasValue(property, value) {
return this._node.hasValue(property, value);
}
hasExplicit(property, value) {
return this._node.hasExplicit(property, value);
}
hasPath(properties) {
return this._node.hasPath(properties);
}
hasPathTo(properties, endPoint) {
return this._node.hasPathTo(properties, endPoint);
}
hasPathToSomeInSet(properties, endPoints) {
return this._node.hasPathToSomeInSet(properties, endPoints);
}
/**
* Checks if the node has a value for this property that is the exact same object as the given value
* (as opposed to has() which also returns true for equivalent literal values in Literal objects)
* @param property
* @param value
* @returns {boolean}
*/
hasExact(property, value = null) {
return this._node.hasExact(property, value);
}
hasProperty(property) {
return this._node.hasProperty(property);
}
hasInverse(property, value = null) {
return this._node.hasInverse(property, value);
}
hasInverseProperty(property) {
return this._node.hasInverseProperty(property);
}
getValue(property, language = '') {
return this._node.getValue(property, language);
}
getProperties(includeFromIncomingArcs = false) {
return this._node.getProperties(includeFromIncomingArcs);
}
getInverseProperties() {
return this._node.getInverseProperties();
}
getMultiple(properties) {
return this._node.getMultiple(properties);
}
getMultipleInverse(properties) {
return this._node.getMultipleInverse(properties);
}
getDeep(property, maxDepth) {
return this._node.getDeep(property, maxDepth);
}
getQuads(property, value) {
return this._node.getQuads(property, value);
}
getInverseQuads(property) {
return this._node.getInverseQuads(property);
}
getAllInverseQuads(includeImplicit) {
return this._node.getAllInverseQuads(includeImplicit);
}
getAllQuads(includeAsObject = false, includeImplicit = false) {
return this._node.getAllQuads(includeAsObject, includeImplicit);
}
/**
* Returns all quads related to this shape.
* Overwrite this method to automatically send over quads to the frontend when this shape is sent over
* This method is used internally by JSONWriter when sending a shape between environments by converting it to JSON & JSON-LD
* @param includeImplicit
*/
getDataQuads(includeImplicit = false) {
return this._node.getAllQuads(includeImplicit);
}
/**
* Fires the given call back when ANY property of this node changes.
* @param callback the method to be called when the change happens. The quads that have changed + the property that was updated are supplied as parameters
* @param context give a context to make sure you can easily unset / clear event listeners. Usually you would provide 'this' as context
*/
onChangeAny(callback, context) {
var _a;
(_a = this.namedNode) === null || _a === void 0 ? void 0 : _a.onChangeAny(callback, context);
}
/**
* Fires the given call back when this node become the value or is no longer the value of another node
* @param callback the method to be called when the change happens. The quads that have changed + the property that was updated are supplied as parameters
* @param context give a context to make sure you can easily unset / clear event listeners. Usually you would provide 'this' as context
*/
onChangeAnyInverse(callback, context) {
var _a;
(_a = this.namedNode) === null || _a === void 0 ? void 0 : _a.onChangeAnyInverse(callback, context);
}
/**
* Fires the given call back when this node changes the values of the given property
* @param callback the method to be called when the change happens. The quads that have changed + the property that was updated are supplied as parameters
* @param context give a context to make sure you can easily unset / clear event listeners. Usually you would provide 'this' as context
*/
onChange(property, callback, context) {
var _a;
(_a = this.namedNode) === null || _a === void 0 ? void 0 : _a.onChange(property, callback, context);
}
/**
* Fires the given callback when this node become the value or is no longer the value of the given property of another node
* Example: if someGroup hasParticipant thisResource, and the group removes this node from its participants, it will trigger onChangeInverse for this node
* @param callback the method to be called when the change happens. The quads that have changed + the property that was updated are supplied as parameters
* @param context give a context to make sure you can easily unset / clear event listeners. Usually you would provide 'this' as context
*/
onChangeInverse(property, callback, context) {
var _a;
(_a = this.namedNode) === null || _a === void 0 ? void 0 : _a.onChangeInverse(property, callback, context);
}
/**
* Call this when you want to stop listening for onChangeAny events. Make sure to provide the exact same BOUND instance of a method to properly clear the listener. OR make sure to provide a context both when setting and clearing the listener.
* @param callback the exact same method you supplied to onChangeAny
* @param context the same context you supplied to onChangeAny
*/
removeOnChangeAny(callback, context) {
var _a;
(_a = this.namedNode) === null || _a === void 0 ? void 0 : _a.removeOnChangeAny(callback, context);
}
/**
* Call this when you want to stop listening for onChangeAnyInverse events. Make sure to provide the exact same BOUND instance of a method to properly clear the listener. OR make sure to provide a context both when setting and clearing the listener.
* @param callback the exact same method you supplied to onChangeAnyInverse
* @param context the same context you supplied to onChangeAnyInverse
*/
removeOnChangeAnyInverse(callback, context) {
var _a;
(_a = this.namedNode) === null || _a === void 0 ? void 0 : _a.removeOnChangeAnyInverse(callback, context);
}
/**
* Call this when you want to stop listening for onChange events. Make sure to provide the exact same BOUND instance of a method as callback to properly clear the listener. OR make sure to provide a context both when setting and clearing the listener.
* @param callback the exact same method you supplied to onChange
* @param context the same context you supplied to onChange
*/
removeOnChange(property, callback, context) {
var _a;
(_a = this.namedNode) === null || _a === void 0 ? void 0 : _a.removeOnChange(property, callback, context);
}
/**
* Call this when you want to stop listening for onChangeInverse events. Make sure to provide the exact same BOUND instance of a method as callback to properly clear the listener. OR make sure to provide a context both when setting and clearing the listener.
* @param callback the exact same method you supplied to onChangeInverse
* @param context the same context you supplied to onChangeInverse
*/
removeOnChangeInverse(property, callback, context) {
var _a;
(_a = this.namedNode) === null || _a === void 0 ? void 0 : _a.removeOnChangeInverse(property, callback, context);
}
/**
* Call this when you want to stop listening for onChangeAny events. Other then removeOnChangeAny you only have to supply the context.
* Use this if you no longer have access to the same bound listener function or you're otherwise unable to clear with removeOnChangeAny
* @param context the same context you supplied to onChangeAny
*/
clearOnChangeAny(context) {
var _a;
(_a = this.namedNode) === null || _a === void 0 ? void 0 : _a.clearOnChangeAny(context);
}
/**
* Call this when you want to stop listening for onChangeAnyInverse events. Other then removeOnChangeAnyInverse you only have to supply the context.
* Use this if you no longer have access to the same bound listener function or you're otherwise unable to clear with removeOnChangeAnyInverse
* @param context the same context you supplied to onChangeAnyInverse
*/
clearOnChangeAnyInverse(context) {
var _a;
(_a = this.namedNode) === null || _a === void 0 ? void 0 : _a.clearOnChangeAnyInverse(context);
}
/**
* Call this when you want to stop listening for onChange events. Other then removeOnChange you only have to supply the context.
* Use this if you no longer have access to the same bound listener function or you're otherwise unable to clear with removeOnChange
* @param context the same context you supplied to onChange
*/
clearOnChange(property, context) {
var _a;
(_a = this.namedNode) === null || _a === void 0 ? void 0 : _a.clearOnChange(property, context);
}
/**
* Call this when you want to stop listening for onChangeInverse events. Other then removeOnChangeInverse you only have to supply the context.
* Use this if you no longer have access to the same bound listener function or you're otherwise unable to clear with removeOnChangeInverse
* @param context the same context you supplied to onChangeAny
*/
clearOnChangeInverse(property, context) {
var _a;
(_a = this.namedNode) === null || _a === void 0 ? void 0 : _a.clearOnChangeInverse(property, context);
}
/**
* Call this when you want to stop listening for onPredicateChange events
* @param context the same context you supplied to onPredicateChange
*/
clearOnPredicateChange(context) {
var _a;
(_a = this.namedNode) === null || _a === void 0 ? void 0 : _a.clearOnPredicateChange(context);
}
/**
* Returns true if this instance has the given type as the value of rdf.type
* Syntactic sugar for this.has(rdf.type,type)
* @param type
*/
isa(type) {
return this.has(rdf.type, type);
}
/**
* Other than NamedNode.promiseLoaded, a Shape will preload whatever data it requires to fulfill the constraints of the shape
* NOTE: loading is handled by the current StorageController, by default there is no StorageController
* @param {boolean} loadInverseProperties
* @returns {Promise<boolean>}
*/
promiseLoaded(loadInverseProperties = false) {
if (!this.loadPromise) {
let promise = this.load(loadInverseProperties);
this.loadPromise = {
done: false,
promise: promise,
};
promise.then((res) => {
this.loadPromise.done = true;
return res;
});
}
return this.loadPromise.promise;
}
/**
* Returns true if this instance has had its promiseLoaded function called and the loading has completed
* NOTE: will return false if the instance has never loaded, regardless of whether the namedNode it represents is already loaded, and even if this instance would not load anything else
*/
isLoaded(includingInverseProperties = false) {
return this.node instanceof NamedNode
? this.namedNode.isLoaded(includingInverseProperties)
: true;
}
reload() {
this.loadPromise = null;
return this.promiseLoaded();
}
load(loadInverseProperties = false) {
if (!this.namedNode)
return Promise.resolve(true);
//load the node itself
return this.namedNode.promiseLoaded(loadInverseProperties).then(() => {
//make sure the reasoner has run on the loaded properties
// return Reasoning.promiseComplete();
return null;
});
}
toString() {
return '[' + this.node + ' as ' + this.constructor.name + ']';
}
print(includeIncomingProperties = true) {
// return Debug.print(this.node,includeIncomingProperties);
return `${this.constructor.name} of ${this.node.print()}`;
// typeof (typeof window !== 'undefined' ? window['dprint'] : global.dprint)(this, includeIncomingProperties);
}
/**
* Returns a new cloned instance with the exact same quads
* The instance only exists locally (as it's not yet saved)
* @returns {T}
*/
clone() {
let constructor = this.constructor;
return new constructor(this.node.clone());
}
}
/**
* Points to the rdfs:Class that this typescript class represents. Each class extending Shape MUST define this explicitly.
The appointed NamedNode value must be a rdfs:Class ([value] rdf:type rdfs:Class in the graph)
@example
An example Shape class that states that all matching nodes must have `rdf:type foaf:Person`.
```tsx
import {foaf} from "./ontologies/foaf";
@linkedShape
export class Person extends Shape {
static targetClass:NamedNode = foaf.Person;
}
```
*/
Shape.targetClass = null;
/**
* Tracks which types (named nodes) map to which Shapes
* @internal
*/
Shape.typesToShapes = new Map();
// static shapeCallbacks: ((shape) => void)[] = [];
Shape.instancesLoaded = new Map();
//# sourceMappingURL=Shape.js.map