Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions docs/class_semantic_validation.puml
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,21 @@ class ContextMapperDslValidationRegistry {
}

class ContextMapperDslValidator {
+ checkContextMappingModel(ContextMappingModel, ValidationAcceptor)
+ checkValue(Value, ValidationAcceptor)
+ validate(AstNode, ValidationAcceptor)
}

class ContextMapperValidationProviderRegistry {
- _providers: Map<string, ContextMapperValidationProvider<AstNode>>
+ get(AstNode): ContextMapperValidationProvider<AstNode>
}

interface ContextMapperValidationProvider {
+ supports(AstNode): boolean
+ validate(T, ValidationAcceptor)
}

ValidationRegistry <|-- ContextMapperDslValidationRegistry
ContextMapperDslValidationRegistry --> ContextMapperDslValidator
ContextMapperDslValidationRegistry --> ContextMapperValidationProviderRegistry
ContextMapperDslValidator --> ContextMapperValidationProviderRegistry
ContextMapperValidationProviderRegistry o-- "0..*" ContextMapperValidationProvider
@enduml
6 changes: 4 additions & 2 deletions src/language/ContextMapperDslModule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import { ContextMapperValidationProviderRegistry } from './validation/ContextMapperValidationProviderRegistry.js'
import { ContextMapperDslScopeProvider } from './references/ContextMapperDslScopeProvider.js'
import { ContextMapperDslFoldingRangeProvider } from './folding/ContextMapperDslFoldingRageProvider.js'
import { ContextMapperDslScopeComputation } from './references/ContextMapperDslScopeComputation.js'

/**
* Declaration of custom services - add your own service classes here.
Expand Down Expand Up @@ -45,14 +46,15 @@
export const ContextMapperDslModule: Module<ContextMapperDslServices, ModuleType> = {
validation: {
ContextMapperDslValidator: () => new ContextMapperDslValidator(validationProviderRegistry),
ValidationRegistry: (services) => new ContextMapperDslValidationRegistry(services)
ValidationRegistry: (services) => new ContextMapperDslValidationRegistry(services, validationProviderRegistry)
},
lsp: {
SemanticTokenProvider: (services) => new ContextMapperDslSemanticTokenProvider(services, semanticTokenProviderRegistry),
FoldingRangeProvider: (services) => new ContextMapperDslFoldingRangeProvider(services)
},
references: {
ScopeProvider: (services) => new ContextMapperDslScopeProvider(services)
ScopeProvider: (services) => new ContextMapperDslScopeProvider(services),
ScopeComputation: (services) => new ContextMapperDslScopeComputation(services)
}
}

Expand Down Expand Up @@ -88,7 +90,7 @@
if (!context.connection) {
// We don't run inside a language server
// Therefore, initialize the configuration provider instantly
shared.workspace.ConfigurationProvider.initialized({})

Check notice on line 93 in src/language/ContextMapperDslModule.ts

View workflow job for this annotation

GitHub Actions / Qodana for JS

Result of method call returning a promise is ignored

Promise returned from initialized is ignored
}
return { shared, ContextMapperDsl }
}
134 changes: 65 additions & 69 deletions src/language/context-mapper-dsl.langium
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ hidden terminal SL_COMMENT: /\/\/[^\n\r]*/;

entry ContextMappingModel:
(
(contextMaps+=ContextMap) |
(contextMap+=ContextMap) |
(boundedContexts+=BoundedContext) |
(domains+=Domain) |
(userRequirements+=UserRequirement) |
Expand All @@ -22,22 +22,20 @@ entry ContextMappingModel:
;

/*
In Langium, elements in unordered groups are optional by default.
However, the usage of the ? cardinality is not allowed in unordered groups.
Therefore, to create a rule with optional, unordered elements, one needs to omit the ? operator and combine them with the & operator.
This behavior may change in the future.
Also, unordered groups may cause unreadable parsing errors. To resolve that, unordered groups can be replaced with a (A | B | C)* rule and enforce non-repetition of elements with a validator.
Unordered groups may cause unreadable parsing errors. To resolve that, unordered groups can be replaced with a (A | B | C)* rule and enforce non-repetition of elements with a validator.
https://github.com/eclipse-langium/langium/discussions/1903
Also, unordered groups cause issues with autocomplete.
Therefore all unordered group rules had to be converted to the recommended workaround from above.
*/

ContextMap:
// {ContextMap} make sure there is always a context map
'ContextMap' (name=ID)?
OPEN
(
('type' ('=')? type=ContextMapType) &
('state' ('=')? state=ContextMapState)
)
('type' ('=')? type+=ContextMapType) |
('state' ('=')? state+=ContextMapState)
)*
('contains' boundedContexts+=[BoundedContext] ("," boundedContexts+=[BoundedContext])*)*
relationships+=Relationship*
CLOSE
Expand All @@ -46,22 +44,22 @@ ContextMap:
BoundedContext:
'BoundedContext' name=ID (
(
('implements' (implementedDomainParts+=[DomainPart]) ("," implementedDomainParts+=[DomainPart])*) &
('realizes' (realizedBoundedContexts+=[BoundedContext]) ("," realizedBoundedContexts+=[BoundedContext])*) &
('refines' refinedBoundedContext=[BoundedContext])
)
('implements' (implementedDomainParts+=[DomainPart]) ("," implementedDomainParts+=[DomainPart])*) |
('realizes' (realizedBoundedContexts+=[BoundedContext]) ("," realizedBoundedContexts+=[BoundedContext])*) |
('refines' refinedBoundedContext+=[BoundedContext])
)*
)
(
OPEN
(
('domainVisionStatement' ('=')? domainVisionStatement=STRING) &
('type' ('=')? type=BoundedContextType) &
(('responsibilities' ('=')? responsibilities+=STRING) ("," responsibilities+=STRING)*) &
('implementationTechnology' ('=')? implementationTechnology=STRING) &
('knowledgeLevel' ('=')? knowledgeLevel=KnowledgeLevel) &
('businessModel' ('=')? businessModel=STRING) &
('evolution' ('=')? evolution=Evolution)
)
('domainVisionStatement' ('=')? domainVisionStatement+=STRING) |
('type' ('=')? type+=BoundedContextType) |
(('responsibilities' ('=')? responsibilities+=STRING) ("," responsibilities+=STRING)*) |
('implementationTechnology' ('=')? implementationTechnology+=STRING) |
('knowledgeLevel' ('=')? knowledgeLevel+=KnowledgeLevel) |
('businessModel' ('=')? businessModel+=STRING) |
('evolution' ('=')? evolution+=Evolution)
)*
(
(
modules+=SculptorModule |
Expand Down Expand Up @@ -91,9 +89,9 @@ Subdomain:
(
OPEN
(
('type' ('=')? type=SubDomainType) &
('domainVisionStatement' ('=')? domainVisionStatement=STRING)
)
('type' ('=')? type+=SubDomainType) |
('domainVisionStatement' ('=')? domainVisionStatement+=STRING)
)*
CLOSE
)?
;
Expand Down Expand Up @@ -147,10 +145,10 @@ UpstreamDownstreamRelationship:
(':' name=ID)?
(OPEN
(
('implementationTechnology' ('=')? implementationTechnology=STRING) &
(('exposedAggregates' ('=')? upstreamExposedAggregates+=[Aggregate]) ("," upstreamExposedAggregates+=[Aggregate])*) &
('downstreamRights' ('=')? downstreamGovernanceRights=DownstreamGovernanceRights)
)
('implementationTechnology' ('=')? implementationTechnology+=STRING) |
(('exposedAggregates' ('=')? upstreamExposedAggregates+=[Aggregate]) ("," upstreamExposedAggregates+=[Aggregate])*) |
('downstreamRights' ('=')? downstreamGovernanceRights+=DownstreamGovernanceRights)
)*
CLOSE)?
)
;
Expand All @@ -166,10 +164,10 @@ CustomerSupplierRelationship:
(':' name=ID)?
(OPEN
(
('implementationTechnology' ('=')? implementationTechnology=STRING) &
(('exposedAggregates' ('=')? upstreamExposedAggregates+=[Aggregate]) ("," upstreamExposedAggregates+=[Aggregate])*) &
('downstreamRights' ('=')? downstreamGovernanceRights=DownstreamGovernanceRights)
)
('implementationTechnology' ('=')? implementationTechnology+=STRING) |
(('exposedAggregates' ('=')? upstreamExposedAggregates+=[Aggregate]) ("," upstreamExposedAggregates+=[Aggregate])*) |
('downstreamRights' ('=')? downstreamGovernanceRights+=DownstreamGovernanceRights)
)*
CLOSE)?
)
;
Expand All @@ -178,23 +176,21 @@ Aggregate:
(doc=STRING)?
"Aggregate" name=ID (OPEN
(
(('responsibilities' ('=')? responsibilities+=STRING) ("," responsibilities+=STRING)*) &
(
(('useCases' ('=')? useCases+=[UseCase]) ("," useCases+=[UseCase])*) |
(('userStories' ('=')? userStories+=[UserStory]) ("," userStories+=[UserStory])*) |
((('features' | 'userRequirements') ('=')? userRequirements+=[UserRequirement]) ("," userRequirements+=[UserRequirement])*)
) &
('owner' ('=')? owner=[BoundedContext]) &
('knowledgeLevel' ('=')? knowledgeLevel=KnowledgeLevel) &
(('likelihoodForChange' | 'structuralVolatility') ('=')? likelihoodForChange=Volatility) &
('contentVolatility' ('=')? contentVolatility=Volatility) &
('availabilityCriticality' ('=')? availabilityCriticality=Criticality) &
('consistencyCriticality' ('=')? consistencyCriticality=Criticality) &
('storageSimilarity' ('=')? storageSimilarity=Similarity) &
('securityCriticality' ('=')? securityCriticality=Criticality) &
('securityZone' ('=')? securityZone=STRING) &
('securityAccessGroup' ('=')? securityAccessGroup=STRING)
)
(('responsibilities' ('=')? responsibilities+=STRING) ("," responsibilities+=STRING)*) |
(('useCases' ('=')? useCases+=[UseCase]) ("," useCases+=[UseCase])*) |
(('userStories' ('=')? userStories+=[UserStory]) ("," userStories+=[UserStory])*) |
((('features' | 'userRequirements') ('=')? userRequirements+=[UserRequirement]) ("," userRequirements+=[UserRequirement])*) |
('owner' ('=')? owner+=[BoundedContext]) |
('knowledgeLevel' ('=')? knowledgeLevel+=KnowledgeLevel) |
(('likelihoodForChange' | 'structuralVolatility') ('=')? likelihoodForChange+=Volatility) |
('contentVolatility' ('=')? contentVolatility+=Volatility) |
('availabilityCriticality' ('=')? availabilityCriticality+=Criticality) |
('consistencyCriticality' ('=')? consistencyCriticality+=Criticality) |
('storageSimilarity' ('=')? storageSimilarity+=Similarity) |
('securityCriticality' ('=')? securityCriticality+=Criticality) |
('securityZone' ('=')? securityZone+=STRING) |
('securityAccessGroup' ('=')? securityAccessGroup+=STRING)
)*
CLOSE)?
;

Expand All @@ -205,13 +201,13 @@ UserRequirement:
UseCase:
'UseCase' name=ID (OPEN
(
('actor' ('=')? role=STRING) &
('secondaryActors' ('=')? secondaryActors+=STRING ("," secondaryActors+=STRING)*) &
('interactions' ('=')? features+=Feature ("," features+=Feature)*) &
('benefit' ('=')? benefit=STRING) &
('scope' ('=')? scope=STRING) &
('level' ('=')? level=STRING)
)
('actor' ('=')? role+=STRING) |
('secondaryActors' ('=')? secondaryActors+=STRING ("," secondaryActors+=STRING)*) |
('interactions' ('=')? features+=Feature ("," features+=Feature)*) |
('benefit' ('=')? benefit+=STRING) |
('scope' ('=')? scope+=STRING) |
('level' ('=')? level+=STRING)
)*
CLOSE)?
;

Expand Down Expand Up @@ -247,10 +243,10 @@ SculptorModule:
'Module' name=ID (
OPEN
(
(external?='external') &
('basePackage' '=' basePackage=JavaIdentifier) &
('hint' '=' hint=STRING)
)
(external+='external') |
('basePackage' '=' basePackage+=JavaIdentifier) |
('hint' '=' hint+=STRING)
)*
(
(aggregates+=Aggregate)
)*
Expand Down Expand Up @@ -286,10 +282,10 @@ StakeholderGroup:
Stakeholder:
'Stakeholder' name=ID (OPEN
(
('influence' ('=')? influence=INFLUENCE) &
('interest' ('=')? interest=INTEREST) &
('description' ('=')? description=STRING)
)
('influence' ('=')? influence+=INFLUENCE) |
('interest' ('=')? interest+=INTEREST) |
('description' ('=')? description+=STRING)
)*
CLOSE)?
;

Expand Down Expand Up @@ -334,10 +330,10 @@ Value:
ValueElicitation:
('Stakeholder'|'Stakeholders') stakeholder=[AbstractStakeholder] (OPEN
(
('priority' ('=')? priority=PRIORITY) &
('impact' ('=')? impact=IMPACT) &
('priority' ('=')? priority+=PRIORITY) |
('impact' ('=')? impact+=IMPACT) |
('consequences' (consequences+=Consequence)+)
)
)*
CLOSE)?
;

Expand All @@ -347,9 +343,9 @@ ValueEpic:
(
'As a' stakeholder=[AbstractStakeholder] 'I value' value=STRING 'as demonstrated in'
(
('realization of' realizedValues+=STRING)+ &
('realization of' realizedValues+=STRING)+ |
('reduction of' reducedValues+=STRING)+
)
)*
)
CLOSE)?
;
Expand Down
18 changes: 18 additions & 0 deletions src/language/references/ContextMapperDslScopeComputation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { AstNode, AstNodeDescription, DefaultScopeComputation, LangiumDocument } from 'langium'
import { CancellationToken } from 'vscode-languageserver'

export class ContextMapperDslScopeComputation extends DefaultScopeComputation {
/*
For the time being imports aren't supported yet.
By default, Langium adds top-level elements to the global scope.
Without imports, this behavior is wrong and therefore no nodes must be exported here.
*/
override computeExportsForNode (
_parentNode: AstNode,
_document: LangiumDocument,
_children?: (root: AstNode) => Iterable<AstNode>,
_cancelToken?: CancellationToken
): Promise<AstNodeDescription[]> {
return Promise.resolve([])
}
}
17 changes: 11 additions & 6 deletions src/language/validation/ContextMapperDslValidationRegistry.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,20 @@
import { type ValidationChecks, ValidationRegistry } from 'langium'
import { ContextMapperDslServices } from '../ContextMapperDslModule.js'
import type { ContextMapperDslAstType } from '../generated/ast.js'
import { ContextMapperDslAstType } from '../generated/ast.js'
import { ContextMapperValidationProviderRegistry } from './ContextMapperValidationProviderRegistry.js'

export class ContextMapperDslValidationRegistry extends ValidationRegistry {
constructor (services: ContextMapperDslServices) {
constructor (services: ContextMapperDslServices, validationProviderRegistry: ContextMapperValidationProviderRegistry) {
super(services)
const validator = services.validation.ContextMapperDslValidator
const checks: ValidationChecks<ContextMapperDslAstType> = {
ContextMappingModel: validator.checkContextMappingModel,
Value: validator.checkValue
}

const typesToValidate = validationProviderRegistry.getRegisteredTypes()

// dynamically set validator for all grammar elements
const checks: ValidationChecks<ContextMapperDslAstType> = Object.fromEntries(
typesToValidate.map(type => [type, validator.validate])
)

super.register(checks, validator)
}
}
11 changes: 3 additions & 8 deletions src/language/validation/ContextMapperDslValidator.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { ValidationAcceptor } from 'langium'
import type { ContextMappingModel, Value } from '../generated/ast.js'
import { AstNode, ValidationAcceptor } from 'langium'
import { ContextMapperValidationProviderRegistry } from './ContextMapperValidationProviderRegistry.js'

export class ContextMapperDslValidator {
Expand All @@ -9,11 +8,7 @@ export class ContextMapperDslValidator {
this._registry = contextMapperValidationProviderRegistry
}

checkContextMappingModel (model: ContextMappingModel, acceptor: ValidationAcceptor) {
this._registry.get(model)?.validate(model, acceptor)
}

checkValue (value: Value, acceptor: ValidationAcceptor) {
this._registry.get(value)?.validate(value, acceptor)
validate (node: AstNode, acceptor: ValidationAcceptor) {
this._registry.get(node)?.validate(node, acceptor)
}
}
1 change: 0 additions & 1 deletion src/language/validation/ContextMapperValidationProvider.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { AstNode, ValidationAcceptor } from 'langium'

export interface ContextMapperValidationProvider<T extends AstNode> {
supports(node: AstNode): node is T
validate (node: T, acceptor: ValidationAcceptor): void
}
Loading
Loading