@oml/owl 0.12.0 → 0.14.0
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/out/owl/owl-abox.js +25 -34
- package/out/owl/owl-abox.js.map +1 -1
- package/out/owl/owl-interfaces.d.ts +1 -1
- package/out/owl/owl-service.js +9 -9
- package/out/owl/owl-service.js.map +1 -1
- package/out/owl/owl-shacl.d.ts +3 -3
- package/out/owl/owl-shacl.js +154 -28
- package/out/owl/owl-shacl.js.map +1 -1
- package/out/owl/owl-sparql.d.ts +6 -1
- package/out/owl/owl-sparql.js +135 -16
- package/out/owl/owl-sparql.js.map +1 -1
- package/out/owl/owl-store.d.ts +0 -3
- package/out/owl/owl-store.js +11 -35
- package/out/owl/owl-store.js.map +1 -1
- package/package.json +2 -2
- package/src/owl/owl-abox.ts +25 -37
- package/src/owl/owl-interfaces.ts +1 -1
- package/src/owl/owl-service.ts +9 -8
- package/src/owl/owl-shacl.ts +172 -33
- package/src/owl/owl-sparql.ts +156 -17
- package/src/owl/owl-store.ts +11 -37
package/out/owl/owl-store.d.ts
CHANGED
|
@@ -21,10 +21,7 @@ export declare class ReasoningStore {
|
|
|
21
21
|
graphs(modelUri: string): ModelGraphs;
|
|
22
22
|
getStore(): Store;
|
|
23
23
|
private resolveGraphs;
|
|
24
|
-
private graphsFromNamespace;
|
|
25
24
|
private graphsFromModelUri;
|
|
26
|
-
private namespaceFromQuads;
|
|
27
|
-
private ontologyIriFromNamespace;
|
|
28
25
|
private requireGraphs;
|
|
29
26
|
private withGraph;
|
|
30
27
|
private removeGraph;
|
package/out/owl/owl-store.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// Copyright (c) 2026 Modelware. All rights reserved.
|
|
2
2
|
import { DataFactory, Store } from 'n3';
|
|
3
|
-
const OML_NAMESPACE = 'http://opencaesar.io/oml#namespace';
|
|
4
3
|
const { namedNode, quad } = DataFactory;
|
|
5
4
|
export class ReasoningStore {
|
|
6
5
|
constructor() {
|
|
@@ -9,9 +8,9 @@ export class ReasoningStore {
|
|
|
9
8
|
}
|
|
10
9
|
// Load a model's quads into its named graph.
|
|
11
10
|
// Retracts any existing quads for that model first.
|
|
12
|
-
// Graph IRI is derived from the
|
|
11
|
+
// Graph IRI is derived from the model URI to keep model snapshots isolated.
|
|
13
12
|
loadModel(modelUri, quads) {
|
|
14
|
-
const graphs = this.resolveGraphs(modelUri
|
|
13
|
+
const graphs = this.resolveGraphs(modelUri);
|
|
15
14
|
this.retractModel(modelUri);
|
|
16
15
|
this.modelGraphs.set(modelUri, graphs);
|
|
17
16
|
this.store.addQuads(quads.map((q) => this.withGraph(q, graphs.own)));
|
|
@@ -25,7 +24,7 @@ export class ReasoningStore {
|
|
|
25
24
|
this.store.addQuads(quads.map((q) => this.withGraph(q, graphs.entailments)));
|
|
26
25
|
}
|
|
27
26
|
applyModelDelta(modelUri, quads, retracted, asserted) {
|
|
28
|
-
const graphs = this.resolveGraphs(modelUri
|
|
27
|
+
const graphs = this.resolveGraphs(modelUri);
|
|
29
28
|
this.modelGraphs.set(modelUri, graphs);
|
|
30
29
|
if (retracted.length > 0) {
|
|
31
30
|
this.store.removeQuads(retracted.map((q) => this.withGraph(q, graphs.own)));
|
|
@@ -48,7 +47,7 @@ export class ReasoningStore {
|
|
|
48
47
|
// Returns the semantic difference — what was added and what was removed.
|
|
49
48
|
// If the diff is empty, nothing downstream needs to run.
|
|
50
49
|
diffModel(modelUri, newQuads) {
|
|
51
|
-
const graphs = this.resolveGraphs(modelUri
|
|
50
|
+
const graphs = this.resolveGraphs(modelUri);
|
|
52
51
|
const existing = this.store.getQuads(null, null, null, graphs.own);
|
|
53
52
|
const next = newQuads.map((q) => this.withGraph(q, graphs.own));
|
|
54
53
|
const existingByKey = new Map(existing.map((q) => [this.quadKey(q), q]));
|
|
@@ -84,30 +83,14 @@ export class ReasoningStore {
|
|
|
84
83
|
getStore() {
|
|
85
84
|
return this.store;
|
|
86
85
|
}
|
|
87
|
-
resolveGraphs(modelUri
|
|
88
|
-
const
|
|
89
|
-
if (
|
|
90
|
-
|
|
91
|
-
this.modelGraphs.set(modelUri, graphs);
|
|
92
|
-
return graphs;
|
|
86
|
+
resolveGraphs(modelUri) {
|
|
87
|
+
const existing = this.modelGraphs.get(modelUri);
|
|
88
|
+
if (existing) {
|
|
89
|
+
return existing;
|
|
93
90
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
return existing;
|
|
98
|
-
}
|
|
99
|
-
// Keep reasoning resilient during transient states (e.g. non-mappable documents):
|
|
100
|
-
// if namespace is missing, fall back to a stable graph derived from the model URI.
|
|
101
|
-
const fallback = this.graphsFromModelUri(modelUri);
|
|
102
|
-
this.modelGraphs.set(modelUri, fallback);
|
|
103
|
-
return fallback;
|
|
104
|
-
}
|
|
105
|
-
graphsFromNamespace(namespace) {
|
|
106
|
-
const ontologyIri = this.ontologyIriFromNamespace(namespace);
|
|
107
|
-
return {
|
|
108
|
-
own: namedNode(ontologyIri),
|
|
109
|
-
entailments: namedNode(`${ontologyIri}__entailments`),
|
|
110
|
-
};
|
|
91
|
+
const graphs = this.graphsFromModelUri(modelUri);
|
|
92
|
+
this.modelGraphs.set(modelUri, graphs);
|
|
93
|
+
return graphs;
|
|
111
94
|
}
|
|
112
95
|
graphsFromModelUri(modelUri) {
|
|
113
96
|
return {
|
|
@@ -115,13 +98,6 @@ export class ReasoningStore {
|
|
|
115
98
|
entailments: namedNode(`${modelUri}__entailments`)
|
|
116
99
|
};
|
|
117
100
|
}
|
|
118
|
-
namespaceFromQuads(quads) {
|
|
119
|
-
const namespaceQuad = quads.find((q) => q.predicate.value === OML_NAMESPACE && q.object.termType === 'NamedNode');
|
|
120
|
-
return namespaceQuad?.object.value;
|
|
121
|
-
}
|
|
122
|
-
ontologyIriFromNamespace(namespace) {
|
|
123
|
-
return namespace.replace(/[\/#]+$/, '');
|
|
124
|
-
}
|
|
125
101
|
requireGraphs(modelUri) {
|
|
126
102
|
const graphs = this.modelGraphs.get(modelUri);
|
|
127
103
|
if (!graphs) {
|
package/out/owl/owl-store.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"owl-store.js","sourceRoot":"","sources":["../../src/owl/owl-store.ts"],"names":[],"mappings":"AAAA,qDAAqD;AAErD,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,IAAI,CAAC;AAGxC,MAAM,
|
|
1
|
+
{"version":3,"file":"owl-store.js","sourceRoot":"","sources":["../../src/owl/owl-store.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oml/owl",
|
|
3
3
|
"description": "The semantic web specific package",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.14.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=20.10.0",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@comunica/query-sparql": "^5.1.3",
|
|
37
|
-
"@oml/language": "0.
|
|
37
|
+
"@oml/language": "0.14.0",
|
|
38
38
|
"langium": "^4.2.1",
|
|
39
39
|
"n3": "^2.0.1",
|
|
40
40
|
"shacl-engine": "^1.1.0",
|
package/src/owl/owl-abox.ts
CHANGED
|
@@ -880,51 +880,39 @@ export class ABoxChainer {
|
|
|
880
880
|
}
|
|
881
881
|
}
|
|
882
882
|
|
|
883
|
-
private collectValidationWarnings(
|
|
883
|
+
private collectValidationWarnings(_index: WorkingIndex, _tbox: TBoxIndex): string[] {
|
|
884
884
|
const warnings: string[] = [];
|
|
885
885
|
|
|
886
|
-
for (const
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
if (!ranges.includes(fact.object.datatype.value)) {
|
|
899
|
-
warnings.push(
|
|
900
|
-
`Datatype range violation on ${fact.predicate.value}: expected ${ranges.join(', ')}, got ${fact.object.datatype.value}.`,
|
|
901
|
-
);
|
|
886
|
+
for (const propertyIri of _tbox.functionalProperties) {
|
|
887
|
+
const bySubject = new Map<string, Set<string>>();
|
|
888
|
+
const facts = _index.edgeFactsByProperty.get(propertyIri) ?? [];
|
|
889
|
+
for (const fact of facts) {
|
|
890
|
+
const objects = bySubject.get(fact.subject.id) ?? new Set<string>();
|
|
891
|
+
objects.add(fact.object.id);
|
|
892
|
+
bySubject.set(fact.subject.id, objects);
|
|
893
|
+
}
|
|
894
|
+
for (const [subjectId, objects] of bySubject.entries()) {
|
|
895
|
+
if (objects.size > 1) {
|
|
896
|
+
warnings.push(`Functional property violation: ${propertyIri} has multiple values for subject ${subjectId}`);
|
|
897
|
+
}
|
|
902
898
|
}
|
|
903
899
|
}
|
|
904
900
|
|
|
905
|
-
for (const
|
|
906
|
-
const
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
warnings.push(`Functional property violation on ${property}: ${facts[0]?.subject.value ?? subjectId} has ${objectIds.length} distinct values.`);
|
|
901
|
+
for (const propertyIri of _tbox.inverseFunctionalProperties) {
|
|
902
|
+
const byObject = new Map<string, Set<string>>();
|
|
903
|
+
const facts = _index.edgeFactsByProperty.get(propertyIri) ?? [];
|
|
904
|
+
for (const fact of facts) {
|
|
905
|
+
const subjects = byObject.get(fact.object.id) ?? new Set<string>();
|
|
906
|
+
subjects.add(fact.subject.id);
|
|
907
|
+
byObject.set(fact.object.id, subjects);
|
|
913
908
|
}
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
const property = split >= 0 ? propertyObject.slice(0, split) : propertyObject;
|
|
919
|
-
const objectId = split >= 0 ? propertyObject.slice(split + 1) : '';
|
|
920
|
-
if (!tbox.inverseFunctionalProperties.has(property) || subjectIds.size <= 1) {
|
|
921
|
-
continue;
|
|
909
|
+
for (const [objectId, subjects] of byObject.entries()) {
|
|
910
|
+
if (subjects.size > 1) {
|
|
911
|
+
warnings.push(`Inverse-functional property violation: ${propertyIri} has multiple subjects for object ${objectId}`);
|
|
912
|
+
}
|
|
922
913
|
}
|
|
923
|
-
const object = index.termByKey.get(objectId);
|
|
924
|
-
const objectLabel = object?.value ?? objectId;
|
|
925
|
-
warnings.push(`Inverse-functional property violation on ${property}: ${objectLabel} has ${subjectIds.size} distinct sources.`);
|
|
926
914
|
}
|
|
927
915
|
|
|
928
|
-
return
|
|
916
|
+
return warnings;
|
|
929
917
|
}
|
|
930
918
|
}
|
|
@@ -133,7 +133,7 @@ export interface OwlShaclValidationResult {
|
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
export interface OwlShaclService {
|
|
136
|
-
validateShacl(modelUri: string
|
|
136
|
+
validateShacl(modelUri: string, shaclSource: string): Promise<OwlShaclValidationResult>;
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
export interface OwlReasoningDependencies {
|
package/src/owl/owl-service.ts
CHANGED
|
@@ -53,6 +53,9 @@ export class ReasoningService {
|
|
|
53
53
|
this.aboxChainer = resolved.aboxChainer;
|
|
54
54
|
this.aboxEntailmentCache = resolved.aboxEntailmentCache;
|
|
55
55
|
this.sparqlService = resolved.createSparqlService(this);
|
|
56
|
+
if (this.sparqlService instanceof SparqlService) {
|
|
57
|
+
this.sparqlService.setOntologyIriResolver((modelUri) => this.resolveOntologyIriForModelUri(modelUri));
|
|
58
|
+
}
|
|
56
59
|
this.ontologyModelIndex = getOntologyModelIndex(services.shared);
|
|
57
60
|
|
|
58
61
|
services.shared.workspace.DocumentBuilder.onDocumentPhase(DocumentState.Validated, (document) => {
|
|
@@ -677,15 +680,13 @@ function isWorkspaceModelUri(modelUri: string): boolean {
|
|
|
677
680
|
}
|
|
678
681
|
|
|
679
682
|
export function resolveCanonicalWorkspaceModelUri(modelUri: string): string {
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
}
|
|
685
|
-
return parsed.with({ query: '', fragment: '' }).toString();
|
|
686
|
-
} catch {
|
|
687
|
-
return modelUri;
|
|
683
|
+
const windowsFileUri = /^file:\/\/\/([A-Za-z]):\/(.*)$/u.exec(modelUri);
|
|
684
|
+
if (windowsFileUri) {
|
|
685
|
+
const drive = windowsFileUri[1].toLowerCase();
|
|
686
|
+
const rest = windowsFileUri[2];
|
|
687
|
+
return `file:///${drive}%3A/${rest}`;
|
|
688
688
|
}
|
|
689
|
+
return modelUri;
|
|
689
690
|
}
|
|
690
691
|
|
|
691
692
|
const BUILT_IN_ONTOLOGIES = new Set([
|
package/src/owl/owl-shacl.ts
CHANGED
|
@@ -14,8 +14,8 @@ import type {
|
|
|
14
14
|
export const ShaclValidateRequest = 'oml/validate';
|
|
15
15
|
|
|
16
16
|
export interface ShaclValidateParams {
|
|
17
|
-
modelUri
|
|
18
|
-
|
|
17
|
+
modelUri: string;
|
|
18
|
+
shacl: string;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export type ShaclValidationIssue = OwlShaclValidationIssue;
|
|
@@ -29,6 +29,8 @@ export type DerivedShaclSelectQuery = {
|
|
|
29
29
|
|
|
30
30
|
type ParsedShaclColumn = {
|
|
31
31
|
path: string;
|
|
32
|
+
pathVariableHint?: string;
|
|
33
|
+
readOnly: boolean;
|
|
32
34
|
name?: string;
|
|
33
35
|
};
|
|
34
36
|
|
|
@@ -36,7 +38,6 @@ type ParsedShaclNodeShape = {
|
|
|
36
38
|
targetClasses: Term[];
|
|
37
39
|
targetNodes: Term[];
|
|
38
40
|
columns: ParsedShaclColumn[];
|
|
39
|
-
includeEntailments: boolean;
|
|
40
41
|
};
|
|
41
42
|
|
|
42
43
|
type ConnectionLike = {
|
|
@@ -59,7 +60,15 @@ const SH_PATH = 'http://www.w3.org/ns/shacl#path';
|
|
|
59
60
|
const SH_PROPERTY = 'http://www.w3.org/ns/shacl#property';
|
|
60
61
|
const SH_TARGET_CLASS = 'http://www.w3.org/ns/shacl#targetClass';
|
|
61
62
|
const SH_TARGET_NODE = 'http://www.w3.org/ns/shacl#targetNode';
|
|
62
|
-
const
|
|
63
|
+
const SH_ALTERNATIVE_PATH = 'http://www.w3.org/ns/shacl#alternativePath';
|
|
64
|
+
const SH_INVERSE_PATH = 'http://www.w3.org/ns/shacl#inversePath';
|
|
65
|
+
const SH_ZERO_OR_MORE_PATH = 'http://www.w3.org/ns/shacl#zeroOrMorePath';
|
|
66
|
+
const SH_ONE_OR_MORE_PATH = 'http://www.w3.org/ns/shacl#oneOrMorePath';
|
|
67
|
+
const SH_ZERO_OR_ONE_PATH = 'http://www.w3.org/ns/shacl#zeroOrOnePath';
|
|
68
|
+
const DASH_READ_ONLY = 'http://datashapes.org/dash#readOnly';
|
|
69
|
+
const RDF_FIRST = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#first';
|
|
70
|
+
const RDF_REST = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#rest';
|
|
71
|
+
const RDF_NIL = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#nil';
|
|
63
72
|
export class ShaclService implements OwlShaclService {
|
|
64
73
|
constructor(
|
|
65
74
|
private readonly sparqlService: OwlSparqlService,
|
|
@@ -67,15 +76,7 @@ export class ShaclService implements OwlShaclService {
|
|
|
67
76
|
private readonly resolveContextIri: (modelUri: string) => string,
|
|
68
77
|
) {}
|
|
69
78
|
|
|
70
|
-
async validateShacl(modelUri: string
|
|
71
|
-
if (!modelUri) {
|
|
72
|
-
return {
|
|
73
|
-
conforms: false,
|
|
74
|
-
issues: [],
|
|
75
|
-
error: 'No contextUri model is available for this markdown document.',
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
+
async validateShacl(modelUri: string, shaclSource: string): Promise<ShaclValidationResult> {
|
|
79
80
|
const trimmedSource = stripShaclFrontMatter(shaclSource).trim();
|
|
80
81
|
if (!trimmedSource) {
|
|
81
82
|
return {
|
|
@@ -105,8 +106,8 @@ export class ShaclService implements OwlShaclService {
|
|
|
105
106
|
}
|
|
106
107
|
|
|
107
108
|
await this.prepareContext(modelUri);
|
|
108
|
-
const
|
|
109
|
-
const constructResult = await this.sparqlService.construct(modelUri, buildValidationConstructQuery(
|
|
109
|
+
const contextOntologyIri = this.resolveContextIri(modelUri);
|
|
110
|
+
const constructResult = await this.sparqlService.construct(modelUri, buildValidationConstructQuery(contextOntologyIri));
|
|
110
111
|
if (!constructResult.success) {
|
|
111
112
|
return {
|
|
112
113
|
conforms: false,
|
|
@@ -172,8 +173,15 @@ export function registerShaclValidationRequests(connection: ConnectionLike, oml:
|
|
|
172
173
|
error: 'Reasoning service validation is unavailable.',
|
|
173
174
|
};
|
|
174
175
|
}
|
|
175
|
-
const modelUri = typeof params?.modelUri === 'string' ? params.modelUri :
|
|
176
|
-
const shaclSource = typeof params?.
|
|
176
|
+
const modelUri = typeof params?.modelUri === 'string' ? params.modelUri.trim() : '';
|
|
177
|
+
const shaclSource = typeof params?.shacl === 'string' ? params.shacl : '';
|
|
178
|
+
if (!modelUri) {
|
|
179
|
+
return {
|
|
180
|
+
conforms: false,
|
|
181
|
+
issues: [],
|
|
182
|
+
error: 'Missing modelUri.',
|
|
183
|
+
};
|
|
184
|
+
}
|
|
177
185
|
const shaclService = createDefaultShaclService(
|
|
178
186
|
reasoningService.getSparqlService(),
|
|
179
187
|
(uri) => reasoningService.ensureQueryContext(uri),
|
|
@@ -199,13 +207,14 @@ export function deriveSelectQueryFromShacl(shaclSource: string, graphIri: string
|
|
|
199
207
|
|
|
200
208
|
const usedNames = new Set<string>(['focus']);
|
|
201
209
|
const variables = nodeShape.columns.map((column) => {
|
|
202
|
-
const base = normalizeSparqlVariableName(column.name ||
|
|
210
|
+
const base = normalizeSparqlVariableName(column.name || column.pathVariableHint || 'value');
|
|
203
211
|
const unique = uniqueVariableName(base, usedNames);
|
|
204
212
|
usedNames.add(unique);
|
|
205
213
|
const sourceVariable = uniqueVariableName(`__${unique}`, usedNames);
|
|
206
214
|
usedNames.add(sourceVariable);
|
|
207
215
|
return {
|
|
208
216
|
path: column.path,
|
|
217
|
+
readOnly: column.readOnly,
|
|
209
218
|
variable: unique,
|
|
210
219
|
sourceVariable,
|
|
211
220
|
name: column.name,
|
|
@@ -221,9 +230,7 @@ export function deriveSelectQueryFromShacl(shaclSource: string, graphIri: string
|
|
|
221
230
|
const whereLines: string[] = [];
|
|
222
231
|
const graphLines: string[] = [];
|
|
223
232
|
const graphPattern = (pattern: string): string =>
|
|
224
|
-
|
|
225
|
-
? `{ GRAPH <${escapeIriForSparql(graphIri)}> { ${pattern} } } UNION { GRAPH <${escapeIriForSparql(`${graphIri}__entailments`)}> { ${pattern} } }`
|
|
226
|
-
: `GRAPH <${escapeIriForSparql(graphIri)}> { ${pattern} }`;
|
|
233
|
+
`GRAPH <${escapeIriForSparql(graphIri)}> { ${pattern} }`;
|
|
227
234
|
|
|
228
235
|
if (nodeShape.targetClasses.length > 0) {
|
|
229
236
|
const values = nodeShape.targetClasses.map((term: Term) => `<${term.value}>`).join(' ');
|
|
@@ -237,7 +244,12 @@ export function deriveSelectQueryFromShacl(shaclSource: string, graphIri: string
|
|
|
237
244
|
}
|
|
238
245
|
|
|
239
246
|
for (const entry of variables) {
|
|
240
|
-
|
|
247
|
+
const pattern = `?focus ${entry.path} ?${entry.sourceVariable} .`;
|
|
248
|
+
if (entry.readOnly) {
|
|
249
|
+
graphLines.push(` OPTIONAL { ${pattern} }`);
|
|
250
|
+
} else {
|
|
251
|
+
graphLines.push(` OPTIONAL { ${graphPattern(pattern)} }`);
|
|
252
|
+
}
|
|
241
253
|
}
|
|
242
254
|
whereLines.push(...graphLines);
|
|
243
255
|
|
|
@@ -260,7 +272,7 @@ export function deriveSelectQueryFromShacl(shaclSource: string, graphIri: string
|
|
|
260
272
|
};
|
|
261
273
|
}
|
|
262
274
|
|
|
263
|
-
function buildValidationConstructQuery(
|
|
275
|
+
function buildValidationConstructQuery(contextOntologyIri: string): string {
|
|
264
276
|
return [
|
|
265
277
|
'CONSTRUCT {',
|
|
266
278
|
' ?s ?p ?o .',
|
|
@@ -268,11 +280,11 @@ function buildValidationConstructQuery(contextIri: string): string {
|
|
|
268
280
|
' ?o a ?oType .',
|
|
269
281
|
'}',
|
|
270
282
|
'WHERE {',
|
|
271
|
-
` GRAPH <${
|
|
272
|
-
` OPTIONAL { GRAPH ?g1 { ?s a ?sType } FILTER(?g1 != <${
|
|
283
|
+
` GRAPH <${contextOntologyIri}> { ?s ?p ?o }`,
|
|
284
|
+
` OPTIONAL { GRAPH ?g1 { ?s a ?sType } FILTER(?g1 != <${contextOntologyIri}>) }`,
|
|
273
285
|
' OPTIONAL {',
|
|
274
286
|
' FILTER(isIRI(?o) || isBlank(?o))',
|
|
275
|
-
` GRAPH ?g2 { ?o a ?oType } FILTER(?g2 != <${
|
|
287
|
+
` GRAPH ?g2 { ?o a ?oType } FILTER(?g2 != <${contextOntologyIri}>)`,
|
|
276
288
|
' }',
|
|
277
289
|
'}',
|
|
278
290
|
].join('\n');
|
|
@@ -299,24 +311,27 @@ function parsePrimaryNodeShape(shapeQuads: readonly Quad[]): ParsedShaclNodeShap
|
|
|
299
311
|
.filter((term) => !isDeactivatedShape(shapesStore, term))
|
|
300
312
|
.map((term): ParsedShaclColumn | undefined => {
|
|
301
313
|
const path = getFirstObject(shapesStore, term, SH_PATH);
|
|
302
|
-
if (!path
|
|
314
|
+
if (!path) {
|
|
315
|
+
return undefined;
|
|
316
|
+
}
|
|
317
|
+
const parsedPath = parseShaclPathToSparql(shapesStore, path);
|
|
318
|
+
if (!parsedPath) {
|
|
303
319
|
return undefined;
|
|
304
320
|
}
|
|
305
321
|
const nameTerm = getFirstObject(shapesStore, term, SH_NAME);
|
|
306
322
|
return {
|
|
307
|
-
path:
|
|
323
|
+
path: parsedPath.sparql,
|
|
324
|
+
pathVariableHint: parsedPath.variableHint,
|
|
325
|
+
readOnly: isReadOnlyShape(shapesStore, term),
|
|
308
326
|
name: nameTerm && nameTerm.termType === 'Literal' ? nameTerm.value : undefined,
|
|
309
327
|
};
|
|
310
328
|
})
|
|
311
329
|
.filter((column): column is ParsedShaclColumn => Boolean(column));
|
|
312
|
-
const includeEntailments = getObjects(shapesStore, shapeTerm, SH_PROPERTY)
|
|
313
|
-
.some((term) => isEntailedShape(shapesStore, term));
|
|
314
330
|
|
|
315
331
|
return {
|
|
316
332
|
targetClasses,
|
|
317
333
|
targetNodes,
|
|
318
334
|
columns,
|
|
319
|
-
includeEntailments,
|
|
320
335
|
};
|
|
321
336
|
}
|
|
322
337
|
|
|
@@ -353,8 +368,8 @@ function isTruthyLiteral(term: Term | undefined): boolean {
|
|
|
353
368
|
return term?.termType === 'Literal' && (term.value === 'true' || term.value === '1');
|
|
354
369
|
}
|
|
355
370
|
|
|
356
|
-
function
|
|
357
|
-
return getObjects(store, subject,
|
|
371
|
+
function isReadOnlyShape(store: N3Store, subject: Term): boolean {
|
|
372
|
+
return getObjects(store, subject, DASH_READ_ONLY).some((term) => isTruthyLiteral(term));
|
|
358
373
|
}
|
|
359
374
|
|
|
360
375
|
function getObjects(store: N3Store, subject: Term, predicateIri: string): Term[] {
|
|
@@ -511,3 +526,127 @@ function localName(iri: string): string {
|
|
|
511
526
|
function escapeIriForSparql(iri: string): string {
|
|
512
527
|
return iri.replace(/>/g, '\\>');
|
|
513
528
|
}
|
|
529
|
+
|
|
530
|
+
type ParsedSparqlPath = {
|
|
531
|
+
sparql: string;
|
|
532
|
+
variableHint: string;
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
function parseShaclPathToSparql(store: N3Store, term: Term): ParsedSparqlPath | undefined {
|
|
536
|
+
if (term.termType === 'NamedNode') {
|
|
537
|
+
return {
|
|
538
|
+
sparql: `<${term.value}>`,
|
|
539
|
+
variableHint: localName(term.value),
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
const sequence = parseRdfList(store, term);
|
|
544
|
+
if (sequence && sequence.length > 0) {
|
|
545
|
+
const parts = sequence
|
|
546
|
+
.map((entry) => parseShaclPathToSparql(store, entry))
|
|
547
|
+
.filter((entry): entry is ParsedSparqlPath => Boolean(entry));
|
|
548
|
+
if (parts.length !== sequence.length) {
|
|
549
|
+
return undefined;
|
|
550
|
+
}
|
|
551
|
+
return {
|
|
552
|
+
sparql: parts.map((entry) => wrapPath(entry.sparql)).join('/'),
|
|
553
|
+
variableHint: parts[parts.length - 1]?.variableHint ?? 'value',
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
if (term.termType !== 'BlankNode') {
|
|
558
|
+
return undefined;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
const alternativePath = getFirstObject(store, term, SH_ALTERNATIVE_PATH);
|
|
562
|
+
if (alternativePath) {
|
|
563
|
+
const options = parseRdfList(store, alternativePath);
|
|
564
|
+
if (!options || options.length === 0) {
|
|
565
|
+
return undefined;
|
|
566
|
+
}
|
|
567
|
+
const parts = options
|
|
568
|
+
.map((entry) => parseShaclPathToSparql(store, entry))
|
|
569
|
+
.filter((entry): entry is ParsedSparqlPath => Boolean(entry));
|
|
570
|
+
if (parts.length !== options.length) {
|
|
571
|
+
return undefined;
|
|
572
|
+
}
|
|
573
|
+
const hintParts = [...new Set(parts.map((entry) => entry.variableHint).filter(Boolean))];
|
|
574
|
+
return {
|
|
575
|
+
sparql: parts.map((entry) => wrapPath(entry.sparql)).join('|'),
|
|
576
|
+
variableHint: hintParts.length > 0 ? hintParts.join('_or_') : 'value',
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
const inversePath = getFirstObject(store, term, SH_INVERSE_PATH);
|
|
581
|
+
if (inversePath) {
|
|
582
|
+
const parsed = parseShaclPathToSparql(store, inversePath);
|
|
583
|
+
if (!parsed) {
|
|
584
|
+
return undefined;
|
|
585
|
+
}
|
|
586
|
+
return {
|
|
587
|
+
sparql: `^${wrapPath(parsed.sparql)}`,
|
|
588
|
+
variableHint: parsed.variableHint,
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
const quantified = [
|
|
593
|
+
{ predicate: SH_ZERO_OR_MORE_PATH, quantifier: '*' },
|
|
594
|
+
{ predicate: SH_ONE_OR_MORE_PATH, quantifier: '+' },
|
|
595
|
+
{ predicate: SH_ZERO_OR_ONE_PATH, quantifier: '?' },
|
|
596
|
+
] as const;
|
|
597
|
+
for (const entry of quantified) {
|
|
598
|
+
const target = getFirstObject(store, term, entry.predicate);
|
|
599
|
+
if (!target) {
|
|
600
|
+
continue;
|
|
601
|
+
}
|
|
602
|
+
const parsed = parseShaclPathToSparql(store, target);
|
|
603
|
+
if (!parsed) {
|
|
604
|
+
return undefined;
|
|
605
|
+
}
|
|
606
|
+
return {
|
|
607
|
+
sparql: `${wrapPath(parsed.sparql)}${entry.quantifier}`,
|
|
608
|
+
variableHint: parsed.variableHint,
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
return undefined;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function parseRdfList(store: N3Store, term: Term): Term[] | undefined {
|
|
616
|
+
if (term.termType !== 'BlankNode' && term.termType !== 'NamedNode') {
|
|
617
|
+
return undefined;
|
|
618
|
+
}
|
|
619
|
+
if (term.termType === 'NamedNode' && term.value === RDF_NIL) {
|
|
620
|
+
return [];
|
|
621
|
+
}
|
|
622
|
+
const visited = new Set<string>();
|
|
623
|
+
const items: Term[] = [];
|
|
624
|
+
let current: Term = term;
|
|
625
|
+
while (true) {
|
|
626
|
+
if (current.termType === 'NamedNode' && current.value === RDF_NIL) {
|
|
627
|
+
return items;
|
|
628
|
+
}
|
|
629
|
+
const key = termKey(current);
|
|
630
|
+
if (!key || visited.has(key)) {
|
|
631
|
+
return undefined;
|
|
632
|
+
}
|
|
633
|
+
visited.add(key);
|
|
634
|
+
const head = getFirstObject(store, current, RDF_FIRST);
|
|
635
|
+
const tail = getFirstObject(store, current, RDF_REST);
|
|
636
|
+
if (!head || !tail) {
|
|
637
|
+
return undefined;
|
|
638
|
+
}
|
|
639
|
+
items.push(head);
|
|
640
|
+
current = tail;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
function wrapPath(path: string): string {
|
|
645
|
+
if (!path || /^<[^>]+>$/.test(path)) {
|
|
646
|
+
return path;
|
|
647
|
+
}
|
|
648
|
+
if (/^\^[^|/*+?]+$/.test(path)) {
|
|
649
|
+
return path;
|
|
650
|
+
}
|
|
651
|
+
return `(${path})`;
|
|
652
|
+
}
|