@oml/owl 0.16.6 → 0.18.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.
@@ -52,6 +52,11 @@ const OML = {
52
52
  hasTarget: namedNode('http://opencaesar.io/oml#hasTarget'),
53
53
  };
54
54
 
55
+ export interface HasValueRestriction {
56
+ property: string;
57
+ value: { kind: 'named'; iri: string } | { kind: 'literal'; value: string; datatype: string; language: string };
58
+ }
59
+
55
60
  export interface TBoxIndex {
56
61
  superClasses: Map<string, string[]>;
57
62
  subProperties: Map<string, string[]>;
@@ -64,6 +69,7 @@ export interface TBoxIndex {
64
69
  transitiveProperties: Set<string>;
65
70
  functionalProperties: Set<string>;
66
71
  inverseFunctionalProperties: Set<string>;
72
+ hasValueRestrictions: Map<string, HasValueRestriction[]>;
67
73
  }
68
74
 
69
75
  export interface ForwardRuleDefinition {
@@ -152,6 +158,42 @@ export class TBoxIndexBuilder {
152
158
  }
153
159
  }
154
160
 
161
+ // cls-hv1: collect owl:hasValue restrictions from blank node restriction nodes.
162
+ // Pattern: C rdfs:subClassOf _:r, _:r owl:onProperty P, _:r owl:hasValue V
163
+ const bnodeProperty = new Map<string, string>();
164
+ const bnodeValue = new Map<string, HasValueRestriction['value']>();
165
+ const classToRestrictionBnodes = new Map<string, string[]>();
166
+ for (const q of quads) {
167
+ if (q.subject.termType === 'BlankNode') {
168
+ if (q.predicate.value === 'http://www.w3.org/2002/07/owl#onProperty' && q.object.termType === 'NamedNode') {
169
+ bnodeProperty.set(q.subject.value, q.object.value);
170
+ } else if (q.predicate.value === 'http://www.w3.org/2002/07/owl#hasValue') {
171
+ if (q.object.termType === 'NamedNode') {
172
+ bnodeValue.set(q.subject.value, { kind: 'named', iri: q.object.value });
173
+ } else if (q.object.termType === 'Literal') {
174
+ bnodeValue.set(q.subject.value, { kind: 'literal', value: q.object.value, datatype: q.object.datatype?.value ?? '', language: q.object.language ?? '' });
175
+ }
176
+ }
177
+ } else if (q.subject.termType === 'NamedNode' && q.predicate.value === RDFS.subClassOf.value && q.object.termType === 'BlankNode') {
178
+ const list = classToRestrictionBnodes.get(q.subject.value) ?? [];
179
+ list.push(q.object.value);
180
+ classToRestrictionBnodes.set(q.subject.value, list);
181
+ }
182
+ }
183
+ for (const [classIri, bnodeIds] of classToRestrictionBnodes) {
184
+ for (const bnodeId of bnodeIds) {
185
+ const property = bnodeProperty.get(bnodeId);
186
+ const value = bnodeValue.get(bnodeId);
187
+ if (property && value) {
188
+ const list = index.hasValueRestrictions.get(classIri) ?? [];
189
+ if (!list.some((r) => r.property === property && this.sameHasValue(r.value, value))) {
190
+ list.push({ property, value });
191
+ index.hasValueRestrictions.set(classIri, list);
192
+ }
193
+ }
194
+ }
195
+ }
196
+
155
197
  if (ontology && isVocabulary(ontology)) {
156
198
  index.forwardRules.push(...this.buildForwardRules(ontology));
157
199
  }
@@ -189,6 +231,7 @@ export class TBoxIndexBuilder {
189
231
  this.mergeSet(merged.transitiveProperties, own.transitiveProperties);
190
232
  this.mergeSet(merged.functionalProperties, own.functionalProperties);
191
233
  this.mergeSet(merged.inverseFunctionalProperties, own.inverseFunctionalProperties);
234
+ this.mergeHasValueRestrictions(merged.hasValueRestrictions, own.hasValueRestrictions);
192
235
  }
193
236
 
194
237
  this.closeSuperClassesTransitively(merged.superClasses);
@@ -224,9 +267,23 @@ export class TBoxIndexBuilder {
224
267
  transitiveProperties: new Set<string>(),
225
268
  functionalProperties: new Set<string>(),
226
269
  inverseFunctionalProperties: new Set<string>(),
270
+ hasValueRestrictions: new Map<string, HasValueRestriction[]>(),
227
271
  };
228
272
  }
229
273
 
274
+ private sameHasValue(a: HasValueRestriction['value'], b: HasValueRestriction['value']): boolean {
275
+ if (a.kind !== b.kind) {
276
+ return false;
277
+ }
278
+ if (a.kind === 'named' && b.kind === 'named') {
279
+ return a.iri === b.iri;
280
+ }
281
+ if (a.kind === 'literal' && b.kind === 'literal') {
282
+ return a.value === b.value && a.datatype === b.datatype && a.language === b.language;
283
+ }
284
+ return false;
285
+ }
286
+
230
287
  private mergeStringArrayMap(target: Map<string, string[]>, source: Map<string, string[]>): void {
231
288
  for (const [key, values] of source.entries()) {
232
289
  const existing = target.get(key) ?? [];
@@ -245,6 +302,18 @@ export class TBoxIndexBuilder {
245
302
  }
246
303
  }
247
304
 
305
+ private mergeHasValueRestrictions(target: Map<string, HasValueRestriction[]>, source: Map<string, HasValueRestriction[]>): void {
306
+ for (const [classIri, restrictions] of source.entries()) {
307
+ const existing = target.get(classIri) ?? [];
308
+ for (const r of restrictions) {
309
+ if (!existing.some((e) => e.property === r.property && this.sameHasValue(e.value, r.value))) {
310
+ existing.push(r);
311
+ }
312
+ }
313
+ target.set(classIri, existing);
314
+ }
315
+ }
316
+
248
317
  private mergeForwardRules(target: ForwardRuleDefinition[], source: ForwardRuleDefinition[]): void {
249
318
  const merged = new Map<string, ForwardRuleDefinition>();
250
319
  for (const rule of target) {
@@ -393,7 +462,10 @@ export class TBoxIndexBuilder {
393
462
  return { kind: 'literal', value: String(literalValue.value), datatype: 'http://www.w3.org/2001/XMLSchema#decimal', language: '' };
394
463
  }
395
464
  if (isDoubleLiteral(literalValue)) {
396
- return { kind: 'literal', value: String(literalValue.value), datatype: 'http://www.w3.org/2001/XMLSchema#double', language: '' };
465
+ const v = literalValue.value;
466
+ const doubleStr = typeof v === 'string' ? v
467
+ : Number.isInteger(v as number) ? `${v}.0e0` : `${v}e0`;
468
+ return { kind: 'literal', value: doubleStr, datatype: 'http://www.w3.org/2001/XMLSchema#double', language: '' };
397
469
  }
398
470
  if (isQuotedLiteral(literalValue)) {
399
471
  if (literalValue.type) {