UNPKG

@eagleoutice/flowr

Version:

Static Dataflow Analyzer and Program Slicer for the R Programming Language

194 lines 8.01 kB
"use strict"; /** * As other project partners want the produced data structures as rdf quads, this module provides * serialization capabilities for this purpose. * <p> * At the time of writing this module I am unaware of any sophisticated rdf library for typescript which allows to serialize objects * directly as rdf quads. Therefore, this module provides a simple serialization mechanism based on the popular n3.js library. * @module */ Object.defineProperty(exports, "__esModule", { value: true }); exports.DefaultQuadSerializationConfiguration = void 0; exports.defaultQuadIdGenerator = defaultQuadIdGenerator; exports.defaultQuadIgnoreIf = defaultQuadIgnoreIf; exports.serialize2quads = serialize2quads; exports.graph2quads = graph2quads; const n3_1 = require("n3"); const namedNode = (v) => n3_1.DataFactory.namedNode(v); const quad = (s, p, o, g) => n3_1.DataFactory.quad(s, p, o, g); const objects_1 = require("./objects"); const assert_1 = require("./assert"); const defaultmap_1 = require("./collections/defaultmap"); const literal = (v, n) => n3_1.DataFactory.literal(v, n); const log_1 = require("./log"); const domain = 'https://uni-ulm.de/r-ast/'; /** * A deterministic counting id generator for quads. */ function defaultQuadIdGenerator() { let counter = 0; const idMap = new defaultmap_1.DefaultMap(() => counter++); return (elem, context) => `${context}/${idMap.get(elem)}`; } const ignoredKeysArray = ['complexNumber', 'markedAsInt', 'info']; /** * A default ignore function that ignores undefined values and some common keys. */ function defaultQuadIgnoreIf() { return (key, value) => value === undefined || ignoredKeysArray.includes(key); } exports.DefaultQuadSerializationConfiguration = { ignore: defaultQuadIgnoreIf(), context: 'unknown-context', getId: defaultQuadIdGenerator(), domain: 'https://uni-ulm.de/r-ast/' }; function retrieveContext(context, obj) { return typeof context === 'string' ? context : context(obj); } const writer = new n3_1.Writer({ format: 'N-Quads' }); /** * Serializes the given object or array to rdf quads. * @param obj - The object to serialize (must be a Record and no array etc.) * @param config - Further configuration options * @returns the serialized quads * @see graph2quads */ function serialize2quads(obj, config) { const useConfig = (0, objects_1.deepMergeObject)(exports.DefaultQuadSerializationConfiguration, config); (0, assert_1.guard)((0, objects_1.isObjectOrArray)(obj), 'cannot serialize non-object to rdf!'); (0, assert_1.guard)(!Array.isArray(obj), 'cannot serialize arrays (must wrap in object)!'); store = new Set(); const quads = []; serializeObject(obj, quads, useConfig); return writer.quadsToString(quads); } /** * Serializes the given directed graph to rdf quads. * This is a mere (type-)convenience wrapper for {@link serialize2quads}. * @see serialize2quads */ function graph2quads(graph, config) { return serialize2quads(graph, config); } function processArrayEntries(key, values, obj, quads, config) { for (const [index, element] of values.entries()) { if (element !== null && element !== undefined && (0, objects_1.isObjectOrArray)(element)) { const context = retrieveContext(config.context, obj); quads.push(quad(namedNode(domain + config.getId(obj, context)), namedNode(domain + key), namedNode(domain + config.getId(element, context)), namedNode(context))); // we now add a next link to the next vertex const next = values[index + 1]; if (next !== undefined) { const nextId = config.getId(next, context); quads.push(quad(namedNode(domain + config.getId(element, context)), namedNode(domain + 'next'), namedNode(domain + nextId), namedNode(context))); } serializeObject(element, quads, config); } else { // for the time being, the index does not seem of interest for the graph summary team. processLiteralEntry(element, key, obj, quads, config, undefined); } } } function processObjectEntries(key, value, obj, quads, config) { const context = retrieveContext(config.context, obj); quads.push(quad(namedNode(domain + config.getId(obj, context)), namedNode(domain + key), namedNode(domain + config.getId(value, context)), namedNode(context))); serializeObject(value, quads, config); } function objToType(value) { let suffix; switch (typeof value) { case 'string': suffix = 'string'; break; case 'number': suffix = Number.isInteger(value) ? 'integer' : 'decimal'; break; case 'boolean': suffix = 'boolean'; break; case 'bigint': suffix = 'integer'; break; default: log_1.log.warn(`unknown ${typeof value} with ${JSON.stringify(value)}`); break; } return suffix ? namedNode(`http://www.w3.org/2001/XMLSchema#${suffix}`) : undefined; } function processLiteralEntry(value, key, obj, quads, config, index) { const context = retrieveContext(config.context, obj); // we have to create an object if there is an index, so that we can link the index! if (index !== undefined) { const newId = `${key}-${index}`; quads.push(quad(namedNode(domain + config.getId(obj, context)), namedNode(domain + key), namedNode(domain + config.getId(newId, context)), namedNode(context))); quads.push(quad(namedNode(domain + config.getId(newId, context)), namedNode(domain + 'value'), literal(String(value), objToType(value)), namedNode(context))); quads.push(quad(namedNode(domain + newId), namedNode(domain + 'order'), literal(String(index), namedNode('http://www.w3.org/2001/XMLSchema#integer')), namedNode(context))); } else { quads.push(quad(namedNode(domain + config.getId(obj, context)), namedNode(domain + key), literal(String(value), objToType(value)), namedNode(context))); } } function processObjectEntry(key, value, obj, quads, config) { if (config.ignore(key, value) || guardCycle(value)) { return; } if ((0, objects_1.isObjectOrArray)(value)) { if (Array.isArray(value)) { processArrayEntries(key, value, obj, quads, config); } else if (value instanceof Map) { for (const [mapKey, mapValue] of value.entries()) { processObjectEntry('key-' + String(mapKey), mapValue, obj, quads, config); } } else if (value instanceof Set) { let i = 0; for (const setValue of value.values()) { processObjectEntry('idx-' + String(i++), setValue, obj, quads, config); } } else { processObjectEntries(key, value, obj, quads, config); } } else { processLiteralEntry(value, key, obj, quads, config, undefined); } } let store = new Set(); function guardCycle(obj) { // @ts-expect-error we do not care about the type here if ((0, objects_1.isObjectOrArray)(obj) && 'id' in obj) { if (store.has(obj)) { return true; } store.add(obj); } return false; } function serializeObject(obj, quads, config) { if (obj === null || obj === undefined) { return; } if (guardCycle(obj)) { return; } if (obj instanceof Map) { for (const [key, value] of obj.entries()) { processObjectEntry('key-' + String(key), value, obj, quads, config); } } else if (obj instanceof Set) { let i = 0; for (const value of obj.values()) { processObjectEntry('idx-' + String(i++), value, obj, quads, config); } } else { for (const [key, value] of Object.entries(obj)) { processObjectEntry(key, value, obj, quads, config); } } } //# sourceMappingURL=quads.js.map