@oml/language 0.19.1 → 0.19.2
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/oml/index.d.ts +1 -0
- package/out/oml/index.js +1 -0
- package/out/oml/index.js.map +1 -1
- package/out/oml/oml-diagram.d.ts +2 -6
- package/out/oml/oml-diagram.js +12 -22
- package/out/oml/oml-diagram.js.map +1 -1
- package/out/oml/oml-identifiers.d.ts +1 -0
- package/out/oml/oml-identifiers.js +21 -0
- package/out/oml/oml-identifiers.js.map +1 -0
- package/out/oml/oml-index.d.ts +1 -0
- package/out/oml/oml-index.js +8 -0
- package/out/oml/oml-index.js.map +1 -1
- package/out/oml/oml-literals.d.ts +1 -0
- package/out/oml/oml-literals.js +12 -0
- package/out/oml/oml-literals.js.map +1 -0
- package/out/oml/oml-rename.js +1 -17
- package/out/oml/oml-rename.js.map +1 -1
- package/out/oml/oml-scope.d.ts +5 -1
- package/out/oml/oml-scope.js +57 -14
- package/out/oml/oml-scope.js.map +1 -1
- package/out/oml/oml-serializer.js +4 -68
- package/out/oml/oml-serializer.js.map +1 -1
- package/out/oml/oml-update.js +116 -39
- package/out/oml/oml-update.js.map +1 -1
- package/package.json +1 -1
- package/src/oml/index.ts +1 -0
- package/src/oml/oml-diagram.ts +17 -32
- package/src/oml/oml-identifiers.ts +26 -0
- package/src/oml/oml-index.ts +8 -0
- package/src/oml/oml-literals.ts +12 -0
- package/src/oml/oml-rename.ts +1 -19
- package/src/oml/oml-scope.ts +59 -19
- package/src/oml/oml-serializer.ts +4 -70
- package/src/oml/oml-update.ts +185 -39
package/src/oml/oml-scope.ts
CHANGED
|
@@ -101,10 +101,26 @@ export class OmlScopeComputation extends DefaultScopeComputation {
|
|
|
101
101
|
*/
|
|
102
102
|
export class OmlScopeProvider extends DefaultScopeProvider {
|
|
103
103
|
protected services: LangiumCoreServices;
|
|
104
|
+
// Descriptions for an ontology's *own* exported symbols, keyed by ontology then referenceType.
|
|
105
|
+
// WeakMap so a re-parsed ontology (new AST root) naturally drops its old entry.
|
|
106
|
+
private ownDescCache = new WeakMap<Ontology, Map<string, AstNodeDescription[]>>();
|
|
107
|
+
// Descriptions contributed by an ontology's *imports* (all imports combined), keyed by the
|
|
108
|
+
// importing ontology then referenceType. The `prefix:name` variant uses the importer's prefix,
|
|
109
|
+
// so this is correctly indexed on the importer side.
|
|
110
|
+
private importedDescCache = new WeakMap<Ontology, Map<string, AstNodeDescription[]>>();
|
|
104
111
|
|
|
105
112
|
constructor(services: LangiumCoreServices) {
|
|
106
113
|
super(services);
|
|
107
114
|
this.services = services;
|
|
115
|
+
// Wipe caches on any document update. Within a sustained linking pass (the hot phase)
|
|
116
|
+
// no documents are mutating, so the caches stay warm. After an update the importer-side
|
|
117
|
+
// cache could otherwise hold descriptions pointing at stale (re-parsed) imported nodes,
|
|
118
|
+
// so we reset rather than try to invalidate selectively.
|
|
119
|
+
const shared = (services as any).shared as LangiumSharedServices | undefined;
|
|
120
|
+
shared?.workspace.DocumentBuilder.onUpdate(() => {
|
|
121
|
+
this.ownDescCache = new WeakMap();
|
|
122
|
+
this.importedDescCache = new WeakMap();
|
|
123
|
+
});
|
|
108
124
|
}
|
|
109
125
|
|
|
110
126
|
override getScope(context: ReferenceInfo): Scope {
|
|
@@ -233,34 +249,52 @@ export class OmlScopeProvider extends DefaultScopeProvider {
|
|
|
233
249
|
return resolved;
|
|
234
250
|
}
|
|
235
251
|
|
|
236
|
-
private
|
|
252
|
+
private pushDescription(out: AstNodeDescription[], node: any, name: string, document: LangiumDocument): void {
|
|
237
253
|
try {
|
|
238
|
-
|
|
239
|
-
return this.createScope(stream([desc]), scope);
|
|
254
|
+
out.push(this.descriptions.createDescription(node, name, document));
|
|
240
255
|
} catch {
|
|
241
|
-
|
|
256
|
+
// ignore — a missing name or invalid node should not abort the rest of the scope
|
|
242
257
|
}
|
|
243
258
|
}
|
|
244
259
|
|
|
245
|
-
private
|
|
246
|
-
let
|
|
260
|
+
private getOwnDescriptions(document: LangiumDocument, ontology: Ontology, referenceType: string): AstNodeDescription[] {
|
|
261
|
+
let perType = this.ownDescCache.get(ontology);
|
|
262
|
+
if (!perType) {
|
|
263
|
+
perType = new Map();
|
|
264
|
+
this.ownDescCache.set(ontology, perType);
|
|
265
|
+
}
|
|
266
|
+
const cached = perType.get(referenceType);
|
|
267
|
+
if (cached) {
|
|
268
|
+
return cached;
|
|
269
|
+
}
|
|
270
|
+
const descs: AstNodeDescription[] = [];
|
|
247
271
|
const prefix = (ontology as any).prefix as string | undefined;
|
|
248
272
|
for (const symbol of this.getExportedSymbols(ontology)) {
|
|
249
273
|
if (!this.reflection.isSubtype(symbol.node.$type, referenceType)) {
|
|
250
274
|
continue;
|
|
251
275
|
}
|
|
252
276
|
if (prefix) {
|
|
253
|
-
|
|
277
|
+
this.pushDescription(descs, symbol.node, `${prefix}:${symbol.name}`, document);
|
|
254
278
|
}
|
|
255
279
|
if (symbol.relationAlias) {
|
|
256
|
-
|
|
280
|
+
this.pushDescription(descs, symbol.node, symbol.name, document);
|
|
257
281
|
}
|
|
258
282
|
}
|
|
259
|
-
|
|
283
|
+
perType.set(referenceType, descs);
|
|
284
|
+
return descs;
|
|
260
285
|
}
|
|
261
286
|
|
|
262
|
-
private
|
|
263
|
-
let
|
|
287
|
+
private getImportedDescriptions(current: Ontology, referenceType: string): AstNodeDescription[] {
|
|
288
|
+
let perType = this.importedDescCache.get(current);
|
|
289
|
+
if (!perType) {
|
|
290
|
+
perType = new Map();
|
|
291
|
+
this.importedDescCache.set(current, perType);
|
|
292
|
+
}
|
|
293
|
+
const cached = perType.get(referenceType);
|
|
294
|
+
if (cached) {
|
|
295
|
+
return cached;
|
|
296
|
+
}
|
|
297
|
+
const descs: AstNodeDescription[] = [];
|
|
264
298
|
for (const resolvedImport of this.resolveImports(current)) {
|
|
265
299
|
const imported = resolvedImport.target;
|
|
266
300
|
const importedSymbols = this.getExportedSymbols(imported.ontology);
|
|
@@ -269,18 +303,24 @@ export class OmlScopeProvider extends DefaultScopeProvider {
|
|
|
269
303
|
continue;
|
|
270
304
|
}
|
|
271
305
|
if (resolvedImport.prefix) {
|
|
272
|
-
|
|
306
|
+
this.pushDescription(descs, symbol.node, `${resolvedImport.prefix}:${symbol.name}`, imported.document);
|
|
273
307
|
}
|
|
274
308
|
const separator = imported.namespace.endsWith('#') || imported.namespace.endsWith('/') ? '' : '#';
|
|
275
|
-
|
|
276
|
-
result,
|
|
277
|
-
symbol.node,
|
|
278
|
-
`<${imported.namespace}${separator}${symbol.name}>`,
|
|
279
|
-
imported.document
|
|
280
|
-
);
|
|
309
|
+
this.pushDescription(descs, symbol.node, `<${imported.namespace}${separator}${symbol.name}>`, imported.document);
|
|
281
310
|
}
|
|
282
311
|
}
|
|
283
|
-
|
|
312
|
+
perType.set(referenceType, descs);
|
|
313
|
+
return descs;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
private addCurrentOntologyEntries(scope: Scope, document: LangiumDocument, ontology: Ontology, referenceType: string): Scope {
|
|
317
|
+
const descs = this.getOwnDescriptions(document, ontology, referenceType);
|
|
318
|
+
return descs.length === 0 ? scope : this.createScope(stream(descs), scope);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
private addImportedOntologyEntries(scope: Scope, current: Ontology, referenceType: string): Scope {
|
|
322
|
+
const descs = this.getImportedDescriptions(current, referenceType);
|
|
323
|
+
return descs.length === 0 ? scope : this.createScope(stream(descs), scope);
|
|
284
324
|
}
|
|
285
325
|
|
|
286
326
|
}
|
|
@@ -86,6 +86,8 @@ import {
|
|
|
86
86
|
isVocabulary,
|
|
87
87
|
isVocabularyBundle,
|
|
88
88
|
} from './generated/ast.js';
|
|
89
|
+
import { escapeIdentifier } from './oml-identifiers.js';
|
|
90
|
+
import { formatDoubleLiteralValue } from './oml-literals.js';
|
|
89
91
|
import {
|
|
90
92
|
findOwningOntologyNode,
|
|
91
93
|
normalizeNamespace,
|
|
@@ -857,12 +859,9 @@ const serializeLiteral = (literal: Literal): string => {
|
|
|
857
859
|
return appendUnitSuffix(base, literal);
|
|
858
860
|
}
|
|
859
861
|
if (isDoubleLiteral(literal)) {
|
|
860
|
-
const v = literal.value as unknown;
|
|
861
862
|
// Langium parses double literals to JS numbers, losing the required e-notation.
|
|
862
|
-
// OML DOUBLE terminal requires e/E
|
|
863
|
-
const base = (
|
|
864
|
-
? (Number.isInteger(v) ? `${v}.0e0` : `${v}e0`)
|
|
865
|
-
: `${literal.value}`;
|
|
863
|
+
// OML DOUBLE terminal requires e/E, but JS may already stringify small numbers with one.
|
|
864
|
+
const base = formatDoubleLiteralValue(literal.value);
|
|
866
865
|
return appendUnitSuffix(base, literal);
|
|
867
866
|
}
|
|
868
867
|
return '';
|
|
@@ -977,16 +976,6 @@ const formatIriRef = (iri: string): string => {
|
|
|
977
976
|
return `<${iri}>`;
|
|
978
977
|
};
|
|
979
978
|
|
|
980
|
-
const escapeIdentifier = (value: string): string => {
|
|
981
|
-
if (!value) {
|
|
982
|
-
return '';
|
|
983
|
-
}
|
|
984
|
-
if (value.startsWith('^')) {
|
|
985
|
-
return value;
|
|
986
|
-
}
|
|
987
|
-
return OML_KEYWORDS.has(value) ? `^${value}` : value;
|
|
988
|
-
};
|
|
989
|
-
|
|
990
979
|
const serializeName = (value?: string): string => escapeIdentifier(value ?? '');
|
|
991
980
|
|
|
992
981
|
const createSerializeContext = (ontology: Ontology): SerializeContext => {
|
|
@@ -1041,58 +1030,3 @@ const pushAnnotations = (lines: string[], indent: string, annotations: Annotatio
|
|
|
1041
1030
|
const stripAngles = (value: string): string => value.replace(/^<|>$/g, '');
|
|
1042
1031
|
|
|
1043
1032
|
const wrapNamespace = (value: string): string => `<${stripAngles(value)}>`;
|
|
1044
|
-
|
|
1045
|
-
const OML_KEYWORDS = new Set([
|
|
1046
|
-
'all',
|
|
1047
|
-
'annotation',
|
|
1048
|
-
'as',
|
|
1049
|
-
'aspect',
|
|
1050
|
-
'asymmetric',
|
|
1051
|
-
'builtIn',
|
|
1052
|
-
'builtin',
|
|
1053
|
-
'bundle',
|
|
1054
|
-
'concept',
|
|
1055
|
-
'description',
|
|
1056
|
-
'differentFrom',
|
|
1057
|
-
'domain',
|
|
1058
|
-
'entity',
|
|
1059
|
-
'exactly',
|
|
1060
|
-
'extends',
|
|
1061
|
-
'forward',
|
|
1062
|
-
'from',
|
|
1063
|
-
'functional',
|
|
1064
|
-
'includes',
|
|
1065
|
-
'instance',
|
|
1066
|
-
'inverse',
|
|
1067
|
-
'irreflexive',
|
|
1068
|
-
'key',
|
|
1069
|
-
'language',
|
|
1070
|
-
'length',
|
|
1071
|
-
'max',
|
|
1072
|
-
'maxExclusive',
|
|
1073
|
-
'maxInclusive',
|
|
1074
|
-
'maxLength',
|
|
1075
|
-
'min',
|
|
1076
|
-
'minExclusive',
|
|
1077
|
-
'minInclusive',
|
|
1078
|
-
'minLength',
|
|
1079
|
-
'oneOf',
|
|
1080
|
-
'pattern',
|
|
1081
|
-
'property',
|
|
1082
|
-
'range',
|
|
1083
|
-
'ref',
|
|
1084
|
-
'reflexive',
|
|
1085
|
-
'relation',
|
|
1086
|
-
'restricts',
|
|
1087
|
-
'reverse',
|
|
1088
|
-
'rule',
|
|
1089
|
-
'sameAs',
|
|
1090
|
-
'scalar',
|
|
1091
|
-
'self',
|
|
1092
|
-
'some',
|
|
1093
|
-
'symmetric',
|
|
1094
|
-
'to',
|
|
1095
|
-
'transitive',
|
|
1096
|
-
'uses',
|
|
1097
|
-
'vocabulary'
|
|
1098
|
-
]);
|
package/src/oml/oml-update.ts
CHANGED
|
@@ -319,9 +319,9 @@ async function executeOperation(
|
|
|
319
319
|
return new Set([context.ontologyIri]);
|
|
320
320
|
case 'createInstanceRef':
|
|
321
321
|
ensureDescriptionOntology(context.ontology, operation.kind);
|
|
322
|
-
await ensureReferenceImport(shared, context.ontology, operation.instanceIri);
|
|
322
|
+
await ensureReferenceImport(shared, context.ontology, operation.instanceIri, contexts);
|
|
323
323
|
if (operation.typeIri) {
|
|
324
|
-
await ensureReferenceImport(shared, context.ontology, operation.typeIri);
|
|
324
|
+
await ensureReferenceImport(shared, context.ontology, operation.typeIri, contexts);
|
|
325
325
|
}
|
|
326
326
|
{
|
|
327
327
|
const ownedTypes: any[] = [];
|
|
@@ -347,9 +347,9 @@ async function executeOperation(
|
|
|
347
347
|
return new Set([context.ontologyIri]);
|
|
348
348
|
case 'createRelationInstanceRef':
|
|
349
349
|
ensureDescriptionOntology(context.ontology, operation.kind);
|
|
350
|
-
await ensureReferenceImport(shared, context.ontology, operation.instanceIri);
|
|
350
|
+
await ensureReferenceImport(shared, context.ontology, operation.instanceIri, contexts);
|
|
351
351
|
if (operation.typeIri) {
|
|
352
|
-
await ensureReferenceImport(shared, context.ontology, operation.typeIri);
|
|
352
|
+
await ensureReferenceImport(shared, context.ontology, operation.typeIri, contexts);
|
|
353
353
|
}
|
|
354
354
|
{
|
|
355
355
|
const ownedTypes: any[] = [];
|
|
@@ -377,12 +377,12 @@ async function executeOperation(
|
|
|
377
377
|
return new Set([context.ontologyIri]);
|
|
378
378
|
case 'addAssertion':
|
|
379
379
|
ensureDescriptionOntology(context.ontology, operation.kind);
|
|
380
|
-
return await addAssertion(shared, context.ontology, operation.subjectIri, operation.predicateIri, operation.object)
|
|
380
|
+
return await addAssertion(shared, context.ontology, operation.subjectIri, operation.predicateIri, operation.object, contexts)
|
|
381
381
|
? new Set([context.ontologyIri])
|
|
382
382
|
: new Set();
|
|
383
383
|
case 'updateAssertion':
|
|
384
384
|
ensureDescriptionOntology(context.ontology, operation.kind);
|
|
385
|
-
return await updateAssertion(shared, context.ontology, operation.subjectIri, operation.predicateIri, operation.object)
|
|
385
|
+
return await updateAssertion(shared, context.ontology, operation.subjectIri, operation.predicateIri, operation.object, contexts)
|
|
386
386
|
? new Set([context.ontologyIri])
|
|
387
387
|
: new Set();
|
|
388
388
|
case 'removeAssertion':
|
|
@@ -391,11 +391,11 @@ async function executeOperation(
|
|
|
391
391
|
? new Set([context.ontologyIri])
|
|
392
392
|
: new Set();
|
|
393
393
|
case 'addAnnotation':
|
|
394
|
-
return await addAnnotation(shared, context.ontology, operation.subjectIri, operation.predicateIri, operation.object)
|
|
394
|
+
return await addAnnotation(shared, context.ontology, operation.subjectIri, operation.predicateIri, operation.object, contexts)
|
|
395
395
|
? new Set([context.ontologyIri])
|
|
396
396
|
: new Set();
|
|
397
397
|
case 'updateAnnotation':
|
|
398
|
-
return await updateAnnotation(shared, context.ontology, operation.subjectIri, operation.predicateIri, operation.object)
|
|
398
|
+
return await updateAnnotation(shared, context.ontology, operation.subjectIri, operation.predicateIri, operation.object, contexts)
|
|
399
399
|
? new Set([context.ontologyIri])
|
|
400
400
|
: new Set();
|
|
401
401
|
case 'removeAnnotation':
|
|
@@ -457,7 +457,14 @@ function ensureUniqueLocalName(ontology: any, memberName: string): void {
|
|
|
457
457
|
}
|
|
458
458
|
}
|
|
459
459
|
|
|
460
|
-
async function addAssertion(
|
|
460
|
+
async function addAssertion(
|
|
461
|
+
shared: any,
|
|
462
|
+
ontology: any,
|
|
463
|
+
subjectIri: string,
|
|
464
|
+
predicateIri: string,
|
|
465
|
+
objectValue: unknown,
|
|
466
|
+
contexts?: ReadonlyMap<string, OntologyContext>
|
|
467
|
+
): Promise<boolean> {
|
|
461
468
|
const normalizedPredicate = normalizeIri(predicateIri);
|
|
462
469
|
const subject = findNamedInstanceByIri(ontology, subjectIri);
|
|
463
470
|
if (!subject) {
|
|
@@ -469,7 +476,7 @@ async function addAssertion(shared: any, ontology: any, subjectIri: string, pred
|
|
|
469
476
|
if ((subject.ownedTypes ?? []).some((entry: any) => matchesPredicateRef(ontology, entry?.type, normalizedTypeIri))) {
|
|
470
477
|
return false;
|
|
471
478
|
}
|
|
472
|
-
await ensureReferenceImport(shared, ontology, normalizedTypeIri);
|
|
479
|
+
await ensureReferenceImport(shared, ontology, normalizedTypeIri, contexts);
|
|
473
480
|
subject.ownedTypes ??= [];
|
|
474
481
|
pushAstArrayChild(subject, 'ownedTypes', {
|
|
475
482
|
$type: 'TypeAssertion',
|
|
@@ -483,7 +490,7 @@ async function addAssertion(shared: any, ontology: any, subjectIri: string, pred
|
|
|
483
490
|
throw new Error(`Predicate '${predicateIri}' applies only to relation instances.`);
|
|
484
491
|
}
|
|
485
492
|
const sourceIri = normalizeIri(asRequiredIri(objectValue, 'source assertion object'));
|
|
486
|
-
await ensureReferenceImport(shared, ontology, sourceIri);
|
|
493
|
+
await ensureReferenceImport(shared, ontology, sourceIri, contexts);
|
|
487
494
|
subject.sources ??= [];
|
|
488
495
|
subject.sources.push(asRef(ontology, sourceIri));
|
|
489
496
|
return true;
|
|
@@ -494,7 +501,7 @@ async function addAssertion(shared: any, ontology: any, subjectIri: string, pred
|
|
|
494
501
|
throw new Error(`Predicate '${predicateIri}' applies only to relation instances.`);
|
|
495
502
|
}
|
|
496
503
|
const targetIri = normalizeIri(asRequiredIri(objectValue, 'target assertion object'));
|
|
497
|
-
await ensureReferenceImport(shared, ontology, targetIri);
|
|
504
|
+
await ensureReferenceImport(shared, ontology, targetIri, contexts);
|
|
498
505
|
subject.targets ??= [];
|
|
499
506
|
subject.targets.push(asRef(ontology, targetIri));
|
|
500
507
|
return true;
|
|
@@ -505,7 +512,7 @@ async function addAssertion(shared: any, ontology: any, subjectIri: string, pred
|
|
|
505
512
|
.filter((assertion: any) => matchesPredicateRef(ontology, assertion?.property, normalizedPredicate));
|
|
506
513
|
let propertyAssertion = matchingAssertions[0];
|
|
507
514
|
if (!propertyAssertion) {
|
|
508
|
-
await ensureReferenceImport(shared, ontology, normalizedPredicate);
|
|
515
|
+
await ensureReferenceImport(shared, ontology, normalizedPredicate, contexts);
|
|
509
516
|
propertyAssertion = {
|
|
510
517
|
$type: 'PropertyValueAssertion',
|
|
511
518
|
property: asRef(ontology, normalizedPredicate),
|
|
@@ -518,9 +525,9 @@ async function addAssertion(shared: any, ontology: any, subjectIri: string, pred
|
|
|
518
525
|
normalizePropertyAssertionGroup(subject, propertyAssertion, matchingAssertions.slice(1));
|
|
519
526
|
}
|
|
520
527
|
if (isIriLike(objectValue)) {
|
|
521
|
-
await ensureReferenceImport(shared, ontology, normalizeIri(asRequiredIri(objectValue, 'assertion object')));
|
|
528
|
+
await ensureReferenceImport(shared, ontology, normalizeIri(asRequiredIri(objectValue, 'assertion object')), contexts);
|
|
522
529
|
} else {
|
|
523
|
-
await ensureTypedLiteralDatatypeImport(shared, ontology, objectValue);
|
|
530
|
+
await ensureTypedLiteralDatatypeImport(shared, ontology, objectValue, contexts);
|
|
524
531
|
}
|
|
525
532
|
appendAssertionValue(ontology, propertyAssertion, objectValue);
|
|
526
533
|
normalizeAllPropertyAssertions(subject, ontology);
|
|
@@ -618,7 +625,14 @@ function removeAssertion(ontology: any, subjectIri: string, predicateIri: string
|
|
|
618
625
|
return changed;
|
|
619
626
|
}
|
|
620
627
|
|
|
621
|
-
async function updateAssertion(
|
|
628
|
+
async function updateAssertion(
|
|
629
|
+
shared: any,
|
|
630
|
+
ontology: any,
|
|
631
|
+
subjectIri: string,
|
|
632
|
+
predicateIri: string,
|
|
633
|
+
objectValue: unknown,
|
|
634
|
+
contexts?: ReadonlyMap<string, OntologyContext>
|
|
635
|
+
): Promise<boolean> {
|
|
622
636
|
const normalizedPredicate = normalizeIri(predicateIri);
|
|
623
637
|
const subject = findNamedInstanceByIri(ontology, subjectIri);
|
|
624
638
|
if (!subject) {
|
|
@@ -627,19 +641,19 @@ async function updateAssertion(shared: any, ontology: any, subjectIri: string, p
|
|
|
627
641
|
|
|
628
642
|
if (normalizedPredicate === RDF_TYPE_IRI || isSourcePredicate(normalizedPredicate) || isTargetPredicate(normalizedPredicate)) {
|
|
629
643
|
const removed = removeAssertion(ontology, subjectIri, predicateIri, undefined);
|
|
630
|
-
const added = await addAssertion(shared, ontology, subjectIri, predicateIri, objectValue);
|
|
644
|
+
const added = await addAssertion(shared, ontology, subjectIri, predicateIri, objectValue, contexts);
|
|
631
645
|
return removed || added;
|
|
632
646
|
}
|
|
633
647
|
|
|
634
648
|
subject.ownedPropertyValues ??= [];
|
|
635
649
|
let propertyAssertion = subject.ownedPropertyValues.find((assertion: any) => matchesPredicateRef(ontology, assertion?.property, normalizedPredicate));
|
|
636
650
|
if (!propertyAssertion) {
|
|
637
|
-
return addAssertion(shared, ontology, subjectIri, predicateIri, objectValue);
|
|
651
|
+
return addAssertion(shared, ontology, subjectIri, predicateIri, objectValue, contexts);
|
|
638
652
|
}
|
|
639
653
|
if (isIriLike(objectValue)) {
|
|
640
|
-
await ensureReferenceImport(shared, ontology, normalizeIri(asRequiredIri(objectValue, 'assertion object')));
|
|
654
|
+
await ensureReferenceImport(shared, ontology, normalizeIri(asRequiredIri(objectValue, 'assertion object')), contexts);
|
|
641
655
|
} else {
|
|
642
|
-
await ensureTypedLiteralDatatypeImport(shared, ontology, objectValue);
|
|
656
|
+
await ensureTypedLiteralDatatypeImport(shared, ontology, objectValue, contexts);
|
|
643
657
|
}
|
|
644
658
|
propertyAssertion.literalValues = [];
|
|
645
659
|
propertyAssertion.referencedValues = [];
|
|
@@ -648,13 +662,20 @@ async function updateAssertion(shared: any, ontology: any, subjectIri: string, p
|
|
|
648
662
|
return true;
|
|
649
663
|
}
|
|
650
664
|
|
|
651
|
-
async function addAnnotation(
|
|
665
|
+
async function addAnnotation(
|
|
666
|
+
shared: any,
|
|
667
|
+
ontology: any,
|
|
668
|
+
subjectIri: string,
|
|
669
|
+
predicateIri: string,
|
|
670
|
+
objectValue: unknown,
|
|
671
|
+
contexts?: ReadonlyMap<string, OntologyContext>
|
|
672
|
+
): Promise<boolean> {
|
|
652
673
|
const subject = findAnnotationSubjectByIri(ontology, subjectIri);
|
|
653
674
|
if (!subject) {
|
|
654
675
|
throw new Error(`Annotation subject '${subjectIri}' was not found in the ontology.`);
|
|
655
676
|
}
|
|
656
677
|
const normalizedPredicate = normalizeIri(predicateIri);
|
|
657
|
-
await ensureReferenceImport(shared, ontology, normalizedPredicate);
|
|
678
|
+
await ensureReferenceImport(shared, ontology, normalizedPredicate, contexts);
|
|
658
679
|
subject.ownedAnnotations ??= [];
|
|
659
680
|
const matchingAnnotations = subject.ownedAnnotations
|
|
660
681
|
.filter((entry: any) => matchesPredicateRef(ontology, entry?.property, normalizedPredicate));
|
|
@@ -671,9 +692,9 @@ async function addAnnotation(shared: any, ontology: any, subjectIri: string, pre
|
|
|
671
692
|
normalizeAnnotationGroup(subject, annotation, matchingAnnotations.slice(1));
|
|
672
693
|
}
|
|
673
694
|
if (isIriLike(objectValue)) {
|
|
674
|
-
await ensureReferenceImport(shared, ontology, normalizeIri(asRequiredIri(objectValue, 'annotation object')));
|
|
695
|
+
await ensureReferenceImport(shared, ontology, normalizeIri(asRequiredIri(objectValue, 'annotation object')), contexts);
|
|
675
696
|
} else {
|
|
676
|
-
await ensureTypedLiteralDatatypeImport(shared, ontology, objectValue);
|
|
697
|
+
await ensureTypedLiteralDatatypeImport(shared, ontology, objectValue, contexts);
|
|
677
698
|
}
|
|
678
699
|
appendAnnotationValue(ontology, annotation, objectValue);
|
|
679
700
|
normalizeAllAnnotations(subject, ontology);
|
|
@@ -718,7 +739,14 @@ function removeAnnotation(ontology: any, subjectIri: string, predicateIri: strin
|
|
|
718
739
|
return changed;
|
|
719
740
|
}
|
|
720
741
|
|
|
721
|
-
async function updateAnnotation(
|
|
742
|
+
async function updateAnnotation(
|
|
743
|
+
shared: any,
|
|
744
|
+
ontology: any,
|
|
745
|
+
subjectIri: string,
|
|
746
|
+
predicateIri: string,
|
|
747
|
+
objectValue: unknown,
|
|
748
|
+
contexts?: ReadonlyMap<string, OntologyContext>
|
|
749
|
+
): Promise<boolean> {
|
|
722
750
|
const subject = findAnnotationSubjectByIri(ontology, subjectIri);
|
|
723
751
|
if (!subject) {
|
|
724
752
|
throw new Error(`Annotation subject '${subjectIri}' was not found in the ontology.`);
|
|
@@ -727,12 +755,12 @@ async function updateAnnotation(shared: any, ontology: any, subjectIri: string,
|
|
|
727
755
|
subject.ownedAnnotations ??= [];
|
|
728
756
|
const annotation = subject.ownedAnnotations.find((entry: any) => matchesPredicateRef(ontology, entry?.property, normalizedPredicate));
|
|
729
757
|
if (!annotation) {
|
|
730
|
-
return addAnnotation(shared, ontology, subjectIri, predicateIri, objectValue);
|
|
758
|
+
return addAnnotation(shared, ontology, subjectIri, predicateIri, objectValue, contexts);
|
|
731
759
|
}
|
|
732
760
|
if (isIriLike(objectValue)) {
|
|
733
|
-
await ensureReferenceImport(shared, ontology, normalizeIri(asRequiredIri(objectValue, 'annotation object')));
|
|
761
|
+
await ensureReferenceImport(shared, ontology, normalizeIri(asRequiredIri(objectValue, 'annotation object')), contexts);
|
|
734
762
|
} else {
|
|
735
|
-
await ensureTypedLiteralDatatypeImport(shared, ontology, objectValue);
|
|
763
|
+
await ensureTypedLiteralDatatypeImport(shared, ontology, objectValue, contexts);
|
|
736
764
|
}
|
|
737
765
|
annotation.literalValues = [];
|
|
738
766
|
annotation.referencedValues = [];
|
|
@@ -1190,7 +1218,12 @@ function toRefText(ontology: any, iri: string): string {
|
|
|
1190
1218
|
return `<${normalizedIri}>`;
|
|
1191
1219
|
}
|
|
1192
1220
|
|
|
1193
|
-
async function ensureReferenceImport(
|
|
1221
|
+
async function ensureReferenceImport(
|
|
1222
|
+
shared: any,
|
|
1223
|
+
ontology: any,
|
|
1224
|
+
iri: string,
|
|
1225
|
+
contexts?: ReadonlyMap<string, OntologyContext>
|
|
1226
|
+
): Promise<void> {
|
|
1194
1227
|
const target = parseIriParts(iri);
|
|
1195
1228
|
if (!target) {
|
|
1196
1229
|
return;
|
|
@@ -1215,7 +1248,8 @@ async function ensureReferenceImport(shared: any, ontology: any, iri: string): P
|
|
|
1215
1248
|
const importedNamespace = normalizeNamespace(String(resolveImportNamespaceRaw(imp) ?? ''));
|
|
1216
1249
|
return importedNamespace === target.namespace;
|
|
1217
1250
|
});
|
|
1218
|
-
const importedOntology =
|
|
1251
|
+
const importedOntology = findOntologyInContexts(contexts, target.namespace)
|
|
1252
|
+
?? await findOntologyByNamespace(shared, target.namespace);
|
|
1219
1253
|
const declaredPrefix = typeof importedOntology?.prefix === 'string' ? importedOntology.prefix.trim() : '';
|
|
1220
1254
|
const preferredPrefix = declaredPrefix && !usedPrefixes.has(declaredPrefix)
|
|
1221
1255
|
? declaredPrefix
|
|
@@ -1363,6 +1397,13 @@ async function findOntologyByNamespace(shared: any, namespace: string): Promise<
|
|
|
1363
1397
|
return ontology && isOntology(ontology) ? ontology : undefined;
|
|
1364
1398
|
}
|
|
1365
1399
|
|
|
1400
|
+
function findOntologyInContexts(contexts: ReadonlyMap<string, OntologyContext> | undefined, namespace: string): any | undefined {
|
|
1401
|
+
if (!contexts) {
|
|
1402
|
+
return undefined;
|
|
1403
|
+
}
|
|
1404
|
+
return contexts.get(normalizeOntologyIriKey(namespace))?.ontology;
|
|
1405
|
+
}
|
|
1406
|
+
|
|
1366
1407
|
function matchesRefTextTarget(
|
|
1367
1408
|
ontology: any,
|
|
1368
1409
|
statement: any,
|
|
@@ -1423,6 +1464,9 @@ function matchesRefTextFragment(statement: any, fragment: string): boolean {
|
|
|
1423
1464
|
}
|
|
1424
1465
|
|
|
1425
1466
|
function asLiteral(ontology: any, value: unknown): any {
|
|
1467
|
+
if (isExplicitLiteralTransport(value)) {
|
|
1468
|
+
return explicitLiteralFromTransport(ontology, value);
|
|
1469
|
+
}
|
|
1426
1470
|
if (isQuantityLiteralTransport(value)) {
|
|
1427
1471
|
const raw = value.value;
|
|
1428
1472
|
const num = typeof raw === 'number' ? raw : Number(raw);
|
|
@@ -1454,12 +1498,6 @@ function asLiteral(ontology: any, value: unknown): any {
|
|
|
1454
1498
|
}
|
|
1455
1499
|
return literal;
|
|
1456
1500
|
}
|
|
1457
|
-
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
1458
|
-
const record = value as Record<string, unknown>;
|
|
1459
|
-
if ((record.$type === 'DoubleLiteral' || record.$type === 'DecimalLiteral') && 'value' in record) {
|
|
1460
|
-
return { $type: record.$type, value: record.value };
|
|
1461
|
-
}
|
|
1462
|
-
}
|
|
1463
1501
|
if (typeof value === 'boolean') {
|
|
1464
1502
|
return { $type: 'BooleanLiteral', value };
|
|
1465
1503
|
}
|
|
@@ -1472,17 +1510,125 @@ function asLiteral(ontology: any, value: unknown): any {
|
|
|
1472
1510
|
return { $type: 'QuotedLiteral', value: String(value ?? '') };
|
|
1473
1511
|
}
|
|
1474
1512
|
|
|
1475
|
-
|
|
1513
|
+
function isExplicitLiteralTransport(value: unknown): value is {
|
|
1514
|
+
$type: 'BooleanLiteral' | 'DecimalLiteral' | 'DoubleLiteral' | 'IntegerLiteral' | 'QuotedLiteral';
|
|
1515
|
+
value?: unknown;
|
|
1516
|
+
langTag?: unknown;
|
|
1517
|
+
language?: unknown;
|
|
1518
|
+
datatypeIri?: unknown;
|
|
1519
|
+
datatypeRefText?: unknown;
|
|
1520
|
+
typeIri?: unknown;
|
|
1521
|
+
typeRefText?: unknown;
|
|
1522
|
+
type?: unknown;
|
|
1523
|
+
unitIri?: unknown;
|
|
1524
|
+
unitRefText?: unknown;
|
|
1525
|
+
unit?: unknown;
|
|
1526
|
+
} {
|
|
1527
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
1528
|
+
return false;
|
|
1529
|
+
}
|
|
1530
|
+
const type = (value as Record<string, unknown>).$type;
|
|
1531
|
+
return type === 'BooleanLiteral'
|
|
1532
|
+
|| type === 'DecimalLiteral'
|
|
1533
|
+
|| type === 'DoubleLiteral'
|
|
1534
|
+
|| type === 'IntegerLiteral'
|
|
1535
|
+
|| type === 'QuotedLiteral';
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
function explicitLiteralFromTransport(ontology: any, value: {
|
|
1539
|
+
$type: 'BooleanLiteral' | 'DecimalLiteral' | 'DoubleLiteral' | 'IntegerLiteral' | 'QuotedLiteral';
|
|
1540
|
+
value?: unknown;
|
|
1541
|
+
langTag?: unknown;
|
|
1542
|
+
language?: unknown;
|
|
1543
|
+
datatypeIri?: unknown;
|
|
1544
|
+
datatypeRefText?: unknown;
|
|
1545
|
+
typeIri?: unknown;
|
|
1546
|
+
typeRefText?: unknown;
|
|
1547
|
+
type?: unknown;
|
|
1548
|
+
unitIri?: unknown;
|
|
1549
|
+
unitRefText?: unknown;
|
|
1550
|
+
unit?: unknown;
|
|
1551
|
+
}): Record<string, unknown> {
|
|
1552
|
+
if (value.$type === 'QuotedLiteral') {
|
|
1553
|
+
const literalValue = String(value.value ?? '');
|
|
1554
|
+
const langTag = literalLanguageTagFromTransport(value);
|
|
1555
|
+
const datatypeIri = literalDatatypeIriFromTransport(ontology, value);
|
|
1556
|
+
const literal: Record<string, unknown> = {
|
|
1557
|
+
$type: 'QuotedLiteral',
|
|
1558
|
+
value: literalValue,
|
|
1559
|
+
};
|
|
1560
|
+
if (langTag) {
|
|
1561
|
+
literal.langTag = langTag;
|
|
1562
|
+
}
|
|
1563
|
+
if (datatypeIri && !isXsdStringDatatypeIri(ontology, datatypeIri)) {
|
|
1564
|
+
literal.type = { $refText: toRefText(ontology, datatypeIri) };
|
|
1565
|
+
}
|
|
1566
|
+
return literal;
|
|
1567
|
+
}
|
|
1568
|
+
if (value.$type === 'BooleanLiteral') {
|
|
1569
|
+
return {
|
|
1570
|
+
$type: 'BooleanLiteral',
|
|
1571
|
+
value: booleanLiteralValue(value.value),
|
|
1572
|
+
};
|
|
1573
|
+
}
|
|
1574
|
+
const literal: Record<string, unknown> = {
|
|
1575
|
+
$type: value.$type,
|
|
1576
|
+
value: numericLiteralValue(value.$type, value.value),
|
|
1577
|
+
};
|
|
1578
|
+
const unitIri = literalUnitIriFromTransport(ontology, value);
|
|
1579
|
+
if (unitIri) {
|
|
1580
|
+
literal.unit = { $refText: toRefText(ontology, unitIri) };
|
|
1581
|
+
}
|
|
1582
|
+
return literal;
|
|
1583
|
+
}
|
|
1584
|
+
|
|
1585
|
+
function numericLiteralValue(type: 'DecimalLiteral' | 'DoubleLiteral' | 'IntegerLiteral', value: unknown): number {
|
|
1586
|
+
const numberValue = typeof value === 'number'
|
|
1587
|
+
? value
|
|
1588
|
+
: typeof value === 'string' && value.trim().length > 0
|
|
1589
|
+
? Number(value.trim())
|
|
1590
|
+
: Number.NaN;
|
|
1591
|
+
if (!Number.isFinite(numberValue)) {
|
|
1592
|
+
throw new Error(`${type} value must be a finite number.`);
|
|
1593
|
+
}
|
|
1594
|
+
if (type === 'IntegerLiteral' && !Number.isInteger(numberValue)) {
|
|
1595
|
+
throw new Error('IntegerLiteral value must be an integer.');
|
|
1596
|
+
}
|
|
1597
|
+
return numberValue;
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
function booleanLiteralValue(value: unknown): boolean {
|
|
1601
|
+
if (typeof value === 'boolean') {
|
|
1602
|
+
return value;
|
|
1603
|
+
}
|
|
1604
|
+
if (typeof value === 'string') {
|
|
1605
|
+
const normalized = value.trim().toLowerCase();
|
|
1606
|
+
if (normalized === 'true') {
|
|
1607
|
+
return true;
|
|
1608
|
+
}
|
|
1609
|
+
if (normalized === 'false') {
|
|
1610
|
+
return false;
|
|
1611
|
+
}
|
|
1612
|
+
}
|
|
1613
|
+
throw new Error('BooleanLiteral value must be true or false.');
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
async function ensureTypedLiteralDatatypeImport(
|
|
1617
|
+
shared: any,
|
|
1618
|
+
ontology: any,
|
|
1619
|
+
value: unknown,
|
|
1620
|
+
contexts?: ReadonlyMap<string, OntologyContext>
|
|
1621
|
+
): Promise<void> {
|
|
1476
1622
|
if (isTypedQuotedLiteralTransport(value)) {
|
|
1477
1623
|
const datatypeIri = literalDatatypeIriFromTransport(ontology, value);
|
|
1478
1624
|
if (datatypeIri && !isXsdStringDatatypeIri(ontology, datatypeIri)) {
|
|
1479
|
-
await ensureReferenceImport(shared, ontology, datatypeIri);
|
|
1625
|
+
await ensureReferenceImport(shared, ontology, datatypeIri, contexts);
|
|
1480
1626
|
}
|
|
1481
1627
|
}
|
|
1482
1628
|
if (isQuantityLiteralTransport(value)) {
|
|
1483
1629
|
const unitIri = literalUnitIriFromTransport(ontology, value);
|
|
1484
1630
|
if (unitIri) {
|
|
1485
|
-
await ensureReferenceImport(shared, ontology, unitIri);
|
|
1631
|
+
await ensureReferenceImport(shared, ontology, unitIri, contexts);
|
|
1486
1632
|
}
|
|
1487
1633
|
}
|
|
1488
1634
|
}
|