Skip to content

Commit

Permalink
feat(uml): improve debuggability of metamodel
Browse files Browse the repository at this point in the history
  • Loading branch information
DerYeger committed Dec 18, 2023
1 parent ccae049 commit 19f9fd0
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 31 deletions.
3 changes: 3 additions & 0 deletions packages/uml-parser/src/metamodel/handlers/Element.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Element } from '../metamodel'

export const ElementHandler = Element.createHandler()
2 changes: 2 additions & 0 deletions packages/uml-parser/src/metamodel/handlers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { AssociationHandler } from './Association'
import { ClassHandler } from './Class'
import { DataTypeHandler } from './DataType'
import { DependencyHandler } from './Dependency'
import { ElementHandler } from './Element'
import { EnumerationHandler } from './Enumeration'
import { EnumerationLiteralHandler } from './EnumerationLiteral'
import { GeneralizationHandler } from './Generalization'
Expand Down Expand Up @@ -33,6 +34,7 @@ export const handlers: Record<`${string}Handler`, MetamodelElement> = {
ClassHandler,
DataTypeHandler,
DependencyHandler,
ElementHandler,
EnumerationHandler,
EnumerationLiteralHandler,
GeneralizationHandler,
Expand Down
115 changes: 84 additions & 31 deletions packages/uml-parser/src/metamodel/metamodel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export class MetamodelElement implements Handler {
private handler: Handler['handle'] | undefined

public constructor(
public readonly name: string,
public readonly isAbstract: boolean,
public readonly tag?: UmlTag,
public readonly type?: UmlType,
Expand All @@ -36,7 +37,9 @@ export class MetamodelElement implements Handler {
}
generalizations?.forEach((parent) => {
if (this.isAbstract && !parent.isAbstract) {
throw new Error('Parent of abstract type must also be abstract')
throw new Error(
`Parent ${parent.name} of abstract element ${this.name} must also be abstract`,
)
}
this.#generalizations.add(parent)
parent.specialize(this)
Expand Down Expand Up @@ -75,8 +78,11 @@ export class MetamodelElement implements Handler {
return false
}

// TODO: Automatically setFallback type here based on the assignableType
public handle(node: GraphNode) {
// Note: The following check is used to ensure that the metamodel is well defined and complete
if (node.model.settings.debug && !this.handler) {
node.model.debug(`No handler for metamodel element ${this.name}`)
}
this.handler?.(node)
this.generalizations.forEach((parent) => {
parent.handle(node)
Expand All @@ -86,7 +92,7 @@ export class MetamodelElement implements Handler {
}
}

public createHandler(handler?: Handler['handle']) {
public createHandler(handler: Handler['handle'] = () => {}) {
if (this.handler !== undefined) {
throw new Error('Handler already assigned')
}
Expand Down Expand Up @@ -114,34 +120,48 @@ export class MetamodelElement implements Handler {

function define(
assignableTag: UmlTag | undefined,
assignableType: UmlType | undefined,
assignableType: UmlType,
...generalizations: MetamodelElement[]
) {
return new MetamodelElement(
assignableType,
false,
assignableTag,
assignableType,
generalizations,
)
}

function defineAbstract(...generalizations: MetamodelElement[]) {
return new MetamodelElement(true, undefined, undefined, generalizations)
function defineAbstract(name: string, ...generalizations: MetamodelElement[]) {
return new MetamodelElement(name, true, undefined, undefined, generalizations)
}

export const Element = defineAbstract()
export const Element = defineAbstract('Element')

export const NamedElement = defineAbstract(Element)
export const NamedElement = defineAbstract('NamedElement', Element)

export const Namespace = defineAbstract(NamedElement)
export const Namespace = defineAbstract('Namespace', NamedElement)

export const RedefinableElement = defineAbstract(NamedElement)
export const RedefinableElement = defineAbstract(
'RedefinableElement',
NamedElement,
)

export const Classifier = defineAbstract(Namespace, RedefinableElement)
export const Classifier = defineAbstract(
'Classifier',
Namespace,
RedefinableElement,
)

export const BehavioredClassifier = defineAbstract(Classifier)
export const BehavioredClassifier = defineAbstract(
'BehavioredClassifier',
Classifier,
)

export const EncapsulatedClassifier = defineAbstract(Classifier)
export const EncapsulatedClassifier = defineAbstract(
'EncapsulatedClassifier',
Classifier,
)

export const Class = define(
undefined,
Expand All @@ -150,11 +170,17 @@ export const Class = define(
EncapsulatedClassifier,
)

export const TypedElement = defineAbstract(NamedElement)
export const TypedElement = defineAbstract('TypedElement', NamedElement)

export const ConnectableElement = defineAbstract(TypedElement)
export const ConnectableElement = defineAbstract(
'ConnectableElement',
TypedElement,
)

export const MultiplicityElement = defineAbstract(Element)
export const MultiplicityElement = defineAbstract(
'MultiplicityElement',
Element,
)

export const Parameter = define(
Uml.Tags.ownedParameter,
Expand All @@ -163,11 +189,17 @@ export const Parameter = define(
MultiplicityElement,
)

export const TemplateableElement = defineAbstract(Element)
export const TemplateableElement = defineAbstract(
'TemplateableElement',
Element,
)

export const ParameterableElement = defineAbstract(Element)
export const ParameterableElement = defineAbstract(
'ParameterableElement',
Element,
)

export const BehavioralFeature = defineAbstract(Element)
export const BehavioralFeature = defineAbstract('BehavioralFeature', Element)

export const Operation = define(
Uml.Tags.ownedOperation,
Expand All @@ -177,11 +209,15 @@ export const Operation = define(
BehavioralFeature,
)

export const Relationship = defineAbstract(Element)
export const Relationship = defineAbstract('Relationship', Element)

export const DirectedRelationship = defineAbstract(Relationship)
export const DirectedRelationship = defineAbstract(
'DirectedRelationship',
Relationship,
)

export const PackageableElement = defineAbstract(
'PackageableElement',
ParameterableElement,
NamedElement,
)
Expand Down Expand Up @@ -228,24 +264,32 @@ export const Realization = define(undefined, Uml.Types.Realization, Abstraction)
// TODO
export const TemplateBinding = define(
undefined,
undefined,
Uml.Types.TemplateBinding,
DirectedRelationship,
)

// TODO
export const TemplateParameter = define(undefined, undefined, Element)
export const TemplateParameter = define(
undefined,
Uml.Types.TemplateParameter,
Element,
)

// TODO
export const TemplateParameterSubstitution = define(
undefined,
undefined,
Uml.Types.TemplateParameterSubstitution,
Element,
)

// TODO
export const TemplateSignature = define(undefined, undefined, Element)
export const TemplateSignature = define(
undefined,
Uml.Types.TemplateSignature,
Element,
)

export const Type = defineAbstract(PackageableElement)
export const Type = defineAbstract('Type', PackageableElement)

export const Usage = define(undefined, Uml.Types.Usage, Dependency)

Expand All @@ -264,18 +308,24 @@ export const InterfaceRealization = define(
)

// TODO
export const Generalization = define(undefined, undefined, DirectedRelationship)
export const Generalization = define(
undefined,
Uml.Types.Generalization,
DirectedRelationship,
)

export const ValueSpecification = defineAbstract(
'ValueSpecification',
TypedElement,
PackageableElement,
)

export const Feature = defineAbstract(RedefinableElement)
export const Feature = defineAbstract('Feature', RedefinableElement)

export const DeploymentTarget = defineAbstract(NamedElement)
export const DeploymentTarget = defineAbstract('DeploymentTarget', NamedElement)

export const StructuralFeature = defineAbstract(
'StructuralFeature',
MultiplicityElement,
TypedElement,
Feature,
Expand All @@ -289,7 +339,10 @@ export const Property = define(
StructuralFeature,
)

export const LiteralSpecification = defineAbstract(ValueSpecification)
export const LiteralSpecification = defineAbstract(
'LiteralSpecification',
ValueSpecification,
)

export const LiteralInteger = define(
undefined,
Expand All @@ -313,7 +366,7 @@ export const PrimitiveType = define(
DataType,
)

export const DeployedArtifact = defineAbstract(NamedElement)
export const DeployedArtifact = defineAbstract('DeployedArtifact', NamedElement)

export const InstanceSpecification = define(
undefined,
Expand Down
4 changes: 4 additions & 0 deletions packages/uml-parser/src/uml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ const Types = {
Property: 'Property',
Realization: 'Realization',
Substitution: 'Substitution',
TemplateBinding: 'TemplateBinding',
TemplateParameter: 'TemplateParameter',
TemplateParameterSubstitution: 'TemplateParameterSubstitution',
TemplateSignature: 'TemplateSignature',
Usage: 'Usage',
} as const

Expand Down

0 comments on commit 19f9fd0

Please sign in to comment.