n3 2.11.5 → 2.11.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/browser/n3.esm.min.js +12 -12
- package/browser/n3.min.js +10 -10
- package/lib/N3Lexer.js +25 -15
- package/lib/N3Parser.js +12 -5
- package/lib/N3Store.js +46 -5
- package/package.json +1 -1
- package/src/N3Lexer.js +27 -19
- package/src/N3Parser.js +15 -5
- package/src/N3Store.js +57 -8
package/lib/N3Lexer.js
CHANGED
|
@@ -4,7 +4,6 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
|
-
var _buffer = require("buffer");
|
|
8
7
|
var _IRIs = _interopRequireDefault(require("./IRIs"));
|
|
9
8
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
9
|
// **N3Lexer** tokenizes N3 documents.
|
|
@@ -94,6 +93,9 @@ function isSeparatorCode(code) {
|
|
|
94
93
|
return code === SPACE || code === TAB || code === LF || code === CR || code === HASH;
|
|
95
94
|
}
|
|
96
95
|
|
|
96
|
+
// Unfinished input in a stream up to this length is tokenized again with every chunk
|
|
97
|
+
const MIN_RESCAN_LENGTH = 1024;
|
|
98
|
+
|
|
97
99
|
// ## Constructor
|
|
98
100
|
class N3Lexer {
|
|
99
101
|
constructor(options) {
|
|
@@ -721,31 +723,39 @@ class N3Lexer {
|
|
|
721
723
|
}
|
|
722
724
|
// Otherwise, the input must be a stream
|
|
723
725
|
else {
|
|
724
|
-
|
|
726
|
+
let decoder,
|
|
727
|
+
retryLength = 0;
|
|
725
728
|
if (typeof input.setEncoding === 'function') input.setEncoding('utf8');
|
|
726
729
|
// Adds the data chunk to the buffer and parses as far as possible
|
|
727
730
|
input.on('data', data => {
|
|
728
731
|
if (this._tokenization === tokenization && this._input !== null && data.length !== 0) {
|
|
729
|
-
//
|
|
730
|
-
if (
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
732
|
+
// Decode bytes, keeping an incomplete trailing character for the next chunk
|
|
733
|
+
if (typeof data !== 'string') {
|
|
734
|
+
decoder = decoder || new TextDecoder('utf-8', {
|
|
735
|
+
ignoreBOM: true
|
|
736
|
+
});
|
|
737
|
+
if (!(data = decoder.decode(data, {
|
|
738
|
+
stream: true
|
|
739
|
+
}))) return;
|
|
737
740
|
}
|
|
738
|
-
//
|
|
739
|
-
else
|
|
740
|
-
|
|
741
|
-
|
|
741
|
+
// Only read a BOM at the start
|
|
742
|
+
if (typeof this._input === 'undefined') this._input = this._readStartingBom(data);else this._input += data;
|
|
743
|
+
// Tokenize as far as possible. When a previous attempt left a long unfinished token,
|
|
744
|
+
// wait until the buffered input has doubled, so the token is not rescanned for every chunk.
|
|
745
|
+
if (this._input.length >= retryLength) {
|
|
742
746
|
this._tokenizeToEnd(callback, false);
|
|
747
|
+
retryLength = this._input !== null && this._input.length > MIN_RESCAN_LENGTH ? 2 * this._input.length : 0;
|
|
743
748
|
}
|
|
744
749
|
}
|
|
745
750
|
});
|
|
746
751
|
// Parses until the end
|
|
747
752
|
input.on('end', () => {
|
|
748
|
-
if (this._tokenization === tokenization &&
|
|
753
|
+
if (this._tokenization === tokenization && this._input !== null) {
|
|
754
|
+
// Decode any incomplete character left at the end
|
|
755
|
+
const rest = decoder ? decoder.decode() : '';
|
|
756
|
+
if (rest) this._input = typeof this._input === 'string' ? this._input + rest : rest;
|
|
757
|
+
if (typeof this._input === 'string') this._tokenizeToEnd(callback, true);
|
|
758
|
+
}
|
|
749
759
|
});
|
|
750
760
|
input.on('error', error => {
|
|
751
761
|
if (this._tokenization === tokenization) callback(error);
|
package/lib/N3Parser.js
CHANGED
|
@@ -93,9 +93,13 @@ class N3Parser {
|
|
|
93
93
|
const queryPos = baseIRI.indexOf('?');
|
|
94
94
|
const path = queryPos < 0 ? baseIRI : baseIRI.substr(0, queryPos);
|
|
95
95
|
this._basePath = baseIRI.indexOf('/') < 0 ? baseIRI : path.substr(0, path.lastIndexOf('/') + 1);
|
|
96
|
-
|
|
96
|
+
const base = baseIRI;
|
|
97
|
+
baseIRI = baseIRI.match(/^(?:([a-z][a-z0-9+.-]*:))?(\/\/[^\/?#]*)?/i);
|
|
97
98
|
this._baseRoot = baseIRI[0];
|
|
98
99
|
this._baseScheme = baseIRI[1];
|
|
100
|
+
// If the base has an authority but an empty path,
|
|
101
|
+
// relative IRIs merge under the path '/' (RFC 3986 §5.2.3)
|
|
102
|
+
if (baseIRI[2] !== undefined && (base.length === this._baseRoot.length || base[this._baseRoot.length] === '?')) this._basePath = `${this._baseRoot}/`;
|
|
99
103
|
}
|
|
100
104
|
}
|
|
101
105
|
|
|
@@ -1430,8 +1434,11 @@ class N3Parser {
|
|
|
1430
1434
|
}
|
|
1431
1435
|
// Resolve root-relative IRIs at the root of the base IRI
|
|
1432
1436
|
case '/':
|
|
1433
|
-
// Resolve scheme-relative IRIs to the scheme
|
|
1434
|
-
|
|
1437
|
+
// Resolve scheme-relative IRIs to the scheme,
|
|
1438
|
+
// keeping the reference's authority intact (RFC 3986 §5.2.4)
|
|
1439
|
+
if (iri[1] !== '/') return this._baseRoot + this._removeDotSegments(iri);
|
|
1440
|
+
// Without a base scheme, a leading ':' marks where the authority starts
|
|
1441
|
+
return this._baseScheme ? this._removeDotSegments(this._baseScheme + iri) : this._removeDotSegments(`:${iri}`).substr(1);
|
|
1435
1442
|
// Resolve all other IRIs at the base IRI's path
|
|
1436
1443
|
default:
|
|
1437
1444
|
// Relative IRIs cannot contain a colon in the first path segment
|
|
@@ -1458,8 +1465,8 @@ class N3Parser {
|
|
|
1458
1465
|
if (pathStart < 0) {
|
|
1459
1466
|
// Skip two slashes before the authority
|
|
1460
1467
|
if (iri[++i] === '/' && iri[++i] === '/')
|
|
1461
|
-
// Skip to
|
|
1462
|
-
while ((pathStart = i + 1) < length && iri[pathStart] !== '/') i = pathStart;
|
|
1468
|
+
// Skip to the end of the authority
|
|
1469
|
+
while ((pathStart = i + 1) < length && iri[pathStart] !== '/' && iri[pathStart] !== '?' && iri[pathStart] !== '#') i = pathStart;
|
|
1463
1470
|
}
|
|
1464
1471
|
break;
|
|
1465
1472
|
// Don't modify a query string or fragment
|
package/lib/N3Store.js
CHANGED
|
@@ -116,6 +116,31 @@ function difference(s1, s2, depth = 4) {
|
|
|
116
116
|
return target;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
+
// Returns the key of a term in the entity index.
|
|
120
|
+
// Keys mark the term type by their first character, so the IRI of a named node
|
|
121
|
+
// that starts with such a marker (as relative IRIs can) is wrapped in < and >.
|
|
122
|
+
const markedIRI = /^[?_"[.<]/;
|
|
123
|
+
function entityKey(term) {
|
|
124
|
+
// Strings are term ids, which only need wrapping when they are IRIs starting with <
|
|
125
|
+
if (typeof term === 'string') return term.charCodeAt(0) !== 0x3C || term === '<>' ? term : `<${term}>`;
|
|
126
|
+
// IDs of IRIs usually start with a lowercase scheme letter, which never marks a term type
|
|
127
|
+
const id = (0, _N3DataFactory.termToId)(term);
|
|
128
|
+
if (id.charCodeAt(0) >= 0x61 || !term) return id;
|
|
129
|
+
// Keys of quoted triples are built from the keys of their components
|
|
130
|
+
if (term.termType === 'Quad') return JSON.stringify(quadKeyParts(term));
|
|
131
|
+
return term.termType !== 'NamedNode' || !markedIRI.test(term.value) ? id : `<${term.value}>`;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Returns the keys of the components of a quad, nested like the parts of its internal id
|
|
135
|
+
function quadKeyParts(quad) {
|
|
136
|
+
const parts = [nestedKey(quad.subject), nestedKey(quad.predicate), nestedKey(quad.object)];
|
|
137
|
+
if (quad.graph && !(0, _N3Util.isDefaultGraph)(quad.graph)) parts.push(nestedKey(quad.graph));
|
|
138
|
+
return parts;
|
|
139
|
+
}
|
|
140
|
+
function nestedKey(term) {
|
|
141
|
+
return term.termType === 'Quad' ? quadKeyParts(term) : entityKey(term);
|
|
142
|
+
}
|
|
143
|
+
|
|
119
144
|
// ## Constructor
|
|
120
145
|
class N3EntityIndex {
|
|
121
146
|
constructor(options = {}) {
|
|
@@ -132,6 +157,9 @@ class N3EntityIndex {
|
|
|
132
157
|
this._factory = options.factory || _N3DataFactory.default;
|
|
133
158
|
}
|
|
134
159
|
_termFromId(id) {
|
|
160
|
+
// A key in < and > is a named node
|
|
161
|
+
if (id[0] === '<') return this._factory.namedNode(id.substring(1, id.length - 1));
|
|
162
|
+
// A key starting with . is a quoted triple
|
|
135
163
|
if (id[0] === '.') {
|
|
136
164
|
const entities = this._entities;
|
|
137
165
|
const terms = id.split('.');
|
|
@@ -148,12 +176,25 @@ class N3EntityIndex {
|
|
|
148
176
|
let g;
|
|
149
177
|
return s && p && o && ((0, _N3Util.isDefaultGraph)(term.graph) || (g = this._termToNumericId(term.graph))) && this._ids[g ? `.${s}.${p}.${o}.${g}` : `.${s}.${p}.${o}`];
|
|
150
178
|
}
|
|
151
|
-
return this.
|
|
179
|
+
return typeof term === 'string' ? this._stringToNumericId(term) : this._ids[entityKey(term)];
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Returns the numeric id of a term given as a string id
|
|
183
|
+
_stringToNumericId(term) {
|
|
184
|
+
// The string is the key unless it is an IRI starting with <. Read the first character
|
|
185
|
+
// from the stored key after a match, which is cheaper than from a concatenated string.
|
|
186
|
+
const id = this._ids[term];
|
|
187
|
+
if (id ? this._entities[id].charCodeAt(0) !== 0x3C : term.charCodeAt(0) !== 0x3C) return id;
|
|
188
|
+
return term === '<>' ? id : this._ids[`<${term}>`];
|
|
152
189
|
}
|
|
153
190
|
_termToNewNumericId(term) {
|
|
191
|
+
if (typeof term === 'string') return this._stringToNumericId(term) || this._addEntity(entityKey(term));
|
|
154
192
|
// This assumes that no graph term is present - we may wish to error if there is one
|
|
155
|
-
const str = term && term.termType === 'Quad' ? `.${this._termToNewNumericId(term.subject)}.${this._termToNewNumericId(term.predicate)}.${this._termToNewNumericId(term.object)}${(0, _N3Util.isDefaultGraph)(term.graph) ? '' : `.${this._termToNewNumericId(term.graph)}`}` : (
|
|
156
|
-
return this._ids[str] ||
|
|
193
|
+
const str = term && term.termType === 'Quad' ? `.${this._termToNewNumericId(term.subject)}.${this._termToNewNumericId(term.predicate)}.${this._termToNewNumericId(term.object)}${(0, _N3Util.isDefaultGraph)(term.graph) ? '' : `.${this._termToNewNumericId(term.graph)}`}` : entityKey(term);
|
|
194
|
+
return this._ids[str] || this._addEntity(str);
|
|
195
|
+
}
|
|
196
|
+
_addEntity(key) {
|
|
197
|
+
return this._ids[this._entities[++this._id] = key] = this._id;
|
|
157
198
|
}
|
|
158
199
|
createBlankNode(suggestedName) {
|
|
159
200
|
let name, index;
|
|
@@ -1305,7 +1346,7 @@ function intersectMatchPatterns(left, right) {
|
|
|
1305
1346
|
for (let i = 0; i < 4; i++) {
|
|
1306
1347
|
const leftTerm = left[i],
|
|
1307
1348
|
rightTerm = right[i];
|
|
1308
|
-
if (leftTerm === null || leftTerm === undefined) result[i] = rightTerm;else if (rightTerm === null || rightTerm === undefined || (
|
|
1349
|
+
if (leftTerm === null || leftTerm === undefined) result[i] = rightTerm;else if (rightTerm === null || rightTerm === undefined || entityKey(leftTerm) === entityKey(rightTerm)) result[i] = leftTerm;else return false;
|
|
1309
1350
|
}
|
|
1310
1351
|
return result;
|
|
1311
1352
|
}
|
|
@@ -1368,7 +1409,7 @@ class DatasetCoreAndReadableStream extends _readableStream.Readable {
|
|
|
1368
1409
|
object,
|
|
1369
1410
|
graph
|
|
1370
1411
|
} = this;
|
|
1371
|
-
return !this._matchesNothing && (subject === null || subject === undefined || (
|
|
1412
|
+
return !this._matchesNothing && (subject === null || subject === undefined || entityKey(subject) === entityKey(quad.subject)) && (predicate === null || predicate === undefined || entityKey(predicate) === entityKey(quad.predicate)) && (object === null || object === undefined || entityKey(object) === entityKey(quad.object)) && (graph === null || graph === undefined || entityKey(graph) === entityKey(quad.graph));
|
|
1372
1413
|
}
|
|
1373
1414
|
|
|
1374
1415
|
// ### `_assertMatchesPattern` rejects a Quad outside this view.
|
package/package.json
CHANGED
package/src/N3Lexer.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// **N3Lexer** tokenizes N3 documents.
|
|
2
|
-
import { Buffer } from 'buffer';
|
|
3
2
|
import namespaces from './IRIs';
|
|
4
3
|
|
|
5
4
|
const { xsd } = namespaces;
|
|
@@ -59,6 +58,9 @@ function isSeparatorCode(code) {
|
|
|
59
58
|
return code === SPACE || code === TAB || code === LF || code === CR || code === HASH;
|
|
60
59
|
}
|
|
61
60
|
|
|
61
|
+
// Unfinished input in a stream up to this length is tokenized again with every chunk
|
|
62
|
+
const MIN_RESCAN_LENGTH = 1024;
|
|
63
|
+
|
|
62
64
|
// ## Constructor
|
|
63
65
|
export default class N3Lexer {
|
|
64
66
|
constructor(options) {
|
|
@@ -747,36 +749,42 @@ export default class N3Lexer {
|
|
|
747
749
|
}
|
|
748
750
|
// Otherwise, the input must be a stream
|
|
749
751
|
else {
|
|
750
|
-
|
|
752
|
+
let decoder, retryLength = 0;
|
|
751
753
|
if (typeof input.setEncoding === 'function')
|
|
752
754
|
input.setEncoding('utf8');
|
|
753
755
|
// Adds the data chunk to the buffer and parses as far as possible
|
|
754
756
|
input.on('data', data => {
|
|
755
757
|
if (this._tokenization === tokenization && this._input !== null && data.length !== 0) {
|
|
756
|
-
//
|
|
757
|
-
if (
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
// Hold if the buffer ends in an incomplete unicode sequence
|
|
762
|
-
if (data[data.length - 1] & 0x80) {
|
|
763
|
-
this._pendingBuffer = data;
|
|
758
|
+
// Decode bytes, keeping an incomplete trailing character for the next chunk
|
|
759
|
+
if (typeof data !== 'string') {
|
|
760
|
+
decoder = decoder || new TextDecoder('utf-8', { ignoreBOM: true });
|
|
761
|
+
if (!(data = decoder.decode(data, { stream: true })))
|
|
762
|
+
return;
|
|
764
763
|
}
|
|
765
|
-
//
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
764
|
+
// Only read a BOM at the start
|
|
765
|
+
if (typeof this._input === 'undefined')
|
|
766
|
+
this._input = this._readStartingBom(data);
|
|
767
|
+
else
|
|
768
|
+
this._input += data;
|
|
769
|
+
// Tokenize as far as possible. When a previous attempt left a long unfinished token,
|
|
770
|
+
// wait until the buffered input has doubled, so the token is not rescanned for every chunk.
|
|
771
|
+
if (this._input.length >= retryLength) {
|
|
772
772
|
this._tokenizeToEnd(callback, false);
|
|
773
|
+
retryLength = this._input !== null && this._input.length > MIN_RESCAN_LENGTH ?
|
|
774
|
+
2 * this._input.length : 0;
|
|
773
775
|
}
|
|
774
776
|
}
|
|
775
777
|
});
|
|
776
778
|
// Parses until the end
|
|
777
779
|
input.on('end', () => {
|
|
778
|
-
if (this._tokenization === tokenization &&
|
|
779
|
-
|
|
780
|
+
if (this._tokenization === tokenization && this._input !== null) {
|
|
781
|
+
// Decode any incomplete character left at the end
|
|
782
|
+
const rest = decoder ? decoder.decode() : '';
|
|
783
|
+
if (rest)
|
|
784
|
+
this._input = typeof this._input === 'string' ? this._input + rest : rest;
|
|
785
|
+
if (typeof this._input === 'string')
|
|
786
|
+
this._tokenizeToEnd(callback, true);
|
|
787
|
+
}
|
|
780
788
|
});
|
|
781
789
|
input.on('error', error => {
|
|
782
790
|
if (this._tokenization === tokenization)
|
package/src/N3Parser.js
CHANGED
|
@@ -86,9 +86,14 @@ export default class N3Parser {
|
|
|
86
86
|
const path = queryPos < 0 ? baseIRI : baseIRI.substr(0, queryPos);
|
|
87
87
|
this._basePath = baseIRI.indexOf('/') < 0 ? baseIRI :
|
|
88
88
|
path.substr(0, path.lastIndexOf('/') + 1);
|
|
89
|
-
|
|
89
|
+
const base = baseIRI;
|
|
90
|
+
baseIRI = baseIRI.match(/^(?:([a-z][a-z0-9+.-]*:))?(\/\/[^\/?#]*)?/i);
|
|
90
91
|
this._baseRoot = baseIRI[0];
|
|
91
92
|
this._baseScheme = baseIRI[1];
|
|
93
|
+
// If the base has an authority but an empty path,
|
|
94
|
+
// relative IRIs merge under the path '/' (RFC 3986 §5.2.3)
|
|
95
|
+
if (baseIRI[2] !== undefined && (base.length === this._baseRoot.length || base[this._baseRoot.length] === '?'))
|
|
96
|
+
this._basePath = `${this._baseRoot}/`;
|
|
92
97
|
}
|
|
93
98
|
}
|
|
94
99
|
|
|
@@ -1546,8 +1551,13 @@ export default class N3Parser {
|
|
|
1546
1551
|
}
|
|
1547
1552
|
// Resolve root-relative IRIs at the root of the base IRI
|
|
1548
1553
|
case '/':
|
|
1549
|
-
// Resolve scheme-relative IRIs to the scheme
|
|
1550
|
-
|
|
1554
|
+
// Resolve scheme-relative IRIs to the scheme,
|
|
1555
|
+
// keeping the reference's authority intact (RFC 3986 §5.2.4)
|
|
1556
|
+
if (iri[1] !== '/')
|
|
1557
|
+
return this._baseRoot + this._removeDotSegments(iri);
|
|
1558
|
+
// Without a base scheme, a leading ':' marks where the authority starts
|
|
1559
|
+
return this._baseScheme ? this._removeDotSegments(this._baseScheme + iri) :
|
|
1560
|
+
this._removeDotSegments(`:${iri}`).substr(1);
|
|
1551
1561
|
// Resolve all other IRIs at the base IRI's path
|
|
1552
1562
|
default:
|
|
1553
1563
|
// Relative IRIs cannot contain a colon in the first path segment
|
|
@@ -1572,8 +1582,8 @@ export default class N3Parser {
|
|
|
1572
1582
|
if (pathStart < 0) {
|
|
1573
1583
|
// Skip two slashes before the authority
|
|
1574
1584
|
if (iri[++i] === '/' && iri[++i] === '/')
|
|
1575
|
-
// Skip to
|
|
1576
|
-
while ((pathStart = i + 1) < length && iri[pathStart] !== '/')
|
|
1585
|
+
// Skip to the end of the authority
|
|
1586
|
+
while ((pathStart = i + 1) < length && iri[pathStart] !== '/' && iri[pathStart] !== '?' && iri[pathStart] !== '#')
|
|
1577
1587
|
i = pathStart;
|
|
1578
1588
|
}
|
|
1579
1589
|
break;
|
package/src/N3Store.js
CHANGED
|
@@ -118,6 +118,35 @@ function difference(s1, s2, depth = 4) {
|
|
|
118
118
|
return target;
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
// Returns the key of a term in the entity index.
|
|
122
|
+
// Keys mark the term type by their first character, so the IRI of a named node
|
|
123
|
+
// that starts with such a marker (as relative IRIs can) is wrapped in < and >.
|
|
124
|
+
const markedIRI = /^[?_"[.<]/;
|
|
125
|
+
function entityKey(term) {
|
|
126
|
+
// Strings are term ids, which only need wrapping when they are IRIs starting with <
|
|
127
|
+
if (typeof term === 'string')
|
|
128
|
+
return term.charCodeAt(0) !== 0x3C || term === '<>' ? term : `<${term}>`;
|
|
129
|
+
// IDs of IRIs usually start with a lowercase scheme letter, which never marks a term type
|
|
130
|
+
const id = termToId(term);
|
|
131
|
+
if (id.charCodeAt(0) >= 0x61 || !term)
|
|
132
|
+
return id;
|
|
133
|
+
// Keys of quoted triples are built from the keys of their components
|
|
134
|
+
if (term.termType === 'Quad')
|
|
135
|
+
return JSON.stringify(quadKeyParts(term));
|
|
136
|
+
return term.termType !== 'NamedNode' || !markedIRI.test(term.value) ? id : `<${term.value}>`;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Returns the keys of the components of a quad, nested like the parts of its internal id
|
|
140
|
+
function quadKeyParts(quad) {
|
|
141
|
+
const parts = [nestedKey(quad.subject), nestedKey(quad.predicate), nestedKey(quad.object)];
|
|
142
|
+
if (quad.graph && !isDefaultGraph(quad.graph))
|
|
143
|
+
parts.push(nestedKey(quad.graph));
|
|
144
|
+
return parts;
|
|
145
|
+
}
|
|
146
|
+
function nestedKey(term) {
|
|
147
|
+
return term.termType === 'Quad' ? quadKeyParts(term) : entityKey(term);
|
|
148
|
+
}
|
|
149
|
+
|
|
121
150
|
// ## Constructor
|
|
122
151
|
export class N3EntityIndex {
|
|
123
152
|
constructor(options = {}) {
|
|
@@ -135,6 +164,10 @@ export class N3EntityIndex {
|
|
|
135
164
|
}
|
|
136
165
|
|
|
137
166
|
_termFromId(id) {
|
|
167
|
+
// A key in < and > is a named node
|
|
168
|
+
if (id[0] === '<')
|
|
169
|
+
return this._factory.namedNode(id.substring(1, id.length - 1));
|
|
170
|
+
// A key starting with . is a quoted triple
|
|
138
171
|
if (id[0] === '.') {
|
|
139
172
|
const entities = this._entities;
|
|
140
173
|
const terms = id.split('.');
|
|
@@ -159,18 +192,34 @@ export class N3EntityIndex {
|
|
|
159
192
|
return s && p && o && (isDefaultGraph(term.graph) || (g = this._termToNumericId(term.graph))) &&
|
|
160
193
|
this._ids[g ? `.${s}.${p}.${o}.${g}` : `.${s}.${p}.${o}`];
|
|
161
194
|
}
|
|
162
|
-
return this._ids[
|
|
195
|
+
return typeof term === 'string' ? this._stringToNumericId(term) : this._ids[entityKey(term)];
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// Returns the numeric id of a term given as a string id
|
|
199
|
+
_stringToNumericId(term) {
|
|
200
|
+
// The string is the key unless it is an IRI starting with <. Read the first character
|
|
201
|
+
// from the stored key after a match, which is cheaper than from a concatenated string.
|
|
202
|
+
const id = this._ids[term];
|
|
203
|
+
if (id ? this._entities[id].charCodeAt(0) !== 0x3C : term.charCodeAt(0) !== 0x3C)
|
|
204
|
+
return id;
|
|
205
|
+
return term === '<>' ? id : this._ids[`<${term}>`];
|
|
163
206
|
}
|
|
164
207
|
|
|
165
208
|
_termToNewNumericId(term) {
|
|
209
|
+
if (typeof term === 'string')
|
|
210
|
+
return this._stringToNumericId(term) || this._addEntity(entityKey(term));
|
|
166
211
|
// This assumes that no graph term is present - we may wish to error if there is one
|
|
167
212
|
const str = term && term.termType === 'Quad' ?
|
|
168
213
|
`.${this._termToNewNumericId(term.subject)}.${this._termToNewNumericId(term.predicate)}.${this._termToNewNumericId(term.object)}${
|
|
169
214
|
isDefaultGraph(term.graph) ? '' : `.${this._termToNewNumericId(term.graph)}`
|
|
170
215
|
}`
|
|
171
|
-
:
|
|
216
|
+
: entityKey(term);
|
|
217
|
+
|
|
218
|
+
return this._ids[str] || this._addEntity(str);
|
|
219
|
+
}
|
|
172
220
|
|
|
173
|
-
|
|
221
|
+
_addEntity(key) {
|
|
222
|
+
return this._ids[this._entities[++this._id] = key] = this._id;
|
|
174
223
|
}
|
|
175
224
|
|
|
176
225
|
createBlankNode(suggestedName) {
|
|
@@ -1382,7 +1431,7 @@ function intersectMatchPatterns(left, right) {
|
|
|
1382
1431
|
const leftTerm = left[i], rightTerm = right[i];
|
|
1383
1432
|
if (leftTerm === null || leftTerm === undefined)
|
|
1384
1433
|
result[i] = rightTerm;
|
|
1385
|
-
else if (rightTerm === null || rightTerm === undefined ||
|
|
1434
|
+
else if (rightTerm === null || rightTerm === undefined || entityKey(leftTerm) === entityKey(rightTerm))
|
|
1386
1435
|
result[i] = leftTerm;
|
|
1387
1436
|
else
|
|
1388
1437
|
return false;
|
|
@@ -1433,10 +1482,10 @@ class DatasetCoreAndReadableStream extends Readable {
|
|
|
1433
1482
|
_matchesQuad(quad) {
|
|
1434
1483
|
const { subject, predicate, object, graph } = this;
|
|
1435
1484
|
return !this._matchesNothing &&
|
|
1436
|
-
(subject === null || subject === undefined ||
|
|
1437
|
-
(predicate === null || predicate === undefined ||
|
|
1438
|
-
(object === null || object === undefined ||
|
|
1439
|
-
(graph === null || graph === undefined ||
|
|
1485
|
+
(subject === null || subject === undefined || entityKey(subject) === entityKey(quad.subject)) &&
|
|
1486
|
+
(predicate === null || predicate === undefined || entityKey(predicate) === entityKey(quad.predicate)) &&
|
|
1487
|
+
(object === null || object === undefined || entityKey(object) === entityKey(quad.object)) &&
|
|
1488
|
+
(graph === null || graph === undefined || entityKey(graph) === entityKey(quad.graph));
|
|
1440
1489
|
}
|
|
1441
1490
|
|
|
1442
1491
|
// ### `_assertMatchesPattern` rejects a Quad outside this view.
|