const Stream = require("stream"); //class NamedNode { // constructor(value) { // this.termType = "NamedNode"; // this.value = value; // } // equals(term) { // if (typeof term === "undefined" || term === null) { // return false; // } // if (term.termType !== this.termType) { // return false; // } // return term.value === this.value; // } //}; //class BlankNode { // constructor(value) { // this.termType = "BlankNode"; // this.value = value; // } // equals(term) { // if (typeof term === "undefined" || term === null) { // return false; // } // if (term.termType !== this.termType) { // return false; // } // return term.value === this.value; // } //}; //class Literal { // constructor(value, language, dataType) { // this.termType = "Literal"; // this.value = value; // this.language = language; // this.dataType = dataType; // } // equals(term) { // if (typeof term === "undefined" || term === null) { // return false; // } // if (term.termType !== this.termType) { // return false; // } // if (term.value !== this.value) { // return false; // } // if (term.language !== this.language) { // return false; // } // return term.dataType.equals(this.dataType); // } //}; //class Variable { // constructor(value) { // this.termType = "Variable"; // this.value = value; // } // equals(term) { // if (typeof term === "undefined" || term === null) { // return false; // } // if (term.termType !== this.termType) { // return false; // } // return term.value === this.value; // } //}; //class DefaultGraph { // constructor() { // this.termType = "DefaultGraph"; // this.value = ""; // } // equals(term) { // if (typeof term === "undefined" || term === null) { // return false; // } // return term.termType === this.termType; // } //}; //function nodeUnlink(prev, next) { // //if next is the same as prev, we are the last link in the list // if (this.prev === this.next) { // this.prev.cleanup(); // } // prev.next = next; // next.prev = prev; // prev = null; // next = null; //} //class ListNode { // constructor() { // this.next = null; // this.prev = null; // this.counter = null; // } // addAfter(node, counter) { // this.next = node.next; // this.prev = node; // node.next = this; // this.next.prev = this; // this.counter = counter; // } //} //class ListIterator { // constructor(parent) { // this.parent = parent; // this.on = this.parent.next; // } // next() { // if (this.on === this.parent) { // return { // done: true // }; // } // const returning = this.on; // this.on = this.on.next; // } //} //class ListHead { // constructor(key, parentMap) { // this.next = this; // this.prev = this; // this.parent = parentMap; // this.key = key; // } // cleanup() { // this.parent.delete(this.key); // } // *[Symbol.iterator]() { // yield 1; // yield 2; // yield 3; // } //} //class Quad { // constructor(subject, predicate, _object, graph) { // this.termType = "Quad"; // this.value = ""; // this.subject = subject; // this.predicate = predicate; // this._object = _object; // this.graph = graph; // this.subjectNext = null; // this.subjectPrev = null; // this.predicateNext = null; // this.predicatePrev = null; // this.objectNext = null; // this.objectPrev = null; // this.graphNext = null; // this.graphPrev = null; // this.globalNext = null; // this.globalPrev = null; // } // equals(term) { // if (typeof term === "undefined" || term === null) { // return false; // } // if (term.termType !== this.termType) { // return false; // } // if (!term.subject.equals(this.subject)) { // return false; // } // if (!term.predicate.equals(this.predicate)) { // return false; // } // if (!term._object.equals(this._object)) { // return false; // } // if (!term.graph.equals(this.graph)) { // return false; // } // return true; // } // unlink() { // nodeUnlink(this.subjectPrev, this.subjectNext); // nodeUnlink(this.predicatePrev, this.predicateNext); // nodeUnlink(this.objectPrev, this.objectNext); // nodeUnlink(this.graphPrev, this.graphNext); // } //}; //class DataFactory { // constructor() { // this.blankCounter = 0; // } // namedNode(value) { // return NamedNode(value); // } // blankNode(value) { // if (typeof value === "undefined") { // value = "blank" + this.blankCounter.toString(); // this.blankCounter++; // } // return new BlankNode(value); // } // literal(value, languageOrDataType) { // if (languageOrDataType instanceof NamedNode) { // return new Literal(value, "", languageOrDataType); // } else { // return new Literal(value, languageOrDataType, // this.namedNode("http://www.w3.org/1999/02/22-rdf-syntax-ns#langString")); // } // } // variable(value) { // return new Variable(value); // } // defaultGraph() { // return new DefaultGraph(); // } // quad(subject, predicate, _object, graph) { // if (typeof graph === "undefined" || graph === null) { // return new Quad(subject, predicate, _object, new DefaultGraph()); // } else { // return new Quad(subject, predicate, _object, graph); // } // } // fromTerm(term) { // switch (term.termType) { // case "NamedNode": // return this.namedNode(term.value); // case "BlankNode": // return this.blankNode(term.value); // case "Literal": // return new Literal(term.value, term.language, this.fromTerm(term.dataType)); // case "Variable": // return this.variable(term.value); // case "DefaultGraph": // return this.defaultGraph(); // case "Quad": // return this.fromQuad(term); // default: // throw new Error("Unknown term"); // } // fromQuad(quad) { // return this.quad( // this.fromTerm(term.subject), // this.fromTerm(term.predicate), // this.fromTerm(term._object), // this.fromTerm(term.graph)); // } // } //}; function addQuadToMap(counter, map, key, quad, toPop) { let quadMap = null; let popper = null; if (toPop.has(key)) { popper = toPop.get(key); } else { popper = []; toPop.set(key, popper); } if (map.has(key)) { quadMap = map.get(key); } else { quadMap = new Map(); map.set(key, quadMap); } popper.push(counter); quadMap.set(counter, quad); } function popFromSource(list, map) { for (const [key, popper] of list) { const innerMap = map.get(key); if (popper.length === innerMap.size) { map.delete(key) } else { for (const counter of popper) { innerMap.delete(counter); } } } } function cloneTermMap(from, to) { for (const [key, map] of from) { const adding = new Map(); for (const [counter, quad] of map) { adding.set(counter, quad); } to.set(key, adding); } } class Source { constructor() { this.subjects = new Map(); this.predicates = new Map(); this.objects = new Map(); this.graphs = new Map(); this.all = []; this.popping = []; this.counter = 0; } startPush() { this.popping.push({ subjects: new Map(), predicates: new Map(), objects: new Map(), graphs: new Map(), count: 0 }); } push(quad) { const toPop = this.popping[this.popping.length - 1]; addQuadToMap(this.counter, this.subjects, quad.subject.value, quad, toPop.subjects); addQuadToMap(this.counter, this.predicates, quad.predicate.value, quad, toPop.predicates); addQuadToMap(this.counter, this.objects, quad._object.value, quad, toPop.objects); addQuadToMap(this.counter, this.graphs, quad.graph.value, quad, toPop.graphs); this.all.push(quad); toPop.count++; this.counter++; } pop() { if (this.popping.length === 0) { throw new Error("Nothing to pop"); } const toPop = this.popping.pop(); this.all.slice(0, -toPop.count); popFromSource(toPop.subjects, this.subjects); popFromSource(toPop.predicates, this.predicates); popFromSource(toPop.objects, this.objects); popFromSource(toPop.graphs, this.graphs); } //as we always insert at the front of the list, elements are sorted by descending insertion time, //which means by descending counter //we can then walk through each list of found nodes, only stopping on equal counters match(subject, predicate, _object, graph) { const maps = []; if (typeof subject !== "undefined" && subject !== null) { if (this.subjects.has(subject.value)) { maps.push(this.subjects.get(subject.value)); } else { return Stream.Readable.from([]); } } if (typeof predicate !== "undefined" && predicate !== null) { if (this.predicates.has(predicate.value)) { maps.push(this.predicates.get(predicate.value)); } else { return Stream.Readable.from([]); } } if (typeof _object !== "undefined" && _object !== null) { if (this.objects.has(_object.value)) { maps.push(this.objects.get(_object.value)); } else { return Stream.Readable.from([]); } } if (typeof graph !== "undefined" && graph !== null) { if (this.graphs.has(graph.value)) { maps.push(this.graphs.get(graph.value)); } else { return Stream.Readable.from([]); } } if (maps.length === 0) { return Stream.Readable.from(this.all); } const working = []; for (const [counter, quad] of maps[0]) { working.push({ counter: counter, quad: quad }); } for (let i = 1; i < maps.length; i++) { for (let j = 0; j < working.length;) { if (!maps[i].has(working[j].counter)) { working[j] = working[working.length - 1]; working.pop(); } else { j++ } } } return Stream.Readable.from(working.map(work => work.quad)); } clone() { const returning = new Source(); cloneTermMap(this.subjects, returning.subjects); cloneTermMap(this.predicates, returning.predicates); cloneTermMap(this.objects, returning.objects); cloneTermMap(this.graphs, returning.graphs); this.all.forEach(item => returning.all.push(item)); this.popping.forEach(item => returning.popping.push(item)); returning.counter = this.counter; return returning; } pushInto(parent) { let on = 0; for (const toPop of this.popping) { parent.startPush(); for (const quad of this.all.slice(on, on + toPop.count)) { parent.push(quad); } on += toPop.count; } } }; module.exports = Source;