Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 69 additions & 1 deletion lib/abstract-generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -777,12 +777,80 @@ class AbstractGenerator {
}
}

static _replaceTemplates(input, wrapperStart = '{', wrapperEnd = '}') {
let out = '';
let i = 0;

while (i < input.length) {
if (input[i] === '$' && input[i + 1] === '(') {
i += 2;
const contentStart = i;
let depth = 1;
let buf = '';
let quote = null;

while (i < input.length && depth > 0) {
const ch = input[i];

if (quote) {
if (ch === quote && !AbstractGenerator._isEscaped(input, i)) {
quote = null;
}
buf += ch;
} else if (ch === '"' || ch === "'") {
quote = ch;
buf += ch;
} else if (ch === '(') {
depth++;
buf += ch;
} else if (ch === ')') {
depth--;
if (depth > 0) {
buf += ch;
}
} else {
buf += ch;
}

i++;
}

if (depth > 0) {
const fallbackClose = input.indexOf(')', contentStart);
if (fallbackClose !== -1) {
const fallbackContent = input.slice(contentStart, fallbackClose);
out += wrapperStart + fallbackContent + wrapperEnd;
i = fallbackClose + 1;
continue;
}
out += '$(' + input.slice(contentStart);
break;
}

out += wrapperStart + buf + wrapperEnd;
} else {
out += input[i];
i++;
}
}

return out;
}

static _isEscaped(input, index) {
let count = 0;
for (let i = index - 1; i >= 0 && input[i] === '\\'; i--) {
count++;
}
return count % 2 === 1;
}

static parseTemplate(t) {
t = '' + t; // Make sure it's a string.
t = t.replace(/\\\\/g, '@@@BACKWARD-SLASH@@@'); // We want to preserve real backward slashes.
t = t.replace(/\\\(/g, '@@@BRACKET-OPEN@@@'); // Same for opening brackets.
t = t.replace(/\\\)/g, '@@@BRACKET-CLOSE@@@'); // Same for closing brackets.
t = t.replace(/\$\(([^)]*)\)/g, "{$1}");
t = AbstractGenerator._replaceTemplates(t);
t = t.replace(/@@@BRACKET-CLOSE@@@/g, ')');
t = t.replace(/@@@BRACKET-OPEN@@@/g, '(');
t = t.replace(/@@@BACKWARD-SLASH@@@/g, '/');
Expand Down
70 changes: 69 additions & 1 deletion lib/expander.js
Original file line number Diff line number Diff line change
Expand Up @@ -1145,12 +1145,80 @@ function _toWatchedPropertiesString(watchedPropertiesArray) {
}

// TODO: Almost literally copied from abstract-generator.js. Is there an elegant way of re-using that function?
function _isEscaped(input, index) {
let count = 0;
for (let i = index - 1; i >= 0 && input[i] === '\\'; i--) {
count++;
}
return count % 2 === 1;
}

function _replaceTemplates(input) {
let out = '';
let i = 0;

while (i < input.length) {
if (input[i] === '$' && input[i + 1] === '(') {
i += 2;
const contentStart = i;
let depth = 1;
let buf = '';
let quote = null;

while (i < input.length && depth > 0) {
const ch = input[i];

if (quote) {
if (ch === quote && !_isEscaped(input, i)) {
quote = null;
}
buf += ch;
} else if (ch === '"' || ch === "'") {
quote = ch;
buf += ch;
} else if (ch === '(') {
depth++;
buf += ch;
} else if (ch === ')') {
depth--;
if (depth > 0) {
buf += ch;
}
} else {
buf += ch;
}

i++;
}

if (depth > 0) {
const fallbackClose = input.indexOf(')', contentStart);
if (fallbackClose !== -1) {
const fallbackContent = input.slice(contentStart, fallbackClose);
out += fallbackContent;
i = fallbackClose + 1;
continue;
}
out += '$(' + input.slice(contentStart);
break;
}

out += buf;
} else {
out += input[i];
i++;
}
}

return out;
}

function _parseTemplate(t) {
t = '' + t; // Make sure it's a string.
t = t.replace(/\\\\/g, '@@@BACKWARD-SLASH@@@'); // We want to preserve real backward slashes.
t = t.replace(/\\\(/g, '@@@BRACKET-OPEN@@@'); // Same for opening brackets.
t = t.replace(/\\\)/g, '@@@BRACKET-CLOSE@@@'); // Same for closing brackets.
t = t.replace(/\$\(([^)]*)\)/g, "$1");
t = _replaceTemplates(t);
t = t.replace(/@@@BRACKET-CLOSE@@@/g, ')');
t = t.replace(/@@@BRACKET-OPEN@@@/g, '(');
t = t.replace(/@@@BACKWARD-SLASH@@@/g, '/');
Expand Down
4 changes: 4 additions & 0 deletions lib/rml-generator.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ describe('YARRRML to RML', function () {
work('escape-bracket/mapping.yarrrml', 'escape-bracket/mapping.rml.ttl', done, {includeMetadata: false});
});

it('xpath contains template', function (done) {
work('xpath-contains-template/mapping.yml', 'xpath-contains-template/mapping.rml.ttl', done, {includeMetadata: false});
});

it('object is number', function (done) {
work('object-number/mapping.yarrrml', 'object-number/mapping.rml.ttl', done, {includeMetadata: false});
});
Expand Down
35 changes: 35 additions & 0 deletions test/xpath-contains-template/mapping.rml.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
@prefix rr: <http://www.w3.org/ns/r2rml#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix fnml: <http://semweb.mmlab.be/ns/fnml#> .
@prefix fno: <https://w3id.org/function/ontology#> .
@prefix d2rq: <http://www.wiwiss.fu-berlin.de/suhl/bizer/D2RQ/0.1#> .
@prefix rml: <http://semweb.mmlab.be/ns/rml#> .
@prefix ql: <http://semweb.mmlab.be/ns/ql#> .
@prefix : <http://mapping.example.com/> .
@prefix ex: <http://example.com/> .

:map_nodes_000 rml:logicalSource :source_000 ;
rdf:type rr:TriplesMap ;
rdfs:label "nodes" ;
rr:subjectMap :s_000 ;
rr:predicateObjectMap :pom_000 .

:source_000 rdf:type rml:LogicalSource ;
rml:source "track_metadata.xml" ;
rml:iterator "/osm/node" ;
rml:referenceFormulation ql:XPath .

:s_000 rdf:type rr:SubjectMap ;
rr:template "http://example.com/node/{@id}" .

:pom_000 rdf:type rr:PredicateObjectMap ;
rr:predicateMap :pm_000 ;
rr:objectMap :om_000 .

:pm_000 rdf:type rr:PredicateMap ;
rr:constant ex:positiveNotes .

:om_000 rdf:type rr:ObjectMap ;
rml:reference "/html/body//h3[contains(., \"Pros\")]/following::ul[1]/li/span" ;
rr:termType rr:Literal .
10 changes: 10 additions & 0 deletions test/xpath-contains-template/mapping.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
prefixes:
ex: "http://example.com/"

mappings:
nodes:
sources:
- [track_metadata.xml~xpath, /osm/node]
s: "ex:node/$(@id)"
po:
- [ex:positiveNotes, '$(/html/body//h3[contains(., "Pros")]/following::ul[1]/li/span)']