Skip to content

Commit

Permalink
feat(uml): improve metamodel
Browse files Browse the repository at this point in the history
  • Loading branch information
DerYeger committed Dec 18, 2023
1 parent 87a748c commit aeae6fe
Show file tree
Hide file tree
Showing 55 changed files with 614 additions and 449 deletions.
22 changes: 11 additions & 11 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 { getRefiner } from './refiners'
import { getHandler } from './metamodel/handler-registry'
import { Uml } from './uml'

export const UmlRefiner = definePlugin({
Expand Down Expand Up @@ -32,12 +32,12 @@ function refine(model: GraphModel, strict: boolean): GraphModel {
throw new Error(`Unknown type: ${type}`)
}
})
// console.log(
// Stream.from(model.edges)
// .map((edge) => `${edge.source.tag} --${edge.tag}-> ${edge.target.tag}`)
// .join('\n')
// .concat('\n'),
// )
console.log(
Stream.from(model.edges)
.map((edge) => `${edge.source.tag} --${edge.tag}-> ${edge.target.tag}`)
.join('\n')
.concat('\n'),
)
return model
}

Expand Down Expand Up @@ -66,17 +66,17 @@ function narrowModelRefine(model: GraphModel) {
}

function refineNode(node: GraphNode, strict: boolean) {
const refiner = getRefiner(node)
if (!refiner) {
const handler = getHandler(node)
if (!handler) {
if (strict) {
throw new Error(
`No refiner for node with tag ${node.tag} and type ${Uml.getType(
`No handler for node with tag ${node.tag} and type ${Uml.getRawType(
node,
)}`,
)
}
return
}
refiner.refine(node)
handler(node)
Stream.from(node.children).forEach((child) => refineNode(child, strict))
}
47 changes: 47 additions & 0 deletions packages/uml-parser/src/metamodel/handler-registry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import type { GraphNode } from '@cm2ml/ir'

import { Uml } from '../uml'
import type { UmlTag, UmlType } from '../uml'

import { handlers } from './handlers'
import type { Handler } from './metamodel'

export const typeHandlers: Partial<Record<UmlType, Handler>> = {
Abstraction: handlers.AbstractionHandler,
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,
}

export const tagHandlers: Partial<Record<UmlTag, Handler>> = {
interfaceRealization: handlers.InterfaceRealizationHandler,
ownedAttribute: handlers.PropertyHandler,
ownedLiteral: handlers.EnumerationLiteralHandler,
ownedParameter: handlers.ParameterHandler,
ownedOperation: handlers.OperationHandler,
}

export function getHandler(node: GraphNode) {
const type = Uml.getType(node)
if (type) {
return typeHandlers[type]
}
const tag = node.tag
if (Uml.isValidTag(tag)) {
return tagHandlers[tag]
}
return undefined
}
3 changes: 3 additions & 0 deletions packages/uml-parser/src/metamodel/handlers/Abstraction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Abstraction } from '../metamodel'

export const AbstractionHandler = Abstraction.createHandler()
3 changes: 3 additions & 0 deletions packages/uml-parser/src/metamodel/handlers/Class.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Class } from '../metamodel'

export const ClassHandler = Class.createHandler()
3 changes: 3 additions & 0 deletions packages/uml-parser/src/metamodel/handlers/DataType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { DataType } from '../metamodel'

export const DataTypeHandler = DataType.createHandler()
27 changes: 27 additions & 0 deletions packages/uml-parser/src/metamodel/handlers/Dependency.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { GraphNode } from '@cm2ml/ir'

import { Uml } from '../../uml'
import { Dependency } from '../metamodel'

export const DependencyHandler = Dependency.createHandler((node: GraphNode) => {
const clientId = node.getAttribute(Uml.Attributes.client)?.value.literal
if (!clientId) {
throw new Error('Missing client attribute on dependency')
}
const client = node.model.getNodeById(clientId)
if (!client) {
throw new Error(`Could not find client with id ${clientId} for dependency`)
}
const supplierId = node.getAttribute(Uml.Attributes.supplier)?.value.literal
if (!supplierId) {
throw new Error('Missing supplier attribute on dependency')
}
const supplier = node.model.getNodeById(supplierId)
if (!supplier) {
throw new Error(
`Could not find supplier with id ${supplierId} for dependency`,
)
}
// TODO: Update tag
node.model.addEdge('dependency', client, supplier)
})
3 changes: 3 additions & 0 deletions packages/uml-parser/src/metamodel/handlers/Enumeration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Enumeration } from '../metamodel'

export const EnumerationHandler = Enumeration.createHandler()
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { EnumerationLiteral } from '../metamodel'

// TODO
export const EnumerationLiteralHandler = EnumerationLiteral.createHandler()
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { Stream } from '@yeger/streams'

import { Uml, copyAttributes } from '../uml'
import { Uml, copyAttributes } from '../../uml'
import { Generalization } from '../metamodel'

import { DirectedRelationship } from './directedRelationship'

export const Generalization = DirectedRelationship.extend(
(node) => node.tag === Uml.Tags.generalization,
export const GeneralizationHandler = Generalization.createHandler(
(generalization) => {
const specfic = generalization.parent
if (!specfic) {
Expand Down
3 changes: 3 additions & 0 deletions packages/uml-parser/src/metamodel/handlers/Interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Interface } from '../metamodel'

export const InterfaceHandler = Interface.createHandler()
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { Uml } from '../uml'
import { Uml } from '../../uml'
import { InterfaceRealization } from '../metamodel'

import { Realization } from './realization'

// TODO
export const InterfaceRealization = Realization.extend(
(node) => node.tag === Uml.Tags.interfaceRealization,
export const InterfaceRealizationHandler = InterfaceRealization.createHandler(
(node) => {
// Contract
const contract = node.getAttribute('contract')?.value.literal
Expand Down
3 changes: 3 additions & 0 deletions packages/uml-parser/src/metamodel/handlers/LiteralInteger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { LiteralInteger } from '../metamodel'

export const LiteralIntegerHandler = LiteralInteger.createHandler()
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { LiteralUnlimitedNatural } from '../metamodel'

export const LiteralUnlimitedNaturalHandler =
LiteralUnlimitedNatural.createHandler()
3 changes: 3 additions & 0 deletions packages/uml-parser/src/metamodel/handlers/Model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { Model } from '../metamodel'

export const ModelHandler = Model.createHandler()
24 changes: 24 additions & 0 deletions packages/uml-parser/src/metamodel/handlers/MultiplicityElement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Uml } from '../../uml'
import { MultiplicityElement } from '../metamodel'

export const MultiplicityElementHandler = MultiplicityElement.createHandler(
(node) => {
node.children.forEach((child) => {
if (child.tag === Uml.Tags.lowerValue) {
node.model.addEdge('lowerValue', node, child)
const lowerValue = child.getAttribute('value')?.value.literal
if (lowerValue === undefined) {
throw new Error('LowerValue must have a value')
}
node.addAttribute({ name: 'lower', value: { literal: lowerValue } })
} else if (child.tag === Uml.Tags.upperValue) {
node.model.addEdge('upperValue', node, child)
const upperValue = child.getAttribute('value')?.value.literal
if (upperValue === undefined) {
throw new Error('UpperValue must have a value')
}
node.addAttribute({ name: 'upper', value: { literal: upperValue } })
}
})
},
)
17 changes: 17 additions & 0 deletions packages/uml-parser/src/metamodel/handlers/Operation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Uml } from '../../uml'
import { Operation, Parameter } from '../metamodel'

// TODO
export const OperationHandler = Operation.createHandler((node) => {
const parent = node.parent
if (!parent) {
throw new Error('OwnedOperation must have a parent')
}
node.model.addEdge('ownedOperation', parent, node)
node.children.forEach((child) => {
if (Parameter.isAssignable(child)) {
node.model.addEdge('ownedParameter', node, child)
}
})
node.tag = Uml.Types.Operation
})
Original file line number Diff line number Diff line change
@@ -1,38 +1,37 @@
import type { GraphNode } from '@cm2ml/ir'

import { Uml, copyAttributes } from '../uml'
import { Uml, copyAttributes } from '../../uml'
import {
ElementImport,
Package,
PackageImport,
PackageMerge,
PackageableElement,
} from '../metamodel'

import { ElementImport } from './elementImport'
import { PackageableElement } from './packageableElement'
import { PackageImport } from './packageImport'
import { PackageMerge } from './packageMerge'

export const Package = PackageableElement.extend(
(node) => Uml.getType(node) === Uml.Types.Package,
(node) => {
node.children.forEach((child) => {
if (ElementImport.isAssignable(child)) {
addElementImport(node, child)
}
if (Package.isAssignable(child)) {
node.model.addEdge('nestedPackage', node, child)
}
if (PackageableElement.isAssignable(child)) {
node.model.addEdge(Uml.Tags.packagedElement, node, child)
}
if (PackageImport.isAssignable(child)) {
addPackageImport(node, child)
}
if (PackageMerge.isAssignable(child)) {
addPackageMerge(node, child)
}
// TODO
// if (Model.isType(child)) {
// node.model.addEdge('ownedType', node, child)
// }
})
},
)
export const PackageHandler = Package.createHandler((node) => {
node.children.forEach((child) => {
if (ElementImport.isAssignable(child)) {
addElementImport(node, child)
}
if (Package.isAssignable(child)) {
node.model.addEdge('nestedPackage', node, child)
}
if (PackageableElement.isAssignable(child)) {
node.model.addEdge(Uml.Tags.packagedElement, node, child)
}
if (PackageImport.isAssignable(child)) {
addPackageImport(node, child)
}
if (PackageMerge.isAssignable(child)) {
addPackageMerge(node, child)
}
// TODO
// if (Model.isType(child)) {
// node.model.addEdge('ownedType', node, child)
// }
})
})

function addElementImport(node: GraphNode, elementImport: GraphNode) {
const importedElementId = elementImport.getAttribute(
Expand Down
14 changes: 14 additions & 0 deletions packages/uml-parser/src/metamodel/handlers/PackageableElement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Package, PackageableElement } from '../metamodel'

export const PackageableElementHandler = PackageableElement.createHandler(
(node) => {
const parent = node.parent
if (!parent) {
return
}
if (!Package.isAssignable(parent)) {
return
}
node.model.addEdge('owningPackage', node, parent)
},
)
11 changes: 11 additions & 0 deletions packages/uml-parser/src/metamodel/handlers/Parameter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Uml } from '../../uml'
import { Parameter } from '../metamodel'

// TODO
export const ParameterHandler = Parameter.createHandler((node) => {
const parent = node.parent
if (parent) {
node.model.addEdge('operation', node, parent)
}
node.tag = Uml.Types.Parameter
})
3 changes: 3 additions & 0 deletions packages/uml-parser/src/metamodel/handlers/PrimitiveType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { PrimitiveType } from '../metamodel'

export const PrimitiveTypeHandler = PrimitiveType.createHandler()
7 changes: 7 additions & 0 deletions packages/uml-parser/src/metamodel/handlers/Property.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Uml } from '../../uml'
import { Property } from '../metamodel'

export const PropertyHandler = Property.createHandler((node) => {
// TODO
node.tag = Uml.Types.Property
})
18 changes: 18 additions & 0 deletions packages/uml-parser/src/metamodel/handlers/TypedElement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Uml } from '../../uml'
import { TypedElement } from '../metamodel'

// TODO
export const TypedElementHandler = TypedElement.createHandler((node) => {
const type = node.getAttribute('type')?.value.literal
if (type === undefined) {
return
}
if (Uml.isValidType(type)) {
return
}
const resolvedType = node.model.getNodeById(type)
if (!resolvedType) {
return
}
node.model.addEdge('type', node, resolvedType)
})
Loading

0 comments on commit aeae6fe

Please sign in to comment.