diff --git a/packages/uml-parser/src/index.ts b/packages/uml-parser/src/index.ts index c4d7086d..d9599574 100644 --- a/packages/uml-parser/src/index.ts +++ b/packages/uml-parser/src/index.ts @@ -3,7 +3,7 @@ import { compose, definePlugin } from '@cm2ml/plugin' import { XmiParser } from '@cm2ml/xmi-parser' import { Stream } from '@yeger/streams' -import { getHandler } from './metamodel/handler-registry' +import { inferHandler } from './metamodel/handler-registry' import { Uml, setFallbackType } from './uml' export const UmlRefiner = definePlugin({ @@ -73,7 +73,7 @@ function stripModel(model: GraphModel) { } function refineNode(node: GraphNode, strict: boolean) { - const handler = getHandler(node) + const handler = inferHandler(node) if (!handler) { if (strict) { throw new Error( @@ -84,6 +84,6 @@ function refineNode(node: GraphNode, strict: boolean) { } return } - handler(node) + handler.handle(node) Stream.from(node.children).forEach((child) => refineNode(child, strict)) } diff --git a/packages/uml-parser/src/metamodel/handler-registry.ts b/packages/uml-parser/src/metamodel/handler-registry.ts index 38f84182..4cde3ccf 100644 --- a/packages/uml-parser/src/metamodel/handler-registry.ts +++ b/packages/uml-parser/src/metamodel/handler-registry.ts @@ -4,49 +4,36 @@ import { Uml } from '../uml' import type { UmlTag, UmlType } from '../uml' import { handlers } from './handlers' -import type { Handler } from './metamodel' +import type { MetamodelElement } from './metamodel' -export const typeHandlers: Partial> = { - Abstraction: handlers.AbstractionHandler, - Association: handlers.AssociationHandler, - Class: handlers.ClassHandler, - DataType: handlers.DataTypeHandler, - Dependency: handlers.DependencyHandler, - Enumeration: handlers.EnumerationHandler, - EnumerationLiteral: handlers.EnumerationLiteralHandler, - Generalization: handlers.GeneralizationHandler, - Interface: handlers.InterfaceHandler, - InterfaceRealization: handlers.InterfaceRealizationHandler, - LiteralInteger: handlers.LiteralIntegerHandler, - LiteralUnlimitedNatural: handlers.LiteralUnlimitedNaturalHandler, - Model: handlers.ModelHandler, - Operation: handlers.OperationHandler, - Package: handlers.PackageHandler, - Parameter: handlers.ParameterHandler, - PrimitiveType: handlers.PrimitiveTypeHandler, - Property: handlers.PropertyHandler, - Realization: handlers.RealizationHandler, - Substitution: handlers.SubstitutionHandler, - Usage: handlers.UsageHandler, +// This registry includes all handlers that are addressable by a UML tag or type +const handlerRegistry = new Map() + +function registerHandler(key: UmlTag | UmlType, handler: MetamodelElement) { + if (handlerRegistry.has(key)) { + throw new Error(`Handler for ${key} already registered`) + } + handlerRegistry.set(key, handler) } -export const tagHandlers: Partial> = { - interfaceRealization: handlers.InterfaceRealizationHandler, - ownedAttribute: handlers.PropertyHandler, - ownedLiteral: handlers.EnumerationLiteralHandler, - ownedParameter: handlers.ParameterHandler, - ownedOperation: handlers.OperationHandler, - substitution: handlers.SubstitutionHandler, +function getHandler(key: string | undefined) { + if (Uml.isValidTag(key) || Uml.isValidType(key)) { + return handlerRegistry.get(key) + } + return undefined } -export function getHandler(node: GraphNode) { - const type = Uml.getType(node) - if (type) { - return typeHandlers[type] +Object.values(handlers).forEach((handler) => { + const tag = handler.assignableTag + if (tag !== undefined) { + registerHandler(tag, handler) } - const tag = node.tag - if (Uml.isValidTag(tag)) { - return tagHandlers[tag] + const type = handler.assignableType + if (type !== undefined) { + registerHandler(type, handler) } - return undefined +}) + +export function inferHandler(node: GraphNode) { + return getHandler(Uml.getType(node)) ?? getHandler(node.tag) } diff --git a/packages/uml-parser/src/metamodel/handlers/index.ts b/packages/uml-parser/src/metamodel/handlers/index.ts index d71b95bb..651b38d6 100644 --- a/packages/uml-parser/src/metamodel/handlers/index.ts +++ b/packages/uml-parser/src/metamodel/handlers/index.ts @@ -1,3 +1,5 @@ +import type { MetamodelElement } from '../metamodel' + import { AbstractionHandler } from './Abstraction' import { AssociationHandler } from './Association' import { ClassHandler } from './Class' @@ -23,7 +25,9 @@ import { SubstitutionHandler } from './Substitution' import { TypedElementHandler } from './TypedElement' import { UsageHandler } from './Usage' -export const handlers = { +// This record includes ALL handlers. +// Handlers MUST be added to this record, in order to properly instantiate the hierarchy chain. +export const handlers: Record<`${string}Handler`, MetamodelElement> = { AbstractionHandler, AssociationHandler, ClassHandler, diff --git a/packages/uml-parser/src/metamodel/metamodel.ts b/packages/uml-parser/src/metamodel/metamodel.ts index f5e100cb..25585cbb 100644 --- a/packages/uml-parser/src/metamodel/metamodel.ts +++ b/packages/uml-parser/src/metamodel/metamodel.ts @@ -3,7 +3,9 @@ import type { GraphNode } from '@cm2ml/ir' import { Uml } from '../uml' import type { UmlTag, UmlType } from '../uml' -export type Handler = (node: GraphNode) => void +export interface Handler { + handle: (node: GraphNode) => void +} export interface Definition { generalizations: MetamodelElement[] @@ -11,14 +13,14 @@ export interface Definition { assignableTypes?: UmlType[] } -export class MetamodelElement { +export class MetamodelElement implements Handler { readonly #generalizations = new Set() readonly #specializations = new Set() readonly #assignableTags = new Set() readonly #assignableTypes = new Set() - private handler: Handler | undefined + private handler: Handler['handle'] | undefined // TODO: Make assignableTags and assignableTypes a single element here. Store it separately from the sets public constructor( @@ -82,12 +84,12 @@ export class MetamodelElement { }) } - public createHandler(handler?: Handler): Handler { + public createHandler(handler?: Handler['handle']) { if (this.handler !== undefined) { throw new Error('Handler already assigned') } this.handler = handler - return (node) => this.handle(node) + return this } private specialize(specialization: MetamodelElement) { diff --git a/packages/uml-parser/src/uml.ts b/packages/uml-parser/src/uml.ts index 661bc305..ff5ff8ff 100644 --- a/packages/uml-parser/src/uml.ts +++ b/packages/uml-parser/src/uml.ts @@ -24,22 +24,6 @@ function isValidTag(tag: string | undefined): tag is UmlTag { return tag !== undefined && tag in Tags } -// function normalizeTag(tag: string | undefined) { -// if (!tag) { -// return undefined -// } -// let normalizedTag = tag -// const indexOfColon = tag.indexOf(':') -// if (indexOfColon !== -1) { -// normalizedTag = tag.slice(indexOfColon + 1) -// } -// normalizedTag = normalizedTag[0]?.toLowerCase() + normalizedTag.slice(1) -// if (isValidTag(normalizedTag)) { -// return normalizedTag -// } -// return undefined -// } - const Types = { Abstraction: 'Abstraction', Association: 'Association',