@ontola/memoized-factory
Version:
RDF Factory with neat memory usage and good cpu performance.
330 lines (269 loc) • 8.1 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var core = require('@ontologies/core');
function _defineProperty(obj, key, value) {
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
if (enumerableOnly) symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
});
keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread2(target) {
for (var i = 1; i < arguments.length; i++) {
var source = arguments[i] != null ? arguments[i] : {};
if (i % 2) {
ownKeys(Object(source), true).forEach(function (key) {
_defineProperty(target, key, source[key]);
});
} else if (Object.getOwnPropertyDescriptors) {
Object.defineProperties(target, Object.getOwnPropertyDescriptors(source));
} else {
ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
}
return target;
}
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 core.PlainFactory {
constructor(opts = {}) {
super(_objectSpread2({
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 = core.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 = core.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 = core.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 core.TermType.BlankNode:
{
const mapValue = this.blankNodeMap[mapId];
return mapValue ? mapValue.id : this.index++;
}
case core.TermType.NamedNode:
{
const mapValue = this.namedNodeMap[mapId];
return mapValue ? mapValue.id : this.index++;
}
case core.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 core.TermType.BlankNode:
case core.TermType.NamedNode:
return term.value;
case core.TermType.Literal:
{
return `${term.value},${term.language},${term.datatype.value}`;
}
default:
return undefined;
}
}
}
MemoizedHashFactory.FactorySupport = {
[core.Feature.collections]: false,
[core.Feature.defaultGraphType]: false,
[core.Feature.equalsMethod]: false,
[core.Feature.id]: true,
[core.Feature.idStamp]: true,
[core.Feature.identity]: true,
[core.Feature.reversibleId]: true,
[core.Feature.variableType]: false
};
var index = new MemoizedHashFactory();
exports.MemoizedHashFactory = MemoizedHashFactory;
exports.default = index;
//# sourceMappingURL=index.js.map