UNPKG

@ontola/memoized-factory

Version:

RDF Factory with neat memory usage and good cpu performance.

217 lines (214 loc) 7.33 kB
import { PlainFactory, TermType, Feature } from '@ontologies/core'; const rdflibQuadPatch = { get why() { return this.graph; }, }; const rdfBase = (factory) => ({ equals(other) { return factory.equals.call(factory, this, other); }, /* rdflib compat */ /** @deprecated */ toCanonical() { return this; }, /** @deprecated */ toNT() { return factory.toNQ(this); }, /** @deprecated */ toString() { return factory.toNQ(this); }, /** @deprecated */ get uri() { return this.value; }, /** @deprecated */ set uri(uri) { this.value = uri; }, }); const datatypes = { boolean: "http://www.w3.org/2001/XMLSchema#boolean", dateTime: "http://www.w3.org/2001/XMLSchema#dateTime", decimal: "http://www.w3.org/2001/XMLSchema#decimal", double: "http://www.w3.org/2001/XMLSchema#double", integer: "http://www.w3.org/2001/XMLSchema#integer", langString: "http://www.w3.org/1999/02/22-rdf-syntax-ns#langString", string: "http://www.w3.org/2001/XMLSchema#string", }; function createException(type, value) { const valueType = (value && typeof value === "object") ? value.constructor : typeof value; return new TypeError(`Value of ${type} has to be type string, was value '${value}' of type '${valueType}'`); } /** * RDF DataFactory which stores every value once at most. * * This version uses hashing which might be more CPU consuming but has deterministic id creation. */ class MemoizedHashFactory extends PlainFactory { constructor(opts = {}) { super({ supports: MemoizedHashFactory.FactorySupport, ...opts }); this.memoizationMap = {}; this.blankNodeMap = {}; this.namedNodeMap = {}; this.literalMap = {}; this.quadMap = {}; this.index = 1; this.bnIndex = opts.bnIndex || 1; this.base = rdfBase(this); } blankNode(value) { if (value && typeof value !== "string") { throw createException("BlankNode", value); } const usedValue = value || `_:b${++this.bnIndex}`; const mapId = this.mapId({ termType: "BlankNode", value: usedValue }); if (mapId && this.blankNodeMap[mapId]) { return this.blankNodeMap[mapId]; } const term = Object.create(this.base); term.termType = TermType.BlankNode; term.value = usedValue; term.id = this.index++; this.blankNodeMap[mapId] = term; this.memoizationMap[term.id] = term; return term; } namedNode(value) { if (typeof value !== "string") { throw createException("NamedNode", value); } const mapId = this.mapId({ termType: "NamedNode", value }); if (this.namedNodeMap[mapId]) { return this.namedNodeMap[mapId]; } const term = Object.create(this.base); term.termType = TermType.NamedNode; term.value = value; term.id = this.index++; this.namedNodeMap[mapId] = term; this.memoizationMap[term.id] = term; return term; } defaultGraph() { return this.namedNode("rdf:defaultGraph"); } literal(value, languageOrDatatype) { if (typeof value !== "string") { return this.parseLiteral(value); } const isLangString = typeof languageOrDatatype === "string"; const datatype = isLangString ? this.namedNode(datatypes.langString) : (languageOrDatatype || this.namedNode(datatypes.string)); if (datatype === undefined) { throw Error("datatype must be defined"); } const language = isLangString ? (languageOrDatatype || "") : ""; const mapId = this.mapId({ termType: "Literal", value, datatype, language }); if (this.literalMap[mapId]) { return this.literalMap[mapId]; } const term = Object.create(this.base); term.termType = TermType.Literal; term.datatype = datatype; term.language = language; term.value = value; term.id = this.index++; this.literalMap[mapId] = term; this.memoizationMap[term.id] = term; return term; } quad(subject, predicate, object, graph) { const usedGraph = graph || this.defaultGraph(); const quadMapId = `${this.id(subject)},${this.id(predicate)},${this.id(object)},${(graph ? this.id(graph) : 0)}`; if (this.quadMap[quadMapId]) { return this.quadMap[quadMapId]; } const quad = Object.create(rdflibQuadPatch); quad.id = this.index++; quad.subject = subject; quad.predicate = predicate; quad.object = object; quad.graph = usedGraph; this.quadMap[quadMapId] = quad; this.memoizationMap[quad.id] = quad; return quad; } equals(a, b) { if (!a || !b) { return a === b; } if (Array.isArray(a) && Array.isArray(b)) { return this.id(a[0]) === this.id(b[0]) && this.id(a[1]) === this.id(b[1]) && this.id(a[2]) === this.id(b[2]) && this.id(a[3]) === this.id(b[3]); } return this.id(a) === this.id(b); } fromId(id) { return this.memoizationMap[id]; } id(term) { if (Array.isArray(term) || typeof term === "undefined") { return -1; } if (term.id) { return term.id; } const mapId = this.mapId(term); if (this.isQuad(term)) { const mapValue = this.quadMap[mapId]; return mapValue ? mapValue.id : this.index++; } switch (term.termType) { case TermType.BlankNode: { const mapValue = this.blankNodeMap[mapId]; return mapValue ? mapValue.id : this.index++; } case TermType.NamedNode: { const mapValue = this.namedNodeMap[mapId]; return mapValue ? mapValue.id : this.index++; } case TermType.Literal: { const mapValue = this.literalMap[mapId]; return mapValue ? mapValue.id : this.index++; } default: return -1; } } mapId(term) { if (this.isQuad(term)) { return `${this.id(term.subject)},${this.id(term.predicate)},${this.id(term.object)},${(term.graph ? this.id(term.graph) : 0)}`; } switch (term.termType) { case TermType.BlankNode: case TermType.NamedNode: return term.value; case TermType.Literal: { return `${term.value},${term.language},${term.datatype.value}`; } default: return undefined; } } } MemoizedHashFactory.FactorySupport = { [Feature.collections]: false, [Feature.defaultGraphType]: false, [Feature.equalsMethod]: false, [Feature.id]: true, [Feature.idStamp]: true, [Feature.identity]: true, [Feature.reversibleId]: true, [Feature.variableType]: false, }; var index = new MemoizedHashFactory(); export default index; export { MemoizedHashFactory }; //# sourceMappingURL=index.js.map