Skip to content

Commit

Permalink
feat(uml): automatically create handler registry
Browse files Browse the repository at this point in the history
  • Loading branch information
DerYeger committed Dec 18, 2023
1 parent 88ec648 commit 22204ec
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 63 deletions.
6 changes: 3 additions & 3 deletions packages/uml-parser/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down Expand Up @@ -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(
Expand All @@ -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))
}
63 changes: 25 additions & 38 deletions packages/uml-parser/src/metamodel/handler-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<Record<UmlType, Handler>> = {
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<UmlTag | UmlType, MetamodelElement>()

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<Record<UmlTag, Handler>> = {
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)
}
6 changes: 5 additions & 1 deletion packages/uml-parser/src/metamodel/handlers/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { MetamodelElement } from '../metamodel'

import { AbstractionHandler } from './Abstraction'
import { AssociationHandler } from './Association'
import { ClassHandler } from './Class'
Expand All @@ -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,
Expand Down
12 changes: 7 additions & 5 deletions packages/uml-parser/src/metamodel/metamodel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,24 @@ 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[]
assignableTags?: UmlTag[]
assignableTypes?: UmlType[]
}

export class MetamodelElement {
export class MetamodelElement implements Handler {
readonly #generalizations = new Set<MetamodelElement>()
readonly #specializations = new Set<MetamodelElement>()

readonly #assignableTags = new Set<UmlTag>()
readonly #assignableTypes = new Set<string>()

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(
Expand Down Expand Up @@ -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) {
Expand Down
16 changes: 0 additions & 16 deletions packages/uml-parser/src/uml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down

0 comments on commit 22204ec

Please sign in to comment.