From aadbd3131d99f0af4b0f1b7264eede2644e7e3ac Mon Sep 17 00:00:00 2001 From: Bogdan Kostov Date: Fri, 22 Mar 2024 15:33:51 +0100 Subject: [PATCH] [FIX #215] Rewrite references resolution algorithm so that it does not depend on property processing order --- src/utils/JsonLdUtils.tsx | 67 ++++++++++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 19 deletions(-) diff --git a/src/utils/JsonLdUtils.tsx b/src/utils/JsonLdUtils.tsx index ce61a77d..ba7f26e5 100644 --- a/src/utils/JsonLdUtils.tsx +++ b/src/utils/JsonLdUtils.tsx @@ -19,7 +19,8 @@ export default class JsonLdUtils { input: JsonLdInput, context: JsonLdContext, ): Promise { - return compact(input, context).then((res) => JsonLdUtils.resolveReferences(res)); + let map = new Map(); + return compact(input, context).then((res) => JsonLdUtils.resolveReferences(res, map)); } /** @@ -65,38 +66,56 @@ export default class JsonLdUtils { * Replaces JSON-LD references to nodes (i.e., nodes with a single attribute - iri) with existing nodes encountered * in the specified input. * @param input JSON-LD compaction result to be processed + * @param idMap Map of already processed nodes (id -> node) to replace references with. Optional */ - public static resolveReferences(input: JsonLdDictionary): T { - const idMap = new Map(); + public static resolveReferences( + input: JsonLdDictionary, + idMap: Map = new Map(), + ): T { JsonLdUtils.processNode(input, idMap); + // replace references + idMap.forEach((r) => { + r.references.forEach((callback) => callback(r.entity)); + }); return input as T; } - private static processNode(node: object, idMap: Map) { + /** + * Finds all entities and associates them with callbacks to replace references + * @param node + * @param idMap + * @private + */ + private static processNode(node: object, idMap: Map) { if (!node.hasOwnProperty("iri")) { return; } // @ts-ignore - idMap.set(node.iri, node); + const nodeReferenced = JsonLdUtils.getReferenced(node.iri, idMap); + nodeReferenced.entity = node; Object.getOwnPropertyNames(node) .sort() .forEach((p) => { const val = node[p]; if (Array.isArray(val)) { for (let i = 0, len = val.length; i < len; i++) { - if (typeof val[i] === "object") { - const reference = JsonLdUtils.getReferencedNodeIfExists(val[i], idMap); - if (reference) { - val[i] = reference; + if (typeof val[i] === "object" && val[i].hasOwnProperty("iri")) { + const referenced = JsonLdUtils.getReferenced(val[i].iri, idMap); + if (JsonLdUtils.isReference(val[i])) { + referenced.references.push((o) => { + val[i] = o; + }); } else { JsonLdUtils.processNode(val[i], idMap); } } } - } else if (typeof val === "object") { - const reference = JsonLdUtils.getReferencedNodeIfExists(val, idMap); - if (reference) { - node[p] = reference; + } else if (typeof val === "object" && val.hasOwnProperty("iri")) { + const referenced = JsonLdUtils.getReferenced(val.iri, idMap); + if (JsonLdUtils.isReference(val)) { + referenced.references.push((o) => { + node[p] = o; + }); } else { JsonLdUtils.processNode(val, idMap); } @@ -104,13 +123,18 @@ export default class JsonLdUtils { }); } - private static getReferencedNodeIfExists(node: any, idMap: Map): object | undefined { - const valProps = Object.getOwnPropertyNames(node); - if (valProps.length === 1 && valProps[0] === "iri" && idMap.has(node.iri)) { - return idMap.get(node.iri); - } else { - return undefined; + private static getReferenced(iri: string, idMap: Map): Referenced { + let referenced = idMap.get(iri); + if (!referenced) { + referenced = { entity: null, references: [] }; + idMap.set(iri, referenced); } + return referenced; + } + + private static isReference(node: any) { + const valProps = Object.getOwnPropertyNames(node); + return valProps.length === 1 && valProps[0] === "iri"; } /** @@ -120,3 +144,8 @@ export default class JsonLdUtils { return "_:" + Math.random().toString(36).substring(8); } } + +interface Referenced { + entity: object; + references: Array<(val: object) => void>; +}