@lionweb/validation 0.7.0-beta.18 → 0.7.0-beta.19
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/CHANGELOG.md +0 -1
- package/package.json +4 -4
- package/src/index.ts +4 -0
- package/src/issues/LanguageIssues.ts +147 -0
- package/src/issues/ReferenceIssues.ts +83 -0
- package/src/issues/SyntaxIssues.ts +84 -0
- package/src/issues/ValidationIssue.ts +29 -0
- package/src/issues/index.ts +4 -0
- package/src/languages/CompositeLionWebLanguageWrapper.ts +57 -0
- package/src/languages/LanguageRegistry.ts +44 -0
- package/src/languages/LanguageUtils.ts +63 -0
- package/src/languages/LionCore-M3.json +2356 -0
- package/src/languages/LionCore-builtins.json +372 -0
- package/src/languages/LionWebLanguageWrapper.ts +91 -0
- package/src/languages/MetaPointerMap.ts +41 -0
- package/src/languages/index.ts +2 -0
- package/src/runners/FileUtils.ts +59 -0
- package/src/runners/RunCheckFolder.ts +7 -0
- package/src/runners/RunCheckFolderWithLanguage.ts +45 -0
- package/src/runners/RunCheckOneFile.ts +7 -0
- package/src/runners/RunCheckOneFileWithLanguage.ts +35 -0
- package/src/runners/RunLioncoreDiff.ts +23 -0
- package/src/runners/Utils.ts +54 -0
- package/src/runners/index.ts +2 -0
- package/src/validators/LionWebChunkDefinitions.ts +104 -0
- package/src/validators/LionWebLanguageReferenceValidator.ts +201 -0
- package/src/validators/LionWebLanguageValidator.ts +79 -0
- package/src/validators/LionWebReferenceValidator.ts +225 -0
- package/src/validators/LionWebSyntaxValidator.ts +14 -0
- package/src/validators/LionWebValidator.ts +71 -0
- package/src/validators/ValidationFunctions.ts +129 -0
- package/src/validators/generic/SyntaxValidator.ts +164 -0
- package/src/validators/generic/ValidationResult.ts +17 -0
- package/src/validators/generic/index.ts +3 -0
- package/src/validators/generic/schema/DefinitionSchema.ts +52 -0
- package/src/validators/generic/schema/ValidationTypes.ts +134 -0
- package/src/validators/generic/schema/index.ts +2 -0
- package/src/validators/index.ts +8 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { LionWebJsonChunk } from "@lionweb/json"
|
|
2
|
+
import { LionWebJsonDiff } from "@lionweb/json-diff"
|
|
3
|
+
import fs from "fs"
|
|
4
|
+
|
|
5
|
+
const file1 = process.argv[2]
|
|
6
|
+
const file2 = process.argv[3]
|
|
7
|
+
|
|
8
|
+
if (file1 !== null && file1 !== undefined) {
|
|
9
|
+
const jsonString1 = fs.readFileSync(file1, "utf-8")
|
|
10
|
+
const json1 = JSON.parse(jsonString1)
|
|
11
|
+
const jsonString2 = fs.readFileSync(file2, "utf-8")
|
|
12
|
+
const json2 = JSON.parse(jsonString2)
|
|
13
|
+
|
|
14
|
+
const lwDiff = new LionWebJsonDiff()
|
|
15
|
+
lwDiff.diffLwChunk(json1 as LionWebJsonChunk, json2 as LionWebJsonChunk)
|
|
16
|
+
if (lwDiff.diffResult.changes.length === 0) {
|
|
17
|
+
console.log("LionWebJsonDiff: equal")
|
|
18
|
+
} else {
|
|
19
|
+
console.log("LionWebJsonDiff: " + lwDiff.diff)
|
|
20
|
+
}
|
|
21
|
+
} else {
|
|
22
|
+
console.log("Error in arguments")
|
|
23
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import fs from "fs"
|
|
2
|
+
import path from "path"
|
|
3
|
+
import { ValidationResult } from "../validators/generic/ValidationResult.js"
|
|
4
|
+
|
|
5
|
+
export function getFilesRecursive(dirPath: string, arrayOfFiles: string[]) {
|
|
6
|
+
const files = fs.readdirSync(dirPath)
|
|
7
|
+
arrayOfFiles = arrayOfFiles || []
|
|
8
|
+
|
|
9
|
+
files.forEach(function (file: string) {
|
|
10
|
+
if (fs.statSync(dirPath + "/" + file).isDirectory()) {
|
|
11
|
+
arrayOfFiles = getFilesRecursive(dirPath + "/" + file, arrayOfFiles)
|
|
12
|
+
} else {
|
|
13
|
+
arrayOfFiles.push(path.join(dirPath, "/", file))
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
return arrayOfFiles
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function getFilesDirect(dirPath: string, arrayOfFiles: string[]) {
|
|
20
|
+
const files = fs.readdirSync(dirPath)
|
|
21
|
+
arrayOfFiles = arrayOfFiles || []
|
|
22
|
+
|
|
23
|
+
files.forEach(function (file: string) {
|
|
24
|
+
if (fs.statSync(dirPath + "/" + file).isFile()) {
|
|
25
|
+
arrayOfFiles.push(file)
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
return arrayOfFiles
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function getAllDirectories(dirPath: string, arrayOfDirs: string[]) {
|
|
32
|
+
const files = fs.readdirSync(dirPath, { withFileTypes: true })
|
|
33
|
+
arrayOfDirs = arrayOfDirs || []
|
|
34
|
+
|
|
35
|
+
files.forEach(function (file: fs.Dirent) {
|
|
36
|
+
if (file.isDirectory()) {
|
|
37
|
+
arrayOfDirs = getAllDirectories(dirPath + "/" + file.name, arrayOfDirs)
|
|
38
|
+
arrayOfDirs.push(path.join(dirPath, "/", file.name))
|
|
39
|
+
} else {
|
|
40
|
+
// ignore files
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
return arrayOfDirs
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function printIssues(result: ValidationResult, file?: string): void {
|
|
47
|
+
result.issues.forEach(issue => console.log((file == undefined ? "" : `File ${file}: `) + issue.errorMsg()))
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function issuestoString(vresult: ValidationResult, file?: string): string {
|
|
51
|
+
let result = "ISSUES: "
|
|
52
|
+
vresult.issues.forEach(issue => (result += (file === undefined ? "NOFILE" : `File ${file}: `) + issue.errorMsg() + "\n"))
|
|
53
|
+
return result
|
|
54
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { DefinitionSchema, MAY_BE_NULL, PropertyDef, PrimitiveDef } from "./generic/index.js"
|
|
2
|
+
import { validateId, validateKey, validateSerializationFormatVersion, validateVersion } from "./ValidationFunctions.js"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The structure below defines the structure of a LionWeb Chunk by defining all the properties.
|
|
6
|
+
* It can
|
|
7
|
+
* - be used by the SyntaxValidator to validate an object sat runtime.
|
|
8
|
+
* - used to generate all the TypeScript types for a LionWebChunk.
|
|
9
|
+
*/
|
|
10
|
+
export const LionWebSchema: DefinitionSchema = new DefinitionSchema([
|
|
11
|
+
{
|
|
12
|
+
name: "LionWebJsonMetaPointer",
|
|
13
|
+
properties: [
|
|
14
|
+
PropertyDef({ name: "key", type: "LionWebKey" }),
|
|
15
|
+
PropertyDef({ name: "version", type: "LionWebVersion" }),
|
|
16
|
+
PropertyDef({ name: "language", type: "LionWebKey" })
|
|
17
|
+
]
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: "LionWebJsonMetaPointer",
|
|
21
|
+
properties: [
|
|
22
|
+
PropertyDef({ name: "key", type: "LionWebKey" }),
|
|
23
|
+
PropertyDef({ name: "version", type: "LionWebVersion" }),
|
|
24
|
+
PropertyDef({ name: "language", type: "LionWebKey" }),
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: "ResponseMessage",
|
|
29
|
+
properties: [
|
|
30
|
+
PropertyDef({ name: "kind", type: "JSstring" }),
|
|
31
|
+
PropertyDef({ name: "message", type: "JSstring" }),
|
|
32
|
+
PropertyDef({ name: "data", type: "JSobject", mayBeNull: true, isOptional: true })
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: "LionWebJsonChunk",
|
|
37
|
+
properties: [
|
|
38
|
+
PropertyDef({ name: "serializationFormatVersion", type: "LionWebSerializationFormatVersion" }),
|
|
39
|
+
PropertyDef({ name: "languages", type: "LionWebJsonUsedLanguage", isList: true }),
|
|
40
|
+
PropertyDef({ name: "nodes", type: "LionWebJsonNode", isList: true })
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "LionWebJsonUsedLanguage",
|
|
45
|
+
properties: [
|
|
46
|
+
PropertyDef({ name: "key", type: "LionWebKey" }),
|
|
47
|
+
PropertyDef({ name: "version", type: "LionWebVersion" })
|
|
48
|
+
]
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: "LionWebJsonNode",
|
|
52
|
+
properties: [
|
|
53
|
+
PropertyDef({ name: "id", type: "LionWebId" }),
|
|
54
|
+
PropertyDef({ name: "classifier", type: "LionWebJsonMetaPointer" }),
|
|
55
|
+
PropertyDef({ name: "properties", type: "LionWebJsonProperty", isList: true }),
|
|
56
|
+
PropertyDef({ name: "containments", type: "LionWebJsonContainment", isList: true }),
|
|
57
|
+
PropertyDef({ name: "references", type: "LionWebJsonReference", isList: true }),
|
|
58
|
+
PropertyDef({ name: "annotations", type: "LionWebId", isList: true }),
|
|
59
|
+
PropertyDef({ name: "parent", type: "LionWebId", mayBeNull: MAY_BE_NULL }),
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: "LionWebJsonProperty",
|
|
64
|
+
properties: [
|
|
65
|
+
PropertyDef({ name: "property", type: "LionWebJsonMetaPointer" }),
|
|
66
|
+
PropertyDef({ name: "value", type: "JSstring", mayBeNull: MAY_BE_NULL }),
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
name: "LionWebJsonContainment",
|
|
71
|
+
properties: [
|
|
72
|
+
PropertyDef({ name: "containment", type: "LionWebJsonMetaPointer" }),
|
|
73
|
+
PropertyDef({ name: "children", type: "LionWebId", isList: true }),
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: "LionWebJsonReference",
|
|
78
|
+
properties: [
|
|
79
|
+
PropertyDef({ name: "reference", type: "LionWebJsonMetaPointer"}),
|
|
80
|
+
PropertyDef({ name: "targets", type: "LionWebJsonReferenceTarget", isList: true}),
|
|
81
|
+
]
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: "LionWebJsonReferenceTarget",
|
|
85
|
+
properties: [
|
|
86
|
+
PropertyDef({ name: "resolveInfo", type: "JSstring", mayBeNull: MAY_BE_NULL }),
|
|
87
|
+
PropertyDef({ name: "reference", type: "LionWebId", mayBeNull: MAY_BE_NULL }),
|
|
88
|
+
]
|
|
89
|
+
},
|
|
90
|
+
/**
|
|
91
|
+
* Elements without properties are assumed to be JSON/JS primitive values, and tested using `typeof`
|
|
92
|
+
* and the (optional) validate function.
|
|
93
|
+
*/
|
|
94
|
+
PrimitiveDef({ name: "LionWebId", primitiveType: "string", validate: validateId }),
|
|
95
|
+
PrimitiveDef({ name: "LionWebKey", primitiveType: "string", validate: validateKey }),
|
|
96
|
+
PrimitiveDef({ name: "LionWebVersion",primitiveType: "string", validate: validateVersion }),
|
|
97
|
+
PrimitiveDef({ name: "LionWebSerializationFormatVersion",primitiveType: "string", validate: validateSerializationFormatVersion }),
|
|
98
|
+
PrimitiveDef({ name: "JSstring", primitiveType: "string" }),
|
|
99
|
+
PrimitiveDef({ name: "JSobject",primitiveType: "object" }),
|
|
100
|
+
])
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import { isEqualMetaPointer, LionWebJsonContainment, LionWebJsonNode, LionWebJsonProperty, LionWebJsonReference } from "@lionweb/json"
|
|
2
|
+
import {
|
|
3
|
+
JsonContext,
|
|
4
|
+
LION_CORE_BUILTINS_INAMED_NAME,
|
|
5
|
+
LIONWEB_BOOLEAN_TYPE, LIONWEB_INTEGER_TYPE, LIONWEB_JSON_TYPE, LIONWEB_STRING_TYPE,
|
|
6
|
+
LionWebJsonChunkWrapper,
|
|
7
|
+
M3_Keys,
|
|
8
|
+
MetaPointers,
|
|
9
|
+
NodeUtils
|
|
10
|
+
} from "@lionweb/json-utils"
|
|
11
|
+
import {
|
|
12
|
+
Language_ContainmentMetaPointerNotInClass_Issue,
|
|
13
|
+
Language_IncorrectPropertyMetaPointer_Issue,
|
|
14
|
+
Language_PropertyMetaPointerNotInClass_Issue,
|
|
15
|
+
Language_PropertyValue_Issue,
|
|
16
|
+
Language_ReferenceMetaPointerNotInClass_Issue,
|
|
17
|
+
Language_UnknownConcept_Issue
|
|
18
|
+
} from "../issues/index.js"
|
|
19
|
+
import {
|
|
20
|
+
Language_IncorrectContainmentMetaPointer_Issue,
|
|
21
|
+
Language_IncorrectReferenceMetaPointer_Issue,
|
|
22
|
+
Language_UnknownContainment_Issue,
|
|
23
|
+
Language_UnknownProperty_Issue,
|
|
24
|
+
Language_UnknownReference_Issue
|
|
25
|
+
} from "../issues/LanguageIssues.js"
|
|
26
|
+
// import {
|
|
27
|
+
// KnownLanguages, LanguageRegistry
|
|
28
|
+
// } from "../languages/LanguageRegistry.js"
|
|
29
|
+
import { LanguageRegistry } from "../languages/index.js"
|
|
30
|
+
import { ValidationResult } from "./generic/ValidationResult.js"
|
|
31
|
+
import { validateBoolean, validateInteger, validateJSON } from "./ValidationFunctions.js"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Check against the language definition
|
|
36
|
+
*/
|
|
37
|
+
export class LionWebLanguageReferenceValidator {
|
|
38
|
+
validationResult: ValidationResult
|
|
39
|
+
|
|
40
|
+
constructor(validationResult: ValidationResult, private registry: LanguageRegistry) {
|
|
41
|
+
this.validationResult = validationResult
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// reset() {
|
|
45
|
+
// this.validationResult.reset();
|
|
46
|
+
// }
|
|
47
|
+
|
|
48
|
+
// TODO test reference and children implementation
|
|
49
|
+
validate(obj: LionWebJsonChunkWrapper): void {
|
|
50
|
+
obj.jsonChunk.nodes.forEach((node, nodeIndex) => {
|
|
51
|
+
const nodeContext = new JsonContext(null, ["node", nodeIndex])
|
|
52
|
+
const nodeConcept = this.registry.getNodeByMetaPointer(node.classifier)
|
|
53
|
+
if (nodeConcept === undefined && nodeContext !== undefined) {
|
|
54
|
+
this.validationResult.issue(new Language_UnknownConcept_Issue(nodeContext, node.classifier))
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
node.properties.forEach((property, propIndex) => {
|
|
58
|
+
this.validateProperty(node, nodeConcept, property, nodeContext.concat("properties", propIndex))
|
|
59
|
+
})
|
|
60
|
+
node.containments.forEach((containment, childIndex) => {
|
|
61
|
+
this.validateContainment(node, nodeConcept, containment, nodeContext.concat("containments", childIndex))
|
|
62
|
+
})
|
|
63
|
+
node.references.forEach((reference, refIndex) => {
|
|
64
|
+
this.validateReference(node, nodeConcept, reference, nodeContext.concat("references", refIndex))
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private validateContainment(_node: LionWebJsonNode, nodeConcept: LionWebJsonNode | undefined, containment: LionWebJsonContainment, context: JsonContext) {
|
|
70
|
+
const metaConcept = this.registry.getNodeByMetaPointer(containment.containment)
|
|
71
|
+
if (metaConcept === null || metaConcept === undefined) {
|
|
72
|
+
this.validationResult.issue(new Language_UnknownContainment_Issue(context, containment.containment))
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
if (metaConcept.classifier.key !== M3_Keys.Containment) {
|
|
76
|
+
this.validationResult.issue(new Language_IncorrectContainmentMetaPointer_Issue(context, containment.containment, metaConcept.classifier.key))
|
|
77
|
+
}
|
|
78
|
+
if (nodeConcept !== undefined) {
|
|
79
|
+
const cons = this.registry.languages.allContainments(nodeConcept)
|
|
80
|
+
if (!cons.includes(metaConcept)) {
|
|
81
|
+
this.validationResult.issue(new Language_ContainmentMetaPointerNotInClass_Issue(context, containment.containment, nodeConcept))
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// TODO check type of children
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private validateReference(_node: LionWebJsonNode, nodeConcept: LionWebJsonNode | undefined, ref: LionWebJsonReference, context: JsonContext) {
|
|
89
|
+
const referenceDefinition = this.registry.getNodeByMetaPointer(ref.reference)
|
|
90
|
+
if (referenceDefinition === null || referenceDefinition === undefined) {
|
|
91
|
+
this.validationResult.issue(new Language_UnknownReference_Issue(context, ref.reference))
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
if (referenceDefinition.classifier.key !== M3_Keys.Reference) {
|
|
95
|
+
this.validationResult.issue(new Language_IncorrectReferenceMetaPointer_Issue(context, ref.reference, referenceDefinition.classifier.key))
|
|
96
|
+
}
|
|
97
|
+
if (nodeConcept !== undefined) {
|
|
98
|
+
const refs = this.registry.languages.allReferenceDefinitions(nodeConcept)
|
|
99
|
+
if (!refs.includes(referenceDefinition)) {
|
|
100
|
+
this.validationResult.issue(new Language_ReferenceMetaPointerNotInClass_Issue(context, ref.reference, nodeConcept))
|
|
101
|
+
return
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// TODO Check type of reference (if possible)
|
|
106
|
+
|
|
107
|
+
// TODO Check for duplicate targets?
|
|
108
|
+
// No, already done without language check
|
|
109
|
+
// If so, what to check because there can be either or both a `resolveInfo` and a `reference`
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Checks wwhether the value of `prop1` is correct in relation with its property definition in the referred language.
|
|
114
|
+
* @param prop
|
|
115
|
+
*/
|
|
116
|
+
validateProperty(_node: LionWebJsonNode, nodeConcept: LionWebJsonNode | undefined, prop: LionWebJsonProperty, context: JsonContext): void {
|
|
117
|
+
if (prop.value === null) {
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
const propertyDefinition = this.registry.getNodeByMetaPointer(prop.property)
|
|
121
|
+
if ( propertyDefinition === undefined) {
|
|
122
|
+
this.validationResult.issue(new Language_UnknownProperty_Issue(context, prop.property))
|
|
123
|
+
return
|
|
124
|
+
}
|
|
125
|
+
if (propertyDefinition.classifier.key !== M3_Keys.Property) {
|
|
126
|
+
this.validationResult.issue(new Language_IncorrectPropertyMetaPointer_Issue(context, prop.property, propertyDefinition.classifier.key))
|
|
127
|
+
return
|
|
128
|
+
}
|
|
129
|
+
if (nodeConcept !== undefined) {
|
|
130
|
+
const props = this.registry.languages.allProperties(nodeConcept)
|
|
131
|
+
if (!props.includes(propertyDefinition)) {
|
|
132
|
+
this.validationResult.issue(new Language_PropertyMetaPointerNotInClass_Issue(context, prop.property, nodeConcept))
|
|
133
|
+
return
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const refType = NodeUtils.findReference(propertyDefinition, MetaPointers.PropertyType)
|
|
138
|
+
// const refType = propertyDefinition.references.find(ref => isEqualMetaPointer(ref.reference, MetaPointers.PropertyType))
|
|
139
|
+
const propertyName = propertyDefinition.properties.find(p => p.property.key === LION_CORE_BUILTINS_INAMED_NAME)?.value
|
|
140
|
+
// console.log("Found type should be " + refType.targets[0].reference);
|
|
141
|
+
if (propertyName !== null && propertyName !== undefined) {
|
|
142
|
+
if (refType !== null && refType !== undefined) {
|
|
143
|
+
const typeReferenceId = refType.targets[0].reference
|
|
144
|
+
switch (typeReferenceId) {
|
|
145
|
+
case LIONWEB_BOOLEAN_TYPE:
|
|
146
|
+
validateBoolean(prop.value, this.validationResult, context)
|
|
147
|
+
break
|
|
148
|
+
case LIONWEB_INTEGER_TYPE:
|
|
149
|
+
validateInteger(prop.value, this.validationResult, context)
|
|
150
|
+
break
|
|
151
|
+
case LIONWEB_STRING_TYPE:
|
|
152
|
+
// Each string is correct and having another JSON type is already captured
|
|
153
|
+
break
|
|
154
|
+
case LIONWEB_JSON_TYPE:
|
|
155
|
+
validateJSON(prop.value, this.validationResult, context)
|
|
156
|
+
break
|
|
157
|
+
default: {
|
|
158
|
+
// Check for enumeration
|
|
159
|
+
// console.log("validateProperty: 0")
|
|
160
|
+
const propTypeReference = NodeUtils.findReference(propertyDefinition, MetaPointers.PropertyType)
|
|
161
|
+
if (propTypeReference === undefined) {
|
|
162
|
+
// console.log("validateProperty: 1")
|
|
163
|
+
break
|
|
164
|
+
}
|
|
165
|
+
const propType = this.registry.findNode(propTypeReference.targets[0].reference)
|
|
166
|
+
if (propType === undefined) {
|
|
167
|
+
// console.log("validateProperty: 1.4 for " + prop.value + " ==> " + propTypeReference.targets[0].reference)
|
|
168
|
+
break
|
|
169
|
+
}
|
|
170
|
+
let match = false
|
|
171
|
+
if (isEqualMetaPointer(propType.classifier, MetaPointers.Enumeration)) {
|
|
172
|
+
const literalsContainment = NodeUtils.findContainment(propType, MetaPointers.EnumerationLiterals)
|
|
173
|
+
if (literalsContainment === undefined) {
|
|
174
|
+
// console.log("validateProperty: 2")
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
const literals = literalsContainment.children.map(child => this.registry.findNode(child))
|
|
178
|
+
match = literals.some(lit => {
|
|
179
|
+
if (lit === undefined) {
|
|
180
|
+
// console.log("validateProperty: 3")
|
|
181
|
+
return false
|
|
182
|
+
}
|
|
183
|
+
const litKeyProp = NodeUtils.findProperty(lit, MetaPointers.IKeyedKey)
|
|
184
|
+
if (litKeyProp === undefined) {
|
|
185
|
+
// console.log("validateProperty: 4")
|
|
186
|
+
return false
|
|
187
|
+
}
|
|
188
|
+
return prop.value === litKeyProp.value
|
|
189
|
+
})
|
|
190
|
+
}
|
|
191
|
+
if (!match) {
|
|
192
|
+
this.validationResult.issue(new Language_PropertyValue_Issue(context, prop.property.key, prop.value, propType.classifier.key))
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
} else {
|
|
197
|
+
// TODO refType not found, but
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { isEqualMetaPointer, LionWebJsonChunk, LionWebJsonNode } from "@lionweb/json"
|
|
2
|
+
import { isLionWebM3Language, JsonContext, LionWebJsonChunkWrapper, MetaPointers } from "@lionweb/json-utils"
|
|
3
|
+
import { asMinimalJsonString } from "@lionweb/ts-utils"
|
|
4
|
+
import { GenericIssue } from "../issues/index.js"
|
|
5
|
+
import { MissingM3Language_Issue } from "../issues/LanguageIssues.js"
|
|
6
|
+
import { isConcept, LanguageRegistry } from "../languages/index.js"
|
|
7
|
+
import { ValidationResult } from "./generic/ValidationResult.js"
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Validates whether a chunk is a valid language definition
|
|
11
|
+
*/
|
|
12
|
+
export class LionWebLanguageValidator {
|
|
13
|
+
validationResult: ValidationResult
|
|
14
|
+
chunkWrapper: LionWebJsonChunkWrapper | undefined
|
|
15
|
+
// availableLanguages: LionWebLanguageDefinition[] = []
|
|
16
|
+
|
|
17
|
+
constructor(validationResult: ValidationResult, private registry: LanguageRegistry) {
|
|
18
|
+
this.validationResult = validationResult
|
|
19
|
+
this.chunkWrapper = undefined
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Check whether the metamodel is a Language.
|
|
24
|
+
* Assumption is that _chunk_ is already validated as a correct :LionWebJsonChunk
|
|
25
|
+
* @param chunk
|
|
26
|
+
*/
|
|
27
|
+
validateLanguage(chunk: LionWebJsonChunk) {
|
|
28
|
+
this.chunkWrapper = new LionWebJsonChunkWrapper(chunk)
|
|
29
|
+
const usedM3Language = chunk.languages.find(lang => isLionWebM3Language(lang))
|
|
30
|
+
if (usedM3Language === undefined) {
|
|
31
|
+
this.validationResult.issue(new MissingM3Language_Issue(new JsonContext(null, ["languages"])))
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const languageNodes = this.chunkWrapper.findNodesOfClassifier(MetaPointers.Language)
|
|
35
|
+
if (languageNodes.length !== 1) {
|
|
36
|
+
// TODO Better error handling.
|
|
37
|
+
console.error("Error: xpected exactly one Language node, found " + languageNodes.length + " => " + asMinimalJsonString(languageNodes))
|
|
38
|
+
}
|
|
39
|
+
chunk.nodes.forEach((node, index) => {
|
|
40
|
+
if (!isConcept(node)) {
|
|
41
|
+
this.validationResult.issue(new GenericIssue(new JsonContext(null, ["nodes", index]), `node ${node.id} is not a concept`))
|
|
42
|
+
} else {
|
|
43
|
+
this.validateConcept(node)
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
validateConcept(node: LionWebJsonNode): void {
|
|
49
|
+
node.properties.forEach(prop => {
|
|
50
|
+
const properties = this.chunkWrapper?.findNodesOfClassifier(MetaPointers.Property)
|
|
51
|
+
const matchedProperty = properties?.find(p => isEqualMetaPointer(p.classifier, prop.property))
|
|
52
|
+
// DUMMY
|
|
53
|
+
return matchedProperty !== undefined
|
|
54
|
+
})
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
validateNode(node: LionWebJsonNode): void {
|
|
58
|
+
const classifier = this.registry.getNodeByMetaPointer(node.classifier)
|
|
59
|
+
if (classifier === undefined) {
|
|
60
|
+
this.validationResult.issue(
|
|
61
|
+
new GenericIssue(new JsonContext(null, ["nodes"]), `Classifier ${node.classifier.key} not found for node ${node.id}`)
|
|
62
|
+
)
|
|
63
|
+
return
|
|
64
|
+
}
|
|
65
|
+
this.validateProperties(node, classifier)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
validateProperties(node: LionWebJsonNode, classifier: LionWebJsonNode): void {
|
|
69
|
+
node.properties.forEach(actualProp => {
|
|
70
|
+
const propClassifier = this.registry.getNodeByMetaPointer(actualProp.property)
|
|
71
|
+
if (propClassifier === undefined) {
|
|
72
|
+
this.validationResult.issue(
|
|
73
|
+
new GenericIssue(new JsonContext(null, ["nodes", "properties"]),
|
|
74
|
+
`Property ${actualProp.property.key} not found for classifier ${classifier.id}`)
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
}
|