Skip to content

Commit

Permalink
feat: inverse inheritance for assignability
Browse files Browse the repository at this point in the history
  • Loading branch information
DerYeger committed Dec 12, 2023
1 parent 24cb772 commit e24a31b
Show file tree
Hide file tree
Showing 15 changed files with 156 additions and 73 deletions.
7 changes: 7 additions & 0 deletions packages/uml-parser/src/refiners/class.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Uml } from '../uml'

import { UmlElement } from './umlElement'

export const Class = new UmlElement(
(node) => Uml.getType(node) === Uml.Types.Class,
)
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import type { GraphNode } from '@cm2ml/ir'

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

import { PackageableElement } from './packageableElement'

export class Dependency extends PackageableElement {
public isApplicable(node: GraphNode) {
return Uml.getType(node) === Uml.Types.Dependency
}

public refine(node: GraphNode) {
super.refine(node)
export const Dependency = PackageableElement.extend(
(node: GraphNode) => Uml.getType(node) === Uml.Types.Dependency,
(node: GraphNode) => {
const clientId = node.getAttribute(Uml.Attributes.client)?.value.literal
if (!clientId) {
throw new Error('Missing client attribute on dependency')
Expand All @@ -33,5 +29,5 @@ export class Dependency extends PackageableElement {
}
// TODO: Update tag
node.model.addEdge('dependency', client, supplier)
}
}
},
)
7 changes: 7 additions & 0 deletions packages/uml-parser/src/refiners/elementImport.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Uml } from '../uml'

import { UmlElement } from './umlElement'

export const ElementImport = new UmlElement(
(node) => node.tag === Uml.Tags.elementImport,
)
31 changes: 31 additions & 0 deletions packages/uml-parser/src/refiners/generalization.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Stream } from '@yeger/streams'

import { Uml } from '../uml'

import { DirectedRelationship } from './relationship'

export const Generalization = DirectedRelationship.extend(
(node) => node.tag === Uml.Tags.generalization,
(node) => {
const specfic = node.parent
if (!specfic) {
throw new Error('Missing parent for generalization')
}
const generalChild = Stream.from(node.children).find(
(child) => child.tag === Uml.Tags.general,
)
if (!generalChild) {
throw new Error('Missing general child for generalization')
}
const generalId = generalChild.getAttribute('idref')?.value.literal
if (!generalId) {
throw new Error('Missing idref attribute on general')
}
const general = node.model.getNodeById(generalId)
if (!general) {
throw new Error(`Could not find general with id ${generalId}`)
}
node.model.addEdge('generalization', specfic, general)
node.model.removeNode(node)
},
)
37 changes: 14 additions & 23 deletions packages/uml-parser/src/refiners/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,26 @@ import type { GraphNode } from '@cm2ml/ir'

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

import { Dependency } from './nodes/dependency'
import { Package } from './nodes/package'
import { PackageableElement } from './nodes/packageableElement'
import { UmlElement } from './refiner'
import { Class } from './class'
import { Dependency } from './dependency'
import { Generalization } from './generalization'
import { Model } from './model'
import { Package } from './package'
import { PackageableElement } from './packageableElement'
import type { UmlElement } from './umlElement'

// TODO: Remove
class Noop extends UmlElement {
public isApplicable(_node: GraphNode): boolean {
return false
}
}

// TODO: Remove
const noop = new Noop()

// TODO: Make non-partial
// TODO: Make non-partial?
const UmlTagRefiners: Partial<Record<UmlTag, UmlElement>> = {
// elementImport: noop,
packagedElement: new PackageableElement(),
// packageImport: noop,
// packageMerge: noop,
generalization: Generalization,
packagedElement: PackageableElement,
}

// TODO: Make non-partial
const UmlTypeRefiners: Partial<Record<UmlType, UmlElement>> = {
[Uml.Types.Class]: noop,
[Uml.Types.Dependency]: new Dependency(),
[Uml.Types.Model]: noop,
[Uml.Types.Package]: new Package(),
[Uml.Types.Class]: Class,
[Uml.Types.Dependency]: Dependency,
[Uml.Types.Model]: Model,
[Uml.Types.Package]: Package,
}

export function getRefiner(node: GraphNode) {
Expand Down
7 changes: 7 additions & 0 deletions packages/uml-parser/src/refiners/model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Uml } from '../uml'

import { UmlElement } from './umlElement'

export const Model = new UmlElement(
(node) => Uml.getType(node) === Uml.Types.Model,
)
15 changes: 0 additions & 15 deletions packages/uml-parser/src/refiners/nodes/packageableElement.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,38 +1,38 @@
import type { Attributable, GraphNode } from '@cm2ml/ir'

import { Model, Uml } from '../../uml'
import { Uml } from '../uml'

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

export class Package extends PackageableElement {
public isApplicable(node: GraphNode) {
return node.tag === Uml.Types.Package || super.isApplicable(node)
}

public refine(node: GraphNode) {
super.refine(node)
export const Package = PackageableElement.extend(
(node) => Uml.getType(node) === Uml.Types.Package,
(node) => {
node.children.forEach((child) => {
if (Model.isElementImport(child)) {
if (ElementImport.isAssignable(child)) {
addElementImport(node, child)
}
if (Model.isPackage(child)) {
if (Package.isAssignable(child)) {
node.model.addEdge('nestedPackage', node, child)
}
if (Model.isPackagedElement(child)) {
if (PackageableElement.isAssignable(child)) {
node.model.addEdge(Uml.Tags.packagedElement, node, child)
}
if (Model.isPackageImport(child)) {
if (PackageImport.isAssignable(child)) {
addPackageImport(node, child)
}
if (Model.isPackageMerge(child)) {
if (PackageMerge.isAssignable(child)) {
addPackageMerge(node, child)
}
if (Model.isType(child)) {
node.model.addEdge('ownedType', 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
7 changes: 7 additions & 0 deletions packages/uml-parser/src/refiners/packageImport.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Uml } from '../uml'

import { UmlElement } from './umlElement'

export const PackageImport = new UmlElement(
(node) => node.tag === Uml.Tags.packageImport,
)
7 changes: 7 additions & 0 deletions packages/uml-parser/src/refiners/packageMerge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Uml } from '../uml'

import { UmlElement } from './umlElement'

export const PackageMerge = new UmlElement(
(node) => node.tag === Uml.Tags.packageMerge,
)
7 changes: 7 additions & 0 deletions packages/uml-parser/src/refiners/packageableElement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { Uml } from '../uml'

import { UmlElement } from './umlElement'

export const PackageableElement = new UmlElement(
(node) => node.tag === Uml.Tags.packagedElement,
)
7 changes: 0 additions & 7 deletions packages/uml-parser/src/refiners/refiner.ts

This file was deleted.

7 changes: 7 additions & 0 deletions packages/uml-parser/src/refiners/relationship.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { UmlElement } from './umlElement'

// TODO
export const Relationship = new UmlElement(() => false)

// TODO
export const DirectedRelationship = Relationship.extend(() => false)
36 changes: 36 additions & 0 deletions packages/uml-parser/src/refiners/umlElement.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { GraphNode } from '@cm2ml/ir'

export class UmlElement {
readonly #isAssignable: (node: GraphNode) => boolean
readonly #refine?: (node: GraphNode) => void
private readonly subRefiners: UmlElement[] = []
public constructor(
isAssignable: (node: GraphNode) => boolean,
refine?: (node: GraphNode) => void,
private readonly parent?: UmlElement,
) {
this.#isAssignable = isAssignable
this.#refine = refine
}

public isAssignable(node: GraphNode): boolean {
return (
this.#isAssignable(node) ||
this.subRefiners.some((r) => r.isAssignable(node))
)
}

public refine(node: GraphNode): void {
this.parent?.refine(node)
this.#refine?.(node)
}

public extend(
isAssignable: (node: GraphNode) => boolean,
refine?: (node: GraphNode) => void,
): UmlElement {
const subRefiner = new UmlElement(isAssignable, refine, this)
this.subRefiners.push(subRefiner)
return subRefiner
}
}
2 changes: 2 additions & 0 deletions packages/uml-parser/src/uml.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { parseNamespace } from '@cm2ml/utils'

const Tags = {
elementImport: 'elementImport',
general: 'general',
generalization: 'generalization',
packagedElement: 'packagedElement',
packageImport: 'packageImport',
packageMerge: 'packageMerge',
Expand Down

0 comments on commit e24a31b

Please sign in to comment.