UNPKG

@mnakhla/rocketrml

Version:

This is a forked rocketrml mapper for the RDF mapping language

145 lines (135 loc) 4.47 kB
const N3 = require('n3'); const jsonld = require('jsonld'); const helper = require('../input-parser/helper'); const prefixHelper = require('../helper/prefixHelper'); const { jsonLDGraphToObj } = require('../helper/replace'); const quadsToJsonLD = async (nquads) => { let doc = await jsonld.fromRDF(nquads, { format: 'application/n-quads' }); doc = await jsonld.compact(doc, {}); return doc; }; const ttlToJson = (ttl) => new Promise((resolve, reject) => { const parser = new N3.Parser({ baseIRI: 'http://base.com/' }); const writer = new N3.Writer({ format: 'N-Triples' }); ttl = helper.escapeChar(ttl); parser.parse(ttl, (error, quad, prefixes) => { if (error) { reject(error); } else if (quad) { writer.addQuad(quad); } else { writer.end(async (writeError, result) => { if (writeError) { reject(writeError); return; } try { const json = await quadsToJsonLD(result); resolve([json, prefixes]); } catch (e) { reject(e); } }); } }); }); function hasLogicalSource(e) { return Object.keys(e).find((x) => x.match(/.*logicalSource/)); } function hasSubjectMap(e) { return Object.keys(e).find((x) => x.match(/.*subjectMap/)); } function isFunction(e) { if (e.predicateObjectMap) { const predicateObjectMap = helper.addArray(e.predicateObjectMap); for (const obj of predicateObjectMap) { if ( obj.predicate && obj.predicate['@id'] && obj.predicate['@id'].indexOf('executes') !== -1 ) { return true; } if (obj.predicateMap && obj.predicateMap && obj.predicateMap['@id']) { const predMap = obj.predicateMap; if ( predMap && predMap.constant && predMap.constant['@id'] && predMap.constant['@id'].indexOf('executes') !== -1 ) { return true; } } } } return false; } const getTopLevelMappings = (graphArray) => { const toplevelMappings = []; if (!graphArray || !graphArray.length) { // graphArray is not an array throw 'Error during processing mapfile: Wrong shape!'; } graphArray.forEach((e) => { const id = e['@id']; if (hasLogicalSource(e) && !isFunction(e)) { if (!hasSubjectMap(e)) { throw `${id} is missing a subjectMap!`; } toplevelMappings.push(id); } }); if (graphArray.length === 0) { // mapfile does not contain any toplevel mappings throw 'getTopLevelMappings(): Error during processing mapfile: no toplevel found! (only blank nodes)'; } return toplevelMappings; }; const bNodeIssuer = (prefix) => { let counter = 0; return () => `_:${prefix}${++counter}`; }; const replaceConstantShortProps = (graph) => { const issuer = bNodeIssuer('re'); const newNodes = []; for (const i in graph) { // even if we don't support graph ['subject', 'predicate', 'object', 'graph', 'language'].forEach((prop) => { if (graph[i][prop]) { const bNodeId = issuer(); // create new blank nodes, as to not mess with json-ld structure (will be replaced in next step - jsonLDGraphToObj) newNodes.push({ '@id': bNodeId, constant: graph[i][prop] }); graph[i][`${prop}Map`] = { '@id': bNodeId }; delete graph[i][prop]; } }); } graph.push(...newNodes); }; // returns object with prefixes, graph, and all top-level mappings const expandedJsonMap = async (ttl) => { const [response, prefixes] = await ttlToJson(ttl); const result = {}; result.prefixes = prefixes; const regex = /@base <(.*)>/; let base = '_:'; if (ttl.match(regex) && ttl.match(regex)[1]) { base = ttl.match(regex)[1]; } if (!result.prefixes) { result.prefixes = {}; } result.prefixes.base = base; const prefixFreeGraph = response['@graph'].map((node) => prefixHelper.checkAndRemovePrefixesFromObject(node, result.prefixes), ); replaceConstantShortProps(prefixFreeGraph); const connectedGraph = jsonLDGraphToObj(prefixFreeGraph); result.data = connectedGraph; result.topLevelMappings = getTopLevelMappings(result.data); return result; }; module.exports.ttlToJson = ttlToJson; module.exports.expandedJsonMap = expandedJsonMap;