jsonld-streaming-parser
Version:
A fast and lightweight streaming JSON-LD parser
128 lines • 6.85 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.EntryHandlerArrayValue = void 0;
const Util_1 = require("../Util");
const jsonld_context_parser_1 = require("jsonld-context-parser");
/**
* Handles values that are part of an array.
*/
class EntryHandlerArrayValue {
isPropertyHandler() {
return false;
}
isStackProcessor() {
return true;
}
async validate(parsingContext, util, keys, depth, inProperty) {
return this.test(parsingContext, util, null, keys, depth);
}
async test(parsingContext, util, key, keys, depth) {
return typeof keys[depth] === 'number';
}
async handle(parsingContext, util, key, keys, value, depth) {
let parentKey = await util.unaliasKeywordParent(keys, depth);
// Check if we have an anonymous list
if (parentKey === '@list') {
// Our value is part of an array
// Determine the list root key
let listRootKey = null;
let listRootDepth = 0;
for (let i = depth - 2; i > 0; i--) {
const keyOption = keys[i];
if (typeof keyOption === 'string' || typeof keyOption === 'number') {
listRootDepth = i;
listRootKey = keyOption;
break;
}
}
if (listRootKey !== null) {
// Emit the given objects as list elements
const values = await util.valueToTerm(await parsingContext.getContext(keys), listRootKey, value, depth, keys);
for (const object of values) {
await this.handleListElement(parsingContext, util, object, value, depth, keys.slice(0, listRootDepth), listRootDepth);
}
// If no values were found, emit a falsy list element to force an empty RDF list to be emitted.
if (values.length === 0) {
await this.handleListElement(parsingContext, util, null, value, depth, keys.slice(0, listRootDepth), listRootDepth);
}
}
}
else if (parentKey === '@set') {
// Our value is part of a set, so we just add it to the parent-parent
await parsingContext.newOnValueJob(keys.slice(0, -2), value, depth - 2, false);
}
else if (parentKey !== undefined && parentKey !== '@type') {
// Buffer our value using the parent key as predicate
// Determine the first parent key that is *not* an array key
// This is needed in case we have an @list container with nested arrays,
// where each of them should produce nested RDF lists.
for (let i = depth - 1; i > 0; i--) {
if (typeof keys[i] !== 'number') {
parentKey = await util.unaliasKeyword(keys[i], keys, i);
break;
}
}
// Check if the predicate is marked as an @list in the context
const parentContext = await parsingContext.getContext(keys.slice(0, -1));
if ('@list' in Util_1.Util.getContextValueContainer(parentContext, parentKey)) {
// Our value is part of an array
// Emit the given objects as list elements
parsingContext.emittedStack[depth + 1] = true; // Ensure the creation of bnodes for empty nodes
const values = await util.valueToTerm(await parsingContext.getContext(keys), parentKey, value, depth, keys);
for (const object of values) {
await this.handleListElement(parsingContext, util, object, value, depth, keys.slice(0, -1), depth - 1);
}
// If no values were found, emit a falsy list element to force an empty RDF list to be emitted.
if (values.length === 0) {
await this.handleListElement(parsingContext, util, null, value, depth, keys.slice(0, -1), depth - 1);
}
}
else {
// Copy the stack values up one level so that the next job can access them.
parsingContext.shiftStack(depth, 1);
// Execute the job one level higher
await parsingContext.newOnValueJob(keys.slice(0, -1), value, depth - 1, false);
// Remove any defined contexts at this level to avoid it to propagate to the next array element.
parsingContext.contextTree.removeContext(keys.slice(0, -1));
}
}
}
async handleListElement(parsingContext, util, value, valueOriginal, depth, listRootKeys, listRootDepth) {
// Buffer our value as an RDF list using the listRootKey as predicate
let listPointer = parsingContext.listPointerStack[depth];
if (valueOriginal !== null && (await util.unaliasKeywords(valueOriginal, listRootKeys, depth))['@value'] !== null) {
if (!listPointer || !listPointer.value) {
const linkTerm = util.dataFactory.blankNode();
listPointer = { value: linkTerm, listRootDepth, listId: linkTerm };
}
else {
// rdf:rest links are always emitted before the next element,
// as the blank node identifier is only created at that point.
// Because of this reason, the final rdf:nil is emitted when the stack depth is decreased.
const newLinkTerm = util.dataFactory.blankNode();
parsingContext.emitQuad(depth, util.dataFactory.quad(listPointer.value, util.rdfRest, newLinkTerm, util.getDefaultGraph()));
// Update the list pointer for the next element
listPointer.value = newLinkTerm;
}
// Emit a list element for the current value
// Omit rdf:first if the value is invalid
if (value) {
parsingContext.emitQuad(depth, util.dataFactory.quad(listPointer.value, util.rdfFirst, value, util.getDefaultGraph()));
}
}
else {
// A falsy list element if found.
// Mark it as an rdf:nil list until another valid list element comes in
if (!listPointer) {
listPointer = { listRootDepth, listId: util.rdfNil };
}
}
parsingContext.listPointerStack[depth] = listPointer;
// Error if an annotation was defined
if (parsingContext.rdfstar && parsingContext.annotationsBuffer[depth]) {
parsingContext.emitError(new jsonld_context_parser_1.ErrorCoded(`Found an illegal annotation inside a list`, jsonld_context_parser_1.ERROR_CODES.INVALID_ANNOTATION));
}
}
}
exports.EntryHandlerArrayValue = EntryHandlerArrayValue;
//# sourceMappingURL=EntryHandlerArrayValue.js.map
;