@oml/language 0.9.0 → 0.11.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/oml/oml-candidates.d.ts +17 -7
- package/out/oml/oml-candidates.js +119 -21
- package/out/oml/oml-candidates.js.map +1 -1
- package/out/oml/oml-completion.js +2 -1
- package/out/oml/oml-completion.js.map +1 -1
- package/out/oml/oml-document.d.ts +4 -4
- package/out/oml/oml-document.js +42 -7
- package/out/oml/oml-document.js.map +1 -1
- package/out/oml/oml-edit.d.ts +12 -0
- package/out/oml/oml-edit.js +201 -43
- package/out/oml/oml-edit.js.map +1 -1
- package/out/oml/oml-index.d.ts +26 -6
- package/out/oml/oml-index.js +305 -43
- package/out/oml/oml-index.js.map +1 -1
- package/out/oml/oml-rename.js +19 -1
- package/out/oml/oml-rename.js.map +1 -1
- package/out/oml/oml-utils.js +1 -0
- package/out/oml/oml-utils.js.map +1 -1
- package/package.json +1 -1
- package/src/oml/oml-candidates.ts +144 -23
- package/src/oml/oml-completion.ts +2 -1
- package/src/oml/oml-document.ts +49 -9
- package/src/oml/oml-edit.ts +211 -42
- package/src/oml/oml-index.ts +330 -44
- package/src/oml/oml-rename.ts +21 -1
- package/src/oml/oml-utils.ts +1 -0
- package/out/oml/oml-index-manager.d.ts +0 -10
- package/out/oml/oml-index-manager.js +0 -48
- package/out/oml/oml-index-manager.js.map +0 -1
- package/src/oml/oml-index-manager.ts +0 -56
package/src/oml/oml-edit.ts
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
// Copyright (c) 2026 Modelware. All rights reserved.
|
|
2
2
|
|
|
3
|
-
import { URI } from 'langium';
|
|
3
|
+
import { DocumentState, URI } from 'langium';
|
|
4
4
|
import { RequestType, type WorkspaceEdit } from 'vscode-languageserver-protocol';
|
|
5
5
|
import {
|
|
6
6
|
isAnnotation,
|
|
7
7
|
isConceptInstance,
|
|
8
8
|
isDescription,
|
|
9
|
+
isDescriptionBox,
|
|
9
10
|
isImport,
|
|
10
11
|
isOntology,
|
|
11
12
|
isPropertyValueAssertion,
|
|
12
13
|
isRelationInstance,
|
|
13
14
|
isRule,
|
|
14
15
|
isTypeAssertion,
|
|
16
|
+
isVocabulary,
|
|
17
|
+
isVocabularyBundle,
|
|
15
18
|
} from './generated/ast.js';
|
|
16
19
|
import { getOntologyModelIndex } from './oml-index.js';
|
|
17
20
|
import { serializeOntology } from './oml-serializer.js';
|
|
@@ -40,8 +43,10 @@ type OmlEditOperation =
|
|
|
40
43
|
| { kind: 'createInstanceRef'; ontologyIri: string; instanceIri: string; typeIri?: string }
|
|
41
44
|
| { kind: 'createRelationInstanceRef'; ontologyIri: string; instanceIri: string; typeIri?: string }
|
|
42
45
|
| { kind: 'addAssertion'; ontologyIri: string; subjectIri: string; predicateIri: string; object: unknown }
|
|
46
|
+
| { kind: 'updateAssertion'; ontologyIri: string; subjectIri: string; predicateIri: string; object: unknown }
|
|
43
47
|
| { kind: 'removeAssertion'; ontologyIri: string; subjectIri: string; predicateIri: string; object?: unknown }
|
|
44
48
|
| { kind: 'addAnnotation'; ontologyIri: string; subjectIri: string; predicateIri: string; object: unknown }
|
|
49
|
+
| { kind: 'updateAnnotation'; ontologyIri: string; subjectIri: string; predicateIri: string; object: unknown }
|
|
45
50
|
| { kind: 'removeAnnotation'; ontologyIri: string; subjectIri: string; predicateIri: string; object?: unknown }
|
|
46
51
|
| { kind: 'deleteMemberCascade'; ontologyIri: string; memberIri: string }
|
|
47
52
|
| { kind: 'deleteMemberRef'; ontologyIri: string; memberIri: string; typeIri?: string };
|
|
@@ -116,7 +121,12 @@ async function resolveOntologyContext(shared: any, ontologyIri: string, cache: M
|
|
|
116
121
|
const builder: any = shared.workspace.DocumentBuilder;
|
|
117
122
|
const document = langiumDocuments.getDocument(uri)
|
|
118
123
|
?? await langiumDocuments.getOrCreateDocument(uri);
|
|
119
|
-
|
|
124
|
+
// Only build if not yet Linked. Forcing validation:true when the workspace
|
|
125
|
+
// is already Validated causes a document-state conflict in Langium on Windows.
|
|
126
|
+
const docState: number | undefined = document?.state;
|
|
127
|
+
if (docState === undefined || docState < DocumentState.Linked) {
|
|
128
|
+
await builder.build([document], { validation: false });
|
|
129
|
+
}
|
|
120
130
|
const ontology = document?.parseResult?.value;
|
|
121
131
|
if (!ontology || !isOntology(ontology)) {
|
|
122
132
|
throw new Error(`Resolved document '${modelUri}' is not an ontology.`);
|
|
@@ -149,8 +159,7 @@ async function executeOperation(
|
|
|
149
159
|
ownedPropertyValues: [],
|
|
150
160
|
ownedTypes: []
|
|
151
161
|
};
|
|
152
|
-
|
|
153
|
-
context.ontology.ownedStatements.push(statement);
|
|
162
|
+
pushAstArrayChild(context.ontology, 'ownedStatements', statement);
|
|
154
163
|
}
|
|
155
164
|
return new Set([context.ontologyIri]);
|
|
156
165
|
case 'createRelationInstance':
|
|
@@ -166,15 +175,14 @@ async function executeOperation(
|
|
|
166
175
|
sources: [],
|
|
167
176
|
targets: []
|
|
168
177
|
};
|
|
169
|
-
|
|
170
|
-
context.ontology.ownedStatements.push(statement);
|
|
178
|
+
pushAstArrayChild(context.ontology, 'ownedStatements', statement);
|
|
171
179
|
}
|
|
172
180
|
return new Set([context.ontologyIri]);
|
|
173
181
|
case 'createInstanceRef':
|
|
174
182
|
ensureDescriptionOntology(context.ontology, operation.kind);
|
|
175
|
-
ensureReferenceImport(context.ontology, operation.instanceIri);
|
|
183
|
+
await ensureReferenceImport(shared, context.ontology, operation.instanceIri);
|
|
176
184
|
if (operation.typeIri) {
|
|
177
|
-
ensureReferenceImport(context.ontology, operation.typeIri);
|
|
185
|
+
await ensureReferenceImport(shared, context.ontology, operation.typeIri);
|
|
178
186
|
}
|
|
179
187
|
{
|
|
180
188
|
const ownedTypes: any[] = [];
|
|
@@ -192,15 +200,17 @@ async function executeOperation(
|
|
|
192
200
|
ownedTypes
|
|
193
201
|
};
|
|
194
202
|
statement.__targetIri = normalizeIri(operation.instanceIri);
|
|
195
|
-
|
|
196
|
-
|
|
203
|
+
pushAstArrayChild(context.ontology, 'ownedStatements', statement);
|
|
204
|
+
for (const typeAssertion of ownedTypes) {
|
|
205
|
+
attachAstNode(typeAssertion, statement, 'ownedTypes');
|
|
206
|
+
}
|
|
197
207
|
}
|
|
198
208
|
return new Set([context.ontologyIri]);
|
|
199
209
|
case 'createRelationInstanceRef':
|
|
200
210
|
ensureDescriptionOntology(context.ontology, operation.kind);
|
|
201
|
-
ensureReferenceImport(context.ontology, operation.instanceIri);
|
|
211
|
+
await ensureReferenceImport(shared, context.ontology, operation.instanceIri);
|
|
202
212
|
if (operation.typeIri) {
|
|
203
|
-
ensureReferenceImport(context.ontology, operation.typeIri);
|
|
213
|
+
await ensureReferenceImport(shared, context.ontology, operation.typeIri);
|
|
204
214
|
}
|
|
205
215
|
{
|
|
206
216
|
const ownedTypes: any[] = [];
|
|
@@ -220,13 +230,20 @@ async function executeOperation(
|
|
|
220
230
|
targets: []
|
|
221
231
|
};
|
|
222
232
|
statement.__targetIri = normalizeIri(operation.instanceIri);
|
|
223
|
-
|
|
224
|
-
|
|
233
|
+
pushAstArrayChild(context.ontology, 'ownedStatements', statement);
|
|
234
|
+
for (const typeAssertion of ownedTypes) {
|
|
235
|
+
attachAstNode(typeAssertion, statement, 'ownedTypes');
|
|
236
|
+
}
|
|
225
237
|
}
|
|
226
238
|
return new Set([context.ontologyIri]);
|
|
227
239
|
case 'addAssertion':
|
|
228
240
|
ensureDescriptionOntology(context.ontology, operation.kind);
|
|
229
|
-
return addAssertion(context.ontology, operation.subjectIri, operation.predicateIri, operation.object)
|
|
241
|
+
return await addAssertion(shared, context.ontology, operation.subjectIri, operation.predicateIri, operation.object)
|
|
242
|
+
? new Set([context.ontologyIri])
|
|
243
|
+
: new Set();
|
|
244
|
+
case 'updateAssertion':
|
|
245
|
+
ensureDescriptionOntology(context.ontology, operation.kind);
|
|
246
|
+
return await updateAssertion(shared, context.ontology, operation.subjectIri, operation.predicateIri, operation.object)
|
|
230
247
|
? new Set([context.ontologyIri])
|
|
231
248
|
: new Set();
|
|
232
249
|
case 'removeAssertion':
|
|
@@ -235,7 +252,11 @@ async function executeOperation(
|
|
|
235
252
|
? new Set([context.ontologyIri])
|
|
236
253
|
: new Set();
|
|
237
254
|
case 'addAnnotation':
|
|
238
|
-
return addAnnotation(context.ontology, operation.subjectIri, operation.predicateIri, operation.object)
|
|
255
|
+
return await addAnnotation(shared, context.ontology, operation.subjectIri, operation.predicateIri, operation.object)
|
|
256
|
+
? new Set([context.ontologyIri])
|
|
257
|
+
: new Set();
|
|
258
|
+
case 'updateAnnotation':
|
|
259
|
+
return await updateAnnotation(shared, context.ontology, operation.subjectIri, operation.predicateIri, operation.object)
|
|
239
260
|
? new Set([context.ontologyIri])
|
|
240
261
|
: new Set();
|
|
241
262
|
case 'removeAnnotation':
|
|
@@ -269,7 +290,10 @@ async function resolveOntologyContextByModelUri(
|
|
|
269
290
|
const builder: any = shared.workspace.DocumentBuilder;
|
|
270
291
|
const document = langiumDocuments.getDocument(uri)
|
|
271
292
|
?? await langiumDocuments.getOrCreateDocument(uri);
|
|
272
|
-
|
|
293
|
+
const docState2: number | undefined = document?.state;
|
|
294
|
+
if (docState2 === undefined || docState2 < DocumentState.Linked) {
|
|
295
|
+
await builder.build([document], { validation: false });
|
|
296
|
+
}
|
|
273
297
|
const ontology = document?.parseResult?.value;
|
|
274
298
|
if (!ontology || !isOntology(ontology)) {
|
|
275
299
|
throw new Error(`Resolved document '${modelUri}' is not an ontology.`);
|
|
@@ -297,7 +321,7 @@ function ensureUniqueLocalName(ontology: any, memberName: string): void {
|
|
|
297
321
|
}
|
|
298
322
|
}
|
|
299
323
|
|
|
300
|
-
function addAssertion(ontology: any, subjectIri: string, predicateIri: string, objectValue: unknown): boolean {
|
|
324
|
+
async function addAssertion(shared: any, ontology: any, subjectIri: string, predicateIri: string, objectValue: unknown): Promise<boolean> {
|
|
301
325
|
const normalizedPredicate = normalizeIri(predicateIri);
|
|
302
326
|
const subject = findNamedInstanceByIri(ontology, subjectIri);
|
|
303
327
|
if (!subject) {
|
|
@@ -309,9 +333,9 @@ function addAssertion(ontology: any, subjectIri: string, predicateIri: string, o
|
|
|
309
333
|
if ((subject.ownedTypes ?? []).some((entry: any) => matchesPredicateRef(ontology, entry?.type, normalizedTypeIri))) {
|
|
310
334
|
return false;
|
|
311
335
|
}
|
|
312
|
-
ensureReferenceImport(ontology, normalizedTypeIri);
|
|
336
|
+
await ensureReferenceImport(shared, ontology, normalizedTypeIri);
|
|
313
337
|
subject.ownedTypes ??= [];
|
|
314
|
-
subject
|
|
338
|
+
pushAstArrayChild(subject, 'ownedTypes', {
|
|
315
339
|
$type: 'TypeAssertion',
|
|
316
340
|
type: asRef(ontology, normalizedTypeIri)
|
|
317
341
|
});
|
|
@@ -323,7 +347,7 @@ function addAssertion(ontology: any, subjectIri: string, predicateIri: string, o
|
|
|
323
347
|
throw new Error(`Predicate '${predicateIri}' applies only to relation instances.`);
|
|
324
348
|
}
|
|
325
349
|
const sourceIri = normalizeIri(asRequiredIri(objectValue, 'source assertion object'));
|
|
326
|
-
ensureReferenceImport(ontology, sourceIri);
|
|
350
|
+
await ensureReferenceImport(shared, ontology, sourceIri);
|
|
327
351
|
subject.sources ??= [];
|
|
328
352
|
subject.sources.push(asRef(ontology, sourceIri));
|
|
329
353
|
return true;
|
|
@@ -334,7 +358,7 @@ function addAssertion(ontology: any, subjectIri: string, predicateIri: string, o
|
|
|
334
358
|
throw new Error(`Predicate '${predicateIri}' applies only to relation instances.`);
|
|
335
359
|
}
|
|
336
360
|
const targetIri = normalizeIri(asRequiredIri(objectValue, 'target assertion object'));
|
|
337
|
-
ensureReferenceImport(ontology, targetIri);
|
|
361
|
+
await ensureReferenceImport(shared, ontology, targetIri);
|
|
338
362
|
subject.targets ??= [];
|
|
339
363
|
subject.targets.push(asRef(ontology, targetIri));
|
|
340
364
|
return true;
|
|
@@ -343,6 +367,7 @@ function addAssertion(ontology: any, subjectIri: string, predicateIri: string, o
|
|
|
343
367
|
subject.ownedPropertyValues ??= [];
|
|
344
368
|
let propertyAssertion = subject.ownedPropertyValues.find((assertion: any) => matchesPredicateRef(ontology, assertion?.property, normalizedPredicate));
|
|
345
369
|
if (!propertyAssertion) {
|
|
370
|
+
await ensureReferenceImport(shared, ontology, normalizedPredicate);
|
|
346
371
|
propertyAssertion = {
|
|
347
372
|
$type: 'PropertyValueAssertion',
|
|
348
373
|
property: asRef(ontology, normalizedPredicate),
|
|
@@ -350,7 +375,12 @@ function addAssertion(ontology: any, subjectIri: string, predicateIri: string, o
|
|
|
350
375
|
referencedValues: [],
|
|
351
376
|
containedValues: []
|
|
352
377
|
};
|
|
353
|
-
subject
|
|
378
|
+
pushAstArrayChild(subject, 'ownedPropertyValues', propertyAssertion);
|
|
379
|
+
}
|
|
380
|
+
if (isIriLike(objectValue)) {
|
|
381
|
+
await ensureReferenceImport(shared, ontology, normalizeIri(asRequiredIri(objectValue, 'assertion object')));
|
|
382
|
+
} else {
|
|
383
|
+
await ensureTypedLiteralDatatypeImport(shared, ontology, objectValue);
|
|
354
384
|
}
|
|
355
385
|
appendAssertionValue(ontology, propertyAssertion, objectValue);
|
|
356
386
|
return true;
|
|
@@ -447,20 +477,57 @@ function removeAssertion(ontology: any, subjectIri: string, predicateIri: string
|
|
|
447
477
|
return changed;
|
|
448
478
|
}
|
|
449
479
|
|
|
450
|
-
function
|
|
480
|
+
async function updateAssertion(shared: any, ontology: any, subjectIri: string, predicateIri: string, objectValue: unknown): Promise<boolean> {
|
|
481
|
+
const normalizedPredicate = normalizeIri(predicateIri);
|
|
482
|
+
const subject = findNamedInstanceByIri(ontology, subjectIri);
|
|
483
|
+
if (!subject) {
|
|
484
|
+
throw new Error(`Subject '${subjectIri}' was not found in the ontology.`);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
if (normalizedPredicate === RDF_TYPE_IRI || isSourcePredicate(normalizedPredicate) || isTargetPredicate(normalizedPredicate)) {
|
|
488
|
+
const removed = removeAssertion(ontology, subjectIri, predicateIri, undefined);
|
|
489
|
+
const added = await addAssertion(shared, ontology, subjectIri, predicateIri, objectValue);
|
|
490
|
+
return removed || added;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
subject.ownedPropertyValues ??= [];
|
|
494
|
+
let propertyAssertion = subject.ownedPropertyValues.find((assertion: any) => matchesPredicateRef(ontology, assertion?.property, normalizedPredicate));
|
|
495
|
+
if (!propertyAssertion) {
|
|
496
|
+
return addAssertion(shared, ontology, subjectIri, predicateIri, objectValue);
|
|
497
|
+
}
|
|
498
|
+
if (isIriLike(objectValue)) {
|
|
499
|
+
await ensureReferenceImport(shared, ontology, normalizeIri(asRequiredIri(objectValue, 'assertion object')));
|
|
500
|
+
} else {
|
|
501
|
+
await ensureTypedLiteralDatatypeImport(shared, ontology, objectValue);
|
|
502
|
+
}
|
|
503
|
+
propertyAssertion.literalValues = [];
|
|
504
|
+
propertyAssertion.referencedValues = [];
|
|
505
|
+
propertyAssertion.containedValues = [];
|
|
506
|
+
appendAssertionValue(ontology, propertyAssertion, objectValue);
|
|
507
|
+
return true;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
async function addAnnotation(shared: any, ontology: any, subjectIri: string, predicateIri: string, objectValue: unknown): Promise<boolean> {
|
|
451
511
|
const subject = findAnnotationSubjectByIri(ontology, subjectIri);
|
|
452
512
|
if (!subject) {
|
|
453
513
|
throw new Error(`Annotation subject '${subjectIri}' was not found in the ontology.`);
|
|
454
514
|
}
|
|
515
|
+
const normalizedPredicate = normalizeIri(predicateIri);
|
|
516
|
+
await ensureReferenceImport(shared, ontology, normalizedPredicate);
|
|
455
517
|
subject.ownedAnnotations ??= [];
|
|
456
518
|
const annotation: any = {
|
|
457
519
|
$type: 'Annotation',
|
|
458
|
-
property: asRef(ontology,
|
|
520
|
+
property: asRef(ontology, normalizedPredicate),
|
|
459
521
|
literalValues: [],
|
|
460
522
|
referencedValues: []
|
|
461
523
|
};
|
|
524
|
+
if (isIriLike(objectValue)) {
|
|
525
|
+
await ensureReferenceImport(shared, ontology, normalizeIri(asRequiredIri(objectValue, 'annotation object')));
|
|
526
|
+
} else {
|
|
527
|
+
await ensureTypedLiteralDatatypeImport(shared, ontology, objectValue);
|
|
528
|
+
}
|
|
462
529
|
appendAnnotationValue(ontology, annotation, objectValue);
|
|
463
|
-
subject
|
|
530
|
+
pushAstArrayChild(subject, 'ownedAnnotations', annotation);
|
|
464
531
|
return true;
|
|
465
532
|
}
|
|
466
533
|
|
|
@@ -502,6 +569,28 @@ function removeAnnotation(ontology: any, subjectIri: string, predicateIri: strin
|
|
|
502
569
|
return changed;
|
|
503
570
|
}
|
|
504
571
|
|
|
572
|
+
async function updateAnnotation(shared: any, ontology: any, subjectIri: string, predicateIri: string, objectValue: unknown): Promise<boolean> {
|
|
573
|
+
const subject = findAnnotationSubjectByIri(ontology, subjectIri);
|
|
574
|
+
if (!subject) {
|
|
575
|
+
throw new Error(`Annotation subject '${subjectIri}' was not found in the ontology.`);
|
|
576
|
+
}
|
|
577
|
+
const normalizedPredicate = normalizeIri(predicateIri);
|
|
578
|
+
subject.ownedAnnotations ??= [];
|
|
579
|
+
const annotation = subject.ownedAnnotations.find((entry: any) => matchesPredicateRef(ontology, entry?.property, normalizedPredicate));
|
|
580
|
+
if (!annotation) {
|
|
581
|
+
return addAnnotation(shared, ontology, subjectIri, predicateIri, objectValue);
|
|
582
|
+
}
|
|
583
|
+
if (isIriLike(objectValue)) {
|
|
584
|
+
await ensureReferenceImport(shared, ontology, normalizeIri(asRequiredIri(objectValue, 'annotation object')));
|
|
585
|
+
} else {
|
|
586
|
+
await ensureTypedLiteralDatatypeImport(shared, ontology, objectValue);
|
|
587
|
+
}
|
|
588
|
+
annotation.literalValues = [];
|
|
589
|
+
annotation.referencedValues = [];
|
|
590
|
+
appendAnnotationValue(ontology, annotation, objectValue);
|
|
591
|
+
return true;
|
|
592
|
+
}
|
|
593
|
+
|
|
505
594
|
function deleteMemberRef(ontology: any, memberIri: string, typeIri: string | undefined): boolean {
|
|
506
595
|
if (!Array.isArray(ontology.ownedStatements)) {
|
|
507
596
|
return false;
|
|
@@ -818,9 +907,7 @@ function matchesPredicateRef(ontology: any, ref: any, iri: string): boolean {
|
|
|
818
907
|
return false;
|
|
819
908
|
}
|
|
820
909
|
const normalizedWanted = normalizeIri(iri);
|
|
821
|
-
const refText =
|
|
822
|
-
? ref
|
|
823
|
-
: (typeof ref.$refText === 'string' ? ref.$refText : undefined);
|
|
910
|
+
const refText = getRefText(ref);
|
|
824
911
|
if (refText) {
|
|
825
912
|
const normalizedRefText = normalizeIri(expandRefTextToIri(ontology, refText) ?? refText);
|
|
826
913
|
if (isSameIriTarget(normalizedRefText, normalizedWanted)) {
|
|
@@ -835,13 +922,12 @@ function matchesPredicateRef(ontology: any, ref: any, iri: string): boolean {
|
|
|
835
922
|
function appendAssertionValue(ontology: any, assertion: any, objectValue: unknown): void {
|
|
836
923
|
if (isIriLike(objectValue)) {
|
|
837
924
|
const normalizedObjectIri = normalizeIri(asRequiredIri(objectValue, 'assertion object'));
|
|
838
|
-
ensureReferenceImport(ontology, normalizedObjectIri);
|
|
839
925
|
assertion.referencedValues ??= [];
|
|
840
926
|
assertion.referencedValues.push(asRef(ontology, normalizedObjectIri));
|
|
841
927
|
return;
|
|
842
928
|
}
|
|
843
929
|
assertion.literalValues ??= [];
|
|
844
|
-
assertion
|
|
930
|
+
pushAstArrayChild(assertion, 'literalValues', asLiteral(ontology, objectValue));
|
|
845
931
|
}
|
|
846
932
|
|
|
847
933
|
function removeAssertionValue(ontology: any, assertion: any, objectValue: unknown): void {
|
|
@@ -850,20 +936,19 @@ function removeAssertionValue(ontology: any, assertion: any, objectValue: unknow
|
|
|
850
936
|
assertion.referencedValues = (assertion.referencedValues ?? []).filter((ref: any) => !matchesPredicateRef(ontology, ref, normalizedIri));
|
|
851
937
|
return;
|
|
852
938
|
}
|
|
853
|
-
const normalizedLiteral = normalizeLiteralValue(asLiteral(objectValue));
|
|
939
|
+
const normalizedLiteral = normalizeLiteralValue(asLiteral(ontology, objectValue));
|
|
854
940
|
assertion.literalValues = (assertion.literalValues ?? []).filter((literal: any) => normalizeLiteralValue(literal) !== normalizedLiteral);
|
|
855
941
|
}
|
|
856
942
|
|
|
857
943
|
function appendAnnotationValue(ontology: any, annotation: any, objectValue: unknown): void {
|
|
858
944
|
if (isIriLike(objectValue)) {
|
|
859
945
|
const normalizedObjectIri = normalizeIri(asRequiredIri(objectValue, 'annotation object'));
|
|
860
|
-
ensureReferenceImport(ontology, normalizedObjectIri);
|
|
861
946
|
annotation.referencedValues ??= [];
|
|
862
947
|
annotation.referencedValues.push(asRef(ontology, normalizedObjectIri));
|
|
863
948
|
return;
|
|
864
949
|
}
|
|
865
950
|
annotation.literalValues ??= [];
|
|
866
|
-
annotation
|
|
951
|
+
pushAstArrayChild(annotation, 'literalValues', asLiteral(ontology, objectValue));
|
|
867
952
|
}
|
|
868
953
|
|
|
869
954
|
function removeAnnotationValue(ontology: any, annotation: any, objectValue: unknown): void {
|
|
@@ -872,7 +957,7 @@ function removeAnnotationValue(ontology: any, annotation: any, objectValue: unkn
|
|
|
872
957
|
annotation.referencedValues = (annotation.referencedValues ?? []).filter((ref: any) => !matchesPredicateRef(ontology, ref, normalizedIri));
|
|
873
958
|
return;
|
|
874
959
|
}
|
|
875
|
-
const normalizedLiteral = normalizeLiteralValue(asLiteral(objectValue));
|
|
960
|
+
const normalizedLiteral = normalizeLiteralValue(asLiteral(ontology, objectValue));
|
|
876
961
|
annotation.literalValues = (annotation.literalValues ?? []).filter((literal: any) => normalizeLiteralValue(literal) !== normalizedLiteral);
|
|
877
962
|
}
|
|
878
963
|
|
|
@@ -1006,7 +1091,7 @@ function expandLocalRefText(rawNamespace: string | undefined, localName: string)
|
|
|
1006
1091
|
return `${namespace}#${local}`;
|
|
1007
1092
|
}
|
|
1008
1093
|
|
|
1009
|
-
function ensureReferenceImport(ontology: any, iri: string): void {
|
|
1094
|
+
async function ensureReferenceImport(shared: any, ontology: any, iri: string): Promise<void> {
|
|
1010
1095
|
const target = parseIriParts(iri);
|
|
1011
1096
|
if (!target) {
|
|
1012
1097
|
return;
|
|
@@ -1036,17 +1121,55 @@ function ensureReferenceImport(ontology: any, iri: string): void {
|
|
|
1036
1121
|
}
|
|
1037
1122
|
const prefix = pickImportPrefix(target.namespace, usedPrefixes);
|
|
1038
1123
|
const importedRef = `<${target.namespace}${target.separator}>`;
|
|
1124
|
+
const importKind = await resolveImportKind(shared, ontology, target.namespace);
|
|
1039
1125
|
const importStatement: any = {
|
|
1040
1126
|
$type: 'Import',
|
|
1041
|
-
kind:
|
|
1127
|
+
kind: importKind,
|
|
1042
1128
|
prefix,
|
|
1043
1129
|
imported: { $refText: importedRef }
|
|
1044
1130
|
};
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1131
|
+
pushAstArrayChild(ontology, 'ownedImports', importStatement);
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
async function resolveImportKind(shared: any, importing: any, importedNamespace: string): Promise<'extends' | 'includes' | 'uses'> {
|
|
1135
|
+
const imported = await findOntologyByNamespace(shared, importedNamespace);
|
|
1136
|
+
if (imported) {
|
|
1137
|
+
if (importing.$type === imported.$type) {
|
|
1138
|
+
return 'extends';
|
|
1139
|
+
}
|
|
1140
|
+
if (isVocabulary(importing) && isDescription(imported)) {
|
|
1141
|
+
return 'uses';
|
|
1142
|
+
}
|
|
1143
|
+
if (isDescriptionBox(importing) && isVocabulary(imported)) {
|
|
1144
|
+
return 'uses';
|
|
1145
|
+
}
|
|
1146
|
+
if (isVocabularyBundle(importing) && isVocabulary(imported)) {
|
|
1147
|
+
return 'includes';
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
if (isVocabularyBundle(importing)) {
|
|
1151
|
+
return 'includes';
|
|
1152
|
+
}
|
|
1153
|
+
if (isDescriptionBox(importing)) {
|
|
1154
|
+
return 'uses';
|
|
1048
1155
|
}
|
|
1049
|
-
|
|
1156
|
+
return 'extends';
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
async function findOntologyByNamespace(shared: any, namespace: string): Promise<any | undefined> {
|
|
1160
|
+
const index = getOntologyModelIndex(shared);
|
|
1161
|
+
const modelUri = index.resolveModelUri(normalizeNamespace(namespace));
|
|
1162
|
+
if (!modelUri) {
|
|
1163
|
+
return undefined;
|
|
1164
|
+
}
|
|
1165
|
+
const uri = URI.parse(modelUri);
|
|
1166
|
+
const langiumDocuments: any = shared.workspace.LangiumDocuments;
|
|
1167
|
+
const builder: any = shared.workspace.DocumentBuilder;
|
|
1168
|
+
const document = langiumDocuments.getDocument(uri)
|
|
1169
|
+
?? await langiumDocuments.getOrCreateDocument(uri);
|
|
1170
|
+
await builder.build([document], { validation: false });
|
|
1171
|
+
const ontology = document?.parseResult?.value;
|
|
1172
|
+
return ontology && isOntology(ontology) ? ontology : undefined;
|
|
1050
1173
|
}
|
|
1051
1174
|
|
|
1052
1175
|
function parseIriParts(iri: string): { namespace: string; fragment: string; separator: '#' | '/' } | undefined {
|
|
@@ -1198,7 +1321,17 @@ function pickImportPrefix(namespace: string, usedPrefixes: ReadonlySet<string>):
|
|
|
1198
1321
|
return `${candidateBase}${suffix}`;
|
|
1199
1322
|
}
|
|
1200
1323
|
|
|
1201
|
-
function asLiteral(value: unknown): any {
|
|
1324
|
+
function asLiteral(ontology: any, value: unknown): any {
|
|
1325
|
+
if (isTypedQuotedLiteralTransport(value)) {
|
|
1326
|
+
const literalValue = String(value.value ?? '');
|
|
1327
|
+
const datatypeIri = typeof value.datatypeIri === 'string' ? value.datatypeIri.trim() : '';
|
|
1328
|
+
const datatypeRefText = typeof value.datatypeRefText === 'string' ? value.datatypeRefText.trim() : '';
|
|
1329
|
+
return datatypeIri
|
|
1330
|
+
? { $type: 'QuotedLiteral', value: literalValue, type: { $refText: toRefText(ontology, normalizeIri(datatypeIri)) } }
|
|
1331
|
+
: datatypeRefText
|
|
1332
|
+
? { $type: 'QuotedLiteral', value: literalValue, type: { $refText: datatypeRefText } }
|
|
1333
|
+
: { $type: 'QuotedLiteral', value: literalValue };
|
|
1334
|
+
}
|
|
1202
1335
|
if (typeof value === 'boolean') {
|
|
1203
1336
|
return { $type: 'BooleanLiteral', value };
|
|
1204
1337
|
}
|
|
@@ -1211,6 +1344,42 @@ function asLiteral(value: unknown): any {
|
|
|
1211
1344
|
return { $type: 'QuotedLiteral', value: String(value ?? '') };
|
|
1212
1345
|
}
|
|
1213
1346
|
|
|
1347
|
+
async function ensureTypedLiteralDatatypeImport(shared: any, ontology: any, value: unknown): Promise<void> {
|
|
1348
|
+
if (!isTypedQuotedLiteralTransport(value)) {
|
|
1349
|
+
return;
|
|
1350
|
+
}
|
|
1351
|
+
const datatypeIri = typeof value.datatypeIri === 'string' ? normalizeIri(value.datatypeIri) : '';
|
|
1352
|
+
if (!datatypeIri) {
|
|
1353
|
+
return;
|
|
1354
|
+
}
|
|
1355
|
+
await ensureReferenceImport(shared, ontology, datatypeIri);
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
function isTypedQuotedLiteralTransport(value: unknown): value is { $type?: unknown; value?: unknown; datatypeIri?: unknown; datatypeRefText?: unknown } {
|
|
1359
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
1360
|
+
return false;
|
|
1361
|
+
}
|
|
1362
|
+
const record = value as Record<string, unknown>;
|
|
1363
|
+
if (record.$type === 'QuotedLiteral') {
|
|
1364
|
+
return true;
|
|
1365
|
+
}
|
|
1366
|
+
return (typeof record.datatypeIri === 'string' || typeof record.datatypeRefText === 'string') && 'value' in record;
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
function attachAstNode<T extends Record<string, any>>(node: T, parent: any, containerProperty: string): T {
|
|
1370
|
+
(node as any).$container = parent;
|
|
1371
|
+
(node as any).$containerProperty = containerProperty;
|
|
1372
|
+
return node;
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
function pushAstArrayChild(parent: any, property: string, node: any): void {
|
|
1376
|
+
if (!Array.isArray(parent[property])) {
|
|
1377
|
+
parent[property] = [];
|
|
1378
|
+
}
|
|
1379
|
+
attachAstNode(node, parent, property);
|
|
1380
|
+
parent[property].push(node);
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1214
1383
|
function asRequiredIri(value: unknown, context: string): string {
|
|
1215
1384
|
if (!isIriLike(value)) {
|
|
1216
1385
|
throw new Error(`Expected IRI for ${context}.`);
|