Skip to content

Commit 4dba7f4

Browse files
committed
Review RDF* support.
1 parent b9f1279 commit 4dba7f4

File tree

2 files changed

+37
-29
lines changed

2 files changed

+37
-29
lines changed

src/N3DataFactory.js

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ const { rdf, xsd } = namespaces;
77

88
let DEFAULTGRAPH;
99
let _blankNodeCounter = 0;
10-
let quotedMatchingRegex = /^"(.*".*)(?="[^"]*$)/;
10+
11+
const escapedLiteral = /^"(.*".*)(?="[^"]*$)/;
12+
const quadId = /^<<("(?:""|[^"])*"[^ ]*|[^ ]+) ("(?:""|[^"])*"[^ ]*|[^ ]+) ("(?:""|[^"])*"[^ ]*|[^ ]+) ?("(?:""|[^"])*"[^ ]*|[^ ]+)?>>$/;
1113

1214
// ## DataFactory singleton
1315
const DataFactory = {
@@ -188,8 +190,10 @@ export function termFromId(id, factory) {
188190

189191
// Identify the term type based on the first character
190192
switch (id[0]) {
191-
case '_': return factory.blankNode(id.substr(2));
192-
case '?': return factory.variable(id.substr(1));
193+
case '?':
194+
return factory.variable(id.substr(1));
195+
case '_':
196+
return factory.blankNode(id.substr(2));
193197
case '"':
194198
// Shortcut for internal literals
195199
if (factory === DataFactory)
@@ -203,16 +207,15 @@ export function termFromId(id, factory) {
203207
id[endPos + 1] === '@' ? id.substr(endPos + 2)
204208
: factory.namedNode(id.substr(endPos + 3)));
205209
case '<':
206-
// Parse quad
207-
let result = id.match(/^<<("(?:""|[^"])*"[^ ]*|[^ ]+) ("(?:""|[^"])*"[^ ]*|[^ ]+) ("(?:""|[^"])*"[^ ]*|[^ ]+) ?("(?:""|[^"])*"[^ ]*|[^ ]+)?>>$/);
208-
let quad = [];
209-
for (let i = 1; i < result.length; i++) {
210-
if (typeof result[i] !== 'undefined')
211-
quad.push(termFromId(unescape(result[i]), factory));
212-
}
213-
214-
return factory.quad(...quad);
215-
default: return factory.namedNode(id);
210+
const components = quadId.exec(id);
211+
return factory.quad(
212+
termFromId(unescapeQuotes(components[1]), factory),
213+
termFromId(unescapeQuotes(components[2]), factory),
214+
termFromId(unescapeQuotes(components[3]), factory),
215+
components[4] && termFromId(unescapeQuotes(components[4]), factory)
216+
);
217+
default:
218+
return factory.namedNode(id);
216219
}
217220
}
218221

@@ -235,12 +238,14 @@ export function termToId(term) {
235238
(term.language ? '@' + term.language :
236239
(term.datatype && term.datatype.value !== xsd.string ? '^^' + term.datatype.value : ''));
237240
case 'Quad':
241+
// To identify RDF* quad components, we escape quotes by doubling them.
242+
// This avoids the overhead of backslash parsing of Turtle-like syntaxes.
238243
return `<<${
239-
escape(termToId(term.subject))
244+
escapeQuotes(termToId(term.subject))
240245
} ${
241-
escape(termToId(term.predicate))
246+
escapeQuotes(termToId(term.predicate))
242247
} ${
243-
escape(termToId(term.object))
248+
escapeQuotes(termToId(term.object))
244249
}${
245250
(isDefaultGraph(term.graph)) ? '' : ` ${termToId(term.graph)}`
246251
}>>`;
@@ -285,13 +290,13 @@ export class Quad extends Term {
285290
export { Quad as Triple };
286291

287292
// ### Escapes the quotes within the given literal
288-
export function escape(id) {
289-
return id.replace(quotedMatchingRegex, (_, quoted) => `"${quoted.replace(/"/g, '""')}`);
293+
export function escapeQuotes(id) {
294+
return id.replace(escapedLiteral, (_, quoted) => `"${quoted.replace(/"/g, '""')}`);
290295
}
291296

292297
// ### Unescapes the quotes within the given literal
293-
export function unescape(id) {
294-
return id.replace(quotedMatchingRegex, (_, quoted) => `"${quoted.replace(/""/g, '"')}`);
298+
export function unescapeQuotes(id) {
299+
return id.replace(escapedLiteral, (_, quoted) => `"${quoted.replace(/""/g, '"')}`);
295300
}
296301

297302
// ### Creates an IRI

test/Term-test.js

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ import {
1010
termFromId,
1111
} from '../src/';
1212

13-
import { escape, unescape } from '../src/N3DataFactory';
13+
import {
14+
escapeQuotes,
15+
unescapeQuotes,
16+
} from '../src/N3DataFactory';
1417

1518
describe('Term', function () {
1619
describe('The Term module', function () {
@@ -474,40 +477,40 @@ describe('Term', function () {
474477
});
475478
});
476479

477-
describe('escape', function () {
480+
describe('escaping', function () {
478481
it('should unescape an escaped string correctly', function () {
479482
let id = '"Hello ""World"""@en-us';
480-
unescape(id).should.equal('"Hello "World""@en-us');
483+
unescapeQuotes(id).should.equal('"Hello "World""@en-us');
481484
});
482485

483486
it('should escape an unescaped string correctly', function () {
484487
let id = '"Hello "World""@en-us';
485-
escape(id).should.equal('"Hello ""World"""@en-us');
488+
escapeQuotes(id).should.equal('"Hello ""World"""@en-us');
486489
});
487490

488491
it('should not change an unescaped string', function () {
489492
let id = '"Hello "World""@en-us';
490-
unescape(id).should.equal(id);
493+
unescapeQuotes(id).should.equal(id);
491494
});
492495

493496
it('should not change a string without quotes', function () {
494497
let id = '"Hello World"@en-us';
495-
escape(id).should.equal(id);
498+
escapeQuotes(id).should.equal(id);
496499
});
497500

498501
it('should not change a blank node', function () {
499502
let id = '_:b1';
500-
escape(id).should.equal(id);
503+
escapeQuotes(id).should.equal(id);
501504
});
502505

503506
it('should not change a variable', function () {
504507
let id = '?v1';
505-
escape(id).should.equal(id);
508+
escapeQuotes(id).should.equal(id);
506509
});
507510

508511
it('should not change the empty string', function () {
509512
let id = '';
510-
escape(id).should.equal(id);
513+
escapeQuotes(id).should.equal(id);
511514
});
512515
});
513516
});

0 commit comments

Comments
 (0)