@oml/owl 0.9.0 → 0.10.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.
@@ -257,8 +257,8 @@ const BUILT_IN_ONTOLOGIES = new Set([
257
257
  'http://www.w3.org/2003/11/swrlb',
258
258
  ]);
259
259
 
260
- const { namedNode, literal, quad } = DataFactory;
261
- type TermNode = ReturnType<typeof namedNode>;
260
+ const { blankNode, namedNode, literal, quad } = DataFactory;
261
+ type TermNode = ReturnType<typeof namedNode> | ReturnType<typeof blankNode>;
262
262
 
263
263
  function fnv1a(input: string): string {
264
264
  let hash = 2166136261;
@@ -578,24 +578,24 @@ export class Oml2OwlMapper {
578
578
  const sourceVar = this.toSwrlVariable('s', triples);
579
579
  const targetVar = this.toSwrlVariable('t', triples);
580
580
 
581
- const classAtom = skolemize(ctx.namespace, 'rule-atom', relationEntityIri, 'relationEntity', 'class');
581
+ const classAtom = blankNode();
582
582
  triples.push(this.quadWithGraph(classAtom, namedNode(RDF.type), namedNode(SWRL.ClassAtom)));
583
583
  triples.push(this.quadWithGraph(classAtom, namedNode(SWRL.classPredicate), namedNode(relationEntityIri)));
584
584
  triples.push(this.quadWithGraph(classAtom, namedNode(SWRL.argument1), relationVar));
585
585
 
586
- const sourceAtom = skolemize(ctx.namespace, 'rule-atom', relationEntityIri, 'relationEntity', 'source');
586
+ const sourceAtom = blankNode();
587
587
  triples.push(this.quadWithGraph(sourceAtom, namedNode(RDF.type), namedNode(SWRL.IndividualPropertyAtom)));
588
588
  triples.push(this.quadWithGraph(sourceAtom, namedNode(SWRL.propertyPredicate), namedNode(OML.hasSource)));
589
589
  triples.push(this.quadWithGraph(sourceAtom, namedNode(SWRL.argument1), relationVar));
590
590
  triples.push(this.quadWithGraph(sourceAtom, namedNode(SWRL.argument2), sourceVar));
591
591
 
592
- const targetAtom = skolemize(ctx.namespace, 'rule-atom', relationEntityIri, 'relationEntity', 'target');
592
+ const targetAtom = blankNode();
593
593
  triples.push(this.quadWithGraph(targetAtom, namedNode(RDF.type), namedNode(SWRL.IndividualPropertyAtom)));
594
594
  triples.push(this.quadWithGraph(targetAtom, namedNode(SWRL.propertyPredicate), namedNode(OML.hasTarget)));
595
595
  triples.push(this.quadWithGraph(targetAtom, namedNode(SWRL.argument1), relationVar));
596
596
  triples.push(this.quadWithGraph(targetAtom, namedNode(SWRL.argument2), targetVar));
597
597
 
598
- const headAtom = skolemize(ctx.namespace, 'rule-atom', relationEntityIri, 'relationEntity', 'forward');
598
+ const headAtom = blankNode();
599
599
  triples.push(this.quadWithGraph(headAtom, namedNode(RDF.type), namedNode(SWRL.IndividualPropertyAtom)));
600
600
  triples.push(this.quadWithGraph(headAtom, namedNode(SWRL.propertyPredicate), namedNode(forwardIri)));
601
601
  triples.push(this.quadWithGraph(headAtom, namedNode(SWRL.argument1), sourceVar));
@@ -635,7 +635,7 @@ export class Oml2OwlMapper {
635
635
  if (predicate.type.ref && isScalar(predicate.type.ref)) {
636
636
  const argument = this.toSwrlDArgument(predicate.argument, ctx, triples);
637
637
  if (!argument) return [];
638
- const atom = skolemize(ctx.namespace, 'rule-atom', atomSeed, 'type', 'dataRange', typeIri, this.termKey(argument));
638
+ const atom = blankNode();
639
639
  triples.push(this.quadWithGraph(atom, namedNode(RDF.type), namedNode(SWRL.DataRangeAtom)));
640
640
  triples.push(this.quadWithGraph(atom, namedNode(SWRL.dataRange), namedNode(typeIri)));
641
641
  triples.push(this.quadWithGraph(atom, namedNode(SWRL.argument1), argument));
@@ -644,7 +644,7 @@ export class Oml2OwlMapper {
644
644
 
645
645
  const argument = this.toSwrlIArgument(predicate.argument, ctx, triples);
646
646
  if (!argument) return [];
647
- const atom = skolemize(ctx.namespace, 'rule-atom', atomSeed, 'type', 'class', typeIri, this.termKey(argument));
647
+ const atom = blankNode();
648
648
  triples.push(this.quadWithGraph(atom, namedNode(RDF.type), namedNode(SWRL.ClassAtom)));
649
649
  triples.push(this.quadWithGraph(atom, namedNode(SWRL.classPredicate), namedNode(typeIri)));
650
650
  triples.push(this.quadWithGraph(atom, namedNode(SWRL.argument1), argument));
@@ -658,18 +658,18 @@ export class Oml2OwlMapper {
658
658
  const argument2 = this.toSwrlIArgument(predicate.argument2, ctx, triples);
659
659
  if (!typeIri || !argument || !argument1 || !argument2) return [];
660
660
 
661
- const typeAtom = skolemize(ctx.namespace, 'rule-atom', atomSeed, 'relationEntity', 'type', typeIri, this.termKey(argument));
661
+ const typeAtom = blankNode();
662
662
  triples.push(this.quadWithGraph(typeAtom, namedNode(RDF.type), namedNode(SWRL.ClassAtom)));
663
663
  triples.push(this.quadWithGraph(typeAtom, namedNode(SWRL.classPredicate), namedNode(typeIri)));
664
664
  triples.push(this.quadWithGraph(typeAtom, namedNode(SWRL.argument1), argument));
665
665
 
666
- const sourceAtom = skolemize(ctx.namespace, 'rule-atom', atomSeed, 'relationEntity', 'source', this.termKey(argument), this.termKey(argument1));
666
+ const sourceAtom = blankNode();
667
667
  triples.push(this.quadWithGraph(sourceAtom, namedNode(RDF.type), namedNode(SWRL.IndividualPropertyAtom)));
668
668
  triples.push(this.quadWithGraph(sourceAtom, namedNode(SWRL.propertyPredicate), namedNode(OML.hasSource)));
669
669
  triples.push(this.quadWithGraph(sourceAtom, namedNode(SWRL.argument1), argument));
670
670
  triples.push(this.quadWithGraph(sourceAtom, namedNode(SWRL.argument2), argument1));
671
671
 
672
- const targetAtom = skolemize(ctx.namespace, 'rule-atom', atomSeed, 'relationEntity', 'target', this.termKey(argument), this.termKey(argument2));
672
+ const targetAtom = blankNode();
673
673
  triples.push(this.quadWithGraph(targetAtom, namedNode(RDF.type), namedNode(SWRL.IndividualPropertyAtom)));
674
674
  triples.push(this.quadWithGraph(targetAtom, namedNode(SWRL.propertyPredicate), namedNode(OML.hasTarget)));
675
675
  triples.push(this.quadWithGraph(targetAtom, namedNode(SWRL.argument1), argument));
@@ -686,9 +686,7 @@ export class Oml2OwlMapper {
686
686
  const isDataPredicate =
687
687
  (predicate.property.ref && isScalarProperty(predicate.property.ref)) ||
688
688
  (predicate.property.ref && isAnnotationProperty(predicate.property.ref) && Boolean(predicate.argument2.literal));
689
- const atom = isDataPredicate
690
- ? skolemize(ctx.namespace, 'rule-atom', atomSeed, 'property', 'data', propertyIri, this.termKey(argument1))
691
- : skolemize(ctx.namespace, 'rule-atom', atomSeed, 'property', 'object', propertyIri, this.termKey(argument1));
689
+ const atom = blankNode();
692
690
  if (isDataPredicate) {
693
691
  const argument2 = this.toSwrlDArgument(predicate.argument2, ctx, triples);
694
692
  if (!argument2) return [];
@@ -711,7 +709,7 @@ export class Oml2OwlMapper {
711
709
  const argument1 = this.toSwrlIArgument(predicate.argument1, ctx, triples);
712
710
  const argument2 = this.toSwrlIArgument(predicate.argument2, ctx, triples);
713
711
  if (!argument1 || !argument2) return [];
714
- const atom = skolemize(ctx.namespace, 'rule-atom', atomSeed, 'sameAs', this.termKey(argument1), this.termKey(argument2));
712
+ const atom = blankNode();
715
713
  triples.push(this.quadWithGraph(atom, namedNode(RDF.type), namedNode(SWRL.SameIndividualAtom)));
716
714
  triples.push(this.quadWithGraph(atom, namedNode(SWRL.argument1), argument1));
717
715
  triples.push(this.quadWithGraph(atom, namedNode(SWRL.argument2), argument2));
@@ -722,7 +720,7 @@ export class Oml2OwlMapper {
722
720
  const argument1 = this.toSwrlIArgument(predicate.argument1, ctx, triples);
723
721
  const argument2 = this.toSwrlIArgument(predicate.argument2, ctx, triples);
724
722
  if (!argument1 || !argument2) return [];
725
- const atom = skolemize(ctx.namespace, 'rule-atom', atomSeed, 'differentFrom', this.termKey(argument1), this.termKey(argument2));
723
+ const atom = blankNode();
726
724
  triples.push(this.quadWithGraph(atom, namedNode(RDF.type), namedNode(SWRL.DifferentIndividualsAtom)));
727
725
  triples.push(this.quadWithGraph(atom, namedNode(SWRL.argument1), argument1));
728
726
  triples.push(this.quadWithGraph(atom, namedNode(SWRL.argument2), argument2));
@@ -735,7 +733,7 @@ export class Oml2OwlMapper {
735
733
  const argumentsList = predicate.arguments
736
734
  .map((argument) => this.toSwrlDArgument(argument, ctx, triples))
737
735
  .filter((argument): argument is Quad_Object => Boolean(argument));
738
- const atom = skolemize(ctx.namespace, 'rule-atom', atomSeed, 'builtin', builtInIri, ...argumentsList.map((argument) => this.termKey(argument)));
736
+ const atom = blankNode();
739
737
  triples.push(this.quadWithGraph(atom, namedNode(RDF.type), namedNode(SWRL.BuiltinAtom)));
740
738
  triples.push(this.quadWithGraph(atom, namedNode(SWRL.builtin), namedNode(builtInIri)));
741
739
  triples.push(this.quadWithGraph(atom, namedNode(SWRL.arguments), this.createRdfList(ctx, argumentsList, triples, `rule-builtin-args|${atomSeed}|${builtInIri}`)));
@@ -836,7 +834,7 @@ export class Oml2OwlMapper {
836
834
 
837
835
  private mapAnonymousInstance(
838
836
  instance: AnonymousInstance,
839
- node: ReturnType<typeof namedNode>,
837
+ node: TermNode,
840
838
  ctx: MappingContext,
841
839
  triples: Quad[],
842
840
  source?: TermNode,
@@ -947,7 +945,7 @@ export class Oml2OwlMapper {
947
945
  .filter((iri): iri is string => Boolean(iri))
948
946
  .map((iri) => namedNode(iri));
949
947
  if (!instanceNodes.length) return;
950
- const listHead = this.createRdfList(ctx, instanceNodes, triples, `instance-enum|${classNode.value}|${instanceNodes.map((node) => node.value).join('|')}`);
948
+ const listHead = this.createBlankNodeRdfList(instanceNodes, triples);
951
949
  triples.push(this.quadWithGraph(classNode, namedNode(OWL.oneOf), listHead));
952
950
  }
953
951
 
@@ -963,9 +961,8 @@ export class Oml2OwlMapper {
963
961
  .map((lit) => this.toLiteral(lit, ctx))
964
962
  .filter((node): node is Quad_Object => Boolean(node));
965
963
  if (!literalNodes.length) return;
966
- const literalSignature = literalNodes.map((node) => this.termKey(node)).join('|');
967
- const listHead = this.createRdfList(ctx, literalNodes, triples, `scalar-enum|${scalarNode.value}|${literalSignature}`);
968
- const dataRangeNode = skolemize(ctx.namespace, 'datatype-enum', scalarNode.value, literalSignature);
964
+ const listHead = this.createBlankNodeRdfList(literalNodes, triples);
965
+ const dataRangeNode = blankNode();
969
966
  triples.push(this.quadWithGraph(dataRangeNode, namedNode(RDF.type), namedNode(RDFS.Datatype)));
970
967
  triples.push(this.quadWithGraph(dataRangeNode, namedNode(OWL.oneOf), listHead));
971
968
  triples.push(this.quadWithGraph(scalarNode, namedNode(OWL.equivalentClass), dataRangeNode));
@@ -1005,7 +1002,7 @@ export class Oml2OwlMapper {
1005
1002
  axiom: ScalarEquivalenceAxiom,
1006
1003
  ctx: MappingContext,
1007
1004
  triples: Quad[],
1008
- ): Array<ReturnType<typeof namedNode>> {
1005
+ ): TermNode[] {
1009
1006
  const facets: Array<{ iri: string; value: Quad_Object }> = [];
1010
1007
 
1011
1008
  axiom.length.forEach((value) => {
@@ -1061,10 +1058,10 @@ export class Oml2OwlMapper {
1061
1058
 
1062
1059
  return datatypes.map((datatypeIri) => {
1063
1060
  const facetSignature = facets.map((facet) => `${facet.iri}=${this.termKey(facet.value)}`).join('|');
1064
- const restrictionNode = skolemize(ctx.namespace, 'restriction', 'datatype', datatypeIri, facetSignature);
1061
+ const restrictionNode = blankNode();
1065
1062
  triples.push(this.quadWithGraph(restrictionNode, namedNode(OWL.onDatatype), namedNode(datatypeIri)));
1066
1063
  const restrictionItems = facets.map((facet, index) => {
1067
- const facetNode = skolemize(ctx.namespace, 'restriction-facet', datatypeIri, String(index), facet.iri, this.termKey(facet.value));
1064
+ const facetNode = blankNode();
1068
1065
  triples.push(this.quadWithGraph(facetNode, namedNode(facet.iri), facet.value));
1069
1066
  return facetNode;
1070
1067
  });
@@ -1078,7 +1075,7 @@ export class Oml2OwlMapper {
1078
1075
  axiom: PropertyRestrictionAxiom,
1079
1076
  ctx: MappingContext,
1080
1077
  triples: Quad[],
1081
- ): ReturnType<typeof namedNode> | undefined {
1078
+ ): TermNode | undefined {
1082
1079
  if (isPropertyRangeRestrictionAxiom(axiom)) {
1083
1080
  return this.mapRangeRestriction(axiom, ctx, triples);
1084
1081
  }
@@ -1098,11 +1095,11 @@ export class Oml2OwlMapper {
1098
1095
  axiom: PropertyRangeRestrictionAxiom,
1099
1096
  ctx: MappingContext,
1100
1097
  triples: Quad[],
1101
- ): ReturnType<typeof namedNode> | undefined {
1098
+ ): TermNode | undefined {
1102
1099
  const propertyIri = this.resolveIri(axiom.property, ctx);
1103
1100
  const rangeIri = this.resolveIri(axiom.range, ctx);
1104
1101
  if (!propertyIri || !rangeIri) return undefined;
1105
- const node = skolemize(ctx.namespace, 'restriction', propertyIri, axiom.kind, rangeIri, 'range');
1102
+ const node = blankNode();
1106
1103
 
1107
1104
  triples.push(this.quadWithGraph(node, namedNode(RDF.type), namedNode(OWL.Restriction)));
1108
1105
  triples.push(this.quadWithGraph(node, namedNode(OWL.onProperty), namedNode(propertyIri)));
@@ -1115,12 +1112,12 @@ export class Oml2OwlMapper {
1115
1112
  axiom: PropertyCardinalityRestrictionAxiom,
1116
1113
  ctx: MappingContext,
1117
1114
  triples: Quad[],
1118
- ): ReturnType<typeof namedNode> | undefined {
1115
+ ): TermNode | undefined {
1119
1116
  const propertyIri = this.resolveIri(axiom.property, ctx);
1120
1117
  if (!propertyIri) return undefined;
1121
1118
  const resolvedProperty = (axiom.property as any)?.ref as SemanticProperty | undefined;
1122
1119
  const rangeIri = this.resolveIri(axiom.range, ctx) ?? '';
1123
- const node = skolemize(ctx.namespace, 'restriction', propertyIri, axiom.kind, String(axiom.cardinality), rangeIri);
1120
+ const node = blankNode();
1124
1121
  triples.push(this.quadWithGraph(node, namedNode(RDF.type), namedNode(OWL.Restriction)));
1125
1122
  triples.push(this.quadWithGraph(node, namedNode(OWL.onProperty), namedNode(propertyIri)));
1126
1123
  if (rangeIri) {
@@ -1141,15 +1138,10 @@ export class Oml2OwlMapper {
1141
1138
  axiom: PropertyValueRestrictionAxiom,
1142
1139
  ctx: MappingContext,
1143
1140
  triples: Quad[],
1144
- ): ReturnType<typeof namedNode> | undefined {
1141
+ ): TermNode | undefined {
1145
1142
  const propertyIri = this.resolveIri(axiom.property, ctx);
1146
1143
  if (!propertyIri) return undefined;
1147
- const valueSeed = axiom.literalValue
1148
- ? this.literalKey(axiom.literalValue, ctx)
1149
- : axiom.referencedValue
1150
- ? this.resolveIri(axiom.referencedValue, ctx) ?? ''
1151
- : 'contained';
1152
- const node = skolemize(ctx.namespace, 'restriction', propertyIri, 'hasValue', valueSeed);
1144
+ const node = blankNode();
1153
1145
  triples.push(this.quadWithGraph(node, namedNode(RDF.type), namedNode(OWL.Restriction)));
1154
1146
  triples.push(this.quadWithGraph(node, namedNode(OWL.onProperty), namedNode(propertyIri)));
1155
1147
 
@@ -1175,10 +1167,10 @@ export class Oml2OwlMapper {
1175
1167
  axiom: PropertySelfRestrictionAxiom,
1176
1168
  ctx: MappingContext,
1177
1169
  triples: Quad[],
1178
- ): ReturnType<typeof namedNode> | undefined {
1170
+ ): TermNode | undefined {
1179
1171
  const propertyIri = this.resolveIri(axiom.property, ctx);
1180
1172
  if (!propertyIri) return undefined;
1181
- const node = skolemize(ctx.namespace, 'restriction', propertyIri, 'hasSelf');
1173
+ const node = blankNode();
1182
1174
  triples.push(this.quadWithGraph(node, namedNode(RDF.type), namedNode(OWL.Restriction)));
1183
1175
  triples.push(this.quadWithGraph(node, namedNode(OWL.onProperty), namedNode(propertyIri)));
1184
1176
  triples.push(this.quadWithGraph(node, namedNode(OWL.hasSelf), literal('true', namedNode(XSD.boolean))));
@@ -1261,19 +1253,40 @@ export class Oml2OwlMapper {
1261
1253
  items: Quad_Object[],
1262
1254
  triples: Quad[],
1263
1255
  listSeed: string,
1264
- ): ReturnType<typeof namedNode> {
1256
+ ): TermNode {
1265
1257
  if (!items.length) {
1266
1258
  return namedNode(RDF.nil);
1267
1259
  }
1268
- const base = `${listSeed}|${items.map((item) => this.termKey(item)).join('|')}`;
1269
- const head = skolemize(ctx.namespace, 'list', base, '0');
1260
+ const head = blankNode();
1270
1261
  let current = head;
1271
1262
  items.forEach((item, index) => {
1272
1263
  triples.push(this.quadWithGraph(current, namedNode(RDF.first), item));
1273
1264
  if (index === items.length - 1) {
1274
1265
  triples.push(this.quadWithGraph(current, namedNode(RDF.rest), namedNode(RDF.nil)));
1275
1266
  } else {
1276
- const next = skolemize(ctx.namespace, 'list', base, String(index + 1));
1267
+ const next = blankNode();
1268
+ triples.push(this.quadWithGraph(current, namedNode(RDF.rest), next));
1269
+ current = next;
1270
+ }
1271
+ });
1272
+ return head;
1273
+ }
1274
+
1275
+ private createBlankNodeRdfList(
1276
+ items: Quad_Object[],
1277
+ triples: Quad[],
1278
+ ): ReturnType<typeof blankNode> | ReturnType<typeof namedNode> {
1279
+ if (!items.length) {
1280
+ return namedNode(RDF.nil);
1281
+ }
1282
+ const head = blankNode();
1283
+ let current = head;
1284
+ items.forEach((item, index) => {
1285
+ triples.push(this.quadWithGraph(current, namedNode(RDF.first), item));
1286
+ if (index === items.length - 1) {
1287
+ triples.push(this.quadWithGraph(current, namedNode(RDF.rest), namedNode(RDF.nil)));
1288
+ } else {
1289
+ const next = blankNode();
1277
1290
  triples.push(this.quadWithGraph(current, namedNode(RDF.rest), next));
1278
1291
  current = next;
1279
1292
  }
@@ -1286,7 +1299,7 @@ export class Oml2OwlMapper {
1286
1299
  items: Quad_Object[],
1287
1300
  triples: Quad[],
1288
1301
  listSeed: string,
1289
- ): ReturnType<typeof namedNode> {
1302
+ ): TermNode {
1290
1303
  const head = this.createRdfList(ctx, items, triples, listSeed);
1291
1304
  if (head.value === RDF.nil) {
1292
1305
  return head;
@@ -1295,12 +1308,11 @@ export class Oml2OwlMapper {
1295
1308
  while (current.value !== RDF.nil) {
1296
1309
  triples.push(this.quadWithGraph(current, namedNode(RDF.type), namedNode(SWRL.AtomList)));
1297
1310
  const next = triples.find(
1298
- (quad) => quad.subject.termType === 'NamedNode'
1299
- && quad.subject.value === current.value
1311
+ (quad) => quad.subject.value === current.value
1300
1312
  && quad.predicate.value === RDF.rest
1301
- && quad.object.termType === 'NamedNode',
1313
+ && (quad.object.termType === 'NamedNode' || quad.object.termType === 'BlankNode'),
1302
1314
  )?.object;
1303
- if (!next || next.termType !== 'NamedNode') {
1315
+ if (!next || (next.termType !== 'NamedNode' && next.termType !== 'BlankNode')) {
1304
1316
  break;
1305
1317
  }
1306
1318
  current = next;
@@ -1437,11 +1449,6 @@ export class Oml2OwlMapper {
1437
1449
  return `${term.termType}:${term.value}`;
1438
1450
  }
1439
1451
 
1440
- private literalKey(value: Literal, ctx: MappingContext): string {
1441
- const term = this.toLiteral(value, ctx);
1442
- return term ? this.termKey(term) : '';
1443
- }
1444
-
1445
1452
  private toLiteral(value: Literal, ctx: MappingContext): Quad_Object | undefined {
1446
1453
  if (isBooleanLiteral(value)) {
1447
1454
  return literal(String(value.value), namedNode(XSD.boolean));
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { DocumentState, URI, type LangiumDocument } from 'langium';
4
4
  import type { NamedNode, Quad } from 'n3';
5
- import { OntologyModelIndex, getOntologyModelIndex, isDescription, isDescriptionBundle, isOntology, isVocabulary, isVocabularyBundle, type Import, type OmlServices, type Ontology } from '@oml/language';
5
+ import { OmlIndex, getOntologyModelIndex, isDescription, isDescriptionBundle, isOntology, isVocabulary, isVocabularyBundle, type Import, type OmlServices, type Ontology } from '@oml/language';
6
6
  import { Oml2OwlMapper } from './owl-mapper.js';
7
7
  import { ReasoningStore } from './owl-store.js';
8
8
  import { ABoxChainer, ABoxEntailmentCache, type ChainingResult } from './owl-abox.js';
@@ -33,7 +33,7 @@ export class ReasoningService {
33
33
  private readonly aboxEntailmentCache: OwlABoxEntailmentState;
34
34
  private readonly importGraph: OwlImportGraph;
35
35
  private readonly sparqlService: OwlSparqlService;
36
- private readonly ontologyModelIndex: OntologyModelIndex;
36
+ private readonly ontologyModelIndex: OmlIndex;
37
37
  private readonly preparedModelAliases = new Map<string, string>();
38
38
  private readonly activeDocuments = new Set<string>();
39
39
  private readonly modelLoadInFlight = new Map<string, Promise<void>>();
@@ -166,11 +166,11 @@ export class ReasoningService {
166
166
  return { complete, iterations, newQuadsCount, validationWarnings: [] };
167
167
  }
168
168
 
169
- removeModel(modelUri: string): void {
169
+ onDocumentDeleted(document: LangiumDocument): void {
170
+ const modelUri = document.uri.toString();
170
171
  const dependents = this.importGraph.dependentsOf(modelUri);
171
172
  this.importGraph.remove(modelUri);
172
173
  this.reasoningStore.retractModel(modelUri);
173
- this.ontologyModelIndex.removeModel(modelUri);
174
174
  this.tboxIndexCache.invalidateOwn(modelUri);
175
175
  this.tboxIndexCache.invalidateMerged(modelUri);
176
176
  this.aboxEntailmentCache.invalidate(modelUri);
@@ -241,6 +241,27 @@ export class ReasoningService {
241
241
  await this.ensureContextDatasetReady(resolvedModelUri);
242
242
  }
243
243
 
244
+ async getContextMemberLabelSnapshot(modelUri: string): Promise<Record<string, string>> {
245
+ const resolvedModelUri = this.resolveQueryModelUri(modelUri);
246
+ await this.ensureWorkspaceBuilt();
247
+ await this.ensureModelLoaded(resolvedModelUri);
248
+ await this.ensureContextDatasetReady(resolvedModelUri);
249
+ const modelUris = [resolvedModelUri, ...this.importGraph.dependenciesOf(resolvedModelUri)]
250
+ .map((uri) => this.resolveDocumentModelUri(uri))
251
+ .filter((uri): uri is string => Boolean(uri));
252
+ await this.ensureOntologyDocumentsLoaded(modelUris);
253
+ return this.ontologyModelIndex.getMemberLabelSnapshot(modelUris);
254
+ }
255
+
256
+ async getWorkspaceMemberLabelSnapshot(): Promise<Record<string, string>> {
257
+ await this.ensureWorkspaceBuilt();
258
+ const modelUris = [...new Set(this.preparedModelAliases.values())]
259
+ .map((uri) => this.resolveDocumentModelUri(uri))
260
+ .filter((uri): uri is string => Boolean(uri));
261
+ await this.ensureOntologyDocumentsLoaded(modelUris);
262
+ return this.ontologyModelIndex.getMemberLabelSnapshot(modelUris);
263
+ }
264
+
244
265
  getContextIri(modelUri: string): string {
245
266
  return this.resolveOntologyIriForModelUri(this.resolveQueryModelUri(modelUri));
246
267
  }
@@ -248,16 +269,16 @@ export class ReasoningService {
248
269
  setActiveDocuments(modelUris: string[]): void {
249
270
  this.activeDocuments.clear();
250
271
  for (const modelUri of modelUris) {
251
- this.activeDocuments.add(modelUri);
272
+ this.activeDocuments.add(resolveCanonicalWorkspaceModelUri(modelUri));
252
273
  }
253
274
  }
254
275
 
255
276
  markDocumentActive(modelUri: string): void {
256
- this.activeDocuments.add(modelUri);
277
+ this.activeDocuments.add(resolveCanonicalWorkspaceModelUri(modelUri));
257
278
  }
258
279
 
259
280
  markDocumentInactive(modelUri: string): void {
260
- this.activeDocuments.delete(modelUri);
281
+ this.activeDocuments.delete(resolveCanonicalWorkspaceModelUri(modelUri));
261
282
  }
262
283
 
263
284
  private extractImportedModelUris(ontology: Ontology): string[] {
@@ -360,11 +381,13 @@ export class ReasoningService {
360
381
  if (!document) {
361
382
  try {
362
383
  document = await this.services.shared.workspace.LangiumDocuments.getOrCreateDocument(uri);
363
- } catch {
384
+ } catch (error) {
364
385
  // Another concurrent caller may have created it first.
365
386
  document = this.services.shared.workspace.LangiumDocuments.getDocument(uri);
366
387
  if (!document) {
367
- throw new Error(`Unable to access model document '${modelUri}'.`);
388
+ const wrapped = new Error(`Unable to access model document '${modelUri}'.`);
389
+ (wrapped as any).cause = error;
390
+ throw wrapped;
368
391
  }
369
392
  }
370
393
  }
@@ -384,6 +407,29 @@ export class ReasoningService {
384
407
  }
385
408
  }
386
409
 
410
+ private async ensureOntologyDocumentsLoaded(modelUris: Iterable<string>): Promise<void> {
411
+ const documents = this.services.shared.workspace.LangiumDocuments;
412
+ const builder = this.services.shared.workspace.DocumentBuilder;
413
+ const loadTargets: LangiumDocument[] = [];
414
+ for (const modelUri of new Set(modelUris)) {
415
+ const uri = URI.parse(modelUri);
416
+ const document = documents.getDocument(uri)
417
+ ?? (typeof documents.getOrCreateDocument === 'function'
418
+ ? await documents.getOrCreateDocument(uri)
419
+ : undefined);
420
+ if (!document) {
421
+ continue;
422
+ }
423
+ if ((document as any).state >= DocumentState.Linked) {
424
+ continue;
425
+ }
426
+ loadTargets.push(document);
427
+ }
428
+ if (loadTargets.length > 0) {
429
+ await builder.build(loadTargets, { validation: false });
430
+ }
431
+ }
432
+
387
433
  private async ensureWorkspaceBuilt(): Promise<void> {
388
434
  if (this.workspaceBuilt) {
389
435
  return;
@@ -495,11 +541,12 @@ export class ReasoningService {
495
541
  }
496
542
 
497
543
  async countContextDatasetQuads(modelUri: string): Promise<number> {
544
+ const resolvedModelUri = this.resolveQueryModelUri(modelUri);
498
545
  await this.ensureWorkspaceBuilt();
499
- await this.ensureModelLoaded(modelUri);
500
- await this.ensureContextDatasetReady(modelUri);
546
+ await this.ensureModelLoaded(resolvedModelUri);
547
+ await this.ensureContextDatasetReady(resolvedModelUri);
501
548
  const store = this.reasoningStore.getStore();
502
- const queryGraphs = this.resolveContextGraphs(modelUri);
549
+ const queryGraphs = this.resolveContextGraphs(resolvedModelUri);
503
550
  let total = 0;
504
551
  for (const graph of queryGraphs) {
505
552
  total += store.countQuads(null, null, null, graph);
@@ -508,16 +555,37 @@ export class ReasoningService {
508
555
  }
509
556
 
510
557
  private resolveWorkspaceModelUri(uri: string): string | undefined {
511
- const prepared = this.preparedModelAliases.get(uri);
558
+ const canonicalUri = resolveCanonicalWorkspaceModelUri(uri);
559
+ const prepared = this.preparedModelAliases.get(uri) ?? this.preparedModelAliases.get(canonicalUri);
512
560
  if (prepared) {
513
561
  return prepared;
514
562
  }
515
- if (isWorkspaceModelUri(uri)) {
516
- return uri;
563
+ if (isWorkspaceModelUri(canonicalUri)) {
564
+ return canonicalUri;
517
565
  }
518
566
  return this.ontologyModelIndex.resolveModelUri(uri);
519
567
  }
520
568
 
569
+ private resolveDocumentModelUri(uri: string): string | undefined {
570
+ const normalizedUri = uri.trim();
571
+ if (!normalizedUri) {
572
+ return undefined;
573
+ }
574
+ if (isWorkspaceModelUri(normalizedUri)) {
575
+ return normalizedUri;
576
+ }
577
+ const indexed = this.ontologyModelIndex.resolveModelUri(normalizedUri);
578
+ if (indexed) {
579
+ return indexed;
580
+ }
581
+ const canonicalUri = resolveCanonicalWorkspaceModelUri(normalizedUri);
582
+ const prepared = this.preparedModelAliases.get(normalizedUri) ?? this.preparedModelAliases.get(canonicalUri);
583
+ if (!prepared || prepared === normalizedUri) {
584
+ return undefined;
585
+ }
586
+ return this.ontologyModelIndex.resolveModelUri(prepared) ?? (isWorkspaceModelUri(prepared) ? prepared : undefined);
587
+ }
588
+
521
589
  private resolveLoadedDependencyOrder(modelUri: string): string[] {
522
590
  const ordered = [...this.importGraph.dependenciesOf(modelUri), modelUri];
523
591
  const seen = new Set<string>();
@@ -605,6 +673,18 @@ function isWorkspaceModelUri(modelUri: string): boolean {
605
673
  }
606
674
  }
607
675
 
676
+ export function resolveCanonicalWorkspaceModelUri(modelUri: string): string {
677
+ try {
678
+ const parsed = URI.parse(modelUri);
679
+ if (!parsed.path.toLowerCase().endsWith('.oml')) {
680
+ return modelUri;
681
+ }
682
+ return parsed.with({ query: '', fragment: '' }).toString();
683
+ } catch {
684
+ return modelUri;
685
+ }
686
+ }
687
+
608
688
  const BUILT_IN_ONTOLOGIES = new Set([
609
689
  'http://www.w3.org/2001/XMLSchema',
610
690
  'http://www.w3.org/1999/02/22-rdf-syntax-ns',
@@ -36,6 +36,7 @@ type ParsedShaclNodeShape = {
36
36
  targetClasses: Term[];
37
37
  targetNodes: Term[];
38
38
  columns: ParsedShaclColumn[];
39
+ includeEntailments: boolean;
39
40
  };
40
41
 
41
42
  type ConnectionLike = {
@@ -58,7 +59,7 @@ const SH_PATH = 'http://www.w3.org/ns/shacl#path';
58
59
  const SH_PROPERTY = 'http://www.w3.org/ns/shacl#property';
59
60
  const SH_TARGET_CLASS = 'http://www.w3.org/ns/shacl#targetClass';
60
61
  const SH_TARGET_NODE = 'http://www.w3.org/ns/shacl#targetNode';
61
-
62
+ const OML_ENTAILED = 'http://opencaesar.io/oml#entailed';
62
63
  export class ShaclService implements OwlShaclService {
63
64
  constructor(
64
65
  private readonly sparqlService: OwlSparqlService,
@@ -219,11 +220,15 @@ export function deriveSelectQueryFromShacl(shaclSource: string, graphIri: string
219
220
  ].join(' ');
220
221
  const whereLines: string[] = [];
221
222
  const graphLines: string[] = [];
223
+ const graphPattern = (pattern: string): string =>
224
+ nodeShape.includeEntailments
225
+ ? `{ GRAPH <${escapeIriForSparql(graphIri)}> { ${pattern} } } UNION { GRAPH <${escapeIriForSparql(`${graphIri}__entailments`)}> { ${pattern} } }`
226
+ : `GRAPH <${escapeIriForSparql(graphIri)}> { ${pattern} }`;
222
227
 
223
228
  if (nodeShape.targetClasses.length > 0) {
224
229
  const values = nodeShape.targetClasses.map((term: Term) => `<${term.value}>`).join(' ');
225
230
  whereLines.push(` VALUES ?__targetClass { ${values} }`);
226
- graphLines.push(' ?focus a ?__targetClass .');
231
+ graphLines.push(` ${graphPattern('?focus a ?__targetClass .')}`);
227
232
  }
228
233
 
229
234
  if (nodeShape.targetNodes.length > 0) {
@@ -232,12 +237,9 @@ export function deriveSelectQueryFromShacl(shaclSource: string, graphIri: string
232
237
  }
233
238
 
234
239
  for (const entry of variables) {
235
- graphLines.push(` OPTIONAL { ?focus <${entry.path}> ?${entry.sourceVariable} . }`);
240
+ graphLines.push(` OPTIONAL { ${graphPattern(`?focus <${entry.path}> ?${entry.sourceVariable} .`)} }`);
236
241
  }
237
-
238
- whereLines.push(` GRAPH <${escapeIriForSparql(graphIri)}> {`);
239
242
  whereLines.push(...graphLines);
240
- whereLines.push(' }');
241
243
 
242
244
  const columnVariables = ['focus', ...variables.map((entry) => entry.variable)];
243
245
  const columnLabels = new Map<string, string>([['focus', 'focus']]);
@@ -307,11 +309,14 @@ function parsePrimaryNodeShape(shapeQuads: readonly Quad[]): ParsedShaclNodeShap
307
309
  };
308
310
  })
309
311
  .filter((column): column is ParsedShaclColumn => Boolean(column));
312
+ const includeEntailments = getObjects(shapesStore, shapeTerm, SH_PROPERTY)
313
+ .some((term) => isEntailedShape(shapesStore, term));
310
314
 
311
315
  return {
312
316
  targetClasses,
313
317
  targetNodes,
314
318
  columns,
319
+ includeEntailments,
315
320
  };
316
321
  }
317
322
 
@@ -344,8 +349,12 @@ function isDeactivatedShape(store: N3Store, subject: Term): boolean {
344
349
  return getObjects(store, subject, SH_DEACTIVATED).some((term) => isTruthyLiteral(term));
345
350
  }
346
351
 
347
- function isTruthyLiteral(term: Term): boolean {
348
- return term.termType === 'Literal' && (term.value === 'true' || term.value === '1');
352
+ function isTruthyLiteral(term: Term | undefined): boolean {
353
+ return term?.termType === 'Literal' && (term.value === 'true' || term.value === '1');
354
+ }
355
+
356
+ function isEntailedShape(store: N3Store, subject: Term): boolean {
357
+ return getObjects(store, subject, OML_ENTAILED).some((term) => isTruthyLiteral(term));
349
358
  }
350
359
 
351
360
  function getObjects(store: N3Store, subject: Term, predicateIri: string): Term[] {